Merge pull request #2832 from razzfazz/handle_eintr
[mono.git] / mcs / class / System.Security / System.Security.Cryptography.Xml / SymmetricKeyWrap.cs
1 //
2 // SymmetricKeyWrap.cs - Implements symmetric key wrap algorithms
3 //
4 // Author:
5 //      Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2004
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31
32 using System.IO;
33 using System.Security.Cryptography;
34
35 namespace System.Security.Cryptography.Xml { 
36
37         internal class SymmetricKeyWrap {
38
39                 public SymmetricKeyWrap ()
40                 {
41                 }
42
43                 public static byte[] AESKeyWrapEncrypt (byte[] rgbKey, byte[] rgbWrappedKeyData)
44                 {
45                         SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael");
46
47                         // Apparently no one felt the need to document that this requires Electronic Codebook mode.
48                         symAlg.Mode = CipherMode.ECB;
49
50                         // This was also not documented anywhere.
51                         symAlg.IV = new byte [16] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
52         
53                         ICryptoTransform transform = symAlg.CreateEncryptor (rgbKey, symAlg.IV);
54
55                         int N = rgbWrappedKeyData.Length / 8;
56                         byte[] A;
57                         byte[] B = new Byte [16];
58                         byte [] C = new byte [8 * (N + 1)];
59
60                         // 1. if N is 1:
61                         //       B = AES(K)enc(0xA6A6A6A6A6A6A6A6|P(1))
62                         //       C(0) = MSB(B)
63                         //       C(1) = LSB(B)
64                         if (N == 1) {
65                                 A = new byte [8] {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6};
66                                 transform.TransformBlock (Concatenate (A, rgbWrappedKeyData), 0, 16, B, 0);
67                                 Buffer.BlockCopy (MSB(B), 0, C, 0, 8);
68                                 Buffer.BlockCopy (LSB(B), 0, C, 8, 8);
69                         } else {
70                                 // if N > 1, perform the following steps:
71                                 // 2. Initialize variables:
72                                 //       Set A to 0xA6A6A6A6A6A6A6A6
73                                 //       For i = 1 to N,
74                                 //          R(i) = P(i)
75                                 A = new byte [8] {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6};
76         
77                                 byte[][] R = new byte [N + 1][];
78                                 for (int i = 1; i <= N; i += 1) {
79                                         R [i] = new byte [8];
80                                         Buffer.BlockCopy (rgbWrappedKeyData, 8 * (i - 1), R [i], 0, 8);
81                                 }
82
83                                 // 3. Calculate intermediate values:
84                                 //       For j = 0 to 5
85                                 //          For i = 1 to N
86                                 //             t = i + j * N
87                                 //             B = AES(K)enc(A|R(i))
88                                 //             A = XOR(t, MSB(B))
89                                 //             R(i) = LSB(B)
90
91                                 for (int j = 0; j <= 5; j += 1) {
92                                         for (int i = 1; i <= N; i += 1) {
93                                                 transform.TransformBlock (Concatenate (A, R [i]), 0, 16, B, 0);
94         
95                                                 // Yawn.  It was nice of those at NIST to document how exactly we should XOR 
96                                                 // an integer value with a byte array.  Not.
97                                                 byte[] T = BitConverter.GetBytes ((long) (N * j + i));
98
99                                                 // This is nice.
100                                                 if (BitConverter.IsLittleEndian)
101                                                         Array.Reverse (T);
102
103                                                 A = Xor (T, MSB(B));
104                                                 R [i] = LSB (B);
105                                         }
106                                 }
107
108                                 // 4. Output the results:
109                                 //       Set C(0) = A
110                                 //       For i = 1 to N
111                                 //          C(i) = R(i)
112                                 Buffer.BlockCopy (A, 0, C, 0, 8);
113                                 for (int i = 1; i <= N; i += 1)
114                                         Buffer.BlockCopy (R [i], 0, C, 8 * i, 8);
115                         }
116                         return C;
117                 }
118
119                 public static byte[] AESKeyWrapDecrypt (byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData)
120                 {
121                         SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael");
122                         symAlg.Mode = CipherMode.ECB;
123                         symAlg.Key = rgbKey;
124
125                         int N = ( rgbEncryptedWrappedKeyData.Length / 8 ) - 1;
126
127                         // From RFC 3394 - Advanced Encryption Standard (AES) Key Wrap Algorithm
128                         //
129                         // Inputs: Ciphertext, (n+1) 64-bit values (C0, C1, ..., Cn), and Key, K (the KEK)
130                         // Outputs: Plaintext, n 64-bit values (P1, P2, ..., Pn)
131                         //
132                         // 1. Initialize variables.
133                         //    Set A = C[0] 
134
135                         byte[] A = new byte [8];
136                         Buffer.BlockCopy (rgbEncryptedWrappedKeyData, 0, A, 0, 8);
137
138                         //    For i = 1 to n
139                         //    R[i] = C[i]
140
141                         byte[] R = new byte [N * 8];
142                         Buffer.BlockCopy (rgbEncryptedWrappedKeyData, 8, R, 0, rgbEncryptedWrappedKeyData.Length - 8);
143
144                         // 2. Compute intermediate values.
145                         //    For j = 5 to 0
146                         //       For i = n to 1
147                         //          B = AES-1(K, (A^t) | R[i]) where t = n*j+i
148                         //          A = MSB (64,B)
149                         //          R[i] = LSB (64,B)
150
151                         ICryptoTransform transform = symAlg.CreateDecryptor ();
152
153                         for (int j = 5; j >= 0; j -= 1) {
154                                 for (int i = N; i >= 1; i -= 1) {
155                                         byte[] T = BitConverter.GetBytes ((long) N * j + i);
156                                         if (BitConverter.IsLittleEndian)
157                                                 Array.Reverse (T);
158
159                                         byte[] B = new Byte [16];
160                                         byte[] r = new Byte [8];
161                                         Buffer.BlockCopy (R, 8 * (i - 1), r, 0, 8);
162                                         byte[] ciphertext = Concatenate (Xor (A, T), r);
163                                         transform.TransformBlock (ciphertext, 0, 16, B, 0);
164                                         A = MSB (B);
165                                         Buffer.BlockCopy (LSB (B), 0, R, 8 * (i - 1), 8);
166                                 }
167                         }
168
169                         // 3. Output results
170                         //    If A is an appropriate initial value
171                         //    Then
172                         //       For i = 1 to n
173                         //          P[i] = R[i]
174                         //    Else
175                         //       Return an error
176
177                         return R;
178                 }
179
180                 public static byte[] TripleDESKeyWrapEncrypt (byte[] rgbKey, byte[] rgbWrappedKeyData)
181                 {
182                         SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("TripleDES");
183
184                         // Algorithm from http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap
185                         // The following algorithm wraps (encrypts) a key (the wrapped key, WK) under a TRIPLEDES
186                         // key-encryption-key (KEK) as adopted from [CMS-Algorithms].
187
188                         // 1. Represent the key being wrapped as an octet sequence. If it is a TRIPLEDES key, 
189                         //    this is 24 octets (192 bits) with odd parity bit as the bottom bit of each octet.
190
191                         // rgbWrappedKeyData is the key being wrapped.
192
193                         // 2. Compute the CMS key checksum (Section 5.6.1) call this CKS.
194
195                         byte[] cks = ComputeCMSKeyChecksum (rgbWrappedKeyData);
196
197                         // 3. Let WKCKS = WK || CKS, where || is concatenation.
198
199                         byte[] wkcks = Concatenate (rgbWrappedKeyData, cks);
200
201                         // 4. Generate 8 random octets and call this IV.
202                         symAlg.GenerateIV ();
203
204                         // 5. Encrypt WKCKS in CBC mode using KEK as the key and IV as the initialization vector.
205                         //    Call the results TEMP1.
206
207                         symAlg.Mode = CipherMode.CBC;
208                         symAlg.Padding = PaddingMode.None;
209                         symAlg.Key = rgbKey;
210                         byte[] temp1 = Transform (wkcks, symAlg.CreateEncryptor ());
211
212                         // 6. Let TEMP2 = IV || TEMP1.
213
214                         byte[] temp2 = Concatenate (symAlg.IV, temp1);
215
216                         // 7. Reverse the order of the octets in TEMP2 and call the result TEMP3.
217
218                         Array.Reverse (temp2); // TEMP3 is TEMP2
219
220                         // 8. Encrypt TEMP3 in CBC mode using the KEK and an initialization vector of 0x4adda22c79e82105. 
221                         //    The resulting cipher text is the desired result.  It is 40 octets long if a 168 bit key
222                         //    is being wrapped.
223
224                         symAlg.IV = new Byte [8] {0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05};
225
226                         byte[] rtnval = Transform (temp2, symAlg.CreateEncryptor ());
227
228                         return rtnval;
229                 }
230
231                 public static byte[] TripleDESKeyWrapDecrypt (byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData)
232                 {
233                         SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("TripleDES");
234
235                         // Algorithm from http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap
236                         // The following algorithm unwraps (decrypts) a key as adopted from [CMS-Algorithms].
237
238                         // 1. Check the length of the cipher text is reasonable given the key type.  It must be
239                         //    40 bytes for a 168 bit key and either 32, 40, or 48 bytes for a 128, 192, or 256 bit
240                         //    key. If the length is not supported or inconsistent with the algorithm for which the
241                         //    key is intended, return error.
242
243                         // 2. Decrypt the cipher text with TRIPLEDES in CBC mode using the KEK and an initialization
244                         //    vector (IV) of 0x4adda22c79e82105.  Call the output TEMP3.
245
246                         symAlg.Mode = CipherMode.CBC;
247                         symAlg.Padding = PaddingMode.None;
248                         symAlg.Key = rgbKey;
249                         symAlg.IV = new Byte [8] {0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05};
250
251                         byte[] temp3 = Transform (rgbEncryptedWrappedKeyData, symAlg.CreateDecryptor ());
252
253                         // 3. Reverse the order of the octets in TEMP3 and call the result TEMP2.
254
255                         Array.Reverse (temp3); // TEMP2 is TEMP3.
256
257                         // 4. Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
258
259                         byte[] temp1 = new Byte [temp3.Length - 8];
260                         byte[] iv = new Byte [8];
261
262                         Buffer.BlockCopy (temp3, 0, iv, 0, 8);
263                         Buffer.BlockCopy (temp3, 8, temp1, 0, temp1.Length);
264
265                         // 5. Decrypt TEMP1 using TRIPLEDES in CBC mode using the KEK and the IV found in the previous step.
266                         //    Call the result WKCKS.
267
268                         symAlg.IV = iv;
269                         byte[] wkcks = Transform (temp1, symAlg.CreateDecryptor ());
270
271                         // 6. Decompose WKCKS.  CKS is the last 8 octets and WK, the wrapped key, are those octets before
272                         //    the CKS.
273
274                         byte[] cks = new byte [8];
275                         byte[] wk = new byte [wkcks.Length - 8];
276
277                         Buffer.BlockCopy (wkcks, 0, wk, 0, wk.Length);
278                         Buffer.BlockCopy (wkcks, wk.Length, cks, 0, 8);
279
280                         // 7. Calculate the CMS key checksum over the WK and compare with the CKS extracted in the above
281                         //    step. If they are not equal, return error.
282
283                         // 8. WK is the wrapped key, now extracted for use in data decryption.
284                         return wk;
285                 }
286
287                 private static byte[] Transform (byte[] data, ICryptoTransform t)
288                 {
289                         MemoryStream output = new MemoryStream ();
290                         CryptoStream crypto = new CryptoStream (output, t, CryptoStreamMode.Write);
291
292                         crypto.Write (data, 0, data.Length);
293                         crypto.FlushFinalBlock ();
294
295                         byte[] result = output.ToArray ();
296                         
297                         output.Close ();
298                         crypto.Close ();
299
300                         return result; 
301                 }
302
303                 private static byte[] ComputeCMSKeyChecksum (byte[] data)
304                 {
305                         byte[] hash = HashAlgorithm.Create ("SHA1").ComputeHash (data);
306                         byte[] output = new byte [8];
307
308                         Buffer.BlockCopy (hash, 0, output, 0, 8);
309
310                         return output;
311                 }
312
313                 private static byte[] Concatenate (byte[] buf1, byte[] buf2)
314                 {
315                         byte[] output = new byte [buf1.Length + buf2.Length];
316                         Buffer.BlockCopy (buf1, 0, output, 0, buf1.Length);
317                         Buffer.BlockCopy (buf2, 0, output, buf1.Length, buf2.Length);
318                         return output;
319                 }
320
321                 private static byte[] MSB (byte[] input)
322                 {
323                         return MSB (input, 8);
324                 }
325
326                 private static byte[] MSB (byte[] input, int bytes)
327                 {
328                         byte[] output = new byte [bytes];
329                         Buffer.BlockCopy (input, 0, output, 0, bytes);
330                         return output;
331                 }
332
333                 private static byte[] LSB (byte[] input)
334                 {
335                         return LSB (input, 8);
336                 }
337
338                 private static byte[] LSB (byte[] input, int bytes)
339                 {
340                         byte[] output = new byte [bytes];
341                         Buffer.BlockCopy (input, bytes, output, 0, bytes);
342                         return output;
343                 }
344
345                 private static byte[] Xor (byte[] x, byte[] y)
346                 {
347                         // This should *not* happen.
348                         if (x.Length != y.Length)
349                                 throw new CryptographicException ("Error performing Xor: arrays different length.");
350
351                         byte[] output = new byte [x.Length];
352                         for (int i = 0; i < x.Length; i += 1)
353                                 output [i] = (byte) (x [i] ^ y [i]);
354                         return output;
355                 }
356
357 /*              private static byte[] Xor (byte[] x, int n)
358                 {
359                         byte[] output = new Byte [x.Length];
360                         for (int i = 0; i < x.Length; i += 1)
361                                 output [i] = (byte) ((int) x [i] ^ n);
362                         return output;
363                 }*/
364         }
365 }
366