import { HalfCryptoKeyPair } from "encryption/types"; export interface RSAKeyEncryptorI { encryptKey(key: CryptoKey): Promise; decryptKey( encryptedKey: ArrayBuffer, decryptedKeyAlgorithm: AesKeyAlgorithm ): Promise; } export function RSAKeyEncryptor( keyPair: CryptoKeyPair | HalfCryptoKeyPair ): RSAKeyEncryptorI { const { publicKey, privateKey } = keyPair as CryptoKeyPair; const exportFormat = "jwk"; const algorithm = RSAKeyEncryptor.getDefaultAlgorithm(); return { async encryptKey(key: CryptoKey): Promise { if (!publicKey) { throw new Error( "Cannot encrypt key as publicKey has not been supplied." ); } return await crypto.subtle.wrapKey( exportFormat, key, publicKey, algorithm ); }, async decryptKey( encryptedKey: ArrayBuffer, decryptedKeyAlgorithm: AesKeyAlgorithm ): Promise { if (!privateKey) { throw new Error( "Cannot decrypt key as publicKey has not been supplied." ); } return await crypto.subtle.unwrapKey( exportFormat, encryptedKey, privateKey, algorithm, decryptedKeyAlgorithm, true, ["encrypt", "decrypt"] ); } }; } RSAKeyEncryptor.getDefaultAlgorithm = function(): RsaHashedKeyGenParams { return Object.freeze({ name: "RSA-OAEP", modulusLength: 2048, // From MDN: Unless you have a good reason to use something else, specify 65537 here ([0x01, 0x00, 0x01]). publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: "SHA-512" }); }; RSAKeyEncryptor.generateKeyPair = async function(): Promise { return (await crypto.subtle.generateKey( RSAKeyEncryptor.getDefaultAlgorithm(), // We need to be able to save this key to the user document, so it needs to be extractable true, ["wrapKey", "unwrapKey"] )) as CryptoKeyPair; };