暗号プラグインの実装方法

---
### StreamRelay.NET.exe の暗号機能を呼び出しているプラグインの実装方法について説明する

StreamRelay.NET.exe/StreamRelay.NET.x86.exeには、いくつかの機能をプラグインとして実装できるようにインターフェイスを公開している。

今回は、暗号機能のためのインターフェイスを使って、プラグインを作成する方法を記述する。

---
### 既存のプラグイン

ストリームを包んでデータを暗号化/復号するストリーム。

そんな感じ。

プラグイン的には、このあたり。

- Plugins\Plugin.Classless.Hasher.dll
- Plugins\Plugin.BouncyCastle.Crypto.dll
- Plugins\Plugin.CSharpCode.SharpZipLib

---
### 実装したプラグインの呼び出され方

プラグインで指定された引数を渡して、Streamを暗号で包んだりする。

という感じ。

---
### 実装するインターフェイス

以下のインターフェイスを継承したクラスを実装すればよい

- IPlugin.dll 中の jp.dip.rocketeer.Plugins.IPluginCipherEntity インターフェイス
- IPlugin.dll 中の jp.dip.rocketeer.Plugins.IPluginCipher インターフェイス

このクラス2つだけ実装すればよい。

---
### jp.dip.rocketeer.Plugins.IPluginCipher インターフェイス

StreamRelay.NET.exeや、他のプラグインから、引数/オプションの解析処理中に呼び出して、引数の指定内容によって、IPluginCipherEntityを返す。

あとは、IPluginCipherEntity インターフェイスのオブジェクトが暗号化/復号を実施する。

具体的には、-(Local|Remote)Proxy の netstreamスキーム指定時の、暗号化/復号の解析で
**IPluginCipherEntity CreateCipherEntity(Dictionary&lt;String, Object&gt; iList, Boolean IsInput,ref String errMsg)**
を呼び出すので、自分の引数であれば、IPluginCipherEntityオブジェクトを返してほしい。

---
#### jp.dip.rocketeer.Plugins.IPluginCipherインターフェイスで定義しているメソッド/プロパティ

実装する必要があるのは以下

jp.dip.rocketeer.Plugins.IPluginCipherインターフェイスで定義されているのは、以下のとおり

<dl>
 <dt><b>Dictionary&lt;String, String[]&gt; ListDescription(out String SubDescription)</b>
  <dd>usage の文章を返す。<BR>暗号プラグインの場合、「-ListCipher」オプションで、暗号プラグインの一覧を返すので、その説明文を返す。<BR>
<dl>
 <dt><b>SubDescription</b>
  <dd>「EncryptCipherAlgorithm」に与える引数の書式を格納してほしい。例えば私のツールだと「アルゴリズム名/モード/パディング」なので、「CipherName/CipherMode/CipherPadding」という文字列を格納している
 <dt><b>Dictionary&lt;String, String[]&gt;</b>
  <dd>書式ごとのオプションを返してほしい。<br>私の.NET Framework標準の暗号ライブラリを呼び出すプラグインの場合、
   <ol>
    <li>「CipherName」という名前で「AES」「DES」「RC2」「Rijndael」「TripleDES」という文字列配列
    <li>「CipherMode」という名前で「CBC」「ECB」「OFB」「CFB」「CTS」という文字列配列
    <li>「CipherPadding」という名前で「None」「PKCS7」「Zeros」「ANSIX923」「ISO10126」という文字列配列
   </ol>
</dl>
 <dt><b>jp.dip.rocketeer.Plugins.IPluginCipherEntity CreateCipherEntity(Dictionary&lt;String, Object&gt; iList, Boolean IsInput,ref String errMsg)</b>
  <dd>オプションに基づいて暗号化/復号のオブジェクトを生成する<br>
IsInput : 入力側か、出力側か<br>
errMsg : エラーが発生したら、これに書き込んでください<br>
iList : ディクショナリの配列で、名前(Key)にはオプションの名前、値にはオプションの値(Object型)が収められているので、それに応じた暗号化/復号のオブジェクトを生成してほしい。<br>
名前には、jp.dip.rocketeer.Plugins.CipherParametersにいくつか列挙している。<br>
<br>
現時点(2021/07/05)では、こんな感じ
<dl>
 <dt><B>Name</b>
  <dd>暗号アルゴリズム名(String)
 <dt><B>Mode</b>
  <dd>ブロック暗号方式のモードの名前(String)
 <dt><B>Padding</b>
  <dd>ブロック暗号方式のパディング方式の名前(String)
 <dt><B>IV</b>
  <dd>初期値(Initial Vector)(byte[])
 <dt><B>Password</b>
  <dd>共通鍵(SecretData(sCommonNET.dll))
 <dt><B>PasswordHash</b>
  <dd>共通鍵を攪拌するオブジェクト(jp.dip.rocketeer.Plugins.IPluginPasswordStirringEntity)
 <dt><B>Rounds</b>
  <dd>BouncyCastle の RC5(64Bit用)(int)
 <dt><B>macSize</b>
  <dd>macSize(byte), Mac用
 <dt><B>cfbSize</b>
  <dd>CFB Size(byte), Mac用
 <dt><B>Cipher</b>
  <dd>暗号オプション
 <dt><B>Nonce</b>
  <dd>Aeadモードの Nonce
 <dt><B>NonceHex</b>
  <dd>Aeadモードの Nonce
 <dt><B>AssociatedText</b>
  <dd>Aeadモードの AssociatedText
 <dt><B>AssociatedTextHex</b>
  <dd>Aeadモードの AssociatedText
 <dt><B>Nb</b>
  <dd>Aeadモード/KCCM の Nb
