暗号化のために多くの暗号が一般的に必要です。 今日、インターフェースとクラスの組み合わせは少しバラバラになっています。 また、AEADスタイルの暗号は、追加の認証情報を提供する機能を必要とするため、サポートされていません。 現在の設計も割り当てが発生しやすく、配列が返されるため、これらを回避するのは困難です。
具象クラスによって実装される汎用抽象基本クラス。 これにより、拡張が可能になります。また、静的メソッドではなくクラスを使用することで、拡張メソッドを作成したり、呼び出し間で状態を保持したりすることができます。 APIは、クラスのリサイクルを許可して、より少ない割り当てを可能にする必要があります(毎回新しいインスタンスを必要とせず、管理されていないキーなどをキャッチします)。 追跡されるリソースの管理されていない性質のため、クラスはIDisposableを実装する必要があります
public abstract class Cipher : IDisposable
{
public virtual int TagSize { get; }
public virtual int IVSize { get; }
public virtual int BlockSize { get; }
public virtual bool SupportsAssociatedData { get; }
public abstract void Init(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
public abstract void Init(ReadOnlySpan<byte> iv);
public abstract int Update(ReadOnlySpan<byte> input, Span<byte> output);
public abstract int Finish(ReadOnlySpan<byte> input, Span<byte> output);
public abstract void AddAssociatedData(ReadOnlySpan<byte> associatedData);
public abstract int GetTag(Span<byte> span);
public abstract void SetTag(ReadOnlySpan<byte> tagSpan);
}
(入力/出力ソースは、IOソースのような神話上のスパンベースのストリームです)
using (var cipher = new AesGcmCipher(bitsize: 256))
{
cipher.Init(myKey, nonce);
while (!inputSource.EOF)
{
var inputSpan = inputSource.ReadSpan(cipher.BlockSize);
cipher.Update(inputSpan);
outputSource.Write(inputSpan);
}
cipher.AddAssociatedData(extraInformation);
cipher.Finish(finalBlockData);
cipher.GetTag(tagData);
}
参照dotnet / corefx#7023
提案されたAPIに関する迅速な判断のフィードバック(役立つように努めています):
Span<T>
はNetStandard2にありません。AEAD APIの健全性の証拠として、このような提案されたAPIの最初の実装はAES-GCMではなく、HMACタグ付きのクラシック/デフォルトのAES-CBCである必要があります。 その単純な理由は、誰もがAES-CBC + HMAC AEAD実装を、単純で既存のよく知られた.NETクラスを使用して構築できることです。 退屈な古い[AES-CBC + HMAC]を最初に新しいAEADAPIで動作させましょう。これは、誰もがMVPとテストドライブを簡単に行えるためです。
ノンス/ IVの命名の問題は私が未定だったもので、IVへの変更に満足しているので変更されます。
Getメソッドが何かを返す場合、これにより割り当てが回避されます。 何かを返すオーバーロードGet()が存在する可能性があります。 名前の変更が必要かもしれませんが、私はAPI全体が基本的に割り当てなしである必要があるという考えとかなり結婚しています。
ストリームなどについては、低レベルのプリミティブから簡単に構築できる高レベルのAPIであるため、あまり気になりません。
終了する前にタグを取得することは許可されるべきではありませんが、finishを呼び出したときに知っておく必要があるため、APIに含める必要があるかどうかはわかりませんが、定義された動作である必要があるため、APIデザインを更新して考えられている他のすべてをキャプチャできるように、動作セクション。
どの暗号については、特定の暗号を唯一のターゲットにするべきではないと思います。新しい汎用APIを証明するために、数値に合わせる必要があります。 AESGCMとCBCの両方をカバーする必要があります。
(トピックに関するフィードバックの良し悪しは常に役に立ちます!)
@Drawaesは、このAPIでボールを転がしてくれてありがとう。 いくつかの考え:
@bartonjs
MyKeyStore.GetCipher();
初期化されていません。 使い捨てなので、通常の使い捨てパターンですべての参照をドロップできます。 キーを設定しようとすると、無効な操作例外がスローされます。
はい、読み取り専用スパンです。電話のチューブを使用していないときに調整します。
@morganbr問題ありません...私はそれが何よりも起こるのを見たいだけです;)
私は現在のものにはほとんど興味がありません。それはとても混乱しているので、すべての合理的な現代のアルゴリズムに直接パッチを当てるだけです(他のクラスには3DESを残してください:)
問題は、EnvelopedCms(またはEncryptedXml)のようなコンテナ形式が3DES-CBC、AES-CBCなどで動作する必要があるかもしれないということです。ECIES/ AES-256-CBC-PKCS7 / HMAC-SHA-2で暗号化されたものを復号化したい人-256はおそらく、彼らが古くてくだらないことをしているとは感じないでしょう。
AE専用の場合は、名前のどこかに反映されているはずです。 今のところ、それは一般的な「暗号」です(ある時点で、辞書/用語集に腰を下ろして、「操作モードの暗号化アルゴリズム」という言葉があるかどうかを調べます。 "cipher" == "algorithm"、したがって "Aes")。
:)私はそれが私の主題分野ではない、または私にとってあまり興味がないことを単に指摘していたので、私はこれに対する影響について考えていなかったこのトピックについてあなたとコミュニティに任せたいと思っています。
これらをすばやくスキャンした後、1つのオプションは、「暗号」またはクラスと呼ばれるもののインスタンスを取得できるようにすることです。 これは最初の波では行われないかもしれませんが、すぐにフォローアップすることができます。 APIが非常に効率的である場合、内部で独自のことを行う必要がある理由はわかりません。これはまさにこのAPIのユースケースです。
ネーミングのサイドバーとして...しかし、私はそれが難しいことを認めなければなりません
openssl =暗号
Ruby =暗号
Go = AEADなどのducktypedインターフェイスを備えた暗号パッケージ
Java =暗号
今、私はすべて違うのですが...トレンドがあります。 より良い何かが可能であれば、それはクールです。
おそらく「BlockModeCipher」...?
いくつか変更を加えました。より適切な名前が決定された場合は、名前を変更します。
質問に答えようとしたとき、APIにはすでに暗号化と復号化の区別がないことに気付きました。そのため、あなたの例では、データを暗号化するか復号化するかがわかりません。 それを入れると、いくらか明確になるかもしれません。
APIが適切なタグの使用を強制できるいくつかの方法を想像できます(これは、すでにSymmetricAlgorithm / ICryptoTransform / CryptoStreamがあるため、対称暗号化だけでなく、AEAD APIであるという仮定に基づいています)。 タグ付けを実施する例のように、これらを規範的なものと見なさないでください。
方法別:
class Cipher
{
void InitializeEncryption(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
// Ensures decryptors get a tag
void InitializeDecryption(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> tag);
// Ensure encryptors produce a tag
void FinishEncryption(ReadOnlySpan<byte> input, Span<byte> output, Span<byte> tag);
// Throws if tag didn't verify
void FinishDecryption(ReadOnlySpan<byte> input, Span<byte> output);
// Update and properties are unchanged, but GetTag and SetTag are gone
}
クラス別:
class Cipher
{
// Has properties and update, but Initialize and Finish aren't present
}
class Encryptor : Cipher
{
void Initialize(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
void Finish(ReadOnlySpan<byte> input, Span<byte> output, Span<byte> tag);
}
class Decryptor : Cipher
{
void Initialize(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv, ReadOnlySpan<byte> tag);
// Throws if tag didn't verify
void Finish(ReadOnlySpan<byte> input, Span<byte> output);
}
class AesGCMEncryptor : Encryptor {}
class AesGCMDecryptor : Decryptor {}
}
とはいえ、復号化でバッファリングされない場合、復号化が実際に終了し、タグがチェックされることを確認することは実用的ですか? Updateは、チェックする時間だとどういうわけか理解できますか? それはDisposeがすべきことですか? (オブジェクトを破棄するまでにデータをすでに信頼している可能性があるため、破棄は少し危険です)
命名に関しては、私たちの先例はSymmetricAlgorithmとAsymmetricAlgorithmです。 これがAEADを対象としている場合、いくつかのアイデアはAuthenticatedSymmetricAlgorithmまたはAuthenticatedEncryptionAlgorithmである可能性があります。
いくつかの考えとAPIのアイデア:
public interface IAEADConfig
{
// size of the input block (plaintext)
int BlockSize { get; }
// size of the output per input-block;
// typically a multiple of BlockSize or equal to BlockSize.
int FeedbackSize { get; }
// IV size; CAESAR completition uses a fixed-length IV
int IVSize { get; }
// CAESAR competition uses a fixed-length key
int KeySize { get; }
// CAESAR competition states that typical AEAD ciphers have a constant gap between plaintext length
// and ciphertext length, but the requirement is to have a constant *limit* on the gap.
int MaxTagSize { get; }
// allows for AE-only algorithms
bool IsAdditionalDataSupported { get; }
}
public interface ICryptoAEADTransform : ICryptoTransform
{
// new AEAD-specific ICryptoTransform interface will allow CryptoStream implementation
// to distinguish AEAD transforms.
// AEAD decryptor transforms should throw on auth failure, but current CryptoStream
// logic swallows exceptions.
// Alternatively, we can create a new AEAD_Auth_Failed exception class, and
// CryptoTransform is modified to catch that specific exception.
}
public interface IAEADAlgorithm : IDisposable, IAEADConfig
{
// separates object creation from initialization/keying; allows for unkeyed factories
void Initialize(ArraySegment<byte> key);
void Encrypt(
ArraySegment<byte> iv, // readonly; covered by authentication
ArraySegment<byte> plaintext, // readonly; covered by authentication
ref ArraySegment<byte> ciphertext, // must be of at least [plaintext_length + MaxTagSize] length. iv is not part of ciphertext.
ArraySegment<byte> additionalData = default(ArraySegment<byte>) // readonly; optional; covered by authentication
); // no failures expected under normal operation - abnormal failures will throw
bool Decrypt(
ArraySegment<byte> iv, // readonly
ArraySegment<byte> ciphertext, // readonly
ref ArraySegment<byte> plaintext, // must be of at least [ciphertext_length - MaxTagSize] length.
ArraySegment<byte> additionalData = default(ArraySegment<byte>), // readonly; optional
bool isAuthenticateOnly = false // supports Authentication-only mode
);// auth failures expected under normal operation - return false on auth failure; throw on abnormal failure; true on success
/* Notes:
* Array.LongLength should be used instead of Array.Length to accomodate byte arrays longer than 2^32.
* Ciphertext/Plaintext produced by Encrypt()/Decrypt() must be determined *only* by method inputs (combined with a key).
- (ie. if randomness or other hidden inputs are needed, they must be a part of iv)
* Encrypt()/Decrypt() are allowed to write to ciphertext/plaintext segments under all conditions (failure/success/abnormal)
- some implementations might be more stringent than others, and ex. not leak decrypted plaintext on auth failures
*/
ICryptoAEADTransform CreateEncryptor(
ArraySegment<byte> key,
ArraySegment<byte> iv,
ArraySegment<byte> additionalData = default(ArraySegment<byte>)
);
ICryptoAEADTransform CreateDecryptor(
ArraySegment<byte> key,
ArraySegment<byte> iv,
ArraySegment<byte> additionalData = default(ArraySegment<byte>)
);
// Streaming AEAD can be done with good-old CryptoStream
// (possibly modified to be AEAD-aware).
}
@sdrapkin 、考えを投稿してくれてありがとう。 タグはAPIのどこに配置する必要がありますか? それらは暗黙的に暗号文の一部ですか? もしそうなら、それはいくつかのアルゴリズムが実際には持っていないプロトコルを意味します。 暗黙のタグ配置が受け入れられるかどうか、または個別に実行する必要があるかどうかを確認するために、人々が気にする可能性のあるプロトコルを理解したいと思います。
@morganbr暗号化/復号化の問題にも気づきましたが、今夜は修正する時間がなかったので、デザインに満足しています。 より積極的なリサイクルが可能になるため、クラスよりもメソッドの方が好きです(キーとIVのバッファーが合計される可能性があります)。
廃棄前のチェックも。 残念ながら、操作の終了を通知することはできません。
@sdrapkinインターフェースは、前述のバージョンの問題のため、ダメだと思います。 将来、デフォルトの埋め込みに依存しない限り。 また、インターフェイスのディスパッチは遅くなります。スパンはより用途の広い下位プリミティブであるため、配列セグメントも使用できます。 ただし、後で需要があった場合は、arraysegmentがスパンするように拡張メソッドを追加できます。
あなたのプロパティのいくつかは興味があるので、私が私の電話ではなくコンピュータにいるときに更新されます。
すべてのラウンドで良いフィードバック!
@morganbrタグは暗号文の一部です。 これは、 CAESAR API (AES-GCMを含む)をモデルにしています。
@Drawaes私は考えを説明するためだけにインターフェースを使用しました-静的メソッド/クラスで完全に大丈夫です。 スパン
便利なCAESARAPIについて詳しく見ていきます。
Spanに関しては、2.1の時間枠で出荷されています。これは、このAPIの最初の実装が出荷されるのと同じ時間(または少なくとも可能な限り早い時間)になることを願っています。
現在のプレリリースのnugetパッケージを見ると、.net標準1.0までサポートされており、リリース時に変更する予定はありません。
たぶん、 @ stephentoubは、私たちが話しているように、フレームワーク全体にSpanベースのAPIを追加する作業を行っていることを確認できます。
(スパンのNuget)[https://www.nuget.org/packages/System.Memory/4.4.0-preview2-25405-01]
ですから、これがまったく新しいAPIの唯一の本当の選択だと思います。 次に、拡張メソッドなどを追加して、必要に応じてArraySegmentを取得できます。それが十分に役立つ場合は、フレームワークに追加できますが、ArraySegmentをSpanに変換するのは簡単ですが、逆の場合はデータをコピーする必要があります。
上記のAPIで私が目にする問題は、「チャンク化された」データのパフォーマンスが低下することです。 たとえば、ネットワークトラフィックの場合、単一の認証済みブロックが既存のストリームからの複数の読み取りに分割される場合、すべてを単一の[データ構造の挿入]にバッファリングし、一度に暗号化/復号化する必要があります。 敗北は、そのデータに対してあらゆる種類のゼロコピーを実行しようとするすべての試みを打ち負かします。
Pipelinesが提供するようなネットワークフレームワークは、ほとんどすべてのコピーを回避することができますが、このAPIで何らかの暗号化にヒットした場合、そのすべてが失われます。
別の構成オブジェクト(またはバッグ)は、私が持っている別のAPIに関する最近の議論に実際に関与しています。 原則として反対ではありませんが、将来的に大きくなると、メインオブジェクトに多数のプロパティが存在することで混乱する可能性があります。
私にはいくつかのことが起こりました。
コンテナ形式(EnvelopedCms、EncryptedXml)がキーを抽出する必要があるのか、それとも単にキーを生成して記憶するのは彼ら次第なのか(彼らがそれを書き留める必要がある限り)はわかりません。
(昨日は「コメント」ボタンを押さなかったようですので、1910Zで「いくつか変更を加えました」以降は何も確認されません)
真のタグサイズは可変である必要があります。 同意しました。
今のところ暗号化だけを見てみると、ユースケースを単純化するためです。 一部の暗号は何も返さない、少ない、または多いというのは正しいことです。 十分な大きさのバッファを提供しないとどうなるかについては、一般的な質問があります。
spanを使用する新しいTextEncodingインターフェースでは、出力するのに十分なスペースがあるかどうかを定義する列挙型を戻り型にし、代わりに実際に「out」パラメーターでサイズを書き込むことについての提案がありました。 これは可能性です。
CCMの場合、何も返さず、finishを呼び出すまで内部バッファリングする必要があります。この時点で、ロット全体をダンプする必要があります。 1つのブロックにすべてのデータがある場合、最初の呼び出しとして単にfinishを呼び出すことを妨げるものはありません(この場合、より適切な名前がある可能性があります)。 または、これらの暗号を更新しようとすると、スローする可能性があります。 たとえば、CCMで継続を実行しようとすると、CNGは無効なサイズエラーを返します。
タグが復号化に設定されている場合、パケット全体を読み取るまでわからないことがよくあります。TLSを例にとると、最後にタグに到達するために8 * 2kのネットワークパケットを読み取る必要がある場合があります。 16kブロックの。 したがって、復号化を開始する前に16k全体をバッファリングする必要があり、重複する可能性はありません(これは、ディスクまたはディスクであるかどうかにかかわらず、IOバウンドプロセスがこれらのタイプの暗号で一般的であるというだけでTLSに使用されるとは言いません通信網)。
@Drawaesre 。 チャンクストリームとバッファリングの制限:
あなたはあなたの戦いを選ばなければなりません。 AEの世界では、すべての1つの便利な目標に合わせて統合APIを作成することはできません。そのような目標はたくさんあります。 元。 インフェルノにはまともなチャンクAEADストリーミングがありますが、それは一気に標準ではなく、そのような標準は存在しません。 より高いレベルでは、目標は「セキュリティで保護されたチャネル」です( this 、 this 、およびthisを参照)。
ただし、今のところはもっと小さく考える必要があります。 チャンキング/バッファ制限は、標準化の取り組みのレーダーにさえありません(「大きな平文を持つAEAD 」セクション)。
暗号化/復号化操作は、基本的に変換に関するものです。 これらの変換にはバッファーが必要であり、インプレースではありません(少なくとも、Encrypt変換の場合、出力バッファーは入力バッファーよりも大きくする必要があります)。
RFC5116も興味深いかもしれません。
@ Drawaes 、TLSを起動するのは興味深いことです。 アプリケーションにはそれ自体を防御する方法がないため、SSLStream(このAPIを使用する場合)が認証されていない結果をアプリケーションに返さないようにする必要があると私は主張します。
もちろんですが、それはSSLStreamsの問題です。 私はパイプラインの上にこの正確なもの(暗号ビットのためにCNGとOpenSSLを呼び出すプロトコルレベルで管理されたTLS)のプロトタイプを作成しました。 ロジックは非常に単純で、暗号化されたデータが入り、バッファを所定の場所で復号化し、アウトバウンドに接続して、タグに到達するまで繰り返します。 タグコールが終了しました...
スローされた場合は、パイプラインを閉じます。 スローされない場合は、フラッシュして、次のステージが同じスレッドで、またはディスパッチを介して機能するようにします。
私の概念実証はプライムタイムの準備ができていませんでしたが、これを使用し、大量のコピーなどを回避することで、非常に適切なパフォーマンスの向上が見られました;)
ネットワークの問題は、パイプライン内のものが独自のバッファの割り当てを開始し、システム内を移動しているバッファを可能な限り使用しないことです。
OpenSslの暗号化とCNGには、これと同じメソッドUpdate、Update、Finishがあります。 仕上げは、説明したようにタグを出力できます。 更新はブロックサイズ(CNGの場合)である必要があり、OpenSslの場合、ブロックサイズに到達するために最小限のバッファリングを実行します。
それらはプリミティブであるため、より高いレベルの機能を期待できるかどうかはわかりません。 プリミティブではなく「ユーザー」レベルのAPIを設計してそれらを構築する場合、キーの生成、IVの構築、認証されたチャンク全体をすべて実装する必要があると主張するので、このAPIのターゲットレベルが実際に何であるかによって異なります。
間違ったボタン
@blowdart 、ナンス管理についていくつかの興味深いアイデアを持っていました。
したがって、ナンス管理は基本的にユーザーの問題であり、セットアップに固有のものです。
だから...それを要件にします。 ナンス管理をプラグインする必要があります...デフォルトの実装や実装をまったく提供しないでください。 これは、単純ではなく
cipher.Init(myKey, nonce);
リスクを理解する特定のジェスチャーをユーザーに強制します。
@blowdartのアイデアは、ナンス管理の問題とアルゴリズム間の違いの両方に役立つ可能性があります。 ナンス管理が解決する必要のある問題であることをユーザーが確実に理解できるようにするために、組み込みの実装を持たないことが重要である可能性が高いことに同意します。 このようなものはどのように見えますか?
interface INonceProvider
{
public void GetNextNonce(Span<byte> writeNonceHere);
}
class AesGcmCipher : Cipher
{
public AesGcmCipher(ReadOnlySpan<byte> key, INonceProvider nonceProvider);
}
// Enables platform-specific hardware keys
class AesGcmCng : Cipher
{
public AesGcmCng(CngKey key, INonceProvider nonceProvider);
}
// Example of AEAD that might not need a nonce
class AesCBCHmac : Cipher
{
public AesCBC(ReadOnlySpan<byte> key)
}
class Cipher
{
// As above, but doesn't take keys, IVs, or nonces
}
しかし、INonceProviderのポイントは何ですか? これは単なる追加のインターフェイス/タイプです。Initがnoneを取り、ブロックを開始する前に呼び出す必要がある場合、追加の/インターフェイスがない場合も同じではありませんか?
また、私は暗号の専門家ではありませんが、AESはIVを必要としません(これはナンスではありませんが、ユーザーが提供する必要がありますか?)
それは単なる余分なインターフェース/タイプです
それがポイントです。 それは本質的に、ゼロ化された、あるいはランダムなバイト配列を渡すだけでなく、_noncemanagement_が問題であると言っています。 また、人々がGetNextNonce
を「前回とは異なるものを返す」という意味であると解釈した場合、不注意による再利用を防ぐのに役立つ可能性があります。
また、ナンス管理の問題がないアルゴリズム(AESSIVやおそらくAES + CBC + HMACなど)では必要ないことも役立ちます。
正確なIV / nonce要件は、モードによって異なります。 例えば:
AES CBCにはIVが必要ですか? では、InitializationVectorProviderを使用しますか? それはナンスではありませんが
ナンスのように最後のブロックを再利用すると、ivが予測できるため、tls攻撃が発生しました。 CBCのシーケンシャルナンスを明示的に使用することはできません。
ええ、でもIVはナンスではないので、nomceプロバイダーという用語を使うことはできません
私は、AESCBCがIVを必要としないことを意味するつもりはありませんでした-それは必要です。 他のデータからIVを導出するいくつかのスキームについて推測するつもりでした。
確かに私のポイントは私が一般的に好きだと思います...私はプロバイダーをプールすることができます;)しかしそれをivプロバイダーと呼ぶか、意図を明確にするために2つのインターフェースを持っています
@morganbr INonceProvider
暗号コンストラクターに渡されるファクトリは悪い設計です。 _nonce_がそれ自体では存在しないという事実を完全に見逃しています。「_... usedonce_」制約には_context_があります。 CTRおよびGCM(CTRを使用)モードの場合、_nonce_の_context_は_key_です。 つまり。 _nonceプロバイダー_は、特定の_key_のコンテキスト内で1回だけ使用されるナンスを返す必要があります。
提案されたAPIのINonceProvider
はキーを認識しないため、正しいナンスを生成できません(統計的ランダムネスが機能するのに十分な大きさのビットスペースであっても、ナンスとは異なるランダムネスを介する場合を除きます)安全に)。
このディスカッションスレッドが何を達成しようとしているのか完全にはわかりません。 さまざまな認証付き暗号化の設計アイデアについて説明します...わかりました。 すでに.NETCoreに組み込まれている認証付き暗号化インターフェイス(特にASP.NET APIに組み込まれている)についてはどうでしょうか。 IAuthenticatedEncryptorなどがあります。これらの機能はすべて、すでに実装されており、拡張可能であり、現在.NETCoreの一部として出荷されています。 DataProtection暗号が完璧だと言っているわけではありませんが、それらを無視する計画はありますか? それらを変更しますか? 同化またはリファクタリング?
DataProtection暗号は@GrabYourPitchforks (Levi Broderick)によって構築されました。 彼は主題を知っており、彼の意見/入力/フィードバックはこのコミュニティにとって最も価値があります。 私は誰よりも暗号をテーマにしたエンターテインメントを楽しんでいますが、誰かが暗号APIの設計に真剣に取り組みたい場合は、すでにMSチームに所属している実際の専門家を雇う必要があります。
@ sdrapkin 、キーを認識する必要があるナンスプロバイダーは良い点です。 それを強制するためにこれらのAPIを変更する合理的な方法があるのだろうか。
DataProtectionは優れたAPIですが、より高レベルの構成です。 キーの生成と管理、IV、および出力プロトコルをカプセル化します。 誰かが(たとえば)別のプロトコルの実装でGCMを使用する必要がある場合、DataProtectionはそれを可能にしません。
.NET暗号化チームには、@ bartonjs 、 @ blowdart 、および私が含まれます。 もちろん、 @ GrabYourPitchforksがチャイムを鳴らしたいのであれば、彼は大歓迎です。
@morganbrは、これが低レベルのプリミティブであると想定されているという点で同意します(実際、タイトルにそのように記載されています)。 データ保護などはユーザーコードで直接使用するように設計されており、足元で自分を撃つ能力を低下させますが、このプリミティブを見ると、フレームワークとライブラリが共通ベースでより高いレベルの構造を構築できるようになっています。
その考えを念頭に置いて、キーが提供されるたびに提供する必要がある場合は、プロバイダーは問題ありません。 TLSの使用について説明すると、少し面倒になります(ネットワークトラフィックでのAESブロックモードのよく知られた使用法がすべてです)。
「フレーム」を取得します(インターネットのMTU〜1500で2 + TUを超える可能性があります)。 ナンス(または4バイトが「非表示」のままのナンスの一部)が含まれています。次に、シェルの「プロバイダー」でこの値を設定し、decryptを呼び出して、バッファーを復号化するサイクルを実行して、単一のプレーンテキストを取得する必要があります。 。
あなたがそれに満足しているなら、私はそれと一緒に暮らすことができます。 私はこれをうまく進めたいと思っているので、上記のデザインを私たちが同意できるものに更新することに熱心です。
議論をフォークしてくれてありがとう、これに飛びつくための自由な時間を取ってください。 @Drawaes 、この進化する会話のゴールドスタンダード/ターゲットとしてトップの投稿を確認/更新できますか? そうでない場合は、更新できますか?
現在の提案には致命的な問題があり、それからおしゃべりすぎるという他の問題があると思います。
// current proposed usage
using (var cipher = new AesGcmCipher(bitsize: 256))
{
cipher.Init(myKey, nonce);
while (!inputSource.EOF)
{
var inputSpan = inputSource.ReadSpan(cipher.BlockSize);
cipher.Update(inputSpan);
outputSource.Write(inputSpan);
}
cipher.AddAssociatedData(extraInformation); // <= fatal, one can't just do this
cipher.Finish(finalBlockData);
cipher.GetTag(tagData);
}
真のAEADプリミティブを見ると、プライバシーデータと認証されたデータはロックステップが混在しています。 Auth Data1とCipherText1についてはこちらをご覧ください。 もちろん、これは1つだけでなく、複数のブロックでも継続します。
全世界がミームなので、抵抗できません、ごめんなさい:)
また、APIはnew、init、updateなどとおしゃべりなようです。このプログラマーのモデルを提案します
// proposed, see detailed comments below
using (var cipher = new AesGcmCipher(myKey, iv, aad)) // 1
{
// 2
while (!inputSource.EOF)
{
var inputSpan = inputSource.ReadSpan(16411); // 3
var outSpan = cipher.Encrypt(inputSpan); // 4
outputSource.Write(outSpan);
}
var tag = cipher.Finish(finalBlockData); // 5
}
cipher.Init(mykey, nonce, aad);
を見てきました。 (例: BCryptEncryptsのCipherModeInfo param )。 また、myKeyのサイズはすでにAES128、192、256を確立しており、別のパラメーターは必要ありません。tag
も、現時点で最も重要な必要なデータです。それを返します。実際にはさらに一歩進んで、なぜ
using (var cipher = new AesGcmCipher(myKey, iv, aad))
{
var tag = cipher.EncryptFinal(inputSpan, outputSpan);
}
また、 INonceProvider
などから離れてください。 暗号プリミティブはこれを必要とせず、 byte[]
iv(小さなデータで私のお気に入り)またはSpan
(新しいクールだと思われるが抽象化が多すぎるIMHO)に固執するだけです。 Nonceプロバイダーは上のレイヤーで動作し、その結果はここに表示されるiv
になる可能性があります。
プリミティブを非常にプリミティブにすることの問題は、人々が単にそれらを誤って使用することです。 プロバイダーがあれば、少なくともいくつかの考えをそれらの使用に強制することができます。
GCMが特定されているAEAD全般について話しています。 したがって、最初に、特定のケース( nonce
)ではなく、一般化されたケース( iv
)が設計を推進する必要があります。
第二に、単にbyte[] iv
からGetNextNonce(Span<byte> writeNonceHere)
にシフトするだけで、実際にナンスの問題をどのように解決できるでしょうか。 問題の名前/ラベルを変更しただけで、同時に問題を本来よりも複雑にしています。
第三に、 iv
保護に関するポリシーに取り組んでいるので、主要な保護ポリシーにも取り組む必要がありますか? 鍵配布ポリシーはどうですか? それらは明らかに高いレベルの懸念です。
最後に、nonceは、上位層での使用に関して非常に状況に応じて変化します。 クロスレイヤーの懸念事項が結合されている脆弱なアーキテクチャーは必要ありません。
率直に言って、誰かが私が何をしているのか知っていると言うジェスチャーをしない限り、プリミティブを隠すことができれば、私はそれを推し進めます。 しかし、できません。 「これは利用可能です、私はそれを使用します」という人々がいるので、そこにはあまりにも多くの悪い暗号実装があります。 AES自体を見てください。HMACなしで使用します。
私はAPIがデフォルトで安全であることを望んでいます、そしてそれがもう少し苦痛を意味するなら、率直に言って私はそれですべてです。 開発者の99%は、暗号化に関して自分が何をしているのかわからないため、1%の開発者は優先度を低くする必要があります。
スパンは存在しません。 何が来るのか、何が来ないのかは気にしません-NetStandard2にはありません
@Drawaesが指摘する@sdrapkinは、 Span<T>
が.NET Standard 1.0であるため、任意のフレームワークで使用できます。 また、参照されている実際のウィンドウにしかアクセスできないため、 ArraySegment<T>
よりも安全です。 アレイ全体ではなく。
またReadOnlySpan<T>
は、そのウィンドウへの変更を防ぎます。 ここでも、渡された配列セグメントとは異なり、渡された配列への参照を変更および/または保持できます。
Spanは、同期APIの一般的な目的です(Spanを使用するAPIは、配列だけでなく、スタックされたネイティブメモリにも対応できます。これがアイシングです)
すなわち
ArraySegmentを使用すると、読み取り専用がドキュメントから提案されます。 範囲外の読み取り/変更は防止されません
void Encrypt(
ArraySegment<byte> iv, // readonly; covered by authentication
ArraySegment<byte> plaintext, // readonly; covered by authentication
ref ArraySegment<byte> ciphertext, // must be of at least [plaintext_length + MaxTagSize] length. iv is not part of ciphertext.
ArraySegment<byte> additionalData = default(ArraySegment<byte>) // readonly; optional; covered by authentication
);
ただし、Spanでは、読み取り専用がAPIによって強制されます。 配列の範囲外の読み取りが防止されているだけでなく
void Encrypt(
ReadOnlySpan<byte> iv, // covered by authentication
ReadOnlySpan<byte> plaintext, // covered by authentication
Span<byte> ciphertext, // must be of at least [plaintext_length + MaxTagSize] length. iv is not part of ciphertext.
ReadOnlySpan<byte> additionalData = ReadOnlySpan<byte>.Empty) // optional; covered by authentication
);
これは、パラメーターの意図をはるかによく伝えます。 また、範囲外の読み取り/書き込みに関してエラーが発生しにくくなります。
@benaadams @Drawaesは、 Span<T>
がNetStandard(出荷されたNetStandard )にあるとは決して言いませんでした。 彼が言ったことは、(1) Span<T>
が出荷されたNetStandardに含まれていないことに同意することです。 (2) Span<T>
は_ "2.1の時間枠で出荷" _されます。
ただし、この特定のGithubの問題については、(読み取り専用) Span<T>
の議論は現在、バイクシェディングです。設計するAPIの範囲や目的は明確ではありません。
生の低レベルプリミティブAEADAPI(例:CAESARに類似)を使用するか、次のようにします。
または、高レベルの誤用-不可能または耐性のあるAEADAPIを使用します。
または、両方を行います。 または、分析の麻痺に入り、この問題を今すぐ解決することもできます。
コア言語の一部であるため、暗号には堅固で低レベルの基本的なAPIが必要だと強く感じています。 それができたら、高レベルのAPIまたは「補助輪」APIを作成することで、コアまたはコミュニティによってすばやくブリッジすることができます。 しかし、私は誰にでもその逆をエレガントに行うように挑戦します。 さらに、トピックは「暗号の一般的な低レベルプリミティブ」です。
@Drawaesこれを収束して解決するためのタイムラインはありますか? そのようなGitHubアラート以外にマイクロソフト以外の人々を巻き込む計画はありますか? 30分の電話会議のように? 私はうさぎの穴から離れようとしていますが、.NETコア暗号はある程度の成熟度と安定性になると確信しています..そのような議論のためにトリアージすることができます。
私たちはまだ注意を払い、これに取り組んでいます。 Microsoft Cryptography Board(Microsoftの暗号化の使用法をアドバイスする一連の研究者やその他の専門家)と会いました。 @ bartonjsは間もなく共有する詳細情報を入手する予定です。
少しのデータフローの落書きと暗号委員会のアドバイスに基づいて、私たちは次のことを思いつきました。 私たちのモデルはGCM、CCM、SIV、およびCBC + HMACでした(現在、SIVまたはCBC + HMACを実行することについて話しているのではなく、形状を証明したかっただけです)。
`` `C#
パブリックインターフェイスINonceProvider
{{
ReadOnlySpan
}
パブリック抽象クラスAuthenticatedEncryptor:IDisposable
{{
public int NonceOrIVSizeInBits {get; }
public int TagSizeInBits {get; }
public bool SupportsAssociatedData {get; }
public ReadOnlySpan
public ReadOnlySpan
protected AuthenticatedEncryptor(
int tagSizeInBits,
bool supportsAssociatedData,
int nonceOrIVSizeInBits) => throw null;
protected abstract bool TryEncrypt(
ReadOnlySpan<byte> data,
ReadOnlySpan<byte> associatedData,
Span<byte> encryptedData,
out int bytesWritten,
Span<byte> tag,
Span<byte> nonceOrIVUsed);
public abstract void GetEncryptedSizeRange(
int dataLength,
out int minEncryptedLength,
out int maxEncryptedLength);
public bool TryEncrypt(
ReadOnlySpan<byte> data,
ReadOnlySpan<byte> associatedData,
Span<byte> encryptedData,
out int bytesWritten) => throw null;
public byte[] Encrypt(
ReadOnlySpan<byte> data,
ReadOnlySpan<byte> associatedData) => throw null;
// some variant of the Dispose pattern here.
}
パブリックシールクラスAesGcmEncryptor:AuthenticatedEncryptor
{{
public AesGcmEncryptor(ReadOnlySpan
:base(128、true、96)
{{
}
}
パブリックシールクラスAesCcmEncryptor:AuthenticatedEncryptor
{{
public AesCcmEncryptor(
ReadOnlySpan
int nonceSizeInBits、
INonceProvider nonceProvider、
int tagSizeInBits)
:base(tagSizeInBits、true、nonceSizeInBits)
{{
nonceSizeとtagSizeをアルゴリズム仕様に対して検証します。
}
}
パブリック抽象クラスAuthenticatedDecryptor:IDisposable
{{
public abstract bool TryDecrypt(
ReadOnlySpan
ReadOnlySpan
ReadOnlySpan
ReadOnlySpan
スパン
out int bytesWritten);
public abstract void GetEncryptedSizeRange(
int encryptedDataLength,
out int minDecryptedLength,
out int maxDecryptedLength);
public byte[] Decrypt(
ReadOnlySpan<byte> tag,
ReadOnlySpan<byte> nonceOrIV,
ReadOnlySpan<byte> encryptedData,
ReadOnlySpan<byte> associatedData) => throw null;
// some variant of the Dispose pattern here.
}
パブリックシールクラスAesGcmDecryptor:AuthenticatedDecryptor
{{
public AesGcmDecryptor(ReadOnlySpan
}
パブリックシールクラスAesCcmDecryptor:AuthenticatedDecryptor
{{
public AesCcmDecryptor(ReadOnlySpan
}
`` `
この提案は、データストリーミングを排除します。 その点については、あまり柔軟性がありません。 現実世界のニーズ(低い)と関連するリスク(GCMの場合は非常に高い)またはその不可能性(CCM)の組み合わせは、それがなくなったことを意味します。
この提案では、暗号化に外部化されたナンスのソースを使用します。 このインターフェースのパブリック実装はありません。 各アプリケーション/プロトコルは、適切にフィードできるように、独自にキーをコンテキストに結び付ける必要があります。 TryEncryptへの各呼び出しはGetNextNonceへの1回の呼び出しのみを行いますが、その特定のTryEncryptが成功する保証はないため、それがナンスを再試行する必要があるかどうかを理解するのはアプリケーション次第です。 CBC + HMACの場合、用語の混乱を避けるために、新しいインターフェイスIIVProviderを作成します。 SIVの場合、IVが構築されるため、許容できるパラメーターはありません。 仕様に基づくと、ナンス(使用されている場合)は、associatedDataの一部と見なされているようです。 したがって、少なくともSIVは、TryEncryptのパラメーターとしてnonceOrIVを使用することは一般的に適用できないことを示唆しています。
TryDecryptは、間違いなく無効なタグをスローします。 宛先が小さすぎる場合にのみfalseを返します(Try-メソッドのルールに従って)
フィードバックを確実に受け入れるもの:
associatedData
をデフォルトのReadOnlySpan<byte>.Empty
で最後に移動する必要がありますか?byte[]
返すメソッドへの愛や憎しみを主張したい人はいますか? (スパン方式を使用することで低割り当てを実現できます。これは便宜上です)byte[]
-returningメソッドは、maxのバッファーを割り当て、次にArray.Resizeを必要に応じて割り当てます。min=max=input.Length
の場合ですが、CBC + HMACまたはSIVの場合は当てはまりません。間違いなくバイト-ビットではありません。
キーを認識していないプロバイダーは大きな間違いです。
キーを認識していないプロバイダーは大きな間違いです。
ナンスプロバイダーは好きなように書くことができます。 提供していません。
決定論的クリーンアップ/ IDisposable
どうですか?
決定論的クリーンアップ/ IDisposableはどうですか?
よろしくお願いします。 AuthenticatedEncryptor / AuthenticatedDecryptorに追加しました。 ナンスプロバイダーでの廃棄可能性を調査する必要はないと思います。呼び出し元はusingステートメントをスタックするだけです。
INonceProvider
の概念/目的は私には意味がありません(他の人にエコーする)。 プリミティブをプリミティブにします-キーを渡すのと同じ方法でナンスを渡します(つまり、バイトとして-ただし宣言されます)。 AE / AEAD仕様では、ノンスの生成/派生方法のアルゴリズムを強制しません。これは、上位層の責任です(少なくとも、プリミティブをプリミティブにするモデルでは)。
ストリーミングはありませんか? 本当に? コアの基本レベルでAES-GCMのようなストリーム暗号からストリーミングを強制的に削除する理由は何ですか?
たとえば、あなたの暗号通貨ボードは、私たちがレビューしたこれら2つの最近のシナリオをどのように推奨していますか?
クライアントには、10〜30GBの大きなヘルスケアファイルがあります。 コアは2台のマシン間でのみデータストリームを確認するため、1パスストリームです。 明らかに、10GBのファイルごとに新しいキーが発行されますが、そのようなワークフローはすべて役に立たなくなりました。 ここで、a)データをバッファリングする(メモリ、パイプライニングなし)b)暗号化を実行する(パイプライン内のすべてのマシンがアイドル状態になります!)c)データを書き出す(aとbの後に書き込まれる最初のバイトは100%)終わり) ? 冗談だと言ってください。 皆さんは故意に「暗号化は負担です」をゲームに戻しています。
物理的セキュリティユニットには複数の4Kストリームがあり、これらも静止シナリオ用に暗号化されています。 フレッシュキーの発行は15GBの境界で行われます。 クリップ全体をバッファリングすることを提案しますか?
ストリーミングサポートの削除を求めて実際に実際のソフトウェアを構築している人々のコミュニティからの意見は見当たりません。 しかし、その後、チームはコミュニティダイアログから姿を消し、内部で群がり、誰も求めていないもの、実際のアプリケーションを強制終了し、「暗号化は遅くて費用がかかるので、スキップしますか?」
エコシステム全体に決定を課す代わりに、両方のオプションをサポートするEncrypt
とEncryptFinal
を提供できます。
エレガントなデザインは、制御ではなく複雑さを排除します。
コアの基本レベルでAES-GCMのようなストリーム暗号からストリーミングを強制的に削除する理由は何ですか?
こんな感じだったと思います
この提案は、データストリーミングを排除します。 その点については、あまり柔軟性がありません。 現実世界のニーズ(低い)と関連するリスク(GCMの場合は非常に高い)またはその不可能性(CCM)の組み合わせは、それがなくなったことを意味します。
GCMには、キーの回復を可能にするおっとの瞬間が多すぎます。 攻撃者が選択された暗号文を実行し、タグ検証の前からストリーミング出力を監視できる場合、攻撃者はキーを回復できます。 (または、暗号解読者の1人が私に教えてくれます)。 事実上、タグ検証前の任意の時点でGCM処理されたデータが観察可能である場合、キーは危険にさらされています。
Crypto Boardは、最初のシナリオではGCMを使用せず、CBC + HMACを使用することを推奨すると確信しています。
2番目のシナリオが4kフレーミングであり、各4kフレームを暗号化している場合、それはこのモデルで機能します。 各4k +ナンス+タグフレームは、バイトを取り戻す前に復号化および検証されるため、キーストリーム/キーをリークすることはありません。
比較のために:私は現在、この「プリミティブをプリミティブにする」暗号化APIを開発しています。 これが認証付き暗号化の私のクラスです。
私にとっては、キーとは関係なく暗号プリミティブについて話すことができると便利であることがわかりました。 たとえば、特定のプリミティブを任意のAEADアルゴリズムで動作するメソッドにプラグインし、キーなどの生成をそのメソッドに任せたいことがよくあります。 したがって、 AeadAlgorithmクラスと個別のKeyクラスがあります。
すでにいくつかのバグを防いだもう1つの非常に便利なことは、すべてに単純なbyte[]
またはSpan<byte>
を使用する代わりに、異なる形状のデータを表すために異なるタイプを使用することです。
AeadAlgorithm API(クリックして展開)
public abstract class AeadAlgorithm : Algorithm
{
public int KeySize { get; }
public int NonceSize { get; }
public int TagSize { get; }
public byte[] Decrypt(
Key key,
Nonce nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> ciphertext)
public void Decrypt(
Key key,
Nonce nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> ciphertext,
Span<byte> plaintext)
public byte[] Encrypt(
Key key,
Nonce nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> plaintext)
public void Encrypt(
Key key,
Nonce nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> plaintext,
Span<byte> ciphertext)
public bool TryDecrypt(
Key key,
Nonce nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> ciphertext,
out byte[] plaintext)
public bool TryDecrypt(
Key key,
Nonce nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> ciphertext,
Span<byte> plaintext)
}
@bartonjs彼/彼女は正しいですあなたは認証まで出力しないプログラムに頼る必要があります。 したがって、たとえば、認証を行っていない(またはまだ認証していない)場合は、ブロックの入力を制御できるため、出力を把握し、そこから逆方向に作業できます...
たとえば、中間者攻撃では、既知のブロックをcbcストリームに注入し、従来のビットフリッピング攻撃を実行できます。
データの大きなチャンクを解決する方法がわからない場合は、シリアルナンスなどでチャンクすることに感謝します... alaTLS。
言い換えると、私が行っているのは、汎用ライブラリには不十分なネットワークの小さなサイズの場合のみです。
オープンな精神で、Microsoft Cryptography Review Boardに誰が参加しているか(理想的には、このトピックをレビューした特定のメンバーのコメント/意見)を明らかにすることは可能ですか? ブライアン・ラマッキアと他に誰?
_逆心理学を使用する:_
ストリーミングAEADが出てうれしいです。 これは、 Infernoが平均的なJoeにとって唯一の実用的なCryptoStream
ベースのストリーミングAEADであり続けることを意味します。 MS Crypto Review Boardに感謝します!
@ektrahのコメントに基づいて、彼の(彼女の?)アプローチは、私が以前に参照したRFC5116によって推進されています。 RFC5116には多くの注目すべき引用があります。
3.1。 ノンス生成の要件
セキュリティには、キーの固定値について、認証された暗号化操作の呼び出しごとに各ナンス値が異なるという要件を尊重する方法でナンスを構築することが不可欠です。
..。
- AEADアルゴリズム仕様の要件
各AEADアルゴリズムは、N_MINからN_MAXオクテットまでの長さのナンスを受け入れる必要があります。N_MINとN_MAXの値は、そのアルゴリズムに固有です。 N_MAXとN_MINの値は同じである可能性があります。 各アルゴリズムは、12オクテットの長さのナンスを受け入れる必要があります。 以下で説明するランダム化またはステートフルアルゴリズムでは、N_MAX値がゼロになる場合があります。
..。
認証付き暗号化アルゴリズムは、たとえば暗号文出力に組み込まれる内部初期化ベクトルの生成のために、ランダムなソースを組み込むか、または利用することができます(MAY)。 この種のAEADアルゴリズムは、ランダム化と呼ばれます。 ただし、暗号化のみがランダムであり、復号化は常に決定論的であることに注意してください。 ランダム化されたアルゴリズムは、ゼロに等しいN_MAXの値を持つ場合があります。
認証付き暗号化アルゴリズムは、暗号化操作の呼び出しの間に維持される内部状態情報を組み込むことができます。たとえば、アルゴリズムによって内部ナンスとして使用される個別の値の構築を可能にします。 この種のAEADアルゴリズムは、ステートフルと呼ばれます。 この方法は、アプリケーションが長さゼロのナンスを入力した場合でも、優れたセキュリティを提供するためにアルゴリズムで使用できます。 ステートフルアルゴリズムは、ゼロに等しいN_MAXの値を持つ場合があります。
検討する価値のある可能性のあるアイデアの1つは、長さがゼロ/ヌルのNonceを渡すことです。これは、デフォルトでさえある可能性があります。 このような「特別な」Nonce値を渡すと、実際のNonce値がランダム化され、Encryptの出力として使用できるようになります。
「理由」のためにINonceProvider
が残っている場合、別のアイデアはReset()
呼び出しを追加することです。これは、 AuthenticatedEncryptor
が再入力されるたびにトリガーされます。 一方、 AuthenticatedEncryptor
インスタンスのキーを再生成しない計画の場合、ストリーミングチャンク暗号化API(チャンク=ネットワークパケットなど)を構築する場合はGCが破棄され、すべてのチャンクは別のキーで暗号化されています(例:Netflix MSLプロトコル、Inferno、その他)。 特に、AEADエンジンのプールを維持し、そのプールからインスタンスを借用してenc / decを実行する並列enc / dec操作の場合。 GCに愛を与えましょう:)
私の見解では、暗号プリミティブの唯一の目的は、適切に設計された高レベルのセキュリティプロトコルを実装することです。 そのようなプロトコルはすべて、独自の方法でナンスを生成することを主張しています。 例えば:
GCMは、一般的なナンスサイズ(96ビット)のランダム化されたナンスには非常に脆弱です。 そして、私は実際にランダム化されたナンスをサポートするセキュリティプロトコルを知りません。
暗号プリミティブを提供するAPIを増やす必要はあまりありません。 開発者の99.9%は、セキュリティ関連のシナリオ(データベースへのパスワードの保存、保存中のファイルの暗号化、ソフトウェアアップデートの安全な転送など)のための高レベルのレシピを必要としています。
ただし、このような高レベルのレシピのAPIはまれです。 多くの場合、使用可能なAPIはHTTPSと暗号プリミティブのみであり、開発者は独自のセキュリティプロトコルを使用する必要があります。 IMOの解決策は、プリミティブを操作するためのAPIの設計に多くの労力を費やすことではありません。 これは、高レベルのレシピ用のAPIです。
皆さん、フィードバックをありがとう! いくつかの質問:
1。 a =ユーザーに早期に警告しないことが問題になる可能性があると思います。 上記の誰か、10GBのファイルからのシナリオを想像してみてください。 彼らはストリーミングを取得していると考え、その後、別の開発者が暗号を変更し、次にコードが値を返す前に10GBをバッファリング(または試行)しています。
@bartonjs :コミュニティの関与を欠いた密室での決定が効果的な正当化であるかどうかを一日中議論することができますが、トピックから外れるので、そうさせます。 それに加えて、より豊かな対面やリアルタイムのコミュニケーションがなければ、私はそこにいる人を怒らせたくありません。
具体的には、steaming =>タグ検証の前に復号化されたデータを呼び出し元に返します=>セキュリティなし。 これは音ではありません。 @bartonjsは、「選択暗号文=>出力の監視=>キーの回復」を主張し、 @ drawaesは、「ブロックの入力の制御=>したがって、出力を知っている=>「そこから作業する」」と主張します。
AES-GCMでは、タグが行うのは整合性検証(改ざん防止)だけです。 プライバシーへの影響はありません。 実際、AES-GCMからGCM / GHASHタグ処理を削除すると、AES-CTRモードになります。 プライバシーの側面を処理するのはこの構成です。 また、CTRはビットフリップに順応性がありますが、基本的なAESプリミティブが危険にさらされることを意味するため、2人が主張している方法(キーまたはプレーンテキストの回復)のいずれにおいても「壊れ」ません。 あなたの暗号解読者(それは誰ですか?)が私たちの他の人が知らないことを知っているなら、彼/彼女はそれを公開するべきです。 可能な唯一のことは、攻撃された人がビットNを反転し、平文のビットNが反転したことを知ることができるということです。しかし、彼らは実際の平文が何であるかを決して知りません。
そう
1)プレーンテキストのプライバシーは常に適用されます
2)整合性の検証は単純に延期され(ストリームの終わりまで)、
3)キーが危険にさらされることはありません。
ストリーミングが基本である製品やシステムの場合、少なくともAEADから通常のAES暗号化に一時的にステップダウンし、タグの検証時にAEADに戻るというトレードオフを設計できます。 これにより、「すべてをバッファリングしたいのですが、気が狂っていますか?暗号化はできません!」という代わりに、セキュリティを採用するための革新的な概念がいくつか解き放たれます。
これは、 Encrypt
とEncryptFinal
(または同等のもの)の両方ではなく、 EncryptFinal
だけを実装するためです。
現在、AES-GCMは、「おっと瞬間」が豊富な魔法の獣ではありません。 それは単にAES-CTR + GHASH(私がそうするかもしれない場合は一種のハッシュ)です。 プライバシーに関連する考慮事項はCTRモードから継承され、整合性に関連するタグの考慮事項は、仕様で許可されている可変タグサイズに由来します。 それでも、AES-CTR + GHASHは、最初のアルゴリズムがプライバシーを処理し、2番目のアルゴリズムが整合性を処理するという点でAES-CBC + HMAC-SHA256のようなものと非常に似ています。 AES-CBC + HMAC-SHA256では、暗号文のビットフリップは、復号化されたテキストの対応するブロックを破損し(CTRとは異なり)、次の復号化された平文ブロックのビットを決定論的に反転します(CTRなど)。 繰り返しになりますが、攻撃者は結果の平文がどうなるかわかりません-ビットが反転しただけです(CTRのように)。 最後に、整合性チェック(HMAC-SHA256)がそれをキャッチします。 ただし、最後のバイトのみを処理します(GHASHなど)。
したがって、整合性がOKになるまですべての復号化されたデータを差し控えるというあなたの議論が本当に良いのであれば、それは一貫して適用されるべきです。 したがって、AES-CBCパスから出力されるすべてのデータも、HMAC-SHA256が通過するまで(ライブラリによって内部的に)バッファリングされる必要があります。 つまり、基本的に.NETでは、ストリーミングデータはAEADの進歩の恩恵を受けることさえできません。 .NETは、ストリーミングデータを強制的にダウングレードします。 暗号化なしまたは通常の暗号化のどちらかを選択します。 AEADはありません。 バッファリングが技術的に実用的でない場合、アーキテクトは少なくともエンドユーザーに「ドローンの映像が破損している可能性がある」ことを「目が見えない」のではなく警告するオプションを持っている必要があります。
データはますます大きくなり、セキュリティを強化する必要があります。 ストリーミングは、設計者が採用しなければならない現実でもあります。 世界が真に統合されたAEADアルゴリズムを作成し、中流の改ざんの途中で破損をネイティブに検出できるようになるまで、私たちは暗号化と認証に固執します。 真のAEADプリミティブが研究されていますが、今のところ暗号化と認証があります。
私は「AES-GCM」についてはあまり気にしませんが、ストリーミングワークロードをサポートできる高速で人気のあるAEADアルゴリズムについては気にしません。これは、データが豊富でハイパーコネクテッドな世界で非常に普及しています。
Crypto Boardは、最初のシナリオではGCMを使用せず、CBC + HMACを使用することを推奨します。
上記のすべて、またはシナリオの詳細を除いて、AES-CBC-HMACが無料ではないことを示唆しています。 AES-CBC暗号化は並列化できないため、またGHASHはPCLMULQDQ命令を介して高速化できるため、 AES-GCMよりも約3倍遅くなります。 したがって、AES-GCMで1GB /秒の場合、AES-CBC-HMACで最大300MB /秒に達することになります。 これもまた、「暗号はあなたを遅くし、それをスキップする」という考え方を実行します-セキュリティの人々が戦うために一生懸命努力するものです。
各4kフレームの暗号化
ビデオコーデックは突然暗号化を行う必要がありますか? または、暗号化レイヤーはビデオコーデックを理解する必要がありますか? これは、データセキュリティ層のビットストリームにすぎません。 それがビデオ/ゲノムデータ/画像/独自のフォーマットなどであるという事実は、セキュリティレイヤーの懸念事項ではありません。 全体的なソリューションは、コアの責任を混同してはなりません。
NISTは、96ビットを超える長さのランダム化されたIVを許可します。 NIST800-38Dのセクション8.2.2を参照してください。 ここでは何も新しいことはありません。ナンスの要件はCTRモードに由来します。 これは、ほとんどのストリーム暗号でもかなり標準的です。 ノンスに対する突然の恐怖を理解していません-それは常にnumber used once
た。 それでも、 INonce
の議論は不格好なインターフェースになりますが、少なくとも、ストリームなしの面付けのようなイノベーションを排除するものではありません。 AEADセキュリティとストリーミングワークロードの革新を得ることができれば、いつでもINonce
に譲歩します。 イノベーションのストリーミングのような基本的なものを呼ぶのは嫌いですが、それは私たちが後退するのではないかと心配しています。
私は仕事で長い一日を過ごした後、これをタイプするために私の子供たちと一緒に映画の夜をあきらめたただの男です。 私は疲れていて、間違っている可能性があります。 しかし、少なくとも、逸話や「委員会の理由」やその他のブードゥーではなく、オープンな事実に基づくコミュニティダイアログを用意してください。 私は安全な.NETとAzureのイノベーションを促進するビジネスをしています。 目標は一致していると思います。
コミュニティのSkype通話をお願いできますか? このような複雑なトピックを表現することは、テキストの巨大な壁に吹き込みます。 かなりお願いしますか?
Skype通話は行わないでください。これは「密室の会議」の定義であり、コミュニティで利用できる記録はありません。 Githubの問題は、すべての関係者が市民の文書化された談話を持つための適切な手段です(MS-comment-removalの前例を無視します)。
MS Crypto ReviewBoardはおそらくSkype通話も行いました。 このスレッドに参加しているMSの人々のせいではありません-彼らはMSCrypto Review Boardの象牙の塔へのアクセスと説得力が非常に限られている可能性があります(それが何であれ誰でも)。
AEADのストリーミングについて:
バイトサイズのストリーミング_暗号化_は、GCM、CTR + HMACなどのMACファーストモードでは可能ですが、CCMなどのMACファーストモードでは不可能です。 バイトサイズのストリーミング_decryption_は基本的にリークしているため、誰も考慮していません。 ブロックサイズのストリーミング_encryption_はCBC + HMACでも可能ですが、それによって何も変わりません。 つまり。 AEADをストリーミングするためのバイトサイズまたはブロックサイズのアプローチには欠陥があります。
チャンクサイズのストリーミング_encryption_と_decryption_はうまく機能しますが、2つの制約があります。
それらは(ブロックサイズを超えて)バッファリングする必要があります。 これは、バッファリングが制御/上限設定されている場合(例:Inferno)、または処理するために上位層(呼び出し層)に任されている場合、ライブラリ/ APIによって実行できます。 どちらの方法でも機能します。
チャンクストリーミングAEADは標準化されていません。 元。 nacl-stream 、Inferno、MS独自のDataProtection、make-your-own。
これは、これまでの議論のすべての人がすでに知っていることの要約にすぎません。
@sdrapkin 、私が正しく理解していることを確認するために、ストリーミング暗号化を提供するがストリーミング復号化を提供しないこのAPIで大丈夫ですか?
@sdrapkinええと、リアルタイムでブレインストーミングを行う人間は確かに有益です。記録管理の懸念は議事録で解決できます。 技術的な側面に戻ると、チャンクはストリーミング復号化には機能しますが、それは低レベルのセキュリティプリミティブではありません。 これはカスタムプロトコルです。 そして、あなたが指摘したような非標準的なもの。
@morganbr
_このAPIはストリーミング暗号化を提供しますが、ストリーミング復号化は提供しませんか?_
いいえ、違います。 そのようなAPIが利用可能であれば、バッファ復号化では復号化できない(メモリ不足)サイズのストリーム暗号化暗号文を簡単に作成できます。
^^^^これまでのところ、あまり合意はありませんが、非対称APIがどのように機能するかは、私たち全員が同意できると思います。 「暗号化メソッドがあったので、私が信頼すると思ったストリーム復号化メソッドです」と、上記の@sdrapkinコメントの両方から。
@Drawaes同意しました。 非対称のenc / decAPIはひどいものになります。
更新の人はいますか?
どうやら私はいくつかの攻撃を混同しました。
ストリーム暗号(AES-CTRおよびAES-GCM)に固有の弱点により、選択暗号文攻撃が可能になり、任意の平文回復が可能になります。 選択暗号文攻撃に対する防御は認証です。 したがって、AES-GCMは影響を受けません...ストリーミング復号化を実行していて、サイドチャネル監視から平文が何であったかを識別できない場合を除きます。 たとえば、復号化されたデータがXMLとして処理されている場合、空白または<以外の文字が復号化されたデータの先頭にあると、非常に迅速に失敗します。 つまり、「ストリーミング復号化は、ストリーム暗号設計に関する懸念を再導入します」(お気づきかもしれませんが、.NETには何もありません)。
キーの回復がどこから来たのかを探している間、認証のような論文がありますGCM(Ferguson / Microsoft)の弱点ですが、短いタグサイズに基づいて認証キーを回復しています(これは、Windowsの実装で96ビットタグのみが許可される理由の一部です)。 GCMのストリーミングが危険である理由について、他の認証キー回復ベクトルについておそらくアドバイスを受けました。
以前のコメントで、 @ sdrapkinは、「バイトサイズのストリーミング復号化は基本的にリークしているため、誰にも考慮されていません。...ストリーミングAEADへのバイトサイズまたはブロックサイズのアプローチには欠陥があります。」 これは、CCM(およびSIV)がストリーミング暗号化を実行できないことと相まって、一方のストリーミングが他方ではないというコメントは奇妙であり、ワンショット暗号化と復号化。
つまり、前回のAPI提案(https://github.com/dotnet/corefx/issues/23629#issuecomment-329202845)に戻ったようです。 休みの間に忘れてしまった未解決の問題が他にない限り。
@bartonjsへようこそ
私はすぐに、しかし簡単に寝るつもりです:
このスレッドでは、以前にプロトコル設計とプリミティブ設計を統合しました。 選択暗号文攻撃はプロトコル設計の問題であり、原始的な問題ではありません。
ストリーミングAEAD復号化により、少なくともプライバシーを確保し、最後のバイトですぐにプライバシー+信頼性にアップグレードできます。 AEADでのストリーミングサポート(つまり、従来のプライバシーのみ)がなければ、人々をより低いプライバシーのみの保証に恒久的に制限することになります。
技術的なメリットが不十分であるか、あなたが私の議論の権威に(当然のことながら)懐疑的である場合、私は外部の権威ルートを試してみます。 実際の基盤となる実装は、ストリーミングモードでAEAD(AES GCMを含む)をサポートしていることを知っておく必要があります。 WindowsコアOS( bcrypt
)では、 BCryptEncrypt
またはBCryptDecrypt
関数を介してGCMをストリーミングできます。 そこでdwFlagsを参照してください。 またはユーザーコードの例。 または、Microsoftが作成したCLRラッパー。 または、実装が今年初めにNISTFIP-140-2認定を受けていること。 または、MicrosoftとNISTの両方がAES実装に多大なリソースを費やし、こことここでそれを認定したこと。 そして、これらすべてにもかかわらず、誰もプリミティブに誤りを犯していません。 .NET Coreが突然登場し、強力な基盤となる実装を弱体化させるために独自の暗号化を課すことはまったく意味がありません。 特に、ストリーミングとワンショットの両方を同時にサポートできる場合は、非常に簡単です。
もっと? 上記は、「新しい」evp APIを使用している場合でも、OpenSSLに当てはまります。
そしてそれはBouncyCastleにも当てはまります。
そして、それはJava暗号化アーキテクチャにも当てはまります。
乾杯!
シド
@sidshetye ++ 10暗号ボードが非常に懸念している場合、なぜWindows CNGにこれを行わせるのですか?
MicrosoftのNISTFIPS-140-2 AES検証(例:# 4064 )を確認すると、次のことに気付くでしょう。
AES-GCM:
AES-CCM:
ストリーミングの検証はありません。 NISTがその元をチェックするかどうかさえわかりません。 AES-GCMの実装では、64Gbを超えるプレーンテキストを暗号化することはできません(GCMのもう1つのばかげた制限)。
私の使用は16kを超えてはいけないので、私はストリーミングに大いに夢中になっているわけではありませんが、断片化されたバッファーは素晴らしく、リスクをまったくもたらさないはずです(cngがまさにその目的のためのインターフェースを作ったのではないかと思います)...たとえば、いくつかのスパンまたは同様のもの(たとえばリンクリスト)を渡して、一度に復号化できるようにしたい。 連続したバッファに復号化する場合は、すべて問題ありません。
ですから、「ストリーミングスタイル」のAPIでシャドウクリプトボードを移動するのは今のところダメだと思います。それでは、ワンショットAPIを作成していきましょう。 十分な数の人が後で必要性を示した場合、APIを拡張する余地は常にあります
@sdrapkinの要点は、NISTLabsとMSFTによる広範なレビューを受けたのはストリーミングAPIであるということです。 検証される各ビルドは80,000ドルから50,000ドルの間であり、MSFT(およびOpenSSLとOracleおよびその他の暗号の大物)は、これらのAPIと実装を10年以上検証するために多額の投資を行ってきました。 .NETは、ストリーミングまたはワンショットに関係なく、0、8、1016、1024以外のサイズをサポートすると確信しているため、テストプランの特定のプレーンテキストサイズに気を取られないようにします。 重要なのは、これらすべての戦闘テスト済みAPI(文字通り、武器サポートシステム上)であり、これらすべてのプラットフォームで、暗号プリミティブAPIレベルでのストリーミングAEADをサポートします。 残念ながら、これまでのところ、これに反対するすべての議論は、暗号プリミティブレベルでの疑似懸念として引用されているアプリケーションまたはプロトコルレベルの懸念でした。
私はすべて「最高のアイデアを勝ち取ろう」と思っていますが、.netコア暗号化チーム(MSFTまたはコミュニティ)が画期的な発見を持っていない限り、これまでのところ、すべての異なる組織からの暗号化を行っているすべての人が間違っていることはわかりませんそして彼らは正しい。
PS:私たちはここで意見の相違があることを知っていますが、私たちは皆、プラットフォームとその顧客にとって最良のものを望んでいます。
@Drawaes今日定義されているAEADインターフェイス(必ずしも実装ではない)がストリーミングAPIサーフェスをサポートしていない限り、2つのインターフェイスまたはカスタムインターフェイスがなくても、どのように拡張できるかわかりません。 それは惨事でしょう。 この議論が将来を保証するインターフェースにつながることを願っています(または、少なくとも、長年にわたって使用されてきた他のAEADインターフェースを反映しています!)。
私は同意する傾向があります。 しかし、この問題はどこにも速くは進んでおらず、それが発生すると、2.1に到達しないか、問題を解決するための時間がないまま突っ込んでいく必要があります。 正直に言うと、古いラッパーに戻って、2.0用にリニューアルしているところです;)
Java 、 OpenSSL 、 C#Bouncy Castle 、 CLRSecurity用のリファレンスAPIがいくつかあります。 率直に言って、長期的には、C#にJavaの「Java暗号化アーキテクチャ」のようなものが必要です。すべての暗号化実装は、ユーザーコードに影響を与えることなく、暗号化ライブラリを交換できる確立されたインターフェイスに反しています。
ここに戻って、.NET CoreのICryptoTransform
インターフェイスを次のように拡張するのが最善だと思います
public interface IAuthenticatedCryptoTransform : ICryptoTransform
{
bool CanChainBlocks { get; }
byte[] GetTag();
void SetExpectedTag(byte[] tag);
}
すべてのbyte[]
をSpan
化する場合、全体的な一貫性を保つために、 System.Security.Cryptography
名前空間のAPI全体に浸透する必要があります。
編集:JCAリンクを修正
すべてのbyte []をスパン化する場合、全体的な一貫性を保つために、System.Security.Cryptography名前空間のAPI全体に浸透する必要があります。
私たちはすでにそれをしました。 インターフェイスを変更できないため、ICryptoTransformを除くすべて。
.NETCoreのICryptoTransformを拡張するのが最善だと思います...
これに伴う問題は、呼び出しパターンが最後にタグを取得するのが非常に厄介なことです(特に、CryptoStreamが関係している場合)。 もともと書いたのですが、醜いです。 GCMパラメータはCBC / ECBパラメータとは異なるため、これらのいずれかを取得する方法の問題もあります。
だから、ここに私の考えがあります。
そのための私のかなり生の考え(既存の提案に追加するので、要約ではなく仮想のデフォルトのimplとして推測しますが、ワンショットは残ります):
`` `C#
部分クラスAuthenticatedEncryptor
{{
//操作がすでに進行中の場合にスローします
public abstract void Initialize(ReadOnlySpan
//成功した場合はtrue、「宛先が小さすぎる」場合はfalse、それ以外の場合は例外。
public abstract bool TryEncrypt(ReadOnlySpan
// leftEncryptedDataが小さすぎる場合はfalse、他の入力が小さすぎる場合はスローします。NonceOrIVSizeInBitsおよびTagSizeInBitsプロパティを参照してください。
// NonceOrIvUsedはInitializeに移動できますが、入力として解釈される可能性があります。
public abstract bool TryFinish(ReadOnlySpan
}
部分クラスAuthenticatedDecryptor
{{
//操作がすでに進行中の場合にスローします
public abstract void Initialize(ReadOnlySpan
//成功した場合はtrue、「宛先が小さすぎる」場合はfalse、それ以外の場合は例外。
public abstract bool TryDecrypt(ReadOnlySpan
//不正なタグをスローしますが、とにかくデータをリークする可能性があります。
//(CBC + HMACにはremainingDecryptedDataが必要なので、remainingDataを追加することもできますか?)
public abstract bool TryFinish(ReadOnlySpan
}
`` `
AssociatedDataはInitializeにあります。これは、最後にそれを必要とするアルゴリズムがそれを保持でき、最初にそれを必要とするアルゴリズムが他の方法でそれを保持できないためです。
ストリーミングがどのように見えるか(そして、ストリーミング暗号化モードのときにCCMが内部でバッファリングするべきか、スローするべきか)の形が決まったら、ボードに戻ります。
@bartonjs暗号化/復号化全体で対称性を保つために、ストリームの最後からタグを取り出してプログラミングすることの意味を知っています。 トリッキーですが、各ユーザーに解決を任せた場合はさらに悪化します。 MITで共有できる実装があります。 私のチームの内部を見る必要があります(私の机/携帯電話ではありません)
中間点は、OpenSSLやNTのbcryptのようなもので、最後の復号化呼び出しの直前にタグをプラグインする必要があります。これは、タグの比較が行われるためです。 つまり、 SetExpectedTag
(最終的な復号化の前)とGetTag
(最終的な暗号化の後)は機能しますが、タグ管理をユーザーにオフロードします。 ほとんどの場合、タグは自然な時間的順序であるため、暗号ストリームにタグを追加するだけです。
Initialize
自体(復号化)のタグを期待すると、空間(バイトフロー)と時間(開始ではなく終了時のタグチェック)の対称性が失われ、その有用性が制限されると思います。 しかし、上記のタグAPIはそれを解決します。
また、暗号化の場合、 Initialize
は暗号変換の前にIVを必要とします。
最後に、暗号化と復号化のために、 Initialize
は変換の前にAES暗号化キーを必要とします。 (私は明らかな何かが欠けているか、あなたはそのビットを入力するのを忘れましたか?)
初期化自体(復号化)でタグを期待すると、対称性が失われると思います
CBC + HMACでは、通常、復号化を開始する前にHMACを検証することをお勧めします。これは、タグファーストの復号化アルゴリズムです。 同様に、計算中にタグに対して破壊的な操作を行い、最終的な答えが0であることを確認するだけの「純粋なAE」アルゴリズムが存在する可能性があります。したがって、関連するデータ値と同様に、最初にそれを必要とするアルゴリズムが存在する可能性があるため、完全に一般化されたAPIで最初に来ること。
それらをSetAssociatedData
とSetTag
にフロートさせると、基本クラスはアルゴリズムに依存しませんが、使用法はアルゴリズムに依存するという問題があります。 AesGcmをAesCbcHmacSha256またはSomeTagDesctructiveAlgorithmに変更すると、タグがまだ提供されていないため、TryDecryptがスローされるようになりました。 私にとって、それはまったく多形ではないよりも悪いので、柔軟性を許可することは、モデルを分解してアルゴリズムごとに完全に分離することを示唆しています。 (はい、 NeedsTagFirst
のようなより多くのアルゴリズム識別特性プロパティによって制御できますが、それは実際には使用が難しくなるだけです)
また、暗号化の場合、Initializeは暗号変換の前にIVを必要とします。
最後に、暗号化と復号化のために、Initializeは変換の前にAES暗号化キーを必要とします。
キーはクラスctorパラメーターでした。 IV / nonceは、ctorパラメーターのIV / nonceプロバイダーから取得されます。
プロバイダーモデルはSIVを解決します。暗号化中にIVが与えられない場合、データに代わってIVが生成されます。 それ以外の場合、SIVにはパラメーターがあり、空の値を指定する必要があります。
またはあなたはそのビットを入力するのを忘れましたか?
ストリーミングメソッドは、ctorパラメーターとしてキーとIV / nonceプロバイダーをすでに持っている私の既存の提案に追加されていました。
@bartonjs :一部のアルゴが最初にタグを付け、他のアルゴが最後にタグを付けることができるという良い点と、それが元の仕様への追加であることを思い出させてくれてありがとう。 ユースケースを検討すると簡単になることがわかったので、クラウドファーストの例を次に示します。
ストレージに保持されている1つ以上の10GBAES-GCM暗号化ファイル(つまり、暗号文の後のタグ)に対して分析を実行します。 分析のワーカーは、複数のインバウンドストリームを別々のマシン/クラスターに同時に復号化し、最後のバイト+タグのチェックの後、各分析ワークロードを開始します。 すべてのストレージ、ワーカー、分析VMはAzureUS-Westにあります。
ここでは、すべてのストリームの最後でタグをフェッチして、AuthenticatedDecryptorのInitialize
メソッドに提供する方法はありません。 そのため、ユーザーがGCMを使用するためにコードを変更することを志願したとしても、APIの使用を開始することすらできません。
考えてみると、さまざまなAEADに対応し、ユーザーコードを変更しないAPIを使用できる唯一の方法は、さまざまなAEADアルゴリズムの暗号プロバイダーが自動的にタグを処理する場合です。 Javaは、GCMの暗号文の最後にタグを配置することでこれを行い、ユーザーの介入なしに復号化中にタグを取り出します。 それ以外は、誰かがアルゴリズムを大幅に変更するときはいつでも(たとえば、CBC-HMAC => GCM)、タグファーストとタグラスト処理の相互に排他的な性質のために、コードを変更する必要があります。
私見、私たちは最初に
また
オプション1は、バッファー管理が複雑になる可能性があるため、ライブラリーコンシューマーの全体的なエクスペリエンスを実際に簡素化します。 ライブラリでうまく解決すれば、各ユーザーが毎回解決する必要はありません。 さらに、すべてのAEADは同じインターフェイス(タグファースト、タグラスト、タグレス)を取得し、アルゴリズムの交換も簡単です。
私の投票はオプション1になります。
最後に、実装を掘り下げて、GCMを介したICryptoTransform
ストリーミング操作でタグのインストリームソースを自動的に抽出できるようにしました。 これはCLRSecurity独自のラッパーに対する重要な更新であり、追加のバッファーコピーにもかかわらず、それでも非常に高速です(Windows10ブートキャンプのテストMacbookProでは約4GB /秒)。 基本的に、CLRセキュリティをラップして自分たちでオプション1を作成したので、他の場所でそれを行う必要はありません。 このビジュアルは、 ICryptoTransform
インターフェイスのTransformBlock
とTransformFinalBlock
内で何が起こっているかを説明するのに役立ちます。
@sidshetyeクラウドファーストの例がブロックされている理由がわかりません。 ストレージから読み取る場合は、最初に最後の数タグバイトをダウンロードして、それを復号化コンストラクターctorに提供できます。 Azure Storage APIを使用する場合、これはCloudBlockBlob.DownloadRangeXxx
を介して実行されます。
@GrabYourPitchforksこの例ではあまり気にしないでください。ただし、これはAzure BlobStorageの特定の機能です。 一般に、VMベースのストレージ(IaaS)またはAzureストレージ以外のワークロードは、通常、シークできないネットワークストリームを取得します。
私は個人的に、 @ GrabYourPitchforksを見るのをとても楽しみにしています-イェーイ!
ストレージに保持されている1つ以上の10GBAES-GCM暗号化ファイル(つまり、暗号文の後のタグ)に対して分析を実行します。 分析のワーカーは、複数のインバウンドストリームを別々のマシン/クラスターに同時に復号化し、最後のバイト+タグのチェックの後、各分析ワークロードを開始します。 すべてのストレージ、ワーカー、分析VMはAzureUS-Westにあります。
@sidshetye 、あなたはダムと危険なプリミティブとスマートとハグ可能なプロトコルを別々に保つことに固執していました! 私には夢がありました-そして私はそれを信じました。 そして、あなたはこれを私たちに投げます。 これはプロトコルであり、システム設計です。 あなたが説明したそのプロトコルを設計した人は誰でも-めちゃくちゃになりました。 四角いペグを丸い穴に合わせることができないことを叫ぶのは今のところ意味がありません。
GCMで暗号化された10Gbファイルは、プリミティブエッジの近くに危険なほど存在しているだけでなく(64Gb以降はGCMは適切ではありません)、暗号文全体をバッファリングする必要があるという暗黙のアサーションもありました。
10GbファイルをGCMで暗号化する人は誰でも、圧倒的な確率でプロトコルの間違いを犯しています。 解決策:チャンク暗号化。 TLSには可変長の16k制限チャンクがあり、他にも、より単純なPKIフリーのフレーバーがあります。 この架空の例の「クラウドファースト」の色気は、デザインの間違いを減らすものではありません。
(私はこのスレッドでやるべきことがたくさんあります。)
@sdrapkinは、データ保護レイヤーからIAuthenticatedEncryptor
インターフェースを再利用することについて指摘しました。 正直なところ、データ保護レイヤーは暗号化の実行方法についてかなり意見が分かれているため、これがプリミティブの正しい抽象化ではないと思います。 たとえば、IVまたはナンスの自己選択を禁止し、準拠する実装がAADの概念を理解することを義務付け、ある程度独自の結果を生成します。 AES-GCMの場合、 IAuthenticatedEncryptor.Encrypt
からの戻り値は、サブキーの派生に使用される奇妙なほとんど何もないものの連結です。これは、提供されたプレーンテキスト上でAES-GCMを実行した結果の暗号文です(ただし、 AAD!)、およびAES-GCMタグ。 したがって、保護されたペイロードの生成に関連する各ステップは安全ですが、ペイロード自体は受け入れられている規則に従わず、データ保護ライブラリ以外に、結果の暗号文を正常に復号化できる人を見つけることはできません。 そのため、アプリ開発者向けのライブラリには適していますが、プリミティブによって実装されるインターフェイスには恐ろしい候補になります。
また、認証されたすべての暗号化アルゴリズムが実装することになっているOne True Interface(tm) IAuthenticatedEncryptionAlgorithm
を使用することに大きな価値はないと思います。 これらのプリミティブは、単純なブロック暗号プリミティブやハッシュプリミティブとは異なり、「複雑」です。 これらの複雑なプリミティブには、変数が多すぎます。 プリミティブAEのみですか、それともAEADですか? アルゴリズムはIV /ナンスをまったく受け入れますか? (そうでないものもいくつか見ました。)入力IV /ナンスまたはデータをどのように構造化する必要があるかについて懸念はありますか? IMOの複雑なプリミティブは、単純にスタンドアロンAPIである必要があり、高レベルのライブラリは、関心のある特定の複雑なプリミティブをサポートします。 次に、上位レベルのライブラリは、シナリオに適していると思われる統一されたAPIを公開します。
@sdrapkinまた話題から外れます。 システムはプリミティブを使用して構築されているとだけ言っておきます。 ここでの暗号プリミティブはむき出しで強力です。 システム/プロトコル層がバッファリングを処理している間、 これもクラスターレベルで、ワンショットプリミティブが強制するメインシステムメモリにはありません。 「チャンキング」境界はX(ここではX = 10GB)です。これは、クラスターのバッファー容量がほぼ無制限であり、最後のバイトがクラスターにロードされるまで何も開始されないため、64GB未満であるためです。 これはまさに関心の分離であり、私が話している強みに合わせて各レイヤーを最適化します。 そして、これは、基盤となるプリミティブが上位レイヤーの設計/制限をハンディキャップしない場合にのみ発生する可能性があります(より多くの現実世界のアプリには独自のレガシーハンディキャップが付属していることに注意してください)。
NIST 800-38d sec9.1の状態:
許可されていない当事者がIVの生成を制御したり、影響を与えたりするのを防ぐために、
GCMは、次の要件を満たす暗号化モジュール内にのみ実装する必要があります。
FIPSパブ。 140-2。 特に、モジュールの暗号化境界には、
セクションの構造の1つに従ってIVを生成する「生成ユニット」。 上記8.2。
FIPS140-2の要件に照らして検証するためのモジュールの文書化
モジュールがIVの一意性要件にどのように準拠しているかを説明します。
これは、GCM IVを内部で自動生成する必要があることを意味します(外部から渡さないでください)。
@sdrapkin良い点ですが、さらに詳しく読むと、96ビット以上のIVの長さの場合、セクション8.2.2では、少なくとも96ビットがランダムであるランダムビットジェネレーター(RBG)を使用してIVを生成できます(他のビットは0である可能性があります)。 私は先月、このスレッド自体でこれについて言及しました(ここではナンスの下で)。
LT; DR: INonce
は、NISTおよびFIPSガイドラインへの違反につながるトラップです。
セクション9.1は、FIPS 140-2の場合、IV生成ユニット(完全にランダム、つまり秒8.2.2または決定論的実装、つまり秒8.2.1)は、FIPS検証を受けるモジュール境界内になければならないと簡単に述べています。 以来 ...
... FIPS認定を受けているほとんどの暗号ライブラリ(OracleのJava、WinNTのbcryptprimitives、OpenSSLなどを参照)は、IVにRBGルートを使用し、入力にバイト配列を使用します。 INonce
インターフェイスを持つことは、ユーザーがそのインターフェイスの実装を暗号化関数に渡す必要があることを暗黙的に示唆しているため、実際にはNISTおよびFIPSの観点からはトラップであることに注意してください。 ただし、 INonce
のユーザー実装は、9か月以上および5万ドル以上のNIST認定プロセスを経ていないことがほぼ保証されています。 ただし、RGB構造(すでに暗号ライブラリにある)を使用してバイト配列を送信したばかりの場合は、ガイドラインに完全に準拠しています。
前にも言いましたが、これらの既存の暗号ライブラリはAPIサーフェスを進化させ、複数のシナリオでバトルテストされています。 この長いスレッドで触れたこと以上のものです。 私の投票は、車輪の再発明を試みるのではなく、それらすべてのライブラリ、すべての検証、およびすべてのインストールにわたってその知識と経験を活用することです。 車輪の再発明をしないでください。 ロケットを発明するためにそれを使用してください:)
したがって、最後の具体的な提案はhttps://github.com/dotnet/corefx/issues/23629#issuecomment-334328439からのものです。
partial class AuthenticatedEncryptor
{
// throws if an operation is already in progress
public abstract void Initialize(ReadOnlySpan<byte> associatedData);
// true on success, false on “destination too small”, exception on anything else.
public abstract bool TryEncrypt(ReadOnlySpan<byte> data, Span<byte> encryptedData, out int bytesRead, out int bytesWritten);
// false if remainingEncryptedData is too small, throws if other inputs are too small, see NonceOrIVSizeInBits and TagSizeInBits properties.
// NonceOrIvUsed could move to Initialize, but then it might be interpreted as an input.
public abstract bool TryFinish(ReadOnlySpan<byte> remainingData, Span<byte> remainingEncryptedData, out int bytesWritten, Span<byte> tag, Span<byte> nonceOrIvUsed);
}
partial class AuthenticatedDecryptor
{
// throws if an operation is already in progress
public abstract void Initialize(ReadOnlySpan<byte> tag, ReadOnlySpan<byte> nonceOrIv, ReadOnlySpan<byte> associatedData);
// true on success, false on “destination too small”, exception on anything else.
public abstract bool TryDecrypt(ReadOnlySpan<byte> data, Span<byte> decryptedData, out int bytesRead, out int bytesWritten);
// throws on bad tag, but might leak the data anyways.
// (remainingDecryptedData is required for CBC+HMAC, and so may as well add remainingData, I guess?)
public abstract bool TryFinish(ReadOnlySpan<byte> remainingData, Span<byte> remainingDecryptedData, out int bytesWritten);
}
それ以来、いくつかの潜在的な問題が提起されています。
INonceProvider
は、不必要に複雑であるか、NISTおよびFIPSガイドラインへの違反につながる可能性があります。私は次のことを提案したいと思います:
byte[]
と同等のものに傾倒します。 ただし、実装のスワッピングは、注入されたINonceProvider
を使用するとよりシームレスになります。 @sidshetyeのコメントは反駁できないのでしょうか、それとも単にRNGを呼び出すだけの単純なINonceProvider
の含意は、依然として準拠していると見なすことができますか?INonceProvider
からIIVProvider
への名前変更、およびnonceOrIv*
からiv*
への名前変更を要求します。 結局のところ、私たちは常にIVを扱っていますが、必ずしもナンスを扱っているわけではありません。事前のタグは私のシナリオのスターターではないので、おそらく自分の補足を保持します。 この分野で高性能コードを書くのがみんなのお茶かどうかはわかりません。
問題は、不要な遅延が発生することです。 フレームのデコードを開始するには、メッセージ全体を事前にバッファリングして、最後にタグを取得する必要があります。 つまり、基本的にIOと復号化をオーバーラップさせることはできません。
最後にそれを許可するのがなぜそんなに難しいのかわかりません。 しかし、私はこのAPIの障害を取り除くつもりはありません。それは、私のシナリオではまったく興味がないだけです。
IVは一般的な用語であり、ナンスは特定の種類のIVですよね?
いいえ。 nonce
は1回使用される番号です。 ナンスを指定するアルゴリズムは、再利用がアルゴリズムの保証に違反していることを示します。 GCMの場合、同じキーと異なるメッセージで同じナンスを使用すると、GHASHキーが危険にさらされ、GCMがCTRに低下する可能性があります。
http://nvlpubs.nist.gov/nistpubs/ir/2013/NIST.IR.7298r2.pdfから:
Nonce:同じキーで繰り返されることのないセキュリティプロトコルで使用される値。 たとえば、チャレンジ/レスポンス認証プロトコルでチャレンジとして使用されるナンスは、通常、認証キーが変更されるまで繰り返さないでください。 そうしないと、リプレイ攻撃の可能性があります。 ナンスをチャレンジとして使用することは、ランダムチャレンジとは異なる要件です。これは、ナンスが必ずしも予測できないわけではないためです。
「IV」には、同じ厳しい要件はありません。 たとえば、CBCでIVを繰り返すと、暗号化されたメッセージが同じIVの前のメッセージと同じか異なるかだけがリークします。 アルゴリズムを弱めることはありません。
ナンスは一度使用される数です。
「IV」には、同じ厳しい要件はありません。
@bartonjsはい。 ナンスは暗号プリミティブを初期化するために使用されるので、それはその初期化ベクトルであると私は推論します。 それは私が見つけることができるIVの定義に完全に準拠しています。 牛であることが動物であるよりも厳しい要件を持っているのと同じように、それはより厳しい要件を持っています。 現在の文言は「cowOrAnimal」パラメータを要求しているようです。 異なるモードがIVの異なる要件を持っているという事実は、それらがすべて何らかの形のIVを要求しているという事実を変更しません。 足りないものがある場合は、必ず現在の文言を維持してください。ただし、私が知る限り、「iv」または「IIVProvider」だけが単純で正しいものです。
nonceOrIv
バイクシェディングにふけるには:
96ビットGCMIVは、4バイトのsalt
および8バイトのnonce
として定義される場合があります(例:RFC 5288)。 RFC 4106は、GCM nonce
を4バイトのsalt
および8バイトのiv
$として定義しています。 RFC 5084(CMSのGCM)は、CCMはnonce
を取り、GCMはiv
を取りますが、_ "... AES-CCMとAES-GCMの共通の用語セットを持つと述べています、AES-GCM IVは、このドキュメントの残りの部分ではナンスと呼ばれます。 "_ RFC 5647(SSHのGCM)は_"注:[RFC5116]では、IVはナンスと呼ばれます。 "_ RFC 4543( IPSecのGCM)は、_「パケット内のIVフィールドと区別するために、AES-GMACIV入力をナンスと呼びます。」_ RFC7714(SRTPのGCM)は、12バイトのIV
について説明しています。
ほとんどのGCM仕様で一貫性が完全に欠如していることを考えると、 nonceOrIv
ちょっと理にかなっています。 $ 0.02
ここで自分自身を表明する他の顧客のように、事前にタグを要求することは私たちにとっても初心者ではありません。 この人為的に導入された制限により、.NETが同時ストリームを処理する方法はありません。 スケーラビリティを完全に殺します。
それが複雑さを増すという主張を裏付けることができますか? それは実際には些細なことだからです。 さらに、プラットフォーム固有の暗号化実装(ラッピングする)にはこの制限はありません。 具体的には、入力タグは、計算されたタグと比較して、単に一定時間である必要があるためです。 また、計算されたタグは、 TryFinish
の間に最後のブロックが復号化された後にのみ使用可能になります。 したがって、基本的に、実装を開始すると、 TryFinish
までインスタンス内にtag
を格納しているだけであることがわかります。 あなたはそれをオプションの入力として非常にうまく持つことができます
public abstract bool TryFinish(ReadOnlySpan<byte> remainingData,
Span<byte> remainingDecryptedData,
out int bytesWritten,
ReadOnlySpan<byte> tag = null); // <==
また、すべての暗号シナリオをカバーする単一のインターフェースに正規化するのに一生懸命努力していると思います。 私も一般化されたインターフェースを好みますが、機能やスケーラビリティを犠牲にすることは決してありません。特に、言語自体の標準的なcrytoライブラリのような基本的なレイヤーではそうです。 私見ですが、そうしていることに気付いた場合、それは通常、抽象化に欠陥があることを意味します。
単純で一貫性のあるインターフェースが必要な場合は、Javaアプローチをお勧めします。これも以前にオプション1として取り上げました。 また、上記のタグファースト/タグラストの問題を、アルゴリズムの実装内に保持することで回避します(IMHO、そうあるべきだと思います)。 私のチームはこれを実装していないので、それは私たちの決定ではありませんが、決定を下して実装を開始する必要がある場合は、確実にこのルートに進みます。
INonce
インターフェースは避けてください。準拠した低レベルのインターフェースには、単純なbyte[]
またはspan<>
で十分です。
IV vsNonce-一般化されたケースは確かにIVです。 GCMの場合、IVはノンスである必要があります(例:Car vs RedOrCar)。 そして、これをコピーして貼り付けているときに、 @ timovzlが非常によく似た例を使用していることに気づきました:)
@sidshetye (1)事前にタグを必要とするアルゴリズムをサポートし、(2)他のすべての状況でTryFinish
までのタグのみを必要とするという正確な提案をすることができますか?
次のような方向で考えていると思いますか?
Initialize
のタグはnullにすることができます。 事前にそれを必要とするアルゴリズムだけがnullをスローします。TryFinish
のタグは必須です。または、(または)事前にタグをすでに必要としているアルゴリズムではnullにすることができます。上記は、ドキュメントとノウハウの形で複雑さを増すだけだと思います。 低レベルのAPIの場合、とにかく適切なドキュメントとノウハウが必要になるため、これは小さな犠牲と見なすことができます。
他の実装との互換性とストリーミングのために、これが可能であるはずだと私は確信し始めています。
@timovzl確かに、私はこれのために明日しばらく予算を立てたいと思っています。
@Timovzl 、今日は時間があったのですが、これはかなりうさぎの穴でした! これは長いですが、ほとんどのユースケースをキャプチャし、.NET Core / Standard方向( Span<>
)を採用しながら.NET暗号の長所( ICryptoTransform
)をキャプチャすると思います。 読み直しましたが、以下にタイプミスがないことを願っています。 また、スピーディーなブレーンストーミングには、リアルタイムのコミュニケーション(チャット、電話会議など)が不可欠だと思います。 ぜひご検討ください。
最初に、APIのユーザー向けに作成されたプログラミングモデルについて説明します。
var aesGcm = new AesGcm();
using (var encryptor = aesGcm.CreateAuthenticatedEncryptor(Key, IV, AAD))
{
using (var cryptoOutStream = new CryptoStream(cipherOutStream, encryptor, CryptoStreamMode.Write))
{
clearInStream.CopyTo(cryptoOutStream);
}
}
var aesGcm = new AesGcm();
using (var decryptor = aesGcm.CreateAuthenticatedDecryptor(Key, IV, AAD))
{
using (var decryptStream = new CryptoStream(cipherInStream, encryptor, CryptoStreamMode.Write))
{
decryptStream.CopyTo(clearOutStream);
}
}
非ストリーミングはストリーミングの特殊なケースであるため、上記のユーザーコードをAuthenticatedSymmetricAlgorithm
(以下に定義)のヘルパーメソッドにラップして、より単純なAPIを公開できます。 すなわち
public class AesGcm : AuthenticatedSymmetricAlgorithm
{
...
// These return only after consuming entire input buffer
// Code like Streaming Encrypt from above within here
public abstract bool TryEncrypt(ReadOnlySpan<byte> clearData, Span<byte> encryptedData);
// Code like Streaming Decrypt from above within here
public abstract bool TryDecrypt(ReadOnlySpan<byte> encryptedData, Span<byte> clearData);
...
}
これは、次のような単純なAPIの提示を兼ねることができます。
var aesGcm = new AesGcm(Key, IV, AAD);
aesGcm.TryEncrypt(clearData, encryptedData);
var tag = aesGcm.Tag;
var aesGcm = new AesGcm(Key, IV, AAD);
aesGcm.Tag = tag;
aesGcm.TryDecrypt(encryptedData, clearData);
corefxソースを見ると、Span <>はいたるところにあります。 これにはSystem.Security.Cryptography。*が含まれます-対称暗号を除いて、その最初の層で認証された暗号化を上に修正しましょう。
ICipherTransform
を作成しますこれは、スパン対応バージョンのICryptoTransform
のようなものです。 フレームワークのアップグレードの一環としてインターフェース自体を変更するだけですが、人々はそれについて敏感になる可能性があるため、私はそれをICipherTransform
と呼んでいます。
public partial interface ICipherTransform : System.IDisposable
{
bool CanReuseTransform { get; }
bool CanTransformMultipleBlocks { get; } // multiple blocks in single call?
bool CanChainBlocks { get; } // multiple blocks across Transform/TransformFinal
int InputBlockSize { get; }
int OutputBlockSize { get; }
int TransformBlock(ReadOnlySpan<byte> inputBuffer, int inputOffset, int inputCount, Span<byte> outputBuffer, int outputOffset);
Span<byte> TransformFinalBlock(ReadOnlySpan<byte> inputBuffer, int inputOffset, int inputCount);
}
ICryptoTransform
を[Obsolete]
$としてマークします.NET暗号の知識がある人に礼儀正しくする
[Obsolete("See ICipherTransform")]
public partial interface ICryptoTransform : System.IDisposable { ... }
SymmetricAlgorithm
クラスを拡張しますpublic abstract class SymmetricAlgorithm : IDisposable
{
...
public abstract ICipherTransform CreateDecryptor(ReadOnlySpan<byte> Key, ReadOnlySpan<byte> IV);
public abstract ICipherTransform CreateEncryptor(ReadOnlySpan<byte> Key, ReadOnlySpan<byte> IV);
public virtual ReadOnlySpan<byte> KeySpan {...}
public virtual ReadOnlySpan<byte> IVSpan {...}
public virtual ReadOnlySpan<byte> KeySpan {...}
...
}
CryptoStream
を拡張しますこれは、System.RuntimeのStream
と同じです。 さらに、AEADケースに続くc'torを追加します。
重要: CryptoStream
は、暗号化中にストリームの最後にタグを追加し、復号化中にタグ(TagSizeバイト)を自動的に抽出するために、 FlushFinalBlock
の必須アップグレードが必要になります。 これは、Javaの暗号化アーキテクチャやC#BouncyCastleなどの他の戦闘テスト済みAPIに似ています。 これは避けられませんが、ストリーミングではタグが最後に生成されますが、復号化中に最後のブロックが変換されるまで必要ないため、これを行うのに最適な場所です。 利点は、プログラミングモデルを大幅に簡素化することです。
注:1)CBC-HMACでは、最初にタグを検証することを選択できます。 これはより安全なオプションですが、そうであれば、実際には2パスアルゴリズムになります。 1番目のパスはHMACタグを計算し、2番目のパスは実際に復号化を行います。 したがって、メモリストリームまたはネットワークストリームは常にメモリにバッファリングする必要があり、ワンショットモデルになります。 ストリーミングではありません。 GCMやCCMなどの真のAEADアルゴリズムは、効率的にストリーミングできます。
public class CryptoStream : Stream, IDisposable
{
...
public CryptoStream(Stream stream, IAuthenticatedCipherTransform transform, CryptoStreamMode mode);
public override int Read(Span<byte> buffer, int offset, int count);
public override Task<int> ReadAsync(Span<byte> buffer, int offset, int count, CancellationToken cancellationToken);
public override void Write(ReadOnlySpan<byte> buffer, int offset, int count);
public override Task WriteAsync(ReadOnlySpan<byte> buffer, int offset, int count, CancellationToken cancellationToken);
public void FlushFinalBlock()
{
...
// If IAuthenticatedCipherTransform
// If encrypting, `TransformFinalBlock` -> `GetTag` -> append to out stream
// If decryption, extract last `TagSize` bytes -> `SetExpectedTag` -> `TransformFinalBlock`
...
}
...
}
上記により、不足しているビットを追加して、関連データによる認証付き暗号化(AEAD)を可能にすることができます。
ICipherTransform
を拡張しますこれにより、 CryptoStream
はその仕事を適切に行うことができます。 IAuthenticatedCipherTransform
インターフェースを使用して、独自のカスタムストリーミングクラス/使用法を実装することもできますが、 CryptoStream
を使用すると、非常にまとまりのある一貫性のある.net暗号化APIが実現します。
public interface IAuthenticatedCipherTransform : ICipherTransform
{
Span<byte> GetTag();
void SetExpectedTag(Span<byte> tag);
}
SymmetricAlgorithm
を展開するだけです
public abstract class AuthenticatedSymmetricAlgorithm : SymmetricAlgorithm
{
...
// Program Key/IV/AAD via class properties OR CreateAuthenticatedEn/Decryptor() params
public abstract IAuthenticatedCipherTransform CreateAuthenticatedDecryptor(ReadOnlySpan<byte> Key = default, ReadOnlySpan<byte> IV = default, ReadOnlySpan<byte> AuthenticatedData = default);
public abstract IAuthenticatedCipherTransform CreateAuthenticatedEncryptor(ReadOnlySpan<byte> Key = default, ReadOnlySpan<byte> IV = default, ReadOnlySpan<byte> AuthenticatedData = default);
public virtual Span<byte> AuthenticatedData {...}
public virtual Span<byte> Tag {...}
public virtual int TagSize {...}
...
}
public class AesGcm : AuthenticatedSymmetricAlgorithm
{
public AesGcm(ReadOnlySpan<byte> Key = default, ReadOnlySpan<byte> IV = default, ReadOnlySpan<byte> AuthenticatedData = default)
/* other stuff like valid key sizes etc similar to `System.Security.Cryptography.Aes` */
}
@sidshetye私は努力を称賛します。
ストリーミング-GCMを介した暗号化は実行可能です。 ストリーミング-GCMを介した復号化は
このスレッドの前半で、非対称の暗号化/復号化APIの可能性が(概念的に)提起されましたが、それは非常に悪い考えであるというコンセンサスがあったと思います。
要約すると、GCM復号化用の高レベルのバイトグラニュラーストリーミングAPIを使用することはできません。 以前何度も言いましたが、また言います。 ストリーミングAPIを使用する唯一の方法は、チャンク暗号化です。 チャンク暗号化のメリーゴーランドをみんなに惜しまないでしょう。
MSがGCMAPIに対して何をすることを決定したとしても、RUPは許可されません。
@sdrapkin RUPについてはここで詳しく説明しましたが、すでにその橋を渡っています。 簡単に言うと、RUPは、タグが検証されるまでデータの復号化を使用する必要がないことを意味しますが、Java JCE、WinNT bcrypt、OpenSSLなどのように、メソッドの境界で強制する必要はありません。 ほとんどの暗号プリミティブ、特に低レベルのものと同様に、注意して使用してください。
^^^そんなに。 私は、より高いレベルのストリームAPIなどに同意し、それを適切に適用します。 ただし、低レベルのプリミティブを使用する場合は、分割バッファーなどを使用できる必要があります。データが使用されないようにするのは私次第です。 タグの計算/チェックポイントで例外をスローしますが、低レベルのプリミティブをハムストリングしないでください。
データが使用されないようにするのは私次第です
間違い。 それはあなた次第ではありません。 AES-GCMには非常に_特定の_定義があり、その定義はそれがあなた次第ではないことを保証します。 必要なのは、個別のAES-CTRプリミティブと個別のGHASHプリミティブであり、これらを組み合わせて、必要に応じて適用できます。 しかし、個別のAES-CTRプリミティブとGHASHプリミティブについては説明していませんね。 AES-GCMについて話し合っています。 また、AES-GCMでは、RUPが許可されていない必要があります。
また、crypto.stackexchangeからのIlmariKaronenの回答を確認することをお勧めします。
@sdrapkinあなたは良い点を指摘します。 ただし、最終的にはRUPで安全なアルゴリズムを使用し、そのアルゴリズムをここで決定されたAPIに適合させることが望ましいでしょう。 したがって、次のものを選択する必要があります。
ストリーミングシナリオを検出して、この使用法が正しくない理由を説明する例外をスローできますか? または、ストリーミング実装でバッファ全体を消費するようにする必要がありますか? 後者は残念なことです。ストリーミングしていると思うかもしれませんが、そうではありません。
ストリーミング実装によってチェックされるSupportsStreaming(out string whyNot)
メソッドを追加できます。
一般的にストリーミング/タグアットザエンドに反対する確固たる議論がありますか? そうでない場合は、APIでそれを排除しないことを目指すべきだと思います。
@sdrapkin :これはアプリケーションではなくライブラリであるため、RUPの全体像を見てみましょう。 したがって、これは、未検証のデータの実際のリリース/使用というよりも、バッファリングとレイヤー設計の問題です。 AES GCM用のNIST特別刊行物800-38Dを見ると、次のことがわかります。
GCM仕様では、プレーンテキストの最大長を2 ^ 39-256ビット〜64GBと定義しています。 システムメモリ内のバッファリングに近い場所でのバッファリングは不合理です。
GCM仕様では、タグが失敗した場合の出力をFAILと定義しています。 ただし、タグの検証まで、実装内のどのレイヤーをバッファリングする必要があるかについては規範的ではありません。 次のようなコールスタックを見てみましょう。
A => AESGCM_Decrypt_App(key, iv, ciphertext, aad, tag)
B => +- AESGCM_Decrypt_DotNet(key, iv, ciphertext, aad, tag)
C => +- AESGCM_Decrypt_OpenSSL(key, iv, ciphertext, aad, tag)
どこ
Aはアプリケーション層のAESGCMです
Bは言語層のAES-GCMです
Cはプラットフォーム層のAES-GCMです
タグがチェックアウトされた場合、プレーンテキストは(A)で解放されますが、そうでない場合はFAILが返されます。 ただし、仕様のどこにも、進行中の平文をバッファリングする唯一の場所がメインメモリであることや、(B)や(C)などでバッファリングを行う必要があることを示唆しているところはまったくありません。 実際、OpenSSL、(C)の例でのWindows NT Bcryptは、ストリーミングが上位層でのバッファリングを許可します。 また、Java JCA、MicrosoftのCLRセキュリティ、および上記の私の提案は、ストリーミングがアプリケーション層でのバッファリングを許可する(B)の例です。 平文をリリースする前に、Aの設計者がより優れたバッファリング機能を持っていないと想定するのは思いがけないことです。 そのバッファは、理論上および実際には、ネットワーク全体のメモリまたはSSDまたはストレージクラスタである可能性があります。 またはパンチカード;)!
バッファリングは別としても、このペーパーでは、キーの鮮度や電源の無期限の障害全体でのIVの非反復など、他の実際的な懸念事項(セクション9.1、設計上の考慮事項および9.2、運用上の考慮事項を参照)について説明します。 明らかに、これをレイヤーB、つまりここに焼き付けることはしません。
@timovzl上記の最近の提案は、ワンショット(アーキテクトはバッファリングを気にしない)とストリーミング(アーキテクトはより優れたバッファリング機能を備えています)の両方のシナリオに対応しています。 低レベルのストリーミングAPIドキュメントで、消費者がバッファリングの責任を負っていることが明確である限り、セキュリティ証明の低下はなく、仕様からの逸脱はありません。
編集:文法、タイプミス、マークダウンを機能させようとしている
ビンゴ..ここでも、タグの検証が行われる場所については、レイヤー設計者が決定します。 私は、未確認のデータをアプリケーション層にリリースすることを決して主張していません。
議論は、これらの「消費者」レベルのAPIなのか、それとも真のプリミティブなのかということに戻ります。 それらが真のプリミティブである場合は、機能を公開して、より高いレベルの「より安全な」APIをその上に構築できるようにする必要があります。 ノンスの議論で、これらは真のプリミティブである必要があることはすでに決定されています。つまり、足で自分を撃つことができるということです。ストリーミング/部分暗号文のデコードにも同じことが当てはまると思います。
そうは言っても、人々がこれらの上に自分自身を「転がす」のを防ぐために、「より高い」レベルでより安全なAPIを迅速に提供することが不可欠です。
私の興味はネットワーキング/パイプラインから来ています。部分的なバッファーを実行できず、「ワンショット」を実行する必要がある場合、これらのAPIの欠点は何のメリットもないので、BCrypt / OpenSslなどに直接アクセスし続けます。
私の興味はネットワーキング/パイプラインから来ています。部分的なバッファーを実行できず、「ワンショット」を実行する必要がある場合、これらのAPIの欠点は何のメリットもないので、BCrypt / OpenSslなどに直接アクセスし続けます。
その通り。 必要性がなくなることはないので、人々は他の実装を使用するか、独自の実装をロールバックします。 これは、適切な警告ドキュメントを使用してストリーミングを許可するよりも必ずしも安全な結果ではありません。
@Timovzl 、前回の提案に関して、多くの技術的なフィードバックと設計要件を引き出したと思います。 実装とリリースについての考えは?
@Sidshetyeは、すべての要件に対応していると私が信じる詳細な提案をしました。 RUPに関する単一の批判は、これ以上の反対なしに対処されました。 (具体的には、RUPはいくつかのレイヤーの1つで防止される可能性があり、低レベルAPIがどちらを指示するべきではありません。また、ストリーミングを提供しないと、より悪い影響が予想されます。)
進歩のために、最新の提案についてさらに懸念を持っている人は誰でも話してください-そしてもちろん、代替案を提供したいと思います。
私はこの提案とAPIの具体化に熱心に取り組んでいます。
@Sidshetye 、私はいくつかの質問と提案があります:
SymmetricAlgorithm
から継承することが望ましいですか? 統合したい既存のコンポーネントはありますか? そのアプローチの利点を逃していない限り、基本クラスのないAuthenticatedEncryptionAlgorithm
を見たいと思います。 他に何もないとしても、望ましくないCreateEncryptor
/ CreateDecryptor
(認証されていない!)メソッドの公開を回避します。SymmetricAlgorithm
を継承し続けない限り、 AuthenticatedSymmetricAlgorithm
は、従来の用語である認証付き暗号化に従って、 AuthenticatedEncryptionAlgorithm
に名前を変更できます。Tag
プロパティを設定するのではなく、 TryEncrypt
/ TryDecrypt
を変更して、タグの書き込み/受信を行います。key
、 iv
、およびauthenticatedAdditionalData
を設定する目的は何ですか? 私は、複数の有効なアプローチと、可能な限り変更可能なプロパティを避けたいと思います。 それらなしで更新された提案を作成できますか?AesGcm
の状態が必要ですか? 私の本能は、 iv
とauthenticatedAdditionalData
はメッセージごとであるため、これらを確実に排除することです。 key
は、通常、単一のキーで複数の操作を実行する必要があるため、状態として保持する価値がある場合があります。 それでも、通話ごとにキーを取得することも可能です。 同じ質問がCreateAuthenticatorEncryptor
にも当てはまります。 いずれにせよ、パラメータを渡すために_一方向_に落ち着く必要があります。 私は賛否両論について話し合いたいと思っています。 私はAesGcm
で主要な状態に傾いており、残りはそれぞれCreateAuthenticatedEncryptor
またはTryEncrypt
です。 すでに合意している場合は、最新の提案を提示してください。 :-)ICipherTransform
はおそらく抽象クラスCipherTransform
である必要があります。これにより、既存の実装を壊すことなくメソッドを追加できます。authenticatedData
またはauthenticatedAdditionalData
と言うべきですか? さらに、パラメータ名plaintext
とciphertext
を選択する必要があると思います。TryEncrypt
のクライアントコードがciphertext
を提供するために必要なスパンの長さをどのように知ることができるかをまだ理解しようとしています! TryDecrypt
とplaintext
の長さについても同じです。 確かに、成功するまでループでそれらを試して、失敗した各反復の後に長さを2倍にすることは想定されていませんか?最後に、先を考えて、この上に構築された高レベルのAPIはどのように見えるでしょうか? 純粋にAPIの使用法を見ると、ストリーミングAPIと非ストリーミングAPIの両方がすでに非常に単純であるため、改善の余地はほとんどないようです。 私が想像する主な違いは、自動IV、自動出力サイズ、そしておそらく暗号化されるデータの量の制限です。
Windowsではストリーミングが可能です。 OpenSSLもそうです。 それらの両方は、ほとんどが既存の概念にそれを鳩穴に入れました(しかし、彼らは両方とも「あなたが対処しなければならない側にこのことがあります、さもないと私はエラーになります」とレンチを投げました)。
Goはしませんし、libsodiumはしません。
最初の波はそれを許したようですが、後の波は許しません。 間違いなく後の波に乗っているので、それを許さないことに固執するつもりだと思います。 ワンショットモデル(暗号化/復号化、キーは呼び出し間で維持できます)を導入した後、ストリーミングの需要が高まった場合は、再評価できます。 したがって、そのパターンに準拠したAPI提案は有益であるように思われます。 SIVもCCMもストリーミング暗号化をサポートしていませんが、それらのストリーミングAPIは潜在的に大量のバッファリングを行っています。 物事を明確に保つ方が良いようです。
アルゴリズム自体(SIV)がタグを暗号化の出力に組み込んでいない限り、プロポーザルはタグをペイロードに埋め込まないでください(GCMとCCMはそれを別個のデータとして呼び出します)。 ( E(...) => (c, t)
とE(...) => c || t
またはE(...) => t || c
)。 APIのユーザーは確かにそれを連結として使用できます(スパンを適切に開くだけです)。
GCM仕様では、タグの不一致時にFAIL以外のものをリリースすることは許可されていません。 NISTはそれについて非常に明確です。 McGrew&ViegaによるオリジナルのGCMペーパーにも、次のように書かれています。
復号化操作はプレーンテキストではなくFAILを返し、カプセル化解除は停止し、プレーンテキストは転送またはさらに処理されるのではなく破棄されます。
以前のコメントはどれもRUPに対処していませんでした-彼らは単にそれを手で振っただけです(「より高い層がそれを処理します」-ええ、そうです)。
簡単です。GCM暗号化はストリーミングできます。 GCM復号化はストリーミングできません。 それ以外はGCMではなくなりました。
最初の波はそれを許したようですが、後の波は許しません。 間違いなく後の波に乗っているので、それを許さないことに固執するつもりだと思います。
@bartonjsは、文字通りすべての技術的および論理的分析を無視し、代わりにGoおよびlibsodiumプロジェクトの日付を実際の分析の弱いプロキシとして使用していますか? プロジェクトの名前に基づいて同様の議論をする場合を想像してみてください。 さらに、インターフェースと実装を決定しています。 AEADの非ストリーミングインターフェイスを決定すると、そのような実装がすべて不可能になることをご存知ですか?
ワンショットモデル(暗号化/復号化、キーは呼び出し間で維持できます)を導入した後、ストリーミングの需要が高まった場合は、再評価できます。
GitHubでこれまでに示された需要が不十分なのはなぜですか? 技術的または顧客の要求のメリットよりも少ない作業で済むことをサポートすることは、完全に気まぐれに見えるところまで来ています。
@bartonjsは、文字通りすべての技術的および論理的分析を無視し、代わりにGoおよびlibsodiumプロジェクトの日付を実際の分析の弱いプロキシとして使用していますか?
いいえ、私はそれが非常に危険であり、AEADのストリーミングを避けるべきであると言うプロの暗号学者のアドバイスを使用しています。 次に、CNGチームからの情報を使用しています。「理論的には多くの人がそれを望んでいると言っていますが、実際にはほとんど誰もそれをしていません」(テレメトリとフィールド支援要求の事例証拠の違いはわかりません)。 他の図書館が一発の道を進んだという事実は、単に決定を_強化_します。
GitHubでこれまでに示された需要が不十分なのはなぜですか?
いくつかのシナリオが言及されています。 呼び出し元にデータの再構築を行わせる代わりに、APIを複雑にすることを保証するのに十分なシナリオがあると思われる場合は、断片化されたバッファーの処理はおそらくReadOnlySequence
を受け入れることで対処できます。
大きなファイルは問題ですが、GCMのカットオフは64GBにすぎないため、大きなファイルはすでに問題になっています。これは「それほど大きくはありません」(大丈夫、かなり大きいですが、「おっ、それは大きい」ではありません)。それは従来)。 メモリマップトファイルを使用すると、2GBのRAMを必要とせずにスパン(最大2 ^ 31-1)を利用できます。 だから私たちは最大値から数ビットを削りました...それはおそらく時間の経過とともに起こるでしょう。
AEADの非ストリーミングインターフェイスを決定すると、そのような実装がすべて不可能になることをご存知ですか?
@GrabYourPitchforksが正しかったこと(https://github.com/dotnet/corefx/issues/23629#issuecomment-334638891)は、おそらく賢明な統一インターフェースがないことをますます確信しています。 GCM _requiring_ a nonce / IVおよびSIV_forbidding_は、AEADモード/アルゴリズムの初期化には、何が起こるかについての知識がすでに必要であることを意味します... AEADには実際には「抽象化された」概念はありません。 SIVは、「タグ」の行き先を指示します。 GCM / CCMはしません。 SIVは、仕様上、タグファーストです。
SIVは、すべてのデータが揃うまで暗号化を開始できません。 したがって、そのストリーミング暗号化は、スロー(つまり、呼び出さないようにする必要があることを意味します)またはバッファ(n ^ 2の操作時間になる可能性があります)のいずれかになります。 CCMは、長さがわかるまで開始できません。 ただし、CNGでは長さの暗号化前のヒントが許可されていないため、同じボート内にあります。
デフォルトでは、正しいことよりも間違ったことを実行しやすい新しいコンポーネントを設計するべきではありません。 ストリーミング復号化により、Streamクラス(CryptoStreamを使用して行う提案)を非常に簡単に接続でき、タグが検証される前にデータ検証のバグを簡単に取得できます。これにより、AEの利点がほぼ完全に無効になります。 。 ( IGcmDecryptor
=> CryptoStream
=> StreamReader
=> XmlReader
=>「待ってください、それは合法的なXMLではありません...」=>適応的暗号文オラクル) 。
それは要点に達しつつあります...顧客の要求。
残念ながら、私の人生で何度も耳にしたことがあります。申し訳ありませんが、あなたは私たちが考えている顧客ではありません。 おそらくあなたはGCMを安全に行う方法を知っていると思います。 タグの検証が完了するまで、揮発性のファイル/バッファなどにのみストリーミングすることを知っています。 あなたはナンス管理が何を意味するかを知っています、そしてあなたはそれを間違えるリスクを知っています。 ストリームサイズに注意を払い、2 ^ 36-64バイト後に新しいGCMセグメントにカットオーバーすることを知っています。 あなたはそれがすべて言われ、行われた後、あなたがそれらのことを間違えた場合、それはあなたのバグであることを知っています。
一方、私が考えている顧客は、上司から言われたので「これを暗号化する必要がある」ということを知っている人です。 そして彼らは、暗号化を行う方法を検索するときに、いくつかのチュートリアルで「常にAEを使用する」と述べ、GCMについて言及していることを知っています。 次に、CryptoStreamを使用する「.NETでの暗号化」チュートリアルを見つけます。 次に、彼らはパイプラインを接続し、SSLv2を選択したのと同じことをしただけだとは思いもしませんでした...理論的にはチェックボックスをオンにしましたが、実際にはそうではありません。 そして、彼らがそれを行うとき、そのバグはよく知っているすべての人に属しますが、間違ったことを簡単に行うことはできません。
あなたは私たちが考えている顧客ではありません[...]一方、私が考えている顧客は、上司から言われたので「これを暗号化する必要がある」ことを知っている人です[...]
@bartonjsの数か月前、私たちはすでに、低レベルのAPI(強力ですが特定の条件下では安全ではない)と高レベルのAPI(絶対確実)を使用して、2つの顧客プロファイルをターゲットにすることを決定していました。 タイトルにもあります。 それは確かに自由な国ですが、そうでないと主張することによってゴールポストを動かすことは今や不誠実です。
一方、私が考えている顧客は、上司から言われたので「これを暗号化する必要がある」ということを知っている人です。 そして彼らは、暗号化を行う方法を検索するときに、いくつかのチュートリアルで「常にAEを使用する」と述べ、GCMについて言及していることを知っています。 次に、CryptoStreamを使用する「.NETでの暗号化」チュートリアルを見つけます。 次に、彼らはパイプラインを接続し、SSLv2を選択したのと同じことをしただけだとは思いもしませんでした...理論的にはチェックボックスをオンにしましたが、実際にはそうではありません。 そして、彼らがそれを行うとき、そのバグはよりよく知っているすべての人に属しますが、間違ったことを簡単に行うことはできません。
@bartonjs待って、低レベルのプリミティブはどうなりましたか? この特定の問題の目的は、ベビーシッターに対する柔軟性だと思いました。 計画が変更されたかどうかを必ずお知らせください。そうすれば、全員が同じことを話し合うことになります。
また、ブロックごとの方法はまだ検討中ですか、それともワンショットの方法ですか?
@GrabYourPitchforksが正しかったこと(#23629(コメント))は、おそらく賢明な統一インターフェースがないことをますます確信しています。
すべての例を見ると、これはますます無駄に見え始めます-特に実装にそのような異なる制限がある低レベルのAPIの場合。 おそらく、統一されたインターフェイスではなく、AES-GCMを使用して確かな例を設定する必要があります。 ちなみに、後者は将来の高レベルAPIにとってまだ興味深いかもしれません。 より制限的であるというその特性は、おそらくそこでの統合インターフェースをはるかに簡単にするでしょう。
ブロックごとの方法はまだ検討中ですか、それともワンショットの方法ですか?
https://github.com/dotnet/corefx/issues/23629#issuecomment -378605071で述べたように、リスク対報酬対表現されたユースケースは、AEのワンショットバージョンのみを許可する必要があると言っているように感じます。
私は議論全体を読んだわけではなく、ランダムな部分だけを読んだ。 あなたがどの方向に向かっているのかわかりません。 私が書いたものがこの文脈で意味をなさない場合は申し訳ありません。 私の2¢:
私たち(.NETセキュリティチーム)は、自分たちの間で、そしてマイクロソフト内のより広範な暗号化チームと協議しました。 このスレッドで言及されている問題や懸念の多くについて話し合いました。 最終的に、これらの懸念は、フレームワーク内のコアビルディングブロックとしてストリーミングGCMAPIを導入することを保証するほど説得力がありませんでした。
この決定は、必要が生じた場合、将来再検討することができます。 それまでの間、現在よりも悪化することはありません。現在、GCMサポートのストリーミングにサードパーティの暗号ライブラリを使用している開発者は、引き続き使用でき、意図した導入によって壊れることはありません。非ストリーミングGCMAPI。
メモリに収まらないデータの暗号化に対処するにはどうすればよいですか?
@pgolebiowski安全なストリーミング暗号化を提供するために特別に設計された高レベルの.NET暗号化ライブラリを使用します。
@sdrapkinこれは口で言うほど簡単ではありません。 「安全」は質問することがたくさんあります。 証明され、実際に信頼できるものは何ですか? あなたは自分自身を言います:
Bouncy Castle c#ライブラリ(典型的なStackOverflowの推奨事項)。 Bouncy Castle c#は、巨大な(145k LOC)、パフォーマンスの低い暗号の博物館カタログ(一部は古代)であり、古いJava実装は同じように古い.NET(2.0?)に移植されています。
さて、オプションは何ですか? 多分あなた自身の図書館? 実際にはありません。 うーん...多分libsodium-net? 実際にはありません。
かなり信頼できるソース(Microsoftやコミュニティで広く使用されているなど)からの監査済みライブラリを実際に探す場合、そのようなライブラリは.NETCoreの世界には存在しないと思います。
@pgolebiowskiオプションは、確立された.NETフレームワークを使用することです。 あなたが望むように、証明され、信頼できるものそのものです。 他のライブラリ(私のものを含む)は、MicrosoftがECDHのような欠落している暗号プリミティブをNetStandardに追加するのを待ちます。
インフェルノフォークもご覧いただけます。 いくつかの些細な変更でNetStandard20が達成されたフォークが少なくとも2つあります。
あなたの図書館は2年前に1人の男によって2日以内に監査されました。 信じられない、ごめんなさい。 マイクロソフトには、そのための専用チームがあります。Cure53のチームよりも信頼度の高い人々です。
真剣に、私たちは多くのことに対するサードパーティのサポートについて話すことができます。 ただし、必要なセキュリティ関連のものはすべて、標準ライブラリによって提供される必要があります。
@pgolebiowski誰かに何かを信頼するように説得しようとするのは私からはほど遠いですが、あなたの発言は正確ではありません。 インフェルノは、「Cure53」組織の2人の専門家によって監査されました。 監査には2日かかり、ライブラリ全体は約1,000行のコードでした。 これは、監査人1人あたり1日あたり約250行のコードであり、非常に管理しやすいものです。
実際、簡単な監査機能は、信頼したくない人のために、Infernoの重要な機能の1つです。
このスレッドの目的は、AES-GCMのサポートを追加することです。 あなたのライブラリはAES-GCMさえサポートしていません。 他の人にあなたのコードを使用してもらいたい場合は、corefxの提案として提出してください。 適切なスレッドで。
もう1つ、このアルゴリズムをサポートしている場合でも、.net暗号化ボードによってレビューされておらず、corefxの一部ではありません。 そのようなレビューの候補でさえありません。 これは、この無駄な議論と宣伝の終わりを意味します。
@pgolebiowski私は何も宣伝しませんでした-単にあなたの質問に答え、あなたのユースケースの代替案を提案し、不正確な主張を修正しました。 AES-GCMはストリーミング暗号化には適していません。これは.NETセキュリティチームによってレビューおよび合意されたものであるため、信頼できます。
ストリーミングAES-GCMをどこでも防御していますか? 思い出せない。 しかし、言ったことを思い出すことができます:
- ストリームは重要です。 セキュリティの脆弱性を意味するために直接サポートできない場合は、可能であれば、ストリームを公開する低レベルAPIの上に構築された高レベルラッパーを提供します(非効率的ですが安全な方法で)。
- AES-GCMがどのシナリオでもストリームを絶対に使用できない場合は、ストリームに基づくAES-CBC-HMACの正当な実装を提供してください。 または他のAEアルゴリズム。
またはcorefxの未解決の問題を述べる:
メモリに収まらないデータの暗号化に対処するにはどうすればよいですか?
ボーナス:
インフェルノフォークもご覧いただけます。 いくつかの些細な変更でNetStandard20が達成されたフォークが少なくとも2つあります。 [...]インフェルノは「Cure53」組織の2人の専門家によって監査されました[...]簡単な監査は、正確に信頼したくない人のためのインフェルノの重要な機能の1つです。
私は何も宣伝しませんでした
余談ですが、ほとんどの人が考えているという意味で、私は「ストリーミング」を本当に望んでいませんでした。 パフォーマンスとメモリ使用の理由から、「ブロック」処理が必要でした。 私は実際に結果を「ストリーミング」したくありません。 これがopensslとcngのサポートがそれを失うのは恥ずべきことのように思われ、基本的に私が考えることができるどのシナリオでも「プリミティブ」を役に立たなくします。
@Drawaes考えてみると、ブロック操作はストリームを使用するよりもはるかに安全かもしれません。 素人はストリームに触れる可能性がありますが、ブロック操作よりもワンショットAPIを使用します。 さらに、ブロック操作は、たとえばXmlReader
と_まっすぐに_組み合わせることができません。 したがって、実際には、説明されている危険の多くはストリームオブジェクトに適用されますが、操作のブロックには適用されません。
ワンショットAPIも利用可能な場合にブロック操作を操作するには、実行していることを理解しており、特に低レベルの調整が必要であることを示しています。 素人を保護することができ、柔軟性があります。
RUPを回避することに関しては、GCMにとってブロック操作が本当にどれだけ有利であるかをまだ考えています。 暗号化は完全なメリットを享受しますが、復号化はわずかなメリットしかありません。 完全な暗号文の保存を回避することはできますが、それでも完全な平文をバッファリングする必要があります。 復号化機能は、中間の平文をディスクに保存することを選択できます。 しかし、その見返りとして、エラーの余地を増やしました。 この問題をより高いレベルで解決しないという説得力のある議論がありますか(たとえば、そこにチャンクするか、真のストリーミングアルゴリズムを使用します)?
TLSとパイプライン。 現在(そして近い将来)パイプラインは4kブロックを使用しますが、tlsメッセージは16kの暗号文にすることができます。1回のショットで、復号化する前に16kを単一の連続バッファーにコピーする必要があります。 ブロックの場合、4または5と言うことがあり、ブロックを競合させるために最大16バイトをバッファリングする必要がある場合があります。
@Drawaes 16kはまだ一定であり、巨大ではありません。 それはこの文脈で大きな違いを生むでしょうか?
はい、それはパイプライン内の別のコピーを意味します。 これは、パフォーマンスに大きな影響を及ぼします。
これを実現するには何が必要ですか? 次のステップは何ですか? @Drawaes
AES-GCMについては、対応する問題がロックされているため、配信が損なわれていると思います: https://github.com/dotnet/corefx/issues/7023。 @blowdart 、ロックを解除できますか? 人々が議論できないとき、進歩を遂げることは本当に難しいです。 または、それがオプションでない場合は、この機能を公開できる代替ソリューションを提案することもできます。
いいえ、ロックを解除していません。 決定が下され、トピックが完了しました。
@blowdart返信ありがとうございます。 多分これは十分に明確ではなかったことを理解しています:
または、それがオプションでない場合は、この機能を公開できる代替ソリューションを提案することもできます。
AES-GCMをサポートする決定があることを感謝します。 これは素晴らしいです、私は間違いなくそのアルゴリズムが欲しいです。 したがって、実際にサポートしてもらうのはすばらしいことです。 AES-GCMの設計と実装に関するディスカッションをここで開催しますか、それとも新しい号で開催しますか?
また、そのトピックが完了したら、それを閉じてみませんか? そして、その問題のタイトルを変更して、より明確にします。現在、実装に関する議論がここで行われることを示唆しています: https://github.com/dotnet/corefx/issues/7023。 たぶん、最初にサポートするAEADアルゴリズムを決定するようなものです。
言い換えれば、現在の状況では、AES-GCMを前進させるために何が必要かが不明であるというフィードバックを提供します。
@karelz
@pgolebiowskiすでにPRが出ています。 おそらく来週の水曜日にマスターで利用できるようになるでしょう。
最も参考になるコメント
いいえ、私はそれが非常に危険であり、AEADのストリーミングを避けるべきであると言うプロの暗号学者のアドバイスを使用しています。 次に、CNGチームからの情報を使用しています。「理論的には多くの人がそれを望んでいると言っていますが、実際にはほとんど誰もそれをしていません」(テレメトリとフィールド支援要求の事例証拠の違いはわかりません)。 他の図書館が一発の道を進んだという事実は、単に決定を_強化_します。
いくつかのシナリオが言及されています。 呼び出し元にデータの再構築を行わせる代わりに、APIを複雑にすることを保証するのに十分なシナリオがあると思われる場合は、断片化されたバッファーの処理はおそらく
ReadOnlySequence
を受け入れることで対処できます。大きなファイルは問題ですが、GCMのカットオフは64GBにすぎないため、大きなファイルはすでに問題になっています。これは「それほど大きくはありません」(大丈夫、かなり大きいですが、「おっ、それは大きい」ではありません)。それは従来)。 メモリマップトファイルを使用すると、2GBのRAMを必要とせずにスパン(最大2 ^ 31-1)を利用できます。 だから私たちは最大値から数ビットを削りました...それはおそらく時間の経過とともに起こるでしょう。
@GrabYourPitchforksが正しかったこと(https://github.com/dotnet/corefx/issues/23629#issuecomment-334638891)は、おそらく賢明な統一インターフェースがないことをますます確信しています。 GCM _requiring_ a nonce / IVおよびSIV_forbidding_は、AEADモード/アルゴリズムの初期化には、何が起こるかについての知識がすでに必要であることを意味します... AEADには実際には「抽象化された」概念はありません。 SIVは、「タグ」の行き先を指示します。 GCM / CCMはしません。 SIVは、仕様上、タグファーストです。
SIVは、すべてのデータが揃うまで暗号化を開始できません。 したがって、そのストリーミング暗号化は、スロー(つまり、呼び出さないようにする必要があることを意味します)またはバッファ(n ^ 2の操作時間になる可能性があります)のいずれかになります。 CCMは、長さがわかるまで開始できません。 ただし、CNGでは長さの暗号化前のヒントが許可されていないため、同じボート内にあります。
デフォルトでは、正しいことよりも間違ったことを実行しやすい新しいコンポーネントを設計するべきではありません。 ストリーミング復号化により、Streamクラス(CryptoStreamを使用して行う提案)を非常に簡単に接続でき、タグが検証される前にデータ検証のバグを簡単に取得できます。これにより、AEの利点がほぼ完全に無効になります。 。 (
IGcmDecryptor
=>CryptoStream
=>StreamReader
=>XmlReader
=>「待ってください、それは合法的なXMLではありません...」=>適応的暗号文オラクル) 。残念ながら、私の人生で何度も耳にしたことがあります。申し訳ありませんが、あなたは私たちが考えている顧客ではありません。 おそらくあなたはGCMを安全に行う方法を知っていると思います。 タグの検証が完了するまで、揮発性のファイル/バッファなどにのみストリーミングすることを知っています。 あなたはナンス管理が何を意味するかを知っています、そしてあなたはそれを間違えるリスクを知っています。 ストリームサイズに注意を払い、2 ^ 36-64バイト後に新しいGCMセグメントにカットオーバーすることを知っています。 あなたはそれがすべて言われ、行われた後、あなたがそれらのことを間違えた場合、それはあなたのバグであることを知っています。
一方、私が考えている顧客は、上司から言われたので「これを暗号化する必要がある」ということを知っている人です。 そして彼らは、暗号化を行う方法を検索するときに、いくつかのチュートリアルで「常にAEを使用する」と述べ、GCMについて言及していることを知っています。 次に、CryptoStreamを使用する「.NETでの暗号化」チュートリアルを見つけます。 次に、彼らはパイプラインを接続し、SSLv2を選択したのと同じことをしただけだとは思いもしませんでした...理論的にはチェックボックスをオンにしましたが、実際にはそうではありません。 そして、彼らがそれを行うとき、そのバグはよく知っているすべての人に属しますが、間違ったことを簡単に行うことはできません。