File: State.pike

package info (click to toggle)
pike8.0 8.0.1956-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 60,580 kB
  • sloc: ansic: 259,734; xml: 36,320; makefile: 3,748; sh: 1,713; cpp: 1,349; awk: 1,036; lisp: 655; javascript: 468; asm: 242; objc: 240; pascal: 157; sed: 34
file content (386 lines) | stat: -rw-r--r-- 11,959 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
#pike __REAL_VERSION__
// #pragma strict_types
#require constant(SSL.Cipher)

//! The state object handles a one-way stream of packets, and operates
//! in either decryption or encryption mode. A connection switches
//! from one set of state objects to another, one or more times during
//! its lifetime.

import ".";
import Constants;

function(int, int, string|void: Alert) alert;

protected void create(Connection con)
{
  session = con->session;
  alert = con->alert;
}

//! Information about the used algorithms.
Session session;

//! Message Authentication Code
Cipher.MACAlgorithm mac;

//! Encryption or decryption object.
Cipher.CipherAlgorithm crypt;

function(string(8bit),int(1bit)|void:string(8bit)) compress;

//! 64-bit sequence number.
int seq_num = 0;    /* Bignum, values 0, .. 2^64-1 are valid */

//! TLS IV prefix length.
int tls_iv;

//! TLS 1.2 IV salt.
//! This is used as a prefix for the IV for the AEAD cipher algorithms.
string salt;

