App Attest APIの概要

※2020/9/1執筆→11/16全体公開（忘れていました）

[App Attest API](https://developer.apple.com/news/?id=2sngpulc)は、iOS 14以上で使用できる、[DeviceCheck](https://developer.apple.com/documentation/devicecheck)の一種です。

たとえばアプリがハックされると、チートや広告の削除、有料機能の不正利用などが行われます。
それを防ぐため、デバイスで特殊な暗号鍵を生成し、サーバーが機密データにアクセスする際にAppleの認証を受けることで、アプリが正当なことを証明できます。

![App Attest API](https://docs-assets.developer.apple.com/published/9ed42beab6/rendered2x-1596232267.png)

### アプリの実装方法

![App Attest API](https://docs-assets.developer.apple.com/published/c41cca8684/rendered2x-1597268789.png)

[こちら](https://developer.apple.com/documentation/devicecheck/establishing_your_app_s_integrity)参照。[DCAppAttestService](https://developer.apple.com/documentation/devicecheck/dcappattestservice)を用いて以下のように実装します。

#### 1. API利用可否の判定

```Swift
guard DCAppAttestService.shared.isSupported else {
    log.error("Unsupported")
    return
}

// サービス利用可
```

#### 2. 暗号鍵の生成

```Swift
DCAppAttestService.shared.generateKey { keyId, error in
    guard error == nil, let keyId = keyId else {
        log.error(error.debugDescription ?? "Unknown error")
        return
    }

    // KeyId生成完了
}
```

#### 3. チャレンジの要求

サーバー（Your server）にKeyIdを送り、チャレンジ（サーバーが決めたランダム値）を受け取ります。

```Swift
let challenge = // サーバーから受け取る
```

#### 4. keyIdとチャレンジを使って認証

```Swift
let hash = Data(SHA256.hash(data: challenge))

DCAppAttestService.shared.attestKey(keyId, clientDataHash: hash) { attestation, error in
    if let error = error {
        log.error(error.debugDescription)
    } else if let attestation = attestation {
        // 認証OK/NG判定
    } else {
        log.error("Attestation is null")
    }
}
```

ここまでは、アプリ起動時に1回だけ行えばよい

#### 5. アプリの正当性の主張

以後、サーバーリクエスト毎に、アプリの正当性を主張します。

今回はチャレンジとサーバーリクエスト内容を組み合わせたハッシュを作成します。

```Swift
let challenge = // サーバーから受け取る
let request = [ "action": "getUser",
                "userId": "1234",
                "challenge": challenge ]
guard let clientData = try? JSONEncoder().encode(request) else { return }
let hash = Data(SHA256.hash(data: clientData))

service.generateAssertion(keyId, clientDataHash: hash) { assertion, error in
    if let error = error {
        log.error(error.debugDescription)
    } else if let assertion = assertion {
        // 正当性OK/NG判定
    } else {
        log.error("Assertion is null")
    }
}
```

### サーバーの実装方法

![App Attest API](https://docs-assets.developer.apple.com/published/18445dcadc/rendered2x-1597268788.png)

[こちら](https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server)参照。やることは以下

・（Your Appに対して）チャレンジの払い出しと保持
・（App attestに対して）認証オブジェクトの生成

※サーバーの実装方法はよくわかっておらず、調査中

### DeviceCheckについて

App Attest単体では、不正を行ったデバイスの特定はできないので、特定したい場合DeviceCheckと併用します。
DeviceCheckは、[こちら](https://qiita.com/owen/items/85dff1e45083d2805140)の記事が分かりやすいです。

### 参考
* [Verify your app’s integrity with the new App Attest API - News - Apple Developer](https://developer.apple.com/news/?id=2sngpulc)
* [Establishing Your App’s Integrity](https://developer.apple.com/documentation/devicecheck/establishing_your_app_s_integrity)
* [Validating Apps That Connect to Your Server](https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server)
* [iOS11で追加されたDeviceCheckについて](https://qiita.com/owen/items/85dff1e45083d2805140)