first chrome implementation
This commit is contained in:
93
projects/common/src/lib/helpers/crypto-helper.ts
Normal file
93
projects/common/src/lib/helpers/crypto-helper.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export class CryptoHelper {
|
||||
/**
|
||||
* Generate a base64 encoded IV.
|
||||
*/
|
||||
static generateIV(): string {
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
return Buffer.from(iv).toString('base64');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash (SHA-256) a text string.
|
||||
*/
|
||||
static async hash(text: string): Promise<string> {
|
||||
const textUint8 = new TextEncoder().encode(text); // encode as (utf-8) Uint8Array
|
||||
const hashBuffer = await crypto.subtle.digest('SHA-256', textUint8); // hash the message
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
|
||||
const hashHex = hashArray
|
||||
.map((b) => b.toString(16).padStart(2, '0'))
|
||||
.join(''); // convert bytes to hex string
|
||||
return hashHex;
|
||||
}
|
||||
|
||||
static v4(): string {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
static async deriveKey(password: string): Promise<CryptoKey> {
|
||||
const algo = {
|
||||
name: 'PBKDF2',
|
||||
hash: 'SHA-256',
|
||||
salt: new TextEncoder().encode('3e7cdebd-3b4c-4125-a18c-05750cad8ec3'),
|
||||
iterations: 1000,
|
||||
};
|
||||
return crypto.subtle.deriveKey(
|
||||
algo,
|
||||
await crypto.subtle.importKey(
|
||||
'raw',
|
||||
new TextEncoder().encode(password),
|
||||
{
|
||||
name: algo.name,
|
||||
},
|
||||
false,
|
||||
['deriveKey']
|
||||
),
|
||||
{
|
||||
name: 'AES-GCM',
|
||||
length: 256,
|
||||
},
|
||||
false,
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
}
|
||||
|
||||
static async encrypt(
|
||||
text: string,
|
||||
ivBase64String: string,
|
||||
password: string
|
||||
): Promise<string> {
|
||||
const algo = {
|
||||
name: 'AES-GCM',
|
||||
length: 256,
|
||||
iv: Buffer.from(ivBase64String, 'base64'),
|
||||
};
|
||||
|
||||
const cipherText = await crypto.subtle.encrypt(
|
||||
algo,
|
||||
await CryptoHelper.deriveKey(password),
|
||||
new TextEncoder().encode(text)
|
||||
);
|
||||
return Buffer.from(cipherText).toString('base64');
|
||||
}
|
||||
|
||||
static async decrypt(
|
||||
encryptedBase64String: string,
|
||||
ivBase64String: string,
|
||||
password: string
|
||||
): Promise<string> {
|
||||
const algo = {
|
||||
name: 'AES-GCM',
|
||||
length: 256,
|
||||
iv: Buffer.from(ivBase64String, 'base64'),
|
||||
};
|
||||
return new TextDecoder().decode(
|
||||
await crypto.subtle.decrypt(
|
||||
algo,
|
||||
await CryptoHelper.deriveKey(password),
|
||||
Buffer.from(encryptedBase64String, 'base64')
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
10
projects/common/src/lib/helpers/date-helper.ts
Normal file
10
projects/common/src/lib/helpers/date-helper.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export class DateHelper {
|
||||
static dateToISOLikeButLocal(date: Date): string {
|
||||
const offsetMs = date.getTimezoneOffset() * 60 * 1000;
|
||||
const msLocal = date.getTime() - offsetMs;
|
||||
const dateLocal = new Date(msLocal);
|
||||
const iso = dateLocal.toISOString();
|
||||
const isoLocal = iso.slice(0, 19).replace('T', ' ').replaceAll(':', '.');
|
||||
return isoLocal;
|
||||
}
|
||||
}
|
||||
128
projects/common/src/lib/helpers/nostr-helper.ts
Normal file
128
projects/common/src/lib/helpers/nostr-helper.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { bech32 } from '@scure/base';
|
||||
import * as utils from '@noble/curves/abstract/utils';
|
||||
import { getPublicKey } from 'nostr-tools';
|
||||
|
||||
export interface NostrHexObject {
|
||||
represents: string;
|
||||
hex: string;
|
||||
}
|
||||
|
||||
export interface NostrPubkeyObject {
|
||||
hex: string;
|
||||
npub: string;
|
||||
}
|
||||
|
||||
export interface NostrPrivkeyObject {
|
||||
hex: string;
|
||||
nsec: string;
|
||||
}
|
||||
|
||||
export class NostrHelper {
|
||||
static getNostrPrivkeyObject(nsec_OR_hex: string): NostrPrivkeyObject {
|
||||
// 1. Assume we got an nsec.
|
||||
// Try to generate hex value.
|
||||
try {
|
||||
const hexObject = this.#nSomething2hexObject(nsec_OR_hex);
|
||||
if (hexObject.represents !== 'nsec') {
|
||||
throw new Error('The provided string is NOT an nsec.');
|
||||
}
|
||||
|
||||
// Everything is fine. The provided string IS an nsec.
|
||||
return {
|
||||
hex: hexObject.hex,
|
||||
nsec: nsec_OR_hex,
|
||||
};
|
||||
} catch (error) {
|
||||
// Continue.
|
||||
}
|
||||
|
||||
// 2. Assume we got an hex.
|
||||
// Try to generate the nsec.
|
||||
try {
|
||||
const nsec = NostrHelper.privkey2nsec(nsec_OR_hex);
|
||||
return {
|
||||
hex: nsec_OR_hex,
|
||||
nsec,
|
||||
};
|
||||
} catch (error) {
|
||||
// Continue;
|
||||
}
|
||||
|
||||
throw new Error('Could not convert the provided string into nsec/hex.');
|
||||
}
|
||||
|
||||
static getNostrPubkeyObject(npub_OR_hex: string): NostrPubkeyObject {
|
||||
// 1. Assume we got an npub.
|
||||
// Try to generate hex value.
|
||||
try {
|
||||
const hexObject = this.#nSomething2hexObject(npub_OR_hex);
|
||||
if (hexObject.represents !== 'npub') {
|
||||
throw new Error('The provided string is NOT an npub.');
|
||||
}
|
||||
|
||||
// Everything is fine. The provided string IS an npub.
|
||||
return {
|
||||
hex: hexObject.hex,
|
||||
npub: npub_OR_hex,
|
||||
};
|
||||
} catch (error) {
|
||||
// Continue.
|
||||
}
|
||||
|
||||
// 2. Assume we got an hex.
|
||||
// Try to generate the npub.
|
||||
try {
|
||||
const npub = NostrHelper.pubkey2npub(npub_OR_hex);
|
||||
return {
|
||||
hex: npub_OR_hex,
|
||||
npub,
|
||||
};
|
||||
} catch (error) {
|
||||
// Continue;
|
||||
}
|
||||
|
||||
throw new Error('Could not convert the provided string into npub/hex.');
|
||||
}
|
||||
|
||||
static pubkey2npub(hex: string): string {
|
||||
const data = utils.hexToBytes(hex);
|
||||
const words = bech32.toWords(data);
|
||||
return bech32.encode('npub', words, 5000);
|
||||
}
|
||||
|
||||
static privkey2nsec(hex: string): string {
|
||||
const data = utils.hexToBytes(hex);
|
||||
const words = bech32.toWords(data);
|
||||
return bech32.encode('nsec', words, 5000);
|
||||
}
|
||||
|
||||
static pubkeyFromPrivkey(hex: string): string {
|
||||
const privkeyBytes = utils.hexToBytes(hex);
|
||||
return getPublicKey(privkeyBytes);
|
||||
}
|
||||
|
||||
static hex2bytes(hex: string): Uint8Array {
|
||||
return utils.hexToBytes(hex);
|
||||
}
|
||||
|
||||
static splitKey(text: string, first: number, last: number): string {
|
||||
const part1 = text.slice(0, first);
|
||||
const part2 = '...';
|
||||
const part3 = text.slice(-last);
|
||||
return `${part1}${part2}${part3}`;
|
||||
}
|
||||
|
||||
static #nSomething2hexObject(nSomething: string): NostrHexObject {
|
||||
const { prefix, words } = bech32.decode(
|
||||
nSomething as `${string}1${string}`,
|
||||
5000
|
||||
);
|
||||
const data = new Uint8Array(bech32.fromWords(words));
|
||||
|
||||
return {
|
||||
represents: prefix,
|
||||
hex: utils.bytesToHex(data),
|
||||
};
|
||||
}
|
||||
}
|
||||
8
projects/common/src/lib/helpers/text-helper.ts
Normal file
8
projects/common/src/lib/helpers/text-helper.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export class TextHelper {
|
||||
/**
|
||||
* Takes a string returns something like "\<first-x-chars>...\<last-y-chars>""
|
||||
*/
|
||||
static split(text: string, first: number, last: number): string {
|
||||
return `${text.slice(0, first)}...${text.slice(-last)}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user