in src/recordlayer.js [306:345]
async _readEncryptedRecord(type, length, buf) {
if (length > MAX_ENCRYPTED_RECORD_SIZE) {
throw new TLSError(ALERT_DESCRIPTION.RECORD_OVERFLOW);
}
// The outer type for encrypted records is always APPLICATION_DATA.
if (type !== RECORD_TYPE.APPLICATION_DATA) {
throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
}
// Decrypt and decode the contained `TLSInnerPlaintext` struct:
//
// struct {
// opaque content[TLSPlaintext.length];
// ContentType type;
// uint8 zeros[length_of_padding];
// } TLSInnerPlaintext;
//
// The additional data for the decryption is the `TLSCiphertext` record
// header, which is a fixed size and immediately prior to current buffer position.
buf.incr(-RECORD_HEADER_SIZE);
const additionalData = buf.readBytes(RECORD_HEADER_SIZE);
const ciphertext = buf.readBytes(length);
const paddedPlaintext = await this._recvDecryptState.decrypt(ciphertext, additionalData);
// We have to scan backwards over the zero padding at the end of the struct
// in order to find the non-zero `type` byte.
let i;
for (i = paddedPlaintext.byteLength - 1; i >= 0; i--) {
if (paddedPlaintext[i] !== 0) {
break;
}
}
if (i < 0) {
throw new TLSError(ALERT_DESCRIPTION.UNEXPECTED_MESSAGE);
}
type = paddedPlaintext[i];
// `change_cipher_spec` records must always be plaintext.
if (type === RECORD_TYPE.CHANGE_CIPHER_SPEC) {
throw new TLSError(ALERT_DESCRIPTION.DECODE_ERROR);
}
return [type, paddedPlaintext.slice(0, i)];
}