使用Chromium quic_toy配置TLS握手的经验

Quic Toy中默认使用的是QuicCrypto握手,现在尝试将其配置为TLS1.3握手。
首先应当让version支持TLS,从quic的流程看,version的设置在QUICToyClient::SendRequestAndPrintResponse中完成。

  quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();

  std::string quic_version_string = GetQuicFlag(FLAGS_quic_version);
  const int32_t quic_ietf_draft = GetQuicFlag(FLAGS_quic_ietf_draft);
  if (quic_ietf_draft > 0) {
    quic::QuicVersionInitializeSupportForIetfDraft(quic_ietf_draft);
    if (quic_version_string.length() == 0) {
      quic_version_string = "T099";
    }
  }
  if (quic_version_string.length() > 0) {
    if (quic_version_string[0] == 'T') {
      // ParseQuicVersionString checks quic_supports_tls_handshake.
      SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
    }
    quic::ParsedQuicVersion parsed_quic_version =
        quic::ParseQuicVersionString(quic_version_string);
    if (parsed_quic_version.transport_version ==
        quic::QUIC_VERSION_UNSUPPORTED) {
      return 1;
    }
    versions.clear();
    versions.push_back(parsed_quic_version);
    quic::QuicEnableVersion(parsed_quic_version);
  }

  if (GetQuicFlag(FLAGS_force_version_negotiation)) {
    versions.insert(versions.begin(),
                    quic::QuicVersionReservedForNegotiation());
  }

Version包含两个部分,Transport_version与Handshake_protocol。
从GDB信息看,versions只保存了QUICCrypto部分,TLS全部被过滤掉了。
过滤的TLS算法的逻辑在:

quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
  ......
  ......
ParsedQuicVersionVector CurrentSupportedVersions() {
  return FilterSupportedVersions(AllSupportedVersions());
}
ParsedQuicVersionVector FilterSupportedVersions(
    ParsedQuicVersionVector versions) {
  ParsedQuicVersionVector filtered_versions;
  filtered_versions.reserve(versions.size());
  for (ParsedQuicVersion version : versions) {
    if (version.handshake_protocol == PROTOCOL_TLS1_3 &&
        !GetQuicFlag(FLAGS_quic_supports_tls_handshake)) {
      continue;
    }
    if (version.transport_version == QUIC_VERSION_99) {
      if (GetQuicReloadableFlag(quic_enable_version_99)) {
        filtered_versions.push_back(version);
      }
    } else if (version.transport_version == QUIC_VERSION_48) {
      if (GetQuicReloadableFlag(quic_enable_version_48_2)) {
        filtered_versions.push_back(version);
      }
    } else if (version.transport_version == QUIC_VERSION_47) {
      if (GetQuicReloadableFlag(quic_enable_version_47)) {
        filtered_versions.push_back(version);
      }
    } else if (version.transport_version == QUIC_VERSION_39) {
      if (!GetQuicReloadableFlag(quic_disable_version_39)) {
        filtered_versions.push_back(version);
      }
    } else {
      filtered_versions.push_back(version);
    }
  }
  return filtered_versions;
}

可见如果要enable TLS1_3,需要使能 FLAGS_quic_supports_tls_handshake。
从上面的QUICToyClient::SendRequestAndPrintResponse代码片段,可以看到:

 if (quic_version_string.length() > 0) {
    if (quic_version_string[0] == 'T') {
      // ParseQuicVersionString checks quic_supports_tls_handshake.
      SetQuicFlag(FLAGS_quic_supports_tls_handshake, true);
    }
    quic::ParsedQuicVersion parsed_quic_version =
        quic::ParseQuicVersionString(quic_version_string);
    if (parsed_quic_version.transport_version ==
        quic::QUIC_VERSION_UNSUPPORTED) {
      return 1;
    }
    versions.clear();
    versions.push_back(parsed_quic_version);
    quic::QuicEnableVersion(parsed_quic_version);
  }

如果quic_version设置为了'T'开头,那会设置TLS FLAG,并且清除掉先前所有的version(QUIC_CRYPTO)。
而quic_version由FLAGS_quic_version决定:

