復号器の作成の流れ

rustls は以下のような型を持つ値を返す関数を提供する。
シグネチャを見るだけだと、サーバまたはクライアントの片側が持っておくべき暗号ペアを意味しているように見える。この値を導出するための処理を参考にすれば両側の encrypted payload を復号するための解読器を初期化できる。

type MessageCipherPair = (Box<dyn MessageDecrypter>, Box<dyn MessageEncrypter>);


key_block の生成

参考:https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/rustls/src/tls12/mod.rs#L182-L200
参考:https://datatracker.ietf.org/doc/html/rfc5246#section-6.3:~:text=key_block%20%3D%20PRF(SecurityParameters.master_secret%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22key%20expansion%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SecurityParameters.server_random%20%2B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SecurityParameters.client_random)%3B


必要なもの

  • master_secret
  • client_random
  • server_random


{client,server}_{write_key,iv} の生成

通信で利用される AEAD algorithm が要求する鍵の形式に沿うように key_block から以下をこの順番に切り出す
  • client_write_key
  • server_write_key
  • client_write_iv
  • server_write_iv


復号器の初期化

参考:https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/rustls/src/crypto/ring/tls12.rs#L131-L143
各サイドの write_key と write_iv があれば初期化可能。
AeadKey::new に復号したいサイドの write_key を与えると AeadKey が得られるので、これを decrypter の第一引数として渡す。第二引数は iv をそのまま与えればよい。
参考:https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/rustls/src/tls12/mod.rs#L172-L179


/// A key for an AEAD algorithm.
///
/// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length.
pub struct AeadKey {
    buf: [u8; Self::MAX_LEN],
    used: usize,
}
impl AeadKey {
    #[cfg(feature = "tls12")]
    pub(crate) fn new(buf: &[u8]) -> Self {
        debug_assert!(buf.len() <= Self::MAX_LEN);
        let mut key = Self::from([0u8; Self::MAX_LEN]);
        key.buf[..buf.len()].copy_from_slice(buf);
        key.used = buf.len();
        key
    }
}


rustls 内での MessageDecrypter の利用

https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/rustls/src/record_layer.rs#L85 (decrypt_incoming) でのみ decrypt() が呼び出されている。
seq は復号を進めるたびにインクリメントする必要があるらしい。


decrypt_incomingMessageDeframer.pop から呼び出されている。
MessageDeframer.pop <- ConnectionCore.deframe <- ConnectionCore.process_new_packets <- ConnectionCommon.process_new_packets <- Connection.process_new_packets


https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/examples/src/bin/simpleclient.rs#L35 などを見ると、基本的に Stream::newClientConnection と socket を渡すことが想定されている?socket ではなくなにかしらのバッファかチャネルを模したものを渡せたら使えそう。
https://github.com/rustls/rustls/blob/078f03334b2de9d8e464755d8836318b9a221346/rustls/src/stream.rs#L6-L17 いわく io::Readio::Write が実装されてればよいらしく、example を見る限りでは TCP で流れてきた payload を渡せばいいだけにみえる。あとはキャプチャした特定の TCP connection のパケットを流し続けるが、うまく不思議な力で master_secret を Connection に与える手法ができればよさそう。