Skip to content
This repository was archived by the owner on Apr 17, 2024. It is now read-only.

Commit 3db8cd9

Browse files
willinoiscopybara-github
authored andcommitted
Add C++ JWT HMAC key type.
PiperOrigin-RevId: 624174566
1 parent 52ab085 commit 3db8cd9

5 files changed

+749
-0
lines changed

cc/jwt/BUILD.bazel

+37
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,27 @@ cc_library(
225225
],
226226
)
227227

228+
cc_library(
229+
name = "jwt_hmac_key",
230+
srcs = ["jwt_hmac_key.cc"],
231+
hdrs = ["jwt_hmac_key.h"],
232+
include_prefix = "tink/jwt",
233+
deps = [
234+
":jwt_hmac_parameters",
235+
":jwt_mac_key",
236+
"//:key",
237+
"//:partial_key_access_token",
238+
"//:restricted_data",
239+
"//util:status",
240+
"//util:statusor",
241+
"@com_google_absl//absl/base:endian",
242+
"@com_google_absl//absl/status",
243+
"@com_google_absl//absl/strings",
244+
"@com_google_absl//absl/strings:string_view",
245+
"@com_google_absl//absl/types:optional",
246+
],
247+
)
248+
228249
# tests
229250

230251
cc_test(
@@ -390,3 +411,19 @@ cc_test(
390411
"@com_google_googletest//:gtest_main",
391412
],
392413
)
414+
415+
cc_test(
416+
name = "jwt_hmac_key_test",
417+
srcs = ["jwt_hmac_key_test.cc"],
418+
deps = [
419+
":jwt_hmac_key",
420+
":jwt_hmac_parameters",
421+
"//:partial_key_access",
422+
"//:restricted_data",
423+
"//util:statusor",
424+
"//util:test_matchers",
425+
"@com_google_absl//absl/status",
426+
"@com_google_absl//absl/types:optional",
427+
"@com_google_googletest//:gtest_main",
428+
],
429+
)

cc/jwt/CMakeLists.txt

+36
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,26 @@ tink_cc_library(
204204
tink::util::statusor
205205
)
206206

207+
tink_cc_library(
208+
NAME jwt_hmac_key
209+
SRCS
210+
jwt_hmac_key.cc
211+
jwt_hmac_key.h
212+
DEPS
213+
tink::jwt::jwt_hmac_parameters
214+
tink::jwt::jwt_mac_key
215+
absl::endian
216+
absl::status
217+
absl::strings
218+
absl::string_view
219+
absl::optional
220+
tink::core::key
221+
tink::core::partial_key_access_token
222+
tink::core::restricted_data
223+
tink::util::status
224+
tink::util::statusor
225+
)
226+
207227
# tests
208228

