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: add out-of-band and did exchange #717

Merged
merged 75 commits into from
May 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
40de2db
Create OOB invitation
jakubkoci Oct 20, 2021
209c00f
Receive OOB connection invitaion
jakubkoci Oct 20, 2021
0103463
Send and recevie request via OOB invitation
jakubkoci Nov 4, 2021
1346f9f
Separate OOB invitation and OOB message handling
jakubkoci Nov 5, 2021
c8fac78
Refactor and get rid of unnecessary dependencies to dispatcher and me…
jakubkoci Nov 8, 2021
90441ed
Allow undefined handshake protocols and request attach attributes
jakubkoci Nov 8, 2021
5aed9a3
Remove unused code and logs
jakubkoci Nov 8, 2021
aea4ad9
Rename oob tests wallets
jakubkoci Nov 8, 2021
e391f59
Add small changes based on review
jakubkoci Nov 9, 2021
16e18f4
Extract getting supported handshake protocols into discover service
jakubkoci Nov 9, 2021
bc58d49
Pass all oob message props via constructor
jakubkoci Nov 10, 2021
09e1afa
Unify create and receive oob message methods
jakubkoci Nov 10, 2021
ed3d10b
Create OOB message with both handshake and requests
jakubkoci Nov 12, 2021
08c6812
Accept OOB message with both handshake and requests
jakubkoci Nov 12, 2021
5add17a
Check if handshake protocols are supported
jakubkoci Nov 17, 2021
c703256
Wait until the connection is made before processing requests
jakubkoci Nov 17, 2021
64b2efb
Reuse connection when it exists
jakubkoci Nov 18, 2021
217984e
Refactor logic when connection already exists
jakubkoci Nov 18, 2021
3bd53f5
Refactor finding of existing connection
jakubkoci Nov 19, 2021
e4c6104
Omit version from handshake protocol list
jakubkoci Nov 19, 2021
207d8e8
Update public API and reorganize folder structure
jakubkoci Nov 19, 2021
88078a8
Add oob invitation encoding and decoding
jakubkoci Nov 22, 2021
87cdb55
Use filter method for handshake protocols from dispatcher
jakubkoci Nov 24, 2021
a7256bf
Create a new connection instead of reusing an existing one
jakubkoci Nov 26, 2021
27a80ed
Update according to main branch API changes
jakubkoci Dec 16, 2021
e2475df
Use connections module instead of service
jakubkoci Dec 17, 2021
6038ae8
Add support for old url encoded connection invitation
jakubkoci Dec 17, 2021
d414cc5
Iterate over all services and reciepient keys to find a connection
jakubkoci Dec 17, 2021
7d03383
Replace custom event handler with async method provided by framework
jakubkoci Dec 28, 2021
8cec4e3
Update types
jakubkoci Dec 28, 2021
f215b48
Pass connection label to old invitation message
jakubkoci Dec 29, 2021
5df8fab
Rename unpack to plaintext
jakubkoci Dec 29, 2021
bf007d5
Add thread to handshake reuse message
jakubkoci Dec 30, 2021
adad44e
Code review updates mainly about API and docs
jakubkoci Jan 8, 2022
260f186
Allow dids in services attribute of oob message
jakubkoci Jan 11, 2022
7a0fe57
Process only first supported requests message
jakubkoci Jan 12, 2022
916a138
Throw error when there are no services in connection record
jakubkoci Jan 13, 2022
08ad1cb
Refactor get requests method
jakubkoci Jan 13, 2022
c69b6c6
Log error insted of commented code
jakubkoci Jan 13, 2022
cb0f06a
Update api after rebase
jakubkoci Jan 18, 2022
4cbf657
Throw error instead of resolving did from services attribute
jakubkoci Jan 18, 2022
9bf2465
Update structure of oob tests
jakubkoci Jan 18, 2022
877d7bb
Add connection record to received message event
jakubkoci Jan 18, 2022
6f71473
Update test of oob with requests witg parsing of encoded invitation
jakubkoci Jan 19, 2022
3249143
Make label mandatory
jakubkoci Jan 19, 2022
c5cc792
Update receive message method signature
jakubkoci Jan 19, 2022
6b1db32
Updates after rebase
jakubkoci Jan 28, 2022
53bcf23
feat(core): did-exchange protocol (#615)
jakubkoci Feb 12, 2022
6529df4
feat(core): add OOB record (#655)
jakubkoci Mar 5, 2022
16944ec
feat: Replace old oob invitation with the new one (#676)
jakubkoci Mar 23, 2022
23235d8
test: reuse connection create and process request tests (#680)
jakubkoci Mar 27, 2022
1bbe3a4
refactor: remove verkeys and did docs from connection record (#687)
jakubkoci Apr 5, 2022
d279e48
feat: find existing connection based on invitation did (#698)
jakubkoci Apr 13, 2022
290fe8a
fix: use new oob instead of connections
jakubkoci Apr 15, 2022
138905c
feat: use did keys for oob and did exchange protocols (#700)
jakubkoci Apr 21, 2022
6c7c48d
test: remove all inline loggers
TimoGlastra Apr 30, 2022
19155c8
fix: convert keys in invitations to correct format
TimoGlastra May 1, 2022
940d96b
fix: always encode keys according to RFCs (#733)
TimoGlastra May 2, 2022
b4feb94
refactor: unify connection record state and role (#732)
TimoGlastra May 2, 2022
21d2bc0
fix: updates after merge
TimoGlastra May 2, 2022
f8011dd
fix: fixes for pickup v2 after merge
TimoGlastra May 2, 2022
daa9f89
Small updates from code review
jakubkoci May 5, 2022
f5d631e
refactor: rename out of band message to invitation
TimoGlastra May 7, 2022
1ef4370
refactor: use id instead of record for oob accept
TimoGlastra May 7, 2022
2b86417
test: fix tests
TimoGlastra May 7, 2022
f4b486d
fix: fixes after merge
TimoGlastra May 7, 2022
5269b42
fix: improve convert to new did document
TimoGlastra May 7, 2022
bf8a805
feat: add handshake reuse handler
TimoGlastra May 8, 2022
9a82813
feat: expose repository method in module
TimoGlastra May 8, 2022
b6c300a
refactor: messageId to invitationId
TimoGlastra May 8, 2022
ca67db7
remove invitation key tag
jakubkoci May 10, 2022
9882a0b
reuse whole services from out-of-band invitation
jakubkoci May 11, 2022
8c438aa
Updates after rebase
jakubkoci May 12, 2022
d7fd2d2
Align postresql tests with agents tests
jakubkoci May 12, 2022
2a5b406
fix: small fixes
TimoGlastra May 13, 2022
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
17 changes: 13 additions & 4 deletions demo/src/Alice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,20 @@ export class Alice extends BaseAgent {
}

private async printConnectionInvite() {
const invite = await this.agent.connections.createConnection()
this.connectionRecordFaberId = invite.connectionRecord.id
const outOfBand = await this.agent.oob.createInvitation()
// FIXME: this won't work as oob doesn't create a connection immediately
const [connectionRecord] = await this.agent.connections.findAllByOutOfBandId(outOfBand.id)
if (!connectionRecord) {
throw new Error(redText(Output.NoConnectionRecordFromOutOfBand))
}
this.connectionRecordFaberId = connectionRecord.id

console.log(Output.ConnectionLink, invite.invitation.toUrl({ domain: `http://localhost:${this.port}` }), '\n')
return invite.connectionRecord
console.log(
Output.ConnectionLink,
outOfBand.outOfBandInvitation.toUrl({ domain: `http://localhost:${this.port}` }),
'\n'
)
return connectionRecord
}

private async waitForConnection() {
Expand Down
6 changes: 5 additions & 1 deletion demo/src/Faber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ export class Faber extends BaseAgent {
}

private async receiveConnectionRequest(invitationUrl: string) {
return await this.agent.connections.receiveInvitationFromUrl(invitationUrl)
const { connectionRecord } = await this.agent.oob.receiveInvitationFromUrl(invitationUrl)
if (!connectionRecord) {
throw new Error(redText(Output.NoConnectionRecordFromOutOfBand))
}
return connectionRecord
}

private async waitForConnection(connectionRecord: ConnectionRecord) {
Expand Down
1 change: 1 addition & 0 deletions demo/src/OutputClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export enum Color {
}

export enum Output {
NoConnectionRecordFromOutOfBand = `\nNo connectionRecord has been created from invitation\n`,
ConnectionEstablished = `\nConnection established!`,
MissingConnectionRecord = `\nNo connectionRecord ID has been set yet\n`,
ConnectionLink = `\nRun 'Receive connection invitation' in Faber and paste this invitation link:\n\n`,
Expand Down
40 changes: 37 additions & 3 deletions packages/core/src/agent/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { CredentialsModule } from '../modules/credentials/CredentialsModule'
import { DidsModule } from '../modules/dids/DidsModule'
import { DiscoverFeaturesModule } from '../modules/discover-features'
import { LedgerModule } from '../modules/ledger/LedgerModule'
import { OutOfBandModule } from '../modules/oob/OutOfBandModule'
import { ProofsModule } from '../modules/proofs/ProofsModule'
import { MediatorModule } from '../modules/routing/MediatorModule'
import { RecipientModule } from '../modules/routing/RecipientModule'
Expand Down Expand Up @@ -61,6 +62,7 @@ export class Agent {
public readonly discovery: DiscoverFeaturesModule
public readonly dids: DidsModule
public readonly wallet: WalletModule
public readonly oob!: OutOfBandModule

public constructor(
initialConfig: InitConfig,
Expand Down Expand Up @@ -123,13 +125,14 @@ export class Agent {
this.discovery = this.container.resolve(DiscoverFeaturesModule)
this.dids = this.container.resolve(DidsModule)
this.wallet = this.container.resolve(WalletModule)
this.oob = this.container.resolve(OutOfBandModule)

// Listen for new messages (either from transports or somewhere else in the framework / extensions)
this.messageSubscription = this.eventEmitter
.observable<AgentMessageReceivedEvent>(AgentEventTypes.AgentMessageReceived)
.pipe(
takeUntil(this.agentConfig.stop$),
concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message))
concatMap((e) => this.messageReceiver.receiveMessage(e.payload.message, { connection: e.payload.connection }))
)
.subscribe()
}
Expand Down Expand Up @@ -224,7 +227,9 @@ export class Agent {
// Also requests mediation ans sets as default mediator
// Because this requires the connections module, we do this in the agent constructor
if (mediatorConnectionsInvite) {
await this.mediationRecipient.provision(mediatorConnectionsInvite)
this.logger.debug('Provision mediation with invitation', { mediatorConnectionsInvite })
const mediatonConnection = await this.getMediationConnection(mediatorConnectionsInvite)
await this.mediationRecipient.provision(mediatonConnection)
}

await this.mediationRecipient.initialize()
Expand Down Expand Up @@ -254,7 +259,7 @@ export class Agent {
}

public async receiveMessage(inboundMessage: unknown, session?: TransportSession) {
await this.messageReceiver.receiveMessage(inboundMessage, session)
return await this.messageReceiver.receiveMessage(inboundMessage, { session })
}

public get injectionContainer() {
Expand All @@ -264,4 +269,33 @@ export class Agent {
public get config() {
return this.agentConfig
}

private async getMediationConnection(mediatorInvitationUrl: string) {
const outOfBandInvitation = await this.oob.parseInvitation(mediatorInvitationUrl)
const outOfBandRecord = await this.oob.findByInvitationId(outOfBandInvitation.id)
const [connection] = outOfBandRecord ? await this.connections.findAllByOutOfBandId(outOfBandRecord.id) : []

if (!connection) {
this.logger.debug('Mediation connection does not exist, creating connection')
// We don't want to use the current default mediator when connecting to another mediator
const routing = await this.mediationRecipient.getRouting({ useDefaultMediator: false })

this.logger.debug('Routing created', routing)
const { connectionRecord: newConnection } = await this.oob.receiveInvitation(outOfBandInvitation, {
routing,
})
this.logger.debug(`Mediation invitation processed`, { outOfBandInvitation })

if (!newConnection) {
throw new AriesFrameworkError('No connection record to provision mediation.')
}

return this.connections.returnWhenIsConnected(newConnection.id)
}

if (!connection.isReady) {
return this.connections.returnWhenIsConnected(connection.id)
}
return connection
}
}
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?.fingerprint,
recipientKey: messageContext.recipientKey?.fingerprint,
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
}
1 change: 1 addition & 0 deletions packages/core/src/agent/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface AgentMessageReceivedEvent extends BaseEvent {
type: typeof AgentEventTypes.AgentMessageReceived
payload: {
message: unknown
connection?: ConnectionRecord
}
}

Expand Down
Loading