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));
}
}
}