//! Destructively decrypts a packet (including inflating and MAC-verification,
//! if needed). On success, returns the decrypted packet. On failure,
//! returns an alert packet. These cases are distinguished by looking
//! at the is_alert attribute of the returned packet.
Alert|Packet decrypt_packet(Packet packet)
{
  /* NOTE: TLS 1.1 recommends performing the hash check before
   *       sending the alerts to protect against timing attacks.
   *
   *       This is also needed to alleviate the "Lucky Thirteen" attack.
   *
   *       We thus delay sending of any alerts to the end of the
   *       function, and attempt to make the same amount of work
   *       even if we have already detected a failure.
   */
  Alert fail;
  ProtocolVersion version = packet->protocol_version;
  string data = packet->fragment;

#ifdef SSL3_DEBUG_CRYPT
  werror("SSL.State->decrypt_packet (3.%d, type: %d): data = %O\n",
	 version & 0xff, packet->content_type, data);
#endif

  int hmac_size = mac && (session->truncated_hmac ? 10 :
			  session->cipher_spec?->hash_size);
  int padding;

  if (hmac_size && session->encrypt_then_mac) {
    string(8bit) digest = data[<hmac_size-1..];
    data = data[..<hmac_size];

    // Set data without HMAC. This never returns an Alert as the data
    // is smaller.
    packet->set_encrypted(data);

    if (mac->hash_packet(packet, seq_num)[..hmac_size-1] !=
	digest) {
      // Bad digest.
#ifdef SSL3_DEBUG
      werror("Failed MAC-verification!!\n");
#endif
#ifdef SSL3_DEBUG_CRYPT
      werror("Expected digest: %O\n"
	     "Calculated digest: %O\n"
	     "Seqence number: %O\n",
	     digest,
	     mac->hash_packet(packet, seq_num)[..hmac_size-1],
	     seq_num);
#endif
      return alert(ALERT_fatal, ALERT_bad_record_mac,
		   "Bad MAC-verification.\n");
    }
    seq_num++;
    hmac_size = 0;
  }

  if (crypt)
  {
#ifdef SSL3_DEBUG_CRYPT
    werror("SSL.State: Trying decrypt...\n");
    //    werror("SSL.State: The encrypted packet is:%O\n", data);
    werror("sizeof of the encrypted packet is:"+sizeof(data)+"\n");
#endif

    string msg = data;

    if (!msg)
    {
      // packet->fragment is zero when the packet format is illegal,
      // so returning early is safe. We should never get to this
      // though, as decrypt_packet isn't called from connection if the
      // fragment isn't successfully parsed.
      return alert(ALERT_fatal, ALERT_unexpected_message,
		   "SSL.State: Failed to get fragment.\n");
    }

    switch(session->cipher_spec->cipher_type) {
    case CIPHER_stream:

      // If data is too small, we can safely abort early.
      if( sizeof(msg) < hmac_size+1 )
        return alert(ALERT_fatal, ALERT_unexpected_message,
                     "SSL.State: Too short message.\n");

      msg = crypt->crypt(msg);
      break;

    case CIPHER_block:

      // If data is too small or doesn't match block size, we can
      // safely abort early.
      if( sizeof(msg) < hmac_size+1 || sizeof(msg) % crypt->block_size() )
        return alert(ALERT_fatal, ALERT_unexpected_message,
                     "SSL.State: Too short message.\n");

      if(version == PROTOCOL_SSL_3_0) {
        // crypt->unpad() performs decrypt.
        if (catch { msg = crypt->unpad(msg, Crypto.PAD_SSL); })
          fail = alert(ALERT_fatal, ALERT_unexpected_message,
                       "SSL.State: Invalid padding.\n");
      } else if (version >= PROTOCOL_TLS_1_0) {

#ifdef SSL3_DEBUG_CRYPT
        werror("SSL.State: Decrypted message: %O.\n", msg);
#endif
        if (catch { msg = crypt->unpad(msg, Crypto.PAD_TLS); }) {
          fail = alert(ALERT_fatal, ALERT_unexpected_message,
                       "SSL.State: Invalid padding.\n");
        } else if (!msg) {
          // TLS 1.1 requires a bad_record_mac alert on invalid padding.
          // Note that mac will still be calculated below even if
          // padding was wrong, to mitigate Lucky Thirteen attacks.
          fail = alert(ALERT_fatal, ALERT_bad_record_mac,
                       "SSL.State: Invalid padding.\n");
        }
      }
      break;

    case CIPHER_aead:

      // NB: Only valid in TLS 1.2 and later.
      string iv;
      if (session->cipher_spec->explicit_iv_size) {
	// The message consists of explicit_iv + crypted-msg + digest.
	iv = salt + msg[..session->cipher_spec->explicit_iv_size-1];
      } else {
	// ChaCha20-POLY1305 uses an implicit iv, and no salt,
	// but we've generalized it here to allow for ciphers
	// using salt and implicit iv.
	iv = sprintf("%s%*c", salt, crypt->iv_size() - sizeof(salt), seq_num);
      }
      int digest_size = crypt->digest_size();
      string digest = msg[<digest_size-1..];
      crypt->set_iv(iv);
      string auth_data = sprintf("%8c%c%2c%2c",
                                 seq_num, packet->content_type, version,
                                 sizeof(msg) -
                                 (session->cipher_spec->explicit_iv_size +
                                  digest_size));
      crypt->update(auth_data);
      msg = crypt->crypt(msg[session->cipher_spec->explicit_iv_size..
                             <digest_size]);
      seq_num++;
      if (digest != crypt->digest()) {
        // Bad digest.
        fail = alert(ALERT_fatal, ALERT_bad_record_mac,
                     "Failed AEAD-verification!!\n");
      }
      break;
    }

    if (!msg) msg = data;
    padding = sizeof(data) - sizeof(msg);
    data = msg;
  }

#ifdef SSL3_DEBUG_CRYPT
  werror("SSL.State: Decrypted_packet %O\n", data);
#endif

  if (tls_iv) {
    // TLS 1.1 IV. RFC 4346 6.2.3.2:
    // The decryption operation for all three alternatives is the same.
    // The receiver decrypts the entire GenericBlockCipher structure and
    // then discards the first cipher block, corresponding to the IV
    // component.
    data = data[tls_iv..];
  }

  if (hmac_size)
  {
#ifdef SSL3_DEBUG_CRYPT
    werror("SSL.State: Trying mac verification...\n");
#endif
    int length = sizeof(data) - hmac_size;
    string digest = data[length ..];
    data = data[.. length - 1];

   /* NOTE: Perform some extra MAC operations, to avoid timing
    *       attacks on the length of the padding.
    *
    *       This is to alleviate the "Lucky Thirteen" attack:
    *       http://www.isg.rhul.ac.uk/tls/TLStiming.pdf
    *
    * NB: This is not needed in encrypt-then-mac mode, since we
    *     always MAC the entire block.
    */
    int block_size = mac->block_size();
    string pad_string = "\0"*block_size;
    string junk = pad_string[<padding-1..];
    if (!((sizeof(data) +
           mac->hash_header_size - session->cipher_spec->hash_size) %
          block_size ) ||
        !(padding % block_size)) {
      // We're at the edge of a MAC block, so we need to
      // pad junk with an extra MAC block of data.
      junk += pad_string;
    }
    junk = mac->hash_raw(junk);

    // Set decrypted data without HMAC.
    fail = fail || [object(Alert)]packet->set_compressed(data);

    if (digest != mac->hash_packet(packet, seq_num)[..hmac_size-1])
      {
#ifdef SSL3_DEBUG
	werror("Failed MAC-verification!!\n");
#endif
#ifdef SSL3_DEBUG_CRYPT
	werror("Expected digest: %O\n"
	       "Calculated digest: %O\n"
	       "Seqence number: %O\n",
	       digest, mac->hash_packet(packet, seq_num), seq_num);
#endif
	fail = fail || alert(ALERT_fatal, ALERT_bad_record_mac,
			     "Bad MAC.\n");
      }
    seq_num++;
  }
  else
  {
    // Set decrypted data.
    fail = fail || [object(Alert)]packet->set_compressed(data);
  }

  if (compress)
  {
#ifdef SSL3_DEBUG_CRYPT
    werror("SSL.State: Trying decompression...\n");
#endif
    data = compress(data);
    if (!data)
      fail = fail || alert(ALERT_fatal, ALERT_unexpected_message,
			   "Invalid compression.\n");
    if (sizeof(data)>16384)
      fail = fail || alert(ALERT_fatal, ALERT_decompression_failure,
                           "Inflated package >16K\n");

    // Set uncompressed data
    fail = fail || [object(Alert)]packet->set_plaintext(data);
  }

  return fail || packet;
}

