From 8296a310686e1a59b772e4b54f593aaded9a5044 Mon Sep 17 00:00:00 2001 From: Orie Steele Date: Sun, 25 Feb 2024 09:15:28 -0600 Subject: [PATCH] Add --- examples/hpke.direct.diag | 2 +- examples/hpke.direct.party-id.diag | 2 +- examples/hpke.wrap.diag | 2 +- src/cose/encrypt/hpke/common.ts | 89 ++++++++++++++++++ src/cose/encrypt/hpke/direct.ts | 104 +-------------------- src/cose/encrypt/hpke/suites.ts | 2 + src/cose/encrypt/hpke/wrap.ts | 140 +---------------------------- 7 files changed, 100 insertions(+), 241 deletions(-) create mode 100644 src/cose/encrypt/hpke/common.ts diff --git a/examples/hpke.direct.diag b/examples/hpke.direct.diag index 7a02399..41ba0cb 100644 --- a/examples/hpke.direct.diag +++ b/examples/hpke.direct.diag @@ -1 +1 @@ -16([h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -4: h'04857b9f69d979aaad64a4dda88b13451a4f50ad4cae017d1ba915922f0b6ffbc80e48a430b6052e4a9c62a5174596894064380292cbe451abd9f11b950803759f'}, h'c1b033e9c44096d7e9aab87df46478c21ff51829b3b7e16c11c176ba9ec50dabc8035a0e7b3bedbe56dd4bd5360de04e7a189e9096321660dd706e014db3c0b8d1f86b9b2d544a708505ade4ea15a9ac']) +16([h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -4: h'04c1132cc6e79a1fb92d0b26eed30c81682c755ee11f40c25ecadd04b60067c91e326be9d3e598a4ff466a503adbf6aaf5ae65c02b5373b41545fdf4d5688e0a6a'}, h'3b763f48dbe3ec5a77655a4184a50694dd5ed918d7afa4898711f0a3b64daa59dfe3285af27c410b142054ee2b487cded3fda822f1d988bdc79cc5360c538a305fdd6867567872626385c8ae4cb2f275']) diff --git a/examples/hpke.direct.party-id.diag b/examples/hpke.direct.party-id.diag index 3487362..1d1e608 100644 --- a/examples/hpke.direct.party-id.diag +++ b/examples/hpke.direct.party-id.diag @@ -1 +1 @@ -16([h'a301182334536469643a6578616d706c653a70617274792d7537536469643a6578616d706c653a70617274792d76', {4: "meriadoc.brandybuck@buckland.example", -4: h'04ff966068fc4a6721c5ca346378960d357e57f2ffb2b2a6ed3b1068c16e47c43add53cafcfd1115233612af72211bea46d74f4d14674874f3b0cd74b58dc68b48'}, h'05272d40e4a6a5c4a21d67942b591d63e43dea1e2a107bce6448a7a1db5039d88669d4d931f7bc0b2ad182ab2236fd2974a9b749e1f1fdb6ca741ddf7fadf72fe633176aad1ac736d68645958519c987']) +16([h'a301182334536469643a6578616d706c653a70617274792d7537536469643a6578616d706c653a70617274792d76', {4: "meriadoc.brandybuck@buckland.example", -4: h'04abbef2a5d77642a6537962f5fa35e5ddf55939dc941277f972d75121ad38d9ad9bcd186bf774f13cad6716bcf7aa5618ca1c8bed2bead9f6847797b226fb0b28'}, h'cfa98068f705cddf86970d94636e9470349eb81bd31732594a85cdbe72d9ed3128c09052397000d5ff5fb76674de5e02285b616da12522990f9805a00ae1e030abe3bcf79d54979b63a39e0ab7e06b92']) diff --git a/examples/hpke.wrap.diag b/examples/hpke.wrap.diag index 376fca1..422f600 100644 --- a/examples/hpke.wrap.diag +++ b/examples/hpke.wrap.diag @@ -1 +1 @@ -96([h'a10101', {5: 64(h'f2c5fa1399b626cd4ed64a252a064731')}, h'32d7bcbace2e8a55a3b3c4a67919bbbb79ffa6dd6b1654591555fd9de7ed2e0d50b65d6305d56fd298d8ed83ee0a8adf55fa38755e846a205f7669495c9d03e26377061dbb292948e863ea86bfabf48d', [[h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -4: h'046eb31da629cba4c1e2406566911edd2ef0f0499d3bde3beec6e8c65f986e29eae1748ac5146c5cd9095bd13540e97b70458c86ae9a1c9e548cb81dac512e95be'}, h'1d6aa896a1256e594b43d847b6987fc45b0c37687c8e0c0c951a42df8764a035']]]) +96([h'a10101', {5: 64(h'b58687fd100844ed0e09f52403c583da')}, h'f410bbdd5f9be2d71ed22e631dac557421191b461b5beba2bb4b3c63f257dbbe5b603fe16126cf1c97adf004beee15c9ea992a9c97cb01e9fdf806839d7deb43161aebd5ddbb88899e3fb2c263bbb483', [[h'a1011823', {4: "meriadoc.brandybuck@buckland.example", -4: h'04f9258bf3f02658ab71cc71da6c1b76953b71dbaba8fddb27aa6a061f1d9fe771d8e3942ff9a2908508e5b1ef84e670f050daeb06c9531b3e45749e230c092b8c'}, h'907610d71192aab229469a5390a66e98f654add5fb27ebe250b0041354d6f22c']]]) diff --git a/src/cose/encrypt/hpke/common.ts b/src/cose/encrypt/hpke/common.ts new file mode 100644 index 0000000..9023b44 --- /dev/null +++ b/src/cose/encrypt/hpke/common.ts @@ -0,0 +1,89 @@ +import { suites, JOSE_HPKE_ALG } from "./suites" +import subtle from '../../../crypto/subtleCryptoProvider' +import { generateKeyPair, exportJWK, calculateJwkThumbprintUri } from "jose" + + + +import { encode } from 'cbor-web'; + +export const computeHPKEAad = (protectedHeader: any, protectedRecipientHeader?: any) => { + if (protectedRecipientHeader) { + // not sure what to do when recipient protected header exists... + return encode([protectedHeader, protectedRecipientHeader]) + } + return protectedHeader +} + +export type JWK = { + kid?: string + alg?: string + kty: string + crv: string +} + +export const isKeyAlgorithmSupported = (recipient: JWK) => { + const supported_alg = Object.keys(suites) as string[] + return supported_alg.includes(`${recipient.alg}`) +} + +export const formatJWK = (jwk: any) => { + const { kid, alg, kty, crv, x, y, d } = jwk + return JSON.parse(JSON.stringify({ + kid, alg, kty, crv, x, y, d + })) +} + +export const publicFromPrivate = (privateKeyJwk: any) => { + const { kid, alg, kty, crv, x, y, ...rest } = privateKeyJwk + return { + kid, alg, kty, crv, x, y + } +} + +export const publicKeyFromJwk = async (publicKeyJwk: any) => { + const api = (await subtle()) + const publicKey = await api.importKey( + 'jwk', + publicKeyJwk, + { + name: 'ECDH', + namedCurve: publicKeyJwk.crv, + }, + true, + [], + ) + return publicKey; +} + +export const privateKeyFromJwk = async (privateKeyJwk: any) => { + const api = (await subtle()) + const privateKey = await api.importKey( + 'jwk', + privateKeyJwk, + { + name: 'ECDH', + namedCurve: privateKeyJwk.crv, + }, + true, + ['deriveBits', 'deriveKey'], + ) + return privateKey +} + +export const generate = async (alg: JOSE_HPKE_ALG) => { + if (!suites[alg]) { + throw new Error('Algorithm not supported') + } + let kp; + if (alg.includes('P256')) { + kp = await generateKeyPair('ECDH-ES+A256KW', { crv: 'P-256', extractable: true }) + } else if (alg.includes('P384')) { + kp = await generateKeyPair('ECDH-ES+A256KW', { crv: 'P-384', extractable: true }) + } else { + throw new Error('Could not generate private key for ' + alg) + } + const privateKeyJwk = await exportJWK(kp.privateKey); + privateKeyJwk.kid = await calculateJwkThumbprintUri(privateKeyJwk) + privateKeyJwk.alg = alg; + return formatJWK(privateKeyJwk) +} diff --git a/src/cose/encrypt/hpke/direct.ts b/src/cose/encrypt/hpke/direct.ts index 16ad6f3..f552be3 100644 --- a/src/cose/encrypt/hpke/direct.ts +++ b/src/cose/encrypt/hpke/direct.ts @@ -3,111 +3,11 @@ import { COSE_Encrypt0, Direct, Protected, Unprotected, UnprotectedHeader } from '../../Params' import { RequestDirectEncryption, RequestDirectDecryption } from '../types' import { Tagged, decodeFirst, encodeAsync } from "cbor-web" -import { generateKeyPair, exportJWK, calculateJwkThumbprintUri } from "jose" -export type JOSE_HPKE_ALG = `HPKE-Base-P256-SHA256-AES128GCM` | `HPKE-Base-P384-SHA256-AES128GCM` -import subtle from '../../../crypto/subtleCryptoProvider' import { computeInfo } from './computeInfo' -import { suites } from './suites' -import { encode } from 'cbor-web'; -export type JWK = { - kid?: string - alg?: string - kty: string - crv: string -} - -export type JWKS = { - keys: JWK[] -} - -export type HPKERecipient = { - encrypted_key: string - header: { - kid?: string - alg?: string - epk?: JWK - encapsulated_key: string, - } -} - - -export const isKeyAlgorithmSupported = (recipient: JWK) => { - const supported_alg = Object.keys(suites) as string[] - return supported_alg.includes(`${recipient.alg}`) -} - -export const formatJWK = (jwk: any) => { - const { kid, alg, kty, crv, x, y, d } = jwk - return JSON.parse(JSON.stringify({ - kid, alg, kty, crv, x, y, d - })) -} - -export const publicFromPrivate = (privateKeyJwk: any) => { - const { kid, alg, kty, crv, x, y, ...rest } = privateKeyJwk - return { - kid, alg, kty, crv, x, y - } -} - -export const publicKeyFromJwk = async (publicKeyJwk: any) => { - const api = (await subtle()) - const publicKey = await api.importKey( - 'jwk', - publicKeyJwk, - { - name: 'ECDH', - namedCurve: publicKeyJwk.crv, - }, - true, - [], - ) - return publicKey; -} +import { suites, JOSE_HPKE_ALG } from './suites' -export const privateKeyFromJwk = async (privateKeyJwk: any) => { - const api = (await subtle()) - const privateKey = await api.importKey( - 'jwk', - privateKeyJwk, - { - name: 'ECDH', - namedCurve: privateKeyJwk.crv, - }, - true, - ['deriveBits', 'deriveKey'], - ) - return privateKey -} - -export const generate = async (alg: JOSE_HPKE_ALG) => { - if (!suites[alg]) { - throw new Error('Algorithm not supported') - } - let kp; - if (alg.includes('P256')) { - kp = await generateKeyPair('ECDH-ES+A256KW', { crv: 'P-256', extractable: true }) - } else if (alg.includes('P384')) { - kp = await generateKeyPair('ECDH-ES+A256KW', { crv: 'P-384', extractable: true }) - } else { - throw new Error('Could not generate private key for ' + alg) - } - const privateKeyJwk = await exportJWK(kp.privateKey); - privateKeyJwk.kid = await calculateJwkThumbprintUri(privateKeyJwk) - privateKeyJwk.alg = alg; - return formatJWK(privateKeyJwk) -} - - - -const computeHPKEAad = (protectedHeader: any, protectedRecipientHeader?: any) => { - if (protectedRecipientHeader) { - // not sure what to do when recipient protected header exists... - return encode([protectedHeader, protectedRecipientHeader]) - } - return protectedHeader -} +import { publicKeyFromJwk, privateKeyFromJwk, computeHPKEAad } from './common' export const encryptDirect = async (req: RequestDirectEncryption) => { if (req.unprotectedHeader === undefined) { diff --git a/src/cose/encrypt/hpke/suites.ts b/src/cose/encrypt/hpke/suites.ts index 5214869..104849d 100644 --- a/src/cose/encrypt/hpke/suites.ts +++ b/src/cose/encrypt/hpke/suites.ts @@ -1,6 +1,8 @@ import { AeadId, CipherSuite, KdfId, KemId } from "hpke-js"; +export type JOSE_HPKE_ALG = `HPKE-Base-P256-SHA256-AES128GCM` | `HPKE-Base-P384-SHA256-AES128GCM` + export const primaryAlgorithm = { 'label': `HPKE-Base-P256-SHA256-AES128GCM`, 'value': 35 diff --git a/src/cose/encrypt/hpke/wrap.ts b/src/cose/encrypt/hpke/wrap.ts index 7422263..181a3af 100644 --- a/src/cose/encrypt/hpke/wrap.ts +++ b/src/cose/encrypt/hpke/wrap.ts @@ -1,147 +1,15 @@ import { createAAD } from '../utils' - -import { COSE_Encrypt, COSE_Encrypt0, Direct, Protected, Unprotected, UnprotectedHeader } from '../../Params' - -import { RequestWrapDecryption, RequestWrapEncryption, RequestDirectEncryption, RequestDirectDecryption } from '../types' +import { COSE_Encrypt, Protected, Unprotected, UnprotectedHeader } from '../../Params' +import { RequestWrapDecryption, RequestWrapEncryption, } from '../types' import { EMPTY_BUFFER } from "../../../cbor" - import { Tagged, decodeFirst, encodeAsync } from "cbor-web" - -import { generateKeyPair, exportJWK, calculateJwkThumbprintUri } from "jose" - -import { AeadId, CipherSuite, KdfId, KemId } from "hpke-js"; - import { computeInfo } from './computeInfo' - -export type JOSE_HPKE_ALG = `HPKE-Base-P256-SHA256-AES128GCM` | `HPKE-Base-P384-SHA256-AES128GCM` -import subtle from '../../../crypto/subtleCryptoProvider' - import * as aes from '../aes' import { encode } from 'cbor-web'; - import { toArrayBuffer } from '../../../cbor' - -export type JWK = { - kid?: string - alg?: string - kty: string - crv: string -} - -export type JWKS = { - keys: JWK[] -} - -export type HPKERecipient = { - encrypted_key: string - header: { - kid?: string - alg?: string - epk?: JWK - encapsulated_key: string, - } -} - -export const suites = { - ['HPKE-Base-P256-SHA256-AES128GCM']: new CipherSuite({ - kem: KemId.DhkemP256HkdfSha256, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }), - ['HPKE-Base-P384-SHA256-AES128GCM']: new CipherSuite({ - kem: KemId.DhkemP384HkdfSha384, - kdf: KdfId.HkdfSha256, - aead: AeadId.Aes128Gcm, - }) -} - -export const isKeyAlgorithmSupported = (recipient: JWK) => { - const supported_alg = Object.keys(suites) as string[] - return supported_alg.includes(`${recipient.alg}`) -} - -export const formatJWK = (jwk: any) => { - const { kid, alg, kty, crv, x, y, d } = jwk - return JSON.parse(JSON.stringify({ - kid, alg, kty, crv, x, y, d - })) -} - -export const publicFromPrivate = (privateKeyJwk: any) => { - const { kid, alg, kty, crv, x, y, ...rest } = privateKeyJwk - return { - kid, alg, kty, crv, x, y - } -} - -export const publicKeyFromJwk = async (publicKeyJwk: any) => { - const api = (await subtle()) - const publicKey = await api.importKey( - 'jwk', - publicKeyJwk, - { - name: 'ECDH', - namedCurve: publicKeyJwk.crv, - }, - true, - [], - ) - return publicKey; -} - -export const privateKeyFromJwk = async (privateKeyJwk: any) => { - const api = (await subtle()) - const privateKey = await api.importKey( - 'jwk', - privateKeyJwk, - { - name: 'ECDH', - namedCurve: privateKeyJwk.crv, - }, - true, - ['deriveBits', 'deriveKey'], - ) - return privateKey -} - -export const generate = async (alg: JOSE_HPKE_ALG) => { - if (!suites[alg]) { - throw new Error('Algorithm not supported') - } - let kp; - if (alg.includes('P256')) { - kp = await generateKeyPair('ECDH-ES+A256KW', { crv: 'P-256', extractable: true }) - } else if (alg.includes('P384')) { - kp = await generateKeyPair('ECDH-ES+A256KW', { crv: 'P-384', extractable: true }) - } else { - throw new Error('Could not generate private key for ' + alg) - } - const privateKeyJwk = await exportJWK(kp.privateKey); - privateKeyJwk.kid = await calculateJwkThumbprintUri(privateKeyJwk) - privateKeyJwk.alg = alg; - return formatJWK(privateKeyJwk) -} - -export const primaryAlgorithm = { - 'label': `HPKE-Base-P256-SHA256-AES128GCM`, - 'value': 35 -} - -export const secondaryAlgorithm = { - 'label': `HPKE-Base-P384-SHA384-AES256GCM`, - 'value': 37 -} - - - -const computeHPKEAad = (protectedHeader: any, protectedRecipientHeader?: any) => { - if (protectedRecipientHeader) { - // not sure what to do when recipient protected header exists... - return encode([protectedHeader, protectedRecipientHeader]) - } - return protectedHeader -} +import { suites, JOSE_HPKE_ALG } from './suites' +import { publicKeyFromJwk, computeHPKEAad, privateKeyFromJwk } from './common' export const encryptWrap = async (req: RequestWrapEncryption) => { if (req.unprotectedHeader === undefined) {