Hatena::Grouprubyist

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

Ruby ロゴ (C) Ruby Association LLC

2011年08月28日(日)

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

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

パケットについておさらいです。

どこを暗号化/MAC/圧縮するか?

以前の日記( SSHサーバを作る (その0) 下調べ - Going My Ruby Way - Rubyist)の一部を再掲します。

フィールド名説明暗号化MAC圧縮
uint32packet_length'packet_length'自体 と 'mac' を含まないパケット長(byte 単位)
bytepadding_length'padding' の長さ(byte単位)
byte[n1]payloadメッセージ
byte[n2]paddingランダムなbyte値によるパディング。
byte[m]macMAC*1

payload の長さ n1 は、

  n1 = 'packet_length' の値 - 'padding_length' の値 - 1 ('padding_length'自体の長さ)

です。

padding の長さ n2 は'padding_length' の値(最小 4、最大 255)です。

これは、packet_length + padding_length + payload + padding の長さが、暗号ブロックサイズか 8 の大きい方の倍数になるように決めます。

現時点の gmrw-ssh2-server 実装でのパケットを組み立てコードです。

  def pack(payload)
    n1    = 4 # packect_length field size
    n2    = 1 # padding_length field size
    mn    = 4 # minimum padding size

    zipped_data = compress[ payload ]
    total_len   = block_align[ zipped_data.length + n1 + n2 + mn ]
    pack_len    = total_len - n1
    padd_len    = pack_len - n2 - zipped_data.length
    padding     = OpenSSL::Random.random_bytes(padd_len)
    packet      = [pack_len, padd_len].pack("NC") + zipped_data + padding

    encrypt[ packet ] + compute_mac[ packet ]
  end

mac の長さ m は MACアルゴリズムにより決まります。初期は 0 です。

また、RFC では(mac まで含めた)全体が 35000 バイト以下のパケットが処理できなければならないと記載されています。


暗号化

暗号化は

packet_length + padding_length + payload + padding

に対して行ないます。サイズは padding により暗号ブロックサイズ(か 8 のどちらか大きい方)にアラインされてます。

payload は圧縮済み(圧縮アルゴリズムが選択されている場合)のものを暗号化します。

MAC

MAC は、暗号化前/復号した後のパケットの

  unencrypted_packet = packet_length + padding_length + payload + padding
  data_for_mac = [ sequence_number, unencrypted_packet ].pack("Na*")

の data_for_mac に対して行ないます。

sequence_number はシーケンスナンバーです。以前の日記*2でも書きましたが、これは送信方向毎に独立した送信/受信カウンタです。初期値が 0 で、符号無し32ビット整数の精度を持ちます。(0xffffffff までいったら 0 に戻ります)

  (初期値)    sequence_number = 0

  (送信/受信) sequenct_number = (sequenct_number + 1) % 0xffff_ffff
圧縮

圧縮/伸長は payload に対して行ないます。パケット毎に flush します。