import type { ECDH, EphemeralKeyPair, Recipient } from './types.js' import { base64ToBytes, bytesToBase64url, generateKeyPair, generateKeyPairFromSeed } from '../util.js' import { concatKDF } from '../Digest.js' import { x25519 } from '@noble/curves/ed25519' export async function computeX25519Ecdh1PUv3Kek( recipient: Recipient, recipientSecret: Uint8Array | ECDH, senderPublicKey: Uint8Array, alg: string ) { const crv = 'X25519' const keyLen = 256 const header = recipient.header if (header.epk?.crv !== crv || typeof header.epk.x == 'undefined') return null // ECDH-1PU requires additional shared secret between // static key of sender and static key of recipient const publicKey = base64ToBytes(header.epk.x) let zE: Uint8Array let zS: Uint8Array if (recipientSecret instanceof Uint8Array) { zE = x25519.getSharedSecret(recipientSecret, publicKey) zS = x25519.getSharedSecret(recipientSecret, senderPublicKey) } else { zE = await recipientSecret(publicKey) zS = await recipientSecret(senderPublicKey) } const sharedSecret = new Uint8Array(zE.length + zS.length) sharedSecret.set(zE) sharedSecret.set(zS, zE.length) // Key Encryption Key let producerInfo let consumerInfo if (recipient.header.apu) producerInfo = base64ToBytes(recipient.header.apu) if (recipient.header.apv) consumerInfo = base64ToBytes(recipient.header.apv) return concatKDF(sharedSecret, keyLen, alg, producerInfo, consumerInfo) } export async function createX25519Ecdh1PUv3Kek( recipientPublicKey: Uint8Array, senderSecret: Uint8Array | ECDH, alg: string, // must be provided as this is the key agreement alg + the key wrapper alg, Example: 'ECDH-ES+A256KW' apu: string | undefined, apv: string | undefined, ephemeralKeyPair: EphemeralKeyPair | undefined ) { const crv = 'X25519' const keyLen = 256 const ephemeral = ephemeralKeyPair ? generateKeyPairFromSeed(ephemeralKeyPair.secretKey) : generateKeyPair() const epk = { kty: 'OKP', crv, x: bytesToBase64url(ephemeral.publicKey) } const zE = x25519.getSharedSecret(ephemeral.secretKey, recipientPublicKey) // ECDH-1PU requires additional shared secret between // static key of sender and static key of recipient let zS if (senderSecret instanceof Uint8Array) { zS = x25519.getSharedSecret(senderSecret, recipientPublicKey) } else { zS = await senderSecret(recipientPublicKey) } const sharedSecret = new Uint8Array(zE.length + zS.length) sharedSecret.set(zE) sharedSecret.set(zS, zE.length) let partyUInfo: Uint8Array = new Uint8Array(0) let partyVInfo: Uint8Array = new Uint8Array(0) if (apu) partyUInfo = base64ToBytes(apu) if (apv) partyVInfo = base64ToBytes(apv) // Key Encryption Key const kek = concatKDF(sharedSecret, keyLen, alg, partyUInfo, partyVInfo) return { epk, kek } }