209229
tink_cc_test(
@@ -364,3 +384,19 @@ tink_cc_test(
364384
tink::util::statusor
365385
tink::util::test_matchers
366386
)
387+
388+
tink_cc_test(
389+
NAME jwt_hmac_key_test
390+
SRCS
391+
jwt_hmac_key_test.cc
392+
DEPS
393+
tink::jwt::jwt_hmac_key
394+
tink::jwt::jwt_hmac_parameters
395+
gmock
396+
absl::status
397+
absl::optional
398+
tink::core::partial_key_access
399+
tink::core::restricted_data
400+
tink::util::statusor
401+
tink::util::test_matchers
402+
)

cc/jwt/jwt_hmac_key.cc

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright 2024 Google LLC
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+
////////////////////////////////////////////////////////////////////////////////
16+
17+
#include "tink/jwt/jwt_hmac_key.h"
18+
19+
#include <string>
20+
21+
#include "absl/base/internal/endian.h"
22+
#include "absl/status/status.h"
23+
#include "absl/strings/escaping.h"
24+
#include "absl/strings/string_view.h"
25+
#include "absl/types/optional.h"
26+
#include "tink/jwt/jwt_hmac_parameters.h"
27+
#include "tink/key.h"
28+
#include "tink/partial_key_access_token.h"
29+
#include "tink/restricted_data.h"
30+
#include "tink/util/status.h"
31+
#include "tink/util/statusor.h"
32+
33+
namespace crypto {
34+
namespace tink {
35+
36+
JwtHmacKey::Builder& JwtHmacKey::Builder::SetParameters(
37+
const JwtHmacParameters& parameters) {
38+
parameters_ = parameters;
39+
return *this;
40+
}
41+
42+
JwtHmacKey::Builder& JwtHmacKey::Builder::SetKeyBytes(
43+
const RestrictedData& key_bytes) {
44+
key_bytes_ = key_bytes;
45+
return *this;
46+
}
47+
48+
JwtHmacKey::Builder& JwtHmacKey::Builder::SetIdRequirement(int id_requirement) {
49+
id_requirement_ = id_requirement;
50+
return *this;
51+
}
52+
53+
JwtHmacKey::Builder& JwtHmacKey::Builder::SetCustomKid(
54+
absl::string_view custom_kid) {
55+
custom_kid_ = custom_kid.data();
56+
return *this;
57+
}
58+
59+
util::StatusOr<absl::optional<std::string>> JwtHmacKey::Builder::ComputeKid() {
60+
switch (parameters_->GetKidStrategy()) {
61+
case JwtHmacParameters::KidStrategy::kBase64EncodedKeyId: {
62+
if (custom_kid_.has_value()) {
63+
return util::Status(
64+
absl::StatusCode::kInvalidArgument,
65+
"Custom kid must not be set for KidStrategy::kBase64EncodedKeyId.");
66+
}
67+
std::string base64_kid;
68+
char buffer[4];
69+
absl::big_endian::Store32(buffer, *id_requirement_);
70+
absl::WebSafeBase64Escape(absl::string_view(buffer, 4), &base64_kid);
71+
return base64_kid;
72+
}
73+
case JwtHmacParameters::KidStrategy::kCustom: {
74+
if (!custom_kid_.has_value()) {
75+
return util::Status(absl::StatusCode::kInvalidArgument,
76+
"Custom kid must be set for KidStrategy::kCustom.");
77+
}
78+
return custom_kid_;
79+
}
80+
case JwtHmacParameters::KidStrategy::kIgnored: {
81+
if (custom_kid_.has_value()) {
82+
return util::Status(
83+
absl::StatusCode::kInvalidArgument,
84+
"Custom kid must not be set for KidStrategy::kIgnored.");
85+
}
86+
return absl::nullopt;
87+
}
88+
default:
89+
// Should be unreachable if all valid kid strategies have been handled.
90+
return util::Status(absl::StatusCode::kFailedPrecondition,
91+
"Unknown kid strategy.");
92+
}
93+
}
94+
95+
util::StatusOr<JwtHmacKey> JwtHmacKey::Builder::Build(
96+
PartialKeyAccessToken token) {
97+
if (!parameters_.has_value()) {
98+
return util::Status(absl::StatusCode::kInvalidArgument,
99+
"JWT HMAC parameters must be specified.");
100+
}
101+
if (!key_bytes_.has_value()) {
102+
return util::Status(absl::StatusCode::kInvalidArgument,
103+
"JWT HMAC key bytes must be specified.");
104+
}
105+
if (parameters_->KeySizeInBytes() != key_bytes_->size()) {
106+
return util::Status(
107+
absl::StatusCode::kInvalidArgument,
108+
"Actual JWT HMAC key size does not match size specified in "
109+
"the parameters.");
110+
}
111+
if (parameters_->HasIdRequirement() && !id_requirement_.has_value()) {
112+
return util::Status(
113+
absl::StatusCode::kInvalidArgument,
114+
"Cannot create key without ID requirement with parameters with ID "
115+
"requirement");
116+
}
117+
if (!parameters_->HasIdRequirement() && id_requirement_.has_value()) {
118+
return util::Status(
119+
absl::StatusCode::kInvalidArgument,
120+
"Cannot create key with ID requirement with parameters without ID "
121+
"requirement");
122+
}
123+
util::StatusOr<absl::optional<std::string>> kid = ComputeKid();
124+
if (!kid.ok()) {
125+
return kid.status();
126+
}
127+
return JwtHmacKey(*parameters_, *key_bytes_, id_requirement_, *kid);
128+
}
129+
130+
bool JwtHmacKey::operator==(const Key& other) const {
131+
const JwtHmacKey* that = dynamic_cast<const JwtHmacKey*>(&other);
132+
if (that == nullptr) {
133+
return false;
134+
}
135+
if (parameters_ != that->parameters_) {
136+
return false;
137+
}
138+
if (key_bytes_ != that->key_bytes_) {
139+
return false;
140+
}
141+
if (id_requirement_ != that->id_requirement_) {
142+
return false;
143+
}
144+
if (kid_ != that->kid_) {
145+
return false;
146+
}
147+
return true;
148+
}
149+
150+
} // namespace tink
151+
} // namespace crypto

cc/jwt/jwt_hmac_key.h

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2024 Google LLC
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+
////////////////////////////////////////////////////////////////////////////////
16+
17+
#ifndef TINK_JWT_JWT_HMAC_KEY_H_
18+
#define TINK_JWT_JWT_HMAC_KEY_H_
19+
20+
#include <string>
21+
22+
#include "absl/strings/string_view.h"
23+
#include "absl/types/optional.h"
24+
#include "tink/jwt/jwt_hmac_parameters.h"
25+
#include "tink/jwt/jwt_mac_key.h"
26+
#include "tink/key.h"
27+
#include "tink/partial_key_access_token.h"
28+
#include "tink/restricted_data.h"
29+
#include "tink/util/statusor.h"
30+
31+
namespace crypto {
32+
namespace tink {
33+
34+
// Represents functions to authenticate and verify JWTs using HMAC.
35+
class JwtHmacKey : public JwtMacKey {
36+
public:
37+
// Creates JWT HMAC key instances.
38+
class Builder {
39+
public:
40+
// Copyable and movable.
41+
Builder(const Builder& other) = default;
42+
Builder& operator=(const Builder& other) = default;
43+
Builder(Builder&& other) = default;
44+
Builder& operator=(Builder&& other) = default;
45+
46+
// Creates initially empty parameters builder.
47+
Builder() = default;
48+
49+
Builder& SetParameters(const JwtHmacParameters& parameters);
50+
Builder& SetKeyBytes(const RestrictedData& key_bytes);
51+
Builder& SetIdRequirement(int id_requirement);
52+
Builder& SetCustomKid(absl::string_view custom_kid);
53+
54+
// Creates JWT HMAC key object from this builder.
55+
util::StatusOr<JwtHmacKey> Build(PartialKeyAccessToken token);
56+
57+
private:
58+
util::StatusOr<absl::optional<std::string>> ComputeKid();
59+
60+
absl::optional<JwtHmacParameters> parameters_;
61+
absl::optional<RestrictedData> key_bytes_;
62+
absl::optional<int> id_requirement_;
63+
absl::optional<std::string> custom_kid_;
64+
};
65+
66+
// Copyable and movable.
67+
JwtHmacKey(const JwtHmacKey& other) = default;
68+
JwtHmacKey& operator=(const JwtHmacKey& other) = default;
69+
JwtHmacKey(JwtHmacKey&& other) = default;
70+
JwtHmacKey& operator=(JwtHmacKey&& other) = default;
71+
72+
const RestrictedData& GetKeyBytes(PartialKeyAccessToken token) const {
73+
return key_bytes_;
74+
}
75+
76+
const JwtHmacParameters& GetParameters() const override {
77+
return parameters_;
78+
}
79+
80+
absl::optional<int> GetIdRequirement() const override {
81+
return id_requirement_;
82+
}
83+
84+
absl::optional<std::string> GetKid() const override { return kid_; }
85+
86+
bool operator==(const Key& other) const override;
87+
88+
private:
89+
JwtHmacKey(const JwtHmacParameters& parameters,
90+
const RestrictedData& key_bytes,
91+
absl::optional<int> id_requirement,
92+
absl::optional<std::string> kid)
93+
: parameters_(parameters),
94+
key_bytes_(key_bytes),
95+
id_requirement_(id_requirement),
96+
kid_(kid) {}
97+
98+
JwtHmacParameters parameters_;
99+
RestrictedData key_bytes_;
100+
absl::optional<int> id_requirement_;
101+
absl::optional<std::string> kid_;
102+
};
103+
104+
} // namespace tink
105+
} // namespace crypto
106+
107+
#endif // TINK_JWT_JWT_HMAC_KEY_H_

0 commit comments

Comments
 (0)