diff --git a/NFCForIoT/CS/NFCForIoT.sln b/NFCForIoT/CS/NFCForIoT.sln
new file mode 100644
index 000000000..ef6619936
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT.sln
@@ -0,0 +1,54 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NFCForIoT", "NFCForIoT\NFCForIoT.csproj", "{9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PcscSdk", "PcscSdk\PcscSdk.csproj", "{78F7351D-0EB3-49E4-B257-36B7977954F9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|ARM = Release|ARM
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|ARM.ActiveCfg = Debug|ARM
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|ARM.Build.0 = Debug|ARM
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|ARM.Deploy.0 = Debug|ARM
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|x64.ActiveCfg = Debug|x64
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|x64.Build.0 = Debug|x64
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|x64.Deploy.0 = Debug|x64
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|x86.ActiveCfg = Debug|x86
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|x86.Build.0 = Debug|x86
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Debug|x86.Deploy.0 = Debug|x86
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|ARM.ActiveCfg = Release|ARM
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|ARM.Build.0 = Release|ARM
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|ARM.Deploy.0 = Release|ARM
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|x64.ActiveCfg = Release|x64
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|x64.Build.0 = Release|x64
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|x64.Deploy.0 = Release|x64
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|x86.ActiveCfg = Release|x86
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|x86.Build.0 = Release|x86
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}.Release|x86.Deploy.0 = Release|x86
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Debug|ARM.ActiveCfg = Debug|ARM
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Debug|ARM.Build.0 = Debug|ARM
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Debug|x64.ActiveCfg = Debug|x64
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Debug|x64.Build.0 = Debug|x64
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Debug|x86.ActiveCfg = Debug|x86
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Debug|x86.Build.0 = Debug|x86
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Release|ARM.ActiveCfg = Release|ARM
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Release|ARM.Build.0 = Release|ARM
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Release|x64.ActiveCfg = Release|x64
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Release|x64.Build.0 = Release|x64
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Release|x86.ActiveCfg = Release|x86
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/NFCForIoT/CS/NFCForIoT/App.xaml b/NFCForIoT/CS/NFCForIoT/App.xaml
new file mode 100644
index 000000000..fcd6291f3
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/NFCForIoT/CS/NFCForIoT/App.xaml.cs b/NFCForIoT/CS/NFCForIoT/App.xaml.cs
new file mode 100644
index 000000000..c12eb3ed6
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/App.xaml.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.ApplicationModel;
+using Windows.ApplicationModel.Activation;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+namespace NFCForIoT
+{
+ ///
+ /// Provides application-specific behavior to supplement the default Application class.
+ ///
+ sealed partial class App : Application
+ {
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ this.Suspending += OnSuspending;
+ }
+
+ ///
+ /// Invoked when the application is launched normally by the end user. Other entry points
+ /// will be used such as when the application is launched to open a specific file.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnLaunched(LaunchActivatedEventArgs e)
+ {
+#if DEBUG
+ if (System.Diagnostics.Debugger.IsAttached)
+ {
+ this.DebugSettings.EnableFrameRateCounter = true;
+ }
+#endif
+ Frame rootFrame = Window.Current.Content as Frame;
+
+ // Do not repeat app initialization when the Window already has content,
+ // just ensure that the window is active
+ if (rootFrame == null)
+ {
+ // Create a Frame to act as the navigation context and navigate to the first page
+ rootFrame = new Frame();
+
+ rootFrame.NavigationFailed += OnNavigationFailed;
+
+ if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
+ {
+ //TODO: Load state from previously suspended application
+ }
+
+ // Place the frame in the current Window
+ Window.Current.Content = rootFrame;
+ }
+
+ if (e.PrelaunchActivated == false)
+ {
+ if (rootFrame.Content == null)
+ {
+ // When the navigation stack isn't restored navigate to the first page,
+ // configuring the new page by passing required information as a navigation
+ // parameter
+ rootFrame.Navigate(typeof(MainPage), e.Arguments);
+ }
+ // Ensure the current window is active
+ Window.Current.Activate();
+ }
+ }
+
+ ///
+ /// Invoked when Navigation to a certain page fails
+ ///
+ /// The Frame which failed navigation
+ /// Details about the navigation failure
+ void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
+ {
+ throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
+ }
+
+ ///
+ /// Invoked when application execution is being suspended. Application state is saved
+ /// without knowing whether the application will be terminated or resumed with the contents
+ /// of memory still intact.
+ ///
+ /// The source of the suspend request.
+ /// Details about the suspend request.
+ private void OnSuspending(object sender, SuspendingEventArgs e)
+ {
+ var deferral = e.SuspendingOperation.GetDeferral();
+ //TODO: Save application state and stop any background activity
+ deferral.Complete();
+ }
+ }
+}
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/LockScreenLogo.scale-200.png b/NFCForIoT/CS/NFCForIoT/Assets/LockScreenLogo.scale-200.png
new file mode 100644
index 000000000..735f57adb
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/LockScreenLogo.scale-200.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/SplashScreen.scale-200.png b/NFCForIoT/CS/NFCForIoT/Assets/SplashScreen.scale-200.png
new file mode 100644
index 000000000..023e7f1fe
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/SplashScreen.scale-200.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/Square150x150Logo.scale-200.png b/NFCForIoT/CS/NFCForIoT/Assets/Square150x150Logo.scale-200.png
new file mode 100644
index 000000000..af49fec1a
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/Square150x150Logo.scale-200.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/Square44x44Logo.scale-200.png b/NFCForIoT/CS/NFCForIoT/Assets/Square44x44Logo.scale-200.png
new file mode 100644
index 000000000..ce342a2ec
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/Square44x44Logo.scale-200.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/NFCForIoT/CS/NFCForIoT/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
new file mode 100644
index 000000000..f6c02ce97
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/Square44x44Logo.targetsize-24_altform-unplated.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/StoreLogo.png b/NFCForIoT/CS/NFCForIoT/Assets/StoreLogo.png
new file mode 100644
index 000000000..7385b56c0
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/StoreLogo.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/Assets/Wide310x150Logo.scale-200.png b/NFCForIoT/CS/NFCForIoT/Assets/Wide310x150Logo.scale-200.png
new file mode 100644
index 000000000..288995b39
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/Assets/Wide310x150Logo.scale-200.png differ
diff --git a/NFCForIoT/CS/NFCForIoT/MainPage.xaml b/NFCForIoT/CS/NFCForIoT/MainPage.xaml
new file mode 100644
index 000000000..810716a93
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/MainPage.xaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NFCForIoT/CS/NFCForIoT/MainPage.xaml.cs b/NFCForIoT/CS/NFCForIoT/MainPage.xaml.cs
new file mode 100644
index 000000000..37d0c2d2d
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/MainPage.xaml.cs
@@ -0,0 +1,266 @@
+using Pcsc;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Devices.SmartCards;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+using System.Threading.Tasks;
+using System.Diagnostics;
+using Pcsc.Common;
+
+// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
+
+namespace NFCForIoT
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class MainPage : Page
+ {
+ public MainPage()
+ {
+ this.InitializeComponent();
+ }
+ private static readonly byte[] PasswordStatic = { 0x11, 0x22, 0x33, 0x44 };
+ private static readonly byte[] PasswordAcknowledgeStatic = { 0xAB, 0xCD };
+
+ SmartCardReader m_cardReader;
+
+ protected async override void OnNavigatedTo(NavigationEventArgs e)
+ {
+ // First try to find a reader that advertises as being NFC
+ var deviceInfo = await SmartCardReaderUtils.GetFirstSmartCardReaderInfo(SmartCardReaderKind.Nfc);
+ if (deviceInfo == null)
+ {
+ // If we didn't find an NFC reader, let's see if there's a "generic" reader meaning we're not sure what type it is
+ deviceInfo = await SmartCardReaderUtils.GetFirstSmartCardReaderInfo(SmartCardReaderKind.Any);
+ }
+
+ if (deviceInfo == null)
+ {
+ LogMessage("NFC card reader mode not supported on this device");
+ return;
+ }
+
+ if (m_cardReader == null)
+ {
+ m_cardReader = await SmartCardReader.FromIdAsync(deviceInfo.Id);
+ m_cardReader.CardAdded += cardReader_CardAdded;
+ m_cardReader.CardRemoved += cardReader_CardRemoved;
+ }
+ }
+
+ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
+ {
+ // Clean up
+ if (m_cardReader != null)
+ {
+ m_cardReader.CardAdded -= cardReader_CardAdded;
+ m_cardReader.CardRemoved -= cardReader_CardRemoved;
+ m_cardReader = null;
+ }
+
+ base.OnNavigatingFrom(e);
+ }
+
+ private void cardReader_CardRemoved(SmartCardReader sender, CardRemovedEventArgs args)
+ {
+ LogMessage("Card removed");
+
+ var ignored = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
+ {
+ UidPanel.Visibility = Visibility.Visible;
+ AccessPanel.Visibility = Visibility.Visible;
+ });
+ }
+
+ private async void cardReader_CardAdded(SmartCardReader sender, CardAddedEventArgs args)
+ {
+ await HandleCard(args.SmartCard);
+ }
+
+ ///
+ /// Sample code to hande a couple of different cards based on the identification process
+ ///
+ /// None
+ private async Task HandleCard(SmartCard card)
+ {
+ try
+ {
+ // Connect to the card
+ using (SmartCardConnection connection = await card.ConnectAsync())
+ {
+ // Try to identify what type of card it was
+ IccDetection cardIdentification = new IccDetection(card, connection);
+ await cardIdentification.DetectCardTypeAync();
+ LogMessage("Connected to card\r\nPC/SC device class: " + cardIdentification.PcscDeviceClass.ToString());
+ LogMessage("Card name: " + cardIdentification.PcscCardName.ToString());
+ LogMessage("ATR: " + BitConverter.ToString(cardIdentification.Atr));
+
+ if ((cardIdentification.PcscDeviceClass == Pcsc.Common.DeviceClass.StorageClass) &&
+ (cardIdentification.PcscCardName == Pcsc.CardName.MifareUltralightC
+ || cardIdentification.PcscCardName == Pcsc.CardName.MifareUltralight
+ || cardIdentification.PcscCardName == Pcsc.CardName.MifareUltralightEV1))
+ {
+ // Handle MIFARE Ultralight
+ MifareUltralight.AccessHandler mifareULAccess = new MifareUltralight.AccessHandler(connection);
+
+ await mifareULAccess.ReadCapsAsync();
+
+ if (!mifareULAccess.isNTag21x)
+ {
+ var ignored1 = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
+ {
+ var msgbox = new Windows.UI.Popups.MessageDialog("This application only works with the NXP NTAG21x line of NFC chips. Sorry.");
+ msgbox.Commands.Add(new Windows.UI.Popups.UICommand("OK"));
+ await msgbox.ShowAsync();
+ });
+
+ return;
+ }
+
+ bool authenticated = false;
+
+ byte[] password = await ParseField(passwordBox, 4);
+ byte[] passwordAck = await ParseField(passwordAckBox, 2);
+
+ if (password != null && passwordAck != null)
+ {
+ try
+ {
+ await mifareULAccess.AuthenticateWithPassword(password, passwordAck);
+ authenticated = true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Exception sending provisioning password: " + ex);
+ }
+
+ if (!authenticated && autoProvision.IsChecked == true)
+ {
+ try
+ {
+ await mifareULAccess.ProvisionPassword(true, 0, password, passwordAck);
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Exception sending provisioning password: " + ex);
+ }
+ }
+ }
+
+ bool accessCountEnabled = false;
+
+ try
+ {
+ uint responseAccess = await mifareULAccess.GetAccessCountAsync();
+ LogMessage("ReadCount: " + responseAccess.ToString());
+
+ var ignored2 = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
+ {
+ accessCount.Text = responseAccess.ToString();
+ });
+
+ accessCountEnabled = true;
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Exception sending Getting Access Count: " + ex);
+ }
+
+ if (!accessCountEnabled)
+ {
+ var ignored3 = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
+ {
+ accessCount.Text = " ";
+ accessCountEnable.Visibility = Visibility.Visible;
+ });
+
+ if (autoProvision.IsChecked == true)
+ {
+ await mifareULAccess.EnableAccessCountAsync();
+ }
+ }
+
+ for (byte i = 0; i < mifareULAccess.Blocks; i++)
+ {
+ byte[] response = await mifareULAccess.ReadAsync((byte)(4 * i));
+ for (byte y = 0; y < 4; y++)
+ {
+ byte[] buf4 = new byte[4];
+ Array.Copy(response, y * 4, buf4, 0, 4);
+ LogMessage((i * 4 + y).ToString("x2") + ": " + BitConverter.ToString(buf4));
+ }
+ }
+
+ byte[] responseUid = await mifareULAccess.GetUidAsync();
+ string uidString = BitConverter.ToString(responseUid);
+ LogMessage("UID: " + uidString);
+
+ var ignored4 = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
+ {
+ uid.Text = uidString;
+ UidPanel.Visibility = Visibility.Visible;
+ });
+
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Exception handling card: " + ex.ToString());
+ }
+ }
+
+ public void LogMessage(string message)
+ {
+ if (!this.Dispatcher.HasThreadAccess)
+ {
+ var ignored = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { LogMessage(message); });
+ return;
+ }
+
+ Debug.WriteLine(message);
+ if (Diagnostics.Text != "")
+ {
+ Diagnostics.Text += "\r\n";
+ }
+ Diagnostics.Text += message;
+ StatusBlockScroller.ChangeView(0, StatusBlockScroller.ExtentHeight, 1);
+ }
+
+ private async Task ParseField(PasswordBox box, int bytesRequired)
+ {
+ byte[] bytes = new byte[bytesRequired];
+
+ string password = null;
+ await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
+ {
+ password = box.Password;
+ });
+
+ if (password.Length < bytesRequired * 2)
+ {
+ return null;
+ }
+
+ for (var i = 0; i < password.Length; i += 2)
+ {
+ var substring = password.Substring(i, 2);
+ bytes[i / 2] = Convert.ToByte(substring, 16);
+ }
+
+ return bytes;
+ }
+ }
+}
diff --git a/NFCForIoT/CS/NFCForIoT/NFCForIoT.csproj b/NFCForIoT/CS/NFCForIoT/NFCForIoT.csproj
new file mode 100644
index 000000000..467d60593
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/NFCForIoT.csproj
@@ -0,0 +1,148 @@
+
+
+
+
+ Debug
+ x86
+ {9B31D288-70CB-4B2E-8B0E-88EA71AD42A5}
+ AppContainerExe
+ Properties
+ NFCForIoT
+ NFCForIoT
+ en-US
+ UAP
+ 10.0.19041.0
+ 10.0.15063.0
+ 14
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ NFCForIoT_TemporaryKey.pfx
+ win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UWP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+
+ App.xaml
+
+
+ MainPage.xaml
+
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+ {78f7351d-0eb3-49e4-b257-36b7977954f9}
+ PcscSdk
+
+
+
+
+ 5.0.0
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/NFCForIoT/CS/NFCForIoT/NFCForIoT_TemporaryKey.pfx b/NFCForIoT/CS/NFCForIoT/NFCForIoT_TemporaryKey.pfx
new file mode 100644
index 000000000..e780d33a6
Binary files /dev/null and b/NFCForIoT/CS/NFCForIoT/NFCForIoT_TemporaryKey.pfx differ
diff --git a/NFCForIoT/CS/NFCForIoT/Package.appxmanifest b/NFCForIoT/CS/NFCForIoT/Package.appxmanifest
new file mode 100644
index 000000000..ed3d6ddb1
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/Package.appxmanifest
@@ -0,0 +1,30 @@
+
+
+
+
+
+ NFCForIoT
+ MSFT
+ Assets\StoreLogo.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NFCForIoT/CS/NFCForIoT/Properties/AssemblyInfo.cs b/NFCForIoT/CS/NFCForIoT/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..0fb5ec0fd
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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("NFCForIoT")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NFCForIoT")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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")]
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/NFCForIoT/CS/NFCForIoT/Properties/Default.rd.xml b/NFCForIoT/CS/NFCForIoT/Properties/Default.rd.xml
new file mode 100644
index 000000000..80a960ce3
--- /dev/null
+++ b/NFCForIoT/CS/NFCForIoT/Properties/Default.rd.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NFCForIoT/CS/PcscSdk/AtrParser.cs b/NFCForIoT/CS/PcscSdk/AtrParser.cs
new file mode 100644
index 000000000..1547c5266
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/AtrParser.cs
@@ -0,0 +1,167 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using Windows.Storage.Streams;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+namespace Pcsc.Common
+{
+ public class AtrInfo
+ {
+ public const int MAXIMUM_ATR_CODES = 4;
+ ///
+ /// Helper class that holds information about the ICC Answer-To-Reset (ATR) information such
+ /// as interface character and offset and length of the historical bytes. It also hold info
+ /// about the validity of the TCK byte.
+ ///
+ public AtrInfo()
+ {
+ ProtocolInterfaceA = new byte[MAXIMUM_ATR_CODES] { 0, 0, 0, 0 };
+ ProtocolInterfaceB = new byte[MAXIMUM_ATR_CODES] { 0, 0, 0, 0 };
+ ProtocolInterfaceC = new byte[MAXIMUM_ATR_CODES] { 0, 0, 0, 0 };
+ ProtocolInterfaceD = new byte[MAXIMUM_ATR_CODES] { 0, 0, 0, 0 };
+
+ HistoricalBytes = null;
+ }
+ ///
+ /// Protocol interface characters TAi
+ ///
+ public byte[] ProtocolInterfaceA { set; get; }
+ ///
+ /// Protocol interface characters TBi
+ ///
+ public byte[] ProtocolInterfaceB { set; get; }
+ ///
+ /// Protocol interface characters TCi
+ ///
+ public byte[] ProtocolInterfaceC { set; get; }
+ ///
+ /// Protocol interface characters TDi
+ ///
+ public byte[] ProtocolInterfaceD { set; get; }
+ ///
+ /// Historical bytes if present
+ ///
+ public IBuffer HistoricalBytes { set; get; }
+ ///
+ /// Check Byte valid
+ ///
+ public bool? TckValid { set; get; }
+ }
+ ///
+ /// Helper class that parses the ATR and populate the AtrInfo class
+ ///
+ class AtrParser
+ {
+ enum AtrHeader : byte
+ {
+ InitialHeader = 0x3B,
+ }
+ ///
+ /// Main parser method that extract information about the ATR byte array
+ ///
+ ///
+ /// returns AtrInfo object if ATR is valid, null otherwise
+ ///
+ public static AtrInfo Parse(byte[] atrBytes)
+ {
+ AtrInfo atrInfo = new AtrInfo();
+ byte initialChar = 0, formatByte = 0;
+ int supportedProtocols = 0;
+
+ using (DataReader reader = DataReader.FromBuffer(atrBytes.AsBuffer()))
+ {
+ initialChar = reader.ReadByte();
+
+ if (initialChar != (byte)AtrHeader.InitialHeader)
+ {
+ return null;
+ }
+
+ formatByte = reader.ReadByte();
+ var interfacePresence = (byte)(formatByte.HiNibble() << 4);
+
+ for (int i = 0; i < AtrInfo.MAXIMUM_ATR_CODES; i++)
+ {
+ if ((interfacePresence & 0x10) != 0)
+ atrInfo.ProtocolInterfaceA[i] = reader.ReadByte();
+
+ if ((interfacePresence & 0x20) != 0)
+ atrInfo.ProtocolInterfaceB[i] = reader.ReadByte();
+
+ if ((interfacePresence & 0x40) != 0)
+ atrInfo.ProtocolInterfaceC[i] = reader.ReadByte();
+
+ if ((interfacePresence & 0x80) != 0)
+ atrInfo.ProtocolInterfaceD[i] = reader.ReadByte();
+ else
+ break;
+
+ interfacePresence = atrInfo.ProtocolInterfaceD[i];
+ supportedProtocols |= (1 << interfacePresence.LowNibble());
+ }
+
+ atrInfo.HistoricalBytes = reader.ReadBuffer(formatByte.LowNibble());
+
+ if ((supportedProtocols & ~1) != 0)
+ {
+ atrInfo.TckValid = ValidateTCK(atrBytes);
+ }
+
+ return atrInfo;
+ }
+ }
+ ///
+ /// Compute the ATR check byte (TCK)
+ ///
+ ///
+ /// return the computed TCK
+ ///
+ private static bool ValidateTCK(byte[] atr)
+ {
+ byte ctk = 0;
+
+ for (byte i = 1; i < atr.Length; i++)
+ {
+ ctk ^= atr[i];
+ }
+
+ return (ctk == 0);
+ }
+ }
+ ///
+ /// Extensions to the Byte primitive data type
+ ///
+ public static class ByteExtension
+ {
+ public const byte NIBBLE_SIZE = 4;
+ ///
+ /// Extracts the high nibble of a byte
+ ///
+ ///
+ /// return byte represeting the high nibble of a byte
+ ///
+ public static byte HiNibble(this byte value)
+ {
+ return (byte)(value >> 4);
+ }
+ ///
+ /// Extracts the low nibble of a byte
+ ///
+ ///
+ /// return byte represeting the low nibble of a byte
+ ///
+ public static byte LowNibble(this byte value)
+ {
+ return (byte)(value & 0x0F);
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/CommonDefs.cs b/NFCForIoT/CS/PcscSdk/CommonDefs.cs
new file mode 100644
index 000000000..8cfdcd5c9
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/CommonDefs.cs
@@ -0,0 +1,24 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+namespace Pcsc.Common
+{
+ ///
+ /// ICC Device class
+ ///
+ public enum DeviceClass : byte
+ {
+ Unknown = 0x00,
+ StorageClass = 0x01, // for PCSC class, there will be subcategory to identify the physical icc
+ Iso14443P4 = 0x02,
+ MifareDesfire = 0x03,
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/DesfireAccessHandler.cs b/NFCForIoT/CS/PcscSdk/DesfireAccessHandler.cs
new file mode 100644
index 000000000..575f7bb8f
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/DesfireAccessHandler.cs
@@ -0,0 +1,257 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+using System.Threading.Tasks;
+using Windows.Devices.SmartCards;
+
+using Pcsc;
+
+namespace Desfire
+{
+ ///
+ /// Access handler class for Desfire based ICC. It provides wrappers for different Desfire
+ /// commands
+ ///
+ public class AccessHandler
+ {
+ ///
+ /// connection object to smart card
+ ///
+ private SmartCardConnection connectionObject { set; get; }
+ ///
+ /// Desfire command APDU
+ ///
+ private DesfireCommand desfireCommand { set; get; }
+ ///
+ /// Desfire response APDU
+ ///
+ private DesfireResponse desfireResponse { set; get; }
+ ///
+ /// Class constructor
+ ///
+ ///
+ /// connection object to a Desfire ICC
+ ///
+ public AccessHandler(SmartCardConnection ScConnection)
+ {
+ connectionObject = ScConnection;
+
+ desfireCommand = new DesfireCommand();
+ }
+ ///
+ /// Read card details commands
+ ///
+ ///
+ /// returns Desfire CardDetails object
+ ///
+ public async Task ReadCardDetailsAsync()
+ {
+ desfireCommand.Command = (byte) DesfireCommand.CommandType.GetVersion;
+ desfireCommand.Data = null;
+
+ DesfireResponse desfireRes = await connectionObject.TransceiveAsync(desfireCommand) as DesfireResponse;
+
+ if (!desfireRes.SubsequentFrame || desfireRes.ResponseData.Length != 7)
+ {
+ return null;
+ }
+
+ CardDetails card = new CardDetails();
+
+ card.HardwareVendorID = desfireRes.ResponseData[0];
+ card.HardwareType = desfireRes.ResponseData[1];
+ card.HardwareSubType = desfireRes.ResponseData[2];
+ card.HardwareMajorVersion = desfireRes.ResponseData[3];
+ card.HardwareMinorVersion = desfireRes.ResponseData[4];
+ card.HardwareStorageSize = desfireRes.ResponseData[5];
+ card.HardwareProtocolType = desfireRes.ResponseData[6];
+
+ desfireCommand.Command = (byte)DesfireCommand.CommandType.GetAdditionalFrame;
+ desfireRes = await connectionObject.TransceiveAsync(desfireCommand) as DesfireResponse;
+
+ if (!desfireRes.SubsequentFrame || desfireRes.ResponseData.Length != 7)
+ {
+ // Not expected
+ return null;
+ }
+ card.SoftwareVendorID = desfireRes.ResponseData[0];
+ card.SoftwareType = desfireRes.ResponseData[1];
+ card.SoftwareSubType = desfireRes.ResponseData[2];
+ card.SoftwareMajorVersion = desfireRes.ResponseData[3];
+ card.SoftwareMinorVersion = desfireRes.ResponseData[4];
+ card.SoftwareStorageSize = desfireRes.ResponseData[5];
+ card.SoftwareProtocolType = desfireRes.ResponseData[6];
+
+ desfireRes = await connectionObject.TransceiveAsync(desfireCommand) as DesfireResponse;
+
+ if (!desfireRes.Succeeded || desfireRes.ResponseData.Length != 14)
+ {
+ // Not expected
+ return null;
+ }
+
+ card.UID = new byte[7];
+ System.Buffer.BlockCopy(desfireRes.ResponseData, 0, card.UID, 0, 7);
+
+ card.ProductionBatchNumber = new byte[5];
+ System.Buffer.BlockCopy(desfireRes.ResponseData, 7, card.ProductionBatchNumber, 0, 5);
+
+ card.WeekOfProduction = desfireRes.ResponseData[12];
+ card.YearOfProduction = desfireRes.ResponseData[13];
+
+ return card;
+ }
+ ///
+ /// Select ICC application by AID
+ ///
+ ///
+ /// application id
+ ///
+ public async Task SelectApplicationAsync(byte[] aid)
+ {
+ if (aid.Length != 3)
+ {
+ throw new NotSupportedException();
+ }
+
+ desfireCommand.Command = (byte)DesfireCommand.CommandType.SelectApplication;
+ desfireCommand.Data = aid;
+
+ DesfireResponse desfireRes = await connectionObject.TransceiveAsync(desfireCommand) as DesfireResponse;
+
+ if (!desfireResponse.Succeeded)
+ {
+ throw new Exception("Failure selecting application, SW=" + desfireResponse.SW + " (" + desfireResponse.SWTranslation + ")");
+ }
+ }
+ ///
+ /// Read data command
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// byte array of read data
+ ///
+ public async Task ReadDataAsync(byte fileNumber, ulong offset, ulong length)
+ {
+ var args = new byte[7];
+ args[0] = fileNumber;
+ args[1] = (byte)(offset & 0xFF);
+ args[2] = (byte)((offset >> 8) & 0xFF);
+ args[3] = (byte)((offset >> 16) & 0xFF);
+ args[4] = (byte)(length & 0xFF);
+ args[5] = (byte)((length >> 8) & 0xFF);
+ args[6] = (byte)((length >> 16) & 0xFF);
+
+ desfireCommand.Command = (byte)DesfireCommand.CommandType.ReadData;
+ desfireCommand.Data = args;
+
+ DesfireResponse desfireRes = await connectionObject.TransceiveAsync(desfireCommand) as DesfireResponse;
+
+ if (!desfireRes.Succeeded)
+ {
+ throw new Exception("Failure selecting application, SW=" + desfireResponse.SW + " (" + desfireResponse.SWTranslation + ")");
+ }
+
+ return desfireRes.ResponseData;
+ }
+ ///
+ /// Read record comand
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// byte array of read data
+ ///
+ public async Task ReadRecordAsync(byte fileNumber, ulong offset, ulong numberOfRecords)
+ {
+ var args = new byte[7];
+ args[0] = fileNumber;
+ args[1] = (byte)(offset & 0xFF);
+ args[2] = (byte)((offset >> 8) & 0xFF);
+ args[3] = (byte)((offset >> 16) & 0xFF);
+ args[4] = (byte)(numberOfRecords & 0xFF);
+ args[5] = (byte)((numberOfRecords >> 8) & 0xFF);
+ args[6] = (byte)((numberOfRecords >> 16) & 0xFF);
+
+ desfireCommand.Command = (byte)DesfireCommand.CommandType.ReadRecord;
+ desfireCommand.Data = args;
+
+ DesfireResponse desfireRes = await connectionObject.TransceiveAsync(desfireCommand) as DesfireResponse;
+
+ if (desfireRes.BoundaryError)
+ {
+ // Boundary error, the record doesn't exist
+ return null;
+ }
+ if (!desfireRes.Succeeded)
+ {
+ throw new Exception("Failure selecting application, SW=" + desfireResponse.SW + " (" + desfireResponse.SWTranslation + ")");
+ }
+
+ return desfireRes.ResponseData;
+ }
+ }
+ ///
+ /// Class hodls Desfire card details information
+ ///
+ public class CardDetails
+ {
+ public byte HardwareVendorID { get; set; }
+ public byte HardwareType { get; set; }
+ public byte HardwareSubType { get; set; }
+ public byte HardwareMajorVersion { get; set; }
+ public byte HardwareMinorVersion { get; set; }
+ public byte HardwareStorageSize { get; set; }
+ public byte HardwareProtocolType { get; set; }
+ public byte SoftwareVendorID { get; set; }
+ public byte SoftwareType { get; set; }
+ public byte SoftwareSubType { get; set; }
+ public byte SoftwareMajorVersion { get; set; }
+ public byte SoftwareMinorVersion { get; set; }
+ public byte SoftwareStorageSize { get; set; }
+ public byte SoftwareProtocolType { get; set; }
+ // 7 bytes
+ public byte[] UID { get; set; }
+ // 5 bytes
+ public byte[] ProductionBatchNumber { get; set; }
+ public byte WeekOfProduction { get; set; }
+ public byte YearOfProduction { get; set; }
+ public override string ToString()
+ {
+ return
+ "HardwareVendorID = " + HardwareVendorID.ToString() + Environment.NewLine +
+ "HardwareType = " + HardwareType.ToString() + Environment.NewLine +
+ "HardwareSubType = " + HardwareSubType.ToString() + Environment.NewLine +
+ "HardwareMajorVersion = " + HardwareMajorVersion.ToString() + Environment.NewLine +
+ "HardwareMinorVersion = " + HardwareMinorVersion.ToString() + Environment.NewLine +
+ "HardwareStorageSize = " + HardwareStorageSize.ToString() + Environment.NewLine +
+ "HardwareProtocolType = " + HardwareProtocolType.ToString() + Environment.NewLine +
+ "SoftwareVendorID = " + SoftwareVendorID.ToString() + Environment.NewLine +
+ "SoftwareType = " + SoftwareType.ToString() + Environment.NewLine +
+ "SoftwareSubType = " + SoftwareSubType.ToString() + Environment.NewLine +
+ "SoftwareMajorVersion = " + SoftwareMajorVersion.ToString() + Environment.NewLine +
+ "SoftwareMinorVersion = " + SoftwareMinorVersion.ToString() + Environment.NewLine +
+ "SoftwareStorageSize = " + SoftwareStorageSize.ToString() + Environment.NewLine +
+ "SoftwareProtocolType = " + SoftwareProtocolType.ToString() + Environment.NewLine +
+ "UID = " + BitConverter.ToString(UID) + Environment.NewLine +
+ "ProductionBatchNumber = " + BitConverter.ToString(ProductionBatchNumber) + Environment.NewLine +
+ "WeekOfProduction = " + WeekOfProduction.ToString() + Environment.NewLine +
+ "YearOfProduction = " + YearOfProduction.ToString() + Environment.NewLine;
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/DesfireCommand.cs b/NFCForIoT/CS/PcscSdk/DesfireCommand.cs
new file mode 100644
index 000000000..90b5e3c0a
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/DesfireCommand.cs
@@ -0,0 +1,101 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+namespace Desfire
+{
+ ///
+ /// Class DesfireCommand extends the Iso7816.ApduCommand and provides
+ /// mappings to Iso7816 command fields
+ ///
+ public class DesfireCommand : Iso7816.ApduCommand
+ {
+ public byte Command
+ {
+ set { base.INS = value; }
+ get { return base.INS; }
+ }
+ public byte[] Data
+ {
+ set { base.CommandData = value; }
+ get { return base.CommandData; }
+ }
+ public enum CommandType : byte
+ {
+ GetVersion = 0x60,
+ GetAdditionalFrame = 0xAF,
+ SelectApplication = 0x5A,
+ ReadData = 0xBD,
+ ReadRecord = 0xBB
+ };
+
+ public DesfireCommand()
+ : base((byte)Iso7816.Cla.ProprietaryCla9x, 0, 0, 0, null, 0)
+ {
+ ApduResponseType = typeof(Desfire.DesfireResponse);
+ }
+ public DesfireCommand(CommandType cmd, byte[] data)
+ : base((byte)Iso7816.Cla.ProprietaryCla9x, (byte)cmd, 0x00, 0x00, data, 0x00)
+ {
+ ApduResponseType = typeof(Desfire.DesfireResponse);
+ }
+ }
+ ///
+ /// Class DesfireResponse extends the Iso7816.ApduResponse.
+ ///
+ public class DesfireResponse : Iso7816.ApduResponse
+ {
+ public DesfireResponse()
+ : base()
+ { }
+ public override string SWTranslation
+ {
+ get
+ {
+ if (SW1 != 0x91)
+ {
+ return "Unknown";
+ }
+ switch (SW2)
+ {
+ case 0x00:
+ return "Success";
+
+ case 0xAF:
+ return "Additional frames expected";
+
+ default:
+ return "Unknown";
+ }
+ }
+ }
+ public override bool Succeeded
+ {
+ get
+ {
+ return SW == 0x9100;
+ }
+ }
+ public bool SubsequentFrame
+ {
+ get
+ {
+ return SW == 0x91AF;
+ }
+ }
+ public bool BoundaryError
+ {
+ get
+ {
+ return SW == 0x91BE;
+ }
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/FelicaAccessHandler.cs b/NFCForIoT/CS/PcscSdk/FelicaAccessHandler.cs
new file mode 100644
index 000000000..dde8c5f4a
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/FelicaAccessHandler.cs
@@ -0,0 +1,150 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System.Threading.Tasks;
+using Windows.Devices.SmartCards;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+
+using Pcsc;
+using System;
+
+namespace Felica
+{
+ ///
+ /// Access handler class for Felica based ICC. It provides wrappers for different Felica
+ /// commands
+ ///
+ public class AccessHandler
+ {
+ ///
+ /// connection object to smart card
+ ///
+ private SmartCardConnection connectionObject { set; get; }
+ ///
+ /// Class constructor
+ ///
+ ///
+ /// connection object to a Felica ICC
+ ///
+ public AccessHandler(SmartCardConnection ScConnection)
+ {
+ connectionObject = ScConnection;
+ }
+ ///
+ /// Wrapper method to read data from the felica card
+ ///
+ ///
+ /// The number of service
+ ///
+ ///
+ /// The service code list in little endian format
+ ///
+ ///
+ ///
+ /// The number of blocks to read
+ ///
+ ///
+ ///
+ /// The list of blocks to be read. Must be multiples of 2 or 3.
+ ///
+ ///
+ /// byte array of the read data
+ ///
+ public async Task ReadAsync(byte serviceCount, byte[] serviceCodeList, byte blockCount, byte[] blockList)
+ {
+ if (serviceCount != 1 || serviceCodeList.Length != 2)
+ {
+ throw new NotSupportedException();
+ }
+
+ var apduRes = await connectionObject.TransceiveAsync(new Felica.Check(serviceCount, serviceCodeList, blockCount, blockList));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure reading Felica card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+ ///
+ /// Wrapper method to write data to the felica card
+ ///
+ ///
+ /// The number of service
+ ///
+ ///
+ /// The service code list in little endian format
+ ///
+ ///
+ ///
+ /// The number of blocks to read
+ ///
+ ///
+ ///
+ /// The list of blocks to be read. Must be multiples of 2 or 3.
+ ///
+ /// ///
+ /// The data to write for the corresponding blocks. Must be multiple of 16 of the block count.
+ ///
+ public async Task WriteAsync(byte serviceCount, byte[] serviceCodeList, byte blockCount, byte[] blockList, byte[] blockData)
+ {
+ if (serviceCount != 1 || serviceCodeList.Length != 2)
+ {
+ throw new NotSupportedException();
+ }
+
+ if (blockData.Length != blockCount * 16)
+ {
+ throw new InvalidOperationException("Invalid blockData size");
+ }
+
+ var apduRes = await connectionObject.TransceiveAsync(new Felica.Update(serviceCount, serviceCodeList, blockCount, blockList, blockData));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure writing Felica card, " + apduRes.ToString());
+ }
+ }
+ ///
+ /// Wrapper method to perform transparent transceive data to the felica card
+ ///
+ ///
+ /// The command to send to the felica card
+ ///
+ ///
+ /// byte array of the read data
+ ///
+ public async Task TransparentExchangeAsync(byte[] commandData)
+ {
+ byte[] responseData = await connectionObject.TransparentExchangeAsync(commandData);
+
+ return responseData;
+ }
+ ///
+ /// Wrapper method get the Felica UID
+ ///
+ ///
+ /// byte array UID
+ ///
+ public async Task GetUidAsync()
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new Felica.GetUid());
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure getting UID of Felica card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/FelicaCommands.cs b/NFCForIoT/CS/PcscSdk/FelicaCommands.cs
new file mode 100644
index 000000000..d118531d1
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/FelicaCommands.cs
@@ -0,0 +1,116 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+
+namespace Felica
+{
+ ///
+ /// Felica Check command when sent to the card the card is expected to read the byte of blocks
+ /// specified in the block list. The service code should be in little endian format.
+ ///
+ public class Check : Pcsc.ReadBinary
+ {
+ public byte ServiceCount { get; private set; }
+ public byte[] ServiceCodeList { get; private set; }
+
+ public byte BlockCount {get; private set; }
+ public byte[] BlockList { get; private set; }
+
+ public Check(byte serviceCount, byte[] serviceCodeList, byte blockCount, byte[] blockList)
+ : base(0, 0)
+ {
+ ServiceCount = serviceCount;
+ ServiceCodeList = serviceCodeList;
+ BlockCount = blockCount;
+ BlockList = blockList;
+ base.CommandData = GetDataIn(serviceCount, serviceCodeList, blockCount, blockList);
+ }
+
+ private static byte[] GetDataIn(byte serviceCount, byte[] serviceCodeList, byte blockCount, byte[] blockList)
+ {
+ DataWriter dataWriter = new DataWriter();
+
+ dataWriter.WriteByte(serviceCount);
+ dataWriter.WriteBytes(serviceCodeList);
+ dataWriter.WriteByte(blockCount);
+ dataWriter.WriteBytes(blockList);
+
+ return dataWriter.DetachBuffer().ToArray();
+ }
+ }
+ ///
+ /// Felica Update command when sent to the card the card is expected to write the byte of blocks
+ /// specified in the block list. The service code should be in little endian format.
+ ///
+ public class Update : Pcsc.UpdateBinary
+ {
+ public byte ServiceCount {get; private set; }
+ public byte[] ServiceCodeList { get; private set; }
+
+ public byte BlockCount {get; private set; }
+ public byte[] BlockList { get; private set; }
+
+ public Update(byte serviceCount, byte[] serviceCodeList, byte blockCount, byte[] blockList, byte[] blockData)
+ : base(0, GetDataIn(serviceCount, serviceCodeList, blockCount, blockList, blockData))
+ {
+ ServiceCount = serviceCount;
+ ServiceCodeList = serviceCodeList;
+ BlockCount = blockCount;
+ BlockList = blockList;
+ }
+
+ private static byte[] GetDataIn(byte serviceCount, byte[] serviceCodeList, byte blockCount, byte[] blockList, byte[] blockData)
+ {
+ DataWriter dataWriter = new DataWriter();
+
+ dataWriter.WriteByte(serviceCount);
+ dataWriter.WriteBytes(serviceCodeList);
+ dataWriter.WriteByte(blockCount);
+ dataWriter.WriteBytes(blockList);
+ dataWriter.WriteBytes(blockData);
+
+ return dataWriter.DetachBuffer().ToArray();
+ }
+ }
+ ///
+ /// Felica GetUid command
+ ///
+ public class GetUid : Pcsc.GetUid
+ {
+ public GetUid()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Felica GetHistoricalBytes command
+ ///
+ public class GetHistoricalBytes : Pcsc.GetHistoricalBytes
+ {
+ public GetHistoricalBytes()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Felica response APDU
+ ///
+ public class ApduResponse : Pcsc.ApduResponse
+ {
+ public ApduResponse()
+ : base()
+ {
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/IccDetection.cs b/NFCForIoT/CS/PcscSdk/IccDetection.cs
new file mode 100644
index 000000000..8bc800816
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/IccDetection.cs
@@ -0,0 +1,166 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+using Windows.Devices.SmartCards;
+
+using Windows.Storage.Streams;
+using System.Diagnostics;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+namespace Pcsc.Common
+{
+ ///
+ /// Class used to detect the type of the ICC card detected. It accept a connection object
+ /// and gets the ATR from the ICC. After the ATR is parsed, the ICC Detection class inspects
+ /// the historical bytes in order to detect the ICC type as specified by PCSC specification.
+ ///
+ public class IccDetection
+ {
+ ///
+ /// PCSC device type
+ ///
+ public DeviceClass PcscDeviceClass { set; get; }
+ ///
+ /// PCSC card name provided in the nn short int
+ ///
+ public Pcsc.CardName PcscCardName { set; get; }
+ ///
+ /// ATR byte array
+ ///
+ public byte[] Atr { set; get; }
+ ///
+ /// ATR info holds information about the interface character along other info
+ ///
+ public AtrInfo AtrInformation { set; get; }
+ ///
+ /// smard card object passed in the constructor
+ ///
+ private SmartCard smartCard { set; get; }
+ ///
+ /// Smard card connection passed in the constructor
+ ///
+ private SmartCardConnection connectionObject { set; get; }
+ ///
+ /// class constructor.
+ ///
+ ///
+ /// smart card object
+ ///
+ ///
+ /// connection object to the smard card
+ ///
+ public IccDetection(SmartCard card, SmartCardConnection connection)
+ {
+ smartCard = card;
+ connectionObject = connection;
+ PcscDeviceClass = DeviceClass.Unknown;
+ PcscCardName = Pcsc.CardName.Unknown;
+ }
+ ///
+ /// Detects the ICC type by parsing, and analyzing the ATR
+ ///
+ ///
+ /// none
+ ///
+ public async System.Threading.Tasks.Task DetectCardTypeAync()
+ {
+ try
+ {
+ var atrBuffer = await smartCard.GetAnswerToResetAsync();
+ Atr = atrBuffer.ToArray();
+
+ Debug.WriteLine("Status: " + (await smartCard.GetStatusAsync()) + "ATR [" + atrBuffer.Length.ToString() + "] = " + BitConverter.ToString(Atr));
+
+ AtrInformation = AtrParser.Parse(Atr);
+
+ if (AtrInformation != null && AtrInformation.HistoricalBytes.Length > 0)
+ {
+ DetectCard();
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message + e.StackTrace);
+ }
+ }
+ ///
+ /// Internal method that analyzes the ATR Historical Bytes,
+ /// it populate the object with info about the ICC
+ ///
+ private void DetectCard()
+ {
+ if (AtrInformation.HistoricalBytes.Length > 1)
+ {
+ byte categoryIndicator;
+
+ using (DataReader reader = DataReader.FromBuffer(AtrInformation.HistoricalBytes))
+ {
+ categoryIndicator = reader.ReadByte();
+
+ if (categoryIndicator == (byte)CategoryIndicator.StatusInfoPresentInTlv)
+ {
+ while (reader.UnconsumedBufferLength > 0)
+ {
+ const byte appIdPresenceIndTag = 0x4F;
+ const byte appIdPresenceIndTagLen = 0x0C;
+
+ var tagValue = reader.ReadByte();
+ var tagLength = reader.ReadByte();
+
+ if (tagValue == appIdPresenceIndTag && tagLength == appIdPresenceIndTagLen)
+ {
+ byte[] pcscRid = { 0xA0, 0x00, 0x00, 0x03, 0x06 };
+ byte[] pcscRidRead = new byte[pcscRid.Length];
+
+ reader.ReadBytes(pcscRidRead);
+
+ if (pcscRid.SequenceEqual(pcscRidRead))
+ {
+ byte storageStandard = reader.ReadByte();
+ ushort cardName = reader.ReadUInt16();
+
+ PcscCardName = (Pcsc.CardName)cardName;
+ PcscDeviceClass = DeviceClass.StorageClass;
+ }
+
+ reader.ReadBuffer(4); // RFU bytes
+ }
+ else
+ {
+ reader.ReadBuffer(tagLength);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Compare with Mifare DesFire card ATR
+ byte[] desfireAtr = { 0x3B, 0x81, 0x80, 0x01, 0x80, 0x80 };
+
+ if (Atr.SequenceEqual(desfireAtr))
+ {
+ PcscDeviceClass = DeviceClass.MifareDesfire;
+ }
+ }
+ }
+ ///
+ /// Helper enum to hold various constants
+ ///
+ enum CategoryIndicator : byte
+ {
+ StatusInfoPresentAtEnd = 0x00,
+ StatusInfoPresentInTlv = 0x80
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/Iso15693AccessHandler.cs b/NFCForIoT/CS/PcscSdk/Iso15693AccessHandler.cs
new file mode 100644
index 000000000..34ec6067c
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Iso15693AccessHandler.cs
@@ -0,0 +1,109 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System.Threading.Tasks;
+using Windows.Devices.SmartCards;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+
+using Pcsc;
+using System;
+
+namespace Iso15693
+{
+ ///
+ /// Access handler class for Iso15693 based ICC. It provides wrappers for different Iso15693
+ /// commands
+ ///
+ public class AccessHandler
+ {
+ ///
+ /// connection object to smart card
+ ///
+ private SmartCardConnection connectionObject { set; get; }
+ ///
+ /// Class constructor
+ ///
+ ///
+ /// connection object to a Iso15693 ICC
+ ///
+ public AccessHandler(SmartCardConnection ScConnection)
+ {
+ connectionObject = ScConnection;
+ }
+ ///
+ /// Wrapper method to read data from the Iso15693 card
+ ///
+ ///
+ /// byte array of the read data
+ ///
+ public async Task ReadAsync(byte blockNumber)
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new Iso15693.Read(blockNumber));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure reading Iso15693 card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+ ///
+ /// Wrapper method to write data to the Iso15693 card
+ ///
+ public async Task WriteAsync(byte blockNumber, byte[] dataToWrite)
+ {
+ if (dataToWrite.Length != 4)
+ {
+ throw new InvalidOperationException("Invalid size of data to write");
+ }
+
+ var apduRes = await connectionObject.TransceiveAsync(new Iso15693.Write(blockNumber, dataToWrite));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure writing Iso15693 card, " + apduRes.ToString());
+ }
+ }
+ ///
+ /// Wrapper method to perform transparent transceive data to the Iso15693 card
+ ///
+ ///
+ /// The command to send to the Iso15693 card
+ ///
+ ///
+ /// byte array of the read data
+ ///
+ public async Task TransparentExchangeAsync(byte[] commandData)
+ {
+ byte[] responseData = await connectionObject.TransparentExchangeAsync(commandData);
+
+ return responseData;
+ }
+ ///
+ /// Wrapper method get the Iso15693 UID
+ ///
+ ///
+ /// byte array UID
+ ///
+ public async Task GetUidAsync()
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new Iso15693.GetUid());
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure getting UID of Iso15693 card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/Iso15693Commands.cs b/NFCForIoT/CS/PcscSdk/Iso15693Commands.cs
new file mode 100644
index 000000000..5bd205add
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Iso15693Commands.cs
@@ -0,0 +1,68 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+
+namespace Iso15693
+{
+ ///
+ /// Iso15693 read command
+ ///
+ public class Read : Pcsc.ReadBinary
+ {
+ public Read(ushort blockNumber)
+ : base((ushort)((blockNumber & 0xff) << 8), 4)
+ {
+ }
+ }
+ ///
+ /// Iso15693 write command
+ ///
+ public class Write : Pcsc.UpdateBinary
+ {
+ public Write(ushort blockNumber, byte[] dataToWrite)
+ : base((ushort)((blockNumber & 0xff) << 8), dataToWrite)
+ {
+ }
+ }
+ ///
+ /// Iso15693 GetUid command
+ ///
+ public class GetUid : Pcsc.GetUid
+ {
+ public GetUid()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Iso15693 GetHistoricalBytes command
+ ///
+ public class GetHistoricalBytes : Pcsc.GetHistoricalBytes
+ {
+ public GetHistoricalBytes()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Iso15693 response APDU
+ ///
+ public class ApduResponse : Pcsc.ApduResponse
+ {
+ public ApduResponse()
+ : base()
+ {
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/Iso7816.cs b/NFCForIoT/CS/PcscSdk/Iso7816.cs
new file mode 100644
index 000000000..185a2a169
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Iso7816.cs
@@ -0,0 +1,283 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+using System.IO;
+using System.Linq;
+using Windows.Storage.Streams;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+namespace Iso7816
+{
+ ///
+ /// Class ApduCommand implments the ISO 7816 apdu commands
+ ///
+ public class ApduCommand
+ {
+ public ApduCommand(byte cla, byte ins, byte p1, byte p2, byte[] commandData, byte? le)
+ {
+ if (commandData != null && commandData.Length > 254)
+ {
+ throw new NotImplementedException();
+ }
+ CLA = cla;
+ INS = ins;
+ P1 = p1;
+ P2 = p2;
+ CommandData = commandData;
+ Le = le;
+
+ ApduResponseType = typeof(Iso7816.ApduResponse);
+ }
+ ///
+ /// Class of instructions
+ ///
+ public byte CLA { get; set; }
+ ///
+ /// Instruction code
+ ///
+ public byte INS { get; set; }
+ ///
+ /// Instruction parameter 1
+ ///
+ public byte P1 { get; set; }
+ ///
+ /// Instruction parameter 2
+ ///
+ public byte P2 { get; set; }
+ ///
+ /// Maximum number of bytes expected in the response ot this command
+ ///
+ public byte? Le { get; set; }
+ ///
+ /// Contiguous array of bytes representing commands data
+ ///
+ public byte[] CommandData { get; set; }
+ ///
+ /// Expected response type for this command.
+ /// Provides mechanism to bind commands to responses
+ ///
+ public Type ApduResponseType { set; get; }
+ ///
+ /// Packs the current command into contiguous buffer bytes
+ ///
+ ///
+ /// buffer holds the current wire/air format of the command
+ ///
+ public IBuffer GetBuffer()
+ {
+ using (DataWriter writer = new DataWriter())
+ {
+ writer.WriteByte(CLA);
+ writer.WriteByte(INS);
+ writer.WriteByte(P1);
+ writer.WriteByte(P2);
+
+ if (CommandData != null && CommandData.Length > 0)
+ {
+ writer.WriteByte((byte)CommandData.Length);
+ writer.WriteBytes(CommandData);
+ }
+
+ if (Le != null)
+ {
+ writer.WriteByte((byte)Le);
+ }
+
+ return writer.DetachBuffer();
+ }
+ }
+ ///
+ /// Helper method to print the command in a readable format
+ ///
+ ///
+ /// return string formatted command
+ ///
+ public override string ToString()
+ {
+ return "ApduCommand CLA=" + CLA.ToString("X2") + ",INS=" + INS.ToString("X2") + ",P1=" + P1.ToString("X2") + ",P2=" + P2.ToString("X2") + ((CommandData != null && CommandData.Length > 0) ? (",Data=" + BitConverter.ToString(CommandData).Replace("-", "")) : "");
+ }
+ }
+ ///
+ /// Class ApduResponse implments handler for the ISO 7816 apdu response
+ ///
+ public class ApduResponse
+ {
+ public const byte TAG_MULTI_BYTE_MASK = 0x1F;
+ public const byte TAG_COMPREHENSION_MASK = 0x80;
+ public const byte TAG_LENGTH_MULTI_BYTE_MASK = 0x80;
+
+ ///
+ /// Class constructor
+ ///
+ public ApduResponse() { }
+ ///
+ /// method to extract the response data, status and qualifier
+ ///
+ ///
+ public virtual void ExtractResponse(IBuffer response)
+ {
+ if (response.Length < 2)
+ {
+ throw new InvalidOperationException("APDU response must be at least 2 bytes");
+ }
+ using (DataReader reader = DataReader.FromBuffer(response))
+ {
+ ResponseData = new byte[response.Length - 2];
+ reader.ReadBytes(ResponseData);
+ SW1 = reader.ReadByte();
+ SW2 = reader.ReadByte();
+ }
+ }
+ ///
+ /// method to extract the matching TLV data object from response data
+ ///
+ public byte[] ExtractTlvDataObject(byte[] referenceTag)
+ {
+ using (var reader = DataReader.FromBuffer(ResponseData.AsBuffer()))
+ {
+ byte nextByte;
+
+ while (reader.UnconsumedBufferLength > 0)
+ {
+ int lengthLength = 0, valueLength = 0;
+ MemoryStream tag = new MemoryStream(), value = new MemoryStream();
+
+ nextByte = reader.ReadByte();
+ tag.WriteByte(nextByte);
+
+ if ((nextByte & TAG_MULTI_BYTE_MASK) == TAG_MULTI_BYTE_MASK)
+ {
+ while (reader.UnconsumedBufferLength > 0)
+ {
+ nextByte = reader.ReadByte();
+ tag.WriteByte(nextByte);
+
+ if ((nextByte & TAG_COMPREHENSION_MASK) != TAG_COMPREHENSION_MASK)
+ break;
+ }
+ }
+
+ if (reader.UnconsumedBufferLength == 0)
+ throw new Exception("Invalid length for TLV response");
+
+ valueLength = reader.ReadByte();
+ lengthLength = 1;
+
+ if ((valueLength & TAG_LENGTH_MULTI_BYTE_MASK) == TAG_LENGTH_MULTI_BYTE_MASK)
+ lengthLength += (valueLength & ~TAG_LENGTH_MULTI_BYTE_MASK);
+
+ while (--lengthLength > 0)
+ valueLength = (valueLength << 8) | reader.ReadByte();
+
+ while (valueLength != 0 && valueLength-- > 0)
+ value.WriteByte(reader.ReadByte());
+
+ if (referenceTag.SequenceEqual(tag.ToArray()))
+ return value.ToArray();
+ }
+
+ throw new Exception("Tag not found in the TLV response");
+ }
+ }
+ ///
+ /// Detects if the command has succeeded
+ ///
+ ///
+ public virtual bool Succeeded
+ {
+ get
+ {
+ return SW == 0x9000;
+ }
+ }
+ ///
+ /// command processing status
+ ///
+ public byte SW1 { get; set; }
+ ///
+ /// command processing qualifier
+ ///
+ public byte SW2 { get; set; }
+ ///
+ /// Wrapper property to read both response status and qualifer
+ ///
+ public ushort SW
+ {
+ get
+ {
+ return (ushort)(((ushort)SW1 << 8) | (ushort)SW2);
+ }
+ set
+ {
+ SW1 = (byte)(value >> 8);
+ SW2 = (byte)(value & 0xFF);
+ }
+ }
+ ///
+ /// Response data
+ ///
+ public byte[] ResponseData { get; set; }
+ ///
+ /// Mapping response status and qualifer to human readable format
+ ///
+ public virtual string SWTranslation
+ {
+ get
+ {
+ switch (SW)
+ {
+ case 0x9000:
+ return "Success";
+
+ case 0x6700:
+ return "Incorrect length or address range error";
+
+ case 0x6800:
+ return "The requested function is not supported by the card";
+
+ default:
+ return "Unknown";
+ }
+ }
+ }
+ ///
+ /// Helper method to print the response in a readable format
+ ///
+ ///
+ /// return string formatted response
+ ///
+ public override string ToString()
+ {
+ return "ApduResponse SW=" + SW.ToString("X4") + " (" + SWTranslation + ")" + ((ResponseData != null && ResponseData.Length > 0) ? (",Data=" + BitConverter.ToString(ResponseData).Replace("-", "")) : "");
+ }
+ }
+ ///
+ /// Class that implements select command
+ ///
+ public class SelectCommand : ApduCommand
+ {
+ public SelectCommand(byte[] aid, byte? le)
+ : base((byte)Iso7816.Cla.CompliantCmd0x, (byte)Iso7816.Ins.SelectFile, 0x04, 0x00, aid, le)
+ {
+ }
+
+ public byte[] AID
+ {
+ set { CommandData = value; }
+ get { return CommandData; }
+ }
+ public override string ToString()
+ {
+ return "SelectCommand AID=" + BitConverter.ToString(CommandData).Replace("-", "");
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/Iso7816Defs.cs b/NFCForIoT/CS/PcscSdk/Iso7816Defs.cs
new file mode 100644
index 000000000..ea34379b9
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Iso7816Defs.cs
@@ -0,0 +1,59 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+namespace Iso7816
+{
+ ///
+ /// Enumeration of possible ISO 7816 Command
+ ///
+ public enum Cla : byte
+ {
+ CompliantCmd0x = 0x00,
+ AppCompliantCmdAx = 0xA0,
+ ProprietaryCla8x = 0x80,
+ ProprietaryCla9x = 0x90,
+ ReservedForPts = 0xFF, // Protocol Type Selelction
+ }
+ ///
+ /// Enumeration of lower nibbile of CLA
+ ///
+ public enum ClaXx : byte
+ {
+ NoSmOrNoSmIndication = 0x00,
+ ProprietarySmFormat = 0x01,
+ SecureMessageNoHeaderSM = 0x10,
+ SecureMessage1p6 = 0x11,
+ }
+ ///
+ /// Enumeration of possible instructions
+ ///
+ public enum Ins : byte
+ {
+ EraseBinary = 0x0E,
+ Verify = 0x20,
+ ManageChannel = 0x70,
+ ExternalAuthenticate = 0x82,
+ GetChallenge = 0x84,
+ InternalAuthenticate = 0x88,
+ SelectFile = 0xA4,
+ ReadBinary = 0xB0,
+ ReadRecords = 0xB2,
+ GetResponse = 0xC0,
+ Envelope = 0xC2,
+ GetData = 0xCA,
+ WriteBinary = 0xD0,
+ WriteRecord = 0xD2,
+ UpdateBinary = 0xD6,
+ PutData = 0xDA,
+ UpdateData = 0xDC,
+ AppendRecord = 0xE2,
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/MifareStandardAccessHandler.cs b/NFCForIoT/CS/PcscSdk/MifareStandardAccessHandler.cs
new file mode 100644
index 000000000..5f7554340
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/MifareStandardAccessHandler.cs
@@ -0,0 +1,161 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System.Threading.Tasks;
+using Windows.Devices.SmartCards;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+
+using Pcsc;
+using System;
+
+namespace MifareStandard
+{
+ public class DefaultKeys
+ {
+ public static readonly byte[] FactoryDefault = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ }
+ ///
+ /// Access handler class for Mifare Standard/Classic based ICC
+ /// commands
+ ///
+ public class AccessHandler
+ {
+ ///
+ /// connection object to smart card
+ ///
+ private SmartCardConnection connectionObject { set; get; }
+ ///
+ /// Class constructor
+ ///
+ ///
+ /// connection object to a Mifare Standard ICC
+ ///
+ public AccessHandler(SmartCardConnection ScConnection)
+ {
+ connectionObject = ScConnection;
+ }
+ ///
+ /// Wrapper method to read 16 bytes
+ ///
+ ///
+ /// start page to read
+ ///
+ ///
+ /// byte array of 16 bytes
+ ///
+ public async Task LoadKeyAsync(byte[] mifareKey, byte keySlotNumber = 0)
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new MifareStandard.LoadKey(mifareKey, keySlotNumber));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure loading key for MIFARE Standard card, " + apduRes.ToString());
+ }
+
+ return;
+ }
+ ///
+ /// Reads 16 bytes
+ ///
+ ///
+ /// Block number to read
+ ///
+ ///
+ /// Choose MIFARE key A or B
+ ///
+ ///
+ /// Slot where key has previously been stored with a previous call to Load Keys
+ ///
+ ///
+ /// byte array of 16 bytes
+ ///
+ public async Task ReadAsync(ushort blockNumber, GeneralAuthenticate.GeneralAuthenticateKeyType keyType, byte keySlotNumber = 0)
+ {
+ var genAuthRes = await connectionObject.TransceiveAsync(new MifareStandard.GeneralAuthenticate(blockNumber, keySlotNumber, keyType));
+ if (!genAuthRes.Succeeded)
+ {
+ throw new Exception("Failure authenticating to MIFARE Standard card, " + genAuthRes.ToString());
+ }
+
+ var readRes = await connectionObject.TransceiveAsync(new MifareStandard.Read(blockNumber));
+ if (!readRes.Succeeded)
+ {
+ throw new Exception("Failure reading MIFARE Standard card, " + readRes.ToString());
+ }
+
+ return readRes.ResponseData;
+ }
+ ///
+ /// Wrapper method write 16 bytes at the pageAddress
+ ///
+ /// Block number
+ ///
+ ///
+ /// Choose MIFARE key A or B
+ ///
+ ///
+ /// Slot where key has previously been stored with a previous call to Load Keys
+ ///
+ /// byte array of the data to write
+ ///
+ public async void WriteAsync(byte blockNumber, byte[] data, GeneralAuthenticate.GeneralAuthenticateKeyType keyType, byte keySlotNumber = 0)
+ {
+ if (data.Length != 16)
+ {
+ throw new NotSupportedException();
+ }
+
+ var genAuthRes = await connectionObject.TransceiveAsync(new MifareStandard.GeneralAuthenticate(blockNumber, keySlotNumber, keyType));
+ if (!genAuthRes.Succeeded)
+ {
+ throw new Exception("Failure authenticating to MIFARE Standard card, " + genAuthRes.ToString());
+ }
+
+ var apduRes = await connectionObject.TransceiveAsync(new MifareStandard.Write(blockNumber, ref data));
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure writing MIFARE Standard card, " + apduRes.ToString());
+ }
+ }
+ ///
+ /// Wrapper method to perform transparent transceive data to the Mifare Standard card
+ ///
+ ///
+ /// The command to send to the Mifare Standard card
+ ///
+ ///
+ /// byte array of the read data
+ ///
+ public async Task TransparentExchangeAsync(byte[] commandData)
+ {
+ byte[] responseData = await connectionObject.TransparentExchangeAsync(commandData);
+
+ return responseData;
+ }
+ ///
+ /// Wrapper method get the Mifare Standard ICC UID
+ ///
+ ///
+ /// byte array UID
+ ///
+ public async Task GetUidAsync()
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new MifareStandard.GetUid());
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure getting UID of MIFARE Standard card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/MifareStandardCommands.cs b/NFCForIoT/CS/PcscSdk/MifareStandardCommands.cs
new file mode 100644
index 000000000..72af62003
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/MifareStandardCommands.cs
@@ -0,0 +1,101 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+
+namespace MifareStandard
+{
+ ///
+ /// Mifare Standard Read commad when sent to the card the card is expected to return 16 bytes
+ ///
+ public class Read : Pcsc.ReadBinary
+ {
+ public Read(ushort address)
+ : base(address, 16)
+ {
+ }
+ }
+ ///
+ /// Mifare Standard Write commad when sent to the card, writes 16 bytes at a time
+ ///
+ public class Write : Pcsc.UpdateBinary
+ {
+ public byte[] Data
+ {
+ set { base.CommandData = ((value.Length != 16) ? ResizeArray(value, 16) : value); }
+ get { return base.CommandData; }
+ }
+ private static byte[] ResizeArray(byte[] data, int size)
+ {
+ Array.Resize(ref data, size);
+ return data;
+ }
+ public Write(byte address, ref byte[] data)
+ : base(address, ((data.Length != 16) ? ResizeArray(data, 16) : data))
+ {
+ }
+ }
+ ///
+ /// Mifare Standard GetUid command
+ ///
+ public class GetUid : Pcsc.GetUid
+ {
+ public GetUid()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Mifare Standard GetHistoricalBytes command
+ ///
+ public class GetHistoricalBytes : Pcsc.GetHistoricalBytes
+ {
+ public GetHistoricalBytes()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Mifare Standard Load Keys commad which stores the supplied key into the specified numbered key slot
+ /// for subsequent use by the General Authenticate command.
+ ///
+ public class LoadKey : Pcsc.LoadKeys
+ {
+ public LoadKey(byte[] mifareKey, byte keySlotNumber)
+ : base(LoadKeysKeyType.CardKey, null, LoadKeysTransmissionType.Plain, LoadKeysStorageType.Volatile, keySlotNumber, mifareKey)
+ {
+ }
+ }
+ ///
+ /// Mifare Standard GetHistoricalBytes command
+ ///
+ public class GeneralAuthenticate : Pcsc.GeneralAuthenticate
+ {
+ public GeneralAuthenticate(ushort address, byte keySlotNumber, GeneralAuthenticateKeyType keyType)
+ : base(GeneralAuthenticateVersionNumber.VersionOne, address, keyType, keySlotNumber)
+ {
+ if (keyType != GeneralAuthenticateKeyType.MifareKeyA && keyType != GeneralAuthenticateKeyType.PicoTagPassKeyB)
+ {
+ throw new Exception("Invalid key type for MIFARE Standard General Authenticate");
+ }
+ }
+ }
+ ///
+ /// Mifare response APDU
+ ///
+ public class ApduResponse : Pcsc.ApduResponse
+ {
+ public ApduResponse()
+ : base()
+ {
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/MifareUltralightAccessHandler.cs b/NFCForIoT/CS/PcscSdk/MifareUltralightAccessHandler.cs
new file mode 100644
index 000000000..1d6c1d414
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/MifareUltralightAccessHandler.cs
@@ -0,0 +1,353 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System.Threading.Tasks;
+using Windows.Devices.SmartCards;
+using System.Collections.Generic;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+using System.Diagnostics;
+using System.Linq;
+
+using Pcsc;
+using System;
+
+namespace MifareUltralight
+{
+ ///
+ /// Access handler class for MifareUL based ICC. It provides wrappers for different MifareUL
+ /// commands
+ ///
+ public class AccessHandler
+ {
+
+
+
+ public const int SerialNumberTopPage = 0;
+ public const int SerialNumberBottomPage = 1;
+ public const int StaticLockPage = 2;
+ public const int CapabilityPage = 3;
+ public const int PasswordPage = 229;
+
+ public const int MemorySizeByte = 2;
+
+ public enum NTagType
+ {
+ Not,
+ NTAG213,
+ NTAG215,
+ NTAG216
+ };
+
+ public struct NTagData
+ {
+ public byte ConfigurationPage;
+ public byte AccessPage;
+ public byte PasswordPage;
+ public byte PasswordAckPage;
+ }
+
+ public readonly Dictionary NTagInfo = new Dictionary()
+ {
+ { NTagType.NTAG213, new NTagData() { ConfigurationPage = 0x29, AccessPage = 0x2A, PasswordPage = 0x2b, PasswordAckPage = 0x2C } },
+ { NTagType.NTAG215, new NTagData() { ConfigurationPage = 0x83, AccessPage = 0x84, PasswordPage = 0x85, PasswordAckPage = 0x86 } },
+ { NTagType.NTAG216, new NTagData() { ConfigurationPage = 0xE3, AccessPage = 0xE4, PasswordPage = 0xE5, PasswordAckPage = 0xE6 } },
+ };
+
+ public uint MemorySize { get; internal set; } = 0;
+ public uint Blocks { get; internal set; } = 0;
+ public uint Pages { get; internal set; } = 0;
+
+ public NTagType NTag { get; internal set; } = NTagType.Not;
+
+ public bool isNTag21x { get; internal set; } = false;
+
+ ///
+ /// connection object to smart card
+ ///
+ private SmartCardConnection connectionObject { set; get; }
+ ///
+ /// Class constructor
+ ///
+ ///
+ /// connection object to a MifareUL ICC
+ ///
+ public AccessHandler(SmartCardConnection ScConnection)
+ {
+ connectionObject = ScConnection;
+ }
+
+ public async Task ReadCapsAsync()
+ {
+ byte[] command = { 0x60 };
+ var response = await connectionObject.TransparentExchangeAsync(command);
+ if (response.Length > 7)
+ {
+ if (response[1] == 0x4 && // NXP
+ response[2] == 0x4 && // NTAG
+ response[3] == 0x2) // 50pf
+ {
+ isNTag21x = true;
+
+ MemorySize = response[6] * 8u;
+
+ switch (response[6])
+ {
+ case 0x0F:
+ NTag = NTagType.NTAG213;
+ MemorySize = 180;
+ break;
+ case 0x11:
+ NTag = NTagType.NTAG215;
+ MemorySize = 540;
+ break;
+ case 0x13:
+ NTag = NTagType.NTAG216;
+ MemorySize = 924;
+ break;
+ default:
+ isNTag21x = false;
+ NTag = NTagType.Not;
+ break;
+ }
+ Blocks = MemorySize / 16 + 1;
+ Pages = MemorySize / 4;
+ }
+ }
+
+ }
+
+ ///
+ /// Wrapper method to read 16 bytes (4 pages) starting at pageAddress
+ ///
+ ///
+ /// start page to read
+ ///
+ ///
+ /// byte array of 16 bytes
+ ///
+ public async Task ReadAsync(byte pageAddress)
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new MifareUltralight.Read(pageAddress));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure reading MIFARE Ultralight card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+ ///
+ /// Wrapper method write 4 bytes at the pageAddress
+ ///
+ /// page address to write
+ ///
+ /// byte array of the data to write
+ ///
+ public async void WriteAsync(byte pageAddress, byte[] data)
+ {
+ if (data.Length != 4)
+ {
+ throw new NotSupportedException();
+ }
+
+ var apduRes = await connectionObject.TransceiveAsync(new MifareUltralight.Write(pageAddress, ref data));
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure writing MIFARE Ultralight card, " + apduRes.ToString());
+ }
+ }
+ ///
+ /// Wrapper method to perform transparent transceive data to the MifareUL card
+ ///
+ ///
+ /// The command to send to the MifareUL card
+ ///
+ ///
+ /// byte array of the read data
+ ///
+ public async Task TransparentExchangeAsync(byte[] commandData)
+ {
+ byte[] responseData = await connectionObject.TransparentExchangeAsync(commandData);
+
+ return responseData;
+ }
+ ///
+ /// Wrapper method get the MifareUL ICC UID
+ ///
+ ///
+ /// byte array UID
+ ///
+ public async Task GetUidAsync()
+ {
+ var apduRes = await connectionObject.TransceiveAsync(new MifareUltralight.GetUid());
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure getting UID of MIFARE Ultralight card, " + apduRes.ToString());
+ }
+
+ return apduRes.ResponseData;
+ }
+
+ public async Task GetAccessCountAsync()
+ {
+ var transRet = await connectionObject.TransparentExchangeAsync(new byte[] { 0x39, 0x2 });
+ if (transRet.Length >= 3)
+ {
+ byte[] convert = new byte[4];
+ convert[0] = transRet[0];
+ convert[1] = transRet[1];
+ convert[2] = transRet[2];
+
+ return BitConverter.ToUInt32(convert, 0);
+ }
+ return uint.MaxValue;
+ }
+
+ public async Task EnableAccessCountAsync()
+ {
+ byte[] accessPage = await ReadAsync(NTagInfo[NTag].AccessPage);
+ byte[] writePage = new byte[4];
+
+ // 4rd bit in 4th byte
+ writePage[0] = (byte)(accessPage[0] & ~0x8 | 0x10);
+ writePage[1] = accessPage[1];
+ writePage[2] = accessPage[2];
+ writePage[3] = accessPage[3];
+
+
+ WriteAsync(NTagInfo[NTag].AccessPage, writePage);
+ }
+
+ public async Task ProvisionPassword(bool authFoWriteOnly, int authLimit, byte[] Password, byte[] PasswordAcknowledge)
+ {
+ try
+ {
+ bool prot = authFoWriteOnly; // false = PWD_AUTH needed for write only, true = PWD_AUTH needed for read and write
+ int authlim = authLimit; // Value between 0 and 7, 1-7 = number of PWD_AUTH attempts allowed, 0 = unlimited number of attempts
+ byte auth0 = 0x4;
+
+ byte[] command = null;
+ byte[] response = null;
+
+ command = new byte[] {
+ 0xA2, // WRITE
+ NTagInfo[NTag].PasswordPage,
+ Password[0], Password[1], Password[2], Password[3] // Password
+ };
+ Debug.WriteLine("Writing to Password Page: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("Password WRITE response: " + BitConverter.ToString(response));
+
+ command = new byte[] {
+ 0xA2, // WRITE
+ NTagInfo[NTag].PasswordAckPage,
+ PasswordAcknowledge[0], PasswordAcknowledge[1], // Password acknowledge
+ 0x00, 0x00 // RFU
+ };
+ Debug.WriteLine("Writing to Password Ack Page: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("Password acknowledge WRITE response: " + BitConverter.ToString(response));
+
+ command = new byte[] {
+ 0x30, // READ
+ NTagInfo[NTag].AccessPage
+ };
+ Debug.WriteLine("Reading from Access Page: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("READ Access Page response: " + BitConverter.ToString(response));
+
+ if (response == null || response.Length < 16)
+ {
+ throw new Exception("Invalid READ AccessPage response");
+ }
+
+ byte accessByte = (byte)(response[0] & 0x87);
+ accessByte |= (byte)(prot ? 0x80 : 0x00);
+ accessByte |= (byte)(authlim & 0x07);
+
+ command = new byte[] {
+ 0xA2, // WRITE
+ NTagInfo[NTag].AccessPage,
+ accessByte, // Set PROT and AUTHLIM in ACCESS byte
+ response[1], response[2], response[3] // Keep old values the same
+ };
+ Debug.WriteLine("Writing to Access Page: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("WRITE ACCESS byte response: " + BitConverter.ToString(response));
+
+ command = new byte[] {
+ 0x30, // READ
+ NTagInfo[NTag].ConfigurationPage // Config
+ };
+ Debug.WriteLine("Reading from Config page: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("READ Config pageresponse: " + BitConverter.ToString(response));
+
+ if (response == null || response.Length < 16)
+ {
+ throw new Exception("Invalid READ Access Page response");
+ }
+
+ command = new byte[] {
+ 0xA2, // WRITE
+ NTagInfo[NTag].ConfigurationPage,
+ response[0], response[1], response[2], // Keep old values the same
+ auth0 // Set AUTH0
+ };
+ Debug.WriteLine("Writing to Config Page: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("WRITE AUTH0 byte response: " + BitConverter.ToString(response));
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Exception sending transparent commands: " + ex);
+ }
+ }
+
+ public async Task AuthenticateWithPassword(byte[] Password, byte[] PasswordAcknowledge)
+ {
+ bool authenticated = false;
+
+ try
+ {
+ byte[] command = null;
+ byte[] response = null;
+
+ command = new byte[] {
+ 0x1B, // PWD_AUTH
+ Password[0], Password[1], Password[2], Password[3] // Password
+ };
+ Debug.WriteLine("PWD_AUTH command: " + BitConverter.ToString(command));
+ response = await connectionObject.TransparentExchangeAsync(command);
+ Debug.WriteLine("PWD_AUTH response: " + BitConverter.ToString(response));
+
+ if (!PasswordAcknowledge.SequenceEqual(response))
+ {
+ Debug.WriteLine("Password acknowledge incorrect");
+ }
+ else
+ {
+ Debug.WriteLine("Password acknowledge correct");
+ authenticated = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("Exception sending transparent commands: " + ex);
+ }
+
+ return authenticated;
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/MifareUltralightCommands.cs b/NFCForIoT/CS/PcscSdk/MifareUltralightCommands.cs
new file mode 100644
index 000000000..b38ffba58
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/MifareUltralightCommands.cs
@@ -0,0 +1,80 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+
+namespace MifareUltralight
+{
+ ///
+ /// Mifare UL Read commad when sent to the card the card is expected to return 16 byte of 4 Mifare
+ /// pages starting at address addr.
+ ///
+ public class Read : Pcsc.ReadBinary
+ {
+ public Read(byte pageAddress)
+ : base(pageAddress, 16)
+ {
+ }
+ }
+
+
+ ///
+ /// Mifare UL Write commad when sent to the card, writes 4 bytes (1 Mifare block) at given
+ /// block address addr.
+ ///
+ public class Write : Pcsc.UpdateBinary
+ {
+ public byte[] Data
+ {
+ set { base.CommandData = ((value.Length != 4) ? ResizeArray(value, 4) : value); }
+ get { return base.CommandData; }
+ }
+ private static byte[] ResizeArray(byte[] data, int size)
+ {
+ Array.Resize(ref data, size);
+ return data;
+ }
+ public Write(byte address, ref byte[] data)
+ : base(address, ((data.Length != 4) ? ResizeArray(data, 4) : data))
+ {
+ }
+ }
+ ///
+ /// Mifare UL GetUid command
+ ///
+ public class GetUid : Pcsc.GetUid
+ {
+ public GetUid()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Mifare UL GetHistoricalBytes command
+ ///
+ public class GetHistoricalBytes : Pcsc.GetHistoricalBytes
+ {
+ public GetHistoricalBytes()
+ : base()
+ {
+ }
+ }
+ ///
+ /// Mifare response APDU
+ ///
+ public class ApduResponse : Pcsc.ApduResponse
+ {
+ public ApduResponse()
+ : base()
+ {
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/Pcsc.cs b/NFCForIoT/CS/PcscSdk/Pcsc.cs
new file mode 100644
index 000000000..c1c62469d
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Pcsc.cs
@@ -0,0 +1,367 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
+
+namespace Pcsc
+{
+ ///
+ /// PCSC GetData command
+ ///
+ public class GetData : Iso7816.ApduCommand
+ {
+ public enum GetDataDataType : byte
+ {
+ Uid = 0x00,
+ HistoricalBytes = 0x01 // Returned data excludes CRC
+ }
+ public GetDataDataType Type
+ {
+ set { base.P1 = (byte)value; }
+ get { return (GetDataDataType)base.P1; }
+ }
+ public GetData(GetDataDataType type)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Pcsc.Ins.GetData, (byte)type, 0, null, 0)
+ {
+ }
+ }
+
+ public class GetAccessCount : Iso7816.ApduCommand
+ {
+ public GetAccessCount()
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Pcsc.Ins.ReadCount, 0, 0, null, 0)
+ {
+ }
+ }
+ ///
+ /// PCSC LoadKeys command
+ ///
+ public class LoadKeys : Iso7816.ApduCommand
+ {
+ public enum LoadKeysKeyType : byte
+ {
+ CardKey = 0x00,
+ ReaderKey = 0x80,
+
+ Mask = 0x80,
+ }
+ public enum LoadKeysTransmissionType : byte
+ {
+ Plain = 0x00,
+ Secured = 0x40,
+
+ Mask = 0x40,
+ }
+ public enum LoadKeysStorageType : byte
+ {
+ Volatile = 0x00,
+ NonVolatile = 0x20,
+
+ Mask = 0x20,
+ }
+ public LoadKeysKeyType KeyType
+ {
+ set { base.P1 = (byte)((base.P1 & ~(byte)LoadKeysKeyType.Mask) | (byte)(value & LoadKeysKeyType.Mask)); }
+ get { return (LoadKeysKeyType)(base.P1 & (byte)LoadKeysKeyType.Mask); }
+ }
+ public LoadKeysTransmissionType TransmissionType
+ {
+ set { base.P1 = (byte)((base.P1 & ~(byte)LoadKeysTransmissionType.Mask) | (byte)(value & LoadKeysTransmissionType.Mask)); }
+ get { return (LoadKeysTransmissionType)(base.P1 & (byte)LoadKeysTransmissionType.Mask); }
+ }
+ public LoadKeysStorageType StorageType
+ {
+ set { base.P1 = (byte)((base.P1 & ~(byte)LoadKeysStorageType.Mask) | (byte)(value & LoadKeysStorageType.Mask)); }
+ get { return (LoadKeysStorageType)(base.P1 & (byte)LoadKeysStorageType.Mask); }
+ }
+ public byte ReaderKeyNumber
+ {
+ set { base.P1 = (byte)((base.P1 & 0xF0) | (byte)(value & 0x0F)); }
+ get { return (byte)(base.P1 & 0x0F); }
+ }
+ public byte KeyNumber
+ {
+ set { base.P2 = value; }
+ get { return base.P2; }
+ }
+ public byte[] KeyData
+ {
+ set { base.CommandData = value; }
+ get { return base.CommandData; }
+ }
+ public LoadKeys(LoadKeysKeyType keyType, byte? readerKeyNumber, LoadKeysTransmissionType transmissionType, LoadKeysStorageType storageType, byte keyNumber, byte[] keyData)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Pcsc.Ins.LoadKeys, (byte)((byte)keyType | (byte)transmissionType | (byte)storageType | (readerKeyNumber ?? 0)), keyNumber, keyData, null)
+ {
+ }
+ }
+ ///
+ /// PCSC GeneralAuthenticate command
+ ///
+ public class GeneralAuthenticate : Iso7816.ApduCommand
+ {
+ public enum GeneralAuthenticateKeyType : byte
+ {
+ MifareKeyA = 0x60,
+ PicoTagPassKeyB = 0x61
+ }
+ public enum GeneralAuthenticateVersionNumber : byte
+ {
+ VersionOne = 0x01
+ }
+ public GeneralAuthenticateVersionNumber VersionNumber
+ {
+ set { base.CommandData[0] = (byte)value; }
+ get { return (GeneralAuthenticateVersionNumber)base.CommandData[0]; }
+ }
+ public ushort Address
+ {
+ set
+ {
+ base.CommandData[1] = (byte)(value >> 8);
+ base.CommandData[2] = (byte)(value & 0x00FF);
+ }
+ get { return (ushort)((base.CommandData[1] << 8) | base.CommandData[2]); }
+ }
+ public byte KeyType
+ {
+ set { base.CommandData[3] = value; }
+ get { return base.CommandData[3]; }
+ }
+ public byte KeyNumber
+ {
+ set { base.CommandData[4] = value; }
+ get { return base.CommandData[4]; }
+ }
+ public GeneralAuthenticate(GeneralAuthenticateVersionNumber version, ushort address, GeneralAuthenticateKeyType keyType, byte keyNo)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Pcsc.Ins.GeneralAuthenticate, 0, 0, new byte[5] { (byte)version, (byte)(address >> 8), (byte)(address & 0x00FF), (byte)keyType, keyNo }, null)
+ {
+ }
+ }
+ ///
+ /// PCSC ReadBinary command
+ ///
+ public class ReadBinary : Iso7816.ApduCommand
+ {
+ public ReadBinary(ushort address, byte? expectedReturnBytes)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Pcsc.Ins.ReadBinary, 0, 0, null, expectedReturnBytes)
+ {
+ this.Address = address;
+ }
+
+ public ushort Address
+ {
+ set
+ {
+ base.P1 = (byte)(value >> 8);
+ base.P2 = (byte)(value & 0x00FF);
+ }
+ get { return (ushort)((base.P1 << 8) | base.P2); }
+ }
+ }
+ ///
+ /// PCSC Updatebinary Command
+ ///
+ public class UpdateBinary : Iso7816.ApduCommand
+ {
+ public UpdateBinary(ushort address, byte[] dataToWrite)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Pcsc.Ins.UpdateBinary, 0, 0, dataToWrite, null)
+ {
+ this.Address = address;
+ }
+
+ public ushort Address
+ {
+ set
+ {
+ base.P1 = (byte)(value >> 8);
+ base.P2 = (byte)(value & 0x00FF);
+ }
+ get { return (ushort)((base.P1 << 8) | base.P2); }
+ }
+ }
+ ///
+ /// PCSC GetUid command
+ ///
+ public class GetUid : GetData
+ {
+ public GetUid()
+ : base(GetData.GetDataDataType.Uid)
+ {
+ }
+ }
+ ///
+ /// PCSC GetHistoricalBytes command
+ ///
+ public class GetHistoricalBytes : GetData
+ {
+ public GetHistoricalBytes()
+ : base(GetData.GetDataDataType.HistoricalBytes)
+ {
+ }
+ }
+ ///
+ /// PCSC ManageSession command
+ ///
+ public class ManageSession : Iso7816.ApduCommand
+ {
+ public enum DataObjectType
+ {
+ Version = 0x80,
+ StartTransparentSession = 0x81,
+ EndTransparentSession = 0x82,
+ Timer = 0x5F46,
+ }
+ public ManageSession(byte[] dataObjects)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Iso7816.Ins.Envelope, 0x00, 0x00, dataObjects, null)
+ {
+ ApduResponseType = typeof(ManageSessionResponse);
+ }
+ }
+ ///
+ /// PCSC TransparentExchange command
+ ///
+ public class TransparentExchange : Iso7816.ApduCommand
+ {
+ public enum DataObjectType
+ {
+ Transceive = 0x95,
+ Timer = 0x5F46,
+ }
+ public TransparentExchange(byte[] dataObjects)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Iso7816.Ins.Envelope, 0x00, 0x01, dataObjects, null)
+ {
+ ApduResponseType = typeof(TransparentExchangeResponse);
+ }
+ }
+ ///
+ /// PCSC IncrementDecrement command
+ ///
+ public class IncrementDecrement : Iso7816.ApduCommand
+ {
+ public enum DataObjectType
+ {
+ Increment = 0xA0,
+ Decrement = 0xA1
+ }
+ public IncrementDecrement(byte[] dataObjects)
+ : base((byte)Iso7816.Cla.ReservedForPts, (byte)Iso7816.Ins.Envelope, 0x00, 0x03, dataObjects, null)
+ {
+ }
+ }
+ ///
+ /// PCSC Apdu response
+ ///
+ public class ApduResponse : Iso7816.ApduResponse
+ {
+ public ApduResponse()
+ : base()
+ {
+ }
+ }
+ ///
+ /// PCSC ManageSession response
+ ///
+ public class ManageSessionResponse : Iso7816.ApduResponse
+ {
+ public enum DataObjectType
+ {
+ GenericErrorStatus = 0xC0,
+ Version = 0x80,
+ }
+ public ManageSessionResponse()
+ : base()
+ {
+ }
+ public override bool Succeeded
+ {
+ get
+ {
+ if (base.Succeeded)
+ {
+ byte[] value = ExtractTlvDataObject(new byte[1] { (byte)DataObjectType.GenericErrorStatus });
+
+ if (value.Length != 3)
+ throw new System.InvalidOperationException("Invalid value size for TLV response");
+
+ return (value[0] == 0x00 && value[1] == 0x90 && value[2] == 0x00); // XX SW1 SW2 XX=Number of bad data object in the APDU
+ }
+
+ return false;
+ }
+ }
+ public char Version
+ {
+ get
+ {
+ byte[] value = ExtractTlvDataObject(new byte[1] { (byte)DataObjectType.Version });
+
+ if (value.Length != 1)
+ throw new System.InvalidOperationException("Invalid value size for TLV response");
+
+ return System.BitConverter.ToChar(value, 0);
+ }
+ }
+ }
+ ///
+ /// PCSC TransparentExchange response
+ ///
+ public class TransparentExchangeResponse : Iso7816.ApduResponse
+ {
+ public enum DataObjectType
+ {
+ GenericErrorStatus = 0xC0,
+ ResponseStatus = 0x96,
+ IccResponse = 0x97,
+ }
+ public TransparentExchangeResponse()
+ : base()
+ {
+ }
+ public override bool Succeeded
+ {
+ get
+ {
+ if (base.Succeeded)
+ {
+ byte[] value = ExtractTlvDataObject(new byte[1] { (byte)DataObjectType.GenericErrorStatus });
+
+ if (value.Length != 3)
+ throw new System.InvalidOperationException("Invalid value size for TLV response");
+
+ return (value[0] == 0x00 && value[1] == 0x90 && value[2] == 0x00); // XX SW1 SW2 XX=Number of bad data object in the APDU
+ }
+
+ return false;
+ }
+ }
+ public ushort ResponseStatus
+ {
+ get
+ {
+ byte[] value = ExtractTlvDataObject(new byte[1] { (byte)DataObjectType.ResponseStatus });
+
+ if (value.Length != 2)
+ throw new System.InvalidOperationException("Invalid value size for TLV response");
+
+ return System.BitConverter.ToUInt16(value, 0);
+ }
+
+ }
+ public byte[] IccResponse
+ {
+ get
+ {
+ return ExtractTlvDataObject(new byte[1] { (byte)DataObjectType.IccResponse });
+ }
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/PcscDefs.cs b/NFCForIoT/CS/PcscSdk/PcscDefs.cs
new file mode 100644
index 000000000..07d4406f5
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/PcscDefs.cs
@@ -0,0 +1,129 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+namespace Pcsc
+{
+ ///
+ /// PCSC storage standard enum (ss byte)
+ ///
+ public enum StorageStandard : byte
+ {
+ Unknown = 0x00,
+ Iso14443AP1 = 0x01,
+ Iso14443AP2 = 0x02,
+ Iso14443AP3 = 0x03,
+ Iso14443ARfu = 0x04,
+
+ Iso14443BP1 = 0x05,
+ Iso14443BP2 = 0x06,
+ Iso14443BP3 = 0x07,
+ Iso14443BRfu = 0x08,
+
+ Iso15693P1 = 0x09,
+ Iso15693P2 = 0x0A,
+ Iso15693P3 = 0x0B,
+ Iso15693P4 = 0x0C,
+
+ Iso7816CI2C = 0x0D,
+ Iso7816CEI2C = 0x0E,
+ Iso7816C2WBP = 0x0F,
+ Iso7816C3WBP = 0x10,
+
+ FeliCa = 0x11,
+
+ LowFreqCContactless = 0x40,
+ }
+
+ ///
+ /// PCSC card name enum (nn short)
+ ///
+ public enum CardName : ushort
+ {
+ Unknown = 0x0000,
+
+ MifareStandard1K = 0x0001,
+ MifareStandard4K = 0x0002,
+ MifareUltralight = 0x0003,
+ SLE55R = 0x0004,
+ SR176 = 0x0006,
+ SRIX4K = 0x0007,
+ AT88RF020 = 0x0008,
+ AT88SC0204CRF = 0x0009,
+ AT88SC0808CRF = 0x000A,
+ AT88SC1616CRF = 0x000B,
+ AT88SC3216CRF = 0x000C,
+ AT88SC6416CRF = 0x000D,
+ SRF55V10P = 0x000E,
+ SRF55V02P = 0x000F,
+ SRF55V10S = 0x0010,
+ SRF55V02S = 0x0011,
+ TAG_IT = 0x0012,
+ LRI512 = 0x0013,
+ ICODESLI = 0x0014,
+ TEMPSENS = 0x0015,
+ ICODE1 = 0x0016,
+ PicoPass2K = 0x0017,
+ PicoPass2KS = 0x0018,
+ PicoPass16K = 0x0019,
+ PicoPass16Ks = 0x001A,
+ PicoPass16K8x2 = 0x001B,
+ PicoPass16KS8x2 = 0x001C,
+ PicoPass32KS16p16 = 0x001D,
+ PicoPass32KS16p8x2 = 0x001E,
+ PicoPass32KS8x2p16 = 0x001F,
+ PicoPass32KS8x2p8x2 = 0x0020,
+ LRI64 = 0x0021,
+ ICODEUID = 0x0022,
+ ICODEEPC = 0x0023,
+ LRI12 = 0x0024,
+ LRI128 = 0x0025,
+ MifareMini = 0x0026,
+ SLE66R01P = 0x0027,
+ SLE66RxxP = 0x0028,
+ SLE66RxxS = 0x0029,
+ SLE55RxxE = 0x002A,
+ SRF55V01P = 0x002B,
+ SRF66V10ST = 0x002C,
+ SRF66V10IT = 0x002D,
+ SRF66V01ST = 0x002E,
+ JewelTag = 0x002F,
+ TopazTag = 0x0030,
+ AT88SC0104CRF = 0x0031,
+ AT88SC0404CRF = 0x0032,
+ AT88RF01C = 0x0033,
+ AT88RF04C = 0x0034,
+ iCodeSL2 = 0x0035,
+ MifarePlusSL1_2K = 0x0036,
+ MifarePlusSL1_4K = 0x0037,
+ MifarePlusSL2_2K = 0x0038,
+ MifarePlusSL2_4K = 0x0039,
+ MifareUltralightC = 0x003A,
+ FeliCa = 0x003B,
+ MelexisSensorTagMLX90129 = 0x003C,
+ MifareUltralightEV1 = 0x003D,
+
+ CardNameMaxValue = MifareUltralightEV1,
+ }
+
+ ///
+ /// Enumeration of possible instructions
+ ///
+ public enum Ins : byte
+ {
+ GetData = 0xCA,
+ LoadKeys = 0x82,
+ GeneralAuthenticate = 0x86,
+ Verify = 0x20,
+ ReadBinary = 0xB0,
+ UpdateBinary = 0xD6,
+ ReadCount = 0x39,
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/PcscSdk.csproj b/NFCForIoT/CS/PcscSdk/PcscSdk.csproj
new file mode 100644
index 000000000..7b472bb19
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/PcscSdk.csproj
@@ -0,0 +1,137 @@
+
+
+
+
+ Debug
+ x86
+ {78F7351D-0EB3-49E4-B257-36B7977954F9}
+ Library
+ Properties
+ PcscSdk
+ PcscSdk
+ en-US
+ UAP
+ 10.0.19041.0
+ 10.0.15063.0
+ 14
+ true
+ 512
+ {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot
+
+
+ ARM
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ ARM
+ bin\ARM\Release\
+ TRACE;NETFX_CORE;WINDOWS_UAP
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+ true
+
+
+ x64
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ x64
+ bin\x64\Release\
+ TRACE;NETFX_CORE;WINDOWS_UAP
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+ true
+
+
+ x86
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE;WINDOWS_UAP
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ x86
+ bin\x86\Release\
+ TRACE;NETFX_CORE;WINDOWS_UAP
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5.0.0
+
+
+
+ 14.0
+
+
+
+
\ No newline at end of file
diff --git a/NFCForIoT/CS/PcscSdk/PcscUtils.cs b/NFCForIoT/CS/PcscSdk/PcscUtils.cs
new file mode 100644
index 000000000..6a413671c
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/PcscUtils.cs
@@ -0,0 +1,138 @@
+//*********************************************************
+//
+// Copyright (c) Microsoft. All rights reserved.
+// This code is licensed under the MIT License (MIT).
+// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
+// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
+// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
+//
+//*********************************************************
+
+using System;
+using System.Threading.Tasks;
+using Windows.Devices.SmartCards;
+using Windows.Storage.Streams;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Devices.Enumeration;
+using System.Linq;
+
+namespace Pcsc
+{
+ public static class SmartCardConnectionExtension
+ {
+ ///
+ /// Extension method to SmartCardConnection class similar to Transmit asyc method, however it accepts PCSC SDK commands.
+ ///
+ ///
+ /// APDU command object to send to the ICC
+ ///
+ ///
+ /// SmartCardConnection object
+ ///
+ /// APDU response object of type defined by the APDU command object
+ public static async Task TransceiveAsync(this SmartCardConnection connection, Iso7816.ApduCommand apduCommand)
+ {
+ Iso7816.ApduResponse apduRes = Activator.CreateInstance(apduCommand.ApduResponseType) as Iso7816.ApduResponse;
+
+ IBuffer responseBuf = await connection.TransmitAsync(apduCommand.GetBuffer());
+
+ apduRes.ExtractResponse(responseBuf);
+
+ return apduRes;
+ }
+
+ ///
+ /// Extension method to SmartCardConnection class to perform a transparent exchange to the ICC
+ ///
+ ///
+ /// SmartCardConnection object
+ ///
+ ///
+ /// Command object to send to the ICC
+ ///
+ /// Response received from the ICC
+ public static async Task TransparentExchangeAsync(this SmartCardConnection connection, byte[] commandData)
+ {
+ byte[] responseData = null;
+ ManageSessionResponse apduRes = await TransceiveAsync(connection, new ManageSession(new byte[2] { (byte)ManageSession.DataObjectType.StartTransparentSession, 0x00 })) as ManageSessionResponse;
+
+ if (!apduRes.Succeeded)
+ {
+ throw new Exception("Failure to start transparent session, " + apduRes.ToString());
+ }
+
+ using (DataWriter dataWriter = new DataWriter())
+ {
+ dataWriter.WriteByte((byte)TransparentExchange.DataObjectType.Transceive);
+ dataWriter.WriteByte((byte)commandData.Length);
+ dataWriter.WriteBytes(commandData);
+
+ TransparentExchangeResponse apduRes1 = await TransceiveAsync(connection, new TransparentExchange(dataWriter.DetachBuffer().ToArray())) as TransparentExchangeResponse;
+
+ if (!apduRes1.Succeeded)
+ {
+ throw new Exception("Failure transceive with card, " + apduRes1.ToString());
+ }
+
+ responseData = apduRes1.IccResponse;
+ }
+
+ ManageSessionResponse apduRes2 = await TransceiveAsync(connection, new ManageSession(new byte[2] { (byte)ManageSession.DataObjectType.EndTransparentSession, 0x00 })) as ManageSessionResponse;
+
+ if (!apduRes2.Succeeded)
+ {
+ throw new Exception("Failure to end transparent session, " + apduRes2.ToString());
+ }
+
+ return responseData;
+ }
+ }
+
+ public class SmartCardReaderUtils
+ {
+ ///
+ /// Checks whether the device supports smart card reader mode
+ ///
+ /// None
+ public static async Task GetFirstSmartCardReaderInfo(SmartCardReaderKind readerKind = SmartCardReaderKind.Any)
+ {
+ // Check if the SmartCardConnection API exists on this currently running SKU of Windows
+ if (!Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Devices.SmartCards.SmartCardConnection"))
+ {
+ // This SKU of Windows does not support NFC card reading, only desktop and phones/mobile devices support NFC card reading
+ return null;
+ }
+
+ // Device selector string is from SmartCardReader.GetDeviceSelector(SmartCardReaderKind.Nfc) except that we remove the conditional
+ // about the interface being enabled, since we want to get the device object regardless of whether the user turned it off in the CPL
+ string query = "System.Devices.InterfaceClassGuid:=\"{DEEBE6AD-9E01-47E2-A3B2-A66AA2C036C9}\"";
+ if (readerKind != SmartCardReaderKind.Any)
+ {
+ query += " AND System.Devices.SmartCards.ReaderKind:=" + (int)readerKind;
+ }
+
+ string query2 = Windows.Devices.SmartCards.SmartCardReader.GetDeviceSelector();
+
+
+ DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(query);
+
+ // There is a bug on some devices that were updated to WP8.1 where an NFC SmartCardReader is
+ // enumerated despite that the device does not support it. As a workaround, we can do an additonal check
+ // to ensure the device truly does support it.
+ var workaroundDetect = await DeviceInformation.FindAllAsync("System.Devices.InterfaceClassGuid:=\"{50DD5230-BA8A-11D1-BF5D-0000F805F530}\"");
+
+ if (workaroundDetect.Count == 0 || devices.Count == 0)
+ {
+ // Not supported
+ return null;
+ }
+
+ // Smart card reader supported, but may be disabled
+ return (from d in devices
+ where d.IsEnabled
+ orderby d.IsDefault descending
+ select d).FirstOrDefault();
+ }
+ }
+}
diff --git a/NFCForIoT/CS/PcscSdk/Properties/AssemblyInfo.cs b/NFCForIoT/CS/PcscSdk/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..a0cbcc7c4
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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("DangerousTags")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DangerousTags")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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")]
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/NFCForIoT/CS/PcscSdk/Properties/Default.rd.xml b/NFCForIoT/CS/PcscSdk/Properties/Default.rd.xml
new file mode 100644
index 000000000..80a960ce3
--- /dev/null
+++ b/NFCForIoT/CS/PcscSdk/Properties/Default.rd.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NFCForIoT/CS/README.md b/NFCForIoT/CS/README.md
new file mode 100644
index 000000000..628a6fd09
--- /dev/null
+++ b/NFCForIoT/CS/README.md
@@ -0,0 +1,58 @@
+# Near Field Communication
+Near Field Communication allows a device to send small pieces of data when in close proxmimity to other NFC devices. NFC hardware is required for this communication.
+On Windows 10 IoT Enterprise, you can communicate with the hardware manually by using the GPIO, I2C or SPI APIs, or you can use the SmartCard APIs if a driver is associated with the hardware.
+
+In this sample, we will demonstrate how to set up the NXP NFC SBC Kit and how to communicate with an NXP NTAG21x.
+
+## Prerequisites
+In order to build and test this sample, you will need the following:
+
+ * [Visual Studio 2015 Update 3](http://go.microsoft.com/fwlink/?LinkId=691129).
+ * [NXP Explore-NFC Kit](http://www.digikey.com/products/en?mpart=OM5577&v=568).
+ * [BullsEye NFC NTAG216 Sticker](https://dangerousthings.com/shop/bullseye/) or [xNTi NFC Implant Kit](https://dangerousthings.com/shop/xnti/)
+
+
+## Set up a Raspberry Pi
+
+ 1. Before powering on your Raspberry Pi, assemble the NXP Explore-NFC kit, and attach to the Rasberry Pi.
+ 1. Set up your Raspberry Pi using the instructions [here](https://docs.microsoft.com/en-us/windows/iot-core/tutorials/rpi).
+
+## Build the ACPI Table.
+In order to assocate the NXP Explore-NFC hardware with the driver, resources need to be allocated for it in the ACPI Table. Included in the sample is a file ```pn71x0.asl```, which
+needs to be compiled in order to apply it to your Raspberry PI.
+
+ 1. Open a ```VS2015 x64 Native Tools Command Prompt```
+ 1. Within the command prompt change directory to the NFCOnIoT sample.
+ 1. Within the command prompt run ```"C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify\asl.exe" pn71x0.asl```. NOTE: you will get a warning; this is benign.
+ 1. This command will have generated a file called ```ACPITABL.dat``` which will be copied to your pi in the next section.
+ 1. You can type ```start .``` to open an explore window here, which you will use in the next step.
+
+## Setup the NFC Hardware
+
+ 1. In the IoT Dashboard, find your Raspberry Pi, then right click and select ```Open Network Share```, Enter credentials if prompted.
+ 1. Once the network share opens, navigate to ```windows\system32```.
+ 1. Copy the ```ACPITABL.dat``` from the explorer window you opened in the previous section, and copy it to the folder in on the network share you opened in the previous step.
+ 1. On the network share, navigate to ```c:\data```.
+ 1. From the Explorer Window which contains the ```ACPITabl.dat```, there is also a file ```pn71x0.inf```. Copy this to ```c:\data``` on the network share.
+ 1. Use [SSH](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/ssh) or [Powershell](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/powershell) to connect to your device.
+ 1. Change directory to ```c:\data``` in the connected session.
+ 1. Run the command ```devcon dp_add pn71x0.inf```.
+ 1. Run the command ```shutdown -r -t 0``` to restart the Rasberry PI for the hardware changes to take effect.
+
+
+## Configure the NFC Service to start automatically
+In order to minimize the number of resources used by IoT Enterprise, the NFC Service does not start by default. To make it start automatically, use [SSH](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/ssh) to connect to the device and then
+
+ 1. Run ```sc config SEMgrSvc start=auto``` to set this service to autostart on boot.
+ 1. Run ```sc start SEMgrSvc``` to start it for this session.
+
+If you prefer [Powershell](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/powershell) to connect to your device, run the following commands in PowerShell.
+
+ 1. ```Set-Service SEMgrSvc -StartupType "Automatic"``` to set this service to autostart on boot.
+ 1. ```Start-Service SEMgrSvc``` to start it for this session.
+
+## Running the NFC Sample
+
+ 1. In the Samples folder you downloaded from Github, open NFCForIoT.sln.
+ 1. Build and deploy the application to your Raspberry Pi.
+ 1. Use the NTAG21x of your choice to see information about it and optionally configure it.
diff --git a/NFCForIoT/CS/pn71x0.asl b/NFCForIoT/CS/pn71x0.asl
new file mode 100644
index 000000000..1b4286db1
--- /dev/null
+++ b/NFCForIoT/CS/pn71x0.asl
@@ -0,0 +1,133 @@
+
+DefinitionBlock ("ACPITABL.dat", "SSDT", 1, "MSFT", "NFCTEST", 1)
+{
+ Scope (\_SB)
+ {
+ // Device (GPI0)
+ // {
+ // Name (_HID, "BCM2845")
+ // Name (_CID, "BCMGPIO")
+ // Name (_UID, 0x0)
+ // Method (_STA)
+ // {
+ // Return(0xf)
+ // }
+ // Method (_CRS, 0x0, NotSerialized) {
+ // Name (RBUF, ResourceTemplate () {
+ // MEMORY32FIXED(ReadWrite, 0x3F200000, 0xB4, )
+ // Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x51 }
+ // Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { 0x53 }
+ // })
+ // Return(RBUF)
+ // }
+ // }
+
+ // Device (I2C1)
+ // {
+ // Name (_HID, "BCM2841")
+ // Name (_CID, "BCMI2C")
+ // Name (_UID, 0x1)
+ // Method (_STA)
+ // {
+ // Return(0xf)
+ // }
+ // Method (_CRS, 0x0, NotSerialized)
+ // {
+ // Name (RBUF, ResourceTemplate()
+ // {
+ // Memory32Fixed(ReadWrite, 0x3F804000, 0x20)
+ // Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) {0x55}
+ // MsftFunctionConfig(Exclusive, PullUp, 0x4, "\\_SB.GPI0", 0, ResourceConsumer, ) {2, 3}
+ // })
+ // Return(RBUF)
+ // }
+ // }
+
+ Scope(GPI0)
+ {
+ OperationRegion(NFPO, GeneralPurposeIO, Zero, One)
+ }
+
+ Device(NFCD)
+ {
+ Name(_HID, "PN71X0")
+ Name(_CID, "ACPI\PN71X0")
+ Name(_CRS, Buffer(0x41)
+ {
+ 0x8e, 0x19, 0x00, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x06, 0x00,
+ 0x80, 0x1a, 0x06, 0x00, 0x28, 0x00, 0x5c, 0x5f, 0x53, 0x42, 0x2e, 0x49,
+ 0x32, 0x43, 0x31, 0x00, 0x8c, 0x20, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x23,
+ 0x00, 0x00, 0x00, 0x17, 0x00, 0x5c, 0x5f, 0x53, 0x42, 0x2e, 0x47, 0x50,
+ 0x49, 0x30, 0x00, 0x79, 0x00
+ })
+ Name(NFCP, Buffer(0x25)
+ {
+ 0x8c, 0x20, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x23, 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x5c, 0x5f, 0x53, 0x42, 0x2e, 0x47, 0x50, 0x49, 0x30, 0x00, 0x79,
+ 0x00
+ })
+ Field(\_SB_.GPI0.NFPO, ByteAcc, NoLock, Preserve)
+ {
+ Connection(\_SB_.NFCD.NFCP),
+ MGPE, 1
+ }
+ Method(POON, 0x0, NotSerialized)
+ {
+ Store(One, MGPE)
+ }
+ Method(POOF, 0x0, NotSerialized)
+ {
+ Store(Zero, MGPE)
+ }
+ Method(_DSM, 0x4, NotSerialized)
+ {
+ Store("Method NFC _DSM begin", Debug)
+ If(LEqual(Arg0, Buffer(0x10)
+ {
+ 0xc4, 0xf6, 0xe7, 0xa2, 0x38, 0x96, 0x85, 0x44, 0x9f, 0x12, 0x6b, 0x4e,
+ 0x20, 0xb6, 0x0d, 0x63
+ }))
+ {
+ If(LEqual(Arg2, Zero))
+ {
+ Store("Method NFC _DSM QUERY", Debug)
+ If(LEqual(Arg1, One))
+ {
+ \_SB_.NFCD.POOF()
+ Sleep(0x14)
+ Return(Buffer(One)
+ {
+ 0x0f
+ })
+ }
+ }
+ If(LEqual(Arg2, 0x2))
+ {
+ Store("Method NFC _DSM SETPOWERMODE", Debug)
+ If(LEqual(Arg3, One))
+ {
+ \_SB_.NFCD.POON()
+ Sleep(0x14)
+ }
+ If(LEqual(Arg3, Zero))
+ {
+ \_SB_.NFCD.POOF()
+ Sleep(0x14)
+ }
+ }
+ If(LEqual(Arg2, 0x3))
+ {
+ Store("Method NFC _DSM EEPROM Config", Debug)
+ Return(Buffer(0x13)
+ {
+ 0x9c, 0x1f, 0x38, 0x19, 0xa8, 0xb9, 0x4b, 0xab, 0xa1, 0xba, 0xd0, 0x20,
+ 0x76, 0x88, 0x2a, 0xe0, 0x03, 0x01, 0x08
+ })
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NFCForIoT/CS/pn71x0.inf b/NFCForIoT/CS/pn71x0.inf
new file mode 100644
index 000000000..5eb632822
--- /dev/null
+++ b/NFCForIoT/CS/pn71x0.inf
@@ -0,0 +1,48 @@
+[Version]
+Signature="$Windows NT$"
+Class=Proximity
+ClassGuid={5630831C-06C9-4856-B327-F5D32586E060}
+Provider=%ManufacturerName%
+DriverVer=06/21/2006,10.0.10572.1000
+[Manufacturer]
+%ManufacturerName%=Standard,NTarm
+[Standard.NTarm]
+%DeviceName%=MyDevice_Install, ACPI\PN71x0
+[SourceDisksNames]
+1=%DiskName%
+[SourceDisksFiles]
+; =================== UMDF Device ==================================
+[DefaultInstall]
+[MyDevice_Install.NT]
+[MyDevice_Install.NT.hw]
+[MyDevice_Install.NT.Services]
+AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall
+[MyDevice_Install.NT.CoInstallers]
+AddReg=CoInstallers_AddReg
+[MyDevice_Install.NT.Wdf]
+UmdfService=NxpNfcPn71x0ClientDriver,NxpNfcPn71x0ClientDriver_Install
+UmdfServiceOrder=NxpNfcPn71x0ClientDriver
+UmdfDirectHardwareAccess=AllowDirectHardwareAccess
+UmdfFileObjectPolicy=AllowNullAndUnknownFileObjects
+UmdfImpersonationLevel=Impersonation
+[NxpNfcPn71x0ClientDriver_Install]
+UmdfLibraryVersion=2.0.0
+ServiceBinary=%12%\UMDF\MSNfcI2C547.dll
+UmdfExtensions=NfcCx0102
+[WUDFRD_ServiceInstall]
+DisplayName=%WudfRdDisplayName%
+ServiceType=1
+StartType=3
+ErrorControl=1
+ServiceBinary=%12%\WUDFRd.sys
+[CoInstallers_AddReg]
+HKR,,CoInstallers32,0x00010000,"WUDFCoinstaller.dll"
+[DestinationDirs]
+[ControlFlags]
+ExcludeFromSelect=*
+; =================== Generic ==================================
+[Strings]
+ManufacturerName="NXP Semiconductors"
+DiskName="NxpNfcPn71x0ClientDriver Installation Disk"
+WudfRdDisplayName="Windows Driver Foundation - User-mode Driver Framework Reflector"
+DeviceName="NxpNfcPn71x0ClientDriver Device"
\ No newline at end of file
diff --git a/NFCForIoT/README.md b/NFCForIoT/README.md
new file mode 100644
index 000000000..d0a2129d7
--- /dev/null
+++ b/NFCForIoT/README.md
@@ -0,0 +1,70 @@
+---
+page_type: sample
+urlFragment: nfc-iot
+languages:
+ - csharp
+products:
+ - windows
+ - windows-iot
+ - windows-10-iot-Enterprise
+description: Learn how use NFC on Windows 10 IoT Enterprise.
+---
+
+# Near Field Communication
+Near Field Communication allows a device to send small pieces of data when in close proxmimity to other NFC devices. NFC hardware is required for this communication.
+On Windows 10 IoT Enterprise, you can communicate with the hardware manually by using the GPIO, I2C or SPI APIs, or you can use the SmartCard APIs if a driver is associated with the hardware.
+
+In this sample, we will demonstrate how to set up the NXP NFC SBC Kit and how to communicate with an NXP NTAG21x.
+
+## Prerequisites
+In order to build and test this sample, you will need the following:
+
+ * [Visual Studio 2015 Update 3](http://go.microsoft.com/fwlink/?LinkId=691129).
+ * [NXP Explore-NFC Kit](http://www.digikey.com/products/en?mpart=OM5577&v=568).
+ * [BullsEye NFC NTAG216 Sticker](https://dangerousthings.com/shop/bullseye/) or [xNTi NFC Implant Kit](https://dangerousthings.com/shop/xnti/)
+
+
+## Set up a Raspberry Pi
+
+ 1. Before powering on your Raspberry Pi, assemble the NXP Explore-NFC kit, and attach to the Rasberry Pi.
+ 1. Set up your Raspberry Pi using the instructions [here](https://docs.microsoft.com/en-us/windows/iot-core/tutorials/rpi).
+
+## Build the ACPI Table.
+In order to assocate the NXP Explore-NFC hardware with the driver, resources need to be allocated for it in the ACPI Table. Included in the sample is a file ```pn71x0.asl```, which
+needs to be compiled in order to apply it to your Raspberry PI.
+
+ 1. Open a ```VS2015 x64 Native Tools Command Prompt```
+ 1. Within the command prompt change directory to the NFCOnIoT sample.
+ 1. Within the command prompt run ```"C:\Program Files (x86)\Windows Kits\10\Tools\x64\ACPIVerify\asl.exe" pn71x0.asl```. NOTE: you will get a warning; this is benign.
+ 1. This command will have generated a file called ```ACPITABL.dat``` which will be copied to your pi in the next section.
+ 1. You can type ```start .``` to open an explore window here, which you will use in the next step.
+
+## Setup the NFC Hardware
+
+ 1. In the IoT Dashboard, find your Raspberry Pi, then right click and select ```Open Network Share```, Enter credentials if prompted.
+ 1. Once the network share opens, navigate to ```windows\system32```.
+ 1. Copy the ```ACPITABL.dat``` from the explorer window you opened in the previous section, and copy it to the folder in on the network share you opened in the previous step.
+ 1. On the network share, navigate to ```c:\data```.
+ 1. From the Explorer Window which contains the ```ACPITabl.dat```, there is also a file ```pn71x0.inf```. Copy this to ```c:\data``` on the network share.
+ 1. Use [SSH](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/ssh) or [Powershell](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/powershell) to connect to your device.
+ 1. Change directory to ```c:\data``` in the connected session.
+ 1. Run the command ```devcon dp_add pn71x0.inf```.
+ 1. Run the command ```shutdown -r -t 0``` to restart the Rasberry PI for the hardware changes to take effect.
+
+
+## Configure the NFC Service to start automatically
+In order to minimize the number of resources used by IoT Enterprise, the NFC Service does not start by default. To make it start automatically, use [SSH](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/ssh) to connect to the device and then
+
+ 1. Run ```sc config SEMgrSvc start=auto``` to set this service to autostart on boot.
+ 1. Run ```sc start SEMgrSvc``` to start it for this session.
+
+If you prefer [Powershell](https://docs.microsoft.com/en-us/windows/iot-core/connect-your-device/powershell) to connect to your device, run the following commands in PowerShell.
+
+ 1. ```Set-Service SEMgrSvc -StartupType "Automatic"``` to set this service to autostart on boot.
+ 1. ```Start-Service SEMgrSvc``` to start it for this session.
+
+## Running the NFC Sample
+
+ 1. In the Samples folder you downloaded from Github, open NFCForIoT.sln.
+ 1. Build and deploy the application to your Raspberry Pi.
+ 1. Use the NTAG21x of your choice to see information about it and optionally configure it.