All files / app/crypto key-encryption.ts

19.23% Statements 5/26
0% Branches 0/1
0% Functions 0/6
20% Lines 5/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 541x   1x                                           1x                             1x                     1x      
import { Buffer } from 'buffer';
 
const algorithmName = 'AES-GCM';
 
function extractEncryptionKey(hash: Uint8Array) {
  return hash.slice(0, 32);
}
 
function extractEncryptionInitVector(hash: Uint8Array) {
  return hash.slice(32, hash.length);
}
 
async function deriveWebCryptoKey(derivedKeyHash: Uint8Array) {
  const format = 'raw';
  const key = extractEncryptionKey(derivedKeyHash);
  const extractable = false;
  const keyUsages: KeyUsage[] = ['encrypt', 'decrypt'];
  return crypto.subtle.importKey(format, key, algorithmName, extractable, keyUsages);
}
 
interface EncryptMnemonicArgs {
  mnemonic: string;
  derivedKeyHash: Uint8Array;
}
export async function encryptMnemonic({ mnemonic, derivedKeyHash }: EncryptMnemonicArgs) {
  const key = await deriveWebCryptoKey(derivedKeyHash);
  const iv = extractEncryptionInitVector(derivedKeyHash);
  const cipherArrayBuffer = await crypto.subtle.encrypt(
    { name: algorithmName, iv },
    key,
    new TextEncoder().encode(mnemonic)
  );
  return Buffer.from(cipherArrayBuffer).toString('hex');
}
 
interface DecryptMnemonicArgs {
  encryptedMnemonic: string;
  derivedKeyHash: Uint8Array;
}
export async function decryptMnemonic({ encryptedMnemonic, derivedKeyHash }: DecryptMnemonicArgs) {
  Iif (derivedKeyHash.length !== 48) throw new Error('Key must be of length 48');
  const key = await deriveWebCryptoKey(derivedKeyHash);
  const iv = extractEncryptionInitVector(derivedKeyHash);
  const algorithm = { name: algorithmName, iv };
  const encryptedBuffer = Buffer.from(encryptedMnemonic, 'hex');
  const decrypted = await crypto.subtle.decrypt(algorithm, key, encryptedBuffer);
  const textDecoder = new TextDecoder();
  return textDecoder.decode(decrypted);
}
 
export function isDecryptionError(error: Error) {
  return String(error) === 'OperationError';
}