Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
OR13 committed Mar 2, 2024
1 parent 277bce1 commit e33a203
Show file tree
Hide file tree
Showing 14 changed files with 1,125 additions and 4 deletions.
15 changes: 11 additions & 4 deletions src/cose/key/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IANACOSEAlgorithms } from "../algorithms"

import { CoseKey } from '.'
export type CoseKeyAgreementAlgorithms = 'ECDH-ES+A128KW'
export type CoseDirectEncryptionAlgorithms = 'HPKE-Base-P256-SHA256-AES128GCM'
export type CoseSignatureAlgorithms = 'ES256' | 'ES384' | 'ES512'
export type ContentTypeOfJsonWebKey = 'application/jwk+json'
export type ContentTypeOfCoseKey = 'application/cose-key'
Expand All @@ -19,16 +20,22 @@ import { thumbprint } from "./thumbprint"
import { formatJwk } from './formatJwk'


export const generate = async <T>(alg: CoseSignatureAlgorithms, contentType: PrivateKeyContentType = 'application/jwk+json'): Promise<T> => {
const knownAlgorithm = Object.values(IANACOSEAlgorithms).find((
export const generate = async <T>(alg: CoseSignatureAlgorithms | CoseDirectEncryptionAlgorithms, contentType: PrivateKeyContentType = 'application/jwk+json'): Promise<T> => {
let knownAlgorithm = Object.values(IANACOSEAlgorithms).find((
entry
) => {
return entry.Name === alg
})
}) as any
if (alg === 'HPKE-Base-P256-SHA256-AES128GCM') {
knownAlgorithm = {
Name: 'ECDH-ES+A128KW',
Curve: 'P-256'
}
}
if (!knownAlgorithm) {
throw new Error('Algorithm is not supported.')
}
const cryptoKeyPair = await generateKeyPair(knownAlgorithm.Name, { extractable: true });
const cryptoKeyPair = await generateKeyPair(knownAlgorithm.Name, { extractable: true, crv: knownAlgorithm.Curve });
const secretKeyJwk = await exportJWK(cryptoKeyPair.privateKey)
const jwkThumbprint = await calculateJwkThumbprint(secretKeyJwk)
secretKeyJwk.kid = jwkThumbprint
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

import * as jose from '../jose-hpke'

import * as cose from '../../src'

it('generate private keys', async () => {
const k1 = await jose.key.generate('HPKE-Base-P256-SHA256-AES128GCM')
const k2 = await cose.key.generate('HPKE-Base-P256-SHA256-AES128GCM', 'application/cose-key')
})
1 change: 1 addition & 0 deletions test/jose-hpke/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src'
79 changes: 79 additions & 0 deletions test/jose-hpke/src/IntegratedEncryption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { base64url } from "jose";

import { publicKeyFromJwk, suites, isKeyAlgorithmSupported, privateKeyFromJwk, JOSE_HPKE_ALG } from "./keys";

export const encrypt = async (plaintext: Uint8Array, publicKeyJwk: any, options = { serialization: 'CompactSerialization'}): Promise<string | any> => {
if (!isKeyAlgorithmSupported(publicKeyJwk)) {
throw new Error('Public key algorithm is not supported')
}
const suite = suites[publicKeyJwk.alg as JOSE_HPKE_ALG]
const sender = await suite.createSenderContext({
recipientPublicKey: await publicKeyFromJwk(publicKeyJwk),
});

const encapsulatedKey = base64url.encode(new Uint8Array(sender.enc))
const protectedHeader = base64url.encode(JSON.stringify({
alg: "dir",
enc: publicKeyJwk.alg,
"epk": {
"kty": "EK",
"ek": encapsulatedKey
}
}))
const hpkeSealAad = new TextEncoder().encode(protectedHeader)
const ciphertext = base64url.encode(new Uint8Array(await sender.seal(plaintext, hpkeSealAad)));
// https://datatracker.ietf.org/doc/html/rfc7516#section-3.1
const compact = `${protectedHeader}...${ciphertext}.`
if (options.serialization === 'CompactSerialization'){
return compact
}
if (options.serialization === 'GeneralJson'){
const [protectedHeader, encryptedKey, initializationVector, ciphertext, tag] = compact.split('.')
return JSON.parse(JSON.stringify({
protected: protectedHeader,
encrypted_key: encryptedKey.length === 0 ? undefined: encryptedKey,
iv: initializationVector.length === 0 ? undefined: initializationVector,
tag: tag.length === 0 ? undefined: tag,
ciphertext: ciphertext.length === 0 ? undefined: ciphertext
}))
}
throw new Error('Unsupported Serialization.')
}

export const decrypt = async (jwe: string | any, privateKeyJwk: any, options = { serialization: 'CompactSerialization'}): Promise<Uint8Array> => {
if (typeof jwe === 'object' && options.serialization !== 'GeneralJson'){
throw new Error('expected object for general json serialization decrypt.')
}
let compact = ''
if (options.serialization === 'CompactSerialization'){
if (typeof jwe !== 'string'){
throw new Error('expected string for compact serialization decrypt.')
}
compact = jwe
}
if (options.serialization === 'GeneralJson'){
if (typeof jwe !== 'object'){
throw new Error('expected object for general json serialization decrypt.')
}
compact = `${jwe.protected}...${jwe.ciphertext}.`
}
if (!isKeyAlgorithmSupported(privateKeyJwk)) {
throw new Error('Public key algorithm is not supported')
}
const suite = suites[privateKeyJwk.alg as JOSE_HPKE_ALG]
const [protectedHeader, _blankEncKey, _blankIv, ciphertext, _blankTag] = compact.split('.');
const decodedProtectedHeader = JSON.parse(new TextDecoder().decode(base64url.decode(protectedHeader)))
if (decodedProtectedHeader.alg !== 'dir'){
throw new Error('Expected alg:dir for integrated encryption.')
}
if (decodedProtectedHeader.enc !== privateKeyJwk.alg){
throw new Error('Private key does not support this algorithm: ' + decodedProtectedHeader.enc)
}
const recipient = await suite.createRecipientContext({
recipientKey: await privateKeyFromJwk(privateKeyJwk),
enc: base64url.decode(decodedProtectedHeader.epk.ek)
})
const hpkeOpenAad = new TextEncoder().encode(protectedHeader)
const plaintext = await recipient.open(base64url.decode(ciphertext), hpkeOpenAad)
return new Uint8Array(plaintext)
}
Loading

0 comments on commit e33a203

Please sign in to comment.