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