1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef REMOTING_PROTOCOL_NEGOTIATING_AUTHENTICATOR_BASE_H_ 6 #define REMOTING_PROTOCOL_NEGOTIATING_AUTHENTICATOR_BASE_H_ 7 8 #include <memory> 9 #include <string> 10 #include <vector> 11 12 #include "base/gtest_prod_util.h" 13 #include "base/macros.h" 14 #include "base/memory/ref_counted.h" 15 #include "remoting/protocol/authenticator.h" 16 17 namespace jingle_xmpp { 18 struct StaticQName; 19 } // namespace jingle_xmpp 20 21 namespace remoting { 22 namespace protocol { 23 24 // This class provides the common base for a meta-authenticator that allows 25 // clients and hosts that support multiple authentication methods to negotiate a 26 // method to use. 27 // 28 // The typical flow is: 29 // * Client sends a message to host with its supported methods. 30 // (clients may additionally pick a method and send its first message). 31 // * Host picks a method and sends its first message (if any). 32 // (if a message for that method was sent by the client, it is processed). 33 // * Client creates the authenticator selected by the host. If the method 34 // starts with a message from the host, it is processed. 35 // * Client and host exchange messages until the authentication is ACCEPTED or 36 // REJECTED. 37 // 38 // The details: 39 // * CreateAuthenticator() may be asynchronous (i.e. require user interaction 40 // to determine initial parameters, like PIN). This happens inside 41 // ProcessMessage, so to the outside this behaves like any asynchronous 42 // message processing. Internally, CreateAuthenticator() receives a 43 // callback, that will resume the authentication once the authenticator is 44 // created. If there is already a message to be processed by the new 45 // authenticator, this callback includes a call to the underlying 46 // ProcessMessage(). 47 // * Some authentication methods may have a specific starting direction (e.g. 48 // host always sends the first message), while others are versatile (e.g. 49 // SPAKE, where either side can send the first message). When an 50 // authenticator is created, it is given a preferred initial state, which 51 // the authenticator may ignore. 52 // * If the new authenticator state doesn't match the preferred one, 53 // the NegotiatingAuthenticator deals with that, by sending an empty 54 // <authenticator> stanza if the method has no message to send, and 55 // ignoring such empty messages on the receiving end. 56 // * The client may optimistically pick a method on its first message (assuming 57 // it doesn't require user interaction to start). If the host doesn't 58 // support that method, it will just discard that message, and choose 59 // another method from the client's supported methods list. 60 // * The host never sends its own supported methods back to the client, so once 61 // the host picks a method from the client's list, it's final. 62 // * Any change in this class must maintain compatibility between any version 63 // mix of webapp, client plugin and host, for both Me2Me and IT2Me. 64 class NegotiatingAuthenticatorBase : public Authenticator { 65 public: 66 // Method represents an authentication algorithm. 67 enum class Method { 68 INVALID, 69 70 // SPAKE2 with P224 using access code in plain-text. Used for It2Me. 71 // TODO(sergeyu): Remove and use SHARED_SECRET_SPAKE2_CURVE25519 once 72 // the population of M50 hosts (which require this for IT2Me) is 73 // sufficiently low: crbug.com/607643. 74 SHARED_SECRET_PLAIN_SPAKE2_P224, 75 76 // SPAKE2 PIN or access code hashed with host_id using HMAC-SHA256. 77 SHARED_SECRET_SPAKE2_P224, 78 SHARED_SECRET_SPAKE2_CURVE25519, 79 80 // SPAKE2 using shared pairing secret. Falls back to PIN-based 81 // authentication when pairing fails. 82 PAIRED_SPAKE2_P224, 83 PAIRED_SPAKE2_CURVE25519, 84 85 // Authentication using third-party authentication server. 86 // SPAKE2 with P224 using shared pairing secret. Falls back to PIN-based 87 // authentication when it fails to authenticate using paired secret. 88 THIRD_PARTY_SPAKE2_P224, 89 THIRD_PARTY_SPAKE2_CURVE25519, 90 }; 91 92 ~NegotiatingAuthenticatorBase() override; 93 94 // Authenticator interface. 95 State state() const override; 96 bool started() const override; 97 RejectionReason rejection_reason() const override; 98 const std::string& GetAuthKey() const override; 99 std::unique_ptr<ChannelAuthenticator> CreateChannelAuthenticator() 100 const override; 101 102 // Calls |current_authenticator_| to process |message|, passing the supplied 103 // |resume_callback|. 104 void ProcessMessageInternal(const jingle_xmpp::XmlElement* message, 105 base::OnceClosure resume_callback); 106 107 protected: 108 friend class NegotiatingAuthenticatorTest; 109 110 static const jingle_xmpp::StaticQName kMethodAttributeQName; 111 static const jingle_xmpp::StaticQName kSupportedMethodsAttributeQName; 112 static const char kSupportedMethodsSeparator; 113 114 static const jingle_xmpp::StaticQName kPairingInfoTag; 115 static const jingle_xmpp::StaticQName kClientIdAttribute; 116 117 // Parses a string that defines an authentication method. Returns 118 // Method::INVALID if the string is invalid. 119 static Method ParseMethodString(const std::string& value); 120 121 // Returns string representation of |method|. 122 static std::string MethodToString(Method method); 123 124 explicit NegotiatingAuthenticatorBase(Authenticator::State initial_state); 125 126 void AddMethod(Method method); 127 128 // Updates |state_| to reflect the current underlying authenticator state. 129 // |resume_callback| is called after the state is updated. 130 void UpdateState(base::OnceClosure resume_callback); 131 132 // Gets the next message from |current_authenticator_|, if any, and fills in 133 // the 'method' tag with |current_method_|. 134 virtual std::unique_ptr<jingle_xmpp::XmlElement> GetNextMessageInternal(); 135 136 std::vector<Method> methods_; 137 Method current_method_ = Method::INVALID; 138 std::unique_ptr<Authenticator> current_authenticator_; 139 State state_; 140 RejectionReason rejection_reason_ = INVALID_CREDENTIALS; 141 142 private: 143 DISALLOW_COPY_AND_ASSIGN(NegotiatingAuthenticatorBase); 144 }; 145 146 } // namespace protocol 147 } // namespace remoting 148 149 #endif // REMOTING_PROTOCOL_NEGOTIATING_AUTHENTICATOR_BASE_H_ 150