mirror of
https://github.com/tlsnotary/PageSigner.git
synced 2026-01-08 22:27:57 -05:00
146 lines
6.3 KiB
JavaScript
146 lines
6.3 KiB
JavaScript
import {TWOPC} from './twopc/TWOPC.js';
|
|
import {TLS} from './TLS.js';
|
|
import {concatTA, sha256, assert, eq, xor, str2ba, pubkeyPEM2raw} from './utils.js';
|
|
|
|
|
|
// class TLSNotarySession impements one notarization session
|
|
// (one client request followed by one server response) using the TLSNotary protocol.
|
|
export class TLSNotarySession{
|
|
// twopc is an instance of class TWOPC. Used to speak to the notary.
|
|
twopc;
|
|
// tls is an instance of class TLS. Used to speak to the webserver.
|
|
tls;
|
|
// probeTLS is used to probe the webserver to see if it supports TLSNotary,
|
|
// before we start any time-intensive 2PC
|
|
probeTLS;
|
|
options;
|
|
request;
|
|
notary;
|
|
pm;
|
|
constructor(server, port, request, notary, sessionOptions, circuits, progressMonitor){
|
|
this.twopc = new TWOPC(notary, request.length, circuits, progressMonitor);
|
|
this.tls = new TLS(server, port, request, sessionOptions);
|
|
this.probeTLS = new TLS(server, port, request, sessionOptions);
|
|
this.request = str2ba(request);
|
|
this.notary = notary;
|
|
this.pm = progressMonitor;
|
|
}
|
|
|
|
async start(){
|
|
await this.probeTLS.buildAndSendClientHello();
|
|
await this.probeTLS.receiveAndParseServerHello();
|
|
await this.probeTLS.sckt.close();
|
|
await this.twopc.preCompute();
|
|
await this.tls.buildAndSendClientHello();
|
|
const serverEcPubkey = await this.tls.receiveAndParseServerHello();
|
|
const serverX = serverEcPubkey.slice(1,33);
|
|
const serverY = serverEcPubkey.slice(33,65);
|
|
this.pm.update('last_stage', {'current': 3, 'total': 10});
|
|
const [pmsShare, cpubBytes] = await this.twopc.getECDHShare(serverX, serverY);
|
|
this.pm.update('last_stage', {'current': 4, 'total': 10});
|
|
|
|
await this.tls.buildClientKeyExchange(cpubBytes);
|
|
const [cr, sr] = await this.tls.getRandoms();
|
|
const [encCF, tagCF, vdCF] = await this.twopc.run(cr, sr, this.tls.getAllHandshakes(), pmsShare);
|
|
// Finished (0x14) with length 12 (0x0c)
|
|
this.tls.updateAllHandshakes(concatTA(new Uint8Array([0x14, 0x00, 0x00, 0x0c]), vdCF));
|
|
await this.tls.sendClientFinished(encCF, tagCF);
|
|
const encSF = await this.tls.receiveServerFinished();
|
|
await this.twopc.checkServerFinished(encSF, this.tls.getAllHandshakes());
|
|
this.pm.update('last_stage', {'current': 5, 'total': 10});
|
|
|
|
const encCountersForRequest = await this.twopc.getEncryptedCounters();
|
|
this.pm.update('last_stage', {'current': 9, 'total': 10});
|
|
const encRequestBlocks = this.encryptRequest(this.request, encCountersForRequest);
|
|
const gctrBlocks = await this.twopc.getGctrBlocks();
|
|
this.pm.update('last_stage', {'current': 10, 'total': 10});
|
|
const [ghashOutputs, ghashInputsBlob] = await this.twopc.getTagFromPowersOfH(encRequestBlocks);
|
|
await this.tls.buildAndSendRequest(gctrBlocks, ghashOutputs, encRequestBlocks);
|
|
const serverRecords = await this.tls.receiveServerResponse();
|
|
await this.tls.sckt.close();
|
|
|
|
const commitHash = await TLSNotarySession.computeCommitHash(serverRecords);
|
|
const [cwkShare, civShare, swkShare, sivShare] = this.twopc.getKeyShares();
|
|
const keyShareHash = await sha256(concatTA(cwkShare, civShare, swkShare, sivShare));
|
|
const pmsShareHash = await sha256(pmsShare);
|
|
const data5 = await this.twopc.send('commitHash', concatTA(
|
|
commitHash,
|
|
keyShareHash,
|
|
pmsShareHash));
|
|
this.twopc.destroy();
|
|
|
|
let o = 0; // offset
|
|
const signature = data5.slice(o, o+=64);
|
|
const notaryPMSShare = data5.slice(o, o+=32);
|
|
const notaryCwkShare = data5.slice(o, o+=16);
|
|
const notaryCivShare = data5.slice(o, o+=4);
|
|
const notarySwkShare = data5.slice(o, o+=16);
|
|
const notarySivShare = data5.slice(o, o+=4);
|
|
const timeBytes = data5.slice(o, o+=8);
|
|
assert(data5.length == o);
|
|
|
|
const [eKey, eValidFrom, eValidUntil, eSigByMasterKey] = this.twopc.getEphemeralKey();
|
|
|
|
// convert certPath to DER.
|
|
const certPath = this.tls.getCertPath();
|
|
var certs = [];
|
|
for (let cert of certPath){
|
|
certs.push(new Uint8Array(cert.toSchema(true).toBER(false)));
|
|
}
|
|
|
|
return {
|
|
'certificates': certs,
|
|
'notarization time': timeBytes,
|
|
'server RSA sig': this.tls.getRSAsignature(),
|
|
'server pubkey for ECDHE': serverEcPubkey,
|
|
'notary PMS share': notaryPMSShare,
|
|
'client PMS share': pmsShare,
|
|
'client random': cr,
|
|
'server random': sr,
|
|
'notary client_write_key share': notaryCwkShare,
|
|
'notary client_write_iv share': notaryCivShare,
|
|
'notary server_write_key share': notarySwkShare,
|
|
'notary server_write_iv share': notarySivShare,
|
|
'client client_write_key share': cwkShare,
|
|
'client client_write_iv share': civShare,
|
|
'client server_write_key share': swkShare,
|
|
'client server_write_iv share': sivShare,
|
|
'client request ciphertext': ghashInputsBlob,
|
|
'server response records': serverRecords,
|
|
'session signature': signature,
|
|
'ephemeral pubkey': eKey,
|
|
'ephemeral valid from': eValidFrom,
|
|
'ephemeral valid until': eValidUntil,
|
|
'ephemeral signed by master key': eSigByMasterKey,
|
|
};
|
|
|
|
}
|
|
|
|
|
|
// encryptRequest encrypts (i.e. XORs) the plaintext request with encrypted counter blocks.
|
|
// (This is how AES-GCM encryption works - first counter blocks are AES-ECB-encrypted,
|
|
// then encrypted counter blocks are XORes with the plaintext to get the ciphertext.)
|
|
encryptRequest(request, encCounterBlocks){
|
|
assert(Math.ceil(request.length/16) === encCounterBlocks.length);
|
|
const encRequestBlocks = [];
|
|
for (let i=0; i < encCounterBlocks.length; i++){
|
|
if (i == encCounterBlocks.length-1){
|
|
// last block, make sure arrays are of the same length before xoring
|
|
let lastBlockLen = this.request.length - i*16;
|
|
encRequestBlocks.push(xor(encCounterBlocks[i].slice(0,lastBlockLen), this.request.slice(i*16)));
|
|
}
|
|
else {
|
|
encRequestBlocks.push(xor(encCounterBlocks[i], this.request.slice(i*16, i*16+16)));
|
|
}
|
|
}
|
|
return encRequestBlocks;
|
|
}
|
|
|
|
|
|
// computeCommitHash computes a hash over all TLS records with MACs
|
|
static async computeCommitHash(encRecords){
|
|
return await sha256(concatTA(...encRecords));
|
|
}
|
|
}
|
|
|