Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年08月25日(木)

SSH サーバを作る(その11) トランスポート層プロトコルのおさらい(2)

| 00:21 | SSH サーバを作る(その11) トランスポート層プロトコルのおさらい(2) - Going My Ruby Way を含むブックマーク はてなブックマーク - SSH サーバを作る(その11) トランスポート層プロトコルのおさらい(2) - Going My Ruby Way SSH サーバを作る(その11) トランスポート層プロトコルのおさらい(2) - Going My Ruby Way のブックマークコメント

DH 鍵合意

以下のプロトコル*1を見てみます。

これらはダイジェスト関数に SHA1 を使います。

また、(素数の)群(=グループ)にそれぞれ Group14、Group1 を使います。

  <client>                         <server>
    |         <KEXINIT(20)>          |
    |<==============================>| (※送信順序不定)
    |                                |
    |................................|.............................
    |                                |
    |  <KEXDH_INIT(30)>              |
    |------------------------------->|   
    |                                | (※DH鍵合意プロトコル)
    |              <KEXDH_REPLY(31)> |
    |<-------------------------------|
    |................................|.............................
    |                                |
    |         <NEWKEYS(21)>          |
    |<==============================>| (※送信順序不定)
    |                                |
メッセージ番号
SSH_MSG_KEXDH_INIT30
SSH_MSG_KEXDH_REPLY31

KEXDH_INIT (Client -> Server)

クライアントは最初 KEXDH_INIT を送信します。パラメータは以下です。

e
クライアントDH公開鍵

KEXDH_REPLY (Server -> Client)

DH 初期化の後、サーバは KEX_KEXDH_REPLY を返信します。

k_s
サーバホスト鍵
f
サーバDH公開鍵
s
サーバホスト鍵で署名された交換ハッシュ H

共有秘密 K と 交換ハッシュ H

K と H の意味合いは、鍵交換のアルゴリズムに関わらず同じです。

共有秘密 K
DH により計算される共有鍵
交換ハッシュ H
K やその他のプロトコルのパラメータの連結文字列から生成するハッシュ。
セッション識別子
最初の 交換ハッシュ H。鍵交換が再度行なわれても更新されない。

K の生成の手順です。

 (1) クライアント DH 公開鍵 e から DH により共有秘密を計算する
 (2) OpenSSL::PKey::DH#compute_key は バイナリの文字列形式で結果を返すので数値化する
 (3) mpint 型にエンコードする

H の生成の手順です。

 (1) SSH のデータ型でエンコードされた各種パラメタの連結文字列 h0 を求める
 (2) h0 からダイジェスト関数によりハッシュ h を求める
 (3) ハッシュ h にホスト鍵で署名する(署名は署名フォーマット(次回以降、説明)にエンコードする

gmrw-ssh2-server 実装での該当するコードです。

  class DH
         :
        (略) 
         :
    #
    # :section: digester / group / dh
    #
    private
    property    :initialize

    property_ro :digester, 'OpenSSL::Digest.const_get(initialize[:digester])'
    forward    [:digest] => :digester

    property_ro :groups,   'SSH2.config.oakley_group'
    property_ro :group,    'groups[initialize[:group]]'

    property_ro :dh,       'OpenSSL::PKey::DH.new'

    #
    # :section: protocol framework
    #
    private
    def ready
      dh.g = group[:g]
      dh.p = OpenSSL::BN.new(*group[:p])

      dh.generate_key! until (0...dh.p).include?(dh.pub_key) &&
                             dh.pub_key.to_i.count_bit > 1
    end

    public
    def key_exchange(service)
      @service = service

      ready ; agree

      [k, h]
    end

    #
    # :section: protocol parameters
    #
    private
    property_ro :v_c,                  'client.version'
    property_ro :v_s,                  'server.version'
    property_ro :i_c,                  'client[:kexinit].dump'
    property_ro :i_s,                  'server[:kexinit].dump'
    property_ro :k_s,                  'host_key.dump'
    property_ro :f,                    'dh.pub_key'

    property_ro :shared_secret,        'dh.compute_key(e)'
    property_ro :binary_shared_secret, 'OpenSSL::BN.new(shared_secret, 2)'
    property_ro :k,                    'encode(:mpint, binary_shared_secret)'

    property_ro :h,                    'digest(h0)'
    property_ro :s,                    'host_key.dumped_sign(h)'

    #
    # :section: DH Key Agreement
    #
    private
    def agree
      send_message :kexdh_reply,
            :host_key_and_certificates => k_s,
            :f                         => f,
            :signature_of_hash         => s
    end

    property_ro :e, 'client.message(:kexdh_init)[:e]'

    def h0
      pack([:string, v_c ],
           [:string, v_s ],
           [:string, i_c ],
           [:string, i_s ],
           [:string, k_s ],
           [:mpint , e   ],
           [:mpint , f   ]) + k
    end
  end

出力された鍵

セッション識別子は、最初の鍵交換の場合、H をそのままコピーします。

KHセッション鍵は保存します。