Function that DER-encodes the public key of RSA Blind Signature - Probabilistic Signature Scheme algorithm.

It is the C++ alternative of the following Goa function:
http://google3/privacy/net/boq/common/tokens/token_types.go;l=85;rcl=515461856

And the test for this function mimics the test for the above mentioned Goa function as well.

PiperOrigin-RevId: 536525707
diff --git a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/at_crypto_utils_test.cc b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/at_crypto_utils_test.cc
index ed1a76b..f3ecdfd 100644
--- a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/at_crypto_utils_test.cc
+++ b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/at_crypto_utils_test.cc
@@ -264,6 +264,93 @@
   }
 }
 
+// copybara:strip_begin(internal comment)
+// The input public key and the expected DER encoding are taken from the
+// following Goa test:
+// http://google3/privacy/net/boq/common/tokens/token_types_test.go;l=21;rcl=528885322
+// copybara:strip_end
+TEST(CryptoUtilsTest, RsaPssDerEncodingTest) {
+  RSAPublicKey public_key_e_not_padded;
+  RSAPublicKey public_key_e_padded;
+
+  public_key_e_not_padded.set_n(absl::HexStringToBytes(
+      "b259758bb02bc75b68b17612c9bf68c5fa05958a334c61e167bc20bcc75757c126e892"
+      "10b9df3989072cf6260e6883c7cd4af4d31dde9915b69b301fbef962de8c71bd2db5ec62"
+      "5da259712f86a8dc3d241e9688c82391b7bf1ebc358311f55c26be910b76f61fea408ed6"
+      "92f1a9578a622c82c0fcf6f69ef3670e38bfc90f63da4f3bbbd088c8ae7a3c5a55e66f64"
+      "74d562d32cce7b7edd7cf0149ca0e96cb6525e81fbba815a8f12748e34e5135f572b2e17"
+      "b7ba430081597e6fb9033c005884d5935118c60d75b010f6fece7ecdcc1cb7d58d138969"
+      "3d43377f4f3de949cb1e4105e792b96d7f04b0cd262ac33cffc5a890d267425e61c19e93"
+      "63550f2285"));
+  // A hex string of 3 bytes in length is passed.
+  public_key_e_not_padded.set_e(absl::HexStringToBytes("010001"));
+
+  public_key_e_padded.set_n(public_key_e_not_padded.n());
+  // A hex string of 4 bytes in length is passed.
+  public_key_e_padded.set_e(absl::HexStringToBytes("00010001"));
+
+  // Convert both padded and not padded rsa public keys to rsa structs.
+  ASSERT_OK_AND_ASSIGN(
+      bssl::UniquePtr<RSA> rsa_e_not_padded,
+      AnonymousTokensRSAPublicKeyToRSA(public_key_e_not_padded));
+  ASSERT_OK_AND_ASSIGN(bssl::UniquePtr<RSA> rsa_e_padded,
+                       AnonymousTokensRSAPublicKeyToRSA(public_key_e_padded));
+  // Encode both padded and not padded rsa structs to DER.
+  ASSERT_OK_AND_ASSIGN(std::string result_e_not_padded,
+                       RsaSsaPssPublicKeyToDerEncoding(rsa_e_not_padded.get()));
+  ASSERT_OK_AND_ASSIGN(std::string result_e_padded,
+                       RsaSsaPssPublicKeyToDerEncoding(rsa_e_padded.get()));
+
+  std::string expected_der_encoding = absl::HexStringToBytes(
+      "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a"
+      "301806092a864886f70d010108300b0609608648016503040202a2030201300382010f00"
+      "3082010a0282010100b259758bb02bc75b68b17612c9bf68c5fa05958a334c61e167bc20"
+      "bcc75757c126e89210b9df3989072cf6260e6883c7cd4af4d31dde9915b69b301fbef962"
+      "de8c71bd2db5ec625da259712f86a8dc3d241e9688c82391b7bf1ebc358311f55c26be91"
+      "0b76f61fea408ed692f1a9578a622c82c0fcf6f69ef3670e38bfc90f63da4f3bbbd088c8"
+      "ae7a3c5a55e66f6474d562d32cce7b7edd7cf0149ca0e96cb6525e81fbba815a8f12748e"
+      "34e5135f572b2e17b7ba430081597e6fb9033c005884d5935118c60d75b010f6fece7ecd"
+      "cc1cb7d58d1389693d43377f4f3de949cb1e4105e792b96d7f04b0cd262ac33cffc5a890"
+      "d267425e61c19e9363550f22850203010001");
+
+  EXPECT_EQ(result_e_not_padded, expected_der_encoding);
+  EXPECT_EQ(result_e_padded, expected_der_encoding);
+}
+
+// The public key used in this test is taken from the test vectors found here:
+// https://www.ietf.org/archive/id/draft-ietf-privacypass-protocol-10.html#name-issuance-protocol-2-blind-rs
+TEST(CryptoUtilsTest, IetfPrivacyPassBlindRsaPublicKeyToDerTest) {
+  RSAPublicKey public_key;
+  public_key.set_n(absl::HexStringToBytes(
+      "cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab43c0df3a8c12993af1"
+      "2b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a224d6b02f09e5c374d0"
+      "cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5e794fc98a313927687"
+      "9757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5733f7597abe44d31c7"
+      "32db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbcd78a337d17b1dba9e8"
+      "28bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2073fe49740c01f00e1"
+      "09106066983d21e5f83f086e2e823c879cd43cef700d2a352a9babd612d03cad02db134b"
+      "7e225a5f"));
+  public_key.set_e(absl::HexStringToBytes("010001"));
+  ASSERT_OK_AND_ASSIGN(bssl::UniquePtr<RSA> rsa,
+                       AnonymousTokensRSAPublicKeyToRSA(public_key));
+  ASSERT_OK_AND_ASSIGN(std::string result,
+                       RsaSsaPssPublicKeyToDerEncoding(rsa.get()));
+
+  std::string expected_der_encoding = absl::HexStringToBytes(
+      "30820152303d06092a864886f70d01010a3030a00d300b0609608648016503040202a11a"
+      "301806092a864886f70d010108300b0609608648016503040202a2030201300382010f00"
+      "3082010a0282010100cb1aed6b6a95f5b1ce013a4cfcab25b94b2e64a23034e4250a7eab"
+      "43c0df3a8c12993af12b111908d4b471bec31d4b6c9ad9cdda90612a2ee903523e6de5a2"
+      "24d6b02f09e5c374d0cfe01d8f529c500a78a2f67908fa682b5a2b430c81eaf1af72d7b5"
+      "e794fc98a3139276879757ce453b526ef9bf6ceb99979b8423b90f4461a22af37aab0cf5"
+      "733f7597abe44d31c732db68a181c6cbbe607d8c0e52e0655fd9996dc584eca0be87afbc"
+      "d78a337d17b1dba9e828bbd81e291317144e7ff89f55619709b096cbb9ea474cead264c2"
+      "073fe49740c01f00e109106066983d21e5f83f086e2e823c879cd43cef700d2a352a9bab"
+      "d612d03cad02db134b7e225a5f0203010001");
+
+  EXPECT_EQ(result, expected_der_encoding);
+}
+
 using CreateTestKeyPairFunction =
     absl::StatusOr<std::pair<RSAPublicKey, RSAPrivateKey>>();
 
