@reclaimprotocol/tls
Version:
TLS 1.2/1.3 for any JavaScript Environment
73 lines (72 loc) • 3.11 kB
JavaScript
import { crypto } from "../crypto/index.js";
import { SUPPORTED_NAMED_CURVE_MAP, SUPPORTED_RECORD_TYPE_MAP, SUPPORTED_SIGNATURE_ALGS_MAP, TLS_PROTOCOL_VERSION_MAP } from "./constants.js";
import { areUint8ArraysEqual, concatenateUint8Arrays } from "./generics.js";
import { expectReadWithLength, packWith3ByteLength, packWithLength } from "./packets.js";
export async function packClientCurveKeyShare(publicKey) {
return concatenateUint8Arrays([
new Uint8Array([SUPPORTED_RECORD_TYPE_MAP['CLIENT_KEY_SHARE']]),
packWith3ByteLength(
// pack with 1 byte length
packWithLength(await crypto.exportKey(publicKey)).slice(1))
]);
}
export async function packClientRsaKeyShare(encPreMaster) {
return concatenateUint8Arrays([
new Uint8Array([SUPPORTED_RECORD_TYPE_MAP['CLIENT_KEY_SHARE']]),
packWith3ByteLength(packWithLength(encPreMaster))
]);
}
export async function processServerKeyShare(data) {
const type = read(1)[0];
if (type !== 0x03) {
throw new Error('expected "named_group" key share');
}
const curveTypeBytes = read(2);
const curveTypeEntry = Object.entries(SUPPORTED_NAMED_CURVE_MAP)
.find(([, { identifier }]) => areUint8ArraysEqual(identifier, curveTypeBytes));
if (!curveTypeEntry) {
throw new Error(`unsupported curve type: ${curveTypeBytes}`);
}
const publicKeyType = curveTypeEntry[0];
const publicKeyBytes = readWLength(1);
const publicKey = await crypto.importKey(curveTypeEntry[1].algorithm, publicKeyBytes, 'public');
const signatureTypeBytes = read(2);
const signatureTypeEntry = Object.entries(SUPPORTED_SIGNATURE_ALGS_MAP)
.find(([, { identifier }]) => areUint8ArraysEqual(identifier, signatureTypeBytes));
if (!signatureTypeEntry) {
throw new Error(`unsupported signature type: ${signatureTypeBytes}`);
}
const signatureAlgorithm = signatureTypeEntry[0];
const signatureBytes = readWLength(2);
return {
publicKeyType,
publicKey,
signatureAlgorithm,
signatureBytes,
};
function read(bytes) {
const result = data.slice(0, bytes);
data = data.slice(bytes);
return result;
}
function readWLength(bytesLength = 2) {
const content = expectReadWithLength(data, bytesLength);
data = data.slice(content.length + bytesLength);
return content;
}
}
export async function createRsaPreMasterSecret(serverCert, tlsVersion, rand = crypto.randomBytes(46)) {
const preMasterSecret = concatenateUint8Arrays([
TLS_PROTOCOL_VERSION_MAP[tlsVersion],
rand
]);
const servPubKey = serverCert.getPublicKey();
if (servPubKey.algorithm !== 'RSASSA-PKCS1-v1_5') {
throw new Error(`expected RSASSA-PKCS1-v1_5 cert, got ${servPubKey.algorithm}`);
}
const publicKey = await crypto
.importKey('RSA-PCKS1_5', servPubKey.buffer, 'public');
const rslt = await crypto
.asymmetricEncrypt('RSA-PCKS1_5', { data: preMasterSecret, publicKey });
return { preMasterSecret, encrypted: rslt };
}