Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
  • Loading branch information
OR13 committed Jun 22, 2024
1 parent 7ebf7ff commit 3ae2b75
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 11 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@

🚧 Experimental 🔥

This is yet another HPKE implementation for JOSE and COSE.
This is yet another HPKE implementation for JOSE.

The purpose of this experimental implementation is to address open questions regarding the IETF drafts.

- [x] Integrated Encryption - how is it signaled in JOSE and COSE, what values are used for "alg" and "enc".
- [x] Integrated Encryption - how is it signaled in JOSE, what values are used for "alg" and "enc".
- [ ] Party U / Party V - identity information in key establishment.
- [ ] Cross Mode Attacks - How are they mitigated in COSE and JOSE?


17 changes: 14 additions & 3 deletions src/jose-hpke/jwe/compact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { base64url } from "jose";
import { privateKeyFromJwk, publicKeyFromJwk } from "../../crypto/keys";
import { isKeyAlgorithmSupported, suites } from "../jwk";

export const encrypt = async (plaintext: Uint8Array, publicKeyJwk: any): Promise<string> => {

import { HPKE_JWT_OPTIONS } from '../types'

export const encrypt = async (plaintext: Uint8Array, publicKeyJwk: any, options?: HPKE_JWT_OPTIONS): Promise<string> => {
if (!isKeyAlgorithmSupported(publicKeyJwk)) {
throw new Error('Public key algorithm is not supported')
}
Expand All @@ -12,11 +15,19 @@ export const encrypt = async (plaintext: Uint8Array, publicKeyJwk: any): Promise
recipientPublicKey: await publicKeyFromJwk(publicKeyJwk),
});
const encodedEncapsulatedKey = base64url.encode(new Uint8Array(sender.enc))
const protectedHeader = base64url.encode(JSON.stringify({
const headerParams = {
alg: publicKeyJwk.alg,
enc: publicKeyJwk.alg.split('-').pop() // HPKE algorithms always end in an AEAD.
}))
} as Record<string, any>
if (options?.keyManagementParameters.apu){
headerParams.apu = base64url.encode(options?.keyManagementParameters.apu)
}
if (options?.keyManagementParameters.apv){
headerParams.apv = base64url.encode(options?.keyManagementParameters.apv)
}
const protectedHeader = base64url.encode(JSON.stringify(headerParams))
const aad = new TextEncoder().encode(protectedHeader)
// apu / apv are protected by aad, not as part of kdf
const ciphertext = base64url.encode(new Uint8Array(await sender.seal(plaintext, aad)));
const encrypted_key = encodedEncapsulatedKey
const iv = ``
Expand Down
10 changes: 5 additions & 5 deletions src/jose-hpke/jwt/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@

import * as jwe from '../jwe'

import { base64url } from 'jose'

import { HPKE_JWT_OPTIONS } from '../types'

import * as jwe from '../jwe'

const encoder = new TextEncoder()
const decoder = new TextDecoder()

export const encryptJWT = async (claims: Record<string, any>, publicKey: Record<string, any>) => {
export const encryptJWT = async (claims: Record<string, any>, publicKey: Record<string, any>, options?: HPKE_JWT_OPTIONS) => {
const plaintext = encoder.encode(JSON.stringify(claims))
return jwe.compact.encrypt(plaintext, publicKey)
return jwe.compact.encrypt(plaintext, publicKey, options)
}

export const decryptJWT = async (jwt: string, privateKey: Record<string, any>) => {
Expand Down
6 changes: 6 additions & 0 deletions src/jose-hpke/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type HPKE_JWT_OPTIONS = {
keyManagementParameters: {
apu: Uint8Array,
apv: Uint8Array
}
}
32 changes: 32 additions & 0 deletions tests/jwt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,36 @@ it('Encrypted JWT with ECDH-ES+A128KW and A128GCM, and party info', async () =>
expect(result.protectedHeader.epk).toBeDefined()
expect(result.protectedHeader.apu).toBe("QWxpY2U")
expect(result.protectedHeader.apv).toBe("Qm9i")
})

it('Encrypted JWT with HPKE-Base-P256-SHA256-A128GCM, and party info ', async () => {
const privateKey = await hpke.jwk.generate('HPKE-Base-P256-SHA256-A128GCM')
const publicKey = await hpke.jwk.publicFromPrivate(privateKey)
const iat = moment().unix()
const exp = moment().add(2, 'hours').unix()
const jwe = await hpke.jwt.encryptJWT({
...claims,
iss: 'urn:example:issuer',
aud: 'urn:example:audience',
iat,
exp,
},
publicKey,
{
keyManagementParameters: {
"apu": jose.base64url.decode("QWxpY2U"),
"apv": jose.base64url.decode("Qm9i"),
}
})
// protected.encapsulated_key.<no iv>.ciphertext.<no tag>
const result = await hpke.jwt.decryptJWT(jwe, privateKey)
expect(result.payload['urn:example:claim']).toBe(true)
expect(result.payload['iss']).toBe('urn:example:issuer')
expect(result.payload['aud']).toBe('urn:example:audience')
expect(result.payload.iat).toBeDefined()
expect(result.payload.exp).toBeDefined()
expect(result.protectedHeader['alg']).toBe('HPKE-Base-P256-SHA256-A128GCM')
expect(result.protectedHeader['enc']).toBe('A128GCM')
expect(result.protectedHeader.apu).toBe("QWxpY2U")
expect(result.protectedHeader.apv).toBe("Qm9i")
})

0 comments on commit 3ae2b75

Please sign in to comment.