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, ¶m_seq, CBS_ASN1_SEQUENCE) || + // SHA384 hash function algorithm identifier will be parameter 0 in the + // parameter sequence. + !CBB_add_asn1(¶m_seq, ¶m0_tag, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) || + !CBB_add_asn1(¶m0_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(¶m_seq, ¶m1_tag, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1) || + !CBB_add_asn1(¶m1_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(¶m_seq, ¶m2_tag, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2) || + !CBB_add_asn1_int64(¶m2_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