From 93a4bef0ffcb16a48dc843a1df3a35a38dd3c04f Mon Sep 17 00:00:00 2001 From: Orie Steele Date: Fri, 23 Feb 2024 14:40:05 -0600 Subject: [PATCH] add direct tag 16 --- examples/hpke.direct.diag | 2 +- examples/hpke.wrap.diag | 2 +- src/cose/encrypt/direct.ts | 10 +++---- src/cose/encrypt/hpke.ts | 55 +++++++++++++++----------------------- src/cose/encrypt/wrap.ts | 6 ++--- test/hpke.test.ts | 2 +- 6 files changed, 31 insertions(+), 46 deletions(-) diff --git a/examples/hpke.direct.diag b/examples/hpke.direct.diag index 029caa1..2539807 100644 --- a/examples/hpke.direct.diag +++ b/examples/hpke.direct.diag @@ -1 +1 @@ -96([h'a1011823', {}, h'cb3fbf75dd90e2848e7cde4451a82f0233dc00a96394c4232c2bc79aacb6b648d2bf9ba72db83c232b5d8832601af2d865317a49b3feb385288b5d28788a3f52200fc012ae8c99c42dcd5fdbdda825dd', [[h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -1: {1: 5, -1: h'048f98972f38af086e2b8cdb40ffd2c0b22ef5d600f263f1541f09907e97dbe0af06aaf0dcdcd47ce7d1913c5dad4d689b89cbfd28ab45d446c662301483676bf9'}}, h'']]]) +16([h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -1: {1: 5, -1: h'047e28c472862e148b3a039640f3a2461855ab43b955b2232575e2c2d2e5998369c38ea1be27f483c2d2fc860345298bf3316737b8771c8aa4783b366914199ab0'}}, h'6fde098224bc4f8661fb7c268dbce2269612a092490e94cb9b0de698ed015e2594e148b5ec418a7c397866c66f26af4ddf8d5c602899fe5d494833390e47075a6b7243e0594e930947c906f365626506']) diff --git a/examples/hpke.wrap.diag b/examples/hpke.wrap.diag index 95ba73b..910c4e8 100644 --- a/examples/hpke.wrap.diag +++ b/examples/hpke.wrap.diag @@ -1 +1 @@ -96([h'a10101', {5: 64(h'41469c98fcbd872dd11a01df13b33743')}, h'55181b9b3e619ed041fbd863ecf62517f1eabafaa3b61f7d204b1b7f5cda6ddd36726df0219c1842351bcc65219a5c2222d0e8163ab478cf61b3916bb3dc0e805fd6aa297bd84b394c4792f9421b0391', [[h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -1: {1: 5, -1: h'04b5f1c2cb90d20ec23ed8adafd0f548ceccc72a1ac3b189bc646eb02ed5621fe3216c88e777d201b24103b65a5dfce8ca5afefffada562a9b776361cf843a8300'}}, h'b4622b2ef308e90fea8c70858089df4ebf95dc6091ac2e45122cb93b5f7b5e26']]]) +96([h'a10101', {5: 64(h'd87da66c769df98f417188913d393f63')}, h'71b7f5825726e08580d95d5b3123af4cb3013bf9835302486fcbf306010af64cb085ce936746a97fadf0ca6e7b59347c03e9404a232603ce7911a5eb31c3070062938e90488fa7b631b4f4a0be619aaf', [[h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -1: {1: 5, -1: h'0484a62d158fadb34eaf812aa7a246dfb1c2aaac313b3dc6d6388fd3729f61dd1b87a9b51a1d12dc281fa8ebfa34d219088e46a6038363717fd94aaa1aeb796ce7'}}, h'755902f15658d8ebbbf47667a5e93f88903188dda2e6f04b58f5f13c466d0070']]]) diff --git a/src/cose/encrypt/direct.ts b/src/cose/encrypt/direct.ts index 0bdb7a4..0346781 100644 --- a/src/cose/encrypt/direct.ts +++ b/src/cose/encrypt/direct.ts @@ -61,17 +61,15 @@ export const encrypt = async (req: RequestDirectEncryption) => { return encodeAsync(new Tagged(COSE_Encrypt_Tag, COSE_Encrypt), { canonical: true }) } - - export const decrypt = async (req: RequestDirectDecryption) => { - const decoded = await decodeFirst(req.ciphertext) - if (decoded.tag !== 96) { - throw new Error('Only tag 96 cose encrypt are supported') - } const receiverPrivateKeyJwk = req.recipients.keys[0] if (receiverPrivateKeyJwk.alg === hpke.primaryAlgorithm.label) { return hpke.decrypt.direct(req) } + const decoded = await decodeFirst(req.ciphertext) + if (decoded.tag !== 96) { + throw new Error('Only tag 96 cose encrypt are supported') + } const [protectedHeader, unprotectedHeader, ciphertext, recipients] = decoded.value if (recipients.length !== 1) { throw new Error('Expected a single recipient for direct decryption') diff --git a/src/cose/encrypt/hpke.ts b/src/cose/encrypt/hpke.ts index bcdfe86..e530e11 100644 --- a/src/cose/encrypt/hpke.ts +++ b/src/cose/encrypt/hpke.ts @@ -127,16 +127,12 @@ export const secondaryAlgorithm = { 'value': 37 } -export const directAlgorithm = { - 'label': `HPKE-Direct`, - 'value': 36 -} - -const computeHPKEAad = (protectedHeader: any, protectedRecipientHeader: any, direct = false) => { - if (direct) { - return protectedHeader +const computeHPKEAad = (protectedHeader: any, protectedRecipientHeader?: any) => { + if (protectedRecipientHeader) { + // not sure what to do when recipient protected header exists... + return encode([protectedHeader, protectedRecipientHeader]) } - return encode([protectedHeader, protectedRecipientHeader]) + return protectedHeader } const encryptWrap = async (req: RequestWrapEncryption) => { @@ -193,6 +189,9 @@ const encryptWrap = async (req: RequestWrapEncryption) => { } export const encryptDirect = async (req: RequestDirectEncryption) => { + if (req.protectedHeader.get(1) !== 35) { + throw new Error('Only alg 35 is supported') + } const protectedHeader = await encodeAsync(req.protectedHeader) const unprotectedHeader = req.unprotectedHeader; const [recipientPublicKeyJwk] = req.recipients.keys @@ -200,36 +199,27 @@ export const encryptDirect = async (req: RequestDirectEncryption) => { const sender = await suite.createSenderContext({ recipientPublicKey: await publicKeyFromJwk(recipientPublicKeyJwk), }); - const recipientProtectedHeader = await encodeAsync(new Map([ - [1, primaryAlgorithm.value], - ])) - const hpkeSealAad = computeHPKEAad(protectedHeader, recipientProtectedHeader, true) + const hpkeSealAad = computeHPKEAad(protectedHeader) const ciphertext = await sender.seal(req.plaintext, hpkeSealAad) const recipientCoseKey = new Map([ [1, 5], // kty: EK [- 1, sender.enc] ]) - const recipientUnprotectedHeader = new Map([ - [4, recipientPublicKeyJwk.kid], // kid - [-1, recipientCoseKey], // epk - ]) - const recipients = [[recipientProtectedHeader, recipientUnprotectedHeader, EMPTY_BUFFER]] - const COSE_Encrypt = [ + unprotectedHeader.set(4, recipientPublicKeyJwk.kid) + unprotectedHeader.set(-1, recipientCoseKey) + const COSE_Encrypt0 = [ protectedHeader, unprotectedHeader, ciphertext, - recipients ] - return encodeAsync(new Tagged(COSE_Encrypt_Tag, COSE_Encrypt), { canonical: true }) + return encodeAsync(new Tagged(16, COSE_Encrypt0), { canonical: true }) } - export const encrypt = { direct: encryptDirect, wrap: encryptWrap } - export const decryptWrap = async (req: RequestWrapDecryption) => { const decoded = await decodeFirst(req.ciphertext) if (decoded.tag !== 96) { @@ -264,20 +254,17 @@ export const decryptWrap = async (req: RequestWrapDecryption) => { export const decryptDirect = async (req: RequestDirectDecryption) => { const decoded = await decodeFirst(req.ciphertext) - if (decoded.tag !== 96) { - throw new Error('Only tag 96 cose encrypt are supported') + if (decoded.tag !== 16) { + throw new Error('Only tag 16 cose encrypt are supported') } - const [protectedHeader, unprotectedHeader, ciphertext, recipients] = decoded.value - const [recipient] = recipients - - const [recipientProtectedHeader, recipientUnprotectedHeader, recipientCipherText] = recipient - const kid = recipientUnprotectedHeader.get(4).toString(); + const [protectedHeader, unprotectedHeader, ciphertext] = decoded.value + const kid = unprotectedHeader.get(4).toString(); const receiverPrivateKeyJwk = req.recipients.keys.find((k) => { return k.kid === kid }) - const decodedRecipientProtectedHeader = await decodeFirst(recipientProtectedHeader) - const recipientAlgorithm = decodedRecipientProtectedHeader.get(1) - const epk = recipientUnprotectedHeader.get(-1) + + const recipientAlgorithm = unprotectedHeader.get(1) + const epk = unprotectedHeader.get(-1) // ensure the epk has the algorithm that is set in the protected header epk.set(3, recipientAlgorithm) // EPK is allowed to have an alg const suite = suites[receiverPrivateKeyJwk.alg as JOSE_HPKE_ALG] @@ -285,7 +272,7 @@ export const decryptDirect = async (req: RequestDirectDecryption) => { recipientKey: await privateKeyFromJwk(receiverPrivateKeyJwk), enc: epk.get(-1) // ek }) - const hpkeSealAad = computeHPKEAad(protectedHeader, recipientProtectedHeader, true) + const hpkeSealAad = computeHPKEAad(protectedHeader) const plaintext = await hpkeRecipient.open(ciphertext, hpkeSealAad) return plaintext } diff --git a/src/cose/encrypt/wrap.ts b/src/cose/encrypt/wrap.ts index a1500b3..d537eaa 100644 --- a/src/cose/encrypt/wrap.ts +++ b/src/cose/encrypt/wrap.ts @@ -13,9 +13,6 @@ import * as hpke from './hpke' export const decrypt = async (req: RequestWrapDecryption) => { const decoded = await decodeFirst(req.ciphertext) - if (decoded.tag !== 96) { - throw new Error('Only tag 96 cose encrypt are supported') - } const [protectedHeader, unprotectedHeader, ciphertext, recipients] = decoded.value const [recipient] = recipients const [recipientProtectedHeader, recipientUnprotectedHeader, recipientCipherText] = recipient @@ -26,6 +23,9 @@ export const decrypt = async (req: RequestWrapDecryption) => { if (receiverPrivateKeyJwk.alg === 'HPKE-Base-P256-SHA256-AES128GCM') { return hpke.decrypt.wrap(req) } + if (decoded.tag !== 96) { + throw new Error('Only tag 96 cose encrypt are supported') + } const decodedRecipientProtectedHeader = await decodeFirst(recipientProtectedHeader) const recipientAlgorithm = decodedRecipientProtectedHeader.get(1) const epk = recipientUnprotectedHeader.get(-1) diff --git a/test/hpke.test.ts b/test/hpke.test.ts index 9ce86a3..ea7b71a 100644 --- a/test/hpke.test.ts +++ b/test/hpke.test.ts @@ -70,7 +70,7 @@ it('direct', async () => { } }) const decoded = transmute.cbor.decodeFirstSync(ct); - expect(decoded.tag).toBe(96) + expect(decoded.tag).toBe(16) const decrypted = await transmute.decrypt.direct({ ciphertext: ct, recipients: {