diff --git a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/constants.h b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/constants.h
index 4f73afd..d020d5b 100644
--- a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/constants.h
+++ b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/constants.h
@@ -62,6 +62,20 @@
 
 constexpr int kHkdfPublicMetadataInfoSizeInBytes = 5;
 
+// Object identifier for Rivest, Shamir, Adleman (RSA) Signature Scheme with
+// Appendix - Probabilistic Signature Scheme (RSASSA-PSS) defined here:
+// https://oidref.com/1.2.840.113549.1.1.10
+constexpr char kRsaSsaPssOid[] = "1.2.840.113549.1.1.10";
+
+// Object identifier for SHA384 defined here:
+// https://oidref.com/2.16.840.1.101.3.4.2.2
+constexpr char kSha384Oid[] = "2.16.840.1.101.3.4.2.2";
+
+// Object identifier for RSA algorithm that uses the Mask Generator Function 1
+// (MGF1) defined here:
+// https://oidref.com/1.2.840.113549.1.1.8
+constexpr char kRsaSsaPssMgf1Oid[] = "1.2.840.113549.1.1.8";
+
 }  // namespace anonymous_tokens
 }  // namespace private_membership
 
diff --git a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc
index 16dad4f..6a620f5 100644
--- a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc
+++ b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.cc
@@ -19,6 +19,7 @@
 
 #include <cstdint>
 #include <iterator>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -30,8 +31,10 @@
 #include "quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/constants.h"
 #include "quiche/blind_sign_auth/anonymous_tokens/cpp/shared/status_utils.h"
 #include "quiche/blind_sign_auth/anonymous_tokens/proto/anonymous_tokens.pb.h"
