Skip to content

Conversation

@vcsjones
Copy link
Member

@vcsjones vcsjones commented Jul 5, 2021

This implements the CBC one shots.

It should be noted that the implementation could be "better" and avoid a few allocations, such as around copying the initialization vector, which will likely be for .NET 7 since it's somewhat non-trivial. This work is already done in #55090 and will be re-opened after the branch for 6.0 is taken.

This also did a significant refactoring of the unit tests to avoid a product of duplicated code between modes and algorithms. This will make the CFB work much easier.

Contributes to #2406.

Work remaining:

  • XML docs

@ghost
Copy link

ghost commented Jul 5, 2021

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@ghost
Copy link

ghost commented Jul 5, 2021

Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @GrabYourPitchforks
See info in area-owners.md if you want to be subscribed.

Issue Details

This implements the CBC one shots.

It should be noted that the implementation could be "better" and avoid a few allocations, such as around copying the initialization vector, which will likely be for .NET 7 since it's somewhat non-trivial. This work is already done in #55090 and will be re-opened after the branch for 6.0 is taken.

This also did a significant refactoring of the unit tests to avoid a cross product of duplicated code between modes and algorithms. This will make the CFB work much easier.

Work remaining:

  • XML docs
Author: vcsjones
Assignees: -
Labels:

area-System.Security, new-api-needs-documentation

Milestone: -

@vcsjones vcsjones marked this pull request as draft July 5, 2021 18:32
@vcsjones
Copy link
Member Author

vcsjones commented Jul 5, 2021

Opening as draft to get a run through CI and because the XML documentation is not done, but the code should be in a reviewable state.

@vcsjones vcsjones marked this pull request as ready for review July 6, 2021 04:15
@vcsjones
Copy link
Member Author

vcsjones commented Jul 6, 2021

Seems like failures are either infrastructure or unrelated. Marking for review.

Assert.True(result, "TryDecrypt");
Assert.Equal(destinationBuffer.Length, bytesWritten);

AssertPlaintexts(plaintext, destinationBuffer.ToArray(), padding);
Copy link
Member

Choose a reason for hiding this comment

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

Seems like the helpers could be spanified. But doesn't really matter.

Copy link
Member

Choose a reason for hiding this comment

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

Or just split the array/span versions of destination:

-Span<byte> destinationBuffer = new byte[expectedPlaintextSize];
+byte[] destination = new byte[expectedPlaintextSize];
+Span<byte> destinationBuffer = destination;

Copy link
Member Author

@vcsjones vcsjones Jul 6, 2021

Choose a reason for hiding this comment

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

I kept the assert helpers as taking arrays because asserting on arrays is nicer with Xunit. Taking a ReadOnlySpan<byte> would mean doing an Assert.True(blah.SequenceEqual(otherBlah)) which doesn't have quite as useful output when the assertion fails.

Since it is test code, I find the better assertion output more valuable than less allocating.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah. And the version for strings is nicer than the one for arrays (particularly byte[])... at least in the past the array mismatch one was most useful if it was in the first few elements. That's why there are so many Asserts over foo.ByteArrayToHex().

One of these days I should make an assert-equality routine and clean up all of the things to do non-allocated SequenceEqual then fall back to making the string on failure. Maybe it'd shave a non-trivial amount of time off of the test execution.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh. Actually it looks like we have one on AssertExtensions. Let me play with that a bit...

Assert.Equal(actual, expected);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Consider a "test" to make sure the derived types all specified all the test methods:

[Fact]
public void TestsDefined()
{
const string DriverSuffix = "Driver";
Type implType = GetType();
Type defType = typeof(NonCryptoHashTestDriver);
List<string>? missingMethods = null;
foreach (MethodInfo info in defType.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic))
{
if (info.IsFamily && info.Name.EndsWith(DriverSuffix, StringComparison.Ordinal))
{
string targetMethodName = info.Name.Substring(0, info.Name.Length - DriverSuffix.Length);
MethodInfo info2 = implType.GetMethod(
targetMethodName,
BindingFlags.Instance | BindingFlags.Public);
if (info2 is null)
{
missingMethods ??= new List<string>();
missingMethods.Add(targetMethodName);
}
}
}
if (missingMethods is not null)
{
Assert.Empty(missingMethods);
}
}

@vcsjones
Copy link
Member Author

vcsjones commented Jul 6, 2021

@bartonjs some last second test improvements:

  1. Use AssertExtensions.SequenceEqual to assert spans. Its output is... acceptable.

      Error Message:
       Showing first 10 differences
     Position 23: Expected: 181, Actual: 74
     Total number of differences: 1 out of 24
    
  2. While switching to AssertExtensions I noticed two asserts were missing in the roundtrip test.

  3. Incorporated a reflection test as suggested.

@bartonjs
Copy link
Member

bartonjs commented Jul 6, 2021

Its output is... acceptable.

Yeah. Maybe one day we'll slip in a specialization for byte spans that does hex strings once they're proven to be a mismatch. (At least, I usually find the hex easier to work with, being fixed width)

@bartonjs
Copy link
Member

bartonjs commented Jul 6, 2021

While switching to AssertExtensions I noticed two asserts were missing in the roundtrip test.

I think I looked at that like a half dozen times trying to decide if they were needed or not, torn between "but all the things flow..." and "why are there two bidirectional blocks with only one assert each?". Clearly I should have just left a comment 😄.

@vcsjones
Copy link
Member Author

vcsjones commented Jul 6, 2021

Maybe one day we'll slip in a specialization for byte spans

Yeah. I can do that outside of this PR.

@vcsjones
Copy link
Member Author

vcsjones commented Jul 7, 2021

Failures appear unrelated.

@bartonjs bartonjs merged commit 847cc56 into dotnet:main Jul 7, 2021
@bartonjs
Copy link
Member

bartonjs commented Jul 7, 2021

Yay. CBC was the important one, but now CFB should give is feature completion.

@vcsjones vcsjones deleted the 2406-one-shot-cbc branch July 8, 2021 06:33
@ghost ghost locked as resolved and limited conversation to collaborators Aug 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants