Piping Chat 0.3.0以降のエンドツーエンド暗号化と公開鍵認証の流れ
ざっくりと
以下のPRで実装された。
なぜ認証したいのか?
流れ
短くするため、AliceがBobが本物か検証する。BobがAliceを検証するのは、以下の流れの名前をひっくり返せばなるはず。
上位のリストだけ追えば、フローがわかるようになっている。
以下の入れ子になっているリストは具体的な説明。
Aliceは事前に信頼できるソースからBobのRSA公開鍵(= Bob_RSA_Pub)を手に入れる。
1つは、セッションID生成に使うため(= Bob_Session_ECDH_Pub)
もう1つは、通信路を暗号化するための公開鍵(= Bob_Encrypt_ECDH_Pub)
上記の2つの鍵の生成方法は全く同じ。内部が乱数だから値が異なるだけ。
形式は、JSON Web Key (JWK) で送られる
コードは「鍵生成部分のコード」に掲載
送る鍵の種類もBobと同様
Alice => Bob, Bob => Aliceで鍵送る順序は、どっちが先でも構わない
AliceはBob_Session_ECDH_Pubを使ってセッションIDを生成するSession_ID
ECDHで交換できる秘密鍵のがセッションIDになるため、Session_IDはAliceもBobも同じものになる。
暗号化通信用の鍵と全く違うものを使って(鍵は独立しているので)セッションIDを生成するため、セッションIDの流出と暗号通信は無関係になる。
Session_IDの生成はSHA256(JSON文字列化(ECDHで得られる共有鍵のJWK))な感じである
セッションIDの生成は、ECDHで鍵を共有していると思うよりランダムバイト列を共有したと思ったほうが、理解しやすいと思う。
AliceはBob_Encrypt_ECDH_Pubを使ってECDHの共有鍵(Encrypt_Key)を生成する
Encrypt_KeyはBobも同じものが生成できる。
Encrypt_Keyを使ってこれからの通信はすべて暗号化される。
Bobは自分のRSA秘密鍵を使って、Session_IDに署名する。
Bobは署名したSession_IDをAliceに送る
AliceはBobの署名されたSession_IDを予め入手してあるBob_RSA_Pubで検証する。
この検証がtrueならばBobは本物だということがわかる
鍵生成部分のコード
以下のコードは実際のPiping ChatだとexportKey()とgenerateKey()は一塊ではないが、簡素化するために一塊にした。 code:ts
// Bob_Session_ECDH_PubやBob_Encrypt_ECDH_Pubの生成方法
await crypto.subtle.exportKey(
'jwk',
(await window.crypto.subtle.generateKey(
{ name: 'ECDH', namedCurve: 'P-256'},
true,
)).publicKey,
)
勘違いしそうな箇所
利便性のため。
常に相手のRSA公開鍵が用意する必要があるのは利便性が低下すると思ったから。
公開鍵認証を使っていないときでもセッションIDは作られる。
現在のところ、セッションIDの役目は公開鍵認証するため。そのため、公開鍵認証しないときはセッションIDは作られるだけで、使われない。
経緯ページ