// // SymmetricKeyWrap.cs - Implements symmetric key wrap algorithms // // Author: // Tim Coleman (tim@timcoleman.com) // // Copyright (C) Tim Coleman, 2004 // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // #if NET_2_0 using System.IO; using System.Security.Cryptography; namespace System.Security.Cryptography.Xml { internal class SymmetricKeyWrap { public SymmetricKeyWrap () { } public static byte[] AESKeyWrapEncrypt (byte[] rgbKey, byte[] rgbWrappedKeyData) { SymmetricAlgorithm symAlg = SymmetricAlgorithm.Create ("Rijndael"); // Apparently no one felt the need to document that this requires Electronic Codebook mode. symAlg.Mode = CipherMode.ECB; // This was also not documented anywhere. symAlg.IV = new byte [16] {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; ICryptoTransform transform = symAlg.CreateEncryptor (rgbKey, symAlg.IV); int N = rgbWrappedKeyData.Length / 8; byte[] A; byte[] B = new Byte [16]; byte [] C = new byte [8 * (N + 1)]; // 1. if N is 1: // B = AES(K)enc(0xA6A6A6A6A6A6A6A6|P(1)) // C(0) = MSB(B) // C(1) = LSB(B) if (N == 1) { A = new byte [8] {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}; transform.TransformBlock (Concatenate (A, rgbWrappedKeyData), 0, 16, B, 0); Buffer.BlockCopy (MSB(B), 0, C, 0, 8); Buffer.BlockCopy (LSB(B), 0, C, 8, 8); } else { // if N > 1, perform the following steps: // 2. Initialize variables: // Set A to 0xA6A6A6A6A6A6A6A6 // For i = 1 to N, // R(i) = P(i) A = new byte [8] {0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6}; byte[][] R = new byte [N + 1][]; for (int i = 1; i <= N; i += 1) { R [i] = new byte [8]; Buffer.BlockCopy (rgbWrappedKeyData, 8 * (i - 1), R [i], 0, 8); } // 3. Calculate intermediate values: // For j = 0 to 5 // For i = 1 to N // t = i + j * N // B = AES(K)enc(A|R(i)) // A = XOR(t, MSB(B)) // R(i) = LSB(B) for (int j = 0; j <= 5; j += 1) { for (int i = 1; i <= N; i += 1) { transform.TransformBlock (Concatenate (A, R [i]), 0, 16, B, 0); // Yawn. It was nice of those at NIST to document how exactly we should XOR // an integer value with a byte array. Not. byte[] T = BitConverter.GetBytes ((long) (N * j + i)); // This is nice. if (BitConverter.IsLittleEndian) Array.Reverse (T); A = Xor (T, MSB(B)); R [i] = LSB (B); } } // 4. Output the results: // Set C(0) = A // For i = 1 to N // C(i) = R(i) Buffer.BlockCopy (A, 0, C, 0, 8); for (int i = 1; i <= N; i += 1) Buffer.BlockCopy (R [i], 0, C, 8 * i, 8); } return C; } [MonoTODO] public static byte[] AESKeyWrapDecrypt (byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData) { throw new NotImplementedException (); } [MonoTODO] public static byte[] TripleDESKeyWrapEncrypt (byte[] rgbKey, byte[] rgbWrappedKeyData) { throw new NotImplementedException (); } [MonoTODO] public static byte[] TripleDESKeyWrapDecrypt (byte[] rgbKey, byte[] rgbEncryptedWrappedKeyData) { throw new NotImplementedException (); } private static byte[] Transform (byte[] data, ICryptoTransform t, bool flush) { MemoryStream output = new MemoryStream (); CryptoStream crypto = new CryptoStream (output, t, CryptoStreamMode.Write); crypto.Write (data, 0, data.Length); byte[] buf; if (flush) { crypto.Close (); output.Close (); buf = output.ToArray (); } else { buf = output.ToArray (); crypto.Close (); output.Close (); } return buf; } private static byte[] ComputeCMSKeyChecksum (byte[] data) { byte[] hash = HashAlgorithm.Create ("SHA1").ComputeHash (data); byte[] output = new byte [8]; Buffer.BlockCopy (hash, 0, output, 0, 8); return output; } private static byte[] Concatenate (byte[] buf1, byte[] buf2) { byte[] output = new byte [buf1.Length + buf2.Length]; Buffer.BlockCopy (buf1, 0, output, 0, buf1.Length); Buffer.BlockCopy (buf2, 0, output, buf1.Length, buf2.Length); return output; } private static byte[] MSB (byte[] input) { byte[] output = new byte [8]; Buffer.BlockCopy (input, 0, output, 0, 8); return output; } private static byte[] LSB (byte[] input) { byte[] output = new byte [8]; Buffer.BlockCopy (input, 8, output, 0, 8); return output; } private static byte[] Xor (byte[] x, byte[] y) { // This should *not* happen. if (x.Length != y.Length) throw new CryptographicException ("Error performing Xor: arrays different length."); byte[] output = new byte [x.Length]; for (int i = 0; i < x.Length; i += 1) output [i] = (byte) (x [i] ^ y [i]); return output; } } } #endif