Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GetCiphertextLength for CBC, CFB, and ECB. #45003

Merged
merged 2 commits into from
Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
public abstract void GenerateIV();
public abstract void GenerateKey();
public int GetCiphertextLengthCbc(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.PKCS7) { throw null; }
public int GetCiphertextLengthCfb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode = System.Security.Cryptography.PaddingMode.None, int feedbackSizeInBits = 8) { throw null; }
vcsjones marked this conversation as resolved.
Show resolved Hide resolved
public int GetCiphertextLengthEcb(int plaintextLength, System.Security.Cryptography.PaddingMode paddingMode) { throw null; }
public bool ValidKeySize(int bitLength) { throw null; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
<data name="Argument_StreamNotWritable" xml:space="preserve">
<value>Stream was not writable.</value>
</data>
<data name="Argument_BitsMustBeWholeBytes" xml:space="preserve">
<value>The value specified in bits must be a whole number of bytes.</value>
</data>
<data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
<value>Non-negative number required.</value>
</data>
Expand Down Expand Up @@ -108,6 +111,15 @@
<data name="Cryptography_InvalidHashAlgorithmOid" xml:space="preserve">
<value>The specified OID ({0}) does not represent a known hash algorithm.</value>
</data>
<data name="Cryptography_MatchBlockSize" xml:space="preserve">
<value>The specified plaintext size is not valid for the the padding and block size.</value>
</data>
<data name="Cryptography_MatchFeedbackSize" xml:space="preserve">
<value>The specified plaintext size is not valid for the the padding and feedback size.</value>
</data>
<data name="Cryptography_PlaintextTooLarge" xml:space="preserve">
<value>The specified plaintext size is too large.</value>
</data>
<data name="NotSupported_SubclassOverride" xml:space="preserve">
<value>Method not supported. Derived class must override.</value>
</data>
Expand All @@ -129,4 +141,7 @@
<data name="InvalidOperation_IncorrectImplementation" xml:space="preserve">
<value>The algorithm's implementation is incorrect.</value>
</data>
<data name="InvalidOperation_UnsupportedBlockSize" xml:space="preserve">
<value>The algorithm's block size is not supported.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,212 @@ public bool ValidKeySize(int bitLength)
return bitLength.IsLegalSize(validSizes);
}

/// <summary>
/// Gets the length of a ciphertext with a given padding mode and plaintext length in ECB mode.
/// </summary>
/// <param name="paddingMode">The padding mode used to pad the plaintext to the algorithm's block size.</param>
/// <param name="plaintextLength">The plaintext length, in bytes.</param>
/// <returns>The length, in bytes, of the ciphertext with padding.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <para>
/// <paramref name="plaintextLength" /> is a negative number.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="plaintextLength" /> when padded is too large to represent as
/// a signed 32-bit integer.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="paddingMode" /> is not a valid padding mode.
/// </para>
/// </exception>
/// <exception cref="CryptographicException">
/// <para>
/// <see cref="BlockSize" /> is not a positive integer.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <see cref="BlockSize" /> is not a whole number of bytes. It must be divisible by 8.
/// </para>
/// </exception>
/// <exception cref="ArgumentException">
/// <para>
/// The padding mode <see cref="PaddingMode.None" /> was used, but <paramref name="plaintextLength" />
/// is not a whole number of blocks.
/// </para>
/// </exception>
public int GetCiphertextLengthEcb(int plaintextLength, PaddingMode paddingMode) =>
GetCiphertextLengthBlockAligned(plaintextLength, paddingMode);

/// <summary>
/// Gets the length of a ciphertext with a given padding mode and plaintext length in CBC mode.
/// </summary>
/// <param name="paddingMode">The padding mode used to pad the plaintext to the algorithm's block size.</param>
/// <param name="plaintextLength">The plaintext length, in bytes.</param>
/// <returns>The length, in bytes, of the ciphertext with padding.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <para>
/// <paramref name="plaintextLength" /> is a negative number.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="plaintextLength" /> when padded is too large to represent as
/// a signed 32-bit integer.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="paddingMode" /> is not a valid padding mode.
/// </para>
/// </exception>
/// <exception cref="CryptographicException">
/// <para>
/// <see cref="BlockSize" /> is not a positive integer.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <see cref="BlockSize" /> is not a whole number of bytes. It must be divisible by 8.
/// </para>
/// </exception>
/// <exception cref="ArgumentException">
/// <para>
/// The padding mode <see cref="PaddingMode.None" /> was used, but <paramref name="plaintextLength" />
/// is not a whole number of blocks.
/// </para>
/// </exception>
public int GetCiphertextLengthCbc(int plaintextLength, PaddingMode paddingMode = PaddingMode.PKCS7) =>
GetCiphertextLengthBlockAligned(plaintextLength, paddingMode);

