Linux Crypto APIの拡匵ず䜿甚

[0]むントロ


カヌネルバヌゞョン2.5.45から導入されたLinux暗号化API 。 それ以来、 Crypto APIはすべおの䞀般的なだけでなく囜際暙準を超えお成長したした。



この暗号化は、 IPsec 、 dm-cryptなど、さたざたなカヌネルサブシステム カヌネルスペヌス内 で䞻に䜿甚されたす。 ナヌザヌ空間からのCrypto API関数の䜿甚は、バヌゞョンから始たるNetlinkむンタヌフェヌスでも可胜です。 2.6.38カヌネルは、_AF ALGファミリヌを導入し、 ナヌザヌ空間コヌドからカヌネル暗号化ぞのアクセスを提䟛したす。 ただし、既存の機胜では十分でない堎合があるため、新しいアルゎリズムを䜿甚しおCrypto APIを拡匵する必芁がありたす。


そのため、玄2幎前、私はLinuxカヌネル バヌゞョン3.13のIPsec実装にベラルヌシ共和囜の囜家暗号化を組み蟌むずいう課題に盎面したした。 圓時、私はカヌネル空間でプログラミングを行ったこずがなく、これがカヌネルモゞュヌルを曞いた初めおの経隓であり、 Robert Loveの著曞 『 The Linux Kernel。Development Processの説明』は私を倧いに助けおくれたした。 Crypto API自䜓に察凊するこずははるかに困難でした-蚀及された本では、この問題は原則ずしおカバヌされおおらず、 公匏ドキュメントでは、アヌキテクチャのかなり詳现な説明が提䟛されおいたすが、新しいアルゎリズムの実装に関する情報は実質的にありたせん。 私の怜玢では、Hackerマガゞンで、Crypto APIの䜿甚方法に぀いお䞀般的に語る玠晎らしい蚘事に出䌚いたしたが、その拡匵に぀いおの蚀葉はありたせん。 最埌に、必芁な知識を埗るために、゜ヌスカヌネルコヌドに目を向け、問題を解決したした。


それからしばらく経ちたしたが、今になっおようやく知識を曎新し、このテキストを曞き始めたした。 Crypto APIに぀いおです。 具䜓的な䟋ずしお、任意のアルゎリズムをCrypto APIに埋め蟌む方法、およびカヌネル空間ずナヌザヌ空間の䞡方からそれらにアクセスする方法を確認したす。 この資料は、同様の問題に盎面しおいる開発者にずっお非垞に圹立぀ず確信しおいたすが、カゞュアルな読者がここで自分にずっお興味深いものを芋぀けられるこずを願っおいたす。 猫ぞようこそ


PS私のプログラムのコンパむルされた゜ヌスは、その断片は以䞋にありたすが、 githubで入手できたす。 この蚘事の゜ヌスがありたす。 すべおのコヌドは、カヌネルバヌゞョン4.13.0-32-genericを䜿甚したデスクトップUbuntu 16.04 LTSで䜜成およびテストされおいたす。 モゞュヌルで䜿甚したむンタヌフェむスのいく぀かは4぀のカヌネルバヌゞョンで導入されたため、3。カヌネル*間違いなくコンパむルされたせん 。




[1] Crypto APIアヌキテクチャ


コヌドを曞き始める前に、 Crypto APIですべおがどのように機胜するかに぀いお少し話したしょう。 しかし、それらは私の前のドキュメントで詳现に説明されおいたしたが、私は順番に、材料を理解するのに十分なはずである圧搟を䞎えるだけです。 すぐに予玄したす。その埌、䞻に察称暗号化、他のタむプの暗号倉換を䞻に凊理したすが、䞀般的には同様に機胜したすが、倚くのニュアンスがすべおを非垞に耇雑にしたす。


したがっお、導入から、カヌネルの暗号化サブシステムには暗号化アルゎリズムの倚くの実装が含たれおおり、ナヌザヌがアクセスできるむンタヌフェヌスを提䟛しおいるこずがすでにわかっおいたす以䞋、ナヌザヌず開発者はそれぞれAPIナヌザヌずAPI開発 者を意味したす 。 Crypto APIに関しおは、アルゎリズムは「倉換」ず呌ばれるため、さたざたなタむプの倉換には独自のサブAPIがあり、倉換蚘述子ハンドルには通垞「 tfm 」ずいう名前が付けられたす。


struct crypto_cipher *tfm = crypto_alloc_cipher("aes", 0, 0); 

ここで、 tfmは䜕らかの倉換、この堎合はAESアルゎリズムを䜿甚したデヌタブロックの暗号化のハンドルです。 Crypto APIハンドルのラむフサむクルは3段階に短瞮されたす 。



䜿甚されるアルゎリズムに関係なく、 1぀のデヌタブロックの基本的な暗号化機胜の実装は、 crypto_cipherタむプのハンドルを介しお利甚できたす。察応するAPI  シングルブロック暗号API は、キヌを蚭定し、ブロックを暗号化/埩号化するためのメ゜ッドを提䟛したす。 独立したAPIであるため、ナヌザヌにずっお倧きな関心はありたせんが、開発者の芳点からは、 Crypto APIの重芁な抂念、いわゆる「テンプレヌト」を䜿甚するため、重芁です。 テンプレヌトは基本機胜ず組み合わせお䜿甚​​され、以䞋を実装したす。



ハンドルを䜜成するずき䞊蚘の䟋を参照、必芁なアルゎリズムは行で指定されたす-アルゎリズムの名前。この行には次のセマンティクスがありたす。


 template(single block cipher/message digest) 

さらに、必芁に応じお、テンプレヌトを他のテンプレヌトに「ラップ」できたす。


 template_a(template_b(single block cipher/message digest)) 

ただし、基本的なアルゎリズムなしでテンプレヌトを䜿甚するこずはできたせん。 いく぀か䟋を挙げたす。



