Skip to content

Commit

Permalink
less wrong, but still wrong HPKE suite_id
Browse files Browse the repository at this point in the history
  • Loading branch information
OR13 committed May 26, 2024
1 parent db11faa commit 1339e06
Showing 1 changed file with 77 additions and 20 deletions.
97 changes: 77 additions & 20 deletions src/cose/encrypt/hpke/direct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,25 @@ import { EMPTY_BUFFER, toArrayBuffer } from '../../../cbor';

import { createAAD } from '../utils';

// from hpke-js
/**
* Converts integer to octet string. I2OSP implementation.
*/
export function i2Osp(n: number, w: number): Uint8Array {
if (w <= 0) {
throw new Error("i2Osp: too small size");
}
if (n >= 256 ** w) {
throw new Error("i2Osp: too large integer");
}
const ret = new Uint8Array(w);
for (let i = 0; i < w && n; i++) {
ret[w - (i + 1)] = n % 256;
n = n >> 8;
}
return ret;
}

const dhkemsuite = new CipherSuite({
kem: KemId.DhkemP256HkdfSha256,
kdf: KdfId.HkdfSha256,
Expand Down Expand Up @@ -58,29 +77,67 @@ const handleDHKemEncrypt = async (req: RequestDirectEncryption) => {
}


const sharedSecretToContentEncryptionKey = async (sharedSecret: Uint8Array) => {
const ikm = sharedSecret;
// https://datatracker.ietf.org/doc/html/rfc9180#section-4-10
// labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
// 🔥 this is ALL WRONG.... 🔥
// need to follow https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-hpke-mlkem-00#name-encap-and-decap
// 🔥 this is ALL WRONG.... 🔥
const suite_id = Buffer.from('0xFFFF', 'hex') // unassigned kem id https://www.iana.org/assignments/hpke/hpke.xhtml
// should be:
// suite_id = concat(
// "HPKE",
// I2OSP(kem_id, 2),
// I2OSP(kdf_id, 2),
// I2OSP(aead_id, 2)
// )

const Expand = async (prk: Uint8Array, info: Uint8Array, length: number) => {
// 🔥 possibly incorrect.
return dhkemsuite.kdf.expand(prk, info, length)
}

const Extract = async (salt: Uint8Array, ikm: Uint8Array) => {
// 🔥 possibly incorrect.
return dhkemsuite.kdf.extract(salt, ikm)
}

const suite_id = Buffer.concat([
Buffer.from('HPKE'),
Buffer.from(i2Osp(0xFFFF, 2)), // 🔥 Not a real kem id 🔥
Buffer.from(i2Osp(0x0001, 2)), // HKDF-SHA256, 32
Buffer.from(i2Osp(0x0001, 2)) // AES-128-GCM
])

// def LabeledExtract(salt, label, ikm):
// labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
// return Extract(salt, labeled_ikm)
const LabeledExtract = async (salt: Uint8Array, label: Uint8Array, ikm: Uint8Array) => {
const labeled_ikm = Buffer.concat([
new TextEncoder().encode('HPKE-v1'),
suite_id,
Buffer.from(''),
ikm
])
const salt = new TextEncoder().encode('') // empty string?
return dhkemsuite.kdf.extract(salt, labeled_ikm)
return Extract(salt, labeled_ikm)
}

// def LabeledExpand(prk, label, info, L):
// labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
// label, info)
// return Expand(prk, labeled_info, L)
const LabeledExpand = async (prk: Uint8Array, label: Uint8Array, info: Uint8Array, length: number) => {
const labeled_info = Buffer.concat([
Buffer.from(i2Osp(length, 2)),
Buffer.from("HPKE-v1"),
suite_id,
label,
info
])
return Expand(prk, labeled_info, 32)
}

// def ExtractAndExpand(dh, kem_context):
// eae_prk = LabeledExtract("", "eae_prk", dh)
// shared_secret = LabeledExpand(eae_prk, "shared_secret",
// kem_context, Nsecret)
// return shared_secret
const ExtractAndExpand = async (ss: Uint8Array, ct: Uint8Array) => {
const eae_prk = await LabeledExtract(new TextEncoder().encode(''), new TextEncoder().encode('eae_prk'), ss)
const shared_secret = LabeledExpand(new Uint8Array(eae_prk), new TextEncoder().encode('shared_secret'), ct, 32)
return shared_secret
}

// 🔥 This is wrong.
// need to follow https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-hpke-mlkem-00#name-encap-and-decap
const sharedSecretToContentEncryptionKey = async (ss: Uint8Array, ct: Uint8Array) => {
return ExtractAndExpand(ss, ct)
}

const handleMLKemEncrypt = async (req: RequestDirectEncryption) => {
Expand All @@ -90,7 +147,7 @@ const handleMLKemEncrypt = async (req: RequestDirectEncryption) => {
const publicKey = base64url.decode(recipientPublicKeyJwk.x)
const { cipherText, sharedSecret } = ml_kem768.encapsulate(publicKey);
const kemCt = cipherText;
const aeadContentEncryptionKey = await sharedSecretToContentEncryptionKey(sharedSecret)
const aeadContentEncryptionKey = await sharedSecretToContentEncryptionKey(sharedSecret, cipherText)
const aeadAlg = 1; // AES 128 GCM
const iv = await aes.getIv(aeadAlg) // random for each direct encryption
const externalAad = EMPTY_BUFFER
Expand Down Expand Up @@ -166,12 +223,12 @@ const handleMLKemDecrypt = async (req: RequestDirectDecryption) => {
const receiverPrivateKeyJwk = req.recipients.keys.find((k) => {
return k.kid === kid
})
const ek = unprotectedHeader.get(Unprotected.Ek)
const ek = unprotectedHeader.get(Unprotected.Ek) // kem-ct
const iv = ctWithIv.slice(0, 16) // AES-128-GCM iv length
const encryptedContent = ctWithIv.slice(16, ctWithIv.length)
const secretKey = base64url.decode(receiverPrivateKeyJwk.d)
const sharedSecret = ml_kem768.decapsulate(ek, secretKey);
const aeadContentEncryptionKey = await sharedSecretToContentEncryptionKey(sharedSecret)
const aeadContentEncryptionKey = await sharedSecretToContentEncryptionKey(sharedSecret, ek)
const aeadAlg = 1; // AES 128 GCM
const externalAad = EMPTY_BUFFER
// const hpkeSealAad = computeHPKEAad(protectedHeader) // confused why I don't need this...
Expand Down

0 comments on commit 1339e06

Please sign in to comment.