@veramo/did-comm
Version:
Veramo messaging plugin implementing DIDComm v2.
397 lines (371 loc) • 13.7 kB
text/typescript
import { DIDComm } from '../didcomm.js'
import { IDIDManager, IIdentifier, IKeyManager, IResolver, TAgent } from '../../../core-types/src'
import { createAgent } from '../../../core/src'
import { DIDManager, MemoryDIDStore } from '../../../did-manager/src'
import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src'
import { KeyManagementSystem } from '../../../kms-local/src'
import { getDidKeyResolver, KeyDIDProvider } from '../../../did-provider-key/src'
import { DIDResolverPlugin } from '../../../did-resolver/src'
import { type DIDDocument, Resolver } from 'did-resolver'
import { type IDIDComm } from '../types/IDIDComm.js'
import { base64ToBytes, bytesToUtf8String } from '@veramo/utils'
const multiBaseDoc = {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
controller: 'did:web:portcullis.1keep.com',
id: 'did:web:portcullis.1keep.com',
keyAgreement: ['did:web:portcullis.1keep.com#1'],
service: [
{
accept: ['didcomm/v2'],
id: '#didcomm',
routingKeys: [],
serviceEndpoint: 'https://portcullis.1keep.com/didcomm',
type: 'DIDCommMessaging',
},
],
verificationMethod: [
{
controller: 'did:web:portcullis.1keep.com',
id: 'did:web:portcullis.1keep.com#0',
publicKeyMultibase: 'z8FRmkyRH9xAsLCk51yXN2Qy6uq4eN4iAesa3v3Hv889v',
type: 'Ed25519VerificationKey2020',
},
{
controller: 'did:web:portcullis.1keep.com',
id: 'did:web:portcullis.1keep.com#1',
publicKeyMultibase: 'z6LSpSrLxbAhg2SHwKk7kwpsH7DM7QjFS5iK6qP87eViohud',
type: 'X25519KeyAgreementKey2020',
},
],
}
const base58Doc = {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
authentication: ['did:web:verifiable.ink#0'],
controller: 'did:web:verifiable.ink',
id: 'did:web:verifiable.ink',
keyAgreement: ['did:web:verifiable.ink#1'],
service: [
{
accept: ['didcomm/v2'],
id: 'did:web:verifiable.ink#didcomm',
routingKeys: [],
serviceEndpoint: 'https://verifiable.ink/didcomm',
type: 'DIDCommMessaging',
},
],
verificationMethod: [
{
controller: 'did:web:verifiable.ink',
id: 'did:web:verifiable.ink#0',
publicKeyBase58: 'JDTRH3N5xMFxRnDzn4NdZc1NPUm9w9Y4ZSehnscCKqBw',
type: 'Ed25519VerificationKey2018',
},
{
controller: 'did:web:verifiable.ink',
id: 'did:web:verifiable.ink#1',
publicKeyBase58: 'Djiwiqccx8kupbtbkMYioj3F4eL9GUcsPJ9LUmNJFN7n',
type: 'X25519KeyAgreementKey2019',
},
],
}
const hexDoc = {
'@context': 'https://www.w3.org/ns/did/v1',
id: 'did:web:iiw-demo.herokuapp.com',
verificationMethod: [
{
id: 'did:web:iiw-demo.herokuapp.com#6eb094077b42b299c8ab5e1981e3dba7e9e331c26acdde4c6bd434dcb4bf856e',
type: 'Ed25519VerificationKey2018',
controller: 'did:web:iiw-demo.herokuapp.com',
publicKeyHex: '6eb094077b42b299c8ab5e1981e3dba7e9e331c26acdde4c6bd434dcb4bf856e',
},
{
id: 'did:web:iiw-demo.herokuapp.com#6e5db42d8d42bca9eee16fef45d41cd3cfaa52fa726ade05055f7d09dcfbf669',
type: 'X25519KeyAgreementKey2019',
controller: 'did:web:iiw-demo.herokuapp.com',
publicKeyHex: '6e5db42d8d42bca9eee16fef45d41cd3cfaa52fa726ade05055f7d09dcfbf669',
},
],
authentication: [
'did:web:iiw-demo.herokuapp.com#6eb094077b42b299c8ab5e1981e3dba7e9e331c26acdde4c6bd434dcb4bf856e',
],
assertionMethod: [
'did:web:iiw-demo.herokuapp.com#6eb094077b42b299c8ab5e1981e3dba7e9e331c26acdde4c6bd434dcb4bf856e',
],
keyAgreement: [
'did:web:iiw-demo.herokuapp.com#6eb094077b42b299c8ab5e1981e3dba7e9e331c26acdde4c6bd434dcb4bf856e',
'did:web:iiw-demo.herokuapp.com#6e5db42d8d42bca9eee16fef45d41cd3cfaa52fa726ade05055f7d09dcfbf669',
],
service: [
{
id: 'did:web:iiw-demo.herokuapp.com#msg',
type: 'Messaging',
serviceEndpoint: 'https://iiw-demo.herokuapp.com/messaging',
description: 'Handles incoming POST messages',
},
{
id: 'did:web:iiw-demo.herokuapp.com#msg-didcomm',
type: 'DIDCommMessaging',
serviceEndpoint: 'https://iiw-demo.herokuapp.com/messaging',
description: 'Handles incoming DIDComm messages',
},
],
}
// https://github.com/aviarytech/didcomm/blob/master/tests/fixtures/didDocs/alice.json
const jwkDocX = {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
id: 'did:example:alice',
verificationMethod: [
{
id: 'did:example:alice#key-0',
controller: 'did:example:alice',
type: 'JsonWebKey2020',
publicKeyJwk: {
kty: 'OKP',
crv: 'X25519',
x: 'tsc9iYfy4hv2Mz5Q-ztGjKXeXzWUDWl5DLpfepJg4Wc',
},
},
],
authentication: ['did:example:alice#key-0'],
assertionMethod: ['did:example:alice#key-0'],
keyAgreement: ['did:example:alice#key-0'],
service: [
{
id: 'did:example:alice#didcomm',
type: 'DIDCommMessaging',
serviceEndpoint: 'http://example.com/didcomm',
routingKeys: [],
},
],
}
const jwkDocEd = {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
id: 'did:example:alice',
verificationMethod: [
{
id: 'did:example:alice#key-0',
controller: 'did:example:alice',
type: 'JsonWebKey2020',
publicKeyJwk: {
kty: 'OKP',
crv: 'Ed25519',
x: 'CV-aGlld3nVdgnhoZK0D36Wk-9aIMlZjZOK2XhPMnkQ',
},
},
],
authentication: ['did:example:alice#key-0'],
assertionMethod: ['did:example:alice#key-0'],
keyAgreement: ['did:example:alice#key-0'],
service: [
{
id: 'did:example:alice#didcomm',
type: 'DIDCommMessaging',
serviceEndpoint: 'http://example.com/didcomm',
routingKeys: [],
},
],
}
describe('didComm', () => {
let senderDID: IIdentifier
let recipientDID: IIdentifier
let agent: TAgent<IResolver & IKeyManager & IDIDManager & IDIDComm>
beforeAll(async () => {
agent = createAgent<IResolver & IKeyManager & IDIDManager & IDIDComm>({
plugins: [
new KeyManager({
store: new MemoryKeyStore(),
kms: {
local: new KeyManagementSystem(new MemoryPrivateKeyStore()),
},
}),
new DIDManager({
providers: {
'did:key': new KeyDIDProvider({ defaultKms: 'local' }),
},
store: new MemoryDIDStore(),
defaultProvider: 'did:key',
}),
new DIDResolverPlugin({
resolver: new Resolver({
...getDidKeyResolver(),
fake: async (did: string) => {
let doc: DIDDocument
if (did === 'did:fake:base58') {
doc = base58Doc
} else if (did === 'did:fake:multibase') {
doc = multiBaseDoc
} else if (did === 'did:fake:hex') {
doc = hexDoc
} else if (did === 'did:fake:jwkx') {
doc = jwkDocX
} else if (did === 'did:fake:jwked') {
doc = jwkDocEd
} else {
throw new Error('Bad didUrl for fake resolver: ' + did)
}
// DIDResolutionResult
return {
didResolutionMetadata: {},
didDocument: doc,
didDocumentMetadata: {},
}
},
}),
}),
new DIDComm(),
],
})
senderDID = await agent.didManagerGetOrCreate({
provider: 'did:key',
alias: 'did-comm packing sender DID',
})
recipientDID = await agent.didManagerGetOrCreate({
provider: 'did:key',
alias: 'did-comm packing receiver DID',
})
})
const createTestMessage = (receiverDID: string) => {
return {
type: 'test',
from: senderDID.did,
to: [receiverDID],
id: 'test',
body: { hello: 'world' },
}
}
describe('recipient key formats', () => {
it('should pack message for public key as base58', async () => {
const packedMessage = await agent.packDIDCommMessage({
message: createTestMessage('did:fake:base58'),
packing: 'authcrypt',
})
expect(packedMessage).toBeDefined()
})
it.todo('should unpack message packed for base58')
it('should pack message for public key as multibase', async () => {
const packedMessage = await agent.packDIDCommMessage({
message: createTestMessage('did:fake:multibase'),
packing: 'authcrypt',
})
expect(packedMessage).toBeDefined()
})
it.todo('should unpack message packed for multibase')
it('should pack message for public key as hex', async () => {
const packedMessage = await agent.packDIDCommMessage({
message: createTestMessage('did:fake:hex'),
packing: 'authcrypt',
})
expect(packedMessage).toBeDefined()
})
it.todo('should unpack message packed for hex')
it('should pack message for public key as jwk with X25519 crv', async () => {
const packedMessage = await agent.packDIDCommMessage({
message: createTestMessage('did:fake:jwkx'),
packing: 'authcrypt',
})
expect(packedMessage).toBeDefined()
})
it.todo('should unpack message packed for jwk with X25519 crv')
it('should pack message for public key as jwk with Ed25519 crv', async () => {
const packedMessage = await agent.packDIDCommMessage({
message: createTestMessage('did:fake:jwked'),
packing: 'authcrypt',
})
expect(packedMessage).toBeDefined()
})
it.todo('should unpack message packed for jwk with Ed25519 crv')
})
describe('packing and algorithms', () => {
describe('anoncrypt packing', () => {
const packing = 'anoncrypt'
it(`should pack and unpack using ${packing}`, async () => {
const message = createTestMessage(recipientDID.did)
const packedMessage = await agent.packDIDCommMessage({
message,
packing: packing as any,
})
const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage)
expect(unpackedMessage.message).toEqual(message)
})
describe.each(['XC20P', 'A256GCM', 'A256CBC-HS512'])(`%s enc`, (enc) => {
it(`should pack and unpack using ${packing} packing and ${enc} enc`, async () => {
const message = createTestMessage(recipientDID.did)
const packedMessage = await agent.packDIDCommMessage({
message,
packing: packing as any,
options: {
enc: enc as any,
},
})
const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage)
expect(unpackedMessage.message).toEqual(message)
})
it.each(['ECDH-ES+A256KW', 'ECDH-ES+XC20PKW'])(
`should pack and unpack using ${packing} packing and ${enc} enc and %s alg`,
async (alg) => {
const message = createTestMessage(recipientDID.did)
const packedMessage = await agent.packDIDCommMessage({
message,
packing: packing as any,
options: {
enc: enc as any,
alg: alg as any,
},
})
const protectedHeader = JSON.parse(
bytesToUtf8String(base64ToBytes(JSON.parse(packedMessage.message).protected)),
)
expect(protectedHeader.alg).toEqual(alg)
expect(protectedHeader.enc).toEqual(enc)
const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage)
expect(unpackedMessage.metaData.packing).toEqual(packing)
expect(unpackedMessage.message).toEqual(message)
},
)
})
})
describe('authcrypt packing', () => {
const packing = 'authcrypt'
it(`should pack and unpack using ${packing}`, async () => {
const message = createTestMessage(recipientDID.did)
const packedMessage = await agent.packDIDCommMessage({
message,
packing: packing as any,
})
const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage)
expect(unpackedMessage.message).toEqual(message)
})
describe.each(['XC20P', 'A256GCM', 'A256CBC-HS512'])(`%s enc`, (enc) => {
it(`should pack and unpack using ${packing} packing and ${enc} enc`, async () => {
const message = createTestMessage(recipientDID.did)
const packedMessage = await agent.packDIDCommMessage({
message,
packing: packing as any,
options: {
enc: enc as any,
},
})
const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage)
expect(unpackedMessage.message).toEqual(message)
})
it.each(['ECDH-1PU+A256KW', 'ECDH-1PU+XC20PKW'])(
`should pack and unpack using ${packing} packing and ${enc} enc and %s alg`,
async (alg) => {
const message = createTestMessage(recipientDID.did)
const packedMessage = await agent.packDIDCommMessage({
message,
packing: packing as any,
options: {
enc: enc as any,
alg: alg as any,
},
})
const unpackedMessage = await agent.unpackDIDCommMessage(packedMessage)
expect(unpackedMessage.metaData.packing).toEqual(packing)
expect(unpackedMessage.message).toEqual(message)
},
)
})
})
})
})