using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using LibHac.Util; using Xunit; namespace LibHac.Tests.CryptoTests; public class RspReader { private StreamReader Reader { get; } public RspReader(Stream stream) { Reader = new StreamReader(stream); } public IEnumerable GetEncryptionTestVectors() { string line; bool isEncryptType = false; var testVector = new EncryptionTestVector(); bool canOutputVector = false; while ((line = Reader.ReadLine()?.Trim()) != null) { if (line.Length == 0) { if (canOutputVector) { testVector.Encrypt = isEncryptType; yield return testVector; testVector = new EncryptionTestVector(); canOutputVector = false; } continue; } if (line[0] == '#') continue; if (line[0] == '[') { if (line == "[ENCRYPT]") isEncryptType = true; if (line == "[DECRYPT]") isEncryptType = false; continue; } string[] kvp = line.Split(new[] { " = " }, StringSplitOptions.None); if (kvp.Length != 2) throw new InvalidDataException(); canOutputVector = true; switch (kvp[0].ToUpperInvariant()) { case "COUNT": testVector.Count = int.Parse(kvp[1]); break; case "DATAUNITLEN": testVector.DataUnitLength = int.Parse(kvp[1]); break; case "KEY": testVector.Key = kvp[1].ToBytes(); break; case "IV": case "I": testVector.Iv = kvp[1].ToBytes(); break; case "PLAINTEXT": case "PT": testVector.PlainText = kvp[1].ToBytes(); break; case "CIPHERTEXT": case "CT": testVector.CipherText = kvp[1].ToBytes(); break; } } } public static TheoryData ReadEncryptionTestVectors(bool getEncryptTests, params string[] filenames) { EncryptionTestVector[] vectorArray = ReadEncryptionTestVectorsArray(getEncryptTests, filenames); var testVectors = new TheoryData(); foreach (EncryptionTestVector test in vectorArray) { testVectors.Add(test); } return testVectors; } public static EncryptionTestVector[] ReadEncryptionTestVectorsArray(bool getEncryptTests, params string[] filenames) { IEnumerable resourcePaths = filenames.Select(x => $"LibHac.Tests.CryptoTests.TestVectors.{x}"); var testVectors = new List(); foreach (string path in resourcePaths) { using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path)) { var reader = new RspReader(stream); foreach (EncryptionTestVector tv in reader.GetEncryptionTestVectors().Where(x => x.Encrypt == getEncryptTests)) { testVectors.Add(tv); } } } return testVectors.ToArray(); } public IEnumerable GetHashTestVectors() { string line; var testVector = new HashTestVector(); bool canOutputVector = false; while ((line = Reader.ReadLine()?.Trim()) != null) { if (line.Length == 0) { if (canOutputVector) { yield return testVector; testVector = new HashTestVector(); canOutputVector = false; } continue; } if (line[0] == '#') continue; if (line[0] == '[') continue; string[] kvp = line.Split(new[] { " = " }, StringSplitOptions.None); if (kvp.Length != 2) throw new InvalidDataException(); canOutputVector = true; switch (kvp[0].ToUpperInvariant()) { case "LEN": testVector.LengthBits = int.Parse(kvp[1]); testVector.LengthBytes = testVector.LengthBits / 8; break; case "MSG": testVector.Message = kvp[1].ToBytes(); break; case "MD": testVector.Digest = kvp[1].ToBytes(); break; } } } public static TheoryData ReadHashTestVectors(params string[] filenames) { HashTestVector[] vectorArray = ReadHashTestVectorsArray(filenames); var testVectors = new TheoryData(); foreach (HashTestVector test in vectorArray) { testVectors.Add(test); } return testVectors; } public static HashTestVector[] ReadHashTestVectorsArray(params string[] filenames) { IEnumerable resourcePaths = filenames.Select(x => $"LibHac.Tests.CryptoTests.TestVectors.{x}"); var testVectors = new List(); foreach (string path in resourcePaths) { using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path)) { var reader = new RspReader(stream); foreach (HashTestVector tv in reader.GetHashTestVectors()) { testVectors.Add(tv); } } } return testVectors.ToArray(); } } public class EncryptionTestVector { public bool Encrypt { get; set; } public int Count { get; set; } public int DataUnitLength { get; set; } public byte[] Key { get; set; } public byte[] Iv { get; set; } public byte[] PlainText { get; set; } public byte[] CipherText { get; set; } } public class HashTestVector { public int LengthBits { get; set; } public int LengthBytes { get; set; } public byte[] Message { get; set; } public byte[] Digest { get; set; } }