// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/internal/signature_algorithm.h" #include #include #include "base/memory/ptr_util.h" #include "base/numerics/safe_math.h" #include "net/cert/internal/cert_error_params.h" #include "net/cert/internal/cert_errors.h" #include "net/der/input.h" #include "net/der/parse_values.h" #include "net/der/parser.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/digest.h" namespace net { namespace { // md2WithRSAEncryption // In dotted notation: 1.2.840.113549.1.1.2 const uint8_t kOidMd2WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x02}; // md4WithRSAEncryption // In dotted notation: 1.2.840.113549.1.1.3 const uint8_t kOidMd4WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x03}; // md5WithRSAEncryption // In dotted notation: 1.2.840.113549.1.1.4 const uint8_t kOidMd5WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04}; // From RFC 5912: // // sha1WithRSAEncryption OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) // pkcs-1(1) 5 } // // In dotted notation: 1.2.840.113549.1.1.5 const uint8_t kOidSha1WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05}; // sha1WithRSASignature is a deprecated equivalent of // sha1WithRSAEncryption. // // It originates from the NIST Open Systems Environment (OSE) // Implementor's Workshop (OIW). // // It is supported for compatibility with Microsoft's certificate APIs and // tools, particularly makecert.exe, which default(ed/s) to this OID for SHA-1. // // See also: https://bugzilla.mozilla.org/show_bug.cgi?id=1042479 // // In dotted notation: 1.3.14.3.2.29 const uint8_t kOidSha1WithRsaSignature[] = {0x2b, 0x0e, 0x03, 0x02, 0x1d}; // From RFC 5912: // // pkcs-1 OBJECT IDENTIFIER ::= // { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } // From RFC 5912: // // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } // // In dotted notation: 1.2.840.113549.1.1.11 const uint8_t kOidSha256WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b}; // From RFC 5912: // // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } // // In dotted notation: 1.2.840.113549.1.1.11 const uint8_t kOidSha384WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c}; // From RFC 5912: // // sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } // // In dotted notation: 1.2.840.113549.1.1.13 const uint8_t kOidSha512WithRsaEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d}; // From RFC 5912: // // ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) // signatures(4) 1 } // // In dotted notation: 1.2.840.10045.4.1 const uint8_t kOidEcdsaWithSha1[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01}; // From RFC 5912: // // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) // ecdsa-with-SHA2(3) 2 } // // In dotted notation: 1.2.840.10045.4.3.2 const uint8_t kOidEcdsaWithSha256[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02}; // From RFC 5912: // // ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) // ecdsa-with-SHA2(3) 3 } // // In dotted notation: 1.2.840.10045.4.3.3 const uint8_t kOidEcdsaWithSha384[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03}; // From RFC 5912: // // ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) // ecdsa-with-SHA2(3) 4 } // // In dotted notation: 1.2.840.10045.4.3.4 const uint8_t kOidEcdsaWithSha512[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04}; // From RFC 5912: // // id-RSASSA-PSS OBJECT IDENTIFIER ::= { pkcs-1 10 } // // In dotted notation: 1.2.840.113549.1.1.10 const uint8_t kOidRsaSsaPss[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a}; // From RFC 5912: // // dsa-with-sha1 OBJECT IDENTIFIER ::= { // iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 3 } // // In dotted notation: 1.2.840.10040.4.3 const uint8_t kOidDsaWithSha1[] = {0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03}; // From RFC 5912: // // dsa-with-sha256 OBJECT IDENTIFIER ::= { // joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) // csor(3) algorithms(4) id-dsa-with-sha2(3) 2 } // // In dotted notation: 2.16.840.1.101.3.4.3.2 const uint8_t kOidDsaWithSha256[] = {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02}; // From RFC 5912: // // id-mgf1 OBJECT IDENTIFIER ::= { pkcs-1 8 } // // In dotted notation: 1.2.840.113549.1.1.8 const uint8_t kOidMgf1[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08}; // RFC 5280 section 4.1.1.2 defines signatureAlgorithm as: // // AlgorithmIdentifier ::= SEQUENCE { // algorithm OBJECT IDENTIFIER, // parameters ANY DEFINED BY algorithm OPTIONAL } WARN_UNUSED_RESULT bool ParseAlgorithmIdentifier(const der::Input& input, der::Input* algorithm, der::Input* parameters) { der::Parser parser(input); der::Parser algorithm_identifier_parser; if (!parser.ReadSequence(&algorithm_identifier_parser)) return false; // There shouldn't be anything after the sequence. This is by definition, // as the input to this function is expected to be a single // AlgorithmIdentifier. if (parser.HasMore()) return false; if (!algorithm_identifier_parser.ReadTag(der::kOid, algorithm)) return false; // Read the optional parameters to a der::Input. The parameters can be at // most one TLV (for instance NULL or a sequence). // // Note that nothing is allowed after the single optional "parameters" TLV. // This is because RFC 5912's notation for AlgorithmIdentifier doesn't // explicitly list an extension point after "parameters". *parameters = der::Input(); if (algorithm_identifier_parser.HasMore() && !algorithm_identifier_parser.ReadRawTLV(parameters)) { return false; } return !algorithm_identifier_parser.HasMore(); } // Returns true if |input| is empty. WARN_UNUSED_RESULT bool IsEmpty(const der::Input& input) { return input.Length() == 0; } // Returns true if the entirety of the input is a NULL value. WARN_UNUSED_RESULT bool IsNull(const der::Input& input) { der::Parser parser(input); der::Input null_value; if (!parser.ReadTag(der::kNull, &null_value)) return false; // NULL values are TLV encoded; the value is expected to be empty. if (!IsEmpty(null_value)) return false; // By definition of this function, the entire input must be a NULL. return !parser.HasMore(); } // Parses an RSA PKCS#1 v1.5 signature algorithm given the DER-encoded // "parameters" from the parsed AlgorithmIdentifier, and the hash algorithm // that was implied by the AlgorithmIdentifier's OID. // // Returns a nullptr on failure. // // RFC 5912 requires that the parameters for RSA PKCS#1 v1.5 algorithms be NULL // ("PARAMS TYPE NULL ARE required"), however an empty parameter is also // allowed for compatibility with non-compliant OCSP responders: // // sa-rsaWithSHA1 SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha1WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha1 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS {IDENTIFIED BY sha1WithRSAEncryption } // } // // sa-sha256WithRSAEncryption SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha256WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha256 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS { IDENTIFIED BY sha256WithRSAEncryption } // } // // sa-sha384WithRSAEncryption SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha384WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha384 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS { IDENTIFIED BY sha384WithRSAEncryption } // } // // sa-sha512WithRSAEncryption SIGNATURE-ALGORITHM ::= { // IDENTIFIER sha512WithRSAEncryption // PARAMS TYPE NULL ARE required // HASHES { mda-sha512 } // PUBLIC-KEYS { pk-rsa } // SMIME-CAPS { IDENTIFIED BY sha512WithRSAEncryption } // } std::unique_ptr ParseRsaPkcs1(DigestAlgorithm digest, const der::Input& params) { // TODO(svaldez): Add warning about non-strict parsing. if (!IsNull(params) && !IsEmpty(params)) return nullptr; return SignatureAlgorithm::CreateRsaPkcs1(digest); } // Parses a DSA signature algorithm given the DER-encoded // "parameters" from the parsed AlgorithmIdentifier, and the hash algorithm // that was implied by the AlgorithmIdentifier's OID. // // Returns a nullptr on failure. // // RFC 5912 requires that the parameters for DSA algorithms be absent. std::unique_ptr ParseDsa(DigestAlgorithm digest, const der::Input& params) { // TODO(svaldez): Add warning about non-strict parsing. if (!IsNull(params) && !IsEmpty(params)) return nullptr; return SignatureAlgorithm::CreateDsa(digest); } // Parses an ECDSA signature algorithm given the DER-encoded "parameters" from // the parsed AlgorithmIdentifier, and the hash algorithm that was implied by // the AlgorithmIdentifier's OID. // // On failure returns a nullptr. // // RFC 5912 requires that the parameters for ECDSA algorithms be absent // ("PARAMS TYPE NULL ARE absent"): // // sa-ecdsaWithSHA1 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA1 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha1 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS {IDENTIFIED BY ecdsa-with-SHA1 } // } // // sa-ecdsaWithSHA256 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA256 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha256 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA256 } // } // // sa-ecdsaWithSHA384 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA384 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha384 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA384 } // } // // sa-ecdsaWithSHA512 SIGNATURE-ALGORITHM ::= { // IDENTIFIER ecdsa-with-SHA512 // VALUE ECDSA-Sig-Value // PARAMS TYPE NULL ARE absent // HASHES { mda-sha512 } // PUBLIC-KEYS { pk-ec } // SMIME-CAPS { IDENTIFIED BY ecdsa-with-SHA512 } // } std::unique_ptr ParseEcdsa(DigestAlgorithm digest, const der::Input& params) { if (!IsEmpty(params)) return nullptr; return SignatureAlgorithm::CreateEcdsa(digest); } // Parses a MaskGenAlgorithm as defined by RFC 5912: // // MaskGenAlgorithm ::= AlgorithmIdentifier{ALGORITHM, // {PKCS1MGFAlgorithms}} // // mgf1SHA1 MaskGenAlgorithm ::= { // algorithm id-mgf1, // parameters HashAlgorithm : sha1Identifier // } // // -- // -- Define the set of mask generation functions // -- // -- If the identifier is id-mgf1, any of the listed hash // -- algorithms may be used. // -- // // PKCS1MGFAlgorithms ALGORITHM ::= { // { IDENTIFIER id-mgf1 PARAMS TYPE HashAlgorithm ARE required }, // ... // } // // Note that the possible mask gen algorithms is extensible. However at present // the only function supported is MGF1, as that is the singular mask gen // function defined by RFC 4055 / RFC 5912. WARN_UNUSED_RESULT bool ParseMaskGenAlgorithm(const der::Input input, DigestAlgorithm* mgf1_hash) { der::Input oid; der::Input params; if (!ParseAlgorithmIdentifier(input, &oid, ¶ms)) return false; // MGF1 is the only supported mask generation algorithm. if (oid != der::Input(kOidMgf1)) return false; return ParseHashAlgorithm(params, mgf1_hash); } // Consumes an optional, explicitly-tagged INTEGER from |parser|, using the // indicated context-specific class number. Values greater than 32-bits will be // rejected. // // Returns true on success and sets |*present| to true if the field was present. WARN_UNUSED_RESULT bool ReadOptionalContextSpecificUint32(der::Parser* parser, uint8_t class_number, uint32_t* out, bool* present) { der::Input value; bool has_value; // Read the context specific value. if (!parser->ReadOptionalTag(der::ContextSpecificConstructed(class_number), &value, &has_value)) { return false; } if (has_value) { // Parse the integer contained in it. der::Parser number_parser(value); uint64_t uint64_value; if (!number_parser.ReadUint64(&uint64_value)) return false; if (number_parser.HasMore()) return false; // Cast the number to a uint32_t base::CheckedNumeric casted(uint64_value); if (!casted.IsValid()) return false; *out = casted.ValueOrDie(); } *present = has_value; return true; } // Parses the parameters for an RSASSA-PSS signature algorithm, as defined by // RFC 5912: // // sa-rsaSSA-PSS SIGNATURE-ALGORITHM ::= { // IDENTIFIER id-RSASSA-PSS // PARAMS TYPE RSASSA-PSS-params ARE required // HASHES { mda-sha1 | mda-sha224 | mda-sha256 | mda-sha384 // | mda-sha512 } // PUBLIC-KEYS { pk-rsa | pk-rsaSSA-PSS } // SMIME-CAPS { IDENTIFIED BY id-RSASSA-PSS } // } // // RSASSA-PSS-params ::= SEQUENCE { // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, // saltLength [2] INTEGER DEFAULT 20, // trailerField [3] INTEGER DEFAULT 1 // } // // Which is to say the parameters MUST be present, and of type // RSASSA-PSS-params. std::unique_ptr ParseRsaPss(const der::Input& params) { der::Parser parser(params); der::Parser params_parser; if (!parser.ReadSequence(¶ms_parser)) return nullptr; // There shouldn't be anything after the sequence (by definition the // parameters is a single sequence). if (parser.HasMore()) return nullptr; bool has_field; der::Input field; // Parse: // hashAlgorithm [0] HashAlgorithm DEFAULT sha1Identifier, DigestAlgorithm hash = DigestAlgorithm::Sha1; if (!params_parser.ReadOptionalTag(der::ContextSpecificConstructed(0), &field, &has_field)) { return nullptr; } if (has_field && !ParseHashAlgorithm(field, &hash)) return nullptr; // Parse: // maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT mgf1SHA1, DigestAlgorithm mgf1_hash = DigestAlgorithm::Sha1; if (!params_parser.ReadOptionalTag(der::ContextSpecificConstructed(1), &field, &has_field)) { return nullptr; } if (has_field && !ParseMaskGenAlgorithm(field, &mgf1_hash)) return nullptr; // Parse: // saltLength [2] INTEGER DEFAULT 20, uint32_t salt_length = 20u; if (!ReadOptionalContextSpecificUint32(¶ms_parser, 2, &salt_length, &has_field)) { return nullptr; } // Parse: // trailerField [3] INTEGER DEFAULT 1 uint32_t trailer_field = 1u; if (!ReadOptionalContextSpecificUint32(¶ms_parser, 3, &trailer_field, &has_field)) { return nullptr; } // RFC 4055 says that the trailer field must be 1: // // The trailerField field is an integer. It provides // compatibility with IEEE Std 1363a-2004 [P1363A]. The value // MUST be 1, which represents the trailer field with hexadecimal // value 0xBC. Other trailer fields, including the trailer field // composed of HashID concatenated with 0xCC that is specified in // IEEE Std 1363a, are not supported. Implementations that // perform signature generation MUST omit the trailerField field, // indicating that the default trailer field value was used. // Implementations that perform signature validation MUST // recognize both a present trailerField field with value 1 and an // absent trailerField field. if (trailer_field != 1) return nullptr; // There must not be any unconsumed data left. (RFC 5912 does not explicitly // include an extensibility point for RSASSA-PSS-params) if (params_parser.HasMore()) return nullptr; return SignatureAlgorithm::CreateRsaPss(hash, mgf1_hash, salt_length); } DEFINE_CERT_ERROR_ID(kUnknownAlgorithmIdentifierOid, "Unknown AlgorithmIdentifier OID"); } // namespace WARN_UNUSED_RESULT bool ParseHashAlgorithm(const der::Input& input, DigestAlgorithm* out) { CBS cbs; CBS_init(&cbs, input.UnsafeData(), input.Length()); const EVP_MD* md = EVP_parse_digest_algorithm(&cbs); if (md == EVP_sha1()) { *out = DigestAlgorithm::Sha1; } else if (md == EVP_sha256()) { *out = DigestAlgorithm::Sha256; } else if (md == EVP_sha384()) { *out = DigestAlgorithm::Sha384; } else if (md == EVP_sha512()) { *out = DigestAlgorithm::Sha512; } else { // TODO(eroman): Support MD2, MD4, MD5 for completeness? // Unsupported digest algorithm. return false; } return true; } RsaPssParameters::RsaPssParameters(DigestAlgorithm mgf1_hash, uint32_t salt_length) : mgf1_hash_(mgf1_hash), salt_length_(salt_length) { } SignatureAlgorithm::~SignatureAlgorithm() = default; std::unique_ptr SignatureAlgorithm::Create( const der::Input& algorithm_identifier, CertErrors* errors) { // TODO(crbug.com/634443): Add useful error information. der::Input oid; der::Input params; if (!ParseAlgorithmIdentifier(algorithm_identifier, &oid, ¶ms)) return nullptr; // TODO(eroman): Each OID is tested for equality in order, which is not // particularly efficient. if (oid == der::Input(kOidSha1WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Sha1, params); if (oid == der::Input(kOidSha256WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Sha256, params); if (oid == der::Input(kOidSha384WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Sha384, params); if (oid == der::Input(kOidSha512WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Sha512, params); if (oid == der::Input(kOidEcdsaWithSha1)) return ParseEcdsa(DigestAlgorithm::Sha1, params); if (oid == der::Input(kOidEcdsaWithSha256)) return ParseEcdsa(DigestAlgorithm::Sha256, params); if (oid == der::Input(kOidEcdsaWithSha384)) return ParseEcdsa(DigestAlgorithm::Sha384, params); if (oid == der::Input(kOidEcdsaWithSha512)) return ParseEcdsa(DigestAlgorithm::Sha512, params); if (oid == der::Input(kOidRsaSsaPss)) return ParseRsaPss(params); if (oid == der::Input(kOidSha1WithRsaSignature)) return ParseRsaPkcs1(DigestAlgorithm::Sha1, params); if (oid == der::Input(kOidMd2WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Md2, params); if (oid == der::Input(kOidMd4WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Md4, params); if (oid == der::Input(kOidMd5WithRsaEncryption)) return ParseRsaPkcs1(DigestAlgorithm::Md5, params); if (oid == der::Input(kOidDsaWithSha1)) return ParseDsa(DigestAlgorithm::Sha1, params); if (oid == der::Input(kOidDsaWithSha256)) return ParseDsa(DigestAlgorithm::Sha256, params); // Unknown OID. if (errors) { errors->AddError(kUnknownAlgorithmIdentifierOid, CreateCertErrorParams2Der("oid", oid, "params", params)); } return nullptr; } std::unique_ptr SignatureAlgorithm::CreateRsaPkcs1( DigestAlgorithm digest) { return base::WrapUnique( new SignatureAlgorithm(SignatureAlgorithmId::RsaPkcs1, digest, nullptr)); } std::unique_ptr SignatureAlgorithm::CreateDsa( DigestAlgorithm digest) { return base::WrapUnique( new SignatureAlgorithm(SignatureAlgorithmId::Dsa, digest, nullptr)); } std::unique_ptr SignatureAlgorithm::CreateEcdsa( DigestAlgorithm digest) { return base::WrapUnique( new SignatureAlgorithm(SignatureAlgorithmId::Ecdsa, digest, nullptr)); } std::unique_ptr SignatureAlgorithm::CreateRsaPss( DigestAlgorithm digest, DigestAlgorithm mgf1_hash, uint32_t salt_length) { return base::WrapUnique(new SignatureAlgorithm( SignatureAlgorithmId::RsaPss, digest, std::make_unique(mgf1_hash, salt_length))); } const RsaPssParameters* SignatureAlgorithm::ParamsForRsaPss() const { if (algorithm_ == SignatureAlgorithmId::RsaPss) return static_cast(params_.get()); return nullptr; } bool SignatureAlgorithm::IsEquivalent(const der::Input& alg1_tlv, const der::Input& alg2_tlv) { if (alg1_tlv == alg2_tlv) return true; std::unique_ptr alg1 = Create(alg1_tlv, nullptr); std::unique_ptr alg2 = Create(alg2_tlv, nullptr); // Do checks that apply to all algorithms. if (!alg1 || !alg2 || (alg1->algorithm() != alg2->algorithm()) || (alg1->digest() != alg2->digest())) { return false; } // Check algorithm-specific parameters for equality. switch (alg1->algorithm()) { case SignatureAlgorithmId::RsaPkcs1: case SignatureAlgorithmId::Ecdsa: case SignatureAlgorithmId::Dsa: DCHECK(!alg1->has_params()); DCHECK(!alg2->has_params()); return true; case SignatureAlgorithmId::RsaPss: { const RsaPssParameters* params1 = alg1->ParamsForRsaPss(); const RsaPssParameters* params2 = alg2->ParamsForRsaPss(); return params1 && params2 && (params1->salt_length() == params2->salt_length()) && (params1->mgf1_hash() == params2->mgf1_hash()); } } return false; } SignatureAlgorithm::SignatureAlgorithm( SignatureAlgorithmId algorithm, DigestAlgorithm digest, std::unique_ptr params) : algorithm_(algorithm), digest_(digest), params_(std::move(params)) {} } // namespace net