🦭

SRP認証クライアントの実装と備忘メモ

に公開

ユーザー認証について検討した時に、暗号化されていない通信路を介してでもユーザ名とパスワードを使って認証可能なSRP(Secure Remote Password)プロトコルがあることを知りました。AWS Cognito等でも利用できるらしく良さそうだと思ったので何番煎じなのかはわかりませんが内容を理解すべく自分でTypeScriptのライブラリを作成しました。有名なSRPにはSRP-3とSRP-6aがあるようですが、SRP-6aを実装しています。実装を通じて理解したSRP認証の流れのポイントを備忘メモとして残しておきます。

成果物

https://github.com/scirexs/srp6a

概要

ユーザー登録時とログイン時で流れが大きく異なる。認証鍵session,keyはフロント・サーバーの両方で計算して導出する。正しいユーザー名とパスワードを使用すれば結果的に同じ値が導出されるが、フロント側とサーバー側で使用する計算式が異なり、使用する変数(引数)も異なる。計算式がそれぞれ異なるが、結果が等しくなるように設計されているのがSRP認証の重要なポイント。

式・変数・単語の定義

説明
| 結合演算子
^ べき乗演算子
H() 一方向ハッシュ関数
RAND() 乱数バイト配列生成
MP(n,k,m) 剰余べき乗関数 (n^k % m)
PAD(d) バイト配列ゼロ詰め固定長化関数
数式変数 説明 表記
N とても大きい素数 prime
g 数値を生成するための原始根 generator
k 複雑性向上のための値 multiplier
s ランダムなソルト salt
U ユーザー名 username
p 平文パスワード password
I ユーザー名+パスワードのハッシュ identity
x 身元証明値生成の鍵となる値 secret
v 身元証明値の検証の鍵となる値 verifier
u 中間者攻撃による改ざんを防止する値 scrambling
a ランダム生成されたフロント側秘密鍵 pvtClient
A ランダム生成されたフロント側公開鍵 pubClient
b ランダム生成されたサーバー側秘密鍵 pvtServer
B ランダム生成されたサーバー側公開鍵 pubServer
S 計算で導出される認証鍵 session
K 計算で導出された認証鍵のハッシュ key
Mc 認証鍵から生成した通信用の値 evidence
Ms 認証鍵から生成した通信用の値 evidenceServer

導出式一覧

数式変数 導出式 メモ
N <constant> -
g <constant> -
U <read from outside> -
p <read from outside> -
I H(U | ":" | p) -
x H(s | I) -
s RAND() -
v MP(g, x, N) -
k H(N | PAD(g)) -
a RAND() -
A MP(g, a, N) -
b RAND() -
B k * v + MP(g, b, N) -
u H(PAD(A) | PAD(B)) -
Sc MP(B - (k * MP(g, x, N)), a + (u * x), N) Client side
Ss MP(MP(v, u, N) * A, b, N) Server side
Kc H(Sc) Client side
Ks H(Ss) Server side
Mc H(H(N) xor H(g), H(U), s, A, B, Kc) -
Mc' H(H(N) xor H(g), H(U), s, A, B, Ks) Verify Mc
Ms H(A, Mc, Ks) -
Ms' H(A, Mc, Ks) Expected Ms

認証手順

ユーザー登録時

  1. フロント側: username,passwordからsaltverifierを生成
  2. フロント側: username,salt,verifierをサーバーに送信
  3. サーバー側: 受け取ったデータをデータベース等に格納

ログイン時

  1. フロント側: pvtClientpubClientのランダム鍵ペアを生成
  2. フロント側: usernameと生成したpubClient公開鍵をサーバーに送信
  3. サーバー側: データを受け取り、データベース等からsaltverifierを取得
  4. サーバー側: pvtServerpubServerのランダム鍵ペアを生成
  5. サーバー側: saltpubServer公開鍵をフロントに送信
  6. フロント側: session認証鍵を計算で導出
  7. フロント側: session認証鍵から通信用の値evidenceを導出
  8. フロント側: session認証鍵からサーバーから送信されるevidenceServerを予測導出
  9. フロント側: 導出した通信用の値evidenceをサーバーに送信
  10. サーバー側: データを受け取り、session認証鍵を異なる計算式で導出
  11. サーバー側: session認証鍵からevidenceを導出する
  12. サーバー側: 受け取ったevidenceと導出したevidenceが等しいことを確認
  13. サーバー側: session認証鍵からevidenceServerを導出する
  14. サーバー側: 認証結果とevidenceServerをフロントに送信
  15. フロント側: 予測したevidenceServerと受け取ったevidenceServerの一致を確認

雑記

SPR認証の実装について書いている記事がいくつかありますが、keyを通信する事になっている実装があったりして最初は混乱しました。自分で使うためにも作ったので、そこまで悪くないものができた気がします。

参考文献

Discussion