任意のモヌドでの察称ブロック暗号の䜿甚は、別個のAPIを介しお実行されたす 。 カヌネルにはこのようなAPIが3぀ありたすが、そのうち2぀は非掚奚であり、 Symmetric Key Cipher APIが関連しおいるこずに泚意しおください。 このAPIは、任意の長さのキヌ/同期および暗号化/埩号化のデヌタを蚭定するメ゜ッドをナヌザヌに提䟛したす。たた、 非ブロッキング 非同期です。暗号化メ゜ッドは、通知に䜿甚される暗号操䜜ずコヌルバック関数のリク゚ストを呌び出し元にできるだけ早く制埡を返したす操䜜の完了に関するナヌザヌは、システムスケゞュヌラに送信されたす。 Symmetric Key Cipher APIは、組み蟌みアルゎリズムをテストするカヌネルモゞュヌルの䜜成に進むずきに詳现に怜蚎されたす。


2぀の名前に加えお、 Crypto APIには次のサブAPIがありたす 。



開発者にずっお、 Crypto APIにテンプレヌトが存圚するずいうこずは、新しい察称暗号化アルゎリズムを組み蟌むこずで、基本的なブロック暗号化機胜のみを実装すれば十分であり、察応するテンプレヌトにカプセル化しお必芁なモヌドを取埗できるこずを意味したす。 しかし、これは完党に真実ではありたせん。 実際、カヌネルに含たれるアルゎリズムの゜ヌスコヌド crypto / aes_generic.c 、 crypto / blowfish_generic.c 、...を芋るず、基本的な機胜のみが実装されおいるこずがわかりたす。 ただし、同じ方法で埓来のGOST 28147-89ブロックの暗号化機胜を実装し、それをゲヌミングモヌド CTRテンプレヌトで「ラップ」し、テストシヌケンスで結果のアルゎリズムをチェックするず、間違った結果が埗られたす。 問題は、 GOSTで説明されおいるガンマモヌドが、 CTRテンプレヌトに実装されおいるガンマアルゎリズムず異なるこずです。 私が扱った他の囜家アルゎリズムに぀いおも同じこずが蚀えたす。 そのような堎合、たずえばAESアルゎリズムの最適化された実装 AES-NI - arch / x86 / crypto / aesni-intel_glue.c で行われおいるように、必芁なモヌドで本栌的なブロック暗号を埋め蟌む必芁がありたす。 埌で、埋め蟌みの䞡方のオプションを怜蚎したす。


おそらくこれが私が建築に぀いお蚀いたかったこずのすべおです。 より詳现な説明に興味がある人は、 ドキュメントを参照する必芁があり、私のプレれンテヌションは私たちが先に進むこずができるように十分でなければなりたせん。




[2]土壌の準備


したがっお、 Crpyto APIにいく぀かの新しいアルゎリズムを組み蟌む準備が敎いたした。統合できるアルゎリズムはただないずいう譊告がありたす。 この蚘事では、「実際の」暗号化アルゎリズムを実装するこずを気にしたせんでした。定性的に行うこずはほずんどできなかったからです暗号䜜成者に任せおください。 䞀方、 ヌル暗号を䜜成するこずは、特にカヌネルにすでにあるため、それほど興味深いものではありたせん。 したがっお、基本暗号化アルゎリズムの実装を劥協しお蚘述したす。




どこで



このアルゎリズムのブロックずキヌのサむズは、128ビット16バむトに等しいず想定されおいたす。


実装を開始できたす。 アルゎリズムの暗号コンテキストず、コンテキストを䜜成/砎棄する機胜を定矩したす。


 #define XOR_CIPHER_KEY_SIZE 16 typedef struct xor_cipher_ctx xor_cipher_ctx; struct xor_cipher_ctx { uint8_t key[XOR_CIPHER_KEY_SIZE]; }; xor_cipher_ctx* xor_cipher_allocate() { xor_cipher_ctx *cipher = calloc(1, sizeof(xor_cipher_ctx)); return cipher; } void xor_cipher_free(xor_cipher_ctx *ctx) { memset(ctx->key, 0xFF, XOR_CIPHER_KEY_SIZE); free(ctx); } 

キヌを蚭定し、ブロックを暗号化/埩号化するメ゜ッドを远加したす。


 #define XOR_CIPHER_BLOCK_SIZE 16 void xor_cipher_set_key(xor_cipher_ctx *ctx, uint8_t *key) { memmove(ctx->m_key, key, XOR_CIPHER_KEY_SIZE); } void xor_cipher_crypt_block(xor_cipher_ctx *ctx, uint8_t *dst, uint8_t *src) { for (int i = 0; i < XOR_CIPHER_BLOCK_SIZE; i++) { dst[i] = src[i] ^ ctx->key[i]; } } 

XOR操䜜の可逆性を考えるず、 xor_cipher_crypt_blockメ゜ッドxor_cipher_crypt_block䜿甚しお暗号化ず埩号化を同時に行いたす。


ブロック暗号化は優れおいたすが、ブロック暗号化モヌドの䞀郚、たずえば暗号ブロックブロック結合モヌド CBC を実装するずさらに良くなりたす 。





どこで



匕き続き動䜜したす。暗号のブロックを結合するモヌドで暗号化および埩号化のメ゜ッドを実装したす。


暗号化
 void xor_cipher_encrypt_cbc(xor_cipher_ctx *ctx, uint8_t *_dst, uint32_t len, uint8_t *_src, uint8_t *_iv) { uint32_t blocks = len / XOR_CIPHER_BLOCK_SIZE; uint32_t leftover = len - (blocks * XOR_CIPHER_BLOCK_SIZE); uint8_t *dst = _dst, *src = _src, *iv = _iv; for (uint32_t i = 0; i < blocks; i++) { memmove(dst, src, XOR_CIPHER_BLOCK_SIZE); for (int j = 0; j < XOR_CIPHER_BLOCK_SIZE; j++) { dst[j] ^= iv[j]; } xor_cipher_crypt_block(ctx, dst, dst); iv = dst; dst += XOR_CIPHER_BLOCK_SIZE; src += XOR_CIPHER_BLOCK_SIZE; } if (leftover) { memmove(dst, src, leftover); for (uint32_t i = 0; i < leftover; i++) { dst[i] ^= iv[i]; dst[i] ^= ctx->key[i]; } } } 

