@nostr-dev-kit/ndk
Version:
NDK - Nostr Development Kit. Includes AI Guardrails to catch common mistakes during development.
107 lines (92 loc) • 3.97 kB
text/typescript
import type { NDKSigner } from "../signers";
/**
* Encryption and gift-wrapping of events
* Implements Nip04, Nip44, Nip59
*/
import type { NDKEncryptionScheme } from "../types";
import type { NDKUser } from "../user";
import type { NDKEvent } from "./index.js";
export type EncryptionMethod = "encrypt" | "decrypt";
export async function encrypt(
this: NDKEvent,
recipient?: NDKUser,
signer?: NDKSigner,
scheme: NDKEncryptionScheme = "nip44",
): Promise<void> {
let encrypted: string | undefined;
if (!this.ndk) throw new Error("No NDK instance found!");
let currentSigner = signer;
if (!currentSigner) {
this.ndk.assertSigner();
currentSigner = this.ndk.signer;
}
if (!currentSigner) throw new Error("no NDK signer");
const currentRecipient =
recipient ||
(() => {
const pTags = this.getMatchingTags("p");
if (pTags.length !== 1) {
throw new Error("No recipient could be determined and no explicit recipient was provided");
}
return this.ndk.getUser({ pubkey: pTags[0][1] });
})();
if (scheme === "nip44" && (await isEncryptionEnabled(currentSigner, "nip44"))) {
encrypted = (await currentSigner.encrypt(currentRecipient, this.content, "nip44")) as string;
}
// support for encrypting events via legacy `nip04`. adapted from Coracle
if ((!encrypted || scheme === "nip04") && (await isEncryptionEnabled(currentSigner, "nip04"))) {
encrypted = (await currentSigner.encrypt(currentRecipient, this.content, "nip04")) as string;
}
if (!encrypted) throw new Error("Failed to encrypt event.");
this.content = encrypted;
}
export async function decrypt(
this: NDKEvent,
sender?: NDKUser,
signer?: NDKSigner,
scheme?: NDKEncryptionScheme,
): Promise<void> {
// Check if we have this decrypted event in cache
if (this.ndk?.cacheAdapter?.getDecryptedEvent) {
const cachedEvent = await this.ndk.cacheAdapter.getDecryptedEvent(this.id);
// If we found a cached decrypted event, use its content
if (cachedEvent) {
this.content = cachedEvent.content;
return;
}
}
let decrypted: string | undefined;
if (!this.ndk) throw new Error("No NDK instance found!");
let currentSigner = signer;
if (!currentSigner) {
this.ndk.assertSigner();
currentSigner = this.ndk.signer;
}
if (!currentSigner) throw new Error("no NDK signer");
const currentSender = sender || this.author;
if (!currentSender) throw new Error("No sender provided and no author available");
const currentScheme = scheme || (this.content.match(/\\?iv=/) ? "nip04" : "nip44");
// simple check for legacy `nip04` encrypted events. adapted from Coracle
if (
(currentScheme === "nip04" || this.kind === 4) &&
(await isEncryptionEnabled(currentSigner, "nip04")) &&
this.content.search("\\?iv=")
) {
decrypted = (await currentSigner.decrypt(currentSender, this.content, "nip04")) as string;
}
if (!decrypted && currentScheme === "nip44" && (await isEncryptionEnabled(currentSigner, "nip44"))) {
decrypted = (await currentSigner.decrypt(currentSender, this.content, "nip44")) as string;
}
if (!decrypted) throw new Error("Failed to decrypt event.");
this.content = decrypted;
// Cache the decrypted event if we have a cache adapter that supports it
// For regular encrypted events (not gift-wrapped), the event ID itself is the cache key
if (this.ndk?.cacheAdapter?.addDecryptedEvent) {
this.ndk.cacheAdapter.addDecryptedEvent(this.id, this);
}
}
async function isEncryptionEnabled(signer: NDKSigner, scheme?: NDKEncryptionScheme): Promise<boolean> {
if (!signer.encryptionEnabled) return false;
if (!scheme) return true;
return Boolean(await signer.encryptionEnabled(scheme));
}