using Edge.Core.Processor; using Edge.Core.Processor.Dispatcher.Attributes; using Edge.Core.UniversalApi; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using ShengJuDesFireEv1CpuCardKeyLoader; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using static ShengJu_CUT100_DES.GroupHandler; using static ShengJu_CUT100_DES.MessageEntity.Outgoing.ModifyPredefinedFileKey; namespace ClassicCpuCardApp { [UniversalApi(Name = GenericAlarm.UniversalApiEventName, EventDataType = typeof(GenericAlarm[]), Description = "Fire GenericAlarms to AlarmBar for attracting users.")] [UniversalApi(Name = OnCardTapEventName, EventDataType = typeof(object), Description = "Fired once a DesFire EV1 card detected by module and card UID is read")] [UniversalApi(Name = OnCardReaderModuleStateChangeEventName, EventDataType = typeof(CardReaderModule), Description = "Fired once card reader module state changed")] [MetaPartsDescriptor( "lang-zh-cn:CPU卡读写卡应用lang-en-us:CPU Card Read&Write App", "lang-zh-cn:CPU卡读写卡应用,用于管理多个 盛炬CUT100_DES 感应卡读写卡模块lang-en-us:CPU卡读写卡应用,用于管理多个 盛炬CUT100_DES 感应卡读写卡模块", new[] { "lang-zh-cn:支付终端lang-en-us:Terminal" })] public class App : IAppProcessor { private AppConfigV1 appConfig; private LocalNetworkMacBasedEncryptionKeyLoader keyLoader; private IEnumerable cardReaderDeviceHandlers; private IEnumerable cardReaderModules; private ILogger logger = NullLogger.Instance; private IServiceProvider services; public const string OnCardTapEventName = "OnCardTap"; public const string OnCardReaderModuleStateChangeEventName = "OnCardReaderModuleStateChange"; public string MetaConfigName { get; set; } public IEnumerable CardReaderModuels => this.cardReaderModules; public class AppConfigV1 { public EncryptedBase64KeySet EncryptedBase64KeySet { get; set; } } private byte[] blankCardDefaultRootKey; private byte[] newRootKey; private byte[] file1DefaultReadKey; private byte[] file1DefaultWriteKey; public App(AppConfigV1 appConfig, IServiceProvider services) { this.appConfig = appConfig; this.services = services; var loggerFactory = services.GetRequiredService(); this.logger = loggerFactory.CreateLogger("DynamicPrivate_ClassicCpuCardApp"); this.keyLoader = new LocalNetworkMacBasedEncryptionKeyLoader(this.logger); } public void Init(IEnumerable processors) { this.cardReaderDeviceHandlers = processors.WithHandlerOrApp() .SelectHandlerOrAppThenCast(); if (this.cardReaderDeviceHandlers == null || !this.cardReaderDeviceHandlers.Any()) throw new InvalidOperationException("当前 APP 的运行必须要求系统中配置一个或者多个“ShengJu_CUT100_DES设备驱动” ,但当前没有获取到,请配置并启用相应设备驱动后重试"); if (this.cardReaderDeviceHandlers.SelectMany(dh => dh.CardReaderModuels).GroupBy(b => new { b.UnderlyingCommunicatorIdentity, b.PhysicalAddress }).Any(g => g.Count() >= 2)) throw new ArgumentException("发现多个 'ShengJu_CUT100_DES设备驱动' 配置中有重复的硬件地址值,请检查相应的设备驱动以确保同一通讯链路上硬件地址唯一,再重试 "); if (this.cardReaderDeviceHandlers.SelectMany(dh => dh.CardReaderModuels).GroupBy(b => b.Name).Any(g => g.Count() >= 2)) throw new ArgumentException("发现多个 'ShengJu_CUT100_DES设备驱动' 配置中有重复的读卡器名称(Name),请检查相应的设备驱动以确保全局名称唯一,再重试 "); foreach (var dh in this.cardReaderDeviceHandlers) { dh.OnModuleStateChange += async (s, a) => { var universalApiHub = this.services.GetRequiredService(); await universalApiHub?.FireEvent(this, OnCardReaderModuleStateChangeEventName, dh); }; } foreach (var dh in this.cardReaderDeviceHandlers) { dh.OnCardTap += async (s, a) => { var universalApiHub = this.services.GetRequiredService(); await universalApiHub?.FireEvent(this, OnCardTapEventName, a); }; } this.cardReaderModules = this.cardReaderDeviceHandlers.SelectMany(dh => dh.CardReaderModuels); } [UniversalApi(Description = "Get all available card reader moduel names")] public async Task> GetCardReaderModuleNames() { return this.cardReaderDeviceHandlers.SelectMany(dh => dh.CardReaderModuels); } [UniversalApi( Description = "Set new RootKey to card, re-build(clear, re-create and set file keys) the files and blocks(4 files, and 4 blocks with 32 bytes for each file).
" + "All the new keys are decrypted from app's config values.
" + "Leave oldRootKeyBytes to null will use the key decrypted from app's config values.
" + "For most DesFireEV1 Cards, the factory-reset root and file read&write keys are 16 bytes of 0
", InputParametersExampleJson = "[\"desktopReader0\",[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]")] public async Task> FormatCard(string cardReaderModuleName, int[] oldRootKeyBytes) { var keySet = this.keyLoader.Get(this.appConfig.EncryptedBase64KeySet); if (oldRootKeyBytes == null) oldRootKeyBytes = keySet.RootKey.Select(b => (int)b).ToArray(); var formatBatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.FormatCardByResetToNewRootKeyAndBuildDefaultFileStructures(cardReaderModuleName, oldRootKeyBytes.Select(i => (byte)i).ToArray(), keySet.RootKey)); var formatResultsRaw = await Task.WhenAll(formatBatchOperationTasks); var formatResults = formatResultsRaw.SelectMany(r => r); if (formatResults.Any(r => r.OverallResultCode != 200)) return formatResults; //if (!setFileKeys) // return formatResults; byte[] oldFileKeyBytes = Enumerable.Repeat(0, 16).Select(i => (byte)i).ToArray(); //文件1 byte[] fileKey = keySet.文件1读密钥; var modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件1读密钥, oldFileKeyBytes, fileKey)); var modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); var modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; fileKey = keySet.文件1读写密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件1读写密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; //文件2 fileKey = keySet.文件2读密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件2读密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; fileKey = keySet.文件2读写密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件2读写密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; //文件3 fileKey = keySet.文件3读密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件3读密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; fileKey = keySet.文件3读写密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件3读写密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; //文件4 fileKey = keySet.文件4读密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件4读密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; fileKey = keySet.文件4读写密钥; modifyPredefinedFileKeybatchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, PredefinedFileKeyTypeEnum.文件4读写密钥, oldFileKeyBytes, fileKey)); modifyPredefinedFileKeyResultsRaw = await Task.WhenAll(modifyPredefinedFileKeybatchOperationTasks); modifyPredefinedFileKeyResults = modifyPredefinedFileKeyResultsRaw.SelectMany(r => r); if (modifyPredefinedFileKeyResults.Any(r => r.OverallResultCode != 200)) return modifyPredefinedFileKeyResults; return modifyPredefinedFileKeyResults; } [UniversalApi( Description = "Modify the read or write keys for the specified file, the new key is decrypted from app's config values, " + "make sure adding those enctryption keys from config UI first.", InputParametersExampleJson = "[\"desktopReader0\",\"文件1读密钥\",[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]")] public async Task ModifyPredefinedFileKey(string cardReaderModuleName, PredefinedFileKeyTypeEnum fileKeyType, int[] oldKeyBytes) { var keySet = this.keyLoader.Get(this.appConfig.EncryptedBase64KeySet); byte[] newKey = null; if (fileKeyType == PredefinedFileKeyTypeEnum.根密钥) newKey = keySet.文件1读密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件1读密钥) newKey = keySet.文件1读密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件1读写密钥) newKey = keySet.文件1读写密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件2读密钥) newKey = keySet.文件2读密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件2读写密钥) newKey = keySet.文件2读写密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件3读密钥) newKey = keySet.文件3读密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件3读写密钥) newKey = keySet.文件3读写密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件4读密钥) newKey = keySet.文件4读密钥; if (fileKeyType == PredefinedFileKeyTypeEnum.文件4读写密钥) newKey = keySet.文件4读写密钥; if ((oldKeyBytes?.Length ?? 0) != 16) throw new ArgumentOutOfRangeException(nameof(oldKeyBytes)); var batchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ModifyPredefinedFileKey(cardReaderModuleName, fileKeyType, oldKeyBytes.Select(i => (byte)i).ToArray(), newKey)); var results = await Task.WhenAll(batchOperationTasks); List final = new List(); foreach (var r in results) final.AddRange(r); return final.ToArray(); } [UniversalApi(Description = "Write a 32 bytes content to card by specify the destination fileId and blockAddress, " + "the default fileId range is from 1 to 4, with 4 blocks in a file with address from 1-4, each block with size 32 bytes.", InputParametersExampleJson = "[\"desktopReader0\",1,1,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]]")] public async Task WriteCardWithRawData(string cardReaderModuleName, byte fileId, byte blockAddress, int[] dataBytes) { var keySet = this.keyLoader.Get(this.appConfig.EncryptedBase64KeySet); byte[] writeKey = null; if (fileId == 1) writeKey = keySet.文件1读写密钥; else if (fileId == 2) writeKey = keySet.文件2读写密钥; else if (fileId == 3) writeKey = keySet.文件3读写密钥; else if (fileId == 4) writeKey = keySet.文件4读写密钥; else throw new ArgumentOutOfRangeException(nameof(fileId)); if (blockAddress == 0 || blockAddress > 4) throw new ArgumentOutOfRangeException(nameof(blockAddress)); if ((dataBytes?.Length ?? 0) != 32) throw new ArgumentOutOfRangeException(nameof(dataBytes)); var batchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.WriteCard(cardReaderModuleName, fileId, blockAddress, dataBytes.Select(i => (byte)i).ToArray(), writeKey)); var results = await Task.WhenAll(batchOperationTasks); List final = new List(); foreach (var r in results) final.AddRange(r); return final.ToArray(); } [UniversalApi(Description = "Write a text content (which must have Utf8 bytes length <=32) to card by specify the destination fileId and blockAddress, " + "the default fileId range is from 1 to 4, with 4 blocks in a file with address from 1-4, each block with size 32 bytes.", InputParametersExampleJson = "[\"desktopReader0\",1,1,\"UserNameIsShao\"]")] public async Task WriteCardWithTextData(string cardReaderModuleName, byte fileId, byte blockAddress, string plainTextData) { var keySet = this.keyLoader.Get(this.appConfig.EncryptedBase64KeySet); byte[] writeKey = null; if (fileId == 1) writeKey = keySet.文件1读写密钥; else if (fileId == 2) writeKey = keySet.文件2读写密钥; else if (fileId == 3) writeKey = keySet.文件3读写密钥; else if (fileId == 4) writeKey = keySet.文件4读写密钥; else throw new ArgumentOutOfRangeException(nameof(fileId)); if (blockAddress == 0 || blockAddress > 4) throw new ArgumentOutOfRangeException(nameof(blockAddress)); var dataBytes = Encoding.UTF8.GetBytes(plainTextData); if ((dataBytes?.Length ?? 0) > 32) throw new ArgumentOutOfRangeException(nameof(dataBytes)); char paddingRightChar = ' '; dataBytes = dataBytes.Concat(Enumerable.Repeat((byte)paddingRightChar, 32 - dataBytes.Length)).ToArray(); var batchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.WriteCard(cardReaderModuleName, fileId, blockAddress, dataBytes, writeKey)); var results = await Task.WhenAll(batchOperationTasks); List final = new List(); foreach (var r in results) final.AddRange(r); return final.ToArray(); } [UniversalApi(Description = "Read a 32 bytes content from card by specify the source fileId and blockAddress, " + "the default fileId range is from 1 to 4, with 4 blocks in a file with address from 1-4, each block with size 32 bytes.", InputParametersExampleJson = "[\"desktopReader0\",1,1]")] public async Task ReadCardContent(string cardReaderModuleName, byte fileId, byte blockAddress) { var keySet = this.keyLoader.Get(this.appConfig.EncryptedBase64KeySet); byte[] readKey = null; if (fileId == 1) readKey = keySet.文件1读密钥; else if (fileId == 2) readKey = keySet.文件2读密钥; else if (fileId == 3) readKey = keySet.文件3读密钥; else if (fileId == 4) readKey = keySet.文件4读密钥; else throw new ArgumentOutOfRangeException(nameof(fileId)); if (blockAddress == 0 || blockAddress > 4) throw new ArgumentOutOfRangeException(nameof(blockAddress)); var batchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ReadCard(cardReaderModuleName, fileId, blockAddress, readKey)); var results = await Task.WhenAll(batchOperationTasks); List final = new List(); foreach (var r in results) final.AddRange(r); return final.ToArray(); } [UniversalApi(Description = "Read card open ID(unsafe) from any card tapped on card reader module(s).", InputParametersExampleJson = "[\"desktopReader0\"]")] public async Task ReadCardID(string cardReaderModuleName) { var batchOperationTasks = this.cardReaderDeviceHandlers.Select(h => h.ReadCardUID(cardReaderModuleName)); var results = await Task.WhenAll(batchOperationTasks); List final = new List(); foreach (var r in results) final.AddRange(r); return final.ToArray(); } [UniversalApi(Description = "Input the plain text key, it'll get encrypted and output a encrypted base64 encoded key which can be safely distributed.")] public async Task GenerateLocalEncryptedBase64Key(string plainTextKey) { return this.keyLoader.EncryptStringToBase64(plainTextKey); } //[UniversalApi(Description = "Input the encryptedBase64Key, and it'll get decrypted with a result of int array.")] //public async Task DecryptedBase64Key(string encryptedBase64Key) //{ // return this.keyLoader.Decrypt(Convert.FromBase64String(encryptedBase64Key)).Select(b => (int)b).ToArray(); //} [UniversalApi(Description = "Input the plain key set, it'll get encrypted and output a encrypted base64 encoded keyset which can be safely distributed.")] public async Task GenerateLocalEncryptedBase64KeySet(InputBase64PlainKeySet plainKeySet) { var output = new EncryptedBase64KeySet(); output.RootKey = this.keyLoader.EncryptStringToBase64(plainKeySet.RootKey); output.文件1读写密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件1读写密钥); output.文件1读密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件1读密钥); output.文件2读写密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件2读写密钥); output.文件2读密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件2读密钥); output.文件3读写密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件3读写密钥); output.文件3读密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件3读密钥); output.文件4读写密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件4读写密钥); output.文件4读密钥 = this.keyLoader.EncryptStringToBase64(plainKeySet.文件4读密钥); output.Description = plainKeySet.Description; return output; } } }