シンプルなデータスクランブルアルゴリズム

時には何かを暗号化する必要がありますが、深刻な暗号化アルゴリズムを引き付けることは場違いなようです-それはスズメの銃のようになります。 たとえば、スニファーを使用してユーザー/トロイの木馬からのトラフィックを簡単に保護する必要がありますが、データ自体には価値がないため、暗号化/復号化、実装自体にも時間がかかります。 または、通常のユーザーからの特定の保存データを何らかの方法で確実に閉じる必要があります。 このようなアルゴリズムは、専門家による標的を絞ったハッキン​​グの試みに耐えられないことは明らかですが、そのようなタスクは通常設定されていませんが、それらの作業を複雑にしようとします。 これは通常、 スクランブルと呼ばれます。



カットの下で、そのようなアルゴリズムのアイデアを提示し、固定キーを持つ通常のXORよりも複雑になることを約束します。 念のため、これらのアルゴリズムは暗号化のふりをしていないという事実に注目しますが、それらを使用してそれらを見つけることができると確信しています。



背景


潜在的なクラッカーは、暗号化を実行するコードにアクセスできないか、リバースエンジニアリングに十分な資格がないか、またはより困難なクラッキングメソッドに時間を費やすほどデータが価値がないと想定されます。



おそらく誰もが、そのような場合、XOR演算を使用して固定長キーの単純な循環オーバーレイを使用することが多いことを知っています。 そして誰もが、そのような保護が初心者の「ハッカー」や上級ユーザーにも耐えられないことをよく知っています。 もっと複雑だが、実装が簡単なものが欲しい。



そして、キーを生成する場合はどうなりますか?


最初に頭に浮かぶのは、キーの長さを見つけることを少なくとも複雑にするのに十分な長さのキーを生成することです。 たとえば、送信者と受信者の両方に既知の入力データで特定の擬似乱数ジェネレーターを使用します。 これらの一般的に使用されるジェネレーターの1つは、 線形合同 PRNGです(PRNGは擬似乱数ジェネレーターです )。 もちろん、これは悪いことだと思いますが、このアプローチで何が悪いのでしょうか? 問題は、ジェネレーター自体のパラメーターを生成するのがかなり難しいことです。 シーケンスが長く、縮退しないように、線形合同PRNGに対して適切なパラメーターをプログラムで選択することはかなり困難です。 この機会に、あなたはD.クヌートの本「プログラミングの芸術」の3.2.1を読むことができます。 したがって、これらのパラメーターは定数としてコードに挿入されることが多く、その結果、潜在的なクラッカーは1つのキーで暗号化された多くのメッセージを受信し、作業が大幅に簡素化されます。



しかし、データ自体を使用してこの擬似ランダムシーケンスを生成するとどうなりますか?


このアイデアは、約20年前に知り合いの学生が卒業証書を書くのを手伝ったときに思いつきました。 暗号化と復号化の両方に同じシーケンスを提供するジェネレータが必要なため、一見、これは不可能に思えます。 奇妙なことに、このような「キラー」論文が、このようなジェネレーターを作成するためのパスを提供します。 はい、元のバイト(またはエンコードのアトミック単位)と暗号化されたバイトを与えると、内部変数の値を同じように変更するアルゴリズムが必要です。 これを達成する方法は? 独創的なものはすべて単純です。次のキー値を計算するには、ソース暗号化バイトのペアに対して可換演算を使用できます。 操作の結果はペアのオペランドの順序に依存しないため、そのようなアルゴリズムは暗号化中と同じ方法で復号化中に変数を変更しますが、他の入力データのキーシーケンスは異なります。



入力依存キージェネレーターの例


より明確にするために、そのようなアルゴリズムの簡単な例を考えてみましょう。

x nをソースデータの次のコード、k nを現在のキー、k n + 1を次のキー値、y nを暗号化されたコードx nとします。

Q(a、b)は、特定の可換関数です。 q(a、b)== q(b、a)のように。

F(a、b、c)は特定の整数関数です。

次に、(de)コーディングの繰り返しは次のように記述できます。

y n := x n xor k n ;

k n + 1 := F(k n 、Q(x n 、y n )、n);

関数F()の実装が一般に想像力と常識によってのみ制限されることが明らかな場合、Q()については、おそらく、詳細、つまり、交換可能になるために満たす必要のある条件を確認する必要があります。 これを達成する最も簡単な方法は、xor、加算、乗算などの可換演算でペアでのみ引数を使用することです。 例:

Q(a、b)= a xor b修正済み:ソースと暗号化されたコードをオーバーレイすると、キーが取得されるため、ここで興奮している可能性があります。これは望ましくありません。個人的に少し複雑な関数を使用します)。

Q(a、b)=((a xor b) または 1)*((a + b) xor 1)。

ご覧のとおり、スーパーデュパー機能Q()を考え出すのは難しくありません。 もう1つ、複雑にする必要がありますか? 再複雑化には特別な意味はないと思います。



さて、今何が悪いのでしょうか?


はい、エンコード関数のコードを知らないと、何かを読むことは非常に困難になります。 しかし、まだどんな手がかりが残っていますか? スクランブラーの入力パラメーターが同じ場合、

  1. 2つのメッセージが同じデータで始まる場合、暗号化されたデータの先頭はまったく同じになります。
  2. 最初のコードのキーは同じです。
  3. 暗号化されたメッセージの長さは、元のメッセージの長さと正確に一致します。