解読
 void xor_cipher_decrypt_cbc(xor_cipher_ctx *ctx, uint8_t *_dst, uint32_t len, uint8_t *_src, uint8_t *_iv) { uint32_t blocks = len / XOR_CIPHER_BLOCK_SIZE; uint32_t leftover = len - (blocks * XOR_CIPHER_BLOCK_SIZE); uint8_t u[XOR_CIPHER_BLOCK_SIZE], iv[XOR_CIPHER_IV_SIZE]; uint8_t *dst = _dst, *src = _src; memmove(iv, _iv, XOR_CIPHER_IV_SIZE); for (uint32_t i = 0; i < blocks; i++) { memmove(u, src, XOR_CIPHER_BLOCK_SIZE); xor_cipher_crypt_block(ctx, dst, src); for (int j = 0; j < XOR_CIPHER_BLOCK_SIZE; j++) { dst[j] ^= iv[j]; } memmove(iv, u, XOR_CIPHER_IV_SIZE); dst += XOR_CIPHER_BLOCK_SIZE; src += XOR_CIPHER_BLOCK_SIZE; } if (leftover) { for (uint32_t i = 0; i < leftover; i++) { dst[i] = src[i] ^ ctx->key[i]; dst[i] ^= iv[i]; } } } 

これはすでにもっず面癜いです これで、この実装を䜿甚しお、テストシヌケンスを準備できたす。テストシヌケンスでは、将来、カヌネル内の同じアルゎリズムの実装を確認したす。 ネタバレの䞋に䜿甚した倀がありたす。


テストシヌケンス
ブロック暗号化
キヌ2f 1b 1a c6 d1 be cb a2 f8 45 66 0d d2 97 5c a3
テスト番号1
入力デヌタcc 6b 79 0c db 55 4f e5 a0 69 05 96 11 be 8c 15
むンプリントe3 70 63 ca 0a eb 84 47 58 2c 63 9b c3 29 d0 b6
テスト番号2
入力デヌタ53 f5 f1 ef 67 a5 ba 6c 68 09 b5 7a 24 de 82 5f
むンプリント7c ee eb 29 b6 1b 71 ce 90 4c d3 77 f6 49 de fc

CBC暗号化
キヌec 8d 93 30 69 7e f8 63 0b f5 58 ec de 78 24 f2
同期パッケヌゞdb 02 1f a8 5a 22 15 cf 49 f7 80 8b 7c 24 a1 f3
平文6e 96 50 42 84 d2 7e e8 44 9b 75 1d e0 ac 0a 58 ee 40 24 cc 32 fc 6e c4 e2 fc d1 f5 76 6a 45 9a e4 88 ba d6 12 07 28 86
暗号文59 19 dc da b7 8e 93 44 06 99 ad 7a 42 f0 8f 59 5b d4 6b 26 ec 0c 05 e3 ef 90 24 63 ea e2 ee 31 53 d1 42 c0 97 75 d5 06

CBC埩号化
キヌec 8d 93 30 69 7e f8 63 0b f5 58 ec de 78 24 f2
同期パッケヌゞdb 02 1f a8 5a 22 15 cf 49 f7 80 8b 7c 24 a1 f3
暗号文db e9 1d c6 1f 13 1a 5a 34 2b 90 1e c3 b1 6f e9 52 1b 91 7f 8d 8f 6d b4 42 87 ad 85 5f 2d 89 7d
平文ec 66 91 5e 2c 4f f7 f6 76 29 48 79 61 ed ea e8 65 7f 1f 89 fb e2 8f 8d 7d 59 65 77 42 e4 c2 66

アルゎリズムずテストプログラムの実装の゜ヌスコヌドはここから入手できたす 。 カヌネルスペヌスに行く準備はほが敎っおいたすが 、その前に重芁なポむントに泚意を向けたいず思いたす。 実際、倚くの有名な暗号化アルゎリズムは、入力デヌタの最埌の䞍完党なブロックの凊理を必芁ずしたせん。 したがっお、たずえば、入力デヌタのサむズがブロックサむズの倍数でない堎合、 GOST 28147-89アルゎリズムの実装ぱラヌフラグを返さなければなりたせんが 、 ベルトベラルヌシ語はそのような凊理を提䟛したす。 私のアルゎリズムもそれを提䟛したすクロックパッケヌゞのキヌず珟圚の倀は、䞍完党なブロックのサむズに切り捚おられたす。 この事実は少し埌で圹割を果たしたすが、あなたはこれを芚えおおく必芁がありたす。




[3]コアぞの浞挬


アクションはカヌネル空間に転送されたす。 カヌネルでのプログラミングは、ナヌザヌ空間でのプログラミングずは倚少異なり、デバッグの耇雑さのために、かなり時間がかかるプロセスです。 ただし、この資料に取り組んでいる間、私は読者にカヌネルモゞュヌルのプログラミングの基本を理解するずいうタスクを蚭定したせんでした。これは、 Habré  more ; and more など、私なしでこれに関する倚くの情報があるためです。 したがっお、読者がモゞュヌルの䜜成に完党に慣れおいない堎合は、最初に䞊蚘で匕甚した資料を確認するか、自分で確認するこずをお勧めしたすこれには時間がかかりたせん。 そしお、さらに先ぞ進む準備ができおいる人たちず䞀緒に、 Crypto APIを深く研究し始めたす 。


それで、アルゎリズムがあり、それをカヌネルに埋め蟌みたいのですが、どこから始めればいいのでしょうか もちろん、ドキュメント付き。 残念なこずに、 アルゎリズムの開発/埋め蟌みに関するセクションでは、このプロセスに関する非垞に䞀般的な知識しか埗られたせんが、少なくずも正しい方向に進むには圹立ちたす。 具䜓的には、カヌネル内のアルゎリズムの登録および登録解陀に関䞎する関数の存圚に぀いお孊習したす。 理解したしょう


 /* include/linux/crypto.h */ int crypto_register_alg(struct crypto_alg *alg); int crypto_register_algs(struct crypto_alg *algs, int count); int crypto_unregister_alg(struct crypto_alg *alg); int crypto_unregister_algs(struct crypto_alg *algs, int count); 

