Hash/Mac プラグインの実装方法

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

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

今回は、Hash/HMAC/MACを計算するためのインターフェイスを使って、プラグインを作成する方法を記述する。

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

ストリームのデータを横目で見ながら何かの計算値を出力するような機能に使っている。

hashとか、HMACとか、MACとか、CRCとか、ただのバイト数カウントするヤツとか・・・

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

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

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

-[Local/Remote]Proxy オプションのnetstreamスキームの引数として渡された引数に応じて、ストリームを包んだり、暗号鍵の攪拌(IPluginPasswordStirringEntity)に呼ばれたりする

という感じ。

---
### HASH と MAC

- 秘密鍵が必要なものがMAC
- 秘密鍵が必要でないものがHASH

という感じなので、

<dl>
 <dt><b>hash</b>
  <dd>hash, crc, など
 <dt><b>mac</b>
  <dd>hmac, mac, など
</dl>

な感じ

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

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

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

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

jp.dip.rocketeer.Plugins.IPluginHashMacEntityインターフェイスは、jp.dip.rocketeer.Plugins.IPluginPasswordStirringEntityインターフェイスを継承しているので、パスワード攪拌関数も兼ねていることになる。

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

StreamRelay.NET.exeや、他のプラグインから、引数/オプションの解析処理中に呼び出して、引数の指定内容によって、IPluginHashMacEntityやIPluginHashMacEntity[]を返す。
すると、IPluginHashMacEntity/IPluginHashMacEntity[]から、Streamを包んでStreamを返すものを呼び出したり、場合によっては暗号鍵の攪拌関数として呼ばれたりする。

という感じ。

具体的には、-(Local|Remote)Proxy の netstreamスキーム指定時の、ハッシュストリームなどの解析で
**IPluginHashMacEntity CreateHashEntity(String iHashArgorithmName, Boolean IsLocal, Boolean IsInput, ref String errMsg)**
または
**IPluginHashMacEntity CreateMacEntity(String iMacArgorithmName, byte[] key, Boolean IsLocal, Boolean IsInput,ref String errMsg)**
を呼び出すので、自分の引数であれば、IPluginHashMacEntityオブジェクトを返してほしい。

具体的には「[Input|Output]HashAlgorithm=&lt;&lt;name&gt;&gt;」または「[Input|Output]KeyMacAlgorithm(Hex)=&lt;&lt;name&gt;&gt;;&lt;&lt;Key|HexKey&gt;&gt;」をnetstreamスキーム(-(Local|Remote)Proxy)で指定されたような時に、呼び出される。

もしくは、(-Local|-Remote)Proxy の netstreamスキーム指定時の、「[Input|Output]AllHashAlgorithm(Hex)(=&lt;&lt;Key&gt;&gt;)」という指定によって
**IPluginHashMacEntity[] CreateAllHashEntity(Boolean IsLocal, Boolean IsInput)**
または
**IPluginHashMacEntity[] CreateAllMacEntity(byte[] key, Boolean IsLocal, Boolean IsInput)**
が呼び出された時には、全ての**IPluginHashMacEntity**オブジェクトを配列で返してほしい。


そして、Stream(HashMacStreamは後述)がCloseしたタイミングで **byte[] GetHash()** が呼ばれるので、計算結果を返してほしい。

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

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

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

<dl>
 <dt><b>Dictionary<String, String> ListDescription(Boolean IsHash)</b>
  <dd>IsHash=trueならハッシュの引数文字列などを返す、IsHash=falseならハッシュの引数文字列などを返す
 <dt><b>IPluginHashMacEntity CreateHashEntity(String iHashArgorithmName, Boolean IsLocal, Boolean IsInput, ref String errMsg)</b>
  <dd>名称から Hash オブジェクトの実体を作成する
 <dt><b>IPluginHashMacEntity[] CreateAllHashEntity(Boolean IsLocal, Boolean IsInput)</b>
  <dd>全ての Hash オブジェクトの実体を作成する
 <dt><b>IPluginHashMacEntity CreateMacEntity(String iMacArgorithmName, byte[] key, Boolean IsLocal, Boolean IsInput,ref String errMsg)</b>
  <dd>名称から Mac オブジェクトの実体を作成する
 <dt><b>IPluginHashMacEntity[] CreateAllMacEntity(byte[] key, Boolean IsLocal, Boolean IsInput)</b>
  <dd>全ての Mac オブジェクトの実体を作成する
