メインコンテンツへスキップ

Go言語のWindows実装のcrypto/randのベースのAPIが変わっていてへぇと思った話

·80 文字·1 分·
技術記事 Go言語 Windows C言語
著者
on-keyday
隠者
目次

TL;DR
#

最近はRtlGenRandomじゃなくてProcessPrngが使われている。

本文
#

Go 言語で暗号学的に安全な乱数を生成するためのパッケージとしてcrypto/randがあるが それの Windows 版の内部実装のコードを見ていたらいつの間にか RtlGenRandom から ProcessPrngという関数に変わっていたということを今更見つけた。

具体的にはこのコミットで変更されたらしい https://github.com/golang/go/commit/693def151adff1af707d82d28f55dba81ceb08e1

RtlGenRandomadvapi32.dll から SystemFunction036 という名前でエクスポートされる関数で ドキュメント化されているが互換性は保証されない、みたいな機能であった。 https://learn.microsoft.com/ja-jp/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom

一方変更された ProcessPrngBCryptPrimitives.dllからエクスポートされており 仕様上も常に TRUE を返すとなっている。 https://learn.microsoft.com/ja-jp/windows/win32/seccng/processprng

Microsoft は CryptoAPI を次世代 API に置き換える方向に向かっておりおそらくその流れを汲んで 移行してっているようだ。 https://learn.microsoft.com/ja-jp/windows/win32/seccng/cng-portal

なお、私が知っていたのはRtlCryptGenRandomが使われていることだけだったのだが crypto/rand の Windows 上の の乱数生成器の歴史を blame して見ていくと 最初に実装された頃(2010 年,14 年前!)は CryptGenRandomを使っていたようだ

https://github.com/golang/go/commit/ccd28e8eb6f81f21093deb730ea70982cb381514 https://learn.microsoft.com/ja-jp/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom

その後非推奨化されたのに伴って代替としてRtlGenRandomになった。

https://github.com/golang/go/issues/33542 https://github.com/golang/go/commit/333e90448a0e55f2e1161853caecf3d30ef3a74a

その前後ではBCryptGenRandomGenRandomなどいくつかの変更候補が現れたりしていた。 いずれも最終的にRtlGenRandomを呼ぶだけのラッパーだから遅いとか他のライブラリや言語の実装がみんなRtlGenRandomを使っているとかと言う理由で却下されている。

議論の中にはRtlGenRandomの内部をリバースエンジニアリングした結果ProcessPrngが呼ばれているという報告なども存在した。

そして、最近(2023 年)になってRtlGenRandomの内部で呼ばれていたProcessPrngを直接呼び出すように 変更されていったという流れのようだ。

感想
#

Windows API って抽象化層が厚いけどこういうシステムのベースのところだと邪魔だから 段々リバースエンジニアリングとかで内部構造の理解が深まっていくにつれて どんどん下のほうの関数を呼ぶようになっていてちょっとでもオーバーヘッドを減らそうという努力を感じた。

あと次世代 API は Windows 2008 Server とか Vista から使えるみたいなことが Microsoft のドキュメントには書いてあるけど普及するまでには時間がかかるんだなぁとも感じた。