import { HalfCryptoKeyPair } from "encryption/types"; export interface RSASignerI { signBinary(data: Uint8Array): Promise; verifyBinary(signature: Uint8Array, data: Uint8Array): Promise; } export function RSASigner( keyPair: CryptoKeyPair | HalfCryptoKeyPair ): RSASignerI { const { publicKey, privateKey } = keyPair as CryptoKeyPair; const algorithm = RSASigner.getDefaultAlgorithm(); return { async signBinary(data: Uint8Array): Promise { if (!publicKey) { throw Error( "Cannot sign data as privateKey has not been supplied." ); } return crypto.subtle.sign(algorithm, privateKey, data); }, async verifyBinary( signature: Uint8Array, data: Uint8Array ): Promise { if (!publicKey) { throw Error( "Cannot verify data as publicKey has not been supplied." ); } return crypto.subtle.verify(algorithm, publicKey, signature, data); } }; } RSASigner.getDefaultAlgorithm = function(): RsaHashedKeyGenParams { const hashBitLength = 512; return Object.freeze({ name: "RSA-PSS", // RFC 3447 suggests salt lengths are either 0 or the length of the output of the hash function saltLength: hashBitLength / 8, 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-" + hashBitLength }); }; RSASigner.generateKeyPair = async function(): Promise { return (await crypto.subtle.generateKey( RSASigner.getDefaultAlgorithm(), // We need to be able to save this key to the user document, so it needs to be extractable true, ["sign", "verify"] )) as CryptoKeyPair; };