Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit 1ce37df

Browse files
authored
Support Two-Key Triple DES (#20275)
Support Two-Key Triple DES
1 parent ff7f4ba commit 1ce37df

File tree

8 files changed

+125
-56
lines changed

8 files changed

+125
-56
lines changed

src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/TripleDES/TripleDESCipherTests.cs

+92-34
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
using System;
6-
using System.Collections.Generic;
7-
using System.Security.Cryptography;
85
using Test.Cryptography;
96
using Xunit;
107

@@ -23,19 +20,45 @@ public static void TripleDESDefaults()
2320
}
2421

2522
[Fact]
26-
public static void TripleDESRoundTrip192BitsNoneECB()
23+
public static void TripleDESGenerate128Key()
2724
{
28-
byte[] key = "c5629363d957054eba793093b83739bb78711db221a82379".HexToByteArray();
25+
using (TripleDES des = TripleDESFactory.Create())
26+
{
27+
des.KeySize = 128;
28+
byte[] key = des.Key;
29+
Assert.Equal(128, key.Length * 8);
30+
}
31+
}
32+
33+
[Fact]
34+
public static void TripleDESInvalidKeySizes()
35+
{
36+
using (TripleDES des = TripleDESFactory.Create())
37+
{
38+
Assert.Throws<CryptographicException>(() => des.KeySize = 128 - des.BlockSize);
39+
Assert.Throws<CryptographicException>(() => des.KeySize = 192 + des.BlockSize);
40+
}
41+
}
42+
43+
[Theory]
44+
[InlineData(192, "e56f72478c7479d169d54c0548b744af5b53efb1cdd26037", "c5629363d957054eba793093b83739bb78711db221a82379")]
45+
[InlineData(128, "1387b981dbb40f34b915c4ed89fd681a740d3b4869c0b575", "c5629363d957054eba793093b83739bb")]
46+
[InlineData(192, "1387b981dbb40f34b915c4ed89fd681a740d3b4869c0b575", "c5629363d957054eba793093b83739bbc5629363d957054e")]
47+
public static void TripleDESRoundTripNoneECB(int keySize, string expectedCipherHex, string keyHex)
48+
{
49+
byte[] key = keyHex.HexToByteArray();
2950

3051
using (TripleDES alg = TripleDESFactory.Create())
3152
{
3253
alg.Key = key;
54+
Assert.Equal(keySize, alg.KeySize);
55+
3356
alg.Padding = PaddingMode.None;
3457
alg.Mode = CipherMode.ECB;
3558

3659
byte[] plainText = "de7d2dddea96b691e979e647dc9d3ca27d7f1ad673ca9570".HexToByteArray();
3760
byte[] cipher = alg.Encrypt(plainText);
38-
byte[] expectedCipher = "e56f72478c7479d169d54c0548b744af5b53efb1cdd26037".HexToByteArray();
61+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
3962
Assert.Equal<byte>(expectedCipher, cipher);
4063

4164
byte[] decrypted = alg.Decrypt(cipher);
@@ -44,22 +67,27 @@ public static void TripleDESRoundTrip192BitsNoneECB()
4467
}
4568
}
4669

47-
[Fact]
48-
public static void TripleDESRoundTrip192BitsNoneCBC()
70+
[Theory]
71+
[InlineData(192, "dea36279600f19c602b6ed9bf3ffdac5ebf25c1c470eb61c", "b43eaf0260813fb47c87ae073a146006d359ad04061eb0e6")]
72+
[InlineData(128, "a25e55381f0cc45541741b9ce6e96b7799aa1e0db70780f7", "b43eaf0260813fb47c87ae073a146006")]
73+
[InlineData(192, "a25e55381f0cc45541741b9ce6e96b7799aa1e0db70780f7", "b43eaf0260813fb47c87ae073a146006b43eaf0260813fb4")]
74+
public static void TripleDESRoundTripNoneCBC(int keySize, string expectedCipherHex, string keyHex)
4975
{
50-
byte[] key = "b43eaf0260813fb47c87ae073a146006d359ad04061eb0e6".HexToByteArray();
76+
byte[] key = keyHex.HexToByteArray();
5177
byte[] iv = "5fbc5bc21b8597d8".HexToByteArray();
5278

5379
using (TripleDES alg = TripleDESFactory.Create())
5480
{
5581
alg.Key = key;
82+
Assert.Equal(keySize, alg.KeySize);
83+
5684
alg.IV = iv;
5785
alg.Padding = PaddingMode.None;
5886
alg.Mode = CipherMode.CBC;
5987

6088
byte[] plainText = "79a86903608e133e020e1dc68c9835250c2f17b0ebeed91b".HexToByteArray();
6189
byte[] cipher = alg.Encrypt(plainText);
62-
byte[] expectedCipher = "dea36279600f19c602b6ed9bf3ffdac5ebf25c1c470eb61c".HexToByteArray();
90+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
6391
Assert.Equal<byte>(expectedCipher, cipher);
6492

6593
byte[] decrypted = alg.Decrypt(cipher);
@@ -68,20 +96,25 @@ public static void TripleDESRoundTrip192BitsNoneCBC()
6896
}
6997
}
7098