+#include "openssl/bytestring.h"
 #include "openssl/err.h"
 #include "openssl/hkdf.h"
+#include "openssl/mem.h"
 #include "openssl/rand.h"
 #include "openssl/rsa.h"
 
@@ -93,6 +96,27 @@
 
 }  // namespace internal
 
+namespace {
+
+// Marshals an RSA public key in the DER format.
+absl::StatusOr<std::string> MarshalRsaPublicKey(const RSA* rsa) {
+  uint8_t* rsa_public_key_bytes;
+  size_t rsa_public_key_bytes_len = 0;
+  if (!RSA_public_key_to_bytes(&rsa_public_key_bytes, &rsa_public_key_bytes_len,
+                               rsa)) {
+    return absl::InvalidArgumentError(absl::StrCat(
+        "Failed to marshall rsa public key to a DER encoded RSAPublicKey "
+        "structure (RFC 8017): ",
+        GetSslErrors()));
+  }
+  std::string rsa_public_key_str(reinterpret_cast<char*>(rsa_public_key_bytes),
+                                 rsa_public_key_bytes_len);
+  OPENSSL_free(rsa_public_key_bytes);
+  return rsa_public_key_str;
+}
+
+}  // namespace
+
 absl::StatusOr<BnCtxPtr> GetAndStartBigNumCtx() {
   // Create context to be used in intermediate computation.
   BnCtxPtr bn_ctx = BnCtxPtr(BN_CTX_new());
@@ -223,10 +247,10 @@
           "Cannot add word to compute RSA sqrt(2): ", GetSslErrors()));
     }
     if (BN_lshift(sqrt2.get(), sqrt2.get(), 32) != 1) {
-        return absl::InternalError(absl::StrCat(
-            "Cannot shift to compute RSA sqrt(2): ", GetSslErrors()));
+      return absl::InternalError(absl::StrCat(
+          "Cannot shift to compute RSA sqrt(2): ", GetSslErrors()));
     }
-    if (BN_add_word(sqrt2.get(), internal::kBoringSSLRSASqrtTwo[i+1]) != 1) {
+    if (BN_add_word(sqrt2.get(), internal::kBoringSSLRSASqrtTwo[i + 1]) != 1) {
       return absl::InternalError(absl::StrCat(
           "Cannot add word to compute RSA sqrt(2): ", GetSslErrors()));
     }
@@ -523,5 +547,126 @@
   return absl::OkStatus();
 }
 
