Write a CBS-based RSA-PSS parameter parser
For now this is just used by the X.509 verification logic and supports
the three sets of parameters we accept. Later EVP_PKEY_RSA_PSS will use
the same parser.
Bug: 384818542
Change-Id: I4d9a83283cbf239268f37d814f00d0b6ec3b46f2
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81655
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/digest/digest_extra.cc b/crypto/digest/digest_extra.cc
index 4312142..03df5f7 100644
--- a/crypto/digest/digest_extra.cc
+++ b/crypto/digest/digest_extra.cc
@@ -23,6 +23,7 @@
#include <openssl/nid.h>
#include <openssl/obj.h>
#include <openssl/sha.h>
+#include <openssl/span.h>
#include "../asn1/internal.h"
#include "../fipsmodule/digest/internal.h"
@@ -102,42 +103,41 @@
{{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04}, 9, NID_sha224},
};
-static const EVP_MD *cbs_to_md(const CBS *cbs) {
- for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMDOIDs); i++) {
- if (CBS_len(cbs) == kMDOIDs[i].oid_len &&
- OPENSSL_memcmp(CBS_data(cbs), kMDOIDs[i].oid, kMDOIDs[i].oid_len) ==
- 0) {
- return EVP_get_digestbynid(kMDOIDs[i].nid);
+static int cbs_to_digest_nid(const CBS *cbs) {
+ for (const auto &md : kMDOIDs) {
+ if (bssl::Span<const uint8_t>(*cbs) ==
+ bssl::Span(md.oid).first(md.oid_len)) {
+ return md.nid;
}
}
-
- return NULL;
+ return NID_undef;
}
const EVP_MD *EVP_get_digestbyobj(const ASN1_OBJECT *obj) {
- // Handle objects with no corresponding OID. Note we don't use |OBJ_obj2nid|
- // here to avoid pulling in the OID table.
- if (obj->nid != NID_undef) {
- return EVP_get_digestbynid(obj->nid);
+ int nid = obj->nid;
+ if (nid == NID_undef) {
+ // Handle objects with no saved NID. Note we don't use |OBJ_obj2nid| here to
+ // avoid pulling in the OID table.
+ CBS cbs;
+ CBS_init(&cbs, OBJ_get0_data(obj), OBJ_length(obj));
+ nid = cbs_to_digest_nid(&cbs);
}
- CBS cbs;
- CBS_init(&cbs, OBJ_get0_data(obj), OBJ_length(obj));
- return cbs_to_md(&cbs);
+ return nid == NID_undef ? nullptr : EVP_get_digestbynid(nid);
}
-const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs) {
+int EVP_parse_digest_algorithm_nid(CBS *cbs) {
CBS algorithm, oid;
if (!CBS_get_asn1(cbs, &algorithm, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&algorithm, &oid, CBS_ASN1_OBJECT)) {
OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_DECODE_ERROR);
- return NULL;
+ return NID_undef;
}
- const EVP_MD *ret = cbs_to_md(&oid);
- if (ret == NULL) {
+ int ret = cbs_to_digest_nid(&oid);
+ if (ret == NID_undef) {
OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_UNKNOWN_HASH);
- return NULL;
+ return NID_undef;
}
// The parameters, if present, must be NULL. Historically, whether the NULL
@@ -150,13 +150,21 @@
CBS_len(¶m) != 0 || //
CBS_len(&algorithm) != 0) {
OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_DECODE_ERROR);
- return NULL;
+ return NID_undef;
}
}
return ret;
}
+const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs) {
+ int nid = EVP_parse_digest_algorithm_nid(cbs);
+ if (nid == NID_undef) {
+ return nullptr;
+ }
+ return EVP_get_digestbynid(nid);
+}
+
static int marshal_digest_algorithm(CBB *cbb, const EVP_MD *md,
bool with_null) {
CBB algorithm, oid, null;
diff --git a/crypto/rsa/internal.h b/crypto/rsa/internal.h
index 21552bf..5fb4f17 100644
--- a/crypto/rsa/internal.h
+++ b/crypto/rsa/internal.h
@@ -28,6 +28,31 @@
size_t param_len, const EVP_MD *md,
const EVP_MD *mgf1md);
+enum rsa_pss_params_t {
+ // RSA-PSS using SHA-256, MGF1 with SHA-256, salt length 32.
+ rsa_pss_sha256,
+ // RSA-PSS using SHA-384, MGF1 with SHA-384, salt length 48.
+ rsa_pss_sha384,
+ // RSA-PSS using SHA-512, MGF1 with SHA-512, salt length 64.
+ rsa_pss_sha512,
+};
+
+// rsa_pss_params_get_md returns the hash function used with |params|. This also
+// specifies the MGF-1 hash and the salt length because we do not support other
+// configurations.
+const EVP_MD *rsa_pss_params_get_md(rsa_pss_params_t params);
+
+// rsa_marshal_pss_params marshals |params| as a DER-encoded RSASSA-PSS-params
+// (RFC 4055). It returns one on success and zero on error.
+int rsa_marshal_pss_params(CBB *cbb, rsa_pss_params_t params);
+
+// rsa_marshal_pss_params decodes a DER-encoded RSASSA-PSS-params
+// (RFC 4055). It returns one on success and zero on error. On success, it sets
+// |*out| to the result. If |allow_explicit_trailer| is non-zero, an explicit
+// encoding of the trailerField is allowed, although it is not valid DER.
+int rsa_parse_pss_params(CBS *cbs, rsa_pss_params_t *out,
+ int allow_explicit_trailer);
+
#if defined(__cplusplus)
} // extern C
diff --git a/crypto/rsa/rsa_asn1.cc b/crypto/rsa/rsa_asn1.cc
index a71cc3a..1a1fab3 100644
--- a/crypto/rsa/rsa_asn1.cc
+++ b/crypto/rsa/rsa_asn1.cc
@@ -20,12 +20,17 @@
#include <openssl/bn.h>
#include <openssl/bytestring.h>
+#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/mem.h>
+#include <openssl/nid.h>
+#include <openssl/span.h>
+#include <openssl/x509.h>
-#include "../fipsmodule/rsa/internal.h"
#include "../bytestring/internal.h"
+#include "../fipsmodule/rsa/internal.h"
#include "../internal.h"
+#include "internal.h"
static int parse_integer(CBS *cbs, BIGNUM **out) {
@@ -281,3 +286,146 @@
OPENSSL_free(der);
return ret;
}
+
+static const uint8_t kPSSParamsSHA256[] = {
+ 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30,
+ 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20};
+
+static const uint8_t kPSSParamsSHA384[] = {
+ 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30,
+ 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x02, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30};
+
+static const uint8_t kPSSParamsSHA512[] = {
+ 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30,
+ 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x02, 0x03, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40};
+
+const EVP_MD *rsa_pss_params_get_md(rsa_pss_params_t params) {
+ switch (params) {
+ case rsa_pss_sha256:
+ return EVP_sha256();
+ case rsa_pss_sha384:
+ return EVP_sha384();
+ case rsa_pss_sha512:
+ return EVP_sha512();
+ }
+ abort();
+}
+
+int rsa_marshal_pss_params(CBB *cbb, rsa_pss_params_t params) {
+ bssl::Span<const uint8_t> bytes;
+ switch (params) {
+ case rsa_pss_sha256:
+ bytes = kPSSParamsSHA256;
+ break;
+ case rsa_pss_sha384:
+ bytes = kPSSParamsSHA384;
+ break;
+ case rsa_pss_sha512:
+ bytes = kPSSParamsSHA512;
+ break;
+ }
+
+ return CBB_add_bytes(cbb, bytes.data(), bytes.size());
+}
+
+// 1.2.840.113549.1.1.8
+static const uint8_t kMGF1OID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x08};
+
+int rsa_parse_pss_params(CBS *cbs, rsa_pss_params_t *out,
+ int allow_explicit_trailer) {
+ // See RFC 4055, section 3.1.
+ //
+ // hashAlgorithm, maskGenAlgorithm, and saltLength all have DEFAULTs
+ // corresponding to SHA-1. We do not support SHA-1 with PSS, so we do not
+ // bother recognizing the omitted versions.
+ CBS params, hash_wrapper, mask_wrapper, mask_alg, mask_oid, salt_wrapper;
+ uint64_t salt_len;
+ if (!CBS_get_asn1(cbs, ¶ms, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1(¶ms, &hash_wrapper,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+ // |hash_wrapper| will be parsed below.
+ !CBS_get_asn1(¶ms, &mask_wrapper,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
+ !CBS_get_asn1(&mask_wrapper, &mask_alg, CBS_ASN1_SEQUENCE) ||
+ !CBS_get_asn1(&mask_alg, &mask_oid, CBS_ASN1_OBJECT) ||
+ // We only support MGF-1.
+ bssl::Span<const uint8_t>(mask_oid) != kMGF1OID ||
+ // The remainder of |mask_alg| will be parsed below.
+ CBS_len(&mask_wrapper) != 0 ||
+ !CBS_get_asn1(¶ms, &salt_wrapper,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2) ||
+ !CBS_get_asn1_uint64(&salt_wrapper, &salt_len) ||
+ CBS_len(&salt_wrapper) != 0) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+
+ // The trailer field must be 1 (0xbc). This value is DEFAULT, so the structure
+ // is required to omit it in DER.
+ if (CBS_len(¶ms) != 0 && allow_explicit_trailer) {
+ CBS trailer_wrapper;
+ uint64_t trailer;
+ if (!CBS_get_asn1(¶ms, &trailer_wrapper,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3) ||
+ !CBS_get_asn1_uint64(&trailer_wrapper, &trailer) || //
+ trailer != 1) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+ }
+ if (CBS_len(¶ms) != 0) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+
+ int hash_nid = EVP_parse_digest_algorithm_nid(&hash_wrapper);
+ if (hash_nid == NID_undef || CBS_len(&hash_wrapper) != 0) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+
+ // We only support combinations where the MGF-1 hash matches the overall hash.
+ int mgf1_hash_nid = EVP_parse_digest_algorithm_nid(&mask_alg);
+ if (mgf1_hash_nid != hash_nid || CBS_len(&mask_alg) != 0) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+
+ // We only support salt lengths that match the hash length.
+ rsa_pss_params_t ret;
+ uint64_t hash_len;
+ switch (hash_nid) {
+ case NID_sha256:
+ ret = rsa_pss_sha256;
+ hash_len = 32;
+ break;
+ case NID_sha384:
+ ret = rsa_pss_sha384;
+ hash_len = 48;
+ break;
+ case NID_sha512:
+ ret = rsa_pss_sha512;
+ hash_len = 64;
+ break;
+ default:
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+ if (salt_len != hash_len) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_ENCODING);
+ return 0;
+ }
+
+ *out = ret;
+ return 1;
+}
diff --git a/crypto/x509/rsa_pss.cc b/crypto/x509/rsa_pss.cc
index 689f64d..3ad9172 100644
--- a/crypto/x509/rsa_pss.cc
+++ b/crypto/x509/rsa_pss.cc
@@ -24,6 +24,7 @@
#include <openssl/evp.h>
#include <openssl/obj.h>
+#include "../rsa/internal.h"
#include "internal.h"
@@ -46,111 +47,18 @@
IMPLEMENT_ASN1_FUNCTIONS_const(RSA_PSS_PARAMS)
-// Given an MGF1 Algorithm ID decode to an Algorithm Identifier
-static X509_ALGOR *rsa_mgf1_decode(const X509_ALGOR *alg) {
- if (OBJ_obj2nid(alg->algorithm) != NID_mgf1 || alg->parameter == NULL ||
- alg->parameter->type != V_ASN1_SEQUENCE) {
- return NULL;
- }
-
- const uint8_t *p = alg->parameter->value.sequence->data;
- int plen = alg->parameter->value.sequence->length;
- return d2i_X509_ALGOR(NULL, &p, plen);
-}
-
-static RSA_PSS_PARAMS *rsa_pss_decode(const X509_ALGOR *alg) {
- if (alg->parameter == NULL || alg->parameter->type != V_ASN1_SEQUENCE) {
- return NULL;
- }
-
- const uint8_t *p = alg->parameter->value.sequence->data;
- int plen = alg->parameter->value.sequence->length;
- return d2i_RSA_PSS_PARAMS(NULL, &p, plen);
-}
-
-static int is_allowed_pss_md(const EVP_MD *md) {
- int md_type = EVP_MD_type(md);
- return md_type == NID_sha256 || md_type == NID_sha384 ||
- md_type == NID_sha512;
-}
-
-// rsa_md_to_algor sets |*palg| to an |X509_ALGOR| describing the digest |md|,
-// which must be an allowed PSS digest.
-static int rsa_md_to_algor(X509_ALGOR **palg, const EVP_MD *md) {
- // SHA-1 should be omitted (DEFAULT), but we do not allow SHA-1.
- assert(is_allowed_pss_md(md));
- *palg = X509_ALGOR_new();
- if (*palg == NULL) {
+static int rsa_pss_decode(const X509_ALGOR *alg, rsa_pss_params_t *out) {
+ if (alg->parameter == nullptr || alg->parameter->type != V_ASN1_SEQUENCE) {
return 0;
}
- if (!X509_ALGOR_set_md(*palg, md)) {
- X509_ALGOR_free(*palg);
- *palg = NULL;
- return 0;
- }
- return 1;
-}
-// rsa_md_to_mgf1 sets |*palg| to an |X509_ALGOR| describing MGF-1 with the
-// digest |mgf1md|, which must be an allowed PSS digest.
-static int rsa_md_to_mgf1(X509_ALGOR **palg, const EVP_MD *mgf1md) {
- // SHA-1 should be omitted (DEFAULT), but we do not allow SHA-1.
- assert(is_allowed_pss_md(mgf1md));
- X509_ALGOR *algtmp = NULL;
- ASN1_STRING *stmp = NULL;
- // need to embed algorithm ID inside another
- if (!rsa_md_to_algor(&algtmp, mgf1md) ||
- !ASN1_item_pack(algtmp, ASN1_ITEM_rptr(X509_ALGOR), &stmp)) {
- goto err;
- }
- *palg = X509_ALGOR_new();
- if (!*palg) {
- goto err;
- }
- if (!X509_ALGOR_set0(*palg, OBJ_nid2obj(NID_mgf1), V_ASN1_SEQUENCE, stmp)) {
- goto err;
- }
- stmp = NULL;
-
-err:
- ASN1_STRING_free(stmp);
- X509_ALGOR_free(algtmp);
- if (*palg) {
- return 1;
- }
-
- return 0;
-}
-
-static const EVP_MD *rsa_algor_to_md(const X509_ALGOR *alg) {
- if (!alg) {
- // If omitted, PSS defaults to SHA-1, which we do not allow.
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- return NULL;
- }
- const EVP_MD *md = EVP_get_digestbyobj(alg->algorithm);
- if (md == NULL || !is_allowed_pss_md(md)) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- return NULL;
- }
- return md;
-}
-
-static const EVP_MD *rsa_mgf1_to_md(const X509_ALGOR *alg) {
- if (!alg) {
- // If omitted, PSS defaults to MGF-1 with SHA-1, which we do not allow.
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- return NULL;
- }
- // Check mask and lookup mask hash algorithm.
- X509_ALGOR *maskHash = rsa_mgf1_decode(alg);
- if (maskHash == NULL) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- return NULL;
- }
- const EVP_MD *ret = rsa_algor_to_md(maskHash);
- X509_ALGOR_free(maskHash);
- return ret;
+ // Although a syntax error in DER, we tolerate an explicitly-encoded trailer.
+ // See the certificates in cl/362617931.
+ CBS cbs;
+ CBS_init(&cbs, alg->parameter->value.sequence->data,
+ alg->parameter->value.sequence->length);
+ return rsa_parse_pss_params(&cbs, out, /*allow_explicit_trailer=*/true) &&
+ CBS_len(&cbs) == 0;
}
int x509_rsa_ctx_to_pss(EVP_MD_CTX *ctx, X509_ALGOR *algor) {
@@ -162,203 +70,111 @@
return 0;
}
- if (sigmd != mgf1md || !is_allowed_pss_md(sigmd)) {
+ if (sigmd != mgf1md) {
OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
return 0;
}
int md_len = (int)EVP_MD_size(sigmd);
- if (saltlen == RSA_PSS_SALTLEN_DIGEST) {
- saltlen = md_len;
- } else if (saltlen != md_len) {
+ if (saltlen != RSA_PSS_SALTLEN_DIGEST && saltlen != md_len) {
OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
return 0;
}
- int ret = 0;
- ASN1_STRING *os = NULL;
- RSA_PSS_PARAMS *pss = RSA_PSS_PARAMS_new();
- if (!pss) {
- goto err;
+ rsa_pss_params_t params;
+ switch (EVP_MD_type(sigmd)) {
+ case NID_sha256:
+ params = rsa_pss_sha256;
+ break;
+ case NID_sha384:
+ params = rsa_pss_sha384;
+ break;
+ case NID_sha512:
+ params = rsa_pss_sha512;
+ break;
+ default:
+ OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
+ return 0;
}
- // The DEFAULT value is 20, but this does not match any supported digest.
- assert(saltlen != 20);
- pss->saltLength = ASN1_INTEGER_new();
- if (!pss->saltLength || //
- !ASN1_INTEGER_set_int64(pss->saltLength, saltlen)) {
- goto err;
+ // Encode |params| to an |ASN1_STRING|.
+ uint8_t buf[128]; // The largest param fits comfortably in 128 bytes.
+ CBB cbb;
+ CBB_init_fixed(&cbb, buf, sizeof(buf));
+ if (!rsa_marshal_pss_params(&cbb, params)) {
+ return 0;
}
-
- if (!rsa_md_to_algor(&pss->hashAlgorithm, sigmd) ||
- !rsa_md_to_mgf1(&pss->maskGenAlgorithm, mgf1md)) {
- goto err;
- }
-
- // Finally create string with pss parameter encoding.
- if (!ASN1_item_pack(pss, ASN1_ITEM_rptr(RSA_PSS_PARAMS), &os)) {
- goto err;
+ bssl::UniquePtr<ASN1_STRING> params_str(
+ ASN1_STRING_type_new(V_ASN1_SEQUENCE));
+ if (params_str == nullptr ||
+ !ASN1_STRING_set(params_str.get(), CBB_data(&cbb), CBB_len(&cbb))) {
+ return 0;
}
if (!X509_ALGOR_set0(algor, OBJ_nid2obj(NID_rsassaPss), V_ASN1_SEQUENCE,
- os)) {
- goto err;
+ params_str.get())) {
+ return 0;
}
- os = NULL;
- ret = 1;
-
-err:
- RSA_PSS_PARAMS_free(pss);
- ASN1_STRING_free(os);
- return ret;
+ params_str.release(); // |X509_ALGOR_set0| took ownership.
+ return 1;
}
int x509_rsa_pss_to_ctx(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
EVP_PKEY *pkey) {
assert(OBJ_obj2nid(sigalg->algorithm) == NID_rsassaPss);
-
- // Decode PSS parameters
- int ret = 0;
- RSA_PSS_PARAMS *pss = rsa_pss_decode(sigalg);
-
- {
- if (pss == NULL) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- goto err;
- }
-
- const EVP_MD *mgf1md = rsa_mgf1_to_md(pss->maskGenAlgorithm);
- const EVP_MD *md = rsa_algor_to_md(pss->hashAlgorithm);
- if (mgf1md == NULL || md == NULL) {
- goto err;
- }
-
- // We require the MGF-1 and signing hashes to match.
- if (mgf1md != md) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- goto err;
- }
-
- // We require the salt length be the hash length. The DEFAULT value is 20,
- // but this does not match any supported salt length.
- uint64_t salt_len = 0;
- if (pss->saltLength == NULL ||
- !ASN1_INTEGER_get_uint64(&salt_len, pss->saltLength) ||
- salt_len != EVP_MD_size(md)) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- goto err;
- }
- assert(salt_len <= INT_MAX);
-
- // The trailer field must be 1 (0xbc). This value is DEFAULT, so the
- // structure is required to omit it in DER. Although a syntax error, we also
- // tolerate an explicitly-encoded value. See the certificates in
- // cl/362617931.
- if (pss->trailerField != NULL && ASN1_INTEGER_get(pss->trailerField) != 1) {
- OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
- goto err;
- }
-
- EVP_PKEY_CTX *pctx;
- if (!EVP_DigestVerifyInit(ctx, &pctx, md, NULL, pkey) ||
- !EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
- !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, (int)salt_len) ||
- !EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf1md)) {
- goto err;
- }
-
- ret = 1;
+ rsa_pss_params_t params;
+ if (!rsa_pss_decode(sigalg, ¶ms)) {
+ OPENSSL_PUT_ERROR(X509, X509_R_INVALID_PSS_PARAMETERS);
+ return 0;
}
-err:
- RSA_PSS_PARAMS_free(pss);
- return ret;
+ const EVP_MD *md = rsa_pss_params_get_md(params);
+ EVP_PKEY_CTX *pctx;
+ if (!EVP_DigestVerifyInit(ctx, &pctx, md, nullptr, pkey) ||
+ !EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
+ !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, RSA_PSS_SALTLEN_DIGEST) ||
+ !EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, md)) {
+ return 0;
+ }
+
+ return 1;
}
int x509_print_rsa_pss_params(BIO *bp, const X509_ALGOR *sigalg, int indent,
ASN1_PCTX *pctx) {
assert(OBJ_obj2nid(sigalg->algorithm) == NID_rsassaPss);
+ rsa_pss_params_t params;
+ if (!rsa_pss_decode(sigalg, ¶ms)) {
+ return BIO_puts(bp, " (INVALID PSS PARAMETERS)\n") <= 0;
+ }
- int rv = 0;
- X509_ALGOR *maskHash = NULL;
- RSA_PSS_PARAMS *pss = rsa_pss_decode(sigalg);
- if (!pss) {
- if (BIO_puts(bp, " (INVALID PSS PARAMETERS)\n") <= 0) {
- goto err;
- }
- rv = 1;
- goto err;
+ const char *hash_str = nullptr;
+ uint32_t salt_len = 0;
+ switch (params) {
+ case rsa_pss_sha256:
+ hash_str = "sha256";
+ salt_len = 32;
+ break;
+ case rsa_pss_sha384:
+ hash_str = "sha384";
+ salt_len = 48;
+ break;
+ case rsa_pss_sha512:
+ hash_str = "sha512";
+ salt_len = 64;
+ break;
}
if (BIO_puts(bp, "\n") <= 0 || //
!BIO_indent(bp, indent, 128) || //
- BIO_puts(bp, "Hash Algorithm: ") <= 0) {
- goto err;
- }
-
- if (pss->hashAlgorithm) {
- if (i2a_ASN1_OBJECT(bp, pss->hashAlgorithm->algorithm) <= 0) {
- goto err;
- }
- } else if (BIO_puts(bp, "sha1 (default)") <= 0) {
- goto err;
- }
-
- if (BIO_puts(bp, "\n") <= 0 || //
+ BIO_printf(bp, "Hash Algorithm: %s\n", hash_str) <= 0 ||
!BIO_indent(bp, indent, 128) || //
- BIO_puts(bp, "Mask Algorithm: ") <= 0) {
- goto err;
+ BIO_printf(bp, "Mask Algorithm: mgf1 with %s\n", hash_str) <= 0 ||
+ !BIO_indent(bp, indent, 128) || //
+ BIO_printf(bp, "Salt Length: 0x%x\n", salt_len) <= 0 ||
+ !BIO_indent(bp, indent, 128) || //
+ BIO_puts(bp, "Trailer Field: 0xBC (default)\n") <= 0) {
+ return 0;
}
- if (pss->maskGenAlgorithm) {
- maskHash = rsa_mgf1_decode(pss->maskGenAlgorithm);
- if (maskHash == NULL) {
- if (BIO_puts(bp, "INVALID") <= 0) {
- goto err;
- }
- } else {
- if (i2a_ASN1_OBJECT(bp, pss->maskGenAlgorithm->algorithm) <= 0 ||
- BIO_puts(bp, " with ") <= 0 ||
- i2a_ASN1_OBJECT(bp, maskHash->algorithm) <= 0) {
- goto err;
- }
- }
- } else if (BIO_puts(bp, "mgf1 with sha1 (default)") <= 0) {
- goto err;
- }
- BIO_puts(bp, "\n");
-
- if (!BIO_indent(bp, indent, 128) || //
- BIO_puts(bp, "Salt Length: 0x") <= 0) {
- goto err;
- }
-
- if (pss->saltLength) {
- if (i2a_ASN1_INTEGER(bp, pss->saltLength) <= 0) {
- goto err;
- }
- } else if (BIO_puts(bp, "14 (default)") <= 0) {
- goto err;
- }
- BIO_puts(bp, "\n");
-
- if (!BIO_indent(bp, indent, 128) || //
- BIO_puts(bp, "Trailer Field: 0x") <= 0) {
- goto err;
- }
-
- if (pss->trailerField) {
- if (i2a_ASN1_INTEGER(bp, pss->trailerField) <= 0) {
- goto err;
- }
- } else if (BIO_puts(bp, "BC (default)") <= 0) {
- goto err;
- }
- BIO_puts(bp, "\n");
-
- rv = 1;
-
-err:
- RSA_PSS_PARAMS_free(pss);
- X509_ALGOR_free(maskHash);
- return rv;
+ return 1;
}
diff --git a/include/openssl/digest.h b/include/openssl/digest.h
index 710c6e6..b604aba 100644
--- a/include/openssl/digest.h
+++ b/include/openssl/digest.h
@@ -213,6 +213,11 @@
// returns the digest function or NULL on error.
OPENSSL_EXPORT const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs);
+// EVP_parse_digest_algorithm_nid behaves like |EVP_parse_digest_algorithm|
+// except it returns |NID_undef| on error and some other value on success. This
+// may be used to avoid depending on every digest algorithm in the library.
+OPENSSL_EXPORT int EVP_parse_digest_algorithm_nid(CBS *cbs);
+
// EVP_marshal_digest_algorithm marshals |md| as an AlgorithmIdentifier
// structure and appends the result to |cbb|. It returns one on success and zero
// on error. It sets the parameters field to NULL. Use