std::string quic_version_string = GetQuicFlag(FLAGS_quic_version);

在quic_toy_client.cc文件中有定义FLAGS_quic_version:

DEFINE_QUIC_COMMAND_LINE_FLAG(
    std::string,
    quic_version,
    "",
    "QUIC version to speak, e.g. 21. If not set, then all available "
    "versions are offered in the handshake. Also supports wire versions "
    "such as Q043 or T099.");

是否设置这个值为T099就可以了?我们还缺少服务端的支持,先暂停客户端,去看看服务端的配置。
在quic_simple_server_bin.cc中,对应用程序参数的解析如下:

  if (line->HasSwitch("quic_ietf_draft")) {
    if (!base::StringToInt(line->GetSwitchValueASCII("quic_ietf_draft"),
                           &FLAGS_quic_ietf_draft)) {
      LOG(ERROR) << "--quic_ietf_draft must be an integer\n";
      return 1;
    }
    if (FLAGS_quic_ietf_draft > 0) {
      quic::QuicVersionInitializeSupportForIetfDraft(FLAGS_quic_ietf_draft);
      quic::QuicEnableVersion(quic::ParsedQuicVersion(quic::PROTOCOL_TLS1_3,
                                                      quic::QUIC_VERSION_99));
    }
  }

嗯,看来只要指定--quic_ietf_draft就可以打开TLS握手,但是这样的话就必须要用IETF QUIC。
先用IETF QUIC试试,在server侧配置IEFT选项

./quic_server --quic_response_cache_dir=/tmp/quic-data/www.example.org --certificate_file=./leaf_cert.pem --key_file=./leaf_cert.pkcs8 --quic_ietf_draft=18

在Client侧,通过配置

DEFINE_QUIC_COMMAND_LINE_FLAG(
    int32_t,
    quic_ietf_draft,
    0,
    "QUIC IETF draft number to use over the wire, e.g. 18. "
    "By default this sets quic_version to T099. "
    "This also enables required internal QUIC flags.");

我错误的将这里的0修改为了1,结果出现错误:

Server talks QUIC, but none of the versions supported by this client: ff000001

奇怪的为什么版本号变成了ff000001。我调试了代码,发现version里面的值是正确的,但是ParsedQuicVersionVectorToString(versions)函数解析成为ff000001。
而在服务端,我调试看到,在QuicDispatcher::ProcessPacket函数中,对packet_info的解析变成了0, 0。
抓包,发现version字段里面也是ff00001,说明客户端发送出来的时候就是该值。
那为什么会编程ff00001呢,答案在ParsedQuicVersionVectorToString函数,该函数实现如下:

switch (parsed_version.transport_version) {
    case QUIC_VERSION_39:
      return MakeVersionLabel(proto, '0', '3', '9');
    case QUIC_VERSION_43:
      return MakeVersionLabel(proto, '0', '4', '3');
    case QUIC_VERSION_46:
      return MakeVersionLabel(proto, '0', '4', '6');
    case QUIC_VERSION_47:
      return MakeVersionLabel(proto, '0', '4', '7');
    case QUIC_VERSION_48:
      return MakeVersionLabel(proto, '0', '4', '8');
    case QUIC_VERSION_99:
      if (parsed_version.handshake_protocol == PROTOCOL_TLS1_3 &&
          GetQuicFlag(FLAGS_quic_ietf_draft_version) != 0) {
        return MakeVersionLabel(0xff, 0x00, 0x00,
                                GetQuicFlag(FLAGS_quic_ietf_draft_version));
      }
      return MakeVersionLabel(proto, '0', '9', '9');
    case QUIC_VERSION_RESERVED_FOR_NEGOTIATION:
      return CreateRandomVersionLabelForNegotiation();
    default:
      // This is a bug because we should never attempt to convert an invalid
      // QuicTransportVersion to be written to the wire.
      QUIC_BUG << "Unsupported QuicTransportVersion: "
               << parsed_version.transport_version;
      return 0;
  }

