summaryrefslogtreecommitdiff
path: root/chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c')
-rw-r--r--chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c118
1 files changed, 95 insertions, 23 deletions
diff --git a/chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c b/chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c
index 8ebedf99958..e17d510d7f3 100644
--- a/chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c
+++ b/chromium/third_party/boringssl/src/crypto/curve25519/spake25519.c
@@ -14,6 +14,7 @@
#include <openssl/curve25519.h>
+#include <assert.h>
#include <string.h>
#include <openssl/bytestring.h>
@@ -267,25 +268,6 @@ static const uint8_t kSpakeMSmallPrecomp[15 * 2 * 32] = {
0xa6, 0x76, 0x81, 0x28, 0xb2, 0x65, 0xe8, 0x47, 0x14, 0xc6, 0x39, 0x06,
};
-enum spake2_state_t {
- spake2_state_init = 0,
- spake2_state_msg_generated,
- spake2_state_key_generated,
-};
-
-struct spake2_ctx_st {
- uint8_t private_key[32];
- uint8_t my_msg[32];
- uint8_t password_scalar[32];
- uint8_t password_hash[SHA512_DIGEST_LENGTH];
- uint8_t *my_name;
- size_t my_name_len;
- uint8_t *their_name;
- size_t their_name_len;
- enum spake2_role_t my_role;
- enum spake2_state_t state;
-};
-
SPAKE2_CTX *SPAKE2_CTX_new(enum spake2_role_t my_role,
const uint8_t *my_name, size_t my_name_len,
const uint8_t *their_name, size_t their_name_len) {
@@ -332,6 +314,48 @@ static void left_shift_3(uint8_t n[32]) {
}
}
+typedef union {
+ uint8_t bytes[32];
+ uint32_t words[8];
+} scalar;
+
+// kOrder is the order of the prime-order subgroup of curve25519 in
+// little-endian order.
+static const scalar kOrder = {{0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
+ 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}};
+
+// scalar_cmov copies |src| to |dest| if |mask| is all ones.
+static void scalar_cmov(scalar *dest, const scalar *src, crypto_word_t mask) {
+ for (size_t i = 0; i < 8; i++) {
+ dest->words[i] =
+ constant_time_select_w(mask, src->words[i], dest->words[i]);
+ }
+}
+
+// scalar_double sets |s| to |2×s|.
+static void scalar_double(scalar *s) {
+ uint32_t carry = 0;
+
+ for (size_t i = 0; i < 8; i++) {
+ const uint32_t carry_out = s->words[i] >> 31;
+ s->words[i] = (s->words[i] << 1) | carry;
+ carry = carry_out;
+ }
+}
+
+// scalar_add sets |dest| to |dest| plus |src|.
+static void scalar_add(scalar *dest, const scalar *src) {
+ uint32_t carry = 0;
+
+ for (size_t i = 0; i < 8; i++) {
+ uint64_t tmp = ((uint64_t)dest->words[i] + src->words[i]) + carry;
+ dest->words[i] = (uint32_t)tmp;
+ carry = (uint32_t)(tmp >> 32);
+ }
+}
+
int SPAKE2_generate_msg(SPAKE2_CTX *ctx, uint8_t *out, size_t *out_len,
size_t max_out_len, const uint8_t *password,
size_t password_len) {
@@ -359,13 +383,61 @@ int SPAKE2_generate_msg(SPAKE2_CTX *ctx, uint8_t *out, size_t *out_len,
SHA512(password, password_len, password_tmp);
OPENSSL_memcpy(ctx->password_hash, password_tmp, sizeof(ctx->password_hash));
x25519_sc_reduce(password_tmp);
- OPENSSL_memcpy(ctx->password_scalar, password_tmp, sizeof(ctx->password_scalar));
+
+ // Due to a copy-paste error, the call to |left_shift_3| was omitted after
+ // the |x25519_sc_reduce|, just above. This meant that |ctx->password_scalar|
+ // was not a multiple of eight to clear the cofactor and thus three bits of
+ // the password hash would leak. In order to fix this in a unilateral way,
+ // points of small order are added to the mask point such that it is in the
+ // prime-order subgroup. Since the ephemeral scalar is a multiple of eight,
+ // these points will cancel out when calculating the shared secret.
+ //
+ // Adding points of small order is the same as adding multiples of the prime
+ // order to the password scalar. Since that's faster, that is what is done
+ // below. The prime order (kOrder) is a large prime, thus odd, thus the LSB
+ // is one. So adding it will flip the LSB. Adding twice it will flip the next
+ // bit and so one for all the bottom three bits.
+
+ scalar password_scalar;
+ OPENSSL_memcpy(&password_scalar, password_tmp, sizeof(password_scalar));
+
+ // |password_scalar| is the result of |x25519_sc_reduce| and thus is, at
+ // most, $l-1$ (where $l$ is |kOrder|, the order of the prime-order subgroup
+ // of Ed25519). In the following, we may add $l + 2×l + 4×l$ for a max value
+ // of $8×l-1$. That is < 2**256, as required.
+
+ if (!ctx->disable_password_scalar_hack) {
+ scalar order = kOrder;
+ scalar tmp;
+
+ OPENSSL_memset(&tmp, 0, sizeof(tmp));
+ scalar_cmov(&tmp, &order,
+ constant_time_eq_w(password_scalar.bytes[0] & 1, 1));
+ scalar_add(&password_scalar, &tmp);
+
+ scalar_double(&order);
+ OPENSSL_memset(&tmp, 0, sizeof(tmp));
+ scalar_cmov(&tmp, &order,
+ constant_time_eq_w(password_scalar.bytes[0] & 2, 2));
+ scalar_add(&password_scalar, &tmp);
+
+ scalar_double(&order);
+ OPENSSL_memset(&tmp, 0, sizeof(tmp));
+ scalar_cmov(&tmp, &order,
+ constant_time_eq_w(password_scalar.bytes[0] & 4, 4));
+ scalar_add(&password_scalar, &tmp);
+
+ assert((password_scalar.bytes[0] & 7) == 0);
+ }
+
+ OPENSSL_memcpy(ctx->password_scalar, password_scalar.bytes,
+ sizeof(ctx->password_scalar));
ge_p3 mask;
x25519_ge_scalarmult_small_precomp(&mask, ctx->password_scalar,
- ctx->my_role == spake2_role_alice
- ? kSpakeMSmallPrecomp
- : kSpakeNSmallPrecomp);
+ ctx->my_role == spake2_role_alice
+ ? kSpakeMSmallPrecomp
+ : kSpakeNSmallPrecomp);
// P* = P + mask.
ge_cached mask_cached;