UNPKG

@veramo/did-comm

Version:

Veramo messaging plugin implementing DIDComm v2.

1,182 lines (1,085 loc) 56 kB
import { createJWE, createX25519ECDH, Decrypter, decryptJWE, ECDH, Encrypter, JWE } from 'did-jwt' import { randomBytes } from '@noble/hashes/utils' import { generateX25519KeyPairFromSeed } from '../utils.js' import { a256cbcHs512AnonDecrypterX25519WithA256KW, a256cbcHs512AnonEncrypterX25519WithA256KW, a256cbcHs512AuthDecrypterX25519WithA256KW, a256cbcHs512AuthEncrypterX25519WithA256KW, a256gcmAnonDecrypterX25519WithA256KW, a256gcmAnonEncrypterX25519WithA256KW, a256gcmAuthDecrypterEcdh1PuV3x25519WithA256KW, a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW, xc20pAnonDecrypterX25519WithA256KW, xc20pAnonEncrypterX25519WithA256KW, xc20pAuthDecrypterEcdh1PuV3x25519WithA256KW, xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW, } from '../encryption/a256kw-encrypters.js' import { decodeBase64url, encodeBase64url } from '../../../utils/src' import * as u8a from 'uint8arrays' const test_vectors = { pass: [ { key: 'b9NnuOCB0hm7YGNvaE9DMhwH_wjZA1-gWD6dA0JWdL0', cleartext: '{"id":"1234567890","typ":"application/didcomm-plain+json","type":"http://example.com/protocols/lets_do_lunch/1.0/proposal","from":"did:example:alice","to":["did:example:bob"],"created_time":1516269022,"expires_time":1516385931,"body":{"messagespecificattribute":"and its value"}}', jwe: { ciphertext: 'KWS7gJU7TbyJlcT9dPkCw-ohNigGaHSukR9MUqFM0THbCTCNkY-g5tahBFyszlKIKXs7qOtqzYyWbPou2q77XlAeYs93IhF6NvaIjyNqYklvj-OtJt9W2Pj5CLOMdsR0C30wchGoXd6wEQZY4ttbzpxYznqPmJ0b9KW6ZP-l4_DSRYe9B-1oSWMNmqMPwluKbtguC-riy356Xbu2C9ShfWmpmjz1HyJWQhZfczuwkWWlE63g26FMskIZZd_jGpEhPFHKUXCFwbuiw_Iy3R0BIzmXXdK_w7PZMMPbaxssl2UeJmLQgCAP8j8TukxV96EKa6rGgULvlo7qibjJqsS5j03bnbxkuxwbfyu3OxwgVzFWlyHbUH6p', protected: 'eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6IkpIanNtSVJaQWFCMHpSR193TlhMVjJyUGdnRjAwaGRIYlc1cmo4ZzBJMjQifSwiYXB2IjoiTmNzdUFuclJmUEs2OUEtcmtaMEw5WFdVRzRqTXZOQzNaZzc0QlB6NTNQQSIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24iLCJlbmMiOiJYQzIwUCIsImFsZyI6IkVDREgtRVMrQTI1NktXIn0', recipients: [ { encrypted_key: '3n1olyBR3nY7ZGAprOx-b7wYAKza6cvOYjNwVg3miTnbLwPP_FmE1A', header: { kid: 'did:example:bob#key-x25519-1', }, }, ], tag: '6ylC_iAs4JvDQzXeY6MuYQ', iv: 'ESpmcyGiZpRjc5urDela21TOOTW8Wqd1', }, }, { key: '+Egu1APNlDqx3YHm1LAsAHeEQi7kCUvhIXunTMpuplQ=', cleartext: 'i1WprsPZJy9VP2xiqbyHIQ3q0hOdleHb+e7wlV0u9e/+lzO4IYue0NqBcsrPfnF9EVqQkEpi2maC5ym79H4k4w==', jwe: { protected: 'eyJlbmMiOiJYQzIwUCJ9', recipients: [ { header: { alg: 'ECDH-ES+A256KW', epk: { kty: 'OKP', crv: 'X25519', x: '2s6Xfe4ignhNvVHVn3s25FO4xBNTk170ZqqllhHHBVQ' }, }, encrypted_key: 'OHFmMeOC068rgtG_H6NRBvXh-n__bynU_lkL7xMo6hIPGRTW4yNmVA', }, { header: { alg: 'ECDH-ES+XC20PKW', tag: 'oLF5ceVwDl11UMiNYIbnJw', iv: '0__i0CcUPS_R_FOQ33hZJqAtxfsfouHj', epk: { kty: 'OKP', crv: 'X25519', x: 'fcVzcQ3-lO-hmpDtLX_1N7Z1UP31nNCqjEz3SU23zUU' }, }, encrypted_key: 'PBYJSfnq2gunqyc3goI-vSCwF5bMBHvenvxs8q8ntsA', }, ], iv: 'vw-eDC1N1KQZrAyTXsCh8bI4Xul2Ap0g', ciphertext: 'X9Qp6fnJIpcxbsKQete7nioEFC4sC9d0gS7SH4TDeSlSlSqT9ln3UP6x2hmjz-gn5hg5PEUFaIUkETNuBSyMzHyUpE_Lx5iNAzuRnstfpQ_ZG6hka93Mbw', tag: '9FQnp03c5Us_d1OdPYM7hA', }, }, { key: 'MCFWVpCL17Y6+Ck8mVhlHv0ezly3SWDEya4DS/4zU14=', cleartext: 'q3kCxeF6O/OSCD2R2UjkhS+DMjCN09QPchTK91LOEk5w2HQEJ+Ewo1BpkEYAuRE2CDfcMp4hIYIKc+n88BDALA==', jwe: { protected: 'eyJlbmMiOiJYQzIwUCJ9', recipients: [ { header: { alg: 'ECDH-ES+A256KW', epk: { kty: 'OKP', crv: 'X25519', x: 'h6HwUrtHy0gc7KA5PSrRYtIIJxr4V3L2b0lxhWwLhUo' }, }, encrypted_key: '21iyqdWq11QvnngzFHj3wkkIRVOK6rjsdFvXJFH0kMKc8FMA4BXOOg', }, { header: { alg: 'ECDH-ES+XC20PKW', tag: 'TGlpNP9uMvW-y6Uc1jXfZg', iv: 'szLQC8qOTAcTVbE_QCljXMaC8CmJX3T7', epk: { kty: 'OKP', crv: 'X25519', x: '2rxr9f5MTxxTkmyWVGNnSX4coM3MvJguCRsQCrxt9w4' }, }, encrypted_key: 'ndhYa5ZyPr-X9FAeXLQladjmlZirzkmmHCm5rGr98s8', }, ], iv: 'Msj-fX46TRocNzhGkzL81wKhfDb9qHeg', ciphertext: 'tcFKEWxhz-Q-nsnJppYqmFC5R0VT8fdNR0vJzUynlx--_3KDFIuDUdf0lwuwycbYXrEw94EyxfVZb3tkOADHXNaIC0njlB07_D7__eyK0N5bD88TXSe4lA', tag: 'GNnsfuK6-OYVwVc5N0jyHg', }, }, { key: 'KHJWEr94Z1qgaCCbsKu/oCxb07LR/ufntkr1Lu0stWU=', cleartext: '2+YQ5xghWN7RL1BUPHgv39BJRynuZ2+KaiMRsBlnvZzjJclMYSY67SneTmysv3X3yP/DEDkZH2TVPFcHrJRYGg==', jwe: { protected: 'eyJlbmMiOiJYQzIwUCJ9', recipients: [ { header: { alg: 'ECDH-ES+A256KW', epk: { kty: 'OKP', crv: 'X25519', x: 'e7nXXNUZHQQd1lPTK0bzXWteGZGRg2cr73RsaKr2Lyo' }, }, encrypted_key: 'OASoE721beho7x6dGXKj6LL9NR9z7OI12ZaisNHV3b6EyJjxbWIGeg', }, { header: { alg: 'ECDH-ES+XC20PKW', tag: 'bdMaXGwUwNX-obqn7eqO3g', iv: 'RzCIyIO4JJbLo544aqpFbIeH7pq1BIR3', epk: { kty: 'OKP', crv: 'X25519', x: 'lVudXos0kqGtiGpgYj7W_CoWnlKAR5WiNeB_yHZhSS8' }, }, encrypted_key: 'iTxnM7J8iJQHash-un_EMElCjPEpVcXu0BVeKFLjJRM', }, ], iv: 'EeBBbmkL5OvvGbuqqiPVZWUhKKJ2L4Pc', ciphertext: '3ODDtCTQKlX6k0CKOBEE0LsbdUreF7ZeeIj27_pmyZ6uYTKikePR1N24ozdO2oGIGuvC-e9aNMLD8lJmfIbQrCzO6DD-c0AB3xULUF-z92EtI9XaGp08uA', tag: 'g0IWefR0xt-ubzkUJ2Ufeg', }, }, ], fail: [], invalid: [], } describe('One recipient A256KW', () => { let pubkey, secretkey, cleartext: Uint8Array, encrypter: Encrypter, decrypter: Decrypter beforeEach(() => { secretkey = randomBytes(32) pubkey = generateX25519KeyPairFromSeed(secretkey).publicKey cleartext = u8a.fromString('hello world') encrypter = xc20pAnonEncrypterX25519WithA256KW(pubkey) decrypter = xc20pAnonDecrypterX25519WithA256KW(secretkey) }) it('Creates with only ciphertext', async () => { expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter], {}, undefined, true) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected)).enc).toEqual('XC20P') expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter], { more: 'protected' }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { expect.assertions(4) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE(cleartext, [encrypter], { more: 'protected' }, aad) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, decrypter)).rejects.toThrowError('Failed to decrypt') }) }) describe('Multiple recipients A256KW', () => { let pubkey1, secretkey1, pubkey2, secretkey2, cleartext: Uint8Array let encrypter1: Encrypter, decrypter1: Decrypter, encrypter2: Encrypter, decrypter2: Decrypter beforeEach(() => { secretkey1 = randomBytes(32) pubkey1 = generateX25519KeyPairFromSeed(secretkey1).publicKey secretkey2 = randomBytes(32) pubkey2 = generateX25519KeyPairFromSeed(secretkey2).publicKey cleartext = u8a.fromString('my secret message') encrypter1 = xc20pAnonEncrypterX25519WithA256KW(pubkey1) decrypter1 = xc20pAnonDecrypterX25519WithA256KW(secretkey1) encrypter2 = xc20pAnonEncrypterX25519WithA256KW(pubkey2) decrypter2 = xc20pAnonDecrypterX25519WithA256KW(secretkey2) }) it('Creates with only ciphertext', async () => { expect.assertions(4) const jwe = await createJWE(cleartext, [encrypter1, encrypter2]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P' }) expect(await decryptJWE(jwe, decrypter1)).toEqual(cleartext) expect(await decryptJWE(jwe, decrypter2)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { expect.assertions(4) const jwe = await createJWE(cleartext, [encrypter1, encrypter2], { more: 'protected' }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected' }) expect(await decryptJWE(jwe, decrypter2)).toEqual(cleartext) expect(await decryptJWE(jwe, decrypter1)).toEqual(cleartext) }) it('Creates with aad', async () => { expect.assertions(6) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE(cleartext, [encrypter1, encrypter2], { more: 'protected' }, aad) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected' }) expect(await decryptJWE(jwe, decrypter1)).toEqual(cleartext) expect(await decryptJWE(jwe, decrypter2)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, decrypter1)).rejects.toThrowError('Failed to decrypt') await expect(decryptJWE(jwe, decrypter2)).rejects.toThrowError('Failed to decrypt') }) it('Incompatible encrypters throw', async () => { expect.assertions(1) const enc1 = { enc: 'cool enc alg1' } as Encrypter const enc2 = { enc: 'cool enc alg2' } as Encrypter await expect(createJWE(cleartext, [enc1, enc2])).rejects.toThrowError('Incompatible encrypters passed') }) }) describe('ECDH-1PU+A256KW (X25519), Key Wrapping Mode with XC20P content encryption', () => { describe('One recipient', () => { let cleartext: Uint8Array, recipientKey: any, senderKey: any, decrypter: Decrypter beforeEach(() => { recipientKey = generateX25519KeyPairFromSeed(randomBytes(32)) senderKey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') decrypter = xc20pAuthDecrypterEcdh1PuV3x25519WithA256KW(recipientKey.secretKey, senderKey.publicKey) }) it('Creates with only ciphertext', async () => { const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, ) expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid, no apu and no apv', async () => { const kid = 'did:example:receiver#key-1' const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { kid, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toBeUndefined() expect(jwe.recipients!![0].header.apv).toBeUndefined() expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with no kid, apu and apv', async () => { const apu = encodeBase64url('Alice') const apv = encodeBase64url('Bob') const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { apu, apv, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P' }) expect(jwe.recipients!![0].header.kid).toBeUndefined() expect(jwe.recipients!![0].header.apu).toEqual(apu) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid, apu and apv', async () => { const kid = 'did:example:receiver#key-1' const apu = encodeBase64url('Alice') const apv = encodeBase64url('Bob') const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { kid, apu, apv, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toEqual(apu) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, ) const skid = 'did:example:sender#key-1' expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter], { skid, more: 'protected' }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', skid, more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, ) expect.assertions(4) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE(cleartext, [encrypter], { more: 'protected' }, aad) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, decrypter)).rejects.toThrowError('Failed to decrypt') }) describe('using remote ECDH', () => { const message = 'hello world' const receiverPair = generateX25519KeyPairFromSeed(randomBytes(32)) const receiverRemoteECDH = createX25519ECDH(receiverPair.secretKey) const senderPair = generateX25519KeyPairFromSeed(randomBytes(32)) const senderRemoteECDH: ECDH = createX25519ECDH(senderPair.secretKey) it('creates anon JWE with remote ECDH', async () => { const encrypter = xc20pAnonEncrypterX25519WithA256KW(receiverPair.publicKey) const jwe: JWE = await createJWE(u8a.fromString(message), [encrypter]) const decrypter = xc20pAnonDecrypterX25519WithA256KW(receiverRemoteECDH) const decryptedBytes = await decryptJWE(jwe, decrypter) const receivedMessage = u8a.toString(decryptedBytes) expect(receivedMessage).toEqual(message) }) it('creates and decrypts auth JWE', async () => { const encrypter = xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( receiverPair.publicKey, senderRemoteECDH, ) const jwe: JWE = await createJWE(u8a.fromString(message), [encrypter]) const decrypter = xc20pAuthDecrypterEcdh1PuV3x25519WithA256KW( receiverRemoteECDH, senderPair.publicKey, ) const decryptedBytes = await decryptJWE(jwe, decrypter) const receivedMessage = u8a.toString(decryptedBytes) expect(receivedMessage).toEqual(message) }) it(`throws error when using bad secret key size`, async () => { expect.assertions(1) const badSecretKey = randomBytes(64) expect(() => { createX25519ECDH(badSecretKey) }).toThrow('invalid_argument') }) it(`throws error when using bad public key size`, async () => { expect.assertions(1) const ecdh: ECDH = createX25519ECDH(randomBytes(32)) const badPublicKey = randomBytes(64) expect(ecdh(badPublicKey)).rejects.toThrow('invalid_argument') }) }) }) describe('Multiple recipients', () => { let cleartext: any, senderkey: any const recipients: any[] = [] beforeEach(() => { senderkey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') recipients[0] = { kid: 'did:example:receiver1#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[0] = { ...recipients[0], ...{ encrypter: xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipients[0].recipientkey.publicKey, senderkey.secretKey, { kid: recipients[0].kid }, ), decrypter: xc20pAuthDecrypterEcdh1PuV3x25519WithA256KW( recipients[0].recipientkey.secretKey, senderkey.publicKey, ), }, } recipients[1] = { kid: 'did:example:receiver2#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[1] = { ...recipients[1], ...{ encrypter: xc20pAuthEncrypterEcdh1PuV3x25519WithA256KW( recipients[1].recipientkey.publicKey, senderkey.secretKey, { kid: recipients[1].kid }, ), decrypter: xc20pAuthDecrypterEcdh1PuV3x25519WithA256KW( recipients[1].recipientkey.secretKey, senderkey.publicKey, ), }, } }) it('Creates with only ciphertext', async () => { expect.assertions(4) const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { expect.assertions(4) const skid = 'did:example:sender#key-1' const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected', skid, }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected', skid }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { expect.assertions(6) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE( cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected' }, aad, ) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'XC20P', more: 'protected' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') }) it('Incompatible encrypters throw', async () => { expect.assertions(1) const enc1 = { enc: 'cool enc alg1' } as Encrypter const enc2 = { enc: 'cool enc alg2' } as Encrypter await expect(createJWE(cleartext, [enc1, enc2])).rejects.toThrowError('Incompatible encrypters passed') }) }) }) describe('ECDH-ES+A256KW (X25519), Key Wrapping Mode with A256GCM content encryption', () => { describe('One recipient', () => { let cleartext: Uint8Array, recipientKey: any, senderKey: any, decrypter: Decrypter beforeEach(() => { recipientKey = generateX25519KeyPairFromSeed(randomBytes(32)) senderKey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') decrypter = a256gcmAnonDecrypterX25519WithA256KW(recipientKey.secretKey) }) it('Creates with only ciphertext', async () => { const encrypter = a256gcmAnonEncrypterX25519WithA256KW(recipientKey.publicKey) expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid, no apu and no apv', async () => { const kid = 'did:example:receiver#key-1' const encrypter = a256gcmAnonEncrypterX25519WithA256KW(recipientKey.publicKey, kid) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toBeUndefined() expect(jwe.recipients!![0].header.apv).toBeUndefined() expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with no kid, with apv', async () => { const apv = encodeBase64url('Bob') const encrypter = a256gcmAnonEncrypterX25519WithA256KW(recipientKey.publicKey, undefined, apv) expect.assertions(5) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(jwe.recipients!![0].header.kid).toBeUndefined() expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid and apv', async () => { const kid = 'did:example:receiver#key-1' const apv = encodeBase64url('Bob') const encrypter = a256gcmAnonEncrypterX25519WithA256KW(recipientKey.publicKey, kid, apv) expect.assertions(5) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { const encrypter = a256gcmAnonEncrypterX25519WithA256KW(recipientKey.publicKey) const skid = 'did:example:sender#key-1' expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter], { skid, more: 'protected' }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', skid, more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { const encrypter = a256gcmAnonEncrypterX25519WithA256KW(recipientKey.publicKey) expect.assertions(4) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE(cleartext, [encrypter], { more: 'protected' }, aad) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, decrypter)).rejects.toThrowError('Failed to decrypt') }) describe('using remote ECDH', () => { const message = 'hello world' const receiverPair = generateX25519KeyPairFromSeed(randomBytes(32)) const receiverRemoteECDH = createX25519ECDH(receiverPair.secretKey) it('creates JWE with remote ECDH', async () => { const encrypter = a256gcmAnonEncrypterX25519WithA256KW(receiverPair.publicKey) const jwe: JWE = await createJWE(u8a.fromString(message), [encrypter]) const decrypter = a256gcmAnonDecrypterX25519WithA256KW(receiverRemoteECDH) const decryptedBytes = await decryptJWE(jwe, decrypter) const receivedMessage = u8a.toString(decryptedBytes) expect(receivedMessage).toEqual(message) }) }) }) describe('Multiple recipients', () => { let cleartext: any, senderkey: any const recipients: any[] = [] beforeEach(() => { senderkey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') recipients[0] = { kid: 'did:example:receiver1#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[0] = { ...recipients[0], ...{ encrypter: a256gcmAnonEncrypterX25519WithA256KW( recipients[0].recipientkey.publicKey, recipients[0].kid, ), decrypter: a256gcmAnonDecrypterX25519WithA256KW(recipients[0].recipientkey.secretKey), }, } recipients[1] = { kid: 'did:example:receiver2#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[1] = { ...recipients[1], ...{ encrypter: a256gcmAnonEncrypterX25519WithA256KW( recipients[1].recipientkey.publicKey, recipients[1].kid, ), decrypter: a256gcmAnonDecrypterX25519WithA256KW(recipients[1].recipientkey.secretKey), }, } }) it('Creates with only ciphertext', async () => { expect.assertions(4) const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { expect.assertions(4) const skid = 'did:example:sender#key-1' const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected', skid, }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', more: 'protected', skid }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { expect.assertions(6) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE( cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected' }, aad, ) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', more: 'protected' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') }) it('Incompatible encrypters throw', async () => { expect.assertions(1) const enc1 = { enc: 'cool enc alg1' } as Encrypter const enc2 = { enc: 'cool enc alg2' } as Encrypter await expect(createJWE(cleartext, [enc1, enc2])).rejects.toThrowError('Incompatible encrypters passed') }) }) }) describe('ECDH-1PU+A256KW (X25519), Key Wrapping Mode with A256GCM content encryption', () => { describe('One recipient', () => { let cleartext: Uint8Array, recipientKey: any, senderKey: any, decrypter: Decrypter beforeEach(() => { recipientKey = generateX25519KeyPairFromSeed(randomBytes(32)) senderKey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') decrypter = a256gcmAuthDecrypterEcdh1PuV3x25519WithA256KW(recipientKey.secretKey, senderKey.publicKey) }) it('Creates with only ciphertext', async () => { const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, ) expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid, no apu and no apv', async () => { const kid = 'did:example:receiver#key-1' const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { kid, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toBeUndefined() expect(jwe.recipients!![0].header.apv).toBeUndefined() expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with no kid, with apu and apv', async () => { const apu = encodeBase64url('Alice') const apv = encodeBase64url('Bob') const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { apu, apv, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(jwe.recipients!![0].header.kid).toBeUndefined() expect(jwe.recipients!![0].header.apu).toEqual(apu) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid and apu and apv', async () => { const kid = 'did:example:receiver#key-1' const apu = encodeBase64url('Alice') const apv = encodeBase64url('Bob') const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { kid, apu, apv, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toEqual(apu) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, ) const skid = 'did:example:sender#key-1' expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter], { skid, more: 'protected' }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', skid, more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, ) expect.assertions(4) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE(cleartext, [encrypter], { more: 'protected' }, aad) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, decrypter)).rejects.toThrowError('Failed to decrypt') }) describe('using remote ECDH', () => { const message = 'hello world' const receiverPair = generateX25519KeyPairFromSeed(randomBytes(32)) const receiverRemoteECDH = createX25519ECDH(receiverPair.secretKey) const senderPair = generateX25519KeyPairFromSeed(randomBytes(32)) const senderRemoteECDH: ECDH = createX25519ECDH(senderPair.secretKey) it('creates JWE with remote ECDH', async () => { const encrypter = a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( receiverPair.publicKey, senderRemoteECDH, ) const jwe: JWE = await createJWE(u8a.fromString(message), [encrypter]) const decrypter = a256gcmAuthDecrypterEcdh1PuV3x25519WithA256KW( receiverRemoteECDH, senderPair.publicKey, ) const decryptedBytes = await decryptJWE(jwe, decrypter) const receivedMessage = u8a.toString(decryptedBytes) expect(receivedMessage).toEqual(message) }) }) }) describe('Multiple recipients', () => { let cleartext: any, senderkey: any const recipients: any[] = [] beforeEach(() => { senderkey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') recipients[0] = { kid: 'did:example:receiver1#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[0] = { ...recipients[0], ...{ encrypter: a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipients[0].recipientkey.publicKey, senderkey.secretKey, { kid: recipients[0].kid }, ), decrypter: a256gcmAuthDecrypterEcdh1PuV3x25519WithA256KW( recipients[0].recipientkey.secretKey, senderkey.publicKey, ), }, } recipients[1] = { kid: 'did:example:receiver2#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[1] = { ...recipients[1], ...{ encrypter: a256gcmAuthEncrypterEcdh1PuV3x25519WithA256KW( recipients[1].recipientkey.publicKey, senderkey.secretKey, { kid: recipients[1].kid }, ), decrypter: a256gcmAuthDecrypterEcdh1PuV3x25519WithA256KW( recipients[1].recipientkey.secretKey, senderkey.publicKey, ), }, } }) it('Creates with only ciphertext', async () => { expect.assertions(4) const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { expect.assertions(4) const skid = 'did:example:sender#key-1' const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected', skid, }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', more: 'protected', skid }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { expect.assertions(6) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE( cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected' }, aad, ) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256GCM', more: 'protected' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') }) it('Incompatible encrypters throw', async () => { expect.assertions(1) const enc1 = { enc: 'cool enc alg1' } as Encrypter const enc2 = { enc: 'cool enc alg2' } as Encrypter await expect(createJWE(cleartext, [enc1, enc2])).rejects.toThrowError('Incompatible encrypters passed') }) }) }) describe('ECDH-ES+A256KW (X25519), Key Wrapping Mode with A256CBC-HS512 content encryption', () => { describe('One recipient', () => { let cleartext: Uint8Array, recipientKey: any, senderKey: any, decrypter: Decrypter beforeEach(() => { recipientKey = generateX25519KeyPairFromSeed(randomBytes(32)) senderKey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') decrypter = a256cbcHs512AnonDecrypterX25519WithA256KW(recipientKey.secretKey) }) it('Creates with only ciphertext', async () => { const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(recipientKey.publicKey) expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid, no apu and no apv', async () => { const kid = 'did:example:receiver#key-1' const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(recipientKey.publicKey, kid) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toBeUndefined() expect(jwe.recipients!![0].header.apv).toBeUndefined() expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with no kid, with apv', async () => { const apv = encodeBase64url('Bob') const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(recipientKey.publicKey, undefined, apv) expect.assertions(5) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(jwe.recipients!![0].header.kid).toBeUndefined() expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid and apv', async () => { const kid = 'did:example:receiver#key-1' const apv = encodeBase64url('Bob') const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(recipientKey.publicKey, kid, apv) expect.assertions(5) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(recipientKey.publicKey) const skid = 'did:example:sender#key-1' expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter], { skid, more: 'protected' }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512', skid, more: 'protected', }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(recipientKey.publicKey) expect.assertions(4) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE(cleartext, [encrypter], { more: 'protected' }, aad) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512', more: 'protected' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, decrypter)).rejects.toThrowError('Failed to decrypt') }) describe('using remote ECDH', () => { const message = 'hello world' const receiverPair = generateX25519KeyPairFromSeed(randomBytes(32)) const receiverRemoteECDH = createX25519ECDH(receiverPair.secretKey) it('creates JWE with remote ECDH', async () => { const encrypter = a256cbcHs512AnonEncrypterX25519WithA256KW(receiverPair.publicKey) const jwe: JWE = await createJWE(u8a.fromString(message), [encrypter]) const decrypter = a256cbcHs512AnonDecrypterX25519WithA256KW(receiverRemoteECDH) const decryptedBytes = await decryptJWE(jwe, decrypter) const receivedMessage = u8a.toString(decryptedBytes) expect(receivedMessage).toEqual(message) }) }) }) describe('Multiple recipients', () => { let cleartext: any, senderkey: any const recipients: any[] = [] beforeEach(() => { senderkey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') recipients[0] = { kid: 'did:example:receiver1#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[0] = { ...recipients[0], ...{ encrypter: a256cbcHs512AnonEncrypterX25519WithA256KW( recipients[0].recipientkey.publicKey, recipients[0].kid, ), decrypter: a256cbcHs512AnonDecrypterX25519WithA256KW(recipients[0].recipientkey.secretKey), }, } recipients[1] = { kid: 'did:example:receiver2#key-1', recipientkey: generateX25519KeyPairFromSeed(randomBytes(32)), } recipients[1] = { ...recipients[1], ...{ encrypter: a256cbcHs512AnonEncrypterX25519WithA256KW( recipients[1].recipientkey.publicKey, recipients[1].kid, ), decrypter: a256cbcHs512AnonDecrypterX25519WithA256KW(recipients[1].recipientkey.secretKey), }, } }) it('Creates with only ciphertext', async () => { expect.assertions(4) const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) }) it('Creates with data in protected header', async () => { expect.assertions(4) const skid = 'did:example:sender#key-1' const jwe = await createJWE(cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected', skid, }) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512', more: 'protected', skid, }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) }) it('Creates with aad', async () => { expect.assertions(6) const aad = u8a.fromString('this data is authenticated') const jwe = await createJWE( cleartext, [recipients[0].encrypter, recipients[1].encrypter], { more: 'protected' }, aad, ) expect(u8a.fromString(jwe.aad!!, 'base64url')).toEqual(aad) expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512', more: 'protected' }) expect(await decryptJWE(jwe, recipients[0].decrypter)).toEqual(cleartext) expect(await decryptJWE(jwe, recipients[1].decrypter)).toEqual(cleartext) delete jwe.aad await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') await expect(decryptJWE(jwe, recipients[0].decrypter)).rejects.toThrowError('Failed to decrypt') }) it('Incompatible encrypters throw', async () => { expect.assertions(1) const enc1 = { enc: 'cool enc alg1' } as Encrypter const enc2 = { enc: 'cool enc alg2' } as Encrypter await expect(createJWE(cleartext, [enc1, enc2])).rejects.toThrowError('Incompatible encrypters passed') }) }) }) describe('ECDH-1PU+A256KW (X25519), Key Wrapping Mode with A256CBC-HS512 content encryption', () => { describe('One recipient', () => { let cleartext: Uint8Array, recipientKey: any, senderKey: any, decrypter: Decrypter beforeEach(() => { recipientKey = generateX25519KeyPairFromSeed(randomBytes(32)) senderKey = generateX25519KeyPairFromSeed(randomBytes(32)) cleartext = u8a.fromString('my secret message') decrypter = a256cbcHs512AuthDecrypterX25519WithA256KW(recipientKey.secretKey, senderKey.publicKey) }) it('Creates with only ciphertext', async () => { const encrypter = a256cbcHs512AuthEncrypterX25519WithA256KW(recipientKey.publicKey, senderKey.secretKey) expect.assertions(3) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid, no apu and no apv', async () => { const kid = 'did:example:receiver#key-1' const encrypter = a256cbcHs512AuthEncrypterX25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { kid, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toBeUndefined() expect(jwe.recipients!![0].header.apv).toBeUndefined() expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with no kid, with apu and apv', async () => { const apu = encodeBase64url('Alice') const apv = encodeBase64url('Bob') const encrypter = a256cbcHs512AuthEncrypterX25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { apu, apv, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(jwe.recipients!![0].header.kid).toBeUndefined() expect(jwe.recipients!![0].header.apu).toEqual(apu) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, decrypter)).toEqual(cleartext) }) it('Creates with kid and apu and apv', async () => { const kid = 'did:example:receiver#key-1' const apu = encodeBase64url('Alice') const apv = encodeBase64url('Bob') const encrypter = a256cbcHs512AuthEncrypterX25519WithA256KW( recipientKey.publicKey, senderKey.secretKey, { kid, apu, apv, }, ) expect.assertions(6) const jwe = await createJWE(cleartext, [encrypter]) expect(jwe.aad).toBeUndefined() expect(JSON.parse(decodeBase64url(jwe.protected))).toEqual({ enc: 'A256CBC-HS512' }) expect(jwe.recipients!![0].header.kid).toEqual(kid) expect(jwe.recipients!![0].header.apu).toEqual(apu) expect(jwe.recipients!![0].header.apv).toEqual(apv) expect(await decryptJWE(jwe, d