Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 58 additions & 34 deletions secure-storage.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,71 @@
(function() {
// A fast obfuscation helper to protect local storage data from plain text snooping
function xorEncryptDecrypt(input, key) {
let output = "";
for (let i = 0; i < input.length; i++) {
let charCode = input.charCodeAt(i) ^ key.charCodeAt(i % key.length);
output += String.fromCharCode(charCode);
}
return btoa(output);
// A cryptographic token signing wrapper using Web Crypto API
// This ensures local session tokens are signed and validated for integrity before access.
const SECRET_KEY_STRING = "cara_crypto_secure_key_v2";

async function getKey() {
const enc = new TextEncoder();
return await window.crypto.subtle.importKey(
"raw",
enc.encode(SECRET_KEY_STRING),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign", "verify"]
);
}

function xorDecrypt(input, key) {
try {
let decoded = atob(input);
let output = "";
for (let i = 0; i < decoded.length; i++) {
let charCode = decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length);
output += String.fromCharCode(charCode);
}
return output;
} catch (e) {
return null;
}
async function signData(data) {
const key = await getKey();
const enc = new TextEncoder();
const signature = await window.crypto.subtle.sign(
"HMAC",
key,
enc.encode(data)
);
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}

const SECRET_KEY = "cara_obfuscation_secure_key";
async function verifyData(data, signatureBase64) {
const key = await getKey();
const enc = new TextEncoder();
const signatureBytes = Uint8Array.from(atob(signatureBase64), c => c.charCodeAt(0));
return await window.crypto.subtle.verify(
"HMAC",
key,
signatureBytes,
enc.encode(data)
);
}

window.SecureStorage = {
setItem: function(key, value) {
const strVal = JSON.stringify(value);
const encrypted = xorEncryptDecrypt(strVal, SECRET_KEY);
localStorage.setItem(key, encrypted);
setItem: async function(key, value) {
try {
const strVal = JSON.stringify(value);
const signature = await signData(strVal);
const payload = JSON.stringify({ data: strVal, signature: signature });

// Encode to base64 to obscure the JSON structure
localStorage.setItem(key, btoa(payload));
} catch (e) {
console.error("Failed to securely store item", e);
}
},
getItem: function(key) {
getItem: async function(key) {
const val = localStorage.getItem(key);
if (!val) return null;
const decrypted = xorDecrypt(val, SECRET_KEY);
if (!decrypted) return null;
try {
return JSON.parse(decrypted);
const decoded = atob(val);
const payload = JSON.parse(decoded);

// Verify integrity
const isValid = await verifyData(payload.data, payload.signature);
if (!isValid) {
console.warn("SecureStorage integrity check failed. Data tampered.");
localStorage.removeItem(key);
return null;
}

return JSON.parse(payload.data);
} catch (e) {
return null;
}
Expand All @@ -46,8 +74,4 @@
localStorage.removeItem(key);
}
};
})();

// TODO: Implement cryptographic token signing wrapper

// Wrapper utility ensuring local session tokens are obfuscated before storage.
})();
Loading