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