private int GetCiphertextLengthBlockAligned(int plaintextLength, PaddingMode paddingMode)
{
if (plaintextLength < 0)
throw new ArgumentOutOfRangeException(nameof(plaintextLength), SR.ArgumentOutOfRange_NeedNonNegNum);

int blockSizeBits = BlockSize; // The BlockSize property is in bits.

if (blockSizeBits <= 0 || (blockSizeBits & 0b111) != 0)
throw new InvalidOperationException(SR.InvalidOperation_UnsupportedBlockSize);

int blockSizeBytes = blockSizeBits >> 3;
int wholeBlocks = Math.DivRem(plaintextLength, blockSizeBytes, out int remainder) * blockSizeBytes;

switch (paddingMode)
{
case PaddingMode.None when remainder != 0:
throw new ArgumentException(SR.Cryptography_MatchBlockSize, nameof(plaintextLength));
case PaddingMode.None:
case PaddingMode.Zeros when remainder == 0:
return plaintextLength;
case PaddingMode.Zeros:
case PaddingMode.PKCS7:
case PaddingMode.ANSIX923:
case PaddingMode.ISO10126:
if (int.MaxValue - wholeBlocks < blockSizeBytes)
{
throw new ArgumentOutOfRangeException(nameof(plaintextLength), SR.Cryptography_PlaintextTooLarge);
}

return wholeBlocks + blockSizeBytes;
default:
throw new ArgumentOutOfRangeException(nameof(paddingMode), SR.Cryptography_InvalidPaddingMode);
}
}

/// <summary>
/// Gets the length of a ciphertext with a given padding mode and plaintext length in CFB mode.
/// </summary>
/// <param name="paddingMode">The padding mode used to pad the plaintext to the feedback size.</param>
/// <param name="plaintextLength">The plaintext length, in bytes.</param>
/// <param name="feedbackSizeInBits">The feedback size, in bits.</param>
/// <returns>The length, in bytes, of the ciphertext with padding.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <para>
/// <paramref name="feedbackSizeInBits" /> is not a positive number.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="plaintextLength" /> is a negative number.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="plaintextLength" /> when padded is too large to represent as
/// a signed 32-bit integer.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="paddingMode" /> is not a valid padding mode.
/// </para>
/// </exception>
/// <exception cref="ArgumentException">
/// <para>
/// The padding mode <see cref="PaddingMode.None" /> was used, but <paramref name="plaintextLength" />
/// is not a whole number of blocks.
/// </para>
/// <para>
/// - or -
/// </para>
/// <para>
/// <paramref name="feedbackSizeInBits" /> is not a whole number of bytes. It must be divisible by 8.
/// </para>
/// </exception>
/// <remarks>
/// <paramref name="feedbackSizeInBits" /> accepts any value that is a valid feedback size, regardless if the algorithm
/// supports the specified feedback size.
/// </remarks>
public int GetCiphertextLengthCfb(int plaintextLength, PaddingMode paddingMode = PaddingMode.None, int feedbackSizeInBits = 8)
vcsjones marked this conversation as resolved.
Show resolved Hide resolved
{
if (plaintextLength < 0)
throw new ArgumentOutOfRangeException(nameof(plaintextLength), SR.ArgumentOutOfRange_NeedNonNegNum);

if (feedbackSizeInBits <= 0)
throw new ArgumentOutOfRangeException(nameof(feedbackSizeInBits), SR.ArgumentOutOfRange_NeedPosNum);

if ((feedbackSizeInBits & 0b111) != 0)
throw new ArgumentException(SR.Argument_BitsMustBeWholeBytes, nameof(feedbackSizeInBits));

int feedbackSizeInBytes = feedbackSizeInBits >> 3;
int feedbackAligned = Math.DivRem(plaintextLength, feedbackSizeInBytes, out int remainder) * feedbackSizeInBytes;

switch (paddingMode)
{
case PaddingMode.None when remainder != 0:
throw new ArgumentException(SR.Cryptography_MatchFeedbackSize, nameof(plaintextLength));
case PaddingMode.None:
case PaddingMode.Zeros when remainder == 0:
return plaintextLength;
case PaddingMode.Zeros:
case PaddingMode.PKCS7:
case PaddingMode.ANSIX923:
case PaddingMode.ISO10126:
if (int.MaxValue - feedbackAligned < feedbackSizeInBytes)
{
throw new ArgumentOutOfRangeException(nameof(plaintextLength), SR.Cryptography_PlaintextTooLarge);
}

return feedbackAligned + feedbackSizeInBytes;
default:
throw new ArgumentOutOfRangeException(nameof(paddingMode), SR.Cryptography_InvalidPaddingMode);
}
}

protected CipherMode ModeValue;
protected PaddingMode PaddingValue;
protected byte[]? KeyValue;
Expand Down
Loading