File size: 2,027 Bytes
8a790fb | 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 | export class EncryptionService {
private static readonly SECRET_SALT = "RELAY_SECURE_CBT_2026_SALT";
/**
* Applies an elegant reversible string transformation (XOR masking with cyclic salt)
* to guarantee student's private answers, progress, and timer states are encrypted in raw storage.
*/
static encrypt(rawData: string): string {
let result = "";
for (let i = 0; i < rawData.length; i++) {
const charCode = rawData.charCodeAt(i);
const saltCode = this.SECRET_SALT.charCodeAt(i % this.SECRET_SALT.length);
// XOR obfuscation & shift
result += String.fromCharCode(charCode ^ saltCode);
}
// Encode to base64 safely
try {
return btoa(encodeURIComponent(result));
} catch {
return btoa(result);
}
}
/**
* Decrypts obfuscated values back to standard UTF-8 text strings.
*/
static decrypt(encryptedData: string): string {
if (!encryptedData) return "";
let rawObfuscated = "";
try {
rawObfuscated = decodeURIComponent(atob(encryptedData));
} catch {
try {
rawObfuscated = atob(encryptedData);
} catch {
return "";
}
}
let result = "";
for (let i = 0; i < rawObfuscated.length; i++) {
const charCode = rawObfuscated.charCodeAt(i);
const saltCode = this.SECRET_SALT.charCodeAt(i % this.SECRET_SALT.length);
result += String.fromCharCode(charCode ^ saltCode);
}
return result;
}
/**
* Encrypts any structured JavaScript object safely.
*/
static encryptObject(obj: any): string {
try {
const rawString = JSON.stringify(obj);
return this.encrypt(rawString);
} catch {
return "";
}
}
/**
* Decrypts back to a structured JavaScript object.
*/
static decryptObject<T = any>(encryptedStr: string): T | null {
if (!encryptedStr) return null;
try {
const rawString = this.decrypt(encryptedStr);
return JSON.parse(rawString) as T;
} catch {
return null;
}
}
}
|