spake2_authenticator.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // Copyright 2016 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/spake2_authenticator.h"
  5. #include <utility>
  6. #include "base/base64.h"
  7. #include "base/logging.h"
  8. #include "base/memory/ptr_util.h"
  9. #include "base/sys_byteorder.h"
  10. #include "crypto/hmac.h"
  11. #include "crypto/secure_util.h"
  12. #include "remoting/base/constants.h"
  13. #include "remoting/base/rsa_key_pair.h"
  14. #include "remoting/protocol/ssl_hmac_channel_authenticator.h"
  15. #include "third_party/boringssl/src/include/openssl/curve25519.h"
  16. #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
  17. namespace remoting {
  18. namespace protocol {
  19. namespace {
  20. // Each peer sends 2 messages: <spake-message> and <verification-hash>. The
  21. // content of <spake-message> is the output of SPAKE2_generate_msg() and must
  22. // be passed to SPAKE2_process_msg() on the other end. This is enough to
  23. // generate authentication key. <verification-hash> is sent to confirm that both
  24. // ends get the same authentication key (which means they both know the
  25. // password). This verification hash is calculated in
  26. // CalculateVerificationHash() as follows:
  27. // HMAC_SHA256(auth_key, ("host"|"client") + local_jid.length() + local_jid +
  28. // remote_jid.length() + remote_jid)
  29. // where auth_key is the key produced by SPAKE2.
  30. const jingle_xmpp::StaticQName kSpakeMessageTag = {kChromotingXmlNamespace,
  31. "spake-message"};
  32. const jingle_xmpp::StaticQName kVerificationHashTag = {kChromotingXmlNamespace,
  33. "verification-hash"};
  34. const jingle_xmpp::StaticQName kCertificateTag = {kChromotingXmlNamespace,
  35. "certificate"};
  36. std::unique_ptr<jingle_xmpp::XmlElement> EncodeBinaryValueToXml(
  37. const jingle_xmpp::StaticQName& qname,
  38. const std::string& content) {
  39. std::string content_base64;
  40. base::Base64Encode(content, &content_base64);
  41. std::unique_ptr<jingle_xmpp::XmlElement> result(new jingle_xmpp::XmlElement(qname));
  42. result->SetBodyText(content_base64);
  43. return result;
  44. }
  45. // Finds tag named |qname| in base_message and decodes it from base64 and stores
  46. // in |data|. If the element is not present then found is set to false otherwise
  47. // it's set to true. If the element is there and it's content couldn't be decoded
  48. // then false is returned.
  49. bool DecodeBinaryValueFromXml(const jingle_xmpp::XmlElement* message,
  50. const jingle_xmpp::QName& qname,
  51. bool* found,
  52. std::string* data) {
  53. const jingle_xmpp::XmlElement* element = message->FirstNamed(qname);
  54. *found = element != nullptr;
  55. if (!*found)
  56. return true;
  57. if (!base::Base64Decode(element->BodyText(), data)) {
  58. LOG(WARNING) << "Failed to parse " << qname.LocalPart();
  59. return false;
  60. }
  61. return !data->empty();
  62. }
  63. std::string PrefixWithLength(const std::string& str) {
  64. uint32_t length = base::HostToNet32(str.size());
  65. return std::string(reinterpret_cast<char*>(&length), sizeof(length)) + str;
  66. }
  67. } // namespace
  68. // static
  69. std::unique_ptr<Authenticator> Spake2Authenticator::CreateForClient(
  70. const std::string& local_id,
  71. const std::string& remote_id,
  72. const std::string& shared_secret,
  73. Authenticator::State initial_state) {
  74. return base::WrapUnique(new Spake2Authenticator(
  75. local_id, remote_id, shared_secret, false, initial_state));
  76. }
  77. // static
  78. std::unique_ptr<Authenticator> Spake2Authenticator::CreateForHost(
  79. const std::string& local_id,
  80. const std::string& remote_id,
  81. const std::string& local_cert,
  82. scoped_refptr<RsaKeyPair> key_pair,
  83. const std::string& shared_secret,
  84. Authenticator::State initial_state) {
  85. std::unique_ptr<Spake2Authenticator> result(new Spake2Authenticator(
  86. local_id, remote_id, shared_secret, true, initial_state));
  87. result->local_cert_ = local_cert;
  88. result->local_key_pair_ = key_pair;
  89. return std::move(result);
  90. }
  91. Spake2Authenticator::Spake2Authenticator(const std::string& local_id,
  92. const std::string& remote_id,
  93. const std::string& shared_secret,
  94. bool is_host,
  95. Authenticator::State initial_state)
  96. : local_id_(local_id),
  97. remote_id_(remote_id),
  98. shared_secret_(shared_secret),
  99. is_host_(is_host),
  100. state_(initial_state) {
  101. spake2_context_ = SPAKE2_CTX_new(
  102. is_host ? spake2_role_bob : spake2_role_alice,
  103. reinterpret_cast<const uint8_t*>(local_id_.data()), local_id_.size(),
  104. reinterpret_cast<const uint8_t*>(remote_id_.data()), remote_id_.size());
  105. // Generate first message and push it to |pending_messages_|.
  106. uint8_t message[SPAKE2_MAX_MSG_SIZE];
  107. size_t message_size;
  108. int result = SPAKE2_generate_msg(
  109. spake2_context_, message, &message_size, sizeof(message),
  110. reinterpret_cast<const uint8_t*>(shared_secret_.data()),
  111. shared_secret_.size());
  112. CHECK(result);
  113. local_spake_message_.assign(reinterpret_cast<char*>(message), message_size);
  114. }
  115. Spake2Authenticator::~Spake2Authenticator() {
  116. SPAKE2_CTX_free(spake2_context_);
  117. }
  118. Authenticator::State Spake2Authenticator::state() const {
  119. if (state_ == ACCEPTED && !outgoing_verification_hash_.empty())
  120. return MESSAGE_READY;
  121. return state_;
  122. }
  123. bool Spake2Authenticator::started() const {
  124. return started_;
  125. }
  126. Authenticator::RejectionReason Spake2Authenticator::rejection_reason() const {
  127. DCHECK_EQ(state(), REJECTED);
  128. return rejection_reason_;
  129. }
  130. void Spake2Authenticator::ProcessMessage(const jingle_xmpp::XmlElement* message,
  131. base::OnceClosure resume_callback) {
  132. ProcessMessageInternal(message);
  133. std::move(resume_callback).Run();
  134. }
  135. void Spake2Authenticator::ProcessMessageInternal(
  136. const jingle_xmpp::XmlElement* message) {
  137. DCHECK_EQ(state(), WAITING_MESSAGE);
  138. // Parse the certificate.
  139. bool cert_present;
  140. if (!DecodeBinaryValueFromXml(message, kCertificateTag, &cert_present,
  141. &remote_cert_)) {
  142. state_ = REJECTED;
  143. rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
  144. return;
  145. }
  146. // Client always expects certificate in the first message.
  147. if (!is_host_ && remote_cert_.empty()) {
  148. LOG(WARNING) << "No valid host certificate.";
  149. state_ = REJECTED;
  150. rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
  151. return;
  152. }
  153. bool spake_message_present = false;
  154. std::string spake_message;
  155. bool verification_hash_present = false;
  156. std::string verification_hash;
  157. if (!DecodeBinaryValueFromXml(message, kSpakeMessageTag,
  158. &spake_message_present, &spake_message) ||
  159. !DecodeBinaryValueFromXml(message, kVerificationHashTag,
  160. &verification_hash_present,
  161. &verification_hash)) {
  162. state_ = REJECTED;
  163. rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
  164. return;
  165. }
  166. // |auth_key_| is generated when <spake-message> is received.
  167. if (auth_key_.empty()) {
  168. if (!spake_message_present) {
  169. LOG(WARNING) << "<spake-message> not found.";
  170. state_ = REJECTED;
  171. rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
  172. return;
  173. }
  174. uint8_t key[SPAKE2_MAX_KEY_SIZE];
  175. size_t key_size;
  176. started_ = true;
  177. int result = SPAKE2_process_msg(
  178. spake2_context_, key, &key_size, sizeof(key),
  179. reinterpret_cast<const uint8_t*>(spake_message.data()),
  180. spake_message.size());
  181. if (!result) {
  182. state_ = REJECTED;
  183. rejection_reason_ = RejectionReason::INVALID_CREDENTIALS;
  184. return;
  185. }
  186. CHECK(key_size);
  187. auth_key_.assign(reinterpret_cast<char*>(key), key_size);
  188. outgoing_verification_hash_ =
  189. CalculateVerificationHash(is_host_, local_id_, remote_id_);
  190. expected_verification_hash_ =
  191. CalculateVerificationHash(!is_host_, remote_id_, local_id_);
  192. } else if (spake_message_present) {
  193. LOG(WARNING) << "Received duplicate <spake-message>.";
  194. state_ = REJECTED;
  195. rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
  196. return;
  197. }
  198. if (spake_message_sent_ && !verification_hash_present) {
  199. LOG(WARNING) << "Didn't receive <verification-hash> when expected.";
  200. state_ = REJECTED;
  201. rejection_reason_ = RejectionReason::PROTOCOL_ERROR;
  202. return;
  203. }
  204. if (verification_hash_present) {
  205. if (verification_hash.size() != expected_verification_hash_.size() ||
  206. !crypto::SecureMemEqual(verification_hash.data(),
  207. expected_verification_hash_.data(),
  208. verification_hash.size())) {
  209. state_ = REJECTED;
  210. rejection_reason_ = RejectionReason::INVALID_CREDENTIALS;
  211. return;
  212. }
  213. state_ = ACCEPTED;
  214. return;
  215. }
  216. state_ = MESSAGE_READY;
  217. }
  218. std::unique_ptr<jingle_xmpp::XmlElement> Spake2Authenticator::GetNextMessage() {
  219. DCHECK_EQ(state(), MESSAGE_READY);
  220. std::unique_ptr<jingle_xmpp::XmlElement> message = CreateEmptyAuthenticatorMessage();
  221. if (!spake_message_sent_) {
  222. if (!local_cert_.empty()) {
  223. message->AddElement(
  224. EncodeBinaryValueToXml(kCertificateTag, local_cert_).release());
  225. }
  226. message->AddElement(
  227. EncodeBinaryValueToXml(kSpakeMessageTag, local_spake_message_)
  228. .release());
  229. spake_message_sent_ = true;
  230. }
  231. if (!outgoing_verification_hash_.empty()) {
  232. message->AddElement(EncodeBinaryValueToXml(kVerificationHashTag,
  233. outgoing_verification_hash_)
  234. .release());
  235. outgoing_verification_hash_.clear();
  236. }
  237. if (state_ != ACCEPTED) {
  238. state_ = WAITING_MESSAGE;
  239. }
  240. return message;
  241. }
  242. const std::string& Spake2Authenticator::GetAuthKey() const {
  243. return auth_key_;
  244. }
  245. std::unique_ptr<ChannelAuthenticator>
  246. Spake2Authenticator::CreateChannelAuthenticator() const {
  247. DCHECK_EQ(state(), ACCEPTED);
  248. CHECK(!auth_key_.empty());
  249. if (is_host_) {
  250. return SslHmacChannelAuthenticator::CreateForHost(
  251. local_cert_, local_key_pair_, auth_key_);
  252. } else {
  253. return SslHmacChannelAuthenticator::CreateForClient(remote_cert_,
  254. auth_key_);
  255. }
  256. }
  257. std::string Spake2Authenticator::CalculateVerificationHash(
  258. bool from_host,
  259. const std::string& local_id,
  260. const std::string& remote_id) {
  261. std::string message = (from_host ? "host" : "client") +
  262. PrefixWithLength(local_id) +
  263. PrefixWithLength(remote_id);
  264. crypto::HMAC hmac(crypto::HMAC::SHA256);
  265. std::string result(hmac.DigestLength(), '\0');
  266. if (!hmac.Init(auth_key_) ||
  267. !hmac.Sign(message, reinterpret_cast<uint8_t*>(&result[0]),
  268. result.length())) {
  269. LOG(FATAL) << "Failed to calculate HMAC.";
  270. }
  271. return result;
  272. }
  273. } // namespace protocol
  274. } // namespace remoting