@reclaimprotocol/tls
Version:
WebCrypto Based Cross Platform TLS
75 lines (74 loc) • 2.68 kB
JavaScript
import { SUPPORTED_EXTENSION_MAP, SUPPORTED_EXTENSIONS, SUPPORTED_NAMED_CURVE_MAP, SUPPORTED_NAMED_CURVES } from "./constants.js";
import { areUint8ArraysEqual, getTlsVersionFromBytes, uint8ArrayToStr } from "./generics.js";
import { expectReadWithLength } from "./packets.js";
/**
* Parse a length-encoded list of extensions
* sent by the server
*/
export function parseServerExtensions(data) {
return parseExtensions(data, {
'ALPN': (extData) => {
const data = expectReadWithLength(extData);
const alpnBytes = expectReadWithLength(data, 1);
return uint8ArrayToStr(alpnBytes);
},
'SUPPORTED_VERSIONS': getTlsVersionFromBytes,
'PRE_SHARED_KEY': () => ({ supported: true }),
'KEY_SHARE': (extData) => {
const typeBytes = extData.slice(0, 2);
const type = SUPPORTED_NAMED_CURVES
.find(k => areUint8ArraysEqual(SUPPORTED_NAMED_CURVE_MAP[k].identifier, typeBytes));
if (!type) {
throw new Error(`Unsupported key type '${typeBytes}'`);
}
const publicKey = expectReadWithLength(extData.slice(2));
return { type, publicKey };
}
});
}
/**
* Parse a length-encoded list of extensions
* sent by the client
*/
export function parseClientExtensions(data) {
return parseExtensions(data, {
'SERVER_NAME': (extData) => {
extData = expectReadWithLength(extData);
const byte = extData[0];
extData = extData.slice(1);
const serverNameBytes = expectReadWithLength(extData);
return {
type: byte,
serverName: uint8ArrayToStr(serverNameBytes)
};
}
});
}
function parseExtensions(data, parsers) {
data = readWLength(2);
const map = {};
const seenExtensions = new Set();
while (data.length) {
const typeByte = read(2)[1];
const extData = readWLength(2);
const type = SUPPORTED_EXTENSIONS
.find(k => SUPPORTED_EXTENSION_MAP[k] === typeByte);
if (seenExtensions.has(typeByte)) {
throw new Error(`Duplicate extension '${type}' (${typeByte})`);
}
if (type && type in parsers) {
map[type] = parsers[type](extData);
}
}
return map;
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;
}
}