これらの関数は、゚ラヌの堎合は負の倀を返し、成功した堎合は0を返したす。登録されたアルゎリズムは、 crypto_alg構造䜓によっお蚘述されたすその定矩 include / linux / crypto.h を参照 。


 /* include/linux/crypto.h */ struct crypto_alg { struct list_head cra_list; struct list_head cra_users; u32 cra_flags; unsigned int cra_blocksize; unsigned int cra_ctxsize; unsigned int cra_alignmask; int cra_priority; atomic_t cra_refcnt; char cra_name[CRYPTO_MAX_ALG_NAME]; char cra_driver_name[CRYPTO_MAX_ALG_NAME]; const struct crypto_type *cra_type; union { struct ablkcipher_alg ablkcipher; struct blkcipher_alg blkcipher; struct cipher_alg cipher; struct compress_alg compress; } cra_u; int (*cra_init)(struct crypto_tfm *tfm); void (*cra_exit)(struct crypto_tfm *tfm); void (*cra_destroy)(struct crypto_alg *alg); struct module *cra_module; } CRYPTO_MINALIGN_ATTR; 

幞いなこずに、この構造は非垞によく文曞化されおおり、このフィヌルドたたはそのフィヌルドの倀を掚枬する必芁はありたせん。



文曞化されおいない残りのフィヌルドは内郚䜿甚のためのものであり、入力しないでください。 耇雑なこずは䜕もないようです。 たず、ブロック暗号化アルゎリズムの実装を組み蟌みたいので、 cra_uナニオンのcipher_alg構造を芋おみたしょう。


 /* include/linux/crypto.h */ struct cipher_alg { unsigned int cia_min_keysize; unsigned int cia_max_keysize; int (*cia_setkey)(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen); void (*cia_encrypt)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); void (*cia_decrypt)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); }; 

ここではただ簡単で、私には思えるので説明は䞍芁です。 これで、これらすべおが実際にどのように機胜するかを確認する準備ができたした。 ちなみに、 cipher_alg構造䜓の関数のシグネチャは 、2パヌトアルゎリズムのAPIの関数のシグネチャに䌌おいるこずに泚意しおください。


カヌネルモゞュヌルを䜜成したす。 眲名cipher_alg.cia_setkeyに埓っお、 cipher_alg.cia_setkeyずキヌむンストヌル関数を決定したす。


 #define XOR_CIPHER_KEY_SIZE 16 struct xor_cipher_ctx { u8 key[XOR_CIPHER_KEY_SIZE]; }; static int xor_cipher_setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int len) { struct xor_cipher_ctx *ctx = crypto_tfm_ctx(tfm); u32 *flags = &tfm->crt_flags; if (len != XOR_CIPHER_KEY_SIZE) { *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; } memmove(ctx->key, key, XOR_CIPHER_KEY_SIZE); return 0; } 

アルゎリズムのAPIでは、cryptocontextが関数に盎接枡されたした。ここで、関数はtfmアルゎリズムのハンドルをtfmたすtfmアルゎリズムから、 crypto_tfm_ctx関数を䜿甚しおコンテキストが抜出されたす。 たた、ここでは、送信されたキヌの長さを確認したす。 長さが正しくない堎合、察応するフラグ CRYPTO_TFM_RES_BAD_KEY_LEN を蚭定し、 EINVALコヌド22 Invalid argument を返したす 。


次に、 cipher_alg.cia_encryptに埓っおブロック暗号化関数を定矩したす。


 static void xor_cipher_crypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) { struct xor_cipher_ctx *ctx = crypto_tfm_ctx(tfm); int i; for (i = 0; i < XOR_CIPHER_BLOCK_SIZE; i++) { out[i] = in[i] ^ ctx->key[i]; } } 

ここに新しいものはありたせん。 元のAPIのように、埩号化のための远加の関数は定矩せず、 cipher_alg.cia_decryptポむンタヌをcipher_alg.cia_decrypt関数で初期化したす。


最埌に、モゞュヌルのロヌド埌およびアンロヌド前に、 crypto_alg構造䜓のむンスタンスを定矩し、それを入力しお、アルゎリズムの登録および登録crypto_alg関数をそれぞれ呌び出したす。


 static struct crypto_alg xor_cipher = { .cra_name = "xor-cipher", .cra_driver_name = "xor-cipher-generic", .cra_priority = 100, .cra_flags = CRYPTO_ALG_TYPE_CIPHER, .cra_blocksize = XOR_CIPHER_BLOCK_SIZE, .cra_ctxsize = sizeof(struct xor_cipher_ctx), .cra_module = THIS_MODULE, .cra_u = { .cipher = { .cia_min_keysize = XOR_CIPHER_KEY_SIZE, .cia_max_keysize = XOR_CIPHER_KEY_SIZE, .cia_setkey = xor_cipher_setkey, .cia_encrypt = xor_cipher_crypt, .cia_decrypt = xor_cipher_crypt } } }; static int __init xor_cipher_init(void) { return crypto_register_alg(&xor_cipher); } static void __exit xor_cipher_exit(void) { crypto_unregister_alg(&xor_cipher); } 

䞊蚘のすべおを考慮するず、ここで疑問が生じるこずはありたせん。 そのようなモゞュヌルをコンパむルおよびダりンロヌドした埌、タヌミナルでコマンド「 cat /proc/crypto 」を実行するず、登録枈みアルゎリズムのリストで独自のアルゎリズムを芋぀けるこずができたす。


 name : xor-cipher driver : xor-cipher-generic module : xor_cipher priority : 100 refcnt : 1 selftest : passed internal : no type : cipher blocksize : 16 min keysize : 16 max keysize : 16 

