私は現在、AES暗号化を必要とするファイルシステムの目的でいくつかのカーネル開発を経験しており、次の制約に遭遇します。
- 任意の長さの平文を暗号化できなければなりません
- パディング(ある場合)は目立たない
- バイトのパディングの結果として生じる可能性のあるオーバーヘッドは許容できません
紙の上では、これを解決する簡単な方法があります:CTR暗号化モードを使用してください!
その素晴らしいアイデア(うーん...)で、私はLinuxカーネルの暗号化APIソースに飛び込んで、始める方法を学びます。
この時点で、暗号化関数にはその他の関数の使用が含まれていることに気付きました。
- crypt_inplace関数。その目的は、ユーザーが暗号文を指定された平文と同じメモリ領域に格納したい場合を処理することです。( crypt_segmentと同じですが、メモリの制約があります)
- crypt_segment関数。標準の暗号化機能です。データのブロック全体(つまり、AESの場合は16バイト)を暗号化します。
- crypt_final関数。指定されたプレーンテキストの長さLが、基になるブロック暗号ブロックサイズの倍数でない場合、この関数は残りのバイトに対して暗号化を実行します。
したがって、Lバイト長のプレーンテキストとAESを使用すると、最初のL / 16ブロックは、要求された内容に応じてcrypt_segmentまたはcrypt_inplaceを使用して処理されます。残りのLmod16バイトは、crypt_finalを使用して暗号化されます。
内部のcrypt_segment関数は次のように定義されています:( crypt_inplaceは非常に似ています)
static int crypto_ctr_crypt_segment(struct blkcipher_walk *walk,
struct crypto_cipher *tfm)
{
void (*fn)(struct crypto_tfm *, u8 *, const u8 *) =
crypto_cipher_alg(tfm)->cia_encrypt;
unsigned int bsize = crypto_cipher_blocksize(tfm);
u8 *ctrblk = walk->iv;
u8 *src = walk->src.virt.addr;
u8 *dst = walk->dst.virt.addr;
unsigned int nbytes = walk->nbytes;
do {
/* create keystream */
fn(crypto_cipher_tfm(tfm), dst, ctrblk);
crypto_xor(dst, src, bsize);
/* increment counter in counterblock */
crypto_inc(ctrblk, bsize);
src += bsize;
dst += bsize;
} while ((nbytes -= bsize) >= bsize);
return nbytes;
}
ご覧のとおり、カウンターはブロックサイズごとにインクリメントされます。ほとんどのユースケースでは問題にはなりませんが、次のシナリオを検討してください。
- 指定された場所pで要求を書き込みます(これは、たとえばハードドライブ上の(セクター、オフセット)特定の値である可能性があります):11バイト長のプレーンテキスト
- p + 3での8バイトの読み取り要求:ここでは、書き込まれた暗号文から最初の平文の最後の8バイトを取得します。
最初のステップは、暗号化シーケンスでcrypt_finalを1回呼び出すだけで実行されます。次に、11個の暗号化されたバイトが適切な場所に書き込まれます。しかし、「ブロック全体」の暗号化のために、このデータチャンクの最後の8バイトを取得する場合、解読操作を実行するには、 p、p + 1、およびp+2に格納されている前の3バイトが必要です。
明らかに、ファイルシステムの観点では、読み取りが要求されたときに、ハードウェアに依存する仮定を行わない限り、カーネルがこの種のことを知る方法はありません。
したがって、ここに私の質問があります:バイトベースで常に(解読)操作を実行するようにCTRモードを設定する方法はありますか、それともこれを強制するためにCTRモードの独自の実装を作成する必要がありますか?(この構成操作を実行するためのエントリポイントがctrソースコードに見つかりませんでした。何かを見逃した可能性があります)
よろしくお願いします。大きな投稿であなたを驚かせていないことを願っています!
PS:この投稿のコードスニペットは、Linuxカーネルソースツリーのcryptoディレクトリの下にあるctr.cファイルにあります。表示されるバージョンは、3.8-rc3カーネルリリースのものです。
編集 :
実際、CTRモードは、任意の長さのデータを処理するように設計されています。ISO /IEC10116仕様にある説明を思い出します。
平文Pが等しい長さ(jビット)のチャンク(P i)0<i<nに分割されていると仮定します。Kを暗号化用に提供されたキーとし、IVをカウンターの初期化ベクトルとします
。
暗号文Cは、平文Pのようにチャンク(C i)0<i<nに分割されます。
CTRモードでは、処理された各チャンクをウォークするカウンターが導入されます。とはいえ、チャンクP i(またはC i )の暗号化(または解読)に使用されるカウンターブロックを呼び出します。
)CTR i
最後に、CTR 1 = IV
とします。これらの表記法を使用すると、計算は次のようになります。
1からnまでのiの場合
- Y = AES_encrypt(CTR i、K)<-Yは16バイト長です
- E = Truncate(Y、j)<- Yの左端のjビットのみを保持します
- C i = P i XOR E
- CTR i + 1 = ComputeNextCTR(CTR i)<-通常、 ComputeNextCTRは単純なインクリメントです。
終わり
LinuxカーネルにあるCTRバージョンでは、この動作は、jが基になるブロック暗号ブロックサイズ(AESでは128ビット)の値を取得することで強制されます。ただし、指定された平文が適切な長さでない場合に切り捨てが発生する最後のステップを除きます。 。
私の質問は:暗号化APIに必要なjパラメータを適用するように指示する方法はありますか?私には、答えは「いいえ」のように思われるので、この追加機能を取得するには、ホイールを「再発明」し、CTRモードの新しい実装を作成する必要があります。何か見落としているかもしれませんので、はっきりと述べてくださる方に感謝いたします。
ボーナス1:答えが「いいえ」の場合、Crypto APIのアルガピがどのように機能するかについての簡単な概要は非常に歓迎されます(私は現在これを掘り下げています)。
よろしくお願いします。