Skip to content

104080 custom rsapss salt length#119255

Open
henning-krause wants to merge 52 commits intodotnet:mainfrom
henning-krause:104080-custom-rsapss-salt-length
Open

104080 custom rsapss salt length#119255
henning-krause wants to merge 52 commits intodotnet:mainfrom
henning-krause:104080-custom-rsapss-salt-length

Conversation

@henning-krause
Copy link
Copy Markdown

@henning-krause henning-krause commented Sep 2, 2025

Added support for custom PSS salt length for RSA based signature operations. Resolves #104080.

With this PR, I have extended the RsaSignaturePadding class with a custom salt length as discussed in #104080. I've also updated the CmsSigner and the CertificateRequest class to support this.

CoseSigner did not need functionality updates, because the spec does not support custom salt length. I have added checks to prevent this to be configured on the CoseSigner.

Tests where either added or updated to test the new functionality.

All RSA implementations which support this (MAC doesn't seem to support this) were updated.

Copilot AI review requested due to automatic review settings September 2, 2025 05:45
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Sep 2, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request adds support for custom PSS salt length for RSA-based signature operations. The implementation extends the RSASignaturePadding class with a new CreatePss(int saltLength) method and updates related cryptographic components to utilize custom salt lengths.

Key changes include:

  • Extended RSASignaturePadding to support custom PSS salt lengths with new constants and factory method
  • Updated all RSA implementation backends (OpenSSL, CNG, BCrypt, etc.) to handle custom salt lengths
  • Added comprehensive test coverage for various salt length scenarios
  • Updated high-level APIs like CmsSigner and CertificateRequest to support the new functionality

Reviewed Changes

Copilot reviewed 40 out of 41 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.h Added pssSaltLength parameter to RSA sign/verify functions
src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c Implemented custom salt length support in OpenSSL backend
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/RSASignaturePadding.cs Added CreatePss method, PssSaltLength property, and related constants
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/RSAPssX509SignatureGenerator.cs Updated to use custom salt lengths from padding configuration
src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.cs Modified PSS encoding/verification to accept custom salt lengths
src/libraries/Common/src/System/Security/Cryptography/RsaPaddingProcessor.DigestInfo.cs Added salt length calculation logic and moved digest info constants
src/libraries/System.Security.Cryptography/tests/X509Certificates/CertificateCreation/RSAPssX509SignatureGeneratorTests.cs Added comprehensive tests for custom PSS salt lengths
src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs Updated validation to accept any PSS mode padding
src/libraries/System.Security.Cryptography.Cose/src/System/Security/Cryptography/Cose/CoseSigner.cs Added validation to prevent custom salt lengths in COSE

Comment thread src/libraries/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs Outdated
Comment thread src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs Outdated
henning-krause and others added 3 commits September 2, 2025 07:51
…tes/CertificateCreation/CertificateRequestChainTests.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
….Cryptography.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ngProcessor.DigestInfo.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
if (emLen < hLen + sLen + 2)
if (emLen < hLen + saltLength + 2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything else in this function is named what it is in the spec. I'd prefer if you reintroduced the sLen variable a few lines up, but instead of int sLen = hLen; it would be int sLen = saltLength; (so the parameter has a good name for callers, but the function uses the name from the spec)

And, of course, after introducing sLen, undo all of the sLen => saltLength changes in this function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you undid the sLen to saltLength rename in EncodePss, but not in VerifyPss.

0x11, 0xf2, 0x10, 0xbd, 0x19, 0x47, 0x75, 0x3c, 0x30, 0xc4, 0x0f, 0xd0, 0x7b, 0xc2, 0x6b, 0x4b
};

RSASignaturePadding padding = RSASignaturePadding.CreatePss(validateWithCorrectSaltLength ? RSASignaturePadding.PssSaltLengthMax : 2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since max ends up being a specific number when paired with the key, there should be a test that signs with max and verifies with the number, and vice versa.

Probably not this one, since it's not visibly calling sign.

private byte[] GetSignaturePaddingForCustomPssSaltLength(X509Certificate2 certificate, HashAlgorithmName hashAlgorithmName)
{
string digestOid = PkcsHelpers.GetOidFromHashAlgorithm(hashAlgorithmName);
using RSA? publicKey = certificate.GetRSAPublicKey();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In cryptography code, other than test code, we (almost?) always use braced using-Dispose statements so it's much clearer when objects are freed.

But, rather than re-opening a key handle here, could we maybe just save the key size in the call to SignCore?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bartonjs So SignCore would return the signatureParameters via an out variable?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems reasonable, yeah.

"07849DC26FCBB2F3BD5F57BDF214BAE374575F1BD4E6816482324799417CB379",
messageDigestAttr.MessageDigest.ByteArrayToHex());


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it turned a blank line into a "non-blank blank line" (only has spaces). That should be trimmed down to a non-diff.

{
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(TestData.RsaBigExponentParams);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vcsjones when I saw this line I immediately thought "but didn't we (you) move us off of this key?", but it looks like no...

Am I entirely hallucinating, or what?

Comment on lines +82 to +83
pssStructure.ReadEncodedValue(); // Ignore the hash algorithm OID
pssStructure.ReadEncodedValue(); // Ignore the mask generation function OID
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pre-existing test that verifies these values doesn't take a custom salt size. We should verify them here to show that we didn't break that codepath with custom salt sizes.

While we're at it, we should also add hash algorithm variance.

(We can verify the algorithm/MGF OIDs in a different test than the custom salt size, but we do want to verify both pieces of data in some test)


// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
if (emLen < hLen + sLen + 2)
if (emLen < hLen + saltLength + 2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you undid the sLen to saltLength rename in EncodePss, but not in VerifyPss.

pssStructure.ReadEncodedValue(); // Ignore the hash algorithm OID
pssStructure.ReadEncodedValue(); // Ignore the mask generation function OID
ReadOnlyMemory<byte> hashAlgorithm = pssStructure.ReadEncodedValue(); // Ignore the hash algorithm OID
ReadOnlyMemory<byte> mgf = pssStructure.ReadEncodedValue(); // Ignore the mask generation function OID
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous feedback was that the hashAlgorithm and mgf values needed to be verified. You have now captured them into locals, but I don't see you verifying that they're the expected values.

Again, the purpose is to make sure that we're writing down a confirming/correct block when a custom PSS size is specified, and I don't see any other test doing that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was interrupted last time I worked on this, so I committed early. I'll fix this next time together with the remaining open issues.

@jeffhandley jeffhandley added the needs-author-action An issue or pull request that requires more info or actions from the author. label Mar 8, 2026
Copilot AI review requested due to automatic review settings March 24, 2026 06:19
@dotnet-policy-service dotnet-policy-service Bot removed needs-author-action An issue or pull request that requires more info or actions from the author. no-recent-activity labels Mar 24, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 39 changed files in this pull request and generated 7 comments.

Comment on lines +4 to +5
using System.Formats.Asn1;
using System.Security.Cryptography.Asn1;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These using directives appear to be unused in this file. Consider removing them to avoid IDE0005 / analyzer warnings (which may be treated as errors in some build configurations).

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +25
/// A constant value indicating that the PSS salt length should be the maximum allowable
/// </summary>
/// <remarks>A constant is used to define the upper limit for the salt length in PSS-based
/// cryptographic operations. The maximum length is determined by the hash algorithm's output size.</remarks>
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remarks for PssSaltLengthMax say the maximum salt length is determined by the hash algorithm output size, but it also depends on the RSA key size (max is emLen - hLen - 2). Consider updating the docs to reflect the key-size dependency.

Suggested change
/// A constant value indicating that the PSS salt length should be the maximum allowable
/// </summary>
/// <remarks>A constant is used to define the upper limit for the salt length in PSS-based
/// cryptographic operations. The maximum length is determined by the hash algorithm's output size.</remarks>
/// A constant value indicating that the PSS salt length should be the maximum allowable for the given RSA key and hash algorithm.
/// </summary>
/// <remarks>
/// When this value is used, the effective salt length is chosen by the implementation so that it does not exceed
/// the maximum permitted by RSASSA-PSS, which depends on both the RSA key size and the hash algorithm's output
/// size (at most <c>emLen - hLen - 2</c> bytes, where <c>emLen</c> is the encoded message length in bytes and
/// <c>hLen</c> is the hash output length in bytes).
/// </remarks>

Copilot uses AI. Check for mistakes.
/// Specifies the salt length to use for PSS padding. This property is only relevant when the <see cref="Mode"/> is <see cref="RSASignaturePaddingMode.Pss"/>.
/// </summary>
/// <remarks>
/// This value must either be a positive number or one of the special constants <see cref="PssSaltLengthIsHashLength"/> or <see cref="PssSaltLengthMax"/>.
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PssSaltLength remarks say the value must be a "positive" number, but CreatePss allows 0 (and tests cover it). Consider updating the wording to "non-negative" to match the API behavior.

Suggested change
/// This value must either be a positive number or one of the special constants <see cref="PssSaltLengthIsHashLength"/> or <see cref="PssSaltLengthMax"/>.
/// This value must either be a non-negative number or one of the special constants <see cref="PssSaltLengthIsHashLength"/> or <see cref="PssSaltLengthMax"/>.

Copilot uses AI. Check for mistakes.
Comment on lines +1777 to 1778
if (certKind == CertKind.RsaPkcs1 || certKind == CertKind.RsaPss ||certKind == CertKind.RsaPssWithCustomSaltLength || certKind == CertKind.RsaPssWithMaxSaltLength)
{
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor formatting: missing space after "||" makes the condition harder to read and is inconsistent with typical formatting in this file.

Copilot uses AI. Check for mistakes.
{
public static partial class SignedCmsTests
{
private static bool AreCustomPssSaltLengthsSupported => PlatformDetection.IsWindows || PlatformDetection.IsLinux;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AreCustomPssSaltLengthsSupported is hard-coded to Windows/Linux, but custom PSS salt lengths are supported on other OpenSSL-based Unix platforms too. Consider reusing PlatformSupport.AreCustomSaltLengthsSupportedWithPss here to avoid unnecessarily skipping coverage on supported platforms (e.g., FreeBSD).

Suggested change
private static bool AreCustomPssSaltLengthsSupported => PlatformDetection.IsWindows || PlatformDetection.IsLinux;
private static bool AreCustomPssSaltLengthsSupported => PlatformSupport.AreCustomSaltLengthsSupportedWithPss;

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +141
// PSS signature with parameters are only supported on .NET 11 or later
#if NET11_0_OR_GREATER
if (PlatformSupport.AreCustomSaltLengthsSupportedWithPss)
{
signer.CheckSignature(true);
}
else
{
Assert.Throws<CryptographicException>(() => signer.CheckSignature(true));
}
#else
Assert.Throws<CryptographicException>(() => signer.CheckSignature(true));
#endif
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments/guards are inconsistent: this block says PSS parameters are only supported on .NET 11+, but the later cms.CheckSignature block mentions .NET 10+ without an equivalent compile-time guard. Consider aligning the comments (and/or the #if) so the minimum supported version is unambiguous.

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +95
internal static int CalculatePssSaltLength(int pssSaltLength, int rsaKeySizeInBits, HashAlgorithmName hashAlgorithm)
{
int emLen = BytesRequiredForBitCount(rsaKeySizeInBits);
int hLen = HashLength(hashAlgorithm);
return pssSaltLength switch
{
PssSaltLengthMax => Math.Max(0, emLen - hLen - 2),
PssSaltLengthIsHashLength => hLen,
_ => pssSaltLength
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CalculatePssSaltLength computes emLen from rsaKeySizeInBits, but PSS encoding/verification in this codebase uses emBits = keySize - 1. For RSA keys whose bit-length is not a multiple of 8, this will overestimate the maximum salt length by 1 byte and can cause CreatePss(PssSaltLengthMax) to fail with key-too-small for otherwise-valid keys. Consider basing emLen on (rsaKeySizeInBits - 1) to align with EncodePss/VerifyPss.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Security community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: RSA PSS salt length

5 participants