Initial commit
This commit is contained in:
15
.idea/.idea.PN532-Aime-Reader/.idea/.gitignore
generated
vendored
Normal file
15
.idea/.idea.PN532-Aime-Reader/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider 忽略的文件
|
||||
/modules.xml
|
||||
/.idea.PN532-Aime-Reader.iml
|
||||
/projectSettingsUpdater.xml
|
||||
/contentModel.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# 已忽略包含查询文件的默认文件夹
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
4
.idea/.idea.PN532-Aime-Reader/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.PN532-Aime-Reader/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
8
.idea/.idea.PN532-Aime-Reader/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.PN532-Aime-Reader/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/.idea.PN532-Aime-Reader/.idea/misc.xml
generated
Normal file
12
.idea/.idea.PN532-Aime-Reader/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MaterialThemeProjectNewConfig">
|
||||
<option name="metadata">
|
||||
<MTProjectMetadataState>
|
||||
<option name="migrated" value="true" />
|
||||
<option name="pristineConfig" value="false" />
|
||||
<option name="userId" value="-5326c7b6:1983fee25c2:-7ffc" />
|
||||
</MTProjectMetadataState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/.idea.PN532-Aime-Reader/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.PN532-Aime-Reader/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
34
PN532-Aime-Reader.sln
Normal file
34
PN532-Aime-Reader.sln
Normal file
@@ -0,0 +1,34 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.2.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NfcAime.Cli", "src\NfcAime.Cli\Nfcaime.Cli.csproj", "{BAD35401-BA7C-633C-3088-B4D88E8C1516}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NfcAime.Dll", "src\NfcAime.Dll\NfcAime.Dll.csproj", "{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{BAD35401-BA7C-633C-3088-B4D88E8C1516}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BAD35401-BA7C-633C-3088-B4D88E8C1516}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BAD35401-BA7C-633C-3088-B4D88E8C1516}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BAD35401-BA7C-633C-3088-B4D88E8C1516}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BAD35401-BA7C-633C-3088-B4D88E8C1516}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{BAD35401-BA7C-633C-3088-B4D88E8C1516}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {2F6D6CBD-6360-4FBA-84B9-36604BFE2F8B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
8
PN532-Aime-Reader.sln.DotSettings.user
Normal file
8
PN532-Aime-Reader.sln.DotSettings.user
Normal file
@@ -0,0 +1,8 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArray_002Ecs_002Fl_003AC_0021_003FUsers_003Fmczhi_003FAppData_003FRoaming_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F84fcf9255ea045aab003a751cdb5a2f6573620_003F0d_003Fef77d1e2_003FArray_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AReadOnlySpan_002Ecs_002Fl_003AC_0021_003FUsers_003Fmczhi_003FAppData_003FRoaming_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FSourcesCache_003F47bfed48817fad7d8e1a89bf3530e4be7277b022a9c7477c5a243031605a5f_003FReadOnlySpan_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStackFrameIterator_002Ecs_002Fl_003AC_0021_003FUsers_003Fmczhi_003FAppData_003FRoaming_003FJetBrains_003FRider2026_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8b6d5d70605340b2a1b73ddc36254a2ae8e910_003Fbe_003F5760bbf2_003FStackFrameIterator_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">C:\Program Files\Microsoft Visual Studio\18\Community\MSBuild\Current\Bin\MSBuild.exe</s:String>
|
||||
<s:Int64 x:Key="/Default/Environment/Hierarchy/Build/BuildTool/MsbuildVersion/@EntryValue">1179648</s:Int64>
|
||||
|
||||
</wpf:ResourceDictionary>
|
||||
18
src/NfcAime.Cli/AccessCodeFormatter.cs
Normal file
18
src/NfcAime.Cli/AccessCodeFormatter.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Nfcaime.Cli;
|
||||
|
||||
internal static class AccessCodeFormatter
|
||||
{
|
||||
private const int DecryptedByteCount = 16;
|
||||
private const int AccessCodeHexLength = 20;
|
||||
|
||||
internal static string ToAccessCodeString(ReadOnlySpan<byte> decryptedBytes)
|
||||
{
|
||||
if (decryptedBytes.Length != DecryptedByteCount)
|
||||
{
|
||||
throw new ArgumentException($"Expected {DecryptedByteCount} decrypted bytes.", nameof(decryptedBytes));
|
||||
}
|
||||
|
||||
var hex = Convert.ToHexString(decryptedBytes);
|
||||
return hex[^AccessCodeHexLength..];
|
||||
}
|
||||
}
|
||||
398
src/NfcAime.Cli/FeliCaDecryptor.cs
Normal file
398
src/NfcAime.Cli/FeliCaDecryptor.cs
Normal file
@@ -0,0 +1,398 @@
|
||||
namespace Nfcaime.Cli;
|
||||
|
||||
internal sealed class FeliCaDecryptor
|
||||
{
|
||||
private const int IterAdd = 5;
|
||||
private static readonly byte[][] SBox = new byte[][]
|
||||
{
|
||||
new byte[]
|
||||
{
|
||||
0xaf, 0xa8, 0x63, 0xa9, 0x24, 0xc0, 0xf1, 0xbe, 0xe7, 0xb3, 0xac, 0x16, 0x85, 0xb9, 0x11, 0x6f,
|
||||
0x10, 0x31, 0x32, 0xae, 0xb1, 0x78, 0x23, 0xd2, 0xa2, 0xca, 0xf9, 0x2b, 0x45, 0xb8, 0x3c, 0x62,
|
||||
0x1a, 0x6d, 0x82, 0x8c, 0x00, 0xd9, 0xe2, 0x7a, 0x46, 0x19, 0x2a, 0xcc, 0x13, 0xa7, 0xe4, 0xb7,
|
||||
0x84, 0x9b, 0x8e, 0xff, 0xfb, 0x8b, 0x03, 0x89, 0x30, 0x29, 0xd3, 0x87, 0x01, 0xbf, 0x35, 0xda,
|
||||
0x59, 0x5c, 0xcd, 0x09, 0x7f, 0x70, 0x41, 0x81, 0x6c, 0x5f, 0x53, 0xc2, 0x72, 0xd4, 0x79, 0x48,
|
||||
0x95, 0xb4, 0x5b, 0x39, 0x44, 0x9d, 0xfd, 0x51, 0x71, 0xf6, 0xd7, 0xd5, 0x2d, 0x9f, 0xed, 0x33,
|
||||
0xea, 0xaa, 0x5a, 0x17, 0x6a, 0xba, 0xbd, 0xe9, 0x9c, 0xce, 0x4d, 0x7c, 0x18, 0x3b, 0x0d, 0xee,
|
||||
0x0c, 0x80, 0xa1, 0x0a, 0x8f, 0xb5, 0x6b, 0xf7, 0x3f, 0xd1, 0x47, 0x25, 0xc6, 0x36, 0xe8, 0x7d,
|
||||
0x7b, 0x3e, 0xf3, 0x99, 0x75, 0x05, 0x1c, 0xe0, 0xcf, 0x34, 0xbb, 0x9e, 0xeb, 0x91, 0xad, 0x26,
|
||||
0x8d, 0xc7, 0x66, 0x08, 0xe6, 0x58, 0x40, 0x14, 0xa6, 0xc5, 0xfa, 0x12, 0xec, 0xc4, 0xf0, 0x21,
|
||||
0x8a, 0x86, 0xd6, 0xe1, 0x06, 0xb6, 0x60, 0x74, 0x2e, 0xe3, 0xfe, 0x27, 0x2f, 0x5e, 0xf4, 0xb2,
|
||||
0x68, 0x28, 0x56, 0xef, 0x9a, 0x61, 0x22, 0xa3, 0xc9, 0x0b, 0x02, 0x96, 0x4f, 0xdb, 0xe5, 0x90,
|
||||
0x4a, 0x4c, 0x92, 0xcb, 0x98, 0x69, 0xd0, 0x57, 0x64, 0x0e, 0x37, 0x50, 0xab, 0xbc, 0xde, 0x76,
|
||||
0x07, 0xc8, 0x3d, 0x49, 0xfc, 0x77, 0x94, 0xf8, 0xc1, 0x97, 0x65, 0x3a, 0x4b, 0x83, 0x6e, 0x73,
|
||||
0x88, 0x1e, 0x5d, 0x04, 0x54, 0x67, 0xdf, 0x15, 0xb0, 0xc3, 0x93, 0x2c, 0x38, 0xdd, 0xf5, 0xa4,
|
||||
0x55, 0x0f, 0x4e, 0x1d, 0xa0, 0x1f, 0x20, 0xa5, 0xdc, 0x43, 0xf2, 0xd8, 0x7e, 0x52, 0x1b, 0x42
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x03, 0x4b, 0xb8, 0x97, 0xb7, 0x8f, 0x0d, 0x71, 0xdc, 0x49, 0xa7, 0x90, 0xff, 0xa0, 0x9d, 0xf8,
|
||||
0x23, 0x5f, 0x20, 0x6c, 0x11, 0xf9, 0x69, 0x8e, 0xef, 0xcb, 0xf0, 0x14, 0xdf, 0x1b, 0x7a, 0xc6,
|
||||
0x36, 0x5b, 0xc5, 0x93, 0xb6, 0x28, 0x35, 0xb2, 0x3b, 0xd1, 0xec, 0xf3, 0x9f, 0x80, 0x64, 0x25,
|
||||
0x05, 0xcf, 0x54, 0xaf, 0x46, 0x3f, 0x96, 0x06, 0xe5, 0x10, 0xfb, 0xaa, 0xde, 0x62, 0x41, 0x51,
|
||||
0x59, 0xe7, 0x29, 0xb5, 0x4d, 0x3d, 0xf2, 0x37, 0x6a, 0xe8, 0x16, 0x8b, 0x5c, 0xa8, 0x1e, 0x57,
|
||||
0xe2, 0x09, 0xeb, 0x32, 0xc9, 0x2e, 0xb4, 0xf1, 0xa4, 0x76, 0xd3, 0xc1, 0xa5, 0x70, 0x60, 0x12,
|
||||
0xb1, 0xea, 0x66, 0xe1, 0x4a, 0x92, 0xa1, 0x6d, 0x3a, 0xf4, 0xd5, 0x6f, 0xda, 0xd8, 0x0a, 0x58,
|
||||
0x18, 0x63, 0x2b, 0xa3, 0x5d, 0x67, 0x77, 0x6b, 0x39, 0xc3, 0xdb, 0x4c, 0x02, 0xba, 0xce, 0xbb,
|
||||
0x55, 0x01, 0x17, 0x85, 0x27, 0xe4, 0x30, 0x72, 0xc8, 0x8d, 0xfc, 0x78, 0x19, 0xb9, 0x43, 0x2d,
|
||||
0x08, 0x9b, 0x1a, 0xd9, 0xcd, 0x26, 0xfa, 0x82, 0xcc, 0x7f, 0x75, 0x79, 0x9e, 0x65, 0xfe, 0xed,
|
||||
0x91, 0xc7, 0x31, 0xd0, 0x52, 0x2a, 0x48, 0x84, 0x74, 0xdd, 0x7d, 0xe9, 0xae, 0x6e, 0x33, 0x47,
|
||||
0xd6, 0x50, 0x0c, 0x53, 0xa6, 0xbd, 0x22, 0xac, 0x8a, 0x04, 0x89, 0x42, 0x40, 0xb3, 0x45, 0x5a,
|
||||
0x44, 0xe6, 0xa9, 0x1d, 0xc4, 0x81, 0xe3, 0x15, 0x1f, 0x56, 0x87, 0xd4, 0x3e, 0x0f, 0x95, 0xd7,
|
||||
0xe0, 0x94, 0xab, 0x24, 0x68, 0x07, 0xa2, 0x98, 0x7b, 0xee, 0xf5, 0xb0, 0x88, 0xc0, 0x38, 0x9c,
|
||||
0x99, 0x5e, 0xad, 0xd2, 0x3c, 0x2c, 0x21, 0xca, 0x8c, 0x7e, 0x1c, 0x61, 0xc2, 0x34, 0x7c, 0x2f,
|
||||
0x0b, 0x83, 0x9a, 0x4f, 0xbe, 0x4e, 0xbc, 0x73, 0x13, 0x00, 0xf6, 0x0e, 0x86, 0xbf, 0xfd, 0xf7
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x41, 0x07, 0x93, 0x8a, 0x40, 0xff, 0x0e, 0x2f, 0x54, 0xfc, 0xac, 0xd4, 0x1e, 0xb0, 0xc8, 0x89,
|
||||
0xdf, 0x00, 0x64, 0x4f, 0xf3, 0xcb, 0x2c, 0xc0, 0x8f, 0x6e, 0x17, 0xbf, 0x95, 0x99, 0x2a, 0x0d,
|
||||
0x51, 0xad, 0x4b, 0xca, 0xf7, 0x69, 0x55, 0x38, 0x4c, 0xc6, 0x68, 0xc3, 0xa3, 0x1f, 0x83, 0x3e,
|
||||
0x0b, 0x70, 0xc7, 0xb2, 0x4e, 0xcf, 0xfe, 0x4d, 0xfd, 0x8b, 0xb4, 0x96, 0xce, 0xf9, 0xf1, 0xd3,
|
||||
0x43, 0x60, 0xae, 0xd5, 0xb3, 0xdb, 0x4a, 0xb9, 0xe5, 0xea, 0x6a, 0x48, 0x5d, 0xa7, 0x87, 0xda,
|
||||
0xb8, 0x06, 0x2d, 0x94, 0xc1, 0x66, 0x24, 0x26, 0xb1, 0x9a, 0x25, 0x08, 0x5c, 0x59, 0xa6, 0xaf,
|
||||
0x27, 0x92, 0x88, 0xf2, 0xed, 0x1b, 0x1c, 0x11, 0xd2, 0x22, 0x16, 0x76, 0x56, 0x67, 0x14, 0x9f,
|
||||
0xee, 0x5f, 0xdc, 0x5b, 0xa4, 0x46, 0xdd, 0x10, 0x33, 0x29, 0x7f, 0x5a, 0xc9, 0x97, 0x84, 0x85,
|
||||
0x42, 0xbc, 0xab, 0x77, 0x3c, 0xa1, 0xc2, 0xaa, 0x37, 0x49, 0x61, 0x35, 0x9c, 0x86, 0x19, 0x57,
|
||||
0x5e, 0xe6, 0xe2, 0xa0, 0x04, 0x6d, 0xd1, 0xa8, 0x9d, 0xec, 0x02, 0xbd, 0xd8, 0x47, 0x62, 0x1a,
|
||||
0x05, 0x7d, 0x01, 0x3f, 0x18, 0x6f, 0x75, 0x44, 0xe4, 0x7a, 0xf6, 0xa9, 0xe8, 0x72, 0xf4, 0x6b,
|
||||
0xfb, 0x7e, 0xeb, 0x36, 0x52, 0xde, 0x79, 0x23, 0x8d, 0xe7, 0x71, 0x63, 0x98, 0x6c, 0xd9, 0xe9,
|
||||
0x3b, 0x13, 0x90, 0x03, 0x78, 0x74, 0x20, 0x81, 0xba, 0x34, 0x32, 0xfa, 0x7b, 0x31, 0xd6, 0x3d,
|
||||
0x30, 0x0a, 0xe3, 0x12, 0x8e, 0x0f, 0x1d, 0xbb, 0xa5, 0x73, 0x53, 0x82, 0x80, 0xb5, 0xd7, 0xe1,
|
||||
0x3a, 0x2b, 0xd0, 0xe0, 0x21, 0xbe, 0xcd, 0x15, 0x65, 0xb6, 0xef, 0xcc, 0x91, 0xf5, 0x0c, 0xc4,
|
||||
0x2e, 0x9e, 0x58, 0xf0, 0xc5, 0x9b, 0x39, 0x09, 0xa2, 0x7c, 0x45, 0xb7, 0x28, 0x8c, 0xf8, 0x50
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x76, 0xcd, 0x16, 0x7f, 0xe9, 0x6b, 0x95, 0x3a, 0x4e, 0xbb, 0x6e, 0xcc, 0xd2, 0xb3, 0x69, 0xa9,
|
||||
0x72, 0xaf, 0xc7, 0x82, 0x38, 0x6f, 0xbc, 0x37, 0xa6, 0x28, 0x36, 0xbd, 0xc3, 0xcf, 0x85, 0x46,
|
||||
0x0a, 0x99, 0x63, 0x97, 0x83, 0x56, 0x88, 0xa1, 0x02, 0x64, 0xac, 0x79, 0x55, 0x65, 0xff, 0xc2,
|
||||
0xef, 0x7a, 0x59, 0x2b, 0x1c, 0xe0, 0x8c, 0x8f, 0x5a, 0xe4, 0xb4, 0x9f, 0xfc, 0xf7, 0x45, 0x47,
|
||||
0x4a, 0xea, 0xc0, 0x0d, 0xeb, 0xc8, 0x78, 0xc9, 0x67, 0x03, 0x05, 0x31, 0x14, 0xde, 0xd3, 0x73,
|
||||
0xfa, 0x1f, 0xb6, 0x50, 0x13, 0x04, 0xb7, 0x7b, 0x49, 0x1a, 0xf9, 0x17, 0x4d, 0x4f, 0x86, 0x20,
|
||||
0xc1, 0xfd, 0xa0, 0xa5, 0x48, 0xe1, 0x3c, 0x60, 0xf2, 0x0b, 0x30, 0x0f, 0x4c, 0xee, 0x8d, 0x6d,
|
||||
0xbe, 0x8e, 0xc6, 0xf6, 0xd8, 0x08, 0x94, 0x1b, 0x2a, 0xb5, 0x9e, 0xa2, 0x93, 0xf5, 0x22, 0xa8,
|
||||
0x34, 0x26, 0xa7, 0x7c, 0xec, 0x3f, 0x0e, 0x3b, 0xb2, 0xf4, 0x29, 0xce, 0x2e, 0x42, 0x81, 0x32,
|
||||
0x25, 0x5f, 0x66, 0xdb, 0x7d, 0xd7, 0xe7, 0x2d, 0x43, 0x6a, 0x5b, 0x19, 0xad, 0x3e, 0x9b, 0xc5,
|
||||
0xab, 0x84, 0xc4, 0x6c, 0x33, 0xda, 0xfb, 0x8a, 0x11, 0x54, 0xfe, 0x57, 0x4b, 0x3d, 0x7e, 0x09,
|
||||
0xa3, 0x91, 0x40, 0xbf, 0x68, 0xe5, 0xb9, 0xf3, 0x96, 0xf8, 0x51, 0xca, 0x9a, 0xb0, 0x98, 0xae,
|
||||
0x1e, 0x9c, 0x5e, 0xe8, 0x00, 0x39, 0x12, 0xdc, 0x0c, 0x10, 0x8b, 0x5d, 0xd6, 0x2c, 0x27, 0xd5,
|
||||
0x61, 0x87, 0x53, 0xba, 0x23, 0x71, 0x2f, 0x1d, 0x74, 0x89, 0x9d, 0xdd, 0x35, 0x15, 0xd0, 0x44,
|
||||
0xb1, 0xcb, 0xf0, 0x62, 0x58, 0xa4, 0x77, 0xe3, 0x18, 0xd4, 0xe6, 0x80, 0xd1, 0xaa, 0x90, 0x70,
|
||||
0x01, 0x24, 0xf1, 0x21, 0xd9, 0x06, 0x75, 0xb8, 0x41, 0x5c, 0x92, 0xdf, 0xed, 0x07, 0xe2, 0x52
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xaa, 0x94, 0x99, 0x74, 0x92, 0xd6, 0x1c, 0x46, 0x47, 0x79, 0x2f, 0x56, 0x7a, 0xf7, 0xb5, 0x0a,
|
||||
0xc9, 0x06, 0xd7, 0xfa, 0x40, 0x11, 0xa5, 0xff, 0x1f, 0xe8, 0x00, 0x8c, 0xdd, 0xb7, 0x42, 0x6e,
|
||||
0x35, 0xf0, 0xbe, 0xc2, 0x1d, 0xca, 0xb6, 0x8e, 0xe2, 0x44, 0x4e, 0x9f, 0x51, 0x81, 0x8b, 0x4c,
|
||||
0x5d, 0x59, 0x38, 0xd3, 0x8f, 0x25, 0xb3, 0x09, 0x31, 0xad, 0x0b, 0x6b, 0x0e, 0x50, 0xbb, 0xd8,
|
||||
0x96, 0x88, 0xde, 0x49, 0xea, 0xb4, 0x4a, 0x9a, 0x30, 0x6d, 0x2c, 0x0f, 0xcd, 0xf6, 0x7e, 0x4f,
|
||||
0x52, 0xd9, 0xfb, 0x28, 0x07, 0x63, 0x19, 0xa4, 0xee, 0x01, 0xf8, 0xeb, 0xd2, 0xf5, 0x3d, 0x0d,
|
||||
0x91, 0x5b, 0x67, 0xb8, 0x57, 0xf3, 0x2b, 0xe6, 0x68, 0x16, 0x7b, 0x6f, 0x65, 0xc3, 0x76, 0xe3,
|
||||
0x3c, 0x2e, 0x41, 0x3e, 0xcf, 0x54, 0xe1, 0x17, 0xa7, 0x45, 0x2d, 0x5a, 0xfc, 0xc6, 0x82, 0xef,
|
||||
0xcb, 0x78, 0xac, 0x34, 0xba, 0x71, 0x27, 0x22, 0xf2, 0x12, 0x3f, 0x1a, 0x24, 0xa8, 0xc1, 0x9e,
|
||||
0xc7, 0x18, 0xe4, 0x5f, 0xfe, 0x6c, 0xaf, 0xc8, 0x37, 0xc5, 0x77, 0xbd, 0x02, 0x29, 0x7d, 0xfd,
|
||||
0x5e, 0x83, 0xe9, 0xbf, 0x2a, 0x5c, 0x73, 0xe5, 0xda, 0xe7, 0xec, 0x80, 0x6a, 0x03, 0x58, 0x3b,
|
||||
0x21, 0x13, 0xd1, 0x53, 0x70, 0x93, 0xbc, 0xa1, 0x10, 0xf4, 0x64, 0x85, 0x87, 0xb0, 0x61, 0xb1,
|
||||
0xf1, 0x43, 0x62, 0xc4, 0xf9, 0xa2, 0xae, 0xd4, 0x04, 0x86, 0xdf, 0x26, 0xd0, 0xab, 0x36, 0x1e,
|
||||
0x4b, 0xe0, 0x60, 0x90, 0xcc, 0x98, 0x95, 0x3a, 0x9c, 0x9b, 0x15, 0x1b, 0x33, 0x8d, 0x4d, 0x23,
|
||||
0x9d, 0x84, 0xce, 0xb2, 0x05, 0xa6, 0x0c, 0xb9, 0x14, 0x48, 0xd5, 0xa3, 0x75, 0x08, 0x7c, 0xed,
|
||||
0x7f, 0xdc, 0x89, 0x66, 0xa9, 0xc0, 0x39, 0x72, 0x20, 0x97, 0x8a, 0xa0, 0x55, 0x69, 0xdb, 0x32
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xde, 0x5a, 0x1b, 0x69, 0x3c, 0x42, 0xa3, 0x51, 0x40, 0x38, 0xf1, 0x17, 0x28, 0x1c, 0xb8, 0xac,
|
||||
0xe8, 0x99, 0x45, 0xa0, 0x12, 0x68, 0xce, 0x15, 0xc9, 0x47, 0x6d, 0xc8, 0x46, 0x4e, 0x1e, 0xa6,
|
||||
0x11, 0xf8, 0x3e, 0x31, 0xfa, 0xe6, 0x2d, 0xfd, 0x32, 0x14, 0x9c, 0x01, 0xb1, 0xed, 0x57, 0x71,
|
||||
0xe3, 0x30, 0xcf, 0x0e, 0xe7, 0xcc, 0x37, 0x4a, 0x78, 0xab, 0x1d, 0x96, 0xea, 0x25, 0x85, 0x20,
|
||||
0x49, 0x4d, 0x75, 0x52, 0xee, 0xef, 0xaf, 0x54, 0x33, 0xf9, 0x09, 0x9e, 0x81, 0xe1, 0x73, 0x2a,
|
||||
0xf2, 0x3d, 0x22, 0x13, 0x92, 0x8b, 0xf0, 0xc0, 0x84, 0x0d, 0xa8, 0x04, 0xdc, 0x8d, 0xbb, 0x2c,
|
||||
0x94, 0x03, 0xf5, 0xd1, 0xd0, 0x2e, 0x5d, 0xeb, 0x5e, 0x62, 0x6f, 0x21, 0xb0, 0x91, 0xfe, 0xcb,
|
||||
0x0a, 0xec, 0xb7, 0x2f, 0x34, 0x24, 0xd6, 0xe9, 0x66, 0x82, 0xc6, 0x19, 0x1f, 0xd9, 0xc1, 0xa7,
|
||||
0xf6, 0x88, 0x02, 0x87, 0x07, 0xdd, 0xd8, 0x67, 0xc5, 0xb9, 0x29, 0xa4, 0xc2, 0x65, 0xd3, 0x53,
|
||||
0xaa, 0x3b, 0x79, 0xe0, 0x72, 0xd7, 0x06, 0x1a, 0x58, 0x93, 0x7e, 0x0c, 0x90, 0x6e, 0x74, 0x0f,
|
||||
0x4c, 0x0b, 0xe4, 0x6c, 0x43, 0xd2, 0x41, 0x36, 0xbd, 0xdb, 0x35, 0x59, 0x5f, 0xa9, 0x9f, 0x55,
|
||||
0x80, 0x39, 0x8e, 0xf3, 0xd5, 0xe5, 0x63, 0xc3, 0x77, 0xa2, 0x5b, 0x7b, 0x7f, 0xae, 0xba, 0xa1,
|
||||
0x10, 0xdf, 0x4b, 0x2b, 0x9b, 0x16, 0xbc, 0x00, 0xc7, 0x18, 0xfb, 0x64, 0xff, 0xf4, 0xf7, 0xbe,
|
||||
0x05, 0x56, 0xcd, 0x08, 0x4f, 0x6a, 0x9a, 0x76, 0x7a, 0xb5, 0x95, 0xca, 0x50, 0x7d, 0x7c, 0x6b,
|
||||
0xa5, 0xc4, 0x3a, 0x8a, 0x27, 0xda, 0xb6, 0x61, 0x23, 0x86, 0x70, 0x48, 0x97, 0x9d, 0xfc, 0x8f,
|
||||
0x5c, 0x44, 0xb2, 0xad, 0x83, 0xbf, 0x26, 0xd4, 0xb3, 0x60, 0x98, 0xe2, 0x3f, 0x8c, 0xb4, 0x89
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x19, 0x2e, 0x5d, 0x7a, 0x5b, 0x7c, 0x7e, 0x39, 0xef, 0x8b, 0x2f, 0xcc, 0x86, 0x1b, 0xc2, 0x8c,
|
||||
0x20, 0xfb, 0x59, 0x5f, 0x21, 0xcd, 0x9f, 0x94, 0xa7, 0x0b, 0xba, 0x0e, 0x47, 0x7b, 0xa5, 0x06,
|
||||
0xb7, 0xd8, 0xc9, 0x26, 0xf0, 0x9d, 0x68, 0xf2, 0x5e, 0x12, 0x3a, 0x65, 0xb0, 0x5c, 0xe2, 0x22,
|
||||
0x99, 0x82, 0x10, 0xf5, 0x44, 0x50, 0x74, 0xa1, 0xf9, 0x42, 0xa8, 0x25, 0x4d, 0xea, 0x08, 0x3e,
|
||||
0x96, 0x92, 0xc4, 0x2d, 0xa2, 0x23, 0x34, 0xa9, 0x45, 0x6b, 0x79, 0x7f, 0xc8, 0x4c, 0x43, 0x67,
|
||||
0x29, 0x04, 0xfa, 0xb5, 0x1e, 0x05, 0xc6, 0xb4, 0xaf, 0x1d, 0xbb, 0xf7, 0x30, 0xed, 0xec, 0xab,
|
||||
0x40, 0xe0, 0x98, 0xe9, 0x4b, 0x11, 0x87, 0x15, 0x18, 0xbe, 0xee, 0x78, 0xb1, 0x02, 0x57, 0x90,
|
||||
0x32, 0x8e, 0x61, 0xcb, 0x3c, 0xe6, 0xd7, 0x75, 0x31, 0xb9, 0x54, 0x36, 0xe1, 0xdd, 0x6c, 0x97,
|
||||
0x62, 0xac, 0x41, 0x89, 0x9c, 0x60, 0xf6, 0x63, 0xbc, 0x14, 0x7d, 0x77, 0xd5, 0x09, 0x95, 0xdf,
|
||||
0x17, 0x4a, 0x0c, 0x2b, 0x52, 0x38, 0xfe, 0xb6, 0x33, 0xa4, 0x51, 0x73, 0x6d, 0x4f, 0xb3, 0xbf,
|
||||
0x71, 0xeb, 0x69, 0x3f, 0x13, 0x00, 0x1c, 0x6f, 0x9b, 0x35, 0xca, 0xa3, 0x2a, 0x76, 0x8d, 0x80,
|
||||
0x01, 0x16, 0x03, 0x3d, 0xe5, 0x83, 0x70, 0x8f, 0x5a, 0x58, 0x88, 0xf3, 0xe3, 0x0f, 0xc3, 0x07,
|
||||
0x46, 0xff, 0x49, 0xfd, 0xe8, 0xce, 0x85, 0xd1, 0xdc, 0xf1, 0xb8, 0xd3, 0x6e, 0xd4, 0xa0, 0xad,
|
||||
0x72, 0xda, 0xe7, 0x3b, 0x84, 0xf8, 0xd2, 0x28, 0xde, 0xc1, 0x1a, 0x64, 0x91, 0x0a, 0xe4, 0xc5,
|
||||
0x24, 0x6a, 0xd6, 0x53, 0xc7, 0x81, 0x2c, 0xc0, 0x93, 0xd9, 0x0d, 0xa6, 0xdb, 0xae, 0xcf, 0xf4,
|
||||
0x55, 0x66, 0x1f, 0x37, 0xfc, 0xbd, 0x48, 0xaa, 0x4e, 0x56, 0x27, 0x9e, 0xd0, 0x8a, 0xb2, 0x9a
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x54, 0xd9, 0x42, 0x50, 0xb8, 0x74, 0xab, 0x32, 0xa6, 0x24, 0x48, 0x6f, 0xf0, 0x7e, 0xd7, 0x30,
|
||||
0xb4, 0x6d, 0x06, 0xa2, 0x88, 0xd6, 0x59, 0x39, 0x4c, 0x2c, 0xaf, 0x6e, 0xa8, 0x2b, 0x66, 0x5c,
|
||||
0xb9, 0x1c, 0xe7, 0x53, 0x19, 0x56, 0x8a, 0x11, 0xd8, 0x61, 0x5f, 0x07, 0x03, 0xda, 0xb5, 0xb0,
|
||||
0x08, 0xd0, 0x27, 0x45, 0x00, 0xc3, 0xf5, 0xb1, 0x79, 0x6b, 0xc4, 0x5b, 0x21, 0xe2, 0x55, 0x3d,
|
||||
0xa0, 0xfe, 0x31, 0x9a, 0x36, 0x0f, 0xec, 0xcb, 0xbb, 0x72, 0x29, 0x1a, 0xf9, 0x76, 0x87, 0x6a,
|
||||
0xae, 0xc9, 0x52, 0xb7, 0xeb, 0x7b, 0xf3, 0x05, 0x94, 0x04, 0x01, 0xc6, 0xa1, 0xf4, 0x37, 0xe3,
|
||||
0xfb, 0xc1, 0x4f, 0xba, 0x9e, 0x80, 0x28, 0x2f, 0x5a, 0xf6, 0x91, 0x3e, 0xcf, 0x41, 0xad, 0xbe,
|
||||
0xb6, 0x33, 0x9b, 0xfc, 0xce, 0xc0, 0x68, 0xee, 0x3f, 0x51, 0xbf, 0x4e, 0xd2, 0x49, 0xa9, 0xf7,
|
||||
0x8f, 0x86, 0xc8, 0x2a, 0x67, 0x38, 0xe4, 0x4d, 0xc7, 0x2e, 0x63, 0x7f, 0xef, 0xdf, 0x97, 0xaa,
|
||||
0x5d, 0x62, 0x0b, 0x70, 0xd1, 0xe6, 0x13, 0x99, 0x92, 0xf8, 0xd3, 0x12, 0x3c, 0x64, 0x84, 0xa7,
|
||||
0x0a, 0xb2, 0x9c, 0xea, 0x1b, 0xcc, 0x5e, 0x43, 0x1e, 0xcd, 0x8d, 0x93, 0xe1, 0x71, 0x3b, 0xf2,
|
||||
0xa3, 0x23, 0xe9, 0xc2, 0xdd, 0x98, 0x8b, 0x73, 0x96, 0x57, 0x22, 0x35, 0x0e, 0xa5, 0xac, 0x0c,
|
||||
0xd4, 0x9f, 0x09, 0xdb, 0x3a, 0x16, 0x60, 0xe5, 0x89, 0x85, 0x2d, 0x77, 0x1d, 0x81, 0x20, 0xc5,
|
||||
0x7a, 0xed, 0xca, 0x14, 0x83, 0xfa, 0x1f, 0x26, 0x18, 0x47, 0x9d, 0x02, 0xe0, 0x58, 0x10, 0xfd,
|
||||
0x25, 0x75, 0xbc, 0x46, 0xde, 0xa4, 0x15, 0x40, 0x78, 0x34, 0x4a, 0x17, 0xd5, 0x0d, 0x69, 0x65,
|
||||
0xdc, 0x4b, 0x7c, 0x95, 0xe8, 0x8e, 0xff, 0x7d, 0x90, 0x8c, 0x82, 0x6c, 0xb3, 0xbd, 0x44, 0xf1
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xc3, 0x0b, 0xb8, 0x15, 0x00, 0xa4, 0x3d, 0xd9, 0xfd, 0x16, 0xe9, 0x08, 0x20, 0xe6, 0x8d, 0xc4,
|
||||
0x47, 0x38, 0x25, 0x93, 0x5e, 0xaa, 0xec, 0x2a, 0x10, 0x6e, 0x58, 0x95, 0x26, 0x53, 0x04, 0x1e,
|
||||
0x92, 0x9e, 0x2f, 0x8b, 0xbd, 0x0a, 0x69, 0xa9, 0x3b, 0x41, 0xf0, 0xd0, 0xda, 0x6a, 0xa1, 0x71,
|
||||
0x13, 0x63, 0xef, 0x90, 0x94, 0xb0, 0xeb, 0xe7, 0xdc, 0x73, 0xb9, 0x78, 0x7a, 0xe4, 0xc7, 0x6d,
|
||||
0x7f, 0xa3, 0xac, 0x85, 0x50, 0xf3, 0x91, 0xea, 0x36, 0xfa, 0x9b, 0x8e, 0xf7, 0xae, 0xc1, 0x43,
|
||||
0x1c, 0xc0, 0xca, 0x8f, 0x4e, 0xa2, 0x31, 0x12, 0x74, 0x2c, 0x9d, 0xf1, 0xad, 0x3f, 0x68, 0xbb,
|
||||
0x35, 0x9a, 0x7c, 0x03, 0xfb, 0x09, 0x23, 0x27, 0xd4, 0x4d, 0x17, 0xde, 0x4c, 0xee, 0x76, 0x9c,
|
||||
0xa0, 0xe1, 0x75, 0xdb, 0x5b, 0x11, 0xa7, 0x21, 0xaf, 0x02, 0x42, 0x4b, 0x83, 0xd3, 0xce, 0x9f,
|
||||
0xcd, 0xcc, 0x30, 0x2b, 0x52, 0x22, 0xcb, 0x59, 0x7d, 0x14, 0xf9, 0xb1, 0x70, 0x54, 0xe0, 0x81,
|
||||
0x5a, 0x1d, 0x0e, 0x4a, 0xf4, 0x84, 0xed, 0x96, 0x1a, 0xd7, 0xc9, 0x82, 0x89, 0x2d, 0x87, 0x33,
|
||||
0xe2, 0x5c, 0x51, 0x3a, 0x1b, 0xf6, 0xe5, 0xd8, 0x32, 0x0d, 0x5f, 0x72, 0x88, 0xd6, 0xb7, 0xc6,
|
||||
0xdd, 0x1f, 0x80, 0x37, 0x64, 0xb5, 0x7b, 0xdf, 0xff, 0xd2, 0x40, 0xfc, 0x60, 0x05, 0xb6, 0x66,
|
||||
0x61, 0xc8, 0x28, 0x99, 0xbc, 0xa5, 0x34, 0x19, 0xf8, 0xfe, 0x24, 0x48, 0xd1, 0x0c, 0x55, 0x62,
|
||||
0x6b, 0x86, 0xb3, 0xba, 0x8a, 0x2e, 0x6f, 0xa6, 0x67, 0x65, 0x01, 0xe8, 0x18, 0x3e, 0x7e, 0x77,
|
||||
0x29, 0x8c, 0xf2, 0x07, 0x6c, 0xa8, 0x79, 0x4f, 0x44, 0x46, 0x06, 0xc2, 0xab, 0x0f, 0xb4, 0xb2,
|
||||
0xbe, 0x98, 0xd5, 0x49, 0xe3, 0xf5, 0xcf, 0x97, 0x5d, 0xbf, 0x57, 0x3c, 0x39, 0xc5, 0x45, 0x56
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly byte[][] SBoxInv =
|
||||
{
|
||||
new byte[]
|
||||
{
|
||||
0x24, 0x3c, 0xba, 0x36, 0xe3, 0x85, 0xa4, 0xd0, 0x93, 0x43, 0x73, 0xb9, 0x70, 0x6e, 0xc9, 0xf1,
|
||||
0x10, 0x0e, 0x9b, 0x2c, 0x97, 0xe7, 0x0b, 0x63, 0x6c, 0x29, 0x20, 0xfe, 0x86, 0xf3, 0xe1, 0xf5,
|
||||
0xf6, 0x9f, 0xb6, 0x16, 0x04, 0x7b, 0x8f, 0xab, 0xb1, 0x39, 0x2a, 0x1b, 0xeb, 0x5c, 0xa8, 0xac,
|
||||
0x38, 0x11, 0x12, 0x5f, 0x89, 0x3e, 0x7d, 0xca, 0xec, 0x53, 0xdb, 0x6d, 0x1e, 0xd2, 0x81, 0x78,
|
||||
0x96, 0x46, 0xff, 0xf9, 0x54, 0x1c, 0x28, 0x7a, 0x4f, 0xd3, 0xc0, 0xdc, 0xc1, 0x6a, 0xf2, 0xbc,
|
||||
0xcb, 0x57, 0xfd, 0x4a, 0xe4, 0xf0, 0xb2, 0xc7, 0x95, 0x40, 0x62, 0x52, 0x41, 0xe2, 0xad, 0x49,
|
||||
0xa6, 0xb5, 0x1f, 0x02, 0xc8, 0xda, 0x92, 0xe5, 0xb0, 0xc5, 0x64, 0x76, 0x48, 0x21, 0xde, 0x0f,
|
||||
0x45, 0x58, 0x4c, 0xdf, 0xa7, 0x84, 0xcf, 0xd5, 0x15, 0x4e, 0x27, 0x80, 0x6b, 0x7f, 0xfc, 0x44,
|
||||
0x71, 0x47, 0x22, 0xdd, 0x30, 0x0c, 0xa1, 0x3b, 0xe0, 0x37, 0xa0, 0x35, 0x23, 0x90, 0x32, 0x74,
|
||||
0xbf, 0x8d, 0xc2, 0xea, 0xd6, 0x50, 0xbb, 0xd9, 0xc4, 0x83, 0xb4, 0x31, 0x68, 0x55, 0x8b, 0x5d,
|
||||
0xf4, 0x72, 0x18, 0xb7, 0xef, 0xf7, 0x98, 0x2d, 0x01, 0x03, 0x61, 0xcc, 0x0a, 0x8e, 0x13, 0x00,
|
||||
0xe8, 0x14, 0xaf, 0x09, 0x51, 0x75, 0xa5, 0x2f, 0x1d, 0x0d, 0x65, 0x8a, 0xcd, 0x66, 0x07, 0x3d,
|
||||
0x05, 0xd8, 0x4b, 0xe9, 0x9d, 0x99, 0x7c, 0x91, 0xd1, 0xb8, 0x19, 0xc3, 0x2b, 0x42, 0x69, 0x88,
|
||||
0xc6, 0x79, 0x17, 0x3a, 0x4d, 0x5b, 0xa2, 0x5a, 0xfb, 0x25, 0x3f, 0xbd, 0xf8, 0xed, 0xce, 0xe6,
|
||||
0x87, 0xa3, 0x26, 0xa9, 0x2e, 0xbe, 0x94, 0x08, 0x7e, 0x67, 0x60, 0x8c, 0x9c, 0x5e, 0x6f, 0xb3,
|
||||
0x9e, 0x06, 0xfa, 0x82, 0xae, 0xee, 0x59, 0x77, 0xd7, 0x1a, 0x9a, 0x34, 0xd4, 0x56, 0xaa, 0x33
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xf9, 0x81, 0x7c, 0x00, 0xb9, 0x30, 0x37, 0xd5, 0x90, 0x51, 0x6e, 0xf0, 0xb2, 0x06, 0xfb, 0xcd,
|
||||
0x39, 0x14, 0x5f, 0xf8, 0x1b, 0xc7, 0x4a, 0x82, 0x70, 0x8c, 0x92, 0x1d, 0xea, 0xc3, 0x4e, 0xc8,
|
||||
0x12, 0xe6, 0xb6, 0x10, 0xd3, 0x2f, 0x95, 0x84, 0x25, 0x42, 0xa5, 0x72, 0xe5, 0x8f, 0x55, 0xef,
|
||||
0x86, 0xa2, 0x53, 0xae, 0xed, 0x26, 0x20, 0x47, 0xde, 0x78, 0x68, 0x28, 0xe4, 0x45, 0xcc, 0x35,
|
||||
0xbc, 0x3e, 0xbb, 0x8e, 0xc0, 0xbe, 0x34, 0xaf, 0xa6, 0x09, 0x64, 0x01, 0x7b, 0x44, 0xf5, 0xf3,
|
||||
0xb1, 0x3f, 0xa4, 0xb3, 0x32, 0x80, 0xc9, 0x4f, 0x6f, 0x40, 0xbf, 0x21, 0x4c, 0x74, 0xe1, 0x11,
|
||||
0x5e, 0xeb, 0x3d, 0x71, 0x2e, 0x9d, 0x62, 0x75, 0xd4, 0x16, 0x48, 0x77, 0x13, 0x67, 0xad, 0x6b,
|
||||
0x5d, 0x07, 0x87, 0xf7, 0xa8, 0x9a, 0x59, 0x76, 0x8b, 0x9b, 0x1e, 0xd8, 0xee, 0xaa, 0xe9, 0x99,
|
||||
0x2d, 0xc5, 0x97, 0xf1, 0xa7, 0x83, 0xfc, 0xca, 0xdc, 0xba, 0xb8, 0x4b, 0xe8, 0x89, 0x17, 0x05,
|
||||
0x0b, 0xa0, 0x65, 0x23, 0xd1, 0xce, 0x36, 0x03, 0xd7, 0xe0, 0xf2, 0x91, 0xdf, 0x0e, 0x9c, 0x2c,
|
||||
0x0d, 0x66, 0xd6, 0x73, 0x58, 0x5c, 0xb4, 0x0a, 0x4d, 0xc2, 0x3b, 0xd2, 0xb7, 0xe2, 0xac, 0x33,
|
||||
0xdb, 0x60, 0x27, 0xbd, 0x56, 0x43, 0x24, 0x04, 0x02, 0x8d, 0x7d, 0x7f, 0xf6, 0xb5, 0xf4, 0xfd,
|
||||
0xdd, 0x5b, 0xec, 0x79, 0xc4, 0x22, 0x1f, 0xa1, 0x88, 0x54, 0xe7, 0x19, 0x98, 0x94, 0x7e, 0x31,
|
||||
0xa3, 0x29, 0xe3, 0x5a, 0xcb, 0x6a, 0xb0, 0xcf, 0x6d, 0x93, 0x6c, 0x7a, 0x08, 0xa9, 0x3c, 0x1c,
|
||||
0xd0, 0x63, 0x50, 0xc6, 0x85, 0x38, 0xc1, 0x41, 0x49, 0xab, 0x61, 0x52, 0x2a, 0x9f, 0xd9, 0x18,
|
||||
0x1a, 0x57, 0x46, 0x2b, 0x69, 0xda, 0xfa, 0xff, 0x0f, 0x15, 0x96, 0x3a, 0x8a, 0xfe, 0x9e, 0x0c
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x11, 0xa2, 0x9a, 0xc3, 0x94, 0xa0, 0x51, 0x01, 0x5b, 0xf7, 0xd1, 0x30, 0xee, 0x1f, 0x06, 0xd5,
|
||||
0x77, 0x67, 0xd3, 0xc1, 0x6e, 0xe7, 0x6a, 0x1a, 0xa4, 0x8e, 0x9f, 0x65, 0x66, 0xd6, 0x0c, 0x2d,
|
||||
0xc6, 0xe4, 0x69, 0xb7, 0x56, 0x5a, 0x57, 0x60, 0xfc, 0x79, 0x1e, 0xe1, 0x16, 0x52, 0xf0, 0x07,
|
||||
0xd0, 0xcd, 0xca, 0x78, 0xc9, 0x8b, 0xb3, 0x88, 0x27, 0xf6, 0xe0, 0xc0, 0x84, 0xcf, 0x2f, 0xa3,
|
||||
0x04, 0x00, 0x80, 0x40, 0xa7, 0xfa, 0x75, 0x9d, 0x4b, 0x89, 0x46, 0x22, 0x28, 0x37, 0x34, 0x13,
|
||||
0xff, 0x20, 0xb4, 0xda, 0x08, 0x26, 0x6c, 0x8f, 0xf2, 0x5d, 0x7b, 0x73, 0x5c, 0x4c, 0x90, 0x71,
|
||||
0x41, 0x8a, 0x9e, 0xbb, 0x12, 0xe8, 0x55, 0x6d, 0x2a, 0x25, 0x4a, 0xaf, 0xbd, 0x95, 0x19, 0xa5,
|
||||
0x31, 0xba, 0xad, 0xd9, 0xc5, 0xa6, 0x6b, 0x83, 0xc4, 0xb6, 0xa9, 0xcc, 0xf9, 0xa1, 0xb1, 0x7a,
|
||||
0xdc, 0xc7, 0xdb, 0x2e, 0x7e, 0x7f, 0x8d, 0x4e, 0x62, 0x0f, 0x03, 0x39, 0xfd, 0xb8, 0xd4, 0x18,
|
||||
0xc2, 0xec, 0x61, 0x02, 0x53, 0x1c, 0x3b, 0x7d, 0xbc, 0x1d, 0x59, 0xf5, 0x8c, 0x98, 0xf1, 0x6f,
|
||||
0x93, 0x85, 0xf8, 0x2c, 0x74, 0xd8, 0x5e, 0x4d, 0x97, 0xab, 0x87, 0x82, 0x0a, 0x21, 0x42, 0x5f,
|
||||
0x0d, 0x58, 0x33, 0x44, 0x3a, 0xdd, 0xe9, 0xfb, 0x50, 0x47, 0xc8, 0xd7, 0x81, 0x9b, 0xe5, 0x1b,
|
||||
0x17, 0x54, 0x86, 0x2b, 0xef, 0xf4, 0x29, 0x32, 0x0e, 0x7c, 0x23, 0x15, 0xeb, 0xe6, 0x3c, 0x35,
|
||||
0xe2, 0x96, 0x68, 0x3f, 0x0b, 0x43, 0xce, 0xde, 0x9c, 0xbe, 0x4f, 0x45, 0x72, 0x76, 0xb5, 0x10,
|
||||
0xe3, 0xdf, 0x92, 0xd2, 0xa8, 0x48, 0x91, 0xb9, 0xac, 0xbf, 0x49, 0xb2, 0x99, 0x64, 0x70, 0xea,
|
||||
0xf3, 0x3e, 0x63, 0x14, 0xae, 0xed, 0xaa, 0x24, 0xfe, 0x3d, 0xcb, 0xb0, 0x09, 0x38, 0x36, 0x05
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xc4, 0xf0, 0x28, 0x49, 0x55, 0x4a, 0xf5, 0xfd, 0x75, 0xaf, 0x20, 0x69, 0xc8, 0x43, 0x86, 0x6b,
|
||||
0xc9, 0xa8, 0xc6, 0x54, 0x4c, 0xdd, 0x02, 0x5b, 0xe8, 0x9b, 0x59, 0x77, 0x34, 0xd7, 0xc0, 0x51,
|
||||
0x5f, 0xf3, 0x7e, 0xd4, 0xf1, 0x90, 0x81, 0xce, 0x19, 0x8a, 0x78, 0x33, 0xcd, 0x97, 0x8c, 0xd6,
|
||||
0x6a, 0x4b, 0x8f, 0xa4, 0x80, 0xdc, 0x1a, 0x17, 0x14, 0xc5, 0x07, 0x87, 0x66, 0xad, 0x9d, 0x85,
|
||||
0xb2, 0xf8, 0x8d, 0x98, 0xdf, 0x3e, 0x1f, 0x3f, 0x64, 0x58, 0x40, 0xac, 0x6c, 0x5c, 0x08, 0x5d,
|
||||
0x53, 0xba, 0xff, 0xd2, 0xa9, 0x2c, 0x25, 0xab, 0xe4, 0x32, 0x38, 0x9a, 0xf9, 0xcb, 0xc2, 0x91,
|
||||
0x67, 0xd0, 0xe3, 0x22, 0x29, 0x2d, 0x92, 0x48, 0xb4, 0x0e, 0x99, 0x05, 0xa3, 0x6f, 0x0a, 0x15,
|
||||
0xef, 0xd5, 0x10, 0x4f, 0xd8, 0xf6, 0x00, 0xe6, 0x46, 0x2b, 0x31, 0x57, 0x83, 0x94, 0xae, 0x03,
|
||||
0xeb, 0x8e, 0x13, 0x24, 0xa1, 0x1e, 0x5e, 0xd1, 0x26, 0xd9, 0xa7, 0xca, 0x36, 0x6e, 0x71, 0x37,
|
||||
0xee, 0xb1, 0xfa, 0x7c, 0x76, 0x06, 0xb8, 0x23, 0xbe, 0x21, 0xbc, 0x9e, 0xc1, 0xda, 0x7a, 0x3b,
|
||||
0x62, 0x27, 0x7b, 0xb0, 0xe5, 0x63, 0x18, 0x82, 0x7f, 0x0f, 0xed, 0xa0, 0x2a, 0x9c, 0xbf, 0x11,
|
||||
0xbd, 0xe0, 0x88, 0x0d, 0x3a, 0x79, 0x52, 0x56, 0xf7, 0xb6, 0xd3, 0x09, 0x16, 0x1b, 0x70, 0xb3,
|
||||
0x42, 0x60, 0x2f, 0x1c, 0xa2, 0x9f, 0x72, 0x12, 0x45, 0x47, 0xbb, 0xe1, 0x0b, 0x01, 0x8b, 0x1d,
|
||||
0xde, 0xec, 0x0c, 0x4e, 0xe9, 0xcf, 0xcc, 0x95, 0x74, 0xf4, 0xa5, 0x93, 0xc7, 0xdb, 0x4d, 0xfb,
|
||||
0x35, 0x65, 0xfe, 0xe7, 0x39, 0xb5, 0xea, 0x96, 0xc3, 0x04, 0x41, 0x44, 0x84, 0xfc, 0x6d, 0x30,
|
||||
0xe2, 0xf2, 0x68, 0xb7, 0x89, 0x7d, 0x73, 0x3d, 0xb9, 0x5a, 0x50, 0xa6, 0x3c, 0x61, 0xaa, 0x2e
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x1a, 0x59, 0x9c, 0xad, 0xc8, 0xe4, 0x11, 0x54, 0xed, 0x37, 0x0f, 0x3a, 0xe6, 0x5f, 0x3c, 0x4b,
|
||||
0xb8, 0x15, 0x89, 0xb1, 0xe8, 0xda, 0x69, 0x77, 0x91, 0x56, 0x8b, 0xdb, 0x06, 0x24, 0xcf, 0x18,
|
||||
0xf8, 0xb0, 0x87, 0xdf, 0x8c, 0x35, 0xcb, 0x86, 0x53, 0x9d, 0xa4, 0x66, 0x4a, 0x7a, 0x71, 0x0a,
|
||||
0x48, 0x38, 0xff, 0xdc, 0x83, 0x20, 0xce, 0x98, 0x32, 0xf6, 0xd7, 0xaf, 0x70, 0x5e, 0x73, 0x8a,
|
||||
0x14, 0x72, 0x1e, 0xc1, 0x29, 0x79, 0x07, 0x08, 0xe9, 0x43, 0x46, 0xd0, 0x2f, 0xde, 0x2a, 0x4f,
|
||||
0x3d, 0x2c, 0x50, 0xb3, 0x75, 0xfc, 0x0b, 0x64, 0xae, 0x31, 0x7b, 0x61, 0xa5, 0x30, 0xa0, 0x93,
|
||||
0xd2, 0xbe, 0xc2, 0x55, 0xba, 0x6c, 0xf3, 0x62, 0x68, 0xfd, 0xac, 0x3b, 0x95, 0x49, 0x1f, 0x6b,
|
||||
0xb4, 0x85, 0xf7, 0xa6, 0x03, 0xec, 0x6e, 0x9a, 0x81, 0x09, 0x0c, 0x6a, 0xee, 0x9e, 0x4e, 0xf0,
|
||||
0xab, 0x2d, 0x7e, 0xa1, 0xe1, 0xbb, 0xc9, 0xbc, 0x41, 0xf2, 0xfa, 0x2e, 0x1b, 0xdd, 0x27, 0x34,
|
||||
0xd3, 0x60, 0x04, 0xb5, 0x01, 0xd6, 0x40, 0xf9, 0xd5, 0x02, 0x47, 0xd9, 0xd8, 0xe0, 0x8f, 0x2b,
|
||||
0xfb, 0xb7, 0xc5, 0xeb, 0x57, 0x16, 0xe5, 0x78, 0x8d, 0xf4, 0x00, 0xcd, 0x82, 0x39, 0xc6, 0x96,
|
||||
0xbd, 0xbf, 0xe3, 0x36, 0x45, 0x0e, 0x26, 0x1d, 0x63, 0xe7, 0x84, 0x3e, 0xb6, 0x9b, 0x22, 0xa3,
|
||||
0xf5, 0x8e, 0x23, 0x6d, 0xc3, 0x99, 0x7d, 0x90, 0x97, 0x10, 0x25, 0x80, 0xd4, 0x4c, 0xe2, 0x74,
|
||||
0xcc, 0xb2, 0x5c, 0x33, 0xc7, 0xea, 0x05, 0x12, 0x3f, 0x51, 0xa8, 0xfe, 0xf1, 0x1c, 0x42, 0xca,
|
||||
0xd1, 0x76, 0x28, 0x6f, 0x92, 0xa7, 0x67, 0xa9, 0x19, 0xa2, 0x44, 0x5b, 0xaa, 0xef, 0x58, 0x7f,
|
||||
0x21, 0xc0, 0x88, 0x65, 0xb9, 0x5d, 0x4d, 0x0d, 0x5a, 0xc4, 0x13, 0x52, 0x7c, 0x9f, 0x94, 0x17
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xc7, 0x2b, 0x82, 0x61, 0x5b, 0xd0, 0x96, 0x84, 0xd3, 0x4a, 0x70, 0xa1, 0x9b, 0x59, 0x33, 0x9f,
|
||||
0xc0, 0x20, 0x14, 0x53, 0x29, 0x17, 0xc5, 0x0b, 0xc9, 0x7b, 0x97, 0x02, 0x0d, 0x3a, 0x1e, 0x7c,
|
||||
0x3f, 0x6b, 0x52, 0xe8, 0x75, 0x3d, 0xf6, 0xe4, 0x0c, 0x8a, 0x4f, 0xc3, 0x5f, 0x26, 0x65, 0x73,
|
||||
0x31, 0x23, 0x28, 0x48, 0x74, 0xaa, 0xa7, 0x36, 0x09, 0xb1, 0xe2, 0x91, 0x04, 0x51, 0x22, 0xfc,
|
||||
0x08, 0xa6, 0x05, 0xa4, 0xf1, 0x12, 0x1c, 0x19, 0xeb, 0x40, 0x37, 0xc2, 0xa0, 0x41, 0x1d, 0xd4,
|
||||
0xdc, 0x07, 0x43, 0x8f, 0x47, 0xaf, 0xd1, 0x2e, 0x98, 0xab, 0x01, 0xba, 0xf0, 0x66, 0x68, 0xac,
|
||||
0xf9, 0xe7, 0x69, 0xb6, 0xcb, 0x8d, 0x78, 0x87, 0x15, 0x03, 0xd5, 0xdf, 0xa3, 0x1a, 0x9d, 0x6a,
|
||||
0xea, 0x2f, 0x94, 0x4e, 0x9e, 0x42, 0xd7, 0xb8, 0x38, 0x92, 0xd8, 0xbb, 0xde, 0xdd, 0x9a, 0xbc,
|
||||
0xb0, 0x4c, 0x79, 0xf4, 0x58, 0x3e, 0xe9, 0x83, 0x81, 0xff, 0xe3, 0x55, 0xfd, 0x5d, 0xb2, 0xef,
|
||||
0x9c, 0x6d, 0x54, 0x99, 0x60, 0xda, 0x3b, 0xec, 0xfa, 0x11, 0xd6, 0xc4, 0x2a, 0xed, 0x4b, 0xae,
|
||||
0x13, 0xbf, 0xb9, 0x06, 0x8b, 0xe0, 0x1f, 0x7f, 0x5a, 0xad, 0x90, 0x39, 0x0f, 0xf3, 0xbd, 0x46,
|
||||
0x6c, 0x2c, 0xf2, 0xf8, 0xfe, 0xd9, 0xe6, 0x72, 0x0e, 0x89, 0xbe, 0x5e, 0xc6, 0xa8, 0xcf, 0xf5,
|
||||
0x57, 0x7e, 0x8c, 0xb7, 0xe1, 0x88, 0x7a, 0xc8, 0x1b, 0x18, 0xdb, 0x6f, 0x35, 0xd2, 0x16, 0x32,
|
||||
0x64, 0x63, 0xa5, 0x8e, 0xf7, 0xb4, 0x76, 0x95, 0x86, 0x7d, 0xe5, 0xa9, 0x5c, 0x85, 0x00, 0xc1,
|
||||
0x93, 0x4d, 0xfb, 0x30, 0xa2, 0xb5, 0x25, 0x34, 0x10, 0x77, 0x3c, 0x67, 0x71, 0x2d, 0x44, 0x45,
|
||||
0x56, 0x0a, 0x50, 0xb3, 0xcd, 0x62, 0x80, 0xce, 0x21, 0x49, 0x24, 0xca, 0xee, 0x27, 0x6e, 0xcc
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xa5, 0xb0, 0x6d, 0xb2, 0x51, 0x55, 0x1f, 0xbf, 0x3e, 0x8d, 0xdd, 0x19, 0x92, 0xea, 0x1b, 0xbd,
|
||||
0x32, 0x65, 0x29, 0xa4, 0x89, 0x67, 0xb1, 0x90, 0x68, 0x00, 0xda, 0x0d, 0xa6, 0x59, 0x54, 0xf2,
|
||||
0x10, 0x14, 0x2f, 0x45, 0xe0, 0x3b, 0x23, 0xfa, 0xd7, 0x50, 0xac, 0x93, 0xe6, 0x43, 0x01, 0x0a,
|
||||
0x5c, 0x78, 0x70, 0x98, 0x46, 0xa9, 0x7b, 0xf3, 0x95, 0x07, 0x2a, 0xd3, 0x74, 0xb3, 0x3f, 0xa3,
|
||||
0x60, 0x82, 0x39, 0x4e, 0x34, 0x48, 0xc0, 0x1c, 0xf6, 0xc2, 0x91, 0x64, 0x4d, 0x3c, 0xf8, 0x9d,
|
||||
0x35, 0x9a, 0x94, 0xe3, 0x7a, 0xf0, 0xf9, 0x6e, 0xb9, 0x12, 0xb8, 0x04, 0x2d, 0x02, 0x28, 0x13,
|
||||
0x85, 0x72, 0x80, 0x87, 0xdb, 0x2b, 0xf1, 0x4f, 0x26, 0xa2, 0xe1, 0x49, 0x7e, 0x9c, 0xcc, 0xa7,
|
||||
0xb6, 0xa0, 0xd0, 0x9b, 0x36, 0x77, 0xad, 0x8b, 0x6b, 0x4a, 0x03, 0x1d, 0x05, 0x8a, 0x06, 0x4b,
|
||||
0xaf, 0xe5, 0x31, 0xb5, 0xd4, 0xc6, 0x0c, 0x66, 0xba, 0x83, 0xfd, 0x09, 0x0f, 0xae, 0x71, 0xb7,
|
||||
0x6f, 0xdc, 0x41, 0xe8, 0x17, 0x8e, 0x40, 0x7f, 0x62, 0x30, 0xff, 0xa8, 0x84, 0x25, 0xfb, 0x16,
|
||||
0xce, 0x37, 0x44, 0xab, 0x99, 0x1e, 0xeb, 0x18, 0x3a, 0x47, 0xf7, 0x5f, 0x81, 0xcf, 0xed, 0x58,
|
||||
0x2c, 0x6c, 0xfe, 0x9e, 0x57, 0x53, 0x97, 0x20, 0xca, 0x79, 0x1a, 0x5a, 0x88, 0xf5, 0x69, 0x9f,
|
||||
0xe7, 0xd9, 0x0e, 0xbe, 0x42, 0xdf, 0x56, 0xe4, 0x4c, 0x22, 0xaa, 0x73, 0x0b, 0x15, 0xc5, 0xee,
|
||||
0xfc, 0xc7, 0xd6, 0xcb, 0xcd, 0x8c, 0xe2, 0x76, 0x21, 0xe9, 0xd1, 0xec, 0xc8, 0x7d, 0xd8, 0x8f,
|
||||
0x61, 0x7c, 0x2e, 0xbc, 0xde, 0xb4, 0x75, 0xd2, 0xc4, 0x63, 0x3d, 0xa1, 0x5e, 0x5d, 0x6a, 0x08,
|
||||
0x24, 0xc9, 0x27, 0xbb, 0xef, 0x33, 0x86, 0x5b, 0xd5, 0x38, 0x52, 0x11, 0xf4, 0xc3, 0x96, 0xc1
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x34, 0x5a, 0xdb, 0x2c, 0x59, 0x57, 0x12, 0x2b, 0x30, 0xc2, 0xa0, 0x92, 0xbf, 0xed, 0xbc, 0x45,
|
||||
0xde, 0x27, 0x9b, 0x96, 0xd3, 0xe6, 0xc5, 0xeb, 0xd8, 0x24, 0x4b, 0xa4, 0x21, 0xcc, 0xa8, 0xd6,
|
||||
0xce, 0x3c, 0xba, 0xb1, 0x09, 0xe0, 0xd7, 0x32, 0x66, 0x4a, 0x83, 0x1d, 0x19, 0xca, 0x89, 0x67,
|
||||
0x0f, 0x42, 0x07, 0x71, 0xe9, 0xbb, 0x44, 0x5e, 0x85, 0x17, 0xc4, 0xae, 0x9c, 0x3f, 0x6b, 0x78,
|
||||
0xe7, 0x6d, 0x02, 0xa7, 0xfe, 0x33, 0xe3, 0xd9, 0x0a, 0x7d, 0xea, 0xf1, 0x18, 0x87, 0x7b, 0x62,
|
||||
0x03, 0x79, 0x52, 0x23, 0x00, 0x3e, 0x25, 0xb9, 0xdd, 0x16, 0x68, 0x3b, 0x1f, 0x90, 0xa6, 0x2a,
|
||||
0xc6, 0x29, 0x91, 0x8a, 0x9d, 0xef, 0x1e, 0x84, 0x76, 0xee, 0x4f, 0x39, 0xfb, 0x11, 0x1b, 0x0b,
|
||||
0x93, 0xad, 0x49, 0xb7, 0x05, 0xe1, 0x4d, 0xcb, 0xe8, 0x38, 0xd0, 0x55, 0xf2, 0xf7, 0x0d, 0x8b,
|
||||
0x65, 0xcd, 0xfa, 0xd4, 0x9e, 0xc9, 0x81, 0x4e, 0x14, 0xc8, 0x26, 0xb6, 0xf9, 0xaa, 0xf5, 0x80,
|
||||
0xf8, 0x6a, 0x98, 0xab, 0x58, 0xf3, 0xb8, 0x8e, 0xb5, 0x97, 0x43, 0x72, 0xa2, 0xda, 0x64, 0xc1,
|
||||
0x40, 0x5c, 0x13, 0xb0, 0xe5, 0xbd, 0x08, 0x9f, 0x1c, 0x7e, 0x8f, 0x06, 0xbe, 0x6e, 0x50, 0x1a,
|
||||
0x2f, 0x37, 0xa1, 0xfc, 0x10, 0x2e, 0x70, 0x53, 0x04, 0x20, 0x63, 0x48, 0xe2, 0xfd, 0x6f, 0x7a,
|
||||
0x75, 0x61, 0xb3, 0x35, 0x3a, 0xcf, 0x5b, 0x88, 0x82, 0x51, 0xd2, 0x47, 0xa5, 0xa9, 0x74, 0x6c,
|
||||
0x31, 0x94, 0x7c, 0x9a, 0xc0, 0xec, 0x15, 0x0e, 0x28, 0x01, 0x2d, 0xc3, 0xf0, 0xb4, 0xe4, 0x8d,
|
||||
0xdc, 0xac, 0x3d, 0x5f, 0x86, 0xc7, 0x95, 0x22, 0xf4, 0xb2, 0xa3, 0x54, 0x46, 0xd1, 0x77, 0x8c,
|
||||
0x0c, 0xff, 0xaf, 0x56, 0x5d, 0x36, 0x69, 0x7f, 0x99, 0x4c, 0xd5, 0x60, 0x73, 0xdf, 0x41, 0xf6
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x04, 0xda, 0x79, 0x63, 0x1e, 0xbd, 0xea, 0xe3, 0x0b, 0x65, 0x25, 0x01, 0xcd, 0xa9, 0x92, 0xed,
|
||||
0x18, 0x75, 0x57, 0x30, 0x89, 0x03, 0x09, 0x6a, 0xdc, 0xc7, 0x98, 0xa4, 0x50, 0x91, 0x1f, 0xb1,
|
||||
0x0c, 0x77, 0x85, 0x66, 0xca, 0x12, 0x1c, 0x67, 0xc2, 0xe0, 0x17, 0x83, 0x59, 0x9d, 0xd5, 0x22,
|
||||
0x82, 0x56, 0xa8, 0x9f, 0xc6, 0x60, 0x48, 0xb3, 0x11, 0xfc, 0xa3, 0x28, 0xfb, 0x06, 0xdd, 0x5d,
|
||||
0xba, 0x29, 0x7a, 0x4f, 0xe8, 0xfe, 0xe9, 0x10, 0xcb, 0xf3, 0x93, 0x7b, 0x6c, 0x69, 0x54, 0xe7,
|
||||
0x44, 0xa2, 0x84, 0x1d, 0x8d, 0xce, 0xff, 0xfa, 0x1a, 0x87, 0x90, 0x74, 0xa1, 0xf8, 0x14, 0xaa,
|
||||
0xbc, 0xc0, 0xcf, 0x31, 0xb4, 0xd9, 0xbf, 0xd8, 0x5e, 0x26, 0x2d, 0xd0, 0xe4, 0x3f, 0x19, 0xd6,
|
||||
0x8c, 0x2f, 0xab, 0x39, 0x58, 0x72, 0x6e, 0xdf, 0x3b, 0xe6, 0x3c, 0xb6, 0x62, 0x88, 0xde, 0x40,
|
||||
0xb2, 0x8f, 0x9b, 0x7c, 0x95, 0x43, 0xd1, 0x9e, 0xac, 0x9c, 0xd4, 0x23, 0xe1, 0x0e, 0x4b, 0x53,
|
||||
0x33, 0x46, 0x20, 0x13, 0x34, 0x1b, 0x97, 0xf7, 0xf1, 0xc3, 0x61, 0x4a, 0x6f, 0x5a, 0x21, 0x7f,
|
||||
0x70, 0x2e, 0x55, 0x41, 0x05, 0xc5, 0xd7, 0x76, 0xe5, 0x27, 0x15, 0xec, 0x42, 0x5c, 0x4d, 0x78,
|
||||
0x35, 0x8b, 0xef, 0xd2, 0xee, 0xb5, 0xbe, 0xae, 0x02, 0x3a, 0xd3, 0x5f, 0xc4, 0x24, 0xf0, 0xf9,
|
||||
0x51, 0x4e, 0xeb, 0x00, 0x0f, 0xfd, 0xaf, 0x3e, 0xc1, 0x9a, 0x52, 0x86, 0x81, 0x80, 0x7e, 0xf6,
|
||||
0x2b, 0xcc, 0xb9, 0x7d, 0x68, 0xf2, 0xad, 0x99, 0xa7, 0x07, 0x2c, 0x73, 0x38, 0xb0, 0x6b, 0xb7,
|
||||
0x8e, 0x71, 0xa0, 0xf4, 0x3d, 0xa6, 0x0d, 0x37, 0xdb, 0x0a, 0x47, 0x36, 0x16, 0x96, 0x6d, 0x32,
|
||||
0x2a, 0x5b, 0xe2, 0x45, 0x94, 0xf5, 0xa5, 0x4c, 0xc8, 0x8a, 0x49, 0x64, 0xbb, 0x08, 0xc9, 0xb8
|
||||
}
|
||||
};
|
||||
|
||||
public byte[] Decrypt(ReadOnlySpan<byte> spadInput)
|
||||
{
|
||||
if (spadInput.Length != 16)
|
||||
{
|
||||
throw new ArgumentException("spadInput must be exactly 16 bytes.", nameof(spadInput));
|
||||
}
|
||||
|
||||
var spad = spadInput.ToArray();
|
||||
var nTables = SBoxInv.Length - 1;
|
||||
|
||||
for (var i = 0; i < spad.Length; i++)
|
||||
{
|
||||
spad[i] = SBoxInv[nTables][spad[i]];
|
||||
}
|
||||
|
||||
var count = (spad[15] >> 4) + 7;
|
||||
var table = spad[15] + IterAdd * count;
|
||||
|
||||
for (var iteration = 0; iteration < count; iteration++)
|
||||
{
|
||||
table -= IterAdd;
|
||||
RotateRight(spad, 15, 5);
|
||||
var tableIndex = table % nTables;
|
||||
for (var i = 0; i < 15; i++)
|
||||
{
|
||||
spad[i] = SBoxInv[tableIndex][spad[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return spad;
|
||||
}
|
||||
|
||||
private static void RotateRight(byte[] data, int nBytes, int nBits)
|
||||
{
|
||||
var prior = data[nBytes - 1];
|
||||
for (var i = 0; i < nBytes; i++)
|
||||
{
|
||||
var current = data[i];
|
||||
data[i] = (byte)(((current >> nBits) | ((prior & ((1 << nBits) - 1)) << (8 - nBits))) & 0xFF);
|
||||
prior = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/NfcAime.Cli/FelicaCommandBuilder.cs
Normal file
36
src/NfcAime.Cli/FelicaCommandBuilder.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace Nfcaime.Cli;
|
||||
|
||||
internal static class FelicaCommandBuilder
|
||||
{
|
||||
internal static byte[] BuildPollingCommand() => new byte[] { 0x06, 0x00, 0x88, 0xB4, 0x01, 0x0F };
|
||||
|
||||
internal static byte[] BuildReadWithoutEncryptionCommand(ReadOnlySpan<byte> idm)
|
||||
{
|
||||
if (idm.Length != 8)
|
||||
{
|
||||
throw new ArgumentException("IDm must be exactly 8 bytes.", nameof(idm));
|
||||
}
|
||||
|
||||
// FeliCa Read Without Encryption:
|
||||
// Byte 0: Length (16 bytes = 0x10)
|
||||
// Byte 1: Command Code (0x06)
|
||||
// Byte 2-9: IDm
|
||||
// Byte 10: Number of Services (0x01)
|
||||
// Byte 11-12: Service Code List (0x0B, 0x00 for Service 0x000B, Little Endian)
|
||||
// Byte 13: Number of Blocks (0x01)
|
||||
// Byte 14-15: Block List (0x80, 0x00 for Service 0, Block 0)
|
||||
|
||||
byte[] command = new byte[16];
|
||||
command[0] = 0x10;
|
||||
command[1] = 0x06;
|
||||
idm.CopyTo(command.AsSpan(2, 8));
|
||||
command[10] = 0x01;
|
||||
command[11] = 0x0B;
|
||||
command[12] = 0x00;
|
||||
command[13] = 0x01;
|
||||
command[14] = 0x80;
|
||||
command[15] = 0x00;
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
17
src/NfcAime.Cli/FelicaResponseParser.cs
Normal file
17
src/NfcAime.Cli/FelicaResponseParser.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Nfcaime.Cli;
|
||||
|
||||
internal static class FelicaResponseParser
|
||||
{
|
||||
private const int Spad0Offset = 13;
|
||||
private const int Spad0Length = 16;
|
||||
|
||||
internal static byte[] ParseSpad0(ReadOnlySpan<byte> response)
|
||||
{
|
||||
if (response.Length < Spad0Offset + Spad0Length)
|
||||
{
|
||||
throw new ArgumentException($"Response must be at least {Spad0Offset + Spad0Length} bytes.", nameof(response));
|
||||
}
|
||||
|
||||
return response.Slice(Spad0Offset, Spad0Length).ToArray();
|
||||
}
|
||||
}
|
||||
14
src/NfcAime.Cli/Nfcaime.Cli.csproj
Normal file
14
src/NfcAime.Cli/Nfcaime.Cli.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.Ports" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
9
src/NfcAime.Cli/Pn532/IPn532FrameTransport.cs
Normal file
9
src/NfcAime.Cli/Pn532/IPn532FrameTransport.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal interface IPn532FrameTransport : IDisposable
|
||||
{
|
||||
void Open();
|
||||
void Close();
|
||||
void WriteFrame(ReadOnlySpan<byte> frame);
|
||||
Pn532FrameParseResult ReadFrame(TimeSpan timeout);
|
||||
}
|
||||
10
src/NfcAime.Cli/Pn532/Pn532FrameKind.cs
Normal file
10
src/NfcAime.Cli/Pn532/Pn532FrameKind.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal enum Pn532FrameKind
|
||||
{
|
||||
Ack,
|
||||
Nak,
|
||||
Data,
|
||||
ChecksumError,
|
||||
Invalid
|
||||
}
|
||||
10
src/NfcAime.Cli/Pn532/Pn532FrameParseResult.cs
Normal file
10
src/NfcAime.Cli/Pn532/Pn532FrameParseResult.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal sealed class Pn532FrameParseResult
|
||||
{
|
||||
public required Pn532FrameKind Kind { get; init; }
|
||||
public byte Tfi { get; init; }
|
||||
public byte[] Payload { get; init; } = Array.Empty<byte>();
|
||||
public byte[]? RawFrame { get; init; }
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
77
src/NfcAime.Cli/Pn532/Pn532FrameParser.cs
Normal file
77
src/NfcAime.Cli/Pn532/Pn532FrameParser.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal static class Pn532FrameParser
|
||||
{
|
||||
public static Pn532FrameParseResult Parse(ReadOnlySpan<byte> frame)
|
||||
{
|
||||
if (frame.Length < 6)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Frame too short", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (!frame.StartsWith(Pn532HsuFrame.AckFrame) && !frame.StartsWith(Pn532HsuFrame.NakFrame))
|
||||
{
|
||||
if (frame[0] != Pn532HsuFrame.Preamble || frame[1] != Pn532HsuFrame.StartCode1 || frame[2] != Pn532HsuFrame.StartCode2)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Missing preamble/start codes", RawFrame = frame.ToArray() };
|
||||
}
|
||||
}
|
||||
|
||||
if (frame.StartsWith(Pn532HsuFrame.AckFrame))
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Ack, RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (frame.StartsWith(Pn532HsuFrame.NakFrame))
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Nak, RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var length = frame[3];
|
||||
var lcs = frame[4];
|
||||
if (((length + lcs) & 0xFF) != 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.ChecksumError, Error = "LCS checksum mismatch", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Invalid length", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var expectedLength = 7 + length;
|
||||
if (frame.Length < expectedLength)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Frame length mismatch", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var tfi = frame[5];
|
||||
var payloadLength = length - 1;
|
||||
|
||||
var payload = payloadLength == 0 ? Array.Empty<byte>() : frame.Slice(6, payloadLength).ToArray();
|
||||
var dcs = frame[6 + payloadLength];
|
||||
var sum = tfi;
|
||||
for (var index = 0; index < payload.Length; index++)
|
||||
{
|
||||
sum += payload[index];
|
||||
}
|
||||
|
||||
if (((sum + dcs) & 0xFF) != 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.ChecksumError, Error = "DCS checksum mismatch", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (frame[7 + payloadLength] != Pn532HsuFrame.Postamble)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Missing postamble", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
return new Pn532FrameParseResult
|
||||
{
|
||||
Kind = Pn532FrameKind.Data,
|
||||
Tfi = tfi,
|
||||
Payload = payload,
|
||||
RawFrame = frame[..expectedLength].ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
61
src/NfcAime.Cli/Pn532/Pn532HsuFrame.cs
Normal file
61
src/NfcAime.Cli/Pn532/Pn532HsuFrame.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal static class Pn532HsuFrame
|
||||
{
|
||||
public const byte Preamble = 0x00;
|
||||
public const byte StartCode1 = 0x00;
|
||||
public const byte StartCode2 = 0xFF;
|
||||
public const byte Postamble = 0x00;
|
||||
|
||||
public const byte HostToPn532Tfi = 0xD4;
|
||||
public const byte Pn532ToHostTfi = 0xD5;
|
||||
|
||||
public static readonly byte[] AckFrame =
|
||||
[
|
||||
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00
|
||||
];
|
||||
|
||||
public static readonly byte[] NakFrame =
|
||||
[
|
||||
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00
|
||||
];
|
||||
|
||||
public static byte[] BuildDataFrame(byte tfi, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (data.Length + 1 > byte.MaxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(data), "PN532 HSU frames must fit in 255 bytes.");
|
||||
}
|
||||
|
||||
var length = (byte)(data.Length + 1);
|
||||
var lcs = ComputeLcs(length);
|
||||
var dcs = ComputeDcs(tfi, data);
|
||||
|
||||
var frame = new byte[7 + length];
|
||||
frame[0] = Preamble;
|
||||
frame[1] = StartCode1;
|
||||
frame[2] = StartCode2;
|
||||
frame[3] = length;
|
||||
frame[4] = lcs;
|
||||
frame[5] = tfi;
|
||||
data.CopyTo(frame.AsSpan(6));
|
||||
frame[6 + data.Length] = dcs;
|
||||
frame[7 + data.Length] = Postamble;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public static byte ComputeLcs(byte length)
|
||||
=> (byte)((0x100 - length) & 0xFF);
|
||||
|
||||
public static byte ComputeDcs(byte tfi, ReadOnlySpan<byte> data)
|
||||
{
|
||||
var sum = tfi;
|
||||
for (var index = 0; index < data.Length; index++)
|
||||
{
|
||||
sum += data[index];
|
||||
}
|
||||
|
||||
return (byte)((0x100 - (sum & 0xFF)) & 0xFF);
|
||||
}
|
||||
}
|
||||
96
src/NfcAime.Cli/Pn532/Pn532Session.cs
Normal file
96
src/NfcAime.Cli/Pn532/Pn532Session.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal sealed class Pn532Session : IDisposable
|
||||
{
|
||||
private readonly IPn532FrameTransport _transport;
|
||||
private readonly TimeSpan _timeout;
|
||||
private readonly int _maxRetries;
|
||||
|
||||
public Pn532Session(IPn532FrameTransport transport, TimeSpan timeout, int maxRetries)
|
||||
{
|
||||
_transport = transport;
|
||||
_timeout = timeout;
|
||||
_maxRetries = maxRetries;
|
||||
}
|
||||
|
||||
public void Open() => _transport.Open();
|
||||
|
||||
public void Close() => _transport.Close();
|
||||
|
||||
public void Dispose() => _transport.Dispose();
|
||||
|
||||
public Pn532FrameParseResult SendCommand(ReadOnlySpan<byte> payload)
|
||||
=> SendCommand(payload, responseTimeout: null);
|
||||
|
||||
public Pn532FrameParseResult SendCommand(ReadOnlySpan<byte> payload, TimeSpan? responseTimeout)
|
||||
{
|
||||
var responseReadTimeout = responseTimeout ?? _timeout;
|
||||
var frame = Pn532HsuFrame.BuildDataFrame(Pn532HsuFrame.HostToPn532Tfi, payload);
|
||||
var maxAttempts = _maxRetries + 1;
|
||||
for (var attempt = 0; attempt < maxAttempts; attempt++)
|
||||
{
|
||||
_transport.WriteFrame(frame);
|
||||
var ack = _transport.ReadFrame(_timeout);
|
||||
if (ack.Kind == Pn532FrameKind.Ack)
|
||||
{
|
||||
var response = _transport.ReadFrame(responseReadTimeout);
|
||||
if (response.Kind == Pn532FrameKind.Data)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
if (response.Kind == Pn532FrameKind.ChecksumError)
|
||||
{
|
||||
Console.WriteLine($"PN532 checksum error (response); retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (response.Kind == Pn532FrameKind.Invalid)
|
||||
{
|
||||
if (string.Equals(response.Error, "Read timeout", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"PN532 timeout (response after ACK); retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"PN532 read error (response): {response.Error}; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unexpected response kind after ACK; retry rather than immediately failing.
|
||||
Console.WriteLine($"PN532 unexpected response kind {response.Kind}; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ack.Kind == Pn532FrameKind.Nak)
|
||||
{
|
||||
Console.WriteLine($"PN532 NAK received; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ack.Kind == Pn532FrameKind.ChecksumError)
|
||||
{
|
||||
Console.WriteLine($"PN532 checksum error; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ack.Kind == Pn532FrameKind.Invalid)
|
||||
{
|
||||
if (string.Equals(ack.Error, "Read timeout", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"PN532 timeout (waiting for ACK); retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"PN532 read error: {ack.Error}; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return new Pn532FrameParseResult
|
||||
{
|
||||
Kind = Pn532FrameKind.Invalid,
|
||||
Error = "Retry limit exceeded (no valid ACK/response). Check COM port, baud rate, module mode (HSU/UART), and power."
|
||||
};
|
||||
}
|
||||
}
|
||||
50
src/NfcAime.Cli/Pn532/ReplayFrameTransport.cs
Normal file
50
src/NfcAime.Cli/Pn532/ReplayFrameTransport.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal sealed class ReplayFrameTransport : IPn532FrameTransport
|
||||
{
|
||||
private readonly Queue<byte[]> _frames;
|
||||
private bool _injectCorruption;
|
||||
|
||||
public ReplayFrameTransport(IEnumerable<byte[]> frames, bool injectCorruption)
|
||||
{
|
||||
_frames = new Queue<byte[]>(frames.Select(frame => frame.ToArray()));
|
||||
_injectCorruption = injectCorruption;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteFrame(ReadOnlySpan<byte> frame)
|
||||
{
|
||||
}
|
||||
|
||||
public Pn532FrameParseResult ReadFrame(TimeSpan timeout)
|
||||
{
|
||||
if (_frames.Count == 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Replay buffer empty" };
|
||||
}
|
||||
|
||||
var frame = _frames.Dequeue();
|
||||
if (_injectCorruption)
|
||||
{
|
||||
_injectCorruption = false;
|
||||
if (frame.Length > 0)
|
||||
{
|
||||
frame[^1] ^= 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return Pn532FrameParser.Parse(frame);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_frames.Clear();
|
||||
}
|
||||
}
|
||||
148
src/NfcAime.Cli/Pn532/SerialFrameTransport.cs
Normal file
148
src/NfcAime.Cli/Pn532/SerialFrameTransport.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace Nfcaime.Cli.Pn532;
|
||||
|
||||
internal sealed class SerialFrameTransport : IPn532FrameTransport
|
||||
{
|
||||
private readonly SerialPort _port;
|
||||
private readonly TimeSpan _readChunkTimeout;
|
||||
private readonly int _maxFrameBytes;
|
||||
private bool _wakeupSent;
|
||||
|
||||
public SerialFrameTransport(string portName, int baud, TimeSpan readChunkTimeout, int maxFrameBytes = 300)
|
||||
{
|
||||
var chunkTimeout = readChunkTimeout;
|
||||
if (chunkTimeout <= TimeSpan.Zero || chunkTimeout > TimeSpan.FromMilliseconds(50))
|
||||
{
|
||||
chunkTimeout = TimeSpan.FromMilliseconds(50);
|
||||
}
|
||||
|
||||
_port = new SerialPort(portName, baud)
|
||||
{
|
||||
ReadTimeout = (int)chunkTimeout.TotalMilliseconds,
|
||||
WriteTimeout = (int)readChunkTimeout.TotalMilliseconds,
|
||||
Handshake = Handshake.None,
|
||||
Parity = Parity.None,
|
||||
DataBits = 8,
|
||||
StopBits = StopBits.One,
|
||||
DtrEnable = false,
|
||||
RtsEnable = false
|
||||
};
|
||||
_readChunkTimeout = chunkTimeout;
|
||||
_maxFrameBytes = maxFrameBytes;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
if (!_port.IsOpen)
|
||||
{
|
||||
_port.Open();
|
||||
_port.DiscardInBuffer();
|
||||
_port.DiscardOutBuffer();
|
||||
SendWakeupPattern();
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_port.IsOpen)
|
||||
{
|
||||
_port.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteFrame(ReadOnlySpan<byte> frame)
|
||||
{
|
||||
if (!_wakeupSent)
|
||||
{
|
||||
SendWakeupPattern();
|
||||
}
|
||||
|
||||
// Clear any stale data from previous commands or wake-up echos
|
||||
if (_port.IsOpen && _port.BytesToRead > 0)
|
||||
{
|
||||
_port.DiscardInBuffer();
|
||||
}
|
||||
|
||||
var buffer = frame.ToArray();
|
||||
_port.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
private void SendWakeupPattern()
|
||||
{
|
||||
// Simple HSU wakeup: Just long preamble.
|
||||
// We avoid sending SAMConfig here to keep the buffer clean for the first real command.
|
||||
var wakeup = new byte[] { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
_port.Write(wakeup, 0, wakeup.Length);
|
||||
Thread.Sleep(50);
|
||||
|
||||
if (_port.IsOpen)
|
||||
{
|
||||
_port.DiscardInBuffer();
|
||||
}
|
||||
|
||||
_wakeupSent = true;
|
||||
}
|
||||
|
||||
public Pn532FrameParseResult ReadFrame(TimeSpan timeout)
|
||||
{
|
||||
var deadline = Stopwatch.GetTimestamp() + (long)(timeout.TotalSeconds * Stopwatch.Frequency);
|
||||
var buffer = new List<byte>(_maxFrameBytes);
|
||||
|
||||
while (Stopwatch.GetTimestamp() < deadline)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_port.BytesToRead == 0)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
var next = _port.ReadByte();
|
||||
if (next < 0) continue;
|
||||
buffer.Add((byte)next);
|
||||
|
||||
if (buffer.Count >= 6)
|
||||
{
|
||||
var span = CollectionsMarshal.AsSpan(buffer);
|
||||
for (int i = 0; i <= span.Length - 6; i++)
|
||||
{
|
||||
var window = span[i..];
|
||||
if (window.StartsWith(Pn532HsuFrame.AckFrame))
|
||||
{
|
||||
return Pn532FrameParser.Parse(Pn532HsuFrame.AckFrame);
|
||||
}
|
||||
if (window.StartsWith(Pn532HsuFrame.NakFrame))
|
||||
{
|
||||
return Pn532FrameParser.Parse(Pn532HsuFrame.NakFrame);
|
||||
}
|
||||
if (window.Length >= 7 && window[0] == Pn532HsuFrame.Preamble && window[1] == Pn532HsuFrame.StartCode1 && window[2] == Pn532HsuFrame.StartCode2)
|
||||
{
|
||||
var length = window[3];
|
||||
var expectedLength = 7 + length;
|
||||
if (window.Length >= expectedLength)
|
||||
{
|
||||
return Pn532FrameParser.Parse(window[..expectedLength]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Read timeout" };
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
_port.Dispose();
|
||||
}
|
||||
}
|
||||
299
src/NfcAime.Cli/Program.cs
Normal file
299
src/NfcAime.Cli/Program.cs
Normal file
@@ -0,0 +1,299 @@
|
||||
using Nfcaime.Cli.Pn532;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace Nfcaime.Cli;
|
||||
|
||||
internal static class Program
|
||||
{
|
||||
private const string DefaultPort = "COM14";
|
||||
private const int DefaultBaud = 115200;
|
||||
private const int DefaultRetryCount = 2;
|
||||
private const int DefaultTimeoutMs = 1500;
|
||||
|
||||
private static int Main(string[] args)
|
||||
{
|
||||
if (!TryParseArgs(args, out var options, out var errorMessage))
|
||||
{
|
||||
Console.Error.WriteLine(errorMessage);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Mode: {options.Mode}");
|
||||
Console.WriteLine($"Port: {options.Port}");
|
||||
Console.WriteLine($"Baud: {options.Baud}");
|
||||
Console.WriteLine($"Retries: {options.Retries}");
|
||||
Console.WriteLine($"TimeoutMs: {options.TimeoutMs}");
|
||||
|
||||
if (options.Mode == "replay") RunReplay(options);
|
||||
else if (options.Mode == "diag") RunDiag(options);
|
||||
else RunLive(options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static bool TryParseArgs(string[] args, out CliOptions options, out string errorMessage)
|
||||
{
|
||||
var parsed = new CliOptions { Port = DefaultPort, Baud = DefaultBaud, Mode = "live" };
|
||||
for (var index = 0; index < args.Length; index++)
|
||||
{
|
||||
var arg = args[index];
|
||||
if (!arg.StartsWith("--", StringComparison.Ordinal)) { errorMessage = $"Unknown argument: {arg}"; options = default!; return false; }
|
||||
var equalsIndex = arg.IndexOf('=');
|
||||
var name = equalsIndex >= 0 ? arg[..equalsIndex] : arg;
|
||||
var value = equalsIndex >= 0 ? arg[(equalsIndex + 1)..] : (index + 1 < args.Length && !args[index + 1].StartsWith("--") ? args[++index] : null);
|
||||
switch (name)
|
||||
{
|
||||
case "--port": parsed.Port = value ?? DefaultPort; break;
|
||||
case "--baud": if (int.TryParse(value, out var baud)) parsed.Baud = baud; break;
|
||||
case "--mode": parsed.Mode = value?.ToLowerInvariant() ?? "live"; break;
|
||||
case "--retries": if (int.TryParse(value, out var retries)) parsed.Retries = retries; break;
|
||||
case "--timeoutMs": if (int.TryParse(value, out var timeoutMs)) parsed.TimeoutMs = timeoutMs; break;
|
||||
default: return Fail($"Unknown argument: {name}", out options, out errorMessage);
|
||||
}
|
||||
}
|
||||
options = parsed; errorMessage = string.Empty; return true;
|
||||
}
|
||||
|
||||
private static bool Fail(string message, out CliOptions options, out string errorMessage)
|
||||
{
|
||||
options = default!; errorMessage = message; return false;
|
||||
}
|
||||
|
||||
private sealed class CliOptions { public string Port { get; set; } = DefaultPort; public int Baud { get; set; } = DefaultBaud; public string Mode { get; set; } = "live"; public string? ReplayFile { get; set; } public bool ReplayCorrupt { get; set; } public int Retries { get; set; } = DefaultRetryCount; public int TimeoutMs { get; set; } = DefaultTimeoutMs; }
|
||||
|
||||
private static void RunLive(CliOptions options)
|
||||
{
|
||||
var timeout = TimeSpan.FromMilliseconds(options.TimeoutMs);
|
||||
using var transport = new SerialFrameTransport(options.Port, options.Baud, timeout);
|
||||
using var session = new Pn532Session(transport, timeout, options.Retries);
|
||||
session.Open();
|
||||
try
|
||||
{
|
||||
var result = RunPn532FelicaFlow(session);
|
||||
Console.WriteLine($"CardId: {Convert.ToHexString(result.CardId)}");
|
||||
Console.WriteLine($"AccessCode: {result.AccessCode}");
|
||||
}
|
||||
finally { session.Close(); }
|
||||
}
|
||||
|
||||
private static void RunReplay(CliOptions options)
|
||||
{
|
||||
var timeout = TimeSpan.FromMilliseconds(options.TimeoutMs);
|
||||
var frames = LoadReplayFrames(options.ReplayFile!);
|
||||
using var transport = new ReplayFrameTransport(frames, options.ReplayCorrupt);
|
||||
using var session = new Pn532Session(transport, timeout, options.Retries);
|
||||
session.Open();
|
||||
try
|
||||
{
|
||||
var result = RunPn532FelicaFlow(session);
|
||||
Console.WriteLine($"CardId: {Convert.ToHexString(result.CardId)}");
|
||||
Console.WriteLine($"AccessCode: {result.AccessCode}");
|
||||
}
|
||||
finally { session.Close(); }
|
||||
}
|
||||
|
||||
private static void RunDiag(CliOptions options)
|
||||
{
|
||||
using var serial = new SerialPort(options.Port, options.Baud) { ReadTimeout = 500, WriteTimeout = 500 };
|
||||
serial.Open();
|
||||
serial.Write(new byte[] { 0x55, 0x55, 0x00, 0x00, 0x00 }, 0, 5);
|
||||
Thread.Sleep(50);
|
||||
var fw = Pn532HsuFrame.BuildDataFrame(Pn532HsuFrame.HostToPn532Tfi, new byte[] { 0x02 });
|
||||
serial.Write(fw, 0, fw.Length);
|
||||
Thread.Sleep(200);
|
||||
var buf = new byte[serial.BytesToRead];
|
||||
serial.Read(buf, 0, buf.Length);
|
||||
Console.WriteLine($"RX: {Convert.ToHexString(buf)}");
|
||||
}
|
||||
|
||||
private enum CardKind { Felica, MifareClassic }
|
||||
private sealed record CardTarget(CardKind Kind, byte Tg, byte[] CardId);
|
||||
private sealed record FlowResult(byte[] CardId, string AccessCode);
|
||||
|
||||
private static FlowResult RunPn532FelicaFlow(Pn532Session session)
|
||||
{
|
||||
ExpectPn532ResponseCode(session.SendCommand(new byte[] { 0x02 }), expectedResponseCode: 0x03);
|
||||
ExpectPn532StatusOk(session.SendCommand(new byte[] { 0x14, 0x01, 0x14, 0x01 }), expectedResponseCode: 0x15);
|
||||
ExpectPn532StatusOk(session.SendCommand(new byte[] { 0x32, 0x01, 0x03 }), expectedResponseCode: 0x33);
|
||||
|
||||
var target = WaitForCard(session);
|
||||
Thread.Sleep(100);
|
||||
|
||||
if (target.Kind == CardKind.Felica)
|
||||
{
|
||||
Console.WriteLine($"Card detected! IDm: {Convert.ToHexString(target.CardId)}");
|
||||
var readCmd = FelicaCommandBuilder.BuildReadWithoutEncryptionCommand(target.CardId);
|
||||
var readResponse = SendInDataExchange(session, target.Tg, readCmd, TimeSpan.FromSeconds(5));
|
||||
var spad0 = FelicaResponseParser.ParseSpad0(readResponse);
|
||||
var decryptor = new FeliCaDecryptor();
|
||||
var decrypted = decryptor.Decrypt(spad0);
|
||||
var accessCode = AccessCodeFormatter.ToAccessCodeString(decrypted);
|
||||
return new FlowResult(target.CardId, accessCode);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Card detected! TypeA UID: {Convert.ToHexString(target.CardId)}");
|
||||
var m1AccessCode = TryReadMifareClassicAccessCode(session, target.Tg, target.CardId);
|
||||
if (m1AccessCode is null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to read Mifare Classic AccessCode: no key matched sector 0.");
|
||||
}
|
||||
|
||||
return new FlowResult(target.CardId, m1AccessCode);
|
||||
}
|
||||
|
||||
private static CardTarget WaitForCard(Pn532Session session)
|
||||
{
|
||||
var warned = false;
|
||||
while (true)
|
||||
{
|
||||
var f212 = session.SendCommand(new byte[] { 0x4A, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00 }, TimeSpan.FromMilliseconds(500));
|
||||
if (ExtractFeliCaTarget(f212, out var target)) return target;
|
||||
|
||||
var a106 = session.SendCommand(new byte[] { 0x4A, 0x01, 0x00 }, TimeSpan.FromMilliseconds(600));
|
||||
if (ExtractTypeATarget(a106, out target))
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
if (!warned) { Console.WriteLine("Waiting for Aime/FeliCa or M1 card..."); warned = true; }
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ExtractFeliCaTarget(Pn532FrameParseResult result, out CardTarget target)
|
||||
{
|
||||
target = null;
|
||||
if (result.Kind == Pn532FrameKind.Data && result.Payload.Length >= 15 && result.Payload[0] == 0x4B && result.Payload[1] > 0)
|
||||
{
|
||||
var tg = result.Payload[2];
|
||||
var idm = result.Payload.AsSpan(5, 8).ToArray();
|
||||
target = new CardTarget(CardKind.Felica, tg, idm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ExtractTypeATarget(Pn532FrameParseResult result, out CardTarget target)
|
||||
{
|
||||
target = null;
|
||||
if (result.Kind == Pn532FrameKind.Data && result.Payload.Length >= 8 && result.Payload[0] == 0x4B && result.Payload[1] > 0)
|
||||
{
|
||||
var tg = result.Payload[2];
|
||||
var uidLen = result.Payload[6];
|
||||
var uidOffset = 7;
|
||||
if (uidLen > 0 && result.Payload.Length >= uidOffset + uidLen)
|
||||
{
|
||||
var uid = result.Payload.AsSpan(uidOffset, uidLen).ToArray();
|
||||
target = new CardTarget(CardKind.MifareClassic, tg, uid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string? TryReadMifareClassicAccessCode(Pn532Session session, byte tg, ReadOnlySpan<byte> uid)
|
||||
{
|
||||
if (uid.Length < 4)
|
||||
{
|
||||
throw new InvalidOperationException("Mifare Classic UID is shorter than 4 bytes.");
|
||||
}
|
||||
|
||||
const byte blockNumber = 2; // sector 0 block 2
|
||||
var uid4 = uid[..4];
|
||||
|
||||
foreach (var keyHex in MifareClassicKeys)
|
||||
{
|
||||
var key = Convert.FromHexString(keyHex);
|
||||
if (TryMifareAuthenticate(session, tg, blockNumber, keyTypeA: true, key, uid4) ||
|
||||
TryMifareAuthenticate(session, tg, blockNumber, keyTypeA: false, key, uid4))
|
||||
{
|
||||
var block = ReadMifareBlock(session, tg, blockNumber);
|
||||
var hex = Convert.ToHexString(block);
|
||||
return hex.Length <= 20 ? hex : hex[^20..];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool TryMifareAuthenticate(
|
||||
Pn532Session session,
|
||||
byte tg,
|
||||
byte blockNumber,
|
||||
bool keyTypeA,
|
||||
ReadOnlySpan<byte> key,
|
||||
ReadOnlySpan<byte> uid4
|
||||
)
|
||||
{
|
||||
var cmd = new byte[2 + 1 + 1 + 6 + 4];
|
||||
cmd[0] = 0x40;
|
||||
cmd[1] = tg;
|
||||
cmd[2] = keyTypeA ? (byte)0x60 : (byte)0x61;
|
||||
cmd[3] = blockNumber;
|
||||
key.CopyTo(cmd.AsSpan(4, 6));
|
||||
uid4.CopyTo(cmd.AsSpan(10, 4));
|
||||
|
||||
var response = session.SendCommand(cmd, TimeSpan.FromMilliseconds(1000));
|
||||
if (response.Kind != Pn532FrameKind.Data || response.Payload.Length < 2 || response.Payload[0] != 0x41)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return response.Payload[1] == 0x00;
|
||||
}
|
||||
|
||||
private static byte[] ReadMifareBlock(Pn532Session session, byte tg, byte blockNumber)
|
||||
{
|
||||
var cmd = new byte[] { 0x40, tg, 0x30, blockNumber };
|
||||
var response = ExpectPn532ResponseCode(session.SendCommand(cmd, TimeSpan.FromMilliseconds(1200)), expectedResponseCode: 0x41);
|
||||
if (response.Payload.Length < 2 || response.Payload[1] != 0x00)
|
||||
{
|
||||
throw new InvalidOperationException($"Mifare read block failed: status=0x{(response.Payload.Length > 1 ? response.Payload[1] : 0):X2}");
|
||||
}
|
||||
|
||||
var data = response.Payload.Length == 2 ? Array.Empty<byte>() : response.Payload[2..];
|
||||
if (data.Length < 16)
|
||||
{
|
||||
throw new InvalidOperationException($"Mifare read block returned {data.Length} bytes, expected at least 16.");
|
||||
}
|
||||
|
||||
return data[..16];
|
||||
}
|
||||
|
||||
private static readonly string[] MifareClassicKeys =
|
||||
[
|
||||
"6090D00632F5","019761AA8082","574343467632","A99164400748","62742819AD7C","CC5075E42BA1",
|
||||
"B9DF35A0814C","8AF9C718F23D","58CD5C3673CB","FC80E88EB88C","7A3CDAD7C023","30424C029001",
|
||||
"024E4E44001F","ECBBFA57C6AD","4757698143BD","1D30972E6485","F8526D1A8D6D","1300EC8C7E80",
|
||||
"F80A65A87FFA","DEB06ED4AF8E","4AD96BF28190","000390014D41","0800F9917CB0","730050555253",
|
||||
"4146D4A956C4","131157FBB126","E69DD9015A43","337237F254D5","9A8389F32FBF","7B8FB4A7100B",
|
||||
"C8382A233993","7B304F2A12A6","FC9418BF788B"
|
||||
];
|
||||
|
||||
private static byte[] SendInDataExchange(Pn532Session session, byte tg, ReadOnlySpan<byte> payloadToTarget, TimeSpan? timeout = null)
|
||||
{
|
||||
var pn532Payload = new byte[2 + payloadToTarget.Length];
|
||||
pn532Payload[0] = 0x40; pn532Payload[1] = tg;
|
||||
payloadToTarget.CopyTo(pn532Payload.AsSpan(2));
|
||||
var response = ExpectPn532ResponseCode(session.SendCommand(pn532Payload, responseTimeout: timeout ?? TimeSpan.FromSeconds(4)), expectedResponseCode: 0x41);
|
||||
if (response.Payload.Length < 2 || response.Payload[1] != 0x00)
|
||||
throw new InvalidOperationException($"InDataExchange failed: 0x{(response.Payload.Length > 1 ? response.Payload[1] : 0):X2}");
|
||||
return response.Payload.Length == 2 ? Array.Empty<byte>() : response.Payload[2..];
|
||||
}
|
||||
|
||||
private static Pn532FrameParseResult ExpectPn532ResponseCode(Pn532FrameParseResult response, byte expectedResponseCode)
|
||||
{
|
||||
if (response.Kind != Pn532FrameKind.Data) throw new InvalidOperationException($"PN532 error: {response.Kind} ({response.Error}).");
|
||||
if (response.Payload.Length < 1 || response.Payload[0] != expectedResponseCode)
|
||||
throw new InvalidOperationException($"Expected 0x{expectedResponseCode:X2}, got 0x{(response.Payload.Length > 0 ? response.Payload[0] : 0):X2}");
|
||||
return response;
|
||||
}
|
||||
|
||||
private static void ExpectPn532StatusOk(Pn532FrameParseResult response, byte expectedResponseCode)
|
||||
{
|
||||
response = ExpectPn532ResponseCode(response, expectedResponseCode);
|
||||
if (response.Payload.Length >= 2 && response.Payload[1] != 0x00) throw new InvalidOperationException($"Status fail: 0x{response.Payload[1]:X2}");
|
||||
}
|
||||
|
||||
private static IReadOnlyList<byte[]> LoadReplayFrames(string path) => File.ReadAllLines(path).Where(l => !string.IsNullOrWhiteSpace(l) && !l.Trim().StartsWith('#')).Select(line => line.Split(' ').Select(s => Convert.ToByte(s, 16)).ToArray()).ToList();
|
||||
}
|
||||
47
src/NfcAime.Dll/AccessCodeFormatter.cs
Normal file
47
src/NfcAime.Dll/AccessCodeFormatter.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
|
||||
internal static class AccessCodeFormatter
|
||||
{
|
||||
private const int DecryptedByteCount = 16;
|
||||
private const int AccessCodeHexLength = 20;
|
||||
|
||||
internal static string ToAccessCodeString(ReadOnlySpan<byte> decryptedBytes)
|
||||
{
|
||||
if (decryptedBytes.Length != DecryptedByteCount)
|
||||
{
|
||||
throw new ArgumentException($"Expected {DecryptedByteCount} decrypted bytes.", nameof(decryptedBytes));
|
||||
}
|
||||
|
||||
var bytes = decryptedBytes.ToArray();
|
||||
var hex = BitConverter.ToString(bytes).Replace("-", "");
|
||||
return hex.Substring(hex.Length - AccessCodeHexLength);
|
||||
}
|
||||
|
||||
public static byte[] ToAccessCodeBytes(string accessCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(accessCode) || accessCode.Length != AccessCodeHexLength || !IsHexString(accessCode))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return HexStringToBytes(accessCode);
|
||||
}
|
||||
|
||||
private static bool IsHexString(string s)
|
||||
{
|
||||
return s.All(c => (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
||||
}
|
||||
|
||||
private static byte[] HexStringToBytes(string hex)
|
||||
{
|
||||
byte[] bytes = new byte[hex.Length / 2];
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
185
src/NfcAime.Dll/AimeReader.cs
Normal file
185
src/NfcAime.Dll/AimeReader.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using NfcAime.Dll.PN532;
|
||||
using static NfcAime.Dll.MiFareHandle;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
|
||||
public class AimeReader
|
||||
{
|
||||
private string Port = "COM15";
|
||||
private int Baud = 115200;
|
||||
|
||||
private Pn532Session session = null;
|
||||
|
||||
public enum CardKind { Felica, MifareClassic, Null}
|
||||
private sealed record FlowResult(CardKind CardKind, byte[] CardId, string AccessCode);
|
||||
private sealed record CardTarget(CardKind Kind, byte Tg, byte[] CardId);
|
||||
|
||||
// 兼容低版本 .NET:实现 ToHexString 和 FromHexString
|
||||
public static string ToHexString(byte[] bytes)
|
||||
{
|
||||
return BitConverter.ToString(bytes).Replace("-", "");
|
||||
}
|
||||
|
||||
public static byte[] FromHexString(string hex)
|
||||
{
|
||||
if (hex.Length % 2 != 0)
|
||||
throw new ArgumentException("Invalid hex string length.");
|
||||
var bytes = new byte[hex.Length / 2];
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public AimeReader(string port, int baud)
|
||||
{
|
||||
Port = port;
|
||||
Baud = baud;
|
||||
TimeSpan timeout = TimeSpan.FromMilliseconds(100);
|
||||
using var transport = new SerialFrameTransport(Port, Baud, timeout);
|
||||
session = new Pn532Session(transport, timeout, 0);
|
||||
}
|
||||
|
||||
public (CardKind CardKind, byte[] IDm, string AccessCode) ReadCard()
|
||||
{
|
||||
session.Open();
|
||||
try
|
||||
{
|
||||
var result = RunPn532Flow(session);
|
||||
return (result.CardKind , result.CardId, result.AccessCode);
|
||||
}
|
||||
finally { session.Close(); }
|
||||
}
|
||||
|
||||
public void CloseReader()
|
||||
{
|
||||
session.Close();
|
||||
}
|
||||
|
||||
private FlowResult RunPn532Flow(Pn532Session session)
|
||||
{
|
||||
ExpectPn532ResponseCode(session.SendCommand(new byte[] { 0x02 }), expectedResponseCode: 0x03);
|
||||
ExpectPn532StatusOk(session.SendCommand(new byte[] { 0x14, 0x01, 0x14, 0x01 }), expectedResponseCode: 0x15);
|
||||
ExpectPn532StatusOk(session.SendCommand(new byte[] { 0x32, 0x01, 0x03 }), expectedResponseCode: 0x33);
|
||||
|
||||
var target = WaitForCard(session);
|
||||
Thread.Sleep(100);
|
||||
if (target.Kind == CardKind.Null)
|
||||
{
|
||||
return new FlowResult(target.Kind, target.CardId, "");
|
||||
}
|
||||
if (target.Kind == CardKind.Felica)
|
||||
{
|
||||
Console.WriteLine($"Card detected! IDm: {ToHexString(target.CardId)}");
|
||||
var readCmd = FelicaCommandBuilder.BuildReadWithoutEncryptionCommand(target.CardId);
|
||||
var readResponse = SendInDataExchange(session, target.Tg, readCmd, TimeSpan.FromSeconds(5));
|
||||
var spad0 = FelicaResponseParser.ParseSpad0(readResponse);
|
||||
var decryptor = new FeliCaDecryptor();
|
||||
var decrypted = decryptor.Decrypt(spad0);
|
||||
var accessCode = AccessCodeFormatter.ToAccessCodeString(decrypted);
|
||||
return new FlowResult(target.Kind, target.CardId, accessCode);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Card detected! TypeA UID: {ToHexString(target.CardId)}");
|
||||
var m1AccessCode = TryReadMifareClassicAccessCode(session, target.Tg, target.CardId);
|
||||
if (m1AccessCode is null)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to read Mifare Classic AccessCode: no key matched sector 0.");
|
||||
}
|
||||
|
||||
return new FlowResult(target.Kind, target.CardId, m1AccessCode);
|
||||
}
|
||||
|
||||
private byte[] SendInDataExchange(Pn532Session session, byte tg, ReadOnlySpan<byte> payloadToTarget, TimeSpan? timeout = null)
|
||||
{
|
||||
var pn532Payload = new byte[2 + payloadToTarget.Length];
|
||||
pn532Payload[0] = 0x40; pn532Payload[1] = tg;
|
||||
payloadToTarget.CopyTo(pn532Payload.AsSpan(2));
|
||||
var response = ExpectPn532ResponseCode(session.SendCommand(pn532Payload, responseTimeout: timeout ?? TimeSpan.FromSeconds(4)), expectedResponseCode: 0x41);
|
||||
if (response.Payload.Length < 2 || response.Payload[1] != 0x00)
|
||||
throw new InvalidOperationException($"InDataExchange failed: 0x{(response.Payload.Length > 1 ? response.Payload[1] : 0):X2}");
|
||||
// 替换 System.Range 语法
|
||||
return response.Payload.Length == 2 ? Array.Empty<byte>() : response.Payload.Skip(2).ToArray();
|
||||
}
|
||||
|
||||
private string? TryReadMifareClassicAccessCode(Pn532Session session, byte tg, ReadOnlySpan<byte> uid)
|
||||
{
|
||||
const byte blockNumber = 2; // sector 0 block 2
|
||||
var uid4 = new byte[4];
|
||||
Array.Copy(uid.ToArray(), 0, uid4, 0, 4);
|
||||
|
||||
foreach (var keyHex in MifareClassicKeys)
|
||||
{
|
||||
var key = FromHexString(keyHex);
|
||||
if (TryMifareAuthenticate(session, tg, blockNumber, keyTypeA: true, key, uid4) ||
|
||||
TryMifareAuthenticate(session, tg, blockNumber, keyTypeA: false, key, uid4))
|
||||
{
|
||||
var block = ReadMifareBlock(session, tg, blockNumber);
|
||||
var hex = ToHexString(block);
|
||||
|
||||
return hex.Length <= 20 ? hex : hex.Substring(hex.Length - 20, 20);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Pn532FrameParseResult ExpectPn532ResponseCode(Pn532FrameParseResult response, byte expectedResponseCode)
|
||||
{
|
||||
if (response.Kind != Pn532FrameKind.Data) throw new InvalidOperationException($"PN532 error: {response.Kind} ({response.Error}).");
|
||||
if (response.Payload.Length < 1 || response.Payload[0] != expectedResponseCode)
|
||||
throw new InvalidOperationException($"Expected 0x{expectedResponseCode:X2}, got 0x{(response.Payload.Length > 0 ? response.Payload[0] : 0):X2}");
|
||||
return response;
|
||||
}
|
||||
|
||||
private void ExpectPn532StatusOk(Pn532FrameParseResult response, byte expectedResponseCode)
|
||||
{
|
||||
response = ExpectPn532ResponseCode(response, expectedResponseCode);
|
||||
if (response.Payload.Length >= 2 && response.Payload[1] != 0x00) throw new InvalidOperationException($"Status fail: 0x{response.Payload[1]:X2}");
|
||||
}
|
||||
|
||||
private CardTarget WaitForCard(Pn532Session session)
|
||||
{
|
||||
var f212 = session.SendCommand(new byte[] { 0x4A, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x00 }, TimeSpan.FromMilliseconds(500));
|
||||
if (ExtractFeliCaTarget(f212, out var target)) return target;
|
||||
|
||||
var a106 = session.SendCommand(new byte[] { 0x4A, 0x01, 0x00 }, TimeSpan.FromMilliseconds(600));
|
||||
if (ExtractTypeATarget(a106, out target)) return target;
|
||||
|
||||
return new CardTarget(CardKind.Null, 0, [0]);
|
||||
}
|
||||
|
||||
private bool ExtractFeliCaTarget(Pn532FrameParseResult result, out CardTarget target)
|
||||
{
|
||||
target = null;
|
||||
if (result.Kind == Pn532FrameKind.Data && result.Payload.Length >= 15 && result.Payload[0] == 0x4B && result.Payload[1] > 0)
|
||||
{
|
||||
var tg = result.Payload[2];
|
||||
var idm = result.Payload.AsSpan(5, 8).ToArray();
|
||||
target = new CardTarget(CardKind.Felica, tg, idm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ExtractTypeATarget(Pn532FrameParseResult result, out CardTarget target)
|
||||
{
|
||||
target = null;
|
||||
if (result.Kind == Pn532FrameKind.Data && result.Payload.Length >= 8 && result.Payload[0] == 0x4B && result.Payload[1] > 0)
|
||||
{
|
||||
var tg = result.Payload[2];
|
||||
var uidLen = result.Payload[6];
|
||||
var uidOffset = 7;
|
||||
if (uidLen > 0 && result.Payload.Length >= uidOffset + uidLen)
|
||||
{
|
||||
var uid = result.Payload.AsSpan(uidOffset, uidLen).ToArray();
|
||||
target = new CardTarget(CardKind.MifareClassic, tg, uid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
38
src/NfcAime.Dll/Config.cs
Normal file
38
src/NfcAime.Dll/Config.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
|
||||
internal static class Config
|
||||
{
|
||||
internal const string IOSection = "aimeio";
|
||||
private const string ConfigFileName = @".\segatools.ini";
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern uint GetPrivateProfileString(
|
||||
string lpAppName,
|
||||
string lpKeyName,
|
||||
string lpDefault,
|
||||
StringBuilder lpReturnedString,
|
||||
uint nSize,
|
||||
string lpFileName);
|
||||
|
||||
static Config()
|
||||
{
|
||||
IDmMode = Convert.ToInt32(ReadKey(IOSection, "IDmMode", 1024, "1"));
|
||||
ReaderCOM = ReadKey(IOSection, "ReaderCOM" , 1024, "COM3");
|
||||
ReaderBaud = Convert.ToInt32(ReadKey(IOSection, "Baud" , 1024, "115200"));
|
||||
}
|
||||
|
||||
public static int IDmMode { get; }
|
||||
public static string ReaderCOM { get; }
|
||||
public static int ReaderBaud { get; }
|
||||
|
||||
internal static string ReadKey(string section, string key, uint maxLength, string @default = null)
|
||||
{
|
||||
var sb = new StringBuilder((int)maxLength);
|
||||
GetPrivateProfileString(section, key, @default ?? string.Empty, sb, maxLength, ConfigFileName);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
400
src/NfcAime.Dll/FeliCaDecryptor.cs
Normal file
400
src/NfcAime.Dll/FeliCaDecryptor.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
|
||||
internal sealed class FeliCaDecryptor
|
||||
{
|
||||
private const int IterAdd = 5;
|
||||
private static readonly byte[][] SBox = new byte[][]
|
||||
{
|
||||
new byte[]
|
||||
{
|
||||
0xaf, 0xa8, 0x63, 0xa9, 0x24, 0xc0, 0xf1, 0xbe, 0xe7, 0xb3, 0xac, 0x16, 0x85, 0xb9, 0x11, 0x6f,
|
||||
0x10, 0x31, 0x32, 0xae, 0xb1, 0x78, 0x23, 0xd2, 0xa2, 0xca, 0xf9, 0x2b, 0x45, 0xb8, 0x3c, 0x62,
|
||||
0x1a, 0x6d, 0x82, 0x8c, 0x00, 0xd9, 0xe2, 0x7a, 0x46, 0x19, 0x2a, 0xcc, 0x13, 0xa7, 0xe4, 0xb7,
|
||||
0x84, 0x9b, 0x8e, 0xff, 0xfb, 0x8b, 0x03, 0x89, 0x30, 0x29, 0xd3, 0x87, 0x01, 0xbf, 0x35, 0xda,
|
||||
0x59, 0x5c, 0xcd, 0x09, 0x7f, 0x70, 0x41, 0x81, 0x6c, 0x5f, 0x53, 0xc2, 0x72, 0xd4, 0x79, 0x48,
|
||||
0x95, 0xb4, 0x5b, 0x39, 0x44, 0x9d, 0xfd, 0x51, 0x71, 0xf6, 0xd7, 0xd5, 0x2d, 0x9f, 0xed, 0x33,
|
||||
0xea, 0xaa, 0x5a, 0x17, 0x6a, 0xba, 0xbd, 0xe9, 0x9c, 0xce, 0x4d, 0x7c, 0x18, 0x3b, 0x0d, 0xee,
|
||||
0x0c, 0x80, 0xa1, 0x0a, 0x8f, 0xb5, 0x6b, 0xf7, 0x3f, 0xd1, 0x47, 0x25, 0xc6, 0x36, 0xe8, 0x7d,
|
||||
0x7b, 0x3e, 0xf3, 0x99, 0x75, 0x05, 0x1c, 0xe0, 0xcf, 0x34, 0xbb, 0x9e, 0xeb, 0x91, 0xad, 0x26,
|
||||
0x8d, 0xc7, 0x66, 0x08, 0xe6, 0x58, 0x40, 0x14, 0xa6, 0xc5, 0xfa, 0x12, 0xec, 0xc4, 0xf0, 0x21,
|
||||
0x8a, 0x86, 0xd6, 0xe1, 0x06, 0xb6, 0x60, 0x74, 0x2e, 0xe3, 0xfe, 0x27, 0x2f, 0x5e, 0xf4, 0xb2,
|
||||
0x68, 0x28, 0x56, 0xef, 0x9a, 0x61, 0x22, 0xa3, 0xc9, 0x0b, 0x02, 0x96, 0x4f, 0xdb, 0xe5, 0x90,
|
||||
0x4a, 0x4c, 0x92, 0xcb, 0x98, 0x69, 0xd0, 0x57, 0x64, 0x0e, 0x37, 0x50, 0xab, 0xbc, 0xde, 0x76,
|
||||
0x07, 0xc8, 0x3d, 0x49, 0xfc, 0x77, 0x94, 0xf8, 0xc1, 0x97, 0x65, 0x3a, 0x4b, 0x83, 0x6e, 0x73,
|
||||
0x88, 0x1e, 0x5d, 0x04, 0x54, 0x67, 0xdf, 0x15, 0xb0, 0xc3, 0x93, 0x2c, 0x38, 0xdd, 0xf5, 0xa4,
|
||||
0x55, 0x0f, 0x4e, 0x1d, 0xa0, 0x1f, 0x20, 0xa5, 0xdc, 0x43, 0xf2, 0xd8, 0x7e, 0x52, 0x1b, 0x42
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x03, 0x4b, 0xb8, 0x97, 0xb7, 0x8f, 0x0d, 0x71, 0xdc, 0x49, 0xa7, 0x90, 0xff, 0xa0, 0x9d, 0xf8,
|
||||
0x23, 0x5f, 0x20, 0x6c, 0x11, 0xf9, 0x69, 0x8e, 0xef, 0xcb, 0xf0, 0x14, 0xdf, 0x1b, 0x7a, 0xc6,
|
||||
0x36, 0x5b, 0xc5, 0x93, 0xb6, 0x28, 0x35, 0xb2, 0x3b, 0xd1, 0xec, 0xf3, 0x9f, 0x80, 0x64, 0x25,
|
||||
0x05, 0xcf, 0x54, 0xaf, 0x46, 0x3f, 0x96, 0x06, 0xe5, 0x10, 0xfb, 0xaa, 0xde, 0x62, 0x41, 0x51,
|
||||
0x59, 0xe7, 0x29, 0xb5, 0x4d, 0x3d, 0xf2, 0x37, 0x6a, 0xe8, 0x16, 0x8b, 0x5c, 0xa8, 0x1e, 0x57,
|
||||
0xe2, 0x09, 0xeb, 0x32, 0xc9, 0x2e, 0xb4, 0xf1, 0xa4, 0x76, 0xd3, 0xc1, 0xa5, 0x70, 0x60, 0x12,
|
||||
0xb1, 0xea, 0x66, 0xe1, 0x4a, 0x92, 0xa1, 0x6d, 0x3a, 0xf4, 0xd5, 0x6f, 0xda, 0xd8, 0x0a, 0x58,
|
||||
0x18, 0x63, 0x2b, 0xa3, 0x5d, 0x67, 0x77, 0x6b, 0x39, 0xc3, 0xdb, 0x4c, 0x02, 0xba, 0xce, 0xbb,
|
||||
0x55, 0x01, 0x17, 0x85, 0x27, 0xe4, 0x30, 0x72, 0xc8, 0x8d, 0xfc, 0x78, 0x19, 0xb9, 0x43, 0x2d,
|
||||
0x08, 0x9b, 0x1a, 0xd9, 0xcd, 0x26, 0xfa, 0x82, 0xcc, 0x7f, 0x75, 0x79, 0x9e, 0x65, 0xfe, 0xed,
|
||||
0x91, 0xc7, 0x31, 0xd0, 0x52, 0x2a, 0x48, 0x84, 0x74, 0xdd, 0x7d, 0xe9, 0xae, 0x6e, 0x33, 0x47,
|
||||
0xd6, 0x50, 0x0c, 0x53, 0xa6, 0xbd, 0x22, 0xac, 0x8a, 0x04, 0x89, 0x42, 0x40, 0xb3, 0x45, 0x5a,
|
||||
0x44, 0xe6, 0xa9, 0x1d, 0xc4, 0x81, 0xe3, 0x15, 0x1f, 0x56, 0x87, 0xd4, 0x3e, 0x0f, 0x95, 0xd7,
|
||||
0xe0, 0x94, 0xab, 0x24, 0x68, 0x07, 0xa2, 0x98, 0x7b, 0xee, 0xf5, 0xb0, 0x88, 0xc0, 0x38, 0x9c,
|
||||
0x99, 0x5e, 0xad, 0xd2, 0x3c, 0x2c, 0x21, 0xca, 0x8c, 0x7e, 0x1c, 0x61, 0xc2, 0x34, 0x7c, 0x2f,
|
||||
0x0b, 0x83, 0x9a, 0x4f, 0xbe, 0x4e, 0xbc, 0x73, 0x13, 0x00, 0xf6, 0x0e, 0x86, 0xbf, 0xfd, 0xf7
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x41, 0x07, 0x93, 0x8a, 0x40, 0xff, 0x0e, 0x2f, 0x54, 0xfc, 0xac, 0xd4, 0x1e, 0xb0, 0xc8, 0x89,
|
||||
0xdf, 0x00, 0x64, 0x4f, 0xf3, 0xcb, 0x2c, 0xc0, 0x8f, 0x6e, 0x17, 0xbf, 0x95, 0x99, 0x2a, 0x0d,
|
||||
0x51, 0xad, 0x4b, 0xca, 0xf7, 0x69, 0x55, 0x38, 0x4c, 0xc6, 0x68, 0xc3, 0xa3, 0x1f, 0x83, 0x3e,
|
||||
0x0b, 0x70, 0xc7, 0xb2, 0x4e, 0xcf, 0xfe, 0x4d, 0xfd, 0x8b, 0xb4, 0x96, 0xce, 0xf9, 0xf1, 0xd3,
|
||||
0x43, 0x60, 0xae, 0xd5, 0xb3, 0xdb, 0x4a, 0xb9, 0xe5, 0xea, 0x6a, 0x48, 0x5d, 0xa7, 0x87, 0xda,
|
||||
0xb8, 0x06, 0x2d, 0x94, 0xc1, 0x66, 0x24, 0x26, 0xb1, 0x9a, 0x25, 0x08, 0x5c, 0x59, 0xa6, 0xaf,
|
||||
0x27, 0x92, 0x88, 0xf2, 0xed, 0x1b, 0x1c, 0x11, 0xd2, 0x22, 0x16, 0x76, 0x56, 0x67, 0x14, 0x9f,
|
||||
0xee, 0x5f, 0xdc, 0x5b, 0xa4, 0x46, 0xdd, 0x10, 0x33, 0x29, 0x7f, 0x5a, 0xc9, 0x97, 0x84, 0x85,
|
||||
0x42, 0xbc, 0xab, 0x77, 0x3c, 0xa1, 0xc2, 0xaa, 0x37, 0x49, 0x61, 0x35, 0x9c, 0x86, 0x19, 0x57,
|
||||
0x5e, 0xe6, 0xe2, 0xa0, 0x04, 0x6d, 0xd1, 0xa8, 0x9d, 0xec, 0x02, 0xbd, 0xd8, 0x47, 0x62, 0x1a,
|
||||
0x05, 0x7d, 0x01, 0x3f, 0x18, 0x6f, 0x75, 0x44, 0xe4, 0x7a, 0xf6, 0xa9, 0xe8, 0x72, 0xf4, 0x6b,
|
||||
0xfb, 0x7e, 0xeb, 0x36, 0x52, 0xde, 0x79, 0x23, 0x8d, 0xe7, 0x71, 0x63, 0x98, 0x6c, 0xd9, 0xe9,
|
||||
0x3b, 0x13, 0x90, 0x03, 0x78, 0x74, 0x20, 0x81, 0xba, 0x34, 0x32, 0xfa, 0x7b, 0x31, 0xd6, 0x3d,
|
||||
0x30, 0x0a, 0xe3, 0x12, 0x8e, 0x0f, 0x1d, 0xbb, 0xa5, 0x73, 0x53, 0x82, 0x80, 0xb5, 0xd7, 0xe1,
|
||||
0x3a, 0x2b, 0xd0, 0xe0, 0x21, 0xbe, 0xcd, 0x15, 0x65, 0xb6, 0xef, 0xcc, 0x91, 0xf5, 0x0c, 0xc4,
|
||||
0x2e, 0x9e, 0x58, 0xf0, 0xc5, 0x9b, 0x39, 0x09, 0xa2, 0x7c, 0x45, 0xb7, 0x28, 0x8c, 0xf8, 0x50
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x76, 0xcd, 0x16, 0x7f, 0xe9, 0x6b, 0x95, 0x3a, 0x4e, 0xbb, 0x6e, 0xcc, 0xd2, 0xb3, 0x69, 0xa9,
|
||||
0x72, 0xaf, 0xc7, 0x82, 0x38, 0x6f, 0xbc, 0x37, 0xa6, 0x28, 0x36, 0xbd, 0xc3, 0xcf, 0x85, 0x46,
|
||||
0x0a, 0x99, 0x63, 0x97, 0x83, 0x56, 0x88, 0xa1, 0x02, 0x64, 0xac, 0x79, 0x55, 0x65, 0xff, 0xc2,
|
||||
0xef, 0x7a, 0x59, 0x2b, 0x1c, 0xe0, 0x8c, 0x8f, 0x5a, 0xe4, 0xb4, 0x9f, 0xfc, 0xf7, 0x45, 0x47,
|
||||
0x4a, 0xea, 0xc0, 0x0d, 0xeb, 0xc8, 0x78, 0xc9, 0x67, 0x03, 0x05, 0x31, 0x14, 0xde, 0xd3, 0x73,
|
||||
0xfa, 0x1f, 0xb6, 0x50, 0x13, 0x04, 0xb7, 0x7b, 0x49, 0x1a, 0xf9, 0x17, 0x4d, 0x4f, 0x86, 0x20,
|
||||
0xc1, 0xfd, 0xa0, 0xa5, 0x48, 0xe1, 0x3c, 0x60, 0xf2, 0x0b, 0x30, 0x0f, 0x4c, 0xee, 0x8d, 0x6d,
|
||||
0xbe, 0x8e, 0xc6, 0xf6, 0xd8, 0x08, 0x94, 0x1b, 0x2a, 0xb5, 0x9e, 0xa2, 0x93, 0xf5, 0x22, 0xa8,
|
||||
0x34, 0x26, 0xa7, 0x7c, 0xec, 0x3f, 0x0e, 0x3b, 0xb2, 0xf4, 0x29, 0xce, 0x2e, 0x42, 0x81, 0x32,
|
||||
0x25, 0x5f, 0x66, 0xdb, 0x7d, 0xd7, 0xe7, 0x2d, 0x43, 0x6a, 0x5b, 0x19, 0xad, 0x3e, 0x9b, 0xc5,
|
||||
0xab, 0x84, 0xc4, 0x6c, 0x33, 0xda, 0xfb, 0x8a, 0x11, 0x54, 0xfe, 0x57, 0x4b, 0x3d, 0x7e, 0x09,
|
||||
0xa3, 0x91, 0x40, 0xbf, 0x68, 0xe5, 0xb9, 0xf3, 0x96, 0xf8, 0x51, 0xca, 0x9a, 0xb0, 0x98, 0xae,
|
||||
0x1e, 0x9c, 0x5e, 0xe8, 0x00, 0x39, 0x12, 0xdc, 0x0c, 0x10, 0x8b, 0x5d, 0xd6, 0x2c, 0x27, 0xd5,
|
||||
0x61, 0x87, 0x53, 0xba, 0x23, 0x71, 0x2f, 0x1d, 0x74, 0x89, 0x9d, 0xdd, 0x35, 0x15, 0xd0, 0x44,
|
||||
0xb1, 0xcb, 0xf0, 0x62, 0x58, 0xa4, 0x77, 0xe3, 0x18, 0xd4, 0xe6, 0x80, 0xd1, 0xaa, 0x90, 0x70,
|
||||
0x01, 0x24, 0xf1, 0x21, 0xd9, 0x06, 0x75, 0xb8, 0x41, 0x5c, 0x92, 0xdf, 0xed, 0x07, 0xe2, 0x52
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xaa, 0x94, 0x99, 0x74, 0x92, 0xd6, 0x1c, 0x46, 0x47, 0x79, 0x2f, 0x56, 0x7a, 0xf7, 0xb5, 0x0a,
|
||||
0xc9, 0x06, 0xd7, 0xfa, 0x40, 0x11, 0xa5, 0xff, 0x1f, 0xe8, 0x00, 0x8c, 0xdd, 0xb7, 0x42, 0x6e,
|
||||
0x35, 0xf0, 0xbe, 0xc2, 0x1d, 0xca, 0xb6, 0x8e, 0xe2, 0x44, 0x4e, 0x9f, 0x51, 0x81, 0x8b, 0x4c,
|
||||
0x5d, 0x59, 0x38, 0xd3, 0x8f, 0x25, 0xb3, 0x09, 0x31, 0xad, 0x0b, 0x6b, 0x0e, 0x50, 0xbb, 0xd8,
|
||||
0x96, 0x88, 0xde, 0x49, 0xea, 0xb4, 0x4a, 0x9a, 0x30, 0x6d, 0x2c, 0x0f, 0xcd, 0xf6, 0x7e, 0x4f,
|
||||
0x52, 0xd9, 0xfb, 0x28, 0x07, 0x63, 0x19, 0xa4, 0xee, 0x01, 0xf8, 0xeb, 0xd2, 0xf5, 0x3d, 0x0d,
|
||||
0x91, 0x5b, 0x67, 0xb8, 0x57, 0xf3, 0x2b, 0xe6, 0x68, 0x16, 0x7b, 0x6f, 0x65, 0xc3, 0x76, 0xe3,
|
||||
0x3c, 0x2e, 0x41, 0x3e, 0xcf, 0x54, 0xe1, 0x17, 0xa7, 0x45, 0x2d, 0x5a, 0xfc, 0xc6, 0x82, 0xef,
|
||||
0xcb, 0x78, 0xac, 0x34, 0xba, 0x71, 0x27, 0x22, 0xf2, 0x12, 0x3f, 0x1a, 0x24, 0xa8, 0xc1, 0x9e,
|
||||
0xc7, 0x18, 0xe4, 0x5f, 0xfe, 0x6c, 0xaf, 0xc8, 0x37, 0xc5, 0x77, 0xbd, 0x02, 0x29, 0x7d, 0xfd,
|
||||
0x5e, 0x83, 0xe9, 0xbf, 0x2a, 0x5c, 0x73, 0xe5, 0xda, 0xe7, 0xec, 0x80, 0x6a, 0x03, 0x58, 0x3b,
|
||||
0x21, 0x13, 0xd1, 0x53, 0x70, 0x93, 0xbc, 0xa1, 0x10, 0xf4, 0x64, 0x85, 0x87, 0xb0, 0x61, 0xb1,
|
||||
0xf1, 0x43, 0x62, 0xc4, 0xf9, 0xa2, 0xae, 0xd4, 0x04, 0x86, 0xdf, 0x26, 0xd0, 0xab, 0x36, 0x1e,
|
||||
0x4b, 0xe0, 0x60, 0x90, 0xcc, 0x98, 0x95, 0x3a, 0x9c, 0x9b, 0x15, 0x1b, 0x33, 0x8d, 0x4d, 0x23,
|
||||
0x9d, 0x84, 0xce, 0xb2, 0x05, 0xa6, 0x0c, 0xb9, 0x14, 0x48, 0xd5, 0xa3, 0x75, 0x08, 0x7c, 0xed,
|
||||
0x7f, 0xdc, 0x89, 0x66, 0xa9, 0xc0, 0x39, 0x72, 0x20, 0x97, 0x8a, 0xa0, 0x55, 0x69, 0xdb, 0x32
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xde, 0x5a, 0x1b, 0x69, 0x3c, 0x42, 0xa3, 0x51, 0x40, 0x38, 0xf1, 0x17, 0x28, 0x1c, 0xb8, 0xac,
|
||||
0xe8, 0x99, 0x45, 0xa0, 0x12, 0x68, 0xce, 0x15, 0xc9, 0x47, 0x6d, 0xc8, 0x46, 0x4e, 0x1e, 0xa6,
|
||||
0x11, 0xf8, 0x3e, 0x31, 0xfa, 0xe6, 0x2d, 0xfd, 0x32, 0x14, 0x9c, 0x01, 0xb1, 0xed, 0x57, 0x71,
|
||||
0xe3, 0x30, 0xcf, 0x0e, 0xe7, 0xcc, 0x37, 0x4a, 0x78, 0xab, 0x1d, 0x96, 0xea, 0x25, 0x85, 0x20,
|
||||
0x49, 0x4d, 0x75, 0x52, 0xee, 0xef, 0xaf, 0x54, 0x33, 0xf9, 0x09, 0x9e, 0x81, 0xe1, 0x73, 0x2a,
|
||||
0xf2, 0x3d, 0x22, 0x13, 0x92, 0x8b, 0xf0, 0xc0, 0x84, 0x0d, 0xa8, 0x04, 0xdc, 0x8d, 0xbb, 0x2c,
|
||||
0x94, 0x03, 0xf5, 0xd1, 0xd0, 0x2e, 0x5d, 0xeb, 0x5e, 0x62, 0x6f, 0x21, 0xb0, 0x91, 0xfe, 0xcb,
|
||||
0x0a, 0xec, 0xb7, 0x2f, 0x34, 0x24, 0xd6, 0xe9, 0x66, 0x82, 0xc6, 0x19, 0x1f, 0xd9, 0xc1, 0xa7,
|
||||
0xf6, 0x88, 0x02, 0x87, 0x07, 0xdd, 0xd8, 0x67, 0xc5, 0xb9, 0x29, 0xa4, 0xc2, 0x65, 0xd3, 0x53,
|
||||
0xaa, 0x3b, 0x79, 0xe0, 0x72, 0xd7, 0x06, 0x1a, 0x58, 0x93, 0x7e, 0x0c, 0x90, 0x6e, 0x74, 0x0f,
|
||||
0x4c, 0x0b, 0xe4, 0x6c, 0x43, 0xd2, 0x41, 0x36, 0xbd, 0xdb, 0x35, 0x59, 0x5f, 0xa9, 0x9f, 0x55,
|
||||
0x80, 0x39, 0x8e, 0xf3, 0xd5, 0xe5, 0x63, 0xc3, 0x77, 0xa2, 0x5b, 0x7b, 0x7f, 0xae, 0xba, 0xa1,
|
||||
0x10, 0xdf, 0x4b, 0x2b, 0x9b, 0x16, 0xbc, 0x00, 0xc7, 0x18, 0xfb, 0x64, 0xff, 0xf4, 0xf7, 0xbe,
|
||||
0x05, 0x56, 0xcd, 0x08, 0x4f, 0x6a, 0x9a, 0x76, 0x7a, 0xb5, 0x95, 0xca, 0x50, 0x7d, 0x7c, 0x6b,
|
||||
0xa5, 0xc4, 0x3a, 0x8a, 0x27, 0xda, 0xb6, 0x61, 0x23, 0x86, 0x70, 0x48, 0x97, 0x9d, 0xfc, 0x8f,
|
||||
0x5c, 0x44, 0xb2, 0xad, 0x83, 0xbf, 0x26, 0xd4, 0xb3, 0x60, 0x98, 0xe2, 0x3f, 0x8c, 0xb4, 0x89
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x19, 0x2e, 0x5d, 0x7a, 0x5b, 0x7c, 0x7e, 0x39, 0xef, 0x8b, 0x2f, 0xcc, 0x86, 0x1b, 0xc2, 0x8c,
|
||||
0x20, 0xfb, 0x59, 0x5f, 0x21, 0xcd, 0x9f, 0x94, 0xa7, 0x0b, 0xba, 0x0e, 0x47, 0x7b, 0xa5, 0x06,
|
||||
0xb7, 0xd8, 0xc9, 0x26, 0xf0, 0x9d, 0x68, 0xf2, 0x5e, 0x12, 0x3a, 0x65, 0xb0, 0x5c, 0xe2, 0x22,
|
||||
0x99, 0x82, 0x10, 0xf5, 0x44, 0x50, 0x74, 0xa1, 0xf9, 0x42, 0xa8, 0x25, 0x4d, 0xea, 0x08, 0x3e,
|
||||
0x96, 0x92, 0xc4, 0x2d, 0xa2, 0x23, 0x34, 0xa9, 0x45, 0x6b, 0x79, 0x7f, 0xc8, 0x4c, 0x43, 0x67,
|
||||
0x29, 0x04, 0xfa, 0xb5, 0x1e, 0x05, 0xc6, 0xb4, 0xaf, 0x1d, 0xbb, 0xf7, 0x30, 0xed, 0xec, 0xab,
|
||||
0x40, 0xe0, 0x98, 0xe9, 0x4b, 0x11, 0x87, 0x15, 0x18, 0xbe, 0xee, 0x78, 0xb1, 0x02, 0x57, 0x90,
|
||||
0x32, 0x8e, 0x61, 0xcb, 0x3c, 0xe6, 0xd7, 0x75, 0x31, 0xb9, 0x54, 0x36, 0xe1, 0xdd, 0x6c, 0x97,
|
||||
0x62, 0xac, 0x41, 0x89, 0x9c, 0x60, 0xf6, 0x63, 0xbc, 0x14, 0x7d, 0x77, 0xd5, 0x09, 0x95, 0xdf,
|
||||
0x17, 0x4a, 0x0c, 0x2b, 0x52, 0x38, 0xfe, 0xb6, 0x33, 0xa4, 0x51, 0x73, 0x6d, 0x4f, 0xb3, 0xbf,
|
||||
0x71, 0xeb, 0x69, 0x3f, 0x13, 0x00, 0x1c, 0x6f, 0x9b, 0x35, 0xca, 0xa3, 0x2a, 0x76, 0x8d, 0x80,
|
||||
0x01, 0x16, 0x03, 0x3d, 0xe5, 0x83, 0x70, 0x8f, 0x5a, 0x58, 0x88, 0xf3, 0xe3, 0x0f, 0xc3, 0x07,
|
||||
0x46, 0xff, 0x49, 0xfd, 0xe8, 0xce, 0x85, 0xd1, 0xdc, 0xf1, 0xb8, 0xd3, 0x6e, 0xd4, 0xa0, 0xad,
|
||||
0x72, 0xda, 0xe7, 0x3b, 0x84, 0xf8, 0xd2, 0x28, 0xde, 0xc1, 0x1a, 0x64, 0x91, 0x0a, 0xe4, 0xc5,
|
||||
0x24, 0x6a, 0xd6, 0x53, 0xc7, 0x81, 0x2c, 0xc0, 0x93, 0xd9, 0x0d, 0xa6, 0xdb, 0xae, 0xcf, 0xf4,
|
||||
0x55, 0x66, 0x1f, 0x37, 0xfc, 0xbd, 0x48, 0xaa, 0x4e, 0x56, 0x27, 0x9e, 0xd0, 0x8a, 0xb2, 0x9a
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x54, 0xd9, 0x42, 0x50, 0xb8, 0x74, 0xab, 0x32, 0xa6, 0x24, 0x48, 0x6f, 0xf0, 0x7e, 0xd7, 0x30,
|
||||
0xb4, 0x6d, 0x06, 0xa2, 0x88, 0xd6, 0x59, 0x39, 0x4c, 0x2c, 0xaf, 0x6e, 0xa8, 0x2b, 0x66, 0x5c,
|
||||
0xb9, 0x1c, 0xe7, 0x53, 0x19, 0x56, 0x8a, 0x11, 0xd8, 0x61, 0x5f, 0x07, 0x03, 0xda, 0xb5, 0xb0,
|
||||
0x08, 0xd0, 0x27, 0x45, 0x00, 0xc3, 0xf5, 0xb1, 0x79, 0x6b, 0xc4, 0x5b, 0x21, 0xe2, 0x55, 0x3d,
|
||||
0xa0, 0xfe, 0x31, 0x9a, 0x36, 0x0f, 0xec, 0xcb, 0xbb, 0x72, 0x29, 0x1a, 0xf9, 0x76, 0x87, 0x6a,
|
||||
0xae, 0xc9, 0x52, 0xb7, 0xeb, 0x7b, 0xf3, 0x05, 0x94, 0x04, 0x01, 0xc6, 0xa1, 0xf4, 0x37, 0xe3,
|
||||
0xfb, 0xc1, 0x4f, 0xba, 0x9e, 0x80, 0x28, 0x2f, 0x5a, 0xf6, 0x91, 0x3e, 0xcf, 0x41, 0xad, 0xbe,
|
||||
0xb6, 0x33, 0x9b, 0xfc, 0xce, 0xc0, 0x68, 0xee, 0x3f, 0x51, 0xbf, 0x4e, 0xd2, 0x49, 0xa9, 0xf7,
|
||||
0x8f, 0x86, 0xc8, 0x2a, 0x67, 0x38, 0xe4, 0x4d, 0xc7, 0x2e, 0x63, 0x7f, 0xef, 0xdf, 0x97, 0xaa,
|
||||
0x5d, 0x62, 0x0b, 0x70, 0xd1, 0xe6, 0x13, 0x99, 0x92, 0xf8, 0xd3, 0x12, 0x3c, 0x64, 0x84, 0xa7,
|
||||
0x0a, 0xb2, 0x9c, 0xea, 0x1b, 0xcc, 0x5e, 0x43, 0x1e, 0xcd, 0x8d, 0x93, 0xe1, 0x71, 0x3b, 0xf2,
|
||||
0xa3, 0x23, 0xe9, 0xc2, 0xdd, 0x98, 0x8b, 0x73, 0x96, 0x57, 0x22, 0x35, 0x0e, 0xa5, 0xac, 0x0c,
|
||||
0xd4, 0x9f, 0x09, 0xdb, 0x3a, 0x16, 0x60, 0xe5, 0x89, 0x85, 0x2d, 0x77, 0x1d, 0x81, 0x20, 0xc5,
|
||||
0x7a, 0xed, 0xca, 0x14, 0x83, 0xfa, 0x1f, 0x26, 0x18, 0x47, 0x9d, 0x02, 0xe0, 0x58, 0x10, 0xfd,
|
||||
0x25, 0x75, 0xbc, 0x46, 0xde, 0xa4, 0x15, 0x40, 0x78, 0x34, 0x4a, 0x17, 0xd5, 0x0d, 0x69, 0x65,
|
||||
0xdc, 0x4b, 0x7c, 0x95, 0xe8, 0x8e, 0xff, 0x7d, 0x90, 0x8c, 0x82, 0x6c, 0xb3, 0xbd, 0x44, 0xf1
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xc3, 0x0b, 0xb8, 0x15, 0x00, 0xa4, 0x3d, 0xd9, 0xfd, 0x16, 0xe9, 0x08, 0x20, 0xe6, 0x8d, 0xc4,
|
||||
0x47, 0x38, 0x25, 0x93, 0x5e, 0xaa, 0xec, 0x2a, 0x10, 0x6e, 0x58, 0x95, 0x26, 0x53, 0x04, 0x1e,
|
||||
0x92, 0x9e, 0x2f, 0x8b, 0xbd, 0x0a, 0x69, 0xa9, 0x3b, 0x41, 0xf0, 0xd0, 0xda, 0x6a, 0xa1, 0x71,
|
||||
0x13, 0x63, 0xef, 0x90, 0x94, 0xb0, 0xeb, 0xe7, 0xdc, 0x73, 0xb9, 0x78, 0x7a, 0xe4, 0xc7, 0x6d,
|
||||
0x7f, 0xa3, 0xac, 0x85, 0x50, 0xf3, 0x91, 0xea, 0x36, 0xfa, 0x9b, 0x8e, 0xf7, 0xae, 0xc1, 0x43,
|
||||
0x1c, 0xc0, 0xca, 0x8f, 0x4e, 0xa2, 0x31, 0x12, 0x74, 0x2c, 0x9d, 0xf1, 0xad, 0x3f, 0x68, 0xbb,
|
||||
0x35, 0x9a, 0x7c, 0x03, 0xfb, 0x09, 0x23, 0x27, 0xd4, 0x4d, 0x17, 0xde, 0x4c, 0xee, 0x76, 0x9c,
|
||||
0xa0, 0xe1, 0x75, 0xdb, 0x5b, 0x11, 0xa7, 0x21, 0xaf, 0x02, 0x42, 0x4b, 0x83, 0xd3, 0xce, 0x9f,
|
||||
0xcd, 0xcc, 0x30, 0x2b, 0x52, 0x22, 0xcb, 0x59, 0x7d, 0x14, 0xf9, 0xb1, 0x70, 0x54, 0xe0, 0x81,
|
||||
0x5a, 0x1d, 0x0e, 0x4a, 0xf4, 0x84, 0xed, 0x96, 0x1a, 0xd7, 0xc9, 0x82, 0x89, 0x2d, 0x87, 0x33,
|
||||
0xe2, 0x5c, 0x51, 0x3a, 0x1b, 0xf6, 0xe5, 0xd8, 0x32, 0x0d, 0x5f, 0x72, 0x88, 0xd6, 0xb7, 0xc6,
|
||||
0xdd, 0x1f, 0x80, 0x37, 0x64, 0xb5, 0x7b, 0xdf, 0xff, 0xd2, 0x40, 0xfc, 0x60, 0x05, 0xb6, 0x66,
|
||||
0x61, 0xc8, 0x28, 0x99, 0xbc, 0xa5, 0x34, 0x19, 0xf8, 0xfe, 0x24, 0x48, 0xd1, 0x0c, 0x55, 0x62,
|
||||
0x6b, 0x86, 0xb3, 0xba, 0x8a, 0x2e, 0x6f, 0xa6, 0x67, 0x65, 0x01, 0xe8, 0x18, 0x3e, 0x7e, 0x77,
|
||||
0x29, 0x8c, 0xf2, 0x07, 0x6c, 0xa8, 0x79, 0x4f, 0x44, 0x46, 0x06, 0xc2, 0xab, 0x0f, 0xb4, 0xb2,
|
||||
0xbe, 0x98, 0xd5, 0x49, 0xe3, 0xf5, 0xcf, 0x97, 0x5d, 0xbf, 0x57, 0x3c, 0x39, 0xc5, 0x45, 0x56
|
||||
}
|
||||
};
|
||||
|
||||
private static readonly byte[][] SBoxInv =
|
||||
{
|
||||
new byte[]
|
||||
{
|
||||
0x24, 0x3c, 0xba, 0x36, 0xe3, 0x85, 0xa4, 0xd0, 0x93, 0x43, 0x73, 0xb9, 0x70, 0x6e, 0xc9, 0xf1,
|
||||
0x10, 0x0e, 0x9b, 0x2c, 0x97, 0xe7, 0x0b, 0x63, 0x6c, 0x29, 0x20, 0xfe, 0x86, 0xf3, 0xe1, 0xf5,
|
||||
0xf6, 0x9f, 0xb6, 0x16, 0x04, 0x7b, 0x8f, 0xab, 0xb1, 0x39, 0x2a, 0x1b, 0xeb, 0x5c, 0xa8, 0xac,
|
||||
0x38, 0x11, 0x12, 0x5f, 0x89, 0x3e, 0x7d, 0xca, 0xec, 0x53, 0xdb, 0x6d, 0x1e, 0xd2, 0x81, 0x78,
|
||||
0x96, 0x46, 0xff, 0xf9, 0x54, 0x1c, 0x28, 0x7a, 0x4f, 0xd3, 0xc0, 0xdc, 0xc1, 0x6a, 0xf2, 0xbc,
|
||||
0xcb, 0x57, 0xfd, 0x4a, 0xe4, 0xf0, 0xb2, 0xc7, 0x95, 0x40, 0x62, 0x52, 0x41, 0xe2, 0xad, 0x49,
|
||||
0xa6, 0xb5, 0x1f, 0x02, 0xc8, 0xda, 0x92, 0xe5, 0xb0, 0xc5, 0x64, 0x76, 0x48, 0x21, 0xde, 0x0f,
|
||||
0x45, 0x58, 0x4c, 0xdf, 0xa7, 0x84, 0xcf, 0xd5, 0x15, 0x4e, 0x27, 0x80, 0x6b, 0x7f, 0xfc, 0x44,
|
||||
0x71, 0x47, 0x22, 0xdd, 0x30, 0x0c, 0xa1, 0x3b, 0xe0, 0x37, 0xa0, 0x35, 0x23, 0x90, 0x32, 0x74,
|
||||
0xbf, 0x8d, 0xc2, 0xea, 0xd6, 0x50, 0xbb, 0xd9, 0xc4, 0x83, 0xb4, 0x31, 0x68, 0x55, 0x8b, 0x5d,
|
||||
0xf4, 0x72, 0x18, 0xb7, 0xef, 0xf7, 0x98, 0x2d, 0x01, 0x03, 0x61, 0xcc, 0x0a, 0x8e, 0x13, 0x00,
|
||||
0xe8, 0x14, 0xaf, 0x09, 0x51, 0x75, 0xa5, 0x2f, 0x1d, 0x0d, 0x65, 0x8a, 0xcd, 0x66, 0x07, 0x3d,
|
||||
0x05, 0xd8, 0x4b, 0xe9, 0x9d, 0x99, 0x7c, 0x91, 0xd1, 0xb8, 0x19, 0xc3, 0x2b, 0x42, 0x69, 0x88,
|
||||
0xc6, 0x79, 0x17, 0x3a, 0x4d, 0x5b, 0xa2, 0x5a, 0xfb, 0x25, 0x3f, 0xbd, 0xf8, 0xed, 0xce, 0xe6,
|
||||
0x87, 0xa3, 0x26, 0xa9, 0x2e, 0xbe, 0x94, 0x08, 0x7e, 0x67, 0x60, 0x8c, 0x9c, 0x5e, 0x6f, 0xb3,
|
||||
0x9e, 0x06, 0xfa, 0x82, 0xae, 0xee, 0x59, 0x77, 0xd7, 0x1a, 0x9a, 0x34, 0xd4, 0x56, 0xaa, 0x33
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xf9, 0x81, 0x7c, 0x00, 0xb9, 0x30, 0x37, 0xd5, 0x90, 0x51, 0x6e, 0xf0, 0xb2, 0x06, 0xfb, 0xcd,
|
||||
0x39, 0x14, 0x5f, 0xf8, 0x1b, 0xc7, 0x4a, 0x82, 0x70, 0x8c, 0x92, 0x1d, 0xea, 0xc3, 0x4e, 0xc8,
|
||||
0x12, 0xe6, 0xb6, 0x10, 0xd3, 0x2f, 0x95, 0x84, 0x25, 0x42, 0xa5, 0x72, 0xe5, 0x8f, 0x55, 0xef,
|
||||
0x86, 0xa2, 0x53, 0xae, 0xed, 0x26, 0x20, 0x47, 0xde, 0x78, 0x68, 0x28, 0xe4, 0x45, 0xcc, 0x35,
|
||||
0xbc, 0x3e, 0xbb, 0x8e, 0xc0, 0xbe, 0x34, 0xaf, 0xa6, 0x09, 0x64, 0x01, 0x7b, 0x44, 0xf5, 0xf3,
|
||||
0xb1, 0x3f, 0xa4, 0xb3, 0x32, 0x80, 0xc9, 0x4f, 0x6f, 0x40, 0xbf, 0x21, 0x4c, 0x74, 0xe1, 0x11,
|
||||
0x5e, 0xeb, 0x3d, 0x71, 0x2e, 0x9d, 0x62, 0x75, 0xd4, 0x16, 0x48, 0x77, 0x13, 0x67, 0xad, 0x6b,
|
||||
0x5d, 0x07, 0x87, 0xf7, 0xa8, 0x9a, 0x59, 0x76, 0x8b, 0x9b, 0x1e, 0xd8, 0xee, 0xaa, 0xe9, 0x99,
|
||||
0x2d, 0xc5, 0x97, 0xf1, 0xa7, 0x83, 0xfc, 0xca, 0xdc, 0xba, 0xb8, 0x4b, 0xe8, 0x89, 0x17, 0x05,
|
||||
0x0b, 0xa0, 0x65, 0x23, 0xd1, 0xce, 0x36, 0x03, 0xd7, 0xe0, 0xf2, 0x91, 0xdf, 0x0e, 0x9c, 0x2c,
|
||||
0x0d, 0x66, 0xd6, 0x73, 0x58, 0x5c, 0xb4, 0x0a, 0x4d, 0xc2, 0x3b, 0xd2, 0xb7, 0xe2, 0xac, 0x33,
|
||||
0xdb, 0x60, 0x27, 0xbd, 0x56, 0x43, 0x24, 0x04, 0x02, 0x8d, 0x7d, 0x7f, 0xf6, 0xb5, 0xf4, 0xfd,
|
||||
0xdd, 0x5b, 0xec, 0x79, 0xc4, 0x22, 0x1f, 0xa1, 0x88, 0x54, 0xe7, 0x19, 0x98, 0x94, 0x7e, 0x31,
|
||||
0xa3, 0x29, 0xe3, 0x5a, 0xcb, 0x6a, 0xb0, 0xcf, 0x6d, 0x93, 0x6c, 0x7a, 0x08, 0xa9, 0x3c, 0x1c,
|
||||
0xd0, 0x63, 0x50, 0xc6, 0x85, 0x38, 0xc1, 0x41, 0x49, 0xab, 0x61, 0x52, 0x2a, 0x9f, 0xd9, 0x18,
|
||||
0x1a, 0x57, 0x46, 0x2b, 0x69, 0xda, 0xfa, 0xff, 0x0f, 0x15, 0x96, 0x3a, 0x8a, 0xfe, 0x9e, 0x0c
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x11, 0xa2, 0x9a, 0xc3, 0x94, 0xa0, 0x51, 0x01, 0x5b, 0xf7, 0xd1, 0x30, 0xee, 0x1f, 0x06, 0xd5,
|
||||
0x77, 0x67, 0xd3, 0xc1, 0x6e, 0xe7, 0x6a, 0x1a, 0xa4, 0x8e, 0x9f, 0x65, 0x66, 0xd6, 0x0c, 0x2d,
|
||||
0xc6, 0xe4, 0x69, 0xb7, 0x56, 0x5a, 0x57, 0x60, 0xfc, 0x79, 0x1e, 0xe1, 0x16, 0x52, 0xf0, 0x07,
|
||||
0xd0, 0xcd, 0xca, 0x78, 0xc9, 0x8b, 0xb3, 0x88, 0x27, 0xf6, 0xe0, 0xc0, 0x84, 0xcf, 0x2f, 0xa3,
|
||||
0x04, 0x00, 0x80, 0x40, 0xa7, 0xfa, 0x75, 0x9d, 0x4b, 0x89, 0x46, 0x22, 0x28, 0x37, 0x34, 0x13,
|
||||
0xff, 0x20, 0xb4, 0xda, 0x08, 0x26, 0x6c, 0x8f, 0xf2, 0x5d, 0x7b, 0x73, 0x5c, 0x4c, 0x90, 0x71,
|
||||
0x41, 0x8a, 0x9e, 0xbb, 0x12, 0xe8, 0x55, 0x6d, 0x2a, 0x25, 0x4a, 0xaf, 0xbd, 0x95, 0x19, 0xa5,
|
||||
0x31, 0xba, 0xad, 0xd9, 0xc5, 0xa6, 0x6b, 0x83, 0xc4, 0xb6, 0xa9, 0xcc, 0xf9, 0xa1, 0xb1, 0x7a,
|
||||
0xdc, 0xc7, 0xdb, 0x2e, 0x7e, 0x7f, 0x8d, 0x4e, 0x62, 0x0f, 0x03, 0x39, 0xfd, 0xb8, 0xd4, 0x18,
|
||||
0xc2, 0xec, 0x61, 0x02, 0x53, 0x1c, 0x3b, 0x7d, 0xbc, 0x1d, 0x59, 0xf5, 0x8c, 0x98, 0xf1, 0x6f,
|
||||
0x93, 0x85, 0xf8, 0x2c, 0x74, 0xd8, 0x5e, 0x4d, 0x97, 0xab, 0x87, 0x82, 0x0a, 0x21, 0x42, 0x5f,
|
||||
0x0d, 0x58, 0x33, 0x44, 0x3a, 0xdd, 0xe9, 0xfb, 0x50, 0x47, 0xc8, 0xd7, 0x81, 0x9b, 0xe5, 0x1b,
|
||||
0x17, 0x54, 0x86, 0x2b, 0xef, 0xf4, 0x29, 0x32, 0x0e, 0x7c, 0x23, 0x15, 0xeb, 0xe6, 0x3c, 0x35,
|
||||
0xe2, 0x96, 0x68, 0x3f, 0x0b, 0x43, 0xce, 0xde, 0x9c, 0xbe, 0x4f, 0x45, 0x72, 0x76, 0xb5, 0x10,
|
||||
0xe3, 0xdf, 0x92, 0xd2, 0xa8, 0x48, 0x91, 0xb9, 0xac, 0xbf, 0x49, 0xb2, 0x99, 0x64, 0x70, 0xea,
|
||||
0xf3, 0x3e, 0x63, 0x14, 0xae, 0xed, 0xaa, 0x24, 0xfe, 0x3d, 0xcb, 0xb0, 0x09, 0x38, 0x36, 0x05
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xc4, 0xf0, 0x28, 0x49, 0x55, 0x4a, 0xf5, 0xfd, 0x75, 0xaf, 0x20, 0x69, 0xc8, 0x43, 0x86, 0x6b,
|
||||
0xc9, 0xa8, 0xc6, 0x54, 0x4c, 0xdd, 0x02, 0x5b, 0xe8, 0x9b, 0x59, 0x77, 0x34, 0xd7, 0xc0, 0x51,
|
||||
0x5f, 0xf3, 0x7e, 0xd4, 0xf1, 0x90, 0x81, 0xce, 0x19, 0x8a, 0x78, 0x33, 0xcd, 0x97, 0x8c, 0xd6,
|
||||
0x6a, 0x4b, 0x8f, 0xa4, 0x80, 0xdc, 0x1a, 0x17, 0x14, 0xc5, 0x07, 0x87, 0x66, 0xad, 0x9d, 0x85,
|
||||
0xb2, 0xf8, 0x8d, 0x98, 0xdf, 0x3e, 0x1f, 0x3f, 0x64, 0x58, 0x40, 0xac, 0x6c, 0x5c, 0x08, 0x5d,
|
||||
0x53, 0xba, 0xff, 0xd2, 0xa9, 0x2c, 0x25, 0xab, 0xe4, 0x32, 0x38, 0x9a, 0xf9, 0xcb, 0xc2, 0x91,
|
||||
0x67, 0xd0, 0xe3, 0x22, 0x29, 0x2d, 0x92, 0x48, 0xb4, 0x0e, 0x99, 0x05, 0xa3, 0x6f, 0x0a, 0x15,
|
||||
0xef, 0xd5, 0x10, 0x4f, 0xd8, 0xf6, 0x00, 0xe6, 0x46, 0x2b, 0x31, 0x57, 0x83, 0x94, 0xae, 0x03,
|
||||
0xeb, 0x8e, 0x13, 0x24, 0xa1, 0x1e, 0x5e, 0xd1, 0x26, 0xd9, 0xa7, 0xca, 0x36, 0x6e, 0x71, 0x37,
|
||||
0xee, 0xb1, 0xfa, 0x7c, 0x76, 0x06, 0xb8, 0x23, 0xbe, 0x21, 0xbc, 0x9e, 0xc1, 0xda, 0x7a, 0x3b,
|
||||
0x62, 0x27, 0x7b, 0xb0, 0xe5, 0x63, 0x18, 0x82, 0x7f, 0x0f, 0xed, 0xa0, 0x2a, 0x9c, 0xbf, 0x11,
|
||||
0xbd, 0xe0, 0x88, 0x0d, 0x3a, 0x79, 0x52, 0x56, 0xf7, 0xb6, 0xd3, 0x09, 0x16, 0x1b, 0x70, 0xb3,
|
||||
0x42, 0x60, 0x2f, 0x1c, 0xa2, 0x9f, 0x72, 0x12, 0x45, 0x47, 0xbb, 0xe1, 0x0b, 0x01, 0x8b, 0x1d,
|
||||
0xde, 0xec, 0x0c, 0x4e, 0xe9, 0xcf, 0xcc, 0x95, 0x74, 0xf4, 0xa5, 0x93, 0xc7, 0xdb, 0x4d, 0xfb,
|
||||
0x35, 0x65, 0xfe, 0xe7, 0x39, 0xb5, 0xea, 0x96, 0xc3, 0x04, 0x41, 0x44, 0x84, 0xfc, 0x6d, 0x30,
|
||||
0xe2, 0xf2, 0x68, 0xb7, 0x89, 0x7d, 0x73, 0x3d, 0xb9, 0x5a, 0x50, 0xa6, 0x3c, 0x61, 0xaa, 0x2e
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x1a, 0x59, 0x9c, 0xad, 0xc8, 0xe4, 0x11, 0x54, 0xed, 0x37, 0x0f, 0x3a, 0xe6, 0x5f, 0x3c, 0x4b,
|
||||
0xb8, 0x15, 0x89, 0xb1, 0xe8, 0xda, 0x69, 0x77, 0x91, 0x56, 0x8b, 0xdb, 0x06, 0x24, 0xcf, 0x18,
|
||||
0xf8, 0xb0, 0x87, 0xdf, 0x8c, 0x35, 0xcb, 0x86, 0x53, 0x9d, 0xa4, 0x66, 0x4a, 0x7a, 0x71, 0x0a,
|
||||
0x48, 0x38, 0xff, 0xdc, 0x83, 0x20, 0xce, 0x98, 0x32, 0xf6, 0xd7, 0xaf, 0x70, 0x5e, 0x73, 0x8a,
|
||||
0x14, 0x72, 0x1e, 0xc1, 0x29, 0x79, 0x07, 0x08, 0xe9, 0x43, 0x46, 0xd0, 0x2f, 0xde, 0x2a, 0x4f,
|
||||
0x3d, 0x2c, 0x50, 0xb3, 0x75, 0xfc, 0x0b, 0x64, 0xae, 0x31, 0x7b, 0x61, 0xa5, 0x30, 0xa0, 0x93,
|
||||
0xd2, 0xbe, 0xc2, 0x55, 0xba, 0x6c, 0xf3, 0x62, 0x68, 0xfd, 0xac, 0x3b, 0x95, 0x49, 0x1f, 0x6b,
|
||||
0xb4, 0x85, 0xf7, 0xa6, 0x03, 0xec, 0x6e, 0x9a, 0x81, 0x09, 0x0c, 0x6a, 0xee, 0x9e, 0x4e, 0xf0,
|
||||
0xab, 0x2d, 0x7e, 0xa1, 0xe1, 0xbb, 0xc9, 0xbc, 0x41, 0xf2, 0xfa, 0x2e, 0x1b, 0xdd, 0x27, 0x34,
|
||||
0xd3, 0x60, 0x04, 0xb5, 0x01, 0xd6, 0x40, 0xf9, 0xd5, 0x02, 0x47, 0xd9, 0xd8, 0xe0, 0x8f, 0x2b,
|
||||
0xfb, 0xb7, 0xc5, 0xeb, 0x57, 0x16, 0xe5, 0x78, 0x8d, 0xf4, 0x00, 0xcd, 0x82, 0x39, 0xc6, 0x96,
|
||||
0xbd, 0xbf, 0xe3, 0x36, 0x45, 0x0e, 0x26, 0x1d, 0x63, 0xe7, 0x84, 0x3e, 0xb6, 0x9b, 0x22, 0xa3,
|
||||
0xf5, 0x8e, 0x23, 0x6d, 0xc3, 0x99, 0x7d, 0x90, 0x97, 0x10, 0x25, 0x80, 0xd4, 0x4c, 0xe2, 0x74,
|
||||
0xcc, 0xb2, 0x5c, 0x33, 0xc7, 0xea, 0x05, 0x12, 0x3f, 0x51, 0xa8, 0xfe, 0xf1, 0x1c, 0x42, 0xca,
|
||||
0xd1, 0x76, 0x28, 0x6f, 0x92, 0xa7, 0x67, 0xa9, 0x19, 0xa2, 0x44, 0x5b, 0xaa, 0xef, 0x58, 0x7f,
|
||||
0x21, 0xc0, 0x88, 0x65, 0xb9, 0x5d, 0x4d, 0x0d, 0x5a, 0xc4, 0x13, 0x52, 0x7c, 0x9f, 0x94, 0x17
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xc7, 0x2b, 0x82, 0x61, 0x5b, 0xd0, 0x96, 0x84, 0xd3, 0x4a, 0x70, 0xa1, 0x9b, 0x59, 0x33, 0x9f,
|
||||
0xc0, 0x20, 0x14, 0x53, 0x29, 0x17, 0xc5, 0x0b, 0xc9, 0x7b, 0x97, 0x02, 0x0d, 0x3a, 0x1e, 0x7c,
|
||||
0x3f, 0x6b, 0x52, 0xe8, 0x75, 0x3d, 0xf6, 0xe4, 0x0c, 0x8a, 0x4f, 0xc3, 0x5f, 0x26, 0x65, 0x73,
|
||||
0x31, 0x23, 0x28, 0x48, 0x74, 0xaa, 0xa7, 0x36, 0x09, 0xb1, 0xe2, 0x91, 0x04, 0x51, 0x22, 0xfc,
|
||||
0x08, 0xa6, 0x05, 0xa4, 0xf1, 0x12, 0x1c, 0x19, 0xeb, 0x40, 0x37, 0xc2, 0xa0, 0x41, 0x1d, 0xd4,
|
||||
0xdc, 0x07, 0x43, 0x8f, 0x47, 0xaf, 0xd1, 0x2e, 0x98, 0xab, 0x01, 0xba, 0xf0, 0x66, 0x68, 0xac,
|
||||
0xf9, 0xe7, 0x69, 0xb6, 0xcb, 0x8d, 0x78, 0x87, 0x15, 0x03, 0xd5, 0xdf, 0xa3, 0x1a, 0x9d, 0x6a,
|
||||
0xea, 0x2f, 0x94, 0x4e, 0x9e, 0x42, 0xd7, 0xb8, 0x38, 0x92, 0xd8, 0xbb, 0xde, 0xdd, 0x9a, 0xbc,
|
||||
0xb0, 0x4c, 0x79, 0xf4, 0x58, 0x3e, 0xe9, 0x83, 0x81, 0xff, 0xe3, 0x55, 0xfd, 0x5d, 0xb2, 0xef,
|
||||
0x9c, 0x6d, 0x54, 0x99, 0x60, 0xda, 0x3b, 0xec, 0xfa, 0x11, 0xd6, 0xc4, 0x2a, 0xed, 0x4b, 0xae,
|
||||
0x13, 0xbf, 0xb9, 0x06, 0x8b, 0xe0, 0x1f, 0x7f, 0x5a, 0xad, 0x90, 0x39, 0x0f, 0xf3, 0xbd, 0x46,
|
||||
0x6c, 0x2c, 0xf2, 0xf8, 0xfe, 0xd9, 0xe6, 0x72, 0x0e, 0x89, 0xbe, 0x5e, 0xc6, 0xa8, 0xcf, 0xf5,
|
||||
0x57, 0x7e, 0x8c, 0xb7, 0xe1, 0x88, 0x7a, 0xc8, 0x1b, 0x18, 0xdb, 0x6f, 0x35, 0xd2, 0x16, 0x32,
|
||||
0x64, 0x63, 0xa5, 0x8e, 0xf7, 0xb4, 0x76, 0x95, 0x86, 0x7d, 0xe5, 0xa9, 0x5c, 0x85, 0x00, 0xc1,
|
||||
0x93, 0x4d, 0xfb, 0x30, 0xa2, 0xb5, 0x25, 0x34, 0x10, 0x77, 0x3c, 0x67, 0x71, 0x2d, 0x44, 0x45,
|
||||
0x56, 0x0a, 0x50, 0xb3, 0xcd, 0x62, 0x80, 0xce, 0x21, 0x49, 0x24, 0xca, 0xee, 0x27, 0x6e, 0xcc
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xa5, 0xb0, 0x6d, 0xb2, 0x51, 0x55, 0x1f, 0xbf, 0x3e, 0x8d, 0xdd, 0x19, 0x92, 0xea, 0x1b, 0xbd,
|
||||
0x32, 0x65, 0x29, 0xa4, 0x89, 0x67, 0xb1, 0x90, 0x68, 0x00, 0xda, 0x0d, 0xa6, 0x59, 0x54, 0xf2,
|
||||
0x10, 0x14, 0x2f, 0x45, 0xe0, 0x3b, 0x23, 0xfa, 0xd7, 0x50, 0xac, 0x93, 0xe6, 0x43, 0x01, 0x0a,
|
||||
0x5c, 0x78, 0x70, 0x98, 0x46, 0xa9, 0x7b, 0xf3, 0x95, 0x07, 0x2a, 0xd3, 0x74, 0xb3, 0x3f, 0xa3,
|
||||
0x60, 0x82, 0x39, 0x4e, 0x34, 0x48, 0xc0, 0x1c, 0xf6, 0xc2, 0x91, 0x64, 0x4d, 0x3c, 0xf8, 0x9d,
|
||||
0x35, 0x9a, 0x94, 0xe3, 0x7a, 0xf0, 0xf9, 0x6e, 0xb9, 0x12, 0xb8, 0x04, 0x2d, 0x02, 0x28, 0x13,
|
||||
0x85, 0x72, 0x80, 0x87, 0xdb, 0x2b, 0xf1, 0x4f, 0x26, 0xa2, 0xe1, 0x49, 0x7e, 0x9c, 0xcc, 0xa7,
|
||||
0xb6, 0xa0, 0xd0, 0x9b, 0x36, 0x77, 0xad, 0x8b, 0x6b, 0x4a, 0x03, 0x1d, 0x05, 0x8a, 0x06, 0x4b,
|
||||
0xaf, 0xe5, 0x31, 0xb5, 0xd4, 0xc6, 0x0c, 0x66, 0xba, 0x83, 0xfd, 0x09, 0x0f, 0xae, 0x71, 0xb7,
|
||||
0x6f, 0xdc, 0x41, 0xe8, 0x17, 0x8e, 0x40, 0x7f, 0x62, 0x30, 0xff, 0xa8, 0x84, 0x25, 0xfb, 0x16,
|
||||
0xce, 0x37, 0x44, 0xab, 0x99, 0x1e, 0xeb, 0x18, 0x3a, 0x47, 0xf7, 0x5f, 0x81, 0xcf, 0xed, 0x58,
|
||||
0x2c, 0x6c, 0xfe, 0x9e, 0x57, 0x53, 0x97, 0x20, 0xca, 0x79, 0x1a, 0x5a, 0x88, 0xf5, 0x69, 0x9f,
|
||||
0xe7, 0xd9, 0x0e, 0xbe, 0x42, 0xdf, 0x56, 0xe4, 0x4c, 0x22, 0xaa, 0x73, 0x0b, 0x15, 0xc5, 0xee,
|
||||
0xfc, 0xc7, 0xd6, 0xcb, 0xcd, 0x8c, 0xe2, 0x76, 0x21, 0xe9, 0xd1, 0xec, 0xc8, 0x7d, 0xd8, 0x8f,
|
||||
0x61, 0x7c, 0x2e, 0xbc, 0xde, 0xb4, 0x75, 0xd2, 0xc4, 0x63, 0x3d, 0xa1, 0x5e, 0x5d, 0x6a, 0x08,
|
||||
0x24, 0xc9, 0x27, 0xbb, 0xef, 0x33, 0x86, 0x5b, 0xd5, 0x38, 0x52, 0x11, 0xf4, 0xc3, 0x96, 0xc1
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x34, 0x5a, 0xdb, 0x2c, 0x59, 0x57, 0x12, 0x2b, 0x30, 0xc2, 0xa0, 0x92, 0xbf, 0xed, 0xbc, 0x45,
|
||||
0xde, 0x27, 0x9b, 0x96, 0xd3, 0xe6, 0xc5, 0xeb, 0xd8, 0x24, 0x4b, 0xa4, 0x21, 0xcc, 0xa8, 0xd6,
|
||||
0xce, 0x3c, 0xba, 0xb1, 0x09, 0xe0, 0xd7, 0x32, 0x66, 0x4a, 0x83, 0x1d, 0x19, 0xca, 0x89, 0x67,
|
||||
0x0f, 0x42, 0x07, 0x71, 0xe9, 0xbb, 0x44, 0x5e, 0x85, 0x17, 0xc4, 0xae, 0x9c, 0x3f, 0x6b, 0x78,
|
||||
0xe7, 0x6d, 0x02, 0xa7, 0xfe, 0x33, 0xe3, 0xd9, 0x0a, 0x7d, 0xea, 0xf1, 0x18, 0x87, 0x7b, 0x62,
|
||||
0x03, 0x79, 0x52, 0x23, 0x00, 0x3e, 0x25, 0xb9, 0xdd, 0x16, 0x68, 0x3b, 0x1f, 0x90, 0xa6, 0x2a,
|
||||
0xc6, 0x29, 0x91, 0x8a, 0x9d, 0xef, 0x1e, 0x84, 0x76, 0xee, 0x4f, 0x39, 0xfb, 0x11, 0x1b, 0x0b,
|
||||
0x93, 0xad, 0x49, 0xb7, 0x05, 0xe1, 0x4d, 0xcb, 0xe8, 0x38, 0xd0, 0x55, 0xf2, 0xf7, 0x0d, 0x8b,
|
||||
0x65, 0xcd, 0xfa, 0xd4, 0x9e, 0xc9, 0x81, 0x4e, 0x14, 0xc8, 0x26, 0xb6, 0xf9, 0xaa, 0xf5, 0x80,
|
||||
0xf8, 0x6a, 0x98, 0xab, 0x58, 0xf3, 0xb8, 0x8e, 0xb5, 0x97, 0x43, 0x72, 0xa2, 0xda, 0x64, 0xc1,
|
||||
0x40, 0x5c, 0x13, 0xb0, 0xe5, 0xbd, 0x08, 0x9f, 0x1c, 0x7e, 0x8f, 0x06, 0xbe, 0x6e, 0x50, 0x1a,
|
||||
0x2f, 0x37, 0xa1, 0xfc, 0x10, 0x2e, 0x70, 0x53, 0x04, 0x20, 0x63, 0x48, 0xe2, 0xfd, 0x6f, 0x7a,
|
||||
0x75, 0x61, 0xb3, 0x35, 0x3a, 0xcf, 0x5b, 0x88, 0x82, 0x51, 0xd2, 0x47, 0xa5, 0xa9, 0x74, 0x6c,
|
||||
0x31, 0x94, 0x7c, 0x9a, 0xc0, 0xec, 0x15, 0x0e, 0x28, 0x01, 0x2d, 0xc3, 0xf0, 0xb4, 0xe4, 0x8d,
|
||||
0xdc, 0xac, 0x3d, 0x5f, 0x86, 0xc7, 0x95, 0x22, 0xf4, 0xb2, 0xa3, 0x54, 0x46, 0xd1, 0x77, 0x8c,
|
||||
0x0c, 0xff, 0xaf, 0x56, 0x5d, 0x36, 0x69, 0x7f, 0x99, 0x4c, 0xd5, 0x60, 0x73, 0xdf, 0x41, 0xf6
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x04, 0xda, 0x79, 0x63, 0x1e, 0xbd, 0xea, 0xe3, 0x0b, 0x65, 0x25, 0x01, 0xcd, 0xa9, 0x92, 0xed,
|
||||
0x18, 0x75, 0x57, 0x30, 0x89, 0x03, 0x09, 0x6a, 0xdc, 0xc7, 0x98, 0xa4, 0x50, 0x91, 0x1f, 0xb1,
|
||||
0x0c, 0x77, 0x85, 0x66, 0xca, 0x12, 0x1c, 0x67, 0xc2, 0xe0, 0x17, 0x83, 0x59, 0x9d, 0xd5, 0x22,
|
||||
0x82, 0x56, 0xa8, 0x9f, 0xc6, 0x60, 0x48, 0xb3, 0x11, 0xfc, 0xa3, 0x28, 0xfb, 0x06, 0xdd, 0x5d,
|
||||
0xba, 0x29, 0x7a, 0x4f, 0xe8, 0xfe, 0xe9, 0x10, 0xcb, 0xf3, 0x93, 0x7b, 0x6c, 0x69, 0x54, 0xe7,
|
||||
0x44, 0xa2, 0x84, 0x1d, 0x8d, 0xce, 0xff, 0xfa, 0x1a, 0x87, 0x90, 0x74, 0xa1, 0xf8, 0x14, 0xaa,
|
||||
0xbc, 0xc0, 0xcf, 0x31, 0xb4, 0xd9, 0xbf, 0xd8, 0x5e, 0x26, 0x2d, 0xd0, 0xe4, 0x3f, 0x19, 0xd6,
|
||||
0x8c, 0x2f, 0xab, 0x39, 0x58, 0x72, 0x6e, 0xdf, 0x3b, 0xe6, 0x3c, 0xb6, 0x62, 0x88, 0xde, 0x40,
|
||||
0xb2, 0x8f, 0x9b, 0x7c, 0x95, 0x43, 0xd1, 0x9e, 0xac, 0x9c, 0xd4, 0x23, 0xe1, 0x0e, 0x4b, 0x53,
|
||||
0x33, 0x46, 0x20, 0x13, 0x34, 0x1b, 0x97, 0xf7, 0xf1, 0xc3, 0x61, 0x4a, 0x6f, 0x5a, 0x21, 0x7f,
|
||||
0x70, 0x2e, 0x55, 0x41, 0x05, 0xc5, 0xd7, 0x76, 0xe5, 0x27, 0x15, 0xec, 0x42, 0x5c, 0x4d, 0x78,
|
||||
0x35, 0x8b, 0xef, 0xd2, 0xee, 0xb5, 0xbe, 0xae, 0x02, 0x3a, 0xd3, 0x5f, 0xc4, 0x24, 0xf0, 0xf9,
|
||||
0x51, 0x4e, 0xeb, 0x00, 0x0f, 0xfd, 0xaf, 0x3e, 0xc1, 0x9a, 0x52, 0x86, 0x81, 0x80, 0x7e, 0xf6,
|
||||
0x2b, 0xcc, 0xb9, 0x7d, 0x68, 0xf2, 0xad, 0x99, 0xa7, 0x07, 0x2c, 0x73, 0x38, 0xb0, 0x6b, 0xb7,
|
||||
0x8e, 0x71, 0xa0, 0xf4, 0x3d, 0xa6, 0x0d, 0x37, 0xdb, 0x0a, 0x47, 0x36, 0x16, 0x96, 0x6d, 0x32,
|
||||
0x2a, 0x5b, 0xe2, 0x45, 0x94, 0xf5, 0xa5, 0x4c, 0xc8, 0x8a, 0x49, 0x64, 0xbb, 0x08, 0xc9, 0xb8
|
||||
}
|
||||
};
|
||||
|
||||
public byte[] Decrypt(ReadOnlySpan<byte> spadInput)
|
||||
{
|
||||
if (spadInput.Length != 16)
|
||||
{
|
||||
throw new ArgumentException("spadInput must be exactly 16 bytes.", nameof(spadInput));
|
||||
}
|
||||
|
||||
var spad = spadInput.ToArray();
|
||||
var nTables = SBoxInv.Length - 1;
|
||||
|
||||
for (var i = 0; i < spad.Length; i++)
|
||||
{
|
||||
spad[i] = SBoxInv[nTables][spad[i]];
|
||||
}
|
||||
|
||||
var count = (spad[15] >> 4) + 7;
|
||||
var table = spad[15] + IterAdd * count;
|
||||
|
||||
for (var iteration = 0; iteration < count; iteration++)
|
||||
{
|
||||
table -= IterAdd;
|
||||
RotateRight(spad, 15, 5);
|
||||
var tableIndex = table % nTables;
|
||||
for (var i = 0; i < 15; i++)
|
||||
{
|
||||
spad[i] = SBoxInv[tableIndex][spad[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return spad;
|
||||
}
|
||||
|
||||
private static void RotateRight(byte[] data, int nBytes, int nBits)
|
||||
{
|
||||
var prior = data[nBytes - 1];
|
||||
for (var i = 0; i < nBytes; i++)
|
||||
{
|
||||
var current = data[i];
|
||||
data[i] = (byte)(((current >> nBits) | ((prior & ((1 << nBits) - 1)) << (8 - nBits))) & 0xFF);
|
||||
prior = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/NfcAime.Dll/FelicaCommandBuilder.cs
Normal file
38
src/NfcAime.Dll/FelicaCommandBuilder.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
|
||||
internal static class FelicaCommandBuilder
|
||||
{
|
||||
internal static byte[] BuildPollingCommand() => new byte[] { 0x06, 0x00, 0x88, 0xB4, 0x01, 0x0F };
|
||||
|
||||
internal static byte[] BuildReadWithoutEncryptionCommand(ReadOnlySpan<byte> idm)
|
||||
{
|
||||
if (idm.Length != 8)
|
||||
{
|
||||
throw new ArgumentException("IDm must be exactly 8 bytes.", nameof(idm));
|
||||
}
|
||||
|
||||
// FeliCa Read Without Encryption:
|
||||
// Byte 0: Length (16 bytes = 0x10)
|
||||
// Byte 1: Command Code (0x06)
|
||||
// Byte 2-9: IDm
|
||||
// Byte 10: Number of Services (0x01)
|
||||
// Byte 11-12: Service Code List (0x0B, 0x00 for Service 0x000B, Little Endian)
|
||||
// Byte 13: Number of Blocks (0x01)
|
||||
// Byte 14-15: Block List (0x80, 0x00 for Service 0, Block 0)
|
||||
|
||||
byte[] command = new byte[16];
|
||||
command[0] = 0x10;
|
||||
command[1] = 0x06;
|
||||
idm.CopyTo(command.AsSpan(2, 8));
|
||||
command[10] = 0x01;
|
||||
command[11] = 0x0B;
|
||||
command[12] = 0x00;
|
||||
command[13] = 0x01;
|
||||
command[14] = 0x80;
|
||||
command[15] = 0x00;
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
19
src/NfcAime.Dll/FelicaResponseParser.cs
Normal file
19
src/NfcAime.Dll/FelicaResponseParser.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
|
||||
internal static class FelicaResponseParser
|
||||
{
|
||||
private const int Spad0Offset = 13;
|
||||
private const int Spad0Length = 16;
|
||||
|
||||
internal static byte[] ParseSpad0(ReadOnlySpan<byte> response)
|
||||
{
|
||||
if (response.Length < Spad0Offset + Spad0Length)
|
||||
{
|
||||
throw new ArgumentException($"Response must be at least {Spad0Offset + Spad0Length} bytes.", nameof(response));
|
||||
}
|
||||
|
||||
return response.Slice(Spad0Offset, Spad0Length).ToArray();
|
||||
}
|
||||
}
|
||||
4
src/NfcAime.Dll/FodyWeavers.xml
Normal file
4
src/NfcAime.Dll/FodyWeavers.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Costura />
|
||||
</Weavers>
|
||||
186
src/NfcAime.Dll/FodyWeavers.xsd
Normal file
186
src/NfcAime.Dll/FodyWeavers.xsd
Normal file
@@ -0,0 +1,186 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
|
||||
<xs:element name="Weavers">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:all>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtimes to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtimes names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX86Assemblies instead</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinX86Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X86 (32 bit) assembly names to include, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX64Assemblies instead.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinX64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X64 (64 bit) assembly names to include, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="UnmanagedWinArm64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="DisableCompression" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="DisableCleanup" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="DisableEventSubscription" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The attach method no longer subscribes to the `AppDomain.AssemblyResolve` (.NET 4.x) and `AssemblyLoadContext.Resolving` (.NET 6.0+) events.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ExcludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX86Assemblies instead</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UnmanagedWinX86Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X86 (32 bit) assembly names to include, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Obsolete, use UnmanagedWinX64Assemblies instead</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UnmanagedWinX64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged X64 (64 bit) assembly names to include, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="UnmanagedWinArm64Assemblies" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A list of unmanaged Arm64 (64 bit) assembly names to include, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="PreloadOrder" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="VerifyAssembly" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="GenerateXsd" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
97
src/NfcAime.Dll/MainDll.cs
Normal file
97
src/NfcAime.Dll/MainDll.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
namespace NfcAime.Dll {
|
||||
public class MainDll {
|
||||
|
||||
public static AimeReader reader;
|
||||
static byte[] idm = null;
|
||||
static string accessCode = null;
|
||||
static AimeReader.CardKind cardKind = AimeReader.CardKind.Null;
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern void AllocConsole();
|
||||
//返回API版本
|
||||
[DllExport("aime_io_get_api_version", CallingConvention = CallingConvention.StdCall)]
|
||||
public static ushort GetApiVersion() => 0x0100;
|
||||
|
||||
[DllExport("aime_io_init", CallingConvention = CallingConvention.StdCall)]
|
||||
public static int Init()
|
||||
{
|
||||
AllocConsole();
|
||||
|
||||
reader = new AimeReader(port: Config.ReaderCOM, baud: Config.ReaderBaud);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//卡轮询
|
||||
[DllExport("aime_io_nfc_poll", CallingConvention = CallingConvention.StdCall)]
|
||||
public static int NfcPoll(byte unitNo)
|
||||
{
|
||||
Console.WriteLine(">> Polling...");
|
||||
(cardKind, idm, accessCode) = reader.ReadCard();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//获取Aime AccessCode
|
||||
[DllExport("aime_io_nfc_get_aime_id", CallingConvention = CallingConvention.StdCall)]
|
||||
public static int GetAimeId(byte unitNo, IntPtr luid, nint luidSize)
|
||||
{
|
||||
|
||||
if (unitNo != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (accessCode == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Config.IDmMode == 1 && cardKind == AimeReader.CardKind.Felica)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (cardKind == AimeReader.CardKind.Null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//将卡号复制到缓存区以传递给游戏
|
||||
Marshal.Copy(AccessCodeFormatter.ToAccessCodeBytes(accessCode), 0, luid, (int)luidSize);
|
||||
Console.WriteLine("# " + cardKind + " !!");
|
||||
Console.WriteLine("<< AccessCode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//获取FeliCa ID
|
||||
[DllExport("aime_io_nfc_get_felica_id", CallingConvention = CallingConvention.StdCall)]
|
||||
public static unsafe int GetFelicaId(byte unitNo, ulong* iDM)
|
||||
{
|
||||
if (idm == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (cardKind == AimeReader.CardKind.Felica && Config.IDmMode == 1) //防止传入M1卡
|
||||
{
|
||||
ulong idmValue = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
idmValue = (idmValue << 8) | idm[i];
|
||||
}
|
||||
|
||||
*iDM = idmValue;
|
||||
Console.WriteLine("<< IDm");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//设置LED颜色
|
||||
[DllExport("aime_io_led_set_color", CallingConvention = CallingConvention.StdCall)]
|
||||
public static void SetLedColour(byte unitNo, byte r, byte g, byte b)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
68
src/NfcAime.Dll/MiFareHandle.cs
Normal file
68
src/NfcAime.Dll/MiFareHandle.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll;
|
||||
using NfcAime.Dll.PN532;
|
||||
public static class MiFareHandle
|
||||
{
|
||||
|
||||
public static readonly string[] MifareClassicKeys =
|
||||
[
|
||||
"6090D00632F5","019761AA8082","574343467632","A99164400748","62742819AD7C","CC5075E42BA1",
|
||||
"B9DF35A0814C","8AF9C718F23D","58CD5C3673CB","FC80E88EB88C","7A3CDAD7C023","30424C029001",
|
||||
"024E4E44001F","ECBBFA57C6AD","4757698143BD","1D30972E6485","F8526D1A8D6D","1300EC8C7E80",
|
||||
"F80A65A87FFA","DEB06ED4AF8E","4AD96BF28190","000390014D41","0800F9917CB0","730050555253",
|
||||
"4146D4A956C4","131157FBB126","E69DD9015A43","337237F254D5","9A8389F32FBF","7B8FB4A7100B",
|
||||
"C8382A233993","7B304F2A12A6","FC9418BF788B"
|
||||
];
|
||||
|
||||
public static bool TryMifareAuthenticate(
|
||||
Pn532Session session,
|
||||
byte tg,
|
||||
byte blockNumber,
|
||||
bool keyTypeA,
|
||||
ReadOnlySpan<byte> key,
|
||||
ReadOnlySpan<byte> uid4
|
||||
)
|
||||
{
|
||||
var cmd = new byte[2 + 1 + 1 + 6 + 4];
|
||||
cmd[0] = 0x40;
|
||||
cmd[1] = tg;
|
||||
cmd[2] = keyTypeA ? (byte)0x60 : (byte)0x61;
|
||||
cmd[3] = blockNumber;
|
||||
key.CopyTo(cmd.AsSpan(4, 6));
|
||||
uid4.CopyTo(cmd.AsSpan(10, 4));
|
||||
|
||||
var response = session.SendCommand(cmd, TimeSpan.FromMilliseconds(1000));
|
||||
if (response.Kind != Pn532FrameKind.Data || response.Payload.Length < 2 || response.Payload[0] != 0x41)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return response.Payload[1] == 0x00;
|
||||
}
|
||||
|
||||
public static byte[] ReadMifareBlock(Pn532Session session, byte tg, byte blockNumber)
|
||||
{
|
||||
var cmd = new byte[] { 0x40, tg, 0x30, blockNumber };
|
||||
var response = AimeReader.ExpectPn532ResponseCode(session.SendCommand(cmd, TimeSpan.FromMilliseconds(1200)), expectedResponseCode: 0x41);
|
||||
if (response.Payload.Length < 2 || response.Payload[1] != 0x00)
|
||||
{
|
||||
throw new InvalidOperationException($"Mifare read block failed: status=0x{(response.Payload.Length > 1 ? response.Payload[1] : 0):X2}");
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
if (response.Payload.Length == 2)
|
||||
{
|
||||
data = Array.Empty<byte>();
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new byte[response.Payload.Length - 2];
|
||||
Array.Copy(response.Payload, 2, data, 0, data.Length);
|
||||
}
|
||||
|
||||
var result = new byte[16];
|
||||
Array.Copy(data, 0, result, 0, 16);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
160
src/NfcAime.Dll/NfcAime.Dll.csproj
Normal file
160
src/NfcAime.Dll/NfcAime.Dll.csproj
Normal file
@@ -0,0 +1,160 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project=".\packages\Costura.Fody.6.2.0\build\Costura.Fody.props" Condition="Exists('.\packages\Costura.Fody.6.2.0\build\Costura.Fody.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{2ED77BFD-0EF1-49C4-A199-75D89EC3941E}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>NfcAime.Dll</RootNamespace>
|
||||
<AssemblyName>NfcAime.Dll</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<DllExportTargetFrameworkVersion>v4.5</DllExportTargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<LangVersion>13</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DllExportIdent>8BAB90F1-9613-4660-B2E2-C888DDACEA57</DllExportIdent>
|
||||
<DllExportMetaLibName>DllExport.dll</DllExportMetaLibName>
|
||||
<DllExportNamespace>NfcAime.Dll</DllExportNamespace>
|
||||
<DllExportDDNSCecil>true</DllExportDDNSCecil>
|
||||
<DllExportSkipOnAnyCpu>false</DllExportSkipOnAnyCpu>
|
||||
<DllExportPlatform>Auto</DllExportPlatform>
|
||||
<DllExportOrdinalsBase>1</DllExportOrdinalsBase>
|
||||
<DllExportGenExpLib>false</DllExportGenExpLib>
|
||||
<DllExportOurILAsm>false</DllExportOurILAsm>
|
||||
<DllExportSysObjRebase>false</DllExportSysObjRebase>
|
||||
<DllExportLeaveIntermediateFiles>false</DllExportLeaveIntermediateFiles>
|
||||
<DllExportTimeout>30000</DllExportTimeout>
|
||||
<DllExportPeCheck>6</DllExportPeCheck>
|
||||
<DllExportPatches>0</DllExportPatches>
|
||||
<DllExportRefreshObj>false</DllExportRefreshObj>
|
||||
<DllExportILAsmExternAsm />
|
||||
<DllExportILAsmTypeRef />
|
||||
<DllExportTypeRefOptions>0</DllExportTypeRefOptions>
|
||||
<DllExportRefPackages />
|
||||
<DllExportPreProcType>0</DllExportPreProcType>
|
||||
<DllExportPostProcType>0</DllExportPostProcType>
|
||||
<DllExportDir>$(MSBuildProjectDirectory)\.\packages\DllExport.1.8.1\</DllExportDir>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Costura, Version=6.2.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
|
||||
<HintPath>.\packages\Costura.Fody.6.2.0\lib\netstandard2.0\Costura.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>.\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.IO.Ports, Version=10.0.0.7, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>.\packages\System.IO.Ports.10.0.7\lib\net462\System.IO.Ports.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>.\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>.\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>.\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AccessCodeFormatter.cs" />
|
||||
<Compile Include="Config.cs" />
|
||||
<Compile Include="FelicaCommandBuilder.cs" />
|
||||
<Compile Include="FeliCaDecryptor.cs" />
|
||||
<Compile Include="FelicaResponseParser.cs" />
|
||||
<Compile Include="MainDll.cs" />
|
||||
<Compile Include="MiFareHandle.cs" />
|
||||
<Compile Include="PN532\CompilerSupport.cs" />
|
||||
<Compile Include="PN532\IPn532FrameTransport.cs" />
|
||||
<Compile Include="PN532\Pn532FrameKind.cs" />
|
||||
<Compile Include="PN532\Pn532FrameParser.cs" />
|
||||
<Compile Include="PN532\Pn532FrameParseResult.cs" />
|
||||
<Compile Include="PN532\Pn532HsuFrame.cs" />
|
||||
<Compile Include="PN532\Pn532Session.cs" />
|
||||
<Compile Include="PN532\ReplayFrameTransport.cs" />
|
||||
<Compile Include="PN532\SerialFrameTransport.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="AimeReader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="FodyWeavers.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DllExport">
|
||||
<Version>1.8.1</Version>
|
||||
<Visible>false</Visible>
|
||||
<Wz>1</Wz>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project=".\packages\Fody.6.9.3\build\Fody.targets" Condition="Exists('.\packages\Fody.6.9.3\build\Fody.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('.\packages\Fody.6.9.3\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\Fody.6.9.3\build\Fody.targets'))" />
|
||||
<Error Condition="!Exists('.\packages\Costura.Fody.6.2.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\Costura.Fody.6.2.0\build\Costura.Fody.props'))" />
|
||||
<Error Condition="!Exists('.\packages\Costura.Fody.6.2.0\build\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '.\packages\Costura.Fody.6.2.0\build\Costura.Fody.targets'))" />
|
||||
</Target>
|
||||
<Import Project=".\packages\Costura.Fody.6.2.0\build\Costura.Fody.targets" Condition="Exists('.\packages\Costura.Fody.6.2.0\build\Costura.Fody.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
<ImportGroup Label=".NET DllExport">
|
||||
<Import Project="$(DllExportDir)packages\DllExport.1.8.1\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(DllExportDir)packages\DllExport.1.8.1\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
|
||||
</ImportGroup>
|
||||
<Target Name="DllExportRestorePkg" BeforeTargets="PrepareForBuild">
|
||||
<Error Condition="'$(DllExportNoRestore)'!='true' And !Exists('$(DllExportDir)DllExport.bat')" Text="DllExport.bat was not found in $(DllExportDir); https://github.com/3F/DllExport" />
|
||||
<Exec Condition="'$(DllExportNoRestore)'!='true' And ('$(DllExportModImported)'!='true' Or !Exists('$(DllExportDir)packages\DllExport.1.8.1\tools\net.r_eg.DllExport.targets') Or !Exists('$(DllExportDir)packages\DllExport.1.8.1\gcache')) And Exists('$(DllExportDir)DllExport.bat')" Command=".\DllExport.bat -action Restore" WorkingDirectory="$(DllExportDir)" />
|
||||
<MSBuild Condition="'$(DllExportModImported)'!='true'" Projects="$(DllExportDir)packages\DllExport.1.8.1\tools\net.r_eg.DllExport.targets" Targets="DllExportMetaXBaseTarget" Properties="TargetFramework=$(TargetFramework)">
|
||||
<Output TaskParameter="TargetOutputs" PropertyName="DllExportMetaXBase" />
|
||||
</MSBuild>
|
||||
<ItemGroup>
|
||||
<Reference Include="DllExport, PublicKeyToken=8337224c9ad9e356">
|
||||
<HintPath>$(DllExportDir)packages\DllExport.1.8.1\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName)</HintPath>
|
||||
<Private>False</Private>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<Target Name="DllExportRPkgDynamicImport" BeforeTargets="PostBuildEvent" DependsOnTargets="GetFrameworkPaths" Condition="'$(DllExportModImported)'!='true' And '$(DllExportRPkgDyn)'!='false'">
|
||||
<MSBuild BuildInParallel="true" UseResultsCache="true" Projects="$(MSBuildProjectFullPath)" Properties="Configuration=$(Configuration);DllExportRPkgDyn=true" Targets="Build" />
|
||||
</Target>
|
||||
</Project>
|
||||
23
src/NfcAime.Dll/PN532/CompilerSupport.cs
Normal file
23
src/NfcAime.Dll/PN532/CompilerSupport.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
// 1. 支持 init-only setters (C# 9)
|
||||
internal static class IsExternalInit {}
|
||||
// 2. 支持 required 关键字 (C# 11)
|
||||
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
|
||||
internal class RequiredMemberAttribute : System.Attribute {}
|
||||
// 3. 支持编译器特性检查 (C# 11+)
|
||||
[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true, Inherited = false)]
|
||||
internal class CompilerFeatureRequiredAttribute : System.Attribute
|
||||
{
|
||||
public CompilerFeatureRequiredAttribute(string featureName)
|
||||
{
|
||||
FeatureName = featureName;
|
||||
}
|
||||
public string FeatureName { get; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
13
src/NfcAime.Dll/PN532/IPn532FrameTransport.cs
Normal file
13
src/NfcAime.Dll/PN532/IPn532FrameTransport.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
public interface IPn532FrameTransport : IDisposable
|
||||
{
|
||||
void Open();
|
||||
void Close();
|
||||
void WriteFrame(ReadOnlySpan<byte> frame);
|
||||
Pn532FrameParseResult ReadFrame(TimeSpan timeout);
|
||||
}
|
||||
}
|
||||
10
src/NfcAime.Dll/PN532/Pn532FrameKind.cs
Normal file
10
src/NfcAime.Dll/PN532/Pn532FrameKind.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
public enum Pn532FrameKind
|
||||
{
|
||||
Ack,
|
||||
Nak,
|
||||
Data,
|
||||
ChecksumError,
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
14
src/NfcAime.Dll/PN532/Pn532FrameParseResult.cs
Normal file
14
src/NfcAime.Dll/PN532/Pn532FrameParseResult.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
public sealed class Pn532FrameParseResult
|
||||
{
|
||||
public required Pn532FrameKind Kind { get; init; }
|
||||
public byte Tfi { get; init; }
|
||||
public byte[] Payload { get; init; } = Array.Empty<byte>();
|
||||
public byte[]? RawFrame { get; init; }
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
85
src/NfcAime.Dll/PN532/Pn532FrameParser.cs
Normal file
85
src/NfcAime.Dll/PN532/Pn532FrameParser.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
internal static class Pn532FrameParser
|
||||
{
|
||||
public static Pn532FrameParseResult Parse(ReadOnlySpan<byte> frame)
|
||||
{
|
||||
if (frame.Length < 6)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Frame too short", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var ack = new ReadOnlySpan<byte>(Pn532HsuFrame.AckFrame);
|
||||
var nak = new ReadOnlySpan<byte>(Pn532HsuFrame.NakFrame);
|
||||
|
||||
bool isAck = frame.Length >= 6 && frame.Slice(0, 6).SequenceEqual(ack);
|
||||
bool isNak = frame.Length >= 6 && frame.Slice(0, 6).SequenceEqual(nak);
|
||||
|
||||
if (!isAck && !isNak)
|
||||
{
|
||||
if (frame[0] != Pn532HsuFrame.Preamble || frame[1] != Pn532HsuFrame.StartCode1 || frame[2] != Pn532HsuFrame.StartCode2)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Missing preamble/start codes", RawFrame = frame.ToArray() };
|
||||
}
|
||||
}
|
||||
|
||||
if (isAck)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Ack, RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (isNak)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Nak, RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var length = frame[3];
|
||||
var lcs = frame[4];
|
||||
if (((length + lcs) & 0xFF) != 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.ChecksumError, Error = "LCS checksum mismatch", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Invalid length", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var expectedLength = 7 + length;
|
||||
if (frame.Length < expectedLength)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Frame length mismatch", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
var tfi = frame[5];
|
||||
var payloadLength = length - 1;
|
||||
|
||||
var payload = payloadLength == 0 ? Array.Empty<byte>() : frame.Slice(6, payloadLength).ToArray();
|
||||
var dcs = frame[6 + payloadLength];
|
||||
var sum = tfi;
|
||||
for (var index = 0; index < payload.Length; index++)
|
||||
{
|
||||
sum += payload[index];
|
||||
}
|
||||
|
||||
if (((sum + dcs) & 0xFF) != 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.ChecksumError, Error = "DCS checksum mismatch", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
if (frame[7 + payloadLength] != Pn532HsuFrame.Postamble)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Missing postamble", RawFrame = frame.ToArray() };
|
||||
}
|
||||
|
||||
return new Pn532FrameParseResult
|
||||
{
|
||||
Kind = Pn532FrameKind.Data,
|
||||
Tfi = tfi,
|
||||
Payload = payload,
|
||||
RawFrame = frame.Slice(0, expectedLength).ToArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/NfcAime.Dll/PN532/Pn532HsuFrame.cs
Normal file
63
src/NfcAime.Dll/PN532/Pn532HsuFrame.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
internal static class Pn532HsuFrame
|
||||
{
|
||||
public const byte Preamble = 0x00;
|
||||
public const byte StartCode1 = 0x00;
|
||||
public const byte StartCode2 = 0xFF;
|
||||
public const byte Postamble = 0x00;
|
||||
|
||||
public const byte HostToPn532Tfi = 0xD4;
|
||||
public const byte Pn532ToHostTfi = 0xD5;
|
||||
|
||||
public static readonly byte[] AckFrame =
|
||||
[
|
||||
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00
|
||||
];
|
||||
|
||||
public static readonly byte[] NakFrame =
|
||||
[
|
||||
0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00
|
||||
];
|
||||
|
||||
public static byte[] BuildDataFrame(byte tfi, ReadOnlySpan<byte> data)
|
||||
{
|
||||
if (data.Length + 1 > byte.MaxValue)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(data), "PN532 HSU frames must fit in 255 bytes.");
|
||||
}
|
||||
|
||||
var length = (byte)(data.Length + 1);
|
||||
var lcs = ComputeLcs(length);
|
||||
var dcs = ComputeDcs(tfi, data);
|
||||
|
||||
var frame = new byte[7 + length];
|
||||
frame[0] = Preamble;
|
||||
frame[1] = StartCode1;
|
||||
frame[2] = StartCode2;
|
||||
frame[3] = length;
|
||||
frame[4] = lcs;
|
||||
frame[5] = tfi;
|
||||
data.CopyTo(frame.AsSpan(6));
|
||||
frame[6 + data.Length] = dcs;
|
||||
frame[7 + data.Length] = Postamble;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public static byte ComputeLcs(byte length)
|
||||
=> (byte)((0x100 - length) & 0xFF);
|
||||
|
||||
public static byte ComputeDcs(byte tfi, ReadOnlySpan<byte> data)
|
||||
{
|
||||
var sum = tfi;
|
||||
for (var index = 0; index < data.Length; index++)
|
||||
{
|
||||
sum += data[index];
|
||||
}
|
||||
|
||||
return (byte)((0x100 - (sum & 0xFF)) & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
98
src/NfcAime.Dll/PN532/Pn532Session.cs
Normal file
98
src/NfcAime.Dll/PN532/Pn532Session.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
public sealed class Pn532Session : IDisposable
|
||||
{
|
||||
private readonly IPn532FrameTransport _transport;
|
||||
private readonly TimeSpan _timeout;
|
||||
private readonly int _maxRetries;
|
||||
|
||||
public Pn532Session(IPn532FrameTransport transport, TimeSpan timeout, int maxRetries)
|
||||
{
|
||||
_transport = transport;
|
||||
_timeout = timeout;
|
||||
_maxRetries = maxRetries;
|
||||
}
|
||||
|
||||
public void Open() => _transport.Open();
|
||||
|
||||
public void Close() => _transport.Close();
|
||||
|
||||
public void Dispose() => _transport.Dispose();
|
||||
|
||||
public Pn532FrameParseResult SendCommand(ReadOnlySpan<byte> payload)
|
||||
=> SendCommand(payload, responseTimeout: null);
|
||||
|
||||
public Pn532FrameParseResult SendCommand(ReadOnlySpan<byte> payload, TimeSpan? responseTimeout)
|
||||
{
|
||||
var responseReadTimeout = responseTimeout ?? _timeout;
|
||||
var frame = Pn532HsuFrame.BuildDataFrame(Pn532HsuFrame.HostToPn532Tfi, payload);
|
||||
var maxAttempts = _maxRetries + 1;
|
||||
for (var attempt = 0; attempt < maxAttempts; attempt++)
|
||||
{
|
||||
_transport.WriteFrame(frame);
|
||||
var ack = _transport.ReadFrame(_timeout);
|
||||
if (ack.Kind == Pn532FrameKind.Ack)
|
||||
{
|
||||
var response = _transport.ReadFrame(responseReadTimeout);
|
||||
if (response.Kind == Pn532FrameKind.Data)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
if (response.Kind == Pn532FrameKind.ChecksumError)
|
||||
{
|
||||
Console.WriteLine($"PN532 checksum error (response); retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (response.Kind == Pn532FrameKind.Invalid)
|
||||
{
|
||||
if (string.Equals(response.Error, "Read timeout", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
//Console.WriteLine($"PN532 timeout (response after ACK); retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"PN532 read error (response): {response.Error}; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unexpected response kind after ACK; retry rather than immediately failing.
|
||||
Console.WriteLine($"PN532 unexpected response kind {response.Kind}; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ack.Kind == Pn532FrameKind.Nak)
|
||||
{
|
||||
Console.WriteLine($"PN532 NAK received; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ack.Kind == Pn532FrameKind.ChecksumError)
|
||||
{
|
||||
Console.WriteLine($"PN532 checksum error; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ack.Kind == Pn532FrameKind.Invalid)
|
||||
{
|
||||
if (string.Equals(ack.Error, "Read timeout", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"PN532 timeout (waiting for ACK); retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine($"PN532 read error: {ack.Error}; retry {attempt + 1} of {maxAttempts}.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return new Pn532FrameParseResult
|
||||
{
|
||||
Kind = Pn532FrameKind.Invalid,
|
||||
Error = "Retry limit exceeded (no valid ACK/response). Check COM port, baud rate, module mode (HSU/UART), and power."
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/NfcAime.Dll/PN532/ReplayFrameTransport.cs
Normal file
60
src/NfcAime.Dll/PN532/ReplayFrameTransport.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
internal sealed class ReplayFrameTransport : IPn532FrameTransport
|
||||
{
|
||||
private readonly Queue<byte[]> _frames;
|
||||
private bool _injectCorruption;
|
||||
|
||||
public ReplayFrameTransport(IEnumerable<byte[]> frames, bool injectCorruption)
|
||||
{
|
||||
_frames = new Queue<byte[]>();
|
||||
foreach (var frame in frames)
|
||||
{
|
||||
if (frame == null) continue;
|
||||
byte[] copy = new byte[frame.Length];
|
||||
Array.Copy(frame, copy, frame.Length);
|
||||
_frames.Enqueue(copy);
|
||||
}
|
||||
_injectCorruption = injectCorruption;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
}
|
||||
|
||||
public void WriteFrame(ReadOnlySpan<byte> frame)
|
||||
{
|
||||
}
|
||||
|
||||
public Pn532FrameParseResult ReadFrame(TimeSpan timeout)
|
||||
{
|
||||
if (_frames.Count == 0)
|
||||
{
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Replay buffer empty" };
|
||||
}
|
||||
|
||||
var frame = _frames.Dequeue();
|
||||
if (_injectCorruption)
|
||||
{
|
||||
_injectCorruption = false;
|
||||
if (frame.Length > 0)
|
||||
{
|
||||
frame[frame.Length - 1] ^= 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
return Pn532FrameParser.Parse(frame);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_frames.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
162
src/NfcAime.Dll/PN532/SerialFrameTransport.cs
Normal file
162
src/NfcAime.Dll/PN532/SerialFrameTransport.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
|
||||
namespace NfcAime.Dll.PN532 {
|
||||
internal sealed class SerialFrameTransport : IPn532FrameTransport
|
||||
{
|
||||
private readonly SerialPort _port;
|
||||
private readonly TimeSpan _readChunkTimeout;
|
||||
private readonly int _maxFrameBytes;
|
||||
private bool _wakeupSent;
|
||||
|
||||
public SerialFrameTransport(string portName, int baud, TimeSpan readChunkTimeout, int maxFrameBytes = 300)
|
||||
{
|
||||
var chunkTimeout = readChunkTimeout;
|
||||
if (chunkTimeout <= TimeSpan.Zero || chunkTimeout > TimeSpan.FromMilliseconds(50))
|
||||
{
|
||||
chunkTimeout = TimeSpan.FromMilliseconds(50);
|
||||
}
|
||||
|
||||
_port = new SerialPort(portName, baud)
|
||||
{
|
||||
ReadTimeout = (int)chunkTimeout.TotalMilliseconds,
|
||||
WriteTimeout = (int)readChunkTimeout.TotalMilliseconds,
|
||||
Handshake = Handshake.None,
|
||||
Parity = Parity.None,
|
||||
DataBits = 8,
|
||||
StopBits = StopBits.One,
|
||||
DtrEnable = false,
|
||||
RtsEnable = false
|
||||
};
|
||||
_readChunkTimeout = chunkTimeout;
|
||||
_maxFrameBytes = maxFrameBytes;
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
if (!_port.IsOpen)
|
||||
{
|
||||
_port.Open();
|
||||
_port.DiscardInBuffer();
|
||||
_port.DiscardOutBuffer();
|
||||
SendWakeupPattern();
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (_port.IsOpen)
|
||||
{
|
||||
_port.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteFrame(ReadOnlySpan<byte> frame)
|
||||
{
|
||||
if (!_wakeupSent)
|
||||
{
|
||||
SendWakeupPattern();
|
||||
}
|
||||
|
||||
// Clear any stale data from previous commands or wake-up echos
|
||||
if (_port.IsOpen && _port.BytesToRead > 0)
|
||||
{
|
||||
_port.DiscardInBuffer();
|
||||
}
|
||||
|
||||
var buffer = frame.ToArray();
|
||||
_port.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
private void SendWakeupPattern()
|
||||
{
|
||||
// Simple HSU wakeup: Just long preamble.
|
||||
// We avoid sending SAMConfig here to keep the buffer clean for the first real command.
|
||||
var wakeup = new byte[] { 0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
_port.Write(wakeup, 0, wakeup.Length);
|
||||
Thread.Sleep(50);
|
||||
|
||||
if (_port.IsOpen)
|
||||
{
|
||||
_port.DiscardInBuffer();
|
||||
}
|
||||
|
||||
_wakeupSent = true;
|
||||
}
|
||||
|
||||
public Pn532FrameParseResult ReadFrame(TimeSpan timeout)
|
||||
{
|
||||
var deadline = Stopwatch.GetTimestamp() + (long)(timeout.TotalSeconds * Stopwatch.Frequency);
|
||||
var buffer = new byte[_maxFrameBytes];
|
||||
var count = 0;
|
||||
|
||||
while (Stopwatch.GetTimestamp() < deadline)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_port.BytesToRead == 0)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
var next = _port.ReadByte();
|
||||
if (next < 0) continue;
|
||||
if (count < buffer.Length)
|
||||
{
|
||||
buffer[count++] = (byte)next;
|
||||
}
|
||||
|
||||
if (count >= 6)
|
||||
{
|
||||
var span = new ReadOnlySpan<byte>(buffer, 0, count);
|
||||
for (int i = 0; i <= span.Length - 6; i++)
|
||||
{
|
||||
var window = span.Slice(i);
|
||||
if (window.Length >= 6)
|
||||
{
|
||||
var ack = new ReadOnlySpan<byte>(Pn532HsuFrame.AckFrame);
|
||||
if (window.Slice(0, 6).SequenceEqual(ack))
|
||||
{
|
||||
return Pn532FrameParser.Parse(ack);
|
||||
}
|
||||
|
||||
var nak = new ReadOnlySpan<byte>(Pn532HsuFrame.NakFrame);
|
||||
if (window.Slice(0, 6).SequenceEqual(nak))
|
||||
{
|
||||
return Pn532FrameParser.Parse(nak);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.Length >= 7 && window[0] == Pn532HsuFrame.Preamble && window[1] == Pn532HsuFrame.StartCode1 && window[2] == Pn532HsuFrame.StartCode2)
|
||||
{
|
||||
var length = window[3];
|
||||
var expectedLength = 7 + length;
|
||||
if (window.Length >= expectedLength)
|
||||
{
|
||||
return Pn532FrameParser.Parse(window.Slice(0, expectedLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
return new Pn532FrameParseResult { Kind = Pn532FrameKind.Invalid, Error = "Read timeout" };
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Close();
|
||||
_port.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/NfcAime.Dll/Properties/AssemblyInfo.cs
Normal file
35
src/NfcAime.Dll/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NfcAime.Dll")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NfcAime.Dll")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2026")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2ED77BFD-0EF1-49C4-A199-75D89EC3941E")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
11
src/NfcAime.Dll/packages.config
Normal file
11
src/NfcAime.Dll/packages.config
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Costura.Fody" version="6.2.0" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="Fody" version="6.9.3" targetFramework="net472" developmentDependency="true" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net472" />
|
||||
<package id="System.IO.Ports" version="10.0.7" targetFramework="net472" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net472" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net472" />
|
||||
<package id="DllExport" version="1.8.1" targetFramework="net40" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user