//! Encrypts a packet (including deflating and MAC-generation).
Alert|Packet encrypt_packet(Packet packet)
{
  ProtocolVersion version = packet->protocol_version;
  string data = packet->fragment;
  string digest;
  Alert res;

  if (compress)
  {
    // RFC 5246 6.2.2. states that data growth must be at most 1024
    // bytes. Since zlib doesn't do that, and no other implementation
    // is defined, don't bother checking.
    data = compress(data);

    // Set compressed data.
    res = [object(Alert)]packet->set_compressed(data);
    if(res) return res;
  }

  int hmac_size = mac && (session->truncated_hmac ? 10 :
			  session->cipher_spec?->hash_size);

  if (mac && !session->encrypt_then_mac) {
    digest = mac->hash_packet(packet, seq_num)[..hmac_size-1];
    hmac_size = 0;
  } else
    digest = "";

  if (crypt)
  {
    switch(session->cipher_spec->cipher_type) {
    case CIPHER_stream:
      data = crypt->crypt(data + digest);
      break;
    case CIPHER_block:
      if(version == PROTOCOL_SSL_3_0) {
	data = crypt->crypt(data + digest);
	data += crypt->pad(Crypto.PAD_SSL);
      } else if (version >= PROTOCOL_TLS_1_0) {
	if (tls_iv) {
	  // RFC 4346 6.2.3.2.2:
	  // Generate a cryptographically strong random number R of length
	  // CipherSpec.block_length and prepend it to the plaintext prior
	  // to encryption.
	  string iv = Crypto.Random.random_string(tls_iv);
	  crypt->set_iv(iv);
	  data = iv + crypt->crypt(data) + crypt->crypt(digest) + crypt->pad(Crypto.PAD_TLS);
	} else {
          data = crypt->crypt(data) + crypt->crypt(digest) + crypt->pad(Crypto.PAD_TLS);
        }
      }
      break;
    case CIPHER_aead:
      // FIXME: Do we need to pay attention to threads here?
      string explicit_iv = "";
      string iv;
      if (session->cipher_spec->explicit_iv_size) {
	// RFC 5288 3:
	// The nonce_explicit MAY be the 64-bit sequence number.
	//
	explicit_iv = sprintf("%*c", session->cipher_spec->explicit_iv_size,
			      seq_num);
	iv = salt + explicit_iv;
      } else {
	// Draft ChaCha20-Poly1305 5:
	//   When used in TLS, the "record_iv_length" is zero and the nonce is
	//   the sequence number for the record, as an 8-byte, big-endian
	//   number.
	iv = sprintf("%s%*c", salt, crypt->iv_size() - sizeof(salt), seq_num);
      }
      crypt->set_iv(iv);
      string auth_data = sprintf("%8c%c%2c%2c",
				 seq_num, packet->content_type,
				 version, sizeof(data));
      crypt->update(auth_data);
      data = explicit_iv + crypt->crypt(data) + crypt->digest();
      break;
    }
  }
  else
    data += digest;

  // Set encrypted data.
  res = [object(Alert)]packet->set_encrypted(data);
  if(res) return res;

  if (hmac_size) {
    // Encrypt-then-MAC mode.
    data += mac->hash_packet(packet, seq_num)[..hmac_size-1];

    // Set HMAC protected data.
    res = [object(Alert)]packet->set_encrypted(data);
    if(res) return res;
  }

  seq_num++;
  return packet;
}