using System; namespace NFC.Crypto { /// <summary> /// Key for DESFire Card /// </summary> public class CipherKey { #region Constructors /// <summary> /// Creates Key from Array /// </summary> /// <param name="key">Key</param> /// <param name="cipher">Cipher for Key</param> /// <param name="keyVersion">Version of Key</param> public CipherKey(byte[] key, CipherType cipher, byte keyVersion) { _Cipher = cipher; if (cipher == CipherType.AES && keyVersion < 0x10) { throw new ArgumentOutOfRangeException("KeyVersion is to low for AES Key (Minimum = 0x10)"); } _KeyVersion = keyVersion; if (!CheckKey(key, cipher)) { throw new ArgumentException("Key is not vaild for CipherType"); } if (cipher == CipherType.TDES || cipher == CipherType.TDES_2K || cipher == CipherType.TDES_3K) { _Key = SetKeyVersion(key, keyVersion); } else { _Key = key; } } /// <summary> /// Generates Empty Key /// </summary> /// <param name="cipher">Cipher for Key</param> /// <param name="keyVerion"></param> public CipherKey(CipherType cipher) { _Cipher = cipher; _Key = GenerateEmptyKey(cipher); if (cipher == CipherType.AES) { _KeyVersion = 0x10; } else { _KeyVersion = 0x00; } } #endregion #region Properties /// <summary> /// Key as Array /// </summary> public byte[] _Key { get; private set; } /// <summary> /// CipherType of Key /// </summary> public CipherType _Cipher { get; private set; } /// <summary> /// KeyVersion of Key /// For AES 0x10 is minimum /// </summary> public byte _KeyVersion { get; private set; } #endregion #region Methods /// <summary> /// Generate Empty Key for CipherType /// </summary> /// <param name="cipher">Type of Cipher</param> public byte[] GenerateEmptyKey(CipherType cipher) { uint size = GetKeySize(cipher); byte[] key = new byte[size]; for (int i = 0; i < size; i++) { key[i] = 0; } return key; } /// <summary> /// Check Key Array /// </summary> /// <param name="key">Key</param> /// <param name="cipher">Cipher Type of Key</param> public bool CheckKey(byte[] key, CipherType cipher) { if (key.Length != GetKeySize(cipher)) { return false; } else { return true; } } /// <summary> /// Get KeySize for CipherType /// </summary> /// <param name="cipher">Type of Cipher</param> public uint GetKeySize(CipherType cipher) { switch (cipher) { case CipherType.TDES: return 8; case CipherType.TDES_2K: return 16; case CipherType.TDES_3K: return 24; case CipherType.AES: return 16; default: throw new ArgumentOutOfRangeException("Unknown CipherType."); } } /// <summary> /// Set Key Version for DES/TDES Keys /// KeyVersion is stored in the LSBits of the first 8 Bytes /// Parity Bits are not used from DESFire Cars /// </summary> /// <param name="key"></param> /// <param name="version"></param> /// <returns></returns> public byte[] SetKeyVersion(byte[] key, byte version) { byte[] pow2 = new byte[] { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; byte[] new_key = new byte[key.Length]; key.CopyTo(new_key, 0); for (int i = 0; i < 8; i++) { if ((version & pow2[i]) > 0) { new_key[i] = (byte)(new_key[5] | 0x01); } else { new_key[i] = (byte)(new_key[5] & 0x7F); } } return new_key; } #endregion } }