1 /**
2 * TLS Record Handling
3 * 
4 * Copyright:
5 * (C) 2004-2012,2014 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.tls.record;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_TLS):
15 package:
16 
17 import botan.libstate.libstate;
18 import botan.tls.magic;
19 import botan.tls.version_;
20 import botan.tls.seq_numbers;
21 import botan.tls.session_key;
22 import botan.tls.ciphersuite;
23 import botan.tls.exceptn;
24 import botan.modes.aead.aead;
25 import botan.mac.mac;
26 import botan.algo_factory.algo_factory;
27 import botan.rng.rng;
28 import botan.block.block_cipher;
29 import botan.stream.stream_cipher;
30 import botan.utils.rounding;
31 import botan.utils.xor_buf;
32 import botan.utils.loadstor;
33 import botan.utils.types;
34 import std.algorithm;
35 import std.datetime;
36 import memutils.refcounted;
37 
38 //alias ConnectionCipherState = RefCounted!ConnectionCipherStateImpl;
39 
40 /**
41 * TLS Cipher State
42 */
43 final class ConnectionCipherState
44 {
45 public:
46     /**
47     * Initialize a new cipher state
48     */
49     this()(TLSProtocolVersion _version, 
50            ConnectionSide side, bool our_side, 
51            in TLSCiphersuite suite, auto const ref TLSSessionKeys keys) 
52     {
53         m_start_time = Clock.currTime(UTC());
54         m_implicit_nonce_size = suite.nonceBytesFromRecord();
55         m_explicit_nonce_size = suite.nonceBytesFromHandshake();
56         m_is_ssl3 = _version == TLSProtocolVersion.SSL_V3;
57         SymmetricKey mac_key, cipher_key;
58         InitializationVector iv;
59         
60         if (side == CLIENT)
61         {
62             cipher_key = keys.clientCipherKey().dup;
63             iv = keys.clientIv().dup;
64             mac_key = keys.clientMacKey().dup;
65         }
66         else
67         {
68             cipher_key = keys.serverCipherKey().dup;
69             iv = keys.serverIv().dup;
70             mac_key = keys.serverMacKey().dup;
71         }
72         
73         const string cipher_algo = suite.cipherAlgo();
74         const string mac_algo = suite.macAlgo();
75         
76         if (AEADMode aead = getAead(cipher_algo, our_side ? ENCRYPTION : DECRYPTION))
77         {
78             m_aead = aead;
79             m_aead.setKey(cipher_key ~ mac_key);
80             
81             assert(iv.length == nonceBytesFromHandshake(), "Matching nonce sizes");
82             m_nonce = iv.bitsOf();
83 			assert(nonceBytesFromRecord() == 0 || nonceBytesFromRecord() == 8,
84 				"Ciphersuite uses implemented IV length");
85             m_nonce.resize(m_nonce.length + 8);
86             return;
87         }
88         
89         AlgorithmFactory af = globalState().algorithmFactory();
90         
91         if (const BlockCipher bc = af.prototypeBlockCipher(cipher_algo))
92         {
93             m_block_cipher = bc.clone();
94             m_block_cipher.setKey(cipher_key);
95             m_block_cipher_cbc_state = iv.bitsOf();
96             m_block_size = bc.blockSize;
97             
98             if (_version.supportsExplicitCbcIvs())
99                 m_iv_size = m_block_size;
100         }
101         else if (const StreamCipher sc = af.prototypeStreamCipher(cipher_algo))
102         {
103             m_stream_cipher = sc.clone();
104             m_stream_cipher.setKey(cipher_key);
105         }
106         else
107             throw new InvalidArgument("Unknown TLS cipher " ~ cipher_algo);
108         
109         if (_version == TLSProtocolVersion.SSL_V3)
110             m_mac = af.makeMac("SSL3-MAC(" ~ mac_algo ~ ")");
111         else
112             m_mac = af.makeMac("HMAC(" ~ mac_algo ~ ")");
113         
114         m_mac.setKey(mac_key);
115     }
116 
117     AEADMode aead() { return *m_aead; }
118 
119     ref const(SecureVector!ubyte) aeadNonce(ulong seq)
120     {
121 		storeBigEndian(seq, &m_nonce[nonceBytesFromHandshake()]);
122         return m_nonce;
123     }
124 
125     ref const(SecureVector!ubyte) aeadNonce(const(ubyte)* record, size_t record_len, ulong seq)
126     {
127 
128 		if(nonceBytesFromRecord())
129 		{
130 			if(record_len < nonceBytesFromRecord())
131 				throw new DecodingError("Invalid AEAD packet too short to be valid");
132 			copyMem(&m_nonce[nonceBytesFromHandshake()], record, nonceBytesFromRecord());
133 		}
134 		else
135 		{
136 			/*
137 		        nonce_len == 0 is assumed to mean no nonce in the message but
138 		        instead the AEAD uses the seq number in network order.
139 		   */
140 			storeBigEndian(seq, &m_nonce[nonceBytesFromHandshake()]);
141 		}
142         return m_nonce;
143     }
144 
145 
146     ref const(SecureVector!ubyte) formatAd(ulong seq, ubyte msg_type, TLSProtocolVersion _version, ushort msg_length)
147     {
148         m_ad.clear();
149         foreach (size_t i; 0 .. 8)
150             m_ad.pushBack(get_byte(i, seq));
151         m_ad.pushBack(msg_type);
152         
153         if (_version != TLSProtocolVersion.SSL_V3)
154         {
155             m_ad.pushBack(_version.majorVersion());
156             m_ad.pushBack(_version.minorVersion());
157         }
158         
159         m_ad.pushBack(get_byte(0, msg_length));
160         m_ad.pushBack(get_byte(1, msg_length));
161         
162         return m_ad;
163     }
164 
165     BlockCipher blockCipher() { return *m_block_cipher; }
166 
167     StreamCipher streamCipher() { return *m_stream_cipher; }
168 
169     MessageAuthenticationCode mac() { return *m_mac; }
170 
171     ref SecureVector!ubyte cbcState() { return m_block_cipher_cbc_state; }
172 
173     size_t blockSize() const { return m_block_size; }
174 
175     size_t macSize() const { return m_mac.outputLength; }
176 
177     size_t ivSize() const { return m_iv_size; }
178 
179     size_t nonceBytesFromRecord() const { return m_implicit_nonce_size; }
180 
181     size_t nonceBytesFromHandshake() const { return m_explicit_nonce_size; }
182 
183     bool cipherPaddingSingleByte() const { return m_is_ssl3; }
184 
185     bool cbcWithoutExplicitIv() const
186     { return (m_block_size > 0) && (m_iv_size == 0); }
187 
188     Duration age() const
189     {
190         return Clock.currTime(UTC()) - m_start_time;
191     }
192 
193 private:
194     SysTime m_start_time;
195     Unique!BlockCipher m_block_cipher;
196     SecureVector!ubyte m_block_cipher_cbc_state;
197     Unique!StreamCipher m_stream_cipher;
198     Unique!MessageAuthenticationCode m_mac;
199 
200     Unique!AEADMode m_aead;
201     SecureVector!ubyte m_nonce, m_ad;
202 
203     size_t m_block_size;
204     size_t m_explicit_nonce_size;
205     size_t m_implicit_nonce_size;
206     size_t m_iv_size;
207     bool m_is_ssl3;
208 }
209 
210 /**
211 * Create a TLS record
212 * Params:
213 *  output = the output record is placed here
214 *  msg_type = is the type of the message (handshake, alert, ...)
215 *  msg = is the plaintext message
216 *  msg_length = is the length of msg
217 *  _version = is the protocol version
218 *  seq = is the sequence number
219 *  cipherstate = is the writing cipher state
220 *  rng = is a random number generator
221 * Returns: number of bytes written to write_buffer
222 */
223 void writeRecord(ref SecureVector!ubyte output,
224                  ubyte msg_type, const(ubyte)* msg, size_t msg_length,
225                  TLSProtocolVersion _version,
226                  ulong seq,
227                  ConnectionCipherState cs,
228                  RandomNumberGenerator rng)
229 {
230     output.clear();
231     
232     output.pushBack(msg_type);
233     output.pushBack(_version.majorVersion());
234     output.pushBack(_version.minorVersion());
235     
236     if (_version.isDatagramProtocol())
237     {
238         foreach (size_t i; 0 .. 8)
239             output.pushBack(get_byte(i, seq));
240     }
241     
242     if (!cs) // initial unencrypted handshake records
243     {
244         output.pushBack(get_byte(0, cast(ushort) msg_length));
245         output.pushBack(get_byte(1, cast(ushort) msg_length));
246         
247         output ~= msg[0 .. msg_length];
248         
249         return;
250     }
251     
252     if (AEADMode aead = cs.aead())
253     {
254         const size_t ctext_size = aead.outputLength(msg_length);
255         
256         const(SecureVector!ubyte)* nonce = &cs.aeadNonce(seq);
257         
258         // wrong if start returns something
259 		const size_t rec_size = ctext_size + cs.nonceBytesFromRecord();
260 
261         assert(rec_size <= 0xFFFF, "Ciphertext length fits in field");
262         
263         output.pushBack(get_byte!ushort(0, cast(ushort) rec_size));
264         output.pushBack(get_byte!ushort(1, cast(ushort) rec_size));
265         
266         aead.setAssociatedDataVec(cs.formatAd(seq, msg_type, _version, cast(ushort) msg_length));
267         
268 		output ~= nonce.ptr[cs.nonceBytesFromHandshake() .. cs.nonceBytesFromHandshake() + cs.nonceBytesFromRecord()];
269 		auto start_vec = aead.start(*nonce);
270 		assert(start_vec.empty, "AEAD doesn't return anything from start");
271         
272         const size_t offset = output.length;
273         output ~= msg[0 .. msg_length];
274         aead.finish(output, offset);
275         
276         assert(output.length == offset + ctext_size, "Expected size");
277         
278         assert(output.length < MAX_CIPHERTEXT_SIZE,
279                      "Produced ciphertext larger than protocol allows");
280         return;
281     }
282     
283     cs.mac().update(cs.formatAd(seq, msg_type, _version, cast(ushort) msg_length));
284     
285     cs.mac().update(msg, msg_length);
286     
287     const size_t block_size = cs.blockSize();
288     const size_t iv_size = cs.ivSize();
289     const size_t mac_size = cs.macSize();
290     
291     const size_t buf_size = roundUp(iv_size + msg_length + mac_size + (block_size ? 1 : 0), block_size);
292     
293     if (buf_size > MAX_CIPHERTEXT_SIZE)
294         throw new InternalError("Output record is larger than allowed by protocol");
295     
296     output.pushBack(get_byte!ushort(0, cast(ushort) buf_size));
297     output.pushBack(get_byte!ushort(1, cast(ushort) buf_size));
298     
299     const size_t header_size = output.length;
300     
301     if (iv_size)
302     {
303         output.resize(output.length + iv_size);
304         rng.randomize(&output[$- iv_size], iv_size);
305     }
306     output ~= msg[0 .. msg_length];
307     output.resize(output.length + mac_size);
308     cs.mac().flushInto(&output[output.length - mac_size]);
309     
310     if (block_size)
311     {
312         const size_t pad_val = buf_size - (iv_size + msg_length + mac_size + 1);
313         
314         foreach (size_t i; 0 .. (pad_val + 1))
315             output.pushBack(pad_val);
316     }
317     
318     if (buf_size > MAX_CIPHERTEXT_SIZE)
319         throw new InternalError("Produced ciphertext larger than protocol allows");
320     
321     assert(buf_size + header_size == output.length, "Output buffer is sized properly");
322     
323     if (StreamCipher sc = cs.streamCipher())
324     {
325         sc.cipher1(&output[header_size], buf_size);
326     }
327     else if (BlockCipher bc = cs.blockCipher())
328     {
329         SecureVector!ubyte* cbc_state = &cs.cbcState();
330         
331         assert(buf_size % block_size == 0,
332                      "Buffer is an even multiple of block size");
333         
334         ubyte* buf = &output[header_size];
335         
336         const size_t blocks = buf_size / block_size;
337         
338         xorBuf(buf, cbc_state.ptr, block_size);
339         bc.encrypt(buf);
340         
341         for (size_t i = 1; i < blocks; ++i)
342         {
343             xorBuf(&buf[block_size*i], &buf[block_size*(i-1)], block_size);
344             bc.encrypt(&buf[block_size*i]);
345         }
346         
347         (*cbc_state)[] = buf[block_size*(blocks-1) .. block_size*blocks];
348     }
349     else
350         throw new InternalError("NULL cipher not supported");
351 }
352 
353 /**
354 * Decode a TLS record
355 * Returns: zero if full message, else number of bytes still needed
356 */
357 size_t readTLSRecord(ref SecureVector!ubyte readbuf,
358                      const(ubyte)* input, size_t input_sz,
359                      ref size_t consumed,
360                      ref SecureVector!ubyte record,
361                      ref ulong record_sequence,
362                      ref TLSProtocolVersion record_version,
363                      ref RecordType record_type,
364                      ConnectionSequenceNumbers sequence_numbers,
365                      const(ConnectionCipherState) delegate(ushort) const get_cipherstate)
366 {
367     consumed = 0;
368     if (readbuf.length < TLS_HEADER_SIZE) // header incomplete?
369     {
370         if (size_t needed = fillBufferTo(readbuf, input, input_sz, consumed, TLS_HEADER_SIZE))
371             return needed;
372             
373             assert(readbuf.length == TLS_HEADER_SIZE, "Have an entire header");
374     }
375     
376     // Possible SSLv2 format client hello
377     if (!sequence_numbers && (readbuf[0] & 0x80) && (readbuf[2] == 1))
378     {
379         if (readbuf[3] == 0 && readbuf[4] == 2)
380             throw new TLSException(TLSAlert.PROTOCOL_VERSION, "TLSClient claims to only support SSLv2, rejecting");
381         
382         if (readbuf[3] >= 3) // SSLv2 mapped TLS hello, then?
383         {
384             const size_t record_len = make_ushort(readbuf[0], readbuf[1]) & 0x7FFF;
385             
386             if (size_t needed = fillBufferTo(readbuf,
387                                              input, input_sz, consumed,
388                                              record_len + 2))
389                 return needed;
390             
391             assert(readbuf.length == (record_len + 2), "Have the entire SSLv2 hello");
392             
393             // Fake v3-style handshake message wrapper
394             record_version = TLSProtocolVersion(TLSProtocolVersion.TLS_V10);
395             record_sequence = 0;
396             record_type = HANDSHAKE;
397             
398             record.resize(4 + readbuf.length - 2);
399             
400             record[0] = CLIENT_HELLO_SSLV2;
401             record[1] = 0;
402             record[2] = readbuf[0] & 0x7F;
403             record[3] = readbuf[1];
404             copyMem(&record[4], &readbuf[2], readbuf.length - 2);
405             
406             readbuf.clear();
407             return 0;
408         }
409     }
410 
411     record_version = TLSProtocolVersion(readbuf[1], readbuf[2]);
412 
413     assert(!record_version.isDatagramProtocol(), "Expected TLS");
414 
415     const size_t record_len = make_ushort(readbuf[TLS_HEADER_SIZE-2], readbuf[TLS_HEADER_SIZE-1]);
416     if (record_len > MAX_CIPHERTEXT_SIZE)
417 		throw new TLSException(TLSAlert.RECORD_OVERFLOW, "Received a record that exceeds maximum size: " ~ cast(string)readbuf[]);
418     
419 	if(record_len == 0)
420 		throw new TLSException(TLSAlert.DECODE_ERROR, "Received a completely empty record");
421 
422     if (size_t needed = fillBufferTo(readbuf, input, input_sz, consumed, TLS_HEADER_SIZE + record_len))
423         return needed;
424     
425     assert(cast(size_t)(TLS_HEADER_SIZE) + record_len == readbuf.length, "Have the full record");
426     
427     record_type = cast(RecordType)(readbuf[0]);
428     
429     ushort epoch = 0;
430 
431     if (sequence_numbers)
432     {
433         record_sequence = sequence_numbers.nextReadSequence();
434         epoch = sequence_numbers.currentReadEpoch();
435     }
436     else
437     {
438         // server initial handshake case
439         record_sequence = 0;
440         epoch = 0;
441     }
442 
443     ubyte* record_contents = readbuf.ptr + TLS_HEADER_SIZE;
444 	
445     if (epoch == 0) // Unencrypted initial handshake
446     {
447         record[] = readbuf.ptr[TLS_HEADER_SIZE .. TLS_HEADER_SIZE + record_len];
448         readbuf.clear();
449         return 0; // got a full record
450     }
451     // Otherwise, decrypt, check MAC, return plaintext
452 	auto ccs = get_cipherstate(epoch);
453     ConnectionCipherState cs = cast(ConnectionCipherState) ccs;
454 
455 	assert(cs, "Have cipherstate for this epoch");
456     decryptRecord(record,
457                   record_contents,
458                   record_len,
459                   record_sequence,
460                   record_version,
461                   record_type,
462                   cs);
463     
464     if (sequence_numbers)
465         sequence_numbers.readAccept(record_sequence);
466     readbuf.clear();
467     return 0;
468 }
469 
470 size_t readDTLSRecord(ref SecureVector!ubyte readbuf,
471                       const(ubyte)* input, size_t input_sz,
472                       ref size_t consumed,
473                       ref SecureVector!ubyte record,
474                       ref ulong record_sequence,
475                       ref TLSProtocolVersion record_version,
476                       ref RecordType record_type,
477                       ConnectionSequenceNumbers sequence_numbers,
478                       const(ConnectionCipherState) delegate(ushort) const get_cipherstate)
479 {
480     consumed = 0;
481     if (readbuf.length < DTLS_HEADER_SIZE) // header incomplete?
482     {
483         if (fillBufferTo(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE))
484         {
485             readbuf.clear();
486             return 0;
487         }
488 
489         assert(readbuf.length == DTLS_HEADER_SIZE, "Have an entire header");
490     }
491 
492     record_version = TLSProtocolVersion(readbuf[1], readbuf[2]);
493     
494     assert(record_version.isDatagramProtocol(), "Expected DTLS");
495     
496     const size_t record_len = make_ushort(readbuf[DTLS_HEADER_SIZE-2], readbuf[DTLS_HEADER_SIZE-1]);
497 
498 	// Invalid packet:
499 	if(record_len == 0 || record_len > MAX_CIPHERTEXT_SIZE)
500 	{
501 		readbuf.clear();
502 		return 0;
503 	}
504 
505     if (fillBufferTo(readbuf, input, input_sz, consumed, DTLS_HEADER_SIZE + record_len))
506     {
507         // Truncated packet?
508         readbuf.clear();
509         return 0; // wrong for DTLS?
510     }
511 
512     assert(cast(size_t)(DTLS_HEADER_SIZE) + record_len == readbuf.length, "Have the full record");
513     
514     record_type = cast(RecordType)(readbuf[0]);
515     
516     ushort epoch = 0;
517 
518     record_sequence = loadBigEndian!ulong(&readbuf[3], 0);
519     epoch = (record_sequence >> 48);
520     
521     if (sequence_numbers && sequence_numbers.alreadySeen(record_sequence)) 
522     {
523         readbuf.clear();
524         return 0;
525     }
526     
527     ubyte* record_contents = &readbuf[DTLS_HEADER_SIZE];
528     
529     if (epoch == 0) // Unencrypted initial handshake
530     {
531         record[] = readbuf.ptr[DTLS_HEADER_SIZE .. DTLS_HEADER_SIZE + record_len];
532         readbuf.clear();
533         return 0; // got a full record
534     }
535     try
536     {
537         // Otherwise, decrypt, check MAC, return plaintext
538         auto ccs = get_cipherstate(epoch);
539         ConnectionCipherState cs = cast(ConnectionCipherState) ccs;
540 
541         assert(cs, "Have cipherstate for this epoch");
542         
543         decryptRecord(record,
544                       record_contents,
545                       record_len,
546                       record_sequence,
547                       record_version,
548                       record_type,
549                       cs);
550     } catch (Exception e) {
551         readbuf.clear();
552         record_type = NO_RECORD;
553         return 0;
554     }
555 
556     if (sequence_numbers)
557         sequence_numbers.readAccept(record_sequence);
558     
559     readbuf.clear();
560     return 0;
561 }
562 
563 size_t readRecord(ref SecureVector!ubyte readbuf,
564                   const(ubyte)* input, size_t input_sz,
565                   bool is_datagram,
566                   ref size_t consumed,
567                   ref SecureVector!ubyte record,
568                   ref ulong record_sequence,
569                   ref TLSProtocolVersion record_version,
570                   ref RecordType record_type,
571                   ConnectionSequenceNumbers sequence_numbers,
572                   const(ConnectionCipherState) delegate(ushort) const get_cipherstate)
573 {
574     if (is_datagram)
575         return readDTLSRecord(readbuf, input, input_sz, consumed, record, record_sequence, record_version,
576                               record_type, sequence_numbers, get_cipherstate);
577     else
578         return readTLSRecord(readbuf, input, input_sz, consumed, record, record_sequence, record_version,
579                              record_type, sequence_numbers, get_cipherstate);
580 }
581 
582 private:
583                     
584 size_t fillBufferTo(ref SecureVector!ubyte readbuf, ref const(ubyte)* input, 
585                     ref size_t input_size, ref size_t input_consumed, 
586                     size_t desired)
587 {
588     if (readbuf.length >= desired)
589         return 0; // already have it
590     
591     const size_t taken = std.algorithm.min(input_size, desired - readbuf.length);
592     
593     readbuf ~= input[0 .. taken];
594     input_consumed += taken;
595     input_size -= taken;
596     input += taken;
597     
598     return (desired - readbuf.length); // how many bytes do we still need?
599 }
600 
601 /*
602 * Checks the TLS padding. Returns 0 if the padding is invalid (we
603 * count the padding_length field as part of the padding size so a
604 * valid padding will always be at least one ubyte long), or the length
605 * of the padding otherwise. This is actually padding_length + 1
606 * because both the padding and padding_length fields are padding from
607 * our perspective.
608 *
609 * Returning 0 in the error case should ensure the MAC check will fail.
610 * This approach is suggested in section 6.2.3.2 of RFC 5246.
611 *
612 * Also returns 0 if block_size == 0, so can be safely called with a
613 * stream cipher in use.
614 *
615 * @fixme This should run in constant time
616 */
617 size_t tlsPaddingCheck(bool sslv3_padding, size_t block_size, const(ubyte)* record, in size_t record_len)
618 {
619     const size_t padding_length = record[(record_len-1)];
620 
621     if (padding_length >= record_len)
622         return 0;
623     
624     /*
625     * SSL v3 requires that the padding be less than the block size
626     * but not does specify the value of the padding bytes.
627     */
628     if (sslv3_padding)
629     {
630         if (padding_length > 0 && padding_length < block_size)
631             return (padding_length + 1);
632         else
633             return 0;
634     }
635     
636     /*
637     * TLS v1.0 and up require all the padding bytes be the same value
638     * and allows up to 255 bytes.
639     */
640     const size_t pad_start = record_len - padding_length - 1;
641     
642     size_t cmp = 0;
643     
644     foreach (size_t i; 0 .. padding_length)
645         cmp += record[pad_start + i] ^ padding_length;
646     
647     return cmp ? 0 : padding_length + 1;
648 }
649 
650 void cbcDecryptRecord(const(ubyte)* record_contents, size_t record_len, 
651                       ConnectionCipherState cs, BlockCipher bc)
652 {
653     const size_t block_size = cs.blockSize();
654     
655     assert(record_len % block_size == 0, "Buffer is an even multiple of block size");
656     
657     const size_t blocks = record_len / block_size;
658     
659     assert(blocks >= 1, "At least one ciphertext block");
660     
661     ubyte* buf = cast(ubyte*)record_contents;
662     
663     SecureVector!ubyte last_ciphertext = SecureVector!ubyte(block_size);
664     copyMem(last_ciphertext.ptr, buf, block_size);
665     
666     bc.decrypt(buf);
667     xorBuf(buf, &cs.cbcState()[0], block_size);
668     
669     SecureVector!ubyte last_ciphertext2;
670     
671     for (size_t i = 1; i < blocks; ++i)
672     {
673         last_ciphertext2[] = buf[block_size*i .. block_size*(i+1)];
674         bc.decrypt(&buf[block_size*i]);
675         xorBuf(&buf[block_size*i], last_ciphertext.ptr, block_size);
676         std.algorithm.swap(last_ciphertext, last_ciphertext2);
677     }
678     
679     cs.cbcState() = last_ciphertext;
680 }
681 
682 void decryptRecord(ref SecureVector!ubyte output,
683                    const(ubyte)* record_contents, size_t record_len,
684                    ulong record_sequence,
685                    TLSProtocolVersion record_version,
686                    RecordType record_type,
687                    ConnectionCipherState cs)
688 {
689     if (AEADMode aead = cs.aead())
690     {
691 		const(SecureVector!ubyte)* nonce = &cs.aeadNonce(record_contents, record_len, record_sequence);
692 		const(ubyte)* msg = &record_contents[cs.nonceBytesFromRecord()];
693 		const size_t msg_length = record_len - cs.nonceBytesFromRecord();
694 
695         const size_t ptext_size = aead.outputLength(msg_length);
696         
697         aead.setAssociatedDataVec(cs.formatAd(record_sequence, record_type, record_version, cast(ushort) ptext_size));
698         
699         output ~= aead.start(*nonce);
700         
701         const size_t offset = output.length;
702         output ~= msg[0 .. msg_length];
703         aead.finish(output, offset);
704         
705         assert(output.length == ptext_size + offset, "Produced expected size");
706     }
707     else
708     {
709         // GenericBlockCipher / GenericStreamCipher case
710         
711         bool padding_bad = false;
712         size_t pad_size = 0;
713         
714         if (StreamCipher sc = cs.streamCipher())
715         {
716             sc.cipher1(record_contents, record_len);
717             // no padding to check or remove
718         }
719         else if (BlockCipher bc = cs.blockCipher())
720         {
721             cbcDecryptRecord(record_contents, record_len, cs, bc);
722             
723             pad_size = tlsPaddingCheck(cs.cipherPaddingSingleByte(),
724                                          cs.blockSize(),
725                                          record_contents, record_len);
726             
727             padding_bad = (pad_size == 0);
728         }
729         else
730         {
731             throw new InternalError("No cipher state set but needed to decrypt");
732         }
733         
734         const size_t mac_size = cs.macSize();
735         const size_t iv_size = cs.ivSize();
736         
737         const size_t mac_pad_iv_size = mac_size + pad_size + iv_size;
738         
739         if (record_len < mac_pad_iv_size)
740             throw new DecodingError("Record sent with invalid length");
741         
742         const(ubyte)* plaintext_block = &record_contents[iv_size];
743         const ushort plaintext_length = cast(ushort)(record_len - mac_pad_iv_size);
744         
745         cs.mac().update(cs.formatAd(record_sequence, record_type, record_version, plaintext_length));
746         
747         cs.mac().update(plaintext_block, plaintext_length);
748         
749         Vector!ubyte mac_buf = Vector!ubyte(mac_size);
750         cs.mac().flushInto(mac_buf.ptr);
751         
752         const size_t mac_offset = record_len - (mac_size + pad_size);
753         
754         const bool mac_bad = !sameMem(&record_contents[mac_offset], mac_buf.ptr, mac_size);
755         
756         if (mac_bad || padding_bad)
757             throw new TLSException(TLSAlert.BAD_RECORD_MAC, "Message authentication failure");
758         
759         output[] = plaintext_block[0 .. plaintext_length];
760     }
761 }