1745041013
2025-04-18 19:28:00
2022年11月、私はオープンしました 第99108号 PythonのGithubリポジトリでは、SHA3の実装における最近のCVEの後、Pythonはすべてのハッシュ関連インフラストラクチャの検証済みコードを採用する必要があると主張しています。
先週の時点で、この問題は現在閉鎖されており、Pythonでデフォルトで公開されているすべてのハッシュおよびHMACアルゴリズムが現在提供されています hacl*、検証済みの暗号ライブラリ。機能の損失はなく、Pythonユーザーにとって移行は完全に透明でした。 Pythonベンダー(リポジトリに含まれる)15,000行の検証されたCコードからHACL*。上流のHACL*リポジトリから新しいバージョンを引くことは完全に自動化されており、スクリプトを呼び出すことによって行われます。 HACL*は、Pythonのすべての要件を満たすために新しい機能を正常に実装することができました。など、AlgorithmsのBlake2ファミリーの追加モード、すべてのKeccakバリアントをカバーするSHA3の新しいAPI、ビルドシステムの困難を扱う厳格な抽象パターン、適切なエラー管理(特に、割り当て障害)、およびHaclのinstantivation hacl’s HaclのAlsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic Alsic AlgのAlgを具体化することができました。 2つのハッシュ状態を一度に保持する必要がある最適化を含みます。
これは2。5年の仕事の集大成であり、のかけがえのない助けがなければ起こり得なかったでしょう aymeric fromherz、誰が多くの実装の負担を負担しました。 私見では 早い段階で重要な貢献があり、HACLの「ストリーミング」機能を一般化して、「事前入力」を必要とするブロックアルゴリズムを処理できるようにしました。このわずかにあいまいな一般化は、実際には、フードの下に2つのハッシュ状態を保つ適切な最適化されたHMACを実装するために不可欠でした。 Python側では、Gregory P. Smith、BénédiktTran、そして後にChris Eiblが大チャンピオンであり、多くの助けを提供しました。最後に、HACSシリーズのワークショップは接続を作成し(Hello、Paul Kehrer!)、これを実現するのに十分な勢いを提供しました。 Pythonと検証された暗号化コミュニティの両方に感謝します!
私はしばしばやりたいと思うので、舞台裏で少し見て、研究論文には低レベルであまりにも興味深い技術的側面のいくつかについてコメントします。
ストリーミングAPIのプライマー
多くの暗号化アルゴリズムがあります ブロック アルゴリズムは、最初と最後のブロックの特別な処理で、ブロックごとに入力が提供されると仮定することを意味します。よく知られているブロックアルゴリズムには、ハッシュアルゴリズム、Macアルゴリズム(Poly1305、HMACなど)などが含まれます。実際には、ブロックAPIはユーザーフレンドリーではありません。ブロックにデータが既にチャンクされていることはめったにありません。さらに、結果の計算(例えば、ハッシュ)は、ブロックアルゴリズムの状態を無効にします。これにより、TLS転写産物の中間ハッシュの計算が困難になります。
これらの理由により、暗号化ライブラリは通常公開します ストリーミング APIは、クライアントが任意の長さの入力を提供できることを意味します。その後、ライブラリは入力をバッファリングし、フルブロックが取得されるとすぐにバッファーを洗い流します。通常、ストリーミングAPIは、状態を無効にすることなく中間のハッシュを抽出することもできます。
ストリーミングAPIは、複雑な不変剤で長寿命状態を操作するため、困難です。SHA3の(検証されていない)参照実装は、 悪いcve 2022年には、Pythonが「継承」しました。
ストリーミングAPIは、基礎となるブロックアルゴリズムが無数のさまざまな方法で異なるため、難しいです。すべてのハッシュアルゴリズムは空の最終ブロックを受け入れます。 を除外する Blake2の場合;実行時(Poly1305)にキーを保持する必要があるものもあれば、初期化後(HMAC、最適化)後に破棄できるものもあります。キー(blake2)を処理する前に最初の入力が必要なものもありますが、そうでないものもあります。等々。
この固有の複雑さを考えると、ストリーミングアルゴリズムは検証の良い候補です。 研究論文 2021年、この非常に問題について。
完全な一般的な検証
論文の主なアイデアは、従属タイプを使用して、ブロックアルゴリズムをキャプチャできるということです。
は。それが完了すると、ブロックアルゴリズムの抽象的な定義について、一般的なストリーミング構造を執筆し、検証するだけで十分です。次に、C ++のテンプレートをインスタンス化するのと同じように、一般的なストリーミング構造をコンクリートブロックAPIとVoilàに適用します。これは、その1つのブロックアルゴリズムのストリーミングAPI「無料」です。
拳のヒッチは、私たちが論文で提示したもの(リスト12)との間には非常に大きなデルタがあるということです。 実際にリポジトリに住んでいるもの。具体的には、キャプチャ どれでも ブロックアルゴリズムには、多くの生成が必要です。
- ユーザーは、最終的なダイジェストの長さを指定する場合と指定できない場合があります。たとえば、各SHA3ハッシュには固定出力の長さがありますが、シェイクバリアントはユーザーが提供する長さの結果を生成します。
- ブロックアルゴリズムは、データのブロックの前に事前入力を受信することを期待する場合があります。たとえば、SHA2の場合、プリ入力は空ですが、キー付きハッシュモードのBlake2の場合、プリ入力がキーブロックです。
- 一部のアルゴリズム(Blake2)は最終的なブロックを空にすることを許可しないため、ブロックを熱心に処理することはできません。これはバッファー管理を大きく複雑にし、事前入力と相互作用します。
- いくつかのアルゴリズムは、追加の状態を保持する必要があります。たとえば、Blake2のキー長は初期化時に微調整できますが、長寿命の状態で保持する必要があります。
- 中間ダイジェスト抽出時にブロック状態を無効にすることを避けるために、ストリーミングAPIはフードの下に状態をコピーします。場合によっては、このコピーを積み重ねる方が簡単ですが、他の場合、ヒープに引き渡されるコピーがより理にかなっています。
- 場合によっては、アルゴリズムごとに1つのAPIが必要でした(SHA2- {224,256,384,512}の4つのAPIがあります)。他の場合には、アルゴリズムファミリーごとに1つのAPIが必要でした(6つのKeccakアルゴリズムすべてをカバーするAPIが1つあります。
そのレベルのジェネリティに到達するには、Pythonの連続的な要件によって駆動され、複数のラウンドが必要でした。最終的に、Pythonに着陸した最終アルゴリズムであるHMACの場合、HMACを使用して一般的なストリーミングAPIを「インスタンス化」するためにさらに調整する必要がないため、証明と定義が十分に一般的であることに気付きました。
防弾ビルド
PYをPythonに送信することのハイライトの1つは、インフラストラクチャが私たちが夢見るよりも多くのCIカバレッジを持っていることです。Pythonの完全なビルドは、50以上のツールチェーンとアーキテクチャを超えています。裏返し?かなり迷惑なコーナーケースを発見しました。
HMACを扱うときに、特にトリッキーなビルドの問題が浮上しました。リマインダーとして、
hmac ハッシュアルゴリズムを与えられた一般的な構造であり、キー付きメッセージ認証コードを提供します。要するに、作業のほとんどを個々のハッシュアルゴリズムに扱う高レベルのHMACコードがあります。各ハッシュアルゴリズム自体がさまざまなものになる可能性があります 実装:たとえば、HMAC-BLAKE2Bは、HMAC-BLAKE2B-32(通常の実装)とHMAC-BLAKE2B-256(AVX2 Wide-Vector実装)の両方によって実装されています。
これはすでに問題を引き起こします: HMAC.c から関数を呼び出すことができます Blake2b_256.c、PythonがAVX2を使用してマシンで実行されている場合。ただし、のみ Blake2b_256.c でコンパイルされる場合があります -mavx2:fromのコード
HMAC.c AVX2のないすべてのマシンでも、すべてのマシンで実行されます。つまり、それはしなければなりません ない コンパイルされます -mavx2。これまでのところ、とても良いです、そしてこれは私たちが以前にやったことです。
問題には付属しています HMAC.c の初期状態を作成します Blake2b_256.c:
#include
// ...
__m256i *blake2b_256_state = aligned_malloc(sizeof(__m256i)*4);
// ...
ほとんどのツールチェーンはこのコードに満足していました – immintrin.h タイプを定義します __m256i、そしてしても HMAC.c AVX2の命令が利用可能であると仮定することはできません、コンパイラがゼロイニタイアル化するのは難しいことではありません blake2b_256_state AVX2の指示に頼らずに…古いコンパイラが処理することを拒否したことを除いて immintrin.h ヘッダーがない限り -mavx2 全体の目的を打ち負かしました。
これには、よく知られている「C抽象構造」パターンを使用するためにかなりの量のリファクタリングが必要でした。
// Blake2b_256.h
typedef struct blake2b_256_st_s blake2b_256_st;
blake2b_256_st *blake2b_256_malloc();
// Blake2b_256.c
#include
typedef struct blake2b_256_st_s {
__m256i contents[4];
} blake2b_256_st;
// HMAC.c
blake2b_256_st *blake2b_256_state = blake2b_256_malloc();
これをさらに困難にしたのは、cコードがf*から自動生成されていることです。 とても
抽象化のさまざまな概念。 f*からcに行くコンパイラ、
KRML、このような「抽象構造」の存在下であっても、さまざまなレベルの可視性(パブリック関数、ライブラリ内部関数、翻訳ユニットの内部関数)を処理するはるかに細かい分析を実行するためにオーバーホールする必要がありました。
メモリ割り当て障害の処理
f*でのCの元のモデリングにより、メモリの割り当て障害に関する推論は許可されていましたが、実際には誰もそうすることを悩ませていませんでした。 Pythonの場合、メモリの割り当て障害を伝播できることが望まれました。これは、一般的で可変性のある状態(ブロック状態など)の定義を改善しなければならなかったことを意味しました。ブロックアルゴリズムの定義(SHA2-256など);そして、私たちの一般的なストリーミング構造は、すべての人に発信者までメモリ割り当ての障害を伝播することができます。ありがたいことに、これは大したことではありませんでした:私たちは挿入しました option 途中でタイプ、そして1つの一般的なストリーミング構造があるため、ストリーミングAPIの15以上のコンクリートインスタンスについて、実装と証明を1回だけ更新する必要がありました。
の存在 option ソース内のタイプは、生成されたcのタグ付きユニオンにコンパイルされます。これは少し冗長であり、状態の定義を変更して、 has_failed
より複雑さと検証の取り組みを犠牲にして、メモリ割り当てが失敗したかどうかを評価できるランタイム関数。
上流のHACL*からPythonへの変化を伝播します
私の最初のPython PRには、上流のHACL*リポジトリから必要なファイルを取得するシェルスクリプトが含まれていました。よく作られたSEDの呼びかけを介して、ヘッダーに多目的な定義の束を捨てます。また、私のお気に入りのリファクタリングツール(はい、SED)を使用して、いくつかを調整します。利点は、最初のPRが無駄のないきれいだったことでした。
その後、上流のコードが維持可能でかなり安定していることが明らかになると、ヘッダーがいくつかの追加の定義を含み、すべてメンテナンスを容易にする場合、それは世界の終わりではないことに基づいて、SEDの山が排除されました。
現在、HACL*を更新したい人なら誰でも、Pythonのチェックアウトでシェルスクリプトを実行でき、PythonのSBOM(ソフトウェア材料)で予想されるハッシュを調整すると、最新の改善を統合することができます。
結論
Pythonのようなフラッグシッププロジェクトで、検証済みの暗号コードのこのような大規模な統合を見てうれしいです。これは、検証済みの暗号化が学問的な観点から準備ができているだけでなく、すべてのエンジニアリングの期待を満たしながら、実際のソフトウェアに統合するのに十分な成熟していることを示しています。この旅に沿って助けてくれたすべての人に感謝します!
#現在Pythonで検証済みの暗号化された15000行