mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
95 lines
4.1 KiB
TypeScript
95 lines
4.1 KiB
TypeScript
import * as sodium from 'libsodium-wrappers-sumo';
|
|
import {
|
|
DATUM_INITIAL_HEADER_KEY,
|
|
DatumProtocolCommand,
|
|
datumHeaderXorFeedback,
|
|
decodeDatumHeader,
|
|
encodeDatumFrame,
|
|
} from './datum-codec';
|
|
import { DatumCryptoSession } from './datum-crypto';
|
|
|
|
describe('DatumCryptoSession', () => {
|
|
it('derives stable DATUM public keys from a configured seed', async () => {
|
|
await sodium.ready;
|
|
const seed = Buffer.from('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', 'hex');
|
|
|
|
const first = DatumCryptoSession.generateKeyPairFromSeed(seed);
|
|
const second = DatumCryptoSession.generateKeyPairFromSeed(seed);
|
|
|
|
expect(Buffer.concat([first.edPublicKey, first.xPublicKey]).toString('hex'))
|
|
.toBe(Buffer.concat([second.edPublicKey, second.xPublicKey]).toString('hex'));
|
|
expect(first.edSecretKey).toEqual(second.edSecretKey);
|
|
});
|
|
|
|
it('opens signed sealed handshakes and builds encrypted responses', async () => {
|
|
await sodium.ready;
|
|
const serverIdentity = DatumCryptoSession.generateKeyPair();
|
|
const serverSessionKeys = DatumCryptoSession.generateKeyPair();
|
|
const clientIdentity = DatumCryptoSession.generateKeyPair();
|
|
const clientSessionKeys = DatumCryptoSession.generateKeyPair();
|
|
const server = await DatumCryptoSession.create(serverIdentity, serverSessionKeys);
|
|
const nextHeaderKey = 0x12345678;
|
|
|
|
const signedBody = Buffer.concat([
|
|
clientIdentity.edPublicKey,
|
|
clientIdentity.xPublicKey,
|
|
clientSessionKeys.edPublicKey,
|
|
clientSessionKeys.xPublicKey,
|
|
Buffer.from('datum-test/0\0', 'utf8'),
|
|
Buffer.from([0xfe]),
|
|
uint32(nextHeaderKey),
|
|
Buffer.from([1, 2, 3]),
|
|
]);
|
|
const signature = Buffer.from(sodium.crypto_sign_detached(signedBody, clientIdentity.edSecretKey));
|
|
const encrypted = Buffer.from(sodium.crypto_box_seal(
|
|
Buffer.concat([signedBody, signature]),
|
|
serverIdentity.xPublicKey,
|
|
));
|
|
const frame = encodeDatumFrame({
|
|
header: {
|
|
cmdLen: encrypted.length,
|
|
reserved: 0,
|
|
isSigned: true,
|
|
isEncryptedPubkey: true,
|
|
isEncryptedChannel: false,
|
|
protoCmd: DatumProtocolCommand.HANDSHAKE_INIT,
|
|
},
|
|
payload: encrypted,
|
|
}, DATUM_INITIAL_HEADER_KEY);
|
|
const header = decodeDatumHeader(frame.subarray(0, 4), DATUM_INITIAL_HEADER_KEY);
|
|
|
|
const hello = server.openHandshake(frame.subarray(4, 4 + header.cmdLen));
|
|
expect(hello.userAgent).toBe('datum-test/0');
|
|
expect(hello.nextHeaderKey).toBe(nextHeaderKey);
|
|
expect(hello.sessionXPublicKey).toEqual(clientSessionKeys.xPublicKey);
|
|
|
|
const response = server.buildHandshakeResponse(hello, 'motd');
|
|
const responseHeader = decodeDatumHeader(response.subarray(0, 4), datumHeaderXorFeedback((~nextHeaderKey) >>> 0));
|
|
expect(responseHeader.protoCmd).toBe(DatumProtocolCommand.HANDSHAKE_RESPONSE);
|
|
expect(responseHeader.isSigned).toBe(true);
|
|
expect(responseHeader.isEncryptedPubkey).toBe(true);
|
|
const opened = Buffer.from(sodium.crypto_box_seal_open(
|
|
response.subarray(4),
|
|
clientSessionKeys.xPublicKey,
|
|
clientSessionKeys.xSecretKey,
|
|
));
|
|
expect(opened.subarray(0, 32)).toEqual(clientIdentity.edPublicKey);
|
|
expect(opened.subarray(128, 160)).toEqual(serverSessionKeys.edPublicKey);
|
|
|
|
const ping = server.encryptChannelFrame(DatumProtocolCommand.PING, Buffer.alloc(0));
|
|
const pingHeader = decodeDatumHeader(
|
|
ping.subarray(0, 4),
|
|
datumHeaderXorFeedback(datumHeaderXorFeedback((~nextHeaderKey) >>> 0)),
|
|
);
|
|
expect(pingHeader.protoCmd).toBe(DatumProtocolCommand.PING);
|
|
expect(pingHeader.isEncryptedChannel).toBe(true);
|
|
expect(pingHeader.cmdLen).toBe(sodium.crypto_box_MACBYTES);
|
|
});
|
|
});
|
|
|
|
function uint32(value: number): Buffer {
|
|
const result = Buffer.alloc(4);
|
|
result.writeUInt32LE(value >>> 0, 0);
|
|
return result;
|
|
}
|