+absl::StatusOr<std::string> RsaSsaPssPublicKeyToDerEncoding(const RSA* rsa) {
+  if (rsa == NULL) {
+    return absl::InvalidArgumentError("Public Key rsa is null.");
+  }
+  // Create DER encoded RSA public key string.
+  ANON_TOKENS_ASSIGN_OR_RETURN(std::string rsa_public_key_str,
+                               MarshalRsaPublicKey(rsa));
+  // Main CRYPTO ByteBuilder object cbb which will be passed to CBB_finish to
+  // finalize and output the DER encoding of the RsaSsaPssPublicKey.
+  bssl::ScopedCBB cbb;
+  // initial_capacity only serves as a hint.
+  if (!CBB_init(cbb.get(), /*initial_capacity=*/2 * RSA_size(rsa))) {
+    return absl::InternalError("CBB_init() failed.");
+  }
+
+  // Temporary CBB objects to write ASN1 sequences and object identifiers into.
+  CBB outer_seq, inner_seq, param_seq, sha384_seq, mgf1_seq, mgf1_sha384_seq;
+  CBB param0_tag, param1_tag, param2_tag;
+  CBB rsassa_pss_oid, sha384_oid, mgf1_oid, mgf1_sha384_oid;
+  CBB public_key_bit_str_cbb;
+  // RsaSsaPssPublicKey ASN.1 structure example:
+  //
+  //  SEQUENCE {                                               # outer_seq
+  //    SEQUENCE {                                             # inner_seq
+  //      OBJECT_IDENTIFIER{1.2.840.113549.1.1.10}             # rsassa_pss_oid
+  //      SEQUENCE {                                           # param_seq
+  //        [0] {                                              # param0_tag
+  //              {                                            # sha384_seq
+  //                OBJECT_IDENTIFIER{2.16.840.1.101.3.4.2.2}  # sha384_oid
+  //              }
+  //            }
+  //        [1] {                                              # param1_tag
+  //              {                                            # mgf1_seq
+  //                OBJECT_IDENTIFIER{1.2.840.113549.1.1.8}    # mgf1_oid
+  //                {                                          # mgf1_sha384_seq
+  //                  OBJECT_IDENTIFIER{2.16.840.1.101.3.4.2.2}# mgf1_sha384_oid
+  //                }
+  //              }
+  //            }
+  //        [2] {                                              # param2_tag
+  //              INTEGER { 48 }                               # salt length
+  //            }
+  //      }
+  //    }
+  //    BIT STRING {                                    # public_key_bit_str_cbb
+  //      0                                             # unused bits
+  //      der_encoded_rsa_public_key_structure
+  //    }
+  //  }
+  //
+  // Start with the outer sequence.
+  if (!CBB_add_asn1(cbb.get(), &outer_seq, CBS_ASN1_SEQUENCE) ||
+      // The outer sequence consists of two parts; the inner sequence and the
+      // encoded rsa public key.
+      //
+      // Add the inner sequence to the outer sequence.
+      !CBB_add_asn1(&outer_seq, &inner_seq, CBS_ASN1_SEQUENCE) ||
+      // Add object identifier for RSASSA-PSS algorithm to the inner sequence.
+      !CBB_add_asn1(&inner_seq, &rsassa_pss_oid, CBS_ASN1_OBJECT) ||
+      !CBB_add_asn1_oid_from_text(&rsassa_pss_oid, kRsaSsaPssOid,
+                                  strlen(kRsaSsaPssOid)) ||
+      // Add a parameter sequence to the inner sequence.
+      !CBB_add_asn1(&inner_seq, &param_seq, CBS_ASN1_SEQUENCE) ||
+      // SHA384 hash function algorithm identifier will be parameter 0 in the
+      // parameter sequence.
+      !CBB_add_asn1(&param_seq, &param0_tag,
+                    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+      !CBB_add_asn1(&param0_tag, &sha384_seq, CBS_ASN1_SEQUENCE) ||
+      // Add SHA384 object identifier to finish the SHA384 algorithm identifier
+      // and parameter 0.
+      !CBB_add_asn1(&sha384_seq, &sha384_oid, CBS_ASN1_OBJECT) ||
+      !CBB_add_asn1_oid_from_text(&sha384_oid, kSha384Oid,
+                                  strlen(kSha384Oid)) ||
+      // mgf1-SHA384 algorithm identifier as parameter 1 to the parameter
+      // sequence.
+      !CBB_add_asn1(&param_seq, &param1_tag,
+                    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
+      !CBB_add_asn1(&param1_tag, &mgf1_seq, CBS_ASN1_SEQUENCE) ||
+      // Add mgf1 object identifier to the mgf1-SHA384 algorithm identifier.
+      !CBB_add_asn1(&mgf1_seq, &mgf1_oid, CBS_ASN1_OBJECT) ||
+      !CBB_add_asn1_oid_from_text(&mgf1_oid, kRsaSsaPssMgf1Oid,
+                                  strlen(kRsaSsaPssMgf1Oid)) ||
+      // Add SHA384 algorithm identifier to the mgf1-SHA384 algorithm
+      // identifier.
+      !CBB_add_asn1(&mgf1_seq, &mgf1_sha384_seq, CBS_ASN1_SEQUENCE) ||
+      // Add SHA384 object identifier to finish SHA384 algorithm identifier,
+      // mgf1-SHA384 algorithm identifier and parameter 1.
+      !CBB_add_asn1(&mgf1_sha384_seq, &mgf1_sha384_oid, CBS_ASN1_OBJECT) ||
+      !CBB_add_asn1_oid_from_text(&mgf1_sha384_oid, kSha384Oid,
+                                  strlen(kSha384Oid)) ||
+      // Add salt length as parameter 2 to the parameter sequence to finish the
+      // parameter sequence and the inner sequence.
+      !CBB_add_asn1(&param_seq, &param2_tag,
+                    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2) ||
+      !CBB_add_asn1_int64(&param2_tag, kSaltLengthInBytes48) ||
+      // Add public key to the outer sequence as an ASN1 bitstring.
+      !CBB_add_asn1(&outer_seq, &public_key_bit_str_cbb, CBS_ASN1_BITSTRING) ||
+      !CBB_add_u8(&public_key_bit_str_cbb, 0 /* no unused bits */) ||
+      !CBB_add_bytes(
+          &public_key_bit_str_cbb,
+          reinterpret_cast<const uint8_t*>(rsa_public_key_str.data()),
+          rsa_public_key_str.size())) {
+    return absl::InvalidArgumentError(
+        "Failed to generate encoded self-signed certificate");
+  }
+  // Finish creating the DER-encoding of RsaSsaPssPublicKey.
+  uint8_t* rsa_ssa_pss_public_key_der;
+  size_t rsa_ssa_pss_public_key_der_len;
+  if (!CBB_finish(cbb.get(), &rsa_ssa_pss_public_key_der,
+                  &rsa_ssa_pss_public_key_der_len)) {
+    return absl::InternalError("CBB_finish() failed.");
+  }
+  std::string rsa_ssa_pss_public_key_der_str(
+      reinterpret_cast<const char*>(rsa_ssa_pss_public_key_der),
+      rsa_ssa_pss_public_key_der_len);
+  // Free memory.
+  OPENSSL_free(rsa_ssa_pss_public_key_der);
+  // Return the DER encoding as string.
+  return rsa_ssa_pss_public_key_der_str;
+}
+
 }  // namespace anonymous_tokens
 }  // namespace private_membership