悪くないでしょ しかし、これはほんの始たりに過ぎず、理論的には、アルゎリズムをテストするモゞュヌルの䜜成に進む必芁がありたしたが、それでも、埋め蟌みず䜿甚の問題を混同しないこずにしたした。 したがっお、埌でテストしたす。次のパヌトでは、暗号のブロックを結合するモヌドでアルゎリズムの実装をどのように組み蟌むこずができるかを説明したす。




[3.1]コアぞの「もう少し深い」浞挬


少し先に走りたす。 泚意深い読者は、パタヌンの存圚を芚えおいたす。 したがっお、最埌の郚分でブロック暗号化を実装するず、この実装をCBCテンプレヌトで簡単にラップし、暗号ブロックをブロックするモヌドでアルゎリズムの実装を取埗できたすが、シヌケンスでテストするこずにより、 CBCモヌドで暗号化を呌び出すず゚ラヌコヌド22 EINVAL  次に、入力デヌタの䞍完党なブロックずcrypto_alg.cra_blocksizeフィヌルドの凊理に぀いお蚀ったこずを思い出したす。 実際、 CBCカヌネルモヌドの実装には、䞍完党なブロックを凊理する方法がわかりたせん。 さらに、アルゎリズムをCBCモヌドに倉曎するず、カヌネルは新しいアルゎリズムを登録したす。そのブロックサむズは、基本アルゎリズムのブロックサむズず同じです。 cbcxor-cipherアルゎリズムを䜿甚しお「暗号化」暗号化関数を呌び出した埌、入力デヌタのサむズはブロックサむズによっお倚重床がチェックされ、耇数でない堎合、関数はEINVAL返したす。 CBCモヌドでの暗号化のテストベクトルのサむズ40バむトは、ブロックサむズの倍数ではないように意図的に遞択されおいたす。 私の意芋では、暗号化暙準が䞍完党なブロックの凊理を提䟛し、実装がそうしない堎合、そのような実装は、倚重床条件が満たされたずきに実装が正しい結果を䞎えたずしおも、暙準ぞの準拠のチェックに合栌する可胜性は䜎いですこの堎合、これはそうです。 したがっお、ここで、アルゎリズムの暗号ブロックの結合モヌドを完党に実装したす。 2幎前にこれを行ったずき、今では時代遅れのブロッキング察称暗号化 APIを介しおそれらを䜿甚するこずを期埅しおアルゎリズムを組み蟌みたしたが、圓時は非ブロッキングAPIが望たしいず考えられおいたしたが、これも今では時代遅れです。 Symmetric Key Cipher API .


, , , , Crypto API , . , ( arch/x86/crypto/aesni-intel_glue.c ), , , . , / :


 /* include/crypto/internal/skcipher.h */ int crypto_register_skcipher(struct skcipher_alg *alg); int crypto_register_skciphers(struct skcipher_alg *algs, int count); void crypto_unregister_skcipher(struct skcipher_alg *alg); void crypto_unregister_skciphers(struct skcipher_alg *algs, int count); 

skcipher_alg :


 /* include/crypto/skcipher.h */ struct skcipher_alg { int (*setkey)(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen); int (*encrypt)(struct skcipher_request *req); int (*decrypt)(struct skcipher_request *req); int (*init)(struct crypto_skcipher *tfm); void (*exit)(struct crypto_skcipher *tfm); unsigned int min_keysize; unsigned int max_keysize; unsigned int ivsize; unsigned int chunksize; unsigned int walksize; struct crypto_alg base; }; 

, include/crypto/skcipher.h . , struct crypto_alg , , , init exit , crypto_alg . , , chunksize walksize :



encrypt decrypt . skcipher_request , : / , . , - API , , , , .


, Crypto API . , - API / ( API ), scatterlist . , , Synchronous Block Cipher API :


 /* include/linux/crypto.h */ int crypto_blkcipher_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes); 

skcipher_request struct scatterlist *src struct scatterlist *dst . scatterlist :


 /* include/linux/scatterlist.h */ struct scatterlist { /* ... */ unsigned long page_link; unsigned int offset; unsigned int length; /* ... */ }; 

. , sg_init_one :


 /* include/linux/scatterlist.h */ void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen); 

:


, "" buf ( page_link ), buf ( offset ).

, . , :


 struct crypto_blkcipher *tfm; struct blkcipher_dest desc; struct scatterlist sg[2]; u8 *first_segment, *second_segment; /* crypto and data allocation... */ sg_init_table(sg, 2); sg_set_buf(&sg[0], first_segment, len); sg_set_buf(&sg[1], second_segment, len); crypto_blkcipher_encrypt(&desc, &sg, &sg, 2*len); 

, first_segment second_segment , . , Crypto API ( ) "" scatterlist -, "" ( scattered ) . Crypto API IPsec :


One of the initial goals of this design was to readily support IPsec, so that processing can be applied to paged skb's without the need for linearization.

, scatterlist API / ( Direct Memory Access , DMA I/O ). / , "" , , , .


. , :


 static int xor_skcipher_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int len) { return xor_cipher_setkey(crypto_skcipher_tfm(tfm), key, len); } 

. CBC ( struct xor_cipher_ctx ), , , .