71-
[Fact]
72-
public static void TripleDESRoundTrip192BitsZerosECB()
99+
[Theory]
100+
[InlineData(192, "149ec32f558b27c7e4151e340d8184f18b4e25d2518f69d9", "9da5b265179d65f634dfc95513f25094411e51bb3be877ef")]
101+
[InlineData(128, "02ac5db31cfada874f6042c4e92b09175fd08e93a20f936b", "9da5b265179d65f634dfc95513f25094")]
102+
[InlineData(192, "02ac5db31cfada874f6042c4e92b09175fd08e93a20f936b", "9da5b265179d65f634dfc95513f250949da5b265179d65f6")]
103+
public static void TripleDESRoundTripZerosECB(int keySize, string expectedCipherHex, string keyHex)
73104
{
74-
byte[] key = "9da5b265179d65f634dfc95513f25094411e51bb3be877ef".HexToByteArray();
105+
byte[] key = keyHex.HexToByteArray();
75106

76107
using (TripleDES alg = TripleDESFactory.Create())
77108
{
78109
alg.Key = key;
110+
Assert.Equal(keySize, alg.KeySize);
111+
79112
alg.Padding = PaddingMode.Zeros;
80113
alg.Mode = CipherMode.ECB;
81114

82115
byte[] plainText = "77a8b2efb45addb38d7ef3aa9e6ab5d71957445ab8".HexToByteArray();
83116
byte[] cipher = alg.Encrypt(plainText);
84-
byte[] expectedCipher = "149ec32f558b27c7e4151e340d8184f18b4e25d2518f69d9".HexToByteArray();
117+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
85118
Assert.Equal<byte>(expectedCipher, cipher);
86119

87120
byte[] decrypted = alg.Decrypt(cipher);
@@ -90,14 +123,19 @@ public static void TripleDESRoundTrip192BitsZerosECB()
90123
}
91124
}
92125

93-
[Fact]
94-
public static void TripleDESRoundTrip192BitsISO10126ECB()
126+
[Theory]
127+
[InlineData(192, "9da5b265179d65f634dfc95513f25094411e51bb3be877ef")]
128+
[InlineData(128, "9da5b265179d65f634dfc95513f25094")]
129+
[InlineData(192, "9da5b265179d65f634dfc95513f250949da5b265179d65f6")]
130+
public static void TripleDESRoundTripISO10126ECB(int keySize, string keyHex)
95131
{
96-
byte[] key = "9da5b265179d65f634dfc95513f25094411e51bb3be877ef".HexToByteArray();
132+
byte[] key = keyHex.HexToByteArray();
97133

98134
using (TripleDES alg = TripleDESFactory.Create())
99135
{
100136
alg.Key = key;
137+
Assert.Equal(keySize, alg.KeySize);
138+
101139
alg.Padding = PaddingMode.ISO10126;
102140
alg.Mode = CipherMode.ECB;
103141

@@ -112,21 +150,26 @@ public static void TripleDESRoundTrip192BitsISO10126ECB()
112150
}
113151
}
114152

