All files index.ts

91.37% Statements 53/58
58.33% Branches 7/12
100% Functions 13/13
90.74% Lines 49/54

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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 1181x 1x     1x 95x       1x 96x       1x 10x   1x           1x 1x 6x 91x   91x       91x     1x 6x 91x   91x       91x     1x 6x   6x 1158x   1158x         1158x     1158x       6x 6x 1158x         1158x     6x       3x 3x   3x   3428x   68x       68x   68x       3x       3x 3x   3x 1x     2x   2x   2x 386x     2x      
const chars = ((): string[] => {
  const chars = new Set();
 
  // Basic ASCII printable characters
  for (let i = 32; i <= 126; i++) {
    chars.add(String.fromCodePoint(i));
  }
 
  // Extended Latin characters
  for (let i = 160; i <= 255; i++) {
    chars.add(String.fromCodePoint(i));
  }
 
  // Add some common symbols
  const extraChars = ["€", "£", "¥", "©", "®", "™", "°", "±", "×", "÷"];
  extraChars.forEach((char) => chars.add(char));
 
  return [...chars].map(String);
})();
 
/**
 * Simple encryption utility class using plain JavaScript with salt-based XOR encryption
 */
export class SynthexCrypto {
  private static getKeyNumber = (key: string): number =>
    [...key].reduce<number>((acc, char, index) => {
      const code = char.codePointAt(0);
 
      Iif (!code) {
        throw new Error(`No code found for '${char}' char`);
      }
 
      return acc + (code + 1 + index * 10);
    }, 0);
 
  private static getSignature = (key: string): string =>
    [...key].reduce<string>((acc, char, index) => {
      const code = char.codePointAt(0);
 
      Iif (!code) {
        throw new Error(`No code found for '${char}' char`);
      }
 
      return btoa(code.toString(36)).slice(0, -2) + btoa(index.toString(36)).slice(0, -2) + acc;
    }, "");
 
  private static generateCharsMap = (key: string) => {
    const keyNumber = this.getKeyNumber(key);
 
    const map = chars.map((char, index) => {
      const code = char.codePointAt(0);
 
      Iif (!code) {
        throw new Error(`No code found for '${char}' char`);
      }
 
      const value =
        btoa((code * keyNumber).toString(36)).slice(0, -2) +
        btoa((code * index * keyNumber).toString(36)).slice(0, -2);
 
      return { char, value };
    });
 
    // Check for repeated values in the map
    const valueSet = new Set<string>();
    for (const entry of map) {
      Iif (valueSet.has(entry.value)) {
        throw new Error(
          `Duplicate value detected in map for char '${entry.char}' with value '${entry.value}'`,
        );
      }
      valueSet.add(entry.value);
    }
 
    return map;
  };
 
  public static encrypt(key: string, value: string): string {
    const map = this.generateCharsMap(key);
    const signature = this.getSignature(key);
 
    const encrypted = [...value]
      .map((valueChar) => {
        const mapEntry = map.find(({ char }) => char === valueChar);
 
        Iif (!mapEntry) {
          throw new Error(`Character ${valueChar} not found in map`);
        }
 
        const encoded = mapEntry.value;
 
        return encoded;
      })
      .join("");
 
    return signature + encrypted;
  }
 
  public static decrypt(key: string, value: string): string {
    const map = this.generateCharsMap(key);
    const signature = this.getSignature(key);
 
    if (!value.startsWith(signature)) {
      throw new Error("Invalid signature");
    }
 
    let decrypted = value;
 
    decrypted = decrypted.slice(signature.length);
 
    map.forEach(({ char, value }) => {
      decrypted = decrypted.split(value).join(char);
    });
 
    return decrypted;
  }
}