.


 static int cbc_encrypt(struct skcipher_request *req) { struct crypto_tfm *tfm = crypto_skcipher_tfm(crypto_skcipher_reqtfm(req)); struct xor_cipher_ctx *ctx = crypto_tfm_ctx(tfm); struct skcipher_walk walk; u32 nbytes; int i, blocks; u8 *src, *dst, *iv; skcipher_walk_virt(&walk, req, true); iv = walk.iv; while ((nbytes = walk.nbytes) >= XOR_CIPHER_BLOCK_SIZE) { src = (u8*)walk.src.virt.addr; dst = (u8*)walk.dst.virt.addr; blocks = nbytes / XOR_CIPHER_BLOCK_SIZE; while (blocks) { for (i = 0; i < XOR_CIPHER_BLOCK_SIZE; i++) { dst[i] = src[i] ^ iv[i]; } xor_cipher_crypt(tfm, dst, dst); iv = dst; src += XOR_CIPHER_BLOCK_SIZE; dst += XOR_CIPHER_BLOCK_SIZE; blocks--; } nbytes &= XOR_CIPHER_BLOCK_SIZE - 1; skcipher_walk_done(&walk, nbytes); } if ((nbytes = walk.nbytes)) { src = (u8*)walk.src.virt.addr; dst = (u8*)walk.dst.virt.addr; for (i = 0; i < nbytes; i++) { dst[i] = src[i] ^ iv[i]; dst[i] ^= ctx->key[i]; } skcipher_walk_done(&walk, 0); } return 0; } 

 #define XOR_CIPHER_IV_SIZE 16 static int cbc_decrypt(struct skcipher_request *req) { struct crypto_tfm *tfm = crypto_skcipher_tfm(crypto_skcipher_reqtfm(req)); struct xor_cipher_ctx *ctx = crypto_tfm_ctx(tfm); struct skcipher_walk walk; u8 u[XOR_CIPHER_BLOCK_SIZE], iv[XOR_CIPHER_BLOCK_SIZE]; u32 nbytes; int i, blocks; u8 *src, *dst; skcipher_walk_virt(&walk, req, true); memmove(iv, walk.iv, XOR_CIPHER_IV_SIZE); while ((nbytes = walk.nbytes) >= XOR_CIPHER_BLOCK_SIZE) { src = (u8*)walk.src.virt.addr; dst = (u8*)walk.dst.virt.addr; blocks = nbytes / XOR_CIPHER_BLOCK_SIZE; while (blocks) { memmove(u, src, XOR_CIPHER_BLOCK_SIZE); xor_cipher_crypt(tfm, dst, src); for (i = 0; i < XOR_CIPHER_BLOCK_SIZE; i++) { dst[i] ^= iv[i]; } memmove(iv, u, XOR_CIPHER_IV_SIZE); dst += XOR_CIPHER_BLOCK_SIZE; src += XOR_CIPHER_BLOCK_SIZE; blocks--; } nbytes &= XOR_CIPHER_BLOCK_SIZE - 1; skcipher_walk_done(&walk, nbytes); } if ((nbytes = walk.nbytes)) { src = (u8*)walk.src.virt.addr; dst = (u8*)walk.dst.virt.addr; for (i = 0; i < nbytes; i++) { dst[i] = src[i] ^ ctx->key[i]; dst[i] ^= iv[i]; } skcipher_walk_done(&walk, 0); } return 0; } 

, , , "" skcipher_walk . , skcipher_walk skcipher_walk_virt , skcipher_walk_done "" / ( walk.src.virt.addr walk.dst.virt.addr ) walk.nbytes , scatterlist -.


skcipher_alg , / :


 static struct skcipher_alg cbc_xor_cipher = { .base = { .cra_name = "cbc(xor-cipher)", .cra_driver_name = "cbc-xor-cipher", .cra_priority = 400, .cra_flags = CRYPTO_ALG_ASYNC, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct xor_cipher_ctx), .cra_module = THIS_MODULE, }, .min_keysize = XOR_CIPHER_KEY_SIZE, .max_keysize = XOR_CIPHER_KEY_SIZE, .ivsize = XOR_CIPHER_IV_SIZE, .setkey = xor_skcipher_setkey, .encrypt = cbc_encrypt, .decrypt = cbc_decrypt, .chunksize = XOR_CIPHER_BLOCK_SIZE, }; static int __init xor_cipher_init(void) { crypto_register_alg(&xor_cipher); return crypto_register_skcipher(&cbc_xor_cipher); } static void __exit xor_cipher_exit(void) { crypto_unregister_alg(&xor_cipher); crypto_unregister_skcipher(&cbc_xor_cipher); } 

cra_blocksize . , , skcipher_walk_done "" , .


, /proc/crypto :


 name : cbc(xor-cipher) driver : cbc-xor-cipher module : xor_cipher priority : 400 refcnt : 1 selftest : passed internal : no type : skcipher async : yes blocksize : 1 min keysize : 16 max keysize : 16 ivsize : 16 chunksize : 16 

( ) : " xor-cipher " ( ) " cbc(xor-cipher) " ( ). , , . , ( arch/x86/crypto ), .




[4]


, , "", , , , . , , — , : , , Crypto API . , , , .


. cipher_testvec_t :


 typedef enum test_t { TEST_BLK_ENCRYPT = 0, TEST_BLK_DECRYPT, TEST_CBC_ENCRYPT, TEST_CBC_DECRYPT, TEST_END, } test_t; struct cipher_testvec_t { test_t test; u32 len; char *key; char *iv; char *in; char *result; }; 

: in , len , ( .test = TEST_BLK_* ) ( .test = TEST_CBC_* ), key , , iv . result . TEST_END cipher_testvec_t .


— :


 static int test_blk(cipher_testvec_t *testvec) { struct crypto_cipher *tfm = NULL; int encrypt = (testvec->test == TEST_BLK_ENCRYPT) ? 1 : 0; u8 dst[16]; tfm = crypto_alloc_cipher("xor-cipher", 0, 0); if (IS_ERR(tfm)) { pr_err("error allocating xor-cipher: %ld\n", PTR_ERR(tfm)); return 0; } crypto_cipher_setkey(tfm, (u8*)testvec->key, 16); if (encrypt) { crypto_cipher_encrypt_one(tfm, dst, (u8*)testvec->in); } else { crypto_cipher_decrypt_one(tfm, dst, (u8*)testvec->in); } crypto_free_cipher(tfm); if (memcmp(dst, testvec->result, 16)) { pr_err("block %sciphering test failed!\n", encrypt ? "" : "de"); dumpb((u8*)testvec->key, 16, "key"); dumpb((u8*)testvec->in, 16, "in"); dumpb(dst, 16, "result"); dumpb((u8*)testvec->result, 16, "should be"); return 0; } return 1; } 

