diff --git a/src/background.ts b/src/background.ts index bc14b0b..a03d3b4 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,4 +1,6 @@ import { LoginMessage, Message, MessageType } from './message'; +import { optionsStorage } from './options-storage'; +import { decrypt, encrypt } from './crypto'; const COOKIE_NAME = 'sid'; @@ -19,6 +21,13 @@ const handleLogin = async (message: LoginMessage) => { console.log(cookie); + if (cookie) { + const opts = await optionsStorage.getAll(); + const test1 = await encrypt(cookie.value, opts.encryptionPassword); + const test2 = await decrypt(test1, opts.encryptionPassword); + console.log(opts.encryptionPassword, test1, test2); + } + const url = new URL(message.url); url.pathname = '/game.php'; url.searchParams.set('screen', 'overview_villages'); diff --git a/src/crypto.ts b/src/crypto.ts new file mode 100644 index 0000000..0f98ce7 --- /dev/null +++ b/src/crypto.ts @@ -0,0 +1,85 @@ +// https://github.com/bradyjoslin/webcrypto-example/blob/master/script.js + +const ITERATIONS = 100000; + +const enc = new TextEncoder(); +const dec = new TextDecoder(); + +export const encrypt = async (data: string, password: string) => { + const salt = crypto.getRandomValues(new Uint8Array(16)); + const iv = crypto.getRandomValues(new Uint8Array(12)); + const passwordKey = await getPasswordKey(password); + const aesKey = await deriveKey(passwordKey, salt, ['encrypt']); + const encryptedContent = await crypto.subtle.encrypt( + { + name: 'AES-GCM', + iv: iv, + }, + aesKey, + enc.encode(data) + ); + + const encryptedContentArr = new Uint8Array(encryptedContent); + let buf = new Uint8Array( + salt.byteLength + iv.byteLength + encryptedContentArr.byteLength + ); + buf.set(salt, 0); + buf.set(iv, salt.byteLength); + buf.set(encryptedContentArr, salt.byteLength + iv.byteLength); + return bufToBase64(buf); +}; + +export const decrypt = async (encryptedData: string, password: string) => { + const encryptedDataBuff = base64ToBuf(encryptedData); + const salt = encryptedDataBuff.slice(0, 16); + const iv = encryptedDataBuff.slice(16, 16 + 12); + const data = encryptedDataBuff.slice(16 + 12); + const passwordKey = await getPasswordKey(password); + const aesKey = await deriveKey(passwordKey, salt, ['decrypt']); + const decryptedContent = await crypto.subtle.decrypt( + { + name: 'AES-GCM', + iv: iv, + }, + aesKey, + data + ); + return dec.decode(decryptedContent); +}; + +const getPasswordKey = (password: string) => { + return crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, [ + 'deriveKey', + ]); +}; + +const deriveKey = ( + passwordKey: CryptoKey, + salt: Uint8Array, + keyUsages: KeyUsage[] +) => { + return crypto.subtle.deriveKey( + { + name: 'PBKDF2', + salt: salt, + iterations: ITERATIONS, + hash: 'SHA-256', + }, + passwordKey, + { name: 'AES-GCM', length: 256 }, + false, + keyUsages + ); +}; + +const bufToBase64 = (buf: Uint8Array) => { + let s = ''; + buf.forEach((c) => { + s += String.fromCharCode(c); + }); + return btoa(s); +}; + +const base64ToBuf = (b64: string) => { + return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); +};