summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorsergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-22 13:06:04 +0000
committersergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-22 13:06:04 +0000
commit34deb103e128d7081e1a3459c0d24c1582290dac (patch)
tree20efa74c0213158808a815300557a6b71f26e909 /remoting
parentd6be33ef12e0768fb8c4e7ad779854f98d913840 (diff)
downloadchromium_src-34deb103e128d7081e1a3459c0d24c1582290dac.zip
chromium_src-34deb103e128d7081e1a3459c0d24c1582290dac.tar.gz
chromium_src-34deb103e128d7081e1a3459c0d24c1582290dac.tar.bz2
Replace V1*ChannelAuthenticator with SslHmacChannelAuthenticator.
The new class will be used for both V1 and V2 authentication BUG=105214 Review URL: http://codereview.chromium.org/8963005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115519 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/protocol/auth_util.cc28
-rw-r--r--remoting/protocol/auth_util.h16
-rw-r--r--remoting/protocol/channel_authenticator.h2
-rw-r--r--remoting/protocol/pepper_session.cc1
-rw-r--r--remoting/protocol/ssl_hmac_channel_authenticator.cc272
-rw-r--r--remoting/protocol/ssl_hmac_channel_authenticator.h110
-rw-r--r--remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc151
-rw-r--r--remoting/protocol/v1_authenticator.cc16
-rw-r--r--remoting/protocol/v1_authenticator_unittest.cc18
-rw-r--r--remoting/protocol/v1_client_channel_authenticator.cc134
-rw-r--r--remoting/protocol/v1_client_channel_authenticator.h58
-rw-r--r--remoting/protocol/v1_host_channel_authenticator.cc144
-rw-r--r--remoting/protocol/v1_host_channel_authenticator.h61
-rw-r--r--remoting/remoting.gyp7
14 files changed, 584 insertions, 434 deletions
diff --git a/remoting/protocol/auth_util.cc b/remoting/protocol/auth_util.cc
index 51f7af5..281dabc 100644
--- a/remoting/protocol/auth_util.cc
+++ b/remoting/protocol/auth_util.cc
@@ -9,12 +9,16 @@
#include "base/string_util.h"
#include "crypto/hmac.h"
#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/socket/ssl_socket.h"
namespace remoting {
namespace protocol {
const char kClientAuthSslExporterLabel[] =
"EXPORTER-remoting-channel-auth-client";
+const char kHostAuthSslExporterLabel[] =
+ "EXPORTER-remoting-channel-auth-host";
const char kSslFakeHostName[] = "chromoting";
@@ -37,23 +41,31 @@ bool VerifySupportAuthToken(const std::string& jid,
}
// static
-bool GetAuthBytes(const std::string& shared_secret,
- const std::string& key_material,
- std::string* auth_bytes) {
+std::string GetAuthBytes(net::SSLSocket* socket,
+ const base::StringPiece& label,
+ const base::StringPiece& shared_secret) {
+ // Get keying material from SSL.
+ unsigned char key_material[kAuthDigestLength];
+ int export_result = socket->ExportKeyingMaterial(
+ label, "", key_material, kAuthDigestLength);
+ if (export_result != net::OK) {
+ LOG(ERROR) << "Error fetching keying material: " << export_result;
+ return std::string();
+ }
+
// Generate auth digest based on the keying material and shared secret.
crypto::HMAC response(crypto::HMAC::SHA256);
- if (!response.Init(key_material)) {
+ if (!response.Init(key_material, kAuthDigestLength)) {
NOTREACHED() << "HMAC::Init failed";
- return false;
+ return std::string();
}
unsigned char out_bytes[kAuthDigestLength];
if (!response.Sign(shared_secret, out_bytes, kAuthDigestLength)) {
NOTREACHED() << "HMAC::Sign failed";
- return false;
+ return std::string();
}
- auth_bytes->assign(out_bytes, out_bytes + kAuthDigestLength);
- return true;
+ return std::string(out_bytes, out_bytes + kAuthDigestLength);
}
} // namespace protocol
diff --git a/remoting/protocol/auth_util.h b/remoting/protocol/auth_util.h
index 7a81c12..e33a473 100644
--- a/remoting/protocol/auth_util.h
+++ b/remoting/protocol/auth_util.h
@@ -7,11 +7,18 @@
#include <string>
+#include "base/string_piece.h"
+
+namespace net {
+class SSLSocket;
+} // namespace net
+
namespace remoting {
namespace protocol {
// Labels for use when exporting the SSL master keys.
extern const char kClientAuthSslExporterLabel[];
+extern const char kHostAuthSslExporterLabel[];
// Fake hostname used for SSL connections.
extern const char kSslFakeHostName[];
@@ -28,10 +35,11 @@ bool VerifySupportAuthToken(const std::string& jid,
const std::string& access_code,
const std::string& auth_token);
-// Returns hash used for channel authentication.
-bool GetAuthBytes(const std::string& shared_secret,
- const std::string& key_material,
- std::string* auth_bytes);
+// Returns authentication bytes that must be used for the given
+// |socket|. Empty string is returned in case of failure.
+std::string GetAuthBytes(net::SSLSocket* socket,
+ const base::StringPiece& label,
+ const base::StringPiece& shared_secret);
} // namespace protocol
} // namespace remoting
diff --git a/remoting/protocol/channel_authenticator.h b/remoting/protocol/channel_authenticator.h
index 535730a..2ef66de 100644
--- a/remoting/protocol/channel_authenticator.h
+++ b/remoting/protocol/channel_authenticator.h
@@ -7,7 +7,7 @@
#include <string>
-#include "base/callback.h"
+#include "base/callback_forward.h"
#include "net/base/net_errors.h"
namespace net {
diff --git a/remoting/protocol/pepper_session.cc b/remoting/protocol/pepper_session.cc
index 8eabb09..0cdfdd9 100644
--- a/remoting/protocol/pepper_session.cc
+++ b/remoting/protocol/pepper_session.cc
@@ -15,7 +15,6 @@
#include "remoting/protocol/jingle_messages.h"
#include "remoting/protocol/pepper_session_manager.h"
#include "remoting/protocol/pepper_stream_channel.h"
-#include "remoting/protocol/v1_client_channel_authenticator.h"
#include "third_party/libjingle/source/talk/p2p/base/candidate.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
diff --git a/remoting/protocol/ssl_hmac_channel_authenticator.cc b/remoting/protocol/ssl_hmac_channel_authenticator.cc
new file mode 100644
index 0000000..f38830a
--- /dev/null
+++ b/remoting/protocol/ssl_hmac_channel_authenticator.cc
@@ -0,0 +1,272 @@
+// 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.
+
+#include "remoting/protocol/ssl_hmac_channel_authenticator.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "crypto/secure_util.h"
+#include "net/base/cert_verifier.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/ssl_config_service.h"
+#include "net/base/x509_certificate.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/socket/ssl_server_socket.h"
+#include "remoting/protocol/auth_util.h"
+
+namespace remoting {
+namespace protocol {
+
+// static
+SslHmacChannelAuthenticator* SslHmacChannelAuthenticator::CreateForClient(
+ const std::string& remote_cert,
+ const std::string& auth_key) {
+ SslHmacChannelAuthenticator* result =
+ new SslHmacChannelAuthenticator(auth_key);
+ result->remote_cert_ = remote_cert;
+ return result;
+}
+
+SslHmacChannelAuthenticator* SslHmacChannelAuthenticator::CreateForHost(
+ const std::string& local_cert,
+ crypto::RSAPrivateKey* local_private_key,
+ const std::string& auth_key) {
+ SslHmacChannelAuthenticator* result =
+ new SslHmacChannelAuthenticator(auth_key);
+ result->local_cert_ = local_cert;
+ result->local_private_key_ = local_private_key;
+ return result;
+}
+
+SslHmacChannelAuthenticator::SslHmacChannelAuthenticator(
+ const std::string& auth_key)
+ : auth_key_(auth_key),
+ local_private_key_(NULL),
+ legacy_mode_(NONE) {
+}
+
+SslHmacChannelAuthenticator::~SslHmacChannelAuthenticator() {
+}
+
+void SslHmacChannelAuthenticator::SetLegacyOneWayMode(LegacyMode legacy_mode) {
+ // Must be called before SecureAndAuthenticate().
+ DCHECK(done_callback_.is_null());
+ legacy_mode_ = legacy_mode;
+}
+
+void SslHmacChannelAuthenticator::SecureAndAuthenticate(
+ net::StreamSocket* socket, const DoneCallback& done_callback) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(socket->IsConnected());
+
+ scoped_ptr<net::StreamSocket> channel_socket(socket);
+ done_callback_ = done_callback;
+
+ int result;
+ if (is_ssl_server()) {
+ scoped_refptr<net::X509Certificate> cert =
+ net::X509Certificate::CreateFromBytes(
+ local_cert_.data(), local_cert_.length());
+ if (!cert) {
+ LOG(ERROR) << "Failed to parse X509Certificate";
+ done_callback.Run(net::ERR_FAILED, NULL);
+ return;
+ }
+
+ net::SSLConfig ssl_config;
+ net::SSLServerSocket* server_socket = net::CreateSSLServerSocket(
+ channel_socket.release(), cert, local_private_key_, ssl_config);
+ socket_.reset(server_socket);
+
+ result = server_socket->Handshake(base::Bind(
+ &SslHmacChannelAuthenticator::OnConnected, base::Unretained(this)));
+ } else {
+ cert_verifier_.reset(new net::CertVerifier());
+
+ net::SSLConfig::CertAndStatus cert_and_status;
+ cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID;
+ cert_and_status.der_cert = remote_cert_;
+
+ net::SSLConfig ssl_config;
+ // Certificate verification and revocation checking are not needed
+ // because we use self-signed certs. Disable it so that the SSL
+ // layer doesn't try to initialize OCSP (OCSP works only on the IO
+ // thread).
+ ssl_config.allowed_bad_certs.push_back(cert_and_status);
+ ssl_config.rev_checking_enabled = false;
+
+ net::HostPortPair host_and_port(kSslFakeHostName, 0);
+ net::SSLClientSocketContext context;
+ context.cert_verifier = cert_verifier_.get();
+ socket_.reset(
+ net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
+ channel_socket.release(), host_and_port,
+ ssl_config, NULL, context));
+
+ result = socket_->Connect(
+ base::Bind(&SslHmacChannelAuthenticator::OnConnected,
+ base::Unretained(this)));
+ }
+
+ if (result == net::ERR_IO_PENDING)
+ return;
+
+ OnConnected(result);
+}
+
+bool SslHmacChannelAuthenticator::is_ssl_server() {
+ return local_private_key_ != NULL;
+}
+
+void SslHmacChannelAuthenticator::OnConnected(int result) {
+ if (result != net::OK) {
+ LOG(WARNING) << "Failed to establish SSL connection";
+ done_callback_.Run(static_cast<net::Error>(result), NULL);
+ return;
+ }
+
+ if (legacy_mode_ != RECEIVE_ONLY) {
+ // Generate authentication digest to write to the socket.
+ std::string auth_bytes = GetAuthBytes(
+ socket_.get(), is_ssl_server() ?
+ kClientAuthSslExporterLabel : kHostAuthSslExporterLabel, auth_key_);
+ if (auth_bytes.empty()) {
+ done_callback_.Run(net::ERR_FAILED, NULL);
+ return;
+ }
+
+ // Allocate a buffer to write the digest.
+ auth_write_buf_ = new net::DrainableIOBuffer(
+ new net::StringIOBuffer(auth_bytes), auth_bytes.size());
+ }
+
+ if (legacy_mode_ != SEND_ONLY) {
+ // Read an incoming token.
+ auth_read_buf_ = new net::GrowableIOBuffer();
+ auth_read_buf_->SetCapacity(kAuthDigestLength);
+ }
+
+ // If WriteAuthenticationBytes() results in |done_callback_| being
+ // called then we must not do anything else because this object may
+ // be destroyed at that point.
+ bool callback_called = false;
+ if (legacy_mode_ != RECEIVE_ONLY)
+ WriteAuthenticationBytes(&callback_called);
+ if (!callback_called && legacy_mode_ != SEND_ONLY)
+ ReadAuthenticationBytes();
+}
+
+void SslHmacChannelAuthenticator::WriteAuthenticationBytes(
+ bool* callback_called) {
+ while (true) {
+ int result = socket_->Write(
+ auth_write_buf_, auth_write_buf_->BytesRemaining(),
+ base::Bind(&SslHmacChannelAuthenticator::OnAuthBytesWritten,
+ base::Unretained(this)));
+ if (result == net::ERR_IO_PENDING)
+ break;
+ if (!HandleAuthBytesWritten(result, callback_called))
+ break;
+ }
+}
+
+void SslHmacChannelAuthenticator::OnAuthBytesWritten(int result) {
+ DCHECK(CalledOnValidThread());
+
+ if (HandleAuthBytesWritten(result, NULL))
+ WriteAuthenticationBytes(NULL);
+}
+
+bool SslHmacChannelAuthenticator::HandleAuthBytesWritten(
+ int result, bool* callback_called) {
+ if (result <= 0) {
+ LOG(ERROR) << "Error writing authentication: " << result;
+ if (callback_called)
+ *callback_called = false;
+ done_callback_.Run(static_cast<net::Error>(result), NULL);
+ return false;
+ }
+
+ auth_write_buf_->DidConsume(result);
+ if (auth_write_buf_->BytesRemaining() > 0)
+ return true;
+
+ auth_write_buf_ = NULL;
+ CheckDone(callback_called);
+ return false;
+}
+
+void SslHmacChannelAuthenticator::ReadAuthenticationBytes() {
+ while (true) {
+ int result = socket_->Read(
+ auth_read_buf_,
+ auth_read_buf_->RemainingCapacity(),
+ base::Bind(&SslHmacChannelAuthenticator::OnAuthBytesRead,
+ base::Unretained(this)));
+ if (result == net::ERR_IO_PENDING)
+ break;
+ if (!HandleAuthBytesRead(result))
+ break;
+ }
+}
+
+void SslHmacChannelAuthenticator::OnAuthBytesRead(int result) {
+ DCHECK(CalledOnValidThread());
+
+ if (HandleAuthBytesRead(result))
+ ReadAuthenticationBytes();
+}
+
+bool SslHmacChannelAuthenticator::HandleAuthBytesRead(int read_result) {
+ if (read_result <= 0) {
+ done_callback_.Run(static_cast<net::Error>(read_result), NULL);
+ return false;
+ }
+
+ auth_read_buf_->set_offset(auth_read_buf_->offset() + read_result);
+ if (auth_read_buf_->RemainingCapacity() > 0)
+ return true;
+
+ if (!VerifyAuthBytes(std::string(
+ auth_read_buf_->StartOfBuffer(),
+ auth_read_buf_->StartOfBuffer() + kAuthDigestLength))) {
+ LOG(WARNING) << "Mismatched authentication";
+ done_callback_.Run(net::ERR_FAILED, NULL);
+ return false;
+ }
+
+ auth_read_buf_ = NULL;
+ CheckDone(NULL);
+ return false;
+}
+
+bool SslHmacChannelAuthenticator::VerifyAuthBytes(
+ const std::string& received_auth_bytes) {
+ DCHECK(received_auth_bytes.length() == kAuthDigestLength);
+
+ // Compute expected auth bytes.
+ std::string auth_bytes = GetAuthBytes(
+ socket_.get(), is_ssl_server() ?
+ kHostAuthSslExporterLabel : kClientAuthSslExporterLabel, auth_key_);
+ if (auth_bytes.empty())
+ return false;
+
+ return crypto::SecureMemEqual(received_auth_bytes.data(),
+ &(auth_bytes[0]), kAuthDigestLength);
+}
+
+void SslHmacChannelAuthenticator::CheckDone(bool* callback_called) {
+ if (auth_write_buf_ == NULL && auth_read_buf_ == NULL) {
+ DCHECK(socket_.get() != NULL);
+ if (callback_called)
+ *callback_called = true;
+ done_callback_.Run(net::OK, socket_.release());
+ }
+}
+
+} // namespace protocol
+} // namespace remoting
diff --git a/remoting/protocol/ssl_hmac_channel_authenticator.h b/remoting/protocol/ssl_hmac_channel_authenticator.h
new file mode 100644
index 0000000..e86e25e
--- /dev/null
+++ b/remoting/protocol/ssl_hmac_channel_authenticator.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef REMOTING_PROTOCOL_SSL_HMAC_CHANNEL_AUTHENTICATOR_H_
+#define REMOTING_PROTOCOL_SSL_HMAC_CHANNEL_AUTHENTICATOR_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "remoting/protocol/channel_authenticator.h"
+
+namespace crypto {
+class RSAPrivateKey;
+} // namespace crypto
+
+namespace net {
+class CertVerifier;
+class DrainableIOBuffer;
+class GrowableIOBuffer;
+class SSLSocket;
+} // namespace net
+
+namespace remoting {
+namespace protocol {
+
+// SslHmacChannelAuthenticator implements ChannelAuthenticator that
+// secures channels using SSL and authenticates them with a shared
+// secret HMAC.
+class SslHmacChannelAuthenticator : public ChannelAuthenticator,
+ public base::NonThreadSafe {
+ public:
+ enum LegacyMode {
+ NONE,
+ SEND_ONLY,
+ RECEIVE_ONLY,
+ };
+
+ // CreateForClient() and CreateForHost() create an authenticator
+ // instances for client and host. |auth_key| specifies shared key
+ // known by both host and client. In case of V1Authenticator the
+ // |auth_key| is set to access code. For EKE-based authentication
+ // |auth_key| is the key established using EKE over the signaling
+ // channel.
+ static SslHmacChannelAuthenticator* CreateForClient(
+ const std::string& remote_cert,
+ const std::string& auth_key);
+
+ static SslHmacChannelAuthenticator* CreateForHost(
+ const std::string& local_cert,
+ crypto::RSAPrivateKey* local_private_key,
+ const std::string& auth_key);
+
+ // TODO(sergeyu): This method is used only for the legacy
+ // V1Authenticator. Remove it when V1Authenticator is removed.
+ void SetLegacyOneWayMode(LegacyMode legacy_mode);
+
+ virtual ~SslHmacChannelAuthenticator();
+
+ // ChannelAuthenticator interface.
+ virtual void SecureAndAuthenticate(
+ net::StreamSocket* socket, const DoneCallback& done_callback) OVERRIDE;
+
+ private:
+ SslHmacChannelAuthenticator(const std::string& auth_key);
+
+ bool is_ssl_server();
+
+ void OnConnected(int result);
+
+ void WriteAuthenticationBytes(bool* callback_called);
+ void OnAuthBytesWritten(int result);
+ bool HandleAuthBytesWritten(int result, bool* callback_called);
+
+ void ReadAuthenticationBytes();
+ void OnAuthBytesRead(int result);
+ bool HandleAuthBytesRead(int result);
+ bool VerifyAuthBytes(const std::string& received_auth_bytes);
+
+ void CheckDone(bool* callback_called);
+
+ // The mutual secret used for authentication.
+ std::string auth_key_;
+
+ // Used in the SERVER mode only.
+ std::string local_cert_;
+ crypto::RSAPrivateKey* local_private_key_;
+
+ // Used in the CLIENT mode only.
+ std::string remote_cert_;
+ scoped_ptr<net::CertVerifier> cert_verifier_;
+
+ LegacyMode legacy_mode_;
+
+ scoped_ptr<net::SSLSocket> socket_;
+ DoneCallback done_callback_;
+
+ scoped_refptr<net::DrainableIOBuffer> auth_write_buf_;
+ scoped_refptr<net::GrowableIOBuffer> auth_read_buf_;
+
+ DISALLOW_COPY_AND_ASSIGN(SslHmacChannelAuthenticator);
+};
+
+} // namespace protocol
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_SSL_HMAC_CHANNEL_AUTHENTICATOR_H_
diff --git a/remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc b/remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc
new file mode 100644
index 0000000..10fdcf7
--- /dev/null
+++ b/remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc
@@ -0,0 +1,151 @@
+// 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.
+
+#include "remoting/protocol/ssl_hmac_channel_authenticator.h"
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "crypto/rsa_private_key.h"
+#include "net/base/net_errors.h"
+#include "remoting/protocol/connection_tester.h"
+#include "remoting/protocol/fake_session.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using testing::_;
+using testing::SaveArg;
+
+namespace remoting {
+namespace protocol {
+
+namespace {
+
+const char kTestSharedSecret[] = "1234-1234-5678";
+const char kTestSharedSecretBad[] = "0000-0000-0001";
+
+class MockChannelDoneCallback {
+ public:
+ MOCK_METHOD2(OnDone, void(net::Error error, net::StreamSocket* socket));
+};
+
+} // namespace
+
+class SslHmacChannelAuthenticatorTest : public testing::Test {
+ public:
+ SslHmacChannelAuthenticatorTest() {
+ }
+ virtual ~SslHmacChannelAuthenticatorTest() {
+ }
+
+ protected:
+ virtual void SetUp() OVERRIDE {
+ FilePath certs_dir;
+ PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir);
+ certs_dir = certs_dir.AppendASCII("net");
+ certs_dir = certs_dir.AppendASCII("data");
+ certs_dir = certs_dir.AppendASCII("ssl");
+ certs_dir = certs_dir.AppendASCII("certificates");
+
+ FilePath cert_path = certs_dir.AppendASCII("unittest.selfsigned.der");
+ ASSERT_TRUE(file_util::ReadFileToString(cert_path, &host_cert_));
+
+ FilePath key_path = certs_dir.AppendASCII("unittest.key.bin");
+ std::string key_string;
+ ASSERT_TRUE(file_util::ReadFileToString(key_path, &key_string));
+ std::vector<uint8> key_vector(
+ reinterpret_cast<const uint8*>(key_string.data()),
+ reinterpret_cast<const uint8*>(key_string.data() +
+ key_string.length()));
+ private_key_.reset(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector));
+ }
+
+ void RunChannelAuth(bool expected_fail) {
+ client_fake_socket_.reset(new FakeSocket());
+ host_fake_socket_.reset(new FakeSocket());
+ client_fake_socket_->PairWith(host_fake_socket_.get());
+
+ client_auth_->SecureAndAuthenticate(
+ client_fake_socket_.release(),
+ base::Bind(&MockChannelDoneCallback::OnDone,
+ base::Unretained(&client_callback_)));
+
+ host_auth_->SecureAndAuthenticate(
+ host_fake_socket_.release(),
+ base::Bind(&MockChannelDoneCallback::OnDone,
+ base::Unretained(&host_callback_)));
+
+ net::StreamSocket* client_socket = NULL;
+ net::StreamSocket* host_socket = NULL;
+
+ if (expected_fail) {
+ EXPECT_CALL(client_callback_, OnDone(net::ERR_FAILED, NULL));
+ EXPECT_CALL(host_callback_, OnDone(net::ERR_FAILED, NULL));
+ } else {
+ EXPECT_CALL(client_callback_, OnDone(net::OK, _))
+ .WillOnce(SaveArg<1>(&client_socket));
+ EXPECT_CALL(host_callback_, OnDone(net::OK, _))
+ .WillOnce(SaveArg<1>(&host_socket));
+ }
+
+ message_loop_.RunAllPending();
+
+ client_socket_.reset(client_socket);
+ host_socket_.reset(host_socket);
+ }
+
+ MessageLoop message_loop_;
+
+ scoped_ptr<crypto::RSAPrivateKey> private_key_;
+ std::string host_cert_;
+ scoped_ptr<FakeSocket> client_fake_socket_;
+ scoped_ptr<FakeSocket> host_fake_socket_;
+ scoped_ptr<ChannelAuthenticator> client_auth_;
+ scoped_ptr<ChannelAuthenticator> host_auth_;
+ MockChannelDoneCallback client_callback_;
+ MockChannelDoneCallback host_callback_;
+ scoped_ptr<net::StreamSocket> client_socket_;
+ scoped_ptr<net::StreamSocket> host_socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(SslHmacChannelAuthenticatorTest);
+};
+
+// Verify that a channel can be connected using a valid shared secret.
+TEST_F(SslHmacChannelAuthenticatorTest, SuccessfulAuth) {
+ client_auth_.reset(SslHmacChannelAuthenticator::CreateForClient(
+ host_cert_, kTestSharedSecret));
+ host_auth_.reset(SslHmacChannelAuthenticator::CreateForHost(
+ host_cert_, private_key_.get(), kTestSharedSecret));
+
+ RunChannelAuth(false);
+
+ EXPECT_TRUE(client_socket_.get() != NULL);
+ EXPECT_TRUE(host_socket_.get() != NULL);
+
+ StreamConnectionTester tester(host_socket_.get(), client_socket_.get(),
+ 100, 2);
+
+ tester.Start();
+ message_loop_.Run();
+ tester.CheckResults();
+}
+
+// Verify that channels cannot be using invalid shared secret.
+TEST_F(SslHmacChannelAuthenticatorTest, InvalidChannelSecret) {
+ client_auth_.reset(SslHmacChannelAuthenticator::CreateForClient(
+ host_cert_, kTestSharedSecretBad));
+ host_auth_.reset(SslHmacChannelAuthenticator::CreateForHost(
+ host_cert_, private_key_.get(), kTestSharedSecret));
+
+ RunChannelAuth(true);
+
+ EXPECT_TRUE(host_socket_.get() == NULL);
+}
+
+} // namespace protocol
+} // namespace remoting
diff --git a/remoting/protocol/v1_authenticator.cc b/remoting/protocol/v1_authenticator.cc
index afeb2d6..c39fd278 100644
--- a/remoting/protocol/v1_authenticator.cc
+++ b/remoting/protocol/v1_authenticator.cc
@@ -9,8 +9,7 @@
#include "crypto/rsa_private_key.h"
#include "remoting/base/constants.h"
#include "remoting/protocol/auth_util.h"
-#include "remoting/protocol/v1_client_channel_authenticator.h"
-#include "remoting/protocol/v1_host_channel_authenticator.h"
+#include "remoting/protocol/ssl_hmac_channel_authenticator.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
using buzz::QName;
@@ -78,7 +77,11 @@ XmlElement* V1ClientAuthenticator::GetNextMessage() {
ChannelAuthenticator*
V1ClientAuthenticator::CreateChannelAuthenticator() const {
DCHECK_EQ(state_, ACCEPTED);
- return new V1ClientChannelAuthenticator(remote_cert_, shared_secret_);
+ SslHmacChannelAuthenticator* result =
+ SslHmacChannelAuthenticator::CreateForClient(
+ remote_cert_, shared_secret_);
+ result->SetLegacyOneWayMode(SslHmacChannelAuthenticator::SEND_ONLY);
+ return result;
};
V1HostAuthenticator::V1HostAuthenticator(
@@ -134,8 +137,11 @@ XmlElement* V1HostAuthenticator::GetNextMessage() {
ChannelAuthenticator*
V1HostAuthenticator::CreateChannelAuthenticator() const {
DCHECK_EQ(state_, ACCEPTED);
- return new V1HostChannelAuthenticator(
- local_cert_, local_private_key_.get(), shared_secret_);
+ SslHmacChannelAuthenticator* result =
+ SslHmacChannelAuthenticator::CreateForHost(
+ local_cert_, local_private_key_.get(), shared_secret_);
+ result->SetLegacyOneWayMode(SslHmacChannelAuthenticator::RECEIVE_ONLY);
+ return result;
};
V1HostAuthenticatorFactory::V1HostAuthenticatorFactory(
diff --git a/remoting/protocol/v1_authenticator_unittest.cc b/remoting/protocol/v1_authenticator_unittest.cc
index b5295fd..5ffa317 100644
--- a/remoting/protocol/v1_authenticator_unittest.cc
+++ b/remoting/protocol/v1_authenticator_unittest.cc
@@ -15,14 +15,13 @@
#include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/connection_tester.h"
#include "remoting/protocol/fake_session.h"
-#include "remoting/protocol/v1_client_channel_authenticator.h"
-#include "remoting/protocol/v1_host_channel_authenticator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
using testing::_;
using testing::DeleteArg;
+using testing::Mock;
using testing::SaveArg;
namespace remoting {
@@ -144,6 +143,9 @@ class V1AuthenticatorTest : public testing::Test {
message_loop_.RunAllPending();
+ Mock::VerifyAndClearExpectations(&client_callback_);
+ Mock::VerifyAndClearExpectations(&host_callback_);
+
client_socket_.reset(client_socket);
host_socket_.reset(host_socket);
}
@@ -200,17 +202,5 @@ TEST_F(V1AuthenticatorTest, InvalidSecret) {
ASSERT_EQ(Authenticator::REJECTED, host_->state());
}
-// Verify that channels cannot be using invalid shared secret.
-TEST_F(V1AuthenticatorTest, InvalidChannelSecret) {
- client_auth_.reset(new V1ClientChannelAuthenticator(
- host_cert_, kTestSharedSecretBad));
- host_auth_.reset(new V1HostChannelAuthenticator(
- host_cert_, private_key_.get(),kTestSharedSecret));
-
- RunChannelAuth(true);
-
- EXPECT_TRUE(host_socket_.get() == NULL);
-}
-
} // namespace protocol
} // namespace remoting
diff --git a/remoting/protocol/v1_client_channel_authenticator.cc b/remoting/protocol/v1_client_channel_authenticator.cc
deleted file mode 100644
index 1f995fc..0000000
--- a/remoting/protocol/v1_client_channel_authenticator.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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.
-
-#include "remoting/protocol/v1_client_channel_authenticator.h"
-
-#include "net/base/cert_verifier.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/ssl_config_service.h"
-#include "net/socket/client_socket_factory.h"
-#include "net/socket/ssl_client_socket.h"
-#include "remoting/protocol/auth_util.h"
-
-namespace remoting {
-namespace protocol {
-
-V1ClientChannelAuthenticator::V1ClientChannelAuthenticator(
- const std::string& host_cert,
- const std::string& shared_secret)
- : host_cert_(host_cert),
- shared_secret_(shared_secret),
- socket_(NULL) {
-}
-
-V1ClientChannelAuthenticator::~V1ClientChannelAuthenticator() {
-}
-
-void V1ClientChannelAuthenticator::SecureAndAuthenticate(
- net::StreamSocket* socket, const DoneCallback& done_callback) {
- DCHECK(CalledOnValidThread());
- DCHECK(socket->IsConnected());
-
- done_callback_ = done_callback;
-
- cert_verifier_.reset(new net::CertVerifier());
-
- net::SSLConfig::CertAndStatus cert_and_status;
- cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID;
- cert_and_status.der_cert = host_cert_;
-
- net::SSLConfig ssl_config;
- // Certificate verification and revocation checking are not needed
- // because we use self-signed certs. Disable it so that the SSL
- // layer doesn't try to initialize OCSP (OCSP works only on the IO
- // thread).
- ssl_config.allowed_bad_certs.push_back(cert_and_status);
- ssl_config.rev_checking_enabled = false;
-
- net::HostPortPair host_and_port(kSslFakeHostName, 0);
- net::SSLClientSocketContext context;
- context.cert_verifier = cert_verifier_.get();
- socket_.reset(
- net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
- socket, host_and_port, ssl_config, NULL, context));
-
- int result = socket_->Connect(
- base::Bind(&V1ClientChannelAuthenticator::OnConnected,
- base::Unretained(this)));
- if (result == net::ERR_IO_PENDING)
- return;
- OnConnected(result);
-}
-
-void V1ClientChannelAuthenticator::OnConnected(int result) {
- if (result != net::OK) {
- LOG(ERROR) << "Failed to establish SSL connection";
- done_callback_.Run(static_cast<net::Error>(result), NULL);
- }
-
- // Get keying material from SSL.
- unsigned char key_material[kAuthDigestLength];
- int export_result = socket_->ExportKeyingMaterial(
- kClientAuthSslExporterLabel, "", key_material, kAuthDigestLength);
- if (export_result != net::OK) {
- LOG(ERROR) << "Error fetching keying material: " << export_result;
- done_callback_.Run(static_cast<net::Error>(export_result), NULL);
- return;
- }
-
- // Generate authentication digest to write to the socket.
- std::string auth_bytes;
- if (!GetAuthBytes(shared_secret_,
- std::string(reinterpret_cast<char*>(key_material),
- kAuthDigestLength),
- &auth_bytes)) {
- done_callback_.Run(net::ERR_FAILED, NULL);
- return;
- }
-
- // Allocate a buffer to write the digest.
- auth_write_buf_ = new net::DrainableIOBuffer(
- new net::StringIOBuffer(auth_bytes), auth_bytes.size());
- WriteAuthenticationBytes();
-}
-
-void V1ClientChannelAuthenticator::WriteAuthenticationBytes() {
- while (true) {
- int result = socket_->Write(
- auth_write_buf_, auth_write_buf_->BytesRemaining(),
- base::Bind(&V1ClientChannelAuthenticator::OnAuthBytesWritten,
- base::Unretained(this)));
- if (result == net::ERR_IO_PENDING)
- break;
- if (!HandleAuthBytesWritten(result))
- break;
- }
-}
-
-void V1ClientChannelAuthenticator::OnAuthBytesWritten(int result) {
- DCHECK(CalledOnValidThread());
-
- if (HandleAuthBytesWritten(result))
- WriteAuthenticationBytes();
-}
-
-bool V1ClientChannelAuthenticator::HandleAuthBytesWritten(int result) {
- if (result <= 0) {
- LOG(ERROR) << "Error writing authentication: " << result;
- done_callback_.Run(static_cast<net::Error>(result), NULL);
- return false;
- }
-
- auth_write_buf_->DidConsume(result);
- if (auth_write_buf_->BytesRemaining() > 0)
- return true;
-
- done_callback_.Run(net::OK, socket_.release());
- return false;
-}
-
-} // namespace protocol
-} // namespace remoting
diff --git a/remoting/protocol/v1_client_channel_authenticator.h b/remoting/protocol/v1_client_channel_authenticator.h
deleted file mode 100644
index c4b9494..0000000
--- a/remoting/protocol/v1_client_channel_authenticator.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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.
-
-#ifndef REMOTING_PROTOCOL_V1_CLIENT_CHANNEL_AUTHENTICATOR_H_
-#define REMOTING_PROTOCOL_V1_CLIENT_CHANNEL_AUTHENTICATOR_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
-#include "net/base/completion_callback.h"
-#include "remoting/protocol/channel_authenticator.h"
-
-namespace net {
-class CertVerifier;
-class DrainableIOBuffer;
-class GrowableIOBuffer;
-class SSLClientSocket;
-} // namespace net
-
-namespace remoting {
-namespace protocol {
-
-class V1ClientChannelAuthenticator : public ChannelAuthenticator,
- public base::NonThreadSafe {
- public:
- V1ClientChannelAuthenticator(const std::string& host_cert,
- const std::string& shared_secret);
- virtual ~V1ClientChannelAuthenticator();
-
- // ChannelAuthenticator implementation.
- virtual void SecureAndAuthenticate(
- net::StreamSocket* socket, const DoneCallback& done_callback) OVERRIDE;
-
- private:
- void OnConnected(int result);
- void WriteAuthenticationBytes();
- void OnAuthBytesWritten(int result);
- bool HandleAuthBytesWritten(int result);
-
- std::string host_cert_;
- std::string shared_secret_;
- scoped_ptr<net::SSLClientSocket> socket_;
- DoneCallback done_callback_;
-
- scoped_ptr<net::CertVerifier> cert_verifier_;
- scoped_refptr<net::DrainableIOBuffer> auth_write_buf_;
-
- DISALLOW_COPY_AND_ASSIGN(V1ClientChannelAuthenticator);
-};
-
-} // namespace protocol
-} // namespace remoting
-
-#endif // REMOTING_PROTOCOL_V1_CLIENT_CHANNEL_AUTHENTICATOR_H_
diff --git a/remoting/protocol/v1_host_channel_authenticator.cc b/remoting/protocol/v1_host_channel_authenticator.cc
deleted file mode 100644
index 3e30ee1..0000000
--- a/remoting/protocol/v1_host_channel_authenticator.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// 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.
-
-#include "remoting/protocol/v1_host_channel_authenticator.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "crypto/rsa_private_key.h"
-#include "crypto/secure_util.h"
-#include "net/base/io_buffer.h"
-#include "net/base/net_errors.h"
-#include "net/base/ssl_config_service.h"
-#include "net/base/x509_certificate.h"
-#include "net/socket/ssl_server_socket.h"
-#include "remoting/protocol/auth_util.h"
-
-namespace remoting {
-namespace protocol {
-
-V1HostChannelAuthenticator::V1HostChannelAuthenticator(
- const std::string& local_cert,
- crypto::RSAPrivateKey* local_private_key,
- const std::string& shared_secret)
- : local_cert_(local_cert),
- local_private_key_(local_private_key),
- shared_secret_(shared_secret),
- socket_(NULL) {
-}
-
-V1HostChannelAuthenticator::~V1HostChannelAuthenticator() {
-}
-
-void V1HostChannelAuthenticator::SecureAndAuthenticate(
- net::StreamSocket* socket, const DoneCallback& done_callback) {
- DCHECK(CalledOnValidThread());
-
- scoped_ptr<net::StreamSocket> channel_socket(socket);
- done_callback_ = done_callback;
-
- scoped_refptr<net::X509Certificate> cert =
- net::X509Certificate::CreateFromBytes(
- local_cert_.data(), local_cert_.length());
- if (!cert) {
- LOG(ERROR) << "Failed to parse X509Certificate";
- done_callback.Run(net::ERR_FAILED, NULL);
- return;
- }
-
- net::SSLConfig ssl_config;
- socket_.reset(net::CreateSSLServerSocket(
- channel_socket.release(), cert, local_private_key_, ssl_config));
-
- int result = socket_->Handshake(
- base::Bind(&V1HostChannelAuthenticator::OnConnected,
- base::Unretained(this)));
- if (result == net::ERR_IO_PENDING) {
- return;
- }
- OnConnected(result);
-}
-
-void V1HostChannelAuthenticator::OnConnected(int result) {
- if (result != net::OK) {
- LOG(ERROR) << "Failed to establish SSL connection";
- done_callback_.Run(static_cast<net::Error>(result), NULL);
- }
-
- // Read an authentication digest.
- auth_read_buf_ = new net::GrowableIOBuffer();
- auth_read_buf_->SetCapacity(kAuthDigestLength);
- DoAuthRead();
-}
-
-void V1HostChannelAuthenticator::DoAuthRead(){
- while (true) {
- int result = socket_->Read(
- auth_read_buf_,
- auth_read_buf_->RemainingCapacity(),
- base::Bind(&V1HostChannelAuthenticator::OnAuthBytesRead,
- base::Unretained(this)));
- if (result == net::ERR_IO_PENDING)
- break;
- if (!HandleAuthBytesRead(result))
- break;
- }
-}
-
-void V1HostChannelAuthenticator::OnAuthBytesRead(int result) {
- DCHECK(CalledOnValidThread());
-
- if (HandleAuthBytesRead(result))
- DoAuthRead();
-}
-
-bool V1HostChannelAuthenticator::HandleAuthBytesRead(int read_result) {
- if (read_result <= 0) {
- done_callback_.Run(static_cast<net::Error>(read_result), NULL);
- return false;
- }
-
- auth_read_buf_->set_offset(auth_read_buf_->offset() + read_result);
- if (auth_read_buf_->RemainingCapacity() > 0)
- return true;
-
- if (!VerifyAuthBytes(std::string(
- auth_read_buf_->StartOfBuffer(),
- auth_read_buf_->StartOfBuffer() + kAuthDigestLength))) {
- LOG(WARNING) << "Mismatched authentication";
- done_callback_.Run(net::ERR_FAILED, NULL);
- return false;
- }
-
- done_callback_.Run(net::OK, socket_.release());
- return false;
-}
-
-bool V1HostChannelAuthenticator::VerifyAuthBytes(
- const std::string& received_auth_bytes) {
- DCHECK(received_auth_bytes.length() == kAuthDigestLength);
-
- unsigned char key_material[kAuthDigestLength];
- int export_result = socket_->ExportKeyingMaterial(
- kClientAuthSslExporterLabel, "", key_material, kAuthDigestLength);
- if (export_result != net::OK) {
- LOG(ERROR) << "Error fetching keying material: " << export_result;
- done_callback_.Run(static_cast<net::Error>(export_result), NULL);
- return false;
- }
-
- std::string auth_bytes;
- if (!GetAuthBytes(shared_secret_,
- std::string(key_material, key_material + kAuthDigestLength),
- &auth_bytes)) {
- done_callback_.Run(net::ERR_FAILED, NULL);
- return false;
- }
-
- return crypto::SecureMemEqual(received_auth_bytes.data(),
- &(auth_bytes[0]), kAuthDigestLength);
-}
-
-} // namespace protocol
-} // namespace remoting
diff --git a/remoting/protocol/v1_host_channel_authenticator.h b/remoting/protocol/v1_host_channel_authenticator.h
deleted file mode 100644
index 56183ab..0000000
--- a/remoting/protocol/v1_host_channel_authenticator.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-#ifndef REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_
-#define REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_
-
-#include "remoting/protocol/channel_authenticator.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "base/threading/non_thread_safe.h"
-
-namespace crypto {
-class RSAPrivateKey;
-} // namespace crypto
-
-namespace net {
-class GrowableIOBuffer;
-class SSLServerSocket;
-class SSLSocket;
-} // namespace net
-
-namespace remoting {
-namespace protocol {
-
-class V1HostChannelAuthenticator : public ChannelAuthenticator,
- public base::NonThreadSafe {
- public:
- // Caller retains ownership of |local_private_key|. It must exist
- // while this object exists.
- V1HostChannelAuthenticator(const std::string& local_cert,
- crypto::RSAPrivateKey* local_private_key,
- const std::string& shared_secret);
- virtual ~V1HostChannelAuthenticator();
-
- // ChannelAuthenticator interface.
- virtual void SecureAndAuthenticate(
- net::StreamSocket* socket, const DoneCallback& done_callback) OVERRIDE;
-
- private:
- void OnConnected(int result);
- void DoAuthRead();
- void OnAuthBytesRead(int result);
- bool HandleAuthBytesRead(int result);
- bool VerifyAuthBytes(const std::string& received_auth_bytes);
-
- std::string local_cert_;
- crypto::RSAPrivateKey* local_private_key_;
- std::string shared_secret_;
- scoped_ptr<net::SSLServerSocket> socket_;
- DoneCallback done_callback_;
-
- scoped_refptr<net::GrowableIOBuffer> auth_read_buf_;
-
- DISALLOW_COPY_AND_ASSIGN(V1HostChannelAuthenticator);
-};
-
-} // namespace protocol
-} // namespace remoting
-
-#endif // REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 5b2bc26..19e995e 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -814,16 +814,14 @@
'protocol/session_manager.h',
'protocol/socket_reader_base.cc',
'protocol/socket_reader_base.h',
+ 'protocol/ssl_hmac_channel_authenticator.cc',
+ 'protocol/ssl_hmac_channel_authenticator.h',
'protocol/transport_config.cc',
'protocol/transport_config.h',
'protocol/util.cc',
'protocol/util.h',
'protocol/v1_authenticator.cc',
'protocol/v1_authenticator.h',
- 'protocol/v1_client_channel_authenticator.cc',
- 'protocol/v1_client_channel_authenticator.h',
- 'protocol/v1_host_channel_authenticator.cc',
- 'protocol/v1_host_channel_authenticator.h',
'protocol/video_reader.cc',
'protocol/video_reader.h',
'protocol/video_stub.h',
@@ -946,6 +944,7 @@
'protocol/ppapi_module_stub.cc',
'protocol/rtp_video_reader_unittest.cc',
'protocol/rtp_video_writer_unittest.cc',
+ 'protocol/ssl_hmac_channel_authenticator_unittest.cc',
'protocol/v1_authenticator_unittest.cc',
'run_all_unittests.cc',
],