</dl>
</dl>
これらの名称に応じて値の型も替わっているので、それに応じて、オブジェクトを生成してほしい。

jp.dip.rocketeer.Plugins.IPluginBaseインターフェイスで定義されている分については、[インターフェイス IPluginBase について](https://qiita.com/tomoki0sanaki/items/60613c29f0d627353e67)を参照

---
#### jp.dip.rocketeer.Plugins.IPluginCipher#CreateCipherEntity メソッドの Dictionary&lt;String, Object&gt; 型について

例えば、
`netstream://EncryptCipherAlgorithm=AES/CBC/PKCS7&EncryptCipherPassword=password&EncryptCipherPasswordHashAlgorithm=SHA512&EncryptCipherIV=IVIVIVIV`
コマンドライン上だと**「&」**をエスケープして、
`netstream://EncryptCipherAlgorithm=AES/cbc/PKCS7^&EncryptCipherPassword=password^&EncryptCipherPasswordHashAlgorithm=SHA512^&EncryptCipherIV=IVIVIVIV`
だけど・・・

↑は

- .NET Framework標準のAESのオブジェクトを使って
- モードは「CBC」で、パディングは「PKCS7」 
- IVは「IVIVIVIV」
- 暗号鍵は「password」で
- 暗号鍵はSHA512で攪拌する

という設定となるのだけど・・・

さて、、、
↑の場合、jp.dip.rocketeer.Plugins.IPluginCipher#CreateCipherEntityメソッドに渡されるDictionary&lt;String, Object&gt;型には

1. 「Name」という名前で文字列「AES」
2. 「Mode」という名前で文字列「CBC」
3. 「Padding」という名前で文字列「PKCS7」
4. 「IV」という名前で文字列「IVIVIVIV」のバイト配列
5. 「Password」という名前でjp.dip.rocketeer.Common.SecretData型に「password」が格納
6. 「PasswordHash」という名前でjp.dip.rocketeer.Common.Cipher.DefaultHashMac型でSHA512を実行するオブジェクト

という6つのディクショナリを持つ。

そして、暗号鍵は、

(list をDictionary&lt;String, Object&gt;型の引数だとして・・・)

`Byte[] hako = ((jp.dip.rocketeer.Common.SecretData)list[jp.dip.rocketeer.Plugins.CipherParameters.Password.ToString()]).GetSecret();`

でバイト配列として取得できる(「password」のバイト配列)

これを SHA512で攪拌するには、

`jp.dip.rocketeer.Plugins.IPluginPasswordStirringEntity tempObj =(jp.dip.rocketeer.Plugins.IPluginPasswordStirringEntity)list[jp.dip.rocketeer.Plugins.CipherParameters.PasswordHash.ToString()];
hako = tempObj.GetHash(hako);`

で、攪拌してくれるので、これを適当なサイズに切り取って(※)、暗号化/復号オブジェクトの暗号鍵として使えばよいだろう。

(※) : 暗号アルゴリズムによって、暗号鍵のサイズは厳密に定義されているので、それを意味している。

---
#### jp.dip.rocketeer.Common.SecretData 型について

暗号鍵(パスワード)はsCommonNET.dllで実装しているjp.dip.rocketeer.Common.SecretData型で渡されるので、sCommonNET.dllもインポートする必要があるだろう。

---
#### jp.dip.rocketeer.Plugins.IPluginCipherEntity インターフェイスで定義しているメソッド/プロパティ

実装する必要があるのは以下

jp.dip.rocketeer.Plugins.IPluginCipherEntityインターフェイスで定義されているのは、以下のとおり

<dl>
 <dt><b>Boolean IsInput { get; set; }</b>
  <dd>InputStream or OutputStream
 <dt><b>Boolean IsTest()</b>
  <dd>指定されたオプションで暗号オブジェクトを生成できるかのテストをする<br>パスワードは、後から渡される場合もあるので、NULL のままこの関数が呼ばれる場合もある
 <dt><b>InOutStream CreateStream(Stream baseStream)</b>
  <dd>Cipherストリームを返す
 <dt><b>String ArgorithmName()</b>
  <dd>Cipher アルゴリズム名を返す
 <dt><b>IPluginCipherEntity Copy()</b>
  <dd>マルチスレッドを考慮して自分自身のディープコピーを返す
</dl>

---
### jp.dip.rocketeer.Plugins.InOutStream について

[この入力と出力のどちらか(または両方)のストリームのクラスは、フィルタストリームのプラグインのところで説明した](https://qiita.com/tomoki0sanaki/items/c012f3b399439bbbdb41#jpdiprocketeerpluginsinoutstream-%E3%82%AF%E3%83%A9%E3%82%B9)ので、省略。

---
### 目次へ戻る

[目次というか最初の一歩](http://qiita.com/tomoki0sanaki/items/85f20cbe18378e33cd6f)