Chromium Code Reviews| Index: crypto/mutual_auth.cc |
| diff --git a/crypto/mutual_auth.cc b/crypto/mutual_auth.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..48c9764944acc08444ec6492787013d994234bbf |
| --- /dev/null |
| +++ b/crypto/mutual_auth.cc |
| @@ -0,0 +1,247 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +// This code implements SPAKE2, a varient of EKE: |
| +// http://www.di.ens.fr/~pointche/pub.php?reference=AbPo04 |
| + |
| +#include <crypto/mutual_auth.h> |
| + |
| +#include <base/logging.h> |
| +#include <base/rand_util.h> |
| +#include <crypto/p224.h> |
| +#include <crypto/secure_util.h> |
| + |
| +namespace { |
| + |
| +// The following two points (M and N in the protocol) are verifiable random |
| +// points on the curve and can be generated with the following code: |
| + |
| +// #include <stdint.h> |
| +// #include <stdio.h> |
| +// #include <string.h> |
| +// |
| +// #include <openssl/ec.h> |
| +// #include <openssl/obj_mac.h> |
| +// #include <openssl/sha.h> |
| +// |
| +// static const char kSeed1[] = "P224 point generation seed (M)"; |
| +// static const char kSeed2[] = "P224 point generation seed (N)"; |
| +// |
| +// void find_seed(const char* seed) { |
| +// SHA256_CTX sha256; |
| +// uint8_t digest[SHA256_DIGEST_LENGTH]; |
| +// |
| +// SHA256_Init(&sha256); |
| +// SHA256_Update(&sha256, seed, strlen(seed)); |
| +// SHA256_Final(digest, &sha256); |
| +// |
| +// BIGNUM x, y; |
| +// EC_GROUP* p224 = EC_GROUP_new_by_curve_name(NID_secp224r1); |
| +// EC_POINT* p = EC_POINT_new(p224); |
| +// |
| +// for (unsigned i = 0;; i++) { |
| +// BN_init(&x); |
| +// BN_bin2bn(digest, 28, &x); |
| +// |
| +// if (EC_POINT_set_compressed_coordinates_GFp( |
| +// p224, p, &x, digest[28] & 1, NULL)) { |
| +// BN_init(&y); |
| +// EC_POINT_get_affine_coordinates_GFp(p224, p, &x, &y, NULL); |
| +// char* x_str = BN_bn2hex(&x); |
| +// char* y_str = BN_bn2hex(&y); |
| +// printf("Found after %u iterations:\n%s\n%s\n", i, x_str, y_str); |
| +// OPENSSL_free(x_str); |
| +// OPENSSL_free(y_str); |
| +// BN_free(&x); |
| +// BN_free(&y); |
| +// break; |
| +// } |
| +// |
| +// SHA256_Init(&sha256); |
| +// SHA256_Update(&sha256, digest, sizeof(digest)); |
| +// SHA256_Final(digest, &sha256); |
| +// |
| +// BN_free(&x); |
| +// } |
| +// |
| +// EC_POINT_free(p); |
| +// EC_GROUP_free(p224); |
| +// } |
| +// |
| +// int main() { |
| +// find_seed(kSeed1); |
| +// find_seed(kSeed2); |
| +// return 0; |
| +// } |
| + |
| +const crypto::p224::Point kM = { |
| + {174237515, 77186811, 235213682, 33849492, |
| + 33188520, 48266885, 177021753, 81038478}, |
| + {104523827, 245682244, 266509668, 236196369, |
| + 28372046, 145351378, 198520366, 113345994}, |
| + {1, 0, 0, 0, 0, 0, 0}, |
| +}; |
| + |
| +const crypto::p224::Point kN = { |
| + {136176322, 263523628, 251628795, 229292285, |
| + 5034302, 185981975, 171998428, 11653062}, |
| + {197567436, 51226044, 60372156, 175772188, |
| + 42075930, 8083165, 160827401, 65097570}, |
| + {1, 0, 0, 0, 0, 0, 0}, |
| +}; |
| + |
| +} // anonymous namespace |
| + |
| +namespace crypto { |
| + |
| +SharedSecretMutualAuthentication::SharedSecretMutualAuthentication( |
| + PeerType peer_type, |
| + const base::StringPiece& password, |
| + const base::StringPiece& session) |
| + : is_server_(peer_type == kServer), |
| + state_(kStateInitial) { |
| + // x_ is a random scalar. |
| + base::RandBytes(x_, sizeof(x_)); |
| + |
| + // X = g**x_ |
| + p224::Point X; |
| + p224::ScalarBaseMult(x_, &X); |
| + |
| + // The "password" in the SPAKE2 protocol is |
| + // SHA256(P(password) + P(session)) where P is function that prepends a |
| + // uint32, big-endian length prefix. |
| + uint8 password_length[4], session_length[4]; |
| + password_length[0] = password.size() >> 24; |
| + password_length[1] = password.size() >> 16; |
| + password_length[2] = password.size() >> 8; |
| + password_length[3] = password.size(); |
| + session_length[0] = session.size() >> 24; |
| + session_length[1] = session.size() >> 16; |
| + session_length[2] = session.size() >> 8; |
| + session_length[3] = session.size(); |
|
Wez
2011/11/09 22:43:46
nit: Do we not have helper functions in crypto/, o
agl
2011/11/10 17:18:19
In net/ we use htonl/ntohl and memcpy. For the sak
|
| + SHA256HashString(std::string(reinterpret_cast<const char *>(password_length), |
| + sizeof(password_length)) + |
| + password.as_string() + |
| + std::string(reinterpret_cast<const char *>(session_length), |
| + sizeof(session_length)) + |
| + session.as_string(), |
| + pw_, |
| + sizeof(pw_)); |
| + |
| + // The client masks the Diffie-Hellman value, X, by adding M**pw and the |
| + // server uses N**pw. |
| + p224::Point MNpw; |
| + p224::ScalarMult(is_server_ ? kN : kM, pw_, &MNpw); |
| + |
| + // X* = X + (N|M)**pw |
| + p224::Point Xstar; |
| + p224::Add(X, MNpw, &Xstar); |
| + |
| + next_message_ = Xstar.ToString(); |
| +} |
| + |
| +const std::string& SharedSecretMutualAuthentication::GetMessage() { |
| + if (state_ == kStateInitial) { |
| + state_ = kStateRecvDH; |
| + return next_message_; |
| + } else if (state_ == kStateSendHash) { |
| + state_ = kStateRecvHash; |
| + return next_message_; |
| + } |
| + |
| + LOG(ERROR) << "SharedSecretMutualAuthentication::GetMessage called in" |
| + " bad state " << state_; |
|
Wez
2011/11/09 22:43:46
Is this actually a sufficiently severe failure of
agl
2011/11/10 17:18:19
You're the one writing the calling code so, if you
|
| + NOTREACHED(); |
| + next_message_ = ""; |
| + return next_message_; |
| +} |
| + |
| +SharedSecretMutualAuthentication::Result |
| +SharedSecretMutualAuthentication::ProcessMessage( |
| + const base::StringPiece& message) { |
| + if (state_ == kStateRecvHash) { |
| + // This is the final state of the protocol: we are reading the peer's |
| + // authentication hash and checking that it matches the one that we expect. |
| + if (message.size() != sizeof(expected_authenticator_)) { |
| + error_ = "peer's hash had an incorrect size"; |
| + return kResultFailed; |
| + } |
| + if (!SecureMemEqual(message.data(), expected_authenticator_, |
| + message.size())) { |
| + error_ = "peer's hash had incorrect value"; |
| + return kResultFailed; |
| + } |
| + state_ = kStateDone; |
| + return kResultSuccess; |
| + } |
| + |
| + if (state_ != kStateRecvDH) { |
| + LOG(ERROR) << "SharedSecretMutualAuthentication::ProcessMessage called in" |
| + " bad state " << state_; |
| + error_ = "internal error"; |
| + return kResultFailed; |
|
Wez
2011/11/09 22:43:46
Similarly, is this actually a potentially severe f
agl
2011/11/10 17:18:19
Ditto.
|
| + } |
| + |
| + // Y* is the other party's masked, Diffie-Hellman value. |
| + p224::Point Ystar; |
| + if (!Ystar.SetFromString(message)) { |
| + error_ = "failed to parse peer's masked Diffie-Hellman value"; |
| + return kResultFailed; |
| + } |
| + |
| + // We calculate the mask value: (N|M)**pw |
| + p224::Point MNpw, minus_MNpw, Y, k; |
| + p224::ScalarMult(is_server_ ? kM : kN, pw_, &MNpw); |
| + p224::Negate(MNpw, &minus_MNpw); |
| + // Y = Y* - (N|M)**pw |
| + p224::Add(Ystar, minus_MNpw, &Y); |
| + // K = Y**x_ |
| + p224::ScalarMult(Y, x_, &k); |
|
Wez
2011/11/09 22:43:46
nit: As for the P224 impl, I find this easier to r
agl
2011/11/10 17:18:19
Done.
|
| + // If everything worked out, then K is the same for both parties. |
| + std::string k_str = k.ToString(); |
| + |
| + std::string client_masked_dh, server_masked_dh; |
|
Wez
2011/11/09 22:43:46
Add a comment here to indicate that we're just cal
agl
2011/11/10 17:18:19
Done.
|
| + if (is_server_) { |
| + client_masked_dh = message.as_string(); |
| + server_masked_dh = next_message_; |
| + } else { |
| + client_masked_dh = next_message_; |
| + server_masked_dh = message.as_string(); |
| + } |
| + |
| + std::string client_hash_contents; |
| + client_hash_contents = "client"; |
| + client_hash_contents += client_masked_dh; |
| + client_hash_contents += server_masked_dh; |
| + client_hash_contents += std::string(reinterpret_cast<const char *>(pw_), |
| + sizeof(pw_)); |
| + client_hash_contents += k_str; |
| + |
| + std::string server_hash_contents; |
| + server_hash_contents = "server"; |
| + server_hash_contents += client_masked_dh; |
| + server_hash_contents += server_masked_dh; |
| + server_hash_contents += std::string(reinterpret_cast<const char *>(pw_), |
| + sizeof(pw_)); |
| + server_hash_contents += k_str; |
|
Wez
2011/11/09 22:43:46
Consider having a static function to do these, sin
agl
2011/11/10 17:18:19
Done.
|
| + |
| + uint8 client_hash[kSHA256Length], server_hash[kSHA256Length]; |
| + SHA256HashString(client_hash_contents, client_hash, sizeof(client_hash)); |
| + SHA256HashString(server_hash_contents, server_hash, sizeof(server_hash)); |
| + |
| + const uint8* my_hash = is_server_ ? server_hash : client_hash; |
| + const uint8* their_hash = is_server_ ? client_hash : server_hash; |
| + |
| + next_message_ = |
| + std::string(reinterpret_cast<const char*>(my_hash), kSHA256Length); |
| + memcpy(expected_authenticator_, their_hash, kSHA256Length); |
| + state_ = kStateSendHash; |
| + return kResultPending; |
| +} |
| + |
| +const std::string& SharedSecretMutualAuthentication::error() const { |
| + return error_; |
| +} |
| + |
| +} // namespace crypto |