1 // Copyright 2014 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.
5 #include "extensions/browser/api/cast_channel/cast_auth_util.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "extensions/browser/api/cast_channel/cast_message_util.h"
13 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
14 #include "extensions/common/cast/cast_cert_validator.h"
16 namespace extensions
{
18 namespace cast_channel
{
21 const char* const kParseErrorPrefix
= "Failed to parse auth message: ";
23 const unsigned char kAudioOnlyPolicy
[] =
24 {0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x05, 0x02};
26 namespace cast_crypto
= ::extensions::api::cast_crypto
;
28 // Extracts an embedded DeviceAuthMessage payload from an auth challenge reply
30 AuthResult
ParseAuthMessage(const CastMessage
& challenge_reply
,
31 DeviceAuthMessage
* auth_message
) {
32 if (challenge_reply
.payload_type() != CastMessage_PayloadType_BINARY
) {
33 return AuthResult::CreateWithParseError(
34 "Wrong payload type in challenge reply",
35 AuthResult::ERROR_WRONG_PAYLOAD_TYPE
);
37 if (!challenge_reply
.has_payload_binary()) {
38 return AuthResult::CreateWithParseError(
39 "Payload type is binary but payload_binary field not set",
40 AuthResult::ERROR_NO_PAYLOAD
);
42 if (!auth_message
->ParseFromString(challenge_reply
.payload_binary())) {
43 return AuthResult::CreateWithParseError(
44 "Cannot parse binary payload into DeviceAuthMessage",
45 AuthResult::ERROR_PAYLOAD_PARSING_FAILED
);
48 VLOG(1) << "Auth message: " << AuthMessageToString(*auth_message
);
50 if (auth_message
->has_error()) {
51 return AuthResult::CreateWithParseError(
52 "Auth message error: " +
53 base::IntToString(auth_message
->error().error_type()),
54 AuthResult::ERROR_MESSAGE_ERROR
);
56 if (!auth_message
->has_response()) {
57 return AuthResult::CreateWithParseError(
58 "Auth message has no response field", AuthResult::ERROR_NO_RESPONSE
);
63 AuthResult
TranslateVerificationResult(
64 const cast_crypto::VerificationResult
& result
) {
65 AuthResult translated
;
66 translated
.error_message
= result
.error_message
;
67 translated
.nss_error_code
= result
.library_error_code
;
68 switch (result
.error_type
) {
69 case cast_crypto::VerificationResult::ERROR_NONE
:
70 translated
.error_type
= AuthResult::ERROR_NONE
;
72 case cast_crypto::VerificationResult::ERROR_CERT_INVALID
:
73 translated
.error_type
= AuthResult::ERROR_CERT_PARSING_FAILED
;
75 case cast_crypto::VerificationResult::ERROR_CERT_UNTRUSTED
:
76 translated
.error_type
= AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA
;
78 case cast_crypto::VerificationResult::ERROR_SIGNATURE_INVALID
:
79 translated
.error_type
= AuthResult::ERROR_SIGNED_BLOBS_MISMATCH
;
81 case cast_crypto::VerificationResult::ERROR_INTERNAL
:
82 translated
.error_type
= AuthResult::ERROR_UNEXPECTED_AUTH_LIBRARY_RESULT
;
85 translated
.error_type
= AuthResult::ERROR_CERT_NOT_SIGNED_BY_TRUSTED_CA
;
92 AuthResult::AuthResult()
93 : error_type(ERROR_NONE
), nss_error_code(0), channel_policies(POLICY_NONE
) {
96 AuthResult::~AuthResult() {
100 AuthResult
AuthResult::CreateWithParseError(const std::string
& error_message
,
101 ErrorType error_type
) {
102 return AuthResult(kParseErrorPrefix
+ error_message
, error_type
, 0);
106 AuthResult
AuthResult::CreateWithNSSError(const std::string
& error_message
,
107 ErrorType error_type
,
108 int nss_error_code
) {
109 return AuthResult(error_message
, error_type
, nss_error_code
);
112 AuthResult::AuthResult(const std::string
& error_message
,
113 ErrorType error_type
,
115 : error_message(error_message
),
116 error_type(error_type
),
117 nss_error_code(nss_error_code
) {
120 AuthResult
AuthenticateChallengeReply(const CastMessage
& challenge_reply
,
121 const std::string
& peer_cert
) {
122 if (peer_cert
.empty()) {
123 AuthResult result
= AuthResult::CreateWithParseError(
124 "Peer cert was empty.", AuthResult::ERROR_PEER_CERT_EMPTY
);
128 DeviceAuthMessage auth_message
;
129 AuthResult result
= ParseAuthMessage(challenge_reply
, &auth_message
);
130 if (!result
.success()) {
134 const AuthResponse
& response
= auth_message
.response();
135 result
= VerifyCredentials(response
, peer_cert
);
136 if (!result
.success()) {
140 const std::string
& audio_policy
=
141 std::string(reinterpret_cast<const char*>(kAudioOnlyPolicy
),
142 (arraysize(kAudioOnlyPolicy
) / sizeof(unsigned char)));
143 if (response
.client_auth_certificate().find(audio_policy
) !=
145 result
.channel_policies
|= AuthResult::POLICY_AUDIO_ONLY
;
151 // This function does the following
152 // * Verifies that the trusted CA |response.intermediate_certificate| is
153 // whitelisted for use.
154 // * Verifies that |response.client_auth_certificate| is signed
155 // by the trusted CA certificate.
156 // * Verifies that |response.signature| matches the signature
157 // of |peer_cert| by |response.client_auth_certificate|'s public
159 AuthResult
VerifyCredentials(const AuthResponse
& response
,
160 const std::string
& peer_cert
) {
161 // Verify the certificate
162 scoped_ptr
<cast_crypto::CertVerificationContext
> verification_context
;
163 cast_crypto::VerificationResult ret
= cast_crypto::VerifyDeviceCert(
164 response
.client_auth_certificate(),
165 std::vector
<std::string
>(response
.intermediate_certificate().begin(),
166 response
.intermediate_certificate().end()),
167 &verification_context
);
170 ret
= verification_context
->VerifySignatureOverData(response
.signature(),
173 return TranslateVerificationResult(ret
);
176 } // namespace cast_channel
178 } // namespace extensions