これに対処する方法、しかしあなたの若い人生を置くことはありませんか? もちろん、戦う方法はたくさんありますが、安くて明るくしたいのですが、ありますか? これについていくつかのアイデアがあります。

最初の2つのポイントを克服するために、非常にシンプルですが効果的なテクニックがあります。 暗号化するときは、各メッセージの前にランダムデータを挿入します。 1バイト(コード)で十分です。 次のキーはデータに依存するため、同一のメッセージであっても、異なるキーシーケンスを取得します。 受信者は、メッセージを復号化した後にこのプレフィックスを破棄するだけです。

3番目の段落と戦うために、メッセージの前後にランダムデータを追加できます。



他にアイデアはありますか?


そして! いつもアイデアがあります!

圧縮形式でデータを送信するとします。 または、データはすでに部分的に暗号化されています。 または、各メッセージ/ブロックは非常に長く、コードがほぼ均一に分布したバイナリデータで構成されます。 この場合、メッセージ内のコードの順序に干渉すると、潜在的なクラッカーの寿命が著しく複雑になる可能性があります。 あなた自身がデータブロックにいくつかの原始的なバイトミキサーを思い付くことができると確信していますが、興味深い美しいアイデアを約束しました。



データシャッフル


通常、この問題を解決するには、いくつかのGSPChを使用して、交換されるデータブロック内のコードインデックスのペアを取得します。 この方法の不快な点は、データの一部が同じ場所に残らないことを保証するのが難しいことです。 信頼できる結果を得るために、すべてのメッセージコードを単純に調べて、それぞれをランダムなコードと交換することもできますが、許容できる結果を得るために必要な順列の数も明確ではありません。 しかし、もう1つの迷惑があります-ジェネレータの二乗分布が悪い場合(そして線形一致のものがこの病気に苦しんでおり、絶望的である場合)、特定のブロックサイズでは、出力値のループが発生する可能性があります。 私は長い間、高速の擬似ランダムデータシャフラーのアイデアに行きましたが、スクランブルのアルゴリズムとしてだけでなく、あなたの注意に値すると信じています。



ちょっとした理論。 D. Knutの著書「The Art of Programming」の3.2.1.2項では、生成されるシーケンスの長さがモジュラスに等しくなるように、線形合同ジェネレータの係数を選択する基準を見つけることができます。 これはどういう意味ですか? つまり、モジュールmのジェネレーターでは、0〜m -1の各値が1回だけ発行されます。 なぜこれが必要なのですか? シャッフラーは、メッセージのすべてのバイト(コード)の場所を確実に変更することが望ましいことを思い出してください。 つまり、このデータブロックの長さがこのm自体に等しい場合、ジェネレータの次の値に等しいインデックスで、メッセージの次の入力バイト(コード)を出力バッファに書き込むだけで十分です。 このアルゴリズムの単純さは非常に魅力的であるため、私は通り過ぎることができませんでした。



しかし、いつも魅惑的な何かで起こるように、いくつかの問題がありました。 まず、すべてのmが同じように有用である限りません。 同じ本の同じ章から、 mが第1級数の素数の積である場合、原則として要素の完全な列挙を達成できないことがわかります(もちろん、私たちは興味のない列の列挙は別として)。 与えられたシーケンス長で必要なジェネレーターを取得することは不可能であるため、任意の長さのメッセージがある場合、常にそのようなジェネレーターを見つけることができるとは限りません。 行き止まり? 本当に任意の長さのジェネレーターが必要ですか? 潜在的なクラッカーにとって、メッセージの長さを知ることは非常に役立つことを思い出してください。 次に、入力データに応じて、ジェネレーターに使用した闘争の方法を既に知っています。 そうです、ランダムなゴミを投げる必要があり、何よりも有用なデータの前に。 確かに、各ブロックで何らかの方法で有用な情報の量を示す必要があるという事実に問題があります。 あなたの場合、すべてのメッセージ/データブロックの長さが固定されている場合、修正してm-あなたに都合の良い最初の値を選択します。これは入力ブロックの長さよりも大きく、本の3.2.1.3の定理Aの基準を満たします



ジェネレーターパラメーターx n + 1 =(a * x n + c)mod mの基準について:

  1. cとmは互いに素です。
  2. a-1は、mのすべての素因数pに対するpの倍数です。
  3. mが4の倍数である場合、a-1は4の倍数でなければなりません。


少ない血でこれを達成する方法は? このオプションをお勧めします:

m = 2 n 、ここでn> 3;

c = p、pは素数&p> 2;

a = 4 * k + 1。

3つの条件がすべて満たされ、そのような値を選択するのは非常に簡単であることに気付くのは簡単です。



他にアイデアはありますか?


シャッフラーとデータ依存ジェネレーターを組み合わせるというアイデアは非常に明白です。 これを行うには、まずメッセージの長さをシャッフルブロックのサイズに合わせるために必要な量のガベージをジェネレーターに供給し、次にメッセージ自体のデータを実行します。 shufflerから取得したインデックスに従って、すべての出力コードを記述します。



今日はこれで十分だと思います。



修正:発見されたバグを修正し、失敗した例を取り消しました。




All Articles