Skip to content

Commit d26036b

Browse files
authored
Restore cobalt/browser/device_authentication.{cc/h} and unit test (#7143)
Restore device authentication from C25. This will be implemented as the same as C25 for C26 on 3P devices. Issue: 442607580
1 parent 980a7f5 commit d26036b

File tree

3 files changed

+394
-0
lines changed

3 files changed

+394
-0
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "cobalt/browser/device_authentication.h"
16+
17+
#include <algorithm>
18+
#include <map>
19+
20+
#include "base/base64.h"
21+
#include "base/base64url.h"
22+
#include "base/logging.h"
23+
#include "base/strings/escape.h"
24+
#include "base/time/time.h"
25+
#include "crypto/hmac.h"
26+
#include "starboard/system.h"
27+
28+
namespace cobalt {
29+
namespace browser {
30+
31+
namespace {
32+
33+
constexpr size_t kSHA256DigestSize = 32;
34+
35+
bool ComputeSignatureFromSignAPI(const std::string& message,
36+
uint8_t* signature) {
37+
return SbSystemSignWithCertificationSecretKey(
38+
reinterpret_cast<const uint8_t*>(message.data()), message.size(),
39+
signature, kSHA256DigestSize);
40+
}
41+
42+
// Check to see if we can query the platform for the secret key. If so,
43+
// go ahead and use it to sign the message, otherwise try to use the
44+
// SbSystemSignWithCertificationSecretKey() method to sign the message. If
45+
// both methods fail, return an empty string.
46+
std::string ComputeBase64Signature(const std::string& message) {
47+
uint8_t signature[kSHA256DigestSize];
48+
49+
if (ComputeSignatureFromSignAPI(message, signature)) {
50+
DLOG(INFO) << "Using certification signature provided by "
51+
<< "SbSystemSignWithCertificationSecretKey().";
52+
} else {
53+
return std::string();
54+
}
55+
56+
std::string base_64_url_signature;
57+
base::Base64UrlEncode(std::string(signature, signature + kSHA256DigestSize),
58+
base::Base64UrlEncodePolicy::OMIT_PADDING,
59+
&base_64_url_signature);
60+
return base_64_url_signature;
61+
}
62+
63+
std::string NumberToFourByteString(size_t n) {
64+
std::string str;
65+
str += static_cast<char>(((n & 0xff000000) >> 24));
66+
str += static_cast<char>(((n & 0x00ff0000) >> 16));
67+
str += static_cast<char>(((n & 0x0000ff00) >> 8));
68+
str += static_cast<char>((n & 0x000000ff));
69+
return str;
70+
}
71+
72+
// Used by ComputeMessage() to create a message component as a string.
73+
std::string BuildMessageFragment(const std::string& key,
74+
const std::string& value) {
75+
std::string msg_fragment = NumberToFourByteString(key.length()) + key +
76+
NumberToFourByteString(value.length()) + value;
77+
return msg_fragment;
78+
}
79+
80+
// Returns the certification scope provided by the platform to use with device
81+
// authentication.
82+
std::string GetCertScopeFromPlatform() {
83+
// Get cert_scope and base_64_secret
84+
const size_t kCertificationScopeLength = 1023;
85+
char cert_scope_property[kCertificationScopeLength + 1] = {0};
86+
bool result =
87+
SbSystemGetProperty(kSbSystemPropertyCertificationScope,
88+
cert_scope_property, kCertificationScopeLength);
89+
if (!result) {
90+
DLOG(ERROR) << "Unable to get kSbSystemPropertyCertificationScope";
91+
return std::string();
92+
}
93+
94+
return cert_scope_property;
95+
}
96+
97+
// Returns the start time provided by the platform for use with device
98+
// authentication.
99+
std::string GetStartTime() {
100+
return std::to_string(static_cast<int64_t>(base::Time::Now().ToDoubleT()));
101+
}
102+
103+
} // namespace
104+
105+
std::string GetDeviceAuthenticationSignedURLQueryString() {
106+
std::string cert_scope = GetCertScopeFromPlatform();
107+
if (cert_scope.empty()) {
108+
LOG(WARNING) << "Error retrieving certification scope required for "
109+
<< "device authentication.";
110+
return std::string();
111+
}
112+
std::string start_time = GetStartTime();
113+
CHECK(!start_time.empty());
114+
115+
std::string base64_signature =
116+
ComputeBase64Signature(ComputeMessage(cert_scope, start_time));
117+
118+
return GetDeviceAuthenticationSignedURLQueryStringFromComponents(
119+
cert_scope, start_time, base64_signature);
120+
}
121+
122+
std::string GetDeviceAuthenticationSignedURLQueryStringFromComponents(
123+
const std::string& cert_scope,
124+
const std::string& start_time,
125+
const std::string& base64_signature) {
126+
CHECK(!cert_scope.empty());
127+
CHECK(!start_time.empty());
128+
129+
if (base64_signature.empty()) {
130+
return std::string();
131+
}
132+
133+
std::map<std::string, std::string> signed_query_components;
134+
signed_query_components["cert_scope"] = cert_scope;
135+
signed_query_components["start_time"] = start_time;
136+
signed_query_components["sig"] = base64_signature;
137+
138+
std::string query;
139+
for (const auto& query_component : signed_query_components) {
140+
const std::string& key = query_component.first;
141+
const std::string& value = query_component.second;
142+
143+
if (!query.empty()) {
144+
query += "&";
145+
}
146+
query += base::EscapeQueryParamValue(key, true);
147+
if (!value.empty()) {
148+
query += "=" + base::EscapeQueryParamValue(value, true);
149+
}
150+
}
151+
152+
return query;
153+
}
154+
155+
// Combine multiple message components into a string that will be used as the
156+
// message that we will sign.
157+
std::string ComputeMessage(const std::string& cert_scope,
158+
const std::string& start_time) {
159+
// Build message from cert_scope and start_time.
160+
return BuildMessageFragment("cert_scope", cert_scope) +
161+
BuildMessageFragment("start_time", start_time);
162+
}
163+
164+
void ComputeHMACSHA256SignatureWithProvidedKey(const std::string& message,
165+
const std::string& base64_key,
166+
uint8_t* signature,
167+
size_t signature_size_in_bytes) {
168+
CHECK_GE(signature_size_in_bytes, 32U);
169+
170+
std::string key;
171+
base::Base64Decode(base64_key, &key);
172+
173+
// Generate signature from message using HMAC-SHA256.
174+
crypto::HMAC hmac(crypto::HMAC::SHA256);
175+
if (!hmac.Init(key)) {
176+
DLOG(ERROR) << "Unable to initialize HMAC-SHA256.";
177+
}
178+
if (!hmac.Sign(message, signature, signature_size_in_bytes)) {
179+
DLOG(ERROR) << "Unable to sign HMAC-SHA256.";
180+
}
181+
}
182+
183+
} // namespace browser
184+
} // namespace cobalt
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef COBALT_BROWSER_DEVICE_AUTHENTICATION_H_
16+
#define COBALT_BROWSER_DEVICE_AUTHENTICATION_H_
17+
18+
#include <string>
19+
20+
#include "starboard/configuration.h"
21+
#include "starboard/types.h"
22+
23+
namespace cobalt {
24+
namespace browser {
25+
26+
// Returns a base64 encoded SHA256 hash representing the device authentication
27+
// signature, the device certification scope, and the current time as query
28+
// parameters in a string. The signature is the result of signing a message,
29+
// containing the certification scope and the current time, with a secret key
30+
// provided by the device.
31+
std::string GetDeviceAuthenticationSignedURLQueryString();
32+
33+
// The following methods are deterministic helper functions used in the
34+
// implementation of the public method above. They are declared here so that
35+
// they can be tested in (white box) unit tests.
36+
37+
// Similar to the method above, but all platform queries are resolved to make
38+
// this a deterministic and testable function.
39+
std::string GetDeviceAuthenticationSignedURLQueryStringFromComponents(
40+
const std::string& cert_scope,
41+
const std::string& start_time,
42+
const std::string& base64_signature);
43+
44+
// Given the certification parameters, computes a message to be used as input
45+
// to the signing process.
46+
std::string ComputeMessage(const std::string& cert_scope,
47+
const std::string& start_time);
48+
49+
// Given a message (arbitrary sequence of bytes) and a base64-encoded key
50+
// key, compute the HMAC-SHA256 signature and store it in the |signature_hash|
51+
// out parameter. Note that 32 bytes will be written to the output hash, it is
52+
// an error if |signature_hash_size_in_bytes| is less than 32.
53+
void ComputeHMACSHA256SignatureWithProvidedKey(
54+
const std::string& message,
55+
const std::string& base64_key,
56+
uint8_t* signature_hash,
57+
size_t signature_hash_size_in_bytes);
58+
59+
} // namespace browser
60+
} // namespace cobalt
61+
62+
#endif // COBALT_BROWSER_DEVICE_AUTHENTICATION_H_
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Copyright 2019 The Cobalt Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "cobalt/browser/device_authentication.h"
16+
17+
#include "base/base64.h"
18+
#include "base/base64url.h"
19+
#include "testing/gtest/include/gtest/gtest.h"
20+
21+
namespace cobalt {
22+
namespace browser {
23+
24+
constexpr size_t kSHA256DigestSize = 32;
25+
26+
namespace {
27+
28+
std::string ToBase64Message(const std::string& cert_scope,
29+
const std::string& start_time) {
30+
std::string base64_message;
31+
base::Base64Encode(browser::ComputeMessage(cert_scope, start_time),
32+
&base64_message);
33+
return base64_message;
34+
}
35+
36+
std::string ComputeBase64URLSignatureFromBase64Message(
37+
const std::string& base64_message,
38+
const std::string& base64_secret_key) {
39+
uint8_t signature[kSHA256DigestSize];
40+
41+
std::string message;
42+
CHECK(base::Base64Decode(base64_message, &message));
43+
ComputeHMACSHA256SignatureWithProvidedKey(message, base64_secret_key,
44+
signature, kSHA256DigestSize);
45+
46+
std::string base_64_url_signature;
47+
base::Base64UrlEncode(std::string(signature, signature + kSHA256DigestSize),
48+
base::Base64UrlEncodePolicy::OMIT_PADDING,
49+
&base_64_url_signature);
50+
return base_64_url_signature;
51+
}
52+
53+
std::string CreateSignedURLQueryString(const std::string& cert_scope,
54+
const std::string& start_time,
55+
const std::string& base64_secret_key) {
56+
return GetDeviceAuthenticationSignedURLQueryStringFromComponents(
57+
cert_scope, start_time,
58+
ComputeBase64URLSignatureFromBase64Message(
59+
ToBase64Message(cert_scope, start_time), base64_secret_key));
60+
}
61+
62+
} // namespace
63+
64+
TEST(DeviceAuthenticationTest, ComputeMessageTest) {
65+
EXPECT_EQ(
66+
"AAAACmNlcnRfc2NvcGUAAAANbXktY2VydC1zY29wZQAAAApzdGFydF90aW1lAAAACjE1NjUx"
67+
"NjE1NTY=",
68+
ToBase64Message("my-cert-scope", "1565161556"));
69+
EXPECT_EQ(
70+
"AAAACmNlcnRfc2NvcGUAAAATbXktb3RoZXItY2VydC1zY29wZQAAAApzdGFydF90aW1lAAAA"
71+
"CjE1NjUxNjMwNjU=",
72+
ToBase64Message("my-other-cert-scope", "1565163065"));
73+
EXPECT_EQ(
74+
"AAAACmNlcnRfc2NvcGUAAAATbXktb3RoZXItY2VydC1zY29wZQAAAApzdGFydF90aW1lAAAA"
75+
"CjE1NjUxNjMyNDU=",
76+
ToBase64Message("my-other-cert-scope", "1565163245"));
77+
EXPECT_EQ(
78+
"AAAACmNlcnRfc2NvcGUAAAATbXktb3RoZXItY2VydC1zY29wZQAAAApzdGFydF90aW1lAAAA"
79+
"CjI3OTAzMjY2Mzk=",
80+
ToBase64Message("my-other-cert-scope", "2790326639"));
81+
EXPECT_EQ(
82+
"AAAACmNlcnRfc2NvcGUAAAAEeWFjcwAAAApzdGFydF90aW1lAAAACjEzNDAwMDAzMTI=",
83+
ToBase64Message("yacs", "1340000312"));
84+
}
85+
86+
TEST(DeviceAuthenticationTest, ComputeSignatureWithProvidedSecretTest) {
87+
EXPECT_EQ("5duoLo5TELzyMQ6Fz-nmtLH3-nR4GrYfJ5RqTWU33LY",
88+
ComputeBase64URLSignatureFromBase64Message(
89+
"AAAACmNlcnRfc2NvcGUAAAANbXktY2VydC1zY29wZQAAAApzdGFydF90aW1lAA"
90+
"AACjE1NjUxNjE1NTY=",
91+
"abcdefghijklmnop1234567890abcdefghijklmnopqr"));
92+
EXPECT_EQ("zrHJxDjsg60vF-fnGD9J7QaK2fw2QEZD7PIIfHMYmUg",
93+
ComputeBase64URLSignatureFromBase64Message(
94+
"AAAACmNlcnRfc2NvcGUAAAATbXktb3RoZXItY2VydC1zY29wZQAAAApzdGFydF"
95+
"90aW1lAAAACjE1NjUxNjMwNjU=",
96+
"abcdefghijklmnop1234567890abcdefghijklmnopqr"));
97+
EXPECT_EQ("FnycnpFnmzgcNBIdJoymQvrS0_1uet_bmCtpuAmQR2s",
98+
ComputeBase64URLSignatureFromBase64Message(
99+
"AAAACmNlcnRfc2NvcGUAAAATbXktb3RoZXItY2VydC1zY29wZQAAAApzdGFydF"
100+
"90aW1lAAAACjE1NjUxNjMyNDU=",
101+
"abcdefghi5555nop1234567890abcdef5555klmn6666"));
102+
EXPECT_EQ("N-nDY11_8HWFKEZPiQenQrC3SXuuPahs-er-n7Xh6ig",
103+
ComputeBase64URLSignatureFromBase64Message(
104+
"AAAACmNlcnRfc2NvcGUAAAATbXktb3RoZXItY2VydC1zY29wZQAAAApzdGFydF"
105+
"90aW1lAAAACjI3OTAzMjY2Mzk=",
106+
"abcdefghi5555nop1234567890abcdef5555klmn6666"));
107+
EXPECT_EQ("dX0apAEsG2yWm9da6qTded4qd-mqgExeuJam99z-AHk",
108+
ComputeBase64URLSignatureFromBase64Message(
109+
"AAAACmNlcnRfc2NvcGUAAAAEeWFjcwAAAApzdGFydF90aW1lAAAACjEzNDAwMD"
110+
"AzMTI=",
111+
"11111111111111111111111111111111111111111111"));
112+
}
113+
114+
// This is the main end-to-end test for device authentication.
115+
TEST(DeviceAuthenticationTest,
116+
GetDeviceAuthenticationSignedURLQueryStringFromComponentsTest) {
117+
EXPECT_EQ(
118+
"cert_scope=my-cert-scope&sig=5duoLo5TELzyMQ6Fz-nmtLH3-"
119+
"nR4GrYfJ5RqTWU33LY&start_time=1565161556",
120+
CreateSignedURLQueryString(
121+
"my-cert-scope", "1565161556",
122+
"abcdefghijklmnop1234567890abcdefghijklmnopqr"));
123+
EXPECT_EQ(
124+
"cert_scope=my-other-cert-scope&sig="
125+
"Lf2zunrdhjH8q3ehdUy0tneTtamWigcyTgQl7zxWgnQ&start_time=123456789",
126+
CreateSignedURLQueryString(
127+
"my-other-cert-scope", "123456789",
128+
"abcdefghijklmnop1234567890abcdefghijklmnopqr"));
129+
EXPECT_EQ(
130+
"cert_scope=my-other-cert-scope&sig="
131+
"c5YZB0Rtv8Nf8gLSbE052ZvCUEpouP28nUq77FEgg88&start_time=11111111",
132+
CreateSignedURLQueryString(
133+
"my-other-cert-scope", "11111111",
134+
"11111111111111111111111111111111111111111111"));
135+
EXPECT_EQ(
136+
"cert_scope=yacs&sig=YKjLEzSl2_ub-05Ajaww0HOOPElxEPUc4SEiQnGAfaE&start_"
137+
"time=11111111",
138+
CreateSignedURLQueryString(
139+
"yacs", "11111111", "11111111111111111111111111111111111111111111"));
140+
}
141+
142+
TEST(DeviceAuthenticationTest, NoCertSignatureImpliesNoQueryParameters) {
143+
EXPECT_EQ("", GetDeviceAuthenticationSignedURLQueryStringFromComponents(
144+
"my_cert_scope", "1234", ""));
145+
}
146+
147+
} // namespace browser
148+
} // namespace cobalt

0 commit comments

Comments
 (0)