mbed TLSでAES-GCMを利用する

# 記事の概要
mbet TLSを用いて、共通鍵暗号に256ビットAES、認証付き暗号にGCMを採用したAES-GCMを利用します。

サンプルプログラムは以下に置いてあります。
https://github.com/matsuikosuke/mbedTLSTest

# AES-GCMとは何か

AESはブロック暗号（共通鍵暗号）です。
秘密鍵のサイズは128ビット、192ビット、256ビットがあります。

それぞれ平文を128ビット、192ビット、256ビットのブロックに分割した上で、暗号化します。
暗号化の暗号モードには様々な種類があります。ECB、CBC、CFB、OFB、CTRなどです。

この暗号化を攻撃するには、それぞれ$2^{128}$、$2^{192}$、$2^{256}$の計算量が必要になります。

そしてAES-GCMとは、暗号モードとしてCTR（カウンター）モードを使用し、そのCTRモードに認証の仕組みを導入したブロック暗号（共通鍵暗号）です。

CTRモードについては以下の記事をご参照ください
[共通鍵暗号の暗号モード CTRについて](https://qiita.com/asksaito/items/130863fe9e6a08dcd65d)

暗号化には、共通鍵暗号の秘密鍵と公開鍵暗号の公開鍵を使います。
その際に、公開鍵が改ざんされていないことを保証するのが認証局の仕組みです。
認証局は公開鍵の発行と失効手続きをします。

# サンプルコード
ネット上やmbet TLSのサンプルなどを参照して以下のサンプルコードを作成しました。

【参考】

- [gcm.c](https://github.com/wolfeidau/mbedtls/blob/master/source/gcm.c) 
- [gcm_test.c](https://github.com/aosp-mirror/platform_external_dropbear/blob/master/libtomcrypt/src/encauth/gcm/gcm_test.c)
    - このサンプルは以下の2点を修正しないといけません
        - 文字列の長さを指定するのにstrlen()を使用せずに、定数のパラメータを渡す
        - 認証タグを書き込むmbedtls_gcm_finish()を実行する

```c
static const unsigned char key[32] =
{
    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
    0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
    0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 
};

static const unsigned char iv[64] =
{
    0x93, 0x13, 0x22, 0x5d, 0xf8, 0x84, 0x06, 0xe5,
    0x55, 0x90, 0x9c, 0x5a, 0xff, 0x52, 0x69, 0xaa,
    0x6a, 0x7a, 0x95, 0x38, 0x53, 0x4f, 0x7d, 0xa1,
    0xe4, 0xc3, 0x03, 0xd2, 0xa3, 0x18, 0xa7, 0x28,
    0xc3, 0xc0, 0xc9, 0x51, 0x56, 0x80, 0x95, 0x39,
    0xfc, 0xf0, 0xe2, 0x42, 0x9a, 0x6b, 0x52, 0x54,
    0x16, 0xae, 0xdb, 0xf5, 0xa0, 0xde, 0x6a, 0x57,
    0xa6, 0x37, 0xb3, 0x9b, 0xb0, 0xee, 0x7a, 0x67
};
static int iv_len = 64;

static const unsigned char input[256] =
{
  0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
  0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
  0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
  0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
  0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
  0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
  0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
  0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55,
  0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
  0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
  0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
  0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
  0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
  0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
  0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
  0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55,
  0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
  0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
  0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
  0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
  0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
  0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
  0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
  0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55,
  0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
  0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
  0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
  0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
  0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
  0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
  0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
  0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55
};
static int inout_len = 256;


int mbedtls_gcm_self_test(void)
{
    mbedtls_gcm_context ctx;
    unsigned char output[256] = {0};
    unsigned char decrypt_input[256] = {0};
    unsigned char tag_buf[16];
    int ret;
    mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
    int key_len = 256; //128 or 196 or 256

    // ENCRYPT
    // ENCRYPT step1: init the context
    mbedtls_gcm_init( &ctx );

    // ENCRYPT step2: set the key
    ret = mbedtls_gcm_setkey( &ctx, cipher, key,  key_len );
    if( ret != 0 )
        goto exit;

    // ENCRYPT step3: Initialise the GCM cipher
    ret = mbedtls_gcm_starts( &ctx, MBEDTLS_GCM_ENCRYPT, iv, iv_len, NULL, 0);
    if( ret != 0 )
        goto exit;

    // ENCRYPT step4: Send the intialised cipher some data and store it
    ret = mbedtls_gcm_update( &ctx, inout_len, input, output );
    if( ret != 0 )
        goto exit;

    // ENCRYPT step5: write the 16-byte auth tag that's appended to the end
    ret = mbedtls_gcm_finish( &ctx, tag_buf, 16 );
    if( ret != 0 )
        goto exit;

    // ENCRYPT step6: Free up the context
    mbedtls_gcm_free( &ctx );

    // DECRYPT
    // DECRYPT step1: init the context
    mbedtls_gcm_init( &ctx );

    // DECRYPT step2: set the key
    ret = mbedtls_gcm_setkey( &ctx, cipher, key,  key_len );
    if( ret != 0 )
        goto exit;

    // DECRYPT step3: Initialise the GCM cipher
    ret = mbedtls_gcm_starts( &ctx, MBEDTLS_GCM_DECRYPT, iv, iv_len, NULL, 0);
    if( ret != 0 )
        goto exit;

    // DECRYPT step4: Send the intialised cipher some data and store it
    ret = mbedtls_gcm_update( &ctx, inout_len, output, decrypt_input );
    if( ret != 0 )
        goto exit;

    // DECRYPT step5: write the 16-byte auth tag that's appended to the end
    ret = mbedtls_gcm_finish( &ctx, tag_buf, 16 );
    if( ret != 0 )
        goto exit;

    // DECRYPT step6: Check result
    if( memcmp( input, decrypt_input, 256 ) != 0 )
    {
        ret = 1;
        goto exit;
    }

    // DECRYPT step7: Free up the context
    mbedtls_gcm_free( &ctx );

exit:
    if( ret != 0 )
    {
        mbedtls_gcm_free( &ctx );
    }

    return ret;
}
```

## include file

```c
#include "mbedtls/gcm.h"
```

## 定数と変数
サンプルでは適当に鍵を固定値にしておきます。

```c
static const unsigned char key[32] = "abcdefghijklmnopABCDEFGHIJKLMNOP";
```

`iv`と入力データも適当に用意しておきます。

内部変数は以下を定義します。

```c
mbedtls_gcm_context ctx;
unsigned char buf[64];
unsigned char tag_buf[16];
int ret;
mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_AES;
unsigned char key_len = 256; //128 or 196 or 256
```

## 暗号化
### 暗号化：初期化
contextを初期化します。

```c
mbedtls_gcm_init( &ctx );
```

鍵を設定します。
返り値が0でない場合は、エラーとみなしてcontextを解放して終了します。

```c
ret = mbedtls_gcm_setkey( &ctx, cipher, key,  key_len );
```

### 暗号化：鍵の設定
鍵の設定を行います。

```c
ret = mbedtls_gcm_setkey( &ctx, cipher, key,  key_len );
```

### 暗号化：GCM暗号化の初期設定

```c
ret = mbedtls_gcm_starts( &ctx, MBEDTLS_GCM_ENCRYPT, iv, iv_len, NULL, 0);
```

第2引数は`mode`で`MBEDTLS_GCM_ENCRYPT`か`MBEDTLS_GCM_DECRYPT`を設定します。

第5引数は`additional data`であり、何も設定しないので`NULL`を代入しています。
第6引数は`additional data`の長さであり、何も設定しないので0です。

### 暗号化：GCM暗号化
入力データをGCM暗号化して、出力バッファに保存します。

```c
ret = mbedtls_gcm_update( &ctx, inout_len, input, output );
```

### 暗号化：16バイト認証タグの追加

16バイトの認証タグを追加します

```c
ret = mbedtls_gcm_finish( &ctx, tag_buf, 16 );
```

### 暗号化：メモリの解放
最後にcontextを解放します

```c
mbedtls_gcm_free( &ctx );
```


### 暗号化：（注意事項）入力データが16の倍数ではない場合

`mbedtls_gcm_update()`の第2引数には入力データのサイズを入力しますが、それは16の倍数でないといけません。
もし入力データのサイズが16の倍数ではない場合、関数 mbedtls_gcm_finish()を呼び出す前に、16バイト未満のデータを別に処理しないといけません。

例えば入力データが266バイトならば、16の倍数の256バイトの後に、残りの10バイトを処理します。

```c
mbedtls_gcm_update( &ctx, 256, input, output );
mbedtls_gcm_update( &ctx, 10, input + 256, output + 256);
mbedtls_gcm_finish( &ctx, tag_buf, 16 );
```

## 復号化
サンプルコードでは上記で作成した暗号文を復号します。
暗号化の場合とほとんど同じなので説明は省略しますが、`mbedtls_gcm_starts`は暗号化と異なり、第2引数は`MBEDTLS_GCM_DECRYPT`になります。

```c
ret = mbedtls_gcm_starts( &ctx, MBEDTLS_GCM_DECRYPT, iv, iv_len, NULL, 0);
```

また、`mbedtls_gcm_update`の第3引数には暗号化データを代入し、復号されたデータが第4引数に保存されます。

```c
ret = mbedtls_gcm_update( &ctx, inout_len, output, decrypt_input );
```

# 参照
[mbed TLSでAES-GCMを利用する](https://qiita.com/Kosuke_Matsui/items/340c6b906fed2c6ea40b)
[mbed TLSでRSAを利用する「RSA-OAEPの暗号化と復号」](https://qiita.com/Kosuke_Matsui/items/4c64c993e5979a65620d)
[mbed TLSでRSAを利用する「RSAの公開鍵と秘密鍵の作成」](https://qiita.com/Kosuke_Matsui/items/786e88eace7646c8a7e6)
[mbed TLSおよびOberonでECDHを利用する](https://qiita.com/Kosuke_Matsui/items/4390f5e58339e95dcdae)
[mbed TLSでHKDFを利用する](https://qiita.com/Kosuke_Matsui/items/93f25f47a6171fe94d30)
[mbed TLSでJWTを利用する](https://qiita.com/Kosuke_Matsui/private/915e6607b2b81de7f55d)