JetPack Security  を触ってみる

## Jetpack Security とは
- Google I/O 2019で発表されたのセキュリティライブラリ
- 既存のAES暗号化・復号化に比べて安易
- 暗号化する際のパフォーマンスが優れている
- `EncryptedFile`、`EncryptedSharedPreferences`がメインのクラスとしてあり、SharedPreferenceとファイルI/Oをサポート
- `EncryptedFile`では [googleが出してる暗号化ライブラリ tink](https://github.com/google/tink)のロジックが内包されている。
- min SDK version 23

## Master Keyの作成

Jetpack Securityは、キー管理に2部構成のシステムを使用します。

- ファイルまたは共有設定データを暗号化するための 1つ以上のキーを含むキーセット。キーセット自体は SharedPreferences に格納されます。

- すべてのキーセットを暗号化するマスターキー。このキーは Android キーストア システムを使用して保存されます。

- Android Keystoreのラッパーclassである`MasterKeys`を使用するとたった２行でMaster Keyを作成出来る。

```Kotlin
    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
```

## EncryptedSharedPreferencesを触ってみる

#### 通常のSharedPreferences

```Kotlin
   val data = getSharedPreferences("Sample", Context.MODE_PRIVATE)

   val editor = data.edit()
   editor.putInt("IntSave", 10)
   editor.apply()

   val intSaved = data.getInt("IntSave", 1)
   Log.d("IntSave", intSaved.toString())
```

キーと値がそのまま保存される。
<img width="338" alt="スクリーンショット 2019-12-10 10.06.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69103/04e9fb32-9574-586d-8c89-f17e17560925.png">
<img width="441" alt="スクリーンショット 2019-12-10 10.05.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69103/b150f0c7-b0a6-8d05-f404-76a8870ede87.png">

#### EncryptedSharedPreferences

```Kotlin
    
    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

    val sharedPreferences = EncryptedSharedPreferences
      .create(
        "Sample",
        masterKeyAlias,
        context,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
      )

    val editor = sharedPreferences.edit()
    editor.putInt("IntSave", 10)
    editor.apply()

    val intSaved = sharedPreferences.getInt("IntSave", 1)
    Log.d("IntSave", intSaved.toString())
```

キーと値が暗号化されて保存される。
<img width="253" alt="スクリーンショット 2019-12-10 23.28.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69103/60ce1713-6c5e-55fc-f0ec-9fa7ca23e8d3.png">
<img width="1033" alt="スクリーンショット 2019-12-10 23.27.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69103/8323789e-bff2-c081-f9fc-b09989fbdbd8.png">


#### 速度を比較してみる
**SharedPreferenceTestのテストコード**

```Kotlin
@RunWith(AndroidJUnit4::class)
class SharedPreferenceTest {

  private lateinit var data: SharedPreferences
  private lateinit var editor: SharedPreferences.Editor

  @Before
  fun setup() {
    // Context of the app under test.
    val appContext = InstrumentationRegistry.getInstrumentation().targetContext

    data = appContext.getSharedPreferences("Sample", Context.MODE_PRIVATE)
    editor = data.edit()
  }

  @Test
  fun sharedPreference() {

    for (i in 1..10000) {
      editor.putInt("IntSave", i)
      editor.apply()

      val intSaved = data.getInt("IntSave", 1)
      assertEquals(intSaved, i)
    }
  }
}

```

**EncryptedSharedPreferenceのテストコード**

```Kotlin
@RunWith(AndroidJUnit4::class)
class EncryptedSharedPreferenceTest {

  private lateinit var data: SharedPreferences
  private lateinit var editor: SharedPreferences.Editor

  @Before
  fun setup() {
    val appContext = InstrumentationRegistry.getInstrumentation().targetContext

    val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
    val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

    data = EncryptedSharedPreferences
      .create(
        "Sample",
        masterKeyAlias,
        appContext,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
      )

    editor = data.edit()
  }

  @Test
  fun encryptedSharedPreference() {
    for (i in 1..10000) {
      editor.putInt("IntSave", i)
      editor.apply()

      val intSaved = data.getInt("IntSave", 1)
      Assert.assertEquals(intSaved, i)
    }
  }
}
```

|測定結果|EncryptedSharedPreferences  |SharedPreferences |
|---|---|---|
|最遅|6212  |567  |
|平均|6119.3  |514.3  |
|最速|6028|426 |
単位 ms　実行環境　Pixel 3

比べてみると10倍の差が開いていますが、EncryptedSharedPreferencesも十分早いパフォーマンスが出ますね。


## EncryptedFileを触ってみる

#### Write File
`MY SUPER SECRET INFORMATION`という内容のtxtファイルを書き込んでみる。

```Kotlin
val fileToWrite = "my_other_sensitive_data.txt"
val encryptedFile = EncryptedFile.Builder(
    File(context.getFilesDir(), fileToWrite),
    context,
    masterKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

// Write to a file.
try {
    val outputStream: FileOutputStream? = encryptedFile.openFileOutput()
    outputStream?.apply {
        write("MY SUPER SECRET INFORMATION"
            .toByteArray(Charset.forName("UTF-8")))
        flush()
        close()
    }
} catch (ex: IOException) {
    // Error occurred opening file for writing.
}
```
#### Read File
`MY SUPER SECRET INFORMATION`という内容がログに出力され、ちゃんと読める様になっている。
BufferedReaderだけで読み込むと、`(�]�}�Wr<������q1Bv����B��|)��j_��>��uBLN#���Y�w���;�̴?�w��M���;�K�M�Ƕ�`と表示され、しっかりとencryptされているのがわかる。

```Kotlin
val fileToRead = "my_sensitive_data.txt"
lateinit var byteStream: ByteArrayOutputStream
val encryptedFile = EncryptedFile.Builder(
    File(context.getFilesDir(), fileToRead),
    context,
    masterKeyAlias,
    EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()

try {
    encryptedFile.openFileInput().use { fileInputStream ->
        try {
            val sb = StringBuilder()
            val br = BufferedReader(InputStreamReader(fileInputStream) as Reader?)
            br.readLine()
                .forEach {
                    sb.append(it)
                }
            br.close()
            // MY SUPER SECRET INFORMATIONと出力される
            Log.d("fileContents", sb.toString())

        } catch (ex: Exception) {
            // Error occurred opening raw file for reading.
        } finally {
            fileInputStream.close()
        }
    }
} catch (ex: IOException) {
    // Error occurred opening encrypted file for reading.
}
```