Skip to content

Instantly share code, notes, and snippets.

@starbackr-dev
Created May 29, 2023 19:20
Show Gist options
  • Save starbackr-dev/558c30c126bcb2e10d39e1dc9c58d8a6 to your computer and use it in GitHub Desktop.
Save starbackr-dev/558c30c126bcb2e10d39e1dc9c58d8a6 to your computer and use it in GitHub Desktop.
This code demonstrates how clients can implement NIP-59 for secure DMs using a paid relay which requires AUTH (NIP-42)
const {WebSocket} = require('websocket-polyfill');
const {relayInit,nip04,
getEventHash,
getPublicKey,
verifySignature,
signEvent,
nip42,finishEvent,
generatePrivateKey} = require('nostr-tools');
const dm1PrivateKey = 'c776822b744112daeb279a7616650d87124df3c04fc16d3a08a4e3a68f7042d3';
const dm2PrivateKey = '5bf7669f0db1a683df230949f37bc00564954ceccc220bbd77fc15cad1b0a278';
const relayUrl = 'wss://securedm.current.fyi';
let connectStatus = false;
const connectAndAuth = (relay, privateKey) => {
return new Promise((resolve, reject) => {
relay.connect();
console.log('after connect')
relay.on('connect', () => {
console.log(`connected to ${relay.url}`)
})
relay.on('error', () => {
console.log(`failed to connect to ${relay.url}`)
})
relay.on('notice', (msg) => {
console.log('notice: ', msg);
})
relay.on('auth', async (challenge) => {
console.log(`Auth from ${relay.url} with challenge ${challenge}`)
try {
await nip42.authenticate({
challenge,
relay,
sign: e => finishEvent(e, privateKey)
});
console.log(`Authed to ${relay.url}`)
resolve(true);
} catch (e) {
console.log(`Auth failed: ${relay.url}`)
reject(false);
}
});
});
}
const postDM = (relay) => {
return new Promise((resolve, reject) => {
(async () => {
let message= 'if you can see this message then secure DM works..: Sent at ' + Math.floor(Date.now() / 1000);
console.log('Sending Message: ', message);
let ciphertext = await nip04.encrypt(dm1PrivateKey, getPublicKey(dm2PrivateKey), message);
let postevent = {
kind: 4,
pubkey: getPublicKey(dm1PrivateKey),
created_at: Math.floor(Date.now() / 1000),
tags: [
['p', getPublicKey(dm2PrivateKey)],
['expiration', ((Math.floor(Date.now() / 1000))+86400).toString()]
],
content: ciphertext
};
postevent.id = getEventHash(postevent);
postevent.sig = await signEvent(postevent, dm1PrivateKey);
//Create a dummy key for NIP-59 gift-wrap
let tempKey = generatePrivateKey();
ciphertext = await nip04.encrypt(tempKey, getPublicKey(dm2PrivateKey), JSON.stringify(postevent));
let secureevent = {
kind: 1059,
pubkey: getPublicKey(tempKey),
created_at: Math.floor(Date.now() / 1000),
tags: [
['p', getPublicKey(dm2PrivateKey)],
['expiration', ((Math.floor(Date.now() / 1000))+86400).toString()]
],
content: ciphertext
};
secureevent.id = getEventHash(secureevent);
secureevent.sig = await signEvent(secureevent, tempKey);
//console.log('sent event: ', secureevent);
let pub = await relay.publish(secureevent);
pub.on('ok', () => {
console.log(`${relay.url} has accepted our event`)
resolve(true);
})
pub.on('failed', reason => {
console.log(`failed to publish to ${relay.url}: ${reason}`)
resolve(false);
})
})()
});
}
const getReceivedDM = (relay) => {
return new Promise((resolve, reject) => {
let sub = relay.sub([
{
limit: 1,
kinds: [1059],
'#p': [getPublicKey(dm2PrivateKey)],
}
])
sub.on('event', event => {
//console.log('event: ', event);
decrypt(event);
})
sub.on('eose', () => {
console.log('in eose');
resolve(true);
})
});
}
async function decrypt(event) {
const kind4 = JSON.parse(await nip04.decrypt(dm2PrivateKey, event.pubkey, event.content));
if (kind4.kind === 4) {
const message = await nip04.decrypt(dm2PrivateKey, kind4.pubkey, kind4.content);
console.log('Decrypted received message: ', message);
}
}
async function run() {
// Connect as DM user 1 and send a DM to user 2
let relay1 = relayInit(relayUrl);
connectStatus = await connectAndAuth(relay1, dm1PrivateKey);
if (connectStatus) await postDM(relay1);
console.log('after post DM');
relay1.close();
//Connect as DM user 2 and receive a DM from user 1
let relay2 = relayInit(relayUrl);
let connectStatus2 = await connectAndAuth(relay2, dm2PrivateKey);
if (connectStatus2) await getReceivedDM(relay2);
relay2.close();
}
run().catch(console.log);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment