mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 12:28:42 +00:00
91 lines
5.1 KiB
JavaScript
91 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { Certificate, DhtRunner, Identity, PrivateKey, PublicKey, hash } from '../index.js';
|
|
|
|
const payload = Buffer.from([0, 255, 1, 0, 42]);
|
|
|
|
test('RSA keys sign, encrypt, and survive password-protected serialization', async () => {
|
|
const key = await PrivateKey.generate({ algorithm: 'rsa', bits: 2048 });
|
|
const publicKey = key.getPublicKey();
|
|
assert.match(publicKey.getId(), /^[0-9a-f]{40}$/);
|
|
assert.match(publicKey.getLongId(), /^[0-9a-f]{64}$/);
|
|
const signature = key.sign(payload);
|
|
assert.equal(publicKey.checkSignature(payload, signature), true);
|
|
assert.equal(publicKey.checkSignature(Buffer.from('other'), signature), false);
|
|
assert.deepEqual(key.decrypt(publicKey.encrypt(payload)), payload);
|
|
const restored = new PrivateKey(key.serialize('test password'), 'test password');
|
|
const restoredPublic = new PublicKey(publicKey.serialize());
|
|
assert.equal(restoredPublic.getLongId(), publicKey.getLongId());
|
|
assert.equal(restoredPublic.checkSignature(payload, restored.sign(payload)), true);
|
|
assert.deepEqual(restored.decrypt(restoredPublic.encrypt(payload)), payload);
|
|
assert.throws(() => new PrivateKey(key.serialize('password'), 'wrong password'));
|
|
assert.throws(() => key.decrypt(Buffer.from('invalid ciphertext')));
|
|
assert.throws(() => key.serialize('bad\0password'), TypeError);
|
|
});
|
|
|
|
test('EC keys sign and invalid crypto inputs raise JS errors', async () => {
|
|
const key = await PrivateKey.generate();
|
|
const restored = new PrivateKey(key.serialize());
|
|
assert.equal(restored.getPublicKey().checkSignature(payload, key.sign(payload)), true);
|
|
assert.throws(() => key.getPublicKey().encrypt(payload));
|
|
for (const Type of [PrivateKey, PublicKey, Certificate]) {
|
|
assert.throws(() => new Type(Buffer.alloc(0)));
|
|
assert.throws(() => new Type(Buffer.from('not a key or certificate')));
|
|
assert.throws(() => new Type(null), TypeError);
|
|
}
|
|
assert.throws(() => PrivateKey.generate({ algorithm: 'unknown' }), TypeError);
|
|
assert.throws(() => PrivateKey.generate({ algorithm: 'rsa', bits: 1 }), RangeError);
|
|
assert.throws(() => PrivateKey.generate({ algorithm: 'rsa', bits: 2048.5 }), RangeError);
|
|
assert.throws(() => key.sign('not bytes'), TypeError);
|
|
assert.throws(() => PrivateKey.prototype.sign.call({}, payload), TypeError);
|
|
});
|
|
|
|
test('identities own matching keys and certificates and support import', async () => {
|
|
const identity = await Identity.generate('nodejs crypto');
|
|
const certificate = new Certificate(identity.certificate.serialize());
|
|
const key = new PrivateKey(identity.privateKey.serialize());
|
|
const restored = new Identity(key, certificate);
|
|
assert.equal(restored.certificate.getLongId(), key.getPublicKey().getLongId());
|
|
assert.equal(certificate.getPublicKey().checkSignature(payload, key.sign(payload)), true);
|
|
assert.throws(() => new Identity({}, certificate), TypeError);
|
|
const other = await PrivateKey.generate();
|
|
assert.throws(() => new Identity(other, certificate), /match/i);
|
|
});
|
|
|
|
test('signed and encrypted values round-trip between authenticated nodes', { timeout: 30000 }, async () => {
|
|
const writerIdentity = await Identity.generate('writer');
|
|
const readerIdentity = await Identity.generate('reader', { algorithm: 'rsa', bits: 2048 });
|
|
const writer = new DhtRunner();
|
|
const reader = new DhtRunner();
|
|
try {
|
|
writer.run({ port: 0, ipv4: '127.0.0.1', ipv6: '::1', identity: writerIdentity });
|
|
reader.run({ port: 0, ipv4: '127.0.0.1', ipv6: '::1', identity: readerIdentity });
|
|
writer.bootstrap('127.0.0.1', reader.getBoundPort());
|
|
reader.bootstrap('127.0.0.1', writer.getBoundPort());
|
|
const signedKey = hash('nodejs signed value');
|
|
assert.equal(await writer.putSigned(signedKey, payload, { id: 41n }), true);
|
|
const signed = await reader.get(signedKey);
|
|
assert.ok(signed.some(value => value.data.equals(payload) && value.isSigned));
|
|
assert.equal(signed[0].owner, writerIdentity.certificate.getLongId());
|
|
const encryptedKey = hash('nodejs encrypted value');
|
|
assert.equal(await writer.putEncrypted(encryptedKey, payload, readerIdentity.certificate.getPublicKey(), { id: 42n }), true);
|
|
const decrypted = await reader.get(encryptedKey);
|
|
assert.ok(decrypted.some(value => value.data.equals(payload)));
|
|
assert.throws(() => writer.putEncrypted(encryptedKey, payload, {}), TypeError);
|
|
assert.throws(() => writer.putEncrypted(encryptedKey, payload, writerIdentity.certificate.getPublicKey()), /RSA/i);
|
|
} finally {
|
|
await Promise.all([writer.close(), reader.close()]);
|
|
}
|
|
});
|
|
|
|
test('signed operations require an identity and reject stopped runners', async () => {
|
|
const runner = new DhtRunner();
|
|
try {
|
|
assert.throws(() => runner.run({ identity: {} }), TypeError);
|
|
runner.run({ port: 0, ipv4: '127.0.0.1', ipv6: '::1' });
|
|
assert.throws(() => runner.putSigned(hash('unsigned'), payload), /identity/i);
|
|
} finally {
|
|
await runner.close();
|
|
}
|
|
assert.throws(() => runner.putSigned(hash('closed'), payload), /closed/i);
|
|
}); |