| // Copyright 2025 The BoringSSL Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <benchmark/benchmark.h> |
| |
| #include <openssl/base.h> |
| #include <openssl/curve25519.h> |
| #include <openssl/err.h> |
| #include <openssl/evp.h> |
| #include <openssl/rand.h> |
| #include "../crypto/mem_internal.h" |
| #include "../crypto/trust_token/internal.h" |
| |
| namespace { |
| |
| const uint8_t kClientData[] = "\x70TEST CLIENT DATA"; |
| const uint64_t kRedemptionTime = 13374242; |
| |
| enum class TrustTokenStage { |
| kBeginIssue, |
| kIssue, |
| kFinishIssue, |
| kBeginRedeem, |
| kRedeem, |
| }; |
| |
| // NOTE: Fixture is not thread-safe. |
| class TrustTokenFixture : public benchmark::Fixture { |
| public: |
| TrustTokenFixture() = default; |
| |
| protected: |
| bool PrepareClientAndIssuer(const TRUST_TOKEN_METHOD *method, |
| size_t batchsize, benchmark::State &state) { |
| method_ = method; |
| batchsize_ = batchsize; |
| client_.reset(TRUST_TOKEN_CLIENT_new(method_, batchsize_)); |
| issuer_.reset(TRUST_TOKEN_ISSUER_new(method_, batchsize_)); |
| size_t priv_key_len, pub_key_len; |
| if (!client_ || !issuer_ || |
| !TRUST_TOKEN_generate_key( |
| method_, priv_key_, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, |
| pub_key_, &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0) || |
| !TRUST_TOKEN_CLIENT_add_key(client_.get(), &key_index_, pub_key_, |
| pub_key_len) || |
| !TRUST_TOKEN_ISSUER_add_key(issuer_.get(), priv_key_, priv_key_len)) { |
| state.SkipWithError("failed to generate trust token key."); |
| return false; |
| } |
| |
| uint8_t public_key[32], private_key[64]; |
| ED25519_keypair(public_key, private_key); |
| priv_.reset( |
| EVP_PKEY_from_raw_private_key(EVP_pkey_ed25519(), private_key, 32)); |
| pub_.reset( |
| EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), public_key, 32)); |
| if (!priv_ || !pub_) { |
| state.SkipWithError("failed to generate trust token SRR key."); |
| return false; |
| } |
| |
| TRUST_TOKEN_CLIENT_set_srr_key(client_.get(), pub_.get()); |
| TRUST_TOKEN_ISSUER_set_srr_key(issuer_.get(), priv_.get()); |
| uint8_t metadata_key[32]; |
| RAND_bytes(metadata_key, sizeof(metadata_key)); |
| if (!TRUST_TOKEN_ISSUER_set_metadata_key(issuer_.get(), metadata_key, |
| sizeof(metadata_key))) { |
| state.SkipWithError("failed to generate trust token metadata key."); |
| return false; |
| } |
| return true; |
| } |
| |
| static TRUST_TOKEN_PRETOKEN *trust_token_pretoken_dup( |
| const TRUST_TOKEN_PRETOKEN *in) { |
| return static_cast<TRUST_TOKEN_PRETOKEN *>( |
| OPENSSL_memdup(in, sizeof(TRUST_TOKEN_PRETOKEN))); |
| } |
| |
| void SpeedBeginIssue(benchmark::State &state) { |
| for (auto _ : state) { |
| uint8_t *issue_msg = NULL; |
| size_t test_msg_len; |
| int ok = TRUST_TOKEN_CLIENT_begin_issuance(client_.get(), &issue_msg, |
| &test_msg_len, batchsize_); |
| OPENSSL_free(issue_msg); |
| if (!ok) { |
| state.SkipWithError("TRUST_TOKEN_CLIENT_begin_issuance failed."); |
| return; |
| } |
| // Clear pretokens. |
| sk_TRUST_TOKEN_PRETOKEN_pop_free(client_->pretokens, |
| TRUST_TOKEN_PRETOKEN_free); |
| client_->pretokens = sk_TRUST_TOKEN_PRETOKEN_new_null(); |
| } |
| } |
| bool BeginIssue(benchmark::State &state) { |
| uint8_t *issue_msg = NULL; |
| size_t msg_len = 0; |
| if (!TRUST_TOKEN_CLIENT_begin_issuance(client_.get(), &issue_msg, &msg_len, |
| batchsize_)) { |
| state.SkipWithError("TRUST_TOKEN_CLIENT_begin_issuance failed."); |
| return false; |
| } |
| free_issue_msg_.Reset(issue_msg, msg_len); |
| pretokens_.reset(sk_TRUST_TOKEN_PRETOKEN_deep_copy( |
| client_->pretokens, trust_token_pretoken_dup, |
| TRUST_TOKEN_PRETOKEN_free)); |
| return true; |
| } |
| void SpeedIssue(benchmark::State &state) { |
| uint8_t *issue_resp = NULL; |
| size_t test_resp_len, tokens_issued; |
| for (auto _ : state) { |
| int ok = TRUST_TOKEN_ISSUER_issue( |
| issuer_.get(), &issue_resp, &test_resp_len, &tokens_issued, |
| free_issue_msg_.data(), free_issue_msg_.size(), |
| /*public_metadata=*/0, |
| /*private_metadata=*/0, |
| /*max_issuance=*/batchsize_); |
| OPENSSL_free(issue_resp); |
| if (!ok) { |
| state.SkipWithError("TRUST_TOKEN_ISSUER_issue failed."); |
| } |
| } |
| } |
| bool Issue(benchmark::State &state) { |
| uint8_t *issue_resp = NULL; |
| size_t resp_len = 0; |
| size_t tokens_issued; |
| if (!TRUST_TOKEN_ISSUER_issue(issuer_.get(), &issue_resp, &resp_len, |
| &tokens_issued, free_issue_msg_.data(), |
| free_issue_msg_.size(), |
| /*public_metadata=*/0, /*private_metadata=*/0, |
| /*max_issuance=*/batchsize_)) { |
| state.SkipWithError("TRUST_TOKEN_ISSUER_issue failed."); |
| return false; |
| } |
| free_issue_resp_.Reset(issue_resp, resp_len); |
| return true; |
| } |
| void SpeedFinishIssue(benchmark::State &state) { |
| for (auto _ : state) { |
| size_t key_index2; |
| bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> test_tokens( |
| TRUST_TOKEN_CLIENT_finish_issuance(client_.get(), &key_index2, |
| free_issue_resp_.data(), |
| free_issue_resp_.size())); |
| |
| // Reset pretokens. |
| client_->pretokens = sk_TRUST_TOKEN_PRETOKEN_deep_copy( |
| pretokens_.get(), trust_token_pretoken_dup, |
| TRUST_TOKEN_PRETOKEN_free); |
| if (!test_tokens) { |
| state.SkipWithError("TRUST_TOKEN_CLIENT_finish_issuance failed."); |
| return; |
| } |
| } |
| } |
| bool FinishIssue(benchmark::State &state) { |
| tokens_.reset(TRUST_TOKEN_CLIENT_finish_issuance(client_.get(), &key_index_, |
| free_issue_resp_.data(), |
| free_issue_resp_.size())); |
| if (!tokens_ || sk_TRUST_TOKEN_num(tokens_.get()) < 1) { |
| state.SkipWithError("TRUST_TOKEN_CLIENT_finish_issuance failed."); |
| return false; |
| } |
| return true; |
| } |
| void SpeedBeginRedeem(benchmark::State &state) { |
| const TRUST_TOKEN *token = sk_TRUST_TOKEN_value(tokens_.get(), 0); |
| for (auto _ : state) { |
| uint8_t *redeem_msg = NULL; |
| size_t test_redeem_msg_len; |
| int ok = TRUST_TOKEN_CLIENT_begin_redemption( |
| client_.get(), &redeem_msg, &test_redeem_msg_len, token, kClientData, |
| sizeof(kClientData) - 1, kRedemptionTime); |
| OPENSSL_free(redeem_msg); |
| if (!ok) { |
| state.SkipWithError("TRUST_TOKEN_CLIENT_begin_redemption failed."); |
| return; |
| } |
| } |
| } |
| bool BeginRedeem(benchmark::State &state) { |
| const TRUST_TOKEN *token = sk_TRUST_TOKEN_value(tokens_.get(), 0); |
| uint8_t *redeem_msg = NULL; |
| size_t redeem_msg_len = 0; |
| if (!TRUST_TOKEN_CLIENT_begin_redemption( |
| client_.get(), &redeem_msg, &redeem_msg_len, token, kClientData, |
| sizeof(kClientData) - 1, kRedemptionTime)) { |
| state.SkipWithError("TRUST_TOKEN_CLIENT_begin_redemption failed."); |
| return false; |
| } |
| free_redeem_msg_.Reset(redeem_msg, redeem_msg_len); |
| return true; |
| } |
| void SpeedRedeem(benchmark::State &state) { |
| for (auto _ : state) { |
| uint32_t public_value; |
| uint8_t private_value; |
| TRUST_TOKEN *rtoken; |
| uint8_t *client_data = NULL; |
| size_t client_data_len; |
| int ok = TRUST_TOKEN_ISSUER_redeem( |
| issuer_.get(), &public_value, &private_value, &rtoken, &client_data, |
| &client_data_len, free_redeem_msg_.data(), free_redeem_msg_.size()); |
| OPENSSL_free(client_data); |
| if (!ok) { |
| state.SkipWithError("TRUST_TOKEN_ISSUER_redeem failed."); |
| return; |
| } |
| TRUST_TOKEN_free(rtoken); |
| } |
| } |
| |
| void Speed(benchmark::State &state, const TRUST_TOKEN_METHOD *method, |
| size_t batchsize, TrustTokenStage stage) { |
| if (!PrepareClientAndIssuer(method, batchsize, state)) { |
| return; |
| } |
| if (stage == TrustTokenStage::kBeginIssue) { |
| SpeedBeginIssue(state); |
| return; |
| } |
| if (!BeginIssue(state)) { |
| return; |
| } |
| if (stage == TrustTokenStage::kIssue) { |
| SpeedIssue(state); |
| return; |
| } |
| if (!Issue(state)) { |
| return; |
| } |
| if (stage == TrustTokenStage::kFinishIssue) { |
| SpeedFinishIssue(state); |
| return; |
| } |
| if (!FinishIssue(state)) { |
| return; |
| } |
| if (stage == TrustTokenStage::kBeginRedeem) { |
| SpeedBeginRedeem(state); |
| return; |
| } |
| if (!BeginRedeem(state)) { |
| return; |
| } |
| if (stage == TrustTokenStage::kRedeem) { |
| SpeedRedeem(state); |
| return; |
| } |
| } |
| |
| private: |
| size_t batchsize_; |
| const TRUST_TOKEN_METHOD *method_; |
| bssl::UniquePtr<TRUST_TOKEN_CLIENT> client_; |
| bssl::UniquePtr<TRUST_TOKEN_ISSUER> issuer_; |
| |
| uint8_t priv_key_[TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE]; |
| uint8_t pub_key_[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE]; |
| |
| bssl::UniquePtr<EVP_PKEY> priv_; |
| bssl::UniquePtr<EVP_PKEY> pub_; |
| |
| bssl::Array<uint8_t> free_issue_msg_; |
| bssl::Array<uint8_t> free_issue_resp_; |
| bssl::UniquePtr<STACK_OF(TRUST_TOKEN_PRETOKEN)> pretokens_; |
| size_t key_index_; |
| bssl::UniquePtr<STACK_OF(TRUST_TOKEN)> tokens_; |
| bssl::Array<uint8_t> free_redeem_msg_; |
| }; |
| |
| void BM_SpeedTrustTokenKeyGen(benchmark::State &state, |
| const TRUST_TOKEN_METHOD *method) { |
| for (auto _ : state) { |
| uint8_t priv_key[TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE]; |
| uint8_t pub_key[TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE]; |
| size_t priv_key_len, pub_key_len; |
| if (!TRUST_TOKEN_generate_key( |
| method, priv_key, &priv_key_len, TRUST_TOKEN_MAX_PRIVATE_KEY_SIZE, |
| pub_key, &pub_key_len, TRUST_TOKEN_MAX_PUBLIC_KEY_SIZE, 0)) { |
| state.SkipWithError("TRUST_TOKEN_generate_key failed."); |
| return; |
| } |
| } |
| } |
| |
| BENCHMARK_CAPTURE(BM_SpeedTrustTokenKeyGen, SpeedTrustTokenKeyGen experiment_v1, |
| TRUST_TOKEN_experiment_v1()); |
| BENCHMARK_CAPTURE(BM_SpeedTrustTokenKeyGen, |
| SpeedTrustTokenKeyGen experiment_v2_voprf, |
| TRUST_TOKEN_experiment_v2_voprf()); |
| BENCHMARK_CAPTURE(BM_SpeedTrustTokenKeyGen, |
| SpeedTrustTokenKeyGen experiment_v2_pmb, |
| TRUST_TOKEN_experiment_v2_pmb()); |
| |
| #define SPEED(step, method, batchsize) \ |
| BENCHMARK_DEFINE_F(TrustTokenFixture, \ |
| BM_SpeedTrustToken_##method##step##Batch##batchsize)( \ |
| benchmark::State & state) { \ |
| Speed(state, TRUST_TOKEN_##method(), batchsize, TrustTokenStage::k##step); \ |
| } \ |
| BENCHMARK_REGISTER_F(TrustTokenFixture, \ |
| BM_SpeedTrustToken_##method##step##Batch##batchsize); |
| |
| SPEED(BeginIssue, experiment_v1, 1) |
| SPEED(BeginIssue, experiment_v1, 10) |
| SPEED(Issue, experiment_v1, 1) |
| SPEED(Issue, experiment_v1, 10) |
| SPEED(FinishIssue, experiment_v1, 1) |
| SPEED(FinishIssue, experiment_v1, 10) |
| SPEED(BeginRedeem, experiment_v1, 1) |
| SPEED(BeginRedeem, experiment_v1, 10) |
| SPEED(Redeem, experiment_v1, 1) |
| SPEED(Redeem, experiment_v1, 10) |
| |
| SPEED(BeginIssue, experiment_v2_pmb, 1) |
| SPEED(BeginIssue, experiment_v2_pmb, 10) |
| SPEED(Issue, experiment_v2_pmb, 1) |
| SPEED(Issue, experiment_v2_pmb, 10) |
| SPEED(FinishIssue, experiment_v2_pmb, 1) |
| SPEED(FinishIssue, experiment_v2_pmb, 10) |
| SPEED(BeginRedeem, experiment_v2_pmb, 1) |
| SPEED(BeginRedeem, experiment_v2_pmb, 10) |
| SPEED(Redeem, experiment_v2_pmb, 1) |
| SPEED(Redeem, experiment_v2_pmb, 10) |
| |
| SPEED(BeginIssue, experiment_v2_voprf, 1) |
| SPEED(BeginIssue, experiment_v2_voprf, 10) |
| SPEED(Issue, experiment_v2_voprf, 1) |
| SPEED(Issue, experiment_v2_voprf, 10) |
| SPEED(FinishIssue, experiment_v2_voprf, 1) |
| SPEED(FinishIssue, experiment_v2_voprf, 10) |
| SPEED(BeginRedeem, experiment_v2_voprf, 1) |
| SPEED(BeginRedeem, experiment_v2_voprf, 10) |
| SPEED(Redeem, experiment_v2_voprf, 1) |
| SPEED(Redeem, experiment_v2_voprf, 10) |
| |
| } // namespace |