MTProtoのエンドツーエンドの暗号化に関するこの記事は、上級ユーザー向けです。あなたはあまり威圧的なソースから秘密のチャットについての詳細を学びたい場合は、親切に私たちの一般的なFAQを参照してくださバージョン4.6以降、主要なTelegramクライアントはMTProto2.0を使用していることに注意してください。MTProto v.1.0は廃止され、現在段階的に廃止されています。
秘密のチャットは、メッセージがチャットの参加者によってのみ保持されたキーで暗号化されている一対一のチャットです。 これらのエンドツーエンドの暗号化されたシークレットチャットのスキーマは、クラウドチャットに使用されるものとは異なることに注意してください。
MTProto2.0に関する注意
この記事では、MTProtoプロトコルバージョン2.0のエンドツーエンドの暗号化レイヤーについて説明します。バージョン1.0との主な違い(ここでは参照用に説明します)は次のとおりです。
- SHA-256はSHA-1の代わりに使用されます;
- パディングバイトはmsg_keyの計算に関与しています。
- msg_keyは暗号化されるメッセージだけでなく、秘密のチャットキーの一部にも依存します。
- 12。.0の代わりに1024paddingバイトが使用されます。.V.1.0の15パディングバイト。
関連項目:MTProto2.0:Cloud Chats,server-client encryption
鍵生成
鍵はDiffie-Hellmanプロトコルを使用して生成されます。
次のシナリオを考えてみましょう:ユーザー Aは、ユーザー Bとのエンドツーエンドの暗号化通信を開始したいと考えています。
要求を送信する
ユーザー Aはメッセージを実行します。getDhConfig diffie-Hellmanパラメータを取得するには、素数pと高次要素gを使用します。
新しいキー生成手順の前にこのメソッドを実行することは非常に重要です。 毎回すべての値を受け取る必要がないように、パラメータの値をバージョンとともにキャッシュすることは理にかなっています。 クライアントに保存されているバージョンがまだ最新の場合、サーバーはコンストラクタメッセージを返します。dhConfigNotModified。クライアントは、pが安全な2048ビット素数であるかどうか(pと(p-1)/2の両方が素数であり、2^2047<p<2^2048)をチェックし、gが素数次数(p-1)/2の巡回サブグループを生成するかどうか、すなわち二次剰余であるかどうかをチェックすることが期待されている。すなわち、g=2に対してp mod8=7;g=3に対してp mod3=2;g=4に対して余分な条件はありません;g=5に対してp mod5=1または4;g=6に対してp mod24=19または23; そして、p mod7=3,5または6g=7。 Gとpがクライアントによってチェックされた後、将来的に長い計算が繰り返されないように、結果をキャッシュすることは理にかなっています。 このキャッシュは、承認キーの生成に使用されるキャッシュと共有される場合があります。
クライアントに不適切な乱数発生器がある場合は、random_lengthパラメータ(random_length>0)を渡すことが理にかなっているので、サーバーは適切な長さの重要:サーバーのランダムシーケンスを生の形式で使用することは安全ではない可能性があります。 たとえば、同じ長さのクライアント乱数(client_random)を生成し、final_random := random XOR client_random
を使用することで、クライアントシーケンスと組み合わせる必要があります。
クライアントAは、(十分なエントロピーまたはサーバーのランダムを使用して)2048ビットの数値aを計算し、メッセージを実行します。g_a := pow(g, a) mod dh_prime
を渡した後のrequestEncryption。
ユーザー Bは、チャットコンストラクタencryptedChatRequestedを使用して、関連するすべての承認キー(すべての承認されたデバイス)の更新updateEncryptionを受け取ります。 ユーザーには、ユーザー Aに関する基本情報が表示され、要求を受け入れるか拒否するように求められる必要があります。
両方のクライアントは、g、g_a、g_bが1より大きく、p-1より小さいことを確認します。 G_aとg_bが2^{2048-64}とp-2^{2048-64}の間にあることを確認することをお勧めします。
要求の受け入れ
ユーザー Bがクライアントインターフェイスでaとの秘密チャットの作成を確認した後、クライアントBはDiffie-Hellmanメソッドの最新 その後、aの規則と同様の規則を使用して、ランダムな2048ビット数bを生成します。 encryptedChatRequestedを使用して更新からg_aを受信すると、最終的な共有キーkey = (pow(g_a, b) mod dh_prime)
<256バイトの場合、キーの長さが正確に256バイトになるように、パディングとして先頭のゼロバイトをいくつか追加します。 その指紋key_fingerprintは、SHA1(キー)の最後の64ビットに等しくなります。 注1:この特定のケースでは、Sha1はMTProto2.0秘密のチャットでもここで使用されます。
注2: この指紋は、クライアントソフトウェアを開発するときにバグを検出するための鍵交換手順の健全性チェックとして使用されます。 クライアントのキービジュアライゼーションは、SHA1の最初の128ビット(初期キー)と、SHA256の最初の160ビット(シークレットチャットがレイヤー46に更新されたときに使用されるキー)を使用して生成されます。
クライアントBはメッセージを実行します。それを渡した後のacceptEncryptiong_b := pow(g, b) mod dh_prime
とkey_fingerprint。
クライアントBのすべての許可されたデバイスについて、現在のデバイスを除く、updateEncryptionの更新は、コンストラクター encryptedChatDiscardedとともに送信されます。 その後、秘密のチャットにアクセスできる唯一のデバイスは、メッセージを呼び出したデバイスBです。acceptEncryption。
ユーザー Aは、チャットを開始した承認キーのコンストラクター encryptedChatを使用してupdateEncryption更新を送信します。更新からg_bを使用すると、クライアントAは共有キーkey = (pow(g_b, a) mod dh_prime)
<256バイトの場合、キーの長さが正確に256バイトになるように、パディングとして先頭のゼロバイトをいくつか追加します。 受信したキーの指紋がencryptedChatに渡された指紋と同じ場合、着信メッセージを送信して処理できます。 それ以外の場合は、メッセージ。discardEncryptionを実行し、ユーザーに通知する必要があります。
Perfect Forward Secrecy
過去の通信を安全に保つために、公式のTelegramクライアントは、キーが100を超えるメッセージの復号化と暗号化に使用された後、または一週間以上使用された後、キーが少なくとも一つのメッセージの暗号化に使用されていれば、再キーイングを開始します。 古いキーは安全に破棄され、現在使用されている新しいキーにアクセスしても再構築することはできません。
再キーイングプロトコルは、この記事でさらに説明されています:秘密のチャットでの完全な転送秘密。
公式のTelegramクライアントと互換性があるためには、クライアントが秘密のチャットで前方秘密をサポートする必要があることに注意してください。
秘密のチャットでのメッセージの送受信
送信メッセージのシリアル化と暗号化
DecryptedMessage型のTLオブジェクトが作成され、プレーンテキストでメッ 下位互換性のために、オブジェクトはコンストラクター decryptedMessageLayerでラップされ、サポートされているレイヤー(46から始まる)が示されている必要があります。
エンドツーエンドの暗号化されたメッセージの内容のTLスキーマは、ここで利用可能です”
結果の構造は、汎用TLルールを使用 結果の配列の先頭には、これらの4バイトを数えない配列の長さを含む4バイトが追加されます。
バイト配列は、その長さを16バイトで割り切れるように、12から1024のランダムパディングバイトでパディングされます。 (古いMTProto1.0暗号化では、0から15のパディングバイトのみが使用されました。)
メッセージキー msg_keyは、前のステップで取得したデータのSHA256の128中間ビットとして計算され、共有キーキーから32バイトの前に付加されます。 (以前のMTProto1.0暗号化では、msg_keyは、前の手順で取得したデータのSHA1の128下位ビットとして、パディングバイトを除いて、異なる方法で計算されました。Mtproto2.0では、AESキー aes_keyおよび初期化ベクトルaes_ivが、(キーは、キー生成中に取得された共有キーである)次のように計算される:msg_key_large=SHA256(substr(key,88+x,32)+plaintext+random_padding);
mtproto2.0の場合、x=0の場合秘密のチャットの発信者からのメッセージ、反対方向のメッセージのx=8。
廃止されたMTProto1の場合。0、msg_key、aes_key、およびaes_ivは別々に計算されました(参照については、このドキュメントを参照してください)。データは、256ビットキー aes_key、および256ビット初期化ベクトルaes-ivで暗号化され、AES-256encryption with infinite garble extension(IGE)を使用します。 暗号化キー fingerprint key_fingerprintとメッセージキー msg_keyが、結果のバイト配列の先頭に追加されます。
暗号化されたデータは、メッセージに埋め込まれています。sendEncrypted API呼び出しは、秘密のチャットの相手に配信するためにTelegramサーバーに渡されます。
MTProto2.0からMTProto1にアップグレードします。0
秘密チャットの両当事者が少なくともレイヤ73を使用しているとすぐに、すべての送信メッセージにMTProto2.0のみを使用する必要があります。 最初に受信したメッセージの中には、秘密チャットの作成中に十分に高い開始層がネゴシエートされていない場合、MTProto1.0を使用するものがあります。 MTProto2.0で暗号化された最初のメッセージ(またはレイヤ73以上の最初のメッセージ)を受信した後、シーケンス番号が高いすべてのメッセージもMTProto2.0で暗号化
現在の層が73より低い限り、各当事者はMTProto1.0で受信したメッセージを復号化しようとする必要があり、これが成功しない場合(msg_keyが一致しない)、MTProto2.0を試 最初のMTProto2.0暗号化されたメッセージが到着したら(またはレイヤーが73にアップグレードされたら)、それ以上のメッセージのMTProto1.0復号化を試みる必要はありません(クライアントがまだいくつかのギャップが閉じられるのを待っている場合を除きます)。
受信メッセージの復号化
上記の手順は逆の順序で実行されます。 暗号化されたメッセージを受信すると、msg_keyが実際には、復号化されたメッセージのSHA256ハッシュの128中間ビットに等しく、共有キーから取得された32バイトメッセージ層がクライアントでサポートされている層よりも大きい場合は、クライアントのバージョンが古くなっていることをユーザーに通知し、更新を
シーケンス番号
可能な操作から保護するために、すべてのメッセージを元の順序で解釈する必要があります。 シークレットチャットは、サーバーから独立してseq_noカウンタを処理するための特別なメカニ
これらのカウンタの適切な処理については、この記事”シークレットチャットのシーケンス番号”で詳しく説明します。
公式のTelegramクライアントと互換性があるためには、クライアントが秘密のチャットでシーケンス番号をサポートしている必要があります。
暗号化されたファイルの送信
秘密のチャットに送信されるすべてのファイルは、チャットの共有キーに関連しないワンタイムキーで暗号化 暗号化されたファイルが送信される前に、メッセージのfileパラメータを使用して、暗号化されたファイルのアドレスが暗号化されたメッセージの外部にsendEncryptedFileメソッドと、直接復号化のためのキーがメッセージの本文で送信されること(コンストラクタdecryptedMessageMediaPhoto、decryptedMessageMediaVideoおよびdecryptedMessageMediaFileのkeyパラメータ。ファイルが秘密のチャットに送信される前に、2つのランダムな256ビットの数値が計算され、これはファイルの暗号化に使用されるAESキーと初期化ベク 無限ガーブル拡張(IGE)を使用したAES-256暗号化は、同様の方法で使用されます。
キー指紋は次のように計算されます。
- digest=md5(key+iv)
- fingerprint=substr(digest,0,4)XOR substr(digest,4,4)
ファイルの暗号化された内容は、クラウドチャットのファイルの内容とほぼ同じ方法でサーバーに保存されます。セーブファイルパーツ。メッセージへの後続の呼び出し。sendEncryptedFileは、格納されたファイルに識別子を割り当て、メッセージとともにアドレスを送信します。 受信者はencryptedMessageで更新を受信し、fileパラメータにはファイル情報が含まれます。
着信および発信の暗号化されたファイルは、コンストラクタinputEncryptedFileを使用して他の秘密のチャットに転送することができ、同じコンテンツをサーバーに二度保存しないようにすることができます。
更新ボックスでの作業
秘密のチャットは、ユーザーではなく、特定のデバイス(またはむしろ承認キー)に関連付けられています。 Ptsを使用してクライアントの状態を記述する従来のメッセージボックスは、長期的なメッセージ保存と異なるデバイスからのメッセージアクセス用に設計されているため、適切ではありません。
この問題の解決策として、追加の一時的なメッセージキューが導入されました。 秘密のチャットからのメッセージに関する更新が送信されると、新しい値のqtsが送信され、接続に長い中断があった場合や更新が失われた場合に差を再構築するのに役立ちます。
イベントの数が増えるにつれて、qtsの値は新しいイベントごとに1ずつ増加します。 初期値は0に等しくない場合があります(およびそうではありません)。
一時キューからのイベントがクライアントによって受信され、格納されたという事実は、メッセージの呼び出しによって明示的に確認されます。receivedQueueメソッドまたは暗黙的にupdatesの呼び出しによって。getDifference(渡されたqtsの値であり、最終状態ではありません)。 クライアントによって配信されたと確認されたすべてのメッセージ、および7日より古いメッセージは、サーバーから削除される可能性があります。
承認解除時に、対応するデバイスのイベントキューが強制的にクリアされ、qtsの値は無関係になります。
新しいレイヤーへの更新
クライアントは、秘密のチャットの反対側にクライアントによってサポートされていることがわかっている最 秘密チャットが最初に作成されるとき、この値は46に初期化する必要があります。 このリモートレイヤ値は、上位レイヤの情報を含むパケットを受信した直後に常に更新する必要があります。:
- その
decryptedMessageLayer
>=46、または - 廃止されたレイヤー8のdecryptedMessageServiceコンストラクターであるかのようにラップされたd e c r e p t e d messageactionnotifylayerサービスメッセージにlayer_noを含む秘密”>)。
リモートクライアントにローカルレイヤについて通知する
リモートクライアントにローカルレイヤを通知するには、クライアントはdecryptedMessageActionNotifyLayer
タイプのメッセージを送信する必要があります。 この通知は、適切なレイヤーのコンストラクターでラップする必要があります。
クライアントがローカルレイヤについてリモートクライアントに通知する必要がある場合は、次の二つのケースがあります。
- 新しい秘密チャットが作成されるとすぐに、秘密鍵が正常に交換された直後に。
- ローカルクライアントが新しいシークレットチャットレイヤーをサポートするように更新された直後。 この場合、通知は現在存在するすべての秘密のチャットに送信する必要があります。 これは、secret chats実装の変更を含む新しいレイヤーに更新する場合にのみ必要であることに注意してください(例: クライアントがレイヤ46からレイヤ47に更新されたときにこれを行う必要はありません)。