暗号学において、単純なXOR暗号は加算暗号の一種であり、[ 1 ]次の原理に従って動作する暗号化アルゴリズムである。
例えば、は排他的論理和(XOR) 演算を表します。[ 2 ]この演算は、モジュラス 2 加算 (または減算、これは同じです) と呼ばれることもあります。[ 3 ]このロジックでは、指定されたキーを使用して各文字にビット単位の XOR 演算子を適用することで、テキスト文字列を暗号化できます。出力を復号化するには、キーを使用して XOR 関数を再適用するだけで、暗号が解除されます。
文字列「Wiki」(8ビットASCIIでは01010111 01101001 01101011 01101001)は、繰り返し鍵11110011を使用して次のように暗号化できます。
そして逆に、復号化の場合は次のようになります。
XOR演算子は、より複雑な暗号の構成要素として非常に一般的です。しかし、定数の繰り返し鍵を使用する単純なXOR暗号は、頻度分析によって容易に解読できます。メッセージの内容が推測できる、あるいは何らかの方法で既知であれば、鍵も明らかになります。XOR暗号の主な利点は、実装が容易であることと、XOR演算の計算コストが低いことです。そのため、単純な繰り返しXOR(つまり、データ全体に対して同じ鍵でXOR演算を行う)暗号は、特別なセキュリティが要求されない場合に情報を隠すために使用されることがあります。XOR暗号は、リバースエンジニアリングを困難にするために、コンピュータマルウェアでよく使用されます。
鍵がランダムで、メッセージの長さ以上であれば、XOR 暗号はメッセージ内に鍵が繰り返される場合よりもはるかに安全です。[ 4 ]キーストリームが擬似乱数発生器によって生成される場合、結果はストリーム暗号になります。真にランダムな鍵の場合、結果はワンタイムパッドとなり、理論的には解読不可能です。
これらの暗号のいずれにおいても、XOR演算子は既知平文攻撃に対して脆弱である。なぜなら平文は暗号文=鍵。暗号文を操作することで、復号された平文の任意のビットを反転させることも容易です。これを可塑性といいます。
XORが暗号化において非常に有用な主な理由は、「完全にバランスが取れている」ためです。与えられた平文入力が0または1の場合、真にランダムな鍵ビットの場合、暗号文の結果は0または1になる確率が等しくなります。[ 5 ]
下の表は、平文と鍵ビットの考えられる4つのペアすべてを示しています。鍵または平文について何もわかっていない場合、暗号文だけでは何も判断できないことは明らかです。[ 5 ]
ANDやORなどの他の論理演算には、このような対応関係はありません。例えば、以下のANDの表をご覧ください。
暗号文が0であれば、平文も0である確率は2/3です。また、暗号文が1であれば、平文も1でなければなりません。これは、XOR方式では明らかに得られないテキストに関する情報を明らかにします。[ a ]
JavaScriptプログラミング言語 を使用した例。[ 6 ]
function xor_Encrypt ( inputString , key ) { let encrypted_Hex = "" ; for ( let i = 0 ; i < inputString . length ; i ++ ) { const plain_string = inputString . charCodeAt ( i ); const key_Char = key . charCodeAt ( i % key . length ); const xor_Result = plain_string ^ key_Char ; // XOR 演算を実行// XOR 結果を 2 桁の 16 進数文字列に変換// 1 桁の場合は '0' で埋める (例: 5 -> "05") let hex = xor_Result . toString ( 16 ); if ( hex . length < 2 ) { hex = "0" + hex ; } encrypted_Hex += hex ; } return encrypted_Hex ; }function xorDecrypt ( hexInput , key ) { let decrypted_String = '' ; // ステップ 1: hexInput 文字列を配列に変換const bytes = []; // 16 進文字列を 2 文字ずつ反復処理for ( let i = 0 ; i < hexInput . length ; i += 2 ) { // 2 文字の 16 進部分文字列 (例: "AB") を取得const hexa_Byte = hexInput . slice ( i , i + 2 ); // 16 進部分文字列を整数に変換 (例: "AB" -> 171) bytes . push ( parseInt ( hexa_Byte , 16 )); } // ステップ 2: 各バイトをキーと XOR し、文字に変換し直すfor ( let i = 0 ; i < bytes . length ; i ++ ) { const byte_Value = bytes [ i ]; const key_Char = key . charCodeAt ( i % key.length ); const xor_Result = byte_Value ^ key_Char ; // XOR演算を実行// XOR結果を文字に変換decrypted_String += String.fromCharCode ( xor_Result ) ; } return decrypted_String ; }from os import urandomdef generate_key ( length : int ) -> bytes : """暗号化キーを生成します。""" return urandom ( length )def xor_strings ( s , t ) -> bytes : """2 つの文字列を XOR で連結します。""" if isinstance ( s , str ): # テキスト文字列には単一の文字が含まれていますreturn "" . join ( chr ( ord ( a ) ^ b ) for a , b in zip ( s , t )) . encode ( "utf8" ) else : # バイトオブジェクトには 0~255 の範囲の整数値が含まれていますreturn bytes ([ a ^ b for a , b in zip ( s , t )])message = "これは秘密のメッセージです" print ( "メッセージ: " , message )key = generate_key ( len ( message )) print ( "Key:" , key )cipherText = xor_strings ( message.encode ( " utf8" ) , key ) print ( "cipherText:" , cipherText ) print ( "decrypted:" , xor_strings ( cipherText , key ) .decode ( " utf8" ))# xor_strings ( cipherText , key ) .decode ( "utf8" ) == messageの場合、テストが成功した場合は print("単体テストが失敗しました" )を実行します。GCHQがInstagramに投稿したパズルを基にした、 Rプログラミング言語を使用したより短い例。
secret_key <- c ( 0xc6 , 0xb5 , 0xca , 0x01 ) |> as.raw ()secret_message <- "I <3 Wikipedia" |> charToRaw () |> xor ( secret_key ) |> base64enc :: base64encode ()secret_message_bytes <- secret_message |> base64enc :: base64decode () xor ( secret_message_bytes , secret_key ) |> rawToChar ()