diff --git a/package.json b/package.json index 8108f566..0480d697 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "reselect": "^4.0.0", "resolve": "1.6.0", "sass-resources-loader": "^2.0.0", + "scrypt-js": "^3.0.1", "source-map-loader": "^0.2.1", "styled-components": "^4.1.3", "ts-jest": "22.0.1", diff --git a/src/state/PersistentStore.ts b/src/state/PersistentStore.ts index 6fc0fa07..966b6665 100644 --- a/src/state/PersistentStore.ts +++ b/src/state/PersistentStore.ts @@ -1,4 +1,11 @@ import { combineReducers, createStore, Store } from 'redux' +import nacl from 'tweetnacl' +import { u8aToHex } from '@kiltprotocol/sdk-js/build/crypto' +import { + encryption, + decryption, + passwordHashing, +} from '../utils/Encryption/Encryption' import * as Attestations from './ducks/Attestations' import * as Balances from './ducks/Balances' @@ -50,10 +57,12 @@ class PersistentStore { } private static NAME = 'reduxState' + private static SALT = 'salt' private static async deserialize( - obj: SerializedState + encryptedState: string ): Promise> { + const obj = JSON.parse(encryptedState) return { attestations: Attestations.Store.deserialize(obj.attestations), claims: Claims.Store.deserialize(obj.claims), @@ -85,12 +94,21 @@ class PersistentStore { public async init(): Promise { const localState = localStorage.getItem(PersistentStore.NAME) + let salt = localStorage.getItem(PersistentStore.SALT) + + if (!salt) { + salt = u8aToHex(nacl.randomBytes(24)) + localStorage.setItem(PersistentStore.SALT, salt) + } + + const password = await passwordHashing('password', salt) let persistedState: Partial = {} if (localState) { try { - persistedState = await PersistentStore.deserialize( - JSON.parse(localState) - ) + const decryptedState = decryption(localState, password) + if (decryptedState) { + persistedState = await PersistentStore.deserialize(decryptedState) + } } catch (error) { console.error('Could not construct persistentStore', error) } @@ -117,10 +135,11 @@ class PersistentStore { ) this.storeInternal.subscribe(() => { - localStorage.setItem( - PersistentStore.NAME, - PersistentStore.serialize(this.storeInternal.getState()) + const serializedState = PersistentStore.serialize( + this.storeInternal.getState() ) + const encryptedState = encryption(serializedState, password) + localStorage.setItem(PersistentStore.NAME, JSON.stringify(encryptedState)) }) return this.storeInternal diff --git a/src/utils/Encryption/Encryption.ts b/src/utils/Encryption/Encryption.ts new file mode 100644 index 00000000..eb317fc6 --- /dev/null +++ b/src/utils/Encryption/Encryption.ts @@ -0,0 +1,30 @@ +import { + encryptSymmetricAsStr, + decryptSymmetricStr, + CryptoInput, + EncryptedSymmetricString, + coToUInt8, +} from '@kiltprotocol/sdk-js/build/crypto' +import { scrypt } from 'scrypt-js' + +export function encryption( + message: string, + secret: CryptoInput +): EncryptedSymmetricString { + return encryptSymmetricAsStr(message, secret) +} + +export function decryption(data: string, secret: CryptoInput): string | null { + return decryptSymmetricStr(JSON.parse(data), secret) +} + +export function passwordHashing( + password: string, + salt: string +): Promise { + const N = 1024 + const r = 8 + const p = 1 + const dkLen = 32 + return scrypt(coToUInt8(password), coToUInt8(salt), N, r, p, dkLen) +}