Skip to content

Commit 91c6386

Browse files
authored
Throw error when reusing keyPair for listens (#208)
* Throw error when reusing keyPair for listens * Do not delete server from listening if it throws KEYPAIR_ALREADY_USED * Do not remove server from listening set if announcer.start() throws * Always clean up if _listen throws before starting the announcer
1 parent b5cd15a commit 91c6386

File tree

3 files changed

+52
-16
lines changed

3 files changed

+52
-16
lines changed

lib/errors.js

+4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ module.exports = class DHTError extends Error {
6464
return new DHTError(msg, 'ALREADY_LISTENING', DHTError.ALREADY_LISTENING)
6565
}
6666

67+
static KEYPAIR_ALREADY_USED (msg = 'Keypair already used') {
68+
return new DHTError(msg, 'KEYPAIR_ALREADY_USED', DHTError.KEYPAIR_ALREADY_USED)
69+
}
70+
6771
static NODE_DESTROYED (msg = 'Node destroyed') {
6872
return new DHTError(msg, 'NODE_DESTROYED', DHTError.NODE_DESTROYED)
6973
}

lib/server.js

+20-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const { unslabbedHash } = require('./crypto')
1010
const SecurePayload = require('./secure-payload')
1111
const Holepuncher = require('./holepuncher')
1212
const { isPrivate } = require('bogon')
13-
const { ALREADY_LISTENING, NODE_DESTROYED } = require('./errors')
13+
const { ALREADY_LISTENING, NODE_DESTROYED, KEYPAIR_ALREADY_USED } = require('./errors')
1414

1515
const HANDSHAKE_CLEAR_WAIT = 10000
1616
const HANDSHAKE_INITIAL_TIMEOUT = 10000
@@ -150,25 +150,30 @@ module.exports = class Server extends EventEmitter {
150150
// From now on, the DHT object which created me is responsible for closing me
151151
this.dht.listening.add(this)
152152

153-
await this.dht.bind()
154-
if (this._closing) return
153+
try {
154+
await this.dht.bind()
155+
if (this._closing) return
155156

156-
this.target = unslabbedHash(keyPair.publicKey)
157+
for (const s of this.dht.listening) {
158+
if (s._keyPair && b4a.equals(s._keyPair.publicKey, keyPair.publicKey)) {
159+
throw KEYPAIR_ALREADY_USED()
160+
}
161+
}
157162

158-
this._keyPair = keyPair
159-
this._announcer = new Announcer(this.dht, keyPair, this.target, opts)
163+
this.target = unslabbedHash(keyPair.publicKey)
164+
this._keyPair = keyPair
165+
this._announcer = new Announcer(this.dht, keyPair, this.target, opts)
160166

161-
this.dht._router.set(this.target, {
162-
relay: null,
163-
record: this._announcer.record,
164-
onpeerhandshake: this._onpeerhandshake.bind(this),
165-
onpeerholepunch: this._onpeerholepunch.bind(this)
166-
})
167+
this.dht._router.set(this.target, {
168+
relay: null,
169+
record: this._announcer.record,
170+
onpeerhandshake: this._onpeerhandshake.bind(this),
171+
onpeerholepunch: this._onpeerholepunch.bind(this)
172+
})
167173

168-
// warm it up for now
169-
this._localAddresses().catch(safetyCatch)
174+
// warm it up for now
175+
this._localAddresses().catch(safetyCatch)
170176

171-
try {
172177
await this._announcer.start()
173178
} catch (err) {
174179
await this._stopListening()

test/lifecycle.js

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const test = require('brittle')
2-
const { swarm } = require('./helpers')
32
const safetyCatch = require('safety-catch')
3+
const hypCrypto = require('hypercore-crypto')
4+
const { swarm } = require('./helpers')
45

56
test('Can destroy a DHT node while server.listen() is called', async function (t) {
67
const [a] = await swarm(t)
@@ -16,3 +17,29 @@ test('Can destroy a DHT node while server.listen() is called', async function (t
1617
await listenProm
1718
t.pass('The listen function does not error when the DHT closes while it is running')
1819
})
20+
21+
test('Cannot listen on multiple servers with the same keypair', async function (t) {
22+
const [a] = await swarm(t)
23+
24+
const s1 = a.createServer()
25+
const s2 = a.createServer()
26+
27+
const s3 = a.createServer()
28+
const s4 = a.createServer()
29+
const s5 = a.createServer()
30+
31+
await s1.listen()
32+
await t.exception(
33+
async () => await s2.listen(),
34+
/KEYPAIR_ALREADY_USED/
35+
)
36+
37+
const keyPair = hypCrypto.keyPair()
38+
39+
await s3.listen(keyPair)
40+
await t.exception(
41+
async () => await s4.listen(keyPair),
42+
/KEYPAIR_ALREADY_USED/
43+
)
44+
await s5.listen(hypCrypto.keyPair())
45+
})

0 commit comments

Comments
 (0)