マイナンバーカードのパスワード残り試行回数を調べる

最近話題のマイナンバーカード。マイナポータルを用いた申請等で必要なカードで、このカード1つに4つのパスワードがそれぞれ設定されています。
パスワードの設定は各々がマイナンバーカードを受け取る際に設定したはずですが…
これらのパスワードはそれぞれ規定回数以上連続で間違えてしまうとロックされ、このロック解除のために役所へ行かないといけなくなります。

そしてこの度、[Japan NFC Reader](https://japannfcreader.tret.jp/) では [Ver 1.0.5 で「マイナンバーカードのパスワードの残り試行回数」を確認できるようになりました。](https://twitter.com/JapanNFCReader/status/1260404832210317315)
また、それに伴い [Japan NFC Reader](https://japannfcreader.tret.jp/) のコア部分を MIT Licence で公開しているライブラリ、[treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) でもマイナンバーカードへの対応作業を開始し、このパスワードの残り試行回数を取得する機能も提供しています。

今回はこちらを使って、実際にマイナンバーカードからパスワードの残り試行回数を取得するところまでを試してみます。

なお、この記事を書くために作成したプロジェクトはまるごと GitHub 上で公開しています。もしよろしければ参考にしてください。
[treastrain/MyNumberPINRemainingChecker](https://github.com/treastrain/MyNumberPINRemainingChecker)

# 実機の用意
実際のマイナンバーカードからデータを読み取るので、シミュレーターでは動作しません。iOS 13.0 以降を搭載した「リーダーモード対応NFC」が使える iPhone が必要です。

# Swift Package Manager でライブラリを導入する
[treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) はマイナンバーカードの他にも、Suica、PASMO といった交通系ICカード、各種電子マネー、運転免許証の読み取りにも対応しており、少しずつ大きなライブラリとなっています。
今回はマイナンバーカードの読み取り機能のみ利用できればよいので、Swift Package Manager を用いて部分的にライブラリを導入します。

Xcode で iOS App のプロジェクトを作成したら、「File」>「Swift Packages」>「Add Package Dependency...」に進みます。
<img width="808" alt="001.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/b77830f5-f4e8-b05b-14ee-fd98321b715c.png">
「Search or enter package repository URL」に「TRETJapanNFCReader」と入力します。
<img width="760" alt="002.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/2423dd26-30ac-4701-d2a5-30dc303a1aef.png">


記事執筆時点ではマイナンバーカードに関する機能は Release version に含まれていないので、master ブランチを選択します。
<img width="760" alt="003.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/c2ab73fd-b415-b7cc-de85-eab9cc7a2a62.png">
本来であれば、マイナンバーカードの読み取りに関する Product のみにチェックを入れればよいのですが名称が長くて全て表示されないので、どれにもチェックを入れずに「Finish」します。
<img width="780" alt="004.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/c8732bde-3413-933b-bd32-916ee54104c6.png">
Target の「Frameworks, Libraries, and Embedded Content」にて、「TRETJapanNFCReader-MIFARE-IndividualNumber」を「Add」します。
<img width="650" alt="005.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/35fa40f3-e0c2-01b5-691e-15fb4f92ccf5.png">

*※個人番号カード（マイナンバーカード）は英語で「Individual Number Card」となっています。*

これで正しくマイナンバーカードに関する Product のみを取り込めました。
<img width="814" alt="006.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/ecfde983-b1ef-8aaf-6706-9e3df79f05e0.png">

# Capability と Entitlements の設定
これ以降は基本的に [treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) の README に示されたとおり進めていくだけです。
まず、Signing & Capabilities で「Near Field Communication Tag Reading」を有効にします。これにより、「Near Field Communication Tag Reader Session Formats」が entitlements ファイルに含まれます。
<img width="681" alt="007.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/b522320d-d8ca-2ee6-d91a-d324b3018de1.png">

# Info.plist の設定
Info.plist に「Privacy - NFC Scan Usage Description」と「ISO7816 application identifiers for NFC Tag Reader Session」を追加します。
「Privacy - NFC Scan Usage Description」には NFC を何のために使用するのかについての説明を、「ISO7816 application identifiers for NFC Tag Reader Session」の配下には以下の AID を記述します。

- Item 0: `D392F000260100000001`
- Item 1: `D3921000310001010408`
- Item 2: `D3921000310001010100`
- Item 3: `D3921000310001010401`

<img width="651" alt="008.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/6632b543-040e-afe8-8b1b-a8e5903e9bb7.png">

# コーディング
以上でもろもろの設定はおしまいです。続いてコーディングしていきます。
すべて `ViewController.swift` に記述します。全文を見たい場合は GitHub 上の [treastrain/MyNumberPINRemainingChecker](https://github.com/treastrain/MyNumberPINRemainingChecker) を見たほうが便利かもしれません。

## ライブラリをインポートする
まずはライブラリをインポートします。

```swift
import UIKit
import TRETJapanNFCReader_MIFARE_IndividualNumber
```

## `IndividualNumberReaderSessionDelegate` に準拠させる
`IndividualNumberReaderSessionDelegate` プロトコルに準拠させます。

`individualNumberReaderSession(didRead:)` はマイナンバーカードからの情報の読み取りが終わると呼ばれるものになります。しかし、今回の目的である「パスワードの残り試行回数を調べる」際には特に使用しません。

`japanNFCReaderSession(didInvalidateWithError:)` は NFC 通信でのエラーハンドリングに使用します。今回は省略します。

```swift
class ViewController: UIViewController, IndividualNumberReaderSessionDelegate {
    // ...
    
    func individualNumberReaderSession(didRead individualNumberCardData: IndividualNumberCardData) {
        // ...
    }
    
    func japanNFCReaderSession(didInvalidateWithError error: Error) {
        // ...
    }
    
    // ...
}
```

## `IndividualNumberReader` を初期化する
マイナンバーカードの読み取りには `IndividualNumberReader` を使用します。

```swift
var reader: IndividualNumberReader!

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.reader = IndividualNumberReader(delegate: self)
    // ...
}
```

## パスワードの種別を指定する
[treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) では、マイナンバーカードの4つのパスワードの残り試行回数の取得に対応しています。それは `IndividualNumberCardPINType` に次のように定義されています。

```swift:TRETJapanNFCReader/Sources/MIFARE/IndividualNumber/IndividualNumberCardPINType.swift
public enum IndividualNumberCardPINType {
    /// 署名用電子証明書（公的個人認証 署名用）
    case digitalSignature
    /// 利用者証明用電子証明書（公的個人認証 利用者証明用）
    case userAuthentication
    /// 券面事項入力補助用
    case cardInfoInputSupport
    /// 個人番号カード用（住民基本台帳事務用）
    case individualNumber
}
```

ここでは、署名用電子証明書のパスワードを取得します。

```swift
// ...
self.reader = IndividualNumberReader(delegate: self)
let pinType = IndividualNumberCardPINType.digitalSignature
// ...
```

## `IndividualNumberReader.lookupRemainingPIN(pinType:completion:)` を呼び出す
以上の準備ができたら、`IndividualNumberReader.lookupRemainingPIN(pinType:completion:)` を呼び出します。`completion` の `Int?` に残り試行回数が含まれています。エラーが発生した場合は `nil` となり、NFC 通信に起因するエラーの場合は先述の `japanNFCReaderSession(didInvalidateWithError:)` にエラー内容が送られてきます。

```swift
// ...
self.reader = IndividualNumberReader(delegate: self)
let pinType = IndividualNumberCardPINType.digitalSignature

self.reader.lookupRemainingPIN(pinType: pinType) { (remaining) in
    if let remaining = remaining {
        print(pinType.description, "残り試行回数:", remaining)
    } else {
        // 取得エラー
    }
}
```

# 実行結果
それでは実行してみましょう。以下のように残り試行回数がコンソールに表示されれば成功です。
<img width="532" alt="009.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/5a6caf3e-8d9c-9207-2ea9-f8af63d6bfae.png">

もしよろしければ GitHub にて Star をしていただけると嬉しいです…！
- [treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) : 今回使用したライブラリ
- [treastrain/MyNumberPINRemainingChecker](https://github.com/treastrain/MyNumberPINRemainingChecker) : 今回の記事のために作成したサンプルプロジェクト

# 環境
- 開発
  - Xcode Version 11.4.1 (11E503a)
  - Apple Swift version 5.2.2 (swiftlang-1103.0.32.6 clang-1103.0.32.51)
  - macOS Catalina 10.15.4 (19E287)
- 実機
  - iPhone X (A1902、MQAY2J/A) 
    - iOS 13.3.1 (17D50)
  - iPhone 11 Pro (A2215、MWCC2J/A) 
    - iOS 13.5 (17F5065a)