Closed Roneri75 closed 4 years ago
Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @jeffhandley See info in area-owners.md if you want to be subscribed.
It looks like there are two things:
net5.0
defaults to a FeedbackSize of 64, and net48
defaults to a feedback size of 8.net5.0
applies padding a little differently than net48
.If you explicitly set the FeedbackSize
, then the results are the same, minus the padding differences.
@bartonjs assuming you want something fixed here, how likely is it that it could make it in to RTM?
Ah, I see. TripleDES
sets FeedBackSize to the block size, but TripleDESCryptoServiceProvider
sets it to 8. So the data would be inconsistent (CFB8 vs CFB64) in the .NET Framework application if someone registered to CryptoConfig that TripleDESCng should be the default 3DES provider. The app would also work if it used new TripleDESCryptoServiceProvider()
. (It looks like it's creating a TripleDESCryptoServiceProvider directly, but there's no TripleDESCryptoServiceProvider.Create
method, it's falling back to TripleDES.Create()
, and thus getting whatever CryptoConfig says the right answer is for 3DES)
I think that this is just an unfortunate circumstance of the type hierarchy for the crypto types here. The only change to consider would be making the TripleDESImplementation class default to FeedbackSize = 8 so that TripleDES.Create().FeedbackSize
produced the same value across a clean .NET Framework installation execution and a .NET 5 execution.
@danmosemsft How would you feel about taking a change to 5.0 that changed the default value of TripleDES.Create().FeedbackSize
to match .NET Framework? It pretty much only affects .NET 5, since it only impacts CFB mode, which we didn't bring back to Core until 5. So it's a "no existing .NET Core applications should be depending on it, but it makes .NET 5 slightly more compatible with .NET Framework than it otherwise would have been" change. I think it's a reasonable concession to compatibility; but I'm way happier changing it for 5 than for 6, so I don't think we would change it in master unless it was already likely to get approved; because then we're deciding if we wanted to be more compatible with .NET 5 or .NET Framework.
@bartonjs another issue is padding. It looks like for CFB8, we are on padding to the feedback size (so 1 byte of padding is added). In .NET Framework we are padding to the whole block (nearest 8 for 3DES).
I don't think that will impact padding removal and the data will interop as long as normal padding removal is used.
Yeah, I guess my willingness to take the FeedbackSize default change is predicated on it meaning that a value encrypted in .NET Framework can be decrypted in .NET 5, and vice-versa (even though they compute different padding).
I'll work on this over the weekend and you can decide if you want to port to release/5.0.
Thanks, @vcsjones. I so owe you a cup of coffee for the help you've given while I've been juggling multiple projects.
@bartonjs so if I understand right
If so this sounds like worth taking to tactics for approval, but note @vcsjones we won't know their decision before Monday, in case that influences whether you want to spend time on it 😃
@danmosemsft Yeah, that's an accurate summary.
@vcsjones My psychic diagnostic powers believe the whole change should be to add
public TripleDESImplementation()
{
// Default CFB to CFB8 to match .NET Framework's default for TripleDES.Create()
FeedbackSizeValue = 8;
}
around hereish
and a "set the minimum number of properties and make sure we can read data from netfx, even though we produce different data in the 'ignore me' bytes" test.
Hopefully yours agrees. I'm eager to find out how close my predictive powers were to reality.
I will guess 80% chance they take it..
I so owe you a cup of coffee
Sold!
@bartonjs You were right about the feedback size. Padding remains an interesting case.
The .NET Framework expects the ciphertext to be padded to the block size, even with CFB8. Otherwise it will blow up. As the way things are today, .NET 5 pads CFB8 data in a way that .NET Framework cannot de-pad (unless you get lucky).
This will work in .NET 5 and fail in .NET Framework:
byte[] ciphertext = Convert.FromBase64String("Vx8GlhTi");
using var tripleDes = TripleDES.Create();
tripleDes.FeedbackSize = 8;
tripleDes.Mode = CipherMode.CFB;
tripleDes.Key = Convert.FromBase64String("aaHRNTLQh++TK3lhShiicCPLQ6IO0YVH");
tripleDes.IV = Convert.FromBase64String("OQHTwYDPlMo=");
using var transform = tripleDes.CreateDecryptor();
Console.WriteLine(Encoding.UTF8.GetString(transform.TransformFinalBlock(ciphertext, 0, ciphertext.Length)));
The reverse is not a problem: we can de-pad .NET Framework encrypted data in .NET 5.
@bartonjs The padding size thing is a bit thorny if we want to start encrypting with full blocks of padding. We might break something for someone going from RC to RTM.
Here are the options, as I figure.
We try some kind of hybrid approach.
This will permit .NET 5 ciphertexts to be read by .NET Framework, and will continue to allow decrypting 1-octet padding sizes as well. This raises the question, "what does this mean for PaddingMode.None
?". This is also perhaps a large change for this late in the .NET 5 cycle.
@danmosemsft there are two potential .NET Framework compatibility breaks here. The first one that was originally discussed is fairly simple and aligns with your initial assessment. That one I still think is worth taking and discussing with "tactics" on Monday. That is fixed in #43259 and that is the only thing in that PR.
The reverse is not a problem: we can de-pad .NET Framework encrypted data in .NET 5.
Then I think at this point we stick with "We do nothing." (for padding, vs feedback size). Upgrading succeeds (old data can be read), but side-by-side is hampered (since old programs can't read new program data). Callers who need the padding to be there can do their encrypts with no padding and manually append the padding data. It's not great, but the workaround is required in the newer application, so it's at least not something that requires a time machine.
cc @jeffhandley as it's 5.0.
When using CipherMode.CFB as Mode in the System.Security.Cryptography.TripleDES to Encrypt e.g. a password you don't get the same encryption string back in .NET 5.0 RC1 as you get in .NET 4.7. We have used code like this since .net 4.5 and it's worked exactly the same since, i should work exactly the same in .NET 5.0 as in 4.x or all apps using this for e.g. saving password encrypted will stop working after upgrade to .NET 5.
I did a test console app to reproduce this, it's the exact the same code for both .NET 5.0 RC1 as for .NET 4.7. Both apps where created in VS 2019 Preview (16..8.0 Preview 3.2). And they do not generate the same result.