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 |