diff --git a/index.html b/index.html index 5ff69b86..3f37a5d5 100644 --- a/index.html +++ b/index.html @@ -24,6 +24,7 @@ + diff --git a/scripts/crypto-utils.js b/scripts/crypto-utils.js new file mode 100644 index 00000000..cc0ecc8c --- /dev/null +++ b/scripts/crypto-utils.js @@ -0,0 +1,92 @@ +/* + * Material You NewTab + * Copyright (c) 2023-2025 XengShi + * Licensed under the GNU General Public License v3.0 (GPL-3.0) + * You should have received a copy of the GNU General Public License along with this program. + * If not, see . + */ + +// Encryption Utilities using Web Crypto API: Functions for encrypting and decrypting data securely. + +/** + * Encrypts the given data using the given key with AES-GCM encryption and returns an object containing the encrypted data and the initialization vector used. + * @param {string} data - The data to encrypt. + * @param {CryptoKey} key - The key to use for encryption. + * @returns {Promise<{ cipherText: string, iv: string }>} - A Promise that resolves with an object containing the encrypted data and the initialization vector used. + */ + +async function encryptData(data, key) { + const enc = new TextEncoder(); + const encodedData = enc.encode(data); + const iv = crypto.getRandomValues(new Uint8Array(16)); // Initialization vector + const algorithm = { name: "AES-GCM", iv }; + + const cipherText = await crypto.subtle.encrypt(algorithm, key, encodedData); + return { + cipherText: Array.from(new Uint8Array(cipherText)).map(byte => String.fromCharCode(byte)).join(''), + iv: Array.from(iv).map(byte => String.fromCharCode(byte)).join('') + }; +} + + +/** + * Decrypts the given encrypted data using the given key and initialization vector (iv). + * @param {string} encrypted - The encrypted data to decrypt. + * @param {string} ivString - The initialization vector (iv) used for encryption. + * @param {CryptoKey} key - The key used for encryption. + * @returns {Promise} - The decrypted data as a string. + */ + +async function decryptData(encrypted, ivString, key) { + const dec = new TextDecoder(); + const iv = new Uint8Array([...ivString].map(char => char.charCodeAt(0))); + const algorithm = { name: "AES-GCM", iv }; + + const buffer = Uint8Array.from([...encrypted].map(char => char.charCodeAt(0))); + const decryptedData = await crypto.subtle.decrypt(algorithm, key, buffer); + return dec.decode(decryptedData); +} + + +/** + * Generates a new AES-GCM encryption key with a length of 256 bits. + * The key can be used for both encryption and decryption operations. + * The generated key is extractable, allowing it to be exported. + * @returns {Promise} - A Promise that resolves to the generated CryptoKey. + */ + +async function generateKey() { + return crypto.subtle.generateKey( + { + name: "AES-GCM", + length: 256, + }, + true, + ["encrypt", "decrypt"] + ); +} + + +/** + * Retrieves an AES-GCM encryption key from localStorage, generating and storing a new one if not found. + * If a key is not already stored, a new one is generated, exported, and saved in localStorage. + * The stored key is imported and returned for encryption and decryption operations. + * @returns {Promise} - A Promise that resolves to the imported CryptoKey. + */ + +async function getKey() { + if (!localStorage.getItem('cryptoKey')) { + const key = await generateKey(); + const exportedKey = await crypto.subtle.exportKey('raw', key); + localStorage.setItem('cryptoKey', Array.from(new Uint8Array(exportedKey)).map(byte => String.fromCharCode(byte)).join('')); + } + + const importedKey = Uint8Array.from(localStorage.getItem('cryptoKey').split('').map(char => char.charCodeAt(0))); + return crypto.subtle.importKey( + 'raw', + importedKey, + { name: "AES-GCM" }, + false, + ["encrypt", "decrypt"] + ); +} \ No newline at end of file diff --git a/scripts/weather.js b/scripts/weather.js index 823b7edf..f9299864 100644 --- a/scripts/weather.js +++ b/scripts/weather.js @@ -203,22 +203,22 @@ document.addEventListener("DOMContentLoaded", async () => { // Function to fetch GPS-based location async function fetchGPSLocation() { - try { - const getLocationFromGPS = () => { - return new Promise((resolve, reject) => { - navigator.geolocation.getCurrentPosition( - (position) => { - resolve({ - latitude: position.coords.latitude, - longitude: position.coords.longitude, - }); - }, - (error) => reject(error), - { timeout: 4000 } - ); - }); - }; + const getLocationFromGPS = () => { + return new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + (position) => { + resolve({ + latitude: position.coords.latitude, + longitude: position.coords.longitude, + }); + }, + (error) => reject(error), + { timeout: 4000 } + ); + }); + }; + try { const { latitude, longitude } = await getLocationFromGPS(); return `${latitude},${longitude}`; } catch (error) { @@ -233,15 +233,15 @@ document.addEventListener("DOMContentLoaded", async () => { try { // Use GPS for dynamic location currentUserLocation = await fetchGPSLocation(); - } catch { + } catch (error) { console.log("Failed to use GPS for location:", error); } } if (!currentUserLocation) { // Fallback to IP-based location if no manual input - const geoLocation = "https://ipinfo.io/json/"; - const locationData = await fetch(geoLocation); + const ipLocation = "https://ipinfo.io/json/"; + const locationData = await fetch(ipLocation); const parsedLocation = await locationData.json(); currentUserLocation = parsedLocation.loc; }