Show / Hide Table of Contents

TryParsePkcs1Pss Method

TryParsePkcs1Pss(ReadOnlySpan<byte>, ReadOnlySpan<byte>, int, out byte[], out bool)

Try to parse the formattedSignature as a PKCS #1 v2 PSS block for verifying (see RFC 8017).

C#
public static bool TryParsePkcs1Pss(ReadOnlySpan<byte> formattedSignature, ReadOnlySpan<byte> digest, int digestAlgorithm, out byte[] mPrimeAndH, out bool isVerified)

Parameters

Type Name Description
ReadOnlySpan<byte> formattedSignature

The data to parse.

ReadOnlySpan<byte> digest

The computed digest, the digest of the message, the data to verify.

int digestAlgorithm

The digest algorithm used to compute the digest, it must be one of the supported algorithms: RsaFormat.Sha1, and so on.

byte[] mPrimeAndH

An output argument, a new byte array containing M-prime concatenated with the H value extracted from the formattedSignature.

bool isVerified

An output argument, a boolean reporting whether the signature verified or not.

Returns

bool

True if the method is able to parse, false otherwise.

Remarks

Note that this method will not return the digest of the data. In fact, the caller must supply the digest. The reason is that the digest of the data (the foundation of the signature) is not part of the formatted data. Rather, the formatted data contains a value derived from the digest.

With earlier RSA padding schemes (such as PKCS #1 v1.5), the signer digested the message (call this the signer's digest), and formatted that digest into a block. That block was encrypted using the private key.

Verification required the verifier to digest the message (call this the verifier's digest), use the public key to decrypt the signature, parse the decrypted data to extract the digest (the signer's digest), then compare (memcmp) the two digests. If the signer's and verifier's digests match, the signature verified.

Signer:
  message --> Digest(message) --> signer's digest
  signer's digest --> format --> RSA(privateKey, formattedData) --> signature
  send message + signature

Verifier: message --> Digest(message) --> verifier's digest signature --> RSA(publicKey, signature) --> formattedDigest formattedDigest --> parse --> signer's digest compare verifier's digest with signer's digest

In contrast, with PSS the signer digests the message, derives a Hash value (known as H) from the message digest, and builds a formatted block using H (and other information used in the derivation process). That block is encrypted using the private key, producing the signature.

To verify a signature, the verifier digests the message to produce the digest. Next, decrypt the signature to obtain the formatted data. Parse that data to obtain H (the signer's H, the value derived from the signer's digest) along with other information needed to perform the derivation operation. Next, the verifier performs the same derivation operation using the verifier's digest and information from the formatted signature (computing the verifier's H), and compares that result with the value from the parsed block (the signer's H).

Signer:
  message --> Digest(message) --> signer's digest
  signer's digest --> derivation(salt, digest) --> signer's H
  signer's H --> format, includes salt and is masked --> formattedData
  formattedData --> RSA(privateKey, formattedData) --> signature
  send message + signature

Verifier: message --> Digest(message) --> verifier's digest signature --> RSA(publicKey, signature) --> formattedData formattedData --> parse, unmask, extract elements --> salt and signer's H verifier's digest --> derivation(salt, digest) --> verifier's H compare verifier's H with signer's H

This method will perform the derivation operation, which is why the caller must supply the digest.

This method will parse the formatted block, obtaining the information necessary to perform the derivation operation. It will use the digest value provided to derive an H value. It will then compare the H value it derived with the H value from the formatted block. If they are the same, it will set the isVerified argument to true. If not, the method will set that value to false.

The method will verify that the parsed data matches the PSS specifications. If not, then it might not derive the H value and simply set isVerified to false. For example, the PSS standard requires the most significant bit of the formatted data to be 0, and the "unmasked" PS to be all 00 bytes.

This method will assume the salt length is the same as the digest length. Although the salt length does not have to be the same as the digest length, the standard recommends doing so. If the signature you are verifying does not use the digest length as the salt length, you cannot use this method. Note that the C# PSS implementation (see the System.Security.Cryptography.RSA class) uses the digest length as the salt length exclusively, the same as this method.

This method will use the message digest implementations from CryptographyProviders.

The method will also return the "M-prime" value followed by the H value.

M-prime || H
  M-prime is (2 * digest length) + 8 bytes long,
  H is digest length bytes long
M-prime is the value used to derive the H value. It consists of
00 00 00 00 00 00 00 00 || digest || salt
eight 00 bytes, then the digest, then digest length bytes of salt
Hence, the mPrimeAndH value returned is
00 00 00 00 00 00 00 00 || digest || salt || H
          8 bytes           dLen     dLen    dLen

For example, if the digest is SHA-256, then the digest length is 32.

00 00 00 00 00 00 00 00 || digest || salt || H 8 bytes 32 32 32

If the caller wishes to compute the H value and make the comparison, the data is made available. The H value is simply the message digest of M-prime. For example, if the digest algorithm is SHA-256, then compute
SHA-256(M-Prime)
  This will be
SHA-256(mPrimeAndH, 0, (2 * digestLength) + 8)
  or
SHA-256(mPrimeAndH, 0, 72) // offset 0, length 72

Compare the result with the last digest length bytes of mPrimeAndH.

This method only supports signatures 128 or 256 bytes (1024 or 2048 bits) long.

It is possible that the method was not able to parse the block enough to build the mPrimeAndH output. In that case, the return from the method will be false, isVerified will be false, and mPrimeAndH will be an empty byte array. It is also possible that the method was able to build mPrimeAndH and yet some parsing operation failed so the return is still false. That is, looking at only mPrimeAndH will not be sufficient to know what happened.

Note that the return value from this method only indicates whether it was able to parse or not. The return value does not have anything to do with whether the signature verifies or not. That is, it is possible the return from this method is true and the isVerified output argument is set to false. In this case, the method was able to parse the signature, it's just that the signature did not verify. In other words, the method successfully completed its task, there was no error preventing it from parsing and making the H value comparison. Its task is to determine if the signature verifies or not. If it determines that the signature did not verify, the method successfully completed its task, just as determining the signature did verify is a successful completion of the task.

In this article
Back to top Generated by DocFX