KFDtool/sw/control/KFDtool.P25/Validator/FieldValidator.cs

353 lines
16 KiB
C#

using KFDtool.P25.Constant;
using KFDtool.Shared;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KFDtool.P25.Validator
{
public class FieldValidator
{
public static bool IsValidKeysetId(int keysetId)
{
/* TIA 102.AACA-A 10.3.11 */
if (keysetId < 0x01 || keysetId > 0xFF)
{
return false;
}
else
{
return true;
}
}
public static bool IsValidSln(int sln)
{
/* TIA 102.AACA-A 10.3.25 */
if (sln < 0 || sln > 65535)
{
return false;
}
else
{
return true;
}
}
public static bool IsValidKeyId(int keyId)
{
/* TIA 102.AACA-A 10.3.10 */
if (keyId < 0x0000 || keyId > 0xFFFF)
{
return false;
}
else
{
return true;
}
}
public static bool IsValidAlgorithmId(int algId)
{
/* TIA-102.BAAC-D 2.8 */
if (algId < 0x00 || algId > 0xFF)
{
return false;
}
else
{
return true;
}
}
public static bool IsValidSingleDesKeyParity(List<byte> key)
{
if (key.Count != 8)
{
throw new ArgumentOutOfRangeException();
}
bool result = true;
foreach (byte b in key)
{
bool set = Convert.ToBoolean(b & 0x01); // least significant bit is the parity bit
// from .NET 4.8 refsrc system\security\cryptography\utils.cs FixupKeyParity()
byte c = (byte)(b & 0xfe);
byte tmp1 = (byte)((c & 0xF) ^ (c >> 4));
byte tmp2 = (byte)((tmp1 & 0x3) ^ (tmp1 >> 2));
byte sumBitsMod2 = (byte)((tmp2 & 0x1) ^ (tmp2 >> 1));
bool calc = false;
if (sumBitsMod2 == 0)
{
calc = true;
}
if (set != calc) // parity bit is incorrect
{
return false;
}
}
return result;
}
public static Tuple<ValidateResult,string> KeyloadValidate(int keysetId, int sln, bool isKek, int keyId, int algId, List<byte> key)
{
if (!IsValidKeysetId(keysetId))
{
return Tuple.Create(ValidateResult.Error, "Keyset ID invalid - valid range 1 to 255 (dec), 0x01 to 0xFF (hex)");
}
if (!IsValidSln(sln))
{
return Tuple.Create(ValidateResult.Error, "SLN invalid - valid range 0 to 65535 (dec), 0x0000 to 0xFFFF (hex)");
}
if (!IsValidKeyId(keyId))
{
return Tuple.Create(ValidateResult.Error, "Key ID invalid - valid range 0 to 65535 (dec), 0x0000 to 0xFFFF (hex)");
}
if (!IsValidAlgorithmId(algId))
{
return Tuple.Create(ValidateResult.Error, "Algorithm ID invalid - valid range 0 to 255 (dec), 0x00 to 0xFF (hex)");
}
if (algId == (byte)AlgorithmId.CLEAR)
{
return Tuple.Create(ValidateResult.Error, "Algorithm ID 0x80 is reserved for clear operation");
}
else if (algId == (byte)AlgorithmId.ACCORDION ||
algId == (byte)AlgorithmId.BATON_ODD ||
algId == (byte)AlgorithmId.FIREFLY ||
algId == (byte)AlgorithmId.MAYFLY ||
algId == (byte)AlgorithmId.SAVILLE ||
algId == (byte)AlgorithmId.PADSTONE ||
algId == (byte)AlgorithmId.BATON_EVEN)
{
return Tuple.Create(ValidateResult.Warning, string.Format("Algorithm ID 0x{0:X2} is a Type 1 algorithm - no key validation has been performed", algId));
}
else if (algId == (byte)AlgorithmId.DESOFB || algId == (byte)AlgorithmId.DESXL)
{
if (key.Count != 8)
{
return Tuple.Create(ValidateResult.Error, string.Format("Key length invalid - expected 8 bytes, got {0} bytes", key.Count));
}
if (!IsValidSingleDesKeyParity(key))
{
return Tuple.Create(ValidateResult.Error, "Key parity invalid");
}
// des weak keys per NIST SP 800-67 Rev 2 3.3.2
List<List<byte>> weakKeys = new List<List<byte>>
{
// des weak keys (4)
new List<byte> { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
new List<byte> { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE },
new List<byte> { 0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1 },
new List<byte> { 0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E },
// des semi weak keys (12)
new List<byte> { 0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E },
new List<byte> { 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01 },
new List<byte> { 0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1 },
new List<byte> { 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01 },
new List<byte> { 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE },
new List<byte> { 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01 },
new List<byte> { 0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1 },
new List<byte> { 0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E },
new List<byte> { 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE },
new List<byte> { 0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E },
new List<byte> { 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE },
new List<byte> { 0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1 },
// des possibly weak keys (48)
new List<byte> { 0x01, 0x01, 0x1F, 0x1F, 0x01, 0x01, 0x0E, 0x0E },
new List<byte> { 0x01, 0x01, 0xE0, 0xE0, 0x01, 0x01, 0xF1, 0xF1 },
new List<byte> { 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE },
new List<byte> { 0x01, 0x1F, 0x1F, 0x01, 0x01, 0x0E, 0x0E, 0x01 },
new List<byte> { 0x01, 0x1F, 0xE0, 0xFE, 0x01, 0x0E, 0xF1, 0xFE },
new List<byte> { 0x01, 0x1F, 0xFE, 0xE0, 0x01, 0x0E, 0xFE, 0xF1 },
new List<byte> { 0x01, 0xE0, 0x1F, 0xFE, 0x01, 0xF1, 0x0E, 0xFE },
new List<byte> { 0xFE, 0x01, 0xE0, 0x1F, 0xFE, 0x01, 0xF1, 0x0E },
new List<byte> { 0x01, 0xE0, 0xE0, 0x01, 0x01, 0xF1, 0xF1, 0x01 },
new List<byte> { 0x01, 0xE0, 0xFE, 0x1F, 0x01, 0xF1, 0xFE, 0x0E },
new List<byte> { 0x01, 0xFE, 0x1F, 0xE0, 0x01, 0xFE, 0x0E, 0xF1 },
new List<byte> { 0x01, 0xFE, 0xE0, 0x1F, 0x01, 0xFE, 0xF1, 0x0E },
new List<byte> { 0x01, 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE, 0x01 },
new List<byte> { 0x1F, 0x01, 0x01, 0x1F, 0x0E, 0x01, 0x01, 0x0E },
new List<byte> { 0x1F, 0x01, 0xE0, 0xFE, 0x0E, 0x01, 0xF1, 0xFE },
new List<byte> { 0x1F, 0x01, 0xFE, 0xE0, 0x0E, 0x01, 0xFE, 0xF1 },
new List<byte> { 0x1F, 0x1F, 0x01, 0x01, 0x0E, 0x0E, 0x01, 0x01 },
new List<byte> { 0x1F, 0x1F, 0xE0, 0xE0, 0x0E, 0x0E, 0xF1, 0xF1 },
new List<byte> { 0x1F, 0x1F, 0xFE, 0xFE, 0x0E, 0x0E, 0xFE, 0xFE },
new List<byte> { 0x1F, 0xE0, 0x01, 0xFE, 0x0E, 0xF1, 0x01, 0xFE },
new List<byte> { 0x1F, 0xE0, 0xE0, 0x1F, 0x0E, 0xF1, 0xF1, 0x0E },
new List<byte> { 0x1F, 0xE0, 0xFE, 0x01, 0x0E, 0xF1, 0xFE, 0x01 },
new List<byte> { 0x1F, 0xFE, 0x01, 0xE0, 0x0E, 0xFE, 0x01, 0xF1 },
new List<byte> { 0x1F, 0xFE, 0xE0, 0x01, 0x0E, 0xFE, 0xF1, 0x01 },
new List<byte> { 0x1F, 0xFE, 0xFE, 0x1F, 0x0E, 0xFE, 0xFE, 0x0E },
new List<byte> { 0x1F, 0xFE, 0xFE, 0x1F, 0x0E, 0xFE, 0xFE, 0x0E },
new List<byte> { 0xE0, 0x01, 0x1F, 0xFE, 0xF1, 0x01, 0x0E, 0xFE },
new List<byte> { 0xE0, 0x01, 0xFE, 0x1F, 0xF1, 0x01, 0xFE, 0x0E },
new List<byte> { 0xE0, 0x1F, 0x01, 0xFE, 0xF1, 0x0E, 0x01, 0xFE },
new List<byte> { 0xE0, 0x1F, 0x1F, 0xE0, 0xF1, 0x0E, 0x0E, 0xF1 },
new List<byte> { 0xE0, 0x1F, 0xFE, 0x01, 0xF1, 0x0E, 0xFE, 0x01 },
new List<byte> { 0xE0, 0xE0, 0x01, 0x01, 0xF1, 0xF1, 0x01, 0x01 },
new List<byte> { 0xE0, 0xE0, 0x1F, 0x1F, 0xF1, 0xF1, 0x0E, 0x0E },
new List<byte> { 0xE0, 0xE0, 0xFE, 0xFE, 0xF1, 0xF1, 0xFE, 0xFE },
new List<byte> { 0xE0, 0xFE, 0x01, 0x1F, 0xF1, 0xFE, 0x01, 0x0E },
new List<byte> { 0xE0, 0xFE, 0x1F, 0x01, 0xF1, 0xFE, 0x0E, 0x01 },
new List<byte> { 0xE0, 0xFE, 0xFE, 0xE0, 0xF1, 0xFE, 0xFE, 0xF1 },
new List<byte> { 0xFE, 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01, 0xFE },
new List<byte> { 0xFE, 0x01, 0x1F, 0xE0, 0xFE, 0x01, 0x0E, 0xF1 },
new List<byte> { 0xFE, 0x1F, 0x01, 0xE0, 0xFE, 0x0E, 0x01, 0xF1 },
new List<byte> { 0xFE, 0x1F, 0xE0, 0x01, 0xFE, 0x0E, 0xF1, 0x01 },
new List<byte> { 0xFE, 0x1F, 0x1F, 0xFE, 0xFE, 0x0E, 0x0E, 0xFE },
new List<byte> { 0xFE, 0xE0, 0x01, 0x1F, 0xFE, 0xF1, 0x01, 0x0E },
new List<byte> { 0xFE, 0xE0, 0x1F, 0x01, 0xFE, 0xF1, 0x0E, 0x01 },
new List<byte> { 0xFE, 0xE0, 0xE0, 0xFE, 0xFE, 0xF1, 0xF1, 0xFE },
new List<byte> { 0xFE, 0xFE, 0x01, 0x01, 0xFE, 0xFE, 0x01, 0x01 },
new List<byte> { 0xFE, 0xFE, 0x1F, 0x1F, 0xFE, 0xFE, 0x0E, 0x0E },
new List<byte> { 0xFE, 0xFE, 0xE0, 0xE0, 0xFE, 0xFE, 0xF1, 0xF1 }
};
foreach (List<byte> weak in weakKeys)
{
if (weak.SequenceEqual(key))
{
return Tuple.Create(ValidateResult.Warning, "This key is cryptographically weak");
}
}
List<List<byte>> guessableKeys = new List<List<byte>>
{
new List<byte> { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
new List<byte> { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
new List<byte> { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }
};
foreach (List<byte> weak in guessableKeys)
{
if (weak.SequenceEqual(key))
{
return Tuple.Create(ValidateResult.Warning, "This key is easily guessable");
}
}
}
else if (algId == (byte)AlgorithmId.AES256)
{
if (key.Count != 32)
{
return Tuple.Create(ValidateResult.Error, string.Format("Key length invalid - expected 32 bytes, got {0} bytes", key.Count));
}
List<List<byte>> guessableKeys = new List<List<byte>>
{
new List<byte>
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
},
new List<byte>
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
},
new List<byte>
{
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF
}
};
foreach (List<byte> weak in guessableKeys)
{
if (weak.SequenceEqual(key))
{
return Tuple.Create(ValidateResult.Warning, "This key is easily guessable");
}
}
}
else if (algId == (byte)AlgorithmId.ADP)
{
if (key.Count != 5)
{
return Tuple.Create(ValidateResult.Error, string.Format("Key length invalid - expected 5 bytes, got {0} bytes", key.Count));
}
List<List<byte>> guessableKeys = new List<List<byte>>
{
new List<byte>
{
0x00, 0x00, 0x00, 0x00, 0x00
},
new List<byte>
{
0x00, 0x01, 0x02, 0x03, 0x04
},
new List<byte>
{
0x01, 0x23, 0x45, 0x67, 0x89
}
};
foreach (List<byte> weak in guessableKeys)
{
if (weak.SequenceEqual(key))
{
return Tuple.Create(ValidateResult.Warning, "This key is easily guessable");
}
}
}
else // all other algorithm IDs
{
return Tuple.Create(ValidateResult.Warning, string.Format("Algorithm ID 0x{0:X2} is unassigned - no key validation has been performed", algId));
}
// good practice validators
if (sln == 0)
{
return Tuple.Create(ValidateResult.Warning, "While the SLN 0 is valid, some equipment may have issues using it"); // *cough* Motorola KVLs *cough*
}
if (sln >= 1 && sln <= 4095)
{
if (isKek)
{
return Tuple.Create(ValidateResult.Warning, "This SLN is in the range for TEKs, but the key type KEK is selected");
}
}
else if (sln >= 4096 && sln <= 61439)
{
return Tuple.Create(ValidateResult.Warning, "While this SLN is valid, it uses a crypto group other than 0 or 15, some equipment may have issues using it");
}
else if (sln >= 61440 && sln <= 65535)
{
if (!isKek)
{
return Tuple.Create(ValidateResult.Warning, "This SLN is in the range for KEKs, but the key type TEK is selected");
}
}
return Tuple.Create(ValidateResult.Success, string.Empty);
}
}
}