115-
[Fact]
116-
public static void TripleDESRoundTrip192BitsANSIX923ECB()
153+
[Theory]
154+
[InlineData(192, "149ec32f558b27c7e4151e340d8184f1c90f0a499e20fda9", "9da5b265179d65f634dfc95513f25094411e51bb3be877ef")]
155+
[InlineData(128, "02ac5db31cfada874f6042c4e92b091783620e54a1e75957", "9da5b265179d65f634dfc95513f25094")]
156+
[InlineData(192, "02ac5db31cfada874f6042c4e92b091783620e54a1e75957", "9da5b265179d65f634dfc95513f250949da5b265179d65f6")]
157+
public static void TripleDESRoundTripANSIX923ECB(int keySize, string expectedCipherHex, string keyHex)
117158
{
118-
byte[] key = "9da5b265179d65f634dfc95513f25094411e51bb3be877ef".HexToByteArray();
159+
byte[] key = keyHex.HexToByteArray();
119160

120161
using (TripleDES alg = TripleDESFactory.Create())
121162
{
122163
alg.Key = key;
164+
Assert.Equal(keySize, alg.KeySize);
165+
123166
alg.Padding = PaddingMode.ANSIX923;
124167
alg.Mode = CipherMode.ECB;
125168

126169
byte[] plainText = "77a8b2efb45addb38d7ef3aa9e6ab5d71957445ab8".HexToByteArray();
127170
byte[] cipher = alg.Encrypt(plainText);
128-
129-
byte[] expectedCipher = "149ec32f558b27c7e4151e340d8184f1c90f0a499e20fda9".HexToByteArray();
171+
172+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
130173
Assert.Equal<byte>(expectedCipher, cipher);
131174

132175
byte[] decrypted = alg.Decrypt(cipher);
@@ -161,22 +204,27 @@ public static void TripleDES_FailureToRoundTrip192Bits_DifferentPadding_ANSIX923
161204
}
162205
}
163206

164-
[Fact]
165-
public static void TripleDESRoundTrip192BitsZerosCBC()
207+
[Theory]
208+
[InlineData(192, "65f3dc211876a9daad238aa7d0c7ed7a3662296faf77dff9", "5e970c0d2323d53b28fa3de507d6d20f9f0cd97123398b4d")]
209+
[InlineData(128, "2f55ff6bd8270f1d68dcb342bb674f914d9e1c0e61017a77", "5e970c0d2323d53b28fa3de507d6d20f")]
210+
[InlineData(192, "2f55ff6bd8270f1d68dcb342bb674f914d9e1c0e61017a77", "5e970c0d2323d53b28fa3de507d6d20f5e970c0d2323d53b")]
211+
public static void TripleDESRoundTripZerosCBC(int keySize, string expectedCipherHex, string keyHex)
166212
{
167-
byte[] key = "5e970c0d2323d53b28fa3de507d6d20f9f0cd97123398b4d".HexToByteArray();
213+
byte[] key = keyHex.HexToByteArray();
168214
byte[] iv = "95498b5bf570f4c8".HexToByteArray();
169215

170216
using (TripleDES alg = TripleDESFactory.Create())
171217
{
172218
alg.Key = key;
219+
Assert.Equal(keySize, alg.KeySize);
220+
173221
alg.IV = iv;
174222
alg.Padding = PaddingMode.Zeros;
175223
alg.Mode = CipherMode.CBC;
176224

177225
byte[] plainText = "f9e9a1385bf3bd056d6a06eac662736891bd3e6837".HexToByteArray();
178226
byte[] cipher = alg.Encrypt(plainText);
179-
byte[] expectedCipher = "65f3dc211876a9daad238aa7d0c7ed7a3662296faf77dff9".HexToByteArray();
227+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
180228
Assert.Equal<byte>(expectedCipher, cipher);
181229

182230
byte[] decrypted = alg.Decrypt(cipher);
@@ -185,20 +233,25 @@ public static void TripleDESRoundTrip192BitsZerosCBC()
185233
}
186234
}
187235

188-
[Fact]
189-
public static void TripleDESRoundTrip192BitsPKCS7ECB()
236+
[Theory]
237+
[InlineData(192, "7b8d982ee0c14821daf1b8cf4e407c2eb328627b696ac36e", "155425f12109cd89378795a4ca337b3264689dca497ba2fa")]
238+
[InlineData(128, "ce7daa4723c4f880fb44c2809821fc2183b46f0c32084620", "155425f12109cd89378795a4ca337b32")]
239+
[InlineData(192, "ce7daa4723c4f880fb44c2809821fc2183b46f0c32084620", "155425f12109cd89378795a4ca337b32155425f12109cd89")]
240+
public static void TripleDESRoundTripPKCS7ECB(int keySize, string expectedCipherHex, string keyHex)
190241
{
191-
byte[] key = "155425f12109cd89378795a4ca337b3264689dca497ba2fa".HexToByteArray();
242+
byte[] key = keyHex.HexToByteArray();
192243

193244
using (TripleDES alg = TripleDESFactory.Create())
194245
{
195246
alg.Key = key;
247+
Assert.Equal(keySize, alg.KeySize);
248+
196249
alg.Padding = PaddingMode.PKCS7;
197250
alg.Mode = CipherMode.ECB;
198251

199252
byte[] plainText = "5bd3c4e16a723a17ac60dd0efdb158e269cddfd0fa".HexToByteArray();
200253
byte[] cipher = alg.Encrypt(plainText);
201-
byte[] expectedCipher = "7b8d982ee0c14821daf1b8cf4e407c2eb328627b696ac36e".HexToByteArray();
254+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
202255
Assert.Equal<byte>(expectedCipher, cipher);
203256

204257
byte[] decrypted = alg.Decrypt(cipher);
@@ -207,22 +260,27 @@ public static void TripleDESRoundTrip192BitsPKCS7ECB()
207260
}
208261
}
209262

