1.仕組み
最初に、いくつかの便利なコマンドを学ぶ必要があります。
暗号化オプションを使用してボリュームをフォーマットする
# mkfs.ext4 -O encrypt /dev/xxx
既存のボリュームで暗号化オプションを有効にする
# tune2fs -O encrypt /dev/xxx
暗号化キーを作成する
# mount /dev/xxx /mnt/xxx $ e4crypt add_key Enter passphrase (echo disabled): Added key with descriptor [8e679e4449bb9235]
キーを作成するとき、暗号化サポートのあるボリュームをマウントする必要があります。そうしないと、e4cryptは「No salt values available」エラーを生成します。 暗号化オプションを持つ複数のボリュームがマウントされている場合、それぞれのキーが作成されます。 e4cryptユーティリティはe2fsprogsの一部です。
キーはLinuxカーネルキーリングに追加されます[1]。
キーのリストを読む
$ keyctl show Session Keyring 771961813 --alswrv 1000 65534 keyring: _uid_ses.1000 771026675 --alswrv 1000 65534 \_ keyring: _uid.1000 803843970 --alsw-v 1000 1000 \_ logon: ext4:8e679e4449bb9235
暗号化に使用されるキーのタイプは「ログオン」です。 このタイプのキーのコンテンツ(ペイロード)はユーザー空間からアクセスできません-読み取り、パイプ、印刷コマンドのkeyctlはエラーを返します。 この例では、キーのプレフィックスは「ext4」ですが、「fscrypt」にすることもできます。 keyctlがシステムに存在しない場合は、keyutilsパッケージをインストールする必要があります。
暗号化されたディレクトリの作成
$ mkdir /mnt/xxx/encrypted_folder $ e4crypt set_policy 8e679e4449bb9235 /mnt/xxx/encrypted_folder/ Key with descriptor [8e679e4449bb9235] applied to /mnt/xxx/encrypted_folder/.
ここで、set_policyコマンドには、プレフィックス(ext4)とタイプ(ログオン)を指定せずに、作成されたキーのハンドルが渡されます。 複数のディレクトリを同じキーで暗号化できます。 異なるキーを使用して、異なるディレクトリを暗号化できます。 ディレクトリが暗号化されているキーを確認するには、次のコマンドを実行する必要があります。
$ e4crypt get_policy /mnt/xxx/encrypted_folder/ /mnt/xxx/encrypted_folder/: 8e679e4449bb9235
暗号化されたディレクトリに別のセキュリティポリシーをインストールすると失敗します。
$ e4crypt add_key Enter passphrase (echo disabled): Added key with descriptor [9dafe822ae6e7994] $ e4crypt set_policy 9dafe822ae6e7994 /mnt/xxx/encrypted_folder/ Error [Invalid argument] setting policy. The key descriptor [9dafe822ae6e7994] may not match the existing encryption context for directory [/mnt/xxx/encrypted_folder/].
ただし、このようなディレクトリは簡単に削除できます。
$ rm -rf /mnt/xxx/encrypted_folder/ $ ll /mnt/xxx total 24 drwxr-xr-x 3 user user 4096 Apr 21 15:14 ./ drwxr-xr-x 4 root root 4096 Mar 29 15:30 ../ drwx------ 2 root root 16384 Apr 17 12:41 lost+found/ $
ファイル暗号化
$ echo "My secret file content" > /mnt/xxx/encrypted_folder/my_secrets.txt $ cat /mnt/xxx/encrypted_folder/my_secrets.txt My secret file content $ ll /mnt/xxx/encrypted_folder/ total 12 drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./ drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../ -rw-r--r-- 1 user user 23 Apr 20 14:26 my_secrets.txt
ディレクトリが暗号化されたキーストアにキーがある限り、ディレクトリ内のファイル名とファイルの内容は利用可能です。 キーを無効にすると、ディレクトリへのアクセスは厳しく制限されます。
$ keyctl revoke 803843970 $ keyctl show Session Keyring 771961813 --alswrv 1000 65534 keyring: _uid_ses.1000 771026675 --alswrv 1000 65534 \_ keyring: _uid.1000 803843970: key inaccessible (Key has been revoked)
キーはキャンセルされ、ディレクトリの内容を読み取ります:
$ ll /mnt/xxx/encrypted_folder/ total 12 drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./ drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../ -rw-r--r-- 1 user user 23 Apr 20 14:26 BhqTNRNHDBwpa9S1qCaXwC
ファイル名はすでにabyrvalgです。 しかし、まだファイルを読み取ろうとします:
$ cat /mnt/xxx/encrypted_folder/BhqTNRNHDBwpa9S1qCaXwC cat: /mnt/xxx/encrypted_folder/BhqTNRNHDBwpa9S1qCaXwC: Required key not available
注: Ubuntu 17.04(カーネル4.10.0-19)では、再マウントする前にキーを削除した後もディレクトリにアクセスできます。
$ keyctl show Session Keyring 771961813 --alswrv 1000 65534 keyring: _uid_ses.1000 771026675 --alswrv 1000 65534 \_ keyring: _uid.1000 $ e4crypt get_policy /mnt/xxx/encrypted_folder/ /mnt/xxx/encrypted_folder/: 8e679e4449bb9235
ディレクトリは、記述子「8e679e4449bb9235」のキーで暗号化されます。 キーはリポジトリにありません。 それにもかかわらず、ファイルのディレクトリとコンテンツは自由に利用できます。
$ ll /mnt/xxx/encrypted_folder/ total 12 drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./ drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../ -rw-r--r-- 1 user user 23 Apr 20 14:26 my_secrets.txt $ cat /mnt/xxx/encrypted_folder/my_secrets.txt My secret file content
再マウント:
# umount /dev/xxx # mount /dev/xxx /mnt/xxx $ ll /mnt/xxx/encrypted_folder/ total 12 drwxr-xr-x 2 user user 4096 Apr 20 14:25 ./ drwxr-xr-x 5 user user 4096 Apr 20 14:15 ../ -rw-r--r-- 1 user user 23 Apr 20 14:26 BhqTNRNHDBwpa9S1qCaXwC
2.ファイルシステムの変更
スーパーブロック内:暗号化対応ボリュームに設定されたs_feature_incompatオプションには、EXT4_FEATURE_INCOMPAT_ENCRYPTフラグが含まれています。
s_encrypt_algos [4]-暗号化アルゴリズムを保存します。 現時点ではこれは:
s_encrypt_algos [0] = EXT4_ENCRYPTION_MODE_AES_256_XTS;
s_encrypt_algos [1] = EXT4_ENCRYPTION_MODE_AES_256_CTS;
s_encrypt_pw_salt-フォーマット中にも設定されます。
iノード:i_flagsにはEXT4_ENCRYPT_FLフラグが含まれており、これからオブジェクトが暗号化されていると判断できます。
暗号化されたディレクトリ構造
ディレクトリの内容を読み取るには、iノードによってディスク上の場所を特定する必要があります。
1. iノード番号の決定:
$ stat /mnt/xxx/encrypted_folder/ File: /mnt/xxx/encrypted_folder/ Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 811h/2065d Inode: 14 Links: 2
2. iノードテーブルでiノードを検索します。
Ainod 14は0番目のグループに属しているため、0番目のグループの記述子テーブルを読み取り、その中のiノードテーブルのブロック番号を見つける必要があります。 グループ0の記述子テーブルは、スーパーブロックに続くクラスター内にあります。
# dd if=/dev/xxx of=gdt bs=4096 count=1 skip=1
図 1. 0番目のグループのテーブル記述子
まず、ビットマップブロックのクラスター番号とiノードのビットマップをスキップし、テーブルの先頭から8バイトのオフセットでiノードのテーブルの先頭のクラスター番号を読み取ります-BigEndian形式の0x00000424(1060)。 ディレクトリのinode = 14、テーブル内の256バイトのinodeサイズでは、先頭からオフセット0x0D00になります。 したがって、inodoテーブルの最初のクラスターのみを読み取るだけで十分です。
# dd if=/dev/xxx of=itable bs=4096 count=1 skip=1060
図 2. Ainod暗号化ディレクトリ。
iノードで、i_block []フィールドの先頭を決定します。 なぜなら ext4の場合、i_blockの最初の2バイトはエクステントツリーのヘッダー-0xF30Aです。 次に、暗号化されたディレクトリが保存されているブロック番号-0x00000402(1026)を確認できます。 (図では、i_blockフィールド全体が選択されているのではなく、有益な24バイトのみが選択されています。残りの36バイトはゼロで埋められています。)
3.ディレクトリブロックの読み取り:
# dd if=/dev/xxx of=dirdata bs=4096 count=1 skip=1026
図 3.暗号化されたディレクトリをダンプします。
詳細:最初の2つのエントリ(赤で強調表示)は、エントリ「。」と「..」、それぞれ現在のディレクトリと親ディレクトリです。 現在のinodディレクトリは0x0000000E、レコード長は0x000Cバイト、ファイル名の文字数は01、エントリタイプ02はディレクトリです。 以下は、4バイト境界-2E000000(2Eは文字 '。'-ピリオドに対応)で整列されたディレクトリの名前です。
次の親ディレクトリにはiノード0x00000002(ルートディレクトリ)があり、同じレコード長は0x000C、名前は02文字、タイプも02、その後にディレクトリ名が続く-2E2E0000(2ポイント)。
最後に、このディレクトリの最後のエントリのiノードは0x0000000F、レコードサイズは0x0FDC、名前の文字数は0x10、タイプ01-これは暗号化されたファイルです。 ご覧のとおり、その名前は作成されたmy_secrets.txtと一致しません。 また、元のファイル名には14文字しかなく、ここの16文字ではありません。
注:特に注意深い電卓の読者は、 暗号化されたファイルはディレクトリの最後のエントリであり、そのレコードサイズはブロックの境界を参照する必要があります。 ただし、0xFDCではなく、0x1000-0xC-0xC = 0xFE8です。 これは、「metadata_csum」オプションを使用してボリュームが作成されたためです。このオプションは、Ubuntu 16.10以降でデフォルトで設定されています。 このオプションを有効にすると、このブロックのチェックサムを含む各ディレクトリブロックの最後に12バイトの構造が作成されます。
4.暗号化されたファイルを読み取ります。
ディレクトリのダンプから、ファイルにiノード15(0xF)があることを確認します。 iノードのテーブルでそれを探し、同様にディスク上の位置を決定します。
図 4. Ainod暗号化ファイル。
クラスター0x0000AA00(43520)の内容を読み取ります
# dd if=/dev/xxx of=filedata bs=4096 count=1 skip=43520
図 5.暗号化されたファイルの内容
そして、これはファイルに記録された情報にまったく対応していません。 実際のファイルサイズは、i_size iノードフィールド(図4の青い長方形でマーク)で読み取ることができます:0x00000017-これは、エコーコマンド「My secret file content」+改行文字0x0Aで書き込まれた量です。
3.復号化
デコードファイル名
EXT4 Encryption Design Document [2]によると、ファイル名の復号化は2段階で実行されます。
1. DerivedKey = AES-128-ECB(データ= MasterKey、キー= DirNonce);
2. EncFileName = AES-256-CBC-CTS(データ= DecFileName、キー= DerivedKey);
つまり 最初の段階で、復号化用のキーを取得する必要があります。 これを行うには、キーをキーリングに追加するときに作成されたマスターキーのデータを使用します。これは、128ビットのDirNonceキーでAES-ECBを使用して暗号化されます。 2番目のステージでは、ゼロで満たされた固定初期化ベクトル(IV)を使用します。 AES-ECBの場合、初期化ベクトルは必要ありません。
DirNonceとは何ですか? 暗号化されたディレクトリのiノードに拡張属性があります。
図 6.暗号化されたディレクトリとその拡張属性の説明
inodeサイズが256バイトの場合、約100個の未使用バイトが構造に残り(0x100-EXT2_GOOD_OLD_INODE_SIZE-i_extra_size)、そこに情報を保存できます(図6の赤い領域)。 この領域の最初の4バイトのヘッダー0xEA020000からわかるように、インデックス09の拡張属性はここに格納され、そのデータはヘッダーから0x40バイトだけオフセットされ、サイズは0x1Cです。 データ領域は3つのゾーンに分割されます。最初の(01 01 04 00)では、iノードが暗号化されたアルゴリズムが記録されます。 2番目のものは8バイト(8E 67 9E 44 49 BB 92 35)を格納し、キーハンドルを繰り返します。 3番目のものには、マスターキーを暗号化するときに使用される16バイトのワンタイムコード(非[3])が含まれています。
したがって、ファイル名を復号化するには、以下を行う必要があります。
1)インデックス9でディレクトリの名前のない拡張属性の値を読み取ります-ディレクトリnonsを取得します。
2)AES-ECBアルゴリズムを使用して、128ビットのディレクトリナンセンスをキーとして使用してマスターキーのデータを暗号化します。
3)AES-CBC-CTSアルゴリズムを使用して、前の手順で取得したキーの最初の256ビット(半分)をキーとして使用してファイル名を復号化します。
ファイルの内容の復号化
ファイル名の復号化と同様に実行されますが、ファイルのiノードから取得した拡張属性値がnonseとして使用される点が異なります。 また、CBCの代わりに、コンテンツは完全な64バイトのキーを持つAES-XTSアルゴリズムを使用して復号化されます。 ファイルの先頭からの論理ブロックオフセットがIVとして使用されます。
図 7.暗号化されたファイルのAinodとその拡張属性。
暗号化されたファイルとディレクトリの拡張属性の値を比較すると、暗号化アルゴリズムとキー記述子が同じである一方で、それらの非名前が異なることがわかります(図の黄色と青色のゾーン)。
ファイルのコンテンツはページごとに暗号化されるため、コンテンツを復号化するには、iノードのi_sizeフィールドで指定されたサイズではなく、ファイルのクラスター全体(4K)を使用する必要があります。
4.実装
デコーダーの実装は、Linux Kernel Crypto API [4]に基づいています。 チェーンは、ebc(aes)、cts(cbc(aes))、xts(aes)アルゴリズムの/ proc / cryptoに記述されているものに応じて、2種類の暗号化を使用します。 カーネル4.10.0-19を考えてみましょう。ebc暗号はblkcipher、cts(cbc)、およびxtsを介してskcipherを介して実装されます。
$ cat / proc / crypto
$ cat / proc / crypto
名前:ecb(aes)
ドライバー:ecb(aes-aesni)
モジュール:カーネル
優先度:300
内部:いいえ
タイプ:blkcipher
ブロックサイズ:16
最小キーサイズ:16
最大キーサイズ:32
ivsize:0
geniv:デフォルト
名前:cts(cbc(aes))
ドライバー:cts(cbc-aes-aesni)
モジュール:カーネル
優先度:400
内部:いいえ
タイプ:skcipher
非同期:はい
ブロックサイズ:16
最小キーサイズ:16
最大キーサイズ:32
ivsize:16
チャンクサイズ:16
名前:xts(aes)
ドライバー:xts-aes-aesni
モジュール:aesni_intel
優先度:401
内部:いいえ
タイプ:skcipher
非同期:はい
ブロックサイズ:16
最小キーサイズ:32
最大キーサイズ:64
ivsize:16
チャンクサイズ:16
名前:ecb(aes)
ドライバー:ecb(aes-aesni)
モジュール:カーネル
優先度:300
内部:いいえ
タイプ:blkcipher
ブロックサイズ:16
最小キーサイズ:16
最大キーサイズ:32
ivsize:0
geniv:デフォルト
名前:cts(cbc(aes))
ドライバー:cts(cbc-aes-aesni)
モジュール:カーネル
優先度:400
内部:いいえ
タイプ:skcipher
非同期:はい
ブロックサイズ:16
最小キーサイズ:16
最大キーサイズ:32
ivsize:16
チャンクサイズ:16
名前:xts(aes)
ドライバー:xts-aes-aesni
モジュール:aesni_intel
優先度:401
内部:いいえ
タイプ:skcipher
非同期:はい
ブロックサイズ:16
最小キーサイズ:32
最大キーサイズ:64
ivsize:16
チャンクサイズ:16
blkcipherによる暗号化の実装
typedef enum { ENCRYPT, DECRYPT } cipher_mode; static int do_blkcrypt(const u8* cipher, const u8* key, u32 key_len, void* iv, void* dst, void* src, size_t src_len, cipher_mode mode) { int res; struct crypto_blkcipher* blk; struct blkcipher_desc desc; struct scatterlist sg_src, sg_dst; blk = crypto_alloc_blkcipher(cipher, 0, 0); if (IS_ERR(blk)) { printk(KERN_WARNING "Failed to initialize blkcipher mode %s\n", cipher); return PTR_ERR(blk); } res = crypto_blkcipher_setkey(blk, key, key_len); if (res) { printk(KERN_WARNING "Failed to set key. len=%#x\n", key_len); crypto_free_blkcipher(blk); return res; } crypto_blkcipher_set_iv(blk, iv, 16); sg_init_one(&sg_src, src, src_len); sg_init_one(&sg_dst, dst, src_len); desc.tfm = blk; desc.flags = 0; if (mode == ENCRYPT) res = crypto_blkcipher_encrypt(&desc, &sg_dst, &sg_src, src_len); else res = crypto_blkcipher_decrypt(&desc, &sg_dst, &sg_src, src_len); crypto_free_blkcipher(blk); return res; }
skcipherを介した暗号化の実装
struct tcrypt_result { struct completion completion; int err; }; static void crypt_complete_cb(struct crypto_async_request* req, int error) { struct tcrypt_result* res = req->data; if (error == -EINPROGRESS) return; res->err = error; complete(&res->completion); } static int do_skcrypt(const u8* cipher, const u8* key, u32 key_len, void* iv, void* dst, void* src, size_t src_len, cipher_mode mode) { struct scatterlist src_sg, dst_sg; struct crypto_skcipher* tfm; struct skcipher_request* req = 0; struct tcrypt_result crypt_res; int res = -EFAULT; tfm = crypto_alloc_skcipher(cipher, 0, 0); if (IS_ERR(tfm)) { printk(KERN_WARNING "Failed to initialize skcipher mode %s\n", cipher); res = PTR_ERR(tfm); tfm = NULL; goto out; } req = skcipher_request_alloc(tfm, GFP_NOFS); if (!req) { printk(KERN_WARNING "Couldn't allocate skcipher handle\n"); res = -ENOMEM; goto out; } skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, crypt_complete_cb, &crypt_res); if (crypto_skcipher_setkey(tfm, key, key_len)) { printk(KERN_WARNING "Failed to set key\n"); res = -EINVAL; goto out; } sg_init_one(&src_sg, src, src_len); sg_init_one(&dst_sg, dst, src_len); skcipher_request_set_crypt(req, &src_sg, &dst_sg, src_len, iv); init_completion(&crypt_res.completion); if (mode == ENCRYPT) res = crypto_skcipher_encrypt(req); else res = crypto_skcipher_decrypt(req); switch (res) { case 0: break; case -EINPROGRESS: case -EBUSY: wait_for_completion(&crypt_res.completion); if (!res && !crypt_res.err) { reinit_completion(&crypt_res.completion); break; } default: printk("Skcipher %scrypt returned with err = %d, result %#x\n", mode == ENCRYPT ? "en" : "de", res, crypt_res.err); break; } out: if (tfm) crypto_free_skcipher(tfm); if (req) skcipher_request_free(req); return res; }
マスターキーのデータ(ペイロード)の読み取り
#define MASTER_KEY_SIZE 64 static int GetMasterKey(const u8* descriptor, u8* raw) { struct key* keyring_key = NULL; const struct user_key_payload* ukp; struct fscrypt_key* master_key; keyring_key = request_key(&key_type_logon, descriptor, NULL); if (IS_ERR(keyring_key)) return -EINVAL; if (keyring_key->type != &key_type_logon) { printk_once(KERN_WARNING "%s: key type must be 'logon'\n", __func__); return -EINVAL; } down_read(&keyring_key->sem); ukp = user_key_payload(keyring_key); master_key = (struct fscrypt_key*)ukp->data; up_read(&keyring_key->sem); if (master_key->size != MASTER_KEY_SIZE) { printk(KERN_WARNING "Wrong Master key size %#x\n", master_key->size); return -EINVAL; } memcpy(raw, master_key->raw, master_key->size); return 0; }
注: 4.4より前のカーネルバージョンでは、user_key_payload関数がありません。 キーデータは、struct key * keyring_keyから直接読み取ることができます。
デコードファイル名
int err; u8 iv[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; u8 nonce_dir[16] = { ... }; u8 master_key[64], derived_key[64]; u8 dec_file_name[] = { ... }; u8 enc_file_name[sizeof(dec_file_name)]; err = do_blkcrypt("ecb(aes)", nonce_dir, 16, iv, derived_key, master_key, MASTER_KEY_SIZE, ENCRYPT); if (err) return err; err = do_skcrypt("cts(cbc(aes))", derived_key, MASTER_KEY_SIZE / 2, iv, dec_file_name, enc_file_name, sizeof(dec_file_name), DECRYPT); return err;
コンテンツの復号化
簡単にするために、メモリを使用した作業は省略されています。 スタックで2 x PAGE_SIZEが与えられたとします。
u8 nonce_file[16] = { ... }; u8 enc_file_data[PAGE_SIZE] = { ... }; u8 dec_file_data[PAGE_SIZE]; err = do_blkcrypt("ecb(aes)", nonce_file, 16, iv, derived_key, master_key, MASTER_KEY_SIZE, ENCRYPT); if (err) return err; err = do_skcrypt("xts(aes)", derived_key, MASTER_KEY_SIZE, iv, dec_file_data, enc_file_data, PAGE_SIZE, DECRYPT); return err;
使用済みヘッダーファイル(4.10.0-19に関連)
#include <linux/kernel.h> #include <linux/module.h> #include <linux/scatterlist.h> #include <linux/fscrypto.h>
メイクファイル
obj-m += ciphertest.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
5.結果
ソースデータ:
u8 master_key[MASTER_KEY_SIZE] = { 0xa5, 0xb5, 0xc9, 0x23, 0x02, 0x14, 0xfc, 0xf7, 0x28, 0xdc, 0x90, 0x25, 0x24, 0x9e, 0xe6, 0xbc, 0x7c, 0xa8, 0xf8, 0xe1, 0x94, 0xf6, 0x67, 0x32, 0x33, 0xc4, 0xc1, 0xe8, 0x78, 0x59, 0xab, 0xfb, 0xae, 0xb0, 0xbf, 0x5d, 0x2c, 0x69, 0xc3, 0x8f, 0x51, 0x37, 0x26, 0x3f, 0xd1, 0xce, 0x37, 0xef, 0x3f, 0x80, 0xe3, 0x2d, 0xd5, 0xfd, 0x78, 0x45, 0x62, 0xf3, 0xa5, 0x24, 0x6b, 0xcf, 0x4a, 0x88 }; u8 enc_file_name[] = { 0x41, 0xa8, 0x4e, 0x4d, 0xd4, 0x1c, 0x43, 0x00, 0xa7, 0x5a, 0x2f, 0xd5, 0xaa, 0xa0, 0x5d, 0xb0 }; u8 nonce_dir[] = { 0x37, 0xba, 0x14, 0x16, 0x3e, 0xa8, 0xd5, 0x48, 0xd1, 0x3c, 0xb5, 0x6a, 0x01, 0xb7, 0x7c, 0x41 }; u8 nonce_file[] = { 0x61, 0x63, 0xb8, 0x31, 0xf4, 0xf5, 0xfc, 0x99, 0x1e, 0x3c, 0xf1, 0x8a, 0x23, 0xaf, 0x1e, 0xa8 };
エンコードされたファイル名enc_file_nameは、ディレクトリのダンプから取得されます(図3)。
nonce_dirディレクトリのnonsは、ディレクトリのiノードのダンプから取得されます(図6)
nonce_fileファイルのnonsは、ファイルのiノードのダンプから取得されます(図7)
マスターキーは、説明のためだけにここに示されています。 e4cryptをデバッグすることで取得できます。
作成されたドライバーの結果
参照資料
[1] KERNEL KEY RETENTION SERVICE、 www.kernel.org / doc / Documentation / security / keys.txt
[2] EXT4暗号化設計ドキュメント、 docs.google.com / document / d / 1ft26lUQyuSpiu6VleP70_npaWdRfXFoNnB8JYnykNTg / edit
[3]ウィキペディア-Nonce、 en.wikipedia.org / wiki / Nonce
[4] Linux Kernel Crypto API、 www.kernel.org / doc / html / latest / crypto / index.html
[2] EXT4暗号化設計ドキュメント、 docs.google.com / document / d / 1ft26lUQyuSpiu6VleP70_npaWdRfXFoNnB8JYnykNTg / edit
[3]ウィキペディア-Nonce、 en.wikipedia.org / wiki / Nonce
[4] Linux Kernel Crypto API、 www.kernel.org / doc / html / latest / crypto / index.html