aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-03-23 17:02:55 -0400
committerJack Lloyd <[email protected]>2016-03-23 17:02:55 -0400
commit858cf5c82260e45e5bf51ff17b63f493d8295356 (patch)
tree3186029f089ffdb3e1c9e0ac004018d0953ff5e8
parent646ddaef38845a7ce33e4dcc7a02500a674c7033 (diff)
Add IETF standard ChaCha20Poly1305 ciphersuites to TLS
-rw-r--r--doc/news.rst12
-rw-r--r--doc/todo.rst1
-rw-r--r--src/lib/tls/tls_record.cpp66
-rw-r--r--src/lib/tls/tls_record.h12
-rw-r--r--src/lib/tls/tls_suite_info.cpp28
-rwxr-xr-xsrc/scripts/tls_suite_info.py49
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)