Commit 3c759246 authored by sergeyu's avatar sergeyu Committed by Commit bot

Simplify AuthenticationMethod type and PIN hash handling.

1 Previously AuthenticationMethod was a class. Replaced
  it with a simple enum.
2 Removed SharedSecretHash struct and simplified the code
  that passes around PIN hash.
3 If the host config contains PIN in plain text then now
  the host hashes it instead of using spake2_plain
  authenticator method. This means that spake2_hmac is
  always used for Me2Me even when the PIN is not hashed
  in the config.

This change will make it easier to add new authentication
methods.

BUG=589698

Review URL: https://codereview.chromium.org/1755273003

Cr-Commit-Position: refs/heads/master@{#379365}
parent 244f962e
......@@ -81,12 +81,12 @@ ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime* jni_runtime,
host_pubkey));
std::vector<protocol::AuthenticationMethod> auth_methods;
auth_methods.push_back(protocol::AuthenticationMethod::Spake2Pair());
auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
protocol::AuthenticationMethod::HMAC_SHA256));
auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
protocol::AuthenticationMethod::NONE));
auth_methods.push_back(protocol::AuthenticationMethod::ThirdParty());
auth_methods.push_back(protocol::AuthenticationMethod::THIRD_PARTY);
auth_methods.push_back(protocol::AuthenticationMethod::SPAKE2_PAIR);
auth_methods.push_back(
protocol::AuthenticationMethod::SPAKE2_SHARED_SECRET_HMAC);
auth_methods.push_back(
protocol::AuthenticationMethod::SPAKE2_SHARED_SECRET_PLAIN);
authenticator_.reset(new protocol::NegotiatingClientAuthenticator(
pairing_id, pairing_secret, host_id_,
......
......@@ -705,12 +705,12 @@ void ChromotingInstance::HandleConnect(const base::DictionaryValue& data) {
host_public_key));
std::vector<protocol::AuthenticationMethod> auth_methods;
auth_methods.push_back(protocol::AuthenticationMethod::ThirdParty());
auth_methods.push_back(protocol::AuthenticationMethod::Spake2Pair());
auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
protocol::AuthenticationMethod::HMAC_SHA256));
auth_methods.push_back(protocol::AuthenticationMethod::Spake2(
protocol::AuthenticationMethod::NONE));
auth_methods.push_back(protocol::AuthenticationMethod::THIRD_PARTY);
auth_methods.push_back(protocol::AuthenticationMethod::SPAKE2_PAIR);
auth_methods.push_back(
protocol::AuthenticationMethod::SPAKE2_SHARED_SECRET_HMAC);
auth_methods.push_back(
protocol::AuthenticationMethod::SPAKE2_SHARED_SECRET_PLAIN);
scoped_ptr<protocol::Authenticator> authenticator(
new protocol::NegotiatingClientAuthenticator(
......
......@@ -11,10 +11,33 @@
namespace remoting {
bool ParsePinHashFromConfig(const std::string& value,
const std::string& host_id,
std::string* pin_hash_out) {
size_t separator = value.find(':');
if (separator == std::string::npos)
return false;
if (!base::Base64Decode(value.substr(separator + 1), pin_hash_out))
return false;
std::string function_name = value.substr(0, separator);
if (function_name == "plain") {
*pin_hash_out = protocol::ApplySharedSecretHashFunction(
protocol::HashFunction::HMAC_SHA256, host_id, *pin_hash_out);
return true;
} else if (function_name == "hmac") {
return true;
}
pin_hash_out->clear();
return false;
}
std::string MakeHostPinHash(const std::string& host_id,
const std::string& pin) {
std::string hash = protocol::AuthenticationMethod::ApplyHashFunction(
protocol::AuthenticationMethod::HMAC_SHA256, host_id, pin);
std::string hash = protocol::ApplySharedSecretHashFunction(
protocol::HashFunction::HMAC_SHA256, host_id, pin);
std::string hash_base64;
base::Base64Encode(hash, &hash_base64);
return "hmac:" + hash_base64;
......@@ -23,15 +46,14 @@ std::string MakeHostPinHash(const std::string& host_id,
bool VerifyHostPinHash(const std::string& hash,
const std::string& host_id,
const std::string& pin) {
remoting::protocol::SharedSecretHash hash_parsed;
if (!hash_parsed.Parse(hash)) {
LOG(FATAL) << "Invalid hash.";
std::string hash_parsed;
if (!ParsePinHashFromConfig(hash, host_id, &hash_parsed)) {
LOG(FATAL) << "Failed to parse PIN hash.";
return false;
}
std::string hash_calculated =
remoting::protocol::AuthenticationMethod::ApplyHashFunction(
hash_parsed.hash_function, host_id, pin);
return hash_calculated == hash_parsed.value;
std::string hash_calculated = protocol::ApplySharedSecretHashFunction(
protocol::HashFunction::HMAC_SHA256, host_id, pin);
return hash_calculated == hash_parsed;
}
} // namespace remoting
......@@ -13,6 +13,14 @@ namespace remoting {
// hashed host ID and PIN.
std::string MakeHostPinHash(const std::string& host_id, const std::string& pin);
// Parse string representation of a shared secret hash. The value can be either
// "plain:<pin_in_base64>" or "hmac:<pin_hmac_in_base64>". In the first case the
// returned value is automatically hashed. False is returned if |value| is in
// invalid format.
bool ParsePinHashFromConfig(const std::string& value,
const std::string& host_id,
std::string* pin_hash_out);
// Extracts the hash function from the given hash, uses it to calculate the
// hash of the given host ID and PIN, and compares that hash to the given hash.
// Returns true if the calculated and given hashes are equal.
......
......@@ -69,6 +69,7 @@
#include "remoting/host/me2me_desktop_environment.h"
#include "remoting/host/oauth_token_getter_impl.h"
#include "remoting/host/pairing_registry_delegate.h"
#include "remoting/host/pin_hash.h"
#include "remoting/host/policy_watcher.h"
#include "remoting/host/security_key/gnubby_auth_handler.h"
#include "remoting/host/security_key/gnubby_extension.h"
......@@ -404,7 +405,7 @@ class HostProcess : public ConfigWatcher::Delegate,
scoped_ptr<ConfigWatcher> config_watcher_;
std::string host_id_;
protocol::SharedSecretHash host_secret_hash_;
std::string pin_hash_;
scoped_refptr<RsaKeyPair> key_pair_;
std::string oauth_refresh_token_;
std::string serialized_config_;
......@@ -795,9 +796,9 @@ void HostProcess::CreateAuthenticatorFactory() {
pairing_registry = pairing_registry_;
}
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithPin(
use_service_account_, host_owner_, local_certificate, key_pair_,
client_domain_, host_secret_hash_, pairing_registry);
client_domain_, pin_hash_, pairing_registry);
host_->set_pairing_registry(pairing_registry);
} else {
......@@ -1034,13 +1035,9 @@ bool HostProcess::ApplyConfig(const base::DictionaryValue& config) {
}
std::string host_secret_hash_string;
if (!config.GetString(kHostSecretHashConfigPath,
&host_secret_hash_string)) {
host_secret_hash_string = "plain:";
}
if (!host_secret_hash_.Parse(host_secret_hash_string)) {
LOG(ERROR) << "Invalid host_secret_hash.";
if (!config.GetString(kHostSecretHashConfigPath, &host_secret_hash_string) ||
!ParsePinHashFromConfig(host_secret_hash_string, host_id_, &pin_hash_)) {
LOG(ERROR) << "Cannot parse host_secret_hash configuration value.";
return false;
}
......
......@@ -4,63 +4,60 @@
#include "remoting/protocol/authentication_method.h"
#include <stddef.h>
#include "base/base64.h"
#include "base/logging.h"
#include "crypto/hmac.h"
#include "remoting/protocol/auth_util.h"
#include "remoting/protocol/name_value_map.h"
namespace remoting {
namespace protocol {
// static
AuthenticationMethod AuthenticationMethod::Invalid() {
return AuthenticationMethod();
const NameMapElement<AuthenticationMethod> kAuthenticationMethodStrings[] = {
{AuthenticationMethod::SPAKE2_SHARED_SECRET_PLAIN, "spake2_plain"},
{AuthenticationMethod::SPAKE2_SHARED_SECRET_HMAC, "spake2_hmac"},
{AuthenticationMethod::SPAKE2_PAIR, "spake2_pair"},
{AuthenticationMethod::THIRD_PARTY, "third_party"}};
AuthenticationMethod ParseAuthenticationMethodString(const std::string& value) {
AuthenticationMethod result;
if (!NameToValue(kAuthenticationMethodStrings, value, &result))
return AuthenticationMethod::INVALID;
return result;
}
// static
AuthenticationMethod AuthenticationMethod::Spake2(HashFunction hash_function) {
return AuthenticationMethod(SPAKE2, hash_function);
const std::string AuthenticationMethodToString(
AuthenticationMethod method) {
return ValueToName(kAuthenticationMethodStrings, method);
}
// static
AuthenticationMethod AuthenticationMethod::Spake2Pair() {
return AuthenticationMethod(SPAKE2_PAIR, HMAC_SHA256);
}
HashFunction GetHashFunctionForAuthenticationMethod(
AuthenticationMethod method) {
switch (method) {
case AuthenticationMethod::INVALID:
NOTREACHED();
return HashFunction::NONE;
// static
AuthenticationMethod AuthenticationMethod::ThirdParty() {
return AuthenticationMethod(THIRD_PARTY, NONE);
}
case AuthenticationMethod::SPAKE2_SHARED_SECRET_PLAIN:
case AuthenticationMethod::THIRD_PARTY:
return HashFunction::NONE;
// static
AuthenticationMethod AuthenticationMethod::FromString(
const std::string& value) {
if (value == "spake2_pair") {
return Spake2Pair();
} else if (value == "spake2_plain") {
return Spake2(NONE);
} else if (value == "spake2_hmac") {
return Spake2(HMAC_SHA256);
} else if (value == "third_party") {
return ThirdParty();
} else {
return AuthenticationMethod::Invalid();
case AuthenticationMethod::SPAKE2_SHARED_SECRET_HMAC:
case AuthenticationMethod::SPAKE2_PAIR:
return HashFunction::HMAC_SHA256;
}
NOTREACHED();
return HashFunction::NONE;
}
// static
std::string AuthenticationMethod::ApplyHashFunction(
HashFunction hash_function,
const std::string& tag,
const std::string& shared_secret) {
std::string ApplySharedSecretHashFunction(HashFunction hash_function,
const std::string& tag,
const std::string& shared_secret) {
switch (hash_function) {
case NONE:
case HashFunction::NONE:
return shared_secret;
break;
case HMAC_SHA256: {
case HashFunction::HMAC_SHA256: {
crypto::HMAC response(crypto::HMAC::SHA256);
if (!response.Init(tag)) {
LOG(FATAL) << "HMAC::Init failed";
......@@ -79,76 +76,5 @@ std::string AuthenticationMethod::ApplyHashFunction(
return shared_secret;
}
AuthenticationMethod::AuthenticationMethod()
: type_(INVALID),
hash_function_(NONE) {
}
AuthenticationMethod::AuthenticationMethod(MethodType type,
HashFunction hash_function)
: type_(type),
hash_function_(hash_function) {
DCHECK_NE(type_, INVALID);
}
AuthenticationMethod::HashFunction AuthenticationMethod::hash_function() const {
DCHECK(is_valid());
return hash_function_;
}
const std::string AuthenticationMethod::ToString() const {
DCHECK(is_valid());
switch (type_) {
case INVALID:
NOTREACHED();
break;
case SPAKE2_PAIR:
return "spake2_pair";
case SPAKE2:
switch (hash_function_) {
case NONE:
return "spake2_plain";
case HMAC_SHA256:
return "spake2_hmac";
}
break;
case THIRD_PARTY:
return "third_party";
}
return "invalid";
}
bool AuthenticationMethod::operator ==(
const AuthenticationMethod& other) const {
return type_ == other.type_ &&
hash_function_ == other.hash_function_;
}
bool SharedSecretHash::Parse(const std::string& as_string) {
size_t separator = as_string.find(':');
if (separator == std::string::npos)
return false;
std::string function_name = as_string.substr(0, separator);
if (function_name == "plain") {
hash_function = AuthenticationMethod::NONE;
} else if (function_name == "hmac") {
hash_function = AuthenticationMethod::HMAC_SHA256;
} else {
return false;
}
if (!base::Base64Decode(as_string.substr(separator + 1), &value)) {
return false;
}
return true;
}
} // namespace protocol
} // namespace remoting
// Copyright (c) 2012 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.
// AuthenticationMethod represents an authentication algorithm and its
// configuration. It knows how to parse and format authentication
// method names.
// Currently the following methods are supported:
// spake2_plain - SPAKE2 without hashing applied to the password.
// spake2_hmac - SPAKE2 with HMAC hashing of the password.
#ifndef REMOTING_PROTOCOL_AUTHENTICATION_METHOD_H_
#define REMOTING_PROTOCOL_AUTHENTICATION_METHOD_H_
......@@ -19,73 +11,37 @@ namespace protocol {
class Authenticator;
class AuthenticationMethod {
public:
enum MethodType {
INVALID,
SPAKE2,
SPAKE2_PAIR,
THIRD_PARTY
};
enum HashFunction {
NONE,
HMAC_SHA256,
};
// Constructors for various authentication methods.
static AuthenticationMethod Invalid();
static AuthenticationMethod Spake2(HashFunction hash_function);
static AuthenticationMethod Spake2Pair();
static AuthenticationMethod ThirdParty();
// Parses a string that defines an authentication method. Returns an
// invalid value if the string is invalid.
static AuthenticationMethod FromString(const std::string& value);
// Applies the specified hash function to |shared_secret| with the
// specified |tag| as a key.
static std::string ApplyHashFunction(HashFunction hash_function,
const std::string& tag,
const std::string& shared_secret);
bool is_valid() const { return type_ != INVALID; }
MethodType type() const { return type_; }
// Following methods are valid only when is_valid() returns true.
// Hash function applied to the shared secret on both ends.
HashFunction hash_function() const;
// Returns string representation of the value stored in this object.
const std::string ToString() const;
// AuthenticationMethod represents an authentication algorithm.
enum class AuthenticationMethod {
INVALID,
SPAKE2_SHARED_SECRET_PLAIN,
SPAKE2_SHARED_SECRET_HMAC,
SPAKE2_PAIR,
THIRD_PARTY
};
// Comparison operators so that std::find() can be used with
// collections of this class.
bool operator ==(const AuthenticationMethod& other) const;
bool operator !=(const AuthenticationMethod& other) const {
return !(*this == other);
}
enum class HashFunction {
NONE,
HMAC_SHA256,
};
protected:
AuthenticationMethod();
AuthenticationMethod(MethodType type, HashFunction hash_function);
// Parses a string that defines an authentication method. Returns
// AuthenticationMethod::INVALID if the string is invalid.
AuthenticationMethod ParseAuthenticationMethodString(const std::string& value);
MethodType type_;
HashFunction hash_function_;
};
// Returns string representation of |method|.
const std::string AuthenticationMethodToString(AuthenticationMethod method);
// SharedSecretHash stores hash of a host secret paired with the type
// of the hashing function.
struct SharedSecretHash {
AuthenticationMethod::HashFunction hash_function;
std::string value;
// Returns hash function applied to the shared secret on both ends for the
// spefied |method|.
HashFunction GetHashFunctionForAuthenticationMethod(
AuthenticationMethod method);
// Parse string representation of a shared secret hash. The |as_string|
// must be in form "<hash_function>:<hash_value_base64>".
bool Parse(const std::string& as_string);
};
// Applies the specified hash function to |shared_secret| with the
// specified |tag| as a key.
std::string ApplySharedSecretHashFunction(HashFunction hash_function,
const std::string& tag,
const std::string& shared_secret);
} // namespace protocol
} // namespace remoting
......
......@@ -16,16 +16,14 @@ namespace protocol {
It2MeHostAuthenticatorFactory::It2MeHostAuthenticatorFactory(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& shared_secret,
const std::string& access_code,
const std::string& required_client_domain)
: local_cert_(local_cert),
key_pair_(key_pair),
shared_secret_(shared_secret),
required_client_domain_(required_client_domain) {
}
access_code_(access_code),
required_client_domain_(required_client_domain) {}
It2MeHostAuthenticatorFactory::~It2MeHostAuthenticatorFactory() {
}
It2MeHostAuthenticatorFactory::~It2MeHostAuthenticatorFactory() {}
scoped_ptr<Authenticator> It2MeHostAuthenticatorFactory::CreateAuthenticator(
const std::string& local_jid,
......@@ -48,9 +46,8 @@ scoped_ptr<Authenticator> It2MeHostAuthenticatorFactory::CreateAuthenticator(
}
}
return NegotiatingHostAuthenticator::CreateWithSharedSecret(
local_cert_, key_pair_, shared_secret_, AuthenticationMethod::NONE,
nullptr);
return NegotiatingHostAuthenticator::CreateForIt2Me(local_cert_, key_pair_,
access_code_);
}
} // namespace protocol
......
......@@ -23,11 +23,10 @@ namespace protocol {
// understands both the V2 and legacy V1 authentication mechanisms.
class It2MeHostAuthenticatorFactory : public AuthenticatorFactory {
public:
It2MeHostAuthenticatorFactory(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& shared_secret,
const std::string& required_client_domain);
It2MeHostAuthenticatorFactory(const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& access_code,
const std::string& required_client_domain);
~It2MeHostAuthenticatorFactory() override;
// AuthenticatorFactory interface.
......@@ -39,7 +38,7 @@ class It2MeHostAuthenticatorFactory : public AuthenticatorFactory {
private:
std::string local_cert_;
scoped_refptr<RsaKeyPair> key_pair_;
std::string shared_secret_;
std::string access_code_;
std::string required_client_domain_;
DISALLOW_COPY_AND_ASSIGN(It2MeHostAuthenticatorFactory);
......
......@@ -20,14 +20,13 @@ namespace remoting {
namespace protocol {
// static
scoped_ptr<AuthenticatorFactory>
Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
scoped_ptr<AuthenticatorFactory> Me2MeHostAuthenticatorFactory::CreateWithPin(
bool use_service_account,
const std::string& host_owner,
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& required_client_domain,
const SharedSecretHash& shared_secret_hash,
const std::string& pin_hash,
scoped_refptr<PairingRegistry> pairing_registry) {
scoped_ptr<Me2MeHostAuthenticatorFactory> result(
new Me2MeHostAuthenticatorFactory());
......@@ -36,7 +35,7 @@ Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
result->local_cert_ = local_cert;
result->key_pair_ = key_pair;
result->required_client_domain_ = required_client_domain;
result->shared_secret_hash_ = shared_secret_hash;
result->pin_hash_ = pin_hash;
result->pairing_registry_ = pairing_registry;
return std::move(result);
}
......@@ -128,9 +127,8 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
local_jid, remote_jid));
}
return NegotiatingHostAuthenticator::CreateWithSharedSecret(
local_cert_, key_pair_, shared_secret_hash_.value,
shared_secret_hash_.hash_function, pairing_registry_);
return NegotiatingHostAuthenticator::CreateWithPin(
local_cert_, key_pair_, pin_hash_, pairing_registry_);
}
return make_scoped_ptr(
......
......@@ -27,13 +27,13 @@ class PairingRegistry;
class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
public:
// Create a factory that dispenses shared secret authenticators.
static scoped_ptr<AuthenticatorFactory> CreateWithSharedSecret(
static scoped_ptr<AuthenticatorFactory> CreateWithPin(
bool use_service_account,
const std::string& host_owner,
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& required_client_domain,
const SharedSecretHash& shared_secret_hash,
const std::string& pin_hash,
scoped_refptr<PairingRegistry> pairing_registry);
// Create a factory that dispenses third party authenticators.
......@@ -62,8 +62,8 @@ class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
scoped_refptr<RsaKeyPair> key_pair_;
std::string required_client_domain_;
// Used only for shared secret host authenticators.
SharedSecretHash shared_secret_hash_;
// Used only for PIN-based host authenticators.
std::string pin_hash_;
// Used only for third party host authenticators.
scoped_ptr<TokenValidatorFactory> token_validator_factory_;
......
......@@ -28,13 +28,9 @@ const char NegotiatingAuthenticatorBase::kSupportedMethodsSeparator = ',';
NegotiatingAuthenticatorBase::NegotiatingAuthenticatorBase(
Authenticator::State initial_state)
: current_method_(AuthenticationMethod::Invalid()),
state_(initial_state),
rejection_reason_(INVALID_CREDENTIALS) {
}
: state_(initial_state) {}
NegotiatingAuthenticatorBase::~NegotiatingAuthenticatorBase() {
}
NegotiatingAuthenticatorBase::~NegotiatingAuthenticatorBase() {}
Authenticator::State NegotiatingAuthenticatorBase::state() const {
return state_;
......@@ -83,7 +79,7 @@ void NegotiatingAuthenticatorBase::UpdateState(
scoped_ptr<buzz::XmlElement>
NegotiatingAuthenticatorBase::GetNextMessageInternal() {
DCHECK_EQ(state(), MESSAGE_READY);
DCHECK(current_method_.is_valid());
DCHECK(current_method_ != AuthenticationMethod::INVALID);
scoped_ptr<buzz::XmlElement> result;
if (current_authenticator_->state() == MESSAGE_READY) {
......@@ -93,13 +89,13 @@ NegotiatingAuthenticatorBase::GetNextMessageInternal() {
}
state_ = current_authenticator_->state();
DCHECK(state_ == ACCEPTED || state_ == WAITING_MESSAGE);
result->AddAttr(kMethodAttributeQName, current_method_.ToString());
result->AddAttr(kMethodAttributeQName,
AuthenticationMethodToString(current_method_));
return result;
}
void NegotiatingAuthenticatorBase::AddMethod(
const AuthenticationMethod& method) {
DCHECK(method.is_valid());
void NegotiatingAuthenticatorBase::AddMethod(AuthenticationMethod method) {
DCHECK(method != AuthenticationMethod::INVALID);
methods_.push_back(method);
}
......
......@@ -85,7 +85,7 @@ class NegotiatingAuthenticatorBase : public Authenticator {
explicit NegotiatingAuthenticatorBase(Authenticator::State initial_state);
void AddMethod(const AuthenticationMethod& method);
void AddMethod(AuthenticationMethod method);
// Updates |state_| to reflect the current underlying authenticator state.
// |resume_callback| is called after the state is updated.
......@@ -96,10 +96,10 @@ class NegotiatingAuthenticatorBase : public Authenticator {
virtual scoped_ptr<buzz::XmlElement> GetNextMessageInternal();
std::vector<AuthenticationMethod> methods_;
AuthenticationMethod current_method_;