mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Add an HD wallet
This commit is contained in:
parent
cb78799997
commit
e153020597
4 changed files with 95 additions and 0 deletions
|
|
@ -44,6 +44,7 @@
|
||||||
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.36.0",
|
"@nostrify/nostrify": "jsr:@nostrify/nostrify@^0.36.0",
|
||||||
"@nostrify/policies": "jsr:@nostrify/policies@^0.35.0",
|
"@nostrify/policies": "jsr:@nostrify/policies@^0.35.0",
|
||||||
"@scure/base": "npm:@scure/base@^1.1.6",
|
"@scure/base": "npm:@scure/base@^1.1.6",
|
||||||
|
"@scure/bip32": "npm:@scure/bip32@^1.5.0",
|
||||||
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
|
"@sentry/deno": "https://deno.land/x/sentry@7.112.2/index.mjs",
|
||||||
"@soapbox/kysely-pglite": "jsr:@soapbox/kysely-pglite@^1.0.0",
|
"@soapbox/kysely-pglite": "jsr:@soapbox/kysely-pglite@^1.0.0",
|
||||||
"@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0",
|
"@soapbox/stickynotes": "jsr:@soapbox/stickynotes@^0.4.0",
|
||||||
|
|
|
||||||
24
deno.lock
generated
24
deno.lock
generated
|
|
@ -73,6 +73,7 @@
|
||||||
"npm:@noble/secp256k1@^2.0.0": "npm:@noble/secp256k1@2.1.0",
|
"npm:@noble/secp256k1@^2.0.0": "npm:@noble/secp256k1@2.1.0",
|
||||||
"npm:@scure/base@^1.1.6": "npm:@scure/base@1.1.6",
|
"npm:@scure/base@^1.1.6": "npm:@scure/base@1.1.6",
|
||||||
"npm:@scure/bip32@^1.4.0": "npm:@scure/bip32@1.4.0",
|
"npm:@scure/bip32@^1.4.0": "npm:@scure/bip32@1.4.0",
|
||||||
|
"npm:@scure/bip32@^1.5.0": "npm:@scure/bip32@1.5.0",
|
||||||
"npm:@scure/bip39@^1.3.0": "npm:@scure/bip39@1.3.0",
|
"npm:@scure/bip39@^1.3.0": "npm:@scure/bip39@1.3.0",
|
||||||
"npm:@types/node": "npm:@types/node@18.16.19",
|
"npm:@types/node": "npm:@types/node@18.16.19",
|
||||||
"npm:comlink-async-generator": "npm:comlink-async-generator@0.0.1",
|
"npm:comlink-async-generator": "npm:comlink-async-generator@0.0.1",
|
||||||
|
|
@ -604,6 +605,12 @@
|
||||||
"@noble/hashes": "@noble/hashes@1.4.0"
|
"@noble/hashes": "@noble/hashes@1.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@noble/curves@1.6.0": {
|
||||||
|
"integrity": "sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/hashes": "@noble/hashes@1.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@noble/hashes@1.3.1": {
|
"@noble/hashes@1.3.1": {
|
||||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|
@ -616,6 +623,10 @@
|
||||||
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
"integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
},
|
},
|
||||||
|
"@noble/hashes@1.5.0": {
|
||||||
|
"integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
"@noble/secp256k1@2.1.0": {
|
"@noble/secp256k1@2.1.0": {
|
||||||
"integrity": "sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==",
|
"integrity": "sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
|
|
@ -632,6 +643,10 @@
|
||||||
"integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==",
|
"integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
},
|
},
|
||||||
|
"@scure/base@1.1.9": {
|
||||||
|
"integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
|
||||||
|
"dependencies": {}
|
||||||
|
},
|
||||||
"@scure/bip32@1.3.1": {
|
"@scure/bip32@1.3.1": {
|
||||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -648,6 +663,14 @@
|
||||||
"@scure/base": "@scure/base@1.1.6"
|
"@scure/base": "@scure/base@1.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@scure/bip32@1.5.0": {
|
||||||
|
"integrity": "sha512-8EnFYkqEQdnkuGBVpCzKxyIwDCBLDVj3oiX0EKUFre/tOjL/Hqba1D6n/8RcmaQy4f95qQFrO2A8Sr6ybh4NRw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@noble/curves": "@noble/curves@1.6.0",
|
||||||
|
"@noble/hashes": "@noble/hashes@1.5.0",
|
||||||
|
"@scure/base": "@scure/base@1.1.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@scure/bip39@1.2.1": {
|
"@scure/bip39@1.2.1": {
|
||||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -2132,6 +2155,7 @@
|
||||||
"npm:@isaacs/ttlcache@^1.4.1",
|
"npm:@isaacs/ttlcache@^1.4.1",
|
||||||
"npm:@noble/secp256k1@^2.0.0",
|
"npm:@noble/secp256k1@^2.0.0",
|
||||||
"npm:@scure/base@^1.1.6",
|
"npm:@scure/base@^1.1.6",
|
||||||
|
"npm:@scure/bip32@^1.5.0",
|
||||||
"npm:comlink-async-generator@^0.0.1",
|
"npm:comlink-async-generator@^0.0.1",
|
||||||
"npm:comlink@^4.4.1",
|
"npm:comlink@^4.4.1",
|
||||||
"npm:commander@12.1.0",
|
"npm:commander@12.1.0",
|
||||||
|
|
|
||||||
51
src/DittoWallet.ts
Normal file
51
src/DittoWallet.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { HDKey } from '@scure/bip32';
|
||||||
|
|
||||||
|
import { Conf } from '@/config.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HD wallet based on the `DITTO_NSEC`.
|
||||||
|
* The wallet is used to derive keys for various purposes.
|
||||||
|
* It is a singleton with static methods, and the keys are cached.
|
||||||
|
*/
|
||||||
|
export class DittoWallet {
|
||||||
|
static #root = HDKey.fromMasterSeed(Conf.seckey);
|
||||||
|
static #keys = new Map<string, HDKey>();
|
||||||
|
|
||||||
|
/** Derive the key cached. */
|
||||||
|
static derive(path: string): HDKey {
|
||||||
|
const existing = this.#keys.get(path);
|
||||||
|
if (existing) {
|
||||||
|
return existing;
|
||||||
|
} else {
|
||||||
|
const key = this.#root.derive(path);
|
||||||
|
this.#keys.set(path, key);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Derive the key and return the bytes. */
|
||||||
|
static deriveKey(path: string): Uint8Array {
|
||||||
|
const { privateKey } = this.derive(path);
|
||||||
|
|
||||||
|
if (!privateKey) {
|
||||||
|
throw new Error('Private key not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Database encryption key for AES-GCM encryption of database columns. */
|
||||||
|
static get dbKey(): Uint8Array {
|
||||||
|
return this.deriveKey(Conf.wallet.dbKeyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Captcha encryption key for encrypting answer data in AES-GCM. */
|
||||||
|
static get captchaKey(): Uint8Array {
|
||||||
|
return this.deriveKey(Conf.wallet.captchaKeyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** VAPID secret key, used for web push notifications. ES256. */
|
||||||
|
static get vapidKey(): Uint8Array {
|
||||||
|
return this.deriveKey(Conf.wallet.vapidKeyPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -98,6 +98,25 @@ class Conf {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* BIP-32 derivation paths for different crypto use-cases.
|
||||||
|
* The `DITTO_NSEC` is used as the seed.
|
||||||
|
* Keys can be rotated by changing the derviation path.
|
||||||
|
*/
|
||||||
|
static wallet = {
|
||||||
|
/** Private key for AES-GCM encryption in the Postgres database. */
|
||||||
|
get dbKeyPath(): string {
|
||||||
|
return Deno.env.get('WALLET_DB_KEY_PATH') || "m/0'/1'";
|
||||||
|
},
|
||||||
|
/** Private key for AES-GCM encryption of captcha answer data. */
|
||||||
|
get captchaKeyPath(): string {
|
||||||
|
return Deno.env.get('WALLET_CAPTCHA_KEY_PATH') || "m/0'/2'";
|
||||||
|
},
|
||||||
|
/** VAPID private key path. */
|
||||||
|
get vapidKeyPath(): string {
|
||||||
|
return Deno.env.get('WALLET_VAPID_KEY_PATH') || "m/0'/3'";
|
||||||
|
},
|
||||||
|
};
|
||||||
/** Character limit to enforce for posts made through Mastodon API. */
|
/** Character limit to enforce for posts made through Mastodon API. */
|
||||||
static get postCharLimit(): number {
|
static get postCharLimit(): number {
|
||||||
return Number(Deno.env.get('POST_CHAR_LIMIT') || 5000);
|
return Number(Deno.env.get('POST_CHAR_LIMIT') || 5000);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue