mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 17:15:03 -07:00
Add SV2 DATUM payout hardening
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user