Created
May 29, 2023 19:20
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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