</dl>

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

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

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

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

<dl>
 <dt><b>Boolean IsInput { get; set; }</b>
  <dd>InputStream or OutputStream
 <dt><b>Boolean IsHash { get; set; }</b>
  <dd>Hash or Mac
 <dt><b>Boolean IsLocal { get; set; }</b>
  <dd>接続するストリームがローカル(サーバ)側かリモート(クライアント)側か
 <dt><b>byte[] GetHash()</b>
  <dd>バッファリングしている Hash/Mac の値を返す
 <dt><b>HashMacStream CreateStream(Stream baseStream)</b>
  <dd>引数のストリームにHash/Mac ストリームを被せるようにして返す(後述)
</dl>

jp.dip.rocketeer.Plugins.IPluginPasswordStirringEntityインターフェイスで定義されている分については、[パスワード攪拌関数プラグインの実装方法](https://qiita.com/tomoki0sanaki/items/925cc3def304e861d152)を参照

---
### jp.dip.rocketeer.Plugins.HashMacStream クラス

jp.dip.rocketeer.Plugins.<b>HashMacStream</b> クラスは、jp.dip.rocketeer.Plugins.HashMacStream<b>#Close()</b> 時に、jp.dip.rocketeer.Plugins.IPluginHashMacEntity<b>#GetHash()</b> を呼び出して計算値(ハッシュ値など)を出力するようにしている。

よって、jp.dip.rocketeer.Plugins.<b>HashMacStream</b> クラスは、jp.dip.rocketeer.Plugins.IPluginHashMacEntity<b>#CreateStream(Stream)</b>で返してほしい。

具体的には、jp.dip.rocketeer.Plugins.IPluginHashMacEntity<b>#CreateStream(Stream)</b>内部で、jp.dip.rocketeer.Plugins.<b>HashMacStreamのコンストラクタ</b>を呼べばいいと思う。

つまり、
jp.dip.rocketeer.Plugins.HashMacStreamのコンストラクタは、
<b>HashMacStream(IPluginHashMacEntity iObj, Stream readStream, Stream writeStream)</b>
なので、
jp.dip.rocketeer.Plugins.IPluginHashMacEntity<b>#CreateStream(Stream baseStream)</b>内部で、HashMacStreamを作って返してくれればいい。

なんとなくのなんちゃってコードだとこんな感じ

```c#:
public class exampleHash : IPluginHashMacEntity{
// 例えば、入力方向のハッシュ値を計算する
base.IsInput = true;

// 例えばMD5とする
public MD5 md5;

// HashMacStreamkのCloseで呼ばれる
public override byte[] GetHash(){
	// cryptoStreamに渡した計算オブジェクトから最終結果を取得して、返す
	return this.md5.hash;
}

CryptoStream cryptoStream;

HashMacStream CreateStream(Stream baseStream){
	// ストリーム(接続)ごとに計算するので、新規作成(ディープコピー)しておく
	IPluginHashMacEntity ko = this.Copy(); 

	// baseStreamストリームと、IPluginHashMacEntityオブジェクトを使って、
	// 例えば、ハッシュを計算するストリームCryptoStreamを生成する
        cryptoStream = new CryptoStream(baseStream, ko.md5);

	// ハッシュを計算するストリームを包んだHashMacStream(System.IO.Stream)を返す
	// 入力側は計算するのでcryptoStreamだけど、
	// 出力側は計算しないのでbaseStreamそのままを引数に与える
	return new HashMacStream(ko, cryptoStream, baseStream);
}}
```

みたいな感じで HashMacStreamを生成して、返してほしい。

---
### 目次へ戻る

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