summaryrefslogtreecommitdiffstats
path: root/remoting/protocol/negotiating_authenticator_base.h
blob: c76697e8f565f2af035ed72635fd60217292eefd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// 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.

#ifndef REMOTING_PROTOCOL_NEGOTIATING_AUTHENTICATOR_BASE_H_
#define REMOTING_PROTOCOL_NEGOTIATING_AUTHENTICATOR_BASE_H_

#include <string>
#include <vector>

#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "remoting/protocol/authenticator.h"

namespace buzz {
struct StaticQName;
}  // namespace buzz

namespace remoting {
namespace protocol {

// This class provides the common base for a meta-authenticator that allows
// clients and hosts that support multiple authentication methods to negotiate a
// method to use.
//
// The typical flow is:
//  * Client sends a message to host with its supported methods.
//      (clients may additionally pick a method and send its first message).
//  * Host picks a method and sends its first message (if any).
//      (if a message for that method was sent by the client, it is processed).
//  * Client creates the authenticator selected by the host. If the method
//      starts with a message from the host, it is processed.
//  * Client and host exchange messages until the authentication is ACCEPTED or
//      REJECTED.
//
// The details:
//  * CreateAuthenticator() may be asynchronous (i.e. require user interaction
//      to determine initial parameters, like PIN). This happens inside
//      ProcessMessage, so to the outside this behaves like any asynchronous
//      message processing. Internally, CreateAuthenticator() receives a
//      callback, that will resume the authentication once the authenticator is
//      created. If there is already a message to be processed by the new
//      authenticator, this callback includes a call to the underlying
//      ProcessMessage().
//  * Some authentication methods may have a specific starting direction (e.g.
//      host always sends the first message), while others are versatile (e.g.
//      SPAKE, where either side can send the first message). When an
//      authenticator is created, it is given a preferred initial state, which
//      the authenticator may ignore.
//  * If the new authenticator state doesn't match the preferred one,
//      the NegotiatingAuthenticator deals with that, by sending an empty
//      <authenticator> stanza if the method has no message to send, and
//      ignoring such empty messages on the receiving end.
//  * The client may optimistically pick a method on its first message (assuming
//      it doesn't require user interaction to start). If the host doesn't
//      support that method, it will just discard that message, and choose
//      another method from the client's supported methods list.
//  * The host never sends its own supported methods back to the client, so once
//      the host picks a method from the client's list, it's final.
//  * Any change in this class must maintain compatibility between any version
//      mix of webapp, client plugin and host, for both Me2Me and IT2Me.
class NegotiatingAuthenticatorBase : public Authenticator {
 public:
  // Method represents an authentication algorithm.
  enum class Method {
    INVALID,

    // SPAKE2 PIN or access code hashed with host_id using HMAC-SHA256.
    SHARED_SECRET_SPAKE2_P224,
    SHARED_SECRET_SPAKE2_CURVE25519,

    // SPAKE2 using shared pairing secret. Falls back to PIN-based
    // authentication when pairing fails.
    PAIRED_SPAKE2_P224,
    PAIRED_SPAKE2_CURVE25519,

    // Authentication using third-party authentication server.
    // SPAKE2 with P224 using shared pairing secret. Falls back to PIN-based
    // authentication when it fails to authenticate using paired secret.
    THIRD_PARTY_SPAKE2_P224,
    THIRD_PARTY_SPAKE2_CURVE25519,
  };

  ~NegotiatingAuthenticatorBase() override;

  // Authenticator interface.
  State state() const override;
  bool started() const override;
  RejectionReason rejection_reason() const override;
  const std::string& GetAuthKey() const override;
  scoped_ptr<ChannelAuthenticator> CreateChannelAuthenticator() const override;

  // Calls |current_authenticator_| to process |message|, passing the supplied
  // |resume_callback|.
  void ProcessMessageInternal(const buzz::XmlElement* message,
                              const base::Closure& resume_callback);

 protected:
  friend class NegotiatingAuthenticatorTest;

  static const buzz::StaticQName kMethodAttributeQName;
  static const buzz::StaticQName kSupportedMethodsAttributeQName;
  static const char kSupportedMethodsSeparator;

  static const buzz::StaticQName kPairingInfoTag;
  static const buzz::StaticQName kClientIdAttribute;

  // Parses a string that defines an authentication method. Returns
  // Method::INVALID if the string is invalid.
  static Method ParseMethodString(const std::string& value);

  // Returns string representation of |method|.
  static std::string MethodToString(Method method);

  explicit NegotiatingAuthenticatorBase(Authenticator::State initial_state);

  void AddMethod(Method method);

  // Updates |state_| to reflect the current underlying authenticator state.
  // |resume_callback| is called after the state is updated.
  void UpdateState(const base::Closure& resume_callback);

  // Gets the next message from |current_authenticator_|, if any, and fills in
  // the 'method' tag with |current_method_|.
  virtual scoped_ptr<buzz::XmlElement> GetNextMessageInternal();

  std::vector<Method> methods_;
  Method current_method_ = Method::INVALID;
  scoped_ptr<Authenticator> current_authenticator_;
  State state_;
  RejectionReason rejection_reason_ = INVALID_CREDENTIALS;

 private:
  DISALLOW_COPY_AND_ASSIGN(NegotiatingAuthenticatorBase);
};

}  // namespace protocol
}  // namespace remoting

#endif  // REMOTING_PROTOCOL_NEGOTIATING_AUTHENTICATOR_BASE_H_