From 9cdfccc36a5673f223a593d9d1cc6615b74197fb Mon Sep 17 00:00:00 2001
From: paulavidas <paulavidas@google.com>
Date: Thu, 8 Aug 2019 03:02:55 -0700
Subject: [PATCH] Fix ciphertext segment size validation.

We changed the segment size validation in all Tink streaming keys such that the first block always needs to have room for at least 1 byte of ciphertext. Previously it was not consistent. The decision whether to allow empty ciphertexts in the first segment is somewhat arbitrary, but it should be consistent everywhere.

PiperOrigin-RevId: 262315914
---
 cc/streamingaead/BUILD.bazel                             | 1 +
 cc/streamingaead/CMakeLists.txt                          | 1 +
 cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.cc   | 8 ++++++--
 cc/subtle/aes_ctr_hmac_stream_segment_encrypter.cc       | 2 +-
 cc/subtle/aes_ctr_hmac_stream_segment_encrypter_test.cc  | 2 +-
 cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc       | 2 +-
 cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc  | 4 +++-
 cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc       | 2 +-
 cc/subtle/aes_gcm_hkdf_stream_segment_encrypter_test.cc  | 3 ++-
 cc/subtle/aes_gcm_hkdf_streaming.cc                      | 5 +++--
 .../streamingaead/AesCtrHmacStreamingKeyManager.java     | 8 ++++++--
 .../streamingaead/AesGcmHkdfStreamingKeyManager.java     | 9 +++++++--
 12 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/cc/streamingaead/BUILD.bazel b/cc/streamingaead/BUILD.bazel
