examforge / src /services /EncryptionService.ts
Benjahmin's picture
feat(offline): implement offline resilience for exams
8a790fb
Raw
History Blame Contribute Delete
2.03 kB
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;
}
}
}