HRSS: be strict about unused bits being zero.

It's excessively complex to worry about leaving these few bits for
extensions. If we need to change things, we can spin a new curve ID in
TLS. We don't need to support two versions during the transition because
a fallback to X25519 is still fine.

Change-Id: I0a4019d5693db0f0f3a5379909d99c2e2c762560
Reviewed-on: https://boringssl-review.googlesource.com/c/33704
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/hrss/hrss.c b/crypto/hrss/hrss.c
index dd3f979..d9ed0d9 100644
--- a/crypto/hrss/hrss.c
+++ b/crypto/hrss/hrss.c
@@ -1718,7 +1718,7 @@
   out[6] = 0xf & (p[3] >> 9);
 }
 
-static void poly_unmarshal(struct poly *out, const uint8_t in[POLY_BYTES]) {
+static int poly_unmarshal(struct poly *out, const uint8_t in[POLY_BYTES]) {
   uint16_t *p = out->v;
 
   for (size_t i = 0; i < N / 8; i++) {
@@ -1751,9 +1751,10 @@
     out->v[i] = (int16_t)(out->v[i] << 3) >> 3;
   }
 
-  // There are four unused bits at the top of the final byte. They are always
-  // marshaled as zero by this code but we allow them to take any value when
-  // parsing in order to support future extension.
+  // There are four unused bits in the last byte. We require them to be zero.
+  if ((in[6] & 0xf0) != 0) {
+    return 0;
+  }
 
   // Set the final coefficient as specifed in [HRSSNIST] 1.9.2 step 6.
   uint32_t sum = 0;
@@ -1762,6 +1763,8 @@
   }
 
   out->v[N - 1] = (uint16_t)(0u - sum);
+
+  return 1;
 }
 
 // mod3_from_modQ maps {0, 1, Q-1, 65535} -> {0, 1, 2, 2}. Note that |v| may
@@ -2156,16 +2159,15 @@
                         "HRSS shared key length incorrect");
   SHA256_Final(out_shared_key, &hash_ctx);
 
+  struct poly c;
   // If the ciphertext is publicly invalid then a random shared key is still
   // returned to simply the logic of the caller, but this path is not constant
   // time.
-  if (ciphertext_len != HRSS_CIPHERTEXT_BYTES) {
+  if (ciphertext_len != HRSS_CIPHERTEXT_BYTES ||
+      !poly_unmarshal(&c, ciphertext)) {
     return;
   }
 
-  struct poly c;
-  poly_unmarshal(&c, ciphertext);
-
   struct poly f;
   poly_from_poly3(&f, &priv->f);
 
@@ -2231,7 +2233,9 @@
 int HRSS_parse_public_key(struct HRSS_public_key *out,
                           const uint8_t in[HRSS_PUBLIC_KEY_BYTES]) {
   struct public_key *pub = public_key_from_external(out);
-  poly_unmarshal(&pub->ph, in);
+  if (!poly_unmarshal(&pub->ph, in)) {
+    return 0;
+  }
   OPENSSL_memset(&pub->ph.v[N], 0, 3 * sizeof(uint16_t));
   return 1;
 }
diff --git a/crypto/hrss/hrss_test.cc b/crypto/hrss/hrss_test.cc
index ead717d..714cc7a 100644
--- a/crypto/hrss/hrss_test.cc
+++ b/crypto/hrss/hrss_test.cc
@@ -208,7 +208,7 @@
     for (unsigned j = 0; j < 10; j++) {
       uint8_t encap_entropy[HRSS_ENCAP_BYTES];
       RAND_bytes(encap_entropy, sizeof(encap_entropy));
-      SCOPED_TRACE(Bytes(generate_key_entropy));
+      SCOPED_TRACE(Bytes(encap_entropy));
 
       uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
       uint8_t shared_key[HRSS_KEY_BYTES];
@@ -216,8 +216,15 @@
 
       uint8_t shared_key2[HRSS_KEY_BYTES];
       HRSS_decap(shared_key2, &pub, &priv, ciphertext, sizeof(ciphertext));
-
       EXPECT_EQ(Bytes(shared_key), Bytes(shared_key2));
+
+      uint32_t offset;
+      RAND_bytes((uint8_t*) &offset, sizeof(offset));
+      uint8_t bit;
+      RAND_bytes(&bit, sizeof(bit));
+      ciphertext[offset % sizeof(ciphertext)] ^= (1 << (bit & 7));
+      HRSS_decap(shared_key2, &pub, &priv, ciphertext, sizeof(ciphertext));
+      EXPECT_NE(Bytes(shared_key), Bytes(shared_key2));
     }
   }
 }