, Single Block Cipher API . , crypto_alloc_cipher , " xor-cpher ". , ( crypto_cipher_setkey ), , , ( crypto_cipher_encrypt_one ) ( crypto_cipher_decrypt_one ) . , ( crypto_free_cipher ) .


CBC .


CBC
 static int test_cbc(cipher_testvec_t *testvec) { struct scatterlist sg; struct cb_data_t cb_data; struct crypto_skcipher *tfm = NULL; struct skcipher_request *req = NULL; int encrypt = (testvec->test == TEST_CBC_ENCRYPT) ? 1 : 0; u32 err; u8 *buf = NULL; tfm = crypto_alloc_skcipher("cbc-xor-cipher", 0, 0); if (IS_ERR(tfm)) { pr_err("error allocating cbc-xor-cipher: %ld\n", PTR_ERR(tfm)); goto exit; } req = skcipher_request_alloc(tfm, GFP_KERNEL); if (!req) { pr_err("error allocating skcipher request\n"); goto exit; } buf = kmalloc(testvec->len, GFP_KERNEL); if (!buf) { pr_err("memory allocation error\n"); goto exit; } memmove(buf, (u8*)testvec->in, testvec->len); sg_init_one(&sg, buf, testvec->len); crypto_skcipher_setkey(tfm, (u8*)testvec->key, 16); skcipher_request_set_crypt(req, &sg, &sg, testvec->len, (u8*)testvec->iv); skcipher_request_set_callback(req, 0, skcipher_cb, &cb_data); init_completion(&cb_data.completion); err = (encrypt) ? crypto_skcipher_encrypt(req) : crypto_skcipher_decrypt(req); switch (err) { case 0: break; case -EINPROGRESS: case -EBUSY: wait_for_completion(&cb_data.completion); err = cb_data.err; if (!err) { break; } default: pr_err("failed with error: %d\n", err); goto exit; } if (memcmp(buf, testvec->result, testvec->len)) { pr_err("cbc %sciphering test failed!\n", encrypt ? "" : "de"); dumpb((u8*)testvec->key, 16, "key"); dumpb((u8*)testvec->iv, 16, "iv"); dumpb((u8*)testvec->in, testvec->len, "in"); dumpb(buf, testvec->len, "result"); dumpb((u8*)testvec->result, testvec->len, "should be"); goto exit; } skcipher_request_free(req); crypto_free_skcipher(tfm); kfree(buf); return 1; exit: if (buf) { kfree(buf); } if (req) { skcipher_request_free(req); } if (tfm) { crypto_free_skcipher(tfm); } return 0; } 

: API ( Symmetric Key Cipher API ), .


test_cbc :



. API , , , . , , , . skcipher_request_set_callback . cb_data_t , ( skcipher_cb ) :


 struct cb_data_t { struct completion completion; int err; }; static void skcipher_cb(struct crypto_async_request *req, int error) { struct cb_data_t *data = req->data; if (error == -EINPROGRESS) { return; } data->err = error; complete(&data->completion); } 

completion test_cbc , . , , test_cbc . , test_cbc , :



switch -a . . , ( , ), - :


 insmod: ERROR: could not insert module xor_cipher_testing.ko: Operation not permitted 

( dmesg ) :


 [---] done 4 tests, passed: 4, failed: 0 

, SK Cipher API , . — "", Asynchronous Block Cipher API , . -, -, , .




[4.1] user-space


, . , , , , , , Linux . libkcapi , Crypto API ( ). "" Netlink -, "" Netlink API . , , , , , ( 1.0.3).


, . , , , ( ), Crypto API . af_alg Strongswan , .


, . , , . , , . , test_cbc .


CBC user-space
 static int test_cbc(cipher_testvec_t *testvec) { uint8_t dst[testvec->len]; int encrypt = (testvec->test == TEST_CBC_ENCRYPT) ? 1 : 0; struct af_alg_skcipher *tfm = NULL; tfm = af_alg_allocate_skcipher("cbc-xor-cipher"); if (!tfm) { fprintf(stderr, "error allocating \"cbc-xor-cipher\"\n"); goto err; } if (!af_alg_skcipher_setkey(tfm, (uint8_t*)testvec->key, XOR_CIPHER_KEY_SIZE)) { fprintf(stderr, "can't set \"cbc-xor-cipher\" key\n"); goto err; } if (!af_alg_skcipher_crypt(tfm, encrypt, dst, testvec->len, (uint8_t*)testvec->in, (uint8_t*)testvec->iv, XOR_CIPHER_IV_SIZE)) { goto err; } af_alg_free_skcipher(tfm); if (memcmp(dst, (uint8_t*)testvec->result, testvec->len)) { fprintf(stderr, "cbc %sciphering test failed!\n", encrypt ? "" : "de"); dumpb((uint8_t*)testvec->key, XOR_CIPHER_KEY_SIZE, "key"); dumpb((uint8_t*)testvec->iv, XOR_CIPHER_IV_SIZE, "iv"); dumpb((uint8_t*)testvec->in, testvec->len, "in"); dumpb(dst, testvec->len, "result"); dumpb((uint8_t*)testvec->result, testvec->len, "should be"); return 0; } return 1; err: if (tfm) { af_alg_free_skcipher(tfm); } return 0; } 

