Remove in-place TLS record assembly for now.
Decrypting is very easy to do in-place, but encrypting in-place is a hassle.
The rules actually were wrong due to record-splitting. The aliasing prefix and
the alignment prefix actually differ by 1. Take it out for now in preparation
for tightening the aliasing rules.
If we decide to do in-place encrypt later, probably it'd be more useful to
return header + in-place ciphertext + trailer. (That, in turn, needs a
scatter/gather thing on the AEAD thanks to TLS 1.3's padding and record type
construction.) We may also wish to rethink how record-splitting works here.
Change-Id: I0187d39c541e76ef933b7c2c193323164fd8a156
Reviewed-on: https://boringssl-review.googlesource.com/8230
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/internal.h b/crypto/internal.h
index 433072c..54e78d5 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -152,6 +152,19 @@
#endif
+/* buffers_alias returns one if |a| and |b| alias and zero otherwise. */
+static inline int buffers_alias(const uint8_t *a, size_t a_len,
+ const uint8_t *b, size_t b_len) {
+ /* Cast |a| and |b| to integers. In C, pointer comparisons between unrelated
+ * objects are undefined whereas pointer to integer conversions are merely
+ * implementation-defined. We assume the implementation defined it in a sane
+ * way. */
+ uintptr_t a_u = (uintptr_t)a;
+ uintptr_t b_u = (uintptr_t)b;
+ return a_u + a_len > b_u && b_u + b_len > a_u;
+}
+
+
/* Constant-time utility functions.
*
* The following methods return a bitmask of all ones (0xff...f) for true and 0
diff --git a/ssl/internal.h b/ssl/internal.h
index e9cf918..6d96c6c 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -402,17 +402,21 @@
uint8_t *out_alert, uint8_t *in,
size_t in_len);
-/* ssl_seal_prefix_len returns the length of the prefix before the ciphertext
- * when sealing a record with |ssl|. Note that this value may differ from
- * |ssl_record_prefix_len| when TLS 1.0 CBC record-splitting is enabled. Sealing
- * a small record may also result in a smaller output than this value.
+/* ssl_seal_align_prefix_len returns the length of the prefix before the start
+ * of the bulk of the ciphertext when sealing a record with |ssl|. Callers may
+ * use this to align buffers.
+ *
+ * Note when TLS 1.0 CBC record-splitting is enabled, this includes the one byte
+ * record and is the offset into second record's ciphertext. Thus this value may
+ * differ from |ssl_record_prefix_len| and sealing a small record may result in
+ * a smaller output than this value.
*
* TODO(davidben): Expose this as part of public API once the high-level
* buffer-free APIs are available. */
-size_t ssl_seal_prefix_len(const SSL *ssl);
+size_t ssl_seal_align_prefix_len(const SSL *ssl);
/* ssl_max_seal_overhead returns the maximum overhead of sealing a record with
- * |ssl|. This includes |ssl_seal_prefix_len|.
+ * |ssl|.
*
* TODO(davidben): Expose this as part of public API once the high-level
* buffer-free APIs are available. */
@@ -423,11 +427,12 @@
* and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1
* record splitting and may write two records concatenated.
*
- * For a large record, the ciphertext will begin |ssl_seal_prefix_len| bytes
- * into out. Aligning |out| appropriately may improve performance. It writes at
- * most |in_len| + |ssl_max_seal_overhead| bytes to |out|.
+ * For a large record, the bulk of the ciphertext will begin
+ * |ssl_seal_align_prefix_len| bytes into out. Aligning |out| appropriately may
+ * improve performance. It writes at most |in_len| + |ssl_max_seal_overhead|
+ * bytes to |out|.
*
- * If |in| and |out| alias, |out| + |ssl_seal_prefix_len| must be <= |in|. */
+ * |in| and |out| may not alias. */
int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
uint8_t type, const uint8_t *in, size_t in_len);
diff --git a/ssl/ssl_buffer.c b/ssl/ssl_buffer.c
index df814fa..a3cf360 100644
--- a/ssl/ssl_buffer.c
+++ b/ssl/ssl_buffer.c
@@ -224,7 +224,7 @@
return 0;
}
- size_t header_len = ssl_seal_prefix_len(ssl);
+ size_t header_len = ssl_seal_align_prefix_len(ssl);
/* TODO(davidben): This matches the original behavior in keeping the malloc
* size consistent. Does this matter? |cap| could just be |max_len|. */
diff --git a/ssl/tls_record.c b/ssl/tls_record.c
index 24dfb21..e1553e3 100644
--- a/ssl/tls_record.c
+++ b/ssl/tls_record.c
@@ -116,6 +116,7 @@
#include <openssl/mem.h>
#include "internal.h"
+#include "../crypto/internal.h"
/* kMaxEmptyRecords is the number of consecutive, empty records that will be
@@ -159,7 +160,7 @@
}
}
-size_t ssl_seal_prefix_len(const SSL *ssl) {
+size_t ssl_seal_align_prefix_len(const SSL *ssl) {
if (SSL_IS_DTLS(ssl)) {
return DTLS1_RT_HEADER_LENGTH +
SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx);
@@ -301,12 +302,14 @@
OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
return 0;
}
- /* Check the record header does not alias any part of the input.
- * |SSL_AEAD_CTX_seal| will internally enforce other aliasing requirements. */
- if (in < out + SSL3_RT_HEADER_LENGTH && out < in + in_len) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
- return 0;
- }
+
+ /* Either |in| and |out| don't alias or |in| aligns with the
+ * ciphertext. |tls_seal_record| forbids aliasing, but TLS 1.3 aliases them
+ * internally. */
+ assert(!buffers_alias(in, in_len, out, max_out) ||
+ in ==
+ out + SSL3_RT_HEADER_LENGTH +
+ SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx));
out[0] = type;
@@ -347,6 +350,11 @@
int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
uint8_t type, const uint8_t *in, size_t in_len) {
+ if (buffers_alias(in, in_len, out, max_out)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
+ return 0;
+ }
+
size_t frag_len = 0;
/* TLS 1.3 hides the actual record type inside the encrypted data. */
@@ -369,18 +377,7 @@
if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
ssl_needs_record_splitting(ssl)) {
- /* |do_seal_record| will notice if it clobbers |in[0]|, but not if it
- * aliases the rest of |in|. */
- if (in + 1 <= out && out < in + in_len) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
- return 0;
- }
- /* Ensure |do_seal_record| does not write beyond |in[0]|. */
- size_t frag_max_out = max_out;
- if (out <= in + 1 && in + 1 < out + frag_max_out) {
- frag_max_out = (size_t)(in + 1 - out);
- }
- if (!do_seal_record(ssl, out, &frag_len, frag_max_out, type, in, 1)) {
+ if (!do_seal_record(ssl, out, &frag_len, max_out, type, in, 1)) {
return 0;
}
in++;