pairing_authenticator_base.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2013 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. #include "remoting/protocol/pairing_authenticator_base.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/logging.h"
  9. #include "remoting/base/constants.h"
  10. #include "remoting/protocol/channel_authenticator.h"
  11. namespace remoting {
  12. namespace protocol {
  13. namespace {
  14. const jingle_xmpp::StaticQName kPairingFailedTag =
  15. { kChromotingXmlNamespace, "pairing-failed" };
  16. const jingle_xmpp::StaticQName kPairingErrorAttribute = { "", "error" };
  17. } // namespace
  18. PairingAuthenticatorBase::PairingAuthenticatorBase() {}
  19. PairingAuthenticatorBase::~PairingAuthenticatorBase() = default;
  20. Authenticator::State PairingAuthenticatorBase::state() const {
  21. DCHECK(spake2_authenticator_);
  22. return spake2_authenticator_->state();
  23. }
  24. bool PairingAuthenticatorBase::started() const {
  25. if (!spake2_authenticator_) {
  26. return false;
  27. }
  28. return spake2_authenticator_->started();
  29. }
  30. Authenticator::RejectionReason
  31. PairingAuthenticatorBase::rejection_reason() const {
  32. if (!spake2_authenticator_) {
  33. return RejectionReason::PROTOCOL_ERROR;
  34. }
  35. return spake2_authenticator_->rejection_reason();
  36. }
  37. void PairingAuthenticatorBase::ProcessMessage(
  38. const jingle_xmpp::XmlElement* message,
  39. base::OnceClosure resume_callback) {
  40. DCHECK_EQ(state(), WAITING_MESSAGE);
  41. // The client authenticator creates the underlying authenticator in the ctor
  42. // and the host creates it in response to the first message before deferring
  43. // to this class to process it. Either way, it should exist here.
  44. DCHECK(spake2_authenticator_);
  45. // If pairing failed, and we haven't already done so, try again with the PIN.
  46. if (using_paired_secret_ && HasErrorMessage(message)) {
  47. using_paired_secret_ = false;
  48. spake2_authenticator_.reset();
  49. CreateSpakeAuthenticatorWithPin(
  50. WAITING_MESSAGE,
  51. base::BindOnce(&PairingAuthenticatorBase::ProcessMessage,
  52. weak_factory_.GetWeakPtr(),
  53. base::Owned(new jingle_xmpp::XmlElement(*message)),
  54. std::move(resume_callback)));
  55. return;
  56. }
  57. // Pass the message to the underlying authenticator for processing, but
  58. // check for a failed SPAKE exchange if we're using the paired secret. In
  59. // this case the pairing protocol can continue by communicating the error
  60. // to the peer and retrying with the PIN.
  61. spake2_authenticator_->ProcessMessage(
  62. message,
  63. base::BindOnce(&PairingAuthenticatorBase::CheckForFailedSpakeExchange,
  64. weak_factory_.GetWeakPtr(), std::move(resume_callback)));
  65. }
  66. std::unique_ptr<jingle_xmpp::XmlElement> PairingAuthenticatorBase::GetNextMessage() {
  67. DCHECK_EQ(state(), MESSAGE_READY);
  68. std::unique_ptr<jingle_xmpp::XmlElement> result =
  69. spake2_authenticator_->GetNextMessage();
  70. MaybeAddErrorMessage(result.get());
  71. return result;
  72. }
  73. const std::string& PairingAuthenticatorBase::GetAuthKey() const {
  74. return spake2_authenticator_->GetAuthKey();
  75. }
  76. std::unique_ptr<ChannelAuthenticator>
  77. PairingAuthenticatorBase::CreateChannelAuthenticator() const {
  78. return spake2_authenticator_->CreateChannelAuthenticator();
  79. }
  80. void PairingAuthenticatorBase::MaybeAddErrorMessage(jingle_xmpp::XmlElement* message) {
  81. if (!error_message_.empty()) {
  82. jingle_xmpp::XmlElement* pairing_failed_tag =
  83. new jingle_xmpp::XmlElement(kPairingFailedTag);
  84. pairing_failed_tag->AddAttr(kPairingErrorAttribute, error_message_);
  85. message->AddElement(pairing_failed_tag);
  86. error_message_.clear();
  87. }
  88. }
  89. bool PairingAuthenticatorBase::HasErrorMessage(
  90. const jingle_xmpp::XmlElement* message) const {
  91. const jingle_xmpp::XmlElement* pairing_failed_tag =
  92. message->FirstNamed(kPairingFailedTag);
  93. if (pairing_failed_tag) {
  94. std::string error = pairing_failed_tag->Attr(kPairingErrorAttribute);
  95. LOG(ERROR) << "Pairing failed: " << error;
  96. }
  97. return pairing_failed_tag != nullptr;
  98. }
  99. void PairingAuthenticatorBase::CheckForFailedSpakeExchange(
  100. base::OnceClosure resume_callback) {
  101. // If the SPAKE exchange failed due to invalid credentials, and those
  102. // credentials were the paired secret, then notify the peer that the
  103. // PIN-less connection failed and retry using the PIN.
  104. if (spake2_authenticator_->state() == REJECTED &&
  105. spake2_authenticator_->rejection_reason() ==
  106. RejectionReason::INVALID_CREDENTIALS &&
  107. using_paired_secret_) {
  108. using_paired_secret_ = false;
  109. error_message_ = "invalid-shared-secret";
  110. spake2_authenticator_.reset();
  111. CreateSpakeAuthenticatorWithPin(MESSAGE_READY, std::move(resume_callback));
  112. return;
  113. }
  114. std::move(resume_callback).Run();
  115. }
  116. } // namespace protocol
  117. } // namespace remoting