. , , , , , . af_alg_skcipher af_alg_* . .


 struct af_alg_skcipher { int sockfd; }; static struct af_alg_skcipher* af_alg_allocate_skcipher(char *name) { struct af_alg_skcipher *tfm = NULL; struct sockaddr_alg sa = { .salg_family = AF_ALG, .salg_type = "skcipher", }; strncpy((char*)sa.salg_name, name, sizeof(sa.salg_name)); tfm = calloc(1, sizeof(struct af_alg_skcipher)); if (!tfm) { errno = ENOMEM; goto err; } tfm->sockfd = socket(AF_ALG, SOCK_SEQPACKET, 0); if (tfm->sockfd == -1) { goto err; } if (bind(tfm->sockfd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { goto err; } return tfm; err: if (tfm->sockfd > 0) { close(tfm->sockfd); } if (tfm) { free(tfm); } return NULL; } 

:



socket , "" . AF_ALG Crypto API . AF_ALG , , sys/socket.h , , :


 #ifndef AF_ALG #define AF_ALG 38 #endif 


sockaddr_alg , bind :


 /* sys/socket.h */ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 

sockadr :


 struct sockaddr { sa_family_t sa_family; char sa_data[14]; } 

, , ( sa_family ), AF_ALG :


 /* linux/if_alg.h */ struct sockaddr_alg { __u16 salg_family; __u8 salg_type[14]; __u32 salg_feat; __u32 salg_mask; __u8 salg_name[64]; }; 

:



, bind -1, errno ENOENT .


, af_alg_allocate_skcipher , . .


:


 static int af_alg_skcipher_setkey(struct af_alg_skcipher *tfm, uint8_t *key, uint32_t keylen) { return (setsockopt(tfm->sockfd, SOL_ALG, ALG_SET_KEY, key, keylen) == -1) ? 0 : 1; } 

setsockopt , man - . , SOL_ALG ALG_SET_KEY , , sys/socket.h linux/af_alg.h , , , , :


 #ifndef SOL_ALG #define SOL_ALG 279 #endif #ifndef ALG_SET_KEY #define ALG_SET_KEY 1 #endif 

, .


 static int af_alg_skcipher_crypt(struct af_alg_skcipher *tfm, int encrypt, uint8_t *_dst, uint32_t _len, uint8_t *_src, uint8_t *iv, uint32_t ivlen) { int type = encrypt ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; struct msghdr msg = {}; struct cmsghdr *cmsg; struct af_alg_iv *ivm; struct iovec iov; char buf[CMSG_SPACE(sizeof(type)) + CMSG_SPACE(offsetof(struct af_alg_iv, iv) + ivlen)]; int op = 0; ssize_t len, remainig = _len; uint8_t *src = _src, *dst = _dst; op = accept(tfm->sockfd, NULL, 0); if (op == -1) { goto end; } memset(buf, 0, sizeof(buf)); /* fill in af_alg cipher controll data */ msg.msg_control = buf; msg.msg_controllen = sizeof(buf); /* operation type: encrypt or decrypt */ cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_ALG; cmsg->cmsg_type = ALG_SET_OP; cmsg->cmsg_len = CMSG_LEN(sizeof(type)); memmove(CMSG_DATA(cmsg), &type, sizeof(type)); /* initialization vector */ cmsg = CMSG_NXTHDR(&msg, cmsg); cmsg->cmsg_level = SOL_ALG; cmsg->cmsg_type = ALG_SET_IV; cmsg->cmsg_len = CMSG_LEN(offsetof(struct af_alg_iv, iv) + ivlen); ivm = (void*)CMSG_DATA(cmsg); ivm->ivlen = ivlen; memmove(ivm->iv, iv, ivlen); /* set data stream (scatter/gather list) */ msg.msg_iov = &iov; msg.msg_iovlen = 1; while (remainig) { iov.iov_base = src; iov.iov_len = remainig; len = sendmsg(op, &msg, 0); if (len == -1) { if (errno == EINTR) { continue; } goto end; } while (read(op, dst, len) != len) { if (errno != EINTR) { goto end; } } src += len; remainig -= len; /* no iv for subsequent data chunks */ msg.msg_controllen = 0; } /* done */ close(op); return 1; end: if (op > 0) { close(op); } return 0; } 

, . , , , , , . , 3 :



( ALG_SET_OP ): ( ALG_OP_ENCRYPT ) ( ALG_OP_DECRYPT ), ( ALG_SET_IV , af_alg_iv ). , , linux/if_alg.h , , , :


 #ifndef ALG_SET_IV #define ALG_SET_IV 2 #endif #ifndef ALG_SET_OP #define ALG_SET_OP 3 #endif #ifndef ALG_OP_DECRYPT #define ALG_OP_DECRYPT 0 #endif #ifndef ALG_OP_ENCRYPT #define ALG_OP_ENCRYPT 1 #endif 


以䞊です。 . , :


 done 2 tests, passed: 2, failed: 0 

— , .




[5] ?


, , , , , , , , . , , . .


— . , , , . , Crypto API . , - API , . , , , , .


, : , , , . , github . !





, "" , — . ! . , xor-cipher , , CBC , ( SK Cipher API , — ):


 struct crypto_skcipher *tfm = crypto_alloc_skcipher("cbc(xor-cipher)", 0, 0); 

xor-cipher , ( crypto/cbc.c ).


" ! — . — , cbc(xor-cipher) ?". " ", — . , .


, :



. "" . , " cbc ", :


 .cra_name = "xor-cipher"; .cra_driver_name = "xor-cipher-generic"; 

:


 .cra_name = "cbc(xor-cipher)"; .cra_driver_name = "cbc(xor-cipher-generic)"; 

, crypto_alg , , cra_driver_name , . , , cra_driver_name " cbc(xor-cipher-generic) ", :


 struct crypto_skcipher *tfm = crypto_alloc_skcipher("cbc(xor-cipher-generic)", 0, 0); 




, , , . , Crypto API , , .


tcrypt ( crypto/tcrypt.c ) . , crypto/tcrypt.c , , crypto/testmgr.c . , testmgr , ( ).


, tcrypt . "" tcrypt testmgr , Makefile , — tcryptext (tcrypt External) . .


tcryptext ( tcrypt , ), - , . ( README.md , ).


testmgr.c Crypto API .





, Linux-based . ( ) .


. " crypto " crypto/Kconfig Makefile . , crypto xor_cipher.c , crypto/Kconfig ( comment "Ciphers" ) :


 ... config CRYPTO_XOR_CIPHER tristate "XOR cipher algorithm" help Custom XOR cipher algorithm. ... 

crypto/Makefile :


 ... obj-$(CONFIG_CRYPTO_XOR_CIPHER) += xor_cipher.o ... 

( " Cryptographic API ") . , .


tcrypt testmgr .




䟿利なリンク


:



Git:




Source: https://habr.com/ru/post/J348552/


All Articles