Cleanup
This commit is contained in:
@ -1,12 +1,12 @@
|
||||
import { scryptAsync } from "@noble/hashes/scrypt";
|
||||
import { sha256 } from '@noble/hashes/sha256';
|
||||
import { sha256 } from "@noble/hashes/sha256";
|
||||
import { hmac } from "@noble/hashes/hmac";
|
||||
import { bytesToHex, hexToBytes, randomBytes } from "@noble/hashes/utils";
|
||||
import { base64 } from "@scure/base";
|
||||
import { streamXOR as xchacha20 } from "@stablelib/xchacha20";
|
||||
|
||||
export class InvalidPinError extends Error {
|
||||
constructor(){
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@ -15,56 +15,55 @@ export class InvalidPinError extends Error {
|
||||
* Pin protected data
|
||||
*/
|
||||
export class PinEncrypted {
|
||||
static readonly #opts = {N: 2**20, r: 8, p: 1, dkLen: 32}
|
||||
#decrypted?: Uint8Array
|
||||
#encrypted: PinEncryptedPayload
|
||||
|
||||
constructor(enc: PinEncryptedPayload) {
|
||||
this.#encrypted = enc;
|
||||
}
|
||||
|
||||
get value() {
|
||||
if(!this.#decrypted) throw new Error("Content has not been decrypted yet");
|
||||
return bytesToHex(this.#decrypted);
|
||||
}
|
||||
|
||||
async decrypt(pin: string) {
|
||||
const key = await scryptAsync(pin, base64.decode(this.#encrypted.salt), PinEncrypted.#opts);
|
||||
const ciphertext = base64.decode(this.#encrypted.ciphertext);
|
||||
const nonce = base64.decode(this.#encrypted.iv);
|
||||
const plaintext = xchacha20(key, nonce, ciphertext, new Uint8Array(32));
|
||||
if(plaintext.length !== 32) throw new InvalidPinError();
|
||||
const mac = base64.encode(hmac(sha256, key, plaintext));
|
||||
if(mac !== this.#encrypted.mac) throw new InvalidPinError();
|
||||
this.#decrypted = plaintext;
|
||||
}
|
||||
static readonly #opts = { N: 2 ** 20, r: 8, p: 1, dkLen: 32 };
|
||||
#decrypted?: Uint8Array;
|
||||
#encrypted: PinEncryptedPayload;
|
||||
|
||||
toPayload() {
|
||||
return this.#encrypted;
|
||||
}
|
||||
|
||||
static async create(content: string, pin: string) {
|
||||
const salt = randomBytes(24);
|
||||
const nonce = randomBytes(24);
|
||||
const plaintext = hexToBytes(content);
|
||||
const key = await scryptAsync(pin, salt, PinEncrypted.#opts);
|
||||
const mac = base64.encode(hmac(sha256, key, plaintext));
|
||||
const ciphertext = xchacha20(key, nonce, plaintext, new Uint8Array(32));
|
||||
const ret = new PinEncrypted({
|
||||
salt: base64.encode(salt),
|
||||
ciphertext: base64.encode(ciphertext),
|
||||
iv: base64.encode(nonce),
|
||||
mac
|
||||
});
|
||||
ret.#decrypted = plaintext;
|
||||
return ret;
|
||||
}
|
||||
constructor(enc: PinEncryptedPayload) {
|
||||
this.#encrypted = enc;
|
||||
}
|
||||
|
||||
export interface PinEncryptedPayload {
|
||||
salt: string, // for KDF
|
||||
ciphertext: string
|
||||
iv: string,
|
||||
mac: string
|
||||
|
||||
get value() {
|
||||
if (!this.#decrypted) throw new Error("Content has not been decrypted yet");
|
||||
return bytesToHex(this.#decrypted);
|
||||
}
|
||||
|
||||
|
||||
async decrypt(pin: string) {
|
||||
const key = await scryptAsync(pin, base64.decode(this.#encrypted.salt), PinEncrypted.#opts);
|
||||
const ciphertext = base64.decode(this.#encrypted.ciphertext);
|
||||
const nonce = base64.decode(this.#encrypted.iv);
|
||||
const plaintext = xchacha20(key, nonce, ciphertext, new Uint8Array(32));
|
||||
if (plaintext.length !== 32) throw new InvalidPinError();
|
||||
const mac = base64.encode(hmac(sha256, key, plaintext));
|
||||
if (mac !== this.#encrypted.mac) throw new InvalidPinError();
|
||||
this.#decrypted = plaintext;
|
||||
}
|
||||
|
||||
toPayload() {
|
||||
return this.#encrypted;
|
||||
}
|
||||
|
||||
static async create(content: string, pin: string) {
|
||||
const salt = randomBytes(24);
|
||||
const nonce = randomBytes(24);
|
||||
const plaintext = hexToBytes(content);
|
||||
const key = await scryptAsync(pin, salt, PinEncrypted.#opts);
|
||||
const mac = base64.encode(hmac(sha256, key, plaintext));
|
||||
const ciphertext = xchacha20(key, nonce, plaintext, new Uint8Array(32));
|
||||
const ret = new PinEncrypted({
|
||||
salt: base64.encode(salt),
|
||||
ciphertext: base64.encode(ciphertext),
|
||||
iv: base64.encode(nonce),
|
||||
mac,
|
||||
});
|
||||
ret.#decrypted = plaintext;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export interface PinEncryptedPayload {
|
||||
salt: string; // for KDF
|
||||
ciphertext: string;
|
||||
iv: string;
|
||||
mac: string;
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import { ReqFilter } from "nostr";
|
||||
export function trimFilters(filters: Array<ReqFilter>) {
|
||||
const fNew = [];
|
||||
for (const f of filters) {
|
||||
const ent = Object.entries(f).filter(([,v]) => Array.isArray(v));
|
||||
if(ent.every(([,v]) => (v as Array<string | number>).length > 0)) {
|
||||
const ent = Object.entries(f).filter(([, v]) => Array.isArray(v));
|
||||
if (ent.every(([, v]) => (v as Array<string | number>).length > 0)) {
|
||||
fNew.push(f);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user