From c790d59a01baa1b6577aea97bcfbbbb8144f0fde Mon Sep 17 00:00:00 2001 From: Prem Kumar Date: Sun, 2 Feb 2025 19:41:23 +0530 Subject: [PATCH 1/3] add crypto-utils --- index.html | 5 ++- scripts/crypto-utils.js | 92 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 scripts/crypto-utils.js diff --git a/index.html b/index.html index 5b65796e..93fa5d0a 100644 --- a/index.html +++ b/index.html @@ -24,6 +24,7 @@ + @@ -1179,7 +1180,7 @@

Material You NewTab

- +
Dark Mode
@@ -1470,4 +1471,4 @@

Material You NewTab

- + \ No newline at end of file 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 From b761c31d2c2eabf03bed651f2bb0e4d2e8506482 Mon Sep 17 00:00:00 2001 From: Prem Kumar Date: Sun, 2 Feb 2025 21:57:07 +0530 Subject: [PATCH 2/3] some minor changes --- scripts/weather.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/weather.js b/scripts/weather.js index 2daf2f15..b63fd46c 100644 --- a/scripts/weather.js +++ b/scripts/weather.js @@ -6,7 +6,7 @@ * If not, see . */ -window.addEventListener("DOMContentLoaded", async () => { +document.addEventListener("DOMContentLoaded", async () => { // Cache DOM elements const userAPIInput = document.getElementById("userAPI"); const userLocInput = document.getElementById("userLoc"); @@ -233,15 +233,15 @@ window.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; } From cdcf45adfb3e6db72fb11b022949b6b8b094564a Mon Sep 17 00:00:00 2001 From: Prem Kumar Date: Sun, 2 Feb 2025 22:05:47 +0530 Subject: [PATCH 3/3] less indentation --- scripts/weather.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/weather.js b/scripts/weather.js index b63fd46c..016431c1 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) {