210-
[Fact]
211-
public static void TripleDESRoundTrip192BitsPKCS7CBC()
263+
[Theory]
264+
[InlineData(192, "446f57875e107702afde16b57eaf250b87b8110bef29af89", "6b42da08f93e819fbd26fce0785b0eec3d0cb6bfa053c505")]
265+
[InlineData(128, "ebf995606ceceddf5c90a7302521bc1f6d31f330969cb768", "6b42da08f93e819fbd26fce0785b0eec")]
266+
[InlineData(192, "ebf995606ceceddf5c90a7302521bc1f6d31f330969cb768", "6b42da08f93e819fbd26fce0785b0eec6b42da08f93e819f")]
267+
public static void TripleDESRoundTripPKCS7CBC(int keySize, string expectedCipherHex, string keyHex)
212268
{
213-
byte[] key = "6b42da08f93e819fbd26fce0785b0eec3d0cb6bfa053c505".HexToByteArray();
269+
byte[] key = keyHex.HexToByteArray();
214270
byte[] iv = "8fc67ce5e7f28cde".HexToByteArray();
215271

216272
using (TripleDES alg = TripleDESFactory.Create())
217273
{
218274
alg.Key = key;
275+
Assert.Equal(keySize, alg.KeySize);
276+
219277
alg.IV = iv;
220278
alg.Padding = PaddingMode.PKCS7;
221279
alg.Mode = CipherMode.CBC;
222280

223281
byte[] plainText = "e867f915e275eab27d6951165d26dec6dd0acafcfc".HexToByteArray();
224282
byte[] cipher = alg.Encrypt(plainText);
225-
byte[] expectedCipher = "446f57875e107702afde16b57eaf250b87b8110bef29af89".HexToByteArray();
283+
byte[] expectedCipher = expectedCipherHex.HexToByteArray();
226284
Assert.Equal<byte>(expectedCipher, cipher);
227285

228286
byte[] decrypted = alg.Decrypt(cipher);

src/System.Security.Cryptography.Algorithms/ref/System.Security.Cryptography.Algorithms.cs

-2
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,6 @@ public abstract partial class TripleDES : System.Security.Cryptography.Symmetric
601601
{
602602
protected TripleDES() { }
603603
public override byte[] Key { get { throw null; } set { } }
604-
public override System.Security.Cryptography.KeySizes[] LegalBlockSizes { get { throw null; } }
605-
public override System.Security.Cryptography.KeySizes[] LegalKeySizes { get { throw null; } }
606604
public static new System.Security.Cryptography.TripleDES Create() { throw null; }
607605
public static new System.Security.Cryptography.TripleDES Create(string str) { throw null; }
608606
public static bool IsWeakKey(byte[] rgbKey) { throw null; }

src/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/TripleDesImplementation.cs

+10-10
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,6 @@ internal sealed partial class TripleDesImplementation : TripleDES
1313
private const int BitsPerByte = 8;
1414
private static readonly RandomNumberGenerator s_rng = RandomNumberGenerator.Create();
1515

16-
public override KeySizes[] LegalKeySizes
17-
{
18-
get
19-
{
20-
// CNG does not support 128-bit keys.
21-
// Only support 192-bit keys on all platforms for simplicity.
22-
return new KeySizes[] { new KeySizes(minSize: 3 * 64, maxSize: 3 * 64, skipSize: 0) };
23-
}
24-
}
25-
2616
public override ICryptoTransform CreateDecryptor()
2717
{
2818
return CreateTransform(Key, IV, encrypting: false);
@@ -75,6 +65,16 @@ private ICryptoTransform CreateTransform(byte[] rgbKey, byte[] rgbIV, bool encry
7565
throw new ArgumentException(SR.Cryptography_InvalidIVSize, nameof(rgbIV));
7666
}
7767

68+
if (rgbKey.Length == 16)
69+
{
70+
// Some platforms do not support Two-Key Triple DES, so manually support it here.
71+
// Two-Key Triple DES contains two 8-byte keys {K1}{K2} with {K1} appended to make {K1}{K2}{K1}.
72+
byte[] newkey = new byte[24];
73+
Array.Copy(rgbKey, 0, newkey, 0, 16);
74+
Array.Copy(rgbKey, 0, newkey, 16, 8);
75+
rgbKey = newkey;
76+
}
77+
7878
return CreateTransformCore(Mode, Padding, rgbKey, rgbIV, BlockSize / BitsPerByte, encrypting);
7979
}
8080
}

src/System.Security.Cryptography.Cng/ref/System.Security.Cryptography.Cng.cs

-1
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,6 @@ public TripleDESCng(string keyName, System.Security.Cryptography.CngProvider pro
339339
public TripleDESCng(string keyName, System.Security.Cryptography.CngProvider provider, System.Security.Cryptography.CngKeyOpenOptions openOptions) { }
340340
public override byte[] Key { get { throw null; } set { } }
341341
public override int KeySize { get { throw null; } set { } }
342-
public override System.Security.Cryptography.KeySizes[] LegalKeySizes { get { throw null; } }
343342
public override System.Security.Cryptography.ICryptoTransform CreateDecryptor() { throw null; }
344343
public override System.Security.Cryptography.ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) { throw null; }
345344
public override System.Security.Cryptography.ICryptoTransform CreateEncryptor() { throw null; }

src/System.Security.Cryptography.Cng/src/Internal/Cryptography/CngSymmetricAlgorithmCore.cs

+2
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ private ICryptoTransform CreateCryptoTransform(byte[] rgbKey, byte[] rgbIV, bool
151151
// is correct, and detached from the input parameter.
152152
byte[] iv = _outer.Mode.GetCipherIv(rgbIV).CloneByteArray();
153153

154+
key = _outer.PreprocessKey(key);
155+
154156
return CreateEphemeralCryptoTransformCore(key, iv, encrypting);
155157
}
156158

src/System.Security.Cryptography.Cng/src/Internal/Cryptography/ICngSymmetricAlgorithm.cs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ internal interface ICngSymmetricAlgorithm
3030
bool IsWeakKey(byte[] key);
3131
SafeAlgorithmHandle GetEphemeralModeHandle();
3232
string GetNCryptAlgorithmIdentifier();
33+
byte[] PreprocessKey(byte[] key);
3334
}
3435
}
3536

