UNPKG

2.47 kBPlain TextView Raw
1import type {
2 AuthEncryptParams,
3 ContentEncrypter,
4 ECDH,
5 Encrypter,
6 EncryptionResult,
7 EphemeralKeyPair,
8 KekCreator,
9 KeyWrapper,
10 ProtectedHeader,
11 Recipient,
12} from './types.js'
13import { bytesToBase64url, genX25519EphemeralKeyPair } from '../util.js'
14import { randomBytes } from '@noble/hashes/utils'
15
16export function createFullEncrypter(
17 recipientPublicKey: Uint8Array,
18 senderSecret: Uint8Array | ECDH | undefined,
19 options: Partial<AuthEncryptParams> = {},
20 kekCreator: KekCreator,
21 keyWrapper: KeyWrapper,
22 contentEncrypter: ContentEncrypter
23): Encrypter {
24 async function encryptCek(cek: Uint8Array, ephemeralKeyPair?: EphemeralKeyPair): Promise<Recipient> {
25 const { epk, kek } = await kekCreator.createKek(
26 recipientPublicKey,
27 senderSecret,
28 `${kekCreator.alg}+${keyWrapper.alg}`,
29 options.apu,
30 options.apv,
31 ephemeralKeyPair
32 )
33 const res = await keyWrapper.from(kek).wrap(cek)
34 const recipient: Recipient = {
35 encrypted_key: bytesToBase64url(res.ciphertext),
36 header: {},
37 }
38 if (res.iv) recipient.header.iv = bytesToBase64url(res.iv)
39 if (res.tag) recipient.header.tag = bytesToBase64url(res.tag)
40 if (options.kid) recipient.header.kid = options.kid
41 if (options.apu) recipient.header.apu = options.apu
42 if (options.apv) recipient.header.apv = options.apv
43 if (!ephemeralKeyPair) {
44 recipient.header.alg = `${kekCreator.alg}+${keyWrapper.alg}`
45 recipient.header.epk = epk
46 }
47
48 return recipient
49 }
50
51 async function encrypt(
52 cleartext: Uint8Array,
53 protectedHeader: ProtectedHeader = {},
54 aad?: Uint8Array,
55 ephemeralKeyPair?: EphemeralKeyPair
56 ): Promise<EncryptionResult> {
57 // we won't want alg to be set to dir from xc20pDirEncrypter
58 Object.assign(protectedHeader, { alg: undefined })
59 // Content Encryption Key
60 const cek = randomBytes(32)
61 const recipient: Recipient = await encryptCek(cek, ephemeralKeyPair)
62 // getting an ephemeral key means the epk is set only once per all recipients
63 if (ephemeralKeyPair) {
64 protectedHeader.alg = `${kekCreator.alg}+${keyWrapper.alg}`
65 protectedHeader.epk = ephemeralKeyPair.publicKeyJWK
66 }
67 return {
68 ...(await contentEncrypter.from(cek).encrypt(cleartext, protectedHeader, aad)),
69 recipient,
70 cek,
71 }
72 }
73
74 return { alg: keyWrapper.alg, enc: contentEncrypter.enc, encrypt, encryptCek, genEpk: genX25519EphemeralKeyPair }
75}