diff options
author | Jack Lloyd <[email protected]> | 2016-03-23 17:02:55 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-03-23 17:02:55 -0400 |
commit | 858cf5c82260e45e5bf51ff17b63f493d8295356 (patch) | |
tree | 3186029f089ffdb3e1c9e0ac004018d0953ff5e8 | |
parent | 646ddaef38845a7ce33e4dcc7a02500a674c7033 (diff) |
Add IETF standard ChaCha20Poly1305 ciphersuites to TLS
-rw-r--r-- | doc/news.rst | 12 | ||||
-rw-r--r-- | doc/todo.rst | 1 | ||||
-rw-r--r-- | src/lib/tls/tls_record.cpp | 66 | ||||
-rw-r--r-- | src/lib/tls/tls_record.h | 12 | ||||
-rw-r--r-- | src/lib/tls/tls_suite_info.cpp | 28 | ||||
-rwxr-xr-x | src/scripts/tls_suite_info.py | 49 |
6 files changed, 109 insertions, 59 deletions
diff --git a/doc/news.rst b/doc/news.rst index e7a62b0c2..80b0dfe5a 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -1,6 +1,18 @@ Release Notes ======================================== +Version 1.11.30, Not Yet Released +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Add IETF versions of the ChaCha20Poly1305 TLS ciphersuites from + draft-ietf-tls-chacha20-poly1305-04. The previously implemented + (non-standard) ChaCha20Poly1305 ciphersuites from + draft-agl-tls-chacha20poly1305 remain but are deprecated. + +* A bug in the IETF version of ChaCha20Poly1305 (with 96 bit nonces) + caused incorrect computation when the plaintext or AAD was exactly + a multiple of 16 bytes. + Version 1.11.29, 2016-03-20 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/todo.rst b/doc/todo.rst index 0045b18be..dfab3cdf9 100644 --- a/doc/todo.rst +++ b/doc/todo.rst @@ -17,7 +17,6 @@ TLS * Make DTLS support optional at build time * Make TLS v1.0 and v1.1 optional at build time * Curve25519 key exchange -* IETF standard ChaCha20Poly1305 * TLS OCSP stapling (RFC 6066) * Encrypt-then-MAC extension (RFC 7366) * Authentication using TOFU (sqlite3 storage) diff --git a/src/lib/tls/tls_record.cpp b/src/lib/tls/tls_record.cpp index bdb37baad..efa6e2d18 100644 --- a/src/lib/tls/tls_record.cpp +++ b/src/lib/tls/tls_record.cpp @@ -53,12 +53,16 @@ Connection_Cipher_State::Connection_Cipher_State(Protocol_Version version, m_aead->set_key(cipher_key + mac_key); BOTAN_ASSERT_EQUAL(iv.length(), nonce_bytes_from_handshake(), "Matching nonce sizes"); - m_nonce = iv.bits_of(); + m_nonce = unlock(iv.bits_of()); BOTAN_ASSERT(nonce_bytes_from_record() == 0 || nonce_bytes_from_record() == 8, "Ciphersuite uses implemented IV length"); - m_nonce.resize(m_nonce.size() + 8); + if(m_nonce.size() != 12) + { + m_nonce.resize(m_nonce.size() + 8); + } + return; } @@ -77,20 +81,45 @@ Connection_Cipher_State::Connection_Cipher_State(Protocol_Version version, m_mac->set_key(mac_key); } -const secure_vector<byte>& Connection_Cipher_State::aead_nonce(u64bit seq) +std::vector<byte> Connection_Cipher_State::aead_nonce(u64bit seq) { - store_be(seq, &m_nonce[nonce_bytes_from_handshake()]); - return m_nonce; + if(nonce_bytes_from_handshake() == 12) + { + std::vector<byte> nonce(12); + store_be(seq, nonce.data() + 4); + xor_buf(nonce, m_nonce.data(), m_nonce.size()); + return nonce; + } + else + { + std::vector<byte> nonce = m_nonce; + store_be(seq, &nonce[nonce_bytes_from_handshake()]); + return nonce; + } } -const secure_vector<byte>& +std::vector<byte> Connection_Cipher_State::aead_nonce(const byte record[], size_t record_len, u64bit seq) { - if(nonce_bytes_from_record()) + if(nonce_bytes_from_handshake() == 12) + { + /* + Assumes if the suite specifies 12 bytes come from the handshake then + use the XOR nonce construction from draft-ietf-tls-chacha20-poly1305 + */ + + std::vector<byte> nonce(12); + store_be(seq, nonce.data() + 4); + xor_buf(nonce, m_nonce.data(), m_nonce.size()); + return nonce; + } + else if(nonce_bytes_from_record() > 0) { if(record_len < nonce_bytes_from_record()) throw Decoding_Error("Invalid AEAD packet too short to be valid"); - copy_mem(&m_nonce[nonce_bytes_from_handshake()], record, nonce_bytes_from_record()); + std::vector<byte> nonce = m_nonce; + copy_mem(&nonce[nonce_bytes_from_handshake()], record, nonce_bytes_from_record()); + return nonce; } else { @@ -98,18 +127,21 @@ Connection_Cipher_State::aead_nonce(const byte record[], size_t record_len, u64b nonce_len == 0 is assumed to mean no nonce in the message but instead the AEAD uses the seq number in network order. */ - store_be(seq, &m_nonce[nonce_bytes_from_handshake()]); + std::vector<byte> nonce = m_nonce; + store_be(seq, &nonce[nonce_bytes_from_handshake()]); + return nonce; } - return m_nonce; } -const secure_vector<byte>& +std::vector<byte> Connection_Cipher_State::format_ad(u64bit msg_sequence, byte msg_type, Protocol_Version version, u16bit msg_length) { - m_ad.clear(); + std::vector<byte> m_ad; + m_ad.reserve(13); + for(size_t i = 0; i != 8; ++i) m_ad.push_back(get_byte(i, msg_sequence)); m_ad.push_back(msg_type); @@ -156,7 +188,7 @@ void write_record(secure_vector<byte>& output, { const size_t ctext_size = aead->output_length(msg_length); - const secure_vector<byte>& nonce = cs->aead_nonce(seq); + const std::vector<byte> nonce = cs->aead_nonce(seq); // wrong if start returns something const size_t rec_size = ctext_size + cs->nonce_bytes_from_record(); @@ -167,7 +199,11 @@ void write_record(secure_vector<byte>& output, aead->set_ad(cs->format_ad(seq, msg_type, version, msg_length)); - output += std::make_pair(&nonce[cs->nonce_bytes_from_handshake()], cs->nonce_bytes_from_record()); + if(cs->nonce_bytes_from_record() > 0) + { + output += std::make_pair(&nonce[cs->nonce_bytes_from_handshake()], cs->nonce_bytes_from_record()); + } + BOTAN_ASSERT(aead->start(nonce).empty(), "AEAD doesn't return anything from start"); const size_t offset = output.size(); @@ -350,7 +386,7 @@ void decrypt_record(secure_vector<byte>& output, { if(AEAD_Mode* aead = cs.aead()) { - const secure_vector<byte>& nonce = cs.aead_nonce(record_contents, record_len, record_sequence); + const std::vector<byte> nonce = cs.aead_nonce(record_contents, record_len, record_sequence); const byte* msg = &record_contents[cs.nonce_bytes_from_record()]; const size_t msg_length = record_len - cs.nonce_bytes_from_record(); diff --git a/src/lib/tls/tls_record.h b/src/lib/tls/tls_record.h index d7aa82e71..e3b0b9b58 100644 --- a/src/lib/tls/tls_record.h +++ b/src/lib/tls/tls_record.h @@ -42,13 +42,13 @@ class Connection_Cipher_State AEAD_Mode* aead() { return m_aead.get(); } - const secure_vector<byte>& aead_nonce(u64bit seq); + std::vector<byte> aead_nonce(u64bit seq); - const secure_vector<byte>& aead_nonce(const byte record[], size_t record_len, u64bit seq); + std::vector<byte> aead_nonce(const byte record[], size_t record_len, u64bit seq); - const secure_vector<byte>& format_ad(u64bit seq, byte type, - Protocol_Version version, - u16bit ptext_length); + std::vector<byte> format_ad(u64bit seq, byte type, + Protocol_Version version, + u16bit ptext_length); BlockCipher* block_cipher() { return m_block_cipher.get(); } @@ -82,7 +82,7 @@ class Connection_Cipher_State std::unique_ptr<MessageAuthenticationCode> m_mac; std::unique_ptr<AEAD_Mode> m_aead; - secure_vector<byte> m_nonce, m_ad; + std::vector<byte> m_nonce; size_t m_block_size = 0; size_t m_nonce_bytes_from_handshake; diff --git a/src/lib/tls/tls_suite_info.cpp b/src/lib/tls/tls_suite_info.cpp index 0bebecb82..84e2a30a8 100644 --- a/src/lib/tls/tls_suite_info.cpp +++ b/src/lib/tls/tls_suite_info.cpp @@ -2,8 +2,8 @@ * TLS cipher suite information * * This file was automatically generated from the IANA assignments -* (tls-parameters.txt hash 6a934405ed41aa4d6113dad17f815867741430ac) -* by ./src/scripts/tls_suite_info.py on 2016-01-06 +* (tls-parameters.txt hash fe280cb8b13bfdd306a975ab39fda238f77ae3bc) +* by ./src/scripts/tls_suite_info.py on 2016-03-23 * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -159,6 +159,12 @@ std::vector<u16bit> Ciphersuite::all_known_ciphersuite_ids() 0xCC13, 0xCC14, 0xCC15, + 0xCCA8, + 0xCCA9, + 0xCCAA, + 0xCCAB, + 0xCCAC, + 0xCCAD, 0xFFF0, 0xFFF1, 0xFFF2, @@ -604,6 +610,24 @@ Ciphersuite Ciphersuite::by_id(u16bit suite) case 0xCC15: // DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 return Ciphersuite(0xCC15, "RSA", "DH", "ChaCha20Poly1305", 32, 0, 0, "AEAD", 0, "SHA-256"); + case 0xCCA8: // ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + return Ciphersuite(0xCCA8, "RSA", "ECDH", "ChaCha20Poly1305", 32, 12, 0, "AEAD", 0, "SHA-256"); + + case 0xCCA9: // ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + return Ciphersuite(0xCCA9, "ECDSA", "ECDH", "ChaCha20Poly1305", 32, 12, 0, "AEAD", 0, "SHA-256"); + + case 0xCCAA: // DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + return Ciphersuite(0xCCAA, "RSA", "DH", "ChaCha20Poly1305", 32, 12, 0, "AEAD", 0, "SHA-256"); + + case 0xCCAB: // PSK_WITH_CHACHA20_POLY1305_SHA256 + return Ciphersuite(0xCCAB, "", "PSK", "ChaCha20Poly1305", 32, 12, 0, "AEAD", 0, "SHA-256"); + + case 0xCCAC: // ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 + return Ciphersuite(0xCCAC, "", "ECDHE_PSK", "ChaCha20Poly1305", 32, 12, 0, "AEAD", 0, "SHA-256"); + + case 0xCCAD: // DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 + return Ciphersuite(0xCCAD, "", "DHE_PSK", "ChaCha20Poly1305", 32, 12, 0, "AEAD", 0, "SHA-256"); + case 0xFFF0: // ECDHE_RSA_WITH_AES_128_OCB_SHA256 return Ciphersuite(0xFFF0, "RSA", "ECDH", "AES-128/OCB(12)", 16, 4, 0, "AEAD", 0, "SHA-256"); diff --git a/src/scripts/tls_suite_info.py b/src/scripts/tls_suite_info.py index 6ba807d68..2bff5ad34 100755 --- a/src/scripts/tls_suite_info.py +++ b/src/scripts/tls_suite_info.py @@ -114,9 +114,9 @@ def to_ciphersuite_info(code, name): ivlen = 0 if cipher[0] == 'CHACHA20' and cipher[1] == 'POLY1305': - iv_len = 4 - if (code[0:2] == 'CC'): - iv_len = 0 + iv_len = 12 + if code in ['CC13', 'CC14', 'CC15']: + iv_len = 0 # Google variant return 'Ciphersuite(0x%s, "%s", "%s", "%s", %d, %d, %d, "AEAD", %d, "%s")' % ( code, sig_algo, kex_algo, "ChaCha20Poly1305", cipher_keylen, iv_len, 0, 0, mac_algo) @@ -168,11 +168,6 @@ def process_command_line(args): parser = optparse.OptionParser() - parser.add_option('--with-chacha', action='store_true', default=True, - help='enable experimental ChaCha suites') - parser.add_option('--without-chacha', action='store_false', dest='with_chacha', - help='disable experimental ChaCha suites') - parser.add_option('--with-ocb', action='store_true', default=True, help='enable experimental OCB AEAD suites') parser.add_option('--without-ocb', action='store_false', dest='with_ocb', @@ -224,7 +219,7 @@ def main(args = None): should_use = False if should_use: - suites[name] = (code, to_ciphersuite_info(code, name)) + suites[code] = (name, to_ciphersuite_info(code, name)) sha1 = hashlib.sha1() sha1.update(contents) @@ -236,23 +231,12 @@ def main(args = None): out.close() def define_custom_ciphersuite(name, code): - suites[name] = (code, to_ciphersuite_info(code, name)) - - if options.with_chacha: - # Google servers - draft-agl-tls-chacha20poly1305-04 - define_custom_ciphersuite('ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256', 'CC13') - define_custom_ciphersuite('ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256', 'CC14') - define_custom_ciphersuite('DHE_RSA_WITH_CHACHA20_POLY1305_SHA256', 'CC15') - - if options.with_chacha and False: - # Provisional IETF ChaCha suites - define_custom_ciphersuite('RSA_WITH_CHACHA20_POLY1305_SHA256', 'CD30') - define_custom_ciphersuite('ECDSA_RSA_WITH_CHACHA20_POLY1305_SHA256', 'CD31') - define_custom_ciphersuite('ECDSA_ECDSA_WITH_CHACHA20_POLY1305_SHA256', 'CD32') - define_custom_ciphersuite('DHE_RSA_WITH_CHACHA20_POLY1305_SHA256', 'CD33') - define_custom_ciphersuite('DHE_PSK_WITH_CHACHA20_POLY1305_SHA256', 'CD34') - define_custom_ciphersuite('PSK_WITH_CHACHA20_POLY1305_SHA256', 'CD35') - define_custom_ciphersuite('ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256', 'CD36') + suites[code] = (name, to_ciphersuite_info(code, name)) + + # Google servers - draft-agl-tls-chacha20poly1305-04 + define_custom_ciphersuite('ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256', 'CC13') + define_custom_ciphersuite('ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256', 'CC14') + define_custom_ciphersuite('DHE_RSA_WITH_CHACHA20_POLY1305_SHA256', 'CC15') # Expermental things if options.with_ocb: @@ -316,12 +300,7 @@ std::vector<u16bit> Ciphersuite::all_known_ciphersuite_ids() return std::vector<u16bit>{ """ - csuite_ids = {} - - for (k,v) in suites.items(): - csuite_ids[v[0]] = (k, v[1]) - - for i in sorted(csuite_ids.keys()): + for i in sorted(suites.keys()): suite_info += " 0x%s,\n" % (i) suite_info += """ }; @@ -333,9 +312,9 @@ Ciphersuite Ciphersuite::by_id(u16bit suite) { """ - for i in sorted(csuite_ids.keys()): - suite_name = csuite_ids[i][0] - suite_expr = csuite_ids[i][1] + for i in sorted(suites.keys()): + suite_name = suites[i][0] + suite_expr = suites[i][1] suite_info += " case 0x%s: // %s\n" % (i, suite_name) suite_info += " return %s;\n\n" % (suite_expr) |