Class RsaFormat
- Namespace
- Yubico.YubiKey.Cryptography
- Assembly
- Yubico.YubiKey.dll
This class contains methods that can build and read data formatted for RSA sign/verify and encryption/decryption operations.
public static class RsaFormat
- Inheritance
-
objectRsaFormat
Remarks
Currently this class will format data into only PKCS #1 v1.5 and PKCS #1 v.2 PSS and OAEP constructions. Furthermore, this class will only build specific subsets of PSS and OAEP.
Note that there are attacks on RSA decryption unpadding operations. To learn more about these attacks, whether the YubiKey is vulnerable, and mitigations, see the User's Manual entry on the topic.
Fields
KeySizeBits1024
Use this value to indicate the key size, in bits, is 1024. The
KeySizeBits
values listed in this class are the sizes
supported and provided as a convenience to the user to verify the
supported sizes.
public const int KeySizeBits1024 = 1024
Field Value
- int
KeySizeBits2048
Use this value to indicate the key size, in bits, is 2048. The
KeySizeBits
values listed in this class are the sizes
supported and provided as a convenience to the user to verify the
supported sizes.
public const int KeySizeBits2048 = 2048
Field Value
- int
KeySizeBits3072
Use this value to indicate the key size, in bits, is 3072. The
KeySizeBits
values listed in this class are the sizes
supported and provided as a convenience to the user to verify the
supported sizes.
public const int KeySizeBits3072 = 3072
Field Value
- int
KeySizeBits4096
Use this value to indicate the key size, in bits, is 4096. The
KeySizeBits
values listed in this class are the sizes
supported and provided as a convenience to the user to verify the
supported sizes.
public const int KeySizeBits4096 = 4096
Field Value
- int
Sha1
Use this value to indicate the digest algorithm is SHA-1.
public const int Sha1 = 1
Field Value
- int
Sha256
Use this value to indicate the digest algorithm is SHA-256.
public const int Sha256 = 3
Field Value
- int
Sha384
Use this value to indicate the digest algorithm is SHA-384.
public const int Sha384 = 4
Field Value
- int
Sha512
Use this value to indicate the digest algorithm is SHA-512.
public const int Sha512 = 5
Field Value
- int
Methods
FormatPkcs1Encrypt(ReadOnlySpan<byte>, int)
Build the input data into a PKCS #1 v1.5 formatted block for encryption (see RFC 8017).
public static byte[] FormatPkcs1Encrypt(ReadOnlySpan<byte> inputData, int keySizeBits)
Parameters
inputData
ReadOnlySpan<byte>The data to format.
keySizeBits
intThe size of the key used, in bits. This value must be one of the
RsaFormat.KeySizeBits-x-
values.
Returns
- byte[]
A new byte array containing the formatted data.
Remarks
This method will build a new buffer that is keySizeBits
long
and contains the following data.
00 || 02 || PS || 00 || input data
where PS consists of non-zero random bytes
that is:
00 || 02 || non-zero random bytes || 00 || input data
This method supports only keySizeBits
values that are defined
in this class as KeySizeBits-x-
, such as
RsaFormat.KeySizeBits1024
(x=1024). You can use one of these
values or simply the actual key size in bits. For example, if the key
size in bits is 1024, then either RsaFormat.KeySizeBits1024
or
1024
are valid input to this method.
The standard specifies that PS must be at least 8 bytes long. Hence, for a 1024-bit key, the maximum input data length is 117 bytes. For a 2048-bit key, the maximum input data length is 245 bytes.
1024-bit key:
128-byte buffer: 00 01 || x1 x2 x3 x4 x5 x6 x7 x8 || 00 || 117 bytes
128 = 2 + 8 + 1 + 117
2048-bit key:
256-byte buffer: 00 01 || x1 x2 x3 x4 x5 x6 x7 x8 || 00 || 245 bytes
256 = 2 + 8 + 1 + 245
This method will use the random number generator from CryptographyProviders to generate the random bytes.
For example, if the inputData
is 32 bytes long, and the
keySizeBits
is 1024, the result of this method will look like
the following.
00 01 83 62 10 11 98 03 08 80 90 77 43 61 63 23
34 86 98 07 36 44 56 56 10 01 33 01 24 07 13 20
72 39 55 89 50 14 46 82 17 43 55 40 36 92 42 06
06 18 44 86 29 38 36 67 22 91 40 51 16 40 17 18
56 14 55 25 26 33 21 24 14 08 45 90 85 93 10 77
49 22 53 88 08 12 10 47 84 20 48 27 29 7A 14 00
01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20
Because this method creates a new byte array, and it contains sensitive data, it is a good idea to overwrite the buffer when done with it.
CryptographicOperations.ZeroMemory(formattedData);
Exceptions
- ArgumentException
The data length is too long for the key size, or the
keySizeBits
is not supported.
FormatPkcs1Oaep(ReadOnlySpan<byte>, int, int)
Build the input data into a PKCS #1 v2 OAEP formatted block for encryption (see RFC 8017).
public static byte[] FormatPkcs1Oaep(ReadOnlySpan<byte> inputData, int digestAlgorithm, int keySizeBits)
Parameters
inputData
ReadOnlySpan<byte>The data to format.
digestAlgorithm
intThe algorithm to use in the OAEP operations. It must be one of the digest algorithms defined in this class:
RsaFormat.Sha1
,RsaFormat.Sha256
, and so on.keySizeBits
intThe size of the key used, in bits. This value must be either
1024
or2048
Returns
- byte[]
A new byte array containing the formatted data.
Remarks
The OAEP (Optimal Asymmetric Encryption Padding) operation has a
number of parameters: hash function, mask generating function, and
label (pSource). The caller supplies the digestAlgorithm
as
the hash function, this method will use MGF1 as the mask generating
function, and the empty label.
This method will build a new buffer that is keySizeBits
long
and contains the following data.
00 || masked seed || masked DB
The seed is simply digest length random bytes. The masked seed is the same length.
The DB (data block) is originally
lHash || PS || 01 || input data
The lHash
value is the digest of the label. The standard
specifies the default label is the empty string. This method will
only be able to build an lHash
from the default empty string.
The PS (padding string) is all 00 bytes. Its length is
length(PS) = block size - (input data length + (2 * digest length) + 2)
For example, if the block size is 128 (1024-bit RSA key), the input
data is 16 bytes, and the digest algorithm is SHA-256, then
length(PS) = 128 - (16 + (2 * 32) + 2)
= 128 - 82 = 46
The standard allows a PS of length 0.
Another way to look at this is the maximum input data length based on the block size and digest length.
max input length = block size - ((2 * digestLen) + 2)
For example, if the block size is 128 (1024-bit RSA key), and the
digest algorithm is SHA-256, then
max input length = 128 - ((2 * 32) + 2)
= 128 - 66 = 62.
With a block size of 128 and digest algorithm of SHA-384,
max input length = 128 - ((2 * 48) + 2)
= 128 - 98 = 30.
Note that SHA-512 is simply not possible with a block size of 128.
max input length = 128 - ((2 * 64) + 2)
= 128 - 130 = -2.
If the input data length and digest algorithm make a block too big
for the keySizeBits
, this method will throw an exception.
This method supports only keySizeBits
values that are defined
in this class as KeySizeBits-x-
, such as
RsaFormat.KeySizeBits1024
(x=1024). You can use one of these
values or simply the actual key size in bits. For example, if the key
size in bits is 1024, then either RsaFormat.KeySizeBits1024
or
1024
are valid input to this method.
This method will use the random number generator and message digest implementations from CryptographyProviders.
Because this method creates a new byte array, and it contains sensitive data, it is a good idea to overwrite the buffer when done with it.
CryptographicOperations.ZeroMemory(formattedData);
Exceptions
- ArgumentException
The data length is too long for the key size, or the
keySizeBits
is not supported.
FormatPkcs1Pss(ReadOnlySpan<byte>, int, int)
Build the digest into a PKCS #1 v2 PSS formatted block for signing (see RFC 8017).
public static byte[] FormatPkcs1Pss(ReadOnlySpan<byte> digest, int digestAlgorithm, int keySizeBits)
Parameters
digest
ReadOnlySpan<byte>The message digest value to format.
digestAlgorithm
intThe algorithm used to compute the message digest. It must be one of the digest algorithms defined in this class:
RsaFormat.Sha1
,RsaFormat.Sha256
, and so on.keySizeBits
intThe size of the key used, in bits. This value must be one of the
RsaFormat.KeySizeBits-x-
values.
Returns
- byte[]
A new byte array containing the formatted digest.
Remarks
The PSS (probabilistic signature scheme) padding operation has a
number of parameters: hash function, mask generating function, salt
length, and trailer field. This method will use the input
digestAlgorithm
as the hash function, MGF1 as the mask
generating function, the digest length as the salt length, and 0xBC
as the trailer field.
The default hash function is SHA-1, but the standard recommends using
the same hash function in PSS operations as was used to digest the
data to sign. Hence, this method will do so. The caller provides the
digest (the data to format), along with a flag indicating the
algorithm. The algorithm must be one supported by this class:
RsaFormat.Sha1
, RsaFormat.Sha256
, and so on. Note that
the length of the digest
given must match the
digestAlgorithm
, otherwise the method will throw an exception.
The default salt length is 20, but the standard recommends using the
digest length as the salt length. This method will do that. For
example, if the digest is SHA-256, the salt length will be 32. 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.
Note that it is not possible to use SHA-512 as the digest algorithm with PSS and a 1024-bit key. The formatted data will be at least 2 times digest length plus two bytes long. So a PSS-formatted block with SHA-512 will be at a minimum (2 * 64) + 2 = 130 bytes long. But with a 1024-bit RSA key, the block is 128 bytes long.
This method will use the random number generator and message digest implementations from CryptographyProviders.
This method supports only keySizeBits
values that are defined
in this class as KeySizeBits-x-
, such as
RsaFormat.KeySizeBits1024
(x=1024). You can use one of these
values or simply the actual key size in bits. For example, if the key
size in bits is 1024, then either RsaFormat.KeySizeBits1024
or
1024
are valid input to this method.
Exceptions
- ArgumentException
The digest length does not match the
digestAlgorithm
, or thedigestAlgorithm
is not supported, or thekeySizeBits
is not supported.
FormatPkcs1Sign(ReadOnlySpan<byte>, int, int)
Build the digest into a PKCS #1 v1.5 formatted block for signing (see RFC 8017).
public static byte[] FormatPkcs1Sign(ReadOnlySpan<byte> digest, int digestAlgorithm, int keySizeBits)
Parameters
digest
ReadOnlySpan<byte>The message digest value to format.
digestAlgorithm
intThe algorithm used to compute the message digest. It must be one of the digest algorithms defined in this class:
RsaFormat.Sha1
,RsaFormat.Sha256
, and so on.keySizeBits
intThe size of the key used, in bits. This value must be one of the
RsaFormat.KeySizeBits-x-
values.
Returns
- byte[]
A new byte array containing the formatted digest.
Remarks
This method will build a new buffer that is keySizeBits
long
and contains the following data.
00 || 01 || FF FF ... FF || 00 || DigestInfo(digest)
The DigestInfo
is the DER encoding of the ASN.1 definition
DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithm,
digest OCTET STRING
}
This method supports only the following digest algorithms. Note that
the length of the digest
given must match the
digestAlgorithm
, otherwise the method will throw an exception.
SHA-1 RsaFormat.Sha1 20 bytes
SHA-256 RsaFormat.Sha256 32 bytes
SHA-384 RsaFormat.Sha384 48 bytes
SHA-512 RsaFormat.Sha512 64 bytes
This method supports only keySizeBits
values that are defined
in this class as KeySizeBits-x-
, such as
RsaFormat.KeySizeBits1024
(x=1024). You can use one of these
values or simply the actual key size in bits. For example, if the key
size in bits is 1024, then either RsaFormat.KeySizeBits1024
or
1024
are valid input to this method.
For example, if the digest
is 32 bytes long, the
digestAlgorithm
is RsaFormat.Sha256
and the
keySizeBits
is 1024, the result of this method will look like
the following.
00 01 FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF 00 30 31 30
0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20
01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20
Exceptions
- ArgumentException
The digest length does not match the
digestAlgorithm
, or thedigestAlgorithm
is not supported, or thekeySizeBits
is not supported.
TryParsePkcs1Decrypt(ReadOnlySpan<byte>, out byte[])
Try to parse the formattedData
as a PKCS #1 v1.5 block that
was the result of decryption (see RFC 8017).
public static bool TryParsePkcs1Decrypt(ReadOnlySpan<byte> formattedData, out byte[] outputData)
Parameters
formattedData
ReadOnlySpan<byte>The data to parse.
outputData
byte[]An output argument, the method will return a new byte array containing the unpadded data portion of the block.
Returns
- bool
True
if the method is able to parse,false
otherwise.
Remarks
This method will extract the data from the formatted data. This is
generally the plaintext (the formatted data is the decrypted block).
If it is successful, it will return true
. If it cannot extract
the information, it will return false
. The caller will likely
decrypt an RSA block, then try to parse it as PKCS #1 v2 OAEP. If
successful, the data is collected. If not, try to parse it as PKCS #1
v1.5. Note that while unlikely, it is possible for an OAEP block to
look like PKCS #1 v1.5. If you don't know which format was used, it is
best to try OAEP first, and if it fails, then try PKCS #1 v1.5.
The method will verify that the first byte is 00
, the second
byte is 02
, and that there are at least 8 padding bytes. It
will then expect to find 00
and then the data to return.
Finally, the method will return a new byte array containing the actual data portion.
Because this method creates a new byte array, and it contains sensitive data, it is a good idea to overwrite the buffer when done with it.
CryptographicOperations.ZeroMemory(outputData);
This method only supports blocks 128 or 256 bytes (1024 or 2048 bits) long.
If any element fails (the length of the formattedData
is
not supported, an expected byte is not there, or so on), the method
will return false
. If there is an error, the method might set
the outputData
argument to an empty array, or it might contain
the purported data. If the return is false
and there is data
in outputData
, that data is meaningless.
TryParsePkcs1Oaep(ReadOnlySpan<byte>, int, out byte[])
Try to parse the formattedData
as a PKCS #1 v2 OAEP block that
was the result of decryption (see RFC 8017).
public static bool TryParsePkcs1Oaep(ReadOnlySpan<byte> formattedData, int digestAlgorithm, out byte[] outputData)
Parameters
formattedData
ReadOnlySpan<byte>The data to parse.
digestAlgorithm
intThe algorithm to use in the OAEP operations. It must be one of the digest algorithms defined in this class:
RsaFormat.Sha1
,RsaFormat.Sha256
, and so on.outputData
byte[]An output argument, a new buffer containing the data portion of the block.
Returns
- bool
True
if the method is able to parse,false
otherwise.
Remarks
This method will extract the data from the formatted data and return
it in a new byte array. This is generally the plaintext (the
formatted data is the decrypted block). If it is successful, it will
return true
. If it cannot extract the information, it will
return false
.
The method will verify that the first byte is 00
, then it will
perform MGF1 using the specified digest algorithm on the masked DB
(data block) to unmask the salt, and then perform MGF1 on the salt to
unmaskk the DB. It will then verify that the lHash and PS are correct.
00 || masked salt || masked DB
MGF1(maskedDB)
00 || salt || masked DB
MGF1(salt)
00 || salt || lHash || PS || 01 || output data
The caller supplies the digest algorithm to use in the MGF1. It must
be one of the supported values: RsaFormat.Sha1
, Sha256
,
or so on.
This method will use the message digest implementations from CryptographyProviders.
The lHash
value is the digest of the label (pSource). The
standard specifies the default label is the empty string. This method
will only be able to build an lHash
from the default empty
string.
Finally, the method will return a new byte array containing the actual data portion.
Because this method creates a new byte array, and it contains sensitive data, it is a good idea to overwrite the buffer when done with it.
CryptographicOperations.ZeroMemory(outputData);
This method only supports blocks 128 or 256 bytes (1024 or 2048 bits) long.
If any element fails (the length of the formattedData
is
not supported, an expected byte is not there, or so on), the method
will return false
. If there is an error, the method might set
the outputData
argument to an empty array, or it might contain
the purported data. If the return is false
and there is data
in outputData
, that data is meaningless.
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).
public static bool TryParsePkcs1Pss(ReadOnlySpan<byte> formattedSignature, ReadOnlySpan<byte> digest, int digestAlgorithm, out byte[] mPrimeAndH, out bool isVerified)
Parameters
formattedSignature
ReadOnlySpan<byte>The data to parse.
digest
ReadOnlySpan<byte>The computed digest, the digest of the message, the data to verify.
digestAlgorithm
intThe digest algorithm used to compute the digest, it must be one of the supported algorithms:
RsaFormat.Sha1
, and so on.mPrimeAndH
byte[]An output argument, a new byte array containing M-prime concatenated with the H value extracted from the
formattedSignature
.isVerified
boolAn 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.
TryParsePkcs1Verify(ReadOnlySpan<byte>, out int, out byte[])
Try to parse the formattedSignature
as a PKCS #1 v1.5 block
for verifying (see RFC 8017).
public static bool TryParsePkcs1Verify(ReadOnlySpan<byte> formattedSignature, out int digestAlgorithm, out byte[] digest)
Parameters
formattedSignature
ReadOnlySpan<byte>The data to parse.
digestAlgorithm
intAn output argument, the method will set it to one of the values defined in this class representing the algorithm:
RsaFormat.Sha1
, and so on.digest
byte[]An output argument, the method will set it to be a new byte array containing the digest portion of the signature.
Returns
- bool
True
if the method is able to parse,false
otherwise.
Remarks
This method will extract the message digest algorithm and the message
digest itself from the formatted signature. If it is successful, it
will return true
. If it cannot extract the information, it
will return false
. The caller will likely decrypt an RSA
signature, then try to parse it as PKCS #1 v1.5. If successful, the
digest is collected. If not, try to parse it as PKCS #1 v2 PSS.
The method will verify that the first byte is 00
, the second
byte is 01
, and that the padding bytes are all FF
. It
will then expect to find 00
and then the DigestInfo.
It will read the DigestInfo to determine the algorithm. If the method
recognizes the OID, it will set the output int digestAlgorithm
to one of the supported values: RsaFormat.Sha1
, Sha256
,
or so on.
Finally, the method will return a byte array containing the actual digest. This will be a new buffer.
This method only supports signatures 128 or 256 bytes (1024 or 2048 bits) long.
If any element fails (the length of the formattedSignature
is
not supported, an expected byte is not there, the OID does not
represent a supported algorithm, the digest is not the proper length,
or so on), the method will return false. If there is an error, the
method might set the output digestAlgorithm
to 0 and the
output digest
to an empty byte array. However, the algorithm
and digest output arguments might contain the purported algorithm and
digest.