diff --git a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.h b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.h
index 109476f..292c969 100644
--- a/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.h
+++ b/quiche/blind_sign_auth/anonymous_tokens/cpp/crypto/crypto_utils.h
@@ -183,6 +183,22 @@
     absl::string_view message,
     std::optional<absl::string_view> public_metadata = std::nullopt);
 
+// This method outputs a DER encoding of RSASSA-PSS (RSA Signature Scheme with
+// Appendix - Probabilistic Signature Scheme) Public Key as described here
+// https://datatracker.ietf.org/doc/html/rfc3447.html using the object
+// identifier(s) here: https://oidref.com/1.2.840.113549.1.1.10  and using a
+// fixed salt length of 48 bytes, SHA384 as the signature's hash function as
+// well as the hash function that the signature's mask generating function is
+// based on. A publicly availble equivalent function is available in Goa here:
+// https://github.com/cloudflare/pat-go/blob/11579ba5b0b9b77d3e8e3d5247a98811227ac82e/x509util.go#L56
+//
+// copybara:strip_begin(internal comment)
+// This method serves as a C++ version for the following Goa method:
+// http://google3/privacy/net/boq/common/tokens/token_types.go;l=85;rcl=515461856
+// copybara:strip_end
+absl::StatusOr<std::string> QUICHE_EXPORT RsaSsaPssPublicKeyToDerEncoding(
+    const RSA* rsa);
+
 }  // namespace anonymous_tokens
 }  // namespace private_membership