src/System.Security.Cryptography.Cng/src/System/Security/Cryptography/AesCng.cs

+5
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ string ICngSymmetricAlgorithm.GetNCryptAlgorithmIdentifier()
116116
return Interop.NCrypt.NCRYPT_AES_ALGORITHM;
117117
}
118118

119+
byte[] ICngSymmetricAlgorithm.PreprocessKey(byte[] key)
120+
{
121+
return key;
122+
}
123+
119124
private CngSymmetricAlgorithmCore _core;
120125
}
121126
}

src/System.Security.Cryptography.Cng/src/System/Security/Cryptography/TripleDESCng.cs

+15-9
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,6 @@ public override void GenerateIV()
9494
_core.GenerateIV();
9595
}
9696

97-
public override KeySizes[] LegalKeySizes
98-
{
99-
get
100-
{
101-
// CNG does not support 128-bit keys.
102-
return new KeySizes[] { new KeySizes(minSize: 3 * 64, maxSize: 3 * 64, skipSize: 0) };
103-
}
104-
}
105-
10697
protected override void Dispose(bool disposing)
10798
{
10899
base.Dispose(disposing);
@@ -126,6 +117,21 @@ string ICngSymmetricAlgorithm.GetNCryptAlgorithmIdentifier()
126117
return Interop.NCrypt.NCRYPT_3DES_ALGORITHM;
127118
}
128119

120+
byte[] ICngSymmetricAlgorithm.PreprocessKey(byte[] key)
121+
{
122+
if (key.Length == 16)
123+
{
124+
// Cng does not support Two-Key Triple DES, so manually support it here for consistency with System.Security.Cryptography.Algorithms.
125+
// Two-Key Triple DES contains two 8-byte keys {K1}{K2} with {K1} appended to make {K1}{K2}{K1}.
126+
byte[] newkey = new byte[24];
127+
Array.Copy(key, 0, newkey, 0, 16);
128+
Array.Copy(key, 0, newkey, 16, 8);
129+
return newkey;
130+
}
131+
132+
return key;
133+
}
134+
129135
private CngSymmetricAlgorithmCore _core;
130136
}
131137
}

0 commit comments

Comments
 (0)