import { encoder, concat, uint32be } from './buffer_utils.js'; import { checkEncCryptoKey } from './crypto_key.js'; import digest from './digest.js'; function lengthAndInput(input) { return concat(uint32be(input.length), input); } async function concatKdf(secret, bits, value) { const iterations = Math.ceil((bits >> 3) / 32); const res = new Uint8Array(iterations * 32); for (let iter = 0; iter < iterations; iter++) { const buf = new Uint8Array(4 + secret.length + value.length); buf.set(uint32be(iter + 1)); buf.set(secret, 4); buf.set(value, 4 + secret.length); res.set(await digest('sha256', buf), iter * 32); } return res.slice(0, bits >> 3); } export async function deriveKey(publicKey, privateKey, algorithm, keyLength, apu = new Uint8Array(0), apv = new Uint8Array(0)) { checkEncCryptoKey(publicKey, 'ECDH'); checkEncCryptoKey(privateKey, 'ECDH', 'deriveBits'); const value = concat(lengthAndInput(encoder.encode(algorithm)), lengthAndInput(apu), lengthAndInput(apv), uint32be(keyLength)); let length; if (publicKey.algorithm.name === 'X25519') { length = 256; } else { length = Math.ceil(parseInt(publicKey.algorithm.namedCurve.slice(-3), 10) / 8) << 3; } const sharedSecret = new Uint8Array(await crypto.subtle.deriveBits({ name: publicKey.algorithm.name, public: publicKey, }, privateKey, length)); return concatKdf(sharedSecret, keyLength, value); } export function allowed(key) { switch (key.algorithm.namedCurve) { case 'P-256': case 'P-384': case 'P-521': return true; default: return key.algorithm.name === 'X25519'; } }