3. プッシュメッセージの暗号化
3. プッシュメッセージの暗号化
プッシュメッセージの暗号化は4段階で行う.
-
ECDH [ECDH] により共有秘密を導出する (本ドキュメント第3.1節).
-
共有秘密を認証秘密と組み合わせ, [RFC8188] で用いる入力鍵素材 IKM を作る (本ドキュメント第3.3節).
-
[RFC8188] の手続きでコンテント暗号化鍵と nonce を導出する.
-
[RFC8188] に従い暗号化または復号する.
鍵導出の要約は第3.4節にある. 暗号化コンテントコーディングの使用制限は第4節にある.
3.1. Diffie-Hellman 鍵合意
ユーザエージェントはアプリケーション向けに新しいサブスクリプションを生成するたびに, ECDH [ECDH] 用の P-256 [FIPS186] 鍵対も生成する.
プッシュメッセージ送信時, アプリケーションサーバーは同じ P-256 曲線上で新しい ECDH 鍵対を生成する.
アプリケーションサーバーの ECDH 公開鍵は暗号化コンテントコーディングヘッダの "keyid" パラメータに含める ([RFC8188] 第2.1節).
アプリケーションサーバーは [ECDH] の手続きで自らの ECDH 秘密鍵とユーザエージェントから提供された公開鍵を組み合わせる. プッシュメッセージ受信時, ユーザエージェントは同様に自らの秘密鍵と "keyid" パラメータ内のアプリケーションサーバー公開鍵を組み合わせる. これらの演算は同じ ECDH 共有秘密を生む.
3.2. プッシュメッセージの認証
プッシュメッセージを正しく認証するため, ユーザエージェントが生成する情報に対称の認証秘密を加える. 認証秘密は第3.3節の鍵導出手続きに混ぜる.
ユーザエージェントは MUST で, プッシュメッセージ認証に用いる推測困難な16オクテット列を生成して提供しなければならない. SHOULD として, 暗号学的に強い乱数生成器で生成する [RFC4086].
3.3. 共有秘密と認証秘密の結合
ECDH による共有秘密と認証秘密は, HMAC ベースの鍵導出関数 HKDF [RFC5869] で結合される. 出力は [RFC8188] が用いる入力鍵素材となる.
HKDF は SHA-256 ハッシュ [FIPS180-4] を次の入力で用いる.
salt: 認証秘密
IKM: ECDH で導出した共有秘密
info: ASCII 文字列 "WebPush: info" (NUL 終端なし), ゼロオクテット, ユーザエージェントの ECDH 公開鍵, アプリケーションサーバーの ECDH 公開鍵の連結 (両公開鍵は [X9.62] の非圧縮点形式). すなわち:
key_info = "WebPush: info" || 0x00 || ua_public || as_public
L: 32オクテット (出力長は下位 SHA-256 HMAC の出力長と同じ)
3.4. 暗号化の要約
最終的なコンテント暗号化鍵と nonce は, 次の順で生成する. ここでは HKDF を SHA-256 の HMAC に展開した疑似コードで示す.
-- For a user agent:
ecdh_secret = ECDH(ua_private, as_public)
auth_secret = random(16)
salt = <from content coding header>
-- For an application server:
ecdh_secret = ECDH(as_private, ua_public)
auth_secret = <from user agent>
salt = random(16)
-- For both:
## Use HKDF to combine the ECDH and authentication secrets
# HKDF-Extract(salt=auth_secret, IKM=ecdh_secret)
PRK_key = HMAC-SHA-256(auth_secret, ecdh_secret)
# HKDF-Expand(PRK_key, key_info, L_key=32)
key_info = "WebPush: info" || 0x00 || ua_public || as_public
IKM = HMAC-SHA-256(PRK_key, key_info || 0x01)
## HKDF calculations from RFC 8188
# HKDF-Extract(salt, IKM)
PRK = HMAC-SHA-256(salt, IKM)
# HKDF-Expand(PRK, cek_info, L_cek=16)
cek_info = "Content-Encoding: aes128gcm" || 0x00
CEK = HMAC-SHA-256(PRK, cek_info || 0x01)[0..15]
# HKDF-Expand(PRK, nonce_info, L_nonce=12)
nonce_info = "Content-Encoding: nonce" || 0x00
NONCE = HMAC-SHA-256(PRK, nonce_info || 0x01)[0..11]
プッシュメッセージは単一レコードのみ (第4節) であり, 先頭レコードのシーケンス番号はゼロであるため, 最終 nonce とレコードシーケンス番号の排他的論理和は省略している.