xref: /aosp_15_r20/external/tink/testing/cc/jwt_impl_test.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2021 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 "jwt_impl.h"
18 
19 #include <memory>
20 #include <ostream>
21 #include <sstream>
22 #include <string>
23 
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26 #include "tink/binary_keyset_writer.h"
27 #include "tink/cleartext_keyset_handle.h"
28 #include "tink/jwt/jwt_key_templates.h"
29 #include "tink/jwt/jwt_mac_config.h"
30 #include "tink/jwt/jwt_signature_config.h"
31 #include "tink/util/test_matchers.h"
32 #include "proto/testing_api.grpc.pb.h"
33 
34 namespace crypto {
35 namespace tink {
36 namespace {
37 
38 using ::crypto::tink::BinaryKeysetWriter;
39 using ::crypto::tink::CleartextKeysetHandle;
40 using ::crypto::tink::KeysetHandle;
41 using ::crypto::tink::test::IsOk;
42 using ::google::crypto::tink::KeyTemplate;
43 using ::testing::ElementsAre;
44 using ::testing::Eq;
45 using ::testing::IsEmpty;
46 using ::testing::Not;
47 using ::tink_testing_api::CreationRequest;
48 using ::tink_testing_api::CreationResponse;
49 using ::tink_testing_api::JwtFromJwkSetRequest;
50 using ::tink_testing_api::JwtFromJwkSetResponse;
51 using ::tink_testing_api::JwtSignRequest;
52 using ::tink_testing_api::JwtSignResponse;
53 using ::tink_testing_api::JwtToJwkSetRequest;
54 using ::tink_testing_api::JwtToJwkSetResponse;
55 using ::tink_testing_api::JwtToken;
56 using ::tink_testing_api::JwtValidator;
57 using ::tink_testing_api::JwtVerifyRequest;
58 using ::tink_testing_api::JwtVerifyResponse;
59 
ValidKeyset()60 std::string ValidKeyset() {
61   const KeyTemplate& key_template = ::crypto::tink::JwtHs256Template();
62   util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
63       KeysetHandle::GenerateNew(key_template);
64   EXPECT_THAT(handle.status(), IsOk());
65 
66   std::stringbuf keyset;
67   util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> writer =
68       BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
69   EXPECT_THAT(writer.status(), IsOk());
70 
71   util::Status status = CleartextKeysetHandle::Write((*writer).get(), **handle);
72   EXPECT_THAT(status, IsOk());
73   return keyset.str();
74 }
75 
76 class JwtImplMacTest : public ::testing::Test {
77  protected:
SetUpTestSuite()78   static void SetUpTestSuite() { ASSERT_THAT(JwtMacRegister(), IsOk()); }
79 };
80 
TEST_F(JwtImplMacTest,CreateJwtMacSuccess)81 TEST_F(JwtImplMacTest, CreateJwtMacSuccess) {
82   tink_testing_api::JwtImpl jwt;
83   std::string keyset = ValidKeyset();
84   CreationRequest request;
85   request.mutable_annotated_keyset()->set_serialized_keyset(keyset);
86   CreationResponse response;
87 
88   EXPECT_TRUE(jwt.CreateJwtMac(nullptr, &request, &response).ok());
89   EXPECT_THAT(response.err(), IsEmpty());
90 }
91 
TEST_F(JwtImplMacTest,CreateJwtMacFails)92 TEST_F(JwtImplMacTest, CreateJwtMacFails) {
93   tink_testing_api::JwtImpl jwt;
94   CreationRequest request;
95   request.mutable_annotated_keyset()->set_serialized_keyset("bad keyset");
96   CreationResponse response;
97 
98   EXPECT_TRUE(jwt.CreateJwtMac(nullptr, &request, &response).ok());
99   EXPECT_THAT(response.err(), Not(IsEmpty()));
100 }
101 
TEST_F(JwtImplMacTest,MacComputeVerifySuccess)102 TEST_F(JwtImplMacTest, MacComputeVerifySuccess) {
103   tink_testing_api::JwtImpl jwt;
104   std::string keyset = ValidKeyset();
105   JwtSignRequest comp_request;
106   comp_request.mutable_annotated_keyset()->set_serialized_keyset(keyset);
107   JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
108   raw_jwt->mutable_type_header()->set_value("type_header");
109   raw_jwt->mutable_issuer()->set_value("issuer");
110   raw_jwt->mutable_subject()->set_value("subject");
111   raw_jwt->mutable_audiences()->Add("audience1");
112   raw_jwt->mutable_audiences()->Add("audience2");
113   raw_jwt->mutable_jwt_id()->set_value("jwt_id");
114   raw_jwt->mutable_not_before()->set_seconds(12345);
115   raw_jwt->mutable_not_before()->set_nanos(123000000);
116   raw_jwt->mutable_issued_at()->set_seconds(23456);
117   raw_jwt->mutable_expiration()->set_seconds(34567);
118   auto custom_claims = raw_jwt->mutable_custom_claims();
119   (*custom_claims)["null_claim"].set_null_value(
120       tink_testing_api::NullValue::NULL_VALUE);
121   (*custom_claims)["bool_claim"].set_bool_value(true);
122   (*custom_claims)["number_claim"].set_number_value(123.456);
123   (*custom_claims)["string_claim"].set_string_value("string_value");
124   JwtSignResponse comp_response;
125   EXPECT_TRUE(
126       jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
127   EXPECT_THAT(comp_response.err(), IsEmpty());
128 
129   JwtVerifyRequest verify_request;
130   verify_request.mutable_annotated_keyset()->set_serialized_keyset(keyset);
131   verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
132   JwtValidator* validator = verify_request.mutable_validator();
133   validator->mutable_expected_type_header()->set_value("type_header");
134   validator->mutable_expected_issuer()->set_value("issuer");
135   validator->mutable_expected_audience()->set_value("audience2");
136   validator->mutable_now()->set_seconds(23456);
137   JwtVerifyResponse verify_response;
138 
139   ASSERT_TRUE(
140       jwt.VerifyMacAndDecode(nullptr, &verify_request, &verify_response).ok());
141   ASSERT_THAT(verify_response.err(), IsEmpty());
142   const JwtToken& verified_jwt = verify_response.verified_jwt();
143   EXPECT_EQ(verified_jwt.type_header().value(), "type_header");
144   EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
145   EXPECT_THAT(verified_jwt.subject().value(), Eq("subject"));
146   EXPECT_THAT(verified_jwt.audiences_size(), Eq(2));
147   EXPECT_THAT(verified_jwt.audiences(0), Eq("audience1"));
148   EXPECT_THAT(verified_jwt.audiences(1), Eq("audience2"));
149   EXPECT_THAT(verified_jwt.jwt_id().value(), Eq("jwt_id"));
150   EXPECT_THAT(verified_jwt.not_before().seconds(), Eq(12345));
151   EXPECT_THAT(verified_jwt.not_before().nanos(), Eq(0));
152   EXPECT_THAT(verified_jwt.issued_at().seconds(), Eq(23456));
153   EXPECT_THAT(verified_jwt.issued_at().nanos(), Eq(0));
154   EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
155   EXPECT_THAT(verified_jwt.expiration().nanos(), Eq(0));
156   auto verified_custom_claims = verified_jwt.custom_claims();
157   EXPECT_THAT(verified_custom_claims["null_claim"].null_value(),
158               Eq(tink_testing_api::NullValue::NULL_VALUE));
159   EXPECT_THAT(verified_custom_claims["bool_claim"].bool_value(), Eq(true));
160   EXPECT_THAT(verified_custom_claims["number_claim"].number_value(),
161               Eq(123.456));
162   EXPECT_THAT(verified_custom_claims["string_claim"].string_value(),
163               Eq("string_value"));
164 }
165 
TEST_F(JwtImplMacTest,ComputeBadKeysetFail)166 TEST_F(JwtImplMacTest, ComputeBadKeysetFail) {
167   tink_testing_api::JwtImpl jwt;
168   JwtSignRequest comp_request;
169   comp_request.mutable_annotated_keyset()->set_serialized_keyset("bad keyset");
170   comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("issuer");
171   JwtSignResponse comp_response;
172 
173   EXPECT_TRUE(
174       jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
175   EXPECT_THAT(comp_response.err(), Not(IsEmpty()));
176 }
177 
TEST_F(JwtImplMacTest,VerifyWithWrongIssuerFails)178 TEST_F(JwtImplMacTest, VerifyWithWrongIssuerFails) {
179   tink_testing_api::JwtImpl jwt;
180   std::string keyset = ValidKeyset();
181   JwtSignRequest comp_request;
182   comp_request.mutable_annotated_keyset()->set_serialized_keyset(keyset);
183   comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("unknown");
184   JwtSignResponse comp_response;
185   EXPECT_TRUE(
186       jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
187   EXPECT_THAT(comp_response.err(), IsEmpty());
188 
189   JwtVerifyRequest verify_request;
190   verify_request.mutable_annotated_keyset()->set_serialized_keyset(keyset);
191   verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
192   verify_request.mutable_validator()->mutable_expected_issuer()->set_value(
193       "issuer");
194   JwtVerifyResponse verify_response;
195 
196   EXPECT_TRUE(
197       jwt.VerifyMacAndDecode(nullptr, &verify_request, &verify_response).ok());
198   EXPECT_THAT(verify_response.err(), Not(IsEmpty()));
199 }
200 
201 class JwtImplSignatureTest : public ::testing::Test {
202  protected:
SetUpTestSuite()203   static void SetUpTestSuite() { ASSERT_THAT(JwtSignatureRegister(), IsOk()); }
SetUp()204   void SetUp() override {
205     const KeyTemplate& key_template = ::crypto::tink::JwtEs256Template();
206     util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
207         KeysetHandle::GenerateNew(key_template);
208     EXPECT_THAT(handle.status(), IsOk());
209 
210     std::stringbuf keyset;
211     util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> writer =
212         BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
213     EXPECT_THAT(writer.status(), IsOk());
214 
215     util::Status status =
216         CleartextKeysetHandle::Write((*writer).get(), **handle);
217     EXPECT_THAT(status, IsOk());
218     private_keyset_ = keyset.str();
219 
220     util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> pub_handle =
221         (*handle)->GetPublicKeysetHandle();
222     EXPECT_THAT(pub_handle.status(), IsOk());
223 
224     std::stringbuf pub_keyset;
225     util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> pub_writer =
226         BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&pub_keyset));
227     EXPECT_THAT(writer.status(), IsOk());
228 
229     util::Status pub_status =
230         CleartextKeysetHandle::Write(pub_writer->get(), **pub_handle);
231     EXPECT_THAT(pub_status, IsOk());
232     public_keyset_ = pub_keyset.str();
233   }
234   std::string private_keyset_;
235   std::string public_keyset_;
236 };
237 
TEST_F(JwtImplSignatureTest,CreatePublicKeySignSuccess)238 TEST_F(JwtImplSignatureTest, CreatePublicKeySignSuccess) {
239   tink_testing_api::JwtImpl jwt;
240   CreationRequest request;
241   request.mutable_annotated_keyset()->set_serialized_keyset(private_keyset_);
242   CreationResponse response;
243 
244   EXPECT_TRUE(jwt.CreateJwtPublicKeySign(nullptr, &request, &response).ok());
245   EXPECT_THAT(response.err(), IsEmpty());
246 }
247 
TEST_F(JwtImplSignatureTest,CreatePublicKeySignFailure)248 TEST_F(JwtImplSignatureTest, CreatePublicKeySignFailure) {
249   tink_testing_api::JwtImpl jwt;
250 
251   CreationRequest request;
252   request.mutable_annotated_keyset()->set_serialized_keyset("\x80");
253   CreationResponse response;
254 
255   EXPECT_TRUE(jwt.CreateJwtPublicKeySign(nullptr, &request, &response).ok());
256   EXPECT_THAT(response.err(), Not(IsEmpty()));
257 }
258 
TEST_F(JwtImplSignatureTest,CreatePublicKeyVerifySuccess)259 TEST_F(JwtImplSignatureTest, CreatePublicKeyVerifySuccess) {
260   tink_testing_api::JwtImpl jwt;
261 
262   CreationRequest request;
263   request.mutable_annotated_keyset()->set_serialized_keyset(public_keyset_);
264   CreationResponse response;
265 
266   EXPECT_TRUE(jwt.CreateJwtPublicKeyVerify(nullptr, &request, &response).ok());
267   EXPECT_THAT(response.err(), IsEmpty());
268 }
269 
TEST_F(JwtImplSignatureTest,CreatePublicKeyVerifyFailure)270 TEST_F(JwtImplSignatureTest, CreatePublicKeyVerifyFailure) {
271   tink_testing_api::JwtImpl jwt;
272 
273   CreationRequest request;
274   request.mutable_annotated_keyset()->set_serialized_keyset("\x80");
275   CreationResponse response;
276 
277   EXPECT_TRUE(jwt.CreateJwtPublicKeyVerify(nullptr, &request, &response).ok());
278   EXPECT_THAT(response.err(), Not(IsEmpty()));
279 }
280 
TEST_F(JwtImplSignatureTest,SignVerifySuccess)281 TEST_F(JwtImplSignatureTest, SignVerifySuccess) {
282   tink_testing_api::JwtImpl jwt;
283   JwtSignRequest comp_request;
284   comp_request.mutable_annotated_keyset()->set_serialized_keyset(
285       private_keyset_);
286   JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
287   raw_jwt->mutable_type_header()->set_value("type_header");
288   raw_jwt->mutable_issuer()->set_value("issuer");
289   raw_jwt->mutable_subject()->set_value("subject");
290   raw_jwt->mutable_audiences()->Add("audience1");
291   raw_jwt->mutable_audiences()->Add("audience2");
292   raw_jwt->mutable_jwt_id()->set_value("jwt_id");
293   raw_jwt->mutable_not_before()->set_seconds(12345);
294   raw_jwt->mutable_issued_at()->set_seconds(23456);
295   raw_jwt->mutable_expiration()->set_seconds(34567);
296   auto custom_claims = raw_jwt->mutable_custom_claims();
297   (*custom_claims)["null_claim"].set_null_value(
298       tink_testing_api::NullValue::NULL_VALUE);
299   (*custom_claims)["bool_claim"].set_bool_value(true);
300   (*custom_claims)["number_claim"].set_number_value(123.456);
301   (*custom_claims)["string_claim"].set_string_value("string_value");
302   JwtSignResponse comp_response;
303   EXPECT_TRUE(
304       jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
305   EXPECT_THAT(comp_response.err(), IsEmpty());
306 
307   JwtVerifyRequest verify_request;
308   verify_request.mutable_annotated_keyset()->set_serialized_keyset(
309       public_keyset_);
310   verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
311   JwtValidator* validator = verify_request.mutable_validator();
312   validator->mutable_expected_type_header()->set_value("type_header");
313   validator->mutable_expected_issuer()->set_value("issuer");
314   validator->mutable_expected_audience()->set_value("audience2");
315   validator->mutable_now()->set_seconds(23456);
316   JwtVerifyResponse verify_response;
317 
318   ASSERT_TRUE(
319       jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
320           .ok());
321   ASSERT_THAT(verify_response.err(), IsEmpty());
322   const JwtToken& verified_jwt = verify_response.verified_jwt();
323   EXPECT_EQ(verified_jwt.type_header().value(), "type_header");
324   EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
325   EXPECT_THAT(verified_jwt.subject().value(), Eq("subject"));
326   ASSERT_THAT(verified_jwt.audiences(),
327               ElementsAre("audience1", "audience2"));
328   EXPECT_THAT(verified_jwt.jwt_id().value(), Eq("jwt_id"));
329   EXPECT_THAT(verified_jwt.not_before().seconds(), Eq(12345));
330   EXPECT_THAT(verified_jwt.issued_at().seconds(), Eq(23456));
331   EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
332   auto verified_custom_claims = verified_jwt.custom_claims();
333   EXPECT_THAT(verified_custom_claims["null_claim"].null_value(),
334               Eq(tink_testing_api::NullValue::NULL_VALUE));
335   EXPECT_THAT(verified_custom_claims["bool_claim"].bool_value(), Eq(true));
336   EXPECT_THAT(verified_custom_claims["number_claim"].number_value(),
337               Eq(123.456));
338   EXPECT_THAT(verified_custom_claims["string_claim"].string_value(),
339               Eq("string_value"));
340 }
341 
TEST_F(JwtImplSignatureTest,SignWithBadKeysetFails)342 TEST_F(JwtImplSignatureTest, SignWithBadKeysetFails) {
343   tink_testing_api::JwtImpl jwt;
344   JwtSignRequest comp_request;
345   comp_request.mutable_annotated_keyset()->set_serialized_keyset("bad keyset");
346   comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("issuer");
347   JwtSignResponse comp_response;
348 
349   EXPECT_TRUE(
350       jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
351   EXPECT_THAT(comp_response.err(), Not(IsEmpty()));
352 }
353 
TEST_F(JwtImplSignatureTest,VerifyWithWrongIssuerFails)354 TEST_F(JwtImplSignatureTest, VerifyWithWrongIssuerFails) {
355   tink_testing_api::JwtImpl jwt;
356   JwtSignRequest comp_request;
357   comp_request.mutable_annotated_keyset()->set_serialized_keyset(
358       private_keyset_);
359   comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("unknown");
360   JwtSignResponse comp_response;
361   EXPECT_TRUE(
362       jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
363   EXPECT_THAT(comp_response.err(), IsEmpty());
364 
365   JwtVerifyRequest verify_request;
366   verify_request.mutable_annotated_keyset()->set_serialized_keyset(
367       public_keyset_);
368   verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
369   verify_request.mutable_validator()->mutable_expected_issuer()->set_value(
370       "issuer");
371   JwtVerifyResponse verify_response;
372 
373   EXPECT_TRUE(
374       jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
375           .ok());
376   EXPECT_THAT(verify_response.err(), Not(IsEmpty()));
377 }
378 
TEST_F(JwtImplSignatureTest,SignConvertToAndFromJwkVerifySuccess)379 TEST_F(JwtImplSignatureTest, SignConvertToAndFromJwkVerifySuccess) {
380   tink_testing_api::JwtImpl jwt;
381 
382   // Create a signed token
383   JwtSignRequest comp_request;
384   comp_request.mutable_annotated_keyset()->set_serialized_keyset(
385       private_keyset_);
386   JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
387   raw_jwt->mutable_issuer()->set_value("issuer");
388   raw_jwt->mutable_expiration()->set_seconds(34567);
389   JwtSignResponse comp_response;
390   ASSERT_TRUE(
391       jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
392   ASSERT_THAT(comp_response.err(), IsEmpty());
393 
394   // Generate a JWK set from the public key
395   JwtToJwkSetRequest to_jwk_request;
396   to_jwk_request.set_keyset(public_keyset_);
397 
398   JwtToJwkSetResponse to_jwk_response;
399   ASSERT_TRUE(jwt.ToJwkSet(nullptr, &to_jwk_request, &to_jwk_response).ok());
400   ASSERT_THAT(to_jwk_response.err(), IsEmpty());
401 
402   // Generate a public keyset from the JWK set
403   JwtFromJwkSetRequest from_jwk_request;
404   from_jwk_request.set_jwk_set(to_jwk_response.jwk_set());
405 
406   JwtFromJwkSetResponse from_jwk_response;
407   ASSERT_TRUE(
408       jwt.FromJwkSet(nullptr, &from_jwk_request, &from_jwk_response).ok());
409   ASSERT_THAT(from_jwk_response.err(), IsEmpty());
410 
411   // Verify the token using the public keyset
412   JwtVerifyRequest verify_request;
413   verify_request.mutable_annotated_keyset()->set_serialized_keyset(
414       from_jwk_response.keyset());
415   verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
416   JwtValidator* validator = verify_request.mutable_validator();
417   validator->mutable_expected_issuer()->set_value("issuer");
418   validator->mutable_now()->set_seconds(23456);
419   JwtVerifyResponse verify_response;
420 
421   ASSERT_TRUE(
422       jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
423           .ok());
424   ASSERT_THAT(verify_response.err(), IsEmpty());
425   const JwtToken& verified_jwt = verify_response.verified_jwt();
426   EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
427   EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
428 }
429 
TEST_F(JwtImplSignatureTest,FromJwkInvalidFails)430 TEST_F(JwtImplSignatureTest, FromJwkInvalidFails) {
431   tink_testing_api::JwtImpl jwt;
432   JwtFromJwkSetRequest from_jwk_request;
433   from_jwk_request.set_jwk_set("invalid");
434 
435   JwtFromJwkSetResponse from_jwk_response;
436   ASSERT_TRUE(
437       jwt.FromJwkSet(nullptr, &from_jwk_request, &from_jwk_response).ok());
438   EXPECT_THAT(from_jwk_response.err(), Not(IsEmpty()));
439 }
440 
TEST_F(JwtImplSignatureTest,ToJwkInvalidFails)441 TEST_F(JwtImplSignatureTest, ToJwkInvalidFails) {
442   tink_testing_api::JwtImpl jwt;
443   JwtToJwkSetRequest to_jwk_request;
444   to_jwk_request.set_keyset("invalid");
445 
446   JwtToJwkSetResponse to_jwk_response;
447   ASSERT_TRUE(jwt.ToJwkSet(nullptr, &to_jwk_request, &to_jwk_response).ok());
448   EXPECT_THAT(to_jwk_response.err(), Not(IsEmpty()));
449 }
450 
451 }  // namespace
452 }  // namespace tink
453 }  // namespace crypto
454 
455