UNPKG

2.85 kBPlain TextView Raw
1import type { ECDH, EphemeralKeyPair, Recipient } from './types.js'
2import { base64ToBytes, bytesToBase64url, generateKeyPair, generateKeyPairFromSeed } from '../util.js'
3import { concatKDF } from '../Digest.js'
4import { x25519 } from '@noble/curves/ed25519'
5export async function computeX25519Ecdh1PUv3Kek(
6 recipient: Recipient,
7 recipientSecret: Uint8Array | ECDH,
8 senderPublicKey: Uint8Array,
9 alg: string
10) {
11 const crv = 'X25519'
12 const keyLen = 256
13 const header = recipient.header
14 if (header.epk?.crv !== crv || typeof header.epk.x == 'undefined') return null
15 // ECDH-1PU requires additional shared secret between
16 // static key of sender and static key of recipient
17 const publicKey = base64ToBytes(header.epk.x)
18 let zE: Uint8Array
19 let zS: Uint8Array
20
21 if (recipientSecret instanceof Uint8Array) {
22 zE = x25519.getSharedSecret(recipientSecret, publicKey)
23 zS = x25519.getSharedSecret(recipientSecret, senderPublicKey)
24 } else {
25 zE = await recipientSecret(publicKey)
26 zS = await recipientSecret(senderPublicKey)
27 }
28
29 const sharedSecret = new Uint8Array(zE.length + zS.length)
30 sharedSecret.set(zE)
31 sharedSecret.set(zS, zE.length)
32
33 // Key Encryption Key
34 let producerInfo
35 let consumerInfo
36 if (recipient.header.apu) producerInfo = base64ToBytes(recipient.header.apu)
37 if (recipient.header.apv) consumerInfo = base64ToBytes(recipient.header.apv)
38
39 return concatKDF(sharedSecret, keyLen, alg, producerInfo, consumerInfo)
40}
41
42export async function createX25519Ecdh1PUv3Kek(
43 recipientPublicKey: Uint8Array,
44 senderSecret: Uint8Array | ECDH,
45 alg: string, // must be provided as this is the key agreement alg + the key wrapper alg, Example: 'ECDH-ES+A256KW'
46 apu: string | undefined,
47 apv: string | undefined,
48 ephemeralKeyPair: EphemeralKeyPair | undefined
49) {
50 const crv = 'X25519'
51 const keyLen = 256
52 const ephemeral = ephemeralKeyPair ? generateKeyPairFromSeed(ephemeralKeyPair.secretKey) : generateKeyPair()
53 const epk = { kty: 'OKP', crv, x: bytesToBase64url(ephemeral.publicKey) }
54 const zE = x25519.getSharedSecret(ephemeral.secretKey, recipientPublicKey)
55
56 // ECDH-1PU requires additional shared secret between
57 // static key of sender and static key of recipient
58 let zS
59 if (senderSecret instanceof Uint8Array) {
60 zS = x25519.getSharedSecret(senderSecret, recipientPublicKey)
61 } else {
62 zS = await senderSecret(recipientPublicKey)
63 }
64
65 const sharedSecret = new Uint8Array(zE.length + zS.length)
66 sharedSecret.set(zE)
67 sharedSecret.set(zS, zE.length)
68
69 let partyUInfo: Uint8Array = new Uint8Array(0)
70 let partyVInfo: Uint8Array = new Uint8Array(0)
71 if (apu) partyUInfo = base64ToBytes(apu)
72 if (apv) partyVInfo = base64ToBytes(apv)
73
74 // Key Encryption Key
75 const kek = concatKDF(sharedSecret, keyLen, alg, partyUInfo, partyVInfo)
76 return { epk, kek }
77}