| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "osp/public/authentication_alice.h" |
| |
| #include <sstream> |
| #include <utility> |
| |
| namespace openscreen::osp { |
| |
| AuthenticationAlice::AuthenticationAlice(uint64_t instance_id, |
| AgentFingerprint fingerprint, |
| std::string_view auth_token, |
| std::string_view password, |
| MessageDemuxer& demuxer, |
| Delegate& delegate) |
| : AuthenticationBase(instance_id, |
| std::move(fingerprint), |
| demuxer, |
| delegate), |
| auth_token_(auth_token), |
| password_(password) {} |
| |
| AuthenticationAlice::~AuthenticationAlice() = default; |
| |
| void AuthenticationAlice::StartAuthentication() { |
| if (!auth_data_.sender) { |
| delegate_.OnAuthenticationFailed(instance_id_, |
| Error::Code::kNoActiveConnection); |
| return; |
| } |
| |
| msgs::AuthSpake2Handshake message = { |
| .initiation_token = |
| msgs::AuthInitiationToken{ |
| .token = auth_token_, |
| }, |
| .psk_status = msgs::AuthSpake2PskStatus::kPskShown, |
| .public_value = ComputePublicValue(ComputePrivateKey(fingerprint_))}; |
| auth_data_.sender->WriteMessage(message, &msgs::EncodeAuthSpake2Handshake); |
| } |
| |
| ErrorOr<size_t> AuthenticationAlice::OnStreamMessage(uint64_t instance_id, |
| uint64_t connection_id, |
| msgs::Type message_type, |
| const uint8_t* buffer, |
| size_t buffer_size, |
| Clock::time_point now) { |
| OSP_CHECK_EQ(instance_id_, instance_id); |
| if (!auth_data_.sender) { |
| delegate_.OnAuthenticationFailed(instance_id, |
| Error::Code::kNoActiveConnection); |
| return Error::Code::kNoActiveConnection; |
| } |
| |
| switch (message_type) { |
| case msgs::Type::kAuthSpake2Handshake: { |
| msgs::AuthSpake2Handshake handshake; |
| ssize_t result = |
| msgs::DecodeAuthSpake2Handshake(buffer, buffer_size, handshake); |
| if (result < 0) { |
| if (result == msgs::kParserEOF) { |
| return Error::Code::kCborIncompleteMessage; |
| } |
| Error error{Error::Code::kCborParsing, |
| "Failed to parse AuthSpake2Handshake message."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| return Error::Code::kCborParsing; |
| } else { |
| auto& initiation_token = handshake.initiation_token; |
| if (!initiation_token.token.has_value() || |
| initiation_token.token != auth_token_) { |
| Error error{Error::Code::kInvalidAnswer, |
| "Authentication failed: initiation token mismatch."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| return result; |
| } |
| |
| if (handshake.psk_status == |
| msgs::AuthSpake2PskStatus::kPskNeedsPresentation) { |
| // Compute and save the shared key for verification later. |
| auth_data_.shared_key = |
| ComputeSharedKey(ComputePrivateKey(fingerprint_), |
| handshake.public_value, password_); |
| msgs::AuthSpake2Handshake message = { |
| .initiation_token = |
| msgs::AuthInitiationToken{ |
| .token = auth_token_, |
| }, |
| .psk_status = msgs::AuthSpake2PskStatus::kPskInput, |
| .public_value = |
| ComputePublicValue(ComputePrivateKey(fingerprint_))}; |
| auth_data_.sender->WriteMessage(message, |
| &msgs::EncodeAuthSpake2Handshake); |
| } else if (handshake.psk_status == |
| msgs::AuthSpake2PskStatus::kPskInput) { |
| msgs::AuthSpake2Confirmation message = { |
| .confirmation_value = |
| ComputeSharedKey(ComputePrivateKey(fingerprint_), |
| handshake.public_value, password_)}; |
| auth_data_.sender->WriteMessage(message, |
| &msgs::EncodeAuthSpake2Confirmation); |
| } else { |
| Error error{Error::Code::kInvalidAnswer, |
| "Authentication failed: receive wrong PSK status."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| } |
| return result; |
| } |
| } |
| |
| case msgs::Type::kAuthSpake2Confirmation: { |
| msgs::AuthSpake2Confirmation confirmation; |
| ssize_t result = |
| msgs::DecodeAuthSpake2Confirmation(buffer, buffer_size, confirmation); |
| if (result < 0) { |
| if (result == msgs::kParserEOF) { |
| return Error::Code::kCborIncompleteMessage; |
| } |
| Error error{Error::Code::kCborParsing, |
| "Failed to parse AuthSpake2Confirmation message."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| return Error::Code::kCborParsing; |
| } else { |
| if (std::equal(auth_data_.shared_key.begin(), |
| auth_data_.shared_key.end(), |
| confirmation.confirmation_value.begin())) { |
| msgs::AuthStatus status = { |
| .result = msgs::AuthStatusResult::kAuthenticated}; |
| auth_data_.sender->WriteMessage(status, &msgs::EncodeAuthStatus); |
| delegate_.OnAuthenticationSucceed(instance_id); |
| } else { |
| msgs::AuthStatus status = {.result = |
| msgs::AuthStatusResult::kProofInvalid}; |
| auth_data_.sender->WriteMessage(status, &msgs::EncodeAuthStatus); |
| Error error{Error::Code::kInvalidAnswer, |
| "Authentication failed: shared key mismatch."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| } |
| return result; |
| } |
| } |
| |
| case msgs::Type::kAuthStatus: { |
| msgs::AuthStatus status; |
| ssize_t result = msgs::DecodeAuthStatus(buffer, buffer_size, status); |
| if (result < 0) { |
| if (result == msgs::kParserEOF) { |
| return Error::Code::kCborIncompleteMessage; |
| } |
| Error error{Error::Code::kCborParsing, |
| "Failed to parse AuthStatus message."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| return Error::Code::kCborParsing; |
| } else { |
| if (status.result == msgs::AuthStatusResult::kAuthenticated) { |
| delegate_.OnAuthenticationSucceed(instance_id); |
| } else { |
| std::stringstream ss; |
| ss << "Authentication failed: " << status.result; |
| Error error{Error::Code::kInvalidAnswer, ss.str()}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| } |
| return result; |
| } |
| } |
| |
| default: { |
| Error error{Error::Code::kCborParsing, |
| "Receives authentication message with unprocessable type."}; |
| delegate_.OnAuthenticationFailed(instance_id, error); |
| return Error::Code::kCborParsing; |
| } |
| } |
| } |
| |
| } // namespace openscreen::osp |