/// <summary> /// Calculate a pairwise transit key /// </summary> /// <param name="pmk">Pairwise master key</param> /// <param name="stmac">Station MAC address</param> /// <param name="bssid">BSSID</param> /// <param name="snonce">S-nonce from EAPOL</param> /// <param name="anonce">A-nonce from EAPOL</param> /// <returns>Pairwise transit key</returns> public static byte[] CalculatePTK( byte[] pmk, byte[] stmac, byte[] bssid, byte[] snonce, byte[] anonce /*, int keyVer*/) { var pke = new byte[100]; var ptk = new byte[80]; using (var ms = new MemoryStream(pke)) { using (var bw = new BinaryWriter(ms)) { bw.Write( new byte[] { 0x50, 0x61, 0x69, 0x72, 0x77, 0x69, 0x73, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x65, 0x78, 0x70, 0x61, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0 }); /* Literally the string Pairwise key expansion, with a trailing 0*/ if (CFunctions.memcmp(stmac, bssid) < 0) { bw.Write(stmac); bw.Write(bssid); } else { bw.Write(bssid); bw.Write(stmac); } if (CFunctions.memcmp(snonce, anonce) < 0) { bw.Write(snonce); bw.Write(anonce); } else { bw.Write(anonce); bw.Write(snonce); } bw.Write((byte)0); // Will be swapped out on each round in the loop below } } for (byte i = 0; i < 4; i++) { pke[99] = i; var hmacsha1 = new HMACSHA1(pmk); var hash = hmacsha1.ComputeHash(pke); hash.CopyTo(ptk, i * 20); } return(ptk); }
/// <summary> /// Decrypt a packet encrypted with CCMP /// </summary> /// <param name="encryptedPacket">The encrypted packet</param> /// <param name="temporalKey">The temporal key</param> /// <param name="headerMode">Mode in which headers should be handled</param> /// <param name="decrypted">Decrypted packet</param> /// <returns>True if successful, false if not</returns> /// <remarks>Ported from airdecap-ng</remarks> public static bool TryDecryptCCMP( IEEE802_11 encryptedPacket, byte[] temporalKey, DecryptionHeaderMode headerMode, out IPacket decrypted) { if (temporalKey.Length != 16) { // throw new ArgumentException("temporalKey must be 16 bytes"); decrypted = null; return(false); } var decryptedBytes = encryptedPacket.ToArray(); int z, data_len, blocks, last, offset; bool is_a4, is_qos; var B0 = new byte[16]; var B = new byte[16]; var MIC = new byte[16]; var PacketNumber = new byte[6]; var AdditionalAuthData = new byte[32]; is_a4 = (decryptedBytes[1] & 3) == 3; is_qos = (decryptedBytes[0] & 0x8C) == 0x88; z = 24 + 6 * (is_a4 ? 1 : 0); z += 2 * (is_qos ? 1 : 0); PacketNumber[0] = decryptedBytes[z + 7]; PacketNumber[1] = decryptedBytes[z + 6]; PacketNumber[2] = decryptedBytes[z + 5]; PacketNumber[3] = decryptedBytes[z + 4]; PacketNumber[4] = decryptedBytes[z + 1]; PacketNumber[5] = decryptedBytes[z + 0]; data_len = decryptedBytes.Length - z - 8 - 8; B0[0] = 0x59; B0[1] = 0; Array.Copy(decryptedBytes, 10, B0, 2, 6); Array.Copy(PacketNumber, 0, B0, 8, 6); B0[14] = (byte)((data_len >> 8) & 0xFF); B0[15] = (byte)(data_len & 0xFF); AdditionalAuthData[2] = (byte)(decryptedBytes[0] & 0x8F); AdditionalAuthData[3] = (byte)(decryptedBytes[1] & 0xC7); Array.Copy(decryptedBytes, 4, AdditionalAuthData, 4, 3 * 6); AdditionalAuthData[22] = (byte)(decryptedBytes[22] & 0x0F); if (is_a4) { Array.Copy(decryptedBytes, 24, AdditionalAuthData, 24, 6); if (is_qos) { AdditionalAuthData[30] = (byte)(decryptedBytes[z - 2] & 0x0F); AdditionalAuthData[31] = 0; B0[1] = AdditionalAuthData[30]; AdditionalAuthData[1] = 22 + 2 + 6; } else { AdditionalAuthData[30] = 0; AdditionalAuthData[31] = 0; B0[1] = 0; AdditionalAuthData[1] = 22 + 6; } } else { if (is_qos) { AdditionalAuthData[24] = (byte)(decryptedBytes[z - 2] & 0x0F); AdditionalAuthData[25] = 0; B0[1] = AdditionalAuthData[24]; AdditionalAuthData[1] = 22 + 2; } else { AdditionalAuthData[24] = 0; AdditionalAuthData[25] = 0; B0[1] = 0; AdditionalAuthData[1] = 22; } } using (var aesFactory = Aes.Create()) { aesFactory.Mode = CipherMode.ECB; aesFactory.Key = temporalKey; var aes = aesFactory.CreateEncryptor(); aes.TransformBlock(B0, 0, B0.Length, MIC, 0); CFunctions.XOR(MIC, 0, AdditionalAuthData, 16); aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0); CFunctions.XOR(MIC, 0, AdditionalAuthData.Skip(16).ToArray(), 16); aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0); B0[0] &= 0x07; B0[14] = 0; B0[15] = 0; aes.TransformBlock(B0, 0, B0.Length, B, 0); CFunctions.XOR(decryptedBytes, decryptedBytes.Length - 8, B, 8); blocks = (data_len + 16 - 1) / 16; last = data_len % 16; offset = z + 8; for (var i = 1; i <= blocks; i++) { var n = last > 0 && i == blocks ? last : 16; B0[14] = (byte)((i >> 8) & 0xFF); B0[15] = (byte)(i & 0xFF); aes.TransformBlock(B0, 0, B0.Length, B, 0); CFunctions.XOR(decryptedBytes, offset, B, n); CFunctions.XOR(MIC, 0, decryptedBytes.Skip(offset).ToArray(), n); aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0); offset += n; } } switch (headerMode) { case DecryptionHeaderMode.Removed: { decryptedBytes[1] &= 191; // Remove the protected bit var decryptedBytesList = decryptedBytes.ToList(); decryptedBytesList.RemoveRange(34 - 8, 8); // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing) IEEE802_11 decryptedWithHeader; if (IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decryptedWithHeader)) { decrypted = decryptedWithHeader.Payload; // Remove the altered 802.11 header } else { decrypted = null; return(false); } break; } case DecryptionHeaderMode.Modified: { decryptedBytes[1] &= 191; // Remove the protected bit var decryptedBytesList = decryptedBytes.ToList(); decryptedBytesList.RemoveRange(34 - 8, 8); // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing) if (!IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decrypted)) { return(false); } break; } case DecryptionHeaderMode.Untouched: if (!IEEE802_11Factory.Instance.TryParse(decryptedBytes.ToArray(), out decrypted)) { return(false); } break; default: decrypted = null; return(false); } return(CFunctions.memcmp(decryptedBytes.Skip(offset).Take(8).ToArray(), MIC.Take(8).ToArray()) == 0); }