Skip to content

Instantly share code, notes, and snippets.

@dmennis
Last active June 5, 2020 23:41
Show Gist options
  • Select an option

  • Save dmennis/e95f85e2d30e9e08672e2487282d549d to your computer and use it in GitHub Desktop.

Select an option

Save dmennis/e95f85e2d30e9e08672e2487282d549d to your computer and use it in GitHub Desktop.

Revisions

  1. dmennis revised this gist Jun 5, 2020. 1 changed file with 17 additions and 10 deletions.
    27 changes: 17 additions & 10 deletions OATH-NFC-Controller.swift
    Original file line number Diff line number Diff line change
    @@ -9,13 +9,15 @@
    // The app establishes an open NFC connection with the YubiKey, reads the credentials from the OATH module and retrieves a TOTP code based on the first OATH credential retrieved.
    //
    // There are three main NFC components to this class:
    // 1. StartNFC Session - Line 47 -> Triggered by a button click that starts the NFC reader session
    // 2. NFC Session (OBSERVER) - Line 64 -> Registers to receive callbacks sent from an NFC tag reader session.
    // 3. NFC Session (HANDLER) - Line 91 -> This handles callbacks when the NFC tag reader session detects an ISO 7816 tag
    // 1. StartNFC Session - Line 49 -> Triggered by a button click that starts the NFC reader session
    // 2. NFC Session (OBSERVER) - Line 66 -> Registers to receive callbacks sent from an NFC tag reader session.
    // 3. NFC Session (HANDLER) - Line 94 -> This handles callbacks when the NFC tag reader session detects an ISO 7816 tag
    //
    // The other two main functions are communicating with the OATH module of the YubiKey over the NFCReaderSession.
    // 1. getOATHCredentials() - Line 110 gets the list of all OATH credentials stored on that key
    // 2. getOTPCode() - Line 142 calls the YKFKeyOATHCalculateRequest which asks the YubiKey to generate a TOTP code for given credential
    // 1. getOATHCredentials() - Line 113 gets the list of all OATH credentials stored on that key
    // 2. getOTPCode() - Line 152 calls the YKFKeyOATHCalculateRequest which asks the YubiKey to generate a TOTP code for given credential

    // Updated Github Gist on June 5 here: https://gist.github.com/dmennis/e95f85e2d30e9e08672e2487282d549d

    import UIKit
    import YubiKit
    @@ -72,6 +74,7 @@ class ViewController: UIViewController {
    isNFCObservingSessionStateUpdates = newValue

    let nfcSession = YubiKitManager.shared.nfcSession as! YKFNFCSession

    if isNFCObservingSessionStateUpdates {
    self.nfcSesionStateObservation = nfcSession.observe(\.iso7816SessionState, changeHandler: { [weak self] session, change in
    DispatchQueue.main.async { [weak self] in
    @@ -134,15 +137,19 @@ class ViewController: UIViewController {
    self.lblCode.text = "\(credCount) credentials found."
    }

    self.getOTPCode(oathCredentials: credentials )
    // Get the first YKFOATHCredential from the list of YKFOATHCredential's, if not empty
    if(!credentials.isEmpty) {
    let credential = credentials.first as! YKFOATHCredential
    self.getOTPCode(oathCredential: credential)
    } else {
    // Nothing to calculate, close the NFC session.
    YubiKitManager.shared.nfcSession.stopIso7816Session()
    }
    }
    }

    // Ask YubiKey for the TOTP of the provided OATH credential
    func getOTPCode(oathCredentials: [Any]) {

    // Get the first YKFOATHCredential from the list of YKFOATHCredential's
    let oathCredential = oathCredentials.first as! YKFOATHCredential
    func getOTPCode(oathCredential: YKFOATHCredential) {

    // Create the CALCULATE request
    guard let calculateRequest = YKFKeyOATHCalculateRequest(credential: oathCredential) else { return }
  2. dmennis revised this gist Jun 5, 2020. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions OATH-NFC-Controller.swift
    Original file line number Diff line number Diff line change
    @@ -9,13 +9,13 @@
    // The app establishes an open NFC connection with the YubiKey, reads the credentials from the OATH module and retrieves a TOTP code based on the first OATH credential retrieved.
    //
    // There are three main NFC components to this class:
    // 1. StartNFC Session - Line 41 -> Triggered by a button click that starts the NFC reader session
    // 2. NFC Session (OBSERVER) - Line 57 -> Registers to receive callbacks sent from an NFC tag reader session.
    // 3. NFC Session (HANDLER) - Line 85 -> This handles callbacks when the NFC tag reader session detects an ISO 7816 tag
    // 1. StartNFC Session - Line 47 -> Triggered by a button click that starts the NFC reader session
    // 2. NFC Session (OBSERVER) - Line 64 -> Registers to receive callbacks sent from an NFC tag reader session.
    // 3. NFC Session (HANDLER) - Line 91 -> This handles callbacks when the NFC tag reader session detects an ISO 7816 tag
    //
    // The other two main functions are communicating with the OATH module of the YubiKey over the NFCReaderSession.
    // 1. getOATHCredentials() - Line 105 gets the list of all OATH credentials stored on that key
    // 2. getOTPCode() - Line 138 calls the YKFKeyOATHCalculateRequest which asks the YubiKey to generate a TOTP code for given credential
    // 1. getOATHCredentials() - Line 110 gets the list of all OATH credentials stored on that key
    // 2. getOTPCode() - Line 142 calls the YKFKeyOATHCalculateRequest which asks the YubiKey to generate a TOTP code for given credential

    import UIKit
    import YubiKit
  3. dmennis created this gist Jun 5, 2020.
    191 changes: 191 additions & 0 deletions OATH-NFC-Controller.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,191 @@
    //
    // ViewController.swift
    // OATH-NFC
    //
    // Created by Dennis Hills on 6/5/20.
    // Copyright © 2020 Dennis Hills. All rights reserved.
    //
    // Description: This is sample app demonstrating NFC interaction with the OATH module of a YubiKey using the Yubico iOS SDK
    // The app establishes an open NFC connection with the YubiKey, reads the credentials from the OATH module and retrieves a TOTP code based on the first OATH credential retrieved.
    //
    // There are three main NFC components to this class:
    // 1. StartNFC Session - Line 41 -> Triggered by a button click that starts the NFC reader session
    // 2. NFC Session (OBSERVER) - Line 57 -> Registers to receive callbacks sent from an NFC tag reader session.
    // 3. NFC Session (HANDLER) - Line 85 -> This handles callbacks when the NFC tag reader session detects an ISO 7816 tag
    //
    // The other two main functions are communicating with the OATH module of the YubiKey over the NFCReaderSession.
    // 1. getOATHCredentials() - Line 105 gets the list of all OATH credentials stored on that key
    // 2. getOTPCode() - Line 138 calls the YKFKeyOATHCalculateRequest which asks the YubiKey to generate a TOTP code for given credential

    import UIKit
    import YubiKit

    class ViewController: UIViewController {

    // UI Stuff
    @IBOutlet weak var btnStartNFC: UIButton! // Tap this button to start the NFC Session
    @IBOutlet weak var lblCode: UILabel! // Displays the TOTP code

    // NFC Session observation
    private var isNFCObservingSessionStateUpdates = false
    private var nfcSesionStateObservation: NSKeyValueObservation?

    var oathService: YKFKeyOATHServiceProtocol? = nil

    override func viewWillAppear(_ animated: Bool) {
    if (YubiKitDeviceCapabilities.supportsISO7816NFCTags) {
    guard #available(iOS 13.0, *) else { fatalError() }
    observeNFCSessionStateUpdates = true
    }
    }

    override func viewDidLoad() {
    super.viewDidLoad()
    }

    // Initiates the NFCTagReaderSession for interfacing with an ISO 7816 tag
    @IBAction func startNFCSession(_ sender: Any) {
    guard #available(iOS 13.0, *) else {
    fatalError()
    }

    if YubiKitDeviceCapabilities.supportsISO7816NFCTags {
    if YubiKitManager.shared.nfcSession.iso7816SessionState != .closed {
    YubiKitManager.shared.nfcSession.stopIso7816Session()
    }
    YubiKitManager.shared.nfcSession.startIso7816Session()
    }
    }

    /********************
    NFC Session Observer
    ********************/
    @available(iOS 13.0, *)
    var observeNFCSessionStateUpdates: Bool {
    get {
    return isNFCObservingSessionStateUpdates
    }
    set {
    guard newValue != isNFCObservingSessionStateUpdates else {
    return
    }
    isNFCObservingSessionStateUpdates = newValue

    let nfcSession = YubiKitManager.shared.nfcSession as! YKFNFCSession
    if isNFCObservingSessionStateUpdates {
    self.nfcSesionStateObservation = nfcSession.observe(\.iso7816SessionState, changeHandler: { [weak self] session, change in
    DispatchQueue.main.async { [weak self] in
    self?.nfcSessionStateDidChange()
    }
    })
    } else {
    self.nfcSesionStateObservation = nil
    }
    }
    }

    /*******************
    NFC Session Handler
    ********************/
    @available(iOS 13.0, *)
    func nfcSessionStateDidChange() {
    switch YubiKitManager.shared.nfcSession.iso7816SessionState {
    case .open:
    DispatchQueue.global(qos: .default).async { [weak self] in
    if (YubiKitManager.shared.nfcSession.iso7816SessionState != .closed) {
    guard let self = self else { return }
    // Take action here to begin communicating with the YubiKey
    self.getOATHCredentials()
    }
    }
    case .closed:
    break
    default:
    break
    }
    }

    // Get the list of OATH credentials from the OATH module over the open NFC session
    // This is called by nfcSessionStateDidChange() when the NFCSession is active and open
    func getOATHCredentials() {

    var credCount: Int = 0

    // Using the oathService instance over the open NFC session
    oathService = YubiKitManager.shared.nfcSession.oathService

    oathService?.executeListRequest { (response, error) in
    guard error == nil else {
    print("The list request ended in error \(error!.localizedDescription)")
    return
    }
    // If the error is nil, the response cannot be empty.
    guard response != nil else {
    print("Error in getting list of OATH credentials: \(String(describing: response))")
    fatalError()
    }

    let credentials = response!.credentials
    credCount = credentials.count

    print("The key has \(credentials.count) stored credentials.")

    DispatchQueue.main.async {
    self.lblCode.text = "\(credCount) credentials found."
    }

    self.getOTPCode(oathCredentials: credentials )
    }
    }

    // Ask YubiKey for the TOTP of the provided OATH credential
    func getOTPCode(oathCredentials: [Any]) {

    // Get the first YKFOATHCredential from the list of YKFOATHCredential's
    let oathCredential = oathCredentials.first as! YKFOATHCredential

    // Create the CALCULATE request
    guard let calculateRequest = YKFKeyOATHCalculateRequest(credential: oathCredential) else { return }

    // Initialize the OATH Service
    oathService = YubiKitManager.shared.nfcSession.oathService

    // Execute the calculateRequest
    oathService?.execute(calculateRequest) { (response, error) in
    guard error == nil else {
    print("The calculateRequest ended in error: \(error!.localizedDescription)")
    return
    }

    // If the error is nil, the response cannot be empty.
    guard response != nil else { fatalError() }

    // Retrieve the generated TOTP code
    let otp = response!.otp
    print("The OTP value for credential [\(String(describing: oathCredential.label))] is \(otp)")

    // Display the TOTP code
    DispatchQueue.main.async {
    self.lblCode.text = "\(otp)"
    }

    // Done calculting, close the NFC session.
    YubiKitManager.shared.nfcSession.stopIso7816Session()
    }
    }

    override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if (YubiKitDeviceCapabilities.supportsISO7816NFCTags) {
    guard #available(iOS 13.0, *) else {
    fatalError()
    }
    observeNFCSessionStateUpdates = false
    }
    }

    deinit {
    if #available(iOS 13.0, *) { observeNFCSessionStateUpdates = false }
    }
    }