可见如果是TLS,且开启了IETF,那么就会设置为ff00000x格式,在quic_toy_client.c中我设置为了1,所以出现ff000001。把这个值修改为18,握手成功!
抓包如下:


image.png

第二种尝试:
在quic_toy_client.cc中,将quic_ietf_draft选项配置为0。服务端去掉--quic_ietf_draft=18启动参数。
通过打印,发现client已经使用了T099,服务端也打印支持T099。
握手,出现Error: QUIC_NETWORK_IDLE_TIMEOUT错误。
抓包发现还是IETF的报文,报文里面的version被wireshark解析为Unknown。在服务端查看是在QuicDispatcher::MaybeDispatchPacket函数中被丢弃了,因为server_connection_id.length()检查不通过。
将版本号修改为T048,出现版本不支持问题,原因是supported_version检查不合格。

bool QuicDispatcher::IsSupportedVersion(const ParsedQuicVersion version) {
  for (const ParsedQuicVersion& supported_version :
       version_manager_->GetSupportedVersions()) {
    if (version == supported_version) {
      return true;
    }
  }
  return false;
}

从打印看,支持的版本有:46,43,39版本的QuicCrypto握手,而我们发出的是48版本的TLS握手。
version_manager_的构造函数会过滤掉一些Flag没有enable的版本,其中48版本的Flag为quic_enable_version_48,在server启动前设置此Flag。
重新运行,结果没变还是版本不支持。从打印看,虽然48版本支持了,但是只支持QUICCRYPTO握手。因为如果使用TLS,需要enable FLAGS_quic_supports_tls_handshake,再设置一下server的flag,成功!
抓包看:版本号wireshark还是解析为未知。
另外,从打印的版本号看,48之前的版本,只支持QUIC CRYPTO,48支持TLS,99也支持TLS,且使用48版本是用的IETF格式。
为什么只有48版本以上才支持TLS呢,答案在quic::AllsupportedVersions()中

ParsedQuicVersionVector AllSupportedVersions() {
  ParsedQuicVersionVector supported_versions;
  for (HandshakeProtocol protocol : kSupportedHandshakeProtocols) {
    for (QuicTransportVersion version : kSupportedTransportVersions) {
      if (protocol == PROTOCOL_TLS1_3 &&
          !QuicVersionUsesCryptoFrames(version)) {
        // The TLS handshake is only deployable if CRYPTO frames are also used.
        continue;
      }
      supported_versions.push_back(ParsedQuicVersion(protocol, version));
    }
  }

这里支持的版本为kSupportedHandshakeProtocols与kSupportedTransportVersions的组合。
但是有一个条件,就是如果使用TLS1.3,那必须要满足QuicVersionUsesCryptoFrames(version)

// Returns whether |transport_version| uses CRYPTO frames for the handshake
// instead of stream 1.
QUIC_EXPORT_PRIVATE inline bool QuicVersionUsesCryptoFrames(
    QuicTransportVersion transport_version) {
  return transport_version >= QUIC_VERSION_48;
}

所以,只有48版本及以上才使用TLS1.3。
遗留问题:为什么TLS1.3必然使用的是IETF Quic格式,这个还没明白。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • 科普:QUIC 协议原理分析 作者介绍:lancelot,腾讯资深研发工程师。目前主要负责腾讯 stgw(腾讯安全...
    吸霾少年阅读 9,764评论 0 19
  • 目录 一、https概述 1. 什么是HTTP? 2. 什么是HTTPS? 3. SSL/TLS...
    出走的流星阅读 12,828评论 4 27
  • 前言 HTTPS是什么相信大家都知道,如果你不知道。。。请关闭此文!!!HTTP的数据是明文传输的,没有安全性可言...
    MxlZlh阅读 6,455评论 0 9
  • 饥饿一定给我们的老祖宗留下过深刻的印象,不然他们怎么会发明出那么多么带“吃”字的词句:吃力、吃惊、吃亏、吃醋、吃紧...
    舟不系阅读 359评论 0 0
  • 现在的每一天,我都在努力着,始终相信“年轻没有什么”的奋斗意义!加油,是最棒哒!
    分剧房间阅读 399评论 0 0