index c18e48f16..ef6cde3e1 100644
--- a/cc/streamingaead/BUILD.bazel
+++ b/cc/streamingaead/BUILD.bazel
@@ -66,6 +66,7 @@ cc_library(
         "//cc:core/key_type_manager",
         "//cc:key_manager",
         "//cc:streaming_aead",
+        "//cc/subtle:aes_gcm_hkdf_stream_segment_encrypter",
         "//cc/subtle:aes_gcm_hkdf_streaming",
         "//cc/subtle:random",
         "//cc/util:constants",
diff --git a/cc/streamingaead/CMakeLists.txt b/cc/streamingaead/CMakeLists.txt
index 88ea605ca..4adeaddb9 100644
--- a/cc/streamingaead/CMakeLists.txt
+++ b/cc/streamingaead/CMakeLists.txt
@@ -61,6 +61,7 @@ tink_cc_library(
     tink::core::streaming_aead
     tink::proto::aes_gcm_hkdf_streaming_cc_proto
     tink::proto::tink_cc_proto
+    tink::subtle::aes_gcm_hkdf_stream_segment_encrypter
     tink::subtle::aes_gcm_hkdf_streaming
     tink::subtle::random
     tink::util::constants
diff --git a/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.cc b/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.cc
index a3e2181a1..1bb7dc4eb 100644
--- a/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.cc
+++ b/cc/streamingaead/aes_gcm_hkdf_streaming_key_manager.cc
@@ -16,12 +16,14 @@
 
 #include "tink/streamingaead/aes_gcm_hkdf_streaming_key_manager.h"
 
+#include "tink/subtle/aes_gcm_hkdf_stream_segment_encrypter.h"
 #include "tink/subtle/random.h"
 #include "tink/util/validation.h"
 
 namespace crypto {
 namespace tink {
 
+using ::crypto::tink::subtle::AesGcmHkdfStreamSegmentEncrypter;
 using ::crypto::tink::util::Status;
 using ::crypto::tink::util::StatusOr;
 using ::google::crypto::tink::AesGcmHkdfStreamingKey;
@@ -38,8 +40,10 @@ Status ValidateParams(const AesGcmHkdfStreamingParams& params) {
         params.hkdf_hash_type() == HashType::SHA512)) {
     return Status(util::error::INVALID_ARGUMENT, "unsupported hkdf_hash_type");
   }
-  if (params.ciphertext_segment_size() <
-      (params.derived_key_size() + 8) + 16) {  // header_size + tag_size
+  int header_size = 1 + params.derived_key_size() +
+      AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes;
+  if (params.ciphertext_segment_size() <=
+      header_size + AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes) {
     return Status(util::error::INVALID_ARGUMENT,
                   "ciphertext_segment_size too small");
   }
diff --git a/cc/subtle/aes_ctr_hmac_stream_segment_encrypter.cc b/cc/subtle/aes_ctr_hmac_stream_segment_encrypter.cc
index 60e04e849..67323f0a6 100644
--- a/cc/subtle/aes_ctr_hmac_stream_segment_encrypter.cc
+++ b/cc/subtle/aes_ctr_hmac_stream_segment_encrypter.cc
@@ -65,7 +65,7 @@ util::Status Validate(const AesCtrHmacStreamSegmentEncrypter::Params& params) {
   }
   int header_size = 1 + params.salt.size() +
                     AesCtrHmacStreamSegmentEncrypter::kNoncePrefixSizeInBytes;
-  if (params.ciphertext_segment_size <
+  if (params.ciphertext_segment_size <=
       params.ciphertext_offset + header_size + params.tag_size) {
     return util::Status(util::error::INVALID_ARGUMENT,
                         "ciphertext_segment_size too small");
diff --git a/cc/subtle/aes_ctr_hmac_stream_segment_encrypter_test.cc b/cc/subtle/aes_ctr_hmac_stream_segment_encrypter_test.cc
index 6744e4b03..4589a84e1 100644
--- a/cc/subtle/aes_ctr_hmac_stream_segment_encrypter_test.cc
+++ b/cc/subtle/aes_ctr_hmac_stream_segment_encrypter_test.cc
@@ -175,7 +175,7 @@ TEST(AesCtrHmacStreamSegmentEncrypterTest, WrongCiphertextSegmentSize) {
       for (int ciphertext_offset : {0, 1, 5, 10}) {
         int min_ct_segment_size = key_size + ciphertext_offset +
                                   8 +  // nonce_prefix_size + 1
-                                  tag_size;
+                                  tag_size + 1;
         for (int ct_segment_size :
              {min_ct_segment_size - 5, min_ct_segment_size - 1,
               min_ct_segment_size, min_ct_segment_size + 1,
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc
index 5bbf85edb..b690a22a3 100644
--- a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc
+++ b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter.cc
@@ -79,7 +79,7 @@ util::Status Validate(const AesGcmHkdfStreamSegmentDecrypter::Params& params) {
   }
   int header_size = 1 + params.derived_key_size +
                     AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes;
-  if (params.ciphertext_segment_size <
+  if (params.ciphertext_segment_size <=
       params.ciphertext_offset + header_size +
       AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes) {
     return util::Status(util::error::INVALID_ARGUMENT,
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc
index 195654c17..0ea741bfb 100644
--- a/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc
+++ b/cc/subtle/aes_gcm_hkdf_stream_segment_decrypter_test.cc
@@ -231,7 +231,9 @@ TEST(AesGcmHkdfStreamSegmentDecrypterTest, testWrongCiphertextSegmentSize) {
       for (int ciphertext_offset : {0, 1, 5, 10}) {
         int min_ct_segment_size = derived_key_size + ciphertext_offset +
                                   8 +   // nonce_prefix_size + 1
-                                  16;   // tag_size
+                                  16 +   // tag_size
+                                  1;
+
         for (int ct_segment_size : {min_ct_segment_size - 5,
                 min_ct_segment_size - 1, min_ct_segment_size,
                 min_ct_segment_size + 1, min_ct_segment_size + 10}) {
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc
index 7fdd52848..5f94686a4 100644
--- a/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc
+++ b/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter.cc
@@ -77,7 +77,7 @@ util::Status Validate(const AesGcmHkdfStreamSegmentEncrypter::Params& params) {
   }
   int header_size = 1 + params.salt.size() +
                     AesGcmHkdfStreamSegmentEncrypter::kNoncePrefixSizeInBytes;
-  if (params.ciphertext_segment_size <
+  if (params.ciphertext_segment_size <=
       params.ciphertext_offset + header_size +
       AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes) {
     return util::Status(util::error::INVALID_ARGUMENT,
diff --git a/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter_test.cc b/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter_test.cc
index 6c7be6352..a60b81146 100644
--- a/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter_test.cc
+++ b/cc/subtle/aes_gcm_hkdf_stream_segment_encrypter_test.cc
@@ -163,7 +163,8 @@ TEST(AesGcmHkdfStreamSegmentEncrypterTest, testWrongCiphertextSegmentSize) {
     for (int ciphertext_offset : {0, 1, 5, 10}) {
       int min_ct_segment_size = key_size + ciphertext_offset +
                                 8 +   // nonce_prefix_size + 1
-                                16;   // tag_size
+                                16 +   // tag_size
+                                1;
       for (int ct_segment_size : {min_ct_segment_size - 5,
               min_ct_segment_size - 1, min_ct_segment_size,
               min_ct_segment_size + 1, min_ct_segment_size + 10}) {
diff --git a/cc/subtle/aes_gcm_hkdf_streaming.cc b/cc/subtle/aes_gcm_hkdf_streaming.cc
index 1da6fc9a3..fb1299336 100644
--- a/cc/subtle/aes_gcm_hkdf_streaming.cc
+++ b/cc/subtle/aes_gcm_hkdf_streaming.cc
@@ -57,8 +57,9 @@ Status Validate(absl::string_view ikm,
     return util::Status(util::error::INVALID_ARGUMENT,
                         "ciphertext_offset must be non-negative");
   }
-  if (ciphertext_segment_size <
-      ciphertext_offset + derived_key_size) {
+  if (ciphertext_segment_size <=
+      ciphertext_offset + derived_key_size +
+      AesGcmHkdfStreamSegmentEncrypter::kTagSizeInBytes) {
     return util::Status(util::error::INVALID_ARGUMENT,
                         "ciphertext_segment_size too small");
   }
diff --git a/java/src/main/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManager.java b/java/src/main/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManager.java
index 329f6c720..53d74595d 100644
--- a/java/src/main/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManager.java
+++ b/java/src/main/java/com/google/crypto/tink/streamingaead/AesCtrHmacStreamingKeyManager.java
@@ -52,6 +52,8 @@ class AesCtrHmacStreamingKeyManager
 
   /** Minimum tag size in bytes. This provides minimum 80-bit security strength. */
   private static final int MIN_TAG_SIZE_IN_BYTES = 10;
+  
+  private static final int NONCE_PREFIX_IN_BYTES = 7;
 
   /** @param serializedKey serialized {@code AesCtrHmacStreamingKey} proto */
   @Override
@@ -138,9 +140,11 @@ class AesCtrHmacStreamingKeyManager
     validateHmacParams(params.getHmacParams());
 
     if (params.getCiphertextSegmentSize()
-        < params.getDerivedKeySize() + params.getHmacParams().getTagSize() + 8) {
+        <= params.getDerivedKeySize() + params.getHmacParams().getTagSize() + 1
+           + NONCE_PREFIX_IN_BYTES) {
       throw new GeneralSecurityException(
-          "ciphertext_segment_size must be at least (derived_key_size + tag_size + 8)");
+          "ciphertext_segment_size must be at least (derived_key_size + tag_size + "
+          + "NONCE_PREFIX_IN_BYTES + 2)");
     }
   }
 
diff --git a/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java b/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java
index bfad9233c..2a948b464 100644
--- a/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java
+++ b/java/src/main/java/com/google/crypto/tink/streamingaead/AesGcmHkdfStreamingKeyManager.java
@@ -48,6 +48,9 @@ class AesGcmHkdfStreamingKeyManager
 
   public static final String TYPE_URL =
       "type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey";
+  
+  private static final int NONCE_PREFIX_IN_BYTES = 7;
+  private static final int TAG_SIZE_IN_BYTES = 16;
 
   /** @param serializedKey serialized {@code AesGcmHkdfStreamingKey} proto */
   @Override
@@ -118,9 +121,11 @@ class AesGcmHkdfStreamingKeyManager
     if (params.getHkdfHashType() == HashType.UNKNOWN_HASH) {
       throw new GeneralSecurityException("unknown HKDF hash type");
     }
-    if (params.getCiphertextSegmentSize() < params.getDerivedKeySize() + 8) {
+    if (params.getCiphertextSegmentSize() <= params.getDerivedKeySize() + 1 + NONCE_PREFIX_IN_BYTES
+        + TAG_SIZE_IN_BYTES) {
       throw new GeneralSecurityException(
-          "ciphertext_segment_size must be at least (derived_key_size + 8)");
+          "ciphertext_segment_size must be at least (derived_key_size + NONCE_PREFIX_IN_BYTES + "
+          + "TAG_SIZE_IN_BYTES + 2)");
     }
   }
 }
-- 
GitLab