1 | import type { ECDH, EphemeralKeyPair, Recipient } from './types.js'
|
2 | import { base64ToBytes, bytesToBase64url, generateKeyPair, generateKeyPairFromSeed } from '../util.js'
|
3 | import { concatKDF } from '../Digest.js'
|
4 | import { x25519 } from '@noble/curves/ed25519'
|
5 | export 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 |
|
16 |
|
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 |
|
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 |
|
42 | export async function createX25519Ecdh1PUv3Kek(
|
43 | recipientPublicKey: Uint8Array,
|
44 | senderSecret: Uint8Array | ECDH,
|
45 | alg: string,
|
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 |
|
57 |
|
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 |
|
75 | const kek = concatKDF(sharedSecret, keyLen, alg, partyUInfo, partyVInfo)
|
76 | return { epk, kek }
|
77 | }
|