feat(pwa): add base
All checks were successful
/ build-web (push) Successful in 5m35s
/ build-desktop (linux) (push) Successful in 9m41s
/ build-mobile (push) Successful in 20m12s
/ release (push) Successful in 1m51s
Dependency builds / Build web (pull_request) Has been skipped
Dependency builds / Build desktop (pull_request) Has been skipped
Dependency builds / Build mobile (pull_request) Has been skipped
All checks were successful
/ build-web (push) Successful in 5m35s
/ build-desktop (linux) (push) Successful in 9m41s
/ build-mobile (push) Successful in 20m12s
/ release (push) Successful in 1m51s
Dependency builds / Build web (pull_request) Has been skipped
Dependency builds / Build desktop (pull_request) Has been skipped
Dependency builds / Build mobile (pull_request) Has been skipped
This commit is contained in:
parent
6e977bca7e
commit
7b36218ffa
40 changed files with 4014 additions and 922 deletions
40
packages/storage/src/browserSecure.ts
Normal file
40
packages/storage/src/browserSecure.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { getDatabaseEntry } from "@tensamin/shared/indexedDb";
|
||||
|
||||
type SecureEnvelope = {
|
||||
__tensaminSecure: 1;
|
||||
version: 1;
|
||||
iv: string;
|
||||
data: string;
|
||||
};
|
||||
|
||||
function base64ToBytes(value: string) {
|
||||
const binary = atob(value);
|
||||
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
||||
}
|
||||
|
||||
function isSecureEnvelope(value: unknown): value is SecureEnvelope {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const envelope = value as Partial<SecureEnvelope>;
|
||||
return (
|
||||
envelope.__tensaminSecure === 1 &&
|
||||
envelope.version === 1 &&
|
||||
typeof envelope.iv === "string" &&
|
||||
typeof envelope.data === "string"
|
||||
);
|
||||
}
|
||||
|
||||
export async function loadSecureBrowserValue<T>(key: string) {
|
||||
const stored = await getDatabaseEntry<unknown>("storage", key);
|
||||
if (stored === undefined || !isSecureEnvelope(stored)) {
|
||||
return stored as T | undefined;
|
||||
}
|
||||
|
||||
const masterKey = await getDatabaseEntry<CryptoKey>("keys", "master-v1");
|
||||
if (!masterKey) throw new Error("Secure storage key is unavailable.");
|
||||
const plaintext = await crypto.subtle.decrypt(
|
||||
{ name: "AES-GCM", iv: base64ToBytes(stored.iv) },
|
||||
masterKey,
|
||||
base64ToBytes(stored.data),
|
||||
);
|
||||
return JSON.parse(new TextDecoder().decode(plaintext)) as T;
|
||||
}
|
||||
Loading…
Reference in a new issue