/// <summary> /// Unwrap an AES Key Wrapped-key /// </summary> /// <param name="wrapped">The full wrapped data, the length of a key + 8 bytes</param> /// <returns>The unwrapped key data, or a zero-length array if the unwrap was unsuccessful due to wrong key</returns> public byte[] Unwrap(ICrypto crypto, byte[] wrapped) { if (wrapped == null) { throw new ArgumentNullException("wrapped"); } if (crypto == null) { throw new ArgumentNullException("crypto"); } if (wrapped.Length % (crypto.BlockLength / 2) != 0) { throw new InternalErrorException("The length of the wrapped data must a multiple of half the algorithm block size."); } if (wrapped.Length < 24) { throw new InternalErrorException("The length of the wrapped data must be large enough to accommodate at least a 128-bit key."); } using (IKeyWrapTransform decryptor = crypto.CreateKeyWrapTransform(_salt, KeyWrapDirection.Decrypt)) { return(UnwrapInternal(wrapped, decryptor)); } }
private byte[] UnwrapInternal(byte[] wrapped, IKeyWrapTransform decryptor) { byte[] a = decryptor.A(); int halfBlockLength = decryptor.BlockLength / 2; int wrappedKeyLength = wrapped.Length - a.Length; wrapped = (byte[])wrapped.Clone(); byte[] block = new byte[decryptor.BlockLength]; // wrapped[0..7] contains the A (IV) of the Key Wrap algorithm, // the rest is 'Wrapped Key Data', R[1], ..., R[n]. We do the transform in-place. for (long j = _keyWrapIterations - 1; j >= 0; --j) { for (int i = wrappedKeyLength / halfBlockLength; i >= 1; --i) { long t = ((wrappedKeyLength / halfBlockLength) * j) + i; // MSB(B) = A XOR t Array.Copy(wrapped, 0, block, 0, halfBlockLength); switch (_mode) { case KeyWrapMode.Specification: block.Xor(0, t.GetBigEndianBytes(), 0, halfBlockLength); break; case KeyWrapMode.AxCrypt: block.Xor(0, t.GetLittleEndianBytes(), 0, halfBlockLength); break; } // LSB(B) = R[i] Array.Copy(wrapped, i * halfBlockLength, block, halfBlockLength, halfBlockLength); // B = AESD(K, X xor t | R[i]) where t = (n * j) + i byte[] b = decryptor.TransformBlock(block); // A = MSB(B) Array.Copy(b, 0, wrapped, 0, halfBlockLength); // R[i] = LSB(B) Array.Copy(b, halfBlockLength, wrapped, i * halfBlockLength, halfBlockLength); } } if (!wrapped.IsEquivalentTo(0, a, 0, a.Length)) { return(new byte[0]); } byte[] unwrapped = new byte[wrapped.Length - a.Length]; Array.Copy(wrapped, a.Length, unwrapped, 0, wrapped.Length - a.Length); return(unwrapped); }
public byte[] Wrap(ICrypto crypto, byte[] keyMaterial) { if (crypto == null) { throw new ArgumentNullException("crypto"); } if (keyMaterial == null) { throw new ArgumentNullException("keyMaterial"); } using (IKeyWrapTransform encryptor = crypto.CreateKeyWrapTransform(_salt, KeyWrapDirection.Encrypt)) { return(WrapInternal(keyMaterial, encryptor)); } }
private byte[] WrapInternal(byte[] keyMaterial, IKeyWrapTransform encryptor) { byte[] a = encryptor.A(); byte[] wrapped = new byte[keyMaterial.Length + a.Length]; a.CopyTo(wrapped, 0); Array.Copy(keyMaterial, 0, wrapped, a.Length, keyMaterial.Length); byte[] block = new byte[encryptor.BlockLength]; int halfBlockLength = encryptor.BlockLength / 2; // wrapped[0..halfBlockLength-1] contains the A (IV) of the Key Wrap algorithm, // the rest is 'Key Data'. We do the transform in-place. for (int j = 0; j < _keyWrapIterations; j++) { for (int i = 1; i <= keyMaterial.Length / halfBlockLength; i++) { // B = AESE(K, A | R[i]) Array.Copy(wrapped, 0, block, 0, halfBlockLength); Array.Copy(wrapped, i * halfBlockLength, block, halfBlockLength, halfBlockLength); byte[] b = encryptor.TransformBlock(block); // A = MSB64(B) XOR t where t = (n * j) + i long t = ((keyMaterial.Length / halfBlockLength) * j) + i; switch (_mode) { case KeyWrapMode.Specification: b.Xor(0, t.GetBigEndianBytes(), 0, halfBlockLength); break; case KeyWrapMode.AxCrypt: b.Xor(0, t.GetLittleEndianBytes(), 0, halfBlockLength); break; } Array.Copy(b, 0, wrapped, 0, halfBlockLength); // R[i] = LSB64(B) Array.Copy(b, halfBlockLength, wrapped, i * halfBlockLength, halfBlockLength); } } return(wrapped); }