export class Safe {
- /**
- * Encrypts data with a password and stores it in the Safe.
- */
- async put (name: string, password: string, data: any): Promise<boolean>
- /**
- * Encrypts data with a CryptoKey and stores it in the Safe.
- */
- async put (name: string, key: CryptoKey, data: any): Promise<boolean>
- async put (name: string, passkey: string | CryptoKey, data: any): Promise<boolean> {
- if (storage.getItem(name)) {
- throw new Error(ERR_MSG)
- }
- return this.overwrite(name, passkey as string, data)
- }
+ /**
+ * Encrypts data with a password and stores it in the Safe.
+ */
+ async put (name: string, password: string, data: any): Promise<boolean>
+ /**
+ * Encrypts data with a CryptoKey and stores it in the Safe.
+ */
+ async put (name: string, key: CryptoKey, data: any): Promise<boolean>
+ async put (name: string, passkey: string | CryptoKey, data: any): Promise<boolean> {
+ if (storage.getItem(name)) {
+ throw new Error(ERR_MSG)
+ }
+ return this.overwrite(name, passkey as string, data)
+ }
- /**
- * Encrypts data with a password and stores it in the Safe.
- */
- async overwrite (name: string, password: string, data: any): Promise<boolean>
- /**
- * Encrypts data with a CryptoKey and stores it in the Safe.
- */
- async overwrite (name: string, key: CryptoKey, data: any): Promise<boolean>
- async overwrite (name: string, passkey: string | CryptoKey, data: any): Promise<boolean> {
- if (this.#isNotValid(name, passkey, data)) {
- throw new Error(ERR_MSG)
- }
+ /**
+ * Encrypts data with a password and stores it in the Safe.
+ */
+ async overwrite (name: string, password: string, data: any): Promise<boolean>
+ /**
+ * Encrypts data with a CryptoKey and stores it in the Safe.
+ */
+ async overwrite (name: string, key: CryptoKey, data: any): Promise<boolean>
+ async overwrite (name: string, passkey: string | CryptoKey, data: any): Promise<boolean> {
+ if (this.#isNotValid(name, passkey, data)) {
+ throw new Error(ERR_MSG)
+ }
- const iv = new Entropy()
- if (typeof passkey === 'string') {
- try {
- passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
- passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'])
- } catch (err) {
- throw new Error(ERR_MSG)
- }
- }
+ const iv = new Entropy()
+ if (typeof passkey === 'string') {
+ try {
+ passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
+ passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['encrypt'])
+ } catch (err) {
+ throw new Error(ERR_MSG)
+ }
+ }
- try {
- if (typeof data === 'bigint') {
- data = data.toString()
- }
- data = JSON.stringify(data)
- const encoded = utf8.toBytes(data)
- const encrypted = await subtle.encrypt({ name: 'AES-GCM', iv: iv.buffer }, passkey, encoded)
- const record = {
- encrypted: buffer.toHex(encrypted),
- iv: iv.hex
- }
- await new Promise<void>((resolve, reject) => {
- try {
- storage.setItem(name, JSON.stringify(record))
- resolve()
- } catch (err) {
- reject(err)
- }
- })
- passkey = ''
- } catch (err) {
- throw new Error(ERR_MSG)
- }
- return (storage.getItem(name) != null)
- }
+ try {
+ if (typeof data === 'bigint') {
+ data = data.toString()
+ }
+ data = JSON.stringify(data)
+ const encoded = utf8.toBytes(data)
+ const encrypted = await subtle.encrypt({ name: 'AES-GCM', iv: iv.buffer }, passkey, encoded)
+ const record = {
+ encrypted: buffer.toHex(encrypted),
+ iv: iv.hex
+ }
+ await new Promise<void>((resolve, reject) => {
+ try {
+ storage.setItem(name, JSON.stringify(record))
+ resolve()
+ } catch (err) {
+ reject(err)
+ }
+ })
+ passkey = ''
+ } catch (err) {
+ throw new Error(ERR_MSG)
+ }
+ return (storage.getItem(name) != null)
+ }
- /**
- * Retrieves data from the Safe and decrypts data with a password.
- */
- async get (name: string, password: string): Promise<any>
- /**
- * Retrieves data from the Safe and decrypts data with a CryptoKey.
- */
- async get (name: string, key: CryptoKey): Promise<any>
- async get (name: string, passkey: string | CryptoKey): Promise<any> {
- if (this.#isNotValid(name, passkey)) {
- return null
- }
+ /**
+ * Retrieves data from the Safe and decrypts data with a password.
+ */
+ async get (name: string, password: string): Promise<any>
+ /**
+ * Retrieves data from the Safe and decrypts data with a CryptoKey.
+ */
+ async get (name: string, key: CryptoKey): Promise<any>
+ async get (name: string, passkey: string | CryptoKey): Promise<any> {
+ if (this.#isNotValid(name, passkey)) {
+ return null
+ }
- const item = await new Promise<string | null>(resolve => {
- resolve(storage.getItem(name))
- })
- if (item == null) {
- return null
- }
- const record = JSON.parse(item)
- const encrypted = hex.toBytes(record.encrypted)
- const iv = new Entropy(record.iv)
+ const item = await new Promise<string | null>(resolve => {
+ resolve(storage.getItem(name))
+ })
+ if (item == null) {
+ return null
+ }
+ const record = JSON.parse(item)
+ const encrypted = hex.toBytes(record.encrypted)
+ const iv = new Entropy(record.iv)
- try {
- if (typeof passkey === 'string') {
- passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
- passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['decrypt'])
- }
- } catch (err) {
- return null
- }
+ try {
+ if (typeof passkey === 'string') {
+ passkey = await subtle.importKey('raw', utf8.toBytes(passkey), 'PBKDF2', false, ['deriveBits', 'deriveKey'])
+ passkey = await subtle.deriveKey({ name: 'PBKDF2', hash: 'SHA-512', salt: iv.bytes, iterations: 210000 }, passkey, { name: 'AES-GCM', length: 256 }, false, ['decrypt'])
+ }
+ } catch (err) {
+ return null
+ }
- try {
- const decrypted = await subtle.decrypt({ name: 'AES-GCM', iv: iv.buffer }, passkey, encrypted)
- const decoded = buffer.toUtf8(decrypted)
- const data = JSON.parse(decoded)
- passkey = ''
- storage.removeItem(name)
- return data
- } catch (err) {
- return null
- }
- }
+ try {
+ const decrypted = await subtle.decrypt({ name: 'AES-GCM', iv: iv.buffer }, passkey, encrypted)
+ const decoded = buffer.toUtf8(decrypted)
+ const data = JSON.parse(decoded)
+ passkey = ''
+ storage.removeItem(name)
+ return data
+ } catch (err) {
+ return null
+ }
+ }
- #isNotValid (name: string, passkey: string | CryptoKey, data?: any): boolean {
- if (typeof name !== 'string' || name === '') {
- return true
- }
- if (typeof passkey !== 'string' || passkey === '') {
- if (!(passkey instanceof CryptoKey)) {
- return true
- }
- }
- if (typeof data === 'object') {
- try {
- JSON.stringify(data)
- } catch (err) {
- return true
- }
- }
- return false
- }
+ #isNotValid (name: string, passkey: string | CryptoKey, data?: any): boolean {
+ if (typeof name !== 'string' || name === '') {
+ return true
+ }
+ if (typeof passkey !== 'string' || passkey === '') {
+ if (!(passkey instanceof CryptoKey)) {
+ return true
+ }
+ }
+ if (typeof data === 'object') {
+ try {
+ JSON.stringify(data)
+ } catch (err) {
+ return true
+ }
+ }
+ return false
+ }
}