Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create encrypted storage for beacon #2194

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ VITE_DISABLE_TYPESCRIPT_CHECK=false
VITE_DISABLE_ESLINT_CHECK=false
# if you want to see the errors in the browser
VITE_ENABLE_CHECKS_OVERLAY=false

LOCAL_STORAGE_PASSWORD="umami-password-example"
4 changes: 2 additions & 2 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@umami/desktop",
"productName": "umami",
"version": "2.3.4",
"version": "2.3.5",
"author": "Trilitech <[email protected]>",
"description": "Tezos Desktop Wallet",
"homepage": "https://umamiwallet.com",
Expand Down Expand Up @@ -37,7 +37,7 @@
"theme": "chakra-cli tokens src/style/theme.ts"
},
"devDependencies": {
"@airgap/beacon-wallet": "^4.3.0",
"@airgap/beacon-wallet": "^4.3.1",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.9",
Expand Down
1 change: 1 addition & 0 deletions packages/crypto/src/AES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const encrypt = async (data: string, password: string): Promise<Encrypted
};
};

// V1 - for backup/local storage , V2 - for transactions
type DecryptMode = "V1" | "V2";

export const TOO_MANY_ATTEMPTS_ERROR =
Expand Down
2 changes: 1 addition & 1 deletion packages/state/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
]
},
"dependencies": {
"@airgap/beacon-wallet": "^4.3.0",
"@airgap/beacon-sdk": "^4.3.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/state/src/beacon/WalletClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { WalletClient as WalletClientClass } from "@airgap/beacon-wallet";
import { WalletClient as WalletClientClass } from "@airgap/beacon-sdk";
import { EncryptedBeaconStorage } from "./storage";
import { type Persistor } from "redux-persist";

export const WalletClient =
Expand All @@ -9,6 +10,7 @@ export const WalletClient =
name: "Umami",
iconUrl: "",
appUrl: "https://umamiwallet.com/",
storage: new EncryptedBeaconStorage(),
});

export const logout = (persistor: Persistor) =>
Expand Down
68 changes: 68 additions & 0 deletions packages/state/src/beacon/storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// extend storage from beacon-types such that we encrypt the data we store and decrypt it when we retrieve it
// we can use the same encryption key for all data

import { Storage, type StorageKey, type StorageKeyReturnType } from "@airgap/beacon-sdk";
import { decrypt, encrypt, type EncryptedData } from "@umami/crypto";

// get a static password from enviornment variables

const local_storage_password: string = "umami-test-password-123";

export class EncryptedBeaconStorage extends Storage {
static async isSupported(): Promise<boolean> {
return typeof localStorage !== "undefined";
}

async get<K extends StorageKey>(key: K): Promise<StorageKeyReturnType[K]> {
const decryptData= localStorage.getItem(this.getPrefixedKey(key))?.split(",");
if (decryptData === undefined || local_storage_password === undefined) {
return undefined as StorageKeyReturnType[K];
}
const encryptedValue :EncryptedData= {
iv: decryptData[0],
salt: decryptData[1],
data: decryptData[2],
};
const decryptedValue = decrypt(encryptedValue, local_storage_password);
let result = JSON.parse(await decryptedValue) as StorageKeyReturnType[K];
console.log("Beacon encryption get:", key, result);
return result;

}

async set<K extends StorageKey>(key: K, value: StorageKeyReturnType[K]): Promise<void> {
console.log("Beacon encryption set:", key, value );
if (local_storage_password === undefined) {
return;
}
const stringValue = JSON.stringify(value);
const encryptedValue = await encrypt(stringValue, local_storage_password);
localStorage.setItem(this.getPrefixedKey(key),[encryptedValue.iv, encryptedValue.salt, encryptedValue.data].join(","));
}

async delete<K extends StorageKey>(key: K): Promise<void> {
localStorage.removeItem(this.getPrefixedKey(key));
}

async subscribeToStorageChanged(callback: (arg: {
eventType: "storageCleared" | "entryModified";
key: string | null;
oldValue: string | null;
newValue: string | null;
}) => {}): Promise<void> {
window.addEventListener("storage", (event) => {
if (event.storageArea === localStorage) {
callback({
eventType: event.key ? "entryModified" : "storageCleared",
key: event.key,
oldValue: event.oldValue,
newValue: event.newValue,
});
}
});
}

getPrefixedKey<K extends StorageKey>(key: K): string {
return `beacon:${key}`;
}
}
Loading
Loading