Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/net/quic/congestion_control/cube_root.cc b/net/quic/congestion_control/cube_root.cc
new file mode 100644
index 0000000..c563ad9
--- /dev/null
+++ b/net/quic/congestion_control/cube_root.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/cube_root.h"
+
+#include "base/logging.h"
+
+namespace {
+
+// Find last bit in a 64-bit word.
+int FindMostSignificantBit(uint64 x) {
+ if (!x) {
+ return 0;
+ }
+ int r = 0;
+ if (x & 0xffffffff00000000ull) {
+ x >>= 32;
+ r += 32;
+ }
+ if (x & 0xffff0000u) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x & 0xff00u) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x & 0xf0u) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x & 0xcu) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x & 0x02u) {
+ x >>= 1;
+ r++;
+ }
+ if (x & 0x01u) {
+ r++;
+ }
+ return r;
+}
+
+// 6 bits table [0..63]
+const uint32 cube_root_table[] = {
+ 0, 54, 54, 54, 118, 118, 118, 118, 123, 129, 134, 138, 143, 147, 151,
+ 156, 157, 161, 164, 168, 170, 173, 176, 179, 181, 185, 187, 190, 192, 194,
+ 197, 199, 200, 202, 204, 206, 209, 211, 213, 215, 217, 219, 221, 222, 224,
+ 225, 227, 229, 231, 232, 234, 236, 237, 239, 240, 242, 244, 245, 246, 248,
+ 250, 251, 252, 254
+};
+} // namespace
+
+namespace net {
+
+// Calculate the cube root using a table lookup followed by one Newton-Raphson
+// iteration.
+uint32 CubeRoot::Root(uint64 a) {
+ uint32 msb = FindMostSignificantBit(a);
+ DCHECK_LE(msb, 64u);
+
+ if (msb < 7) {
+ // MSB in our table.
+ return ((cube_root_table[static_cast<uint32>(a)]) + 31) >> 6;
+ }
+ // MSB 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ...
+ // cubic_shift 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, ...
+ uint32 cubic_shift = (msb - 4);
+ cubic_shift = ((cubic_shift * 342) >> 10); // Div by 3, biased high.
+
+ // 4 to 6 bits accuracy depending on MSB.
+ uint32 down_shifted_to_6bit = (a >> (cubic_shift * 3));
+ uint64 root = ((cube_root_table[down_shifted_to_6bit] + 10) << cubic_shift)
+ >> 6;
+
+ // Make one Newton-Raphson iteration.
+ // Since x has an error (inaccuracy due to the use of fix point) we get a
+ // more accurate result by doing x * (x - 1) instead of x * x.
+ root = 2 * root + (a / (root * (root - 1)));
+ root = ((root * 341) >> 10); // Div by 3, biased low.
+ return static_cast<uint32>(root);
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/cube_root.h b/net/quic/congestion_control/cube_root.h
new file mode 100644
index 0000000..293a719
--- /dev/null
+++ b/net/quic/congestion_control/cube_root.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_
+#define NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE CubeRoot {
+ public:
+ // Calculates the cube root using a table lookup followed by one Newton-
+ // Raphson iteration.
+ static uint32 Root(uint64 a);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CubeRoot);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_
diff --git a/net/quic/congestion_control/cube_root_test.cc b/net/quic/congestion_control/cube_root_test.cc
new file mode 100644
index 0000000..8f4729c
--- /dev/null
+++ b/net/quic/congestion_control/cube_root_test.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/cube_root.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class CubeRootTest : public ::testing::Test {
+ protected:
+ CubeRootTest() {
+ }
+};
+
+TEST_F(CubeRootTest, LowRoot) {
+ for (uint32 i = 1; i < 256; ++i) {
+ uint64 cube = i * i * i;
+ uint8 cube_root = CubeRoot::Root(cube);
+ EXPECT_EQ(i, cube_root);
+ }
+}
+
+TEST_F(CubeRootTest, HighRoot) {
+ // Test the range we will opperate in, 1300 to 130 000.
+ // We expect some loss in accuracy, accepting +-0.2%.
+ for (uint64 i = 1300; i < 20000; i += 100) {
+ uint64 cube = i * i * i;
+ uint32 cube_root = CubeRoot::Root(cube);
+ uint32 margin = cube_root >> 9; // Calculate 0.2% roughly by
+ // dividing by 512.
+ EXPECT_LE(i - margin, cube_root);
+ EXPECT_GE(i + margin, cube_root);
+ }
+ for (uint64 i = 20000; i < 130000; i *= 2) {
+ uint64 cube = i * i * i;
+ uint32 cube_root = CubeRoot::Root(cube);
+ uint32 margin = cube_root >> 9;
+ EXPECT_LE(i - margin, cube_root);
+ EXPECT_GE(i + margin, cube_root);
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/cubic.cc b/net/quic/congestion_control/cubic.cc
new file mode 100644
index 0000000..dec529d
--- /dev/null
+++ b/net/quic/congestion_control/cubic.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/cubic.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "net/quic/congestion_control/cube_root.h"
+#include "net/quic/quic_protocol.h"
+
+using std::max;
+
+namespace net {
+
+namespace {
+
+// Constants based on TCP defaults.
+// The following constants are in 2^10 fractions of a second instead of ms to
+// allow a 10 shift right to divide.
+const int kCubeScale = 40; // 1024*1024^3 (first 1024 is from 0.100^3)
+ // where 0.100 is 100 ms which is the scaling
+ // round trip time.
+const int kCubeCongestionWindowScale = 410;
+const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) /
+ kCubeCongestionWindowScale;
+
+const uint32 kDefaultNumConnections = 2;
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+// Additional backoff factor when loss occurs in the concave part of the Cubic
+// curve. This additional backoff factor is expected to give up bandwidth to
+// new concurrent flows and speed up convergence.
+const float kBetaLastMax = 0.85f;
+
+} // namespace
+
+Cubic::Cubic(const QuicClock* clock, QuicConnectionStats* stats)
+ : clock_(clock),
+ num_connections_(kDefaultNumConnections),
+ epoch_(QuicTime::Zero()),
+ last_update_time_(QuicTime::Zero()),
+ stats_(stats) {
+ Reset();
+}
+
+void Cubic::SetNumConnections(int num_connections) {
+ num_connections_ = num_connections;
+}
+
+float Cubic::Alpha() const {
+ // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that
+ // beta here is a cwnd multiplier, and is equal to 1-beta from the paper.
+ // We derive the equivalent alpha for an N-connection emulation as:
+ const float beta = Beta();
+ return 3 * num_connections_ * num_connections_ * (1 - beta) / (1 + beta);
+}
+
+float Cubic::Beta() const {
+ // kNConnectionBeta is the backoff factor after loss for our N-connection
+ // emulation, which emulates the effective backoff of an ensemble of N
+ // TCP-Reno connections on a single loss event. The effective multiplier is
+ // computed as:
+ return (num_connections_ - 1 + kBeta) / num_connections_;
+}
+
+void Cubic::Reset() {
+ epoch_ = QuicTime::Zero(); // Reset time.
+ last_update_time_ = QuicTime::Zero(); // Reset time.
+ last_congestion_window_ = 0;
+ last_max_congestion_window_ = 0;
+ acked_packets_count_ = 0;
+ estimated_tcp_congestion_window_ = 0;
+ origin_point_congestion_window_ = 0;
+ time_to_origin_point_ = 0;
+ last_target_congestion_window_ = 0;
+}
+
+void Cubic::UpdateCongestionControlStats(
+ QuicTcpCongestionWindow new_cubic_mode_cwnd,
+ QuicTcpCongestionWindow new_reno_mode_cwnd) {
+
+ QuicTcpCongestionWindow highest_new_cwnd = std::max(new_cubic_mode_cwnd,
+ new_reno_mode_cwnd);
+ if (last_congestion_window_ < highest_new_cwnd) {
+ // cwnd will increase to highest_new_cwnd.
+ stats_->cwnd_increase_congestion_avoidance +=
+ highest_new_cwnd - last_congestion_window_;
+ if (new_cubic_mode_cwnd > new_reno_mode_cwnd) {
+ // This cwnd increase is due to cubic mode.
+ stats_->cwnd_increase_cubic_mode +=
+ new_cubic_mode_cwnd - last_congestion_window_;
+ }
+ }
+}
+
+QuicTcpCongestionWindow Cubic::CongestionWindowAfterPacketLoss(
+ QuicTcpCongestionWindow current_congestion_window) {
+ if (current_congestion_window < last_max_congestion_window_) {
+ // We never reached the old max, so assume we are competing with another
+ // flow. Use our extra back off factor to allow the other flow to go up.
+ last_max_congestion_window_ =
+ static_cast<int>(kBetaLastMax * current_congestion_window);
+ } else {
+ last_max_congestion_window_ = current_congestion_window;
+ }
+ epoch_ = QuicTime::Zero(); // Reset time.
+ return static_cast<int>(current_congestion_window * Beta());
+}
+
+QuicTcpCongestionWindow Cubic::CongestionWindowAfterAck(
+ QuicTcpCongestionWindow current_congestion_window,
+ QuicTime::Delta delay_min) {
+ acked_packets_count_ += 1; // Packets acked.
+ QuicTime current_time = clock_->ApproximateNow();
+
+ // Cubic is "independent" of RTT, the update is limited by the time elapsed.
+ if (last_congestion_window_ == current_congestion_window &&
+ (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval())) {
+ return max(last_target_congestion_window_,
+ estimated_tcp_congestion_window_);
+ }
+ last_congestion_window_ = current_congestion_window;
+ last_update_time_ = current_time;
+
+ if (!epoch_.IsInitialized()) {
+ // First ACK after a loss event.
+ DVLOG(1) << "Start of epoch";
+ epoch_ = current_time; // Start of epoch.
+ acked_packets_count_ = 1; // Reset count.
+ // Reset estimated_tcp_congestion_window_ to be in sync with cubic.
+ estimated_tcp_congestion_window_ = current_congestion_window;
+ if (last_max_congestion_window_ <= current_congestion_window) {
+ time_to_origin_point_ = 0;
+ origin_point_congestion_window_ = current_congestion_window;
+ } else {
+ time_to_origin_point_ = CubeRoot::Root(kCubeFactor *
+ (last_max_congestion_window_ - current_congestion_window));
+ origin_point_congestion_window_ =
+ last_max_congestion_window_;
+ }
+ }
+ // Change the time unit from microseconds to 2^10 fractions per second. Take
+ // the round trip time in account. This is done to allow us to use shift as a
+ // divide operator.
+ int64 elapsed_time =
+ (current_time.Add(delay_min).Subtract(epoch_).ToMicroseconds() << 10) /
+ base::Time::kMicrosecondsPerSecond;
+
+ int64 offset = time_to_origin_point_ - elapsed_time;
+ QuicTcpCongestionWindow delta_congestion_window = (kCubeCongestionWindowScale
+ * offset * offset * offset) >> kCubeScale;
+
+ QuicTcpCongestionWindow target_congestion_window =
+ origin_point_congestion_window_ - delta_congestion_window;
+
+ DCHECK_LT(0u, estimated_tcp_congestion_window_);
+ // With dynamic beta/alpha based on number of active streams, it is possible
+ // for the required_ack_count to become much lower than acked_packets_count_
+ // suddenly, leading to more than one iteration through the following loop.
+ while (true) {
+ // Update estimated TCP congestion_window.
+ uint32 required_ack_count =
+ estimated_tcp_congestion_window_ / Alpha();
+ if (acked_packets_count_ < required_ack_count) {
+ break;
+ }
+ acked_packets_count_ -= required_ack_count;
+ estimated_tcp_congestion_window_++;
+ }
+
+ // Update cubic mode and reno mode stats in QuicConnectionStats.
+ UpdateCongestionControlStats(target_congestion_window,
+ estimated_tcp_congestion_window_);
+
+ // We have a new cubic congestion window.
+ last_target_congestion_window_ = target_congestion_window;
+
+ // Compute target congestion_window based on cubic target and estimated TCP
+ // congestion_window, use highest (fastest).
+ if (target_congestion_window < estimated_tcp_congestion_window_) {
+ target_congestion_window = estimated_tcp_congestion_window_;
+ }
+
+ DVLOG(1) << "Target congestion_window: " << target_congestion_window;
+ return target_congestion_window;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/cubic.h b/net/quic/congestion_control/cubic.h
new file mode 100644
index 0000000..d9f7bb6
--- /dev/null
+++ b/net/quic/congestion_control/cubic.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Cubic algorithm, helper class to TCP cubic.
+// For details see http://netsrv.csc.ncsu.edu/export/cubic_a_new_tcp_2008.pdf.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_CUBIC_H_
+#define NET_QUIC_CONGESTION_CONTROL_CUBIC_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+// TCP congestion window in QUIC is in packets, not bytes.
+typedef uint32 QuicTcpCongestionWindow;
+
+class NET_EXPORT_PRIVATE Cubic {
+ public:
+ Cubic(const QuicClock* clock, QuicConnectionStats* stats);
+
+ void SetNumConnections(int num_connections);
+
+ // Call after a timeout to reset the cubic state.
+ void Reset();
+
+ // Compute a new congestion window to use after a loss event.
+ // Returns the new congestion window in packets. The new congestion window is
+ // a multiplicative decrease of our current window.
+ QuicTcpCongestionWindow CongestionWindowAfterPacketLoss(
+ QuicTcpCongestionWindow current);
+
+ // Compute a new congestion window to use after a received ACK.
+ // Returns the new congestion window in packets. The new congestion window
+ // follows a cubic function that depends on the time passed since last
+ // packet loss.
+ QuicTcpCongestionWindow CongestionWindowAfterAck(
+ QuicTcpCongestionWindow current,
+ QuicTime::Delta delay_min);
+
+ private:
+ static const QuicTime::Delta MaxCubicTimeInterval() {
+ return QuicTime::Delta::FromMilliseconds(30);
+ }
+
+ // Compute the TCP Cubic alpha and beta based on the current number of
+ // connections.
+ float Alpha() const;
+ float Beta() const;
+
+ // Update congestion control variables in QuicConnectionStats.
+ void UpdateCongestionControlStats(QuicTcpCongestionWindow new_cubic_mode_cwnd,
+ QuicTcpCongestionWindow new_reno_mode_cwnd);
+ const QuicClock* clock_;
+
+ // Number of connections to simulate.
+ int num_connections_;
+
+ // Time when this cycle started, after last loss event.
+ QuicTime epoch_;
+
+ // Time when we updated last_congestion_window.
+ QuicTime last_update_time_;
+
+ // Last congestion window (in packets) used.
+ QuicTcpCongestionWindow last_congestion_window_;
+
+ // Max congestion window (in packets) used just before last loss event.
+ // Note: to improve fairness to other streams an additional back off is
+ // applied to this value if the new value is below our latest value.
+ QuicTcpCongestionWindow last_max_congestion_window_;
+
+ // Number of acked packets since the cycle started (epoch).
+ uint32 acked_packets_count_;
+
+ // TCP Reno equivalent congestion window in packets.
+ QuicTcpCongestionWindow estimated_tcp_congestion_window_;
+
+ // Origin point of cubic function.
+ QuicTcpCongestionWindow origin_point_congestion_window_;
+
+ // Time to origin point of cubic function in 2^10 fractions of a second.
+ uint32 time_to_origin_point_;
+
+ // Last congestion window in packets computed by cubic function.
+ QuicTcpCongestionWindow last_target_congestion_window_;
+
+ // QuicConnectionStats includes congestion control related stats.
+ QuicConnectionStats* stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(Cubic);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_CUBIC_H_
diff --git a/net/quic/congestion_control/cubic_test.cc b/net/quic/congestion_control/cubic_test.cc
new file mode 100644
index 0000000..915274a
--- /dev/null
+++ b/net/quic/congestion_control/cubic_test.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/quic/congestion_control/cubic.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+const float kBeta = 0.7f; // Default Cubic backoff factor.
+const uint32 kNumConnections = 2;
+const float kNConnectionBeta = (kNumConnections - 1 + kBeta) / kNumConnections;
+const float kNConnectionAlpha = 3 * kNumConnections * kNumConnections *
+ (1 - kNConnectionBeta) / (1 + kNConnectionBeta);
+
+class CubicTest : public ::testing::Test {
+ protected:
+ CubicTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ hundred_ms_(QuicTime::Delta::FromMilliseconds(100)),
+ cubic_(&clock_, &stats_) {
+ }
+ const QuicTime::Delta one_ms_;
+ const QuicTime::Delta hundred_ms_;
+ MockClock clock_;
+ QuicConnectionStats stats_;
+ Cubic cubic_;
+};
+
+TEST_F(CubicTest, AboveOrigin) {
+ // Convex growth.
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ uint32 current_cwnd = 10;
+ uint32 expected_cwnd = current_cwnd + 1;
+ // Initialize the state.
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ current_cwnd = expected_cwnd;
+ // Normal TCP phase.
+ for (int i = 0; i < 48; ++i) {
+ for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
+ // Call once per ACK.
+ EXPECT_NEAR(current_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min), 1);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ EXPECT_NEAR(expected_cwnd, current_cwnd, 1);
+ expected_cwnd++;
+ }
+ // Cubic phase.
+ for (int i = 0; i < 52; ++i) {
+ for (uint32 n = 1; n < current_cwnd; ++n) {
+ // Call once per ACK.
+ EXPECT_EQ(current_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
+ expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410)
+ / 1024;
+ EXPECT_EQ(expected_cwnd, current_cwnd);
+}
+
+TEST_F(CubicTest, CwndIncreaseStatsDuringConvexRegion) {
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ uint32 current_cwnd = 10;
+ uint32 expected_cwnd = current_cwnd + 1;
+ // Initialize controller state.
+ clock_.AdvanceTime(one_ms_);
+ expected_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ current_cwnd = expected_cwnd;
+ // Testing Reno mode increase.
+ for (int i = 0; i < 48; ++i) {
+ for (uint32 n = 1; n < current_cwnd / kNConnectionAlpha; ++n) {
+ // Call once per ACK, causing cwnd growth in Reno mode.
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Advance current time so that cwnd update is allowed to happen by Cubic.
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ EXPECT_NEAR(expected_cwnd - 10, stats_.cwnd_increase_congestion_avoidance,
+ 1);
+ EXPECT_NEAR(1u, stats_.cwnd_increase_cubic_mode, 1);
+ expected_cwnd++;
+ }
+ uint32 old_cwnd = current_cwnd;
+ stats_.cwnd_increase_cubic_mode = 0;
+ stats_.cwnd_increase_congestion_avoidance = 0;
+
+ // Testing Cubic mode increase.
+ for (int i = 0; i < 52; ++i) {
+ for (uint32 n = 1; n < current_cwnd; ++n) {
+ // Call once per ACK.
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ // Total time elapsed so far; add min_rtt (0.1s) here as well.
+ float elapsed_time_s = 10.0f + 0.1f;
+ // |expected_cwnd| is initial value of cwnd + K * t^3, where K = 0.4.
+ expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410)
+ / 1024;
+ EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode);
+ EXPECT_EQ(expected_cwnd - old_cwnd,
+ stats_.cwnd_increase_congestion_avoidance);
+}
+
+
+TEST_F(CubicTest, LossEvents) {
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ uint32 current_cwnd = 422;
+ uint32 expected_cwnd = current_cwnd + 1;
+ // Initialize the state.
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+}
+
+TEST_F(CubicTest, BelowOrigin) {
+ // Concave growth.
+ const QuicTime::Delta rtt_min = hundred_ms_;
+ uint32 current_cwnd = 422;
+ uint32 expected_cwnd = current_cwnd + 1;
+ // Initialize the state.
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min));
+ expected_cwnd = static_cast<int>(current_cwnd * kNConnectionBeta);
+ EXPECT_EQ(expected_cwnd,
+ cubic_.CongestionWindowAfterPacketLoss(current_cwnd));
+ current_cwnd = expected_cwnd;
+ // First update after loss to initialize the epoch.
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ uint32 old_cwnd = current_cwnd;
+ // Cubic phase.
+ stats_.cwnd_increase_cubic_mode = 0;
+ stats_.cwnd_increase_congestion_avoidance = 0;
+ for (int i = 0; i < 40 ; ++i) {
+ clock_.AdvanceTime(hundred_ms_);
+ current_cwnd = cubic_.CongestionWindowAfterAck(current_cwnd, rtt_min);
+ }
+ expected_cwnd = 422;
+ EXPECT_EQ(expected_cwnd, current_cwnd);
+ EXPECT_EQ(expected_cwnd - old_cwnd, stats_.cwnd_increase_cubic_mode);
+ EXPECT_EQ(expected_cwnd - old_cwnd,
+ stats_.cwnd_increase_congestion_avoidance);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/hybrid_slow_start.cc b/net/quic/congestion_control/hybrid_slow_start.cc
new file mode 100644
index 0000000..2ae3b50
--- /dev/null
+++ b/net/quic/congestion_control/hybrid_slow_start.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/hybrid_slow_start.h"
+
+#include <algorithm>
+
+using std::max;
+using std::min;
+
+namespace net {
+
+// Note(pwestin): the magic clamping numbers come from the original code in
+// tcp_cubic.c.
+const int64 kHybridStartLowWindow = 16;
+// Number of delay samples for detecting the increase of delay.
+const uint32 kHybridStartMinSamples = 8;
+const int kHybridStartDelayFactorExp = 4; // 2^4 = 16
+// The original paper specifies 2 and 8ms, but those have changed over time.
+const int kHybridStartDelayMinThresholdUs = 4000;
+const int kHybridStartDelayMaxThresholdUs = 16000;
+
+HybridSlowStart::HybridSlowStart(const QuicClock* clock)
+ : clock_(clock),
+ started_(false),
+ hystart_found_(NOT_FOUND),
+ last_sent_sequence_number_(0),
+ round_start_(QuicTime::Zero()),
+ end_sequence_number_(0),
+ last_close_ack_pair_time_(QuicTime::Zero()),
+ rtt_sample_count_(0),
+ current_min_rtt_(QuicTime::Delta::Zero()) {
+}
+
+void HybridSlowStart::OnPacketAcked(
+ QuicPacketSequenceNumber acked_sequence_number, bool in_slow_start) {
+ // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end
+ // the round when the final packet of the burst is received and start it on
+ // the next incoming ack.
+ if (in_slow_start && IsEndOfRound(acked_sequence_number)) {
+ started_ = false;
+ }
+}
+
+void HybridSlowStart::OnPacketSent(QuicPacketSequenceNumber sequence_number) {
+ last_sent_sequence_number_ = sequence_number;
+}
+
+void HybridSlowStart::Restart() {
+ started_ = false;
+ hystart_found_ = NOT_FOUND;
+}
+
+void HybridSlowStart::StartReceiveRound(QuicPacketSequenceNumber last_sent) {
+ DVLOG(1) << "Reset hybrid slow start @" << last_sent;
+ round_start_ = last_close_ack_pair_time_ = clock_->ApproximateNow();
+ end_sequence_number_ = last_sent;
+ current_min_rtt_ = QuicTime::Delta::Zero();
+ rtt_sample_count_ = 0;
+ started_ = true;
+}
+
+bool HybridSlowStart::IsEndOfRound(QuicPacketSequenceNumber ack) const {
+ return end_sequence_number_ <= ack;
+}
+
+bool HybridSlowStart::ShouldExitSlowStart(QuicTime::Delta latest_rtt,
+ QuicTime::Delta min_rtt,
+ int64 congestion_window) {
+ if (!started_) {
+ // Time to start the hybrid slow start.
+ StartReceiveRound(last_sent_sequence_number_);
+ }
+ if (hystart_found_ != NOT_FOUND) {
+ return true;
+ }
+ QuicTime current_time = clock_->ApproximateNow();
+
+ // First detection parameter - ack-train detection.
+ // Since slow start burst out packets we can indirectly estimate the inter-
+ // arrival time by looking at the arrival time of the ACKs if the ACKs are
+ // spread out more then half the minimum RTT packets are being spread out
+ // more than the capacity.
+ // This first trigger will not come into play until we hit roughly 9.6 Mbps
+ // with delayed acks (or 4.8Mbps without delayed acks)
+ // TODO(ianswett): QUIC always uses delayed acks, even at the beginning, so
+ // this should likely be at least 4ms.
+ // TODO(pwestin): we need to make sure our pacing don't trigger this detector.
+ // TODO(ianswett): Pacing or other cases could be handled by checking the send
+ // time of the first acked packet in a receive round.
+ if (current_time.Subtract(last_close_ack_pair_time_).ToMicroseconds() <=
+ kHybridStartDelayMinThresholdUs) {
+ last_close_ack_pair_time_ = current_time;
+ if (current_time.Subtract(round_start_).ToMicroseconds() >=
+ min_rtt.ToMicroseconds() >> 1) {
+ hystart_found_ = ACK_TRAIN;
+ }
+ } else if (last_close_ack_pair_time_ == round_start_) {
+ // If the previous ack wasn't close, then move forward the round start time
+ // to the incoming ack.
+ last_close_ack_pair_time_ = round_start_ = current_time;
+ }
+ // Second detection parameter - delay increase detection.
+ // Compare the minimum delay (current_min_rtt_) of the current
+ // burst of packets relative to the minimum delay during the session.
+ // Note: we only look at the first few(8) packets in each burst, since we
+ // only want to compare the lowest RTT of the burst relative to previous
+ // bursts.
+ rtt_sample_count_++;
+ if (rtt_sample_count_ <= kHybridStartMinSamples) {
+ if (current_min_rtt_.IsZero() || current_min_rtt_ > latest_rtt) {
+ current_min_rtt_ = latest_rtt;
+ }
+ }
+ // We only need to check this once per round.
+ if (rtt_sample_count_ == kHybridStartMinSamples) {
+ // Divide min_rtt by 16 to get a rtt increase threshold for exiting.
+ int min_rtt_increase_threshold_us = min_rtt.ToMicroseconds() >>
+ kHybridStartDelayFactorExp;
+ // Ensure the rtt threshold is never less than 2ms or more than 16ms.
+ min_rtt_increase_threshold_us = min(min_rtt_increase_threshold_us,
+ kHybridStartDelayMaxThresholdUs);
+ QuicTime::Delta min_rtt_increase_threshold =
+ QuicTime::Delta::FromMicroseconds(max(min_rtt_increase_threshold_us,
+ kHybridStartDelayMinThresholdUs));
+
+ if (current_min_rtt_ > min_rtt.Add(min_rtt_increase_threshold)) {
+ hystart_found_= DELAY;
+ }
+ }
+ // Exit from slow start if the cwnd is greater than 16 and an ack train or
+ // increasing delay are found.
+ return congestion_window >= kHybridStartLowWindow &&
+ hystart_found_ != NOT_FOUND;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/hybrid_slow_start.h b/net/quic/congestion_control/hybrid_slow_start.h
new file mode 100644
index 0000000..5d36c53
--- /dev/null
+++ b/net/quic/congestion_control/hybrid_slow_start.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This class is a helper class to TcpCubicSender.
+// Slow start is the initial startup phase of TCP, it lasts until first packet
+// loss. This class implements hybrid slow start of the TCP cubic send side
+// congestion algorithm. The key feaure of hybrid slow start is that it tries to
+// avoid running into the wall too hard during the slow start phase, which
+// the traditional TCP implementation does.
+// http://netsrv.csc.ncsu.edu/export/hybridstart_pfldnet08.pdf
+// http://research.csc.ncsu.edu/netsrv/sites/default/files/hystart_techreport_2008.pdf
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
+#define NET_QUIC_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE HybridSlowStart {
+ public:
+ explicit HybridSlowStart(const QuicClock* clock);
+
+ void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
+ bool in_slow_start);
+
+ void OnPacketSent(QuicPacketSequenceNumber sequence_number);
+
+ // ShouldExitSlowStart should be called on every new ack frame, since a new
+ // RTT measurement can be made then.
+ // rtt: the RTT for this ack packet.
+ // min_rtt: is the lowest delay (RTT) we have seen during the session.
+ // congestion_window: the congestion window in packets.
+ bool ShouldExitSlowStart(QuicTime::Delta rtt,
+ QuicTime::Delta min_rtt,
+ int64 congestion_window);
+
+ // Start a new slow start phase.
+ void Restart();
+
+ // TODO(ianswett): The following methods should be private, but that requires
+ // a follow up CL to update the unit test.
+ // Returns true if this ack the last sequence number of our current slow start
+ // round.
+ // Call Reset if this returns true.
+ bool IsEndOfRound(QuicPacketSequenceNumber ack) const;
+
+ // Call for the start of each receive round (burst) in the slow start phase.
+ void StartReceiveRound(QuicPacketSequenceNumber last_sent);
+
+ // Whether slow start has started.
+ bool started() const {
+ return started_;
+ }
+
+ private:
+ // Whether a condition for exiting slow start has been found.
+ enum HystartState {
+ NOT_FOUND,
+ ACK_TRAIN, // A closely spaced ack train is too long.
+ DELAY, // Too much increase in the round's min_rtt was observed.
+ };
+
+ const QuicClock* clock_;
+ // Whether the hybrid slow start has been started.
+ bool started_;
+ HystartState hystart_found_;
+ // Last sequence number sent which was CWND limited.
+ QuicPacketSequenceNumber last_sent_sequence_number_;
+
+ // Variables for tracking acks received during a slow start round.
+ QuicTime round_start_; // Beginning of each slow start receive round.
+ QuicPacketSequenceNumber end_sequence_number_; // End of the receive round.
+ // Last time when the spacing between ack arrivals was less than 2 ms.
+ // Defaults to the beginning of the round.
+ QuicTime last_close_ack_pair_time_;
+ uint32 rtt_sample_count_; // Number of rtt samples in the current round.
+ QuicTime::Delta current_min_rtt_; // The minimum rtt of current round.
+
+ DISALLOW_COPY_AND_ASSIGN(HybridSlowStart);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_HYBRID_SLOW_START_H_
diff --git a/net/quic/congestion_control/hybrid_slow_start_test.cc b/net/quic/congestion_control/hybrid_slow_start_test.cc
new file mode 100644
index 0000000..bea5840
--- /dev/null
+++ b/net/quic/congestion_control/hybrid_slow_start_test.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/hybrid_slow_start.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class HybridSlowStartTest : public ::testing::Test {
+ protected:
+ HybridSlowStartTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ rtt_(QuicTime::Delta::FromMilliseconds(60)) {
+ }
+ virtual void SetUp() {
+ slow_start_.reset(new HybridSlowStart(&clock_));
+ }
+ const QuicTime::Delta one_ms_;
+ const QuicTime::Delta rtt_;
+ MockClock clock_;
+ scoped_ptr<HybridSlowStart> slow_start_;
+};
+
+TEST_F(HybridSlowStartTest, Simple) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicPacketSequenceNumber end_sequence_number = 3;
+ slow_start_->StartReceiveRound(end_sequence_number);
+
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
+
+ // Test duplicates.
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number));
+
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
+
+ // Test without a new registered end_sequence_number;
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
+
+ end_sequence_number = 20;
+ slow_start_->StartReceiveRound(end_sequence_number);
+ while (sequence_number < end_sequence_number) {
+ EXPECT_FALSE(slow_start_->IsEndOfRound(sequence_number++));
+ }
+ EXPECT_TRUE(slow_start_->IsEndOfRound(sequence_number++));
+}
+
+// TODO(ianswett): Add tests which more realistically invoke the methods,
+// simulating how actual acks arrive and packets are sent.
+TEST_F(HybridSlowStartTest, AckTrain) {
+ // At a typical RTT 60 ms, assuming that the inter arrival timestamp is 1 ms,
+ // we expect to be able to send a burst of 30 packet before we trigger the
+ // ack train detection.
+ const int kMaxLoopCount = 5;
+ QuicPacketSequenceNumber sequence_number = 2;
+ QuicPacketSequenceNumber end_sequence_number = 2;
+ for (int burst = 0; burst < kMaxLoopCount; ++burst) {
+ slow_start_->StartReceiveRound(end_sequence_number);
+ do {
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
+ } while (!slow_start_->IsEndOfRound(sequence_number++));
+ end_sequence_number *= 2; // Exponential growth.
+ }
+ slow_start_->StartReceiveRound(end_sequence_number);
+
+ for (int n = 0;
+ n < 29 && !slow_start_->IsEndOfRound(sequence_number++); ++n) {
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
+ }
+ clock_.AdvanceTime(one_ms_);
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(rtt_, rtt_, 100));
+}
+
+TEST_F(HybridSlowStartTest, Delay) {
+ // We expect to detect the increase at +1/16 of the RTT; hence at a typical
+ // RTT of 60ms the detection will happen at 63.75 ms.
+ const int kHybridStartMinSamples = 8; // Number of acks required to trigger.
+
+ QuicPacketSequenceNumber end_sequence_number = 1;
+ slow_start_->StartReceiveRound(end_sequence_number++);
+
+ // Will not trigger since our lowest RTT in our burst is the same as the long
+ // term RTT provided.
+ for (int n = 0; n < kHybridStartMinSamples; ++n) {
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_, 100));
+ }
+ slow_start_->StartReceiveRound(end_sequence_number++);
+ for (int n = 1; n < kHybridStartMinSamples; ++n) {
+ EXPECT_FALSE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 5)), rtt_, 100));
+ }
+ // Expect to trigger since all packets in this burst was above the long term
+ // RTT provided.
+ EXPECT_TRUE(slow_start_->ShouldExitSlowStart(
+ rtt_.Add(QuicTime::Delta::FromMilliseconds(5)), rtt_, 100));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/leaky_bucket.cc b/net/quic/congestion_control/leaky_bucket.cc
new file mode 100644
index 0000000..f3972f6
--- /dev/null
+++ b/net/quic/congestion_control/leaky_bucket.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/leaky_bucket.h"
+
+#include "base/time/time.h"
+
+namespace net {
+
+LeakyBucket::LeakyBucket(QuicBandwidth draining_rate)
+ : bytes_(0),
+ time_last_updated_(QuicTime::Zero()),
+ draining_rate_(draining_rate) {
+}
+
+void LeakyBucket::SetDrainingRate(QuicTime now, QuicBandwidth draining_rate) {
+ Update(now);
+ draining_rate_ = draining_rate;
+}
+
+void LeakyBucket::Add(QuicTime now, QuicByteCount bytes) {
+ Update(now);
+ bytes_ += bytes;
+}
+
+QuicTime::Delta LeakyBucket::TimeRemaining(QuicTime now) const {
+ QuicTime::Delta time_since_last_update = now.Subtract(time_last_updated_);
+ QuicTime::Delta send_delay = QuicTime::Delta::FromMicroseconds(
+ (bytes_ * base::Time::kMicrosecondsPerSecond) /
+ draining_rate_.ToBytesPerSecond());
+ if (send_delay < time_since_last_update) {
+ return QuicTime::Delta::Zero();
+ }
+ return send_delay.Subtract(time_since_last_update);
+}
+
+QuicByteCount LeakyBucket::BytesPending(QuicTime now) {
+ Update(now);
+ return bytes_;
+}
+
+void LeakyBucket::Update(QuicTime now) {
+ QuicTime::Delta elapsed_time = now.Subtract(time_last_updated_);
+ QuicByteCount bytes_cleared = draining_rate_.ToBytesPerPeriod(elapsed_time);
+ if (bytes_cleared >= bytes_) {
+ bytes_ = 0;
+ } else {
+ bytes_ -= bytes_cleared;
+ }
+ time_last_updated_ = now;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/leaky_bucket.h b/net/quic/congestion_control/leaky_bucket.h
new file mode 100644
index 0000000..eb4cdb0
--- /dev/null
+++ b/net/quic/congestion_control/leaky_bucket.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Helper class to track the rate data can leave the buffer for pacing.
+// A leaky bucket drains the data at a constant rate regardless of fullness of
+// the buffer.
+// See http://en.wikipedia.org/wiki/Leaky_bucket for more details.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_LEAKY_BUCKET_H_
+#define NET_QUIC_CONGESTION_CONTROL_LEAKY_BUCKET_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE LeakyBucket {
+ public:
+ explicit LeakyBucket(QuicBandwidth draining_rate);
+
+ // Set the rate at which the bytes leave the buffer.
+ void SetDrainingRate(QuicTime now, QuicBandwidth draining_rate);
+
+ // Add data to the buffer.
+ void Add(QuicTime now, QuicByteCount bytes);
+
+ // Time until the buffer is empty.
+ QuicTime::Delta TimeRemaining(QuicTime now) const;
+
+ // Number of bytes in the buffer.
+ QuicByteCount BytesPending(QuicTime now);
+
+ private:
+ void Update(QuicTime now);
+
+ QuicByteCount bytes_;
+ QuicTime time_last_updated_;
+ QuicBandwidth draining_rate_;
+
+ DISALLOW_COPY_AND_ASSIGN(LeakyBucket);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_LEAKY_BUCKET_H_
diff --git a/net/quic/congestion_control/leaky_bucket_test.cc b/net/quic/congestion_control/leaky_bucket_test.cc
new file mode 100644
index 0000000..977ef94
--- /dev/null
+++ b/net/quic/congestion_control/leaky_bucket_test.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/leaky_bucket.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class LeakyBucketTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ leaky_bucket_.reset(new LeakyBucket(QuicBandwidth::Zero()));
+ }
+ MockClock clock_;
+ scoped_ptr<LeakyBucket> leaky_bucket_;
+};
+
+TEST_F(LeakyBucketTest, Basic) {
+ QuicBandwidth draining_rate = QuicBandwidth::FromBytesPerSecond(200000);
+ leaky_bucket_->SetDrainingRate(clock_.Now(), draining_rate);
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ EXPECT_EQ(2000u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ leaky_bucket_->TimeRemaining(clock_.Now()));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(1000u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(5),
+ leaky_bucket_->TimeRemaining(clock_.Now()));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(11));
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(2000u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ leaky_bucket_->TimeRemaining(clock_.Now()));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10));
+ EXPECT_EQ(0u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_TRUE(leaky_bucket_->TimeRemaining(clock_.Now()).IsZero());
+}
+
+TEST_F(LeakyBucketTest, ChangeDrainRate) {
+ QuicBandwidth draining_rate = QuicBandwidth::FromBytesPerSecond(200000);
+ leaky_bucket_->SetDrainingRate(clock_.Now(), draining_rate);
+ leaky_bucket_->Add(clock_.Now(), 2000);
+ EXPECT_EQ(2000u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ leaky_bucket_->TimeRemaining(clock_.Now()));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(1000u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(5),
+ leaky_bucket_->TimeRemaining(clock_.Now()));
+ draining_rate = draining_rate.Scale(0.5f); // Cut drain rate in half.
+ leaky_bucket_->SetDrainingRate(clock_.Now(), draining_rate);
+ EXPECT_EQ(1000u, leaky_bucket_->BytesPending(clock_.Now()));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ leaky_bucket_->TimeRemaining(clock_.Now()));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/loss_detection_interface.cc b/net/quic/congestion_control/loss_detection_interface.cc
new file mode 100644
index 0000000..df2983f
--- /dev/null
+++ b/net/quic/congestion_control/loss_detection_interface.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/loss_detection_interface.h"
+
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+
+namespace net {
+
+// Factory for loss detection algorithm.
+LossDetectionInterface* LossDetectionInterface::Create(
+ LossDetectionType loss_type) {
+ switch (loss_type) {
+ case kNack:
+ return new TCPLossAlgorithm();
+ case kTime:
+ return new TimeLossAlgorithm();
+ }
+ LOG(DFATAL) << "Unknown loss detection algorithm:" << loss_type;
+ return nullptr;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/loss_detection_interface.h b/net/quic/congestion_control/loss_detection_interface.h
new file mode 100644
index 0000000..5aaa51d
--- /dev/null
+++ b/net/quic/congestion_control/loss_detection_interface.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The pure virtual class for send side loss detection algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class QuicUnackedPacketMap;
+class RttStats;
+
+class NET_EXPORT_PRIVATE LossDetectionInterface {
+ public:
+ // Creates a TCP loss detector.
+ static LossDetectionInterface* Create(LossDetectionType loss_type);
+
+ virtual ~LossDetectionInterface() {}
+
+ virtual LossDetectionType GetLossDetectionType() const = 0;
+
+ // Called when a new ack arrives or the loss alarm fires.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) = 0;
+
+ // Get the time the LossDetectionAlgorithm wants to re-evaluate losses.
+ // Returns QuicTime::Zero if no alarm needs to be set.
+ virtual QuicTime GetLossTimeout() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_LOSS_DETECTION_INTERFACE_H_
diff --git a/net/quic/congestion_control/pacing_sender.cc b/net/quic/congestion_control/pacing_sender.cc
new file mode 100644
index 0000000..20d2b3b
--- /dev/null
+++ b/net/quic/congestion_control/pacing_sender.cc
@@ -0,0 +1,189 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/pacing_sender.h"
+
+namespace net {
+
+PacingSender::PacingSender(SendAlgorithmInterface* sender,
+ QuicTime::Delta alarm_granularity,
+ uint32 initial_packet_burst)
+ : sender_(sender),
+ alarm_granularity_(alarm_granularity),
+ initial_packet_burst_(initial_packet_burst),
+ burst_tokens_(initial_packet_burst),
+ last_delayed_packet_sent_time_(QuicTime::Zero()),
+ next_packet_send_time_(QuicTime::Zero()),
+ was_last_send_delayed_(false),
+ has_valid_rtt_(false) {
+}
+
+PacingSender::~PacingSender() {}
+
+void PacingSender::SetFromConfig(const QuicConfig& config, bool is_server) {
+ // TODO(ianswett): Consider using the suggested RTT for pacing an initial
+ // response.
+ sender_->SetFromConfig(config, is_server);
+}
+
+void PacingSender::SetNumEmulatedConnections(int num_connections) {
+ sender_->SetNumEmulatedConnections(num_connections);
+}
+
+void PacingSender::OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time) {
+ sender_->OnIncomingQuicCongestionFeedbackFrame(
+ feedback, feedback_receive_time);
+}
+
+void PacingSender::OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionVector& acked_packets,
+ const CongestionVector& lost_packets) {
+ if (rtt_updated) {
+ has_valid_rtt_ = true;
+ }
+ sender_->OnCongestionEvent(
+ rtt_updated, bytes_in_flight, acked_packets, lost_packets);
+}
+
+bool PacingSender::OnPacketSent(
+ QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ HasRetransmittableData has_retransmittable_data) {
+ // Only pace data packets once we have an updated RTT.
+ const bool in_flight =
+ sender_->OnPacketSent(sent_time, bytes_in_flight, sequence_number,
+ bytes, has_retransmittable_data);
+ if (has_retransmittable_data != HAS_RETRANSMITTABLE_DATA || !has_valid_rtt_) {
+ return in_flight;
+ }
+ if (burst_tokens_ > 0) {
+ --burst_tokens_;
+ was_last_send_delayed_ = false;
+ last_delayed_packet_sent_time_ = QuicTime::Zero();
+ next_packet_send_time_ = QuicTime::Zero();
+ return in_flight;
+ }
+ // The next packet should be sent as soon as the current packets has
+ // been transferred. We pace at twice the rate of the underlying
+ // sender's bandwidth estimate during slow start and 1.25x during congestion
+ // avoidance to ensure pacing doesn't prevent us from filling the window.
+ const float kPacingAggression = sender_->InSlowStart() ? 2 : 1.25;
+ QuicTime::Delta delay =
+ BandwidthEstimate().Scale(kPacingAggression).TransferTime(bytes);
+ // If the last send was delayed, and the alarm took a long time to get
+ // invoked, allow the connection to make up for lost time.
+ if (was_last_send_delayed_) {
+ next_packet_send_time_ = next_packet_send_time_.Add(delay);
+ // The send was application limited if it takes longer than the
+ // pacing delay between sent packets.
+ const bool application_limited =
+ last_delayed_packet_sent_time_.IsInitialized() &&
+ sent_time > last_delayed_packet_sent_time_.Add(delay);
+ const bool making_up_for_lost_time = next_packet_send_time_ <= sent_time;
+ // As long as we're making up time and not application limited,
+ // continue to consider the packets delayed, allowing the packets to be
+ // sent immediately.
+ if (making_up_for_lost_time && !application_limited) {
+ last_delayed_packet_sent_time_ = sent_time;
+ } else {
+ was_last_send_delayed_ = false;
+ last_delayed_packet_sent_time_ = QuicTime::Zero();
+ }
+ } else {
+ next_packet_send_time_ =
+ QuicTime::Max(next_packet_send_time_.Add(delay),
+ sent_time.Add(delay).Subtract(alarm_granularity_));
+ }
+ return in_flight;
+}
+
+void PacingSender::OnRetransmissionTimeout(bool packets_retransmitted) {
+ sender_->OnRetransmissionTimeout(packets_retransmitted);
+}
+
+void PacingSender::RevertRetransmissionTimeout() {
+ sender_->RevertRetransmissionTimeout();
+}
+
+QuicTime::Delta PacingSender::TimeUntilSend(
+ QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const {
+ QuicTime::Delta time_until_send =
+ sender_->TimeUntilSend(now, bytes_in_flight, has_retransmittable_data);
+ if (!has_valid_rtt_) {
+ // Don't pace if we don't have an updated RTT estimate.
+ return time_until_send;
+ }
+ if (bytes_in_flight == 0) {
+ // Add more burst tokens anytime the connection is entering quiescence.
+ burst_tokens_ = initial_packet_burst_;
+ }
+ if (burst_tokens_ > 0) {
+ // Don't pace if we have burst tokens available.
+ return time_until_send;
+ }
+
+ if (!time_until_send.IsZero()) {
+ DCHECK(time_until_send.IsInfinite());
+ // The underlying sender prevents sending.
+ return time_until_send;
+ }
+
+ if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) {
+ // Don't pace ACK packets, since they do not count against CWND and do not
+ // cause CWND to grow.
+ return QuicTime::Delta::Zero();
+ }
+
+ // If the next send time is within the alarm granularity, send immediately.
+ if (next_packet_send_time_ > now.Add(alarm_granularity_)) {
+ DVLOG(1) << "Delaying packet: "
+ << next_packet_send_time_.Subtract(now).ToMicroseconds();
+ was_last_send_delayed_ = true;
+ return next_packet_send_time_.Subtract(now);
+ }
+
+ DVLOG(1) << "Sending packet now";
+ return QuicTime::Delta::Zero();
+}
+
+QuicBandwidth PacingSender::BandwidthEstimate() const {
+ return sender_->BandwidthEstimate();
+}
+
+bool PacingSender::HasReliableBandwidthEstimate() const {
+ return sender_->HasReliableBandwidthEstimate();
+}
+
+QuicTime::Delta PacingSender::RetransmissionDelay() const {
+ return sender_->RetransmissionDelay();
+}
+
+QuicByteCount PacingSender::GetCongestionWindow() const {
+ return sender_->GetCongestionWindow();
+}
+
+bool PacingSender::InSlowStart() const {
+ return sender_->InSlowStart();
+}
+
+bool PacingSender::InRecovery() const {
+ return sender_->InRecovery();
+}
+
+QuicByteCount PacingSender::GetSlowStartThreshold() const {
+ return sender_->GetSlowStartThreshold();
+}
+
+CongestionControlType PacingSender::GetCongestionControlType() const {
+ return sender_->GetCongestionControlType();
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/pacing_sender.h b/net/quic/congestion_control/pacing_sender.h
new file mode 100644
index 0000000..f61ae75
--- /dev/null
+++ b/net/quic/congestion_control/pacing_sender.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A send algorithm which adds pacing on top of an another send algorithm.
+// It uses the underlying sender's bandwidth estimate to determine the
+// pacing rate to be used. It also takes into consideration the expected
+// resolution of the underlying alarm mechanism to ensure that alarms are
+// not set too aggressively, and to smooth out variations.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_PACING_SENDER_H_
+#define NET_QUIC_CONGESTION_CONTROL_PACING_SENDER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE PacingSender : public SendAlgorithmInterface {
+ public:
+ // Create a PacingSender to wrap the specified sender. |alarm_granularity|
+ // indicates to the pacer to send that far into the future, since it should
+ // not expect a callback before that time delta. |initial_packet_burst| is
+ // the number of packets sent without pacing after quiescence.
+ PacingSender(SendAlgorithmInterface* sender,
+ QuicTime::Delta alarm_granularity,
+ uint32 initial_packet_burst);
+ virtual ~PacingSender();
+
+ // SendAlgorithmInterface methods.
+ virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
+ virtual void SetNumEmulatedConnections(int num_connections) OVERRIDE;
+ virtual void OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionVector& acked_packets,
+ const CongestionVector& lost_packets) OVERRIDE;
+ virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
+ virtual void RevertRetransmissionTimeout() OVERRIDE;
+ virtual QuicTime::Delta TimeUntilSend(
+ QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
+ virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
+ virtual bool HasReliableBandwidthEstimate() const OVERRIDE;
+ virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
+ virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
+ virtual bool InSlowStart() const OVERRIDE;
+ virtual bool InRecovery() const OVERRIDE;
+ virtual QuicByteCount GetSlowStartThreshold() const OVERRIDE;
+ virtual CongestionControlType GetCongestionControlType() const OVERRIDE;
+
+ private:
+ scoped_ptr<SendAlgorithmInterface> sender_; // Underlying sender.
+ QuicTime::Delta alarm_granularity_;
+ uint32 initial_packet_burst_;
+ mutable uint32 burst_tokens_;
+ // Send time of the last packet considered delayed.
+ QuicTime last_delayed_packet_sent_time_;
+ QuicTime next_packet_send_time_; // When can the next packet be sent.
+ mutable bool was_last_send_delayed_; // True when the last send was delayed.
+ bool has_valid_rtt_; // True if we have at least one RTT update.
+
+ DISALLOW_COPY_AND_ASSIGN(PacingSender);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_PACING_SENDER_H_
diff --git a/net/quic/congestion_control/pacing_sender_test.cc b/net/quic/congestion_control/pacing_sender_test.cc
new file mode 100644
index 0000000..768cc22
--- /dev/null
+++ b/net/quic/congestion_control/pacing_sender_test.cc
@@ -0,0 +1,336 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/pacing_sender.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace net {
+namespace test {
+
+const QuicByteCount kBytesInFlight = 1024;
+
+class PacingSenderTest : public ::testing::Test {
+ protected:
+ PacingSenderTest()
+ : zero_time_(QuicTime::Delta::Zero()),
+ infinite_time_(QuicTime::Delta::Infinite()),
+ sequence_number_(1),
+ mock_sender_(new StrictMock<MockSendAlgorithm>()),
+ pacing_sender_(new PacingSender(mock_sender_,
+ QuicTime::Delta::FromMilliseconds(1),
+ 0)) {
+ // Pick arbitrary time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(9));
+ }
+
+ virtual ~PacingSenderTest() {}
+
+ void CheckPacketIsSentImmediately() {
+ // In order for the packet to be sendable, the underlying sender must
+ // permit it to be sent immediately.
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the packet can be sent immediately.
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
+
+ // Actually send the packet.
+ EXPECT_CALL(*mock_sender_,
+ OnPacketSent(clock_.Now(), kBytesInFlight, sequence_number_,
+ kMaxPacketSize, HAS_RETRANSMITTABLE_DATA));
+ pacing_sender_->OnPacketSent(clock_.Now(), kBytesInFlight,
+ sequence_number_++, kMaxPacketSize,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void CheckAckIsSentImmediately() {
+ // In order for the ack to be sendable, the underlying sender must
+ // permit it to be sent immediately.
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ 0,
+ NO_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the ACK can be sent immediately.
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ NO_RETRANSMITTABLE_DATA));
+
+ // Actually send the packet.
+ EXPECT_CALL(*mock_sender_,
+ OnPacketSent(clock_.Now(), 0, sequence_number_,
+ kMaxPacketSize, NO_RETRANSMITTABLE_DATA));
+ pacing_sender_->OnPacketSent(clock_.Now(), 0,
+ sequence_number_++, kMaxPacketSize,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
+ void CheckPacketIsDelayed(QuicTime::Delta delay) {
+ // In order for the packet to be sendable, the underlying sender must
+ // permit it to be sent immediately.
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ // Verify that the packet is delayed.
+ EXPECT_EQ(delay.ToMicroseconds(),
+ pacing_sender_->TimeUntilSend(
+ clock_.Now(), kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA).ToMicroseconds());
+ }
+ }
+
+ const QuicTime::Delta zero_time_;
+ const QuicTime::Delta infinite_time_;
+ MockClock clock_;
+ QuicPacketSequenceNumber sequence_number_;
+ StrictMock<MockSendAlgorithm>* mock_sender_;
+ scoped_ptr<PacingSender> pacing_sender_;
+};
+
+TEST_F(PacingSenderTest, NoSend) {
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(infinite_time_));
+ EXPECT_EQ(infinite_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
+}
+
+TEST_F(PacingSenderTest, SendNow) {
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*mock_sender_, TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(zero_time_));
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ kBytesInFlight,
+ HAS_RETRANSMITTABLE_DATA));
+ }
+}
+
+TEST_F(PacingSenderTest, VariousSending) {
+ // Start the test in slow start.
+ EXPECT_CALL(*mock_sender_, InSlowStart()).WillRepeatedly(Return(true));
+
+ // Configure bandwith of 1 packet per 2 ms, for which the pacing rate
+ // will be 1 packet per 1 ms.
+ EXPECT_CALL(*mock_sender_, BandwidthEstimate())
+ .WillRepeatedly(Return(QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxPacketSize, QuicTime::Delta::FromMilliseconds(2))));
+
+ // Send a whole pile of packets, and verify that they are not paced.
+ for (int i = 0 ; i < 1000; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // Now update the RTT and verify that packets are actually paced.
+ EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _));
+ SendAlgorithmInterface::CongestionVector empty_map;
+ pacing_sender_->OnCongestionEvent(true, kBytesInFlight, empty_map, empty_map);
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up on time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+ CheckAckIsSentImmediately();
+
+ // Wake up late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(4));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up really late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up really late again, but application pause partway through.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up too early.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+
+ // Wake up early, but after enough time has passed to permit a send.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+TEST_F(PacingSenderTest, CongestionAvoidanceSending) {
+ // Start the test in congestion avoidance.
+ EXPECT_CALL(*mock_sender_, InSlowStart()).WillRepeatedly(Return(false));
+
+ // Configure bandwith of 1 packet per 2 ms, for which the pacing rate
+ // will be 1 packet per 1 ms.
+ EXPECT_CALL(*mock_sender_, BandwidthEstimate())
+ .WillRepeatedly(Return(QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxPacketSize, QuicTime::Delta::FromMilliseconds(2))));
+
+ // Send a whole pile of packets, and verify that they are not paced.
+ for (int i = 0 ; i < 1000; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ // Now update the RTT and verify that packets are actually paced.
+ EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _));
+ SendAlgorithmInterface::CongestionVector empty_map;
+ pacing_sender_->OnCongestionEvent(true, kBytesInFlight, empty_map, empty_map);
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2200us.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(2200));
+
+ // Wake up on time.
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(2200));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(1600));
+ CheckAckIsSentImmediately();
+
+ // Wake up late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(4));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(2400));
+
+ // Wake up really late.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(2400));
+
+ // Wake up really late again, but application pause partway through.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(2200));
+
+ // Wake up too early.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(2200));
+
+ // Wake up early, but after enough time has passed to permit a send.
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1200));
+ CheckPacketIsSentImmediately();
+ CheckPacketIsDelayed(QuicTime::Delta::FromMicroseconds(2600));
+}
+
+TEST_F(PacingSenderTest, InitialBurst) {
+ pacing_sender_.reset();
+ mock_sender_ = new StrictMock<MockSendAlgorithm>();
+ pacing_sender_.reset(new PacingSender(mock_sender_,
+ QuicTime::Delta::FromMilliseconds(1),
+ 10));
+ // Start the test in slow start.
+ EXPECT_CALL(*mock_sender_, InSlowStart()).WillRepeatedly(Return(true));
+
+ // Configure bandwith of 1 packet per 2 ms, for which the pacing rate
+ // will be 1 packet per 1 ms.
+ EXPECT_CALL(*mock_sender_, BandwidthEstimate())
+ .WillRepeatedly(Return(QuicBandwidth::FromBytesAndTimeDelta(
+ kMaxPacketSize, QuicTime::Delta::FromMilliseconds(2))));
+
+ // Update the RTT and verify that the first 10 packets aren't paced.
+ EXPECT_CALL(*mock_sender_, OnCongestionEvent(true, kBytesInFlight, _, _));
+ SendAlgorithmInterface::CongestionVector empty_map;
+ pacing_sender_->OnCongestionEvent(true, kBytesInFlight, empty_map, empty_map);
+
+ // Send 10 packets, and verify that they are not paced.
+ for (int i = 0 ; i < 10; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+
+ // The first packet was a "make up", then we sent two packets "into the
+ // future", so the delay should be 2.
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ CheckPacketIsSentImmediately();
+
+ // Next time TimeUntilSend is called with no bytes in flight, the tokens
+ // should be refilled and there should be no delay.
+ EXPECT_CALL(*mock_sender_,
+ TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA)).
+ WillOnce(Return(zero_time_));
+ EXPECT_EQ(zero_time_,
+ pacing_sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA));
+ for (int i = 0 ; i < 10; ++i) {
+ CheckPacketIsSentImmediately();
+ }
+
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+ CheckPacketIsSentImmediately();
+
+ CheckPacketIsDelayed(QuicTime::Delta::FromMilliseconds(2));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/receive_algorithm_interface.cc b/net/quic/congestion_control/receive_algorithm_interface.cc
new file mode 100644
index 0000000..72164ef
--- /dev/null
+++ b/net/quic/congestion_control/receive_algorithm_interface.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+
+#include "net/quic/congestion_control/tcp_receiver.h"
+
+namespace net {
+
+// Factory for receive side congestion control algorithm.
+ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create(
+ CongestionFeedbackType type) {
+ switch (type) {
+ case kTCP:
+ return new TcpReceiver();
+ }
+ return nullptr;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/receive_algorithm_interface.h b/net/quic/congestion_control/receive_algorithm_interface.h
new file mode 100644
index 0000000..cd96b02
--- /dev/null
+++ b/net/quic/congestion_control/receive_algorithm_interface.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The pure virtual class for receive side congestion algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE ReceiveAlgorithmInterface {
+ public:
+ static ReceiveAlgorithmInterface* Create(CongestionFeedbackType type);
+
+ virtual ~ReceiveAlgorithmInterface() {}
+
+ // Returns false if no QuicCongestionFeedbackFrame block is needed.
+ // Otherwise fills in feedback and return true.
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback) = 0;
+
+ // Should be called for each incoming packet.
+ // bytes: is the packet size in bytes including IP headers.
+ // sequence_number: is the unique sequence number from the QUIC packet header.
+ // timestamp: is the sent timestamp from the QUIC packet header.
+ virtual void RecordIncomingPacket(QuicByteCount bytes,
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime timestamp) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_
diff --git a/net/quic/congestion_control/rtt_stats.cc b/net/quic/congestion_control/rtt_stats.cc
new file mode 100644
index 0000000..b03686d
--- /dev/null
+++ b/net/quic/congestion_control/rtt_stats.cc
@@ -0,0 +1,141 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/rtt_stats.h"
+
+#include <complex> // std::abs
+
+using std::max;
+
+namespace net {
+
+namespace {
+
+// Default initial rtt used before any samples are received.
+const int kInitialRttMs = 100;
+const float kAlpha = 0.125f;
+const float kOneMinusAlpha = (1 - kAlpha);
+const float kBeta = 0.25f;
+const float kOneMinusBeta = (1 - kBeta);
+const float kHalfWindow = 0.5f;
+const float kQuarterWindow = 0.25f;
+
+} // namespace
+
+RttStats::RttStats()
+ : latest_rtt_(QuicTime::Delta::Zero()),
+ min_rtt_(QuicTime::Delta::Zero()),
+ smoothed_rtt_(QuicTime::Delta::Zero()),
+ mean_deviation_(QuicTime::Delta::Zero()),
+ initial_rtt_us_(kInitialRttMs * base::Time::kMicrosecondsPerMillisecond),
+ num_min_rtt_samples_remaining_(0),
+ recent_min_rtt_window_(QuicTime::Delta::Infinite()) {}
+
+bool RttStats::HasUpdates() const {
+ return !smoothed_rtt_.IsZero();
+}
+
+void RttStats::SampleNewRecentMinRtt(uint32 num_samples) {
+ num_min_rtt_samples_remaining_ = num_samples;
+ new_min_rtt_ = RttSample();
+}
+
+void RttStats::ExpireSmoothedMetrics() {
+ mean_deviation_ =
+ max(mean_deviation_,
+ QuicTime::Delta::FromMicroseconds(
+ std::abs(smoothed_rtt_.Subtract(latest_rtt_).ToMicroseconds())));
+ smoothed_rtt_ = max(smoothed_rtt_, latest_rtt_);
+}
+
+// Updates the RTT based on a new sample.
+void RttStats::UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now) {
+ QuicTime::Delta rtt_sample(QuicTime::Delta::Zero());
+ if (send_delta > ack_delay) {
+ rtt_sample = send_delta.Subtract(ack_delay);
+ } else if (!HasUpdates()) {
+ // Even though we received information from the peer suggesting
+ // an invalid (negative) RTT, we can use the send delta as an
+ // approximation until we get a better estimate.
+ rtt_sample = send_delta;
+ }
+
+ if (rtt_sample.IsInfinite() || rtt_sample.IsZero()) {
+ DVLOG(1) << "Ignoring rtt, because it's "
+ << (rtt_sample.IsZero() ? "Zero" : "Infinite");
+ return;
+ }
+ // RTT can't be negative.
+ DCHECK_LT(0, rtt_sample.ToMicroseconds());
+
+ latest_rtt_ = rtt_sample;
+ // First time call or link delay decreases.
+ if (min_rtt_.IsZero() || min_rtt_ > rtt_sample) {
+ min_rtt_ = rtt_sample;
+ }
+ UpdateRecentMinRtt(rtt_sample, now);
+ // First time call.
+ if (!HasUpdates()) {
+ smoothed_rtt_ = rtt_sample;
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(
+ rtt_sample.ToMicroseconds() / 2);
+ } else {
+ mean_deviation_ = QuicTime::Delta::FromMicroseconds(
+ kOneMinusBeta * mean_deviation_.ToMicroseconds() +
+ kBeta * std::abs(smoothed_rtt_.Subtract(rtt_sample).ToMicroseconds()));
+ smoothed_rtt_ = smoothed_rtt_.Multiply(kOneMinusAlpha).Add(
+ rtt_sample.Multiply(kAlpha));
+ DVLOG(1) << " smoothed_rtt(us):" << smoothed_rtt_.ToMicroseconds()
+ << " mean_deviation(us):" << mean_deviation_.ToMicroseconds();
+ }
+}
+
+void RttStats::UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now) {
+ // Recent min_rtt update.
+ if (num_min_rtt_samples_remaining_ > 0) {
+ --num_min_rtt_samples_remaining_;
+ if (new_min_rtt_.rtt.IsZero() || rtt_sample <= new_min_rtt_.rtt) {
+ new_min_rtt_ = RttSample(rtt_sample, now);
+ }
+ if (num_min_rtt_samples_remaining_ == 0) {
+ quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_ = new_min_rtt_;
+ }
+ }
+
+ // Update the three recent rtt samples.
+ if (recent_min_rtt_.rtt.IsZero() || rtt_sample <= recent_min_rtt_.rtt) {
+ recent_min_rtt_ = RttSample(rtt_sample, now);
+ quarter_window_rtt_ = half_window_rtt_ = recent_min_rtt_;
+ } else if (rtt_sample <= half_window_rtt_.rtt) {
+ half_window_rtt_ = RttSample(rtt_sample, now);
+ quarter_window_rtt_ = half_window_rtt_;
+ } else if (rtt_sample <= quarter_window_rtt_.rtt) {
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ }
+
+ // Expire old min rtt samples.
+ if (recent_min_rtt_.time < now.Subtract(recent_min_rtt_window_)) {
+ recent_min_rtt_ = half_window_rtt_;
+ half_window_rtt_ = quarter_window_rtt_;
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ } else if (half_window_rtt_.time <
+ now.Subtract(recent_min_rtt_window_.Multiply(kHalfWindow))) {
+ half_window_rtt_ = quarter_window_rtt_;
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ } else if (quarter_window_rtt_.time <
+ now.Subtract(recent_min_rtt_window_.Multiply(kQuarterWindow))) {
+ quarter_window_rtt_ = RttSample(rtt_sample, now);
+ }
+}
+
+QuicTime::Delta RttStats::SmoothedRtt() const {
+ if (!HasUpdates()) {
+ return QuicTime::Delta::FromMicroseconds(initial_rtt_us_);
+ }
+ return smoothed_rtt_;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/rtt_stats.h b/net/quic/congestion_control/rtt_stats.h
new file mode 100644
index 0000000..5e01687
--- /dev/null
+++ b/net/quic/congestion_control/rtt_stats.h
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A convenience class to store rtt samples and calculate smoothed rtt.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
+#define NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+namespace test {
+class RttStatsPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE RttStats {
+ public:
+ RttStats();
+
+ // Returns true if any RTT measurements have been made.
+ bool HasUpdates() const;
+
+ // Updates the RTT from an incoming ack which is received |send_delta| after
+ // the packet is sent and the peer reports the ack being delayed |ack_delay|.
+ void UpdateRtt(QuicTime::Delta send_delta,
+ QuicTime::Delta ack_delay,
+ QuicTime now);
+
+ // Causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
+ // is larger. The mean deviation is increased to the most recent deviation if
+ // it's larger.
+ void ExpireSmoothedMetrics();
+
+ // Forces RttStats to sample a new recent min rtt within the next
+ // |num_samples| UpdateRtt calls.
+ void SampleNewRecentMinRtt(uint32 num_samples);
+
+ QuicTime::Delta SmoothedRtt() const;
+
+ int64 initial_rtt_us() const {
+ return initial_rtt_us_;
+ }
+
+ // Sets an initial RTT to be used for SmoothedRtt before any RTT updates.
+ void set_initial_rtt_us(int64 initial_rtt_us) {
+ initial_rtt_us_ = initial_rtt_us;
+ }
+
+ QuicTime::Delta latest_rtt() const {
+ return latest_rtt_;
+ }
+
+ // Returns the min_rtt for the entire connection.
+ QuicTime::Delta min_rtt() const {
+ return min_rtt_;
+ }
+
+ // Returns the min_rtt since SampleNewRecentMinRtt has been called, or the
+ // min_rtt for the entire connection if SampleNewMinRtt was never called.
+ QuicTime::Delta recent_min_rtt() const {
+ return recent_min_rtt_.rtt;
+ }
+
+ QuicTime::Delta mean_deviation() const {
+ return mean_deviation_;
+ }
+
+ // Sets how old a recent min rtt sample can be.
+ void set_recent_min_rtt_window(QuicTime::Delta recent_min_rtt_window) {
+ recent_min_rtt_window_ = recent_min_rtt_window;
+ }
+
+ private:
+ friend class test::RttStatsPeer;
+
+ // Used to track a sampled RTT window.
+ struct RttSample {
+ RttSample() : rtt(QuicTime::Delta::Zero()), time(QuicTime::Zero()) { }
+ RttSample(QuicTime::Delta rtt, QuicTime time) : rtt(rtt), time(time) { }
+
+ QuicTime::Delta rtt;
+ QuicTime time; // Time the rtt sample was recorded.
+ };
+
+ // Implements the resampling algorithm and the windowed min rtt algorithm.
+ void UpdateRecentMinRtt(QuicTime::Delta rtt_sample, QuicTime now);
+
+ QuicTime::Delta latest_rtt_;
+ QuicTime::Delta min_rtt_;
+ QuicTime::Delta smoothed_rtt_;
+ // Mean RTT deviation during this session.
+ // Approximation of standard deviation, the error is roughly 1.25 times
+ // larger than the standard deviation, for a normally distributed signal.
+ QuicTime::Delta mean_deviation_;
+ int64 initial_rtt_us_;
+
+ RttSample new_min_rtt_;
+ uint32 num_min_rtt_samples_remaining_;
+
+ // State variables for Kathleen Nichols MinRTT algorithm.
+ QuicTime::Delta recent_min_rtt_window_;
+ RttSample recent_min_rtt_; // a in the windowed algorithm.
+ RttSample half_window_rtt_; // b in the sampled algorithm.
+ RttSample quarter_window_rtt_; // c in the sampled algorithm.
+
+ DISALLOW_COPY_AND_ASSIGN(RttStats);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_RTT_STATS_H_
diff --git a/net/quic/congestion_control/rtt_stats_test.cc b/net/quic/congestion_control/rtt_stats_test.cc
new file mode 100644
index 0000000..6b5ff7d
--- /dev/null
+++ b/net/quic/congestion_control/rtt_stats_test.cc
@@ -0,0 +1,185 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/rtt_stats.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class RttStatsPeer {
+ public:
+ static QuicTime::Delta GetHalfWindowRtt(const RttStats* rtt_stats) {
+ return rtt_stats->half_window_rtt_.rtt;
+ }
+
+ static QuicTime::Delta GetQuarterWindowRtt(const RttStats* rtt_stats) {
+ return rtt_stats->quarter_window_rtt_.rtt;
+ }
+};
+
+class RttStatsTest : public ::testing::Test {
+ protected:
+ RttStats rtt_stats_;
+};
+
+TEST_F(RttStatsTest, MinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(10)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(20)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(30)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(40)));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, RecentMinRtt) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(10),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+
+ rtt_stats_.SampleNewRecentMinRtt(4);
+ for (int i = 0; i < 3; ++i) {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ rtt_stats_.recent_min_rtt());
+ }
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(50),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50), rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, WindowedRecentMinRtt) {
+ // Set the window to 99ms, so 25ms is more than a quarter rtt.
+ rtt_stats_.set_recent_min_rtt_window(QuicTime::Delta::FromMilliseconds(99));
+
+ QuicTime now = QuicTime::Zero();
+ QuicTime::Delta rtt_sample = QuicTime::Delta::FromMilliseconds(10);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.recent_min_rtt());
+
+ // Gradually increase the rtt samples and ensure the recent_min_rtt starts
+ // rising.
+ for (int i = 0; i < 8; ++i) {
+ now = now.Add(QuicTime::Delta::FromMilliseconds(25));
+ rtt_sample = rtt_sample.Add(QuicTime::Delta::FromMilliseconds(10));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(10)),
+ RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ if (i < 3) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10),
+ rtt_stats_.recent_min_rtt());
+ } else if (i < 5) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30),
+ rtt_stats_.recent_min_rtt());
+ } else if (i < 7) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(50),
+ rtt_stats_.recent_min_rtt());
+ } else {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+ }
+ }
+
+ // A new quarter rtt low sets that, but nothing else.
+ rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(5)),
+ RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+
+ // A new half rtt low sets that and the quarter rtt low.
+ rtt_sample = rtt_sample.Subtract(QuicTime::Delta::FromMilliseconds(15));
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(70),
+ rtt_stats_.recent_min_rtt());
+
+ // A new full window loss sets the recent_min_rtt, but not min_rtt.
+ rtt_sample = QuicTime::Delta::FromMilliseconds(65);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt());
+
+ // A new all time low sets both the min_rtt and the recent_min_rtt.
+ rtt_sample = QuicTime::Delta::FromMilliseconds(5);
+ rtt_stats_.UpdateRtt(rtt_sample, QuicTime::Delta::Zero(), now);
+
+ EXPECT_EQ(rtt_sample, rtt_stats_.min_rtt());
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetQuarterWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, RttStatsPeer::GetHalfWindowRtt(&rtt_stats_));
+ EXPECT_EQ(rtt_sample, rtt_stats_.recent_min_rtt());
+}
+
+TEST_F(RttStatsTest, ExpireSmoothedMetrics) {
+ QuicTime::Delta initial_rtt = QuicTime::Delta::FromMilliseconds(10);
+ rtt_stats_.UpdateRtt(initial_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+ EXPECT_EQ(initial_rtt, rtt_stats_.recent_min_rtt());
+ EXPECT_EQ(initial_rtt, rtt_stats_.SmoothedRtt());
+
+ EXPECT_EQ(initial_rtt.Multiply(0.5), rtt_stats_.mean_deviation());
+
+ // Update once with a 20ms RTT.
+ QuicTime::Delta doubled_rtt = initial_rtt.Multiply(2);
+ rtt_stats_.UpdateRtt(doubled_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(initial_rtt.Multiply(1.125), rtt_stats_.SmoothedRtt());
+
+ // Expire the smoothed metrics, increasing smoothed rtt and mean deviation.
+ rtt_stats_.ExpireSmoothedMetrics();
+ EXPECT_EQ(doubled_rtt, rtt_stats_.SmoothedRtt());
+ EXPECT_EQ(initial_rtt.Multiply(0.875), rtt_stats_.mean_deviation());
+
+ // Now go back down to 5ms and expire the smoothed metrics, and ensure the
+ // mean deviation increases to 15ms.
+ QuicTime::Delta half_rtt = initial_rtt.Multiply(0.5);
+ rtt_stats_.UpdateRtt(half_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_GT(doubled_rtt, rtt_stats_.SmoothedRtt());
+ EXPECT_LT(initial_rtt, rtt_stats_.mean_deviation());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/send_algorithm_interface.cc b/net/quic/congestion_control/send_algorithm_interface.cc
new file mode 100644
index 0000000..7245e33
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_interface.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+
+#include "net/quic/congestion_control/tcp_cubic_sender.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class RttStats;
+
+// Factory for send side congestion control algorithm.
+SendAlgorithmInterface* SendAlgorithmInterface::Create(
+ const QuicClock* clock,
+ const RttStats* rtt_stats,
+ CongestionControlType congestion_control_type,
+ QuicConnectionStats* stats) {
+ switch (congestion_control_type) {
+ case kCubic:
+ return new TcpCubicSender(clock, rtt_stats,
+ false /* don't use Reno */,
+ kMaxTcpCongestionWindow, stats);
+ case kReno:
+ return new TcpCubicSender(clock, rtt_stats,
+ true /* use Reno */,
+ kMaxTcpCongestionWindow, stats);
+ case kBBR:
+ LOG(DFATAL) << "BbrTcpSender is not supported.";
+ return nullptr;
+ }
+ return nullptr;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/send_algorithm_interface.h b/net/quic/congestion_control/send_algorithm_interface.h
new file mode 100644
index 0000000..14383f8
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_interface.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The pure virtual class for send side congestion control algorithm.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
+#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class RttStats;
+
+class NET_EXPORT_PRIVATE SendAlgorithmInterface {
+ public:
+ // A sorted vector of packets.
+ typedef std::vector<std::pair<QuicPacketSequenceNumber, TransmissionInfo>>
+ CongestionVector;
+
+ static SendAlgorithmInterface* Create(const QuicClock* clock,
+ const RttStats* rtt_stats,
+ CongestionControlType type,
+ QuicConnectionStats* stats);
+
+ virtual ~SendAlgorithmInterface() {}
+
+ virtual void SetFromConfig(const QuicConfig& config, bool is_server) = 0;
+
+ // Sets the number of connections to emulate when doing congestion control,
+ // particularly for congestion avoidance. Can be set any time.
+ virtual void SetNumEmulatedConnections(int num_connections) = 0;
+
+ // Called when we receive congestion feedback from remote peer.
+ virtual void OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time) = 0;
+
+ // Indicates an update to the congestion state, caused either by an incoming
+ // ack or loss event timeout. |rtt_updated| indicates whether a new
+ // latest_rtt sample has been taken, |byte_in_flight| the bytes in flight
+ // prior to the congestion event. |acked_packets| and |lost_packets| are
+ // any packets considered acked or lost as a result of the congestion event.
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionVector& acked_packets,
+ const CongestionVector& lost_packets) = 0;
+
+ // Inform that we sent |bytes| to the wire, and if the packet is
+ // retransmittable. Returns true if the packet should be tracked by the
+ // congestion manager and included in bytes_in_flight, false otherwise.
+ // |bytes_in_flight| is the number of bytes in flight before the packet was
+ // sent.
+ // Note: this function must be called for every packet sent to the wire.
+ virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) = 0;
+
+ // Called when the retransmission timeout fires. Neither OnPacketAbandoned
+ // nor OnPacketLost will be called for these packets.
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) = 0;
+
+ // Called when the last retransmission timeout was spurious.
+ virtual void RevertRetransmissionTimeout() = 0;
+
+ // Calculate the time until we can send the next packet.
+ virtual QuicTime::Delta TimeUntilSend(
+ QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const = 0;
+
+ // What's the current estimated bandwidth in bytes per second.
+ // Returns 0 when it does not have an estimate.
+ virtual QuicBandwidth BandwidthEstimate() const = 0;
+
+ // Returns true if the current bandwidth estimate is reliable.
+ virtual bool HasReliableBandwidthEstimate() const = 0;
+
+ // Get the send algorithm specific retransmission delay, called RTO in TCP,
+ // Note 1: the caller is responsible for sanity checking this value.
+ // Note 2: this will return zero if we don't have enough data for an estimate.
+ virtual QuicTime::Delta RetransmissionDelay() const = 0;
+
+ // Returns the size of the current congestion window in bytes. Note, this is
+ // not the *available* window. Some send algorithms may not use a congestion
+ // window and will return 0.
+ virtual QuicByteCount GetCongestionWindow() const = 0;
+
+ // Whether the send algorithm is currently in slow start. When true, the
+ // BandwidthEstimate is expected to be too low.
+ virtual bool InSlowStart() const = 0;
+
+ // Whether the send algorithm is currently in recovery.
+ virtual bool InRecovery() const = 0;
+
+ // Returns the size of the slow start congestion window in bytes,
+ // aka ssthresh. Some send algorithms do not define a slow start
+ // threshold and will return 0.
+ virtual QuicByteCount GetSlowStartThreshold() const = 0;
+
+ virtual CongestionControlType GetCongestionControlType() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_
diff --git a/net/quic/congestion_control/send_algorithm_simulator.cc b/net/quic/congestion_control/send_algorithm_simulator.cc
new file mode 100644
index 0000000..f08acff
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_simulator.cc
@@ -0,0 +1,373 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/send_algorithm_simulator.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "net/quic/crypto/quic_random.h"
+
+using std::list;
+using std::make_pair;
+using std::max;
+using std::min;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+const QuicByteCount kPacketSize = 1200;
+
+} // namespace
+
+SendAlgorithmSimulator::Sender::Sender(SendAlgorithmInterface* send_algorithm,
+ RttStats* rtt_stats)
+ : send_algorithm(send_algorithm),
+ rtt_stats(rtt_stats),
+ last_sent(0),
+ last_acked(0),
+ next_acked(1),
+ max_cwnd(0),
+ min_cwnd(100000),
+ max_cwnd_drop(0),
+ last_cwnd(0),
+ last_transfer_bandwidth(QuicBandwidth::Zero()),
+ last_transfer_loss_rate(0) {}
+
+SendAlgorithmSimulator::SendAlgorithmSimulator(
+ MockClock* clock,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt)
+ : clock_(clock),
+ lose_next_ack_(false),
+ forward_loss_rate_(0),
+ reverse_loss_rate_(0),
+ loss_correlation_(0),
+ bandwidth_(bandwidth),
+ rtt_(rtt),
+ buffer_size_(1000000),
+ delayed_ack_timer_(QuicTime::Delta::FromMilliseconds(100)) {
+ uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
+ DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed;
+ simple_random_.set_seed(seed);
+}
+
+SendAlgorithmSimulator::~SendAlgorithmSimulator() {}
+
+void SendAlgorithmSimulator::AddTransfer(Sender* sender, size_t num_bytes) {
+ AddTransfer(sender, num_bytes, clock_->Now());
+}
+
+void SendAlgorithmSimulator::AddTransfer(
+ Sender* sender, size_t num_bytes, QuicTime start_time) {
+ pending_transfers_.push_back(Transfer(sender, num_bytes, start_time));
+ // Record initial stats from when the transfer begins.
+ pending_transfers_.back().sender->RecordStats();
+}
+
+void SendAlgorithmSimulator::TransferBytes() {
+ TransferBytes(kuint64max, QuicTime::Delta::Infinite());
+}
+
+void SendAlgorithmSimulator::TransferBytes(QuicByteCount max_bytes,
+ QuicTime::Delta max_time) {
+ const QuicTime end_time = max_time.IsInfinite() ?
+ QuicTime::Zero().Add(QuicTime::Delta::Infinite()) :
+ clock_->Now().Add(max_time);
+ QuicByteCount bytes_sent = 0;
+ while (!pending_transfers_.empty() &&
+ clock_->Now() < end_time &&
+ bytes_sent < max_bytes) {
+ // Determine the times of next send and of the next ack arrival.
+ PacketEvent send_event = NextSendEvent();
+ PacketEvent ack_event = NextAckEvent();
+ // If both times are infinite, fire a TLP.
+ if (ack_event.time_delta.IsInfinite() &&
+ send_event.time_delta.IsInfinite()) {
+ DVLOG(1) << "Both times are infinite, simulating a TLP.";
+ // TODO(ianswett): Use a more sophisticated TLP timer or never lose
+ // the last ack?
+ clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ SendDataNow(&pending_transfers_.front());
+ } else if (ack_event.time_delta < send_event.time_delta) {
+ DVLOG(1) << "Handling ack of largest observed:"
+ << ack_event.transfer->sender->next_acked << ", advancing time:"
+ << ack_event.time_delta.ToMicroseconds() << "us";
+ // Ack data all the data up to ack time and lose any missing sequence
+ // numbers.
+ clock_->AdvanceTime(ack_event.time_delta);
+ HandlePendingAck(ack_event.transfer);
+ } else {
+ DVLOG(1) << "Sending, advancing time:"
+ << send_event.time_delta.ToMicroseconds() << "us";
+ clock_->AdvanceTime(send_event.time_delta);
+ SendDataNow(send_event.transfer);
+ bytes_sent += kPacketSize;
+ }
+ }
+}
+
+SendAlgorithmSimulator::PacketEvent SendAlgorithmSimulator::NextSendEvent() {
+ QuicTime::Delta next_send_time = QuicTime::Delta::Infinite();
+ Transfer* transfer = nullptr;
+ for (vector<Transfer>::iterator it = pending_transfers_.begin();
+ it != pending_transfers_.end(); ++it) {
+ // If we've already sent enough bytes, wait for them to be acked.
+ if (it->bytes_acked + it->bytes_in_flight >= it->num_bytes) {
+ continue;
+ }
+ // If the flow hasn't started, use the start time.
+ QuicTime::Delta transfer_send_time = it->start_time.Subtract(clock_->Now());
+ if (clock_->Now() >= it->start_time) {
+ transfer_send_time =
+ it->sender->send_algorithm->TimeUntilSend(
+ clock_->Now(), it->bytes_in_flight, HAS_RETRANSMITTABLE_DATA);
+ }
+ if (transfer_send_time < next_send_time) {
+ next_send_time = transfer_send_time;
+ transfer = &(*it);
+ }
+ }
+ DVLOG(1) << "NextSendTime returning delta(ms):"
+ << next_send_time.ToMilliseconds();
+ return PacketEvent(next_send_time, transfer);
+}
+
+// NextAck takes into account packet loss in both forward and reverse
+// direction, as well as correlated losses. And it assumes the receiver acks
+// every other packet when there is no loss.
+SendAlgorithmSimulator::PacketEvent SendAlgorithmSimulator::NextAckEvent() {
+ if (sent_packets_.empty()) {
+ DVLOG(1) << "No outstanding packets to ack for any transfer.";
+ return PacketEvent(QuicTime::Delta::Infinite(), nullptr);
+ }
+
+ // For each connection, find the next acked packet.
+ QuicTime::Delta ack_time = QuicTime::Delta::Infinite();
+ Transfer* transfer = nullptr;
+ for (vector<Transfer>::iterator it = pending_transfers_.begin();
+ it != pending_transfers_.end(); ++it) {
+ QuicTime::Delta transfer_ack_time = FindNextAcked(&(*it));
+ if (transfer_ack_time < ack_time) {
+ ack_time = transfer_ack_time;
+ transfer = &(*it);
+ }
+ }
+
+ return PacketEvent(ack_time, transfer);
+}
+
+QuicTime::Delta SendAlgorithmSimulator::FindNextAcked(Transfer* transfer) {
+ Sender* sender = transfer->sender;
+ if (sender->next_acked == sender->last_acked) {
+ // Determine if the next ack is lost only once, to ensure determinism.
+ lose_next_ack_ =
+ reverse_loss_rate_ * kuint64max > simple_random_.RandUint64();
+ }
+
+ QuicPacketSequenceNumber next_acked = sender->last_acked;
+ QuicTime::Delta next_ack_delay =
+ FindNextAck(transfer, sender->last_acked, &next_acked);
+ if (lose_next_ack_) {
+ next_ack_delay = FindNextAck(transfer, next_acked, &next_acked);
+ }
+ sender->next_acked = next_acked;
+ return next_ack_delay;
+}
+
+QuicTime::Delta SendAlgorithmSimulator::FindNextAck(
+ const Transfer* transfer,
+ QuicPacketSequenceNumber last_acked,
+ QuicPacketSequenceNumber* next_acked) const {
+ *next_acked = last_acked;
+ QuicTime::Delta ack_delay = QuicTime::Delta::Infinite();
+ // Remove any packets that are simulated as lost.
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end(); ++it) {
+ if (transfer != it->transfer) {
+ continue;
+ }
+ // Skip over any packets less than or equal to last_acked.
+ if (it->sequence_number <= last_acked) {
+ continue;
+ }
+ // Lost packets don't trigger an ack.
+ if (it->lost) {
+ continue;
+ }
+ DCHECK_LT(*next_acked, it->sequence_number);
+ // Consider a delayed ack for the current next_acked.
+ if (ack_delay < it->ack_time.Subtract(clock_->Now())) {
+ break;
+ }
+ *next_acked = it->sequence_number;
+ ack_delay = it->ack_time.Subtract(clock_->Now());
+ if (HasRecentLostPackets(transfer, *next_acked) ||
+ (*next_acked - last_acked) >= 2) {
+ break;
+ }
+ ack_delay = ack_delay.Add(delayed_ack_timer_);
+ }
+
+ DVLOG(1) << "FindNextAcked found next_acked_:"
+ << transfer->sender->next_acked
+ << " last_acked:" << transfer->sender->last_acked
+ << " ack_delay(ms):" << ack_delay.ToMilliseconds();
+ return ack_delay;
+}
+
+bool SendAlgorithmSimulator::HasRecentLostPackets(
+ const Transfer* transfer, QuicPacketSequenceNumber next_acked) const {
+ QuicPacketSequenceNumber last_packet = transfer->sender->last_acked;
+ for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+ it != sent_packets_.end() && it->sequence_number < next_acked; ++it) {
+ if (transfer != it->transfer) {
+ continue;
+ }
+ // Lost packets don't trigger an ack.
+ if (it->lost) {
+ return true;
+ }
+ // Buffer dropped packets are skipped automatically, but still end up
+ // being lost and cause acks to be sent immediately.
+ if (it->sequence_number > last_packet + 1) {
+ return true;
+ }
+ last_packet = it->sequence_number;
+ }
+ return false;
+}
+
+void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) {
+ Sender* sender = transfer->sender;
+ DCHECK_LT(sender->last_acked, sender->next_acked);
+ SendAlgorithmInterface::CongestionVector acked_packets;
+ SendAlgorithmInterface::CongestionVector lost_packets;
+ DVLOG(1) << "Acking packets from:" << sender->last_acked
+ << " to " << sender->next_acked
+ << " bytes_in_flight:" << transfer->bytes_in_flight
+ << " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms";
+ // Some entries may be missing from the sent_packets_ array, if they were
+ // dropped due to buffer overruns.
+ SentPacket largest_observed;
+ list<SentPacket>::iterator it = sent_packets_.begin();
+ while (sender->last_acked < sender->next_acked) {
+ ++sender->last_acked;
+ TransmissionInfo info = TransmissionInfo();
+ info.bytes_sent = kPacketSize;
+ info.in_flight = true;
+ // Find the next SentPacket for this transfer.
+ while (it->transfer != transfer) {
+ DCHECK(it != sent_packets_.end());
+ ++it;
+ }
+ // If it's missing from the array, it's a loss.
+ if (it->sequence_number > sender->last_acked) {
+ DVLOG(1) << "Lost packet:" << sender->last_acked
+ << " dropped by buffer overflow.";
+ lost_packets.push_back(make_pair(sender->last_acked, info));
+ continue;
+ }
+ if (it->lost) {
+ lost_packets.push_back(make_pair(sender->last_acked, info));
+ } else {
+ acked_packets.push_back(make_pair(sender->last_acked, info));
+ }
+ // This packet has been acked or lost, remove it from sent_packets_.
+ largest_observed = *it;
+ sent_packets_.erase(it++);
+ }
+
+ DCHECK(largest_observed.ack_time.IsInitialized());
+ DVLOG(1) << "Updating RTT from send_time:"
+ << largest_observed.send_time.ToDebuggingValue() << " to ack_time:"
+ << largest_observed.ack_time.ToDebuggingValue();
+ QuicTime::Delta measured_rtt =
+ largest_observed.ack_time.Subtract(largest_observed.send_time);
+ DCHECK_GE(measured_rtt.ToMicroseconds(), rtt_.ToMicroseconds());
+ sender->rtt_stats->UpdateRtt(measured_rtt,
+ QuicTime::Delta::Zero(),
+ clock_->Now());
+ sender->send_algorithm->OnCongestionEvent(
+ true, transfer->bytes_in_flight, acked_packets, lost_packets);
+ DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()),
+ transfer->bytes_in_flight);
+ transfer->bytes_in_flight -=
+ kPacketSize * (acked_packets.size() + lost_packets.size());
+
+ sender->RecordStats();
+ transfer->bytes_acked += acked_packets.size() * kPacketSize;
+ transfer->bytes_lost += lost_packets.size() * kPacketSize;
+ if (transfer->bytes_acked >= transfer->num_bytes) {
+ // Remove completed transfers and record transfer bandwidth.
+ QuicTime::Delta transfer_time =
+ clock_->Now().Subtract(transfer->start_time);
+ sender->last_transfer_loss_rate = static_cast<float>(transfer->bytes_lost) /
+ (transfer->bytes_lost + transfer->bytes_acked);
+ sender->last_transfer_bandwidth =
+ QuicBandwidth::FromBytesAndTimeDelta(transfer->num_bytes,
+ transfer_time);
+ DCHECK_GE(bandwidth_.ToBitsPerSecond(),
+ sender->last_transfer_bandwidth.ToBitsPerSecond());
+ for (vector<Transfer>::iterator it = pending_transfers_.begin();
+ it != pending_transfers_.end(); ++it) {
+ if (transfer == &(*it)) {
+ pending_transfers_.erase(it);
+ break;
+ }
+ }
+ }
+}
+
+void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) {
+ Sender* sender = transfer->sender;
+ ++sender->last_sent;
+ DVLOG(1) << "Sending packet:" << sender->last_sent
+ << " bytes_in_flight:" << transfer->bytes_in_flight
+ << " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms";
+ sender->send_algorithm->OnPacketSent(
+ clock_->Now(), transfer->bytes_in_flight,
+ sender->last_sent, kPacketSize, HAS_RETRANSMITTABLE_DATA);
+ // Lose the packet immediately if the buffer is full.
+ if (sent_packets_.size() * kPacketSize < buffer_size_) {
+ // TODO(ianswett): This buffer simulation is an approximation.
+ // An ack time of zero means loss.
+ bool packet_lost =
+ forward_loss_rate_ * kuint64max > simple_random_.RandUint64();
+ // Handle correlated loss.
+ if (!sent_packets_.empty() && sent_packets_.back().lost &&
+ loss_correlation_ * kuint64max > simple_random_.RandUint64()) {
+ packet_lost = true;
+ }
+ DVLOG(1) << "losing packet:" << sender->last_sent
+ << " due to random loss.";
+
+ // If the number of bytes in flight are less than the bdp, there's
+ // no buffering delay. Bytes lost from the buffer are not counted.
+ QuicByteCount bdp = bandwidth_.ToBytesPerPeriod(rtt_);
+ QuicTime ack_time = clock_->Now().Add(rtt_);
+ if (kPacketSize > bdp) {
+ ack_time = ack_time.Add(bandwidth_.TransferTime(kPacketSize - bdp));
+ }
+ QuicTime queue_ack_time = sent_packets_.empty() ? QuicTime::Zero() :
+ sent_packets_.back().ack_time.Add(bandwidth_.TransferTime(kPacketSize));
+ ack_time = QuicTime::Max(ack_time, queue_ack_time);
+ sent_packets_.push_back(SentPacket(
+ sender->last_sent, clock_->Now(), ack_time, packet_lost, transfer));
+ } else {
+ DVLOG(1) << "losing packet:" << sender->last_sent
+ << " because the buffer was full.";
+ }
+ transfer->bytes_in_flight += kPacketSize;
+}
+
+// Advance the time by |delta| without sending anything.
+void SendAlgorithmSimulator::AdvanceTime(QuicTime::Delta delta) {
+ clock_->AdvanceTime(delta);
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/send_algorithm_simulator.h b/net/quic/congestion_control/send_algorithm_simulator.h
new file mode 100644
index 0000000..3e61d88
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_simulator.h
@@ -0,0 +1,231 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A test only class to enable simulations of send algorithms.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPrintf;
+
+namespace net {
+
+class SendAlgorithmSimulator {
+ public:
+ struct Sender {
+ Sender(SendAlgorithmInterface* send_algorithm, RttStats* rtt_stats);
+
+ void RecordStats() {
+ QuicByteCount cwnd = send_algorithm->GetCongestionWindow();
+ max_cwnd = std::max(max_cwnd, cwnd);
+ min_cwnd = std::min(min_cwnd, cwnd);
+ if (last_cwnd > cwnd) {
+ max_cwnd_drop = std::max(max_cwnd_drop, last_cwnd - cwnd);
+ }
+ last_cwnd = cwnd;
+ }
+
+ std::string DebugString() {
+ return StringPrintf("observed goodput(bytes/s):%" PRId64
+ " loss rate:%f"
+ " cwnd:%" PRIu64
+ " max_cwnd:%" PRIu64 " min_cwnd:%" PRIu64
+ " max_cwnd_drop:%" PRIu64,
+ last_transfer_bandwidth.ToBytesPerSecond(),
+ last_transfer_loss_rate,
+ send_algorithm->GetCongestionWindow(),
+ max_cwnd, min_cwnd, max_cwnd_drop);
+ }
+
+ SendAlgorithmInterface* send_algorithm;
+ RttStats* rtt_stats;
+
+ // Last sequence number the sender sent.
+ QuicPacketSequenceNumber last_sent;
+ // Last packet sequence number acked.
+ QuicPacketSequenceNumber last_acked;
+ // Packet sequence number to ack up to.
+ QuicPacketSequenceNumber next_acked;
+
+ // Stats collected for understanding the congestion control.
+ QuicByteCount max_cwnd;
+ QuicByteCount min_cwnd;
+ QuicByteCount max_cwnd_drop;
+ QuicByteCount last_cwnd;
+
+ QuicBandwidth last_transfer_bandwidth;
+ float last_transfer_loss_rate;
+ };
+
+ struct Transfer {
+ Transfer(Sender* sender, QuicByteCount num_bytes, QuicTime start_time)
+ : sender(sender),
+ num_bytes(num_bytes),
+ bytes_acked(0),
+ bytes_lost(0),
+ bytes_in_flight(0),
+ start_time(start_time) {}
+
+ Sender* sender;
+ QuicByteCount num_bytes;
+ QuicByteCount bytes_acked;
+ QuicByteCount bytes_lost;
+ QuicByteCount bytes_in_flight;
+ QuicTime start_time;
+ };
+
+ struct SentPacket {
+ SentPacket()
+ : sequence_number(0),
+ send_time(QuicTime::Zero()),
+ ack_time(QuicTime::Zero()),
+ lost(false),
+ transfer(nullptr) {}
+ SentPacket(QuicPacketSequenceNumber sequence_number,
+ QuicTime send_time,
+ QuicTime ack_time,
+ bool lost,
+ Transfer* transfer)
+ : sequence_number(sequence_number),
+ send_time(send_time),
+ ack_time(ack_time),
+ lost(lost),
+ transfer(transfer) {}
+
+ QuicPacketSequenceNumber sequence_number;
+ QuicTime send_time;
+ QuicTime ack_time;
+ bool lost;
+ Transfer* transfer;
+ };
+
+ // |rtt_stats| should be the same RttStats used by the |send_algorithm|.
+ SendAlgorithmSimulator(MockClock* clock_,
+ QuicBandwidth bandwidth,
+ QuicTime::Delta rtt);
+ ~SendAlgorithmSimulator();
+
+ void set_bandwidth(QuicBandwidth bandwidth) {
+ bandwidth_ = bandwidth;
+ }
+
+ void set_forward_loss_rate(float loss_rate) {
+ DCHECK_LT(loss_rate, 1.0f);
+ forward_loss_rate_ = loss_rate;
+ }
+
+ void set_reverse_loss_rate(float loss_rate) {
+ DCHECK_LT(loss_rate, 1.0f);
+ reverse_loss_rate_ = loss_rate;
+ }
+
+ void set_loss_correlation(float loss_correlation) {
+ DCHECK_LT(loss_correlation, 1.0f);
+ loss_correlation_ = loss_correlation;
+ }
+
+ void set_buffer_size(size_t buffer_size_bytes) {
+ buffer_size_ = buffer_size_bytes;
+ }
+
+ void set_delayed_ack_timer(QuicTime::Delta delayed_ack_timer) {
+ delayed_ack_timer_ = delayed_ack_timer;
+ }
+
+ // Advance the time by |delta| without sending anything.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ // Adds a pending sender. The send will run when TransferBytes is called.
+ // Adding two transfers with the same sender is unsupported.
+ void AddTransfer(Sender* sender, size_t num_bytes);
+
+ // Adds a pending sending to start at the specified time.
+ void AddTransfer(Sender* sender, size_t num_bytes, QuicTime start_time);
+
+ // Convenience method to transfer all bytes.
+ void TransferBytes();
+
+ // Transfers bytes through the connection until |max_bytes| are reached,
+ // |max_time| is reached, or all senders have finished sending. If max_bytes
+ // is 0, it does not apply, and if |max_time| is Zero, no time limit applies.
+ void TransferBytes(QuicByteCount max_bytes, QuicTime::Delta max_time);
+
+ private:
+ // A pending packet event, either a send or an ack.
+ struct PacketEvent {
+ PacketEvent(QuicTime::Delta time_delta, Transfer* transfer)
+ : time_delta(time_delta),
+ transfer(transfer) {}
+
+ QuicTime::Delta time_delta;
+ Transfer* transfer;
+ };
+
+ // NextSendTime returns the next time any of the pending transfers send,
+ // and populates transfer if the send time is not infinite.
+ PacketEvent NextSendEvent();
+
+ // NextAckTime takes into account packet loss in both forward and reverse
+ // direction, as well as delayed ack behavior.
+ PacketEvent NextAckEvent();
+
+ // Sets the next acked.
+ QuicTime::Delta FindNextAcked(Transfer* transfer);
+
+ // Sets the |next_acked| packet for the |transfer| starting at the specified
+ // |last_acked|. Returns QuicTime::Delta::Infinite and doesn't set
+ // |next_acked| if there is no ack after |last_acked|.
+ QuicTime::Delta FindNextAck(const Transfer* transfer,
+ QuicPacketSequenceNumber last_acked,
+ QuicPacketSequenceNumber* next_acked) const;
+
+ // Returns true if any of the packets |transfer| is waiting for less than
+ // next_acked have been lost.
+ bool HasRecentLostPackets(const Transfer* transfer,
+ QuicPacketSequenceNumber next_acked) const;
+
+ // Process all the acks that should have arrived by the current time, and
+ // lose any packets that are missing. Returns the number of bytes acked.
+ void HandlePendingAck(Transfer* transfer);
+
+ void SendDataNow(Transfer* transfer);
+
+ // List of all pending transfers waiting to use the connection.
+ std::vector<Transfer> pending_transfers_;
+
+ MockClock* clock_;
+ // Whether the next ack should be lost.
+ bool lose_next_ack_;
+ // The times acks are expected, assuming acks are not lost and every packet
+ // is acked.
+ std::list<SentPacket> sent_packets_;
+
+ test::SimpleRandom simple_random_;
+ float forward_loss_rate_; // Loss rate on the forward path.
+ float reverse_loss_rate_; // Loss rate on the reverse path.
+ float loss_correlation_; // Likelihood the subsequent packet is lost.
+ QuicBandwidth bandwidth_;
+ QuicTime::Delta rtt_;
+ size_t buffer_size_; // In bytes.
+ QuicTime::Delta delayed_ack_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc
new file mode 100644
index 0000000..0aae5fd
--- /dev/null
+++ b/net/quic/congestion_control/tcp_cubic_sender.cc
@@ -0,0 +1,370 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/tcp_cubic_sender.h"
+
+#include <algorithm>
+
+#include "base/metrics/histogram.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+using std::max;
+using std::min;
+
+namespace net {
+
+namespace {
+// Constants based on TCP defaults.
+// The minimum cwnd based on RFC 3782 (TCP NewReno) for cwnd reductions on a
+// fast retransmission. The cwnd after a timeout is still 1.
+const QuicTcpCongestionWindow kMinimumCongestionWindow = 2;
+const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS;
+const int64 kInitialCongestionWindow = 10;
+const int kMaxBurstLength = 3;
+}; // namespace
+
+TcpCubicSender::TcpCubicSender(
+ const QuicClock* clock,
+ const RttStats* rtt_stats,
+ bool reno,
+ QuicTcpCongestionWindow max_tcp_congestion_window,
+ QuicConnectionStats* stats)
+ : hybrid_slow_start_(clock),
+ cubic_(clock, stats),
+ rtt_stats_(rtt_stats),
+ stats_(stats),
+ reno_(reno),
+ num_connections_(2),
+ congestion_window_count_(0),
+ receive_window_(kDefaultSocketReceiveBuffer),
+ prr_out_(0),
+ prr_delivered_(0),
+ ack_count_since_loss_(0),
+ bytes_in_flight_before_loss_(0),
+ largest_sent_sequence_number_(0),
+ largest_acked_sequence_number_(0),
+ largest_sent_at_last_cutback_(0),
+ congestion_window_(kInitialCongestionWindow),
+ previous_congestion_window_(0),
+ slowstart_threshold_(max_tcp_congestion_window),
+ previous_slowstart_threshold_(0),
+ last_cutback_exited_slowstart_(false),
+ max_tcp_congestion_window_(max_tcp_congestion_window) {
+}
+
+TcpCubicSender::~TcpCubicSender() {
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.FinalTcpCwnd", congestion_window_);
+}
+
+void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) {
+ if (is_server) {
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) {
+ // Initial window experiment. Ignore the initial congestion
+ // window suggested by the client and use the default ICWND of
+ // 10 instead.
+ congestion_window_ = kInitialCongestionWindow;
+ } else if (config.HasReceivedInitialCongestionWindow()) {
+ // Set the initial window size.
+ congestion_window_ = min(kMaxInitialWindow,
+ config.ReceivedInitialCongestionWindow());
+ }
+ }
+ if (config.HasReceivedSocketReceiveBuffer()) {
+ // Set the initial socket receive buffer size in bytes.
+ receive_window_ = config.ReceivedSocketReceiveBuffer();
+ }
+}
+
+void TcpCubicSender::SetNumEmulatedConnections(int num_connections) {
+ num_connections_ = max(1, num_connections);
+ cubic_.SetNumConnections(num_connections_);
+}
+
+void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time) {
+ if (feedback.type == kTCP) {
+ receive_window_ = feedback.tcp.receive_window;
+ }
+}
+
+void TcpCubicSender::OnCongestionEvent(
+ bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionVector& acked_packets,
+ const CongestionVector& lost_packets) {
+ if (rtt_updated && InSlowStart() &&
+ hybrid_slow_start_.ShouldExitSlowStart(rtt_stats_->latest_rtt(),
+ rtt_stats_->min_rtt(),
+ congestion_window_)) {
+ slowstart_threshold_ = congestion_window_;
+ }
+ for (CongestionVector::const_iterator it = lost_packets.begin();
+ it != lost_packets.end(); ++it) {
+ OnPacketLost(it->first, bytes_in_flight);
+ }
+ for (CongestionVector::const_iterator it = acked_packets.begin();
+ it != acked_packets.end(); ++it) {
+ OnPacketAcked(it->first, it->second.bytes_sent, bytes_in_flight);
+ }
+}
+
+void TcpCubicSender::OnPacketAcked(
+ QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount bytes_in_flight) {
+ largest_acked_sequence_number_ = max(acked_sequence_number,
+ largest_acked_sequence_number_);
+ if (InRecovery()) {
+ PrrOnPacketAcked(acked_bytes);
+ return;
+ }
+ MaybeIncreaseCwnd(acked_sequence_number, bytes_in_flight);
+ // TODO(ianswett): Should this even be called when not in slow start?
+ hybrid_slow_start_.OnPacketAcked(acked_sequence_number, InSlowStart());
+}
+
+void TcpCubicSender::OnPacketLost(QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes_in_flight) {
+ // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets
+ // already sent should be treated as a single loss event, since it's expected.
+ if (sequence_number <= largest_sent_at_last_cutback_) {
+ if (last_cutback_exited_slowstart_) {
+ ++stats_->slowstart_packets_lost;
+ }
+ DVLOG(1) << "Ignoring loss for largest_missing:" << sequence_number
+ << " because it was sent prior to the last CWND cutback.";
+ return;
+ }
+ ++stats_->tcp_loss_events;
+ last_cutback_exited_slowstart_ = InSlowStart();
+ if (InSlowStart()) {
+ ++stats_->slowstart_packets_lost;
+ }
+ PrrOnPacketLost(bytes_in_flight);
+
+ if (reno_) {
+ congestion_window_ = congestion_window_ >> 1;
+ } else {
+ congestion_window_ =
+ cubic_.CongestionWindowAfterPacketLoss(congestion_window_);
+ }
+ slowstart_threshold_ = congestion_window_;
+ // Enforce TCP's minimum congestion window of 2*MSS.
+ if (congestion_window_ < kMinimumCongestionWindow) {
+ congestion_window_ = kMinimumCongestionWindow;
+ }
+ largest_sent_at_last_cutback_ = largest_sent_sequence_number_;
+ // reset packet count from congestion avoidance mode. We start
+ // counting again when we're out of recovery.
+ congestion_window_count_ = 0;
+ DVLOG(1) << "Incoming loss; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+}
+
+bool TcpCubicSender::OnPacketSent(QuicTime /*sent_time*/,
+ QuicByteCount /*bytes_in_flight*/,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) {
+ // Only update bytes_in_flight_ for data packets.
+ if (is_retransmittable != HAS_RETRANSMITTABLE_DATA) {
+ return false;
+ }
+
+ prr_out_ += bytes;
+ DCHECK_LT(largest_sent_sequence_number_, sequence_number);
+ largest_sent_sequence_number_ = sequence_number;
+ hybrid_slow_start_.OnPacketSent(sequence_number);
+ return true;
+}
+
+QuicTime::Delta TcpCubicSender::TimeUntilSend(
+ QuicTime /* now */,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const {
+ if (has_retransmittable_data == NO_RETRANSMITTABLE_DATA) {
+ // For TCP we can always send an ACK immediately.
+ return QuicTime::Delta::Zero();
+ }
+ if (InRecovery()) {
+ return PrrTimeUntilSend(bytes_in_flight);
+ }
+ if (SendWindow() > bytes_in_flight) {
+ return QuicTime::Delta::Zero();
+ }
+ return QuicTime::Delta::Infinite();
+}
+
+QuicByteCount TcpCubicSender::SendWindow() const {
+ // What's the current send window in bytes.
+ return min(receive_window_, GetCongestionWindow());
+}
+
+QuicBandwidth TcpCubicSender::BandwidthEstimate() const {
+ return QuicBandwidth::FromBytesAndTimeDelta(GetCongestionWindow(),
+ rtt_stats_->SmoothedRtt());
+}
+
+bool TcpCubicSender::HasReliableBandwidthEstimate() const {
+ return !InSlowStart() && !InRecovery();
+}
+
+QuicTime::Delta TcpCubicSender::RetransmissionDelay() const {
+ if (!rtt_stats_->HasUpdates()) {
+ return QuicTime::Delta::Zero();
+ }
+ return QuicTime::Delta::FromMicroseconds(
+ rtt_stats_->SmoothedRtt().ToMicroseconds() +
+ 4 * rtt_stats_->mean_deviation().ToMicroseconds());
+}
+
+QuicByteCount TcpCubicSender::GetCongestionWindow() const {
+ return congestion_window_ * kMaxSegmentSize;
+}
+
+bool TcpCubicSender::InSlowStart() const {
+ return congestion_window_ < slowstart_threshold_;
+}
+
+QuicByteCount TcpCubicSender::GetSlowStartThreshold() const {
+ return slowstart_threshold_ * kMaxSegmentSize;
+}
+
+bool TcpCubicSender::IsCwndLimited(QuicByteCount bytes_in_flight) const {
+ const QuicByteCount congestion_window_bytes = congestion_window_ *
+ kMaxSegmentSize;
+ if (bytes_in_flight >= congestion_window_bytes) {
+ return true;
+ }
+ const QuicByteCount max_burst = kMaxBurstLength * kMaxSegmentSize;
+ const QuicByteCount available_bytes =
+ congestion_window_bytes - bytes_in_flight;
+ const bool slow_start_limited = InSlowStart() &&
+ bytes_in_flight > congestion_window_bytes / 2;
+ return slow_start_limited || available_bytes <= max_burst;
+}
+
+bool TcpCubicSender::InRecovery() const {
+ return largest_acked_sequence_number_ <= largest_sent_at_last_cutback_ &&
+ largest_acked_sequence_number_ != 0;
+}
+
+// Called when we receive an ack. Normal TCP tracks how many packets one ack
+// represents, but quic has a separate ack for each packet.
+void TcpCubicSender::MaybeIncreaseCwnd(
+ QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount bytes_in_flight) {
+ LOG_IF(DFATAL, InRecovery()) << "Never increase the CWND during recovery.";
+ if (!IsCwndLimited(bytes_in_flight)) {
+ // We don't update the congestion window unless we are close to using the
+ // window we have available.
+ return;
+ }
+ if (InSlowStart()) {
+ // congestion_window_cnt is the number of acks since last change of snd_cwnd
+ if (congestion_window_ < max_tcp_congestion_window_) {
+ // TCP slow start, exponential growth, increase by one for each ACK.
+ ++congestion_window_;
+ }
+ DVLOG(1) << "Slow start; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+ return;
+ }
+ if (congestion_window_ >= max_tcp_congestion_window_) {
+ return;
+ }
+ // Congestion avoidance
+ if (reno_) {
+ // Classic Reno congestion avoidance.
+ ++congestion_window_count_;
+ // Divide by num_connections to smoothly increase the CWND at a faster
+ // rate than conventional Reno.
+ if (congestion_window_count_ * num_connections_ >= congestion_window_) {
+ ++congestion_window_;
+ congestion_window_count_ = 0;
+ }
+
+ DVLOG(1) << "Reno; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_
+ << " congestion window count: " << congestion_window_count_;
+ } else {
+ congestion_window_ = min(max_tcp_congestion_window_,
+ cubic_.CongestionWindowAfterAck(
+ congestion_window_, rtt_stats_->min_rtt()));
+ DVLOG(1) << "Cubic; congestion window: " << congestion_window_
+ << " slowstart threshold: " << slowstart_threshold_;
+ }
+}
+
+void TcpCubicSender::OnRetransmissionTimeout(bool packets_retransmitted) {
+ largest_sent_at_last_cutback_ = 0;
+ if (!packets_retransmitted) {
+ return;
+ }
+ cubic_.Reset();
+ hybrid_slow_start_.Restart();
+ previous_slowstart_threshold_ = slowstart_threshold_;
+ slowstart_threshold_ = congestion_window_ / 2;
+ previous_congestion_window_ = congestion_window_;
+ congestion_window_ = kMinimumCongestionWindow;
+}
+
+void TcpCubicSender::RevertRetransmissionTimeout() {
+ if (previous_congestion_window_ == 0) {
+ LOG(DFATAL) << "No previous congestion window to revert to.";
+ return;
+ }
+ congestion_window_ = previous_congestion_window_;
+ slowstart_threshold_ = previous_slowstart_threshold_;
+ previous_congestion_window_ = 0;
+}
+
+void TcpCubicSender::PrrOnPacketLost(QuicByteCount bytes_in_flight) {
+ prr_out_ = 0;
+ bytes_in_flight_before_loss_ = bytes_in_flight;
+ prr_delivered_ = 0;
+ ack_count_since_loss_ = 0;
+}
+
+void TcpCubicSender::PrrOnPacketAcked(QuicByteCount acked_bytes) {
+ prr_delivered_ += acked_bytes;
+ ++ack_count_since_loss_;
+}
+
+QuicTime::Delta TcpCubicSender::PrrTimeUntilSend(
+ QuicByteCount bytes_in_flight) const {
+ DCHECK(InRecovery());
+ // Return QuicTime::Zero In order to ensure limited transmit always works.
+ if (prr_out_ == 0 || bytes_in_flight < kMaxSegmentSize) {
+ return QuicTime::Delta::Zero();
+ }
+ if (SendWindow() > bytes_in_flight) {
+ // During PRR-SSRB, limit outgoing packets to 1 extra MSS per ack, instead
+ // of sending the entire available window. This prevents burst retransmits
+ // when more packets are lost than the CWND reduction.
+ // limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS
+ if (prr_delivered_ + ack_count_since_loss_ * kMaxSegmentSize <= prr_out_) {
+ return QuicTime::Delta::Infinite();
+ }
+ return QuicTime::Delta::Zero();
+ }
+ // Implement Proportional Rate Reduction (RFC6937)
+ // Checks a simplified version of the PRR formula that doesn't use division:
+ // AvailableSendWindow =
+ // CEIL(prr_delivered * ssthresh / BytesInFlightAtLoss) - prr_sent
+ if (prr_delivered_ * slowstart_threshold_ * kMaxSegmentSize >
+ prr_out_ * bytes_in_flight_before_loss_) {
+ return QuicTime::Delta::Zero();
+ }
+ return QuicTime::Delta::Infinite();
+}
+
+CongestionControlType TcpCubicSender::GetCongestionControlType() const {
+ return reno_ ? kReno : kCubic;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/tcp_cubic_sender.h b/net/quic/congestion_control/tcp_cubic_sender.h
new file mode 100644
index 0000000..8851ac4
--- /dev/null
+++ b/net/quic/congestion_control/tcp_cubic_sender.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TCP cubic send side congestion algorithm, emulates the behavior of
+// TCP cubic.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_H_
+#define NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/cubic.h"
+#include "net/quic/congestion_control/hybrid_slow_start.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class RttStats;
+
+namespace test {
+class TcpCubicSenderPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE TcpCubicSender : public SendAlgorithmInterface {
+ public:
+ // Reno option and max_tcp_congestion_window are provided for testing.
+ TcpCubicSender(const QuicClock* clock,
+ const RttStats* rtt_stats,
+ bool reno,
+ QuicTcpCongestionWindow max_tcp_congestion_window,
+ QuicConnectionStats* stats);
+ virtual ~TcpCubicSender();
+
+ // Start implementation of SendAlgorithmInterface.
+ virtual void SetFromConfig(const QuicConfig& config, bool is_server) OVERRIDE;
+ virtual void SetNumEmulatedConnections(int num_connections) OVERRIDE;
+ virtual void OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback,
+ QuicTime feedback_receive_time) OVERRIDE;
+ virtual void OnCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionVector& acked_packets,
+ const CongestionVector& lost_packets) OVERRIDE;
+ virtual bool OnPacketSent(QuicTime sent_time,
+ QuicByteCount bytes_in_flight,
+ QuicPacketSequenceNumber sequence_number,
+ QuicByteCount bytes,
+ HasRetransmittableData is_retransmittable) OVERRIDE;
+ virtual void OnRetransmissionTimeout(bool packets_retransmitted) OVERRIDE;
+ virtual void RevertRetransmissionTimeout() OVERRIDE;
+ virtual QuicTime::Delta TimeUntilSend(
+ QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData has_retransmittable_data) const OVERRIDE;
+ virtual QuicBandwidth BandwidthEstimate() const OVERRIDE;
+ virtual bool HasReliableBandwidthEstimate() const OVERRIDE;
+ virtual QuicTime::Delta RetransmissionDelay() const OVERRIDE;
+ virtual QuicByteCount GetCongestionWindow() const OVERRIDE;
+ virtual bool InSlowStart() const OVERRIDE;
+ virtual bool InRecovery() const OVERRIDE;
+ virtual QuicByteCount GetSlowStartThreshold() const OVERRIDE;
+ virtual CongestionControlType GetCongestionControlType() const OVERRIDE;
+ // End implementation of SendAlgorithmInterface.
+
+ private:
+ friend class test::TcpCubicSenderPeer;
+
+ // TODO(ianswett): Remove these and migrate to OnCongestionEvent.
+ void OnPacketAcked(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount acked_bytes,
+ QuicByteCount bytes_in_flight);
+ void OnPacketLost(QuicPacketSequenceNumber largest_loss,
+ QuicByteCount bytes_in_flight);
+
+ QuicByteCount SendWindow() const;
+ void MaybeIncreaseCwnd(QuicPacketSequenceNumber acked_sequence_number,
+ QuicByteCount bytes_in_flight);
+ bool IsCwndLimited(QuicByteCount bytes_in_flight) const;
+ // Methods for isolating PRR from the rest of TCP Cubic.
+ void PrrOnPacketLost(QuicByteCount bytes_in_flight);
+ void PrrOnPacketAcked(QuicByteCount acked_bytes);
+ QuicTime::Delta PrrTimeUntilSend(QuicByteCount bytes_in_flight) const;
+
+
+ HybridSlowStart hybrid_slow_start_;
+ Cubic cubic_;
+ const RttStats* rtt_stats_;
+ QuicConnectionStats* stats_;
+
+ // If true, Reno congestion control is used instead of Cubic.
+ const bool reno_;
+
+ // Number of connections to simulate.
+ int num_connections_;
+
+ // ACK counter for the Reno implementation.
+ int64 congestion_window_count_;
+
+ // Receiver side advertised window.
+ QuicByteCount receive_window_;
+
+ // Bytes sent and acked since the last loss event. Used for PRR.
+ QuicByteCount prr_out_;
+ QuicByteCount prr_delivered_;
+ size_t ack_count_since_loss_;
+
+ // The congestion window before the last loss event.
+ QuicByteCount bytes_in_flight_before_loss_;
+
+ // Track the largest packet that has been sent.
+ QuicPacketSequenceNumber largest_sent_sequence_number_;
+
+ // Track the largest packet that has been acked.
+ QuicPacketSequenceNumber largest_acked_sequence_number_;
+
+ // Track the largest sequence number outstanding when a CWND cutback occurs.
+ QuicPacketSequenceNumber largest_sent_at_last_cutback_;
+
+ // Congestion window in packets.
+ QuicTcpCongestionWindow congestion_window_;
+
+ // Congestion window before the last loss event or RTO.
+ QuicByteCount previous_congestion_window_;
+
+ // Slow start congestion window in packets, aka ssthresh.
+ QuicTcpCongestionWindow slowstart_threshold_;
+
+ // Slow start threshold before the last loss event or RTO.
+ QuicTcpCongestionWindow previous_slowstart_threshold_;
+
+ // Whether the last loss event caused us to exit slowstart.
+ // Used for stats collection of slowstart_packets_lost
+ bool last_cutback_exited_slowstart_;
+
+ // Maximum number of outstanding packets for tcp.
+ QuicTcpCongestionWindow max_tcp_congestion_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TcpCubicSender);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_H_
diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc
new file mode 100644
index 0000000..851c1a1
--- /dev/null
+++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc
@@ -0,0 +1,699 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/tcp_cubic_sender.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::make_pair;
+using std::min;
+
+namespace net {
+namespace test {
+
+const int64 kInitialCongestionWindow = 10;
+const uint32 kDefaultWindowTCP = kInitialCongestionWindow * kDefaultTCPMSS;
+
+// TODO(ianswett): Remove 10000 once b/10075719 is fixed.
+const QuicTcpCongestionWindow kDefaultMaxCongestionWindowTCP = 10000;
+
+class TcpCubicSenderPeer : public TcpCubicSender {
+ public:
+ TcpCubicSenderPeer(const QuicClock* clock,
+ bool reno,
+ QuicTcpCongestionWindow max_tcp_congestion_window)
+ : TcpCubicSender(
+ clock, &rtt_stats_, reno, max_tcp_congestion_window, &stats_) {
+ }
+
+ QuicTcpCongestionWindow congestion_window() {
+ return congestion_window_;
+ }
+
+ QuicTcpCongestionWindow slowstart_threshold() {
+ return slowstart_threshold_;
+ }
+
+ const HybridSlowStart& hybrid_slow_start() const {
+ return hybrid_slow_start_;
+ }
+
+ RttStats rtt_stats_;
+ QuicConnectionStats stats_;
+
+ using TcpCubicSender::SendWindow;
+};
+
+class TcpCubicSenderTest : public ::testing::Test {
+ protected:
+ TcpCubicSenderTest()
+ : one_ms_(QuicTime::Delta::FromMilliseconds(1)),
+ sender_(new TcpCubicSenderPeer(&clock_, true,
+ kDefaultMaxCongestionWindowTCP)),
+ receiver_(new TcpReceiver()),
+ sequence_number_(1),
+ acked_sequence_number_(0),
+ bytes_in_flight_(0) {
+ standard_packet_.bytes_sent = kDefaultTCPMSS;
+ }
+
+ int SendAvailableSendWindow() {
+ // Send as long as TimeUntilSend returns Zero.
+ int packets_sent = 0;
+ bool can_send = sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero();
+ while (can_send) {
+ sender_->OnPacketSent(clock_.Now(), bytes_in_flight_, sequence_number_++,
+ kDefaultTCPMSS, HAS_RETRANSMITTABLE_DATA);
+ ++packets_sent;
+ bytes_in_flight_ += kDefaultTCPMSS;
+ can_send = sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero();
+ }
+ return packets_sent;
+ }
+
+ // Normal is that TCP acks every other segment.
+ void AckNPackets(int n) {
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(60),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ SendAlgorithmInterface::CongestionVector acked_packets;
+ SendAlgorithmInterface::CongestionVector lost_packets;
+ for (int i = 0; i < n; ++i) {
+ ++acked_sequence_number_;
+ acked_packets.push_back(
+ make_pair(acked_sequence_number_, standard_packet_));
+ }
+ sender_->OnCongestionEvent(
+ true, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ clock_.AdvanceTime(one_ms_);
+ }
+
+ void LoseNPackets(int n) {
+ SendAlgorithmInterface::CongestionVector acked_packets;
+ SendAlgorithmInterface::CongestionVector lost_packets;
+ for (int i = 0; i < n; ++i) {
+ ++acked_sequence_number_;
+ lost_packets.push_back(
+ make_pair(acked_sequence_number_, standard_packet_));
+ }
+ sender_->OnCongestionEvent(
+ false, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= n * kDefaultTCPMSS;
+ }
+
+ // Does not increment acked_sequence_number_.
+ void LosePacket(QuicPacketSequenceNumber sequence_number) {
+ SendAlgorithmInterface::CongestionVector acked_packets;
+ SendAlgorithmInterface::CongestionVector lost_packets;
+ lost_packets.push_back(
+ make_pair(sequence_number, standard_packet_));
+ sender_->OnCongestionEvent(
+ false, bytes_in_flight_, acked_packets, lost_packets);
+ bytes_in_flight_ -= kDefaultTCPMSS;
+ }
+
+ const QuicTime::Delta one_ms_;
+ MockClock clock_;
+ scoped_ptr<TcpCubicSenderPeer> sender_;
+ scoped_ptr<TcpReceiver> receiver_;
+ QuicPacketSequenceNumber sequence_number_;
+ QuicPacketSequenceNumber acked_sequence_number_;
+ QuicByteCount bytes_in_flight_;
+ TransmissionInfo standard_packet_;
+};
+
+TEST_F(TcpCubicSenderTest, SimpleSender) {
+ // At startup make sure we are at the default.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // And that window is un-affected.
+ EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow());
+
+ // Fill the send window with data, then verify that we can't send.
+ SendAvailableSendWindow();
+ EXPECT_FALSE(sender_->TimeUntilSend(clock_.Now(),
+ sender_->GetCongestionWindow(),
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+}
+
+TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) {
+ // Send exactly 10 packets and ensure the CWND ends at 14 packets.
+ const int kNumberOfAcks = 5;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ SendAvailableSendWindow();
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(2);
+ }
+ QuicByteCount bytes_to_send = sender_->SendWindow();
+ // It's expected 2 acks will arrive when the bytes_in_flight are greater than
+ // half the CWND.
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * 2,
+ bytes_to_send);
+}
+
+TEST_F(TcpCubicSenderTest, ExponentialSlowStart) {
+ const int kNumberOfAcks = 20;
+ // At startup make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+ // Make sure we can send.
+ EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(),
+ 0,
+ HAS_RETRANSMITTABLE_DATA).IsZero());
+
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ QuicByteCount bytes_to_send = sender_->SendWindow();
+ EXPECT_EQ(kDefaultWindowTCP + kDefaultTCPMSS * 2 * kNumberOfAcks,
+ bytes_to_send);
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartAckTrain) {
+ sender_->SetNumEmulatedConnections(1);
+ EXPECT_EQ(kDefaultMaxCongestionWindowTCP * kDefaultTCPMSS,
+ sender_->GetSlowStartThreshold());
+
+ // Make sure that we fall out of slow start when we send ACK train longer
+ // than half the RTT, in this test case 30ms, which is more than 30 calls to
+ // Ack2Packets in one round.
+ // Since we start at 10 packet first round will be 5 second round 10 etc
+ // Hence we should pass 30 at 65 = 5 + 10 + 20 + 30
+ const int kNumberOfAcks = 65;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ QuicByteCount expected_send_window =
+ kDefaultWindowTCP + (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // We should now have fallen out of slow start.
+ // Testing Reno phase.
+ // We should need 140(65*2+10) ACK:ed packets before increasing window by
+ // one.
+ for (int i = 0; i < 69; ++i) {
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ QuicByteCount expected_ss_tresh = expected_send_window;
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ EXPECT_EQ(expected_ss_tresh, sender_->GetSlowStartThreshold());
+ EXPECT_EQ(140u, sender_->slowstart_threshold());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+ EXPECT_EQ(2 * kDefaultTCPMSS, sender_->GetCongestionWindow());
+ EXPECT_EQ(expected_send_window / 2 / kDefaultTCPMSS,
+ sender_->slowstart_threshold());
+
+ // Now revert the RTO and ensure the CWND and slowstart threshold revert.
+ sender_->RevertRetransmissionTimeout();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ EXPECT_EQ(140u, sender_->slowstart_threshold());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) {
+ sender_->SetNumEmulatedConnections(1);
+ const int kNumberOfAcks = 10;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Lose a packet to exit slow start.
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Testing Reno phase.
+ // We need to ack half of the pending packet before we can send again.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ AckNPackets(number_of_packets_in_window);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // We need to ack every packet in the window before we exit recovery.
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ for (size_t i = 0; i < number_of_packets_in_window - 2; ++i) {
+ AckNPackets(1);
+ SendAvailableSendWindow();
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ AckNPackets(1);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Now RTO and ensure slow start gets reset.
+ EXPECT_TRUE(sender_->hybrid_slow_start().started());
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_FALSE(sender_->hybrid_slow_start().started());
+}
+
+TEST_F(TcpCubicSenderTest, NoPRRWhenLessThanOnePacketInFlight) {
+ SendAvailableSendWindow();
+ LoseNPackets(kInitialCongestionWindow - 1);
+ AckNPackets(1);
+ // PRR will allow 2 packets for every ack during recovery.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+ // Simulate abandoning all packets by supplying a bytes_in_flight of 0.
+ // PRR should now allow a packet to be sent, even though prr's state
+ // variables believe it has sent enough packets.
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA));
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartPacketLossPRR) {
+ sender_->SetNumEmulatedConnections(1);
+ // Test based on the first example in RFC6937.
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Testing TCP proportional rate reduction.
+ // We should send one packet for every two received acks over the remaining
+ // 18 outstanding packets.
+ size_t number_of_packets_in_window = expected_send_window / kDefaultTCPMSS;
+ // The number of packets before we exit recovery is the original CWND minus
+ // the packet that has been lost and the one which triggered the loss.
+ size_t remaining_packets_in_recovery = number_of_packets_in_window * 2 - 1;
+ for (size_t i = 0; i < remaining_packets_in_recovery - 1; i += 2) {
+ AckNPackets(2);
+ EXPECT_TRUE(sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero());
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // We need to ack another window before we increase CWND by 1.
+ for (size_t i = 0; i < number_of_packets_in_window; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ AckNPackets(1);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartBurstPacketLossPRR) {
+ sender_->SetNumEmulatedConnections(1);
+ // Test based on the second example in RFC6937, though we also implement
+ // forward acknowledgements, so the first two incoming acks will trigger
+ // PRR immediately.
+ // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Ack a packet with a 15 packet gap, losing 13 of them due to FACK.
+ LoseNPackets(13);
+ // Immediately after the loss, ensure at least one packet can be sent.
+ // Losses without subsequent acks can occur with timer based loss detection.
+ EXPECT_TRUE(sender_->TimeUntilSend(
+ clock_.Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA).IsZero());
+ AckNPackets(1);
+
+ // We should now have fallen out of slow start.
+ // We expect window to be cut in half by Reno.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Only 2 packets should be allowed to be sent, per PRR-SSRB
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // Ack the next packet, which triggers another loss.
+ LoseNPackets(1);
+ AckNPackets(1);
+
+ // Send 2 packets to simulate PRR-SSRB.
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ AckNPackets(1);
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ AckNPackets(1);
+ EXPECT_EQ(2, SendAvailableSendWindow());
+
+ // The window should not have changed.
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Exit recovery and return to sending at the new rate.
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ AckNPackets(1);
+ EXPECT_EQ(1, SendAvailableSendWindow());
+ }
+}
+
+TEST_F(TcpCubicSenderTest, RTOCongestionWindowAndRevert) {
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+ EXPECT_EQ(10000u, sender_->slowstart_threshold());
+
+ // Expect the window to decrease to the minimum once the RTO fires
+ // and slow start threshold to be set to 1/2 of the CWND.
+ sender_->OnRetransmissionTimeout(true);
+ EXPECT_EQ(2 * kDefaultTCPMSS, sender_->SendWindow());
+ EXPECT_EQ(5u, sender_->slowstart_threshold());
+
+ // Now repair the RTO and ensure the slowstart threshold reverts.
+ sender_->RevertRetransmissionTimeout();
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+ EXPECT_EQ(10000u, sender_->slowstart_threshold());
+}
+
+TEST_F(TcpCubicSenderTest, RTOCongestionWindowNoRetransmission) {
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+
+ // Expect the window to remain unchanged if the RTO fires but no
+ // packets are retransmitted.
+ sender_->OnRetransmissionTimeout(false);
+ EXPECT_EQ(kDefaultWindowTCP, sender_->SendWindow());
+}
+
+TEST_F(TcpCubicSenderTest, RetransmissionDelay) {
+ const int64 kRttMs = 10;
+ const int64 kDeviationMs = 3;
+ EXPECT_EQ(QuicTime::Delta::Zero(), sender_->RetransmissionDelay());
+
+ sender_->rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(kRttMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+
+ // Initial value is to set the median deviation to half of the initial
+ // rtt, the median in then multiplied by a factor of 4 and finally the
+ // smoothed rtt is added which is the initial rtt.
+ QuicTime::Delta expected_delay =
+ QuicTime::Delta::FromMilliseconds(kRttMs + kRttMs / 2 * 4);
+ EXPECT_EQ(expected_delay, sender_->RetransmissionDelay());
+
+ for (int i = 0; i < 100; ++i) {
+ // Run to make sure that we converge.
+ sender_->rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+ sender_->rtt_stats_.UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs),
+ QuicTime::Delta::Zero(), clock_.Now());
+ }
+ expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4);
+
+ EXPECT_NEAR(kRttMs, sender_->rtt_stats_.SmoothedRtt().ToMilliseconds(), 1);
+ EXPECT_NEAR(expected_delay.ToMilliseconds(),
+ sender_->RetransmissionDelay().ToMilliseconds(),
+ 1);
+ EXPECT_EQ(static_cast<int64>(
+ sender_->GetCongestionWindow() * kNumMicrosPerSecond /
+ sender_->rtt_stats_.SmoothedRtt().ToMicroseconds()),
+ sender_->BandwidthEstimate().ToBytesPerSecond());
+}
+
+TEST_F(TcpCubicSenderTest, SlowStartMaxSendWindow) {
+ const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
+ const int kNumberOfAcks = 100;
+ sender_.reset(
+ new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP));
+
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ QuicByteCount expected_send_window =
+ kMaxCongestionWindowTCP * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) {
+ const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
+ const int kNumberOfAcks = 1000;
+ sender_.reset(
+ new TcpCubicSenderPeer(&clock_, true, kMaxCongestionWindowTCP));
+
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ // Make sure we fall out of slow start.
+ LoseNPackets(1);
+
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+
+ QuicByteCount expected_send_window =
+ kMaxCongestionWindowTCP * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) {
+ const QuicTcpCongestionWindow kMaxCongestionWindowTCP = 50;
+ // Set to 10000 to compensate for small cubic alpha.
+ const int kNumberOfAcks = 10000;
+
+ sender_.reset(
+ new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP));
+
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ // Make sure we fall out of slow start.
+ LoseNPackets(1);
+
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+
+ QuicByteCount expected_send_window =
+ kMaxCongestionWindowTCP * kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, MultipleLossesInOneWindow) {
+ SendAvailableSendWindow();
+ const QuicByteCount initial_window = sender_->GetCongestionWindow();
+ LosePacket(acked_sequence_number_ + 1);
+ const QuicByteCount post_loss_window = sender_->GetCongestionWindow();
+ EXPECT_GT(initial_window, post_loss_window);
+ LosePacket(acked_sequence_number_ + 3);
+ EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
+ LosePacket(sequence_number_ - 1);
+ EXPECT_EQ(post_loss_window, sender_->GetCongestionWindow());
+
+ // Lose a later packet and ensure the window decreases.
+ LosePacket(sequence_number_);
+ EXPECT_GT(post_loss_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, DontTrackAckPackets) {
+ // Send a packet with no retransmittable data, and ensure it's not tracked.
+ EXPECT_FALSE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ sequence_number_++, kDefaultTCPMSS,
+ NO_RETRANSMITTABLE_DATA));
+
+ // Send a data packet with retransmittable data, and ensure it is tracked.
+ EXPECT_TRUE(sender_->OnPacketSent(clock_.Now(), bytes_in_flight_,
+ sequence_number_++, kDefaultTCPMSS,
+ HAS_RETRANSMITTABLE_DATA));
+}
+
+TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) {
+ QuicTcpCongestionWindow congestion_window = sender_->congestion_window();
+ QuicConfig config;
+ QuicConfigPeer::SetReceivedInitialWindow(&config, 2 * congestion_window);
+
+ sender_->SetFromConfig(config, true);
+ EXPECT_EQ(2 * congestion_window, sender_->congestion_window());
+
+ // Verify that kCOPT: kIW10 forces the congestion window to the
+ // default of 10 regardless of ReceivedInitialWindow.
+ QuicTagVector options;
+ options.push_back(kIW10);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ sender_->SetFromConfig(config, true);
+ EXPECT_EQ(congestion_window, sender_->congestion_window());
+}
+
+TEST_F(TcpCubicSenderTest, 2ConnectionCongestionAvoidanceAtEndOfRecovery) {
+ sender_->SetNumEmulatedConnections(2);
+ // Ack 10 packets in 5 acks to raise the CWND to 20.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start, and window should be cut in
+ // half by Reno. New cwnd should be 10.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // No congestion window growth should occur in recovery phase, i.e., until the
+ // currently outstanding 20 packets are acked.
+ for (int i = 0; i < 10; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ EXPECT_TRUE(sender_->InRecovery());
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+ EXPECT_FALSE(sender_->InRecovery());
+
+ // Out of recovery now. Congestion window should not grow for half an RTT.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // Next ack will be the 5th and should increase congestion window by 1MSS.
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ for (int i = 0; i < 2; ++i) {
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Next ack should cause congestion window to grow by 1MSS.
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+TEST_F(TcpCubicSenderTest, 1ConnectionCongestionAvoidanceAtEndOfRecovery) {
+ sender_->SetNumEmulatedConnections(1);
+ // Ack 10 packets in 5 acks to raise the CWND to 20.
+ const int kNumberOfAcks = 5;
+ for (int i = 0; i < kNumberOfAcks; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ }
+ SendAvailableSendWindow();
+ QuicByteCount expected_send_window = kDefaultWindowTCP +
+ (kDefaultTCPMSS * 2 * kNumberOfAcks);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ LoseNPackets(1);
+
+ // We should now have fallen out of slow start, and window should be cut in
+ // half by Reno. New cwnd should be 10.
+ expected_send_window /= 2;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+
+ // No congestion window growth should occur in recovery phase, i.e.,
+ // until the currently outstanding 20 packets are acked.
+ for (int i = 0; i < 10; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ EXPECT_TRUE(sender_->InRecovery());
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+ EXPECT_FALSE(sender_->InRecovery());
+
+ // Out of recovery now. Congestion window should not grow during RTT.
+ for (int i = 0; i < 4; ++i) {
+ // Send our full send window.
+ SendAvailableSendWindow();
+ AckNPackets(2);
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+ }
+
+ // Next ack should cause congestion window to grow by 1MSS.
+ AckNPackets(2);
+ expected_send_window += kDefaultTCPMSS;
+ EXPECT_EQ(expected_send_window, sender_->GetCongestionWindow());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/tcp_loss_algorithm.cc b/net/quic/congestion_control/tcp_loss_algorithm.cc
new file mode 100644
index 0000000..557681e
--- /dev/null
+++ b/net/quic/congestion_control/tcp_loss_algorithm.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace {
+
+// TCP retransmits after 3 nacks.
+static const size_t kNumberOfNacksBeforeRetransmission = 3;
+
+// How many RTTs the algorithm waits before determining a packet is lost due
+// to early retransmission.
+static const double kEarlyRetransmitLossDelayMultiplier = 1.25;
+
+}
+
+TCPLossAlgorithm::TCPLossAlgorithm()
+ : loss_detection_timeout_(QuicTime::Zero()) { }
+
+LossDetectionType TCPLossAlgorithm::GetLossDetectionType() const {
+ return kNack;
+}
+
+// Uses nack counts to decide when packets are lost.
+SequenceNumberSet TCPLossAlgorithm::DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) {
+ SequenceNumberSet lost_packets;
+ loss_detection_timeout_ = QuicTime::Zero();
+ QuicTime::Delta loss_delay =
+ rtt_stats.SmoothedRtt().Multiply(kEarlyRetransmitLossDelayMultiplier);
+ QuicPacketSequenceNumber sequence_number = unacked_packets.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin();
+ it != unacked_packets.end() && sequence_number <= largest_observed;
+ ++it, ++sequence_number) {
+ if (!it->in_flight) {
+ continue;
+ }
+
+ LOG_IF(DFATAL, it->nack_count == 0)
+ << "All packets less than largest observed should have been nacked.";
+ if (it->nack_count >= kNumberOfNacksBeforeRetransmission) {
+ lost_packets.insert(sequence_number);
+ continue;
+ }
+
+ // Only early retransmit(RFC5827) when the last packet gets acked and
+ // there are retransmittable packets in flight.
+ // This also implements a timer-protected variant of FACK.
+ if (it->retransmittable_frames &&
+ unacked_packets.largest_sent_packet() == largest_observed) {
+ // Early retransmit marks the packet as lost once 1.25RTTs have passed
+ // since the packet was sent and otherwise sets an alarm.
+ if (time >= it->sent_time.Add(loss_delay)) {
+ lost_packets.insert(sequence_number);
+ } else {
+ // Set the timeout for the earliest retransmittable packet where early
+ // retransmit applies.
+ loss_detection_timeout_ = it->sent_time.Add(loss_delay);
+ break;
+ }
+ }
+ }
+
+ return lost_packets;
+}
+
+QuicTime TCPLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/tcp_loss_algorithm.h b/net/quic/congestion_control/tcp_loss_algorithm.h
new file mode 100644
index 0000000..5f0bcfb
--- /dev/null
+++ b/net/quic/congestion_control/tcp_loss_algorithm.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
+#define NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+// Class which implement's TCP's approach of detecting loss when 3 nacks have
+// been received for a packet. Also implements TCP's early retransmit(RFC5827).
+class NET_EXPORT_PRIVATE TCPLossAlgorithm : public LossDetectionInterface {
+ public:
+ TCPLossAlgorithm();
+ virtual ~TCPLossAlgorithm() {}
+
+ virtual LossDetectionType GetLossDetectionType() const OVERRIDE;
+
+ // Uses nack counts to decide when packets are lost.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) OVERRIDE;
+
+ // Returns a non-zero value when the early retransmit timer is active.
+ virtual QuicTime GetLossTimeout() const OVERRIDE;
+
+ private:
+ QuicTime loss_detection_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(TCPLossAlgorithm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TCP_LOSS_ALGORITHM_H_
diff --git a/net/quic/congestion_control/tcp_loss_algorithm_test.cc b/net/quic/congestion_control/tcp_loss_algorithm_test.cc
new file mode 100644
index 0000000..988f427
--- /dev/null
+++ b/net/quic/congestion_control/tcp_loss_algorithm_test.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/tcp_loss_algorithm.h"
+#include "net/quic/quic_unacked_packet_map.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class TcpLossAlgorithmTest : public ::testing::Test {
+ protected:
+ TcpLossAlgorithmTest()
+ : unacked_packets_() {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ nullptr, 0, new RetransmittableFrames());
+ unacked_packets_.AddPacket(packet);
+ unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true);
+ }
+
+ void VerifyLosses(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber* losses_expected,
+ size_t num_losses) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_.DetectLostPackets(
+ unacked_packets_, clock_.Now(), largest_observed, rtt_stats_);
+ EXPECT_EQ(num_losses, lost_packets.size());
+ for (size_t i = 0; i < num_losses; ++i) {
+ EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ TCPLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1Packet) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // No loss on one ack.
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, nullptr, 0);
+ // No loss on two acks.
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.NackPacket(1, 2);
+ VerifyLosses(3, nullptr, 0);
+ // Loss on three acks.
+ unacked_packets_.RemoveFromInFlight(4);
+ unacked_packets_.NackPacket(1, 3);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// A stretch ack is an ack that covers more than 1 packet of previously
+// unacknowledged data.
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketWith1StretchAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // Nack the first packet 3 times in a single StretchAck.
+ unacked_packets_.NackPacket(1, 3);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.RemoveFromInFlight(4);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+// Ack a packet 3 packets ahead, causing a retransmit.
+TEST_F(TcpLossAlgorithmTest, NackRetransmit1PacketSingleAck) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // Nack the first packet 3 times in an AckFrame with three missing packets.
+ unacked_packets_.NackPacket(1, 3);
+ unacked_packets_.NackPacket(2, 2);
+ unacked_packets_.NackPacket(3, 1);
+ unacked_packets_.RemoveFromInFlight(4);
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(4, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, EarlyRetransmit1Packet) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, nullptr, 0);
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(1.25)),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.latest_rtt().Multiply(1.25));
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(2, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, EarlyRetransmitAllPackets) {
+ const size_t kNumSentPackets = 5;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ // Advance the time 1/4 RTT between 3 and 4.
+ if (i == 3) {
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ }
+ }
+
+ // Early retransmit when the final packet gets acked and 1.25 RTTs have
+ // elapsed since the packets were sent.
+ unacked_packets_.RemoveFromInFlight(kNumSentPackets);
+ // This simulates a single ack following multiple missing packets with FACK.
+ for (size_t i = 1; i < kNumSentPackets; ++i) {
+ unacked_packets_.NackPacket(i, kNumSentPackets - i);
+ }
+ QuicPacketSequenceNumber lost[] = { 1, 2 };
+ VerifyLosses(kNumSentPackets, lost, arraysize(lost));
+ // The time has already advanced 1/4 an RTT, so ensure the timeout is set
+ // 1.25 RTTs after the earliest pending packet(3), not the last(4).
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt()),
+ loss_algorithm_.GetLossTimeout());
+
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ QuicPacketSequenceNumber lost2[] = { 1, 2, 3 };
+ VerifyLosses(kNumSentPackets, lost2, arraysize(lost2));
+ EXPECT_EQ(clock_.Now().Add(rtt_stats_.SmoothedRtt().Multiply(0.25)),
+ loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost3[] = { 1, 2, 3, 4 };
+ VerifyLosses(kNumSentPackets, lost3, arraysize(lost3));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TcpLossAlgorithmTest, DontEarlyRetransmitNeuteredPacket) {
+ const size_t kNumSentPackets = 2;
+ // Transmit 2 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ // Neuter packet 1.
+ unacked_packets_.RemoveRetransmittability(1);
+
+ // Early retransmit when the final packet gets acked and the first is nacked.
+ unacked_packets_.IncreaseLargestObserved(2);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.NackPacket(1, 1);
+ VerifyLosses(2, nullptr, 0);
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/tcp_receiver.cc b/net/quic/congestion_control/tcp_receiver.cc
new file mode 100644
index 0000000..a8c5489
--- /dev/null
+++ b/net/quic/congestion_control/tcp_receiver.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
+
+namespace net {
+
+// Originally 64K bytes, but increased it to 256K to support higher bitrates.
+// static
+const QuicByteCount TcpReceiver::kReceiveWindowTCP = 256000;
+
+TcpReceiver::TcpReceiver()
+ : receive_window_(kReceiveWindowTCP) {
+}
+
+bool TcpReceiver::GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback) {
+ feedback->type = kTCP;
+ feedback->tcp.receive_window = receive_window_;
+ return true;
+}
+
+void TcpReceiver::RecordIncomingPacket(QuicByteCount bytes,
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime timestamp) {
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/tcp_receiver.h b/net/quic/congestion_control/tcp_receiver.h
new file mode 100644
index 0000000..a5e5b1b
--- /dev/null
+++ b/net/quic/congestion_control/tcp_receiver.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TCP receiver side congestion algorithm, emulates the behaviour of TCP.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_TCP_RECEIVER_H_
+#define NET_QUIC_CONGESTION_CONTROL_TCP_RECEIVER_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE TcpReceiver : public ReceiveAlgorithmInterface {
+ public:
+ TcpReceiver();
+
+ // Size of the (currently fixed) receive window.
+ static const QuicByteCount kReceiveWindowTCP;
+
+ // Start implementation of SendAlgorithmInterface.
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback) OVERRIDE;
+
+ virtual void RecordIncomingPacket(QuicByteCount bytes,
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime timestamp) OVERRIDE;
+
+ private:
+ QuicByteCount receive_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TcpReceiver);
+};
+
+} // namespace net
+#endif // NET_QUIC_CONGESTION_CONTROL_TCP_RECEIVER_H_
diff --git a/net/quic/congestion_control/tcp_receiver_test.cc b/net/quic/congestion_control/tcp_receiver_test.cc
new file mode 100644
index 0000000..e074fdf
--- /dev/null
+++ b/net/quic/congestion_control/tcp_receiver_test.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class QuicTcpReceiverTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ receiver_.reset(new TcpReceiver());
+ }
+ scoped_ptr<TcpReceiver> receiver_;
+};
+
+TEST_F(QuicTcpReceiverTest, SimpleReceiver) {
+ QuicCongestionFeedbackFrame feedback;
+ QuicTime timestamp(QuicTime::Zero());
+ receiver_->RecordIncomingPacket(1, 1, timestamp);
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ EXPECT_EQ(kTCP, feedback.type);
+ EXPECT_EQ(256000u, feedback.tcp.receive_window);
+ receiver_->RecordIncomingPacket(1, 2, timestamp);
+ ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback));
+ EXPECT_EQ(kTCP, feedback.type);
+ EXPECT_EQ(256000u, feedback.tcp.receive_window);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/congestion_control/time_loss_algorithm.cc b/net/quic/congestion_control/time_loss_algorithm.cc
new file mode 100644
index 0000000..3a7b42d
--- /dev/null
+++ b/net/quic/congestion_control/time_loss_algorithm.cc
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace {
+
+// The minimum delay before a packet will be considered lost,
+// regardless of SRTT. Half of the minimum TLP, since the loss algorithm only
+// triggers when a nack has been receieved for the packet.
+static const size_t kMinLossDelayMs = 5;
+
+// How many RTTs the algorithm waits before determining a packet is lost.
+static const double kLossDelayMultiplier = 1.25;
+
+} // namespace
+
+TimeLossAlgorithm::TimeLossAlgorithm()
+ : loss_detection_timeout_(QuicTime::Zero()) { }
+
+LossDetectionType TimeLossAlgorithm::GetLossDetectionType() const {
+ return kTime;
+}
+
+SequenceNumberSet TimeLossAlgorithm::DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) {
+ SequenceNumberSet lost_packets;
+ loss_detection_timeout_ = QuicTime::Zero();
+ QuicTime::Delta loss_delay = QuicTime::Delta::Max(
+ QuicTime::Delta::FromMilliseconds(kMinLossDelayMs),
+ QuicTime::Delta::Max(rtt_stats.SmoothedRtt(), rtt_stats.latest_rtt())
+ .Multiply(kLossDelayMultiplier));
+
+ QuicPacketSequenceNumber sequence_number = unacked_packets.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets.begin();
+ it != unacked_packets.end() && sequence_number <= largest_observed;
+ ++it, ++sequence_number) {
+ if (!it->in_flight) {
+ continue;
+ }
+ LOG_IF(DFATAL, it->nack_count == 0)
+ << "All packets less than largest observed should have been nacked.";
+
+ // Packets are sent in order, so break when we haven't waited long enough
+ // to lose any more packets and leave the loss_time_ set for the timeout.
+ QuicTime when_lost = it->sent_time.Add(loss_delay);
+ if (time < when_lost) {
+ loss_detection_timeout_ = when_lost;
+ break;
+ }
+ lost_packets.insert(sequence_number);
+ }
+
+ return lost_packets;
+}
+
+// loss_time_ is updated in DetectLostPackets, which must be called every time
+// an ack is received or the timeout expires.
+QuicTime TimeLossAlgorithm::GetLossTimeout() const {
+ return loss_detection_timeout_;
+}
+
+} // namespace net
diff --git a/net/quic/congestion_control/time_loss_algorithm.h b/net/quic/congestion_control/time_loss_algorithm.h
new file mode 100644
index 0000000..ae37e1e
--- /dev/null
+++ b/net/quic/congestion_control/time_loss_algorithm.h
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
+#define NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
+
+#include <algorithm>
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+// A loss detection algorithm which avoids spurious losses and retransmissions
+// by waiting 1.25 RTTs after a packet was sent instead of nack count.
+class NET_EXPORT_PRIVATE TimeLossAlgorithm : public LossDetectionInterface {
+ public:
+ TimeLossAlgorithm();
+ virtual ~TimeLossAlgorithm() {}
+
+ virtual LossDetectionType GetLossDetectionType() const OVERRIDE;
+
+ // Declares pending packets less than the largest observed lost when it has
+ // been 1.25 RTT since they were sent. Packets larger than the largest
+ // observed are retransmitted via TLP.
+ virtual SequenceNumberSet DetectLostPackets(
+ const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats) OVERRIDE;
+
+ // Returns the time the next packet will be lost, or zero if there
+ // are no nacked pending packets outstanding.
+ // TODO(ianswett): Ideally the RTT variance and the RTT would be used to
+ // determine the time a packet is considered lost.
+ // TODO(ianswett): Consider using Max(1.25 * srtt, 1.125 * last_rtt).
+ virtual QuicTime GetLossTimeout() const OVERRIDE;
+
+ private:
+ QuicTime loss_detection_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeLossAlgorithm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CONGESTION_CONTROL_TIME_LOSS_ALGORITHM_H_
diff --git a/net/quic/congestion_control/time_loss_algorithm_test.cc b/net/quic/congestion_control/time_loss_algorithm_test.cc
new file mode 100644
index 0000000..4139019
--- /dev/null
+++ b/net/quic/congestion_control/time_loss_algorithm_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/time_loss_algorithm.h"
+#include "net/quic/quic_unacked_packet_map.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class TimeLossAlgorithmTest : public ::testing::Test {
+ protected:
+ TimeLossAlgorithmTest()
+ : unacked_packets_() {
+ rtt_stats_.UpdateRtt(QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ clock_.Now());
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ SerializedPacket packet(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ nullptr, 0, new RetransmittableFrames());
+ unacked_packets_.AddPacket(packet);
+ unacked_packets_.SetSent(sequence_number, clock_.Now(), 1000, true);
+ }
+
+ void VerifyLosses(QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber* losses_expected,
+ size_t num_losses) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_.DetectLostPackets(
+ unacked_packets_, clock_.Now(), largest_observed, rtt_stats_);
+ EXPECT_EQ(num_losses, lost_packets.size());
+ for (size_t i = 0; i < num_losses; ++i) {
+ EXPECT_TRUE(ContainsKey(lost_packets, losses_expected[i]));
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ TimeLossAlgorithm loss_algorithm_;
+ RttStats rtt_stats_;
+ MockClock clock_;
+};
+
+TEST_F(TimeLossAlgorithmTest, NoLossFor500Nacks) {
+ const size_t kNumSentPackets = 5;
+ // Transmit 5 packets.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ unacked_packets_.RemoveFromInFlight(2);
+ for (size_t i = 1; i < 500; ++i) {
+ unacked_packets_.NackPacket(1, i);
+ VerifyLosses(2, nullptr, 0);
+ }
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(1.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+}
+
+TEST_F(TimeLossAlgorithmTest, NoLossUntilTimeout) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1));
+ }
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ unacked_packets_.NackPacket(1, 1);
+ unacked_packets_.RemoveFromInFlight(2);
+ VerifyLosses(2, nullptr, 0);
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+ unacked_packets_.NackPacket(1, 5);
+ VerifyLosses(2, nullptr, 0);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost[] = { 1 };
+ VerifyLosses(2, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TimeLossAlgorithmTest, NoLossWithoutNack) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at 1/10th an RTT interval.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.1));
+ }
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost without a nack.
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyLosses(1, nullptr, 0);
+ // The timer should still not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ VerifyLosses(1, nullptr, 0);
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ VerifyLosses(1, nullptr, 0);
+
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+TEST_F(TimeLossAlgorithmTest, MultipleLossesAtOnce) {
+ const size_t kNumSentPackets = 10;
+ // Transmit 10 packets at once and then go forward an RTT.
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt());
+ // Expect the timer to not be set.
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+ // The packet should not be lost until 1.25 RTTs pass.
+ for (size_t i = 1; i < kNumSentPackets; ++i) {
+ unacked_packets_.NackPacket(i, 1);
+ }
+ unacked_packets_.RemoveFromInFlight(10);
+ VerifyLosses(10, nullptr, 0);
+ // Expect the timer to be set to 0.25 RTT's in the future.
+ EXPECT_EQ(rtt_stats_.SmoothedRtt().Multiply(0.25),
+ loss_algorithm_.GetLossTimeout().Subtract(clock_.Now()));
+ clock_.AdvanceTime(rtt_stats_.SmoothedRtt().Multiply(0.25));
+ QuicPacketSequenceNumber lost[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ VerifyLosses(10, lost, arraysize(lost));
+ EXPECT_EQ(QuicTime::Zero(), loss_algorithm_.GetLossTimeout());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/aead_base_decrypter.h b/net/quic/crypto/aead_base_decrypter.h
new file mode 100644
index 0000000..6257409
--- /dev/null
+++ b/net/quic/crypto/aead_base_decrypter.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_decrypter.h"
+
+#if defined(USE_OPENSSL)
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+#else
+#include <pkcs11t.h>
+#include <seccomon.h>
+typedef struct PK11SymKeyStr PK11SymKey;
+typedef SECStatus (*PK11_DecryptFunction)(
+ PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
+ unsigned char* out, unsigned int* outLen, unsigned int maxLen,
+ const unsigned char* enc, unsigned encLen);
+#endif
+
+namespace net {
+
+// AeadBaseDecrypter is the base class of AEAD QuicDecrypter subclasses.
+class NET_EXPORT_PRIVATE AeadBaseDecrypter : public QuicDecrypter {
+ public:
+#if defined(USE_OPENSSL)
+ AeadBaseDecrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#else
+ AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_DecryptFunction pk11_decrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#endif
+ virtual ~AeadBaseDecrypter();
+
+ // QuicDecrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Decrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) OVERRIDE;
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext) OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_prefix_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNoncePrefixSize = 4;
+
+#if !defined(USE_OPENSSL)
+ struct AeadParams {
+ unsigned int len;
+ union {
+ CK_GCM_PARAMS gcm_params;
+#if !defined(USE_NSS)
+ // USE_NSS means we are using system NSS rather than our copy of NSS.
+ // The system NSS <pkcs11n.h> header doesn't define this type yet.
+ CK_NSS_AEAD_PARAMS nss_aead_params;
+#endif
+ } data;
+ };
+
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const = 0;
+#endif // !defined(USE_OPENSSL)
+
+ private:
+#if defined(USE_OPENSSL)
+ const EVP_AEAD* const aead_alg_;
+#else
+ const CK_MECHANISM_TYPE aead_mechanism_;
+ const PK11_DecryptFunction pk11_decrypt_;
+#endif
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_prefix_size_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The nonce prefix.
+ unsigned char nonce_prefix_[kMaxNoncePrefixSize];
+
+#if defined(USE_OPENSSL)
+ ScopedEVPAEADCtx ctx_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(AeadBaseDecrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AEAD_BASE_DECRYPTER_H_
diff --git a/net/quic/crypto/aead_base_decrypter_nss.cc b/net/quic/crypto/aead_base_decrypter_nss.cc
new file mode 100644
index 0000000..2401222
--- /dev/null
+++ b/net/quic/crypto/aead_base_decrypter_nss.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+AeadBaseDecrypter::AeadBaseDecrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_DecryptFunction pk11_decrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_mechanism_(aead_mechanism),
+ pk11_decrypt_(pk11_decrypt),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ uint8* output,
+ size_t* output_length) {
+ if (ciphertext.length() < auth_tag_size_ ||
+ nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+ // NSS 3.14.x incorrectly requires an output buffer at least as long as
+ // the ciphertext (NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id= 853674). Fortunately
+ // QuicDecrypter::Decrypt() specifies that |output| must be as long as
+ // |ciphertext| on entry.
+ size_t plaintext_size = ciphertext.length() - auth_tag_size_;
+
+ // Import key_ into NSS.
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = key_;
+ key_item.len = key_size_;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+
+ // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is
+ // CKM_AES_GCM, but because of NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a
+ // workaround. Remove this when we require NSS 3.15.
+ CK_MECHANISM_TYPE key_mechanism = aead_mechanism_;
+ if (key_mechanism == CKM_AES_GCM) {
+ key_mechanism = CKM_AES_ECB;
+ }
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so pass PK11_OriginUnwrap as a
+ // placeholder.
+ crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(
+ slot, key_mechanism, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, nullptr));
+ PK11_FreeSlot(slot);
+ slot = nullptr;
+ if (!aead_key) {
+ DVLOG(1) << "PK11_ImportSymKey failed";
+ return false;
+ }
+
+ AeadParams aead_params = {0};
+ FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params);
+
+ SECItem param;
+ param.type = siBuffer;
+ param.data = reinterpret_cast<unsigned char*>(&aead_params.data);
+ param.len = aead_params.len;
+
+ unsigned int output_len;
+ if (pk11_decrypt_(aead_key.get(), aead_mechanism_, ¶m,
+ output, &output_len, ciphertext.length(),
+ reinterpret_cast<const unsigned char*>(ciphertext.data()),
+ ciphertext.length()) != SECSuccess) {
+ return false;
+ }
+
+ if (output_len != plaintext_size) {
+ DVLOG(1) << "Wrong output length";
+ return false;
+ }
+ *output_length = output_len;
+ return true;
+}
+
+QuicData* AeadBaseDecrypter::DecryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return nullptr;
+ }
+ size_t plaintext_size;
+ scoped_ptr<char[]> plaintext(new char[ciphertext.length()]);
+
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, ciphertext,
+ reinterpret_cast<uint8*>(plaintext.get()),
+ &plaintext_size)) {
+ return nullptr;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+StringPiece AeadBaseDecrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/aead_base_decrypter_openssl.cc b/net/quic/crypto/aead_base_decrypter_openssl.cc
new file mode 100644
index 0000000..62a6fb8
--- /dev/null
+++ b/net/quic/crypto/aead_base_decrypter_openssl.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "base/memory/scoped_ptr.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// Clear OpenSSL error stack.
+void ClearOpenSslErrors() {
+ while (ERR_get_error()) {}
+}
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ ClearOpenSslErrors();
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, arraysize(buf));
+ DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+} // namespace
+
+AeadBaseDecrypter::AeadBaseDecrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_alg_(aead_alg),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseDecrypter::~AeadBaseDecrypter() {}
+
+bool AeadBaseDecrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
+ auth_tag_size_, nullptr)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseDecrypter::Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ uint8* output,
+ size_t* output_length) {
+ if (ciphertext.length() < auth_tag_size_ ||
+ nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ if (!EVP_AEAD_CTX_open(
+ ctx_.get(), output, output_length, ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()), ciphertext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size())) {
+ // Because QuicFramer does trial decryption, decryption errors are expected
+ // when encryption level changes. So we don't log decryption errors.
+ ClearOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseDecrypter::DecryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ if (ciphertext.length() < auth_tag_size_) {
+ return nullptr;
+ }
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, ciphertext,
+ reinterpret_cast<uint8*>(plaintext.get()),
+ &plaintext_size)) {
+ return nullptr;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+StringPiece AeadBaseDecrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseDecrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/aead_base_encrypter.h b/net/quic/crypto/aead_base_encrypter.h
new file mode 100644
index 0000000..7138131
--- /dev/null
+++ b/net/quic/crypto/aead_base_encrypter.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_encrypter.h"
+
+#if defined(USE_OPENSSL)
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+#else
+#include <pkcs11t.h>
+#include <seccomon.h>
+typedef struct PK11SymKeyStr PK11SymKey;
+typedef SECStatus (*PK11_EncryptFunction)(
+ PK11SymKey* symKey, CK_MECHANISM_TYPE mechanism, SECItem* param,
+ unsigned char* out, unsigned int* outLen, unsigned int maxLen,
+ const unsigned char* data, unsigned int dataLen);
+#endif
+
+namespace net {
+
+// AeadBaseEncrypter is the base class of AEAD QuicEncrypter subclasses.
+class NET_EXPORT_PRIVATE AeadBaseEncrypter : public QuicEncrypter {
+ public:
+#if defined(USE_OPENSSL)
+ AeadBaseEncrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#else
+ AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_EncryptFunction pk11_encrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size);
+#endif
+ virtual ~AeadBaseEncrypter();
+
+ // QuicEncrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Encrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext,
+ unsigned char* output) OVERRIDE;
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext) OVERRIDE;
+ virtual size_t GetKeySize() const OVERRIDE;
+ virtual size_t GetNoncePrefixSize() const OVERRIDE;
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ protected:
+ // Make these constants available to the subclasses so that the subclasses
+ // can assert at compile time their key_size_ and nonce_prefix_size_ do not
+ // exceed the maximum.
+ static const size_t kMaxKeySize = 32;
+ static const size_t kMaxNoncePrefixSize = 4;
+
+#if !defined(USE_OPENSSL)
+ struct AeadParams {
+ unsigned int len;
+ union {
+ CK_GCM_PARAMS gcm_params;
+#if !defined(USE_NSS)
+ // USE_NSS means we are using system NSS rather than our copy of NSS.
+ // The system NSS <pkcs11n.h> header doesn't define this type yet.
+ CK_NSS_AEAD_PARAMS nss_aead_params;
+#endif
+ } data;
+ };
+
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const = 0;
+#endif
+
+ private:
+#if defined(USE_OPENSSL)
+ const EVP_AEAD* const aead_alg_;
+#else
+ const CK_MECHANISM_TYPE aead_mechanism_;
+ const PK11_EncryptFunction pk11_encrypt_;
+#endif
+ const size_t key_size_;
+ const size_t auth_tag_size_;
+ const size_t nonce_prefix_size_;
+
+ // The key.
+ unsigned char key_[kMaxKeySize];
+ // The nonce prefix.
+ unsigned char nonce_prefix_[kMaxNoncePrefixSize];
+
+#if defined(USE_OPENSSL)
+ ScopedEVPAEADCtx ctx_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(AeadBaseEncrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AEAD_BASE_ENCRYPTER_H_
diff --git a/net/quic/crypto/aead_base_encrypter_nss.cc b/net/quic/crypto/aead_base_encrypter_nss.cc
new file mode 100644
index 0000000..1e408ad
--- /dev/null
+++ b/net/quic/crypto/aead_base_encrypter_nss.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "crypto/scoped_nss_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+AeadBaseEncrypter::AeadBaseEncrypter(CK_MECHANISM_TYPE aead_mechanism,
+ PK11_EncryptFunction pk11_encrypt,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_mechanism_(aead_mechanism),
+ pk11_encrypt_(pk11_encrypt),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+
+ // Import key_ into NSS.
+ SECItem key_item;
+ key_item.type = siBuffer;
+ key_item.data = key_;
+ key_item.len = key_size_;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+
+ // TODO(wtc): For an AES-GCM key, the correct value for |key_mechanism| is
+ // CKM_AES_GCM, but because of NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=853285, use CKM_AES_ECB as a
+ // workaround. Remove this when we require NSS 3.15.
+ CK_MECHANISM_TYPE key_mechanism = aead_mechanism_;
+ if (key_mechanism == CKM_AES_GCM) {
+ key_mechanism = CKM_AES_ECB;
+ }
+
+ // The exact value of the |origin| argument doesn't matter to NSS as long as
+ // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a
+ // placeholder.
+ crypto::ScopedPK11SymKey aead_key(PK11_ImportSymKey(
+ slot, key_mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, nullptr));
+ PK11_FreeSlot(slot);
+ slot = nullptr;
+ if (!aead_key) {
+ DVLOG(1) << "PK11_ImportSymKey failed";
+ return false;
+ }
+
+ AeadParams aead_params = {0};
+ FillAeadParams(nonce, associated_data, auth_tag_size_, &aead_params);
+
+ SECItem param;
+ param.type = siBuffer;
+ param.data = reinterpret_cast<unsigned char*>(&aead_params.data);
+ param.len = aead_params.len;
+
+ unsigned int output_len;
+ if (pk11_encrypt_(aead_key.get(), aead_mechanism_, ¶m,
+ output, &output_len, ciphertext_size,
+ reinterpret_cast<const unsigned char*>(plaintext.data()),
+ plaintext.size()) != SECSuccess) {
+ DVLOG(1) << "pk11_encrypt_ failed";
+ return false;
+ }
+
+ if (output_len != ciphertext_size) {
+ DVLOG(1) << "Wrong output length";
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same sequence number twice.
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_prefix_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+StringPiece AeadBaseEncrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/aead_base_encrypter_openssl.cc b/net/quic/crypto/aead_base_encrypter_openssl.cc
new file mode 100644
index 0000000..25d9967
--- /dev/null
+++ b/net/quic/crypto/aead_base_encrypter_openssl.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <string.h>
+
+#include "base/memory/scoped_ptr.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// In debug builds only, log OpenSSL error stack. Then clear OpenSSL error
+// stack.
+void DLogOpenSslErrors() {
+#ifdef NDEBUG
+ while (ERR_get_error()) {}
+#else
+ while (unsigned long error = ERR_get_error()) {
+ char buf[120];
+ ERR_error_string_n(error, buf, arraysize(buf));
+ DLOG(ERROR) << "OpenSSL error: " << buf;
+ }
+#endif
+}
+
+} // namespace
+
+AeadBaseEncrypter::AeadBaseEncrypter(const EVP_AEAD* aead_alg,
+ size_t key_size,
+ size_t auth_tag_size,
+ size_t nonce_prefix_size)
+ : aead_alg_(aead_alg),
+ key_size_(key_size),
+ auth_tag_size_(auth_tag_size),
+ nonce_prefix_size_(nonce_prefix_size) {
+ DCHECK_LE(key_size_, sizeof(key_));
+ DCHECK_LE(nonce_prefix_size_, sizeof(nonce_prefix_));
+}
+
+AeadBaseEncrypter::~AeadBaseEncrypter() {}
+
+bool AeadBaseEncrypter::SetKey(StringPiece key) {
+ DCHECK_EQ(key.size(), key_size_);
+ if (key.size() != key_size_) {
+ return false;
+ }
+ memcpy(key_, key.data(), key.size());
+
+ EVP_AEAD_CTX_cleanup(ctx_.get());
+
+ if (!EVP_AEAD_CTX_init(ctx_.get(), aead_alg_, key_, key_size_,
+ auth_tag_size_, nullptr)) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+bool AeadBaseEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ DCHECK_EQ(nonce_prefix.size(), nonce_prefix_size_);
+ if (nonce_prefix.size() != nonce_prefix_size_) {
+ return false;
+ }
+ memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size());
+ return true;
+}
+
+bool AeadBaseEncrypter::Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ if (nonce.size() != nonce_prefix_size_ + sizeof(QuicPacketSequenceNumber)) {
+ return false;
+ }
+
+ size_t len;
+ if (!EVP_AEAD_CTX_seal(
+ ctx_.get(),
+ output,
+ &len,
+ plaintext.size() + auth_tag_size_,
+ reinterpret_cast<const uint8_t*>(nonce.data()),
+ nonce.size(),
+ reinterpret_cast<const uint8_t*>(plaintext.data()),
+ plaintext.size(),
+ reinterpret_cast<const uint8_t*>(associated_data.data()),
+ associated_data.size())) {
+ DLogOpenSslErrors();
+ return false;
+ }
+
+ return true;
+}
+
+QuicData* AeadBaseEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ // TODO(ianswett): Introduce a check to ensure that we don't encrypt with the
+ // same sequence number twice.
+ uint8 nonce[sizeof(nonce_prefix_) + sizeof(sequence_number)];
+ const size_t nonce_size = nonce_prefix_size_ + sizeof(sequence_number);
+ DCHECK_LE(nonce_size, sizeof(nonce));
+ memcpy(nonce, nonce_prefix_, nonce_prefix_size_);
+ memcpy(nonce + nonce_prefix_size_, &sequence_number, sizeof(sequence_number));
+ if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), nonce_size),
+ associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+size_t AeadBaseEncrypter::GetKeySize() const { return key_size_; }
+
+size_t AeadBaseEncrypter::GetNoncePrefixSize() const {
+ return nonce_prefix_size_;
+}
+
+size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - auth_tag_size_;
+}
+
+size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + auth_tag_size_;
+}
+
+StringPiece AeadBaseEncrypter::GetKey() const {
+ return StringPiece(reinterpret_cast<const char*>(key_), key_size_);
+}
+
+StringPiece AeadBaseEncrypter::GetNoncePrefix() const {
+ if (nonce_prefix_size_ == 0) {
+ return StringPiece();
+ }
+ return StringPiece(reinterpret_cast<const char*>(nonce_prefix_),
+ nonce_prefix_size_);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/aes_128_gcm_12_decrypter.h b/net/quic/crypto/aes_128_gcm_12_decrypter.h
new file mode 100644
index 0000000..0511c8b
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_decrypter.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+namespace net {
+
+// An Aes128Gcm12Decrypter is a QuicDecrypter that implements the
+// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
+// calling QuicDecrypter::Create(kAESG).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
+// of the nonce is four bytes.
+class NET_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter {
+ public:
+ enum {
+ // Authentication tags are truncated to 96 bits.
+ kAuthTagSize = 12,
+ };
+
+ Aes128Gcm12Decrypter();
+ virtual ~Aes128Gcm12Decrypter();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseDecrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Decrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_
diff --git a/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc b/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
new file mode 100644
index 0000000..d2cd728
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+
+#include <pk11pub.h>
+#include <secerr.h>
+
+#include "base/lazy_instance.h"
+#include "crypto/ghash.h"
+#include "crypto/scoped_nss_types.h"
+
+#if defined(USE_NSS)
+#include <dlfcn.h>
+#endif
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
+
+// On Linux, dynamically link against the system version of libnss3.so. In
+// order to continue working on systems without up-to-date versions of NSS,
+// lookup PK11_Decrypt with dlsym.
+
+// GcmSupportChecker is a singleton which caches the results of runtime symbol
+// resolution of PK11_Decrypt.
+class GcmSupportChecker {
+ public:
+ static PK11_DecryptFunction pk11_decrypt_func() {
+ return pk11_decrypt_func_;
+ }
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
+
+ GcmSupportChecker() {
+#if !defined(USE_NSS)
+ // Using a bundled version of NSS that is guaranteed to have this symbol.
+ pk11_decrypt_func_ = PK11_Decrypt;
+#else
+ // Using system NSS libraries and PCKS #11 modules, which may not have the
+ // necessary function (PK11_Decrypt) or mechanism support (CKM_AES_GCM).
+
+ // If PK11_Decrypt() was successfully resolved, then NSS will support
+ // AES-GCM directly. This was introduced in NSS 3.15.
+ pk11_decrypt_func_ = (PK11_DecryptFunction)dlsym(RTLD_DEFAULT,
+ "PK11_Decrypt");
+#endif
+ }
+
+ // |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt.
+ static PK11_DecryptFunction pk11_decrypt_func_;
+};
+
+// static
+PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = nullptr;
+
+base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Calls PK11_Decrypt if it's available. Otherwise, emulates CKM_AES_GCM using
+// CKM_AES_CTR and the GaloisHash class.
+SECStatus My_Decrypt(PK11SymKey* key,
+ CK_MECHANISM_TYPE mechanism,
+ SECItem* param,
+ unsigned char* out,
+ unsigned int* out_len,
+ unsigned int max_len,
+ const unsigned char* enc,
+ unsigned int enc_len) {
+ // If PK11_Decrypt() was successfully resolved or if bundled version of NSS is
+ // being used, then NSS will support AES-GCM directly.
+ PK11_DecryptFunction pk11_decrypt_func =
+ GcmSupportChecker::pk11_decrypt_func();
+ if (pk11_decrypt_func != nullptr) {
+ return pk11_decrypt_func(key, mechanism, param, out, out_len, max_len, enc,
+ enc_len);
+ }
+
+ // Otherwise, the user has an older version of NSS. Regrettably, NSS 3.14.x
+ // has a bug in the AES GCM code
+ // (https://bugzilla.mozilla.org/show_bug.cgi?id=853285), as well as missing
+ // the PK11_Decrypt function
+ // (https://bugzilla.mozilla.org/show_bug.cgi?id=854063), both of which are
+ // resolved in NSS 3.15.
+
+ DCHECK_EQ(mechanism, static_cast<CK_MECHANISM_TYPE>(CKM_AES_GCM));
+ DCHECK_EQ(param->len, sizeof(CK_GCM_PARAMS));
+
+ const CK_GCM_PARAMS* gcm_params =
+ reinterpret_cast<CK_GCM_PARAMS*>(param->data);
+
+ DCHECK_EQ(gcm_params->ulTagBits,
+ static_cast<CK_ULONG>(Aes128Gcm12Decrypter::kAuthTagSize * 8));
+ if (gcm_params->ulIvLen != 12u) {
+ DVLOG(1) << "ulIvLen is not equal to 12";
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+
+ SECItem my_param = { siBuffer, nullptr, 0 };
+
+ // Step 2. Let H = CIPH_K(128 '0' bits).
+ unsigned char ghash_key[16] = {0};
+ crypto::ScopedPK11Context ctx(PK11_CreateContextBySymKey(
+ CKM_AES_ECB, CKA_ENCRYPT, key, &my_param));
+ if (!ctx) {
+ DVLOG(1) << "PK11_CreateContextBySymKey failed";
+ return SECFailure;
+ }
+ int output_len;
+ if (PK11_CipherOp(ctx.get(), ghash_key, &output_len, sizeof(ghash_key),
+ ghash_key, sizeof(ghash_key)) != SECSuccess) {
+ DVLOG(1) << "PK11_CipherOp failed";
+ return SECFailure;
+ }
+
+ PK11_Finalize(ctx.get());
+
+ if (output_len != sizeof(ghash_key)) {
+ DVLOG(1) << "Wrong output length";
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ // Step 3. If len(IV)=96, then let J0 = IV || 31 '0' bits || 1.
+ CK_AES_CTR_PARAMS ctr_params = {0};
+ ctr_params.ulCounterBits = 32;
+ memcpy(ctr_params.cb, gcm_params->pIv, gcm_params->ulIvLen);
+ ctr_params.cb[12] = 0;
+ ctr_params.cb[13] = 0;
+ ctr_params.cb[14] = 0;
+ ctr_params.cb[15] = 1;
+
+ my_param.type = siBuffer;
+ my_param.data = reinterpret_cast<unsigned char*>(&ctr_params);
+ my_param.len = sizeof(ctr_params);
+
+ ctx.reset(PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, key,
+ &my_param));
+ if (!ctx) {
+ DVLOG(1) << "PK11_CreateContextBySymKey failed";
+ return SECFailure;
+ }
+
+ // Step 6. Calculate the encryption mask of GCTR_K(J0, ...).
+ unsigned char tag_mask[16] = {0};
+ if (PK11_CipherOp(ctx.get(), tag_mask, &output_len, sizeof(tag_mask),
+ tag_mask, sizeof(tag_mask)) != SECSuccess) {
+ DVLOG(1) << "PK11_CipherOp failed";
+ return SECFailure;
+ }
+ if (output_len != sizeof(tag_mask)) {
+ DVLOG(1) << "Wrong output length";
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if (enc_len < Aes128Gcm12Decrypter::kAuthTagSize) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+
+ // The const_cast for |enc| can be removed if system NSS libraries are
+ // NSS 3.14.1 or later (NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=808218).
+ if (PK11_CipherOp(ctx.get(), out, &output_len, max_len,
+ const_cast<unsigned char*>(enc),
+ enc_len - Aes128Gcm12Decrypter::kAuthTagSize) != SECSuccess) {
+ DVLOG(1) << "PK11_CipherOp failed";
+ return SECFailure;
+ }
+
+ PK11_Finalize(ctx.get());
+
+ if (static_cast<unsigned int>(output_len) !=
+ enc_len - Aes128Gcm12Decrypter::kAuthTagSize) {
+ DVLOG(1) << "Wrong output length";
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ crypto::GaloisHash ghash(ghash_key);
+ ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen);
+ ghash.UpdateCiphertext(enc, output_len);
+ unsigned char auth_tag[Aes128Gcm12Decrypter::kAuthTagSize];
+ ghash.Finish(auth_tag, Aes128Gcm12Decrypter::kAuthTagSize);
+ for (unsigned int i = 0; i < Aes128Gcm12Decrypter::kAuthTagSize; i++) {
+ auth_tag[i] ^= tag_mask[i];
+ }
+
+ if (NSS_SecureMemcmp(auth_tag, enc + output_len,
+ Aes128Gcm12Decrypter::kAuthTagSize) != 0) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+
+ *out_len = output_len;
+ return SECSuccess;
+}
+
+} // namespace
+
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AeadBaseDecrypter(CKM_AES_GCM, My_Decrypt, kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+ ignore_result(g_gcm_support_checker.Get());
+}
+
+Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
+
+void Aes128Gcm12Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.gcm_params);
+ CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
+ gcm_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ gcm_params->ulIvLen = nonce.size();
+ gcm_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ gcm_params->ulAADLen = associated_data.size();
+ gcm_params->ulTagBits = auth_tag_size * 8;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
new file mode 100644
index 0000000..109d2da
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
+
+} // namespace
+
+Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
+ : AeadBaseDecrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {}
+
+} // namespace net
diff --git a/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc b/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
new file mode 100644
index 0000000..37ee8f9
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc
@@ -0,0 +1,336 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmDecrypt128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = cf063a34d4a9a76c2c86787d3f96db71
+// IV = 113b9785971864c83b01c787
+// CT =
+// AAD =
+// Tag = 72ac8493e3a5228b5d130a69d2510e42
+// PT =
+//
+// Count = 1
+// Key = a49a5e26a2f8cb63d05546c2a62f5343
+// IV = 907763b19b9b4ab6bd4f0281
+// CT =
+// AAD =
+// Tag = a2be08210d8c470a8df6e8fbd79ec5cf
+// FAIL
+//
+// ...
+//
+// The gcmDecrypt128.rsp file is huge (2.6 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* ct;
+ const char* aad;
+ const char* tag;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. NULL means decryption
+ // failed.
+};
+
+const TestGroupInfo test_group_info[] = {
+ { 128, 96, 0, 0, 128 },
+ { 128, 96, 0, 128, 128 },
+ { 128, 96, 128, 0, 128 },
+ { 128, 96, 408, 160, 128 },
+ { 128, 96, 408, 720, 128 },
+ { 128, 96, 104, 0, 128 },
+};
+
+const TestVector test_group_0[] = {
+ { "cf063a34d4a9a76c2c86787d3f96db71",
+ "113b9785971864c83b01c787",
+ "",
+ "",
+ "72ac8493e3a5228b5d130a69d2510e42",
+ ""
+ },
+ { "a49a5e26a2f8cb63d05546c2a62f5343",
+ "907763b19b9b4ab6bd4f0281",
+ "",
+ "",
+ "a2be08210d8c470a8df6e8fbd79ec5cf",
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+const TestVector test_group_1[] = {
+ { "d1f6af919cde85661208bdce0c27cb22",
+ "898c6929b435017bf031c3c5",
+ "",
+ "7c5faa40e636bbc91107e68010c92b9f",
+ "ae45f11777540a2caeb128be8092468a",
+ NULL // FAIL
+ },
+ { "2370e320d4344208e0ff5683f243b213",
+ "04dbb82f044d30831c441228",
+ "",
+ "d43a8e5089eea0d026c03a85178b27da",
+ "2a049c049d25aa95969b451d93c31c6e",
+ ""
+ },
+ { NULL }
+};
+
+const TestVector test_group_2[] = {
+ { "e98b72a9881a84ca6b76e0f43e68647a",
+ "8b23299fde174053f3d652ba",
+ "5a3c1cf1985dbb8bed818036fdd5ab42",
+ "",
+ "23c7ab0f952b7091cd324835043b5eb5",
+ "28286a321293253c3e0aa2704a278032"
+ },
+ { "33240636cd3236165f1a553b773e728e",
+ "17c4d61493ecdc8f31700b12",
+ "47bb7e23f7bdfe05a8091ac90e4f8b2e",
+ "",
+ "b723c70e931d9785f40fd4ab1d612dc9",
+ "95695a5b12f2870b9cc5fdc8f218a97d"
+ },
+ { "5164df856f1e9cac04a79b808dc5be39",
+ "e76925d5355e0584ce871b2b",
+ "0216c899c88d6e32c958c7e553daa5bc",
+ "",
+ "a145319896329c96df291f64efbe0e3a",
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+const TestVector test_group_3[] = {
+ { "af57f42c60c0fc5a09adb81ab86ca1c3",
+ "a2dc01871f37025dc0fc9a79",
+ "b9a535864f48ea7b6b1367914978f9bfa087d854bb0e269bed8d279d2eea1210e48947"
+ "338b22f9bad09093276a331e9c79c7f4",
+ "41dc38988945fcb44faf2ef72d0061289ef8efd8",
+ "4f71e72bde0018f555c5adcce062e005",
+ "3803a0727eeb0ade441e0ec107161ded2d425ec0d102f21f51bf2cf9947c7ec4aa7279"
+ "5b2f69b041596e8817d0a3c16f8fadeb"
+ },
+ { "ebc753e5422b377d3cb64b58ffa41b61",
+ "2e1821efaced9acf1f241c9b",
+ "069567190554e9ab2b50a4e1fbf9c147340a5025fdbd201929834eaf6532325899ccb9"
+ "f401823e04b05817243d2142a3589878",
+ "b9673412fd4f88ba0e920f46dd6438ff791d8eef",
+ "534d9234d2351cf30e565de47baece0b",
+ "39077edb35e9c5a4b1e4c2a6b9bb1fce77f00f5023af40333d6d699014c2bcf4209c18"
+ "353a18017f5b36bfc00b1f6dcb7ed485"
+ },
+ { "52bdbbf9cf477f187ec010589cb39d58",
+ "d3be36d3393134951d324b31",
+ "700188da144fa692cf46e4a8499510a53d90903c967f7f13e8a1bd8151a74adc4fe63e"
+ "32b992760b3a5f99e9a47838867000a9",
+ "93c4fc6a4135f54d640b0c976bf755a06a292c33",
+ "8ca4e38aa3dfa6b1d0297021ccf3ea5f",
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+const TestVector test_group_4[] = {
+ { "da2bb7d581493d692380c77105590201",
+ "44aa3e7856ca279d2eb020c6",
+ "9290d430c9e89c37f0446dbd620c9a6b34b1274aeb6f911f75867efcf95b6feda69f1a"
+ "f4ee16c761b3c9aeac3da03aa9889c88",
+ "4cd171b23bddb3a53cdf959d5c1710b481eb3785a90eb20a2345ee00d0bb7868c367ab"
+ "12e6f4dd1dee72af4eee1d197777d1d6499cc541f34edbf45cda6ef90b3c024f9272d7"
+ "2ec1909fb8fba7db88a4d6f7d3d925980f9f9f72",
+ "9e3ac938d3eb0cadd6f5c9e35d22ba38",
+ "9bbf4c1a2742f6ac80cb4e8a052e4a8f4f07c43602361355b717381edf9fabd4cb7e3a"
+ "d65dbd1378b196ac270588dd0621f642"
+ },
+ { "d74e4958717a9d5c0e235b76a926cae8",
+ "0b7471141e0c70b1995fd7b1",
+ "e701c57d2330bf066f9ff8cf3ca4343cafe4894651cd199bdaaa681ba486b4a65c5a22"
+ "b0f1420be29ea547d42c713bc6af66aa",
+ "4a42b7aae8c245c6f1598a395316e4b8484dbd6e64648d5e302021b1d3fa0a38f46e22"
+ "bd9c8080b863dc0016482538a8562a4bd0ba84edbe2697c76fd039527ac179ec5506cf"
+ "34a6039312774cedebf4961f3978b14a26509f96",
+ "e192c23cb036f0b31592989119eed55d",
+ "840d9fb95e32559fb3602e48590280a172ca36d9b49ab69510f5bd552bfab7a306f85f"
+ "f0a34bc305b88b804c60b90add594a17"
+ },
+ { "1986310c725ac94ecfe6422e75fc3ee7",
+ "93ec4214fa8e6dc4e3afc775",
+ "b178ec72f85a311ac4168f42a4b2c23113fbea4b85f4b9dabb74e143eb1b8b0a361e02"
+ "43edfd365b90d5b325950df0ada058f9",
+ "e80b88e62c49c958b5e0b8b54f532d9ff6aa84c8a40132e93e55b59fc24e8decf28463"
+ "139f155d1e8ce4ee76aaeefcd245baa0fc519f83a5fb9ad9aa40c4b21126013f576c42"
+ "72c2cb136c8fd091cc4539877a5d1e72d607f960",
+ "8b347853f11d75e81e8a95010be81f17",
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+const TestVector test_group_5[] = {
+ { "387218b246c1a8257748b56980e50c94",
+ "dd7e014198672be39f95b69d",
+ "cdba9e73eaf3d38eceb2b04a8d",
+ "",
+ "ecf90f4a47c9c626d6fb2c765d201556",
+ "48f5b426baca03064554cc2b30"
+ },
+ { "294de463721e359863887c820524b3d4",
+ "3338b35c9d57a5d28190e8c9",
+ "2f46634e74b8e4c89812ac83b9",
+ "",
+ "dabd506764e68b82a7e720aa18da0abe",
+ "46a2e55c8e264df211bd112685"
+ },
+ { "28ead7fd2179e0d12aa6d5d88c58c2dc",
+ "5055347f18b4d5add0ae5c41",
+ "142d8210c3fb84774cdbd0447a",
+ "",
+ "5fd321d9cdb01952dc85f034736c2a7d",
+ "3b95b981086ee73cc4d0cc1422"
+ },
+ { "7d7b6c988137b8d470c57bf674a09c87",
+ "9edf2aa970d016ac962e1fd8",
+ "a85b66c3cb5eab91d5bdc8bc0e",
+ "",
+ "dc054efc01f3afd21d9c2484819f569a",
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+const TestVector* const test_group_array[] = {
+ test_group_0,
+ test_group_1,
+ test_group_2,
+ test_group_3,
+ test_group_4,
+ test_group_5,
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(Aes128Gcm12Decrypter* decrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ if (!decrypter->Decrypt(nonce, associated_data, ciphertext,
+ reinterpret_cast<unsigned char*>(plaintext.get()),
+ &plaintext_size)) {
+ return nullptr;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+TEST(Aes128Gcm12DecrypterTest, Decrypt) {
+ for (size_t i = 0; i < arraysize(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[j].pt;
+
+ // Decode the test vector.
+ string key;
+ string iv;
+ string ct;
+ string aad;
+ string tag;
+ string pt;
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag));
+ if (has_pt) {
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt));
+ }
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+ if (has_pt) {
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ }
+
+ // The test vectors have 16 byte authenticators but this code only uses
+ // the first 12.
+ ASSERT_LE(static_cast<size_t>(Aes128Gcm12Decrypter::kAuthTagSize),
+ tag.length());
+ tag.resize(Aes128Gcm12Decrypter::kAuthTagSize);
+ string ciphertext = ct + tag;
+
+ Aes128Gcm12Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+
+ scoped_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr
+ // pointer.
+ aad.length() ? aad : StringPiece(), ciphertext));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter.h b/net/quic/crypto/aes_128_gcm_12_encrypter.h
new file mode 100644
index 0000000..1d8f321
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+namespace net {
+
+// An Aes128Gcm12Encrypter is a QuicEncrypter that implements the
+// AEAD_AES_128_GCM_12 algorithm specified in RFC 5282. Create an instance by
+// calling QuicEncrypter::Create(kAESG).
+//
+// It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
+// of the nonce is four bytes.
+class NET_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AeadBaseEncrypter {
+ public:
+ enum {
+ // Authentication tags are truncated to 96 bits.
+ kAuthTagSize = 12,
+ };
+
+ Aes128Gcm12Encrypter();
+ virtual ~Aes128Gcm12Encrypter();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseEncrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Aes128Gcm12Encrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
new file mode 100644
index 0000000..6214af5
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+
+#include <pk11pub.h>
+#include <secerr.h>
+
+#include "base/lazy_instance.h"
+#include "crypto/ghash.h"
+#include "crypto/scoped_nss_types.h"
+
+#if defined(USE_NSS)
+#include <dlfcn.h>
+#endif
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
+
+// On Linux, dynamically link against the system version of libnss3.so. In
+// order to continue working on systems without up-to-date versions of NSS,
+// lookup PK11_Encrypt with dlsym.
+
+// GcmSupportChecker is a singleton which caches the results of runtime symbol
+// resolution of PK11_Encrypt.
+class GcmSupportChecker {
+ public:
+ static PK11_EncryptFunction pk11_encrypt_func() {
+ return pk11_encrypt_func_;
+ }
+
+ private:
+ friend struct base::DefaultLazyInstanceTraits<GcmSupportChecker>;
+
+ GcmSupportChecker() {
+#if !defined(USE_NSS)
+ // Using a bundled version of NSS that is guaranteed to have this symbol.
+ pk11_encrypt_func_ = PK11_Encrypt;
+#else
+ // Using system NSS libraries and PCKS #11 modules, which may not have the
+ // necessary function (PK11_Encrypt) or mechanism support (CKM_AES_GCM).
+
+ // If PK11_Encrypt() was successfully resolved, then NSS will support
+ // AES-GCM directly. This was introduced in NSS 3.15.
+ pk11_encrypt_func_ = (PK11_EncryptFunction)dlsym(RTLD_DEFAULT,
+ "PK11_Encrypt");
+#endif
+ }
+
+ // |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt.
+ static PK11_EncryptFunction pk11_encrypt_func_;
+};
+
+// static
+PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = nullptr;
+
+base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Calls PK11_Encrypt if it's available. Otherwise, emulates CKM_AES_GCM using
+// CKM_AES_CTR and the GaloisHash class.
+SECStatus My_Encrypt(PK11SymKey* key,
+ CK_MECHANISM_TYPE mechanism,
+ SECItem* param,
+ unsigned char* out,
+ unsigned int* out_len,
+ unsigned int max_len,
+ const unsigned char* data,
+ unsigned int data_len) {
+ // If PK11_Encrypt() was successfully resolved or if bundled version of NSS is
+ // being used, then NSS will support AES-GCM directly.
+ PK11_EncryptFunction pk11_encrypt_func =
+ GcmSupportChecker::pk11_encrypt_func();
+ if (pk11_encrypt_func != nullptr) {
+ return pk11_encrypt_func(key, mechanism, param, out, out_len, max_len, data,
+ data_len);
+ }
+
+ // Otherwise, the user has an older version of NSS. Regrettably, NSS 3.14.x
+ // has a bug in the AES GCM code
+ // (https://bugzilla.mozilla.org/show_bug.cgi?id=853285), as well as missing
+ // the PK11_Encrypt function
+ // (https://bugzilla.mozilla.org/show_bug.cgi?id=854063), both of which are
+ // resolved in NSS 3.15.
+
+ DCHECK_EQ(mechanism, static_cast<CK_MECHANISM_TYPE>(CKM_AES_GCM));
+ DCHECK_EQ(param->len, sizeof(CK_GCM_PARAMS));
+
+ if (max_len < static_cast<unsigned int>(Aes128Gcm12Encrypter::kAuthTagSize)) {
+ DVLOG(1) << "max_len is less than kAuthTagSize";
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ const CK_GCM_PARAMS* gcm_params =
+ reinterpret_cast<CK_GCM_PARAMS*>(param->data);
+
+ DCHECK_EQ(gcm_params->ulTagBits,
+ static_cast<CK_ULONG>(Aes128Gcm12Encrypter::kAuthTagSize * 8));
+ if (gcm_params->ulIvLen != 12u) {
+ DVLOG(1) << "ulIvLen is not equal to 12";
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+
+ SECItem my_param = { siBuffer, nullptr, 0 };
+
+ // Step 1. Let H = CIPH_K(128 '0' bits).
+ unsigned char ghash_key[16] = {0};
+ crypto::ScopedPK11Context ctx(PK11_CreateContextBySymKey(
+ CKM_AES_ECB, CKA_ENCRYPT, key, &my_param));
+ if (!ctx) {
+ DVLOG(1) << "PK11_CreateContextBySymKey failed";
+ return SECFailure;
+ }
+ int output_len;
+ if (PK11_CipherOp(ctx.get(), ghash_key, &output_len, sizeof(ghash_key),
+ ghash_key, sizeof(ghash_key)) != SECSuccess) {
+ DVLOG(1) << "PK11_CipherOp failed";
+ return SECFailure;
+ }
+
+ PK11_Finalize(ctx.get());
+
+ if (output_len != sizeof(ghash_key)) {
+ DVLOG(1) << "Wrong output length";
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ // Step 2. If len(IV)=96, then let J0 = IV || 31 '0' bits || 1.
+ CK_AES_CTR_PARAMS ctr_params = {0};
+ ctr_params.ulCounterBits = 32;
+ memcpy(ctr_params.cb, gcm_params->pIv, gcm_params->ulIvLen);
+ ctr_params.cb[12] = 0;
+ ctr_params.cb[13] = 0;
+ ctr_params.cb[14] = 0;
+ ctr_params.cb[15] = 1;
+
+ my_param.type = siBuffer;
+ my_param.data = reinterpret_cast<unsigned char*>(&ctr_params);
+ my_param.len = sizeof(ctr_params);
+
+ ctx.reset(PK11_CreateContextBySymKey(CKM_AES_CTR, CKA_ENCRYPT, key,
+ &my_param));
+ if (!ctx) {
+ DVLOG(1) << "PK11_CreateContextBySymKey failed";
+ return SECFailure;
+ }
+
+ // Step 6. Calculate the encryption mask of GCTR_K(J0, ...).
+ unsigned char tag_mask[16] = {0};
+ if (PK11_CipherOp(ctx.get(), tag_mask, &output_len, sizeof(tag_mask),
+ tag_mask, sizeof(tag_mask)) != SECSuccess) {
+ DVLOG(1) << "PK11_CipherOp failed";
+ return SECFailure;
+ }
+ if (output_len != sizeof(tag_mask)) {
+ DVLOG(1) << "Wrong output length";
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ // The const_cast for |data| can be removed if system NSS libraries are
+ // NSS 3.14.1 or later (NSS bug
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=808218).
+ if (PK11_CipherOp(ctx.get(), out, &output_len, max_len,
+ const_cast<unsigned char*>(data), data_len) != SECSuccess) {
+ DVLOG(1) << "PK11_CipherOp failed";
+ return SECFailure;
+ }
+
+ PK11_Finalize(ctx.get());
+
+ if (static_cast<unsigned int>(output_len) != data_len) {
+ DVLOG(1) << "Wrong output length";
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ if ((max_len - Aes128Gcm12Encrypter::kAuthTagSize) <
+ static_cast<unsigned int>(output_len)) {
+ DVLOG(1) << "(max_len - kAuthTagSize) is less than output_len";
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ crypto::GaloisHash ghash(ghash_key);
+ ghash.UpdateAdditional(gcm_params->pAAD, gcm_params->ulAADLen);
+ ghash.UpdateCiphertext(out, output_len);
+ ghash.Finish(out + output_len, Aes128Gcm12Encrypter::kAuthTagSize);
+ for (unsigned int i = 0; i < Aes128Gcm12Encrypter::kAuthTagSize; i++) {
+ out[output_len + i] ^= tag_mask[i];
+ }
+
+ *out_len = output_len + Aes128Gcm12Encrypter::kAuthTagSize;
+ return SECSuccess;
+}
+
+} // namespace
+
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AeadBaseEncrypter(CKM_AES_GCM, My_Encrypt, kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+ ignore_result(g_gcm_support_checker.Get());
+}
+
+Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
+
+void Aes128Gcm12Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.gcm_params);
+ CK_GCM_PARAMS* gcm_params = &aead_params->data.gcm_params;
+ gcm_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ gcm_params->ulIvLen = nonce.size();
+ gcm_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ gcm_params->ulAADLen = associated_data.size();
+ gcm_params->ulTagBits = auth_tag_size * 8;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
new file mode 100644
index 0000000..6489528
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 16;
+const size_t kNoncePrefixSize = 4;
+
+} // namespace
+
+Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
+ : AeadBaseEncrypter(EVP_aead_aes_128_gcm(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {}
+
+} // namespace net
diff --git a/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc b/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
new file mode 100644
index 0000000..8ccaaed
--- /dev/null
+++ b/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The AES GCM test vectors come from the file gcmEncryptExtIV128.rsp
+// downloaded from http://csrc.nist.gov/groups/STM/cavp/index.html on
+// 2013-02-01. The test vectors in that file look like this:
+//
+// [Keylen = 128]
+// [IVlen = 96]
+// [PTlen = 0]
+// [AADlen = 0]
+// [Taglen = 128]
+//
+// Count = 0
+// Key = 11754cd72aec309bf52f7687212e8957
+// IV = 3c819d9a9bed087615030b65
+// PT =
+// AAD =
+// CT =
+// Tag = 250327c674aaf477aef2675748cf6971
+//
+// Count = 1
+// Key = ca47248ac0b6f8372a97ac43508308ed
+// IV = ffd2b598feabc9019262d2be
+// PT =
+// AAD =
+// CT =
+// Tag = 60d20404af527d248d893ae495707d1a
+//
+// ...
+//
+// The gcmEncryptExtIV128.rsp file is huge (2.8 MB), so I selected just a
+// few test vectors for this unit test.
+
+// Describes a group of test vectors that all have a given key length, IV
+// length, plaintext length, AAD length, and tag length.
+struct TestGroupInfo {
+ size_t key_len;
+ size_t iv_len;
+ size_t pt_len;
+ size_t aad_len;
+ size_t tag_len;
+};
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* iv;
+ const char* pt;
+ const char* aad;
+ const char* ct;
+ const char* tag;
+};
+
+const TestGroupInfo test_group_info[] = {
+ { 128, 96, 0, 0, 128 },
+ { 128, 96, 0, 128, 128 },
+ { 128, 96, 128, 0, 128 },
+ { 128, 96, 408, 160, 128 },
+ { 128, 96, 408, 720, 128 },
+ { 128, 96, 104, 0, 128 },
+};
+
+const TestVector test_group_0[] = {
+ { "11754cd72aec309bf52f7687212e8957",
+ "3c819d9a9bed087615030b65",
+ "",
+ "",
+ "",
+ "250327c674aaf477aef2675748cf6971"
+ },
+ { "ca47248ac0b6f8372a97ac43508308ed",
+ "ffd2b598feabc9019262d2be",
+ "",
+ "",
+ "",
+ "60d20404af527d248d893ae495707d1a"
+ },
+ { NULL }
+};
+
+const TestVector test_group_1[] = {
+ { "77be63708971c4e240d1cb79e8d77feb",
+ "e0e00f19fed7ba0136a797f3",
+ "",
+ "7a43ec1d9c0a5a78a0b16533a6213cab",
+ "",
+ "209fcc8d3675ed938e9c7166709dd946"
+ },
+ { "7680c5d3ca6154758e510f4d25b98820",
+ "f8f105f9c3df4965780321f8",
+ "",
+ "c94c410194c765e3dcc7964379758ed3",
+ "",
+ "94dca8edfcf90bb74b153c8d48a17930"
+ },
+ { NULL }
+};
+
+const TestVector test_group_2[] = {
+ { "7fddb57453c241d03efbed3ac44e371c",
+ "ee283a3fc75575e33efd4887",
+ "d5de42b461646c255c87bd2962d3b9a2",
+ "",
+ "2ccda4a5415cb91e135c2a0f78c9b2fd",
+ "b36d1df9b9d5e596f83e8b7f52971cb3"
+ },
+ { "ab72c77b97cb5fe9a382d9fe81ffdbed",
+ "54cc7dc2c37ec006bcc6d1da",
+ "007c5e5b3e59df24a7c355584fc1518d",
+ "",
+ "0e1bde206a07a9c2c1b65300f8c64997",
+ "2b4401346697138c7a4891ee59867d0c"
+ },
+ { NULL }
+};
+
+const TestVector test_group_3[] = {
+ { "fe47fcce5fc32665d2ae399e4eec72ba",
+ "5adb9609dbaeb58cbd6e7275",
+ "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1"
+ "b840382c4bccaf3bafb4ca8429bea063",
+ "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
+ "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf539304373636525"
+ "3ddbc5db8778371495da76d269e5db3e",
+ "291ef1982e4defedaa2249f898556b47"
+ },
+ { "ec0c2ba17aa95cd6afffe949da9cc3a8",
+ "296bce5b50b7d66096d627ef",
+ "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987"
+ "b764b9611f6c0f8641843d5d58f3a242",
+ "f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
+ "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a0716299"
+ "5506fde6309ffc19e716eddf1a828c5a",
+ "890147971946b627c40016da1ecf3e77"
+ },
+ { NULL }
+};
+
+const TestVector test_group_4[] = {
+ { "2c1f21cf0f6fb3661943155c3e3d8492",
+ "23cb5ff362e22426984d1907",
+ "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d6"
+ "8b5615ba7c1220ff6510e259f06655d8",
+ "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e"
+ "3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f"
+ "4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
+ "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222"
+ "b6ad57af43e1895df9dca2a5344a62cc",
+ "57a3ee28136e94c74838997ae9823f3a"
+ },
+ { "d9f7d2411091f947b4d6f1e2d1f0fb2e",
+ "e1934f5db57cc983e6b180e7",
+ "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490"
+ "c2c6f6166f4a59431e182663fcaea05a",
+ "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d"
+ "0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a201"
+ "15d2e51398344b16bee1ed7c499b353d6c597af8",
+ "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d57"
+ "3c7891c2a91fbc48db29967ec9542b23",
+ "21b51ca862cb637cdd03b99a0f93b134"
+ },
+ { NULL }
+};
+
+const TestVector test_group_5[] = {
+ { "fe9bb47deb3a61e423c2231841cfd1fb",
+ "4d328eb776f500a2f7fb47aa",
+ "f1cc3818e421876bb6b8bbd6c9",
+ "",
+ "b88c5c1977b35b517b0aeae967",
+ "43fd4727fe5cdb4b5b42818dea7ef8c9"
+ },
+ { "6703df3701a7f54911ca72e24dca046a",
+ "12823ab601c350ea4bc2488c",
+ "793cd125b0b84a043e3ac67717",
+ "",
+ "b2051c80014f42f08735a7b0cd",
+ "38e6bcd29962e5f2c13626b85a877101"
+ },
+ { NULL }
+};
+
+const TestVector* const test_group_array[] = {
+ test_group_0,
+ test_group_1,
+ test_group_2,
+ test_group_3,
+ test_group_4,
+ test_group_5,
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(Aes128Gcm12Encrypter* encrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+TEST(Aes128Gcm12EncrypterTest, Encrypt) {
+ for (size_t i = 0; i < arraysize(test_group_array); i++) {
+ SCOPED_TRACE(i);
+ const TestVector* test_vectors = test_group_array[i];
+ const TestGroupInfo& test_info = test_group_info[i];
+ for (size_t j = 0; test_vectors[j].key != nullptr; j++) {
+ // Decode the test vector.
+ string key;
+ string iv;
+ string pt;
+ string aad;
+ string ct;
+ string tag;
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].pt, &pt));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].ct, &ct));
+ ASSERT_TRUE(DecodeHexString(test_vectors[j].tag, &tag));
+
+ // The test vector's lengths should look sane. Note that the lengths
+ // in |test_info| are in bits.
+ EXPECT_EQ(test_info.key_len, key.length() * 8);
+ EXPECT_EQ(test_info.iv_len, iv.length() * 8);
+ EXPECT_EQ(test_info.pt_len, pt.length() * 8);
+ EXPECT_EQ(test_info.aad_len, aad.length() * 8);
+ EXPECT_EQ(test_info.pt_len, ct.length() * 8);
+ EXPECT_EQ(test_info.tag_len, tag.length() * 8);
+
+ Aes128Gcm12Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ scoped_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr
+ // pointer.
+ aad.length() ? aad : StringPiece(), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ // The test vectors have 16 byte authenticators but this code only uses
+ // the first 12.
+ ASSERT_LE(static_cast<size_t>(Aes128Gcm12Encrypter::kAuthTagSize),
+ tag.length());
+ tag.resize(Aes128Gcm12Encrypter::kAuthTagSize);
+
+ ASSERT_EQ(ct.length() + tag.length(), encrypted->length());
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ ct.length(), ct.data(), ct.length());
+ test::CompareCharArraysWithHexError(
+ "authentication tag", encrypted->data() + ct.length(), tag.length(),
+ tag.data(), tag.length());
+ }
+ }
+}
+
+TEST(Aes128Gcm12EncrypterTest, GetMaxPlaintextSize) {
+ Aes128Gcm12Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST(Aes128Gcm12EncrypterTest, GetCiphertextSize) {
+ Aes128Gcm12Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/cert_compressor.cc b/net/quic/crypto/cert_compressor.cc
new file mode 100644
index 0000000..ba42152
--- /dev/null
+++ b/net/quic/crypto/cert_compressor.cc
@@ -0,0 +1,646 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/cert_compressor.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_utils.h"
+#include "third_party/zlib/zlib.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+// kCommonCertSubstrings contains ~1500 bytes of common certificate substrings
+// in order to help zlib. This was generated via a fairly dumb algorithm from
+// the Alexa Top 5000 set - we could probably do better.
+static const unsigned char kCommonCertSubstrings[] = {
+ 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30,
+ 0x5f, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01,
+ 0x06, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6d, 0x01, 0x07,
+ 0x17, 0x01, 0x30, 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
+ 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x53, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x34,
+ 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x32, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x2d, 0x61, 0x69, 0x61, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x45, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x2e, 0x63, 0x65,
+ 0x72, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4a, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x7b, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd2,
+ 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xb4, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x30, 0x0b, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+ 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74,
+ 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33,
+ 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55,
+ 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x1d, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86,
+ 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x67, 0x64, 0x73, 0x31, 0x2d, 0x32, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x70, 0x73, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17,
+ 0x0d, 0x31, 0x33, 0x30, 0x35, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x73, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x17, 0x06, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x53, 0x31, 0x17,
+ 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30, 0x39,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d, 0x73,
+ 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20, 0x68,
+ 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x47, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x3c, 0x02, 0x01,
+ 0x03, 0x13, 0x02, 0x55, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x14, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0f, 0x13, 0x14, 0x50,
+ 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, 0x61,
+ 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x14, 0x31, 0x31,
+ 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x53, 0x65, 0x65,
+ 0x20, 0x77, 0x77, 0x77, 0x2e, 0x72, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x2e, 0x67, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
+ 0x69, 0x67, 0x6e, 0x31, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x45, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x64, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x49, 0x6e, 0x74, 0x6c, 0x2d, 0x63, 0x63, 0x72,
+ 0x74, 0x2e, 0x67, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x39, 0x72, 0x61, 0x70, 0x69, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2f, 0x30, 0x81, 0x80, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x74, 0x30, 0x72, 0x30, 0x24, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64,
+ 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x4a, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64,
+ 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74,
+ 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72,
+ 0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee,
+ 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x27,
+ 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x86, 0x30,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x73,
+};
+
+// CertEntry represents a certificate in compressed form. Each entry is one of
+// the three types enumerated in |Type|.
+struct CertEntry {
+ public:
+ enum Type {
+ // Type 0 is reserved to mean "end of list" in the wire format.
+
+ // COMPRESSED means that the certificate is included in the trailing zlib
+ // data.
+ COMPRESSED = 1,
+ // CACHED means that the certificate is already known to the peer and will
+ // be replaced by its 64-bit hash (in |hash|).
+ CACHED = 2,
+ // COMMON means that the certificate is in a common certificate set known
+ // to the peer with hash |set_hash| and certificate index |index|.
+ COMMON = 3,
+ };
+
+ Type type;
+ uint64 hash;
+ uint64 set_hash;
+ uint32 index;
+};
+
+// MatchCerts returns a vector of CertEntries describing how to most
+// efficiently represent |certs| to a peer who has the common sets identified
+// by |client_common_set_hashes| and who has cached the certificates with the
+// 64-bit, FNV-1a hashes in |client_cached_cert_hashes|.
+vector<CertEntry> MatchCerts(const vector<string>& certs,
+ StringPiece client_common_set_hashes,
+ StringPiece client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ vector<CertEntry> entries;
+ entries.reserve(certs.size());
+
+ const bool cached_valid =
+ client_cached_cert_hashes.size() % sizeof(uint64) == 0 &&
+ !client_cached_cert_hashes.empty();
+
+ for (vector<string>::const_iterator i = certs.begin();
+ i != certs.end(); ++i) {
+ CertEntry entry;
+
+ if (cached_valid) {
+ bool cached = false;
+
+ uint64 hash = QuicUtils::FNV1a_64_Hash(i->data(), i->size());
+ // This assumes that the machine is little-endian.
+ for (size_t i = 0; i < client_cached_cert_hashes.size();
+ i += sizeof(uint64)) {
+ uint64 cached_hash;
+ memcpy(&cached_hash, client_cached_cert_hashes.data() + i,
+ sizeof(uint64));
+ if (hash != cached_hash) {
+ continue;
+ }
+
+ entry.type = CertEntry::CACHED;
+ entry.hash = hash;
+ entries.push_back(entry);
+ cached = true;
+ break;
+ }
+
+ if (cached) {
+ continue;
+ }
+ }
+
+ if (common_sets && common_sets->MatchCert(*i, client_common_set_hashes,
+ &entry.set_hash, &entry.index)) {
+ entry.type = CertEntry::COMMON;
+ entries.push_back(entry);
+ continue;
+ }
+
+ entry.type = CertEntry::COMPRESSED;
+ entries.push_back(entry);
+ }
+
+ return entries;
+}
+
+// CertEntriesSize returns the size, in bytes, of the serialised form of
+// |entries|.
+size_t CertEntriesSize(const vector<CertEntry>& entries) {
+ size_t entries_size = 0;
+
+ for (vector<CertEntry>::const_iterator i = entries.begin();
+ i != entries.end(); ++i) {
+ entries_size++;
+ switch (i->type) {
+ case CertEntry::COMPRESSED:
+ break;
+ case CertEntry::CACHED:
+ entries_size += sizeof(uint64);
+ break;
+ case CertEntry::COMMON:
+ entries_size += sizeof(uint64) + sizeof(uint32);
+ break;
+ }
+ }
+
+ entries_size++; // for end marker
+
+ return entries_size;
+}
+
+// SerializeCertEntries serialises |entries| to |out|, which must have enough
+// space to contain them.
+void SerializeCertEntries(uint8* out, const vector<CertEntry>& entries) {
+ for (vector<CertEntry>::const_iterator i = entries.begin();
+ i != entries.end(); ++i) {
+ *out++ = i->type;
+ switch (i->type) {
+ case CertEntry::COMPRESSED:
+ break;
+ case CertEntry::CACHED:
+ memcpy(out, &i->hash, sizeof(i->hash));
+ out += sizeof(uint64);
+ break;
+ case CertEntry::COMMON:
+ // Assumes a little-endian machine.
+ memcpy(out, &i->set_hash, sizeof(i->set_hash));
+ out += sizeof(i->set_hash);
+ memcpy(out, &i->index, sizeof(uint32));
+ out += sizeof(uint32);
+ break;
+ }
+ }
+
+ *out++ = 0; // end marker
+}
+
+// ZlibDictForEntries returns a string that contains the zlib pre-shared
+// dictionary to use in order to decompress a zlib block following |entries|.
+// |certs| is one-to-one with |entries| and contains the certificates for those
+// entries that are CACHED or COMMON.
+string ZlibDictForEntries(const vector<CertEntry>& entries,
+ const vector<string>& certs) {
+ string zlib_dict;
+
+ // The dictionary starts with the common and cached certs in reverse order.
+ size_t zlib_dict_size = 0;
+ for (size_t i = certs.size() - 1; i < certs.size(); i--) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ zlib_dict_size += certs[i].size();
+ }
+ }
+
+ // At the end of the dictionary is a block of common certificate substrings.
+ zlib_dict_size += sizeof(kCommonCertSubstrings);
+
+ zlib_dict.reserve(zlib_dict_size);
+
+ for (size_t i = certs.size() - 1; i < certs.size(); i--) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ zlib_dict += certs[i];
+ }
+ }
+
+ zlib_dict += string(reinterpret_cast<const char*>(kCommonCertSubstrings),
+ sizeof(kCommonCertSubstrings));
+
+ DCHECK_EQ(zlib_dict.size(), zlib_dict_size);
+
+ return zlib_dict;
+}
+
+// HashCerts returns the FNV-1a hashes of |certs|.
+vector<uint64> HashCerts(const vector<string>& certs) {
+ vector<uint64> ret;
+ ret.reserve(certs.size());
+
+ for (vector<string>::const_iterator i = certs.begin();
+ i != certs.end(); ++i) {
+ ret.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size()));
+ }
+
+ return ret;
+}
+
+// ParseEntries parses the serialised form of a vector of CertEntries from
+// |in_out| and writes them to |out_entries|. CACHED and COMMON entries are
+// resolved using |cached_certs| and |common_sets| and written to |out_certs|.
+// |in_out| is updated to contain the trailing data.
+bool ParseEntries(StringPiece* in_out,
+ const vector<string>& cached_certs,
+ const CommonCertSets* common_sets,
+ vector<CertEntry>* out_entries,
+ vector<string>* out_certs) {
+ StringPiece in = *in_out;
+ vector<uint64> cached_hashes;
+
+ out_entries->clear();
+ out_certs->clear();
+
+ for (;;) {
+ if (in.empty()) {
+ return false;
+ }
+ CertEntry entry;
+ const uint8 type_byte = in[0];
+ in.remove_prefix(1);
+
+ if (type_byte == 0) {
+ break;
+ }
+
+ entry.type = static_cast<CertEntry::Type>(type_byte);
+
+ switch (entry.type) {
+ case CertEntry::COMPRESSED:
+ out_certs->push_back(string());
+ break;
+ case CertEntry::CACHED: {
+ if (in.size() < sizeof(uint64)) {
+ return false;
+ }
+ memcpy(&entry.hash, in.data(), sizeof(uint64));
+ in.remove_prefix(sizeof(uint64));
+
+ if (cached_hashes.size() != cached_certs.size()) {
+ cached_hashes = HashCerts(cached_certs);
+ }
+ bool found = false;
+ for (size_t i = 0; i < cached_hashes.size(); i++) {
+ if (cached_hashes[i] == entry.hash) {
+ out_certs->push_back(cached_certs[i]);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ break;
+ }
+ case CertEntry::COMMON: {
+ if (!common_sets) {
+ return false;
+ }
+ if (in.size() < sizeof(uint64) + sizeof(uint32)) {
+ return false;
+ }
+ memcpy(&entry.set_hash, in.data(), sizeof(uint64));
+ in.remove_prefix(sizeof(uint64));
+ memcpy(&entry.index, in.data(), sizeof(uint32));
+ in.remove_prefix(sizeof(uint32));
+
+ StringPiece cert = common_sets->GetCert(entry.set_hash, entry.index);
+ if (cert.empty()) {
+ return false;
+ }
+ out_certs->push_back(cert.as_string());
+ break;
+ }
+ default:
+ return false;
+ }
+ out_entries->push_back(entry);
+ }
+
+ *in_out = in;
+ return true;
+}
+
+// ScopedZLib deals with the automatic destruction of a zlib context.
+class ScopedZLib {
+ public:
+ enum Type {
+ INFLATE,
+ DEFLATE,
+ };
+
+ explicit ScopedZLib(Type type) : z_(nullptr), type_(type) {}
+
+ void reset(z_stream* z) {
+ Clear();
+ z_ = z;
+ }
+
+ ~ScopedZLib() {
+ Clear();
+ }
+
+ private:
+ void Clear() {
+ if (!z_) {
+ return;
+ }
+
+ if (type_ == DEFLATE) {
+ deflateEnd(z_);
+ } else {
+ inflateEnd(z_);
+ }
+ z_ = nullptr;
+ }
+
+ z_stream* z_;
+ const Type type_;
+};
+
+} // anonymous namespace
+
+
+// static
+string CertCompressor::CompressChain(const vector<string>& certs,
+ StringPiece client_common_set_hashes,
+ StringPiece client_cached_cert_hashes,
+ const CommonCertSets* common_sets) {
+ const vector<CertEntry> entries = MatchCerts(
+ certs, client_common_set_hashes, client_cached_cert_hashes, common_sets);
+ DCHECK_EQ(entries.size(), certs.size());
+
+ size_t uncompressed_size = 0;
+ for (size_t i = 0; i < entries.size(); i++) {
+ if (entries[i].type == CertEntry::COMPRESSED) {
+ uncompressed_size += 4 /* uint32 length */ + certs[i].size();
+ }
+ }
+
+ size_t compressed_size = 0;
+ z_stream z;
+ ScopedZLib scoped_z(ScopedZLib::DEFLATE);
+
+ if (uncompressed_size > 0) {
+ memset(&z, 0, sizeof(z));
+ int rv = deflateInit(&z, Z_DEFAULT_COMPRESSION);
+ DCHECK_EQ(Z_OK, rv);
+ if (rv != Z_OK) {
+ return "";
+ }
+ scoped_z.reset(&z);
+
+ string zlib_dict = ZlibDictForEntries(entries, certs);
+
+ rv = deflateSetDictionary(&z, reinterpret_cast<const uint8*>(&zlib_dict[0]),
+ zlib_dict.size());
+ DCHECK_EQ(Z_OK, rv);
+ if (rv != Z_OK) {
+ return "";
+ }
+
+ compressed_size = deflateBound(&z, uncompressed_size);
+ }
+
+ const size_t entries_size = CertEntriesSize(entries);
+
+ string result;
+ result.resize(entries_size + (uncompressed_size > 0 ? 4 : 0) +
+ compressed_size);
+
+ uint8* j = reinterpret_cast<uint8*>(&result[0]);
+ SerializeCertEntries(j, entries);
+ j += entries_size;
+
+ if (uncompressed_size == 0) {
+ return result;
+ }
+
+ uint32 uncompressed_size_32 = uncompressed_size;
+ memcpy(j, &uncompressed_size_32, sizeof(uint32));
+ j += sizeof(uint32);
+
+ int rv;
+
+ z.next_out = j;
+ z.avail_out = compressed_size;
+
+ for (size_t i = 0; i < certs.size(); i++) {
+ if (entries[i].type != CertEntry::COMPRESSED) {
+ continue;
+ }
+
+ uint32 length32 = certs[i].size();
+ z.next_in = reinterpret_cast<uint8*>(&length32);
+ z.avail_in = sizeof(length32);
+ rv = deflate(&z, Z_NO_FLUSH);
+ DCHECK_EQ(Z_OK, rv);
+ DCHECK_EQ(0u, z.avail_in);
+ if (rv != Z_OK || z.avail_in) {
+ return "";
+ }
+
+ z.next_in =
+ const_cast<uint8*>(reinterpret_cast<const uint8*>(certs[i].data()));
+ z.avail_in = certs[i].size();
+ rv = deflate(&z, Z_NO_FLUSH);
+ DCHECK_EQ(Z_OK, rv);
+ DCHECK_EQ(0u, z.avail_in);
+ if (rv != Z_OK || z.avail_in) {
+ return "";
+ }
+ }
+
+ z.avail_in = 0;
+ rv = deflate(&z, Z_FINISH);
+ DCHECK_EQ(Z_STREAM_END, rv);
+ if (rv != Z_STREAM_END) {
+ return "";
+ }
+
+ result.resize(result.size() - z.avail_out);
+ return result;
+}
+
+// static
+bool CertCompressor::DecompressChain(StringPiece in,
+ const vector<string>& cached_certs,
+ const CommonCertSets* common_sets,
+ vector<string>* out_certs) {
+ vector<CertEntry> entries;
+ if (!ParseEntries(&in, cached_certs, common_sets, &entries, out_certs)) {
+ return false;
+ }
+ DCHECK_EQ(entries.size(), out_certs->size());
+
+ scoped_ptr<uint8[]> uncompressed_data;
+ StringPiece uncompressed;
+
+ if (!in.empty()) {
+ if (in.size() < sizeof(uint32)) {
+ return false;
+ }
+
+ uint32 uncompressed_size;
+ memcpy(&uncompressed_size, in.data(), sizeof(uncompressed_size));
+ in.remove_prefix(sizeof(uint32));
+
+ if (uncompressed_size > 128 * 1024) {
+ return false;
+ }
+
+ uncompressed_data.reset(new uint8[uncompressed_size]);
+ z_stream z;
+ ScopedZLib scoped_z(ScopedZLib::INFLATE);
+
+ memset(&z, 0, sizeof(z));
+ z.next_out = uncompressed_data.get();
+ z.avail_out = uncompressed_size;
+ z.next_in = const_cast<uint8*>(reinterpret_cast<const uint8*>(in.data()));
+ z.avail_in = in.size();
+
+ if (Z_OK != inflateInit(&z)) {
+ return false;
+ }
+ scoped_z.reset(&z);
+
+ int rv = inflate(&z, Z_FINISH);
+ if (rv == Z_NEED_DICT) {
+ string zlib_dict = ZlibDictForEntries(entries, *out_certs);
+ const uint8* dict = reinterpret_cast<const uint8*>(zlib_dict.data());
+ if (Z_OK != inflateSetDictionary(&z, dict, zlib_dict.size())) {
+ return false;
+ }
+ rv = inflate(&z, Z_FINISH);
+ }
+
+ if (Z_STREAM_END != rv || z.avail_out > 0 || z.avail_in > 0) {
+ return false;
+ }
+
+ uncompressed = StringPiece(reinterpret_cast<char*>(uncompressed_data.get()),
+ uncompressed_size);
+ }
+
+ for (size_t i = 0; i < entries.size(); i++) {
+ switch (entries[i].type) {
+ case CertEntry::COMPRESSED:
+ if (uncompressed.size() < sizeof(uint32)) {
+ return false;
+ }
+ uint32 cert_len;
+ memcpy(&cert_len, uncompressed.data(), sizeof(cert_len));
+ uncompressed.remove_prefix(sizeof(uint32));
+ if (uncompressed.size() < cert_len) {
+ return false;
+ }
+ (*out_certs)[i] = uncompressed.substr(0, cert_len).as_string();
+ uncompressed.remove_prefix(cert_len);
+ break;
+ case CertEntry::CACHED:
+ case CertEntry::COMMON:
+ break;
+ }
+ }
+
+ if (!uncompressed.empty()) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/cert_compressor.h b/net/quic/crypto/cert_compressor.h
new file mode 100644
index 0000000..d95c5bc
--- /dev/null
+++ b/net/quic/crypto/cert_compressor.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CERT_COMPRESSOR_H_
+#define NET_QUIC_CRYPTO_CERT_COMPRESSOR_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/common_cert_set.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+namespace net {
+
+// CertCompressor provides functions for compressing and decompressing
+// certificate chains using three techniquies:
+// 1) The peer may provide a list of a 64-bit, FNV-1a hashes of certificates
+// that they already have. In the event that one of them is to be
+// compressed, it can be replaced with just the hash.
+// 2) The peer may provide a number of hashes that represent sets of
+// pre-shared certificates (CommonCertSets). If one of those certificates
+// is to be compressed, and it's known to the given CommonCertSets, then it
+// can be replaced with a set hash and certificate index.
+// 3) Otherwise the certificates are compressed with zlib using a pre-shared
+// dictionary that consists of the certificates handled with the above
+// methods and a small chunk of common substrings.
+class NET_EXPORT_PRIVATE CertCompressor {
+ public:
+ // CompressChain compresses the certificates in |certs| and returns a
+ // compressed representation. |common_sets| contains the common certificate
+ // sets known locally and |client_common_set_hashes| contains the hashes of
+ // the common sets known to the peer. |client_cached_cert_hashes| contains
+ // 64-bit, FNV-1a hashes of certificates that the peer already possesses.
+ static std::string CompressChain(const std::vector<std::string>& certs,
+ base::StringPiece client_common_set_hashes,
+ base::StringPiece client_cached_cert_hashes,
+ const CommonCertSets* common_sets);
+
+ // DecompressChain decompresses the result of |CompressChain|, given in |in|,
+ // into a series of certificates that are written to |out_certs|.
+ // |cached_certs| contains certificates that the peer may have omitted and
+ // |common_sets| contains the common certificate sets known locally.
+ static bool DecompressChain(base::StringPiece in,
+ const std::vector<std::string>& cached_certs,
+ const CommonCertSets* common_sets,
+ std::vector<std::string>* out_certs);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CertCompressor);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CERT_COMPRESSOR_H_
diff --git a/net/quic/crypto/cert_compressor_test.cc b/net/quic/crypto/cert_compressor_test.cc
new file mode 100644
index 0000000..1b3c28a
--- /dev/null
+++ b/net/quic/crypto/cert_compressor_test.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/cert_compressor.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+TEST(CertCompressor, EmptyChain) {
+ vector<string> chain;
+ const string compressed = CertCompressor::CompressChain(
+ chain, StringPiece(), StringPiece(), nullptr);
+ EXPECT_EQ("00", base::HexEncode(compressed.data(), compressed.size()));
+
+ vector<string> chain2, cached_certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
+ &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+}
+
+TEST(CertCompressor, Compressed) {
+ vector<string> chain;
+ chain.push_back("testcert");
+ const string compressed = CertCompressor::CompressChain(
+ chain, StringPiece(), StringPiece(), nullptr);
+ ASSERT_GE(compressed.size(), 2u);
+ EXPECT_EQ("0100", base::HexEncode(compressed.substr(0, 2).data(), 2));
+
+ vector<string> chain2, cached_certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
+ &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST(CertCompressor, Common) {
+ vector<string> chain;
+ chain.push_back("testcert");
+ static const uint64 set_hash = 42;
+ scoped_ptr<CommonCertSets> common_sets(
+ CryptoTestUtils::MockCommonCertSets(chain[0], set_hash, 1));
+ const string compressed = CertCompressor::CompressChain(
+ chain,
+ StringPiece(reinterpret_cast<const char*>(&set_hash), sizeof(set_hash)),
+ StringPiece(), common_sets.get());
+ const string common("03" /* common */
+ "2A00000000000000" /* set hash 42 */
+ "01000000" /* index 1 */
+ "00" /* end of list */);
+ EXPECT_EQ(common.data(),
+ base::HexEncode(compressed.data(), compressed.size()));
+
+ vector<string> chain2, cached_certs;
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs,
+ common_sets.get(), &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST(CertCompressor, Cached) {
+ vector<string> chain;
+ chain.push_back("testcert");
+ uint64 hash = QuicUtils::FNV1a_64_Hash(chain[0].data(), chain[0].size());
+ StringPiece hash_bytes(reinterpret_cast<char*>(&hash), sizeof(hash));
+ const string compressed =
+ CertCompressor::CompressChain(chain, StringPiece(), hash_bytes, nullptr);
+
+ EXPECT_EQ("02" /* cached */ +
+ base::HexEncode(hash_bytes.data(), hash_bytes.size()) +
+ "00" /* end of list */,
+ base::HexEncode(compressed.data(), compressed.size()));
+
+ vector<string> cached_certs, chain2;
+ cached_certs.push_back(chain[0]);
+ ASSERT_TRUE(CertCompressor::DecompressChain(compressed, cached_certs, nullptr,
+ &chain2));
+ EXPECT_EQ(chain.size(), chain2.size());
+ EXPECT_EQ(chain[0], chain2[0]);
+}
+
+TEST(CertCompressor, BadInputs) {
+ vector<string> cached_certs, chain;
+
+ /* bad entry type */
+ const string bad_entry("04");
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ base::HexEncode(bad_entry.data(), bad_entry.size()),
+ cached_certs, nullptr, &chain));
+
+ /* no terminator */
+ const string no_terminator("01");
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ base::HexEncode(no_terminator.data(), no_terminator.size()),
+ cached_certs, nullptr, &chain));
+
+ /* hash truncated */
+ const string hash_truncated("0200");
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ base::HexEncode(hash_truncated.data(), hash_truncated.size()),
+ cached_certs, nullptr, &chain));
+
+ /* hash and index truncated */
+ const string hash_and_index_truncated("0300");
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ base::HexEncode(hash_and_index_truncated.data(),
+ hash_and_index_truncated.size()),
+ cached_certs, nullptr, &chain));
+
+ /* without a CommonCertSets */
+ const string without_a_common_cert_set(
+ "03" "0000000000000000" "00000000");
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ base::HexEncode(without_a_common_cert_set.data(),
+ without_a_common_cert_set.size()),
+ cached_certs, nullptr, &chain));
+
+ scoped_ptr<CommonCertSets> common_sets(
+ CryptoTestUtils::MockCommonCertSets("foo", 42, 1));
+
+ /* incorrect hash and index */
+ const string incorrect_hash_and_index(
+ "03" "a200000000000000" "00000000");
+ EXPECT_FALSE(CertCompressor::DecompressChain(
+ base::HexEncode(incorrect_hash_and_index.data(),
+ incorrect_hash_and_index.size()),
+ cached_certs, nullptr, &chain));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/chacha20_poly1305_decrypter.h b/net/quic/crypto/chacha20_poly1305_decrypter.h
new file mode 100644
index 0000000..9d24ba2
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_decrypter.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
+
+#include "net/quic/crypto/aead_base_decrypter.h"
+
+namespace net {
+
+// A ChaCha20Poly1305Decrypter is a QuicDecrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in
+// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305
+// authenticator to 12 bytes. Create an instance by calling
+// QuicDecrypter::Create(kCC12).
+//
+// It uses an authentication tag of 16 bytes (128 bits). There is no
+// fixed nonce prefix.
+class NET_EXPORT_PRIVATE ChaCha20Poly1305Decrypter : public AeadBaseDecrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Decrypter();
+ virtual ~ChaCha20Poly1305Decrypter();
+
+ // Returns true if the underlying crypto library supports ChaCha20+Poly1305.
+ static bool IsSupported();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseDecrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Decrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_DECRYPTER_H_
diff --git a/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc b/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc
new file mode 100644
index 0000000..fbeaaeb
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_decrypter_nss.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+#if defined(USE_NSS)
+
+// System NSS doesn't support ChaCha20+Poly1305 yet.
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(CKM_INVALID_MECHANISM, nullptr, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ NOTIMPLEMENTED();
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() {
+ return false;
+}
+
+void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ NOTIMPLEMENTED();
+}
+
+#else // defined(USE_NSS)
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Decrypt, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() {
+ return true;
+}
+
+void ChaCha20Poly1305Decrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.nss_aead_params);
+ CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params;
+ nss_aead_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ nss_aead_params->ulIvLen = nonce.size();
+ nss_aead_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ nss_aead_params->ulAADLen = associated_data.size();
+ nss_aead_params->ulTagLen = auth_tag_size;
+}
+
+#endif // defined(USE_NSS)
+
+} // namespace net
diff --git a/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc b/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc
new file mode 100644
index 0000000..7f0e24d
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_decrypter_openssl.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
+ : AeadBaseDecrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Decrypter::~ChaCha20Poly1305Decrypter() {}
+
+// static
+bool ChaCha20Poly1305Decrypter::IsSupported() { return true; }
+
+} // namespace net
diff --git a/net/quic/crypto/chacha20_poly1305_decrypter_test.cc b/net/quic/crypto/chacha20_poly1305_decrypter_test.cc
new file mode 100644
index 0000000..e83acc0
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_decrypter_test.cc
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7.
+
+// Each test vector consists of six strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ // Input:
+ const char* key;
+ const char* iv;
+ const char* aad;
+ const char* ct;
+
+ // Expected output:
+ const char* pt; // An empty string "" means decryption succeeded and
+ // the plaintext is zero-length. NULL means decryption
+ // failed.
+};
+
+const TestVector test_vectors[] = {
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ "86d09974840bded2a5ca"
+ },
+ // Modify the ciphertext (ChaCha20 encryption output).
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "f3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ // Modify the ciphertext (Poly1305 authenticator).
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28476", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ // Modify the associated data.
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "dd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475", // "3896e1d6" truncated.
+ NULL // FAIL
+ },
+ { NULL }
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// DecryptWithNonce wraps the |Decrypt| method of |decrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the plaintext.
+QuicData* DecryptWithNonce(ChaCha20Poly1305Decrypter* decrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ size_t plaintext_size = ciphertext.length();
+ scoped_ptr<char[]> plaintext(new char[plaintext_size]);
+
+ if (!decrypter->Decrypt(nonce, associated_data, ciphertext,
+ reinterpret_cast<unsigned char*>(plaintext.get()),
+ &plaintext_size)) {
+ return nullptr;
+ }
+ return new QuicData(plaintext.release(), plaintext_size, true);
+}
+
+TEST(ChaCha20Poly1305DecrypterTest, Decrypt) {
+ if (!ChaCha20Poly1305Decrypter::IsSupported()) {
+ LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped.";
+ return;
+ }
+
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // If not present then decryption is expected to fail.
+ bool has_pt = test_vectors[i].pt;
+
+ // Decode the test vector.
+ string key;
+ string iv;
+ string aad;
+ string ct;
+ string pt;
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct));
+ if (has_pt) {
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt));
+ }
+
+ ChaCha20Poly1305Decrypter decrypter;
+ ASSERT_TRUE(decrypter.SetKey(key));
+ scoped_ptr<QuicData> decrypted(DecryptWithNonce(
+ &decrypter, iv,
+ // This deliberately tests that the decrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ StringPiece(aad.length() ? aad.data() : nullptr, aad.length()), ct));
+ if (!decrypted.get()) {
+ EXPECT_FALSE(has_pt);
+ continue;
+ }
+ EXPECT_TRUE(has_pt);
+
+ ASSERT_EQ(pt.length(), decrypted->length());
+ test::CompareCharArraysWithHexError("plaintext", decrypted->data(),
+ pt.length(), pt.data(), pt.length());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/chacha20_poly1305_encrypter.h b/net/quic/crypto/chacha20_poly1305_encrypter.h
new file mode 100644
index 0000000..4a68caa
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_encrypter.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
+
+#include "net/quic/crypto/aead_base_encrypter.h"
+
+namespace net {
+
+// A ChaCha20Poly1305Encrypter is a QuicEncrypter that implements the
+// AEAD_CHACHA20_POLY1305 algorithm specified in
+// draft-agl-tls-chacha20poly1305-04, except that it truncates the Poly1305
+// authenticator to 12 bytes. Create an instance by calling
+// QuicEncrypter::Create(kCC12).
+//
+// It uses an authentication tag of 16 bytes (128 bits). There is no
+// fixed nonce prefix.
+class NET_EXPORT_PRIVATE ChaCha20Poly1305Encrypter : public AeadBaseEncrypter {
+ public:
+ enum {
+ kAuthTagSize = 12,
+ };
+
+ ChaCha20Poly1305Encrypter();
+ virtual ~ChaCha20Poly1305Encrypter();
+
+ // Returns true if the underlying crypto library supports ChaCha20+Poly1305.
+ static bool IsSupported();
+
+#if !defined(USE_OPENSSL)
+ protected:
+ // AeadBaseEncrypter methods:
+ virtual void FillAeadParams(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const OVERRIDE;
+#endif
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChaCha20Poly1305Encrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
diff --git a/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc b/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc
new file mode 100644
index 0000000..40cd896
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_encrypter_nss.cc
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+
+#include <pk11pub.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+#if defined(USE_NSS)
+
+// System NSS doesn't support ChaCha20+Poly1305 yet.
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(CKM_INVALID_MECHANISM, nullptr, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ NOTIMPLEMENTED();
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() {
+ return false;
+}
+
+void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ NOTIMPLEMENTED();
+}
+
+#else // defined(USE_NSS)
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(CKM_NSS_CHACHA20_POLY1305, PK11_Encrypt, kKeySize,
+ kAuthTagSize, kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() {
+ return true;
+}
+
+void ChaCha20Poly1305Encrypter::FillAeadParams(StringPiece nonce,
+ StringPiece associated_data,
+ size_t auth_tag_size,
+ AeadParams* aead_params) const {
+ aead_params->len = sizeof(aead_params->data.nss_aead_params);
+ CK_NSS_AEAD_PARAMS* nss_aead_params = &aead_params->data.nss_aead_params;
+ nss_aead_params->pIv =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(nonce.data()));
+ nss_aead_params->ulIvLen = nonce.size();
+ nss_aead_params->pAAD =
+ reinterpret_cast<CK_BYTE*>(const_cast<char*>(associated_data.data()));
+ nss_aead_params->ulAADLen = associated_data.size();
+ nss_aead_params->ulTagLen = auth_tag_size;
+}
+
+#endif // defined(USE_NSS)
+
+} // namespace net
diff --git a/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc b/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc
new file mode 100644
index 0000000..e256c2a
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_encrypter_openssl.cc
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+
+#include <openssl/evp.h>
+
+namespace net {
+
+namespace {
+
+const size_t kKeySize = 32;
+const size_t kNoncePrefixSize = 0;
+
+} // namespace
+
+ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
+ : AeadBaseEncrypter(EVP_aead_chacha20_poly1305(), kKeySize, kAuthTagSize,
+ kNoncePrefixSize) {
+ COMPILE_ASSERT(kKeySize <= kMaxKeySize, key_size_too_big);
+ COMPILE_ASSERT(kNoncePrefixSize <= kMaxNoncePrefixSize,
+ nonce_prefix_size_too_big);
+}
+
+ChaCha20Poly1305Encrypter::~ChaCha20Poly1305Encrypter() {}
+
+// static
+bool ChaCha20Poly1305Encrypter::IsSupported() { return true; }
+
+} // namespace net
diff --git a/net/quic/crypto/chacha20_poly1305_encrypter_test.cc b/net/quic/crypto/chacha20_poly1305_encrypter_test.cc
new file mode 100644
index 0000000..4f33337
--- /dev/null
+++ b/net/quic/crypto/chacha20_poly1305_encrypter_test.cc
@@ -0,0 +1,108 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace {
+
+// The test vectors come from draft-agl-tls-chacha20poly1305-04 Section 7.
+
+// Each test vector consists of five strings of lowercase hexadecimal digits.
+// The strings may be empty (zero length). A test vector with a NULL |key|
+// marks the end of an array of test vectors.
+struct TestVector {
+ const char* key;
+ const char* pt;
+ const char* iv;
+ const char* aad;
+ const char* ct;
+};
+
+const TestVector test_vectors[] = {
+ { "4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd110"
+ "0a1007",
+ "86d09974840bded2a5ca",
+ "cd7cf67be39c794a",
+ "87e229d4500845a079c0",
+ "e3e446f7ede9a19b62a4677dabf4e3d24b876bb28475" // "3896e1d6" truncated.
+ },
+ { NULL }
+};
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// EncryptWithNonce wraps the |Encrypt| method of |encrypter| to allow passing
+// in an nonce and also to allocate the buffer needed for the ciphertext.
+QuicData* EncryptWithNonce(ChaCha20Poly1305Encrypter* encrypter,
+ StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+ scoped_ptr<char[]> ciphertext(new char[ciphertext_size]);
+
+ if (!encrypter->Encrypt(nonce, associated_data, plaintext,
+ reinterpret_cast<unsigned char*>(ciphertext.get()))) {
+ return nullptr;
+ }
+
+ return new QuicData(ciphertext.release(), ciphertext_size, true);
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, Encrypt) {
+ if (!ChaCha20Poly1305Encrypter::IsSupported()) {
+ LOG(INFO) << "ChaCha20+Poly1305 not supported. Test skipped.";
+ return;
+ }
+
+ for (size_t i = 0; test_vectors[i].key != nullptr; i++) {
+ // Decode the test vector.
+ string key;
+ string pt;
+ string iv;
+ string aad;
+ string ct;
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].key, &key));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].pt, &pt));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].iv, &iv));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].aad, &aad));
+ ASSERT_TRUE(DecodeHexString(test_vectors[i].ct, &ct));
+
+ ChaCha20Poly1305Encrypter encrypter;
+ ASSERT_TRUE(encrypter.SetKey(key));
+ scoped_ptr<QuicData> encrypted(EncryptWithNonce(
+ &encrypter, iv,
+ // This deliberately tests that the encrypter can handle an AAD that
+ // is set to nullptr, as opposed to a zero-length, non-nullptr pointer.
+ StringPiece(aad.length() ? aad.data() : nullptr, aad.length()), pt));
+ ASSERT_TRUE(encrypted.get());
+
+ test::CompareCharArraysWithHexError("ciphertext", encrypted->data(),
+ encrypted->length(), ct.data(),
+ ct.length());
+ }
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, GetMaxPlaintextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST(ChaCha20Poly1305EncrypterTest, GetCiphertextSize) {
+ ChaCha20Poly1305Encrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/channel_id.cc b/net/quic/crypto/channel_id.cc
new file mode 100644
index 0000000..e707bf0
--- /dev/null
+++ b/net/quic/crypto/channel_id.cc
@@ -0,0 +1,14 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/channel_id.h"
+
+namespace net {
+
+// static
+const char ChannelIDVerifier::kContextStr[] = "QUIC ChannelID";
+// static
+const char ChannelIDVerifier::kClientToServerStr[] = "client -> server";
+
+} // namespace net
diff --git a/net/quic/crypto/channel_id.h b/net/quic/crypto/channel_id.h
new file mode 100644
index 0000000..bd335fb
--- /dev/null
+++ b/net/quic/crypto/channel_id.h
@@ -0,0 +1,98 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CHANNEL_ID_H_
+#define NET_QUIC_CRYPTO_CHANNEL_ID_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_types.h"
+
+namespace net {
+
+// ChannelIDKey is an interface that supports signing with and serializing a
+// ChannelID key.
+class NET_EXPORT_PRIVATE ChannelIDKey {
+ public:
+ virtual ~ChannelIDKey() {}
+
+ // Sign signs |signed_data| using the ChannelID private key and puts the
+ // signature into |out_signature|. It returns true on success.
+ virtual bool Sign(base::StringPiece signed_data,
+ std::string* out_signature) const = 0;
+
+ // SerializeKey returns the serialized ChannelID public key.
+ virtual std::string SerializeKey() const = 0;
+};
+
+// ChannelIDSourceCallback provides a generic mechanism for a ChannelIDSource
+// to call back after an asynchronous GetChannelIDKey operation.
+class ChannelIDSourceCallback {
+ public:
+ virtual ~ChannelIDSourceCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous GetChannelIDKey operation. If |*channel_id_key| is not nullptr
+ // then the channel ID lookup is successful. |Run| may take ownership of
+ // |*channel_id_key| by calling |release| on it.
+ virtual void Run(scoped_ptr<ChannelIDKey>* channel_id_key) = 0;
+};
+
+// ChannelIDSource is an abstract interface by which a QUIC client can obtain
+// a ChannelIDKey for a given hostname.
+class NET_EXPORT_PRIVATE ChannelIDSource {
+ public:
+ virtual ~ChannelIDSource() {}
+
+ // GetChannelIDKey looks up the ChannelIDKey for |hostname|. On success it
+ // returns QUIC_SUCCESS and stores the ChannelIDKey in |*channel_id_key|,
+ // which the caller takes ownership of. On failure, it returns QUIC_FAILURE.
+ //
+ // This function may also return QUIC_PENDING, in which case the
+ // ChannelIDSource will call back, on the original thread, via |callback|
+ // when complete. In this case, the ChannelIDSource will take ownership of
+ // |callback|.
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) = 0;
+};
+
+// ChannelIDVerifier verifies ChannelID signatures.
+class NET_EXPORT_PRIVATE ChannelIDVerifier {
+ public:
+ // kContextStr is prepended to the data to be signed in order to ensure that
+ // a ChannelID signature cannot be used in a different context. (The
+ // terminating NUL byte is inclued.)
+ static const char kContextStr[];
+ // kClientToServerStr follows kContextStr to specify that the ChannelID is
+ // being used in the client to server direction. (The terminating NUL byte is
+ // included.)
+ static const char kClientToServerStr[];
+
+ // Verify returns true iff |signature| is a valid signature of |signed_data|
+ // by |key|.
+ static bool Verify(base::StringPiece key,
+ base::StringPiece signed_data,
+ base::StringPiece signature);
+
+ // FOR TESTING ONLY: VerifyRaw returns true iff |signature| is a valid
+ // signature of |signed_data| by |key|. |is_channel_id_signature| indicates
+ // whether |signature| is a ChannelID signature (with kContextStr prepended
+ // to the data to be signed).
+ static bool VerifyRaw(base::StringPiece key,
+ base::StringPiece signed_data,
+ base::StringPiece signature,
+ bool is_channel_id_signature);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChannelIDVerifier);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHANNEL_ID_H_
diff --git a/net/quic/crypto/channel_id_chromium.cc b/net/quic/crypto/channel_id_chromium.cc
new file mode 100644
index 0000000..53feed3
--- /dev/null
+++ b/net/quic/crypto/channel_id_chromium.cc
@@ -0,0 +1,249 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/channel_id_chromium.h"
+
+#include <string>
+
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "crypto/ec_private_key.h"
+#include "crypto/ec_signature_creator.h"
+#include "net/base/net_errors.h"
+#include "net/cert/asn1_util.h"
+#include "net/ssl/channel_id_service.h"
+
+namespace net {
+
+ChannelIDKeyChromium::ChannelIDKeyChromium(
+ crypto::ECPrivateKey* ec_private_key)
+ : ec_private_key_(ec_private_key) {}
+
+ChannelIDKeyChromium::~ChannelIDKeyChromium() {}
+
+bool ChannelIDKeyChromium::Sign(base::StringPiece signed_data,
+ std::string* out_signature) const {
+ scoped_ptr<crypto::ECSignatureCreator> sig_creator(
+ crypto::ECSignatureCreator::Create(ec_private_key_.get()));
+ if (!sig_creator) {
+ return false;
+ }
+ const size_t len1 = strlen(ChannelIDVerifier::kContextStr) + 1;
+ const size_t len2 = strlen(ChannelIDVerifier::kClientToServerStr) + 1;
+ std::vector<uint8> data(len1 + len2 + signed_data.size());
+ memcpy(&data[0], ChannelIDVerifier::kContextStr, len1);
+ memcpy(&data[len1], ChannelIDVerifier::kClientToServerStr, len2);
+ memcpy(&data[len1 + len2], signed_data.data(), signed_data.size());
+ std::vector<uint8> der_signature;
+ if (!sig_creator->Sign(&data[0], data.size(), &der_signature)) {
+ return false;
+ }
+ std::vector<uint8> raw_signature;
+ if (!sig_creator->DecodeSignature(der_signature, &raw_signature)) {
+ return false;
+ }
+ memcpy(WriteInto(out_signature, raw_signature.size() + 1),
+ &raw_signature[0], raw_signature.size());
+ return true;
+}
+
+std::string ChannelIDKeyChromium::SerializeKey() const {
+ std::string out_key;
+ if (!ec_private_key_->ExportRawPublicKey(&out_key)) {
+ return std::string();
+ }
+ return out_key;
+}
+
+// A Job handles the lookup of a single channel ID. It is owned by the
+// ChannelIDSource. If the operation can not complete synchronously, it will
+// notify the ChannelIDSource upon completion.
+class ChannelIDSourceChromium::Job {
+ public:
+ Job(ChannelIDSourceChromium* channel_id_source,
+ ChannelIDService* channel_id_service);
+
+ // Starts the channel ID lookup. If |QUIC_PENDING| is returned, then
+ // |callback| will be invoked asynchronously when the operation completes.
+ QuicAsyncStatus GetChannelIDKey(const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback);
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_GET_CHANNEL_ID_KEY,
+ STATE_GET_CHANNEL_ID_KEY_COMPLETE,
+ };
+
+ int DoLoop(int last_io_result);
+ void OnIOComplete(int result);
+ int DoGetChannelIDKey(int result);
+ int DoGetChannelIDKeyComplete(int result);
+
+ // Channel ID source to notify when this jobs completes.
+ ChannelIDSourceChromium* const channel_id_source_;
+
+ ChannelIDService* const channel_id_service_;
+
+ std::string channel_id_private_key_;
+ std::string channel_id_cert_;
+ ChannelIDService::RequestHandle channel_id_request_handle_;
+
+ // |hostname| specifies the hostname for which we need a channel ID.
+ std::string hostname_;
+
+ scoped_ptr<ChannelIDSourceCallback> callback_;
+
+ scoped_ptr<ChannelIDKey> channel_id_key_;
+
+ State next_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+ChannelIDSourceChromium::Job::Job(
+ ChannelIDSourceChromium* channel_id_source,
+ ChannelIDService* channel_id_service)
+ : channel_id_source_(channel_id_source),
+ channel_id_service_(channel_id_service),
+ next_state_(STATE_NONE) {
+}
+
+QuicAsyncStatus ChannelIDSourceChromium::Job::GetChannelIDKey(
+ const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) {
+ DCHECK(channel_id_key);
+ DCHECK(callback);
+
+ if (STATE_NONE != next_state_) {
+ DLOG(DFATAL) << "GetChannelIDKey has begun";
+ return QUIC_FAILURE;
+ }
+
+ channel_id_key_.reset();
+
+ hostname_ = hostname;
+
+ next_state_ = STATE_GET_CHANNEL_ID_KEY;
+ switch (DoLoop(OK)) {
+ case OK:
+ channel_id_key->reset(channel_id_key_.release());
+ return QUIC_SUCCESS;
+ case ERR_IO_PENDING:
+ callback_.reset(callback);
+ return QUIC_PENDING;
+ default:
+ channel_id_key->reset();
+ return QUIC_FAILURE;
+ }
+}
+
+int ChannelIDSourceChromium::Job::DoLoop(int last_result) {
+ int rv = last_result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_GET_CHANNEL_ID_KEY:
+ DCHECK(rv == OK);
+ rv = DoGetChannelIDKey(rv);
+ break;
+ case STATE_GET_CHANNEL_ID_KEY_COMPLETE:
+ rv = DoGetChannelIDKeyComplete(rv);
+ break;
+ case STATE_NONE:
+ default:
+ rv = ERR_UNEXPECTED;
+ LOG(DFATAL) << "unexpected state " << state;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+ return rv;
+}
+
+void ChannelIDSourceChromium::Job::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING) {
+ scoped_ptr<ChannelIDSourceCallback> callback(callback_.release());
+ callback->Run(&channel_id_key_);
+ // Will delete |this|.
+ channel_id_source_->OnJobComplete(this);
+ }
+}
+
+int ChannelIDSourceChromium::Job::DoGetChannelIDKey(int result) {
+ next_state_ = STATE_GET_CHANNEL_ID_KEY_COMPLETE;
+
+ return channel_id_service_->GetOrCreateChannelID(
+ hostname_,
+ &channel_id_private_key_,
+ &channel_id_cert_,
+ base::Bind(&ChannelIDSourceChromium::Job::OnIOComplete,
+ base::Unretained(this)),
+ &channel_id_request_handle_);
+}
+
+int ChannelIDSourceChromium::Job::DoGetChannelIDKeyComplete(int result) {
+ DCHECK_EQ(STATE_NONE, next_state_);
+ if (result != OK) {
+ DLOG(WARNING) << "Failed to look up channel ID: " << ErrorToString(result);
+ return result;
+ }
+
+ std::vector<uint8> encrypted_private_key_info(
+ channel_id_private_key_.size());
+ memcpy(&encrypted_private_key_info[0], channel_id_private_key_.data(),
+ channel_id_private_key_.size());
+
+ base::StringPiece spki_piece;
+ if (!asn1::ExtractSPKIFromDERCert(channel_id_cert_, &spki_piece)) {
+ return ERR_UNEXPECTED;
+ }
+ std::vector<uint8> subject_public_key_info(spki_piece.size());
+ memcpy(&subject_public_key_info[0], spki_piece.data(), spki_piece.size());
+
+ crypto::ECPrivateKey* ec_private_key =
+ crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
+ ChannelIDService::kEPKIPassword, encrypted_private_key_info,
+ subject_public_key_info);
+ if (!ec_private_key) {
+ // TODO(wtc): use the new error code ERR_CHANNEL_ID_IMPORT_FAILED to be
+ // added in https://codereview.chromium.org/338093012/.
+ return ERR_UNEXPECTED;
+ }
+ channel_id_key_.reset(new ChannelIDKeyChromium(ec_private_key));
+
+ return result;
+}
+
+ChannelIDSourceChromium::ChannelIDSourceChromium(
+ ChannelIDService* channel_id_service)
+ : channel_id_service_(channel_id_service) {
+}
+
+ChannelIDSourceChromium::~ChannelIDSourceChromium() {
+ STLDeleteElements(&active_jobs_);
+}
+
+QuicAsyncStatus ChannelIDSourceChromium::GetChannelIDKey(
+ const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) {
+ scoped_ptr<Job> job(new Job(this, channel_id_service_));
+ QuicAsyncStatus status = job->GetChannelIDKey(hostname, channel_id_key,
+ callback);
+ if (status == QUIC_PENDING) {
+ active_jobs_.insert(job.release());
+ }
+ return status;
+}
+
+void ChannelIDSourceChromium::OnJobComplete(Job* job) {
+ active_jobs_.erase(job);
+ delete job;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/channel_id_chromium.h b/net/quic/crypto/channel_id_chromium.h
new file mode 100644
index 0000000..08cfff9
--- /dev/null
+++ b/net/quic/crypto/channel_id_chromium.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CHANNEL_ID_CHROMIUM_H_
+#define NET_QUIC_CRYPTO_CHANNEL_ID_CHROMIUM_H_
+
+#include <set>
+
+#include "net/quic/crypto/channel_id.h"
+
+namespace crypto {
+class ECPrivateKey;
+} // namespace crypto
+
+namespace net {
+
+class ChannelIDService;
+
+class NET_EXPORT_PRIVATE ChannelIDKeyChromium: public ChannelIDKey {
+ public:
+ explicit ChannelIDKeyChromium(crypto::ECPrivateKey* ec_private_key);
+ virtual ~ChannelIDKeyChromium();
+
+ // ChannelIDKey interface
+ virtual bool Sign(base::StringPiece signed_data,
+ std::string* out_signature) const OVERRIDE;
+ virtual std::string SerializeKey() const OVERRIDE;
+
+ private:
+ scoped_ptr<crypto::ECPrivateKey> ec_private_key_;
+};
+
+// ChannelIDSourceChromium implements the QUIC ChannelIDSource interface.
+class ChannelIDSourceChromium : public ChannelIDSource {
+ public:
+ explicit ChannelIDSourceChromium(
+ ChannelIDService* channel_id_service);
+ virtual ~ChannelIDSourceChromium();
+
+ // ChannelIDSource interface
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const std::string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) OVERRIDE;
+
+ private:
+ class Job;
+ typedef std::set<Job*> JobSet;
+
+ void OnJobComplete(Job* job);
+
+ // Set owning pointers to active jobs.
+ JobSet active_jobs_;
+
+ // The service for retrieving Channel ID keys.
+ ChannelIDService* const channel_id_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelIDSourceChromium);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CHANNEL_ID_CHROMIUM_H_
diff --git a/net/quic/crypto/channel_id_nss.cc b/net/quic/crypto/channel_id_nss.cc
new file mode 100644
index 0000000..748e4ef
--- /dev/null
+++ b/net/quic/crypto/channel_id_nss.cc
@@ -0,0 +1,83 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/channel_id.h"
+
+#include <keythi.h>
+#include <pk11pub.h>
+#include <sechash.h>
+
+using base::StringPiece;
+
+namespace net {
+
+// static
+bool ChannelIDVerifier::Verify(StringPiece key,
+ StringPiece signed_data,
+ StringPiece signature) {
+ return VerifyRaw(key, signed_data, signature, true);
+}
+
+// static
+bool ChannelIDVerifier::VerifyRaw(StringPiece key,
+ StringPiece signed_data,
+ StringPiece signature,
+ bool is_channel_id_signature) {
+ if (key.size() != 32 * 2 ||
+ signature.size() != 32 * 2) {
+ return false;
+ }
+
+ SECKEYPublicKey public_key;
+ memset(&public_key, 0, sizeof(public_key));
+
+ // DER encoding of the object identifier (OID) of the named curve P-256
+ // (1.2.840.10045.3.1.7). See RFC 6637 Section 11.
+ static const unsigned char p256_oid[] = {
+ 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
+ };
+ public_key.keyType = ecKey;
+ public_key.u.ec.DEREncodedParams.type = siBuffer;
+ public_key.u.ec.DEREncodedParams.data = const_cast<unsigned char*>(p256_oid);
+ public_key.u.ec.DEREncodedParams.len = sizeof(p256_oid);
+
+ unsigned char key_buf[65];
+ key_buf[0] = 0x04;
+ memcpy(&key_buf[1], key.data(), key.size());
+ public_key.u.ec.publicValue.type = siBuffer;
+ public_key.u.ec.publicValue.data = key_buf;
+ public_key.u.ec.publicValue.len = sizeof(key_buf);
+
+ SECItem signature_item = {
+ siBuffer,
+ reinterpret_cast<unsigned char*>(const_cast<char*>(signature.data())),
+ static_cast<unsigned int>(signature.size())
+ };
+
+ unsigned char hash_buf[SHA256_LENGTH];
+ SECItem hash_item = { siBuffer, hash_buf, sizeof(hash_buf) };
+
+ HASHContext* sha256 = HASH_Create(HASH_AlgSHA256);
+ if (!sha256) {
+ return false;
+ }
+ HASH_Begin(sha256);
+ if (is_channel_id_signature) {
+ HASH_Update(sha256, reinterpret_cast<const unsigned char*>(kContextStr),
+ strlen(kContextStr) + 1);
+ HASH_Update(sha256,
+ reinterpret_cast<const unsigned char*>(kClientToServerStr),
+ strlen(kClientToServerStr) + 1);
+ }
+ HASH_Update(sha256,
+ reinterpret_cast<const unsigned char*>(signed_data.data()),
+ signed_data.size());
+ HASH_End(sha256, hash_buf, &hash_item.len, sizeof(hash_buf));
+ HASH_Destroy(sha256);
+
+ return PK11_Verify(&public_key, &signature_item, &hash_item, nullptr) ==
+ SECSuccess;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/channel_id_openssl.cc b/net/quic/crypto/channel_id_openssl.cc
new file mode 100644
index 0000000..f0cdcbf
--- /dev/null
+++ b/net/quic/crypto/channel_id_openssl.cc
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/channel_id.h"
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/obj_mac.h>
+#include <openssl/sha.h>
+
+#include "crypto/openssl_util.h"
+#include "crypto/scoped_openssl_types.h"
+
+using base::StringPiece;
+
+namespace net {
+
+// static
+bool ChannelIDVerifier::Verify(StringPiece key,
+ StringPiece signed_data,
+ StringPiece signature) {
+ return VerifyRaw(key, signed_data, signature, true);
+}
+
+// static
+bool ChannelIDVerifier::VerifyRaw(StringPiece key,
+ StringPiece signed_data,
+ StringPiece signature,
+ bool is_channel_id_signature) {
+ if (key.size() != 32 * 2 ||
+ signature.size() != 32 * 2) {
+ return false;
+ }
+
+ crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free>::Type p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ if (p256.get() == nullptr) {
+ return false;
+ }
+
+ crypto::ScopedBIGNUM x(BN_new()), y(BN_new()), r(BN_new()), s(BN_new());
+
+ ECDSA_SIG sig;
+ sig.r = r.get();
+ sig.s = s.get();
+
+ const uint8* key_bytes = reinterpret_cast<const uint8*>(key.data());
+ const uint8* signature_bytes =
+ reinterpret_cast<const uint8*>(signature.data());
+
+ if (BN_bin2bn(key_bytes + 0, 32, x.get()) == nullptr ||
+ BN_bin2bn(key_bytes + 32, 32, y.get()) == nullptr ||
+ BN_bin2bn(signature_bytes + 0, 32, sig.r) == nullptr ||
+ BN_bin2bn(signature_bytes + 32, 32, sig.s) == nullptr) {
+ return false;
+ }
+
+ crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free>::Type point(
+ EC_POINT_new(p256.get()));
+ if (point.get() == nullptr ||
+ !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(),
+ y.get(), nullptr)) {
+ return false;
+ }
+
+ crypto::ScopedEC_KEY ecdsa_key(EC_KEY_new());
+ if (ecdsa_key.get() == nullptr ||
+ !EC_KEY_set_group(ecdsa_key.get(), p256.get()) ||
+ !EC_KEY_set_public_key(ecdsa_key.get(), point.get())) {
+ return false;
+ }
+
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ if (is_channel_id_signature) {
+ SHA256_Update(&sha256, kContextStr, strlen(kContextStr) + 1);
+ SHA256_Update(&sha256, kClientToServerStr, strlen(kClientToServerStr) + 1);
+ }
+ SHA256_Update(&sha256, signed_data.data(), signed_data.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ return ECDSA_do_verify(digest, sizeof(digest), &sig, ecdsa_key.get()) == 1;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/channel_id_test.cc b/net/quic/crypto/channel_id_test.cc
new file mode 100644
index 0000000..7392723
--- /dev/null
+++ b/net/quic/crypto/channel_id_test.cc
@@ -0,0 +1,259 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/channel_id.h"
+
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+namespace {
+
+// The following ECDSA signature verification test vectors for P-256,SHA-256
+// come from the SigVer.rsp file in
+// http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
+// downloaded on 2013-06-11.
+struct TestVector {
+ // Input:
+ const char* msg;
+ const char* qx;
+ const char* qy;
+ const char* r;
+ const char* s;
+
+ // Expected output:
+ bool result; // true means "P", false means "F"
+};
+
+const TestVector test_vector[] = {
+ { "e4796db5f785f207aa30d311693b3702821dff1168fd2e04c0836825aefd850d"
+ "9aa60326d88cde1a23c7745351392ca2288d632c264f197d05cd424a30336c19"
+ "fd09bb229654f0222fcb881a4b35c290a093ac159ce13409111ff0358411133c"
+ "24f5b8e2090d6db6558afc36f06ca1f6ef779785adba68db27a409859fc4c4a0",
+ "87f8f2b218f49845f6f10eec3877136269f5c1a54736dbdf69f89940cad41555",
+ "e15f369036f49842fac7a86c8a2b0557609776814448b8f5e84aa9f4395205e9",
+ "d19ff48b324915576416097d2544f7cbdf8768b1454ad20e0baac50e211f23b0",
+ "a3e81e59311cdfff2d4784949f7a2cb50ba6c3a91fa54710568e61aca3e847c6",
+ false // F (3 - S changed)
+ },
+ { "069a6e6b93dfee6df6ef6997cd80dd2182c36653cef10c655d524585655462d6"
+ "83877f95ecc6d6c81623d8fac4e900ed0019964094e7de91f1481989ae187300"
+ "4565789cbf5dc56c62aedc63f62f3b894c9c6f7788c8ecaadc9bd0e81ad91b2b"
+ "3569ea12260e93924fdddd3972af5273198f5efda0746219475017557616170e",
+ "5cf02a00d205bdfee2016f7421807fc38ae69e6b7ccd064ee689fc1a94a9f7d2",
+ "ec530ce3cc5c9d1af463f264d685afe2b4db4b5828d7e61b748930f3ce622a85",
+ "dc23d130c6117fb5751201455e99f36f59aba1a6a21cf2d0e7481a97451d6693",
+ "d6ce7708c18dbf35d4f8aa7240922dc6823f2e7058cbc1484fcad1599db5018c",
+ false // F (2 - R changed)
+ },
+ { "df04a346cf4d0e331a6db78cca2d456d31b0a000aa51441defdb97bbeb20b94d"
+ "8d746429a393ba88840d661615e07def615a342abedfa4ce912e562af7149598"
+ "96858af817317a840dcff85a057bb91a3c2bf90105500362754a6dd321cdd861"
+ "28cfc5f04667b57aa78c112411e42da304f1012d48cd6a7052d7de44ebcc01de",
+ "2ddfd145767883ffbb0ac003ab4a44346d08fa2570b3120dcce94562422244cb",
+ "5f70c7d11ac2b7a435ccfbbae02c3df1ea6b532cc0e9db74f93fffca7c6f9a64",
+ "9913111cff6f20c5bf453a99cd2c2019a4e749a49724a08774d14e4c113edda8",
+ "9467cd4cd21ecb56b0cab0a9a453b43386845459127a952421f5c6382866c5cc",
+ false // F (4 - Q changed)
+ },
+ { "e1130af6a38ccb412a9c8d13e15dbfc9e69a16385af3c3f1e5da954fd5e7c45f"
+ "d75e2b8c36699228e92840c0562fbf3772f07e17f1add56588dd45f7450e1217"
+ "ad239922dd9c32695dc71ff2424ca0dec1321aa47064a044b7fe3c2b97d03ce4"
+ "70a592304c5ef21eed9f93da56bb232d1eeb0035f9bf0dfafdcc4606272b20a3",
+ "e424dc61d4bb3cb7ef4344a7f8957a0c5134e16f7a67c074f82e6e12f49abf3c",
+ "970eed7aa2bc48651545949de1dddaf0127e5965ac85d1243d6f60e7dfaee927",
+ "bf96b99aa49c705c910be33142017c642ff540c76349b9dab72f981fd9347f4f",
+ "17c55095819089c2e03b9cd415abdf12444e323075d98f31920b9e0f57ec871c",
+ true // P (0 )
+ },
+ { "73c5f6a67456ae48209b5f85d1e7de7758bf235300c6ae2bdceb1dcb27a7730f"
+ "b68c950b7fcada0ecc4661d3578230f225a875e69aaa17f1e71c6be5c831f226"
+ "63bac63d0c7a9635edb0043ff8c6f26470f02a7bc56556f1437f06dfa27b487a"
+ "6c4290d8bad38d4879b334e341ba092dde4e4ae694a9c09302e2dbf443581c08",
+ "e0fc6a6f50e1c57475673ee54e3a57f9a49f3328e743bf52f335e3eeaa3d2864",
+ "7f59d689c91e463607d9194d99faf316e25432870816dde63f5d4b373f12f22a",
+ "1d75830cd36f4c9aa181b2c4221e87f176b7f05b7c87824e82e396c88315c407",
+ "cb2acb01dac96efc53a32d4a0d85d0c2e48955214783ecf50a4f0414a319c05a",
+ true // P (0 )
+ },
+ { "666036d9b4a2426ed6585a4e0fd931a8761451d29ab04bd7dc6d0c5b9e38e6c2"
+ "b263ff6cb837bd04399de3d757c6c7005f6d7a987063cf6d7e8cb38a4bf0d74a"
+ "282572bd01d0f41e3fd066e3021575f0fa04f27b700d5b7ddddf50965993c3f9"
+ "c7118ed78888da7cb221849b3260592b8e632d7c51e935a0ceae15207bedd548",
+ "a849bef575cac3c6920fbce675c3b787136209f855de19ffe2e8d29b31a5ad86",
+ "bf5fe4f7858f9b805bd8dcc05ad5e7fb889de2f822f3d8b41694e6c55c16b471",
+ "25acc3aa9d9e84c7abf08f73fa4195acc506491d6fc37cb9074528a7db87b9d6",
+ "9b21d5b5259ed3f2ef07dfec6cc90d3a37855d1ce122a85ba6a333f307d31537",
+ false // F (2 - R changed)
+ },
+ { "7e80436bce57339ce8da1b5660149a20240b146d108deef3ec5da4ae256f8f89"
+ "4edcbbc57b34ce37089c0daa17f0c46cd82b5a1599314fd79d2fd2f446bd5a25"
+ "b8e32fcf05b76d644573a6df4ad1dfea707b479d97237a346f1ec632ea5660ef"
+ "b57e8717a8628d7f82af50a4e84b11f21bdff6839196a880ae20b2a0918d58cd",
+ "3dfb6f40f2471b29b77fdccba72d37c21bba019efa40c1c8f91ec405d7dcc5df",
+ "f22f953f1e395a52ead7f3ae3fc47451b438117b1e04d613bc8555b7d6e6d1bb",
+ "548886278e5ec26bed811dbb72db1e154b6f17be70deb1b210107decb1ec2a5a",
+ "e93bfebd2f14f3d827ca32b464be6e69187f5edbd52def4f96599c37d58eee75",
+ false // F (4 - Q changed)
+ },
+ { "1669bfb657fdc62c3ddd63269787fc1c969f1850fb04c933dda063ef74a56ce1"
+ "3e3a649700820f0061efabf849a85d474326c8a541d99830eea8131eaea584f2"
+ "2d88c353965dabcdc4bf6b55949fd529507dfb803ab6b480cd73ca0ba00ca19c"
+ "438849e2cea262a1c57d8f81cd257fb58e19dec7904da97d8386e87b84948169",
+ "69b7667056e1e11d6caf6e45643f8b21e7a4bebda463c7fdbc13bc98efbd0214",
+ "d3f9b12eb46c7c6fda0da3fc85bc1fd831557f9abc902a3be3cb3e8be7d1aa2f",
+ "288f7a1cd391842cce21f00e6f15471c04dc182fe4b14d92dc18910879799790",
+ "247b3c4e89a3bcadfea73c7bfd361def43715fa382b8c3edf4ae15d6e55e9979",
+ false // F (1 - Message changed)
+ },
+ { "3fe60dd9ad6caccf5a6f583b3ae65953563446c4510b70da115ffaa0ba04c076"
+ "115c7043ab8733403cd69c7d14c212c655c07b43a7c71b9a4cffe22c2684788e"
+ "c6870dc2013f269172c822256f9e7cc674791bf2d8486c0f5684283e1649576e"
+ "fc982ede17c7b74b214754d70402fb4bb45ad086cf2cf76b3d63f7fce39ac970",
+ "bf02cbcf6d8cc26e91766d8af0b164fc5968535e84c158eb3bc4e2d79c3cc682",
+ "069ba6cb06b49d60812066afa16ecf7b51352f2c03bd93ec220822b1f3dfba03",
+ "f5acb06c59c2b4927fb852faa07faf4b1852bbb5d06840935e849c4d293d1bad",
+ "049dab79c89cc02f1484c437f523e080a75f134917fda752f2d5ca397addfe5d",
+ false // F (3 - S changed)
+ },
+ { "983a71b9994d95e876d84d28946a041f8f0a3f544cfcc055496580f1dfd4e312"
+ "a2ad418fe69dbc61db230cc0c0ed97e360abab7d6ff4b81ee970a7e97466acfd"
+ "9644f828ffec538abc383d0e92326d1c88c55e1f46a668a039beaa1be631a891"
+ "29938c00a81a3ae46d4aecbf9707f764dbaccea3ef7665e4c4307fa0b0a3075c",
+ "224a4d65b958f6d6afb2904863efd2a734b31798884801fcab5a590f4d6da9de",
+ "178d51fddada62806f097aa615d33b8f2404e6b1479f5fd4859d595734d6d2b9",
+ "87b93ee2fecfda54deb8dff8e426f3c72c8864991f8ec2b3205bb3b416de93d2",
+ "4044a24df85be0cc76f21a4430b75b8e77b932a87f51e4eccbc45c263ebf8f66",
+ false // F (2 - R changed)
+ },
+ { "4a8c071ac4fd0d52faa407b0fe5dab759f7394a5832127f2a3498f34aac28733"
+ "9e043b4ffa79528faf199dc917f7b066ad65505dab0e11e6948515052ce20cfd"
+ "b892ffb8aa9bf3f1aa5be30a5bbe85823bddf70b39fd7ebd4a93a2f75472c1d4"
+ "f606247a9821f1a8c45a6cb80545de2e0c6c0174e2392088c754e9c8443eb5af",
+ "43691c7795a57ead8c5c68536fe934538d46f12889680a9cb6d055a066228369",
+ "f8790110b3c3b281aa1eae037d4f1234aff587d903d93ba3af225c27ddc9ccac",
+ "8acd62e8c262fa50dd9840480969f4ef70f218ebf8ef9584f199031132c6b1ce",
+ "cfca7ed3d4347fb2a29e526b43c348ae1ce6c60d44f3191b6d8ea3a2d9c92154",
+ false // F (3 - S changed)
+ },
+ { "0a3a12c3084c865daf1d302c78215d39bfe0b8bf28272b3c0b74beb4b7409db0"
+ "718239de700785581514321c6440a4bbaea4c76fa47401e151e68cb6c29017f0"
+ "bce4631290af5ea5e2bf3ed742ae110b04ade83a5dbd7358f29a85938e23d87a"
+ "c8233072b79c94670ff0959f9c7f4517862ff829452096c78f5f2e9a7e4e9216",
+ "9157dbfcf8cf385f5bb1568ad5c6e2a8652ba6dfc63bc1753edf5268cb7eb596",
+ "972570f4313d47fc96f7c02d5594d77d46f91e949808825b3d31f029e8296405",
+ "dfaea6f297fa320b707866125c2a7d5d515b51a503bee817de9faa343cc48eeb",
+ "8f780ad713f9c3e5a4f7fa4c519833dfefc6a7432389b1e4af463961f09764f2",
+ false // F (1 - Message changed)
+ },
+ { "785d07a3c54f63dca11f5d1a5f496ee2c2f9288e55007e666c78b007d95cc285"
+ "81dce51f490b30fa73dc9e2d45d075d7e3a95fb8a9e1465ad191904124160b7c"
+ "60fa720ef4ef1c5d2998f40570ae2a870ef3e894c2bc617d8a1dc85c3c557749"
+ "28c38789b4e661349d3f84d2441a3b856a76949b9f1f80bc161648a1cad5588e",
+ "072b10c081a4c1713a294f248aef850e297991aca47fa96a7470abe3b8acfdda",
+ "9581145cca04a0fb94cedce752c8f0370861916d2a94e7c647c5373ce6a4c8f5",
+ "09f5483eccec80f9d104815a1be9cc1a8e5b12b6eb482a65c6907b7480cf4f19",
+ "a4f90e560c5e4eb8696cb276e5165b6a9d486345dedfb094a76e8442d026378d",
+ false // F (4 - Q changed)
+ },
+ { "76f987ec5448dd72219bd30bf6b66b0775c80b394851a43ff1f537f140a6e722"
+ "9ef8cd72ad58b1d2d20298539d6347dd5598812bc65323aceaf05228f738b5ad"
+ "3e8d9fe4100fd767c2f098c77cb99c2992843ba3eed91d32444f3b6db6cd212d"
+ "d4e5609548f4bb62812a920f6e2bf1581be1ebeebdd06ec4e971862cc42055ca",
+ "09308ea5bfad6e5adf408634b3d5ce9240d35442f7fe116452aaec0d25be8c24",
+ "f40c93e023ef494b1c3079b2d10ef67f3170740495ce2cc57f8ee4b0618b8ee5",
+ "5cc8aa7c35743ec0c23dde88dabd5e4fcd0192d2116f6926fef788cddb754e73",
+ "9c9c045ebaa1b828c32f82ace0d18daebf5e156eb7cbfdc1eff4399a8a900ae7",
+ false // F (1 - Message changed)
+ },
+ { "60cd64b2cd2be6c33859b94875120361a24085f3765cb8b2bf11e026fa9d8855"
+ "dbe435acf7882e84f3c7857f96e2baab4d9afe4588e4a82e17a78827bfdb5ddb"
+ "d1c211fbc2e6d884cddd7cb9d90d5bf4a7311b83f352508033812c776a0e00c0"
+ "03c7e0d628e50736c7512df0acfa9f2320bd102229f46495ae6d0857cc452a84",
+ "2d98ea01f754d34bbc3003df5050200abf445ec728556d7ed7d5c54c55552b6d",
+ "9b52672742d637a32add056dfd6d8792f2a33c2e69dafabea09b960bc61e230a",
+ "06108e525f845d0155bf60193222b3219c98e3d49424c2fb2a0987f825c17959",
+ "62b5cdd591e5b507e560167ba8f6f7cda74673eb315680cb89ccbc4eec477dce",
+ true // P (0 )
+ },
+ { NULL }
+};
+
+} // namespace
+
+// A known answer test for ChannelIDVerifier.
+TEST(ChannelIDTest, VerifyKnownAnswerTest) {
+ string msg;
+ string qx;
+ string qy;
+ string r;
+ string s;
+
+ for (size_t i = 0; test_vector[i].msg != nullptr; i++) {
+ SCOPED_TRACE(i);
+ // Decode the test vector.
+ ASSERT_TRUE(DecodeHexString(test_vector[i].msg, &msg));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].qx, &qx));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].qy, &qy));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].r, &r));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].s, &s));
+
+ string key = qx + qy;
+ string signature = r + s;
+
+ // The test vector's lengths should look sane.
+ EXPECT_EQ(32u, qx.size());
+ EXPECT_EQ(32u, qy.size());
+ EXPECT_EQ(32u, r.size());
+ EXPECT_EQ(32u, s.size());
+
+ EXPECT_EQ(test_vector[i].result,
+ ChannelIDVerifier::VerifyRaw(key, msg, signature, false));
+ }
+}
+
+TEST(ChannelIDTest, SignAndVerify) {
+ scoped_ptr<ChannelIDSource> source(
+ CryptoTestUtils::ChannelIDSourceForTesting());
+
+ const string signed_data = "signed data";
+ const string hostname = "foo.example.com";
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status =
+ source->GetChannelIDKey(hostname, &channel_id_key, nullptr);
+ ASSERT_EQ(QUIC_SUCCESS, status);
+
+ string signature;
+ ASSERT_TRUE(channel_id_key->Sign(signed_data, &signature));
+
+ string key = channel_id_key->SerializeKey();
+ EXPECT_TRUE(ChannelIDVerifier::Verify(key, signed_data, signature));
+
+ EXPECT_FALSE(ChannelIDVerifier::Verify("a" + key, signed_data, signature));
+ EXPECT_FALSE(ChannelIDVerifier::Verify(key, "a" + signed_data, signature));
+
+ scoped_ptr<char[]> bad_key(new char[key.size()]);
+ memcpy(bad_key.get(), key.data(), key.size());
+ bad_key[1] ^= 0x80;
+ EXPECT_FALSE(ChannelIDVerifier::Verify(
+ string(bad_key.get(), key.size()), signed_data, signature));
+
+ scoped_ptr<char[]> bad_signature(new char[signature.size()]);
+ memcpy(bad_signature.get(), signature.data(), signature.size());
+ bad_signature[1] ^= 0x80;
+ EXPECT_FALSE(ChannelIDVerifier::Verify(
+ key, signed_data, string(bad_signature.get(), signature.size())));
+
+ EXPECT_FALSE(ChannelIDVerifier::Verify(
+ key, "wrong signed data", signature));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/common_cert_set.cc b/net/quic/crypto/common_cert_set.cc
new file mode 100644
index 0000000..f631cd6
--- /dev/null
+++ b/net/quic/crypto/common_cert_set.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/common_cert_set.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace common_cert_set_0 {
+#include "net/quic/crypto/common_cert_set_0.c"
+}
+
+namespace {
+
+struct CertSet {
+ // num_certs contains the number of certificates in this set.
+ size_t num_certs;
+ // certs is an array of |num_certs| pointers to the DER encoded certificates.
+ const unsigned char* const* certs;
+ // lens is an array of |num_certs| integers describing the length, in bytes,
+ // of each certificate.
+ const size_t* lens;
+ // hash contains the 64-bit, FNV-1a hash of this set.
+ uint64 hash;
+};
+
+const CertSet kSets[] = {
+ {
+ common_cert_set_0::kNumCerts,
+ common_cert_set_0::kCerts,
+ common_cert_set_0::kLens,
+ common_cert_set_0::kHash,
+ },
+};
+
+const uint64 kSetHashes[] = {
+ common_cert_set_0::kHash,
+};
+
+// Compare returns a value less than, equal to or greater than zero if |a| is
+// lexicographically less than, equal to or greater than |b|, respectively.
+int Compare(StringPiece a, const unsigned char* b, size_t b_len) {
+ size_t len = a.size();
+ if (len > b_len) {
+ len = b_len;
+ }
+ int n = memcmp(a.data(), b, len);
+ if (n != 0) {
+ return n;
+ }
+
+ if (a.size() < b_len) {
+ return -1;
+ } else if (a.size() > b_len) {
+ return 1;
+ }
+ return 0;
+}
+
+// CommonCertSetsQUIC implements the CommonCertSets interface using the default
+// certificate sets.
+class CommonCertSetsQUIC : public CommonCertSets {
+ public:
+ // CommonCertSets interface.
+ virtual StringPiece GetCommonHashes() const OVERRIDE {
+ return StringPiece(reinterpret_cast<const char*>(kSetHashes),
+ sizeof(uint64) * arraysize(kSetHashes));
+ }
+
+ virtual StringPiece GetCert(uint64 hash, uint32 index) const OVERRIDE {
+ for (size_t i = 0; i < arraysize(kSets); i++) {
+ if (kSets[i].hash == hash) {
+ if (index < kSets[i].num_certs) {
+ return StringPiece(
+ reinterpret_cast<const char*>(kSets[i].certs[index]),
+ kSets[i].lens[index]);
+ }
+ break;
+ }
+ }
+
+ return StringPiece();
+ }
+
+ virtual bool MatchCert(StringPiece cert, StringPiece common_set_hashes,
+ uint64* out_hash, uint32* out_index) const OVERRIDE {
+ if (common_set_hashes.size() % sizeof(uint64) != 0) {
+ return false;
+ }
+
+ for (size_t i = 0; i < common_set_hashes.size() / sizeof(uint64); i++) {
+ uint64 hash;
+ memcpy(&hash, common_set_hashes.data() + i * sizeof(uint64),
+ sizeof(uint64));
+
+ for (size_t j = 0; j < arraysize(kSets); j++) {
+ if (kSets[j].hash != hash) {
+ continue;
+ }
+
+ if (kSets[j].num_certs == 0) {
+ continue;
+ }
+
+ // Binary search for a matching certificate.
+ size_t min = 0;
+ size_t max = kSets[j].num_certs - 1;
+ while (max >= min) {
+ size_t mid = min + ((max - min) / 2);
+ int n = Compare(cert, kSets[j].certs[mid], kSets[j].lens[mid]);
+ if (n < 0) {
+ if (mid == 0) {
+ break;
+ }
+ max = mid - 1;
+ } else if (n > 0) {
+ min = mid + 1;
+ } else {
+ *out_hash = hash;
+ *out_index = mid;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ static CommonCertSetsQUIC* GetInstance() {
+ return Singleton<CommonCertSetsQUIC>::get();
+ }
+
+ private:
+ CommonCertSetsQUIC() {}
+ virtual ~CommonCertSetsQUIC() {}
+
+ friend struct DefaultSingletonTraits<CommonCertSetsQUIC>;
+ DISALLOW_COPY_AND_ASSIGN(CommonCertSetsQUIC);
+};
+
+} // anonymous namespace
+
+CommonCertSets::~CommonCertSets() {}
+
+// static
+const CommonCertSets* CommonCertSets::GetInstanceQUIC() {
+ return CommonCertSetsQUIC::GetInstance();
+}
+
+} // namespace net
diff --git a/net/quic/crypto/common_cert_set.h b/net/quic/crypto/common_cert_set.h
new file mode 100644
index 0000000..a9e9304
--- /dev/null
+++ b/net/quic/crypto/common_cert_set.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_COMMON_CERT_SET_H_
+#define NET_QUIC_CRYPTO_COMMON_CERT_SET_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+namespace net {
+
+// CommonCertSets is an interface to an object that contains a number of common
+// certificate sets and can match against them.
+class NET_EXPORT_PRIVATE CommonCertSets {
+ public:
+ virtual ~CommonCertSets();
+
+ // GetInstanceQUIC returns the standard QUIC common certificate sets.
+ static const CommonCertSets* GetInstanceQUIC();
+
+ // GetCommonHashes returns a StringPiece containing the hashes of common sets
+ // supported by this object. The 64-bit hashes are concatenated in the
+ // StringPiece.
+ virtual base::StringPiece GetCommonHashes() const = 0;
+
+ // GetCert returns a specific certificate (at index |index|) in the common
+ // set identified by |hash|. If no such certificate is known, an empty
+ // StringPiece is returned.
+ virtual base::StringPiece GetCert(uint64 hash, uint32 index) const = 0;
+
+ // MatchCert tries to find |cert| in one of the common certificate sets
+ // identified by |common_set_hashes|. On success it puts the hash of the
+ // set in |out_hash|, the index of |cert| in the set in |out_index| and
+ // returns true. Otherwise it returns false.
+ virtual bool MatchCert(base::StringPiece cert,
+ base::StringPiece common_set_hashes,
+ uint64* out_hash,
+ uint32* out_index) const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_COMMON_CERT_SET_H_
diff --git a/net/quic/crypto/common_cert_set_0.c b/net/quic/crypto/common_cert_set_0.c
new file mode 100644
index 0000000..1133733
--- /dev/null
+++ b/net/quic/crypto/common_cert_set_0.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+
+#include "net/quic/crypto/common_cert_set_1_50.inc"
+#include "net/quic/crypto/common_cert_set_51_100.inc"
+
+static const size_t kNumCerts = 102;
+static const unsigned char* const kCerts[] = {
+ kDERCert0,
+ kDERCert1,
+ kDERCert2,
+ kDERCert3,
+ kDERCert4,
+ kDERCert5,
+ kDERCert6,
+ kDERCert7,
+ kDERCert8,
+ kDERCert9,
+ kDERCert10,
+ kDERCert11,
+ kDERCert12,
+ kDERCert13,
+ kDERCert14,
+ kDERCert15,
+ kDERCert16,
+ kDERCert17,
+ kDERCert18,
+ kDERCert19,
+ kDERCert20,
+ kDERCert21,
+ kDERCert22,
+ kDERCert23,
+ kDERCert24,
+ kDERCert25,
+ kDERCert26,
+ kDERCert27,
+ kDERCert28,
+ kDERCert29,
+ kDERCert30,
+ kDERCert31,
+ kDERCert32,
+ kDERCert33,
+ kDERCert34,
+ kDERCert35,
+ kDERCert36,
+ kDERCert37,
+ kDERCert38,
+ kDERCert39,
+ kDERCert40,
+ kDERCert41,
+ kDERCert42,
+ kDERCert43,
+ kDERCert44,
+ kDERCert45,
+ kDERCert46,
+ kDERCert47,
+ kDERCert48,
+ kDERCert49,
+ kDERCert50,
+ kDERCert51,
+ kDERCert52,
+ kDERCert53,
+ kDERCert54,
+ kDERCert55,
+ kDERCert56,
+ kDERCert57,
+ kDERCert58,
+ kDERCert59,
+ kDERCert60,
+ kDERCert61,
+ kDERCert62,
+ kDERCert63,
+ kDERCert64,
+ kDERCert65,
+ kDERCert66,
+ kDERCert67,
+ kDERCert68,
+ kDERCert69,
+ kDERCert70,
+ kDERCert71,
+ kDERCert72,
+ kDERCert73,
+ kDERCert74,
+ kDERCert75,
+ kDERCert76,
+ kDERCert77,
+ kDERCert78,
+ kDERCert79,
+ kDERCert80,
+ kDERCert81,
+ kDERCert82,
+ kDERCert83,
+ kDERCert84,
+ kDERCert85,
+ kDERCert86,
+ kDERCert87,
+ kDERCert88,
+ kDERCert89,
+ kDERCert90,
+ kDERCert91,
+ kDERCert92,
+ kDERCert93,
+ kDERCert94,
+ kDERCert95,
+ kDERCert96,
+ kDERCert97,
+ kDERCert98,
+ kDERCert99,
+ kDERCert100,
+ kDERCert101,
+};
+
+static const size_t kLens[] = {
+ 692,
+ 897,
+ 903,
+ 911,
+ 911,
+ 957,
+ 971,
+ 985,
+ 989,
+ 1022,
+ 1032,
+ 1049,
+ 1055,
+ 1071,
+ 1071,
+ 1073,
+ 1075,
+ 1078,
+ 1080,
+ 1082,
+ 1082,
+ 1084,
+ 1088,
+ 1090,
+ 1094,
+ 1097,
+ 1098,
+ 1107,
+ 1118,
+ 1119,
+ 1122,
+ 1124,
+ 1131,
+ 1134,
+ 1136,
+ 1147,
+ 1161,
+ 1162,
+ 1162,
+ 1167,
+ 1167,
+ 1171,
+ 1172,
+ 1181,
+ 1183,
+ 1184,
+ 1187,
+ 1191,
+ 1194,
+ 1194,
+ 1199,
+ 1223,
+ 1226,
+ 1231,
+ 1236,
+ 1239,
+ 1250,
+ 1254,
+ 1256,
+ 1256,
+ 1257,
+ 1260,
+ 1268,
+ 1269,
+ 1269,
+ 1270,
+ 1273,
+ 1276,
+ 1279,
+ 1280,
+ 1282,
+ 1285,
+ 1286,
+ 1287,
+ 1287,
+ 1290,
+ 1291,
+ 1291,
+ 1294,
+ 1294,
+ 1297,
+ 1302,
+ 1302,
+ 1303,
+ 1311,
+ 1330,
+ 1365,
+ 1512,
+ 1520,
+ 1535,
+ 1548,
+ 1550,
+ 1559,
+ 1570,
+ 1581,
+ 1584,
+ 1592,
+ 1592,
+ 1625,
+ 1628,
+ 1767,
+ 1770,
+};
+
+static const uint64 kHash = GG_UINT64_C(0xc9fef74053f99f39);
diff --git a/net/quic/crypto/common_cert_set_1_50.inc b/net/quic/crypto/common_cert_set_1_50.inc
new file mode 100644
index 0000000..5ee471b
--- /dev/null
+++ b/net/quic/crypto/common_cert_set_1_50.inc
@@ -0,0 +1,9794 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 747377 (0xb6771)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: Jun 8 20:43:27 2009 GMT
+ Not After : Jun 7 19:43:27 2013 GMT
+ Subject: C=US, O=Google Inc, CN=Google Internet Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:c9:ed:b7:a4:8b:9c:57:e7:84:3e:40:7d:84:f4:
+ 8f:d1:71:63:53:99:e7:79:74:14:af:44:99:33:20:
+ 92:8d:7b:e5:28:0c:ba:ad:6c:49:7e:83:5f:34:59:
+ 4e:0a:7a:30:cd:d0:d7:c4:57:45:ed:d5:aa:d6:73:
+ 26:ce:ad:32:13:b8:d7:0f:1d:3b:df:dd:dc:08:36:
+ a8:6f:51:44:9b:ca:d6:20:52:73:b7:26:87:35:6a:
+ db:a9:e5:d4:59:a5:2b:fc:67:19:39:fa:93:18:18:
+ 6c:de:dd:25:8a:0e:33:14:47:c2:ef:01:50:79:e4:
+ fd:69:d1:a7:c0:ac:e2:57:6f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ BF:C0:30:EB:F5:43:11:3E:67:BA:9E:91:FB:FC:6A:DA:E3:6B:12:24
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ b8:8a:23:c6:48:96:b1:11:7c:60:77:5e:05:9a:ab:a1:c6:fa:
+ 82:1c:18:07:c4:eb:81:b0:a8:66:eb:49:a8:e9:0c:d3:29:ad:
+ f5:ef:24:4c:fd:e4:4b:ca:7f:5e:63:ab:99:27:cb:9f:36:21:
+ 2c:b9:10:60:67:cd:d2:b4:f0:f0:ab:71:e5:8b:5a:89:27:11:
+ 84:aa:8e:bf:99:f0:9d:09:21:0a:52:19:9a:5a:09:d2:90:b7:
+ fa:0c:f8:7e:78:a2:b0:85:af:5c:4c:99:d9:5c:55:29:f9:a5:
+ 51:42:2e:3a:cb:38:8c:78:3b:cb:f8:fb:95:87:bc:bc:90:f9:
+ 50:32
+-----BEGIN CERTIFICATE-----
+MIICsDCCAhmgAwIBAgIDC2dxMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDkwNjA4MjA0MzI3WhcNMTMwNjA3MTk0MzI3
+WjBGMQswCQYDVQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZ
+R29vZ2xlIEludGVybmV0IEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAye23pIucV+eEPkB9hPSP0XFjU5nneXQUr0SZMyCSjXvlKAy6rWxJfoNf
+NFlOCnowzdDXxFdF7dWq1nMmzq0yE7jXDx07393cCDaob1FEm8rWIFJztyaHNWrb
+qeXUWaUr/GcZOfqTGBhs3t0lig4zFEfC7wFQeeT9adGnwKziV28CAwEAAaOBozCB
+oDAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFL/AMOv1QxE+Z7qekfv8atrjaxIk
+MB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20v
+Y3Jscy9zZWN1cmVjYS5jcmwwDQYJKoZIhvcNAQEFBQADgYEAuIojxkiWsRF8YHde
+BZqrocb6ghwYB8TrgbCoZutJqOkM0ymt9e8kTP3kS8p/XmOrmSfLnzYhLLkQYGfN
+0rTw8Ktx5YtaiScRhKqOv5nwnQkhClIZmloJ0pC3+gz4fniisIWvXEyZ2VxVKfml
+UUIuOss4jHg7y/j7lYe8vJD5UDI=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert0[] = {
+ 0x30, 0x82, 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x0b, 0x67, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30,
+ 0x36, 0x30, 0x38, 0x32, 0x30, 0x34, 0x33, 0x32, 0x37, 0x5a, 0x17, 0x0d,
+ 0x31, 0x33, 0x30, 0x36, 0x30, 0x37, 0x31, 0x39, 0x34, 0x33, 0x32, 0x37,
+ 0x5a, 0x30, 0x46, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xc9, 0xed, 0xb7, 0xa4, 0x8b, 0x9c,
+ 0x57, 0xe7, 0x84, 0x3e, 0x40, 0x7d, 0x84, 0xf4, 0x8f, 0xd1, 0x71, 0x63,
+ 0x53, 0x99, 0xe7, 0x79, 0x74, 0x14, 0xaf, 0x44, 0x99, 0x33, 0x20, 0x92,
+ 0x8d, 0x7b, 0xe5, 0x28, 0x0c, 0xba, 0xad, 0x6c, 0x49, 0x7e, 0x83, 0x5f,
+ 0x34, 0x59, 0x4e, 0x0a, 0x7a, 0x30, 0xcd, 0xd0, 0xd7, 0xc4, 0x57, 0x45,
+ 0xed, 0xd5, 0xaa, 0xd6, 0x73, 0x26, 0xce, 0xad, 0x32, 0x13, 0xb8, 0xd7,
+ 0x0f, 0x1d, 0x3b, 0xdf, 0xdd, 0xdc, 0x08, 0x36, 0xa8, 0x6f, 0x51, 0x44,
+ 0x9b, 0xca, 0xd6, 0x20, 0x52, 0x73, 0xb7, 0x26, 0x87, 0x35, 0x6a, 0xdb,
+ 0xa9, 0xe5, 0xd4, 0x59, 0xa5, 0x2b, 0xfc, 0x67, 0x19, 0x39, 0xfa, 0x93,
+ 0x18, 0x18, 0x6c, 0xde, 0xdd, 0x25, 0x8a, 0x0e, 0x33, 0x14, 0x47, 0xc2,
+ 0xef, 0x01, 0x50, 0x79, 0xe4, 0xfd, 0x69, 0xd1, 0xa7, 0xc0, 0xac, 0xe2,
+ 0x57, 0x6f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa3, 0x30, 0x81,
+ 0xa0, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xbf, 0xc0, 0x30, 0xeb, 0xf5, 0x43, 0x11, 0x3e,
+ 0x67, 0xba, 0x9e, 0x91, 0xfb, 0xfc, 0x6a, 0xda, 0xe3, 0x6b, 0x12, 0x24,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8,
+ 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0xb8, 0x8a, 0x23, 0xc6, 0x48, 0x96, 0xb1, 0x11, 0x7c, 0x60, 0x77, 0x5e,
+ 0x05, 0x9a, 0xab, 0xa1, 0xc6, 0xfa, 0x82, 0x1c, 0x18, 0x07, 0xc4, 0xeb,
+ 0x81, 0xb0, 0xa8, 0x66, 0xeb, 0x49, 0xa8, 0xe9, 0x0c, 0xd3, 0x29, 0xad,
+ 0xf5, 0xef, 0x24, 0x4c, 0xfd, 0xe4, 0x4b, 0xca, 0x7f, 0x5e, 0x63, 0xab,
+ 0x99, 0x27, 0xcb, 0x9f, 0x36, 0x21, 0x2c, 0xb9, 0x10, 0x60, 0x67, 0xcd,
+ 0xd2, 0xb4, 0xf0, 0xf0, 0xab, 0x71, 0xe5, 0x8b, 0x5a, 0x89, 0x27, 0x11,
+ 0x84, 0xaa, 0x8e, 0xbf, 0x99, 0xf0, 0x9d, 0x09, 0x21, 0x0a, 0x52, 0x19,
+ 0x9a, 0x5a, 0x09, 0xd2, 0x90, 0xb7, 0xfa, 0x0c, 0xf8, 0x7e, 0x78, 0xa2,
+ 0xb0, 0x85, 0xaf, 0x5c, 0x4c, 0x99, 0xd9, 0x5c, 0x55, 0x29, 0xf9, 0xa5,
+ 0x51, 0x42, 0x2e, 0x3a, 0xcb, 0x38, 0x8c, 0x78, 0x3b, 0xcb, 0xf8, 0xfb,
+ 0x95, 0x87, 0xbc, 0xbc, 0x90, 0xf9, 0x50, 0x32,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1227750 (0x12bbe6)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: May 21 04:00:00 2002 GMT
+ Not After : Aug 21 04:00:00 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df:
+ 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8:
+ 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29:
+ bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4:
+ 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3:
+ ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92:
+ 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d:
+ 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14:
+ 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd:
+ d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6:
+ d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5:
+ 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39:
+ 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05:
+ 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2:
+ fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32:
+ eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07:
+ 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b:
+ e4:f9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Subject Key Identifier:
+ C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.geotrust.com/resources/repository
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 76:e1:12:6e:4e:4b:16:12:86:30:06:b2:81:08:cf:f0:08:c7:
+ c7:71:7e:66:ee:c2:ed:d4:3b:1f:ff:f0:f0:c8:4e:d6:43:38:
+ b0:b9:30:7d:18:d0:55:83:a2:6a:cb:36:11:9c:e8:48:66:a3:
+ 6d:7f:b8:13:d4:47:fe:8b:5a:5c:73:fc:ae:d9:1b:32:19:38:
+ ab:97:34:14:aa:96:d2:eb:a3:1c:14:08:49:b6:bb:e5:91:ef:
+ 83:36:eb:1d:56:6f:ca:da:bc:73:63:90:e4:7f:7b:3e:22:cb:
+ 3d:07:ed:5f:38:74:9c:e3:03:50:4e:a1:af:98:ee:61:f2:84:
+ 3f:12
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
+WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
+OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
+T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
+JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
+Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
+PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
+aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
+TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
+LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
+BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
+dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
+AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
+NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
+b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert1[] = {
+ 0x30, 0x82, 0x03, 0x7d, 0x30, 0x82, 0x02, 0xe6, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x12, 0xbb, 0xe6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x32, 0x30,
+ 0x35, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x30, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x12, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0xcc, 0x18, 0x63, 0x30, 0xfd,
+ 0xf4, 0x17, 0x23, 0x1a, 0x56, 0x7e, 0x5b, 0xdf, 0x3c, 0x6c, 0x38, 0xe4,
+ 0x71, 0xb7, 0x78, 0x91, 0xd4, 0xbc, 0xa1, 0xd8, 0x4c, 0xf8, 0xa8, 0x43,
+ 0xb6, 0x03, 0xe9, 0x4d, 0x21, 0x07, 0x08, 0x88, 0xda, 0x58, 0x2f, 0x66,
+ 0x39, 0x29, 0xbd, 0x05, 0x78, 0x8b, 0x9d, 0x38, 0xe8, 0x05, 0xb7, 0x6a,
+ 0x7e, 0x71, 0xa4, 0xe6, 0xc4, 0x60, 0xa6, 0xb0, 0xef, 0x80, 0xe4, 0x89,
+ 0x28, 0x0f, 0x9e, 0x25, 0xd6, 0xed, 0x83, 0xf3, 0xad, 0xa6, 0x91, 0xc7,
+ 0x98, 0xc9, 0x42, 0x18, 0x35, 0x14, 0x9d, 0xad, 0x98, 0x46, 0x92, 0x2e,
+ 0x4f, 0xca, 0xf1, 0x87, 0x43, 0xc1, 0x16, 0x95, 0x57, 0x2d, 0x50, 0xef,
+ 0x89, 0x2d, 0x80, 0x7a, 0x57, 0xad, 0xf2, 0xee, 0x5f, 0x6b, 0xd2, 0x00,
+ 0x8d, 0xb9, 0x14, 0xf8, 0x14, 0x15, 0x35, 0xd9, 0xc0, 0x46, 0xa3, 0x7b,
+ 0x72, 0xc8, 0x91, 0xbf, 0xc9, 0x55, 0x2b, 0xcd, 0xd0, 0x97, 0x3e, 0x9c,
+ 0x26, 0x64, 0xcc, 0xdf, 0xce, 0x83, 0x19, 0x71, 0xca, 0x4e, 0xe6, 0xd4,
+ 0xd5, 0x7b, 0xa9, 0x19, 0xcd, 0x55, 0xde, 0xc8, 0xec, 0xd2, 0x5e, 0x38,
+ 0x53, 0xe5, 0x5c, 0x4f, 0x8c, 0x2d, 0xfe, 0x50, 0x23, 0x36, 0xfc, 0x66,
+ 0xe6, 0xcb, 0x8e, 0xa4, 0x39, 0x19, 0x00, 0xb7, 0x95, 0x02, 0x39, 0x91,
+ 0x0b, 0x0e, 0xfe, 0x38, 0x2e, 0xd1, 0x1d, 0x05, 0x9a, 0xf6, 0x4d, 0x3e,
+ 0x6f, 0x0f, 0x07, 0x1d, 0xaf, 0x2c, 0x1e, 0x8f, 0x60, 0x39, 0xe2, 0xfa,
+ 0x36, 0x53, 0x13, 0x39, 0xd4, 0x5e, 0x26, 0x2b, 0xdb, 0x3d, 0xa8, 0x14,
+ 0xbd, 0x32, 0xeb, 0x18, 0x03, 0x28, 0x52, 0x04, 0x71, 0xe5, 0xab, 0x33,
+ 0x3d, 0xe1, 0x38, 0xbb, 0x07, 0x36, 0x84, 0x62, 0x9c, 0x79, 0xea, 0x16,
+ 0x30, 0xf4, 0x5f, 0xc0, 0x2b, 0xe8, 0x71, 0x6b, 0xe4, 0xf9, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xf0, 0x30, 0x81, 0xed, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6,
+ 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10,
+ 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0,
+ 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4e,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x76, 0xe1, 0x12, 0x6e, 0x4e, 0x4b, 0x16, 0x12, 0x86, 0x30, 0x06,
+ 0xb2, 0x81, 0x08, 0xcf, 0xf0, 0x08, 0xc7, 0xc7, 0x71, 0x7e, 0x66, 0xee,
+ 0xc2, 0xed, 0xd4, 0x3b, 0x1f, 0xff, 0xf0, 0xf0, 0xc8, 0x4e, 0xd6, 0x43,
+ 0x38, 0xb0, 0xb9, 0x30, 0x7d, 0x18, 0xd0, 0x55, 0x83, 0xa2, 0x6a, 0xcb,
+ 0x36, 0x11, 0x9c, 0xe8, 0x48, 0x66, 0xa3, 0x6d, 0x7f, 0xb8, 0x13, 0xd4,
+ 0x47, 0xfe, 0x8b, 0x5a, 0x5c, 0x73, 0xfc, 0xae, 0xd9, 0x1b, 0x32, 0x19,
+ 0x38, 0xab, 0x97, 0x34, 0x14, 0xaa, 0x96, 0xd2, 0xeb, 0xa3, 0x1c, 0x14,
+ 0x08, 0x49, 0xb6, 0xbb, 0xe5, 0x91, 0xef, 0x83, 0x36, 0xeb, 0x1d, 0x56,
+ 0x6f, 0xca, 0xda, 0xbc, 0x73, 0x63, 0x90, 0xe4, 0x7f, 0x7b, 0x3e, 0x22,
+ 0xcb, 0x3d, 0x07, 0xed, 0x5f, 0x38, 0x74, 0x9c, 0xe3, 0x03, 0x50, 0x4e,
+ 0xa1, 0xaf, 0x98, 0xee, 0x61, 0xf2, 0x84, 0x3f, 0x12,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 46:fc:eb:ba:b4:d0:2f:0f:92:60:98:23:3f:93:07:8f
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Apr 17 00:00:00 1997 GMT
+ Not After : Oct 24 23:59:59 2016 GMT
+ Subject: O=VeriSign Trust Network, OU=VeriSign, Inc., OU=VeriSign International Server CA - Class 3, OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY LTD.(c)97 VeriSign
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:d8:82:80:e8:d6:19:02:7d:1f:85:18:39:25:a2:
+ 65:2b:e1:bf:d4:05:d3:bc:e6:36:3b:aa:f0:4c:6c:
+ 5b:b6:e7:aa:3c:73:45:55:b2:f1:bd:ea:97:42:ed:
+ 9a:34:0a:15:d4:a9:5c:f5:40:25:dd:d9:07:c1:32:
+ b2:75:6c:c4:ca:bb:a3:fe:56:27:71:43:aa:63:f5:
+ 30:3e:93:28:e5:fa:f1:09:3b:f3:b7:4d:4e:39:f7:
+ 5c:49:5a:b8:c1:1d:d3:b2:8a:fe:70:30:95:42:cb:
+ fe:2b:51:8b:5a:3c:3a:f9:22:4f:90:b2:02:a7:53:
+ 9c:4f:34:e7:ab:04:b2:7b:6f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.1.1
+ CPS: https://www.verisign.com/CPS
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ X509v3 Key Usage:
+ Certificate Sign, CRL Sign
+ Netscape Cert Type:
+ SSL CA, S/MIME CA
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 40:8e:49:97:96:8a:73:dd:8e:4d:ef:3e:61:b7:ca:a0:62:ad:
+ f4:0e:0a:bb:75:3d:e2:6e:d8:2c:c7:bf:f4:b9:8c:36:9b:ca:
+ a2:d0:9c:72:46:39:f6:a6:82:03:65:11:c4:bc:bf:2d:a6:f5:
+ d9:3b:0a:b5:98:fa:b3:78:b9:1e:f2:2b:4c:62:d5:fd:b2:7a:
+ 1d:df:33:fd:73:f9:a5:d8:2d:8c:2a:ea:d1:fc:b0:28:b6:e9:
+ 49:48:13:4b:83:8a:1b:48:7b:24:f7:38:de:6f:41:54:b8:ab:
+ 57:6b:06:df:c7:a2:d4:a9:f6:f1:36:62:80:88:f2:8b:75:d6:
+ 80:71
+-----BEGIN CERTIFICATE-----
+MIIDgzCCAuygAwIBAgIQRvzrurTQLw+SYJgjP5MHjzANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNOTcwNDE3MDAwMDAwWhcNMTYxMDI0MjM1OTU5WjCBujEfMB0GA1UEChMWVmVy
+aVNpZ24gVHJ1c3QgTmV0d29yazEXMBUGA1UECxMOVmVyaVNpZ24sIEluYy4xMzAx
+BgNVBAsTKlZlcmlTaWduIEludGVybmF0aW9uYWwgU2VydmVyIENBIC0gQ2xhc3Mg
+MzFJMEcGA1UECxNAd3d3LnZlcmlzaWduLmNvbS9DUFMgSW5jb3JwLmJ5IFJlZi4g
+TElBQklMSVRZIExURC4oYyk5NyBWZXJpU2lnbjCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEA2IKA6NYZAn0fhRg5JaJlK+G/1AXTvOY2O6rwTGxbtueqPHNFVbLx
+veqXQu2aNAoV1Klc9UAl3dkHwTKydWzEyruj/lYncUOqY/UwPpMo5frxCTvzt01O
+OfdcSVq4wR3Tsor+cDCVQsv+K1GLWjw6+SJPkLICp1OcTzTnqwSye28CAwEAAaOB
+4zCB4DAPBgNVHRMECDAGAQH/AgEAMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw
+KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL0NQUzA0BgNV
+HSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIGCWCGSAGG+EIEAQYKYIZIAYb4RQEI
+ATALBgNVHQ8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMDEGA1UdHwQqMCgwJqAk
+oCKGIGh0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA0GCSqGSIb3DQEB
+BQUAA4GBAECOSZeWinPdjk3vPmG3yqBirfQOCrt1PeJu2CzHv/S5jDabyqLQnHJG
+OfamggNlEcS8vy2m9dk7CrWY+rN4uR7yK0xi1f2yeh3fM/1z+aXYLYwq6tH8sCi2
+6UlIE0uDihtIeyT3ON5vQVS4q1drBt/HotSp9vE2YoCI8ot11oBx
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert2[] = {
+ 0x30, 0x82, 0x03, 0x83, 0x30, 0x82, 0x02, 0xec, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x46, 0xfc, 0xeb, 0xba, 0xb4, 0xd0, 0x2f, 0x0f, 0x92,
+ 0x60, 0x98, 0x23, 0x3f, 0x93, 0x07, 0x8f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x39, 0x37, 0x30, 0x34, 0x31, 0x37, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x30, 0x32, 0x34,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x1f,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x16, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x17, 0x30, 0x15, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69,
+ 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x31, 0x49, 0x30, 0x47, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x40,
+ 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x49, 0x6e, 0x63,
+ 0x6f, 0x72, 0x70, 0x2e, 0x62, 0x79, 0x20, 0x52, 0x65, 0x66, 0x2e, 0x20,
+ 0x4c, 0x49, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x20, 0x4c, 0x54,
+ 0x44, 0x2e, 0x28, 0x63, 0x29, 0x39, 0x37, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81,
+ 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd8, 0x82, 0x80,
+ 0xe8, 0xd6, 0x19, 0x02, 0x7d, 0x1f, 0x85, 0x18, 0x39, 0x25, 0xa2, 0x65,
+ 0x2b, 0xe1, 0xbf, 0xd4, 0x05, 0xd3, 0xbc, 0xe6, 0x36, 0x3b, 0xaa, 0xf0,
+ 0x4c, 0x6c, 0x5b, 0xb6, 0xe7, 0xaa, 0x3c, 0x73, 0x45, 0x55, 0xb2, 0xf1,
+ 0xbd, 0xea, 0x97, 0x42, 0xed, 0x9a, 0x34, 0x0a, 0x15, 0xd4, 0xa9, 0x5c,
+ 0xf5, 0x40, 0x25, 0xdd, 0xd9, 0x07, 0xc1, 0x32, 0xb2, 0x75, 0x6c, 0xc4,
+ 0xca, 0xbb, 0xa3, 0xfe, 0x56, 0x27, 0x71, 0x43, 0xaa, 0x63, 0xf5, 0x30,
+ 0x3e, 0x93, 0x28, 0xe5, 0xfa, 0xf1, 0x09, 0x3b, 0xf3, 0xb7, 0x4d, 0x4e,
+ 0x39, 0xf7, 0x5c, 0x49, 0x5a, 0xb8, 0xc1, 0x1d, 0xd3, 0xb2, 0x8a, 0xfe,
+ 0x70, 0x30, 0x95, 0x42, 0xcb, 0xfe, 0x2b, 0x51, 0x8b, 0x5a, 0x3c, 0x3a,
+ 0xf9, 0x22, 0x4f, 0x90, 0xb2, 0x02, 0xa7, 0x53, 0x9c, 0x4f, 0x34, 0xe7,
+ 0xab, 0x04, 0xb2, 0x7b, 0x6f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xe3, 0x30, 0x81, 0xe0, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x44, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x0b,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x01, 0x01, 0x30,
+ 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x34, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04,
+ 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08,
+ 0x01, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x31, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a, 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24,
+ 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x40, 0x8e, 0x49, 0x97, 0x96,
+ 0x8a, 0x73, 0xdd, 0x8e, 0x4d, 0xef, 0x3e, 0x61, 0xb7, 0xca, 0xa0, 0x62,
+ 0xad, 0xf4, 0x0e, 0x0a, 0xbb, 0x75, 0x3d, 0xe2, 0x6e, 0xd8, 0x2c, 0xc7,
+ 0xbf, 0xf4, 0xb9, 0x8c, 0x36, 0x9b, 0xca, 0xa2, 0xd0, 0x9c, 0x72, 0x46,
+ 0x39, 0xf6, 0xa6, 0x82, 0x03, 0x65, 0x11, 0xc4, 0xbc, 0xbf, 0x2d, 0xa6,
+ 0xf5, 0xd9, 0x3b, 0x0a, 0xb5, 0x98, 0xfa, 0xb3, 0x78, 0xb9, 0x1e, 0xf2,
+ 0x2b, 0x4c, 0x62, 0xd5, 0xfd, 0xb2, 0x7a, 0x1d, 0xdf, 0x33, 0xfd, 0x73,
+ 0xf9, 0xa5, 0xd8, 0x2d, 0x8c, 0x2a, 0xea, 0xd1, 0xfc, 0xb0, 0x28, 0xb6,
+ 0xe9, 0x49, 0x48, 0x13, 0x4b, 0x83, 0x8a, 0x1b, 0x48, 0x7b, 0x24, 0xf7,
+ 0x38, 0xde, 0x6f, 0x41, 0x54, 0xb8, 0xab, 0x57, 0x6b, 0x06, 0xdf, 0xc7,
+ 0xa2, 0xd4, 0xa9, 0xf6, 0xf1, 0x36, 0x62, 0x80, 0x88, 0xf2, 0x8b, 0x75,
+ 0xd6, 0x80, 0x71,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 429597 (0x68e1d)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: Nov 28 16:08:31 2006 GMT
+ Not After : Aug 21 15:08:31 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:be:b8:15:7b:ff:d4:7c:7d:67:ad:83:64:7b:c8:
+ 42:53:2d:df:f6:84:08:20:61:d6:01:59:6a:9c:44:
+ 11:af:ef:76:fd:95:7e:ce:61:30:bb:7a:83:5f:02:
+ bd:01:66:ca:ee:15:8d:6f:a1:30:9c:bd:a1:85:9e:
+ 94:3a:f3:56:88:00:31:cf:d8:ee:6a:96:02:d9:ed:
+ 03:8c:fb:75:6d:e7:ea:b8:55:16:05:16:9a:f4:e0:
+ 5e:b1:88:c0:64:85:5c:15:4d:88:c7:b7:ba:e0:75:
+ e9:ad:05:3d:9d:c7:89:48:e0:bb:28:c8:03:e1:30:
+ 93:64:5e:52:c0:59:70:22:35:57:88:8a:f1:95:0a:
+ 83:d7:bc:31:73:01:34:ed:ef:46:71:e0:6b:02:a8:
+ 35:72:6b:97:9b:66:e0:cb:1c:79:5f:d8:1a:04:68:
+ 1e:47:02:e6:9d:60:e2:36:97:01:df:ce:35:92:df:
+ be:67:c7:6d:77:59:3b:8f:9d:d6:90:15:94:bc:42:
+ 34:10:c1:39:f9:b1:27:3e:7e:d6:8a:75:c5:b2:af:
+ 96:d3:a2:de:9b:e4:98:be:7d:e1:e9:81:ad:b6:6f:
+ fc:d7:0e:da:e0:34:b0:0d:1a:77:e7:e3:08:98:ef:
+ 58:fa:9c:84:b7:36:af:c2:df:ac:d2:f4:10:06:70:
+ 71:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 7b:60:06:e9:dd:a7:1d:29:08:ef:11:f9:d5:3b:3c:d2:2b:53:
+ cb:3e:ed:be:76:60:64:48:a0:e6:cb:e8:49:c3:1a:bf:dd:ad:
+ c5:4c:bd:53:48:55:41:db:18:b1:4e:3b:3a:68:2c:24:5a:41:
+ f5:c8:a9:44:a6:32:29:2d:75:f8:4d:f2:50:8e:f0:e2:9b:e9:
+ e1:e4:3b:70:b7:32:89:db:a8:39:c5:5b:68:56:bd:04:15:c3:
+ b6:cb:1b:24:4a:a7:fc:c4:d5:8d:b6:98:dd:03:f6:b1:b3:94:
+ da:3f:52:a0:a4:50:06:ca:45:67:4e:ff:f1:41:89:40:00:36:
+ 7e:79
+-----BEGIN CERTIFICATE-----
+MIIDizCCAvSgAwIBAgIDBo4dMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI4MTYwODMxWhcNMTgwODIxMTUwODMx
+WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE
+AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE
+CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y
+7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI
+A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo
+HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi
+3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA
+AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7
+a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB
+/wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j
+b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB
+BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ
+KoZIhvcNAQEFBQADgYEAe2AG6d2nHSkI7xH51Ts80itTyz7tvnZgZEig5svoScMa
+v92txUy9U0hVQdsYsU47OmgsJFpB9cipRKYyKS11+E3yUI7w4pvp4eQ7cLcyiduo
+OcVbaFa9BBXDtssbJEqn/MTVjbaY3QP2sbOU2j9SoKRQBspFZ07/8UGJQAA2fnk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert3[] = {
+ 0x30, 0x82, 0x03, 0x8b, 0x30, 0x82, 0x02, 0xf4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x06, 0x8e, 0x1d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31,
+ 0x31, 0x32, 0x38, 0x31, 0x36, 0x30, 0x38, 0x33, 0x31, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x31, 0x35, 0x30, 0x38, 0x33, 0x31,
+ 0x5a, 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xbe, 0xb8, 0x15, 0x7b, 0xff, 0xd4, 0x7c, 0x7d,
+ 0x67, 0xad, 0x83, 0x64, 0x7b, 0xc8, 0x42, 0x53, 0x2d, 0xdf, 0xf6, 0x84,
+ 0x08, 0x20, 0x61, 0xd6, 0x01, 0x59, 0x6a, 0x9c, 0x44, 0x11, 0xaf, 0xef,
+ 0x76, 0xfd, 0x95, 0x7e, 0xce, 0x61, 0x30, 0xbb, 0x7a, 0x83, 0x5f, 0x02,
+ 0xbd, 0x01, 0x66, 0xca, 0xee, 0x15, 0x8d, 0x6f, 0xa1, 0x30, 0x9c, 0xbd,
+ 0xa1, 0x85, 0x9e, 0x94, 0x3a, 0xf3, 0x56, 0x88, 0x00, 0x31, 0xcf, 0xd8,
+ 0xee, 0x6a, 0x96, 0x02, 0xd9, 0xed, 0x03, 0x8c, 0xfb, 0x75, 0x6d, 0xe7,
+ 0xea, 0xb8, 0x55, 0x16, 0x05, 0x16, 0x9a, 0xf4, 0xe0, 0x5e, 0xb1, 0x88,
+ 0xc0, 0x64, 0x85, 0x5c, 0x15, 0x4d, 0x88, 0xc7, 0xb7, 0xba, 0xe0, 0x75,
+ 0xe9, 0xad, 0x05, 0x3d, 0x9d, 0xc7, 0x89, 0x48, 0xe0, 0xbb, 0x28, 0xc8,
+ 0x03, 0xe1, 0x30, 0x93, 0x64, 0x5e, 0x52, 0xc0, 0x59, 0x70, 0x22, 0x35,
+ 0x57, 0x88, 0x8a, 0xf1, 0x95, 0x0a, 0x83, 0xd7, 0xbc, 0x31, 0x73, 0x01,
+ 0x34, 0xed, 0xef, 0x46, 0x71, 0xe0, 0x6b, 0x02, 0xa8, 0x35, 0x72, 0x6b,
+ 0x97, 0x9b, 0x66, 0xe0, 0xcb, 0x1c, 0x79, 0x5f, 0xd8, 0x1a, 0x04, 0x68,
+ 0x1e, 0x47, 0x02, 0xe6, 0x9d, 0x60, 0xe2, 0x36, 0x97, 0x01, 0xdf, 0xce,
+ 0x35, 0x92, 0xdf, 0xbe, 0x67, 0xc7, 0x6d, 0x77, 0x59, 0x3b, 0x8f, 0x9d,
+ 0xd6, 0x90, 0x15, 0x94, 0xbc, 0x42, 0x34, 0x10, 0xc1, 0x39, 0xf9, 0xb1,
+ 0x27, 0x3e, 0x7e, 0xd6, 0x8a, 0x75, 0xc5, 0xb2, 0xaf, 0x96, 0xd3, 0xa2,
+ 0xde, 0x9b, 0xe4, 0x98, 0xbe, 0x7d, 0xe1, 0xe9, 0x81, 0xad, 0xb6, 0x6f,
+ 0xfc, 0xd7, 0x0e, 0xda, 0xe0, 0x34, 0xb0, 0x0d, 0x1a, 0x77, 0xe7, 0xe3,
+ 0x08, 0x98, 0xef, 0x58, 0xfa, 0x9c, 0x84, 0xb7, 0x36, 0xaf, 0xc2, 0xdf,
+ 0xac, 0xd2, 0xf4, 0x10, 0x06, 0x70, 0x71, 0x35, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xe8, 0x30, 0x81, 0xe5, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2c, 0xd5,
+ 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb,
+ 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b,
+ 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98,
+ 0x90, 0x9f, 0xd4, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0,
+ 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0x7b, 0x60, 0x06, 0xe9, 0xdd, 0xa7, 0x1d, 0x29, 0x08,
+ 0xef, 0x11, 0xf9, 0xd5, 0x3b, 0x3c, 0xd2, 0x2b, 0x53, 0xcb, 0x3e, 0xed,
+ 0xbe, 0x76, 0x60, 0x64, 0x48, 0xa0, 0xe6, 0xcb, 0xe8, 0x49, 0xc3, 0x1a,
+ 0xbf, 0xdd, 0xad, 0xc5, 0x4c, 0xbd, 0x53, 0x48, 0x55, 0x41, 0xdb, 0x18,
+ 0xb1, 0x4e, 0x3b, 0x3a, 0x68, 0x2c, 0x24, 0x5a, 0x41, 0xf5, 0xc8, 0xa9,
+ 0x44, 0xa6, 0x32, 0x29, 0x2d, 0x75, 0xf8, 0x4d, 0xf2, 0x50, 0x8e, 0xf0,
+ 0xe2, 0x9b, 0xe9, 0xe1, 0xe4, 0x3b, 0x70, 0xb7, 0x32, 0x89, 0xdb, 0xa8,
+ 0x39, 0xc5, 0x5b, 0x68, 0x56, 0xbd, 0x04, 0x15, 0xc3, 0xb6, 0xcb, 0x1b,
+ 0x24, 0x4a, 0xa7, 0xfc, 0xc4, 0xd5, 0x8d, 0xb6, 0x98, 0xdd, 0x03, 0xf6,
+ 0xb1, 0xb3, 0x94, 0xda, 0x3f, 0x52, 0xa0, 0xa4, 0x50, 0x06, 0xca, 0x45,
+ 0x67, 0x4e, 0xff, 0xf1, 0x41, 0x89, 0x40, 0x00, 0x36, 0x7e, 0x79,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 880226 (0xd6e62)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Equifax, OU=Equifax Secure Certificate Authority
+ Validity
+ Not Before: Nov 27 00:00:00 2006 GMT
+ Not After : Aug 21 16:15:00 2018 GMT
+ Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:be:b8:15:7b:ff:d4:7c:7d:67:ad:83:64:7b:c8:
+ 42:53:2d:df:f6:84:08:20:61:d6:01:59:6a:9c:44:
+ 11:af:ef:76:fd:95:7e:ce:61:30:bb:7a:83:5f:02:
+ bd:01:66:ca:ee:15:8d:6f:a1:30:9c:bd:a1:85:9e:
+ 94:3a:f3:56:88:00:31:cf:d8:ee:6a:96:02:d9:ed:
+ 03:8c:fb:75:6d:e7:ea:b8:55:16:05:16:9a:f4:e0:
+ 5e:b1:88:c0:64:85:5c:15:4d:88:c7:b7:ba:e0:75:
+ e9:ad:05:3d:9d:c7:89:48:e0:bb:28:c8:03:e1:30:
+ 93:64:5e:52:c0:59:70:22:35:57:88:8a:f1:95:0a:
+ 83:d7:bc:31:73:01:34:ed:ef:46:71:e0:6b:02:a8:
+ 35:72:6b:97:9b:66:e0:cb:1c:79:5f:d8:1a:04:68:
+ 1e:47:02:e6:9d:60:e2:36:97:01:df:ce:35:92:df:
+ be:67:c7:6d:77:59:3b:8f:9d:d6:90:15:94:bc:42:
+ 34:10:c1:39:f9:b1:27:3e:7e:d6:8a:75:c5:b2:af:
+ 96:d3:a2:de:9b:e4:98:be:7d:e1:e9:81:ad:b6:6f:
+ fc:d7:0e:da:e0:34:b0:0d:1a:77:e7:e3:08:98:ef:
+ 58:fa:9c:84:b7:36:af:c2:df:ac:d2:f4:10:06:70:
+ 71:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+ X509v3 Authority Key Identifier:
+ keyid:48:E6:68:F9:2B:D2:B2:95:D7:47:D8:23:20:10:4F:33:98:90:9F:D4
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/secureca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.geotrust.com/resources/cps
+
+ Signature Algorithm: sha1WithRSAEncryption
+ af:f3:0e:d6:72:ab:c7:a9:97:ca:2a:6b:84:39:de:79:a9:f0:
+ 81:e5:08:67:ab:d7:2f:20:02:01:71:0c:04:22:c9:1e:88:95:
+ 03:c9:49:3a:af:67:08:49:b0:d5:08:f5:20:3d:80:91:a0:c5:
+ 87:a3:fb:c9:a3:17:91:f9:a8:2f:ae:e9:0f:df:96:72:0f:75:
+ 17:80:5d:78:01:4d:9f:1f:6d:7b:d8:f5:42:38:23:1a:99:93:
+ f4:83:be:3b:35:74:e7:37:13:35:7a:ac:b4:b6:90:82:6c:27:
+ a4:e0:ec:9e:35:bd:bf:e5:29:a1:47:9f:5b:32:fc:e9:99:7d:
+ 2b:39
+-----BEGIN CERTIFICATE-----
+MIIDizCCAvSgAwIBAgIDDW5iMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMTI3MDAwMDAwWhcNMTgwODIxMTYxNTAw
+WjBYMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UE
+AxMoR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64FXv/1Hx9Z62DZHvIQlMt3/aE
+CCBh1gFZapxEEa/vdv2Vfs5hMLt6g18CvQFmyu4VjW+hMJy9oYWelDrzVogAMc/Y
+7mqWAtntA4z7dW3n6rhVFgUWmvTgXrGIwGSFXBVNiMe3uuB16a0FPZ3HiUjguyjI
+A+Ewk2ReUsBZcCI1V4iK8ZUKg9e8MXMBNO3vRnHgawKoNXJrl5tm4MsceV/YGgRo
+HkcC5p1g4jaXAd/ONZLfvmfHbXdZO4+d1pAVlLxCNBDBOfmxJz5+1op1xbKvltOi
+3pvkmL594emBrbZv/NcO2uA0sA0ad+fjCJjvWPqchLc2r8LfrNL0EAZwcTUCAwEA
+AaOB6DCB5TAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFCzVUEGXFYvwjzZhW0r7
+a9mZyTOSMB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMA8GA1UdEwEB
+/wQFMAMBAf8wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j
+b20vY3Jscy9zZWN1cmVjYS5jcmwwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYB
+BQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1c3QuY29tL3Jlc291cmNlcy9jcHMwDQYJ
+KoZIhvcNAQEFBQADgYEAr/MO1nKrx6mXyiprhDneeanwgeUIZ6vXLyACAXEMBCLJ
+HoiVA8lJOq9nCEmw1Qj1ID2AkaDFh6P7yaMXkfmoL67pD9+Wcg91F4BdeAFNnx9t
+e9j1QjgjGpmT9IO+OzV05zcTNXqstLaQgmwnpODsnjW9v+UpoUefWzL86Zl9Kzk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert4[] = {
+ 0x30, 0x82, 0x03, 0x8b, 0x30, 0x82, 0x02, 0xf4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x0d, 0x6e, 0x62, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31,
+ 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d,
+ 0x31, 0x38, 0x30, 0x38, 0x32, 0x31, 0x31, 0x36, 0x31, 0x35, 0x30, 0x30,
+ 0x5a, 0x30, 0x58, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x28, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xbe, 0xb8, 0x15, 0x7b, 0xff, 0xd4, 0x7c, 0x7d,
+ 0x67, 0xad, 0x83, 0x64, 0x7b, 0xc8, 0x42, 0x53, 0x2d, 0xdf, 0xf6, 0x84,
+ 0x08, 0x20, 0x61, 0xd6, 0x01, 0x59, 0x6a, 0x9c, 0x44, 0x11, 0xaf, 0xef,
+ 0x76, 0xfd, 0x95, 0x7e, 0xce, 0x61, 0x30, 0xbb, 0x7a, 0x83, 0x5f, 0x02,
+ 0xbd, 0x01, 0x66, 0xca, 0xee, 0x15, 0x8d, 0x6f, 0xa1, 0x30, 0x9c, 0xbd,
+ 0xa1, 0x85, 0x9e, 0x94, 0x3a, 0xf3, 0x56, 0x88, 0x00, 0x31, 0xcf, 0xd8,
+ 0xee, 0x6a, 0x96, 0x02, 0xd9, 0xed, 0x03, 0x8c, 0xfb, 0x75, 0x6d, 0xe7,
+ 0xea, 0xb8, 0x55, 0x16, 0x05, 0x16, 0x9a, 0xf4, 0xe0, 0x5e, 0xb1, 0x88,
+ 0xc0, 0x64, 0x85, 0x5c, 0x15, 0x4d, 0x88, 0xc7, 0xb7, 0xba, 0xe0, 0x75,
+ 0xe9, 0xad, 0x05, 0x3d, 0x9d, 0xc7, 0x89, 0x48, 0xe0, 0xbb, 0x28, 0xc8,
+ 0x03, 0xe1, 0x30, 0x93, 0x64, 0x5e, 0x52, 0xc0, 0x59, 0x70, 0x22, 0x35,
+ 0x57, 0x88, 0x8a, 0xf1, 0x95, 0x0a, 0x83, 0xd7, 0xbc, 0x31, 0x73, 0x01,
+ 0x34, 0xed, 0xef, 0x46, 0x71, 0xe0, 0x6b, 0x02, 0xa8, 0x35, 0x72, 0x6b,
+ 0x97, 0x9b, 0x66, 0xe0, 0xcb, 0x1c, 0x79, 0x5f, 0xd8, 0x1a, 0x04, 0x68,
+ 0x1e, 0x47, 0x02, 0xe6, 0x9d, 0x60, 0xe2, 0x36, 0x97, 0x01, 0xdf, 0xce,
+ 0x35, 0x92, 0xdf, 0xbe, 0x67, 0xc7, 0x6d, 0x77, 0x59, 0x3b, 0x8f, 0x9d,
+ 0xd6, 0x90, 0x15, 0x94, 0xbc, 0x42, 0x34, 0x10, 0xc1, 0x39, 0xf9, 0xb1,
+ 0x27, 0x3e, 0x7e, 0xd6, 0x8a, 0x75, 0xc5, 0xb2, 0xaf, 0x96, 0xd3, 0xa2,
+ 0xde, 0x9b, 0xe4, 0x98, 0xbe, 0x7d, 0xe1, 0xe9, 0x81, 0xad, 0xb6, 0x6f,
+ 0xfc, 0xd7, 0x0e, 0xda, 0xe0, 0x34, 0xb0, 0x0d, 0x1a, 0x77, 0xe7, 0xe3,
+ 0x08, 0x98, 0xef, 0x58, 0xfa, 0x9c, 0x84, 0xb7, 0x36, 0xaf, 0xc2, 0xdf,
+ 0xac, 0xd2, 0xf4, 0x10, 0x06, 0x70, 0x71, 0x35, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xe8, 0x30, 0x81, 0xe5, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2c, 0xd5,
+ 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b, 0x4a, 0xfb,
+ 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b,
+ 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98,
+ 0x90, 0x9f, 0xd4, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0,
+ 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0xaf, 0xf3, 0x0e, 0xd6, 0x72, 0xab, 0xc7, 0xa9, 0x97,
+ 0xca, 0x2a, 0x6b, 0x84, 0x39, 0xde, 0x79, 0xa9, 0xf0, 0x81, 0xe5, 0x08,
+ 0x67, 0xab, 0xd7, 0x2f, 0x20, 0x02, 0x01, 0x71, 0x0c, 0x04, 0x22, 0xc9,
+ 0x1e, 0x88, 0x95, 0x03, 0xc9, 0x49, 0x3a, 0xaf, 0x67, 0x08, 0x49, 0xb0,
+ 0xd5, 0x08, 0xf5, 0x20, 0x3d, 0x80, 0x91, 0xa0, 0xc5, 0x87, 0xa3, 0xfb,
+ 0xc9, 0xa3, 0x17, 0x91, 0xf9, 0xa8, 0x2f, 0xae, 0xe9, 0x0f, 0xdf, 0x96,
+ 0x72, 0x0f, 0x75, 0x17, 0x80, 0x5d, 0x78, 0x01, 0x4d, 0x9f, 0x1f, 0x6d,
+ 0x7b, 0xd8, 0xf5, 0x42, 0x38, 0x23, 0x1a, 0x99, 0x93, 0xf4, 0x83, 0xbe,
+ 0x3b, 0x35, 0x74, 0xe7, 0x37, 0x13, 0x35, 0x7a, 0xac, 0xb4, 0xb6, 0x90,
+ 0x82, 0x6c, 0x27, 0xa4, 0xe0, 0xec, 0x9e, 0x35, 0xbd, 0xbf, 0xe5, 0x29,
+ 0xa1, 0x47, 0x9f, 0x5b, 0x32, 0xfc, 0xe9, 0x99, 0x7d, 0x2b, 0x39,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120020005 (0x7275c25)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Feb 24 20:05:10 2010 GMT
+ Not After : Aug 13 23:59:00 2018 GMT
+ Subject: C=JP, O=Cybertrust Japan Co., Ltd., CN=Cybertrust Japan Public CA G1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:be:bf:b6:ed:e4:f1:4c:02:98:0b:5a:7c:c1:69:
+ 18:20:48:92:03:08:12:d2:52:39:cf:f4:cc:9f:6c:
+ ea:3a:ab:ff:46:db:f4:dd:e5:31:ed:01:89:90:ac:
+ 44:75:a6:31:5f:a3:41:89:36:97:ab:f4:43:7d:4c:
+ 18:15:5e:73:ca:aa:af:72:ff:4a:98:cc:95:24:5a:
+ 8d:9f:67:98:c6:ce:a1:e6:48:51:f9:8b:3c:b4:32:
+ 82:ec:15:a9:7b:35:a6:87:3e:84:2e:21:4b:6d:d1:
+ 7a:ed:8c:cb:a8:e1:88:0f:1c:75:77:79:45:2b:7b:
+ cb:6f:bb:08:94:75:fd:5a:c9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ 5A:84:4B:BB:97:58:E2:42:E0:8C:AA:9C:A9:BD:62:07:6C:E4:96:AB
+ Signature Algorithm: sha1WithRSAEncryption
+ 31:53:e8:7c:8b:09:f4:98:77:0b:07:05:e4:00:3e:f3:4a:af:
+ 3c:fe:ea:e0:99:aa:4e:f2:ce:c1:94:af:3d:c0:0b:68:a7:fd:
+ 6f:e8:2f:70:6d:22:59:b5:7e:f5:62:c5:36:af:e9:0b:4c:39:
+ 8f:80:4e:a4:37:49:69:78:35:32:d6:b5:f9:f9:48:bc:98:16:
+ fc:ff:3e:fa:df:f8:4b:3e:66:72:4f:02:1c:f8:d2:12:f4:bd:
+ 4c:ed:56:b0:a2:73:c8:4f:82:ce:0d:b4:c4:af:0e:43:70:6e:
+ 08:d4:ec:b0:c1:c4:7f:75:18:99:76:7d:68:d2:13:6c:37:52:
+ 34:56
+-----BEGIN CERTIFICATE-----
+MIIDuTCCAyKgAwIBAgIEBydcJTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEwMDIyNDIwMDUxMFoXDTE4MDgxMzIzNTkwMFowWjELMAkG
+A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMSYw
+JAYDVQQDEx1DeWJlcnRydXN0IEphcGFuIFB1YmxpYyBDQSBHMTCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAvr+27eTxTAKYC1p8wWkYIEiSAwgS0lI5z/TMn2zq
+Oqv/Rtv03eUx7QGJkKxEdaYxX6NBiTaXq/RDfUwYFV5zyqqvcv9KmMyVJFqNn2eY
+xs6h5khR+Ys8tDKC7BWpezWmhz6ELiFLbdF67YzLqOGIDxx1d3lFK3vLb7sIlHX9
+WskCAwEAAaOCAW8wggFrMBIGA1UdEwEB/wQIMAYBAf8CAQAwUwYDVR0gBEwwSjBI
+BgkrBgEEAbE+AQAwOzA5BggrBgEFBQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21u
+aXJvb3QuY29tL3JlcG9zaXRvcnkuY2ZtMA4GA1UdDwEB/wQEAwIBBjCBiQYDVR0j
+BIGBMH+heaR3MHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRp
+b24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG
+A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3SCAgGlMEUGA1UdHwQ+MDww
+OqA4oDaGNGh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8y
+MDE4L2NkcC5jcmwwHQYDVR0OBBYEFFqES7uXWOJC4IyqnKm9Ygds5JarMA0GCSqG
+SIb3DQEBBQUAA4GBADFT6HyLCfSYdwsHBeQAPvNKrzz+6uCZqk7yzsGUrz3AC2in
+/W/oL3BtIlm1fvVixTav6QtMOY+ATqQ3SWl4NTLWtfn5SLyYFvz/Pvrf+Es+ZnJP
+Ahz40hL0vUztVrCic8hPgs4NtMSvDkNwbgjU7LDBxH91GJl2fWjSE2w3UjRW
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert5[] = {
+ 0x30, 0x82, 0x03, 0xb9, 0x30, 0x82, 0x03, 0x22, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x5c, 0x25, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x30, 0x32, 0x32, 0x34, 0x32, 0x30, 0x30, 0x35, 0x31,
+ 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x33, 0x32, 0x33,
+ 0x35, 0x39, 0x30, 0x30, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1a, 0x43, 0x79, 0x62, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20,
+ 0x43, 0x6f, 0x2e, 0x2c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x26, 0x30,
+ 0x24, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1d, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x43, 0x41, 0x20, 0x47,
+ 0x31, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbe, 0xbf, 0xb6, 0xed, 0xe4, 0xf1,
+ 0x4c, 0x02, 0x98, 0x0b, 0x5a, 0x7c, 0xc1, 0x69, 0x18, 0x20, 0x48, 0x92,
+ 0x03, 0x08, 0x12, 0xd2, 0x52, 0x39, 0xcf, 0xf4, 0xcc, 0x9f, 0x6c, 0xea,
+ 0x3a, 0xab, 0xff, 0x46, 0xdb, 0xf4, 0xdd, 0xe5, 0x31, 0xed, 0x01, 0x89,
+ 0x90, 0xac, 0x44, 0x75, 0xa6, 0x31, 0x5f, 0xa3, 0x41, 0x89, 0x36, 0x97,
+ 0xab, 0xf4, 0x43, 0x7d, 0x4c, 0x18, 0x15, 0x5e, 0x73, 0xca, 0xaa, 0xaf,
+ 0x72, 0xff, 0x4a, 0x98, 0xcc, 0x95, 0x24, 0x5a, 0x8d, 0x9f, 0x67, 0x98,
+ 0xc6, 0xce, 0xa1, 0xe6, 0x48, 0x51, 0xf9, 0x8b, 0x3c, 0xb4, 0x32, 0x82,
+ 0xec, 0x15, 0xa9, 0x7b, 0x35, 0xa6, 0x87, 0x3e, 0x84, 0x2e, 0x21, 0x4b,
+ 0x6d, 0xd1, 0x7a, 0xed, 0x8c, 0xcb, 0xa8, 0xe1, 0x88, 0x0f, 0x1c, 0x75,
+ 0x77, 0x79, 0x45, 0x2b, 0x7b, 0xcb, 0x6f, 0xbb, 0x08, 0x94, 0x75, 0xfd,
+ 0x5a, 0xc9, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x6f, 0x30,
+ 0x82, 0x01, 0x6b, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30, 0x48,
+ 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00, 0x30,
+ 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79,
+ 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e,
+ 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47,
+ 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5,
+ 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30,
+ 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32,
+ 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x5a, 0x84,
+ 0x4b, 0xbb, 0x97, 0x58, 0xe2, 0x42, 0xe0, 0x8c, 0xaa, 0x9c, 0xa9, 0xbd,
+ 0x62, 0x07, 0x6c, 0xe4, 0x96, 0xab, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x31, 0x53, 0xe8, 0x7c, 0x8b, 0x09, 0xf4, 0x98, 0x77, 0x0b, 0x07,
+ 0x05, 0xe4, 0x00, 0x3e, 0xf3, 0x4a, 0xaf, 0x3c, 0xfe, 0xea, 0xe0, 0x99,
+ 0xaa, 0x4e, 0xf2, 0xce, 0xc1, 0x94, 0xaf, 0x3d, 0xc0, 0x0b, 0x68, 0xa7,
+ 0xfd, 0x6f, 0xe8, 0x2f, 0x70, 0x6d, 0x22, 0x59, 0xb5, 0x7e, 0xf5, 0x62,
+ 0xc5, 0x36, 0xaf, 0xe9, 0x0b, 0x4c, 0x39, 0x8f, 0x80, 0x4e, 0xa4, 0x37,
+ 0x49, 0x69, 0x78, 0x35, 0x32, 0xd6, 0xb5, 0xf9, 0xf9, 0x48, 0xbc, 0x98,
+ 0x16, 0xfc, 0xff, 0x3e, 0xfa, 0xdf, 0xf8, 0x4b, 0x3e, 0x66, 0x72, 0x4f,
+ 0x02, 0x1c, 0xf8, 0xd2, 0x12, 0xf4, 0xbd, 0x4c, 0xed, 0x56, 0xb0, 0xa2,
+ 0x73, 0xc8, 0x4f, 0x82, 0xce, 0x0d, 0xb4, 0xc4, 0xaf, 0x0e, 0x43, 0x70,
+ 0x6e, 0x08, 0xd4, 0xec, 0xb0, 0xc1, 0xc4, 0x7f, 0x75, 0x18, 0x99, 0x76,
+ 0x7d, 0x68, 0xd2, 0x13, 0x6c, 0x37, 0x52, 0x34, 0x56,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 67109891 (0x4000403)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: May 11 15:32:00 2006 GMT
+ Not After : May 11 23:59:00 2013 GMT
+ Subject: C=US, O=Akamai Technologies Inc, CN=Akamai Subordinate CA 3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:9d:34:76:73:b3:26:44:c4:60:cc:76:5f:8f:d8:
+ 2f:4b:3a:12:56:8c:6d:d5:b4:e2:ac:0c:e1:47:8a:
+ 85:43:12:bc:03:66:85:20:1d:6b:8a:74:72:38:85:
+ 61:a9:73:0b:57:5b:db:c5:9e:b3:66:c5:51:f8:0a:
+ 90:7c:f8:74:14:72:12:80:f4:e8:5a:cd:c8:bb:11:
+ 14:c9:44:2f:ec:e1:af:33:c1:59:29:dd:4c:85:7b:
+ 1c:80:dd:46:a5:64:cf:60:ef:4f:55:93:3e:05:a9:
+ 16:24:2b:48:ff:9f:05:92:de:0c:e7:9f:60:df:54:
+ 6f:a7:16:ee:ff:af:61:a9:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ BE:39:BF:41:66:FA:D4:CE:8B:6E:78:A3:49:7E:DE:3D:C4:2E:2B:F6
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://www.public-trust.com/CPS/OmniRoot.html
+
+ X509v3 Authority Key Identifier:
+ keyid:A6:0C:1D:9F:61:FF:07:17:B5:BF:38:46:DB:43:30:D5:8E:B0:52:06
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 Key Usage: critical
+ Digital Signature, Non Repudiation, Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Signature Algorithm: sha1WithRSAEncryption
+ 76:87:d3:ae:4d:3d:c4:6b:28:e1:52:1f:79:81:1e:e9:62:1a:
+ f7:4f:d9:1a:c0:e5:05:11:fa:77:f9:ff:b1:25:17:5e:ca:19:
+ c8:ac:cc:dc:71:95:ce:cf:66:02:60:c1:7e:ff:ec:d9:b6:70:
+ e1:03:60:33:43:0c:36:55:8d:30:97:5d:5d:97:09:6d:9d:78:
+ 33:a5:56:84:a6:28:b8:a1:19:9d:a0:2c:48:27:be:5c:7b:05:
+ d2:16:94:7c:e9:f1:a6:3e:29:ec:26:63:fc:39:c6:65:50:7c:
+ 52:1f:76:39:16:b4:97:26:39:ab:8e:1d:fd:b5:7a:c0:3a:1d:
+ 3b:7f
+-----BEGIN CERTIFICATE-----
+MIIDxzCCAzCgAwIBAgIEBAAEAzANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTA2MDUxMTE1MzIwMFoXDTEzMDUxMTIzNTkwMFowUTELMAkG
+A1UEBhMCVVMxIDAeBgNVBAoTF0FrYW1haSBUZWNobm9sb2dpZXMgSW5jMSAwHgYD
+VQQDExdBa2FtYWkgU3Vib3JkaW5hdGUgQ0EgMzCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAnTR2c7MmRMRgzHZfj9gvSzoSVoxt1bTirAzhR4qFQxK8A2aFIB1r
+inRyOIVhqXMLV1vbxZ6zZsVR+AqQfPh0FHISgPToWs3IuxEUyUQv7OGvM8FZKd1M
+hXscgN1GpWTPYO9PVZM+BakWJCtI/58Fkt4M559g31Rvpxbu/69hqZ0CAwEAAaOC
+AYYwggGCMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVibGljLXRydXN0
+LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwHQYDVR0OBBYEFL45v0Fm+tTO
+i254o0l+3j3ELiv2MFMGA1UdIARMMEowSAYJKwYBBAGxPgEAMDswOQYIKwYBBQUH
+AgEWLWh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9DUFMvT21uaVJvb3QuaHRt
+bDCBoAYDVR0jBIGYMIGVgBSmDB2fYf8HF7W/OEbbQzDVjrBSBqF5pHcwdTELMAkG
+A1UEBhMCVVMxGDAWBgNVBAoTD0dURSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RF
+IEN5YmVyVHJ1c3QgU29sdXRpb25zLCBJbmMuMSMwIQYDVQQDExpHVEUgQ3liZXJU
+cnVzdCBHbG9iYWwgUm9vdIICAaUwDgYDVR0PAQH/BAQDAgHGMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwDQYJKoZIhvcNAQEFBQADgYEAdofTrk09xGso4VIfeYEe6WIa90/Z
+GsDlBRH6d/n/sSUXXsoZyKzM3HGVzs9mAmDBfv/s2bZw4QNgM0MMNlWNMJddXZcJ
+bZ14M6VWhKYouKEZnaAsSCe+XHsF0haUfOnxpj4p7CZj/DnGZVB8Uh92ORa0lyY5
+q44d/bV6wDodO38=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert6[] = {
+ 0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x03, 0x30, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x04, 0x00, 0x04, 0x03, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x30, 0x36, 0x30, 0x35, 0x31, 0x31, 0x31, 0x35, 0x33, 0x32, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x35, 0x31, 0x31, 0x32, 0x33,
+ 0x35, 0x39, 0x30, 0x30, 0x5a, 0x30, 0x51, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x17, 0x41, 0x6b, 0x61, 0x6d, 0x61,
+ 0x69, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69,
+ 0x65, 0x73, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x17, 0x41, 0x6b, 0x61, 0x6d, 0x61, 0x69, 0x20,
+ 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x20,
+ 0x43, 0x41, 0x20, 0x33, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81,
+ 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0x9d, 0x34, 0x76,
+ 0x73, 0xb3, 0x26, 0x44, 0xc4, 0x60, 0xcc, 0x76, 0x5f, 0x8f, 0xd8, 0x2f,
+ 0x4b, 0x3a, 0x12, 0x56, 0x8c, 0x6d, 0xd5, 0xb4, 0xe2, 0xac, 0x0c, 0xe1,
+ 0x47, 0x8a, 0x85, 0x43, 0x12, 0xbc, 0x03, 0x66, 0x85, 0x20, 0x1d, 0x6b,
+ 0x8a, 0x74, 0x72, 0x38, 0x85, 0x61, 0xa9, 0x73, 0x0b, 0x57, 0x5b, 0xdb,
+ 0xc5, 0x9e, 0xb3, 0x66, 0xc5, 0x51, 0xf8, 0x0a, 0x90, 0x7c, 0xf8, 0x74,
+ 0x14, 0x72, 0x12, 0x80, 0xf4, 0xe8, 0x5a, 0xcd, 0xc8, 0xbb, 0x11, 0x14,
+ 0xc9, 0x44, 0x2f, 0xec, 0xe1, 0xaf, 0x33, 0xc1, 0x59, 0x29, 0xdd, 0x4c,
+ 0x85, 0x7b, 0x1c, 0x80, 0xdd, 0x46, 0xa5, 0x64, 0xcf, 0x60, 0xef, 0x4f,
+ 0x55, 0x93, 0x3e, 0x05, 0xa9, 0x16, 0x24, 0x2b, 0x48, 0xff, 0x9f, 0x05,
+ 0x92, 0xde, 0x0c, 0xe7, 0x9f, 0x60, 0xdf, 0x54, 0x6f, 0xa7, 0x16, 0xee,
+ 0xff, 0xaf, 0x61, 0xa9, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x86, 0x30, 0x82, 0x01, 0x82, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86,
+ 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e,
+ 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64,
+ 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xbe, 0x39, 0xbf, 0x41, 0x66, 0xfa, 0xd4, 0xce,
+ 0x8b, 0x6e, 0x78, 0xa3, 0x49, 0x7e, 0xde, 0x3d, 0xc4, 0x2e, 0x2b, 0xf6,
+ 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30,
+ 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00,
+ 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x2f,
+ 0x4f, 0x6d, 0x6e, 0x69, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x68, 0x74, 0x6d,
+ 0x6c, 0x30, 0x81, 0xa0, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x98,
+ 0x30, 0x81, 0x95, 0x80, 0x14, 0xa6, 0x0c, 0x1d, 0x9f, 0x61, 0xff, 0x07,
+ 0x17, 0xb5, 0xbf, 0x38, 0x46, 0xdb, 0x43, 0x30, 0xd5, 0x8e, 0xb0, 0x52,
+ 0x06, 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27,
+ 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45,
+ 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20,
+ 0x52, 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0xc6,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0x76, 0x87, 0xd3, 0xae, 0x4d, 0x3d, 0xc4, 0x6b, 0x28,
+ 0xe1, 0x52, 0x1f, 0x79, 0x81, 0x1e, 0xe9, 0x62, 0x1a, 0xf7, 0x4f, 0xd9,
+ 0x1a, 0xc0, 0xe5, 0x05, 0x11, 0xfa, 0x77, 0xf9, 0xff, 0xb1, 0x25, 0x17,
+ 0x5e, 0xca, 0x19, 0xc8, 0xac, 0xcc, 0xdc, 0x71, 0x95, 0xce, 0xcf, 0x66,
+ 0x02, 0x60, 0xc1, 0x7e, 0xff, 0xec, 0xd9, 0xb6, 0x70, 0xe1, 0x03, 0x60,
+ 0x33, 0x43, 0x0c, 0x36, 0x55, 0x8d, 0x30, 0x97, 0x5d, 0x5d, 0x97, 0x09,
+ 0x6d, 0x9d, 0x78, 0x33, 0xa5, 0x56, 0x84, 0xa6, 0x28, 0xb8, 0xa1, 0x19,
+ 0x9d, 0xa0, 0x2c, 0x48, 0x27, 0xbe, 0x5c, 0x7b, 0x05, 0xd2, 0x16, 0x94,
+ 0x7c, 0xe9, 0xf1, 0xa6, 0x3e, 0x29, 0xec, 0x26, 0x63, 0xfc, 0x39, 0xc6,
+ 0x65, 0x50, 0x7c, 0x52, 0x1f, 0x76, 0x39, 0x16, 0xb4, 0x97, 0x26, 0x39,
+ 0xab, 0x8e, 0x1d, 0xfd, 0xb5, 0x7a, 0xc0, 0x3a, 0x1d, 0x3b, 0x7f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 145105 (0x236d1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Feb 19 22:45:05 2010 GMT
+ Not After : Feb 18 22:45:05 2020 GMT
+ Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3:
+ fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24:
+ 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6:
+ 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd:
+ 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8:
+ e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37:
+ e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47:
+ 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a:
+ e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c:
+ 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79:
+ f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc:
+ b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad:
+ f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67:
+ 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b:
+ c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b:
+ 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18:
+ 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1:
+ 50:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.geotrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98:
+ f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2:
+ 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5:
+ 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9:
+ 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63:
+ a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad:
+ 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0:
+ c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74:
+ 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51:
+ 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c:
+ fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3:
+ ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d:
+ 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6:
+ 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54:
+ 6c:7a:b7:b7
+-----BEGIN CERTIFICATE-----
+MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG
+EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM
+IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0
+l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e
+6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb
+ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8
+N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5
+HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd
+gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC
+St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w
+EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js
+Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw
+JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B
+AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x
+/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O
+SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61
+04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4
+knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK
+LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert7[] = {
+ 0x30, 0x82, 0x03, 0xd5, 0x30, 0x82, 0x02, 0xbd, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x36, 0xd1, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30,
+ 0x32, 0x31, 0x39, 0x32, 0x32, 0x34, 0x35, 0x30, 0x35, 0x5a, 0x17, 0x0d,
+ 0x32, 0x30, 0x30, 0x32, 0x31, 0x38, 0x32, 0x32, 0x34, 0x35, 0x30, 0x35,
+ 0x5a, 0x30, 0x3c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0e, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0b, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xc7, 0x71, 0xf8, 0x56, 0xc7, 0x1e, 0xd9, 0xcc, 0xb5, 0xad, 0xf6, 0xb4,
+ 0x97, 0xa3, 0xfb, 0xa1, 0xe6, 0x0b, 0x50, 0x5f, 0x50, 0xaa, 0x3a, 0xda,
+ 0x0f, 0xfc, 0x3d, 0x29, 0x24, 0x43, 0xc6, 0x10, 0x29, 0xc1, 0xfc, 0x55,
+ 0x40, 0x72, 0xee, 0xbd, 0xea, 0xdf, 0x9f, 0xb6, 0x41, 0xf4, 0x48, 0x4b,
+ 0xc8, 0x6e, 0xfe, 0x4f, 0x57, 0x12, 0x8b, 0x5b, 0xfa, 0x92, 0xdd, 0x5e,
+ 0xe8, 0xad, 0xf3, 0xf0, 0x1b, 0xb1, 0x7b, 0x4d, 0xfb, 0xcf, 0xfd, 0xd1,
+ 0xe5, 0xf8, 0xe3, 0xdc, 0xe7, 0xf5, 0x73, 0x7f, 0xdf, 0x01, 0x49, 0xcf,
+ 0x8c, 0x56, 0xc1, 0xbd, 0x37, 0xe3, 0x5b, 0xbe, 0xb5, 0x4f, 0x8b, 0x8b,
+ 0xf0, 0xda, 0x4f, 0xc7, 0xe3, 0xdd, 0x55, 0x47, 0x69, 0xdf, 0xf2, 0x5b,
+ 0x7b, 0x07, 0x4f, 0x3d, 0xe5, 0xac, 0x21, 0xc1, 0xc8, 0x1d, 0x7a, 0xe8,
+ 0xe7, 0xf6, 0x0f, 0xa1, 0xaa, 0xf5, 0x6f, 0xde, 0xa8, 0x65, 0x4f, 0x10,
+ 0x89, 0x9c, 0x03, 0xf3, 0x89, 0x7a, 0xa5, 0x5e, 0x01, 0x72, 0x33, 0xed,
+ 0xa9, 0xe9, 0x5a, 0x1e, 0x79, 0xf3, 0x87, 0xc8, 0xdf, 0xc8, 0xc5, 0xfc,
+ 0x37, 0xc8, 0x9a, 0x9a, 0xd7, 0xb8, 0x76, 0xcc, 0xb0, 0x3e, 0xe7, 0xfd,
+ 0xe6, 0x54, 0xea, 0xdf, 0x5f, 0x52, 0x41, 0x78, 0x59, 0x57, 0xad, 0xf1,
+ 0x12, 0xd6, 0x7f, 0xbc, 0xd5, 0x9f, 0x70, 0xd3, 0x05, 0x6c, 0xfa, 0xa3,
+ 0x7d, 0x67, 0x58, 0xdd, 0x26, 0x62, 0x1d, 0x31, 0x92, 0x0c, 0x79, 0x79,
+ 0x1c, 0x8e, 0xcf, 0xca, 0x7b, 0xc1, 0x66, 0xaf, 0xa8, 0x74, 0x48, 0xfb,
+ 0x8e, 0x82, 0xc2, 0x9e, 0x2c, 0x99, 0x5c, 0x7b, 0x2d, 0x5d, 0x9b, 0xbc,
+ 0x5b, 0x57, 0x9e, 0x7c, 0x3a, 0x7a, 0x13, 0xad, 0xf2, 0xa3, 0x18, 0x5b,
+ 0x2b, 0x59, 0x0f, 0xcd, 0x5c, 0x3a, 0xeb, 0x68, 0x33, 0xc6, 0x28, 0x1d,
+ 0x82, 0xd1, 0x50, 0x8b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xd9,
+ 0x30, 0x81, 0xd6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6b, 0x69, 0x3d, 0x6a, 0x18, 0x42,
+ 0x4a, 0xdd, 0x8f, 0x02, 0x65, 0x39, 0xfd, 0x35, 0x24, 0x86, 0x78, 0x91,
+ 0x16, 0x30, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab, 0x05,
+ 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3a, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b,
+ 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30,
+ 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xab, 0xbc, 0xbc,
+ 0x0a, 0x5d, 0x18, 0x94, 0xe3, 0xc1, 0xb1, 0xc3, 0xa8, 0x4c, 0x55, 0xd6,
+ 0xbe, 0xb4, 0x98, 0xf1, 0xee, 0x3c, 0x1c, 0xcd, 0xcf, 0xf3, 0x24, 0x24,
+ 0x5c, 0x96, 0x03, 0x27, 0x58, 0xfc, 0x36, 0xae, 0xa2, 0x2f, 0x8f, 0xf1,
+ 0xfe, 0xda, 0x2b, 0x02, 0xc3, 0x33, 0xbd, 0xc8, 0xdd, 0x48, 0x22, 0x2b,
+ 0x60, 0x0f, 0xa5, 0x03, 0x10, 0xfd, 0x77, 0xf8, 0xd0, 0xed, 0x96, 0x67,
+ 0x4f, 0xfd, 0xea, 0x47, 0x20, 0x70, 0x54, 0xdc, 0xa9, 0x0c, 0x55, 0x7e,
+ 0xe1, 0x96, 0x25, 0x8a, 0xd9, 0xb5, 0xda, 0x57, 0x4a, 0xbe, 0x8d, 0x8e,
+ 0x49, 0x43, 0x63, 0xa5, 0x6c, 0x4e, 0x27, 0x87, 0x25, 0xeb, 0x5b, 0x6d,
+ 0xfe, 0xa2, 0x7f, 0x38, 0x28, 0xe0, 0x36, 0xab, 0xad, 0x39, 0xa5, 0xa5,
+ 0x62, 0xc4, 0xb7, 0x5c, 0x58, 0x2c, 0xaa, 0x5d, 0x01, 0x60, 0xa6, 0x62,
+ 0x67, 0xa3, 0xc0, 0xc7, 0x62, 0x23, 0xf4, 0xe7, 0x6c, 0x46, 0xee, 0xb5,
+ 0xd3, 0x80, 0x6a, 0x22, 0x13, 0xd2, 0x2d, 0x3f, 0x74, 0x4f, 0xea, 0xaf,
+ 0x8c, 0x5f, 0xb4, 0x38, 0x9c, 0xdb, 0xae, 0xce, 0xaf, 0x84, 0x1e, 0xa6,
+ 0xf6, 0x34, 0x51, 0x59, 0x79, 0xd3, 0xe3, 0x75, 0xdc, 0xbc, 0xd7, 0xf3,
+ 0x73, 0xdf, 0x92, 0xec, 0xd2, 0x20, 0x59, 0x6f, 0x9c, 0xfb, 0x95, 0xf8,
+ 0x92, 0x76, 0x18, 0x0a, 0x7c, 0x0f, 0x2c, 0xa6, 0xca, 0xde, 0x8a, 0x62,
+ 0x7b, 0xd8, 0xf3, 0xce, 0x5f, 0x68, 0xbd, 0x8f, 0x3e, 0xc1, 0x74, 0xbb,
+ 0x15, 0x72, 0x3a, 0x16, 0x83, 0xa9, 0x0b, 0xe6, 0x4d, 0x99, 0x9c, 0xd8,
+ 0x57, 0xec, 0xa8, 0x01, 0x51, 0xc7, 0x6f, 0x57, 0x34, 0x5e, 0xab, 0x4a,
+ 0x2c, 0x42, 0xf6, 0x4f, 0x1c, 0x89, 0x78, 0xde, 0x26, 0x4e, 0xf5, 0x6f,
+ 0x93, 0x4c, 0x15, 0x6b, 0x27, 0x56, 0x4d, 0x00, 0x54, 0x6c, 0x7a, 0xb7,
+ 0xb7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 145104 (0x236d0)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Feb 19 22:39:26 2010 GMT
+ Not After : Feb 18 22:39:26 2020 GMT
+ Subject: C=US, O=GeoTrust, Inc., CN=GeoTrust SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:90:b3:80:c1:e4:e5:46:ad:70:60:3d:ba:e5:14:
+ dd:9e:8a:5e:8b:75:5a:e6:ca:6d:41:a5:23:e8:39:
+ 85:26:7a:a7:55:77:9a:48:a1:92:7e:3a:1e:1a:f1:
+ 27:ab:a3:4c:39:cc:cb:3d:47:af:81:ae:16:6a:5c:
+ 37:ef:45:41:fd:fb:9a:97:3c:a0:43:9d:c6:df:17:
+ 21:d1:8a:a2:56:c2:03:49:84:12:81:3e:c9:0a:54:
+ 60:66:b9:8c:54:e4:f9:e6:f9:94:f1:e0:5f:75:11:
+ f2:29:b9:e4:86:a2:b1:89:ad:a6:1e:83:29:63:b2:
+ f0:54:1c:85:0b:7a:e7:e1:2e:0d:af:a4:bd:cd:e7:
+ b1:5a:d7:8c:05:5a:0e:4b:73:28:8b:75:5d:34:d8:
+ 77:0b:e1:74:62:e2:71:30:62:d8:bc:8a:05:e5:31:
+ 63:4a:54:89:6a:33:78:a7:4e:55:24:1d:97:ef:1a:
+ e4:12:c6:0f:30:18:b4:34:4d:e1:d8:23:3b:21:5b:
+ 2d:30:19:25:0e:74:f7:a4:21:4b:a0:a4:20:c9:6c:
+ cd:98:56:c0:f2:a8:5f:3e:26:75:a0:0d:f8:36:88:
+ 8a:2c:5a:7d:67:30:a9:0f:d1:99:70:2e:78:e1:51:
+ 26:af:55:7a:24:be:8c:39:0d:77:9d:de:02:c3:0c:
+ bd:1f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 42:79:54:1B:61:CD:55:2B:3E:63:D5:3C:48:57:F5:9F:FB:45:CE:4A
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.geotrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ d4:ef:53:84:e8:1a:bd:a1:8b:04:c0:a9:f5:5f:a1:10:78:45:
+ 5d:b2:57:6a:4e:24:cb:65:4e:31:97:91:9a:d4:24:f8:e2:27:
+ 66:70:31:9c:c1:62:54:06:e7:97:1d:3a:9a:c0:a4:29:48:0a:
+ af:24:c7:a8:c4:9a:54:c1:7c:4c:78:4c:2b:68:2c:5d:17:a6:
+ 54:78:4c:46:e2:80:c3:1f:38:71:12:d2:d7:53:e3:54:85:50:
+ b8:02:cb:ee:63:3a:f8:56:89:4d:55:bb:2e:c0:c8:18:77:86:
+ 31:0b:0b:70:f0:7e:35:83:a4:2a:13:64:56:67:34:5d:16:5f:
+ 73:ac:7b:06:24:da:4f:50:6d:2a:ab:d0:4d:53:41:c2:8e:bb:
+ 71:03:49:29:86:18:cf:21:42:4c:74:62:51:15:c5:6f:a8:ef:
+ c4:27:e5:1b:33:dd:5a:88:d7:7f:12:d1:a7:61:25:1f:d5:e0:
+ dc:1d:cf:1a:10:d8:a0:cb:5f:8c:fa:0c:e5:bf:71:ff:e5:5d:
+ 44:1d:a6:3e:87:47:fa:1a:4e:83:83:12:3f:88:66:95:98:79:
+ 9a:85:eb:02:47:cd:25:e3:f2:06:04:4e:99:ca:5c:a0:6e:7a:
+ bb:dd:a3:90:1a:45:33:ef:bf:3e:d2:04:c4:b6:e0:2a:85:65:
+ 41:3e:10:d4
+-----BEGIN CERTIFICATE-----
+MIID2TCCAsGgAwIBAgIDAjbQMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTAwMjE5MjIzOTI2WhcNMjAwMjE4MjIzOTI2WjBAMQswCQYDVQQG
+EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xGDAWBgNVBAMTD0dlb1RydXN0
+IFNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJCzgMHk5Uat
+cGA9uuUU3Z6KXot1WubKbUGlI+g5hSZ6p1V3mkihkn46HhrxJ6ujTDnMyz1Hr4Gu
+FmpcN+9FQf37mpc8oEOdxt8XIdGKolbCA0mEEoE+yQpUYGa5jFTk+eb5lPHgX3UR
+8im55IaisYmtph6DKWOy8FQchQt65+EuDa+kvc3nsVrXjAVaDktzKIt1XTTYdwvh
+dGLicTBi2LyKBeUxY0pUiWozeKdOVSQdl+8a5BLGDzAYtDRN4dgjOyFbLTAZJQ50
+96QhS6CkIMlszZhWwPKoXz4mdaAN+DaIiixafWcwqQ/RmXAueOFRJq9VeiS+jDkN
+d53eAsMMvR8CAwEAAaOB2TCB1jAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFEJ5
+VBthzVUrPmPVPEhX9Z/7Rc5KMB8GA1UdIwQYMBaAFMB6mGiNifurBWQMEX2qfWW4
+ysxOMBIGA1UdEwEB/wQIMAYBAf8CAQAwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDov
+L2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYBBQUHAQEE
+KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20wDQYJKoZI
+hvcNAQEFBQADggEBANTvU4ToGr2hiwTAqfVfoRB4RV2yV2pOJMtlTjGXkZrUJPji
+J2ZwMZzBYlQG55cdOprApClICq8kx6jEmlTBfEx4TCtoLF0XplR4TEbigMMfOHES
+0tdT41SFULgCy+5jOvhWiU1Vuy7AyBh3hjELC3DwfjWDpCoTZFZnNF0WX3OsewYk
+2k9QbSqr0E1TQcKOu3EDSSmGGM8hQkx0YlEVxW+o78Qn5Rsz3VqI138S0adhJR/V
+4NwdzxoQ2KDLX4z6DOW/cf/lXUQdpj6HR/oaToODEj+IZpWYeZqF6wJHzSXj8gYE
+TpnKXKBuervdo5AaRTPvvz7SBMS24CqFZUE+ENQ=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert8[] = {
+ 0x30, 0x82, 0x03, 0xd9, 0x30, 0x82, 0x02, 0xc1, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x36, 0xd0, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30,
+ 0x32, 0x31, 0x39, 0x32, 0x32, 0x33, 0x39, 0x32, 0x36, 0x5a, 0x17, 0x0d,
+ 0x32, 0x30, 0x30, 0x32, 0x31, 0x38, 0x32, 0x32, 0x33, 0x39, 0x32, 0x36,
+ 0x5a, 0x30, 0x40, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0e, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0f, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0x90, 0xb3, 0x80, 0xc1, 0xe4, 0xe5, 0x46, 0xad,
+ 0x70, 0x60, 0x3d, 0xba, 0xe5, 0x14, 0xdd, 0x9e, 0x8a, 0x5e, 0x8b, 0x75,
+ 0x5a, 0xe6, 0xca, 0x6d, 0x41, 0xa5, 0x23, 0xe8, 0x39, 0x85, 0x26, 0x7a,
+ 0xa7, 0x55, 0x77, 0x9a, 0x48, 0xa1, 0x92, 0x7e, 0x3a, 0x1e, 0x1a, 0xf1,
+ 0x27, 0xab, 0xa3, 0x4c, 0x39, 0xcc, 0xcb, 0x3d, 0x47, 0xaf, 0x81, 0xae,
+ 0x16, 0x6a, 0x5c, 0x37, 0xef, 0x45, 0x41, 0xfd, 0xfb, 0x9a, 0x97, 0x3c,
+ 0xa0, 0x43, 0x9d, 0xc6, 0xdf, 0x17, 0x21, 0xd1, 0x8a, 0xa2, 0x56, 0xc2,
+ 0x03, 0x49, 0x84, 0x12, 0x81, 0x3e, 0xc9, 0x0a, 0x54, 0x60, 0x66, 0xb9,
+ 0x8c, 0x54, 0xe4, 0xf9, 0xe6, 0xf9, 0x94, 0xf1, 0xe0, 0x5f, 0x75, 0x11,
+ 0xf2, 0x29, 0xb9, 0xe4, 0x86, 0xa2, 0xb1, 0x89, 0xad, 0xa6, 0x1e, 0x83,
+ 0x29, 0x63, 0xb2, 0xf0, 0x54, 0x1c, 0x85, 0x0b, 0x7a, 0xe7, 0xe1, 0x2e,
+ 0x0d, 0xaf, 0xa4, 0xbd, 0xcd, 0xe7, 0xb1, 0x5a, 0xd7, 0x8c, 0x05, 0x5a,
+ 0x0e, 0x4b, 0x73, 0x28, 0x8b, 0x75, 0x5d, 0x34, 0xd8, 0x77, 0x0b, 0xe1,
+ 0x74, 0x62, 0xe2, 0x71, 0x30, 0x62, 0xd8, 0xbc, 0x8a, 0x05, 0xe5, 0x31,
+ 0x63, 0x4a, 0x54, 0x89, 0x6a, 0x33, 0x78, 0xa7, 0x4e, 0x55, 0x24, 0x1d,
+ 0x97, 0xef, 0x1a, 0xe4, 0x12, 0xc6, 0x0f, 0x30, 0x18, 0xb4, 0x34, 0x4d,
+ 0xe1, 0xd8, 0x23, 0x3b, 0x21, 0x5b, 0x2d, 0x30, 0x19, 0x25, 0x0e, 0x74,
+ 0xf7, 0xa4, 0x21, 0x4b, 0xa0, 0xa4, 0x20, 0xc9, 0x6c, 0xcd, 0x98, 0x56,
+ 0xc0, 0xf2, 0xa8, 0x5f, 0x3e, 0x26, 0x75, 0xa0, 0x0d, 0xf8, 0x36, 0x88,
+ 0x8a, 0x2c, 0x5a, 0x7d, 0x67, 0x30, 0xa9, 0x0f, 0xd1, 0x99, 0x70, 0x2e,
+ 0x78, 0xe1, 0x51, 0x26, 0xaf, 0x55, 0x7a, 0x24, 0xbe, 0x8c, 0x39, 0x0d,
+ 0x77, 0x9d, 0xde, 0x02, 0xc3, 0x0c, 0xbd, 0x1f, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x81, 0xd9, 0x30, 0x81, 0xd6, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x42, 0x79,
+ 0x54, 0x1b, 0x61, 0xcd, 0x55, 0x2b, 0x3e, 0x63, 0xd5, 0x3c, 0x48, 0x57,
+ 0xf5, 0x9f, 0xfb, 0x45, 0xce, 0x4a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d,
+ 0x89, 0xfb, 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8,
+ 0xca, 0xcc, 0x4e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f,
+ 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67,
+ 0x74, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0xd4, 0xef, 0x53, 0x84, 0xe8, 0x1a, 0xbd, 0xa1, 0x8b, 0x04, 0xc0,
+ 0xa9, 0xf5, 0x5f, 0xa1, 0x10, 0x78, 0x45, 0x5d, 0xb2, 0x57, 0x6a, 0x4e,
+ 0x24, 0xcb, 0x65, 0x4e, 0x31, 0x97, 0x91, 0x9a, 0xd4, 0x24, 0xf8, 0xe2,
+ 0x27, 0x66, 0x70, 0x31, 0x9c, 0xc1, 0x62, 0x54, 0x06, 0xe7, 0x97, 0x1d,
+ 0x3a, 0x9a, 0xc0, 0xa4, 0x29, 0x48, 0x0a, 0xaf, 0x24, 0xc7, 0xa8, 0xc4,
+ 0x9a, 0x54, 0xc1, 0x7c, 0x4c, 0x78, 0x4c, 0x2b, 0x68, 0x2c, 0x5d, 0x17,
+ 0xa6, 0x54, 0x78, 0x4c, 0x46, 0xe2, 0x80, 0xc3, 0x1f, 0x38, 0x71, 0x12,
+ 0xd2, 0xd7, 0x53, 0xe3, 0x54, 0x85, 0x50, 0xb8, 0x02, 0xcb, 0xee, 0x63,
+ 0x3a, 0xf8, 0x56, 0x89, 0x4d, 0x55, 0xbb, 0x2e, 0xc0, 0xc8, 0x18, 0x77,
+ 0x86, 0x31, 0x0b, 0x0b, 0x70, 0xf0, 0x7e, 0x35, 0x83, 0xa4, 0x2a, 0x13,
+ 0x64, 0x56, 0x67, 0x34, 0x5d, 0x16, 0x5f, 0x73, 0xac, 0x7b, 0x06, 0x24,
+ 0xda, 0x4f, 0x50, 0x6d, 0x2a, 0xab, 0xd0, 0x4d, 0x53, 0x41, 0xc2, 0x8e,
+ 0xbb, 0x71, 0x03, 0x49, 0x29, 0x86, 0x18, 0xcf, 0x21, 0x42, 0x4c, 0x74,
+ 0x62, 0x51, 0x15, 0xc5, 0x6f, 0xa8, 0xef, 0xc4, 0x27, 0xe5, 0x1b, 0x33,
+ 0xdd, 0x5a, 0x88, 0xd7, 0x7f, 0x12, 0xd1, 0xa7, 0x61, 0x25, 0x1f, 0xd5,
+ 0xe0, 0xdc, 0x1d, 0xcf, 0x1a, 0x10, 0xd8, 0xa0, 0xcb, 0x5f, 0x8c, 0xfa,
+ 0x0c, 0xe5, 0xbf, 0x71, 0xff, 0xe5, 0x5d, 0x44, 0x1d, 0xa6, 0x3e, 0x87,
+ 0x47, 0xfa, 0x1a, 0x4e, 0x83, 0x83, 0x12, 0x3f, 0x88, 0x66, 0x95, 0x98,
+ 0x79, 0x9a, 0x85, 0xeb, 0x02, 0x47, 0xcd, 0x25, 0xe3, 0xf2, 0x06, 0x04,
+ 0x4e, 0x99, 0xca, 0x5c, 0xa0, 0x6e, 0x7a, 0xbb, 0xdd, 0xa3, 0x90, 0x1a,
+ 0x45, 0x33, 0xef, 0xbf, 0x3e, 0xd2, 0x04, 0xc4, 0xb6, 0xe0, 0x2a, 0x85,
+ 0x65, 0x41, 0x3e, 0x10, 0xd4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 145106 (0x236d2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Feb 26 21:32:31 2010 GMT
+ Not After : Feb 25 21:32:31 2020 GMT
+ Subject: C=US, O=GeoTrust Inc., OU=Domain Validated SSL, CN=GeoTrust DV SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a6:bb:8e:7a:cd:a4:9c:62:57:d4:51:30:42:7b:
+ 8b:1a:b2:d2:88:06:ad:3b:3c:29:13:0c:31:bc:69:
+ f9:9f:5a:94:da:06:ba:ac:24:04:9e:ce:d4:aa:c4:
+ 48:60:00:f8:34:ae:a1:93:af:de:04:7e:cd:f8:5c:
+ 22:52:0d:56:53:eb:a9:94:cf:fb:74:44:eb:43:94:
+ a4:97:7a:40:57:35:b6:a4:62:da:d5:48:f8:7a:f1:
+ ec:90:b5:5f:39:fe:63:72:70:c8:12:85:d0:a5:2e:
+ 86:13:40:6c:eb:6c:4d:d2:54:fd:5f:3e:26:1f:66:
+ 71:a8:c0:b8:85:9e:f5:f5:75:8f:da:91:4e:89:e3:
+ ca:78:74:30:5f:15:0a:99:a7:ca:83:3a:76:35:48:
+ d0:dc:8b:1a:22:4e:85:a4:4e:fa:49:6d:2b:70:be:
+ 8e:0c:21:c3:62:cc:a4:d1:ad:16:6b:9a:7b:cb:64:
+ ff:8d:ba:42:c3:26:aa:15:78:68:9c:ec:f6:6b:c8:
+ 0c:57:0d:e5:38:07:d3:6a:57:03:9d:20:0e:4b:c4:
+ 7b:81:b0:2a:1c:f5:4a:ea:4a:98:49:fe:02:5b:3d:
+ 03:14:90:28:7e:9a:f4:78:d0:31:84:57:e5:4c:38:
+ 7a:42:11:e2:f5:28:51:03:4b:20:15:bb:22:1a:b6:
+ f0:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 8C:F4:D9:93:0A:47:BC:00:A0:4A:CE:4B:75:6E:A0:B6:B0:B2:7E:FC
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.geotrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 33:91:37:11:db:40:f9:de:8c:b2:02:88:77:af:63:21:c1:ad:
+ b0:0d:fa:a0:78:56:a3:82:fd:bb:49:5f:14:6d:c8:dc:5f:94:
+ da:11:66:7c:1e:91:c5:b6:d8:6d:4f:aa:f2:bf:21:28:7e:52:
+ a2:92:78:08:61:69:21:fe:2d:ec:82:18:84:f4:d3:8d:c5:8a:
+ bb:8a:cc:5d:e6:a3:b6:cc:6e:ad:6f:b3:0e:61:ee:89:ce:13:
+ 34:4f:49:55:f5:39:bb:99:96:f0:f5:ea:5a:3c:9c:16:bd:02:
+ 53:f0:2a:0e:41:6e:eb:ef:9e:f7:70:36:cd:80:2a:76:c8:87:
+ e3:eb:23:b3:96:2c:e6:1d:94:5f:1c:a4:e2:cd:24:31:2b:06:
+ 38:32:61:61:39:5c:89:4c:48:1d:42:c9:67:9e:d2:bf:58:f7:
+ f9:37:31:b0:67:dd:8d:26:36:1a:78:1a:09:19:3c:93:07:70:
+ 2a:e1:7c:29:f5:de:66:57:0b:12:5e:16:ed:5e:bd:37:b3:30:
+ 69:c6:92:a5:f6:19:d8:1d:f8:36:12:b9:4b:95:95:9c:d0:ce:
+ 6c:30:a7:16:fb:f6:4d:64:b6:5f:2a:14:9c:a6:c8:55:8e:20:
+ f9:65:07:24:cc:38:05:4c:20:88:b4:b5:67:94:cf:5d:8e:62:
+ 37:fe:c4:b4
+-----BEGIN CERTIFICATE-----
+MIID+jCCAuKgAwIBAgIDAjbSMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTAwMjI2MjEzMjMxWhcNMjAwMjI1MjEzMjMxWjBhMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UECxMURG9tYWluIFZh
+bGlkYXRlZCBTU0wxGzAZBgNVBAMTEkdlb1RydXN0IERWIFNTTCBDQTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKa7jnrNpJxiV9RRMEJ7ixqy0ogGrTs8
+KRMMMbxp+Z9alNoGuqwkBJ7O1KrESGAA+DSuoZOv3gR+zfhcIlINVlPrqZTP+3RE
+60OUpJd6QFc1tqRi2tVI+Hrx7JC1Xzn+Y3JwyBKF0KUuhhNAbOtsTdJU/V8+Jh9m
+cajAuIWe9fV1j9qRTonjynh0MF8VCpmnyoM6djVI0NyLGiJOhaRO+kltK3C+jgwh
+w2LMpNGtFmuae8tk/426QsMmqhV4aJzs9mvIDFcN5TgH02pXA50gDkvEe4GwKhz1
+SupKmEn+Als9AxSQKH6a9HjQMYRX5Uw4ekIR4vUoUQNLIBW7Ihq28BUCAwEAAaOB
+2TCB1jAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFIz02ZMKR7wAoErOS3VuoLaw
+sn78MB8GA1UdIwQYMBaAFMB6mGiNifurBWQMEX2qfWW4ysxOMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5j
+b20vY3Jscy9ndGdsb2JhbC5jcmwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAB
+hhhodHRwOi8vb2NzcC5nZW90cnVzdC5jb20wDQYJKoZIhvcNAQEFBQADggEBADOR
+NxHbQPnejLICiHevYyHBrbAN+qB4VqOC/btJXxRtyNxflNoRZnwekcW22G1PqvK/
+ISh+UqKSeAhhaSH+LeyCGIT0043FiruKzF3mo7bMbq1vsw5h7onOEzRPSVX1ObuZ
+lvD16lo8nBa9AlPwKg5BbuvvnvdwNs2AKnbIh+PrI7OWLOYdlF8cpOLNJDErBjgy
+YWE5XIlMSB1CyWee0r9Y9/k3MbBn3Y0mNhp4GgkZPJMHcCrhfCn13mZXCxJeFu1e
+vTezMGnGkqX2Gdgd+DYSuUuVlZzQzmwwpxb79k1ktl8qFJymyFWOIPllByTMOAVM
+IIi0tWeUz12OYjf+xLQ=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert9[] = {
+ 0x30, 0x82, 0x03, 0xfa, 0x30, 0x82, 0x02, 0xe2, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x36, 0xd2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30,
+ 0x32, 0x32, 0x36, 0x32, 0x31, 0x33, 0x32, 0x33, 0x31, 0x5a, 0x17, 0x0d,
+ 0x32, 0x30, 0x30, 0x32, 0x32, 0x35, 0x32, 0x31, 0x33, 0x32, 0x33, 0x31,
+ 0x5a, 0x30, 0x61, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x14, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61,
+ 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31,
+ 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47, 0x65,
+ 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x44, 0x56, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xa6, 0xbb, 0x8e, 0x7a, 0xcd, 0xa4, 0x9c, 0x62, 0x57, 0xd4, 0x51,
+ 0x30, 0x42, 0x7b, 0x8b, 0x1a, 0xb2, 0xd2, 0x88, 0x06, 0xad, 0x3b, 0x3c,
+ 0x29, 0x13, 0x0c, 0x31, 0xbc, 0x69, 0xf9, 0x9f, 0x5a, 0x94, 0xda, 0x06,
+ 0xba, 0xac, 0x24, 0x04, 0x9e, 0xce, 0xd4, 0xaa, 0xc4, 0x48, 0x60, 0x00,
+ 0xf8, 0x34, 0xae, 0xa1, 0x93, 0xaf, 0xde, 0x04, 0x7e, 0xcd, 0xf8, 0x5c,
+ 0x22, 0x52, 0x0d, 0x56, 0x53, 0xeb, 0xa9, 0x94, 0xcf, 0xfb, 0x74, 0x44,
+ 0xeb, 0x43, 0x94, 0xa4, 0x97, 0x7a, 0x40, 0x57, 0x35, 0xb6, 0xa4, 0x62,
+ 0xda, 0xd5, 0x48, 0xf8, 0x7a, 0xf1, 0xec, 0x90, 0xb5, 0x5f, 0x39, 0xfe,
+ 0x63, 0x72, 0x70, 0xc8, 0x12, 0x85, 0xd0, 0xa5, 0x2e, 0x86, 0x13, 0x40,
+ 0x6c, 0xeb, 0x6c, 0x4d, 0xd2, 0x54, 0xfd, 0x5f, 0x3e, 0x26, 0x1f, 0x66,
+ 0x71, 0xa8, 0xc0, 0xb8, 0x85, 0x9e, 0xf5, 0xf5, 0x75, 0x8f, 0xda, 0x91,
+ 0x4e, 0x89, 0xe3, 0xca, 0x78, 0x74, 0x30, 0x5f, 0x15, 0x0a, 0x99, 0xa7,
+ 0xca, 0x83, 0x3a, 0x76, 0x35, 0x48, 0xd0, 0xdc, 0x8b, 0x1a, 0x22, 0x4e,
+ 0x85, 0xa4, 0x4e, 0xfa, 0x49, 0x6d, 0x2b, 0x70, 0xbe, 0x8e, 0x0c, 0x21,
+ 0xc3, 0x62, 0xcc, 0xa4, 0xd1, 0xad, 0x16, 0x6b, 0x9a, 0x7b, 0xcb, 0x64,
+ 0xff, 0x8d, 0xba, 0x42, 0xc3, 0x26, 0xaa, 0x15, 0x78, 0x68, 0x9c, 0xec,
+ 0xf6, 0x6b, 0xc8, 0x0c, 0x57, 0x0d, 0xe5, 0x38, 0x07, 0xd3, 0x6a, 0x57,
+ 0x03, 0x9d, 0x20, 0x0e, 0x4b, 0xc4, 0x7b, 0x81, 0xb0, 0x2a, 0x1c, 0xf5,
+ 0x4a, 0xea, 0x4a, 0x98, 0x49, 0xfe, 0x02, 0x5b, 0x3d, 0x03, 0x14, 0x90,
+ 0x28, 0x7e, 0x9a, 0xf4, 0x78, 0xd0, 0x31, 0x84, 0x57, 0xe5, 0x4c, 0x38,
+ 0x7a, 0x42, 0x11, 0xe2, 0xf5, 0x28, 0x51, 0x03, 0x4b, 0x20, 0x15, 0xbb,
+ 0x22, 0x1a, 0xb6, 0xf0, 0x15, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xd9, 0x30, 0x81, 0xd6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x8c, 0xf4, 0xd9, 0x93, 0x0a,
+ 0x47, 0xbc, 0x00, 0xa0, 0x4a, 0xce, 0x4b, 0x75, 0x6e, 0xa0, 0xb6, 0xb0,
+ 0xb2, 0x7e, 0xfc, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb, 0xab,
+ 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc, 0x4e,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0,
+ 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26,
+ 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x33, 0x91,
+ 0x37, 0x11, 0xdb, 0x40, 0xf9, 0xde, 0x8c, 0xb2, 0x02, 0x88, 0x77, 0xaf,
+ 0x63, 0x21, 0xc1, 0xad, 0xb0, 0x0d, 0xfa, 0xa0, 0x78, 0x56, 0xa3, 0x82,
+ 0xfd, 0xbb, 0x49, 0x5f, 0x14, 0x6d, 0xc8, 0xdc, 0x5f, 0x94, 0xda, 0x11,
+ 0x66, 0x7c, 0x1e, 0x91, 0xc5, 0xb6, 0xd8, 0x6d, 0x4f, 0xaa, 0xf2, 0xbf,
+ 0x21, 0x28, 0x7e, 0x52, 0xa2, 0x92, 0x78, 0x08, 0x61, 0x69, 0x21, 0xfe,
+ 0x2d, 0xec, 0x82, 0x18, 0x84, 0xf4, 0xd3, 0x8d, 0xc5, 0x8a, 0xbb, 0x8a,
+ 0xcc, 0x5d, 0xe6, 0xa3, 0xb6, 0xcc, 0x6e, 0xad, 0x6f, 0xb3, 0x0e, 0x61,
+ 0xee, 0x89, 0xce, 0x13, 0x34, 0x4f, 0x49, 0x55, 0xf5, 0x39, 0xbb, 0x99,
+ 0x96, 0xf0, 0xf5, 0xea, 0x5a, 0x3c, 0x9c, 0x16, 0xbd, 0x02, 0x53, 0xf0,
+ 0x2a, 0x0e, 0x41, 0x6e, 0xeb, 0xef, 0x9e, 0xf7, 0x70, 0x36, 0xcd, 0x80,
+ 0x2a, 0x76, 0xc8, 0x87, 0xe3, 0xeb, 0x23, 0xb3, 0x96, 0x2c, 0xe6, 0x1d,
+ 0x94, 0x5f, 0x1c, 0xa4, 0xe2, 0xcd, 0x24, 0x31, 0x2b, 0x06, 0x38, 0x32,
+ 0x61, 0x61, 0x39, 0x5c, 0x89, 0x4c, 0x48, 0x1d, 0x42, 0xc9, 0x67, 0x9e,
+ 0xd2, 0xbf, 0x58, 0xf7, 0xf9, 0x37, 0x31, 0xb0, 0x67, 0xdd, 0x8d, 0x26,
+ 0x36, 0x1a, 0x78, 0x1a, 0x09, 0x19, 0x3c, 0x93, 0x07, 0x70, 0x2a, 0xe1,
+ 0x7c, 0x29, 0xf5, 0xde, 0x66, 0x57, 0x0b, 0x12, 0x5e, 0x16, 0xed, 0x5e,
+ 0xbd, 0x37, 0xb3, 0x30, 0x69, 0xc6, 0x92, 0xa5, 0xf6, 0x19, 0xd8, 0x1d,
+ 0xf8, 0x36, 0x12, 0xb9, 0x4b, 0x95, 0x95, 0x9c, 0xd0, 0xce, 0x6c, 0x30,
+ 0xa7, 0x16, 0xfb, 0xf6, 0x4d, 0x64, 0xb6, 0x5f, 0x2a, 0x14, 0x9c, 0xa6,
+ 0xc8, 0x55, 0x8e, 0x20, 0xf9, 0x65, 0x07, 0x24, 0xcc, 0x38, 0x05, 0x4c,
+ 0x20, 0x88, 0xb4, 0xb5, 0x67, 0x94, 0xcf, 0x5d, 0x8e, 0x62, 0x37, 0xfe,
+ 0xc4, 0xb4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 146025 (0x23a69)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
+ Validity
+ Not Before: Apr 5 15:15:55 2013 GMT
+ Not After : Apr 4 15:15:55 2015 GMT
+ Subject: C=US, O=Google Inc, CN=Google Internet Authority G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9c:2a:04:77:5c:d8:50:91:3a:06:a3:82:e0:d8:
+ 50:48:bc:89:3f:f1:19:70:1a:88:46:7e:e0:8f:c5:
+ f1:89:ce:21:ee:5a:fe:61:0d:b7:32:44:89:a0:74:
+ 0b:53:4f:55:a4:ce:82:62:95:ee:eb:59:5f:c6:e1:
+ 05:80:12:c4:5e:94:3f:bc:5b:48:38:f4:53:f7:24:
+ e6:fb:91:e9:15:c4:cf:f4:53:0d:f4:4a:fc:9f:54:
+ de:7d:be:a0:6b:6f:87:c0:d0:50:1f:28:30:03:40:
+ da:08:73:51:6c:7f:ff:3a:3c:a7:37:06:8e:bd:4b:
+ 11:04:eb:7d:24:de:e6:f9:fc:31:71:fb:94:d5:60:
+ f3:2e:4a:af:42:d2:cb:ea:c4:6a:1a:b2:cc:53:dd:
+ 15:4b:8b:1f:c8:19:61:1f:cd:9d:a8:3e:63:2b:84:
+ 35:69:65:84:c8:19:c5:46:22:f8:53:95:be:e3:80:
+ 4a:10:c6:2a:ec:ba:97:20:11:c7:39:99:10:04:a0:
+ f0:61:7a:95:25:8c:4e:52:75:e2:b6:ed:08:ca:14:
+ fc:ce:22:6a:b3:4e:cf:46:03:97:97:03:7e:c0:b1:
+ de:7b:af:45:33:cf:ba:3e:71:b7:de:f4:25:25:c2:
+ 0d:35:89:9d:9d:fb:0e:11:79:89:1e:37:c5:af:8e:
+ 72:69
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
+
+ X509v3 Subject Key Identifier:
+ 4A:DD:06:16:1B:BC:F6:68:B5:76:F5:81:B6:BB:62:1A:BA:5A:81:2F
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.geotrust.com/crls/gtglobal.crl
+
+ Authority Information Access:
+ OCSP - URI:http://gtglobal-ocsp.geotrust.com
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.11129.2.5.1
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 36:d7:06:80:11:27:ad:2a:14:9b:38:77:b3:23:a0:75:58:bb:
+ b1:7e:83:42:ba:72:da:1e:d8:8e:36:06:97:e0:f0:95:3b:37:
+ fd:1b:42:58:fe:22:c8:6b:bd:38:5e:d1:3b:25:6e:12:eb:5e:
+ 67:76:46:40:90:da:14:c8:78:0d:ed:95:66:da:8e:86:6f:80:
+ a1:ba:56:32:95:86:dc:dc:6a:ca:04:8c:5b:7f:f6:bf:cc:6f:
+ 85:03:58:c3:68:51:13:cd:fd:c8:f7:79:3d:99:35:f0:56:a3:
+ bd:e0:59:ed:4f:44:09:a3:9e:38:7a:f6:46:d1:1d:12:9d:4f:
+ be:d0:40:fc:55:fe:06:5e:3c:da:1c:56:bd:96:51:7b:6f:57:
+ 2a:db:a2:aa:96:dc:8c:74:c2:95:be:f0:6e:95:13:ff:17:f0:
+ 3c:ac:b2:10:8d:cc:73:fb:e8:8f:02:c6:f0:fb:33:b3:95:3b:
+ e3:c2:cb:68:58:73:db:a8:24:62:3b:06:35:9d:0d:a9:33:bd:
+ 78:03:90:2e:4c:78:5d:50:3a:81:d4:ee:a0:c8:70:38:dc:b2:
+ f9:67:fa:87:40:5d:61:c0:51:8f:6b:83:6b:cd:05:3a:ca:e1:
+ a7:05:78:fc:ca:da:94:d0:2c:08:3d:7e:16:79:c8:a0:50:20:
+ 24:54:33:71
+-----BEGIN CERTIFICATE-----
+MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
+EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
+bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
+VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
+h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
+ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
+EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
+DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
+VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
+K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
+KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
+ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
+BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
+/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
+zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
+HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
+WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
+yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert10[] = {
+ 0x30, 0x82, 0x04, 0x04, 0x30, 0x82, 0x02, 0xec, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x02, 0x3a, 0x69, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x42, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x47,
+ 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30,
+ 0x34, 0x30, 0x35, 0x31, 0x35, 0x31, 0x35, 0x35, 0x35, 0x5a, 0x17, 0x0d,
+ 0x31, 0x35, 0x30, 0x34, 0x30, 0x34, 0x31, 0x35, 0x31, 0x35, 0x35, 0x35,
+ 0x5a, 0x30, 0x49, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x9c, 0x2a, 0x04, 0x77, 0x5c, 0xd8, 0x50, 0x91, 0x3a, 0x06, 0xa3,
+ 0x82, 0xe0, 0xd8, 0x50, 0x48, 0xbc, 0x89, 0x3f, 0xf1, 0x19, 0x70, 0x1a,
+ 0x88, 0x46, 0x7e, 0xe0, 0x8f, 0xc5, 0xf1, 0x89, 0xce, 0x21, 0xee, 0x5a,
+ 0xfe, 0x61, 0x0d, 0xb7, 0x32, 0x44, 0x89, 0xa0, 0x74, 0x0b, 0x53, 0x4f,
+ 0x55, 0xa4, 0xce, 0x82, 0x62, 0x95, 0xee, 0xeb, 0x59, 0x5f, 0xc6, 0xe1,
+ 0x05, 0x80, 0x12, 0xc4, 0x5e, 0x94, 0x3f, 0xbc, 0x5b, 0x48, 0x38, 0xf4,
+ 0x53, 0xf7, 0x24, 0xe6, 0xfb, 0x91, 0xe9, 0x15, 0xc4, 0xcf, 0xf4, 0x53,
+ 0x0d, 0xf4, 0x4a, 0xfc, 0x9f, 0x54, 0xde, 0x7d, 0xbe, 0xa0, 0x6b, 0x6f,
+ 0x87, 0xc0, 0xd0, 0x50, 0x1f, 0x28, 0x30, 0x03, 0x40, 0xda, 0x08, 0x73,
+ 0x51, 0x6c, 0x7f, 0xff, 0x3a, 0x3c, 0xa7, 0x37, 0x06, 0x8e, 0xbd, 0x4b,
+ 0x11, 0x04, 0xeb, 0x7d, 0x24, 0xde, 0xe6, 0xf9, 0xfc, 0x31, 0x71, 0xfb,
+ 0x94, 0xd5, 0x60, 0xf3, 0x2e, 0x4a, 0xaf, 0x42, 0xd2, 0xcb, 0xea, 0xc4,
+ 0x6a, 0x1a, 0xb2, 0xcc, 0x53, 0xdd, 0x15, 0x4b, 0x8b, 0x1f, 0xc8, 0x19,
+ 0x61, 0x1f, 0xcd, 0x9d, 0xa8, 0x3e, 0x63, 0x2b, 0x84, 0x35, 0x69, 0x65,
+ 0x84, 0xc8, 0x19, 0xc5, 0x46, 0x22, 0xf8, 0x53, 0x95, 0xbe, 0xe3, 0x80,
+ 0x4a, 0x10, 0xc6, 0x2a, 0xec, 0xba, 0x97, 0x20, 0x11, 0xc7, 0x39, 0x99,
+ 0x10, 0x04, 0xa0, 0xf0, 0x61, 0x7a, 0x95, 0x25, 0x8c, 0x4e, 0x52, 0x75,
+ 0xe2, 0xb6, 0xed, 0x08, 0xca, 0x14, 0xfc, 0xce, 0x22, 0x6a, 0xb3, 0x4e,
+ 0xcf, 0x46, 0x03, 0x97, 0x97, 0x03, 0x7e, 0xc0, 0xb1, 0xde, 0x7b, 0xaf,
+ 0x45, 0x33, 0xcf, 0xba, 0x3e, 0x71, 0xb7, 0xde, 0xf4, 0x25, 0x25, 0xc2,
+ 0x0d, 0x35, 0x89, 0x9d, 0x9d, 0xfb, 0x0e, 0x11, 0x79, 0x89, 0x1e, 0x37,
+ 0xc5, 0xaf, 0x8e, 0x72, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xfb, 0x30, 0x81, 0xf8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xc0, 0x7a, 0x98, 0x68, 0x8d, 0x89, 0xfb,
+ 0xab, 0x05, 0x64, 0x0c, 0x11, 0x7d, 0xaa, 0x7d, 0x65, 0xb8, 0xca, 0xcc,
+ 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x4a, 0xdd, 0x06, 0x16, 0x1b, 0xbc, 0xf6, 0x68, 0xb5, 0x76, 0xf5, 0x81,
+ 0xb6, 0xbb, 0x62, 0x1a, 0xba, 0x5a, 0x81, 0x2f, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0,
+ 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x67, 0x74, 0x67, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f,
+ 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x67, 0x74, 0x67,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2d, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x10, 0x30, 0x0e, 0x30, 0x0c,
+ 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x05, 0x01,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x36, 0xd7, 0x06, 0x80,
+ 0x11, 0x27, 0xad, 0x2a, 0x14, 0x9b, 0x38, 0x77, 0xb3, 0x23, 0xa0, 0x75,
+ 0x58, 0xbb, 0xb1, 0x7e, 0x83, 0x42, 0xba, 0x72, 0xda, 0x1e, 0xd8, 0x8e,
+ 0x36, 0x06, 0x97, 0xe0, 0xf0, 0x95, 0x3b, 0x37, 0xfd, 0x1b, 0x42, 0x58,
+ 0xfe, 0x22, 0xc8, 0x6b, 0xbd, 0x38, 0x5e, 0xd1, 0x3b, 0x25, 0x6e, 0x12,
+ 0xeb, 0x5e, 0x67, 0x76, 0x46, 0x40, 0x90, 0xda, 0x14, 0xc8, 0x78, 0x0d,
+ 0xed, 0x95, 0x66, 0xda, 0x8e, 0x86, 0x6f, 0x80, 0xa1, 0xba, 0x56, 0x32,
+ 0x95, 0x86, 0xdc, 0xdc, 0x6a, 0xca, 0x04, 0x8c, 0x5b, 0x7f, 0xf6, 0xbf,
+ 0xcc, 0x6f, 0x85, 0x03, 0x58, 0xc3, 0x68, 0x51, 0x13, 0xcd, 0xfd, 0xc8,
+ 0xf7, 0x79, 0x3d, 0x99, 0x35, 0xf0, 0x56, 0xa3, 0xbd, 0xe0, 0x59, 0xed,
+ 0x4f, 0x44, 0x09, 0xa3, 0x9e, 0x38, 0x7a, 0xf6, 0x46, 0xd1, 0x1d, 0x12,
+ 0x9d, 0x4f, 0xbe, 0xd0, 0x40, 0xfc, 0x55, 0xfe, 0x06, 0x5e, 0x3c, 0xda,
+ 0x1c, 0x56, 0xbd, 0x96, 0x51, 0x7b, 0x6f, 0x57, 0x2a, 0xdb, 0xa2, 0xaa,
+ 0x96, 0xdc, 0x8c, 0x74, 0xc2, 0x95, 0xbe, 0xf0, 0x6e, 0x95, 0x13, 0xff,
+ 0x17, 0xf0, 0x3c, 0xac, 0xb2, 0x10, 0x8d, 0xcc, 0x73, 0xfb, 0xe8, 0x8f,
+ 0x02, 0xc6, 0xf0, 0xfb, 0x33, 0xb3, 0x95, 0x3b, 0xe3, 0xc2, 0xcb, 0x68,
+ 0x58, 0x73, 0xdb, 0xa8, 0x24, 0x62, 0x3b, 0x06, 0x35, 0x9d, 0x0d, 0xa9,
+ 0x33, 0xbd, 0x78, 0x03, 0x90, 0x2e, 0x4c, 0x78, 0x5d, 0x50, 0x3a, 0x81,
+ 0xd4, 0xee, 0xa0, 0xc8, 0x70, 0x38, 0xdc, 0xb2, 0xf9, 0x67, 0xfa, 0x87,
+ 0x40, 0x5d, 0x61, 0xc0, 0x51, 0x8f, 0x6b, 0x83, 0x6b, 0xcd, 0x05, 0x3a,
+ 0xca, 0xe1, 0xa7, 0x05, 0x78, 0xfc, 0xca, 0xda, 0x94, 0xd0, 0x2c, 0x08,
+ 0x3d, 0x7e, 0x16, 0x79, 0xc8, 0xa0, 0x50, 0x20, 0x24, 0x54, 0x33, 0x71,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120033005 (0x7278eed)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Apr 18 16:36:18 2012 GMT
+ Not After : Aug 13 16:35:17 2018 GMT
+ Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79:
+ d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a:
+ 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2:
+ 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01:
+ 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7:
+ 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6:
+ 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c:
+ a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70:
+ 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77:
+ d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae:
+ 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18:
+ 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85:
+ ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9:
+ 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5:
+ c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a:
+ ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0:
+ 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27:
+ 1a:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://cybertrust.omniroot.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 93:1d:fe:8b:ae:46:ec:cb:a9:0f:ab:e5:ef:ca:b2:68:16:68:
+ d8:8f:fa:13:a9:af:b3:cb:2d:e7:4b:6e:8e:69:2a:c2:2b:10:
+ 0a:8d:f6:ae:73:b6:b9:fb:14:fd:5f:6d:b8:50:b6:c4:8a:d6:
+ 40:7e:d7:c3:cb:73:dc:c9:5d:5b:af:b0:41:b5:37:eb:ea:dc:
+ 20:91:c4:34:6a:f4:a1:f3:96:9d:37:86:97:e1:71:a4:dd:7d:
+ fa:44:84:94:ae:d7:09:04:22:76:0f:64:51:35:a9:24:0f:f9:
+ 0b:db:32:da:c2:fe:c1:b9:2a:5c:7a:27:13:ca:b1:48:3a:71:
+ d0:43
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIEByeO7TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEyMDQxODE2MzYxOFoXDTE4MDgxMzE2MzUxN1owWjELMAkG
+A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz
+dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO
+KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn
+c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg
+kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc
+B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAUcw
+ggFDMBIGA1UdEwEB/wQIMAYBAf8CAQMwSgYDVR0gBEMwQTA/BgRVHSAAMDcwNQYI
+KwYBBQUHAgEWKWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0
+b3J5MA4GA1UdDwEB/wQEAwIBBjCBiQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYT
+AlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJl
+clRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3SCAgGlMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVi
+bGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwDQYJKoZIhvcN
+AQEFBQADgYEAkx3+i65G7MupD6vl78qyaBZo2I/6E6mvs8st50tujmkqwisQCo32
+rnO2ufsU/V9tuFC2xIrWQH7Xw8tz3MldW6+wQbU36+rcIJHENGr0ofOWnTeGl+Fx
+pN19+kSElK7XCQQidg9kUTWpJA/5C9sy2sL+wbkqXHonE8qxSDpx0EM=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert11[] = {
+ 0x30, 0x82, 0x04, 0x15, 0x30, 0x82, 0x03, 0x7e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x8e, 0xed, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x32, 0x30, 0x34, 0x31, 0x38, 0x31, 0x36, 0x33, 0x36, 0x31,
+ 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x33, 0x31, 0x36,
+ 0x33, 0x35, 0x31, 0x37, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69,
+ 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04,
+ 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79,
+ 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e,
+ 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d,
+ 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12,
+ 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88,
+ 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7,
+ 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5,
+ 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab,
+ 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5,
+ 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f,
+ 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77,
+ 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0,
+ 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd,
+ 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0,
+ 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4,
+ 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9,
+ 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c,
+ 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c,
+ 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b,
+ 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76,
+ 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27,
+ 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x47, 0x30,
+ 0x82, 0x01, 0x43, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30,
+ 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x37, 0x30, 0x35, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x29, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x81, 0x89, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77,
+ 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23,
+ 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45,
+ 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82,
+ 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e,
+ 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52,
+ 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x93, 0x1d, 0xfe,
+ 0x8b, 0xae, 0x46, 0xec, 0xcb, 0xa9, 0x0f, 0xab, 0xe5, 0xef, 0xca, 0xb2,
+ 0x68, 0x16, 0x68, 0xd8, 0x8f, 0xfa, 0x13, 0xa9, 0xaf, 0xb3, 0xcb, 0x2d,
+ 0xe7, 0x4b, 0x6e, 0x8e, 0x69, 0x2a, 0xc2, 0x2b, 0x10, 0x0a, 0x8d, 0xf6,
+ 0xae, 0x73, 0xb6, 0xb9, 0xfb, 0x14, 0xfd, 0x5f, 0x6d, 0xb8, 0x50, 0xb6,
+ 0xc4, 0x8a, 0xd6, 0x40, 0x7e, 0xd7, 0xc3, 0xcb, 0x73, 0xdc, 0xc9, 0x5d,
+ 0x5b, 0xaf, 0xb0, 0x41, 0xb5, 0x37, 0xeb, 0xea, 0xdc, 0x20, 0x91, 0xc4,
+ 0x34, 0x6a, 0xf4, 0xa1, 0xf3, 0x96, 0x9d, 0x37, 0x86, 0x97, 0xe1, 0x71,
+ 0xa4, 0xdd, 0x7d, 0xfa, 0x44, 0x84, 0x94, 0xae, 0xd7, 0x09, 0x04, 0x22,
+ 0x76, 0x0f, 0x64, 0x51, 0x35, 0xa9, 0x24, 0x0f, 0xf9, 0x0b, 0xdb, 0x32,
+ 0xda, 0xc2, 0xfe, 0xc1, 0xb9, 0x2a, 0x5c, 0x7a, 0x27, 0x13, 0xca, 0xb1,
+ 0x48, 0x3a, 0x71, 0xd0, 0x43,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120010508 (0x727370c)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Sep 8 17:35:16 2010 GMT
+ Not After : Sep 8 17:34:08 2020 GMT
+ Subject: O=Cybertrust Inc, CN=Cybertrust Public SureServer SV CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:ba:99:8d:b7:e1:cd:73:88:f9:b9:dd:de:f4:
+ 05:f3:25:f5:3f:c5:52:1e:51:5a:3f:9a:ff:4d:84:
+ b7:50:7f:f1:10:8a:5d:7f:64:55:1c:3b:a3:f3:ff:
+ 97:7f:1c:4b:ed:6f:7f:e9:54:ec:97:2a:42:03:67:
+ 7f:b9:c8:6c:a2:97:f8:40:93:24:c3:25:5e:a5:66:
+ 8b:86:bd:d7:b9:26:22:6e:d2:66:83:b3:78:c1:7c:
+ 58:76:11:eb:16:55:47:32:f0:b9:34:10:bd:8f:26:
+ a2:25:68:c1:14:2b:a2:73:d6:66:3d:44:87:5c:13:
+ 7f:58:91:62:3d:57:7f:6c:ae:42:e8:12:7e:bd:78:
+ f1:f1:ac:5c:35:60:68:45:bc:53:73:87:11:1d:c5:
+ 2e:fa:60:35:da:91:f9:da:f2:55:6c:bf:ca:a2:57:
+ 5c:c8:64:bc:a9:5b:15:a0:fc:1c:f3:44:2e:bd:06:
+ f2:68:d8:40:2d:bb:b3:61:25:92:93:25:1c:77:46:
+ 90:bf:d0:af:b7:83:a0:3c:87:5e:a5:91:a8:ff:c1:
+ 31:1b:b6:4b:ac:12:34:08:d5:db:ec:89:87:63:06:
+ a7:53:f8:d5:f5:e6:66:ac:5e:84:65:46:c9:f4:3a:
+ 25:0f:6c:cc:0f:66:b8:9a:55:a1:46:6c:fc:91:23:
+ 5f:bd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.50
+ CPS: http://cybertrust.omniroot.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 04:98:60:DF:80:1B:96:49:5D:65:56:2D:A5:2C:09:24:0A:EC:DC:B9
+ Signature Algorithm: sha1WithRSAEncryption
+ 5f:df:8b:cf:29:79:78:2b:f3:7c:f4:82:5f:79:e0:e1:b3:28:
+ bd:08:75:41:ce:8c:88:d7:0e:55:b9:02:b5:05:79:3e:bb:52:
+ 31:b3:4b:1e:b1:fe:d3:a2:21:43:d2:91:d3:16:fa:6b:79:e4:
+ 8e:4d:19:ec:4c:86:68:34:52:b7:6f:c2:bd:9c:78:be:f0:6f:
+ 3f:3d:9e:9f:49:74:c4:7c:97:19:45:57:ac:6f:fa:5a:3e:3f:
+ d3:d6:e3:2b:dc:8a:f8:c8:0a:0d:6b:8c:3f:94:78:37:98:88:
+ 61:91:df:59:14:0f:09:c5:63:54:fb:f4:f6:af:97:ec:fc:63:
+ 64:43:a6:bc:cc:e4:e3:1f:df:73:b0:6e:f7:b5:c8:29:9b:ae:
+ 25:52:b8:b4:72:e1:de:93:48:f1:28:9f:7e:66:3f:3f:8b:55:
+ 0f:f8:16:07:71:05:d7:65:9c:d7:1b:3c:34:e6:44:16:3a:bd:
+ d8:60:93:83:83:0c:88:96:65:33:40:df:6a:ac:ff:fe:94:51:
+ 61:bb:89:3f:f7:ac:c4:e4:b3:47:e2:fd:a2:6a:32:83:e2:7e:
+ 6f:f0:12:8e:a3:66:76:40:97:fb:11:e1:f7:73:1f:da:8b:1c:
+ 31:42:8b:9f:11:c5:49:a5:60:ed:48:2b:05:84:15:ab:2f:8a:
+ 2c:51:72:c0
+-----BEGIN CERTIFICATE-----
+MIIEGzCCAwOgAwIBAgIEByc3DDANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEwMDkwODE3MzUxNloX
+DTIwMDkwODE3MzQwOFowRjEXMBUGA1UEChMOQ3liZXJ0cnVzdCBJbmMxKzApBgNV
+BAMTIkN5YmVydHJ1c3QgUHVibGljIFN1cmVTZXJ2ZXIgU1YgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjupmNt+HNc4j5ud3e9AXzJfU/xVIeUVo/
+mv9NhLdQf/EQil1/ZFUcO6Pz/5d/HEvtb3/pVOyXKkIDZ3+5yGyil/hAkyTDJV6l
+ZouGvde5JiJu0maDs3jBfFh2EesWVUcy8Lk0EL2PJqIlaMEUK6Jz1mY9RIdcE39Y
+kWI9V39srkLoEn69ePHxrFw1YGhFvFNzhxEdxS76YDXakfna8lVsv8qiV1zIZLyp
+WxWg/BzzRC69BvJo2EAtu7NhJZKTJRx3RpC/0K+3g6A8h16lkaj/wTEbtkusEjQI
+1dvsiYdjBqdT+NX15masXoRlRsn0OiUPbMwPZriaVaFGbPyRI1+9AgMBAAGjgfww
+gfkwEgYDVR0TAQH/BAgwBgEB/wIBADBPBgNVHSAESDBGMEQGCSsGAQQBsT4BMjA3
+MDUGCCsGAQUFBwIBFilodHRwOi8vY3liZXJ0cnVzdC5vbW5pcm9vdC5jb20vcmVw
+b3NpdG9yeTAOBgNVHQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys+ghU
+NoZ7OrUETfAwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NkcDEucHVibGljLXRy
+dXN0LmNvbS9DUkwvT21uaXJvb3QyMDI1LmNybDAdBgNVHQ4EFgQUBJhg34Ablkld
+ZVYtpSwJJArs3LkwDQYJKoZIhvcNAQEFBQADggEBAF/fi88peXgr83z0gl954OGz
+KL0IdUHOjIjXDlW5ArUFeT67UjGzSx6x/tOiIUPSkdMW+mt55I5NGexMhmg0Urdv
+wr2ceL7wbz89np9JdMR8lxlFV6xv+lo+P9PW4yvcivjICg1rjD+UeDeYiGGR31kU
+DwnFY1T79Pavl+z8Y2RDprzM5OMf33Owbve1yCmbriVSuLRy4d6TSPEon35mPz+L
+VQ/4FgdxBddlnNcbPDTmRBY6vdhgk4ODDIiWZTNA32qs//6UUWG7iT/3rMTks0fi
+/aJqMoPifm/wEo6jZnZAl/sR4fdzH9qLHDFCi58RxUmlYO1IKwWEFasviixRcsA=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert12[] = {
+ 0x30, 0x82, 0x04, 0x1b, 0x30, 0x82, 0x03, 0x03, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x37, 0x0c, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x30, 0x39, 0x30, 0x38, 0x31, 0x37, 0x33, 0x35, 0x31, 0x36, 0x5a, 0x17,
+ 0x0d, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x31, 0x37, 0x33, 0x34, 0x30,
+ 0x38, 0x5a, 0x30, 0x46, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0e, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x22, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x53, 0x75,
+ 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x53, 0x56, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3,
+ 0xba, 0x99, 0x8d, 0xb7, 0xe1, 0xcd, 0x73, 0x88, 0xf9, 0xb9, 0xdd, 0xde,
+ 0xf4, 0x05, 0xf3, 0x25, 0xf5, 0x3f, 0xc5, 0x52, 0x1e, 0x51, 0x5a, 0x3f,
+ 0x9a, 0xff, 0x4d, 0x84, 0xb7, 0x50, 0x7f, 0xf1, 0x10, 0x8a, 0x5d, 0x7f,
+ 0x64, 0x55, 0x1c, 0x3b, 0xa3, 0xf3, 0xff, 0x97, 0x7f, 0x1c, 0x4b, 0xed,
+ 0x6f, 0x7f, 0xe9, 0x54, 0xec, 0x97, 0x2a, 0x42, 0x03, 0x67, 0x7f, 0xb9,
+ 0xc8, 0x6c, 0xa2, 0x97, 0xf8, 0x40, 0x93, 0x24, 0xc3, 0x25, 0x5e, 0xa5,
+ 0x66, 0x8b, 0x86, 0xbd, 0xd7, 0xb9, 0x26, 0x22, 0x6e, 0xd2, 0x66, 0x83,
+ 0xb3, 0x78, 0xc1, 0x7c, 0x58, 0x76, 0x11, 0xeb, 0x16, 0x55, 0x47, 0x32,
+ 0xf0, 0xb9, 0x34, 0x10, 0xbd, 0x8f, 0x26, 0xa2, 0x25, 0x68, 0xc1, 0x14,
+ 0x2b, 0xa2, 0x73, 0xd6, 0x66, 0x3d, 0x44, 0x87, 0x5c, 0x13, 0x7f, 0x58,
+ 0x91, 0x62, 0x3d, 0x57, 0x7f, 0x6c, 0xae, 0x42, 0xe8, 0x12, 0x7e, 0xbd,
+ 0x78, 0xf1, 0xf1, 0xac, 0x5c, 0x35, 0x60, 0x68, 0x45, 0xbc, 0x53, 0x73,
+ 0x87, 0x11, 0x1d, 0xc5, 0x2e, 0xfa, 0x60, 0x35, 0xda, 0x91, 0xf9, 0xda,
+ 0xf2, 0x55, 0x6c, 0xbf, 0xca, 0xa2, 0x57, 0x5c, 0xc8, 0x64, 0xbc, 0xa9,
+ 0x5b, 0x15, 0xa0, 0xfc, 0x1c, 0xf3, 0x44, 0x2e, 0xbd, 0x06, 0xf2, 0x68,
+ 0xd8, 0x40, 0x2d, 0xbb, 0xb3, 0x61, 0x25, 0x92, 0x93, 0x25, 0x1c, 0x77,
+ 0x46, 0x90, 0xbf, 0xd0, 0xaf, 0xb7, 0x83, 0xa0, 0x3c, 0x87, 0x5e, 0xa5,
+ 0x91, 0xa8, 0xff, 0xc1, 0x31, 0x1b, 0xb6, 0x4b, 0xac, 0x12, 0x34, 0x08,
+ 0xd5, 0xdb, 0xec, 0x89, 0x87, 0x63, 0x06, 0xa7, 0x53, 0xf8, 0xd5, 0xf5,
+ 0xe6, 0x66, 0xac, 0x5e, 0x84, 0x65, 0x46, 0xc9, 0xf4, 0x3a, 0x25, 0x0f,
+ 0x6c, 0xcc, 0x0f, 0x66, 0xb8, 0x9a, 0x55, 0xa1, 0x46, 0x6c, 0xfc, 0x91,
+ 0x23, 0x5f, 0xbd, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfc, 0x30,
+ 0x81, 0xf9, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x4f,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x48, 0x30, 0x46, 0x30, 0x44, 0x06,
+ 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x32, 0x30, 0x37,
+ 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69,
+ 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54,
+ 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0,
+ 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64,
+ 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f,
+ 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x04, 0x98, 0x60, 0xdf, 0x80, 0x1b, 0x96, 0x49, 0x5d,
+ 0x65, 0x56, 0x2d, 0xa5, 0x2c, 0x09, 0x24, 0x0a, 0xec, 0xdc, 0xb9, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x5f, 0xdf, 0x8b, 0xcf, 0x29,
+ 0x79, 0x78, 0x2b, 0xf3, 0x7c, 0xf4, 0x82, 0x5f, 0x79, 0xe0, 0xe1, 0xb3,
+ 0x28, 0xbd, 0x08, 0x75, 0x41, 0xce, 0x8c, 0x88, 0xd7, 0x0e, 0x55, 0xb9,
+ 0x02, 0xb5, 0x05, 0x79, 0x3e, 0xbb, 0x52, 0x31, 0xb3, 0x4b, 0x1e, 0xb1,
+ 0xfe, 0xd3, 0xa2, 0x21, 0x43, 0xd2, 0x91, 0xd3, 0x16, 0xfa, 0x6b, 0x79,
+ 0xe4, 0x8e, 0x4d, 0x19, 0xec, 0x4c, 0x86, 0x68, 0x34, 0x52, 0xb7, 0x6f,
+ 0xc2, 0xbd, 0x9c, 0x78, 0xbe, 0xf0, 0x6f, 0x3f, 0x3d, 0x9e, 0x9f, 0x49,
+ 0x74, 0xc4, 0x7c, 0x97, 0x19, 0x45, 0x57, 0xac, 0x6f, 0xfa, 0x5a, 0x3e,
+ 0x3f, 0xd3, 0xd6, 0xe3, 0x2b, 0xdc, 0x8a, 0xf8, 0xc8, 0x0a, 0x0d, 0x6b,
+ 0x8c, 0x3f, 0x94, 0x78, 0x37, 0x98, 0x88, 0x61, 0x91, 0xdf, 0x59, 0x14,
+ 0x0f, 0x09, 0xc5, 0x63, 0x54, 0xfb, 0xf4, 0xf6, 0xaf, 0x97, 0xec, 0xfc,
+ 0x63, 0x64, 0x43, 0xa6, 0xbc, 0xcc, 0xe4, 0xe3, 0x1f, 0xdf, 0x73, 0xb0,
+ 0x6e, 0xf7, 0xb5, 0xc8, 0x29, 0x9b, 0xae, 0x25, 0x52, 0xb8, 0xb4, 0x72,
+ 0xe1, 0xde, 0x93, 0x48, 0xf1, 0x28, 0x9f, 0x7e, 0x66, 0x3f, 0x3f, 0x8b,
+ 0x55, 0x0f, 0xf8, 0x16, 0x07, 0x71, 0x05, 0xd7, 0x65, 0x9c, 0xd7, 0x1b,
+ 0x3c, 0x34, 0xe6, 0x44, 0x16, 0x3a, 0xbd, 0xd8, 0x60, 0x93, 0x83, 0x83,
+ 0x0c, 0x88, 0x96, 0x65, 0x33, 0x40, 0xdf, 0x6a, 0xac, 0xff, 0xfe, 0x94,
+ 0x51, 0x61, 0xbb, 0x89, 0x3f, 0xf7, 0xac, 0xc4, 0xe4, 0xb3, 0x47, 0xe2,
+ 0xfd, 0xa2, 0x6a, 0x32, 0x83, 0xe2, 0x7e, 0x6f, 0xf0, 0x12, 0x8e, 0xa3,
+ 0x66, 0x76, 0x40, 0x97, 0xfb, 0x11, 0xe1, 0xf7, 0x73, 0x1f, 0xda, 0x8b,
+ 0x1c, 0x31, 0x42, 0x8b, 0x9f, 0x11, 0xc5, 0x49, 0xa5, 0x60, 0xed, 0x48,
+ 0x2b, 0x05, 0x84, 0x15, 0xab, 0x2f, 0x8a, 0x2c, 0x51, 0x72, 0xc0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 7 (0x7)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=America Online Inc., CN=America Online Root Certification Authority 1
+ Validity
+ Not Before: Jun 4 17:26:39 2004 GMT
+ Not After : Jun 4 17:26:39 2029 GMT
+ Subject: C=US, ST=Virginia, L=Dulles, O=America Online Inc., CN=AOL Member CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:f8:63:d8:f1:cc:d6:11:6f:05:e1:d6:9f:43:27:
+ 27:25:fa:af:67:87:74:ac:dc:d6:fc:c2:9a:bd:33:
+ 72:4e:65:b0:5a:cd:eb:f8:a9:69:48:39:6e:68:2e:
+ 71:16:6e:9e:59:7c:c2:7c:cd:ed:6e:43:d8:09:42:
+ 4e:0d:9a:7d:ee:b5:5a:23:81:d2:a4:5b:a9:51:54:
+ 1c:df:f6:84:df:19:c3:3e:94:2d:8d:ba:10:f8:e8:
+ 43:08:0f:32:35:6c:35:31:f8:d6:d3:fc:09:31:d6:
+ a9:a1:7a:20:06:59:0c:e0:2b:8d:84:c3:37:a0:08:
+ 1e:f1:35:73:10:dd:4f:fd:0c:72:93:26:6e:af:c5:
+ 1c:39:e3:ca:f3:95:6f:30:c2:85:3d:4d:84:20:c8:
+ 3e:3d:d0:40:d6:fe:06:4a:18:73:0b:6e:57:67:db:
+ 83:c1:13:66:97:d3:bd:59:bc:7e:fa:2f:36:45:14:
+ cd:bc:bf:ab:68:77:bf:48:eb:11:89:4e:6a:84:f3:
+ 5d:1c:e5:6b:6a:00:e6:6b:8d:48:a4:09:b9:21:dc:
+ 2d:66:29:f4:56:9e:f0:05:68:ff:cc:c1:c9:88:bc:
+ d2:2c:0b:af:1d:74:1a:86:68:a4:6d:14:74:ec:24:
+ 80:f8:95:b9:f3:2e:3c:3d:20:6f:09:02:38:ea:3a:
+ 38:05
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 61:A6:99:6D:24:9F:0E:11:88:E6:39:E0:FE:74:D1:05:69:52:A9:43
+ X509v3 Authority Key Identifier:
+ keyid:00:AD:D9:A3:F6:79:F6:6E:74:A9:7F:33:3D:81:17:D7:4C:CF:33:DE
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://pki-info.aol.com/AOL/index.html
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.aol.com/AOL/MasterCRL.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 0c:9d:bc:cc:1d:d4:2e:91:1b:af:a0:3a:eb:cd:5e:fc:25:2a:
+ 29:8e:b3:20:e0:17:37:fa:fc:bb:c5:b5:14:bb:1c:66:0e:6f:
+ 58:5f:c6:71:d0:13:89:c7:ad:23:0b:ed:4c:b8:58:c1:e3:c2:
+ a4:24:4c:65:76:0d:b3:86:64:4f:28:ba:cf:96:f8:65:9a:0e:
+ 82:26:f5:82:85:4e:35:20:b3:45:cc:60:ee:0f:4e:20:94:3a:
+ 2b:2f:cb:23:10:9b:46:1b:7e:c3:56:75:49:24:a4:b8:4d:9f:
+ 1c:68:d4:e6:f2:2f:af:8e:ed:2b:b7:e5:96:6b:1c:3d:8d:bf:
+ 20:d3:6f:2d:54:2f:9c:79:35:fd:da:06:de:68:20:20:4b:af:
+ 5d:ab:5e:66:c3:14:64:7b:f7:02:e6:27:96:ad:18:1e:ab:f3:
+ 82:60:fc:4c:5f:b6:0a:52:7b:9e:9c:3b:2e:ce:3c:42:5f:36:
+ 6d:6b:fe:a1:76:8a:22:21:fd:5b:e8:bd:7f:9f:ce:51:74:48:
+ 6c:ac:b5:d1:a2:6a:fa:07:44:de:d0:db:a9:8d:18:1f:f1:b9:
+ c5:e8:2a:eb:ba:3d:3b:18:8c:c0:0c:30:b3:c9:21:1c:33:4c:
+ 3a:49:53:d4:a8:ba:ba:38:23:3d:3a:65:82:5e:79:71:15:f8:
+ 25:2b:7d:19
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgIBBzANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc
+MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP
+bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTA0MDYwNDE3
+MjYzOVoXDTI5MDYwNDE3MjYzOVowZzELMAkGA1UEBhMCVVMxETAPBgNVBAgTCFZp
+cmdpbmlhMQ8wDQYDVQQHEwZEdWxsZXMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5l
+IEluYy4xFjAUBgNVBAMTDUFPTCBNZW1iZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQD4Y9jxzNYRbwXh1p9DJycl+q9nh3Ss3Nb8wpq9M3JOZbBa
+zev4qWlIOW5oLnEWbp5ZfMJ8ze1uQ9gJQk4Nmn3utVojgdKkW6lRVBzf9oTfGcM+
+lC2NuhD46EMIDzI1bDUx+NbT/Akx1qmheiAGWQzgK42EwzegCB7xNXMQ3U/9DHKT
+Jm6vxRw548rzlW8wwoU9TYQgyD490EDW/gZKGHMLbldn24PBE2aX071ZvH76LzZF
+FM28v6tod79I6xGJTmqE810c5WtqAOZrjUikCbkh3C1mKfRWnvAFaP/MwcmIvNIs
+C68ddBqGaKRtFHTsJID4lbnzLjw9IG8JAjjqOjgFAgMBAAGjgeUwgeIwDgYDVR0P
+AQH/BAQDAgGGMB0GA1UdDgQWBBRhppltJJ8OEYjmOeD+dNEFaVKpQzAfBgNVHSME
+GDAWgBQArdmj9nn2bnSpfzM9gRfXTM8z3jAPBgNVHRMBAf8EBTADAQH/MEgGA1Ud
+IARBMD8wPQYEVR0gADA1MDMGCCsGAQUFBwIBFidodHRwczovL3BraS1pbmZvLmFv
+bC5jb20vQU9ML2luZGV4Lmh0bWwwNQYDVR0fBC4wLDAqoCigJoYkaHR0cDovL2Ny
+bC5hb2wuY29tL0FPTC9NYXN0ZXJDUkwuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQAM
+nbzMHdQukRuvoDrrzV78JSopjrMg4Bc3+vy7xbUUuxxmDm9YX8Zx0BOJx60jC+1M
+uFjB48KkJExldg2zhmRPKLrPlvhlmg6CJvWChU41ILNFzGDuD04glDorL8sjEJtG
+G37DVnVJJKS4TZ8caNTm8i+vju0rt+WWaxw9jb8g028tVC+ceTX92gbeaCAgS69d
+q15mwxRke/cC5ieWrRgeq/OCYPxMX7YKUnuenDsuzjxCXzZta/6hdooiIf1b6L1/
+n85RdEhsrLXRomr6B0Te0NupjRgf8bnF6Crruj07GIzADDCzySEcM0w6SVPUqLq6
+OCM9OmWCXnlxFfglK30Z
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert13[] = {
+ 0x30, 0x82, 0x04, 0x2b, 0x30, 0x82, 0x03, 0x13, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x07, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x63, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1c,
+ 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x13, 0x41, 0x6d, 0x65,
+ 0x72, 0x69, 0x63, 0x61, 0x20, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x2d, 0x41, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x61, 0x20, 0x4f,
+ 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x31,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x34, 0x30, 0x36, 0x30, 0x34, 0x31, 0x37,
+ 0x32, 0x36, 0x33, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x39, 0x30, 0x36, 0x30,
+ 0x34, 0x31, 0x37, 0x32, 0x36, 0x33, 0x39, 0x5a, 0x30, 0x67, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x08, 0x56, 0x69,
+ 0x72, 0x67, 0x69, 0x6e, 0x69, 0x61, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x13, 0x06, 0x44, 0x75, 0x6c, 0x6c, 0x65, 0x73, 0x31,
+ 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x13, 0x41, 0x6d,
+ 0x65, 0x72, 0x69, 0x63, 0x61, 0x20, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0d, 0x41, 0x4f, 0x4c, 0x20, 0x4d, 0x65, 0x6d, 0x62,
+ 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xf8, 0x63, 0xd8, 0xf1, 0xcc, 0xd6, 0x11, 0x6f, 0x05, 0xe1,
+ 0xd6, 0x9f, 0x43, 0x27, 0x27, 0x25, 0xfa, 0xaf, 0x67, 0x87, 0x74, 0xac,
+ 0xdc, 0xd6, 0xfc, 0xc2, 0x9a, 0xbd, 0x33, 0x72, 0x4e, 0x65, 0xb0, 0x5a,
+ 0xcd, 0xeb, 0xf8, 0xa9, 0x69, 0x48, 0x39, 0x6e, 0x68, 0x2e, 0x71, 0x16,
+ 0x6e, 0x9e, 0x59, 0x7c, 0xc2, 0x7c, 0xcd, 0xed, 0x6e, 0x43, 0xd8, 0x09,
+ 0x42, 0x4e, 0x0d, 0x9a, 0x7d, 0xee, 0xb5, 0x5a, 0x23, 0x81, 0xd2, 0xa4,
+ 0x5b, 0xa9, 0x51, 0x54, 0x1c, 0xdf, 0xf6, 0x84, 0xdf, 0x19, 0xc3, 0x3e,
+ 0x94, 0x2d, 0x8d, 0xba, 0x10, 0xf8, 0xe8, 0x43, 0x08, 0x0f, 0x32, 0x35,
+ 0x6c, 0x35, 0x31, 0xf8, 0xd6, 0xd3, 0xfc, 0x09, 0x31, 0xd6, 0xa9, 0xa1,
+ 0x7a, 0x20, 0x06, 0x59, 0x0c, 0xe0, 0x2b, 0x8d, 0x84, 0xc3, 0x37, 0xa0,
+ 0x08, 0x1e, 0xf1, 0x35, 0x73, 0x10, 0xdd, 0x4f, 0xfd, 0x0c, 0x72, 0x93,
+ 0x26, 0x6e, 0xaf, 0xc5, 0x1c, 0x39, 0xe3, 0xca, 0xf3, 0x95, 0x6f, 0x30,
+ 0xc2, 0x85, 0x3d, 0x4d, 0x84, 0x20, 0xc8, 0x3e, 0x3d, 0xd0, 0x40, 0xd6,
+ 0xfe, 0x06, 0x4a, 0x18, 0x73, 0x0b, 0x6e, 0x57, 0x67, 0xdb, 0x83, 0xc1,
+ 0x13, 0x66, 0x97, 0xd3, 0xbd, 0x59, 0xbc, 0x7e, 0xfa, 0x2f, 0x36, 0x45,
+ 0x14, 0xcd, 0xbc, 0xbf, 0xab, 0x68, 0x77, 0xbf, 0x48, 0xeb, 0x11, 0x89,
+ 0x4e, 0x6a, 0x84, 0xf3, 0x5d, 0x1c, 0xe5, 0x6b, 0x6a, 0x00, 0xe6, 0x6b,
+ 0x8d, 0x48, 0xa4, 0x09, 0xb9, 0x21, 0xdc, 0x2d, 0x66, 0x29, 0xf4, 0x56,
+ 0x9e, 0xf0, 0x05, 0x68, 0xff, 0xcc, 0xc1, 0xc9, 0x88, 0xbc, 0xd2, 0x2c,
+ 0x0b, 0xaf, 0x1d, 0x74, 0x1a, 0x86, 0x68, 0xa4, 0x6d, 0x14, 0x74, 0xec,
+ 0x24, 0x80, 0xf8, 0x95, 0xb9, 0xf3, 0x2e, 0x3c, 0x3d, 0x20, 0x6f, 0x09,
+ 0x02, 0x38, 0xea, 0x3a, 0x38, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x81, 0xe5, 0x30, 0x81, 0xe2, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x61, 0xa6, 0x99, 0x6d,
+ 0x24, 0x9f, 0x0e, 0x11, 0x88, 0xe6, 0x39, 0xe0, 0xfe, 0x74, 0xd1, 0x05,
+ 0x69, 0x52, 0xa9, 0x43, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x00, 0xad, 0xd9, 0xa3, 0xf6, 0x79, 0xf6,
+ 0x6e, 0x74, 0xa9, 0x7f, 0x33, 0x3d, 0x81, 0x17, 0xd7, 0x4c, 0xcf, 0x33,
+ 0xde, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x48, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x41, 0x30, 0x3f, 0x30, 0x3d, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x35, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x27, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x70, 0x6b, 0x69, 0x2d, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x61, 0x6f,
+ 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x4f, 0x4c, 0x2f, 0x69, 0x6e,
+ 0x64, 0x65, 0x78, 0x2e, 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x35, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30, 0x2a, 0xa0, 0x28, 0xa0,
+ 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x61, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x4f,
+ 0x4c, 0x2f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x43, 0x52, 0x4c, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x0c,
+ 0x9d, 0xbc, 0xcc, 0x1d, 0xd4, 0x2e, 0x91, 0x1b, 0xaf, 0xa0, 0x3a, 0xeb,
+ 0xcd, 0x5e, 0xfc, 0x25, 0x2a, 0x29, 0x8e, 0xb3, 0x20, 0xe0, 0x17, 0x37,
+ 0xfa, 0xfc, 0xbb, 0xc5, 0xb5, 0x14, 0xbb, 0x1c, 0x66, 0x0e, 0x6f, 0x58,
+ 0x5f, 0xc6, 0x71, 0xd0, 0x13, 0x89, 0xc7, 0xad, 0x23, 0x0b, 0xed, 0x4c,
+ 0xb8, 0x58, 0xc1, 0xe3, 0xc2, 0xa4, 0x24, 0x4c, 0x65, 0x76, 0x0d, 0xb3,
+ 0x86, 0x64, 0x4f, 0x28, 0xba, 0xcf, 0x96, 0xf8, 0x65, 0x9a, 0x0e, 0x82,
+ 0x26, 0xf5, 0x82, 0x85, 0x4e, 0x35, 0x20, 0xb3, 0x45, 0xcc, 0x60, 0xee,
+ 0x0f, 0x4e, 0x20, 0x94, 0x3a, 0x2b, 0x2f, 0xcb, 0x23, 0x10, 0x9b, 0x46,
+ 0x1b, 0x7e, 0xc3, 0x56, 0x75, 0x49, 0x24, 0xa4, 0xb8, 0x4d, 0x9f, 0x1c,
+ 0x68, 0xd4, 0xe6, 0xf2, 0x2f, 0xaf, 0x8e, 0xed, 0x2b, 0xb7, 0xe5, 0x96,
+ 0x6b, 0x1c, 0x3d, 0x8d, 0xbf, 0x20, 0xd3, 0x6f, 0x2d, 0x54, 0x2f, 0x9c,
+ 0x79, 0x35, 0xfd, 0xda, 0x06, 0xde, 0x68, 0x20, 0x20, 0x4b, 0xaf, 0x5d,
+ 0xab, 0x5e, 0x66, 0xc3, 0x14, 0x64, 0x7b, 0xf7, 0x02, 0xe6, 0x27, 0x96,
+ 0xad, 0x18, 0x1e, 0xab, 0xf3, 0x82, 0x60, 0xfc, 0x4c, 0x5f, 0xb6, 0x0a,
+ 0x52, 0x7b, 0x9e, 0x9c, 0x3b, 0x2e, 0xce, 0x3c, 0x42, 0x5f, 0x36, 0x6d,
+ 0x6b, 0xfe, 0xa1, 0x76, 0x8a, 0x22, 0x21, 0xfd, 0x5b, 0xe8, 0xbd, 0x7f,
+ 0x9f, 0xce, 0x51, 0x74, 0x48, 0x6c, 0xac, 0xb5, 0xd1, 0xa2, 0x6a, 0xfa,
+ 0x07, 0x44, 0xde, 0xd0, 0xdb, 0xa9, 0x8d, 0x18, 0x1f, 0xf1, 0xb9, 0xc5,
+ 0xe8, 0x2a, 0xeb, 0xba, 0x3d, 0x3b, 0x18, 0x8c, 0xc0, 0x0c, 0x30, 0xb3,
+ 0xc9, 0x21, 0x1c, 0x33, 0x4c, 0x3a, 0x49, 0x53, 0xd4, 0xa8, 0xba, 0xba,
+ 0x38, 0x23, 0x3d, 0x3a, 0x65, 0x82, 0x5e, 0x79, 0x71, 0x15, 0xf8, 0x25,
+ 0x2b, 0x7d, 0x19,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 11:20:96:f6:c8:03:7c:9e:07:b1:38:bf:2e:72:10:8a:d7:ed
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=FR, O=Certplus, CN=Class 2 Primary CA
+ Validity
+ Not Before: Jun 5 00:00:00 2007 GMT
+ Not After : Jun 20 00:00:00 2019 GMT
+ Subject: C=FR, O=KEYNECTIS, CN=CLASS 2 KEYNECTIS CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:be:fe:44:23:04:d4:ef:2f:3b:86:aa:35:58:
+ 81:d1:e1:9a:d6:b1:d4:27:45:28:fc:d1:1e:46:85:
+ ba:54:23:11:7d:e0:66:3f:d4:a3:57:66:78:f9:6b:
+ eb:74:7c:2a:b8:37:a5:e8:70:ae:82:b5:4e:d4:81:
+ fe:5b:e2:ea:e7:22:16:f8:f9:d7:ba:3a:f6:88:56:
+ dc:c4:f2:a0:a4:e5:75:06:60:72:2b:fb:f5:94:ee:
+ 2c:83:28:de:91:9a:b3:83:3a:b0:9f:08:fa:dd:d8:
+ 9e:8c:24:e6:df:66:5b:c8:7e:a3:62:4d:3f:3a:85:
+ 23:ec:e8:71:8f:0a:00:ac:89:6d:7e:d8:72:e5:dd:
+ c1:94:8e:5f:e4:73:e6:c1:c6:0c:87:58:4f:37:da:
+ d1:a9:88:26:76:b4:ee:11:8d:f6:ad:b2:a7:bc:73:
+ c4:cd:1c:6e:1a:e6:8d:72:56:44:a0:98:f7:92:f9:
+ d7:79:9b:03:e6:68:5f:a4:5c:7c:3d:50:b4:83:cc:
+ e5:ac:0d:e1:3e:4f:14:f2:b4:e4:7d:bf:71:a4:c3:
+ 97:73:38:d6:52:7c:c8:a4:b5:ea:e9:b2:54:56:d4:
+ eb:b8:57:3a:40:52:5a:5e:46:27:a3:7b:30:2d:08:
+ 3d:85:1e:9a:f0:32:a8:f2:10:a2:83:9b:e2:28:f6:
+ 9d:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.4.1.22234.2.5.3.3
+ CPS: http://www.keynectis.com/PC
+ Policy: 1.3.6.4.1.22234.2.5.1.3
+ CPS: http://www.keynectis.com/PC
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.certplus.com/CRL/class2.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 00:11:41:DF:3B:9D:3B:CB:B8:A2:C1:33:92:A8:81:CC:E5:7D:E7:99
+ X509v3 Authority Key Identifier:
+ keyid:E3:73:2D:DF:CB:0E:28:0C:DE:DD:B3:A4:CA:79:B8:8E:BB:E8:30:89
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 08:88:fe:1f:a2:ca:cd:e2:a0:f1:2e:7c:67:49:fb:dc:94:ac:
+ 7f:41:0d:78:01:ba:31:f7:9b:fb:31:18:77:2f:66:25:94:b8:
+ 6d:16:74:81:f1:c0:ae:67:c6:14:45:7a:01:d1:13:88:fc:e2:
+ 8d:22:1d:bd:1e:0c:c7:a9:7e:d0:c3:97:f6:37:5b:41:5e:67:
+ 94:8e:ab:69:02:17:18:f5:4d:38:c2:49:28:09:6e:5a:9b:a6:
+ 27:db:c0:5f:8f:44:9c:90:65:99:d8:b3:2e:c1:92:ee:1a:9d:
+ 0f:72:45:20:fa:2c:0c:9c:5d:cd:5b:54:41:54:4f:d3:e2:c7:
+ 59:84:3f:17:7b:7d:0e:c2:ef:62:c7:ba:b1:26:6c:83:4e:d3:
+ 19:c5:ff:56:a7:b4:45:3f:7a:9e:fa:d0:39:3e:80:46:75:5d:
+ 5a:79:7a:33:c5:01:bc:02:44:ce:1b:c0:31:4e:47:96:15:6e:
+ e7:e4:76:f0:c2:90:0d:a1:78:f4:38:00:91:2b:65:7c:79:13:
+ a8:3e:91:14:dc:88:05:08:d7:6f:53:f6:15:43:ee:c5:53:56:
+ 1a:02:b5:a6:a2:46:8d:1e:13:e4:67:c2:45:5f:40:5e:10:42:
+ 58:b5:cd:44:a3:94:4c:1c:54:90:4d:91:9a:26:8b:ad:a2:80:
+ 50:8d:14:14
+-----BEGIN CERTIFICATE-----
+MIIEKzCCAxOgAwIBAgISESCW9sgDfJ4HsTi/LnIQitftMA0GCSqGSIb3DQEBBQUA
+MD0xCzAJBgNVBAYTAkZSMREwDwYDVQQKEwhDZXJ0cGx1czEbMBkGA1UEAxMSQ2xh
+c3MgMiBQcmltYXJ5IENBMB4XDTA3MDYwNTAwMDAwMFoXDTE5MDYyMDAwMDAwMFow
+QDELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUtFWU5FQ1RJUzEdMBsGA1UEAxMUQ0xB
+U1MgMiBLRVlORUNUSVMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDGvv5EIwTU7y87hqo1WIHR4ZrWsdQnRSj80R5GhbpUIxF94GY/1KNXZnj5a+t0
+fCq4N6XocK6CtU7Ugf5b4urnIhb4+de6OvaIVtzE8qCk5XUGYHIr+/WU7iyDKN6R
+mrODOrCfCPrd2J6MJObfZlvIfqNiTT86hSPs6HGPCgCsiW1+2HLl3cGUjl/kc+bB
+xgyHWE832tGpiCZ2tO4Rjfatsqe8c8TNHG4a5o1yVkSgmPeS+dd5mwPmaF+kXHw9
+ULSDzOWsDeE+TxTytOR9v3Gkw5dzONZSfMikterpslRW1Ou4VzpAUlpeRiejezAt
+CD2FHprwMqjyEKKDm+Io9p3LAgMBAAGjggEgMIIBHDASBgNVHRMBAf8ECDAGAQH/
+AgEAMH0GA1UdIAR2MHQwOAYLKwYEAYGtWgIFAwMwKTAnBggrBgEFBQcCARYbaHR0
+cDovL3d3dy5rZXluZWN0aXMuY29tL1BDMDgGCysGBAGBrVoCBQEDMCkwJwYIKwYB
+BQUHAgEWG2h0dHA6Ly93d3cua2V5bmVjdGlzLmNvbS9QQzA3BgNVHR8EMDAuMCyg
+KqAohiZodHRwOi8vd3d3LmNlcnRwbHVzLmNvbS9DUkwvY2xhc3MyLmNybDAOBgNV
+HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAARQd87nTvLuKLBM5KogczlfeeZMB8GA1Ud
+IwQYMBaAFONzLd/LDigM3t2zpMp5uI676DCJMA0GCSqGSIb3DQEBBQUAA4IBAQAI
+iP4fosrN4qDxLnxnSfvclKx/QQ14Abox95v7MRh3L2YllLhtFnSB8cCuZ8YURXoB
+0ROI/OKNIh29HgzHqX7Qw5f2N1tBXmeUjqtpAhcY9U04wkkoCW5am6Yn28Bfj0Sc
+kGWZ2LMuwZLuGp0PckUg+iwMnF3NW1RBVE/T4sdZhD8Xe30Owu9ix7qxJmyDTtMZ
+xf9Wp7RFP3qe+tA5PoBGdV1aeXozxQG8AkTOG8AxTkeWFW7n5HbwwpANoXj0OACR
+K2V8eROoPpEU3IgFCNdvU/YVQ+7FU1YaArWmokaNHhPkZ8JFX0BeEEJYtc1Eo5RM
+HFSQTZGaJoutooBQjRQU
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert14[] = {
+ 0x30, 0x82, 0x04, 0x2b, 0x30, 0x82, 0x03, 0x13, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x12, 0x11, 0x20, 0x96, 0xf6, 0xc8, 0x03, 0x7c, 0x9e, 0x07,
+ 0xb1, 0x38, 0xbf, 0x2e, 0x72, 0x10, 0x8a, 0xd7, 0xed, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x30, 0x3d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x46, 0x52, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x08, 0x43, 0x65, 0x72, 0x74, 0x70, 0x6c, 0x75, 0x73, 0x31, 0x1b,
+ 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x43, 0x6c, 0x61,
+ 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79,
+ 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x36, 0x30,
+ 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39,
+ 0x30, 0x36, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30,
+ 0x40, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x09, 0x4b, 0x45, 0x59, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x53, 0x31, 0x1d,
+ 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x14, 0x43, 0x4c, 0x41,
+ 0x53, 0x53, 0x20, 0x32, 0x20, 0x4b, 0x45, 0x59, 0x4e, 0x45, 0x43, 0x54,
+ 0x49, 0x53, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xc6, 0xbe, 0xfe, 0x44, 0x23, 0x04, 0xd4, 0xef, 0x2f, 0x3b,
+ 0x86, 0xaa, 0x35, 0x58, 0x81, 0xd1, 0xe1, 0x9a, 0xd6, 0xb1, 0xd4, 0x27,
+ 0x45, 0x28, 0xfc, 0xd1, 0x1e, 0x46, 0x85, 0xba, 0x54, 0x23, 0x11, 0x7d,
+ 0xe0, 0x66, 0x3f, 0xd4, 0xa3, 0x57, 0x66, 0x78, 0xf9, 0x6b, 0xeb, 0x74,
+ 0x7c, 0x2a, 0xb8, 0x37, 0xa5, 0xe8, 0x70, 0xae, 0x82, 0xb5, 0x4e, 0xd4,
+ 0x81, 0xfe, 0x5b, 0xe2, 0xea, 0xe7, 0x22, 0x16, 0xf8, 0xf9, 0xd7, 0xba,
+ 0x3a, 0xf6, 0x88, 0x56, 0xdc, 0xc4, 0xf2, 0xa0, 0xa4, 0xe5, 0x75, 0x06,
+ 0x60, 0x72, 0x2b, 0xfb, 0xf5, 0x94, 0xee, 0x2c, 0x83, 0x28, 0xde, 0x91,
+ 0x9a, 0xb3, 0x83, 0x3a, 0xb0, 0x9f, 0x08, 0xfa, 0xdd, 0xd8, 0x9e, 0x8c,
+ 0x24, 0xe6, 0xdf, 0x66, 0x5b, 0xc8, 0x7e, 0xa3, 0x62, 0x4d, 0x3f, 0x3a,
+ 0x85, 0x23, 0xec, 0xe8, 0x71, 0x8f, 0x0a, 0x00, 0xac, 0x89, 0x6d, 0x7e,
+ 0xd8, 0x72, 0xe5, 0xdd, 0xc1, 0x94, 0x8e, 0x5f, 0xe4, 0x73, 0xe6, 0xc1,
+ 0xc6, 0x0c, 0x87, 0x58, 0x4f, 0x37, 0xda, 0xd1, 0xa9, 0x88, 0x26, 0x76,
+ 0xb4, 0xee, 0x11, 0x8d, 0xf6, 0xad, 0xb2, 0xa7, 0xbc, 0x73, 0xc4, 0xcd,
+ 0x1c, 0x6e, 0x1a, 0xe6, 0x8d, 0x72, 0x56, 0x44, 0xa0, 0x98, 0xf7, 0x92,
+ 0xf9, 0xd7, 0x79, 0x9b, 0x03, 0xe6, 0x68, 0x5f, 0xa4, 0x5c, 0x7c, 0x3d,
+ 0x50, 0xb4, 0x83, 0xcc, 0xe5, 0xac, 0x0d, 0xe1, 0x3e, 0x4f, 0x14, 0xf2,
+ 0xb4, 0xe4, 0x7d, 0xbf, 0x71, 0xa4, 0xc3, 0x97, 0x73, 0x38, 0xd6, 0x52,
+ 0x7c, 0xc8, 0xa4, 0xb5, 0xea, 0xe9, 0xb2, 0x54, 0x56, 0xd4, 0xeb, 0xb8,
+ 0x57, 0x3a, 0x40, 0x52, 0x5a, 0x5e, 0x46, 0x27, 0xa3, 0x7b, 0x30, 0x2d,
+ 0x08, 0x3d, 0x85, 0x1e, 0x9a, 0xf0, 0x32, 0xa8, 0xf2, 0x10, 0xa2, 0x83,
+ 0x9b, 0xe2, 0x28, 0xf6, 0x9d, 0xcb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x7d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x76,
+ 0x30, 0x74, 0x30, 0x38, 0x06, 0x0b, 0x2b, 0x06, 0x04, 0x01, 0x81, 0xad,
+ 0x5a, 0x02, 0x05, 0x03, 0x03, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6b, 0x65, 0x79, 0x6e,
+ 0x65, 0x63, 0x74, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x43,
+ 0x30, 0x38, 0x06, 0x0b, 0x2b, 0x06, 0x04, 0x01, 0x81, 0xad, 0x5a, 0x02,
+ 0x05, 0x01, 0x03, 0x30, 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6b, 0x65, 0x79, 0x6e, 0x65, 0x63,
+ 0x74, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x43, 0x30, 0x37,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x30, 0x30, 0x2e, 0x30, 0x2c, 0xa0,
+ 0x2a, 0xa0, 0x28, 0x86, 0x26, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x63, 0x65, 0x72, 0x74, 0x70, 0x6c, 0x75, 0x73,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x63, 0x6c, 0x61,
+ 0x73, 0x73, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x00, 0x11,
+ 0x41, 0xdf, 0x3b, 0x9d, 0x3b, 0xcb, 0xb8, 0xa2, 0xc1, 0x33, 0x92, 0xa8,
+ 0x81, 0xcc, 0xe5, 0x7d, 0xe7, 0x99, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe3, 0x73, 0x2d, 0xdf, 0xcb,
+ 0x0e, 0x28, 0x0c, 0xde, 0xdd, 0xb3, 0xa4, 0xca, 0x79, 0xb8, 0x8e, 0xbb,
+ 0xe8, 0x30, 0x89, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x08,
+ 0x88, 0xfe, 0x1f, 0xa2, 0xca, 0xcd, 0xe2, 0xa0, 0xf1, 0x2e, 0x7c, 0x67,
+ 0x49, 0xfb, 0xdc, 0x94, 0xac, 0x7f, 0x41, 0x0d, 0x78, 0x01, 0xba, 0x31,
+ 0xf7, 0x9b, 0xfb, 0x31, 0x18, 0x77, 0x2f, 0x66, 0x25, 0x94, 0xb8, 0x6d,
+ 0x16, 0x74, 0x81, 0xf1, 0xc0, 0xae, 0x67, 0xc6, 0x14, 0x45, 0x7a, 0x01,
+ 0xd1, 0x13, 0x88, 0xfc, 0xe2, 0x8d, 0x22, 0x1d, 0xbd, 0x1e, 0x0c, 0xc7,
+ 0xa9, 0x7e, 0xd0, 0xc3, 0x97, 0xf6, 0x37, 0x5b, 0x41, 0x5e, 0x67, 0x94,
+ 0x8e, 0xab, 0x69, 0x02, 0x17, 0x18, 0xf5, 0x4d, 0x38, 0xc2, 0x49, 0x28,
+ 0x09, 0x6e, 0x5a, 0x9b, 0xa6, 0x27, 0xdb, 0xc0, 0x5f, 0x8f, 0x44, 0x9c,
+ 0x90, 0x65, 0x99, 0xd8, 0xb3, 0x2e, 0xc1, 0x92, 0xee, 0x1a, 0x9d, 0x0f,
+ 0x72, 0x45, 0x20, 0xfa, 0x2c, 0x0c, 0x9c, 0x5d, 0xcd, 0x5b, 0x54, 0x41,
+ 0x54, 0x4f, 0xd3, 0xe2, 0xc7, 0x59, 0x84, 0x3f, 0x17, 0x7b, 0x7d, 0x0e,
+ 0xc2, 0xef, 0x62, 0xc7, 0xba, 0xb1, 0x26, 0x6c, 0x83, 0x4e, 0xd3, 0x19,
+ 0xc5, 0xff, 0x56, 0xa7, 0xb4, 0x45, 0x3f, 0x7a, 0x9e, 0xfa, 0xd0, 0x39,
+ 0x3e, 0x80, 0x46, 0x75, 0x5d, 0x5a, 0x79, 0x7a, 0x33, 0xc5, 0x01, 0xbc,
+ 0x02, 0x44, 0xce, 0x1b, 0xc0, 0x31, 0x4e, 0x47, 0x96, 0x15, 0x6e, 0xe7,
+ 0xe4, 0x76, 0xf0, 0xc2, 0x90, 0x0d, 0xa1, 0x78, 0xf4, 0x38, 0x00, 0x91,
+ 0x2b, 0x65, 0x7c, 0x79, 0x13, 0xa8, 0x3e, 0x91, 0x14, 0xdc, 0x88, 0x05,
+ 0x08, 0xd7, 0x6f, 0x53, 0xf6, 0x15, 0x43, 0xee, 0xc5, 0x53, 0x56, 0x1a,
+ 0x02, 0xb5, 0xa6, 0xa2, 0x46, 0x8d, 0x1e, 0x13, 0xe4, 0x67, 0xc2, 0x45,
+ 0x5f, 0x40, 0x5e, 0x10, 0x42, 0x58, 0xb5, 0xcd, 0x44, 0xa3, 0x94, 0x4c,
+ 0x1c, 0x54, 0x90, 0x4d, 0x91, 0x9a, 0x26, 0x8b, 0xad, 0xa2, 0x80, 0x50,
+ 0x8d, 0x14, 0x14,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1184831531 (0x469f182b)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
+ Validity
+ Not Before: Nov 26 20:33:13 2009 GMT
+ Not After : Nov 1 04:00:00 2015 GMT
+ Subject: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, CN=USERTrust Legacy Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d9:4d:20:3a:e6:29:30:86:f2:e9:86:89:76:34:
+ 4e:68:1f:96:44:f7:d1:f9:d6:82:4e:a6:38:9e:ee:
+ cb:5b:e1:8e:2e:bd:f2:57:80:fd:c9:3f:fc:90:73:
+ 44:bc:8f:bb:57:5b:e5:2d:1f:14:30:75:36:f5:7f:
+ bc:cf:56:f4:7f:81:ff:ae:91:cd:d8:d2:6a:cb:97:
+ f9:f7:cd:90:6a:45:2d:c4:bb:a4:85:13:68:57:5f:
+ ef:29:ba:2a:ca:ea:f5:cc:a4:04:9b:63:cd:00:eb:
+ fd:ed:8d:dd:23:c6:7b:1e:57:1d:36:7f:1f:08:9a:
+ 0d:61:db:5a:6c:71:02:53:28:c2:fa:8d:fd:ab:bb:
+ b3:f1:8d:74:4b:df:bd:bd:cc:06:93:63:09:95:c2:
+ 10:7a:9d:25:90:32:9d:01:c2:39:53:b0:e0:15:6b:
+ c7:d7:74:e5:a4:22:9b:e4:94:ff:84:91:fb:2d:b3:
+ 19:43:2d:93:0f:9c:12:09:e4:67:b9:27:7a:32:ad:
+ 7a:2a:cc:41:58:c0:6e:59:5f:ee:38:2b:17:22:9c:
+ 89:fa:6e:e7:e5:57:35:f4:5a:ed:92:95:93:2d:f9:
+ cc:24:3f:a5:1c:3d:27:bd:22:03:73:cc:f5:ca:f3:
+ a9:f4:dc:fe:cf:e9:d0:5c:d0:0f:ab:87:fc:83:fd:
+ c8:a9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.1.3.4
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/server1.crl
+
+ X509v3 Subject Key Identifier:
+ AF:A4:40:AF:9F:16:FE:AB:31:FD:FB:D5:97:8B:F5:91:A3:24:86:16
+ X509v3 Authority Key Identifier:
+ keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 33:46:31:c3:2a:b7:b7:41:0e:aa:8e:93:14:2f:78:c3:4a:8e:
+ 16:5a:dc:72:32:94:96:57:9a:ac:bc:55:a8:57:cf:7c:e0:79:
+ 62:ff:31:ee:d5:9c:54:d0:c0:fd:87:e2:15:06:9e:be:a2:4a:
+ d0:82:eb:6e:4a:58:6a:d9:1f:11:c0:c8:e3:9e:e3:d6:c5:4f:
+ f7:ff:c3:ef:36:8a:68:aa:b2:50:92:ab:59:9d:ea:5b:27:1f:
+ 16:a9:3c:45:5f:eb:a5:2a:5d:56:29:8d:3a:14:0d:12:74:71:
+ be:d6:ab:97:de:92:87:61:21:88:7b:41:46:3d:fc:3d:4f:d0:
+ 54:5b
+-----BEGIN CERTIFICATE-----
+MIIELTCCA5agAwIBAgIERp8YKzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
+ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
+ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wOTEx
+MjYyMDMzMTNaFw0xNTExMDEwNDAwMDBaMH8xCzAJBgNVBAYTAlVTMQswCQYDVQQI
+EwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VS
+VFJVU1QgTmV0d29yazEqMCgGA1UEAxMhVVNFUlRydXN0IExlZ2FjeSBTZWN1cmUg
+U2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2U0gOuYp
+MIby6YaJdjROaB+WRPfR+daCTqY4nu7LW+GOLr3yV4D9yT/8kHNEvI+7V1vlLR8U
+MHU29X+8z1b0f4H/rpHN2NJqy5f5982QakUtxLukhRNoV1/vKboqyur1zKQEm2PN
+AOv97Y3dI8Z7HlcdNn8fCJoNYdtabHECUyjC+o39q7uz8Y10S9+9vcwGk2MJlcIQ
+ep0lkDKdAcI5U7DgFWvH13TlpCKb5JT/hJH7LbMZQy2TD5wSCeRnuSd6Mq16KsxB
+WMBuWV/uOCsXIpyJ+m7n5Vc19FrtkpWTLfnMJD+lHD0nvSIDc8z1yvOp9Nz+z+nQ
+XNAPq4f8g/3IqQIDAQABo4HsMIHpMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8E
+CDAGAQH/AgEAMBkGA1UdIAQSMBAwDgYMKwYBBAGyMQECAQMEMDMGCCsGAQUFBwEB
+BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZW50cnVzdC5uZXQwMwYDVR0f
+BCwwKjAooCagJIYiaHR0cDovL2NybC5lbnRydXN0Lm5ldC9zZXJ2ZXIxLmNybDAd
+BgNVHQ4EFgQUr6RAr58W/qsx/fvVl4v1kaMkhhYwHwYDVR0jBBgwFoAU8BdiE1U9
+s/8KAGv7UISX8+1i0BowDQYJKoZIhvcNAQEFBQADgYEAM0Yxwyq3t0EOqo6TFC94
+w0qOFlrccjKUllearLxVqFfPfOB5Yv8x7tWcVNDA/YfiFQaevqJK0ILrbkpYatkf
+EcDI457j1sVP9//D7zaKaKqyUJKrWZ3qWycfFqk8RV/rpSpdVimNOhQNEnRxvtar
+l96Sh2EhiHtBRj38PU/QVFs=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert15[] = {
+ 0x30, 0x82, 0x04, 0x2d, 0x30, 0x82, 0x03, 0x96, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x46, 0x9f, 0x18, 0x2b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xc3, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e,
+ 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c,
+ 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x31, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x31, 0x31,
+ 0x32, 0x36, 0x32, 0x30, 0x33, 0x33, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x31,
+ 0x35, 0x31, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ 0x30, 0x7f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65,
+ 0x20, 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52,
+ 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21,
+ 0x55, 0x53, 0x45, 0x52, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4c, 0x65,
+ 0x67, 0x61, 0x63, 0x79, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd9, 0x4d, 0x20, 0x3a, 0xe6, 0x29,
+ 0x30, 0x86, 0xf2, 0xe9, 0x86, 0x89, 0x76, 0x34, 0x4e, 0x68, 0x1f, 0x96,
+ 0x44, 0xf7, 0xd1, 0xf9, 0xd6, 0x82, 0x4e, 0xa6, 0x38, 0x9e, 0xee, 0xcb,
+ 0x5b, 0xe1, 0x8e, 0x2e, 0xbd, 0xf2, 0x57, 0x80, 0xfd, 0xc9, 0x3f, 0xfc,
+ 0x90, 0x73, 0x44, 0xbc, 0x8f, 0xbb, 0x57, 0x5b, 0xe5, 0x2d, 0x1f, 0x14,
+ 0x30, 0x75, 0x36, 0xf5, 0x7f, 0xbc, 0xcf, 0x56, 0xf4, 0x7f, 0x81, 0xff,
+ 0xae, 0x91, 0xcd, 0xd8, 0xd2, 0x6a, 0xcb, 0x97, 0xf9, 0xf7, 0xcd, 0x90,
+ 0x6a, 0x45, 0x2d, 0xc4, 0xbb, 0xa4, 0x85, 0x13, 0x68, 0x57, 0x5f, 0xef,
+ 0x29, 0xba, 0x2a, 0xca, 0xea, 0xf5, 0xcc, 0xa4, 0x04, 0x9b, 0x63, 0xcd,
+ 0x00, 0xeb, 0xfd, 0xed, 0x8d, 0xdd, 0x23, 0xc6, 0x7b, 0x1e, 0x57, 0x1d,
+ 0x36, 0x7f, 0x1f, 0x08, 0x9a, 0x0d, 0x61, 0xdb, 0x5a, 0x6c, 0x71, 0x02,
+ 0x53, 0x28, 0xc2, 0xfa, 0x8d, 0xfd, 0xab, 0xbb, 0xb3, 0xf1, 0x8d, 0x74,
+ 0x4b, 0xdf, 0xbd, 0xbd, 0xcc, 0x06, 0x93, 0x63, 0x09, 0x95, 0xc2, 0x10,
+ 0x7a, 0x9d, 0x25, 0x90, 0x32, 0x9d, 0x01, 0xc2, 0x39, 0x53, 0xb0, 0xe0,
+ 0x15, 0x6b, 0xc7, 0xd7, 0x74, 0xe5, 0xa4, 0x22, 0x9b, 0xe4, 0x94, 0xff,
+ 0x84, 0x91, 0xfb, 0x2d, 0xb3, 0x19, 0x43, 0x2d, 0x93, 0x0f, 0x9c, 0x12,
+ 0x09, 0xe4, 0x67, 0xb9, 0x27, 0x7a, 0x32, 0xad, 0x7a, 0x2a, 0xcc, 0x41,
+ 0x58, 0xc0, 0x6e, 0x59, 0x5f, 0xee, 0x38, 0x2b, 0x17, 0x22, 0x9c, 0x89,
+ 0xfa, 0x6e, 0xe7, 0xe5, 0x57, 0x35, 0xf4, 0x5a, 0xed, 0x92, 0x95, 0x93,
+ 0x2d, 0xf9, 0xcc, 0x24, 0x3f, 0xa5, 0x1c, 0x3d, 0x27, 0xbd, 0x22, 0x03,
+ 0x73, 0xcc, 0xf5, 0xca, 0xf3, 0xa9, 0xf4, 0xdc, 0xfe, 0xcf, 0xe9, 0xd0,
+ 0x5c, 0xd0, 0x0f, 0xab, 0x87, 0xfc, 0x83, 0xfd, 0xc8, 0xa9, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xec, 0x30, 0x81, 0xe9, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x19, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x12, 0x30, 0x10, 0x30, 0x0e, 0x06, 0x0c,
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x01, 0x03, 0x04,
+ 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xaf, 0xa4, 0x40,
+ 0xaf, 0x9f, 0x16, 0xfe, 0xab, 0x31, 0xfd, 0xfb, 0xd5, 0x97, 0x8b, 0xf5,
+ 0x91, 0xa3, 0x24, 0x86, 0x16, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xf0, 0x17, 0x62, 0x13, 0x55, 0x3d,
+ 0xb3, 0xff, 0x0a, 0x00, 0x6b, 0xfb, 0x50, 0x84, 0x97, 0xf3, 0xed, 0x62,
+ 0xd0, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x33, 0x46, 0x31,
+ 0xc3, 0x2a, 0xb7, 0xb7, 0x41, 0x0e, 0xaa, 0x8e, 0x93, 0x14, 0x2f, 0x78,
+ 0xc3, 0x4a, 0x8e, 0x16, 0x5a, 0xdc, 0x72, 0x32, 0x94, 0x96, 0x57, 0x9a,
+ 0xac, 0xbc, 0x55, 0xa8, 0x57, 0xcf, 0x7c, 0xe0, 0x79, 0x62, 0xff, 0x31,
+ 0xee, 0xd5, 0x9c, 0x54, 0xd0, 0xc0, 0xfd, 0x87, 0xe2, 0x15, 0x06, 0x9e,
+ 0xbe, 0xa2, 0x4a, 0xd0, 0x82, 0xeb, 0x6e, 0x4a, 0x58, 0x6a, 0xd9, 0x1f,
+ 0x11, 0xc0, 0xc8, 0xe3, 0x9e, 0xe3, 0xd6, 0xc5, 0x4f, 0xf7, 0xff, 0xc3,
+ 0xef, 0x36, 0x8a, 0x68, 0xaa, 0xb2, 0x50, 0x92, 0xab, 0x59, 0x9d, 0xea,
+ 0x5b, 0x27, 0x1f, 0x16, 0xa9, 0x3c, 0x45, 0x5f, 0xeb, 0xa5, 0x2a, 0x5d,
+ 0x56, 0x29, 0x8d, 0x3a, 0x14, 0x0d, 0x12, 0x74, 0x71, 0xbe, 0xd6, 0xab,
+ 0x97, 0xde, 0x92, 0x87, 0x61, 0x21, 0x88, 0x7b, 0x41, 0x46, 0x3d, 0xfc,
+ 0x3d, 0x4f, 0xd0, 0x54, 0x5b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:37:02
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: O=AlphaSSL, CN=AlphaSSL CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c3:f0:65:88:df:1b:dd:c6:82:87:2f:c9:0b:ba:
+ 54:c6:63:3f:46:75:ac:4b:14:1f:98:72:8b:1c:10:
+ ff:09:a9:52:6e:2f:65:df:65:84:3f:5f:81:b2:d8:
+ f1:4f:d7:f0:5a:bb:c9:af:d0:31:dd:26:46:2a:99:
+ 9e:d8:a9:a3:b6:b8:07:c4:c9:71:f7:95:84:ef:d2:
+ ea:1f:54:a0:e5:be:e4:41:21:56:31:10:64:7d:1e:
+ 63:8e:9c:71:5c:3c:a0:2e:de:67:dc:c8:9a:20:f0:
+ 75:c8:b0:b6:27:81:eb:97:0d:ee:22:45:a5:c2:2f:
+ 34:27:ec:e0:59:12:51:b3:1e:05:e5:38:20:d2:69:
+ 59:7a:59:17:be:1a:4b:39:08:12:79:33:9b:64:68:
+ fe:58:81:dd:88:0c:6a:ba:59:b4:af:24:4f:61:e0:
+ ca:fc:17:5a:d2:3c:72:ab:a7:4c:b7:b9:ea:2d:e3:
+ f4:3f:99:a2:4d:c8:1d:58:f8:7f:53:35:8e:d7:22:
+ 88:b7:61:76:08:13:13:69:66:b0:57:59:13:31:0a:
+ 70:82:2b:93:d7:f6:e2:40:15:d0:1d:01:72:c7:13:
+ 58:6a:5a:ec:19:89:16:3c:e0:c8:8d:86:2a:fa:37:
+ f0:35:32:dd:ec:e5:fe:80:8e:f7:05:67:b4:8b:42:
+ 75:35
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 14:EA:19:55:F0:0E:0D:32:C6:1F:74:33:B7:8E:66:1A:4C:12:31:1E
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.alphassl.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 06:30:42:9b:cf:49:02:7e:89:e9:f5:83:5a:3d:02:f3:bc:b2:
+ 46:de:4a:50:ee:b9:9a:90:73:da:a0:5c:26:ca:82:ac:0e:ad:
+ b3:94:fa:28:2e:b2:e6:49:3f:50:77:0e:95:2f:68:f3:65:3c:
+ 9f:14:f2:68:60:92:b6:fc:04:0d:f6:a4:18:a1:69:60:0d:e3:
+ 9d:68:5b:bc:9e:0b:38:59:8d:21:da:23:fa:99:8a:09:b9:1f:
+ a7:2e:b5:55:6c:47:e7:41:ec:e6:e2:7f:af:55:44:39:e0:ac:
+ 74:ee:65:d3:fa:ab:51:48:30:f1:3e:77:6d:ed:e4:0f:40:98:
+ ee:47:7f:8d:b6:58:27:cd:92:6f:60:23:cc:02:9b:59:28:78:
+ a2:51:9d:d0:4a:9c:e5:93:5e:98:8f:cb:ef:3f:ca:fe:e0:af:
+ a4:c9:5b:6e:40:58:a5:92:2d:bd:5d:65:55:c5:bf:7c:04:41:
+ d9:a4:b5:80:e9:94:60:02:10:38:6a:08:08:d7:53:1c:2d:93:
+ af:c9:13:7b:d4:6c:c4:3a:c4:fb:80:ac:bb:3a:4e:54:7a:cd:
+ 4e:b3:3e:ed:f1:fc:11:4e:9f:f5:f3:14:bc:b9:b1:31:ce:f6:
+ aa:2f:a5:f8:c3:e9:66:a9:b2:20:9d:c4:f8:b8:03:62:a7:85:
+ d1:18:63:5b
+-----BEGIN CERTIFICATE-----
+MIIELzCCAxegAwIBAgILBAAAAAABL07hNwIwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
+MDBaFw0yMjA0MTMxMDAwMDBaMC4xETAPBgNVBAoTCEFscGhhU1NMMRkwFwYDVQQD
+ExBBbHBoYVNTTCBDQSAtIEcyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAw/BliN8b3caChy/JC7pUxmM/RnWsSxQfmHKLHBD/CalSbi9l32WEP1+Bstjx
+T9fwWrvJr9Ax3SZGKpme2KmjtrgHxMlx95WE79LqH1Sg5b7kQSFWMRBkfR5jjpxx
+XDygLt5n3MiaIPB1yLC2J4Hrlw3uIkWlwi80J+zgWRJRsx4F5Tgg0mlZelkXvhpL
+OQgSeTObZGj+WIHdiAxqulm0ryRPYeDK/Bda0jxyq6dMt7nqLeP0P5miTcgdWPh/
+UzWO1yKIt2F2CBMTaWawV1kTMQpwgiuT1/biQBXQHQFyxxNYalrsGYkWPODIjYYq
++jfwNTLd7OX+gI73BWe0i0J1NQIDAQABo4IBIzCCAR8wDgYDVR0PAQH/BAQDAgEG
+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFBTqGVXwDg0yxh90M7eOZhpM
+EjEeMEUGA1UdIAQ+MDwwOgYEVR0gADAyMDAGCCsGAQUFBwIBFiRodHRwczovL3d3
+dy5hbHBoYXNzbC5jb20vcmVwb3NpdG9yeS8wMwYDVR0fBCwwKjAooCagJIYiaHR0
+cDovL2NybC5nbG9iYWxzaWduLm5ldC9yb290LmNybDA9BggrBgEFBQcBAQQxMC8w
+LQYIKwYBBQUHMAGGIWh0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29tL3Jvb3RyMTAf
+BgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUFAAOC
+AQEABjBCm89JAn6J6fWDWj0C87yyRt5KUO65mpBz2qBcJsqCrA6ts5T6KC6y5kk/
+UHcOlS9o82U8nxTyaGCStvwEDfakGKFpYA3jnWhbvJ4LOFmNIdoj+pmKCbkfpy61
+VWxH50Hs5uJ/r1VEOeCsdO5l0/qrUUgw8T53be3kD0CY7kd/jbZYJ82Sb2AjzAKb
+WSh4olGd0Eqc5ZNemI/L7z/K/uCvpMlbbkBYpZItvV1lVcW/fARB2aS1gOmUYAIQ
+OGoICNdTHC2Tr8kTe9RsxDrE+4CsuzpOVHrNTrM+7fH8EU6f9fMUvLmxMc72qi+l
++MPpZqmyIJ3E+LgDYqeF0RhjWw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert16[] = {
+ 0x30, 0x82, 0x04, 0x2f, 0x30, 0x82, 0x03, 0x17, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x37, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x2e, 0x31, 0x11, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x08, 0x41, 0x6c, 0x70, 0x68, 0x61,
+ 0x53, 0x53, 0x4c, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x10, 0x41, 0x6c, 0x70, 0x68, 0x61, 0x53, 0x53, 0x4c, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xc3, 0xf0, 0x65, 0x88, 0xdf, 0x1b, 0xdd, 0xc6, 0x82,
+ 0x87, 0x2f, 0xc9, 0x0b, 0xba, 0x54, 0xc6, 0x63, 0x3f, 0x46, 0x75, 0xac,
+ 0x4b, 0x14, 0x1f, 0x98, 0x72, 0x8b, 0x1c, 0x10, 0xff, 0x09, 0xa9, 0x52,
+ 0x6e, 0x2f, 0x65, 0xdf, 0x65, 0x84, 0x3f, 0x5f, 0x81, 0xb2, 0xd8, 0xf1,
+ 0x4f, 0xd7, 0xf0, 0x5a, 0xbb, 0xc9, 0xaf, 0xd0, 0x31, 0xdd, 0x26, 0x46,
+ 0x2a, 0x99, 0x9e, 0xd8, 0xa9, 0xa3, 0xb6, 0xb8, 0x07, 0xc4, 0xc9, 0x71,
+ 0xf7, 0x95, 0x84, 0xef, 0xd2, 0xea, 0x1f, 0x54, 0xa0, 0xe5, 0xbe, 0xe4,
+ 0x41, 0x21, 0x56, 0x31, 0x10, 0x64, 0x7d, 0x1e, 0x63, 0x8e, 0x9c, 0x71,
+ 0x5c, 0x3c, 0xa0, 0x2e, 0xde, 0x67, 0xdc, 0xc8, 0x9a, 0x20, 0xf0, 0x75,
+ 0xc8, 0xb0, 0xb6, 0x27, 0x81, 0xeb, 0x97, 0x0d, 0xee, 0x22, 0x45, 0xa5,
+ 0xc2, 0x2f, 0x34, 0x27, 0xec, 0xe0, 0x59, 0x12, 0x51, 0xb3, 0x1e, 0x05,
+ 0xe5, 0x38, 0x20, 0xd2, 0x69, 0x59, 0x7a, 0x59, 0x17, 0xbe, 0x1a, 0x4b,
+ 0x39, 0x08, 0x12, 0x79, 0x33, 0x9b, 0x64, 0x68, 0xfe, 0x58, 0x81, 0xdd,
+ 0x88, 0x0c, 0x6a, 0xba, 0x59, 0xb4, 0xaf, 0x24, 0x4f, 0x61, 0xe0, 0xca,
+ 0xfc, 0x17, 0x5a, 0xd2, 0x3c, 0x72, 0xab, 0xa7, 0x4c, 0xb7, 0xb9, 0xea,
+ 0x2d, 0xe3, 0xf4, 0x3f, 0x99, 0xa2, 0x4d, 0xc8, 0x1d, 0x58, 0xf8, 0x7f,
+ 0x53, 0x35, 0x8e, 0xd7, 0x22, 0x88, 0xb7, 0x61, 0x76, 0x08, 0x13, 0x13,
+ 0x69, 0x66, 0xb0, 0x57, 0x59, 0x13, 0x31, 0x0a, 0x70, 0x82, 0x2b, 0x93,
+ 0xd7, 0xf6, 0xe2, 0x40, 0x15, 0xd0, 0x1d, 0x01, 0x72, 0xc7, 0x13, 0x58,
+ 0x6a, 0x5a, 0xec, 0x19, 0x89, 0x16, 0x3c, 0xe0, 0xc8, 0x8d, 0x86, 0x2a,
+ 0xfa, 0x37, 0xf0, 0x35, 0x32, 0xdd, 0xec, 0xe5, 0xfe, 0x80, 0x8e, 0xf7,
+ 0x05, 0x67, 0xb4, 0x8b, 0x42, 0x75, 0x35, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x23, 0x30, 0x82, 0x01, 0x1f, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x14, 0xea, 0x19, 0x55, 0xf0,
+ 0x0e, 0x0d, 0x32, 0xc6, 0x1f, 0x74, 0x33, 0xb7, 0x8e, 0x66, 0x1a, 0x4c,
+ 0x12, 0x31, 0x1e, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3e,
+ 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x32,
+ 0x30, 0x30, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x24, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30,
+ 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30,
+ 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60,
+ 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04,
+ 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x06, 0x30, 0x42, 0x9b, 0xcf, 0x49, 0x02, 0x7e, 0x89,
+ 0xe9, 0xf5, 0x83, 0x5a, 0x3d, 0x02, 0xf3, 0xbc, 0xb2, 0x46, 0xde, 0x4a,
+ 0x50, 0xee, 0xb9, 0x9a, 0x90, 0x73, 0xda, 0xa0, 0x5c, 0x26, 0xca, 0x82,
+ 0xac, 0x0e, 0xad, 0xb3, 0x94, 0xfa, 0x28, 0x2e, 0xb2, 0xe6, 0x49, 0x3f,
+ 0x50, 0x77, 0x0e, 0x95, 0x2f, 0x68, 0xf3, 0x65, 0x3c, 0x9f, 0x14, 0xf2,
+ 0x68, 0x60, 0x92, 0xb6, 0xfc, 0x04, 0x0d, 0xf6, 0xa4, 0x18, 0xa1, 0x69,
+ 0x60, 0x0d, 0xe3, 0x9d, 0x68, 0x5b, 0xbc, 0x9e, 0x0b, 0x38, 0x59, 0x8d,
+ 0x21, 0xda, 0x23, 0xfa, 0x99, 0x8a, 0x09, 0xb9, 0x1f, 0xa7, 0x2e, 0xb5,
+ 0x55, 0x6c, 0x47, 0xe7, 0x41, 0xec, 0xe6, 0xe2, 0x7f, 0xaf, 0x55, 0x44,
+ 0x39, 0xe0, 0xac, 0x74, 0xee, 0x65, 0xd3, 0xfa, 0xab, 0x51, 0x48, 0x30,
+ 0xf1, 0x3e, 0x77, 0x6d, 0xed, 0xe4, 0x0f, 0x40, 0x98, 0xee, 0x47, 0x7f,
+ 0x8d, 0xb6, 0x58, 0x27, 0xcd, 0x92, 0x6f, 0x60, 0x23, 0xcc, 0x02, 0x9b,
+ 0x59, 0x28, 0x78, 0xa2, 0x51, 0x9d, 0xd0, 0x4a, 0x9c, 0xe5, 0x93, 0x5e,
+ 0x98, 0x8f, 0xcb, 0xef, 0x3f, 0xca, 0xfe, 0xe0, 0xaf, 0xa4, 0xc9, 0x5b,
+ 0x6e, 0x40, 0x58, 0xa5, 0x92, 0x2d, 0xbd, 0x5d, 0x65, 0x55, 0xc5, 0xbf,
+ 0x7c, 0x04, 0x41, 0xd9, 0xa4, 0xb5, 0x80, 0xe9, 0x94, 0x60, 0x02, 0x10,
+ 0x38, 0x6a, 0x08, 0x08, 0xd7, 0x53, 0x1c, 0x2d, 0x93, 0xaf, 0xc9, 0x13,
+ 0x7b, 0xd4, 0x6c, 0xc4, 0x3a, 0xc4, 0xfb, 0x80, 0xac, 0xbb, 0x3a, 0x4e,
+ 0x54, 0x7a, 0xcd, 0x4e, 0xb3, 0x3e, 0xed, 0xf1, 0xfc, 0x11, 0x4e, 0x9f,
+ 0xf5, 0xf3, 0x14, 0xbc, 0xb9, 0xb1, 0x31, 0xce, 0xf6, 0xaa, 0x2f, 0xa5,
+ 0xf8, 0xc3, 0xe9, 0x66, 0xa9, 0xb2, 0x20, 0x9d, 0xc4, 0xf8, 0xb8, 0x03,
+ 0x62, 0xa7, 0x85, 0xd1, 0x18, 0x63, 0x5b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:1e:44:a5:f1:71
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 11 12:00:00 2007 GMT
+ Not After : Apr 11 12:00:00 2017 GMT
+ Subject: OU=Alpha CA, O=Alpha, CN=Alpha CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bb:32:2e:2b:13:fd:f4:24:8a:5b:fa:6b:cd:ab:
+ 9c:b2:b6:0b:89:6a:e6:1f:41:ce:8a:24:42:ff:5c:
+ af:5f:90:3d:f5:90:b9:73:c7:70:b4:c2:ca:38:d6:
+ ad:06:15:1a:80:38:1d:79:2b:a5:43:20:e7:b9:fa:
+ 8d:06:22:57:0d:64:b5:d1:4d:ed:24:38:49:b4:6a:
+ 07:d4:33:db:3b:76:38:3f:af:77:69:ef:7a:13:33:
+ 29:7b:40:84:90:35:78:5a:8f:23:29:57:6f:b0:57:
+ ab:37:99:94:28:cf:d3:c7:57:a5:96:b1:8a:81:2e:
+ 73:80:bd:68:ec:1b:11:17:8a:1e:d8:94:77:4b:76:
+ 91:ea:b4:cc:16:33:03:62:b8:06:1a:65:69:be:ac:
+ d6:97:1b:a7:b1:27:a1:c0:25:52:2f:49:bc:da:04:
+ 06:ba:b8:b5:a6:a8:e1:cb:25:87:b6:28:d4:89:6b:
+ 34:01:77:1a:b6:ec:de:59:dc:99:bb:5d:dc:8f:84:
+ c2:b9:62:03:13:63:02:09:9e:e1:09:c8:be:f1:18:
+ 79:71:6d:c9:d0:b5:42:97:ca:f8:34:4d:92:87:c0:
+ 39:fa:5c:21:3d:94:52:04:5a:83:a9:d4:ab:83:05:
+ 28:d8:17:23:24:83:64:9b:21:2f:f8:3b:2b:78:64:
+ 87:93
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 0A:29:FA:AD:AF:4D:FD:FD:5D:7D:76:26:87:AB:AA:5A:AA:74:22:15
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.4146.1.10.10
+ CPS: http://www.alphassl.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Netscape Cert Type:
+ SSL CA
+ X509v3 Extended Key Usage:
+ Microsoft Server Gated Crypto, Netscape Server Gated Crypto
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 71:13:44:fe:0b:ce:87:60:b5:26:7d:a2:b7:75:63:6a:72:43:
+ 70:40:29:ad:0a:49:c6:88:f7:36:cc:d7:85:93:c2:0a:9a:ff:
+ d7:c9:2e:2d:3b:fa:96:39:2f:7b:f3:1e:6e:82:11:6f:d1:20:
+ c9:f8:5d:54:2f:16:28:42:3a:cc:2c:9f:48:87:a3:ba:76:a4:
+ 11:f9:8f:b3:a6:f3:86:3d:62:74:40:e4:2d:94:cd:65:ab:1b:
+ 41:09:8b:f6:db:fd:d3:02:3f:7c:c5:a1:25:a8:d2:95:50:df:
+ 21:2a:71:3b:b1:67:b7:b2:ec:1f:0e:c1:fe:3f:32:3d:3d:87:
+ 68:3a:25:f6:21:9c:5f:5c:9a:1c:10:cc:48:8d:83:76:78:39:
+ 57:67:ea:64:7e:71:1d:8e:40:e6:a5:ab:64:32:f7:83:c7:7b:
+ bd:a4:de:dc:83:13:a4:a2:8c:f3:2a:76:e9:1a:70:4a:51:17:
+ b7:6c:26:df:ee:05:c7:4e:5b:da:36:54:a1:49:79:f6:4a:06:
+ 0a:e3:01:ea:fe:48:73:0b:3d:9c:b8:28:81:f0:b4:a5:c8:62:
+ 9a:11:28:cd:18:d1:07:23:d2:ba:ee:14:db:87:64:ed:2b:aa:
+ 7f:1a:bd:0a:77:14:d5:d5:cc:31:12:a2:ef:06:a3:17:c1:e0:
+ 18:ab:c7:53
+-----BEGIN CERTIFICATE-----
+MIIEMjCCAxqgAwIBAgILBAAAAAABHkSl8XEwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0wNzA0MTExMjAw
+MDBaFw0xNzA0MTExMjAwMDBaMDYxETAPBgNVBAsTCEFscGhhIENBMQ4wDAYDVQQK
+EwVBbHBoYTERMA8GA1UEAxMIQWxwaGEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQC7Mi4rE/30JIpb+mvNq5yytguJauYfQc6KJEL/XK9fkD31kLlz
+x3C0wso41q0GFRqAOB15K6VDIOe5+o0GIlcNZLXRTe0kOEm0agfUM9s7djg/r3dp
+73oTMyl7QISQNXhajyMpV2+wV6s3mZQoz9PHV6WWsYqBLnOAvWjsGxEXih7YlHdL
+dpHqtMwWMwNiuAYaZWm+rNaXG6exJ6HAJVIvSbzaBAa6uLWmqOHLJYe2KNSJazQB
+dxq27N5Z3Jm7XdyPhMK5YgMTYwIJnuEJyL7xGHlxbcnQtUKXyvg0TZKHwDn6XCE9
+lFIEWoOp1KuDBSjYFyMkg2SbIS/4Oyt4ZIeTAgMBAAGjggEeMIIBGjAOBgNVHQ8B
+Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUCin6ra9N/f1d
+fXYmh6uqWqp0IhUwSgYDVR0gBEMwQTA/BgorBgEEAaAyAQoKMDEwLwYIKwYBBQUH
+AgEWI2h0dHA6Ly93d3cuYWxwaGFzc2wuY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQs
+MCowKKAmoCSGImh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwEQYJ
+YIZIAYb4QgEBBAQDAgIEMCAGA1UdJQQZMBcGCisGAQQBgjcKAwMGCWCGSAGG+EIE
+ATAfBgNVHSMEGDAWgBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
+AAOCAQEAcRNE/gvOh2C1Jn2it3VjanJDcEAprQpJxoj3NszXhZPCCpr/18kuLTv6
+ljkve/MeboIRb9EgyfhdVC8WKEI6zCyfSIejunakEfmPs6bzhj1idEDkLZTNZasb
+QQmL9tv90wI/fMWhJajSlVDfISpxO7Fnt7LsHw7B/j8yPT2HaDol9iGcX1yaHBDM
+SI2Ddng5V2fqZH5xHY5A5qWrZDL3g8d7vaTe3IMTpKKM8yp26RpwSlEXt2wm3+4F
+x05b2jZUoUl59koGCuMB6v5Icws9nLgogfC0pchimhEozRjRByPSuu4U24dk7Suq
+fxq9CncU1dXMMRKi7wajF8HgGKvHUw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert17[] = {
+ 0x30, 0x82, 0x04, 0x32, 0x30, 0x82, 0x03, 0x1a, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1e, 0x44, 0xa5,
+ 0xf1, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x37, 0x30, 0x34, 0x31, 0x31, 0x31, 0x32, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x31, 0x31, 0x31,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x36, 0x31, 0x11, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x08, 0x41, 0x6c, 0x70, 0x68, 0x61,
+ 0x20, 0x43, 0x41, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x05, 0x41, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x11, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x08, 0x41, 0x6c, 0x70, 0x68, 0x61, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbb,
+ 0x32, 0x2e, 0x2b, 0x13, 0xfd, 0xf4, 0x24, 0x8a, 0x5b, 0xfa, 0x6b, 0xcd,
+ 0xab, 0x9c, 0xb2, 0xb6, 0x0b, 0x89, 0x6a, 0xe6, 0x1f, 0x41, 0xce, 0x8a,
+ 0x24, 0x42, 0xff, 0x5c, 0xaf, 0x5f, 0x90, 0x3d, 0xf5, 0x90, 0xb9, 0x73,
+ 0xc7, 0x70, 0xb4, 0xc2, 0xca, 0x38, 0xd6, 0xad, 0x06, 0x15, 0x1a, 0x80,
+ 0x38, 0x1d, 0x79, 0x2b, 0xa5, 0x43, 0x20, 0xe7, 0xb9, 0xfa, 0x8d, 0x06,
+ 0x22, 0x57, 0x0d, 0x64, 0xb5, 0xd1, 0x4d, 0xed, 0x24, 0x38, 0x49, 0xb4,
+ 0x6a, 0x07, 0xd4, 0x33, 0xdb, 0x3b, 0x76, 0x38, 0x3f, 0xaf, 0x77, 0x69,
+ 0xef, 0x7a, 0x13, 0x33, 0x29, 0x7b, 0x40, 0x84, 0x90, 0x35, 0x78, 0x5a,
+ 0x8f, 0x23, 0x29, 0x57, 0x6f, 0xb0, 0x57, 0xab, 0x37, 0x99, 0x94, 0x28,
+ 0xcf, 0xd3, 0xc7, 0x57, 0xa5, 0x96, 0xb1, 0x8a, 0x81, 0x2e, 0x73, 0x80,
+ 0xbd, 0x68, 0xec, 0x1b, 0x11, 0x17, 0x8a, 0x1e, 0xd8, 0x94, 0x77, 0x4b,
+ 0x76, 0x91, 0xea, 0xb4, 0xcc, 0x16, 0x33, 0x03, 0x62, 0xb8, 0x06, 0x1a,
+ 0x65, 0x69, 0xbe, 0xac, 0xd6, 0x97, 0x1b, 0xa7, 0xb1, 0x27, 0xa1, 0xc0,
+ 0x25, 0x52, 0x2f, 0x49, 0xbc, 0xda, 0x04, 0x06, 0xba, 0xb8, 0xb5, 0xa6,
+ 0xa8, 0xe1, 0xcb, 0x25, 0x87, 0xb6, 0x28, 0xd4, 0x89, 0x6b, 0x34, 0x01,
+ 0x77, 0x1a, 0xb6, 0xec, 0xde, 0x59, 0xdc, 0x99, 0xbb, 0x5d, 0xdc, 0x8f,
+ 0x84, 0xc2, 0xb9, 0x62, 0x03, 0x13, 0x63, 0x02, 0x09, 0x9e, 0xe1, 0x09,
+ 0xc8, 0xbe, 0xf1, 0x18, 0x79, 0x71, 0x6d, 0xc9, 0xd0, 0xb5, 0x42, 0x97,
+ 0xca, 0xf8, 0x34, 0x4d, 0x92, 0x87, 0xc0, 0x39, 0xfa, 0x5c, 0x21, 0x3d,
+ 0x94, 0x52, 0x04, 0x5a, 0x83, 0xa9, 0xd4, 0xab, 0x83, 0x05, 0x28, 0xd8,
+ 0x17, 0x23, 0x24, 0x83, 0x64, 0x9b, 0x21, 0x2f, 0xf8, 0x3b, 0x2b, 0x78,
+ 0x64, 0x87, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1e,
+ 0x30, 0x82, 0x01, 0x1a, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x0a, 0x29, 0xfa, 0xad, 0xaf, 0x4d, 0xfd, 0xfd, 0x5d,
+ 0x7d, 0x76, 0x26, 0x87, 0xab, 0xaa, 0x5a, 0xaa, 0x74, 0x22, 0x15, 0x30,
+ 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f,
+ 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0, 0x32, 0x01, 0x0a, 0x0a,
+ 0x30, 0x31, 0x30, 0x2f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c,
+ 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x11, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03,
+ 0x02, 0x02, 0x04, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x19,
+ 0x30, 0x17, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a,
+ 0x03, 0x03, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04,
+ 0x01, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50,
+ 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x71, 0x13, 0x44, 0xfe, 0x0b, 0xce,
+ 0x87, 0x60, 0xb5, 0x26, 0x7d, 0xa2, 0xb7, 0x75, 0x63, 0x6a, 0x72, 0x43,
+ 0x70, 0x40, 0x29, 0xad, 0x0a, 0x49, 0xc6, 0x88, 0xf7, 0x36, 0xcc, 0xd7,
+ 0x85, 0x93, 0xc2, 0x0a, 0x9a, 0xff, 0xd7, 0xc9, 0x2e, 0x2d, 0x3b, 0xfa,
+ 0x96, 0x39, 0x2f, 0x7b, 0xf3, 0x1e, 0x6e, 0x82, 0x11, 0x6f, 0xd1, 0x20,
+ 0xc9, 0xf8, 0x5d, 0x54, 0x2f, 0x16, 0x28, 0x42, 0x3a, 0xcc, 0x2c, 0x9f,
+ 0x48, 0x87, 0xa3, 0xba, 0x76, 0xa4, 0x11, 0xf9, 0x8f, 0xb3, 0xa6, 0xf3,
+ 0x86, 0x3d, 0x62, 0x74, 0x40, 0xe4, 0x2d, 0x94, 0xcd, 0x65, 0xab, 0x1b,
+ 0x41, 0x09, 0x8b, 0xf6, 0xdb, 0xfd, 0xd3, 0x02, 0x3f, 0x7c, 0xc5, 0xa1,
+ 0x25, 0xa8, 0xd2, 0x95, 0x50, 0xdf, 0x21, 0x2a, 0x71, 0x3b, 0xb1, 0x67,
+ 0xb7, 0xb2, 0xec, 0x1f, 0x0e, 0xc1, 0xfe, 0x3f, 0x32, 0x3d, 0x3d, 0x87,
+ 0x68, 0x3a, 0x25, 0xf6, 0x21, 0x9c, 0x5f, 0x5c, 0x9a, 0x1c, 0x10, 0xcc,
+ 0x48, 0x8d, 0x83, 0x76, 0x78, 0x39, 0x57, 0x67, 0xea, 0x64, 0x7e, 0x71,
+ 0x1d, 0x8e, 0x40, 0xe6, 0xa5, 0xab, 0x64, 0x32, 0xf7, 0x83, 0xc7, 0x7b,
+ 0xbd, 0xa4, 0xde, 0xdc, 0x83, 0x13, 0xa4, 0xa2, 0x8c, 0xf3, 0x2a, 0x76,
+ 0xe9, 0x1a, 0x70, 0x4a, 0x51, 0x17, 0xb7, 0x6c, 0x26, 0xdf, 0xee, 0x05,
+ 0xc7, 0x4e, 0x5b, 0xda, 0x36, 0x54, 0xa1, 0x49, 0x79, 0xf6, 0x4a, 0x06,
+ 0x0a, 0xe3, 0x01, 0xea, 0xfe, 0x48, 0x73, 0x0b, 0x3d, 0x9c, 0xb8, 0x28,
+ 0x81, 0xf0, 0xb4, 0xa5, 0xc8, 0x62, 0x9a, 0x11, 0x28, 0xcd, 0x18, 0xd1,
+ 0x07, 0x23, 0xd2, 0xba, 0xee, 0x14, 0xdb, 0x87, 0x64, 0xed, 0x2b, 0xaa,
+ 0x7f, 0x1a, 0xbd, 0x0a, 0x77, 0x14, 0xd5, 0xd5, 0xcc, 0x31, 0x12, 0xa2,
+ 0xef, 0x06, 0xa3, 0x17, 0xc1, 0xe0, 0x18, 0xab, 0xc7, 0x53,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120020006 (0x7275c26)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Aug 18 18:36:33 2011 GMT
+ Not After : Aug 9 18:35:49 2018 GMT
+ Subject: C=JP, O=Cybertrust Japan Co., Ltd., CN=Cybertrust Japan Public CA G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:dc:76:fb:b9:44:fb:12:7c:54:b0:bb:41:75:
+ 74:f0:35:47:9e:27:ba:43:44:45:60:19:cd:44:0a:
+ b2:78:e5:fa:1e:24:3b:02:1f:68:77:60:f4:eb:22:
+ 05:0a:37:6e:db:f7:15:20:bb:3d:53:d4:d0:11:8e:
+ d4:eb:67:be:d8:dd:05:37:94:41:25:23:ef:9a:10:
+ a3:c3:f7:00:bd:ba:26:92:67:81:50:a9:4f:9e:91:
+ 3c:4e:20:ba:52:7f:ff:4c:4d:8e:aa:ad:4e:5f:bc:
+ 27:97:02:29:fd:95:83:49:bf:0a:ba:b1:35:18:35:
+ a9:73:88:62:51:ac:14:17:34:50:e7:9f:c6:f3:93:
+ 5d:b9:c5:00:4f:37:90:7a:81:0f:d4:f5:72:29:e8:
+ 8c:62:ac:71:3b:f3:2e:df:2a:5c:e9:be:e7:56:24:
+ 82:28:9a:bf:3d:fd:71:42:fd:c5:7b:6b:37:09:47:
+ 50:3e:72:91:aa:3b:c5:4f:9d:12:6e:3d:80:03:54:
+ 49:41:15:3c:f2:86:44:74:28:f5:b9:a3:1b:db:76:
+ c7:ea:03:89:a0:60:e9:51:f6:a1:64:3e:74:9b:db:
+ 2a:cb:18:3c:c1:e7:68:18:89:ec:ab:38:2e:50:b9:
+ 6f:6e:15:33:28:1d:2f:d5:80:17:86:22:f3:1d:0c:
+ e0:87
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 1B:E4:8D:EF:3A:71:6B:12:65:68:CF:B6:91:BC:39:43:01:8D:75:C9
+ Signature Algorithm: sha1WithRSAEncryption
+ 6d:2b:ed:e9:8f:b6:29:d6:b6:30:ba:e6:6d:b5:c3:53:d2:c9:
+ b8:0a:4e:48:9f:f1:3f:3a:2a:b4:fb:74:96:e6:bd:18:bc:a7:
+ 79:c0:78:c1:39:ba:20:e3:4e:80:d2:24:d5:a5:7b:7f:62:a3:
+ da:d4:0f:0d:a9:9d:aa:c5:e9:bc:08:c9:48:66:98:9e:c0:f9:
+ de:ab:6d:e5:b0:33:c2:70:d0:96:d7:1e:6f:98:04:15:63:48:
+ bb:9a:85:5d:1b:69:f3:b4:4f:f3:a6:70:16:0f:21:19:7f:ad:
+ b0:03:39:49:86:8c:a2:73:4e:93:dc:21:01:68:b0:ef:0d:70:
+ 1a:7a:a2:b2:cc:22:42:91:54:bd:ac:70:04:8d:4c:5c:fc:3a:
+ a2:92:f6:f9:2a:12:40:cd:76:b7:66:f4:d7:d2:ab:de:8e:2c:
+ d9:dd:09:35:37:27:56:0b:ae:b6:ae:9a:83:60:f0:5f:7f:f0:
+ ad:b3:1c:fa:b8:74:5f:c7:ec:c0:0e:ad:d8:c3:cf:9f:d2:30:
+ 0e:5c:51:1f:3e:19:e2:c1:d2:a9:90:0f:e6:57:b9:af:04:76:
+ a2:f2:08:04:25:0a:35:bb:73:04:31:d0:98:46:7f:36:c1:61:
+ 16:a3:8a:8c:99:de:10:05:f2:de:ed:a5:09:64:97:84:8f:42:
+ a8:7b:22:c4
+-----BEGIN CERTIFICATE-----
+MIIENDCCAxygAwIBAgIEBydcJjANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTExMDgxODE4MzYzM1oX
+DTE4MDgwOTE4MzU0OVowWjELMAkGA1UEBhMCSlAxIzAhBgNVBAoMGkN5YmVydHJ1
+c3QgSmFwYW4gQ28uLCBMdGQuMSYwJAYDVQQDDB1DeWJlcnRydXN0IEphcGFuIFB1
+YmxpYyBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALbcdvu5
+RPsSfFSwu0F1dPA1R54nukNERWAZzUQKsnjl+h4kOwIfaHdg9OsiBQo3btv3FSC7
+PVPU0BGO1OtnvtjdBTeUQSUj75oQo8P3AL26JpJngVCpT56RPE4gulJ//0xNjqqt
+Tl+8J5cCKf2Vg0m/CrqxNRg1qXOIYlGsFBc0UOefxvOTXbnFAE83kHqBD9T1cino
+jGKscTvzLt8qXOm+51Ykgiiavz39cUL9xXtrNwlHUD5ykao7xU+dEm49gANUSUEV
+PPKGRHQo9bmjG9t2x+oDiaBg6VH2oWQ+dJvbKssYPMHnaBiJ7Ks4LlC5b24VMygd
+L9WAF4Yi8x0M4IcCAwEAAaOCAQAwgf0wEgYDVR0TAQH/BAgwBgEB/wIBADBTBgNV
+HSAETDBKMEgGCSsGAQQBsT4BADA7MDkGCCsGAQUFBwIBFi1odHRwOi8vY3liZXJ0
+cnVzdC5vbW5pcm9vdC5jb20vcmVwb3NpdG9yeS5jZm0wDgYDVR0PAQH/BAQDAgEG
+MB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1BE3wMEIGA1UdHwQ7MDkwN6A1
+oDOGMWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5jb20vQ1JML09tbmlyb290MjAy
+NS5jcmwwHQYDVR0OBBYEFBvkje86cWsSZWjPtpG8OUMBjXXJMA0GCSqGSIb3DQEB
+BQUAA4IBAQBtK+3pj7Yp1rYwuuZttcNT0sm4Ck5In/E/Oiq0+3SW5r0YvKd5wHjB
+Obog406A0iTVpXt/YqPa1A8NqZ2qxem8CMlIZpiewPneq23lsDPCcNCW1x5vmAQV
+Y0i7moVdG2nztE/zpnAWDyEZf62wAzlJhoyic06T3CEBaLDvDXAaeqKyzCJCkVS9
+rHAEjUxc/Dqikvb5KhJAzXa3ZvTX0qvejizZ3Qk1NydWC662rpqDYPBff/Ctsxz6
+uHRfx+zADq3Yw8+f0jAOXFEfPhniwdKpkA/mV7mvBHai8ggEJQo1u3MEMdCYRn82
+wWEWo4qMmd4QBfLe7aUJZJeEj0KoeyLE
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert18[] = {
+ 0x30, 0x82, 0x04, 0x34, 0x30, 0x82, 0x03, 0x1c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x5c, 0x26, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31,
+ 0x30, 0x38, 0x31, 0x38, 0x31, 0x38, 0x33, 0x36, 0x33, 0x33, 0x5a, 0x17,
+ 0x0d, 0x31, 0x38, 0x30, 0x38, 0x30, 0x39, 0x31, 0x38, 0x33, 0x35, 0x34,
+ 0x39, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x0c, 0x1a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20, 0x43, 0x6f, 0x2e,
+ 0x2c, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x0c, 0x1d, 0x43, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x4a, 0x61, 0x70, 0x61, 0x6e, 0x20, 0x50, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x20, 0x43, 0x41, 0x20, 0x47, 0x32, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, 0xdc, 0x76, 0xfb, 0xb9,
+ 0x44, 0xfb, 0x12, 0x7c, 0x54, 0xb0, 0xbb, 0x41, 0x75, 0x74, 0xf0, 0x35,
+ 0x47, 0x9e, 0x27, 0xba, 0x43, 0x44, 0x45, 0x60, 0x19, 0xcd, 0x44, 0x0a,
+ 0xb2, 0x78, 0xe5, 0xfa, 0x1e, 0x24, 0x3b, 0x02, 0x1f, 0x68, 0x77, 0x60,
+ 0xf4, 0xeb, 0x22, 0x05, 0x0a, 0x37, 0x6e, 0xdb, 0xf7, 0x15, 0x20, 0xbb,
+ 0x3d, 0x53, 0xd4, 0xd0, 0x11, 0x8e, 0xd4, 0xeb, 0x67, 0xbe, 0xd8, 0xdd,
+ 0x05, 0x37, 0x94, 0x41, 0x25, 0x23, 0xef, 0x9a, 0x10, 0xa3, 0xc3, 0xf7,
+ 0x00, 0xbd, 0xba, 0x26, 0x92, 0x67, 0x81, 0x50, 0xa9, 0x4f, 0x9e, 0x91,
+ 0x3c, 0x4e, 0x20, 0xba, 0x52, 0x7f, 0xff, 0x4c, 0x4d, 0x8e, 0xaa, 0xad,
+ 0x4e, 0x5f, 0xbc, 0x27, 0x97, 0x02, 0x29, 0xfd, 0x95, 0x83, 0x49, 0xbf,
+ 0x0a, 0xba, 0xb1, 0x35, 0x18, 0x35, 0xa9, 0x73, 0x88, 0x62, 0x51, 0xac,
+ 0x14, 0x17, 0x34, 0x50, 0xe7, 0x9f, 0xc6, 0xf3, 0x93, 0x5d, 0xb9, 0xc5,
+ 0x00, 0x4f, 0x37, 0x90, 0x7a, 0x81, 0x0f, 0xd4, 0xf5, 0x72, 0x29, 0xe8,
+ 0x8c, 0x62, 0xac, 0x71, 0x3b, 0xf3, 0x2e, 0xdf, 0x2a, 0x5c, 0xe9, 0xbe,
+ 0xe7, 0x56, 0x24, 0x82, 0x28, 0x9a, 0xbf, 0x3d, 0xfd, 0x71, 0x42, 0xfd,
+ 0xc5, 0x7b, 0x6b, 0x37, 0x09, 0x47, 0x50, 0x3e, 0x72, 0x91, 0xaa, 0x3b,
+ 0xc5, 0x4f, 0x9d, 0x12, 0x6e, 0x3d, 0x80, 0x03, 0x54, 0x49, 0x41, 0x15,
+ 0x3c, 0xf2, 0x86, 0x44, 0x74, 0x28, 0xf5, 0xb9, 0xa3, 0x1b, 0xdb, 0x76,
+ 0xc7, 0xea, 0x03, 0x89, 0xa0, 0x60, 0xe9, 0x51, 0xf6, 0xa1, 0x64, 0x3e,
+ 0x74, 0x9b, 0xdb, 0x2a, 0xcb, 0x18, 0x3c, 0xc1, 0xe7, 0x68, 0x18, 0x89,
+ 0xec, 0xab, 0x38, 0x2e, 0x50, 0xb9, 0x6f, 0x6e, 0x15, 0x33, 0x28, 0x1d,
+ 0x2f, 0xd5, 0x80, 0x17, 0x86, 0x22, 0xf3, 0x1d, 0x0c, 0xe0, 0x87, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x00, 0x30, 0x81, 0xfd, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x53, 0x06, 0x03, 0x55,
+ 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06,
+ 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
+ 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08,
+ 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35,
+ 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c,
+ 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32,
+ 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x1b, 0xe4, 0x8d, 0xef, 0x3a, 0x71, 0x6b, 0x12,
+ 0x65, 0x68, 0xcf, 0xb6, 0x91, 0xbc, 0x39, 0x43, 0x01, 0x8d, 0x75, 0xc9,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x6d, 0x2b, 0xed, 0xe9,
+ 0x8f, 0xb6, 0x29, 0xd6, 0xb6, 0x30, 0xba, 0xe6, 0x6d, 0xb5, 0xc3, 0x53,
+ 0xd2, 0xc9, 0xb8, 0x0a, 0x4e, 0x48, 0x9f, 0xf1, 0x3f, 0x3a, 0x2a, 0xb4,
+ 0xfb, 0x74, 0x96, 0xe6, 0xbd, 0x18, 0xbc, 0xa7, 0x79, 0xc0, 0x78, 0xc1,
+ 0x39, 0xba, 0x20, 0xe3, 0x4e, 0x80, 0xd2, 0x24, 0xd5, 0xa5, 0x7b, 0x7f,
+ 0x62, 0xa3, 0xda, 0xd4, 0x0f, 0x0d, 0xa9, 0x9d, 0xaa, 0xc5, 0xe9, 0xbc,
+ 0x08, 0xc9, 0x48, 0x66, 0x98, 0x9e, 0xc0, 0xf9, 0xde, 0xab, 0x6d, 0xe5,
+ 0xb0, 0x33, 0xc2, 0x70, 0xd0, 0x96, 0xd7, 0x1e, 0x6f, 0x98, 0x04, 0x15,
+ 0x63, 0x48, 0xbb, 0x9a, 0x85, 0x5d, 0x1b, 0x69, 0xf3, 0xb4, 0x4f, 0xf3,
+ 0xa6, 0x70, 0x16, 0x0f, 0x21, 0x19, 0x7f, 0xad, 0xb0, 0x03, 0x39, 0x49,
+ 0x86, 0x8c, 0xa2, 0x73, 0x4e, 0x93, 0xdc, 0x21, 0x01, 0x68, 0xb0, 0xef,
+ 0x0d, 0x70, 0x1a, 0x7a, 0xa2, 0xb2, 0xcc, 0x22, 0x42, 0x91, 0x54, 0xbd,
+ 0xac, 0x70, 0x04, 0x8d, 0x4c, 0x5c, 0xfc, 0x3a, 0xa2, 0x92, 0xf6, 0xf9,
+ 0x2a, 0x12, 0x40, 0xcd, 0x76, 0xb7, 0x66, 0xf4, 0xd7, 0xd2, 0xab, 0xde,
+ 0x8e, 0x2c, 0xd9, 0xdd, 0x09, 0x35, 0x37, 0x27, 0x56, 0x0b, 0xae, 0xb6,
+ 0xae, 0x9a, 0x83, 0x60, 0xf0, 0x5f, 0x7f, 0xf0, 0xad, 0xb3, 0x1c, 0xfa,
+ 0xb8, 0x74, 0x5f, 0xc7, 0xec, 0xc0, 0x0e, 0xad, 0xd8, 0xc3, 0xcf, 0x9f,
+ 0xd2, 0x30, 0x0e, 0x5c, 0x51, 0x1f, 0x3e, 0x19, 0xe2, 0xc1, 0xd2, 0xa9,
+ 0x90, 0x0f, 0xe6, 0x57, 0xb9, 0xaf, 0x04, 0x76, 0xa2, 0xf2, 0x08, 0x04,
+ 0x25, 0x0a, 0x35, 0xbb, 0x73, 0x04, 0x31, 0xd0, 0x98, 0x46, 0x7f, 0x36,
+ 0xc1, 0x61, 0x16, 0xa3, 0x8a, 0x8c, 0x99, 0xde, 0x10, 0x05, 0xf2, 0xde,
+ 0xed, 0xa5, 0x09, 0x64, 0x97, 0x84, 0x8f, 0x42, 0xa8, 0x7b, 0x22, 0xc4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 314159292 (0x12b9b0bc)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, O=SECOM Trust.net, OU=Security Communication RootCA1
+ Validity
+ Not Before: Feb 28 08:51:17 2008 GMT
+ Not After : Feb 28 08:51:17 2018 GMT
+ Subject: C=JP, O=SECOM Trust Systems CO.,LTD., CN=SECOM Passport for Web SR 2.0 CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ad:81:dd:77:fa:50:b2:3f:38:54:f7:2b:6a:04:
+ 23:1d:db:84:08:6d:dc:51:41:bf:09:01:d7:84:af:
+ c8:7e:92:f9:72:47:35:6a:d5:85:eb:2d:5a:82:72:
+ 6f:6c:10:e6:37:9d:fc:d7:0a:5f:99:07:24:3d:50:
+ bb:36:f7:d1:50:2d:38:5d:bf:6b:56:2e:e6:79:b4:
+ 59:3e:a0:09:7e:d0:8e:a8:0c:ed:0a:c3:5b:f8:79:
+ b5:c1:23:5a:b3:b5:f3:f2:f6:e4:e4:2a:a9:ff:86:
+ 53:3e:57:cc:9d:00:36:57:f1:c2:af:34:2b:52:68:
+ 57:4d:a1:57:cb:28:c6:21:ba:58:4a:99:a7:65:e5:
+ 85:24:3f:23:42:e7:cb:96:a5:03:97:3f:ab:ea:40:
+ 57:6e:1f:a7:4e:24:25:4e:ee:92:8b:f1:a5:e4:8f:
+ dc:96:56:54:01:17:8c:42:07:df:de:d3:c2:c2:7e:
+ 97:5d:9d:25:f3:e5:04:ee:6e:3b:4f:23:25:ce:4c:
+ 75:e0:e4:e9:19:ab:55:2e:ad:8d:cb:b9:8b:c2:25:
+ 91:4e:09:78:af:35:3c:5f:b1:a7:40:9d:e1:12:0b:
+ 83:e0:7a:c8:f4:af:81:a6:3c:43:b0:e6:59:9b:cc:
+ 66:27:75:49:6e:39:ef:8b:22:ed:9b:99:a4:94:94:
+ 8e:09
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 30:9A:00:57:99:44:63:6B:C9:B2:F2:3D:8D:83:6B:3B:D7:9D:EF:64
+ X509v3 Authority Key Identifier:
+ keyid:A0:73:49:99:68:DC:85:5B:65:E3:9B:28:2F:57:9F:BD:33:BC:07:48
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://repository.secomtrust.net/SC-Root1/SCRoot1CRL.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.2.392.200091.100.901.1
+ CPS: https://repository.secomtrust.net/SC-Root1/
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 4d:b3:4b:19:c4:de:f8:9e:7b:54:3a:69:77:b8:31:ed:50:dc:
+ f4:d2:ad:2f:08:5b:af:77:c1:23:be:14:38:39:fd:2d:4e:d1:
+ d2:29:b6:9d:ad:8e:79:76:41:1a:d5:4c:dd:b6:d1:54:68:bb:
+ 8c:2d:b1:d9:18:77:03:bb:46:91:1a:28:ec:e1:b8:00:2d:8c:
+ b5:0c:a2:24:df:75:ba:58:fc:dd:8a:c1:b4:53:12:29:43:77:
+ ed:4c:73:8d:b1:49:1f:14:56:be:11:8d:6d:d6:55:35:55:25:
+ 48:8a:84:e8:3e:74:38:e2:7d:ba:37:64:69:14:a7:69:d9:c6:
+ 40:3e:74:b4:23:bb:c2:71:9f:2b:3d:6a:88:81:11:ce:2e:bc:
+ 12:9f:c8:18:e7:f9:63:0d:25:87:c3:85:05:ba:9c:9d:b7:a7:
+ 69:41:ee:c8:67:82:d3:2f:03:e6:43:c3:53:8a:60:58:d6:2a:
+ 1a:c0:80:9a:97:89:54:40:e6:25:c5:1e:8e:af:7f:a1:10:bf:
+ 37:13:05:1d:8a:d0:42:18:f8:bb:f9:64:a8:05:56:06:fa:27:
+ 71:0f:5c:79:90:ff:5a:43:a2:a7:b7:6c:68:64:8a:94:25:ee:
+ be:7f:7b:27:0c:92:4b:99:c5:33:3d:93:e0:62:71:29:81:cb:
+ 26:7a:a7:c8
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIEErmwvDANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJK
+UDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBD
+b21tdW5pY2F0aW9uIFJvb3RDQTEwHhcNMDgwMjI4MDg1MTE3WhcNMTgwMjI4MDg1
+MTE3WjBfMQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVt
+cyBDTy4sTFRELjEpMCcGA1UEAxMgU0VDT00gUGFzc3BvcnQgZm9yIFdlYiBTUiAy
+LjAgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtgd13+lCyPzhU
+9ytqBCMd24QIbdxRQb8JAdeEr8h+kvlyRzVq1YXrLVqCcm9sEOY3nfzXCl+ZByQ9
+ULs299FQLThdv2tWLuZ5tFk+oAl+0I6oDO0Kw1v4ebXBI1qztfPy9uTkKqn/hlM+
+V8ydADZX8cKvNCtSaFdNoVfLKMYhulhKmadl5YUkPyNC58uWpQOXP6vqQFduH6dO
+JCVO7pKL8aXkj9yWVlQBF4xCB9/e08LCfpddnSXz5QTubjtPIyXOTHXg5OkZq1Uu
+rY3LuYvCJZFOCXivNTxfsadAneESC4Pgesj0r4GmPEOw5lmbzGYndUluOe+LIu2b
+maSUlI4JAgMBAAGjggEHMIIBAzAdBgNVHQ4EFgQUMJoAV5lEY2vJsvI9jYNrO9ed
+72QwHwYDVR0jBBgwFoAUoHNJmWjchVtl45soL1efvTO8B0gwEgYDVR0TAQH/BAgw
+BgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDov
+L3JlcG9zaXRvcnkuc2Vjb210cnVzdC5uZXQvU0MtUm9vdDEvU0NSb290MUNSTC5j
+cmwwUgYDVR0gBEswSTBHBgoqgwiMmxtkhwUBMDkwNwYIKwYBBQUHAgEWK2h0dHBz
+Oi8vcmVwb3NpdG9yeS5zZWNvbXRydXN0Lm5ldC9TQy1Sb290MS8wDQYJKoZIhvcN
+AQEFBQADggEBAE2zSxnE3viee1Q6aXe4Me1Q3PTSrS8IW693wSO+FDg5/S1O0dIp
+tp2tjnl2QRrVTN220VRou4wtsdkYdwO7RpEaKOzhuAAtjLUMoiTfdbpY/N2KwbRT
+EilDd+1Mc42xSR8UVr4RjW3WVTVVJUiKhOg+dDjifbo3ZGkUp2nZxkA+dLQju8Jx
+nys9aoiBEc4uvBKfyBjn+WMNJYfDhQW6nJ23p2lB7shngtMvA+ZDw1OKYFjWKhrA
+gJqXiVRA5iXFHo6vf6EQvzcTBR2K0EIY+Lv5ZKgFVgb6J3EPXHmQ/1pDoqe3bGhk
+ipQl7r5/eycMkkuZxTM9k+BicSmByyZ6p8g=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert19[] = {
+ 0x30, 0x82, 0x04, 0x36, 0x30, 0x82, 0x03, 0x1e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x12, 0xb9, 0xb0, 0xbc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x50,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a,
+ 0x50, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x53, 0x45, 0x43, 0x4f, 0x4d, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x31, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x38, 0x30, 0x32, 0x32, 0x38, 0x30, 0x38, 0x35, 0x31, 0x31, 0x37,
+ 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x32, 0x32, 0x38, 0x30, 0x38, 0x35,
+ 0x31, 0x31, 0x37, 0x5a, 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x25, 0x30, 0x23, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x45, 0x43, 0x4f, 0x4d, 0x20,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x73, 0x20, 0x43, 0x4f, 0x2e, 0x2c, 0x4c, 0x54, 0x44, 0x2e, 0x31, 0x29,
+ 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x45, 0x43,
+ 0x4f, 0x4d, 0x20, 0x50, 0x61, 0x73, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x57, 0x65, 0x62, 0x20, 0x53, 0x52, 0x20, 0x32,
+ 0x2e, 0x30, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xad, 0x81, 0xdd, 0x77, 0xfa, 0x50, 0xb2, 0x3f, 0x38, 0x54,
+ 0xf7, 0x2b, 0x6a, 0x04, 0x23, 0x1d, 0xdb, 0x84, 0x08, 0x6d, 0xdc, 0x51,
+ 0x41, 0xbf, 0x09, 0x01, 0xd7, 0x84, 0xaf, 0xc8, 0x7e, 0x92, 0xf9, 0x72,
+ 0x47, 0x35, 0x6a, 0xd5, 0x85, 0xeb, 0x2d, 0x5a, 0x82, 0x72, 0x6f, 0x6c,
+ 0x10, 0xe6, 0x37, 0x9d, 0xfc, 0xd7, 0x0a, 0x5f, 0x99, 0x07, 0x24, 0x3d,
+ 0x50, 0xbb, 0x36, 0xf7, 0xd1, 0x50, 0x2d, 0x38, 0x5d, 0xbf, 0x6b, 0x56,
+ 0x2e, 0xe6, 0x79, 0xb4, 0x59, 0x3e, 0xa0, 0x09, 0x7e, 0xd0, 0x8e, 0xa8,
+ 0x0c, 0xed, 0x0a, 0xc3, 0x5b, 0xf8, 0x79, 0xb5, 0xc1, 0x23, 0x5a, 0xb3,
+ 0xb5, 0xf3, 0xf2, 0xf6, 0xe4, 0xe4, 0x2a, 0xa9, 0xff, 0x86, 0x53, 0x3e,
+ 0x57, 0xcc, 0x9d, 0x00, 0x36, 0x57, 0xf1, 0xc2, 0xaf, 0x34, 0x2b, 0x52,
+ 0x68, 0x57, 0x4d, 0xa1, 0x57, 0xcb, 0x28, 0xc6, 0x21, 0xba, 0x58, 0x4a,
+ 0x99, 0xa7, 0x65, 0xe5, 0x85, 0x24, 0x3f, 0x23, 0x42, 0xe7, 0xcb, 0x96,
+ 0xa5, 0x03, 0x97, 0x3f, 0xab, 0xea, 0x40, 0x57, 0x6e, 0x1f, 0xa7, 0x4e,
+ 0x24, 0x25, 0x4e, 0xee, 0x92, 0x8b, 0xf1, 0xa5, 0xe4, 0x8f, 0xdc, 0x96,
+ 0x56, 0x54, 0x01, 0x17, 0x8c, 0x42, 0x07, 0xdf, 0xde, 0xd3, 0xc2, 0xc2,
+ 0x7e, 0x97, 0x5d, 0x9d, 0x25, 0xf3, 0xe5, 0x04, 0xee, 0x6e, 0x3b, 0x4f,
+ 0x23, 0x25, 0xce, 0x4c, 0x75, 0xe0, 0xe4, 0xe9, 0x19, 0xab, 0x55, 0x2e,
+ 0xad, 0x8d, 0xcb, 0xb9, 0x8b, 0xc2, 0x25, 0x91, 0x4e, 0x09, 0x78, 0xaf,
+ 0x35, 0x3c, 0x5f, 0xb1, 0xa7, 0x40, 0x9d, 0xe1, 0x12, 0x0b, 0x83, 0xe0,
+ 0x7a, 0xc8, 0xf4, 0xaf, 0x81, 0xa6, 0x3c, 0x43, 0xb0, 0xe6, 0x59, 0x9b,
+ 0xcc, 0x66, 0x27, 0x75, 0x49, 0x6e, 0x39, 0xef, 0x8b, 0x22, 0xed, 0x9b,
+ 0x99, 0xa4, 0x94, 0x94, 0x8e, 0x09, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x07, 0x30, 0x82, 0x01, 0x03, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x30, 0x9a, 0x00, 0x57, 0x99, 0x44,
+ 0x63, 0x6b, 0xc9, 0xb2, 0xf2, 0x3d, 0x8d, 0x83, 0x6b, 0x3b, 0xd7, 0x9d,
+ 0xef, 0x64, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xa0, 0x73, 0x49, 0x99, 0x68, 0xdc, 0x85, 0x5b, 0x65,
+ 0xe3, 0x9b, 0x28, 0x2f, 0x57, 0x9f, 0xbd, 0x33, 0xbc, 0x07, 0x48, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x49, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x42, 0x30, 0x40, 0x30, 0x3e,
+ 0xa0, 0x3c, 0xa0, 0x3a, 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
+ 0x73, 0x65, 0x63, 0x6f, 0x6d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x53, 0x43, 0x2d, 0x52, 0x6f, 0x6f, 0x74, 0x31, 0x2f,
+ 0x53, 0x43, 0x52, 0x6f, 0x6f, 0x74, 0x31, 0x43, 0x52, 0x4c, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, 0x30,
+ 0x49, 0x30, 0x47, 0x06, 0x0a, 0x2a, 0x83, 0x08, 0x8c, 0x9b, 0x1b, 0x64,
+ 0x87, 0x05, 0x01, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2e, 0x73, 0x65, 0x63, 0x6f, 0x6d, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x53, 0x43, 0x2d, 0x52, 0x6f, 0x6f, 0x74,
+ 0x31, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x4d, 0xb3,
+ 0x4b, 0x19, 0xc4, 0xde, 0xf8, 0x9e, 0x7b, 0x54, 0x3a, 0x69, 0x77, 0xb8,
+ 0x31, 0xed, 0x50, 0xdc, 0xf4, 0xd2, 0xad, 0x2f, 0x08, 0x5b, 0xaf, 0x77,
+ 0xc1, 0x23, 0xbe, 0x14, 0x38, 0x39, 0xfd, 0x2d, 0x4e, 0xd1, 0xd2, 0x29,
+ 0xb6, 0x9d, 0xad, 0x8e, 0x79, 0x76, 0x41, 0x1a, 0xd5, 0x4c, 0xdd, 0xb6,
+ 0xd1, 0x54, 0x68, 0xbb, 0x8c, 0x2d, 0xb1, 0xd9, 0x18, 0x77, 0x03, 0xbb,
+ 0x46, 0x91, 0x1a, 0x28, 0xec, 0xe1, 0xb8, 0x00, 0x2d, 0x8c, 0xb5, 0x0c,
+ 0xa2, 0x24, 0xdf, 0x75, 0xba, 0x58, 0xfc, 0xdd, 0x8a, 0xc1, 0xb4, 0x53,
+ 0x12, 0x29, 0x43, 0x77, 0xed, 0x4c, 0x73, 0x8d, 0xb1, 0x49, 0x1f, 0x14,
+ 0x56, 0xbe, 0x11, 0x8d, 0x6d, 0xd6, 0x55, 0x35, 0x55, 0x25, 0x48, 0x8a,
+ 0x84, 0xe8, 0x3e, 0x74, 0x38, 0xe2, 0x7d, 0xba, 0x37, 0x64, 0x69, 0x14,
+ 0xa7, 0x69, 0xd9, 0xc6, 0x40, 0x3e, 0x74, 0xb4, 0x23, 0xbb, 0xc2, 0x71,
+ 0x9f, 0x2b, 0x3d, 0x6a, 0x88, 0x81, 0x11, 0xce, 0x2e, 0xbc, 0x12, 0x9f,
+ 0xc8, 0x18, 0xe7, 0xf9, 0x63, 0x0d, 0x25, 0x87, 0xc3, 0x85, 0x05, 0xba,
+ 0x9c, 0x9d, 0xb7, 0xa7, 0x69, 0x41, 0xee, 0xc8, 0x67, 0x82, 0xd3, 0x2f,
+ 0x03, 0xe6, 0x43, 0xc3, 0x53, 0x8a, 0x60, 0x58, 0xd6, 0x2a, 0x1a, 0xc0,
+ 0x80, 0x9a, 0x97, 0x89, 0x54, 0x40, 0xe6, 0x25, 0xc5, 0x1e, 0x8e, 0xaf,
+ 0x7f, 0xa1, 0x10, 0xbf, 0x37, 0x13, 0x05, 0x1d, 0x8a, 0xd0, 0x42, 0x18,
+ 0xf8, 0xbb, 0xf9, 0x64, 0xa8, 0x05, 0x56, 0x06, 0xfa, 0x27, 0x71, 0x0f,
+ 0x5c, 0x79, 0x90, 0xff, 0x5a, 0x43, 0xa2, 0xa7, 0xb7, 0x6c, 0x68, 0x64,
+ 0x8a, 0x94, 0x25, 0xee, 0xbe, 0x7f, 0x7b, 0x27, 0x0c, 0x92, 0x4b, 0x99,
+ 0xc5, 0x33, 0x3d, 0x93, 0xe0, 0x62, 0x71, 0x29, 0x81, 0xcb, 0x26, 0x7a,
+ 0xa7, 0xc8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 314159330 (0x12b9b0e2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=JP, O=SECOM Trust.net, OU=Security Communication RootCA1
+ Validity
+ Not Before: Feb 17 04:49:28 2012 GMT
+ Not After : Feb 17 04:49:28 2022 GMT
+ Subject: C=JP, O=SECOM Trust Systems CO.,LTD., CN=SECOM Passport for Web SR 2.0 CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ad:81:dd:77:fa:50:b2:3f:38:54:f7:2b:6a:04:
+ 23:1d:db:84:08:6d:dc:51:41:bf:09:01:d7:84:af:
+ c8:7e:92:f9:72:47:35:6a:d5:85:eb:2d:5a:82:72:
+ 6f:6c:10:e6:37:9d:fc:d7:0a:5f:99:07:24:3d:50:
+ bb:36:f7:d1:50:2d:38:5d:bf:6b:56:2e:e6:79:b4:
+ 59:3e:a0:09:7e:d0:8e:a8:0c:ed:0a:c3:5b:f8:79:
+ b5:c1:23:5a:b3:b5:f3:f2:f6:e4:e4:2a:a9:ff:86:
+ 53:3e:57:cc:9d:00:36:57:f1:c2:af:34:2b:52:68:
+ 57:4d:a1:57:cb:28:c6:21:ba:58:4a:99:a7:65:e5:
+ 85:24:3f:23:42:e7:cb:96:a5:03:97:3f:ab:ea:40:
+ 57:6e:1f:a7:4e:24:25:4e:ee:92:8b:f1:a5:e4:8f:
+ dc:96:56:54:01:17:8c:42:07:df:de:d3:c2:c2:7e:
+ 97:5d:9d:25:f3:e5:04:ee:6e:3b:4f:23:25:ce:4c:
+ 75:e0:e4:e9:19:ab:55:2e:ad:8d:cb:b9:8b:c2:25:
+ 91:4e:09:78:af:35:3c:5f:b1:a7:40:9d:e1:12:0b:
+ 83:e0:7a:c8:f4:af:81:a6:3c:43:b0:e6:59:9b:cc:
+ 66:27:75:49:6e:39:ef:8b:22:ed:9b:99:a4:94:94:
+ 8e:09
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 30:9A:00:57:99:44:63:6B:C9:B2:F2:3D:8D:83:6B:3B:D7:9D:EF:64
+ X509v3 Authority Key Identifier:
+ keyid:A0:73:49:99:68:DC:85:5B:65:E3:9B:28:2F:57:9F:BD:33:BC:07:48
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://repository.secomtrust.net/SC-Root1/SCRoot1CRL.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.2.392.200091.100.901.1
+ CPS: https://repository.secomtrust.net/SC-Root1/
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 32:ff:99:55:69:c1:3e:48:62:52:69:ac:09:10:5f:93:3f:69:
+ c4:53:47:34:44:a3:7d:9c:19:2f:94:9b:ba:68:14:e7:75:e4:
+ 9b:ad:f2:65:a7:68:13:f9:ca:1d:6b:8e:1c:94:62:10:70:4c:
+ 14:6a:c5:a9:64:47:c5:c4:de:b3:0d:04:1f:fd:a0:ae:3f:68:
+ 59:de:af:4a:b9:d2:cb:8f:09:ec:df:8c:b9:34:3a:0f:5e:e6:
+ bd:97:79:f3:e2:ef:19:6a:8a:97:19:7d:d1:f9:04:41:fc:93:
+ 7a:54:44:42:a7:bc:e7:79:a9:9e:68:d8:1c:5d:39:5d:97:bb:
+ bb:f1:14:b7:b5:0c:f3:bc:11:9e:dc:67:b1:df:d4:91:68:e5:
+ df:6d:ac:69:f3:b9:56:87:25:e4:cc:8d:86:d1:38:ce:93:ec:
+ 76:c1:c4:01:44:1e:13:16:90:73:5e:5d:80:4d:42:69:68:5b:
+ aa:4d:0f:f5:38:2e:0e:d6:28:9d:d4:b9:5a:d1:4d:08:39:b6:
+ f4:47:9c:82:48:e6:b4:71:8e:0d:05:cd:15:48:8b:e7:ae:12:
+ fd:24:e8:6d:7a:cf:bf:1e:4e:7e:6d:87:f3:af:56:8f:4e:12:
+ 42:4f:b4:7c:5b:fd:10:f8:78:e3:9b:52:3d:cd:a9:99:75:b0:
+ c3:c9:fd:24
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIEErmw4jANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJK
+UDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBD
+b21tdW5pY2F0aW9uIFJvb3RDQTEwHhcNMTIwMjE3MDQ0OTI4WhcNMjIwMjE3MDQ0
+OTI4WjBfMQswCQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVt
+cyBDTy4sTFRELjEpMCcGA1UEAxMgU0VDT00gUGFzc3BvcnQgZm9yIFdlYiBTUiAy
+LjAgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtgd13+lCyPzhU
+9ytqBCMd24QIbdxRQb8JAdeEr8h+kvlyRzVq1YXrLVqCcm9sEOY3nfzXCl+ZByQ9
+ULs299FQLThdv2tWLuZ5tFk+oAl+0I6oDO0Kw1v4ebXBI1qztfPy9uTkKqn/hlM+
+V8ydADZX8cKvNCtSaFdNoVfLKMYhulhKmadl5YUkPyNC58uWpQOXP6vqQFduH6dO
+JCVO7pKL8aXkj9yWVlQBF4xCB9/e08LCfpddnSXz5QTubjtPIyXOTHXg5OkZq1Uu
+rY3LuYvCJZFOCXivNTxfsadAneESC4Pgesj0r4GmPEOw5lmbzGYndUluOe+LIu2b
+maSUlI4JAgMBAAGjggEHMIIBAzAdBgNVHQ4EFgQUMJoAV5lEY2vJsvI9jYNrO9ed
+72QwHwYDVR0jBBgwFoAUoHNJmWjchVtl45soL1efvTO8B0gwEgYDVR0TAQH/BAgw
+BgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDov
+L3JlcG9zaXRvcnkuc2Vjb210cnVzdC5uZXQvU0MtUm9vdDEvU0NSb290MUNSTC5j
+cmwwUgYDVR0gBEswSTBHBgoqgwiMmxtkhwUBMDkwNwYIKwYBBQUHAgEWK2h0dHBz
+Oi8vcmVwb3NpdG9yeS5zZWNvbXRydXN0Lm5ldC9TQy1Sb290MS8wDQYJKoZIhvcN
+AQEFBQADggEBADL/mVVpwT5IYlJprAkQX5M/acRTRzREo32cGS+Um7poFOd15Jut
+8mWnaBP5yh1rjhyUYhBwTBRqxalkR8XE3rMNBB/9oK4/aFner0q50suPCezfjLk0
+Og9e5r2XefPi7xlqipcZfdH5BEH8k3pUREKnvOd5qZ5o2BxdOV2Xu7vxFLe1DPO8
+EZ7cZ7Hf1JFo5d9trGnzuVaHJeTMjYbROM6T7HbBxAFEHhMWkHNeXYBNQmloW6pN
+D/U4Lg7WKJ3UuVrRTQg5tvRHnIJI5rRxjg0FzRVIi+euEv0k6G16z78eTn5th/Ov
+Vo9OEkJPtHxb/RD4eOObUj3NqZl1sMPJ/SQ=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert20[] = {
+ 0x30, 0x82, 0x04, 0x36, 0x30, 0x82, 0x03, 0x1e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x12, 0xb9, 0xb0, 0xe2, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x50,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a,
+ 0x50, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x53, 0x45, 0x43, 0x4f, 0x4d, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x31, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x32, 0x30, 0x32, 0x31, 0x37, 0x30, 0x34, 0x34, 0x39, 0x32, 0x38,
+ 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x32, 0x31, 0x37, 0x30, 0x34, 0x34,
+ 0x39, 0x32, 0x38, 0x5a, 0x30, 0x5f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x25, 0x30, 0x23, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x45, 0x43, 0x4f, 0x4d, 0x20,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x73, 0x20, 0x43, 0x4f, 0x2e, 0x2c, 0x4c, 0x54, 0x44, 0x2e, 0x31, 0x29,
+ 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x45, 0x43,
+ 0x4f, 0x4d, 0x20, 0x50, 0x61, 0x73, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x57, 0x65, 0x62, 0x20, 0x53, 0x52, 0x20, 0x32,
+ 0x2e, 0x30, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xad, 0x81, 0xdd, 0x77, 0xfa, 0x50, 0xb2, 0x3f, 0x38, 0x54,
+ 0xf7, 0x2b, 0x6a, 0x04, 0x23, 0x1d, 0xdb, 0x84, 0x08, 0x6d, 0xdc, 0x51,
+ 0x41, 0xbf, 0x09, 0x01, 0xd7, 0x84, 0xaf, 0xc8, 0x7e, 0x92, 0xf9, 0x72,
+ 0x47, 0x35, 0x6a, 0xd5, 0x85, 0xeb, 0x2d, 0x5a, 0x82, 0x72, 0x6f, 0x6c,
+ 0x10, 0xe6, 0x37, 0x9d, 0xfc, 0xd7, 0x0a, 0x5f, 0x99, 0x07, 0x24, 0x3d,
+ 0x50, 0xbb, 0x36, 0xf7, 0xd1, 0x50, 0x2d, 0x38, 0x5d, 0xbf, 0x6b, 0x56,
+ 0x2e, 0xe6, 0x79, 0xb4, 0x59, 0x3e, 0xa0, 0x09, 0x7e, 0xd0, 0x8e, 0xa8,
+ 0x0c, 0xed, 0x0a, 0xc3, 0x5b, 0xf8, 0x79, 0xb5, 0xc1, 0x23, 0x5a, 0xb3,
+ 0xb5, 0xf3, 0xf2, 0xf6, 0xe4, 0xe4, 0x2a, 0xa9, 0xff, 0x86, 0x53, 0x3e,
+ 0x57, 0xcc, 0x9d, 0x00, 0x36, 0x57, 0xf1, 0xc2, 0xaf, 0x34, 0x2b, 0x52,
+ 0x68, 0x57, 0x4d, 0xa1, 0x57, 0xcb, 0x28, 0xc6, 0x21, 0xba, 0x58, 0x4a,
+ 0x99, 0xa7, 0x65, 0xe5, 0x85, 0x24, 0x3f, 0x23, 0x42, 0xe7, 0xcb, 0x96,
+ 0xa5, 0x03, 0x97, 0x3f, 0xab, 0xea, 0x40, 0x57, 0x6e, 0x1f, 0xa7, 0x4e,
+ 0x24, 0x25, 0x4e, 0xee, 0x92, 0x8b, 0xf1, 0xa5, 0xe4, 0x8f, 0xdc, 0x96,
+ 0x56, 0x54, 0x01, 0x17, 0x8c, 0x42, 0x07, 0xdf, 0xde, 0xd3, 0xc2, 0xc2,
+ 0x7e, 0x97, 0x5d, 0x9d, 0x25, 0xf3, 0xe5, 0x04, 0xee, 0x6e, 0x3b, 0x4f,
+ 0x23, 0x25, 0xce, 0x4c, 0x75, 0xe0, 0xe4, 0xe9, 0x19, 0xab, 0x55, 0x2e,
+ 0xad, 0x8d, 0xcb, 0xb9, 0x8b, 0xc2, 0x25, 0x91, 0x4e, 0x09, 0x78, 0xaf,
+ 0x35, 0x3c, 0x5f, 0xb1, 0xa7, 0x40, 0x9d, 0xe1, 0x12, 0x0b, 0x83, 0xe0,
+ 0x7a, 0xc8, 0xf4, 0xaf, 0x81, 0xa6, 0x3c, 0x43, 0xb0, 0xe6, 0x59, 0x9b,
+ 0xcc, 0x66, 0x27, 0x75, 0x49, 0x6e, 0x39, 0xef, 0x8b, 0x22, 0xed, 0x9b,
+ 0x99, 0xa4, 0x94, 0x94, 0x8e, 0x09, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x07, 0x30, 0x82, 0x01, 0x03, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x30, 0x9a, 0x00, 0x57, 0x99, 0x44,
+ 0x63, 0x6b, 0xc9, 0xb2, 0xf2, 0x3d, 0x8d, 0x83, 0x6b, 0x3b, 0xd7, 0x9d,
+ 0xef, 0x64, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xa0, 0x73, 0x49, 0x99, 0x68, 0xdc, 0x85, 0x5b, 0x65,
+ 0xe3, 0x9b, 0x28, 0x2f, 0x57, 0x9f, 0xbd, 0x33, 0xbc, 0x07, 0x48, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x49, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x42, 0x30, 0x40, 0x30, 0x3e,
+ 0xa0, 0x3c, 0xa0, 0x3a, 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e,
+ 0x73, 0x65, 0x63, 0x6f, 0x6d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x53, 0x43, 0x2d, 0x52, 0x6f, 0x6f, 0x74, 0x31, 0x2f,
+ 0x53, 0x43, 0x52, 0x6f, 0x6f, 0x74, 0x31, 0x43, 0x52, 0x4c, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, 0x30,
+ 0x49, 0x30, 0x47, 0x06, 0x0a, 0x2a, 0x83, 0x08, 0x8c, 0x9b, 0x1b, 0x64,
+ 0x87, 0x05, 0x01, 0x30, 0x39, 0x30, 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x2e, 0x73, 0x65, 0x63, 0x6f, 0x6d, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x53, 0x43, 0x2d, 0x52, 0x6f, 0x6f, 0x74,
+ 0x31, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x32, 0xff,
+ 0x99, 0x55, 0x69, 0xc1, 0x3e, 0x48, 0x62, 0x52, 0x69, 0xac, 0x09, 0x10,
+ 0x5f, 0x93, 0x3f, 0x69, 0xc4, 0x53, 0x47, 0x34, 0x44, 0xa3, 0x7d, 0x9c,
+ 0x19, 0x2f, 0x94, 0x9b, 0xba, 0x68, 0x14, 0xe7, 0x75, 0xe4, 0x9b, 0xad,
+ 0xf2, 0x65, 0xa7, 0x68, 0x13, 0xf9, 0xca, 0x1d, 0x6b, 0x8e, 0x1c, 0x94,
+ 0x62, 0x10, 0x70, 0x4c, 0x14, 0x6a, 0xc5, 0xa9, 0x64, 0x47, 0xc5, 0xc4,
+ 0xde, 0xb3, 0x0d, 0x04, 0x1f, 0xfd, 0xa0, 0xae, 0x3f, 0x68, 0x59, 0xde,
+ 0xaf, 0x4a, 0xb9, 0xd2, 0xcb, 0x8f, 0x09, 0xec, 0xdf, 0x8c, 0xb9, 0x34,
+ 0x3a, 0x0f, 0x5e, 0xe6, 0xbd, 0x97, 0x79, 0xf3, 0xe2, 0xef, 0x19, 0x6a,
+ 0x8a, 0x97, 0x19, 0x7d, 0xd1, 0xf9, 0x04, 0x41, 0xfc, 0x93, 0x7a, 0x54,
+ 0x44, 0x42, 0xa7, 0xbc, 0xe7, 0x79, 0xa9, 0x9e, 0x68, 0xd8, 0x1c, 0x5d,
+ 0x39, 0x5d, 0x97, 0xbb, 0xbb, 0xf1, 0x14, 0xb7, 0xb5, 0x0c, 0xf3, 0xbc,
+ 0x11, 0x9e, 0xdc, 0x67, 0xb1, 0xdf, 0xd4, 0x91, 0x68, 0xe5, 0xdf, 0x6d,
+ 0xac, 0x69, 0xf3, 0xb9, 0x56, 0x87, 0x25, 0xe4, 0xcc, 0x8d, 0x86, 0xd1,
+ 0x38, 0xce, 0x93, 0xec, 0x76, 0xc1, 0xc4, 0x01, 0x44, 0x1e, 0x13, 0x16,
+ 0x90, 0x73, 0x5e, 0x5d, 0x80, 0x4d, 0x42, 0x69, 0x68, 0x5b, 0xaa, 0x4d,
+ 0x0f, 0xf5, 0x38, 0x2e, 0x0e, 0xd6, 0x28, 0x9d, 0xd4, 0xb9, 0x5a, 0xd1,
+ 0x4d, 0x08, 0x39, 0xb6, 0xf4, 0x47, 0x9c, 0x82, 0x48, 0xe6, 0xb4, 0x71,
+ 0x8e, 0x0d, 0x05, 0xcd, 0x15, 0x48, 0x8b, 0xe7, 0xae, 0x12, 0xfd, 0x24,
+ 0xe8, 0x6d, 0x7a, 0xcf, 0xbf, 0x1e, 0x4e, 0x7e, 0x6d, 0x87, 0xf3, 0xaf,
+ 0x56, 0x8f, 0x4e, 0x12, 0x42, 0x4f, 0xb4, 0x7c, 0x5b, 0xfd, 0x10, 0xf8,
+ 0x78, 0xe3, 0x9b, 0x52, 0x3d, 0xcd, 0xa9, 0x99, 0x75, 0xb0, 0xc3, 0xc9,
+ 0xfd, 0x24,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120024505 (0x7276db9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Nov 30 16:35:21 2010 GMT
+ Not After : Aug 10 15:34:26 2018 GMT
+ Subject: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79:
+ d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a:
+ 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2:
+ 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01:
+ 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7:
+ 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6:
+ 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c:
+ a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70:
+ 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77:
+ d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae:
+ 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18:
+ 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85:
+ ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9:
+ 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5:
+ c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a:
+ ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0:
+ 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27:
+ 1a:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+ Signature Algorithm: sha1WithRSAEncryption
+ 16:b4:2c:c9:f1:5e:e1:a2:7b:9b:78:20:7a:4a:70:70:86:19:
+ 00:b7:05:2a:e8:c9:25:39:0f:c3:64:3c:75:09:d9:89:15:80:
+ 07:c2:8d:bc:29:a5:64:50:cf:71:75:47:23:bd:4d:d8:7f:77:
+ 9a:51:10:6e:4e:1f:20:3c:47:9c:43:74:7f:96:84:10:4c:13:
+ 43:be:f8:e0:72:2e:ff:bf:ae:3c:0a:03:60:82:4b:6f:f9:9a:
+ c5:1e:f6:af:90:3b:9f:61:3b:3e:de:9b:05:1a:c6:2c:3c:57:
+ 21:08:0f:54:fa:28:63:6c:e8:1b:9c:0f:cf:dd:30:44:13:b9:
+ 57:fe
+-----BEGIN CERTIFICATE-----
+MIIEODCCA6GgAwIBAgIEBydtuTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEwMTEzMDE2MzUyMVoXDTE4MDgxMDE1MzQyNlowWjELMAkG
+A1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVz
+dDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uO
+KymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnn
+c+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPg
+kAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFc
+B5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaOCAWow
+ggFmMBIGA1UdEwEB/wQIMAYBAf8CAQMwTgYDVR0gBEcwRTBDBgRVHSAAMDswOQYI
+KwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9yZXBvc2l0
+b3J5LmNmbTAOBgNVHQ8BAf8EBAMCAQYwgYkGA1UdIwSBgTB/oXmkdzB1MQswCQYD
+VQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUg
+Q3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRy
+dXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vd3d3
+LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwvMjAxOC9jZHAuY3JsMB0GA1Ud
+DgQWBBTlnVkwgkdYzKz6CFQ2hns6tQRN8DANBgkqhkiG9w0BAQUFAAOBgQAWtCzJ
+8V7honubeCB6SnBwhhkAtwUq6MklOQ/DZDx1CdmJFYAHwo28KaVkUM9xdUcjvU3Y
+f3eaURBuTh8gPEecQ3R/loQQTBNDvvjgci7/v648CgNggktv+ZrFHvavkDufYTs+
+3psFGsYsPFchCA9U+ihjbOgbnA/P3TBEE7lX/g==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert21[] = {
+ 0x30, 0x82, 0x04, 0x38, 0x30, 0x82, 0x03, 0xa1, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x6d, 0xb9, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x31, 0x31, 0x33, 0x30, 0x31, 0x36, 0x33, 0x35, 0x32,
+ 0x31, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x38, 0x31, 0x30, 0x31, 0x35,
+ 0x33, 0x34, 0x32, 0x36, 0x5a, 0x30, 0x5a, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x45, 0x31, 0x12, 0x30, 0x10,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x42, 0x61, 0x6c, 0x74, 0x69,
+ 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
+ 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3, 0x04,
+ 0xbb, 0x22, 0xab, 0x98, 0x3d, 0x57, 0xe8, 0x26, 0x72, 0x9a, 0xb5, 0x79,
+ 0xd4, 0x29, 0xe2, 0xe1, 0xe8, 0x95, 0x80, 0xb1, 0xb0, 0xe3, 0x5b, 0x8e,
+ 0x2b, 0x29, 0x9a, 0x64, 0xdf, 0xa1, 0x5d, 0xed, 0xb0, 0x09, 0x05, 0x6d,
+ 0xdb, 0x28, 0x2e, 0xce, 0x62, 0xa2, 0x62, 0xfe, 0xb4, 0x88, 0xda, 0x12,
+ 0xeb, 0x38, 0xeb, 0x21, 0x9d, 0xc0, 0x41, 0x2b, 0x01, 0x52, 0x7b, 0x88,
+ 0x77, 0xd3, 0x1c, 0x8f, 0xc7, 0xba, 0xb9, 0x88, 0xb5, 0x6a, 0x09, 0xe7,
+ 0x73, 0xe8, 0x11, 0x40, 0xa7, 0xd1, 0xcc, 0xca, 0x62, 0x8d, 0x2d, 0xe5,
+ 0x8f, 0x0b, 0xa6, 0x50, 0xd2, 0xa8, 0x50, 0xc3, 0x28, 0xea, 0xf5, 0xab,
+ 0x25, 0x87, 0x8a, 0x9a, 0x96, 0x1c, 0xa9, 0x67, 0xb8, 0x3f, 0x0c, 0xd5,
+ 0xf7, 0xf9, 0x52, 0x13, 0x2f, 0xc2, 0x1b, 0xd5, 0x70, 0x70, 0xf0, 0x8f,
+ 0xc0, 0x12, 0xca, 0x06, 0xcb, 0x9a, 0xe1, 0xd9, 0xca, 0x33, 0x7a, 0x77,
+ 0xd6, 0xf8, 0xec, 0xb9, 0xf1, 0x68, 0x44, 0x42, 0x48, 0x13, 0xd2, 0xc0,
+ 0xc2, 0xa4, 0xae, 0x5e, 0x60, 0xfe, 0xb6, 0xa6, 0x05, 0xfc, 0xb4, 0xdd,
+ 0x07, 0x59, 0x02, 0xd4, 0x59, 0x18, 0x98, 0x63, 0xf5, 0xa5, 0x63, 0xe0,
+ 0x90, 0x0c, 0x7d, 0x5d, 0xb2, 0x06, 0x7a, 0xf3, 0x85, 0xea, 0xeb, 0xd4,
+ 0x03, 0xae, 0x5e, 0x84, 0x3e, 0x5f, 0xff, 0x15, 0xed, 0x69, 0xbc, 0xf9,
+ 0x39, 0x36, 0x72, 0x75, 0xcf, 0x77, 0x52, 0x4d, 0xf3, 0xc9, 0x90, 0x2c,
+ 0xb9, 0x3d, 0xe5, 0xc9, 0x23, 0x53, 0x3f, 0x1f, 0x24, 0x98, 0x21, 0x5c,
+ 0x07, 0x99, 0x29, 0xbd, 0xc6, 0x3a, 0xec, 0xe7, 0x6e, 0x86, 0x3a, 0x6b,
+ 0x97, 0x74, 0x63, 0x33, 0xbd, 0x68, 0x18, 0x31, 0xf0, 0x78, 0x8d, 0x76,
+ 0xbf, 0xfc, 0x9e, 0x8e, 0x5d, 0x2a, 0x86, 0xa7, 0x4d, 0x90, 0xdc, 0x27,
+ 0x1a, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x6a, 0x30,
+ 0x82, 0x01, 0x66, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x03, 0x30,
+ 0x4e, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x47, 0x30, 0x45, 0x30, 0x43,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f,
+ 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f,
+ 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30,
+ 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20,
+ 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53,
+ 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36,
+ 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69,
+ 0x6e, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63,
+ 0x64, 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58,
+ 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d,
+ 0xf0, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x16, 0xb4, 0x2c, 0xc9,
+ 0xf1, 0x5e, 0xe1, 0xa2, 0x7b, 0x9b, 0x78, 0x20, 0x7a, 0x4a, 0x70, 0x70,
+ 0x86, 0x19, 0x00, 0xb7, 0x05, 0x2a, 0xe8, 0xc9, 0x25, 0x39, 0x0f, 0xc3,
+ 0x64, 0x3c, 0x75, 0x09, 0xd9, 0x89, 0x15, 0x80, 0x07, 0xc2, 0x8d, 0xbc,
+ 0x29, 0xa5, 0x64, 0x50, 0xcf, 0x71, 0x75, 0x47, 0x23, 0xbd, 0x4d, 0xd8,
+ 0x7f, 0x77, 0x9a, 0x51, 0x10, 0x6e, 0x4e, 0x1f, 0x20, 0x3c, 0x47, 0x9c,
+ 0x43, 0x74, 0x7f, 0x96, 0x84, 0x10, 0x4c, 0x13, 0x43, 0xbe, 0xf8, 0xe0,
+ 0x72, 0x2e, 0xff, 0xbf, 0xae, 0x3c, 0x0a, 0x03, 0x60, 0x82, 0x4b, 0x6f,
+ 0xf9, 0x9a, 0xc5, 0x1e, 0xf6, 0xaf, 0x90, 0x3b, 0x9f, 0x61, 0x3b, 0x3e,
+ 0xde, 0x9b, 0x05, 0x1a, 0xc6, 0x2c, 0x3c, 0x57, 0x21, 0x08, 0x0f, 0x54,
+ 0xfa, 0x28, 0x63, 0x6c, 0xe8, 0x1b, 0x9c, 0x0f, 0xcf, 0xdd, 0x30, 0x44,
+ 0x13, 0xb9, 0x57, 0xfe,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 48:4b:ac:f1:aa:c7:d7:13:43:d1:a2:74:35:49:97:25
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Jun 7 08:09:10 2005 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b1:f7:c3:38:3f:b4:a8:7f:cf:39:82:51:67:d0:
+ 6d:9f:d2:ff:58:f3:e7:9f:2b:ec:0d:89:54:99:b9:
+ 38:99:16:f7:e0:21:79:48:c2:bb:61:74:12:96:1d:
+ 3c:6a:72:d5:3c:10:67:3a:39:ed:2b:13:cd:66:eb:
+ 95:09:33:a4:6c:97:b1:e8:c6:ec:c1:75:79:9c:46:
+ 5e:8d:ab:d0:6a:fd:b9:2a:55:17:10:54:b3:19:f0:
+ 9a:f6:f1:b1:5d:b6:a7:6d:fb:e0:71:17:6b:a2:88:
+ fb:00:df:fe:1a:31:77:0c:9a:01:7a:b1:32:e3:2b:
+ 01:07:38:6e:c3:a5:5e:23:bc:45:9b:7b:50:c1:c9:
+ 30:8f:db:e5:2b:7a:d3:5b:fb:33:40:1e:a0:d5:98:
+ 17:bc:8b:87:c3:89:d3:5d:a0:8e:b2:aa:aa:f6:8e:
+ 69:88:06:c5:fa:89:21:f3:08:9d:69:2e:09:33:9b:
+ 29:0d:46:0f:8c:cc:49:34:b0:69:51:bd:f9:06:cd:
+ 68:ad:66:4c:bc:3e:ac:61:bd:0a:88:0e:c8:df:3d:
+ ee:7c:04:4c:9d:0a:5e:6b:91:d6:ee:c7:ed:28:8d:
+ ab:4d:87:89:73:d0:6e:a4:d0:1e:16:8b:14:e1:76:
+ 44:03:7f:63:ac:e4:cd:49:9c:c5:92:f4:ab:32:a1:
+ 48:5b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 3c:ec:7b:e0:ae:a3:0e:96:6d:30:d7:85:c6:d2:68:5b:45:5a:
+ 82:a6:34:0f:b0:c9:92:23:5e:11:6d:08:11:b2:74:09:23:3a:
+ 35:25:73:58:5e:ca:b9:7c:28:fa:47:ec:f9:a0:03:58:50:b6:
+ 53:ef:8c:db:39:e4:67:e9:d8:ca:28:46:d4:a7:e0:f5:38:75:
+ f8:e7:cb:5c:bf:1d:11:3c:6a:40:9b:2d:44:56:d3:f7:ff:05:
+ 28:32:0c:15:c8:64:45:93:e8:21:24:8f:2d:da:7a:84:7b:4f:
+ cf:cd:b2:25:7c:77:10:d3:94:d1:04:91:a8:25:1c:09:22:0f:
+ 7d:44:35:11:14:ef:af:00:fe:5e:ea:5f:8e:b0:d9:92:59:ba:
+ fc:13:96:a0:18:01:56:ce:da:f6:28:0b:b1:af:dd:5c:4f:5c:
+ b2:f3:8f:5a:71:cf:ed:18:ad:63:88:1d:8e:95:f7:ea:95:e7:
+ 1f:ad:90:b8:84:08:47:85:7f:22:2f:1a:1d:48:30:d6:4c:08:
+ d8:37:19:67:32:2b:eb:5c:d0:b2:fc:6e:57:9f:04:35:5e:90:
+ 00:7e:11:c7:de:13:2a:cd:a4:6d:45:26:c7:88:56:a0:f0:6a:
+ f7:d8:e7:fc:27:7e:67:08:d0:bd:fa:b6:c3:61:02:01:65:b9:
+ b8:2f:cf:5a
+-----BEGIN CERTIFICATE-----
+MIIEPDCCAySgAwIBAgIQSEus8arH1xND0aJ0NUmXJTANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTA1MDYwNzA4MDkxMFoXDTIwMDUzMDEwNDgzOFow
+gZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl
+IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY
+aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR8wHQYDVQQDExZVVE4tVVNFUkZpcnN0
+LUhhcmR3YXJlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsffDOD+0
+qH/POYJRZ9Btn9L/WPPnnyvsDYlUmbk4mRb34CF5SMK7YXQSlh08anLVPBBnOjnt
+KxPNZuuVCTOkbJex6MbswXV5nEZejavQav25KlUXEFSzGfCa9vGxXbanbfvgcRdr
+ooj7AN/+GjF3DJoBerEy4ysBBzhuw6VeI7xFm3tQwckwj9vlK3rTW/szQB6g1ZgX
+vIuHw4nTXaCOsqqq9o5piAbF+okh8widaS4JM5spDUYPjMxJNLBpUb35Bs1orWZM
+vD6sYb0KiA7I3z3ufARMnQpea5HW7sftKI2rTYeJc9BupNAeFosU4XZEA39jrOTN
+SZzFkvSrMqFIWwIDAQABo4GqMIGnMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8D
+veAky1QaMB0GA1UdDgQWBBShcl8mGyiYQ5VdBzfVhZadS9LDRTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8v
+Y3JsLnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5jcmwwDQYJ
+KoZIhvcNAQEFBQADggEBADzse+Cuow6WbTDXhcbSaFtFWoKmNA+wyZIjXhFtCBGy
+dAkjOjUlc1heyrl8KPpH7PmgA1hQtlPvjNs55Gfp2MooRtSn4PU4dfjny1y/HRE8
+akCbLURW0/f/BSgyDBXIZEWT6CEkjy3aeoR7T8/NsiV8dxDTlNEEkaglHAkiD31E
+NREU768A/l7qX46w2ZJZuvwTlqAYAVbO2vYoC7Gv3VxPXLLzj1pxz+0YrWOIHY6V
+9+qV5x+tkLiECEeFfyIvGh1IMNZMCNg3GWcyK+tc0LL8blefBDVekAB+EcfeEyrN
+pG1FJseIVqDwavfY5/wnfmcI0L36tsNhAgFlubgvz1o=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert22[] = {
+ 0x30, 0x82, 0x04, 0x3c, 0x30, 0x82, 0x03, 0x24, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x48, 0x4b, 0xac, 0xf1, 0xaa, 0xc7, 0xd7, 0x13, 0x43,
+ 0xd1, 0xa2, 0x74, 0x35, 0x49, 0x97, 0x25, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x35, 0x30, 0x36, 0x30,
+ 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65,
+ 0x20, 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52,
+ 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75,
+ 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55,
+ 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74,
+ 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0xf7, 0xc3, 0x38, 0x3f, 0xb4,
+ 0xa8, 0x7f, 0xcf, 0x39, 0x82, 0x51, 0x67, 0xd0, 0x6d, 0x9f, 0xd2, 0xff,
+ 0x58, 0xf3, 0xe7, 0x9f, 0x2b, 0xec, 0x0d, 0x89, 0x54, 0x99, 0xb9, 0x38,
+ 0x99, 0x16, 0xf7, 0xe0, 0x21, 0x79, 0x48, 0xc2, 0xbb, 0x61, 0x74, 0x12,
+ 0x96, 0x1d, 0x3c, 0x6a, 0x72, 0xd5, 0x3c, 0x10, 0x67, 0x3a, 0x39, 0xed,
+ 0x2b, 0x13, 0xcd, 0x66, 0xeb, 0x95, 0x09, 0x33, 0xa4, 0x6c, 0x97, 0xb1,
+ 0xe8, 0xc6, 0xec, 0xc1, 0x75, 0x79, 0x9c, 0x46, 0x5e, 0x8d, 0xab, 0xd0,
+ 0x6a, 0xfd, 0xb9, 0x2a, 0x55, 0x17, 0x10, 0x54, 0xb3, 0x19, 0xf0, 0x9a,
+ 0xf6, 0xf1, 0xb1, 0x5d, 0xb6, 0xa7, 0x6d, 0xfb, 0xe0, 0x71, 0x17, 0x6b,
+ 0xa2, 0x88, 0xfb, 0x00, 0xdf, 0xfe, 0x1a, 0x31, 0x77, 0x0c, 0x9a, 0x01,
+ 0x7a, 0xb1, 0x32, 0xe3, 0x2b, 0x01, 0x07, 0x38, 0x6e, 0xc3, 0xa5, 0x5e,
+ 0x23, 0xbc, 0x45, 0x9b, 0x7b, 0x50, 0xc1, 0xc9, 0x30, 0x8f, 0xdb, 0xe5,
+ 0x2b, 0x7a, 0xd3, 0x5b, 0xfb, 0x33, 0x40, 0x1e, 0xa0, 0xd5, 0x98, 0x17,
+ 0xbc, 0x8b, 0x87, 0xc3, 0x89, 0xd3, 0x5d, 0xa0, 0x8e, 0xb2, 0xaa, 0xaa,
+ 0xf6, 0x8e, 0x69, 0x88, 0x06, 0xc5, 0xfa, 0x89, 0x21, 0xf3, 0x08, 0x9d,
+ 0x69, 0x2e, 0x09, 0x33, 0x9b, 0x29, 0x0d, 0x46, 0x0f, 0x8c, 0xcc, 0x49,
+ 0x34, 0xb0, 0x69, 0x51, 0xbd, 0xf9, 0x06, 0xcd, 0x68, 0xad, 0x66, 0x4c,
+ 0xbc, 0x3e, 0xac, 0x61, 0xbd, 0x0a, 0x88, 0x0e, 0xc8, 0xdf, 0x3d, 0xee,
+ 0x7c, 0x04, 0x4c, 0x9d, 0x0a, 0x5e, 0x6b, 0x91, 0xd6, 0xee, 0xc7, 0xed,
+ 0x28, 0x8d, 0xab, 0x4d, 0x87, 0x89, 0x73, 0xd0, 0x6e, 0xa4, 0xd0, 0x1e,
+ 0x16, 0x8b, 0x14, 0xe1, 0x76, 0x44, 0x03, 0x7f, 0x63, 0xac, 0xe4, 0xcd,
+ 0x49, 0x9c, 0xc5, 0x92, 0xf4, 0xab, 0x32, 0xa1, 0x48, 0x5b, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xaa, 0x30, 0x81, 0xa7, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd,
+ 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03,
+ 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98,
+ 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5, 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3,
+ 0x45, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x44,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0,
+ 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41,
+ 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x3c, 0xec, 0x7b, 0xe0, 0xae, 0xa3, 0x0e, 0x96,
+ 0x6d, 0x30, 0xd7, 0x85, 0xc6, 0xd2, 0x68, 0x5b, 0x45, 0x5a, 0x82, 0xa6,
+ 0x34, 0x0f, 0xb0, 0xc9, 0x92, 0x23, 0x5e, 0x11, 0x6d, 0x08, 0x11, 0xb2,
+ 0x74, 0x09, 0x23, 0x3a, 0x35, 0x25, 0x73, 0x58, 0x5e, 0xca, 0xb9, 0x7c,
+ 0x28, 0xfa, 0x47, 0xec, 0xf9, 0xa0, 0x03, 0x58, 0x50, 0xb6, 0x53, 0xef,
+ 0x8c, 0xdb, 0x39, 0xe4, 0x67, 0xe9, 0xd8, 0xca, 0x28, 0x46, 0xd4, 0xa7,
+ 0xe0, 0xf5, 0x38, 0x75, 0xf8, 0xe7, 0xcb, 0x5c, 0xbf, 0x1d, 0x11, 0x3c,
+ 0x6a, 0x40, 0x9b, 0x2d, 0x44, 0x56, 0xd3, 0xf7, 0xff, 0x05, 0x28, 0x32,
+ 0x0c, 0x15, 0xc8, 0x64, 0x45, 0x93, 0xe8, 0x21, 0x24, 0x8f, 0x2d, 0xda,
+ 0x7a, 0x84, 0x7b, 0x4f, 0xcf, 0xcd, 0xb2, 0x25, 0x7c, 0x77, 0x10, 0xd3,
+ 0x94, 0xd1, 0x04, 0x91, 0xa8, 0x25, 0x1c, 0x09, 0x22, 0x0f, 0x7d, 0x44,
+ 0x35, 0x11, 0x14, 0xef, 0xaf, 0x00, 0xfe, 0x5e, 0xea, 0x5f, 0x8e, 0xb0,
+ 0xd9, 0x92, 0x59, 0xba, 0xfc, 0x13, 0x96, 0xa0, 0x18, 0x01, 0x56, 0xce,
+ 0xda, 0xf6, 0x28, 0x0b, 0xb1, 0xaf, 0xdd, 0x5c, 0x4f, 0x5c, 0xb2, 0xf3,
+ 0x8f, 0x5a, 0x71, 0xcf, 0xed, 0x18, 0xad, 0x63, 0x88, 0x1d, 0x8e, 0x95,
+ 0xf7, 0xea, 0x95, 0xe7, 0x1f, 0xad, 0x90, 0xb8, 0x84, 0x08, 0x47, 0x85,
+ 0x7f, 0x22, 0x2f, 0x1a, 0x1d, 0x48, 0x30, 0xd6, 0x4c, 0x08, 0xd8, 0x37,
+ 0x19, 0x67, 0x32, 0x2b, 0xeb, 0x5c, 0xd0, 0xb2, 0xfc, 0x6e, 0x57, 0x9f,
+ 0x04, 0x35, 0x5e, 0x90, 0x00, 0x7e, 0x11, 0xc7, 0xde, 0x13, 0x2a, 0xcd,
+ 0xa4, 0x6d, 0x45, 0x26, 0xc7, 0x88, 0x56, 0xa0, 0xf0, 0x6a, 0xf7, 0xd8,
+ 0xe7, 0xfc, 0x27, 0x7e, 0x67, 0x08, 0xd0, 0xbd, 0xfa, 0xb6, 0xc3, 0x61,
+ 0x02, 0x01, 0x65, 0xb9, 0xb8, 0x2f, 0xcf, 0x5a,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120001525 (0x72713f5)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Jan 17 15:16:20 2007 GMT
+ Not After : Jan 17 15:15:46 2014 GMT
+ Subject: DC=ru, DC=yandex, DC=ld, CN=YandexExternalCA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ce:7f:78:9c:38:3c:99:97:b3:5c:22:91:ca:b8:
+ 94:39:a2:5e:57:fe:ba:15:1c:98:86:d2:cd:b0:bb:
+ 7b:d7:62:6c:84:61:31:50:2c:63:35:aa:bf:f2:8c:
+ e0:0a:9c:db:70:a5:03:2b:f7:cf:aa:b8:ee:d2:5a:
+ cf:13:be:dc:53:fa:67:fb:e6:5a:6d:46:e0:f6:25:
+ ac:03:d9:5a:e4:aa:af:e0:bf:dd:8b:d2:5c:a0:ea:
+ f7:e6:5a:0a:2f:5a:11:9f:b4:a8:f2:e9:2f:0b:3d:
+ 31:a1:b3:2a:5f:3c:4b:c2:8c:1c:c6:dc:87:32:22:
+ 55:0f:4b:fe:15:22:f9:39:85:72:cd:16:5b:d1:f6:
+ 23:e3:31:9e:8f:7e:cd:4c:7d:4f:86:c2:e7:41:5a:
+ 41:b8:1d:e7:d2:4d:ca:ec:25:5e:23:fe:5f:de:39:
+ 12:24:09:cd:fa:c9:65:93:26:b0:94:4d:38:a0:c7:
+ 9d:2a:79:18:e2:1f:a0:2a:f1:4c:44:85:a3:4d:53:
+ a1:91:3a:01:10:c9:aa:c3:4f:49:fb:f1:9b:b8:bf:
+ cf:d2:e9:b4:41:84:bf:aa:c8:33:13:50:3b:97:cc:
+ bb:1e:0c:da:f9:8b:5c:3c:83:a3:59:f5:76:ef:98:
+ c1:78:7e:5e:52:18:02:8a:36:d2:c5:c5:f7:83:aa:
+ ca:17
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://www.public-trust.com/CPS/OmniRoot.html
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ DB:41:27:30:4F:1A:F5:5B:3E:84:56:C8:EC:85:98:B3:51:2C:2D:27
+ Signature Algorithm: sha1WithRSAEncryption
+ 19:b8:d2:c4:39:b0:e5:1d:d5:b7:40:96:e8:92:ae:40:36:b4:
+ e9:f7:f5:8b:2d:d4:4e:36:31:4a:d2:d3:e4:1e:ae:45:8d:ec:
+ 97:e0:68:0f:56:f0:14:4e:e4:1a:c9:d0:b7:e6:7c:fb:1f:ed:
+ 52:19:90:69:f4:5f:a9:4f:d6:27:68:d1:fa:94:a9:7b:a3:c9:
+ 97:3c:e0:b3:9d:06:1e:22:f1:82:80:8e:0b:d6:eb:f7:ed:0b:
+ 41:bd:ba:e2:07:f2:3c:87:e1:58:ff:8d:c5:32:30:27:93:d7:
+ 22:47:5c:60:6c:04:4a:e1:b5:0a:65:a3:dd:f4:c7:54:fb:f4:
+ d8:ef
+-----BEGIN CERTIFICATE-----
+MIIEPjCCA6egAwIBAgIEBycT9TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTA3MDExNzE1MTYyMFoXDTE0MDExNzE1MTU0NlowWzESMBAG
+CgmSJomT8ixkARkWAnJ1MRYwFAYKCZImiZPyLGQBGRYGeWFuZGV4MRIwEAYKCZIm
+iZPyLGQBGRYCbGQxGTAXBgNVBAMTEFlhbmRleEV4dGVybmFsQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOf3icODyZl7NcIpHKuJQ5ol5X/roVHJiG
+0s2wu3vXYmyEYTFQLGM1qr/yjOAKnNtwpQMr98+quO7SWs8TvtxT+mf75lptRuD2
+JawD2Vrkqq/gv92L0lyg6vfmWgovWhGftKjy6S8LPTGhsypfPEvCjBzG3IcyIlUP
+S/4VIvk5hXLNFlvR9iPjMZ6Pfs1MfU+GwudBWkG4HefSTcrsJV4j/l/eORIkCc36
+yWWTJrCUTTigx50qeRjiH6Aq8UxEhaNNU6GROgEQyarDT0n78Zu4v8/S6bRBhL+q
+yDMTUDuXzLseDNr5i1w8g6NZ9XbvmMF4fl5SGAKKNtLFxfeDqsoXAgMBAAGjggFv
+MIIBazASBgNVHRMBAf8ECDAGAQH/AgEBMFMGA1UdIARMMEowSAYJKwYBBAGxPgEA
+MDswOQYIKwYBBQUHAgEWLWh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9DUFMv
+T21uaVJvb3QuaHRtbDAOBgNVHQ8BAf8EBAMCAYYwgYkGA1UdIwSBgTB/oXmkdzB1
+MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL
+Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBD
+eWJlclRydXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8MDqgOKA2hjRodHRw
+Oi8vd3d3LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwvMjAxOC9jZHAuY3Js
+MB0GA1UdDgQWBBTbQScwTxr1Wz6EVsjshZizUSwtJzANBgkqhkiG9w0BAQUFAAOB
+gQAZuNLEObDlHdW3QJbokq5ANrTp9/WLLdRONjFK0tPkHq5FjeyX4GgPVvAUTuQa
+ydC35nz7H+1SGZBp9F+pT9YnaNH6lKl7o8mXPOCznQYeIvGCgI4L1uv37QtBvbri
+B/I8h+FY/43FMjAnk9ciR1xgbARK4bUKZaPd9MdU+/TY7w==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert23[] = {
+ 0x30, 0x82, 0x04, 0x3e, 0x30, 0x82, 0x03, 0xa7, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x13, 0xf5, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x30, 0x37, 0x30, 0x31, 0x31, 0x37, 0x31, 0x35, 0x31, 0x36, 0x32,
+ 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x31, 0x37, 0x31, 0x35,
+ 0x31, 0x35, 0x34, 0x36, 0x5a, 0x30, 0x5b, 0x31, 0x12, 0x30, 0x10, 0x06,
+ 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16,
+ 0x02, 0x72, 0x75, 0x31, 0x16, 0x30, 0x14, 0x06, 0x0a, 0x09, 0x92, 0x26,
+ 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x06, 0x79, 0x61, 0x6e,
+ 0x64, 0x65, 0x78, 0x31, 0x12, 0x30, 0x10, 0x06, 0x0a, 0x09, 0x92, 0x26,
+ 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x02, 0x6c, 0x64, 0x31,
+ 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x59, 0x61,
+ 0x6e, 0x64, 0x65, 0x78, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce,
+ 0x7f, 0x78, 0x9c, 0x38, 0x3c, 0x99, 0x97, 0xb3, 0x5c, 0x22, 0x91, 0xca,
+ 0xb8, 0x94, 0x39, 0xa2, 0x5e, 0x57, 0xfe, 0xba, 0x15, 0x1c, 0x98, 0x86,
+ 0xd2, 0xcd, 0xb0, 0xbb, 0x7b, 0xd7, 0x62, 0x6c, 0x84, 0x61, 0x31, 0x50,
+ 0x2c, 0x63, 0x35, 0xaa, 0xbf, 0xf2, 0x8c, 0xe0, 0x0a, 0x9c, 0xdb, 0x70,
+ 0xa5, 0x03, 0x2b, 0xf7, 0xcf, 0xaa, 0xb8, 0xee, 0xd2, 0x5a, 0xcf, 0x13,
+ 0xbe, 0xdc, 0x53, 0xfa, 0x67, 0xfb, 0xe6, 0x5a, 0x6d, 0x46, 0xe0, 0xf6,
+ 0x25, 0xac, 0x03, 0xd9, 0x5a, 0xe4, 0xaa, 0xaf, 0xe0, 0xbf, 0xdd, 0x8b,
+ 0xd2, 0x5c, 0xa0, 0xea, 0xf7, 0xe6, 0x5a, 0x0a, 0x2f, 0x5a, 0x11, 0x9f,
+ 0xb4, 0xa8, 0xf2, 0xe9, 0x2f, 0x0b, 0x3d, 0x31, 0xa1, 0xb3, 0x2a, 0x5f,
+ 0x3c, 0x4b, 0xc2, 0x8c, 0x1c, 0xc6, 0xdc, 0x87, 0x32, 0x22, 0x55, 0x0f,
+ 0x4b, 0xfe, 0x15, 0x22, 0xf9, 0x39, 0x85, 0x72, 0xcd, 0x16, 0x5b, 0xd1,
+ 0xf6, 0x23, 0xe3, 0x31, 0x9e, 0x8f, 0x7e, 0xcd, 0x4c, 0x7d, 0x4f, 0x86,
+ 0xc2, 0xe7, 0x41, 0x5a, 0x41, 0xb8, 0x1d, 0xe7, 0xd2, 0x4d, 0xca, 0xec,
+ 0x25, 0x5e, 0x23, 0xfe, 0x5f, 0xde, 0x39, 0x12, 0x24, 0x09, 0xcd, 0xfa,
+ 0xc9, 0x65, 0x93, 0x26, 0xb0, 0x94, 0x4d, 0x38, 0xa0, 0xc7, 0x9d, 0x2a,
+ 0x79, 0x18, 0xe2, 0x1f, 0xa0, 0x2a, 0xf1, 0x4c, 0x44, 0x85, 0xa3, 0x4d,
+ 0x53, 0xa1, 0x91, 0x3a, 0x01, 0x10, 0xc9, 0xaa, 0xc3, 0x4f, 0x49, 0xfb,
+ 0xf1, 0x9b, 0xb8, 0xbf, 0xcf, 0xd2, 0xe9, 0xb4, 0x41, 0x84, 0xbf, 0xaa,
+ 0xc8, 0x33, 0x13, 0x50, 0x3b, 0x97, 0xcc, 0xbb, 0x1e, 0x0c, 0xda, 0xf9,
+ 0x8b, 0x5c, 0x3c, 0x83, 0xa3, 0x59, 0xf5, 0x76, 0xef, 0x98, 0xc1, 0x78,
+ 0x7e, 0x5e, 0x52, 0x18, 0x02, 0x8a, 0x36, 0xd2, 0xc5, 0xc5, 0xf7, 0x83,
+ 0xaa, 0xca, 0x17, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x6f,
+ 0x30, 0x82, 0x01, 0x6b, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01,
+ 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30,
+ 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00,
+ 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x2f,
+ 0x4f, 0x6d, 0x6e, 0x69, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x68, 0x74, 0x6d,
+ 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01,
+ 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e, 0x30, 0x3c,
+ 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69,
+ 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52, 0x4c, 0x2f,
+ 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xdb,
+ 0x41, 0x27, 0x30, 0x4f, 0x1a, 0xf5, 0x5b, 0x3e, 0x84, 0x56, 0xc8, 0xec,
+ 0x85, 0x98, 0xb3, 0x51, 0x2c, 0x2d, 0x27, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
+ 0x81, 0x00, 0x19, 0xb8, 0xd2, 0xc4, 0x39, 0xb0, 0xe5, 0x1d, 0xd5, 0xb7,
+ 0x40, 0x96, 0xe8, 0x92, 0xae, 0x40, 0x36, 0xb4, 0xe9, 0xf7, 0xf5, 0x8b,
+ 0x2d, 0xd4, 0x4e, 0x36, 0x31, 0x4a, 0xd2, 0xd3, 0xe4, 0x1e, 0xae, 0x45,
+ 0x8d, 0xec, 0x97, 0xe0, 0x68, 0x0f, 0x56, 0xf0, 0x14, 0x4e, 0xe4, 0x1a,
+ 0xc9, 0xd0, 0xb7, 0xe6, 0x7c, 0xfb, 0x1f, 0xed, 0x52, 0x19, 0x90, 0x69,
+ 0xf4, 0x5f, 0xa9, 0x4f, 0xd6, 0x27, 0x68, 0xd1, 0xfa, 0x94, 0xa9, 0x7b,
+ 0xa3, 0xc9, 0x97, 0x3c, 0xe0, 0xb3, 0x9d, 0x06, 0x1e, 0x22, 0xf1, 0x82,
+ 0x80, 0x8e, 0x0b, 0xd6, 0xeb, 0xf7, 0xed, 0x0b, 0x41, 0xbd, 0xba, 0xe2,
+ 0x07, 0xf2, 0x3c, 0x87, 0xe1, 0x58, 0xff, 0x8d, 0xc5, 0x32, 0x30, 0x27,
+ 0x93, 0xd7, 0x22, 0x47, 0x5c, 0x60, 0x6c, 0x04, 0x4a, 0xe1, 0xb5, 0x0a,
+ 0x65, 0xa3, 0xdd, 0xf4, 0xc7, 0x54, 0xfb, 0xf4, 0xd8, 0xef,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1116160165 (0x428740a5)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
+ Validity
+ Not Before: Oct 1 05:00:00 2006 GMT
+ Not After : Jul 26 18:15:15 2014 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df:
+ e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67:
+ a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98:
+ a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a:
+ cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71:
+ 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be:
+ f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41:
+ 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d:
+ be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7:
+ ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41:
+ 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a:
+ 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4:
+ 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e:
+ 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79:
+ 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a:
+ a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e:
+ 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8:
+ 4b:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/server1.crl
+
+ X509v3 Subject Key Identifier:
+ B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+ X509v3 Key Usage:
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
+
+ 1.2.840.113533.7.65.0:
+ 0
+..V7.1....
+ Signature Algorithm: sha1WithRSAEncryption
+ 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8:
+ 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f:
+ 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f:
+ 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60:
+ a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5:
+ 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af:
+ 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3:
+ df:36
+-----BEGIN CERTIFICATE-----
+MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
+ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
+ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw
+MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
+EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV
+BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD
+1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt
+cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46
+OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd
+HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm
+t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET
+MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr
+BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo
+dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v
+Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU
+mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7
+UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF
+BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu
+w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1
+nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert24[] = {
+ 0x30, 0x82, 0x04, 0x42, 0x30, 0x82, 0x03, 0xab, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x42, 0x87, 0x40, 0xa5, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xc3, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e,
+ 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c,
+ 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x31, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x30,
+ 0x30, 0x31, 0x30, 0x35, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31,
+ 0x34, 0x30, 0x37, 0x32, 0x36, 0x31, 0x38, 0x31, 0x35, 0x31, 0x35, 0x5a,
+ 0x30, 0x6c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49,
+ 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74,
+ 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61,
+ 0x6e, 0x63, 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc6,
+ 0xcc, 0xe5, 0x73, 0xe6, 0xfb, 0xd4, 0xbb, 0xe5, 0x2d, 0x2d, 0x32, 0xa6,
+ 0xdf, 0xe5, 0x81, 0x3f, 0xc9, 0xcd, 0x25, 0x49, 0xb6, 0x71, 0x2a, 0xc3,
+ 0xd5, 0x94, 0x34, 0x67, 0xa2, 0x0a, 0x1c, 0xb0, 0x5f, 0x69, 0xa6, 0x40,
+ 0xb1, 0xc4, 0xb7, 0xb2, 0x8f, 0xd0, 0x98, 0xa4, 0xa9, 0x41, 0x59, 0x3a,
+ 0xd3, 0xdc, 0x94, 0xd6, 0x3c, 0xdb, 0x74, 0x38, 0xa4, 0x4a, 0xcc, 0x4d,
+ 0x25, 0x82, 0xf7, 0x4a, 0xa5, 0x53, 0x12, 0x38, 0xee, 0xf3, 0x49, 0x6d,
+ 0x71, 0x91, 0x7e, 0x63, 0xb6, 0xab, 0xa6, 0x5f, 0xc3, 0xa4, 0x84, 0xf8,
+ 0x4f, 0x62, 0x51, 0xbe, 0xf8, 0xc5, 0xec, 0xdb, 0x38, 0x92, 0xe3, 0x06,
+ 0xe5, 0x08, 0x91, 0x0c, 0xc4, 0x28, 0x41, 0x55, 0xfb, 0xcb, 0x5a, 0x89,
+ 0x15, 0x7e, 0x71, 0xe8, 0x35, 0xbf, 0x4d, 0x72, 0x09, 0x3d, 0xbe, 0x3a,
+ 0x38, 0x50, 0x5b, 0x77, 0x31, 0x1b, 0x8d, 0xb3, 0xc7, 0x24, 0x45, 0x9a,
+ 0xa7, 0xac, 0x6d, 0x00, 0x14, 0x5a, 0x04, 0xb7, 0xba, 0x13, 0xeb, 0x51,
+ 0x0a, 0x98, 0x41, 0x41, 0x22, 0x4e, 0x65, 0x61, 0x87, 0x81, 0x41, 0x50,
+ 0xa6, 0x79, 0x5c, 0x89, 0xde, 0x19, 0x4a, 0x57, 0xd5, 0x2e, 0xe6, 0x5d,
+ 0x1c, 0x53, 0x2c, 0x7e, 0x98, 0xcd, 0x1a, 0x06, 0x16, 0xa4, 0x68, 0x73,
+ 0xd0, 0x34, 0x04, 0x13, 0x5c, 0xa1, 0x71, 0xd3, 0x5a, 0x7c, 0x55, 0xdb,
+ 0x5e, 0x64, 0xe1, 0x37, 0x87, 0x30, 0x56, 0x04, 0xe5, 0x11, 0xb4, 0x29,
+ 0x80, 0x12, 0xf1, 0x79, 0x39, 0x88, 0xa2, 0x02, 0x11, 0x7c, 0x27, 0x66,
+ 0xb7, 0x88, 0xb7, 0x78, 0xf2, 0xca, 0x0a, 0xa8, 0x38, 0xab, 0x0a, 0x64,
+ 0xc2, 0xbf, 0x66, 0x5d, 0x95, 0x84, 0xc1, 0xa1, 0x25, 0x1e, 0x87, 0x5d,
+ 0x1a, 0x50, 0x0b, 0x20, 0x12, 0xcc, 0x41, 0xbb, 0x6e, 0x0b, 0x51, 0x38,
+ 0xb8, 0x4b, 0xcb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x13,
+ 0x30, 0x82, 0x01, 0x0f, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01,
+ 0x30, 0x27, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x20, 0x30, 0x1e, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x04, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x33,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0,
+ 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x31, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4,
+ 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0b,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0xf0, 0x17, 0x62, 0x13, 0x55, 0x3d, 0xb3, 0xff, 0x0a, 0x00, 0x6b, 0xfb,
+ 0x50, 0x84, 0x97, 0xf3, 0xed, 0x62, 0xd0, 0x1a, 0x30, 0x19, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf6, 0x7d, 0x07, 0x41, 0x00, 0x04, 0x0c, 0x30,
+ 0x0a, 0x1b, 0x04, 0x56, 0x37, 0x2e, 0x31, 0x03, 0x02, 0x00, 0x81, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x48, 0x0e, 0x2b, 0x6f, 0x20, 0x62,
+ 0x4c, 0x28, 0x93, 0xa3, 0x24, 0x3d, 0x58, 0xab, 0x21, 0xcf, 0x80, 0xf8,
+ 0x9a, 0x97, 0x90, 0x6a, 0x22, 0xed, 0x5a, 0x7c, 0x47, 0x36, 0x99, 0xe7,
+ 0x79, 0x84, 0x75, 0xab, 0x24, 0x8f, 0x92, 0x0a, 0xd5, 0x61, 0x04, 0xae,
+ 0xc3, 0x6a, 0x5c, 0xb2, 0xcc, 0xd9, 0xe4, 0x44, 0x87, 0x6f, 0xdb, 0x8f,
+ 0x38, 0x62, 0xf7, 0x44, 0x36, 0x9d, 0xba, 0xbc, 0x6e, 0x07, 0xc4, 0xd4,
+ 0x8d, 0xe8, 0x1f, 0xd1, 0x0b, 0x60, 0xa3, 0xb5, 0x9c, 0xce, 0x63, 0xbe,
+ 0xed, 0x67, 0xdc, 0xf8, 0xba, 0xde, 0x6e, 0xc9, 0x25, 0xcb, 0x5b, 0xb5,
+ 0x9d, 0x76, 0x70, 0x0b, 0xdf, 0x42, 0x72, 0xf8, 0x4f, 0x41, 0x11, 0x64,
+ 0xa5, 0xd2, 0xea, 0xfc, 0xd5, 0xaf, 0x11, 0xf4, 0x15, 0x38, 0x67, 0x9c,
+ 0x20, 0xa8, 0x4b, 0x77, 0x5a, 0x91, 0x32, 0x42, 0x32, 0xe7, 0x85, 0xb3,
+ 0xdf, 0x36,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 33:65:50:08:79:ad:73:e2:30:b9:e0:1d:0d:7f:ac:91
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+ Validity
+ Not Before: Nov 17 00:00:00 2006 GMT
+ Not After : Dec 30 23:59:59 2020 GMT
+ Subject: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ac:a0:f0:fb:80:59:d4:9c:c7:a4:cf:9d:a1:59:
+ 73:09:10:45:0c:0d:2c:6e:68:f1:6c:5b:48:68:49:
+ 59:37:fc:0b:33:19:c2:77:7f:cc:10:2d:95:34:1c:
+ e6:eb:4d:09:a7:1c:d2:b8:c9:97:36:02:b7:89:d4:
+ 24:5f:06:c0:cc:44:94:94:8d:02:62:6f:eb:5a:dd:
+ 11:8d:28:9a:5c:84:90:10:7a:0d:bd:74:66:2f:6a:
+ 38:a0:e2:d5:54:44:eb:1d:07:9f:07:ba:6f:ee:e9:
+ fd:4e:0b:29:f5:3e:84:a0:01:f1:9c:ab:f8:1c:7e:
+ 89:a4:e8:a1:d8:71:65:0d:a3:51:7b:ee:bc:d2:22:
+ 60:0d:b9:5b:9d:df:ba:fc:51:5b:0b:af:98:b2:e9:
+ 2e:e9:04:e8:62:87:de:2b:c8:d7:4e:c1:4c:64:1e:
+ dd:cf:87:58:ba:4a:4f:ca:68:07:1d:1c:9d:4a:c6:
+ d5:2f:91:cc:7c:71:72:1c:c5:c0:67:eb:32:fd:c9:
+ 92:5c:94:da:85:c0:9b:bf:53:7d:2b:09:f4:8c:9d:
+ 91:1f:97:6a:52:cb:de:09:36:a4:77:d8:7b:87:50:
+ 44:d5:3e:6e:29:69:fb:39:49:26:1e:09:a5:80:7b:
+ 40:2d:eb:e8:27:85:c9:fe:61:fd:7e:e6:7c:97:1d:
+ d5:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePremiumServerCA.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 84:a8:4c:c9:3e:2a:bc:9a:e2:cc:8f:0b:b2:25:77:c4:61:89:
+ 89:63:5a:d4:a3:15:40:d4:fb:5e:3f:b4:43:ea:63:17:2b:6b:
+ 99:74:9e:09:a8:dd:d4:56:15:2e:7a:79:31:5f:63:96:53:1b:
+ 34:d9:15:ea:4f:6d:70:ca:be:f6:82:a9:ed:da:85:77:cc:76:
+ 1c:6a:81:0a:21:d8:41:99:7f:5e:2e:82:c1:e8:aa:f7:93:81:
+ 05:aa:92:b4:1f:b7:9a:c0:07:17:f5:cb:c6:b4:4c:0e:d7:56:
+ dc:71:20:74:38:d6:74:c6:d6:8f:6b:af:8b:8d:a0:6c:29:0b:
+ 61:e0
+-----BEGIN CERTIFICATE-----
+MIIERTCCA66gAwIBAgIQM2VQCHmtc+IwueAdDX+skTANBgkqhkiG9w0BAQUFADCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTA2MTExNzAwMDAwMFoXDTIwMTIzMDIzNTk1OVow
+gakxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xKDAmBgNVBAsT
+H0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xODA2BgNVBAsTLyhjKSAy
+MDA2IHRoYXd0ZSwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD
+VQQDExZ0aGF3dGUgUHJpbWFyeSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEArKDw+4BZ1JzHpM+doVlzCRBFDA0sbmjxbFtIaElZN/wLMxnC
+d3/MEC2VNBzm600JpxzSuMmXNgK3idQkXwbAzESUlI0CYm/rWt0RjSiaXISQEHoN
+vXRmL2o4oOLVVETrHQefB7pv7un9Tgsp9T6EoAHxnKv4HH6JpOih2HFlDaNRe+68
+0iJgDblbnd+6/FFbC6+Ysuku6QToYofeK8jXTsFMZB7dz4dYukpPymgHHRydSsbV
+L5HMfHFyHMXAZ+sy/cmSXJTahcCbv1N9Kwn0jJ2RH5dqUsveCTakd9h7h1BE1T5u
+KWn7OUkmHgmlgHtALevoJ4XJ/mH9fuZ8lx3VnQIDAQABo4HCMIG/MA8GA1UdEwEB
+/wQFMAMBAf8wOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHBz
+Oi8vd3d3LnRoYXd0ZS5jb20vY3BzMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU
+e1tFz6/Oy3r9MZIaarbzRutXSFAwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cDovL2Ny
+bC50aGF3dGUuY29tL1RoYXd0ZVByZW1pdW1TZXJ2ZXJDQS5jcmwwDQYJKoZIhvcN
+AQEFBQADgYEAhKhMyT4qvJrizI8LsiV3xGGJiWNa1KMVQNT7Xj+0Q+pjFytrmXSe
+Cajd1FYVLnp5MV9jllMbNNkV6k9tcMq+9oKp7dqFd8x2HGqBCiHYQZl/Xi6Cweiq
+95OBBaqStB+3msAHF/XLxrRMDtdW3HEgdDjWdMbWj2uvi42gbCkLYeA=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert25[] = {
+ 0x30, 0x82, 0x04, 0x45, 0x30, 0x82, 0x03, 0xae, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x33, 0x65, 0x50, 0x08, 0x79, 0xad, 0x73, 0xe2, 0x30,
+ 0xb9, 0xe0, 0x1d, 0x0d, 0x7f, 0xac, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70,
+ 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09,
+ 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31,
+ 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x31, 0x32, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30,
+ 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20,
+ 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73,
+ 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xac, 0xa0, 0xf0, 0xfb, 0x80, 0x59, 0xd4, 0x9c, 0xc7, 0xa4, 0xcf, 0x9d,
+ 0xa1, 0x59, 0x73, 0x09, 0x10, 0x45, 0x0c, 0x0d, 0x2c, 0x6e, 0x68, 0xf1,
+ 0x6c, 0x5b, 0x48, 0x68, 0x49, 0x59, 0x37, 0xfc, 0x0b, 0x33, 0x19, 0xc2,
+ 0x77, 0x7f, 0xcc, 0x10, 0x2d, 0x95, 0x34, 0x1c, 0xe6, 0xeb, 0x4d, 0x09,
+ 0xa7, 0x1c, 0xd2, 0xb8, 0xc9, 0x97, 0x36, 0x02, 0xb7, 0x89, 0xd4, 0x24,
+ 0x5f, 0x06, 0xc0, 0xcc, 0x44, 0x94, 0x94, 0x8d, 0x02, 0x62, 0x6f, 0xeb,
+ 0x5a, 0xdd, 0x11, 0x8d, 0x28, 0x9a, 0x5c, 0x84, 0x90, 0x10, 0x7a, 0x0d,
+ 0xbd, 0x74, 0x66, 0x2f, 0x6a, 0x38, 0xa0, 0xe2, 0xd5, 0x54, 0x44, 0xeb,
+ 0x1d, 0x07, 0x9f, 0x07, 0xba, 0x6f, 0xee, 0xe9, 0xfd, 0x4e, 0x0b, 0x29,
+ 0xf5, 0x3e, 0x84, 0xa0, 0x01, 0xf1, 0x9c, 0xab, 0xf8, 0x1c, 0x7e, 0x89,
+ 0xa4, 0xe8, 0xa1, 0xd8, 0x71, 0x65, 0x0d, 0xa3, 0x51, 0x7b, 0xee, 0xbc,
+ 0xd2, 0x22, 0x60, 0x0d, 0xb9, 0x5b, 0x9d, 0xdf, 0xba, 0xfc, 0x51, 0x5b,
+ 0x0b, 0xaf, 0x98, 0xb2, 0xe9, 0x2e, 0xe9, 0x04, 0xe8, 0x62, 0x87, 0xde,
+ 0x2b, 0xc8, 0xd7, 0x4e, 0xc1, 0x4c, 0x64, 0x1e, 0xdd, 0xcf, 0x87, 0x58,
+ 0xba, 0x4a, 0x4f, 0xca, 0x68, 0x07, 0x1d, 0x1c, 0x9d, 0x4a, 0xc6, 0xd5,
+ 0x2f, 0x91, 0xcc, 0x7c, 0x71, 0x72, 0x1c, 0xc5, 0xc0, 0x67, 0xeb, 0x32,
+ 0xfd, 0xc9, 0x92, 0x5c, 0x94, 0xda, 0x85, 0xc0, 0x9b, 0xbf, 0x53, 0x7d,
+ 0x2b, 0x09, 0xf4, 0x8c, 0x9d, 0x91, 0x1f, 0x97, 0x6a, 0x52, 0xcb, 0xde,
+ 0x09, 0x36, 0xa4, 0x77, 0xd8, 0x7b, 0x87, 0x50, 0x44, 0xd5, 0x3e, 0x6e,
+ 0x29, 0x69, 0xfb, 0x39, 0x49, 0x26, 0x1e, 0x09, 0xa5, 0x80, 0x7b, 0x40,
+ 0x2d, 0xeb, 0xe8, 0x27, 0x85, 0xc9, 0xfe, 0x61, 0xfd, 0x7e, 0xe6, 0x7c,
+ 0x97, 0x1d, 0xd5, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xc2,
+ 0x30, 0x81, 0xbf, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a,
+ 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x40, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0, 0x33, 0xa0,
+ 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65, 0x6d, 0x69,
+ 0x75, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x84, 0xa8, 0x4c,
+ 0xc9, 0x3e, 0x2a, 0xbc, 0x9a, 0xe2, 0xcc, 0x8f, 0x0b, 0xb2, 0x25, 0x77,
+ 0xc4, 0x61, 0x89, 0x89, 0x63, 0x5a, 0xd4, 0xa3, 0x15, 0x40, 0xd4, 0xfb,
+ 0x5e, 0x3f, 0xb4, 0x43, 0xea, 0x63, 0x17, 0x2b, 0x6b, 0x99, 0x74, 0x9e,
+ 0x09, 0xa8, 0xdd, 0xd4, 0x56, 0x15, 0x2e, 0x7a, 0x79, 0x31, 0x5f, 0x63,
+ 0x96, 0x53, 0x1b, 0x34, 0xd9, 0x15, 0xea, 0x4f, 0x6d, 0x70, 0xca, 0xbe,
+ 0xf6, 0x82, 0xa9, 0xed, 0xda, 0x85, 0x77, 0xcc, 0x76, 0x1c, 0x6a, 0x81,
+ 0x0a, 0x21, 0xd8, 0x41, 0x99, 0x7f, 0x5e, 0x2e, 0x82, 0xc1, 0xe8, 0xaa,
+ 0xf7, 0x93, 0x81, 0x05, 0xaa, 0x92, 0xb4, 0x1f, 0xb7, 0x9a, 0xc0, 0x07,
+ 0x17, 0xf5, 0xcb, 0xc6, 0xb4, 0x4c, 0x0e, 0xd7, 0x56, 0xdc, 0x71, 0x20,
+ 0x74, 0x38, 0xd6, 0x74, 0xc6, 0xd6, 0x8f, 0x6b, 0xaf, 0x8b, 0x8d, 0xa0,
+ 0x6c, 0x29, 0x0b, 0x61, 0xe0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120026506 (0x727758a)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Jul 25 17:58:28 2012 GMT
+ Not After : Jul 25 17:57:44 2019 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df:
+ e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67:
+ a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98:
+ a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a:
+ cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71:
+ 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be:
+ f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41:
+ 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d:
+ be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7:
+ ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41:
+ 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a:
+ 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4:
+ 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e:
+ 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79:
+ 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a:
+ a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e:
+ 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8:
+ 4b:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+ Signature Algorithm: sha1WithRSAEncryption
+ 76:56:58:36:0d:19:98:b4:d9:a5:cb:30:75:ae:b1:d6:80:97:
+ cc:ee:38:72:68:39:b0:02:3e:46:b6:c4:f2:ac:d1:d2:e1:66:
+ 16:e6:85:a4:55:77:cb:2e:1c:59:dd:a5:4b:df:2f:33:bb:ce:
+ 60:57:27:3a:a1:4d:49:6f:55:76:6d:d5:d7:c2:a0:5b:2a:9b:
+ f9:4b:f7:7f:21:dd:ee:5c:57:0d:00:35:3a:f1:8c:46:cb:04:
+ f6:46:8f:ce:05:6a:d5:c4:6c:fe:6e:98:bf:a4:9c:bd:8e:89:
+ 2c:be:71:01:43:cc:36:2a:64:06:56:97:93:a5:47:bd:4a:3f:
+ 8c:1b:75:c8:9e:b0:f0:25:98:77:21:c0:76:a7:51:7a:24:25:
+ 7d:18:35:06:fe:c1:09:c5:0e:3b:99:a8:cd:9d:29:b0:3a:89:
+ f5:ea:e7:2a:e5:e2:24:4e:68:a9:1d:a7:dd:d2:08:4b:a1:d1:
+ 6f:0c:bd:2c:e0:bb:7c:fa:a1:3c:65:cf:3a:52:4b:d3:20:7a:
+ 0a:10:55:f8:ad:43:16:54:27:4e:53:73:c8:a3:96:89:d0:e1:
+ 79:c6:09:78:d5:f5:bd:b1:b3:c5:7f:a6:4b:af:49:11:c8:97:
+ 9c:4f:7c:70:69:16:5c:2d:b8:d0:df:1c:32:52:b9:de:f3:c3:
+ 06:e8:83:22
+-----BEGIN CERTIFICATE-----
+MIIERjCCAy6gAwIBAgIEByd1ijANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEyMDcyNTE3NTgyOFoX
+DTE5MDcyNTE3NTc0NFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0
+IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNl
+cnQgSGlnaCBBc3N1cmFuY2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAMbM5XPm+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9p
+pkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8Ok
+hPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ck
+RZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhak
+aHPQNAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDir
+CmTCv2ZdlYTBoSUeh10aUAsgEsxBu24LUTi4S8sCAwEAAaOCAQAwgf0wEgYDVR0T
+AQH/BAgwBgEB/wIBATBTBgNVHSAETDBKMEgGCSsGAQQBsT4BADA7MDkGCCsGAQUF
+BwIBFi1odHRwOi8vY3liZXJ0cnVzdC5vbW5pcm9vdC5jb20vcmVwb3NpdG9yeS5j
+Zm0wDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1
+BE3wMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5j
+b20vQ1JML09tbmlyb290MjAyNS5jcmwwHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoI
+Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQB2Vlg2DRmYtNmlyzB1rrHWgJfM7jhy
+aDmwAj5GtsTyrNHS4WYW5oWkVXfLLhxZ3aVL3y8zu85gVyc6oU1Jb1V2bdXXwqBb
+Kpv5S/d/Id3uXFcNADU68YxGywT2Ro/OBWrVxGz+bpi/pJy9joksvnEBQ8w2KmQG
+VpeTpUe9Sj+MG3XInrDwJZh3IcB2p1F6JCV9GDUG/sEJxQ47majNnSmwOon16ucq
+5eIkTmipHafd0ghLodFvDL0s4Lt8+qE8Zc86UkvTIHoKEFX4rUMWVCdOU3PIo5aJ
+0OF5xgl41fW9sbPFf6ZLr0kRyJecT3xwaRZcLbjQ3xwyUrne88MG6IMi
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert26[] = {
+ 0x30, 0x82, 0x04, 0x46, 0x30, 0x82, 0x03, 0x2e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x75, 0x8a, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32,
+ 0x30, 0x37, 0x32, 0x35, 0x31, 0x37, 0x35, 0x38, 0x32, 0x38, 0x5a, 0x17,
+ 0x0d, 0x31, 0x39, 0x30, 0x37, 0x32, 0x35, 0x31, 0x37, 0x35, 0x37, 0x34,
+ 0x34, 0x5a, 0x30, 0x6c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74,
+ 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65,
+ 0x72, 0x74, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75,
+ 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f,
+ 0x74, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xc6, 0xcc, 0xe5, 0x73, 0xe6, 0xfb, 0xd4, 0xbb, 0xe5, 0x2d, 0x2d,
+ 0x32, 0xa6, 0xdf, 0xe5, 0x81, 0x3f, 0xc9, 0xcd, 0x25, 0x49, 0xb6, 0x71,
+ 0x2a, 0xc3, 0xd5, 0x94, 0x34, 0x67, 0xa2, 0x0a, 0x1c, 0xb0, 0x5f, 0x69,
+ 0xa6, 0x40, 0xb1, 0xc4, 0xb7, 0xb2, 0x8f, 0xd0, 0x98, 0xa4, 0xa9, 0x41,
+ 0x59, 0x3a, 0xd3, 0xdc, 0x94, 0xd6, 0x3c, 0xdb, 0x74, 0x38, 0xa4, 0x4a,
+ 0xcc, 0x4d, 0x25, 0x82, 0xf7, 0x4a, 0xa5, 0x53, 0x12, 0x38, 0xee, 0xf3,
+ 0x49, 0x6d, 0x71, 0x91, 0x7e, 0x63, 0xb6, 0xab, 0xa6, 0x5f, 0xc3, 0xa4,
+ 0x84, 0xf8, 0x4f, 0x62, 0x51, 0xbe, 0xf8, 0xc5, 0xec, 0xdb, 0x38, 0x92,
+ 0xe3, 0x06, 0xe5, 0x08, 0x91, 0x0c, 0xc4, 0x28, 0x41, 0x55, 0xfb, 0xcb,
+ 0x5a, 0x89, 0x15, 0x7e, 0x71, 0xe8, 0x35, 0xbf, 0x4d, 0x72, 0x09, 0x3d,
+ 0xbe, 0x3a, 0x38, 0x50, 0x5b, 0x77, 0x31, 0x1b, 0x8d, 0xb3, 0xc7, 0x24,
+ 0x45, 0x9a, 0xa7, 0xac, 0x6d, 0x00, 0x14, 0x5a, 0x04, 0xb7, 0xba, 0x13,
+ 0xeb, 0x51, 0x0a, 0x98, 0x41, 0x41, 0x22, 0x4e, 0x65, 0x61, 0x87, 0x81,
+ 0x41, 0x50, 0xa6, 0x79, 0x5c, 0x89, 0xde, 0x19, 0x4a, 0x57, 0xd5, 0x2e,
+ 0xe6, 0x5d, 0x1c, 0x53, 0x2c, 0x7e, 0x98, 0xcd, 0x1a, 0x06, 0x16, 0xa4,
+ 0x68, 0x73, 0xd0, 0x34, 0x04, 0x13, 0x5c, 0xa1, 0x71, 0xd3, 0x5a, 0x7c,
+ 0x55, 0xdb, 0x5e, 0x64, 0xe1, 0x37, 0x87, 0x30, 0x56, 0x04, 0xe5, 0x11,
+ 0xb4, 0x29, 0x80, 0x12, 0xf1, 0x79, 0x39, 0x88, 0xa2, 0x02, 0x11, 0x7c,
+ 0x27, 0x66, 0xb7, 0x88, 0xb7, 0x78, 0xf2, 0xca, 0x0a, 0xa8, 0x38, 0xab,
+ 0x0a, 0x64, 0xc2, 0xbf, 0x66, 0x5d, 0x95, 0x84, 0xc1, 0xa1, 0x25, 0x1e,
+ 0x87, 0x5d, 0x1a, 0x50, 0x0b, 0x20, 0x12, 0xcc, 0x41, 0xbb, 0x6e, 0x0b,
+ 0x51, 0x38, 0xb8, 0x4b, 0xcb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x00, 0x30, 0x81, 0xfd, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x01, 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a,
+ 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01,
+ 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f,
+ 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63,
+ 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82,
+ 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5,
+ 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b,
+ 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72,
+ 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0x3e,
+ 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08,
+ 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x76, 0x56, 0x58, 0x36, 0x0d, 0x19, 0x98, 0xb4, 0xd9, 0xa5,
+ 0xcb, 0x30, 0x75, 0xae, 0xb1, 0xd6, 0x80, 0x97, 0xcc, 0xee, 0x38, 0x72,
+ 0x68, 0x39, 0xb0, 0x02, 0x3e, 0x46, 0xb6, 0xc4, 0xf2, 0xac, 0xd1, 0xd2,
+ 0xe1, 0x66, 0x16, 0xe6, 0x85, 0xa4, 0x55, 0x77, 0xcb, 0x2e, 0x1c, 0x59,
+ 0xdd, 0xa5, 0x4b, 0xdf, 0x2f, 0x33, 0xbb, 0xce, 0x60, 0x57, 0x27, 0x3a,
+ 0xa1, 0x4d, 0x49, 0x6f, 0x55, 0x76, 0x6d, 0xd5, 0xd7, 0xc2, 0xa0, 0x5b,
+ 0x2a, 0x9b, 0xf9, 0x4b, 0xf7, 0x7f, 0x21, 0xdd, 0xee, 0x5c, 0x57, 0x0d,
+ 0x00, 0x35, 0x3a, 0xf1, 0x8c, 0x46, 0xcb, 0x04, 0xf6, 0x46, 0x8f, 0xce,
+ 0x05, 0x6a, 0xd5, 0xc4, 0x6c, 0xfe, 0x6e, 0x98, 0xbf, 0xa4, 0x9c, 0xbd,
+ 0x8e, 0x89, 0x2c, 0xbe, 0x71, 0x01, 0x43, 0xcc, 0x36, 0x2a, 0x64, 0x06,
+ 0x56, 0x97, 0x93, 0xa5, 0x47, 0xbd, 0x4a, 0x3f, 0x8c, 0x1b, 0x75, 0xc8,
+ 0x9e, 0xb0, 0xf0, 0x25, 0x98, 0x77, 0x21, 0xc0, 0x76, 0xa7, 0x51, 0x7a,
+ 0x24, 0x25, 0x7d, 0x18, 0x35, 0x06, 0xfe, 0xc1, 0x09, 0xc5, 0x0e, 0x3b,
+ 0x99, 0xa8, 0xcd, 0x9d, 0x29, 0xb0, 0x3a, 0x89, 0xf5, 0xea, 0xe7, 0x2a,
+ 0xe5, 0xe2, 0x24, 0x4e, 0x68, 0xa9, 0x1d, 0xa7, 0xdd, 0xd2, 0x08, 0x4b,
+ 0xa1, 0xd1, 0x6f, 0x0c, 0xbd, 0x2c, 0xe0, 0xbb, 0x7c, 0xfa, 0xa1, 0x3c,
+ 0x65, 0xcf, 0x3a, 0x52, 0x4b, 0xd3, 0x20, 0x7a, 0x0a, 0x10, 0x55, 0xf8,
+ 0xad, 0x43, 0x16, 0x54, 0x27, 0x4e, 0x53, 0x73, 0xc8, 0xa3, 0x96, 0x89,
+ 0xd0, 0xe1, 0x79, 0xc6, 0x09, 0x78, 0xd5, 0xf5, 0xbd, 0xb1, 0xb3, 0xc5,
+ 0x7f, 0xa6, 0x4b, 0xaf, 0x49, 0x11, 0xc8, 0x97, 0x9c, 0x4f, 0x7c, 0x70,
+ 0x69, 0x16, 0x5c, 0x2d, 0xb8, 0xd0, 0xdf, 0x1c, 0x32, 0x52, 0xb9, 0xde,
+ 0xf3, 0xc3, 0x06, 0xe8, 0x83, 0x22,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120019005 (0x727583d)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Jan 13 19:20:32 2010 GMT
+ Not After : Sep 30 18:19:47 2015 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df:
+ e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67:
+ a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98:
+ a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a:
+ cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71:
+ 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be:
+ f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41:
+ 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d:
+ be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7:
+ ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41:
+ 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a:
+ 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4:
+ 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e:
+ 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79:
+ 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a:
+ a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e:
+ 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8:
+ 4b:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+ Signature Algorithm: sha1WithRSAEncryption
+ 2e:76:85:d9:37:96:6d:af:89:f3:06:78:82:31:c4:46:07:1f:
+ 65:c9:8e:b3:c9:54:78:e6:d1:42:df:75:2e:1e:55:ea:f7:fa:
+ 9b:04:c0:75:7b:d1:79:3c:05:ec:79:c4:52:dd:a6:03:d7:a7:
+ 50:99:3f:05:59:da:c6:55:f4:86:9c:0d:67:a3:49:04:95:32:
+ 1d:c7:87:ec:85:af:64:6e:d5:c5:5f:09:a7:40:7d:16:ba:49:
+ 0d:a2:fd:f6:df:55:30:6c:d7:78:c6:b9:cf:58:29:64:16:4c:
+ a3:20:81:47:b1:44:92:84:16:1b:6f:4a:bc:21:c6:0a:3d:ed:
+ 33:ca
+-----BEGIN CERTIFICATE-----
+MIIETzCCA7igAwIBAgIEBydYPTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEwMDExMzE5MjAzMloXDTE1MDkzMDE4MTk0N1owbDELMAkG
+A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp
+Z2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2UgRVYg
+Um9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S7
+5S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0
+OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChB
+VfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5l
+YYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3hzBW
+BOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsgEsxB
+u24LUTi4S8sCAwEAAaOCAW8wggFrMBIGA1UdEwEB/wQIMAYBAf8CAQEwUwYDVR0g
+BEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEFBQcCARYtaHR0cDovL2N5YmVydHJ1
+c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnkuY2ZtMA4GA1UdDwEB/wQEAwIBBjCB
+iQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29y
+cG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5j
+LjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3SCAgGlMEUGA1Ud
+HwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9jZ2ktYmlu
+L0NSTC8yMDE4L2NkcC5jcmwwHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvD
+MA0GCSqGSIb3DQEBBQUAA4GBAC52hdk3lm2vifMGeIIxxEYHH2XJjrPJVHjm0ULf
+dS4eVer3+psEwHV70Xk8Bex5xFLdpgPXp1CZPwVZ2sZV9IacDWejSQSVMh3Hh+yF
+r2Ru1cVfCadAfRa6SQ2i/fbfVTBs13jGuc9YKWQWTKMggUexRJKEFhtvSrwhxgo9
+7TPK
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert27[] = {
+ 0x30, 0x82, 0x04, 0x4f, 0x30, 0x82, 0x03, 0xb8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x58, 0x3d, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x30, 0x31, 0x31, 0x33, 0x31, 0x39, 0x32, 0x30, 0x33,
+ 0x32, 0x5a, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x39, 0x33, 0x30, 0x31, 0x38,
+ 0x31, 0x39, 0x34, 0x37, 0x5a, 0x30, 0x6c, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69, 0x67, 0x69, 0x43,
+ 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69,
+ 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x2b,
+ 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x22, 0x44, 0x69, 0x67,
+ 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41,
+ 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x45, 0x56, 0x20,
+ 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xc6, 0xcc, 0xe5, 0x73, 0xe6, 0xfb, 0xd4, 0xbb,
+ 0xe5, 0x2d, 0x2d, 0x32, 0xa6, 0xdf, 0xe5, 0x81, 0x3f, 0xc9, 0xcd, 0x25,
+ 0x49, 0xb6, 0x71, 0x2a, 0xc3, 0xd5, 0x94, 0x34, 0x67, 0xa2, 0x0a, 0x1c,
+ 0xb0, 0x5f, 0x69, 0xa6, 0x40, 0xb1, 0xc4, 0xb7, 0xb2, 0x8f, 0xd0, 0x98,
+ 0xa4, 0xa9, 0x41, 0x59, 0x3a, 0xd3, 0xdc, 0x94, 0xd6, 0x3c, 0xdb, 0x74,
+ 0x38, 0xa4, 0x4a, 0xcc, 0x4d, 0x25, 0x82, 0xf7, 0x4a, 0xa5, 0x53, 0x12,
+ 0x38, 0xee, 0xf3, 0x49, 0x6d, 0x71, 0x91, 0x7e, 0x63, 0xb6, 0xab, 0xa6,
+ 0x5f, 0xc3, 0xa4, 0x84, 0xf8, 0x4f, 0x62, 0x51, 0xbe, 0xf8, 0xc5, 0xec,
+ 0xdb, 0x38, 0x92, 0xe3, 0x06, 0xe5, 0x08, 0x91, 0x0c, 0xc4, 0x28, 0x41,
+ 0x55, 0xfb, 0xcb, 0x5a, 0x89, 0x15, 0x7e, 0x71, 0xe8, 0x35, 0xbf, 0x4d,
+ 0x72, 0x09, 0x3d, 0xbe, 0x3a, 0x38, 0x50, 0x5b, 0x77, 0x31, 0x1b, 0x8d,
+ 0xb3, 0xc7, 0x24, 0x45, 0x9a, 0xa7, 0xac, 0x6d, 0x00, 0x14, 0x5a, 0x04,
+ 0xb7, 0xba, 0x13, 0xeb, 0x51, 0x0a, 0x98, 0x41, 0x41, 0x22, 0x4e, 0x65,
+ 0x61, 0x87, 0x81, 0x41, 0x50, 0xa6, 0x79, 0x5c, 0x89, 0xde, 0x19, 0x4a,
+ 0x57, 0xd5, 0x2e, 0xe6, 0x5d, 0x1c, 0x53, 0x2c, 0x7e, 0x98, 0xcd, 0x1a,
+ 0x06, 0x16, 0xa4, 0x68, 0x73, 0xd0, 0x34, 0x04, 0x13, 0x5c, 0xa1, 0x71,
+ 0xd3, 0x5a, 0x7c, 0x55, 0xdb, 0x5e, 0x64, 0xe1, 0x37, 0x87, 0x30, 0x56,
+ 0x04, 0xe5, 0x11, 0xb4, 0x29, 0x80, 0x12, 0xf1, 0x79, 0x39, 0x88, 0xa2,
+ 0x02, 0x11, 0x7c, 0x27, 0x66, 0xb7, 0x88, 0xb7, 0x78, 0xf2, 0xca, 0x0a,
+ 0xa8, 0x38, 0xab, 0x0a, 0x64, 0xc2, 0xbf, 0x66, 0x5d, 0x95, 0x84, 0xc1,
+ 0xa1, 0x25, 0x1e, 0x87, 0x5d, 0x1a, 0x50, 0x0b, 0x20, 0x12, 0xcc, 0x41,
+ 0xbb, 0x6e, 0x0b, 0x51, 0x38, 0xb8, 0x4b, 0xcb, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x6f, 0x30, 0x82, 0x01, 0x6b, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x53, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x4c, 0x30, 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04,
+ 0x01, 0xb1, 0x3e, 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x81,
+ 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1,
+ 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72,
+ 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f,
+ 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f,
+ 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86,
+ 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e,
+ 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64,
+ 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47,
+ 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x2e, 0x76, 0x85, 0xd9, 0x37,
+ 0x96, 0x6d, 0xaf, 0x89, 0xf3, 0x06, 0x78, 0x82, 0x31, 0xc4, 0x46, 0x07,
+ 0x1f, 0x65, 0xc9, 0x8e, 0xb3, 0xc9, 0x54, 0x78, 0xe6, 0xd1, 0x42, 0xdf,
+ 0x75, 0x2e, 0x1e, 0x55, 0xea, 0xf7, 0xfa, 0x9b, 0x04, 0xc0, 0x75, 0x7b,
+ 0xd1, 0x79, 0x3c, 0x05, 0xec, 0x79, 0xc4, 0x52, 0xdd, 0xa6, 0x03, 0xd7,
+ 0xa7, 0x50, 0x99, 0x3f, 0x05, 0x59, 0xda, 0xc6, 0x55, 0xf4, 0x86, 0x9c,
+ 0x0d, 0x67, 0xa3, 0x49, 0x04, 0x95, 0x32, 0x1d, 0xc7, 0x87, 0xec, 0x85,
+ 0xaf, 0x64, 0x6e, 0xd5, 0xc5, 0x5f, 0x09, 0xa7, 0x40, 0x7d, 0x16, 0xba,
+ 0x49, 0x0d, 0xa2, 0xfd, 0xf6, 0xdf, 0x55, 0x30, 0x6c, 0xd7, 0x78, 0xc6,
+ 0xb9, 0xcf, 0x58, 0x29, 0x64, 0x16, 0x4c, 0xa3, 0x20, 0x81, 0x47, 0xb1,
+ 0x44, 0x92, 0x84, 0x16, 0x1b, 0x6f, 0x4a, 0xbc, 0x21, 0xc6, 0x0a, 0x3d,
+ 0xed, 0x33, 0xca,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:41:43
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Domain Validation CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b1:a3:cd:c0:df:33:40:26:eb:de:5a:d7:94:66:
+ d4:01:63:cc:33:44:89:e0:e2:b8:c2:47:0d:8f:ad:
+ 69:86:1c:a8:73:42:0b:f1:72:fb:2d:ac:b5:11:72:
+ 83:22:f6:56:e7:2e:c5:67:71:9d:00:1c:32:bc:e3:
+ ed:2e:08:45:a9:e6:fa:dd:c8:8c:83:05:c1:6f:4b:
+ d0:26:4a:0b:f6:1b:45:c0:4d:7e:93:bc:0d:27:84:
+ ed:30:a3:e9:c6:26:26:dd:2d:1f:d8:8b:c3:ce:19:
+ d0:5b:fc:08:9f:e4:d8:e2:35:e4:a0:68:a6:f6:0d:
+ a3:74:60:42:b2:97:82:24:8e:41:a4:f2:2e:5e:b6:
+ 8e:a7:6e:d9:6c:7f:0d:3b:24:35:6a:d0:ab:5b:6a:
+ f7:97:02:00:3f:51:a6:a7:6e:73:ca:77:0d:76:7c:
+ 9b:b6:30:1a:1a:9c:f7:1f:28:7b:0e:8b:47:1f:e7:
+ 7f:05:8c:c6:c9:c8:bb:cf:e9:dc:7a:41:2e:a1:86:
+ da:d4:39:b2:e2:13:40:a6:a8:3a:fa:0f:53:1e:4f:
+ ec:6e:98:09:1b:ca:9a:77:b3:55:85:85:e9:2e:16:
+ b5:9d:5e:54:f1:4a:7a:6c:39:ba:6e:17:06:34:b3:
+ b2:42:e1:f7:f3:9c:9a:0b:11:44:de:6a:78:8e:b1:
+ 13:4f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 96:AD:FA:B0:5B:B9:83:64:2A:76:C2:1C:8A:69:DA:42:DC:FE:FD:28
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 3a:e7:fc:ae:af:05:43:80:27:75:41:5f:a8:f0:28:8f:1f:8f:
+ 83:7e:b2:b8:ba:ae:75:31:27:88:a5:e5:b9:4e:04:43:d2:ad:
+ e8:13:00:a3:db:19:01:30:9e:6c:3c:52:7f:5c:de:ab:67:c3:
+ 84:04:54:51:99:9e:63:2f:bd:d5:b7:c0:d5:da:03:0e:49:d3:
+ e1:b3:92:4f:df:92:4e:7d:ae:22:6a:ce:d8:bc:fc:7c:ae:6b:
+ b6:8a:ea:45:62:90:11:d3:0b:71:a7:5e:06:22:ff:4d:38:ea:
+ b9:3a:6e:cd:67:1a:02:7f:4b:f3:bf:0e:79:6f:be:d5:29:32:
+ 59:59:1d:96:08:9b:70:8f:f7:1e:5c:46:7b:4e:d0:9d:b4:53:
+ c8:12:02:1b:0d:bb:32:eb:59:53:b9:3e:1b:56:8d:15:c8:f1:
+ 42:3f:77:fe:1f:e5:6d:9e:66:1f:ab:da:b2:83:57:b4:0c:22:
+ d2:86:bc:da:32:d7:c0:ed:70:85:7c:93:aa:f0:97:dc:39:11:
+ d2:d8:89:eb:8d:90:a3:b6:50:25:cb:6c:d9:a6:c3:6f:fb:88:
+ 54:b8:e4:92:70:87:ce:79:3b:f0:de:36:bf:03:04:00:3d:f9:
+ ef:9e:a9:67:a4:f4:86:3e:23:97:b8:2a:71:e2:ed:fe:69:88:
+ 67:bf:26:5c
+-----BEGIN CERTIFICATE-----
+MIIEWjCCA0KgAwIBAgILBAAAAAABL07hQUMwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
+MDBaFw0yMjA0MTMxMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0
+aW9uIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxo83A
+3zNAJuveWteUZtQBY8wzRIng4rjCRw2PrWmGHKhzQgvxcvstrLURcoMi9lbnLsVn
+cZ0AHDK84+0uCEWp5vrdyIyDBcFvS9AmSgv2G0XATX6TvA0nhO0wo+nGJibdLR/Y
+i8POGdBb/Aif5NjiNeSgaKb2DaN0YEKyl4IkjkGk8i5eto6nbtlsfw07JDVq0Ktb
+aveXAgA/UaanbnPKdw12fJu2MBoanPcfKHsOi0cf538FjMbJyLvP6dx6QS6hhtrU
+ObLiE0CmqDr6D1MeT+xumAkbypp3s1WFhekuFrWdXlTxSnpsObpuFwY0s7JC4ffz
+nJoLEUTeaniOsRNPAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
+AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlq36sFu5g2QqdsIcimnaQtz+/SgwRwYD
+VR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2Jh
+bHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9j
+cmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEEMTAvMC0GCCsG
+AQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYDVR0j
+BBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEBADrn
+/K6vBUOAJ3VBX6jwKI8fj4N+sri6rnUxJ4il5blOBEPSregTAKPbGQEwnmw8Un9c
+3qtnw4QEVFGZnmMvvdW3wNXaAw5J0+Gzkk/fkk59riJqzti8/Hyua7aK6kVikBHT
+C3GnXgYi/0046rk6bs1nGgJ/S/O/DnlvvtUpMllZHZYIm3CP9x5cRntO0J20U8gS
+AhsNuzLrWVO5PhtWjRXI8UI/d/4f5W2eZh+r2rKDV7QMItKGvNoy18DtcIV8k6rw
+l9w5EdLYieuNkKO2UCXLbNmmw2/7iFS45JJwh855O/DeNr8DBAA9+e+eqWek9IY+
+I5e4KnHi7f5piGe/Jlw=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert28[] = {
+ 0x30, 0x82, 0x04, 0x5a, 0x30, 0x82, 0x03, 0x42, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x41, 0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x44, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0xa3, 0xcd, 0xc0,
+ 0xdf, 0x33, 0x40, 0x26, 0xeb, 0xde, 0x5a, 0xd7, 0x94, 0x66, 0xd4, 0x01,
+ 0x63, 0xcc, 0x33, 0x44, 0x89, 0xe0, 0xe2, 0xb8, 0xc2, 0x47, 0x0d, 0x8f,
+ 0xad, 0x69, 0x86, 0x1c, 0xa8, 0x73, 0x42, 0x0b, 0xf1, 0x72, 0xfb, 0x2d,
+ 0xac, 0xb5, 0x11, 0x72, 0x83, 0x22, 0xf6, 0x56, 0xe7, 0x2e, 0xc5, 0x67,
+ 0x71, 0x9d, 0x00, 0x1c, 0x32, 0xbc, 0xe3, 0xed, 0x2e, 0x08, 0x45, 0xa9,
+ 0xe6, 0xfa, 0xdd, 0xc8, 0x8c, 0x83, 0x05, 0xc1, 0x6f, 0x4b, 0xd0, 0x26,
+ 0x4a, 0x0b, 0xf6, 0x1b, 0x45, 0xc0, 0x4d, 0x7e, 0x93, 0xbc, 0x0d, 0x27,
+ 0x84, 0xed, 0x30, 0xa3, 0xe9, 0xc6, 0x26, 0x26, 0xdd, 0x2d, 0x1f, 0xd8,
+ 0x8b, 0xc3, 0xce, 0x19, 0xd0, 0x5b, 0xfc, 0x08, 0x9f, 0xe4, 0xd8, 0xe2,
+ 0x35, 0xe4, 0xa0, 0x68, 0xa6, 0xf6, 0x0d, 0xa3, 0x74, 0x60, 0x42, 0xb2,
+ 0x97, 0x82, 0x24, 0x8e, 0x41, 0xa4, 0xf2, 0x2e, 0x5e, 0xb6, 0x8e, 0xa7,
+ 0x6e, 0xd9, 0x6c, 0x7f, 0x0d, 0x3b, 0x24, 0x35, 0x6a, 0xd0, 0xab, 0x5b,
+ 0x6a, 0xf7, 0x97, 0x02, 0x00, 0x3f, 0x51, 0xa6, 0xa7, 0x6e, 0x73, 0xca,
+ 0x77, 0x0d, 0x76, 0x7c, 0x9b, 0xb6, 0x30, 0x1a, 0x1a, 0x9c, 0xf7, 0x1f,
+ 0x28, 0x7b, 0x0e, 0x8b, 0x47, 0x1f, 0xe7, 0x7f, 0x05, 0x8c, 0xc6, 0xc9,
+ 0xc8, 0xbb, 0xcf, 0xe9, 0xdc, 0x7a, 0x41, 0x2e, 0xa1, 0x86, 0xda, 0xd4,
+ 0x39, 0xb2, 0xe2, 0x13, 0x40, 0xa6, 0xa8, 0x3a, 0xfa, 0x0f, 0x53, 0x1e,
+ 0x4f, 0xec, 0x6e, 0x98, 0x09, 0x1b, 0xca, 0x9a, 0x77, 0xb3, 0x55, 0x85,
+ 0x85, 0xe9, 0x2e, 0x16, 0xb5, 0x9d, 0x5e, 0x54, 0xf1, 0x4a, 0x7a, 0x6c,
+ 0x39, 0xba, 0x6e, 0x17, 0x06, 0x34, 0xb3, 0xb2, 0x42, 0xe1, 0xf7, 0xf3,
+ 0x9c, 0x9a, 0x0b, 0x11, 0x44, 0xde, 0x6a, 0x78, 0x8e, 0xb1, 0x13, 0x4f,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x25, 0x30, 0x82, 0x01,
+ 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x96, 0xad, 0xfa, 0xb0, 0x5b, 0xb9, 0x83, 0x64, 0x2a, 0x76, 0xc2, 0x1c,
+ 0x8a, 0x69, 0xda, 0x42, 0xdc, 0xfe, 0xfd, 0x28, 0x30, 0x47, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26,
+ 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d,
+ 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc,
+ 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3a, 0xe7,
+ 0xfc, 0xae, 0xaf, 0x05, 0x43, 0x80, 0x27, 0x75, 0x41, 0x5f, 0xa8, 0xf0,
+ 0x28, 0x8f, 0x1f, 0x8f, 0x83, 0x7e, 0xb2, 0xb8, 0xba, 0xae, 0x75, 0x31,
+ 0x27, 0x88, 0xa5, 0xe5, 0xb9, 0x4e, 0x04, 0x43, 0xd2, 0xad, 0xe8, 0x13,
+ 0x00, 0xa3, 0xdb, 0x19, 0x01, 0x30, 0x9e, 0x6c, 0x3c, 0x52, 0x7f, 0x5c,
+ 0xde, 0xab, 0x67, 0xc3, 0x84, 0x04, 0x54, 0x51, 0x99, 0x9e, 0x63, 0x2f,
+ 0xbd, 0xd5, 0xb7, 0xc0, 0xd5, 0xda, 0x03, 0x0e, 0x49, 0xd3, 0xe1, 0xb3,
+ 0x92, 0x4f, 0xdf, 0x92, 0x4e, 0x7d, 0xae, 0x22, 0x6a, 0xce, 0xd8, 0xbc,
+ 0xfc, 0x7c, 0xae, 0x6b, 0xb6, 0x8a, 0xea, 0x45, 0x62, 0x90, 0x11, 0xd3,
+ 0x0b, 0x71, 0xa7, 0x5e, 0x06, 0x22, 0xff, 0x4d, 0x38, 0xea, 0xb9, 0x3a,
+ 0x6e, 0xcd, 0x67, 0x1a, 0x02, 0x7f, 0x4b, 0xf3, 0xbf, 0x0e, 0x79, 0x6f,
+ 0xbe, 0xd5, 0x29, 0x32, 0x59, 0x59, 0x1d, 0x96, 0x08, 0x9b, 0x70, 0x8f,
+ 0xf7, 0x1e, 0x5c, 0x46, 0x7b, 0x4e, 0xd0, 0x9d, 0xb4, 0x53, 0xc8, 0x12,
+ 0x02, 0x1b, 0x0d, 0xbb, 0x32, 0xeb, 0x59, 0x53, 0xb9, 0x3e, 0x1b, 0x56,
+ 0x8d, 0x15, 0xc8, 0xf1, 0x42, 0x3f, 0x77, 0xfe, 0x1f, 0xe5, 0x6d, 0x9e,
+ 0x66, 0x1f, 0xab, 0xda, 0xb2, 0x83, 0x57, 0xb4, 0x0c, 0x22, 0xd2, 0x86,
+ 0xbc, 0xda, 0x32, 0xd7, 0xc0, 0xed, 0x70, 0x85, 0x7c, 0x93, 0xaa, 0xf0,
+ 0x97, 0xdc, 0x39, 0x11, 0xd2, 0xd8, 0x89, 0xeb, 0x8d, 0x90, 0xa3, 0xb6,
+ 0x50, 0x25, 0xcb, 0x6c, 0xd9, 0xa6, 0xc3, 0x6f, 0xfb, 0x88, 0x54, 0xb8,
+ 0xe4, 0x92, 0x70, 0x87, 0xce, 0x79, 0x3b, 0xf0, 0xde, 0x36, 0xbf, 0x03,
+ 0x04, 0x00, 0x3d, 0xf9, 0xef, 0x9e, 0xa9, 0x67, 0xa4, 0xf4, 0x86, 0x3e,
+ 0x23, 0x97, 0xb8, 0x2a, 0x71, 0xe2, 0xed, 0xfe, 0x69, 0x88, 0x67, 0xbf,
+ 0x26, 0x5c,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:5b:63
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Extended Validation CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cd:a1:46:cc:52:9a:b8:a5:b4:7f:58:d9:cd:d8:
+ 4b:a0:72:0c:97:5b:a6:88:20:c3:b9:3d:46:dc:d0:
+ 5c:52:30:f6:fa:59:4f:85:5f:b0:db:88:b4:a9:5f:
+ 2b:23:48:ac:ab:f5:92:78:14:b6:32:0f:fb:5c:6a:
+ 85:5b:00:90:e0:bb:65:f5:5a:f9:4f:67:7e:c7:6c:
+ 29:ec:93:c0:2b:ca:c4:5e:d8:b0:db:d6:be:3f:9b:
+ 0b:c0:8f:a9:5d:ae:f7:00:02:a4:fc:ba:66:11:38:
+ 77:fe:23:20:25:55:10:c5:bd:82:b9:4c:b1:68:c6:
+ e2:70:7b:83:5c:13:67:c1:a1:f3:7c:0b:a8:99:9a:
+ d0:e2:9b:25:31:c8:2b:8d:40:f6:52:63:b1:a0:ad:
+ 5a:2e:f5:79:36:6d:35:2c:0e:dd:05:e4:d0:e2:07:
+ 48:b7:28:5e:2b:d5:58:d5:6c:d0:0c:a1:01:46:01:
+ 5a:8f:c6:af:64:c7:55:01:5d:e1:d1:c6:6c:50:25:
+ a0:05:ad:00:ab:0c:8d:65:6b:dd:eb:c2:72:54:c9:
+ 0f:3c:00:17:87:22:ef:db:b9:86:78:16:51:ae:77:
+ d9:a6:28:4d:f3:58:8d:83:67:b9:34:25:9b:1c:51:
+ 80:51:f3:83:92:6a:a3:ae:47:9a:d6:e4:8b:1b:c0:
+ ed:b1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ B0:B0:4A:FD:1C:75:28:F8:1C:61:AA:13:F6:FA:C1:90:3D:6B:16:A3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root-r2.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/ExtendedSSLCA
+
+ X509v3 Authority Key Identifier:
+ keyid:9B:E2:07:57:67:1C:1E:C0:6A:06:DE:59:B4:9A:2D:DF:DC:19:86:2E
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 5f:28:90:0c:2d:e9:20:b2:30:7c:88:ab:40:05:fa:b1:9d:5c:
+ 22:93:d5:9d:ca:35:31:fa:2c:ea:1d:93:59:19:c4:a0:0d:fb:
+ 09:40:31:da:64:56:cd:52:be:e7:18:66:e8:6d:09:9b:b2:db:
+ 94:3e:ee:36:45:1e:24:54:b6:20:05:93:b5:31:1a:b8:64:57:
+ e6:d3:2c:01:4c:39:96:79:fe:b7:04:98:12:ef:b7:2e:5a:77:
+ fe:47:f3:79:98:42:dd:16:be:5b:69:2b:c9:26:c8:29:68:77:
+ e6:ac:f6:4e:90:13:28:67:04:ec:72:25:1f:d7:a7:0a:50:7f:
+ 38:0e:72:18:b1:29:b8:ff:ae:a1:d4:54:b8:66:4d:a0:d5:cf:
+ d3:ef:a9:32:2a:c5:97:62:d2:84:cc:b0:a0:d8:98:a9:ca:38:
+ e4:cc:44:35:6f:61:26:b0:2e:98:72:f9:38:32:0d:b4:a1:62:
+ 0a:21:62:15:de:bb:6d:93:10:36:53:3b:4a:21:7b:c2:f5:be:
+ 2e:f6:02:13:e9:ae:4c:70:e9:2a:f6:1f:c3:8b:e5:9f:e0:8d:
+ 2a:28:e8:19:2c:b3:65:dd:f7:f1:6f:97:35:9e:db:92:35:63:
+ 81:d7:27:e4:2b:62:aa:fa:62:a1:71:92:8c:0a:16:b7:3d:b5:
+ 4a:65:5b:02
+-----BEGIN CERTIFICATE-----
+MIIEWzCCA0OgAwIBAgILBAAAAAABL07hW2MwDQYJKoZIhvcNAQEFBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTEwNDEzMTAwMDAwWhcNMjIwNDEz
+MTAwMDAwWjBZMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z
+YTEvMC0GA1UEAxMmR2xvYmFsU2lnbiBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0g
+RzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNoUbMUpq4pbR/WNnN
+2EugcgyXW6aIIMO5PUbc0FxSMPb6WU+FX7DbiLSpXysjSKyr9ZJ4FLYyD/tcaoVb
+AJDgu2X1WvlPZ37HbCnsk8ArysRe2LDb1r4/mwvAj6ldrvcAAqT8umYROHf+IyAl
+VRDFvYK5TLFoxuJwe4NcE2fBofN8C6iZmtDimyUxyCuNQPZSY7GgrVou9Xk2bTUs
+Dt0F5NDiB0i3KF4r1VjVbNAMoQFGAVqPxq9kx1UBXeHRxmxQJaAFrQCrDI1la93r
+wnJUyQ88ABeHIu/buYZ4FlGud9mmKE3zWI2DZ7k0JZscUYBR84OSaqOuR5rW5Isb
+wO2xAgMBAAGjggEvMIIBKzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
+/wIBADAdBgNVHQ4EFgQUsLBK/Rx1KPgcYaoT9vrBkD1rFqMwRwYDVR0gBEAwPjA8
+BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29t
+L3JlcG9zaXRvcnkvMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFs
+c2lnbi5uZXQvcm9vdC1yMi5jcmwwRAYIKwYBBQUHAQEEODA2MDQGCCsGAQUFBzAB
+hihodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9FeHRlbmRlZFNTTENBMB8GA1Ud
+IwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMA0GCSqGSIb3DQEBBQUAA4IBAQBf
+KJAMLekgsjB8iKtABfqxnVwik9WdyjUx+izqHZNZGcSgDfsJQDHaZFbNUr7nGGbo
+bQmbstuUPu42RR4kVLYgBZO1MRq4ZFfm0ywBTDmWef63BJgS77cuWnf+R/N5mELd
+Fr5baSvJJsgpaHfmrPZOkBMoZwTsciUf16cKUH84DnIYsSm4/66h1FS4Zk2g1c/T
+76kyKsWXYtKEzLCg2JipyjjkzEQ1b2EmsC6Ycvk4Mg20oWIKIWIV3rttkxA2UztK
+IXvC9b4u9gIT6a5McOkq9h/Di+Wf4I0qKOgZLLNl3ffxb5c1ntuSNWOB1yfkK2Kq
++mKhcZKMCha3PbVKZVsC
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert29[] = {
+ 0x30, 0x82, 0x04, 0x5b, 0x30, 0x82, 0x03, 0x43, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x5b, 0x63, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4c, 0x31, 0x20, 0x30, 0x1e, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x17, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x52, 0x32, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x59, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73,
+ 0x61, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xcd,
+ 0xa1, 0x46, 0xcc, 0x52, 0x9a, 0xb8, 0xa5, 0xb4, 0x7f, 0x58, 0xd9, 0xcd,
+ 0xd8, 0x4b, 0xa0, 0x72, 0x0c, 0x97, 0x5b, 0xa6, 0x88, 0x20, 0xc3, 0xb9,
+ 0x3d, 0x46, 0xdc, 0xd0, 0x5c, 0x52, 0x30, 0xf6, 0xfa, 0x59, 0x4f, 0x85,
+ 0x5f, 0xb0, 0xdb, 0x88, 0xb4, 0xa9, 0x5f, 0x2b, 0x23, 0x48, 0xac, 0xab,
+ 0xf5, 0x92, 0x78, 0x14, 0xb6, 0x32, 0x0f, 0xfb, 0x5c, 0x6a, 0x85, 0x5b,
+ 0x00, 0x90, 0xe0, 0xbb, 0x65, 0xf5, 0x5a, 0xf9, 0x4f, 0x67, 0x7e, 0xc7,
+ 0x6c, 0x29, 0xec, 0x93, 0xc0, 0x2b, 0xca, 0xc4, 0x5e, 0xd8, 0xb0, 0xdb,
+ 0xd6, 0xbe, 0x3f, 0x9b, 0x0b, 0xc0, 0x8f, 0xa9, 0x5d, 0xae, 0xf7, 0x00,
+ 0x02, 0xa4, 0xfc, 0xba, 0x66, 0x11, 0x38, 0x77, 0xfe, 0x23, 0x20, 0x25,
+ 0x55, 0x10, 0xc5, 0xbd, 0x82, 0xb9, 0x4c, 0xb1, 0x68, 0xc6, 0xe2, 0x70,
+ 0x7b, 0x83, 0x5c, 0x13, 0x67, 0xc1, 0xa1, 0xf3, 0x7c, 0x0b, 0xa8, 0x99,
+ 0x9a, 0xd0, 0xe2, 0x9b, 0x25, 0x31, 0xc8, 0x2b, 0x8d, 0x40, 0xf6, 0x52,
+ 0x63, 0xb1, 0xa0, 0xad, 0x5a, 0x2e, 0xf5, 0x79, 0x36, 0x6d, 0x35, 0x2c,
+ 0x0e, 0xdd, 0x05, 0xe4, 0xd0, 0xe2, 0x07, 0x48, 0xb7, 0x28, 0x5e, 0x2b,
+ 0xd5, 0x58, 0xd5, 0x6c, 0xd0, 0x0c, 0xa1, 0x01, 0x46, 0x01, 0x5a, 0x8f,
+ 0xc6, 0xaf, 0x64, 0xc7, 0x55, 0x01, 0x5d, 0xe1, 0xd1, 0xc6, 0x6c, 0x50,
+ 0x25, 0xa0, 0x05, 0xad, 0x00, 0xab, 0x0c, 0x8d, 0x65, 0x6b, 0xdd, 0xeb,
+ 0xc2, 0x72, 0x54, 0xc9, 0x0f, 0x3c, 0x00, 0x17, 0x87, 0x22, 0xef, 0xdb,
+ 0xb9, 0x86, 0x78, 0x16, 0x51, 0xae, 0x77, 0xd9, 0xa6, 0x28, 0x4d, 0xf3,
+ 0x58, 0x8d, 0x83, 0x67, 0xb9, 0x34, 0x25, 0x9b, 0x1c, 0x51, 0x80, 0x51,
+ 0xf3, 0x83, 0x92, 0x6a, 0xa3, 0xae, 0x47, 0x9a, 0xd6, 0xe4, 0x8b, 0x1b,
+ 0xc0, 0xed, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x2f,
+ 0x30, 0x82, 0x01, 0x2b, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0xb0, 0xb0, 0x4a, 0xfd, 0x1c, 0x75, 0x28, 0xf8, 0x1c,
+ 0x61, 0xaa, 0x13, 0xf6, 0xfa, 0xc1, 0x90, 0x3d, 0x6b, 0x16, 0xa3, 0x30,
+ 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30,
+ 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2d, 0x72, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x44, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x38, 0x30, 0x36,
+ 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
+ 0x64, 0x53, 0x53, 0x4c, 0x43, 0x41, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x9b, 0xe2, 0x07, 0x57, 0x67,
+ 0x1c, 0x1e, 0xc0, 0x6a, 0x06, 0xde, 0x59, 0xb4, 0x9a, 0x2d, 0xdf, 0xdc,
+ 0x19, 0x86, 0x2e, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x5f,
+ 0x28, 0x90, 0x0c, 0x2d, 0xe9, 0x20, 0xb2, 0x30, 0x7c, 0x88, 0xab, 0x40,
+ 0x05, 0xfa, 0xb1, 0x9d, 0x5c, 0x22, 0x93, 0xd5, 0x9d, 0xca, 0x35, 0x31,
+ 0xfa, 0x2c, 0xea, 0x1d, 0x93, 0x59, 0x19, 0xc4, 0xa0, 0x0d, 0xfb, 0x09,
+ 0x40, 0x31, 0xda, 0x64, 0x56, 0xcd, 0x52, 0xbe, 0xe7, 0x18, 0x66, 0xe8,
+ 0x6d, 0x09, 0x9b, 0xb2, 0xdb, 0x94, 0x3e, 0xee, 0x36, 0x45, 0x1e, 0x24,
+ 0x54, 0xb6, 0x20, 0x05, 0x93, 0xb5, 0x31, 0x1a, 0xb8, 0x64, 0x57, 0xe6,
+ 0xd3, 0x2c, 0x01, 0x4c, 0x39, 0x96, 0x79, 0xfe, 0xb7, 0x04, 0x98, 0x12,
+ 0xef, 0xb7, 0x2e, 0x5a, 0x77, 0xfe, 0x47, 0xf3, 0x79, 0x98, 0x42, 0xdd,
+ 0x16, 0xbe, 0x5b, 0x69, 0x2b, 0xc9, 0x26, 0xc8, 0x29, 0x68, 0x77, 0xe6,
+ 0xac, 0xf6, 0x4e, 0x90, 0x13, 0x28, 0x67, 0x04, 0xec, 0x72, 0x25, 0x1f,
+ 0xd7, 0xa7, 0x0a, 0x50, 0x7f, 0x38, 0x0e, 0x72, 0x18, 0xb1, 0x29, 0xb8,
+ 0xff, 0xae, 0xa1, 0xd4, 0x54, 0xb8, 0x66, 0x4d, 0xa0, 0xd5, 0xcf, 0xd3,
+ 0xef, 0xa9, 0x32, 0x2a, 0xc5, 0x97, 0x62, 0xd2, 0x84, 0xcc, 0xb0, 0xa0,
+ 0xd8, 0x98, 0xa9, 0xca, 0x38, 0xe4, 0xcc, 0x44, 0x35, 0x6f, 0x61, 0x26,
+ 0xb0, 0x2e, 0x98, 0x72, 0xf9, 0x38, 0x32, 0x0d, 0xb4, 0xa1, 0x62, 0x0a,
+ 0x21, 0x62, 0x15, 0xde, 0xbb, 0x6d, 0x93, 0x10, 0x36, 0x53, 0x3b, 0x4a,
+ 0x21, 0x7b, 0xc2, 0xf5, 0xbe, 0x2e, 0xf6, 0x02, 0x13, 0xe9, 0xae, 0x4c,
+ 0x70, 0xe9, 0x2a, 0xf6, 0x1f, 0xc3, 0x8b, 0xe5, 0x9f, 0xe0, 0x8d, 0x2a,
+ 0x28, 0xe8, 0x19, 0x2c, 0xb3, 0x65, 0xdd, 0xf7, 0xf1, 0x6f, 0x97, 0x35,
+ 0x9e, 0xdb, 0x92, 0x35, 0x63, 0x81, 0xd7, 0x27, 0xe4, 0x2b, 0x62, 0xaa,
+ 0xfa, 0x62, 0xa1, 0x71, 0x92, 0x8c, 0x0a, 0x16, 0xb7, 0x3d, 0xb5, 0x4a,
+ 0x65, 0x5b, 0x02,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120013506 (0x72742c2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Nov 30 16:24:37 2010 GMT
+ Not After : Nov 30 16:23:46 2017 GMT
+ Subject: C=DE, O=T-Systems International GmbH, OU=Trust Center Services, CN=TeleSec ServerPass CA 1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e6:e8:cd:30:21:8d:b9:8f:ee:f7:54:57:ab:5c:
+ da:9d:70:d0:20:7a:a6:89:f8:e6:21:7d:97:1b:69:
+ 00:83:1d:4f:ba:d2:98:6d:0b:c7:ed:8f:26:01:a6:
+ 66:1c:48:f5:d5:d3:db:0a:ad:bf:77:44:7e:06:33:
+ 90:f7:e7:28:f5:82:c3:f0:43:49:76:ed:73:77:5d:
+ 83:e9:6b:18:01:ea:ee:4f:1d:b2:49:e0:32:02:d1:
+ 25:36:dc:6f:28:64:bf:89:34:83:0f:ea:03:56:df:
+ b6:57:93:bf:c6:56:f7:f0:dd:50:ac:b6:4f:4d:2a:
+ 53:11:f1:d3:24:2f:11:39:0b:05:57:1f:de:3d:71:
+ 1a:2f:b4:9c:f5:ab:e3:d2:d1:d1:29:c0:36:91:21:
+ 61:e7:c5:d0:f6:40:da:e9:f1:4c:96:6c:d7:c9:13:
+ 09:a5:22:5d:06:f7:0c:3d:65:a3:fc:52:b9:7e:72:
+ bd:33:7b:dd:7c:ae:7a:2b:c1:4f:aa:fc:c8:f8:c5:
+ 9d:25:86:53:55:74:bc:1e:ca:42:4a:33:e2:12:2d:
+ dc:d2:29:d9:7b:ad:3a:40:ec:01:d1:05:ec:8a:9c:
+ 23:ea:86:30:be:f3:e6:e9:08:cc:9e:b4:40:8c:af:
+ 02:e7:7e:3f:7e:e2:c1:08:02:d7:23:29:63:7b:eb:
+ 82:55
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+ Policy: 1.3.6.1.4.1.7879.13.2
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 33:DC:9E:96:EC:D8:E8:35:1F:6D:90:1B:0B:38:A4:AF:74:1B:C6:58
+ Signature Algorithm: sha1WithRSAEncryption
+ 74:76:a8:19:77:97:5c:af:5b:8f:e5:e5:ec:dd:59:bb:90:e6:
+ 52:4c:1e:c4:fe:da:34:e6:2a:fc:3c:dd:b0:7b:69:57:82:c5:
+ 24:26:0c:74:1e:d9:a0:7d:7e:8b:fa:76:ab:a1:7d:58:ae:34:
+ 12:fb:99:24:14:94:31:15:62:d7:d9:5c:79:33:1c:e2:1b:91:
+ 13:fd:f8:cf:1c:ff:2c:cc:b5:ed:27:8e:b9:97:30:b1:de:e9:
+ e6:d2:10:e3:a6:d7:9a:f0:39:1a:4e:3b:ae:e2:0b:b2:ea:0d:
+ 80:61:31:33:cc:73:f1:d3:0c:e9:31:26:9e:78:d9:08:67:93:
+ 71:e8:b6:f1:a3:66:fd:00:46:0c:7f:da:c3:fc:63:d8:c5:3f:
+ c8:23:70:b9:a8:60:c2:5e:47:a8:8d:19:89:63:a7:a0:69:07:
+ 9c:27:54:22:2c:4e:c7:3d:99:3c:b9:fc:93:80:65:bd:bb:d0:
+ f8:ea:8c:fb:71:e1:e8:a6:07:40:f1:d0:05:85:da:38:71:59:
+ f4:98:b7:64:d3:76:10:f5:b9:6a:d7:15:0e:58:fe:e6:6b:29:
+ 4a:ee:75:88:9f:5f:66:80:07:6b:6b:55:68:bc:66:39:c5:9f:
+ 57:fe:35:f5:89:0f:07:57:fa:d1:57:ef:4d:b2:d7:77:c8:bc:
+ f8:65:0f:9e
+-----BEGIN CERTIFICATE-----
+MIIEXjCCA0agAwIBAgIEBydCwjANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEwMTEzMDE2MjQzN1oX
+DTE3MTEzMDE2MjM0NlowdjELMAkGA1UEBhMCREUxJTAjBgNVBAoTHFQtU3lzdGVt
+cyBJbnRlcm5hdGlvbmFsIEdtYkgxHjAcBgNVBAsTFVRydXN0IENlbnRlciBTZXJ2
+aWNlczEgMB4GA1UEAxMXVGVsZVNlYyBTZXJ2ZXJQYXNzIENBIDEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDm6M0wIY25j+73VFerXNqdcNAgeqaJ+OYh
+fZcbaQCDHU+60phtC8ftjyYBpmYcSPXV09sKrb93RH4GM5D35yj1gsPwQ0l27XN3
+XYPpaxgB6u5PHbJJ4DIC0SU23G8oZL+JNIMP6gNW37ZXk7/GVvfw3VCstk9NKlMR
+8dMkLxE5CwVXH949cRovtJz1q+PS0dEpwDaRIWHnxdD2QNrp8UyWbNfJEwmlIl0G
+9ww9ZaP8Url+cr0ze918rnorwU+q/Mj4xZ0lhlNVdLweykJKM+ISLdzSKdl7rTpA
+7AHRBeyKnCPqhjC+8+bpCMyetECMrwLnfj9+4sEIAtcjKWN764JVAgMBAAGjggEO
+MIIBCjASBgNVHRMBAf8ECDAGAQH/AgEAMGAGA1UdIARZMFcwSAYJKwYBBAGxPgEA
+MDswOQYIKwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9tbmlyb290LmNvbS9y
+ZXBvc2l0b3J5LmNmbTALBgkrBgEEAb1HDQIwDgYDVR0PAQH/BAQDAgEGMB8GA1Ud
+IwQYMBaAFOWdWTCCR1jMrPoIVDaGezq1BE3wMEIGA1UdHwQ7MDkwN6A1oDOGMWh0
+dHA6Ly9jZHAxLnB1YmxpYy10cnVzdC5jb20vQ1JML09tbmlyb290MjAyNS5jcmww
+HQYDVR0OBBYEFDPcnpbs2Og1H22QGws4pK90G8ZYMA0GCSqGSIb3DQEBBQUAA4IB
+AQB0dqgZd5dcr1uP5eXs3Vm7kOZSTB7E/to05ir8PN2we2lXgsUkJgx0HtmgfX6L
++naroX1YrjQS+5kkFJQxFWLX2Vx5MxziG5ET/fjPHP8szLXtJ465lzCx3unm0hDj
+ptea8DkaTjuu4guy6g2AYTEzzHPx0wzpMSaeeNkIZ5Nx6Lbxo2b9AEYMf9rD/GPY
+xT/II3C5qGDCXkeojRmJY6egaQecJ1QiLE7HPZk8ufyTgGW9u9D46oz7ceHopgdA
+8dAFhdo4cVn0mLdk03YQ9blq1xUOWP7maylK7nWIn19mgAdra1VovGY5xZ9X/jX1
+iQ8HV/rRV+9Nstd3yLz4ZQ+e
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert30[] = {
+ 0x30, 0x82, 0x04, 0x5e, 0x30, 0x82, 0x03, 0x46, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x42, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+ 0x31, 0x31, 0x33, 0x30, 0x31, 0x36, 0x32, 0x34, 0x33, 0x37, 0x5a, 0x17,
+ 0x0d, 0x31, 0x37, 0x31, 0x31, 0x33, 0x30, 0x31, 0x36, 0x32, 0x33, 0x34,
+ 0x36, 0x5a, 0x30, 0x76, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x44, 0x45, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x1c, 0x54, 0x2d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x73, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x31, 0x1e, 0x30, 0x1c,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x20, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x20, 0x53, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x73, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x17, 0x54, 0x65, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x20, 0x53,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x61, 0x73, 0x73, 0x20, 0x43, 0x41,
+ 0x20, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe6,
+ 0xe8, 0xcd, 0x30, 0x21, 0x8d, 0xb9, 0x8f, 0xee, 0xf7, 0x54, 0x57, 0xab,
+ 0x5c, 0xda, 0x9d, 0x70, 0xd0, 0x20, 0x7a, 0xa6, 0x89, 0xf8, 0xe6, 0x21,
+ 0x7d, 0x97, 0x1b, 0x69, 0x00, 0x83, 0x1d, 0x4f, 0xba, 0xd2, 0x98, 0x6d,
+ 0x0b, 0xc7, 0xed, 0x8f, 0x26, 0x01, 0xa6, 0x66, 0x1c, 0x48, 0xf5, 0xd5,
+ 0xd3, 0xdb, 0x0a, 0xad, 0xbf, 0x77, 0x44, 0x7e, 0x06, 0x33, 0x90, 0xf7,
+ 0xe7, 0x28, 0xf5, 0x82, 0xc3, 0xf0, 0x43, 0x49, 0x76, 0xed, 0x73, 0x77,
+ 0x5d, 0x83, 0xe9, 0x6b, 0x18, 0x01, 0xea, 0xee, 0x4f, 0x1d, 0xb2, 0x49,
+ 0xe0, 0x32, 0x02, 0xd1, 0x25, 0x36, 0xdc, 0x6f, 0x28, 0x64, 0xbf, 0x89,
+ 0x34, 0x83, 0x0f, 0xea, 0x03, 0x56, 0xdf, 0xb6, 0x57, 0x93, 0xbf, 0xc6,
+ 0x56, 0xf7, 0xf0, 0xdd, 0x50, 0xac, 0xb6, 0x4f, 0x4d, 0x2a, 0x53, 0x11,
+ 0xf1, 0xd3, 0x24, 0x2f, 0x11, 0x39, 0x0b, 0x05, 0x57, 0x1f, 0xde, 0x3d,
+ 0x71, 0x1a, 0x2f, 0xb4, 0x9c, 0xf5, 0xab, 0xe3, 0xd2, 0xd1, 0xd1, 0x29,
+ 0xc0, 0x36, 0x91, 0x21, 0x61, 0xe7, 0xc5, 0xd0, 0xf6, 0x40, 0xda, 0xe9,
+ 0xf1, 0x4c, 0x96, 0x6c, 0xd7, 0xc9, 0x13, 0x09, 0xa5, 0x22, 0x5d, 0x06,
+ 0xf7, 0x0c, 0x3d, 0x65, 0xa3, 0xfc, 0x52, 0xb9, 0x7e, 0x72, 0xbd, 0x33,
+ 0x7b, 0xdd, 0x7c, 0xae, 0x7a, 0x2b, 0xc1, 0x4f, 0xaa, 0xfc, 0xc8, 0xf8,
+ 0xc5, 0x9d, 0x25, 0x86, 0x53, 0x55, 0x74, 0xbc, 0x1e, 0xca, 0x42, 0x4a,
+ 0x33, 0xe2, 0x12, 0x2d, 0xdc, 0xd2, 0x29, 0xd9, 0x7b, 0xad, 0x3a, 0x40,
+ 0xec, 0x01, 0xd1, 0x05, 0xec, 0x8a, 0x9c, 0x23, 0xea, 0x86, 0x30, 0xbe,
+ 0xf3, 0xe6, 0xe9, 0x08, 0xcc, 0x9e, 0xb4, 0x40, 0x8c, 0xaf, 0x02, 0xe7,
+ 0x7e, 0x3f, 0x7e, 0xe2, 0xc1, 0x08, 0x02, 0xd7, 0x23, 0x29, 0x63, 0x7b,
+ 0xeb, 0x82, 0x55, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0e,
+ 0x30, 0x82, 0x01, 0x0a, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59, 0x30, 0x57, 0x30,
+ 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00,
+ 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66,
+ 0x6d, 0x30, 0x0b, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xbd, 0x47,
+ 0x0d, 0x02, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82,
+ 0x47, 0x58, 0xcc, 0xac, 0xfa, 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5,
+ 0x04, 0x4d, 0xf0, 0x30, 0x42, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b,
+ 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72,
+ 0x6f, 0x6f, 0x74, 0x32, 0x30, 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x33, 0xdc,
+ 0x9e, 0x96, 0xec, 0xd8, 0xe8, 0x35, 0x1f, 0x6d, 0x90, 0x1b, 0x0b, 0x38,
+ 0xa4, 0xaf, 0x74, 0x1b, 0xc6, 0x58, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x74, 0x76, 0xa8, 0x19, 0x77, 0x97, 0x5c, 0xaf, 0x5b, 0x8f,
+ 0xe5, 0xe5, 0xec, 0xdd, 0x59, 0xbb, 0x90, 0xe6, 0x52, 0x4c, 0x1e, 0xc4,
+ 0xfe, 0xda, 0x34, 0xe6, 0x2a, 0xfc, 0x3c, 0xdd, 0xb0, 0x7b, 0x69, 0x57,
+ 0x82, 0xc5, 0x24, 0x26, 0x0c, 0x74, 0x1e, 0xd9, 0xa0, 0x7d, 0x7e, 0x8b,
+ 0xfa, 0x76, 0xab, 0xa1, 0x7d, 0x58, 0xae, 0x34, 0x12, 0xfb, 0x99, 0x24,
+ 0x14, 0x94, 0x31, 0x15, 0x62, 0xd7, 0xd9, 0x5c, 0x79, 0x33, 0x1c, 0xe2,
+ 0x1b, 0x91, 0x13, 0xfd, 0xf8, 0xcf, 0x1c, 0xff, 0x2c, 0xcc, 0xb5, 0xed,
+ 0x27, 0x8e, 0xb9, 0x97, 0x30, 0xb1, 0xde, 0xe9, 0xe6, 0xd2, 0x10, 0xe3,
+ 0xa6, 0xd7, 0x9a, 0xf0, 0x39, 0x1a, 0x4e, 0x3b, 0xae, 0xe2, 0x0b, 0xb2,
+ 0xea, 0x0d, 0x80, 0x61, 0x31, 0x33, 0xcc, 0x73, 0xf1, 0xd3, 0x0c, 0xe9,
+ 0x31, 0x26, 0x9e, 0x78, 0xd9, 0x08, 0x67, 0x93, 0x71, 0xe8, 0xb6, 0xf1,
+ 0xa3, 0x66, 0xfd, 0x00, 0x46, 0x0c, 0x7f, 0xda, 0xc3, 0xfc, 0x63, 0xd8,
+ 0xc5, 0x3f, 0xc8, 0x23, 0x70, 0xb9, 0xa8, 0x60, 0xc2, 0x5e, 0x47, 0xa8,
+ 0x8d, 0x19, 0x89, 0x63, 0xa7, 0xa0, 0x69, 0x07, 0x9c, 0x27, 0x54, 0x22,
+ 0x2c, 0x4e, 0xc7, 0x3d, 0x99, 0x3c, 0xb9, 0xfc, 0x93, 0x80, 0x65, 0xbd,
+ 0xbb, 0xd0, 0xf8, 0xea, 0x8c, 0xfb, 0x71, 0xe1, 0xe8, 0xa6, 0x07, 0x40,
+ 0xf1, 0xd0, 0x05, 0x85, 0xda, 0x38, 0x71, 0x59, 0xf4, 0x98, 0xb7, 0x64,
+ 0xd3, 0x76, 0x10, 0xf5, 0xb9, 0x6a, 0xd7, 0x15, 0x0e, 0x58, 0xfe, 0xe6,
+ 0x6b, 0x29, 0x4a, 0xee, 0x75, 0x88, 0x9f, 0x5f, 0x66, 0x80, 0x07, 0x6b,
+ 0x6b, 0x55, 0x68, 0xbc, 0x66, 0x39, 0xc5, 0x9f, 0x57, 0xfe, 0x35, 0xf5,
+ 0x89, 0x0f, 0x07, 0x57, 0xfa, 0xd1, 0x57, 0xef, 0x4d, 0xb2, 0xd7, 0x77,
+ 0xc8, 0xbc, 0xf8, 0x65, 0x0f, 0x9e,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:45:0c
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Organization Validation CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:dd:35:1d:f2:20:54:26:1a:d0:ef:a5:6f:81:76:
+ 59:70:dc:e7:f4:d4:03:24:1f:24:0e:9d:22:9f:d4:
+ 27:32:7a:2b:7c:ee:8b:e3:61:62:38:17:af:b4:4b:
+ 7a:9f:67:21:1c:2d:95:54:ba:79:ba:b6:c4:f2:0d:
+ 21:74:17:67:74:e2:b1:64:08:99:60:78:fb:67:c2:
+ 4b:f7:27:8d:6f:36:76:cf:31:8c:e5:f1:06:d7:dc:
+ 57:0e:5b:ac:ee:ce:2d:ab:aa:a9:70:2f:02:86:c8:
+ b1:d0:08:07:95:ea:2a:ec:d1:9e:e4:36:5c:3b:a6:
+ 36:b5:43:8b:ab:f7:8e:3e:00:1b:ff:85:59:6b:62:
+ 01:8d:82:e8:4a:ba:38:b3:e0:c3:f4:6d:19:a7:ea:
+ 05:dd:84:67:c2:66:c7:24:02:73:5a:b5:ee:a4:19:
+ d9:fc:00:ce:b6:a4:8d:df:7e:bd:5f:b2:3a:9d:84:
+ 31:4f:c8:63:0c:e4:d8:0d:52:a3:7e:01:1b:d4:67:
+ a5:18:28:eb:01:a7:82:3c:d9:8e:1d:e5:47:0d:ba:
+ 8b:59:14:a3:1f:1f:4b:ea:e2:27:46:86:ce:9d:39:
+ c4:66:41:a7:e2:15:23:6b:56:47:c1:ed:c5:53:e4:
+ d4:80:1f:6b:fa:80:46:98:b2:09:a6:0f:95:be:66:
+ 88:93
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 5D:46:B2:8D:C4:4B:74:1C:BB:ED:F5:73:B6:3A:B7:38:8F:75:9E:7E
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 1b:e0:88:00:c7:05:11:1c:ff:ab:2d:48:42:52:cd:20:68:e7:
+ 7c:41:8d:c1:27:c5:2c:59:67:a0:9a:35:db:b3:50:a7:1b:62:
+ e9:a5:3b:fa:b6:21:07:a7:7c:f3:0e:2b:6e:7e:2e:4d:93:9c:
+ ba:f2:86:3e:63:88:10:d8:5b:61:50:12:db:87:ae:19:bb:d2:
+ df:32:96:00:a8:5e:dc:2d:23:bc:b0:d3:b5:4a:a0:8e:65:91:
+ 2f:d9:f6:82:f6:74:b2:df:7c:26:ef:19:2b:97:2f:e0:a1:ee:
+ b9:17:22:48:3f:a5:f7:0d:60:d5:0d:51:47:e5:58:fe:b7:9f:
+ 8d:5e:75:3c:c6:41:f0:cf:81:54:49:11:c6:17:a4:e0:56:61:
+ dc:3d:3f:dd:67:6c:76:45:da:4a:ea:ae:1a:a4:60:4f:c7:a3:
+ d6:aa:a7:d9:cd:81:2b:c1:66:75:b2:80:8f:f5:87:4d:5f:c2:
+ 5a:f5:90:c6:da:c1:bd:f4:85:a8:3c:23:2a:e1:14:7b:c1:37:
+ dd:62:d1:92:6c:ba:60:7d:88:e4:1c:b7:e4:76:51:38:c4:a9:
+ 47:4e:a8:2b:2e:90:d2:b5:38:51:eb:c1:9c:8a:6a:b5:cc:b2:
+ 1d:e8:c0:56:54:4c:a8:8b:f0:89:32:86:dc:93:32:be:4d:1a:
+ fa:35:75:b5
+-----BEGIN CERTIFICATE-----
+MIIEYDCCA0igAwIBAgILBAAAAAABL07hRQwwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
+MDBaFw0yMjA0MTMxMDAwMDBaMF0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMTMwMQYDVQQDEypHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBW
+YWxpZGF0aW9uIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDdNR3yIFQmGtDvpW+Bdllw3Of01AMkHyQOnSKf1Ccyeit87ovjYWI4F6+0S3qf
+ZyEcLZVUunm6tsTyDSF0F2d04rFkCJlgePtnwkv3J41vNnbPMYzl8QbX3FcOW6zu
+zi2rqqlwLwKGyLHQCAeV6irs0Z7kNlw7pja1Q4ur944+ABv/hVlrYgGNguhKujiz
+4MP0bRmn6gXdhGfCZsckAnNate6kGdn8AM62pI3ffr1fsjqdhDFPyGMM5NgNUqN+
+ARvUZ6UYKOsBp4I82Y4d5UcNuotZFKMfH0vq4idGhs6dOcRmQafiFSNrVkfB7cVT
+5NSAH2v6gEaYsgmmD5W+ZoiTAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMCAQYw
+EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUXUayjcRLdBy77fVztjq3OI91
+nn4wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3
+Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0
+dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEEMTAv
+MC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEw
+HwYDVR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQAD
+ggEBABvgiADHBREc/6stSEJSzSBo53xBjcEnxSxZZ6CaNduzUKcbYumlO/q2IQen
+fPMOK25+Lk2TnLryhj5jiBDYW2FQEtuHrhm70t8ylgCoXtwtI7yw07VKoI5lkS/Z
+9oL2dLLffCbvGSuXL+Ch7rkXIkg/pfcNYNUNUUflWP63n41edTzGQfDPgVRJEcYX
+pOBWYdw9P91nbHZF2krqrhqkYE/Ho9aqp9nNgSvBZnWygI/1h01fwlr1kMbawb30
+hag8IyrhFHvBN91i0ZJsumB9iOQct+R2UTjEqUdOqCsukNK1OFHrwZyKarXMsh3o
+wFZUTKiL8IkyhtyTMr5NGvo1dbU=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert31[] = {
+ 0x30, 0x82, 0x04, 0x60, 0x30, 0x82, 0x03, 0x48, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x45, 0x0c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x5d, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72,
+ 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xdd, 0x35, 0x1d, 0xf2, 0x20, 0x54, 0x26, 0x1a, 0xd0, 0xef,
+ 0xa5, 0x6f, 0x81, 0x76, 0x59, 0x70, 0xdc, 0xe7, 0xf4, 0xd4, 0x03, 0x24,
+ 0x1f, 0x24, 0x0e, 0x9d, 0x22, 0x9f, 0xd4, 0x27, 0x32, 0x7a, 0x2b, 0x7c,
+ 0xee, 0x8b, 0xe3, 0x61, 0x62, 0x38, 0x17, 0xaf, 0xb4, 0x4b, 0x7a, 0x9f,
+ 0x67, 0x21, 0x1c, 0x2d, 0x95, 0x54, 0xba, 0x79, 0xba, 0xb6, 0xc4, 0xf2,
+ 0x0d, 0x21, 0x74, 0x17, 0x67, 0x74, 0xe2, 0xb1, 0x64, 0x08, 0x99, 0x60,
+ 0x78, 0xfb, 0x67, 0xc2, 0x4b, 0xf7, 0x27, 0x8d, 0x6f, 0x36, 0x76, 0xcf,
+ 0x31, 0x8c, 0xe5, 0xf1, 0x06, 0xd7, 0xdc, 0x57, 0x0e, 0x5b, 0xac, 0xee,
+ 0xce, 0x2d, 0xab, 0xaa, 0xa9, 0x70, 0x2f, 0x02, 0x86, 0xc8, 0xb1, 0xd0,
+ 0x08, 0x07, 0x95, 0xea, 0x2a, 0xec, 0xd1, 0x9e, 0xe4, 0x36, 0x5c, 0x3b,
+ 0xa6, 0x36, 0xb5, 0x43, 0x8b, 0xab, 0xf7, 0x8e, 0x3e, 0x00, 0x1b, 0xff,
+ 0x85, 0x59, 0x6b, 0x62, 0x01, 0x8d, 0x82, 0xe8, 0x4a, 0xba, 0x38, 0xb3,
+ 0xe0, 0xc3, 0xf4, 0x6d, 0x19, 0xa7, 0xea, 0x05, 0xdd, 0x84, 0x67, 0xc2,
+ 0x66, 0xc7, 0x24, 0x02, 0x73, 0x5a, 0xb5, 0xee, 0xa4, 0x19, 0xd9, 0xfc,
+ 0x00, 0xce, 0xb6, 0xa4, 0x8d, 0xdf, 0x7e, 0xbd, 0x5f, 0xb2, 0x3a, 0x9d,
+ 0x84, 0x31, 0x4f, 0xc8, 0x63, 0x0c, 0xe4, 0xd8, 0x0d, 0x52, 0xa3, 0x7e,
+ 0x01, 0x1b, 0xd4, 0x67, 0xa5, 0x18, 0x28, 0xeb, 0x01, 0xa7, 0x82, 0x3c,
+ 0xd9, 0x8e, 0x1d, 0xe5, 0x47, 0x0d, 0xba, 0x8b, 0x59, 0x14, 0xa3, 0x1f,
+ 0x1f, 0x4b, 0xea, 0xe2, 0x27, 0x46, 0x86, 0xce, 0x9d, 0x39, 0xc4, 0x66,
+ 0x41, 0xa7, 0xe2, 0x15, 0x23, 0x6b, 0x56, 0x47, 0xc1, 0xed, 0xc5, 0x53,
+ 0xe4, 0xd4, 0x80, 0x1f, 0x6b, 0xfa, 0x80, 0x46, 0x98, 0xb2, 0x09, 0xa6,
+ 0x0f, 0x95, 0xbe, 0x66, 0x88, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x25, 0x30, 0x82, 0x01, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x5d, 0x46, 0xb2, 0x8d, 0xc4, 0x4b,
+ 0x74, 0x1c, 0xbb, 0xed, 0xf5, 0x73, 0xb6, 0x3a, 0xb7, 0x38, 0x8f, 0x75,
+ 0x9e, 0x7e, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30,
+ 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30,
+ 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c,
+ 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f,
+ 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d,
+ 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x1b, 0xe0, 0x88, 0x00, 0xc7, 0x05, 0x11, 0x1c,
+ 0xff, 0xab, 0x2d, 0x48, 0x42, 0x52, 0xcd, 0x20, 0x68, 0xe7, 0x7c, 0x41,
+ 0x8d, 0xc1, 0x27, 0xc5, 0x2c, 0x59, 0x67, 0xa0, 0x9a, 0x35, 0xdb, 0xb3,
+ 0x50, 0xa7, 0x1b, 0x62, 0xe9, 0xa5, 0x3b, 0xfa, 0xb6, 0x21, 0x07, 0xa7,
+ 0x7c, 0xf3, 0x0e, 0x2b, 0x6e, 0x7e, 0x2e, 0x4d, 0x93, 0x9c, 0xba, 0xf2,
+ 0x86, 0x3e, 0x63, 0x88, 0x10, 0xd8, 0x5b, 0x61, 0x50, 0x12, 0xdb, 0x87,
+ 0xae, 0x19, 0xbb, 0xd2, 0xdf, 0x32, 0x96, 0x00, 0xa8, 0x5e, 0xdc, 0x2d,
+ 0x23, 0xbc, 0xb0, 0xd3, 0xb5, 0x4a, 0xa0, 0x8e, 0x65, 0x91, 0x2f, 0xd9,
+ 0xf6, 0x82, 0xf6, 0x74, 0xb2, 0xdf, 0x7c, 0x26, 0xef, 0x19, 0x2b, 0x97,
+ 0x2f, 0xe0, 0xa1, 0xee, 0xb9, 0x17, 0x22, 0x48, 0x3f, 0xa5, 0xf7, 0x0d,
+ 0x60, 0xd5, 0x0d, 0x51, 0x47, 0xe5, 0x58, 0xfe, 0xb7, 0x9f, 0x8d, 0x5e,
+ 0x75, 0x3c, 0xc6, 0x41, 0xf0, 0xcf, 0x81, 0x54, 0x49, 0x11, 0xc6, 0x17,
+ 0xa4, 0xe0, 0x56, 0x61, 0xdc, 0x3d, 0x3f, 0xdd, 0x67, 0x6c, 0x76, 0x45,
+ 0xda, 0x4a, 0xea, 0xae, 0x1a, 0xa4, 0x60, 0x4f, 0xc7, 0xa3, 0xd6, 0xaa,
+ 0xa7, 0xd9, 0xcd, 0x81, 0x2b, 0xc1, 0x66, 0x75, 0xb2, 0x80, 0x8f, 0xf5,
+ 0x87, 0x4d, 0x5f, 0xc2, 0x5a, 0xf5, 0x90, 0xc6, 0xda, 0xc1, 0xbd, 0xf4,
+ 0x85, 0xa8, 0x3c, 0x23, 0x2a, 0xe1, 0x14, 0x7b, 0xc1, 0x37, 0xdd, 0x62,
+ 0xd1, 0x92, 0x6c, 0xba, 0x60, 0x7d, 0x88, 0xe4, 0x1c, 0xb7, 0xe4, 0x76,
+ 0x51, 0x38, 0xc4, 0xa9, 0x47, 0x4e, 0xa8, 0x2b, 0x2e, 0x90, 0xd2, 0xb5,
+ 0x38, 0x51, 0xeb, 0xc1, 0x9c, 0x8a, 0x6a, 0xb5, 0xcc, 0xb2, 0x1d, 0xe8,
+ 0xc0, 0x56, 0x54, 0x4c, 0xa8, 0x8b, 0xf0, 0x89, 0x32, 0x86, 0xdc, 0x93,
+ 0x32, 0xbe, 0x4d, 0x1a, 0xfa, 0x35, 0x75, 0xb5,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:1e:44:a5:f5:2a
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 11 12:00:00 2007 GMT
+ Not After : Apr 11 12:00:00 2017 GMT
+ Subject: OU=Organization Validation CA, O=GlobalSign, CN=GlobalSign Organization Validation CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a1:2f:c4:bc:ce:87:03:e9:67:c1:89:c8:e5:93:
+ fc:7d:b4:ad:9e:f6:63:4e:6a:e8:9c:2c:73:89:a2:
+ 01:f4:8f:21:f8:fd:25:9d:58:16:6d:86:f6:ee:49:
+ 57:75:7e:75:ea:22:11:7e:3d:fb:c7:42:41:dc:fc:
+ c5:0c:91:55:80:7b:eb:64:33:1d:9b:f9:ca:38:e9:
+ ab:c6:25:43:51:25:40:f4:e4:7e:18:55:6a:a9:8f:
+ 10:3a:40:1e:d6:57:83:ef:7f:2f:34:2f:2d:d2:f6:
+ 53:c2:19:0d:b7:ed:c9:81:f5:46:2c:b4:23:42:5e:
+ 9d:13:03:75:ec:ea:6a:fc:57:7c:c9:36:97:3b:98:
+ dc:13:13:ec:ec:41:fa:5d:34:ea:b9:93:e7:10:16:
+ 65:cc:9c:92:fd:f5:c5:9d:3e:4a:b9:09:fc:e4:5f:
+ 1e:69:5f:4d:f4:56:72:44:b1:1d:23:03:c8:36:f6:
+ 65:88:c8:bf:39:16:45:8e:1e:26:6c:51:16:c5:2a:
+ 00:38:c5:a4:13:69:95:7d:ab:01:3b:a8:c4:14:b4:
+ 80:da:ac:1a:44:20:d5:fe:a9:06:7b:14:27:af:e0:
+ 30:21:dd:90:f4:a9:d5:23:19:2e:1e:03:e6:c1:df:
+ 95:29:e4:c1:94:43:dd:3e:90:aa:cb:4b:c9:be:8a:
+ d3:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 7D:6D:2A:EC:66:AB:A7:51:36:AB:02:69:F1:70:8F:C4:59:0B:9A:1F
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.4146.1.20
+ CPS: http://www.globalsign.net/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Netscape Cert Type:
+ SSL CA
+ X509v3 Extended Key Usage:
+ Microsoft Server Gated Crypto, Netscape Server Gated Crypto
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 79:47:fc:15:d7:4c:79:df:0f:7a:9e:ce:d4:7c:4b:63:c9:89:
+ b5:7b:3f:99:12:e8:9c:8c:9a:49:2f:e0:4e:95:4a:ed:c7:bc:
+ be:f1:a2:db:8e:93:1d:ba:71:54:aa:4b:d9:89:22:24:87:c5:
+ 04:a8:ac:82:52:a0:52:f8:b8:e1:4f:a1:27:66:63:21:4a:39:
+ e7:c7:c5:4e:5f:b2:d6:1d:13:6d:30:e9:ce:d7:a2:1c:bc:29:
+ 0a:73:3c:5b:23:49:fe:d6:ff:ca:b0:4f:f5:f2:67:98:c0:47:
+ 11:f8:b7:48:a6:90:09:d6:42:be:ea:b1:b9:53:42:c3:9c:20:
+ c9:fb:a1:5b:b5:56:6d:87:81:c8:60:ac:c4:b9:72:27:0a:8e:
+ 1e:a8:b1:2e:cd:32:a2:78:57:b0:9c:f8:95:bb:43:8e:8c:31:
+ 86:6e:53:0d:c6:12:05:ba:41:6e:a8:35:30:09:18:1d:02:61:
+ ff:fd:ee:35:de:6a:c3:3b:d0:4d:4b:4e:50:b2:56:36:0c:44:
+ 5d:da:1a:65:2a:e6:98:56:a9:63:33:2e:04:e7:ae:e8:f4:8e:
+ b7:b2:da:7d:c0:c8:e2:ae:a6:28:2f:e3:c9:73:bd:fc:07:41:
+ 34:b7:aa:6e:ee:a7:db:d1:93:3c:ed:90:ec:32:92:88:d9:c8:
+ 23:6c:74:21
+-----BEGIN CERTIFICATE-----
+MIIEZzCCA0+gAwIBAgILBAAAAAABHkSl9SowDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0wNzA0MTExMjAw
+MDBaFw0xNzA0MTExMjAwMDBaMGoxIzAhBgNVBAsTGk9yZ2FuaXphdGlvbiBWYWxp
+ZGF0aW9uIENBMRMwEQYDVQQKEwpHbG9iYWxTaWduMS4wLAYDVQQDEyVHbG9iYWxT
+aWduIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIENBMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAoS/EvM6HA+lnwYnI5ZP8fbStnvZjTmronCxziaIB9I8h
++P0lnVgWbYb27klXdX516iIRfj37x0JB3PzFDJFVgHvrZDMdm/nKOOmrxiVDUSVA
+9OR+GFVqqY8QOkAe1leD738vNC8t0vZTwhkNt+3JgfVGLLQjQl6dEwN17Opq/Fd8
+yTaXO5jcExPs7EH6XTTquZPnEBZlzJyS/fXFnT5KuQn85F8eaV9N9FZyRLEdIwPI
+NvZliMi/ORZFjh4mbFEWxSoAOMWkE2mVfasBO6jEFLSA2qwaRCDV/qkGexQnr+Aw
+Id2Q9KnVIxkuHgPmwd+VKeTBlEPdPpCqy0vJvorTOQIDAQABo4IBHzCCARswDgYD
+VR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFH1tKuxm
+q6dRNqsCafFwj8RZC5ofMEsGA1UdIAREMEIwQAYJKwYBBAGgMgEUMDMwMQYIKwYB
+BQUHAgEWJWh0dHA6Ly93d3cuZ2xvYmFsc2lnbi5uZXQvcmVwb3NpdG9yeS8wMwYD
+VR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5nbG9iYWxzaWduLm5ldC9yb290LmNy
+bDARBglghkgBhvhCAQEEBAMCAgQwIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZI
+AYb4QgQBMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA0GCSqGSIb3
+DQEBBQUAA4IBAQB5R/wV10x53w96ns7UfEtjyYm1ez+ZEuicjJpJL+BOlUrtx7y+
+8aLbjpMdunFUqkvZiSIkh8UEqKyCUqBS+LjhT6EnZmMhSjnnx8VOX7LWHRNtMOnO
+16IcvCkKczxbI0n+1v/KsE/18meYwEcR+LdIppAJ1kK+6rG5U0LDnCDJ+6FbtVZt
+h4HIYKzEuXInCo4eqLEuzTKieFewnPiVu0OOjDGGblMNxhIFukFuqDUwCRgdAmH/
+/e413mrDO9BNS05QslY2DERd2hplKuaYVqljMy4E567o9I63stp9wMjirqYoL+PJ
+c738B0E0t6pu7qfb0ZM87ZDsMpKI2cgjbHQh
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert32[] = {
+ 0x30, 0x82, 0x04, 0x67, 0x30, 0x82, 0x03, 0x4f, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1e, 0x44, 0xa5,
+ 0xf5, 0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x37, 0x30, 0x34, 0x31, 0x31, 0x31, 0x32, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x31, 0x31, 0x31,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6a, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1a, 0x4f, 0x72, 0x67, 0x61, 0x6e,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x25, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
+ 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xa1, 0x2f, 0xc4, 0xbc, 0xce, 0x87, 0x03, 0xe9, 0x67,
+ 0xc1, 0x89, 0xc8, 0xe5, 0x93, 0xfc, 0x7d, 0xb4, 0xad, 0x9e, 0xf6, 0x63,
+ 0x4e, 0x6a, 0xe8, 0x9c, 0x2c, 0x73, 0x89, 0xa2, 0x01, 0xf4, 0x8f, 0x21,
+ 0xf8, 0xfd, 0x25, 0x9d, 0x58, 0x16, 0x6d, 0x86, 0xf6, 0xee, 0x49, 0x57,
+ 0x75, 0x7e, 0x75, 0xea, 0x22, 0x11, 0x7e, 0x3d, 0xfb, 0xc7, 0x42, 0x41,
+ 0xdc, 0xfc, 0xc5, 0x0c, 0x91, 0x55, 0x80, 0x7b, 0xeb, 0x64, 0x33, 0x1d,
+ 0x9b, 0xf9, 0xca, 0x38, 0xe9, 0xab, 0xc6, 0x25, 0x43, 0x51, 0x25, 0x40,
+ 0xf4, 0xe4, 0x7e, 0x18, 0x55, 0x6a, 0xa9, 0x8f, 0x10, 0x3a, 0x40, 0x1e,
+ 0xd6, 0x57, 0x83, 0xef, 0x7f, 0x2f, 0x34, 0x2f, 0x2d, 0xd2, 0xf6, 0x53,
+ 0xc2, 0x19, 0x0d, 0xb7, 0xed, 0xc9, 0x81, 0xf5, 0x46, 0x2c, 0xb4, 0x23,
+ 0x42, 0x5e, 0x9d, 0x13, 0x03, 0x75, 0xec, 0xea, 0x6a, 0xfc, 0x57, 0x7c,
+ 0xc9, 0x36, 0x97, 0x3b, 0x98, 0xdc, 0x13, 0x13, 0xec, 0xec, 0x41, 0xfa,
+ 0x5d, 0x34, 0xea, 0xb9, 0x93, 0xe7, 0x10, 0x16, 0x65, 0xcc, 0x9c, 0x92,
+ 0xfd, 0xf5, 0xc5, 0x9d, 0x3e, 0x4a, 0xb9, 0x09, 0xfc, 0xe4, 0x5f, 0x1e,
+ 0x69, 0x5f, 0x4d, 0xf4, 0x56, 0x72, 0x44, 0xb1, 0x1d, 0x23, 0x03, 0xc8,
+ 0x36, 0xf6, 0x65, 0x88, 0xc8, 0xbf, 0x39, 0x16, 0x45, 0x8e, 0x1e, 0x26,
+ 0x6c, 0x51, 0x16, 0xc5, 0x2a, 0x00, 0x38, 0xc5, 0xa4, 0x13, 0x69, 0x95,
+ 0x7d, 0xab, 0x01, 0x3b, 0xa8, 0xc4, 0x14, 0xb4, 0x80, 0xda, 0xac, 0x1a,
+ 0x44, 0x20, 0xd5, 0xfe, 0xa9, 0x06, 0x7b, 0x14, 0x27, 0xaf, 0xe0, 0x30,
+ 0x21, 0xdd, 0x90, 0xf4, 0xa9, 0xd5, 0x23, 0x19, 0x2e, 0x1e, 0x03, 0xe6,
+ 0xc1, 0xdf, 0x95, 0x29, 0xe4, 0xc1, 0x94, 0x43, 0xdd, 0x3e, 0x90, 0xaa,
+ 0xcb, 0x4b, 0xc9, 0xbe, 0x8a, 0xd3, 0x39, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x1f, 0x30, 0x82, 0x01, 0x1b, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x7d, 0x6d, 0x2a, 0xec, 0x66,
+ 0xab, 0xa7, 0x51, 0x36, 0xab, 0x02, 0x69, 0xf1, 0x70, 0x8f, 0xc4, 0x59,
+ 0x0b, 0x9a, 0x1f, 0x30, 0x4b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x44,
+ 0x30, 0x42, 0x30, 0x40, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xa0,
+ 0x32, 0x01, 0x14, 0x30, 0x33, 0x30, 0x31, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x70,
+ 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0,
+ 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42,
+ 0x01, 0x01, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x19, 0x30, 0x17, 0x06, 0x0a, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45,
+ 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff,
+ 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x79,
+ 0x47, 0xfc, 0x15, 0xd7, 0x4c, 0x79, 0xdf, 0x0f, 0x7a, 0x9e, 0xce, 0xd4,
+ 0x7c, 0x4b, 0x63, 0xc9, 0x89, 0xb5, 0x7b, 0x3f, 0x99, 0x12, 0xe8, 0x9c,
+ 0x8c, 0x9a, 0x49, 0x2f, 0xe0, 0x4e, 0x95, 0x4a, 0xed, 0xc7, 0xbc, 0xbe,
+ 0xf1, 0xa2, 0xdb, 0x8e, 0x93, 0x1d, 0xba, 0x71, 0x54, 0xaa, 0x4b, 0xd9,
+ 0x89, 0x22, 0x24, 0x87, 0xc5, 0x04, 0xa8, 0xac, 0x82, 0x52, 0xa0, 0x52,
+ 0xf8, 0xb8, 0xe1, 0x4f, 0xa1, 0x27, 0x66, 0x63, 0x21, 0x4a, 0x39, 0xe7,
+ 0xc7, 0xc5, 0x4e, 0x5f, 0xb2, 0xd6, 0x1d, 0x13, 0x6d, 0x30, 0xe9, 0xce,
+ 0xd7, 0xa2, 0x1c, 0xbc, 0x29, 0x0a, 0x73, 0x3c, 0x5b, 0x23, 0x49, 0xfe,
+ 0xd6, 0xff, 0xca, 0xb0, 0x4f, 0xf5, 0xf2, 0x67, 0x98, 0xc0, 0x47, 0x11,
+ 0xf8, 0xb7, 0x48, 0xa6, 0x90, 0x09, 0xd6, 0x42, 0xbe, 0xea, 0xb1, 0xb9,
+ 0x53, 0x42, 0xc3, 0x9c, 0x20, 0xc9, 0xfb, 0xa1, 0x5b, 0xb5, 0x56, 0x6d,
+ 0x87, 0x81, 0xc8, 0x60, 0xac, 0xc4, 0xb9, 0x72, 0x27, 0x0a, 0x8e, 0x1e,
+ 0xa8, 0xb1, 0x2e, 0xcd, 0x32, 0xa2, 0x78, 0x57, 0xb0, 0x9c, 0xf8, 0x95,
+ 0xbb, 0x43, 0x8e, 0x8c, 0x31, 0x86, 0x6e, 0x53, 0x0d, 0xc6, 0x12, 0x05,
+ 0xba, 0x41, 0x6e, 0xa8, 0x35, 0x30, 0x09, 0x18, 0x1d, 0x02, 0x61, 0xff,
+ 0xfd, 0xee, 0x35, 0xde, 0x6a, 0xc3, 0x3b, 0xd0, 0x4d, 0x4b, 0x4e, 0x50,
+ 0xb2, 0x56, 0x36, 0x0c, 0x44, 0x5d, 0xda, 0x1a, 0x65, 0x2a, 0xe6, 0x98,
+ 0x56, 0xa9, 0x63, 0x33, 0x2e, 0x04, 0xe7, 0xae, 0xe8, 0xf4, 0x8e, 0xb7,
+ 0xb2, 0xda, 0x7d, 0xc0, 0xc8, 0xe2, 0xae, 0xa6, 0x28, 0x2f, 0xe3, 0xc9,
+ 0x73, 0xbd, 0xfc, 0x07, 0x41, 0x34, 0xb7, 0xaa, 0x6e, 0xee, 0xa7, 0xdb,
+ 0xd1, 0x93, 0x3c, 0xed, 0x90, 0xec, 0x32, 0x92, 0x88, 0xd9, 0xc8, 0x23,
+ 0x6c, 0x74, 0x21,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1200005093 (0x47869fe5)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=SecureTrust Corporation, CN=SecureTrust CA
+ Validity
+ Not Before: Dec 22 23:47:39 2008 GMT
+ Not After : Dec 22 23:47:39 2028 GMT
+ Subject: C=US, ST=Illinois, L=Chicago, O=Trustwave Holdings, Inc., CN=Trustwave Organization Validation CA, Level 2/emailAddress=ca@trustwave.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e8:14:ee:a0:da:97:bd:89:bd:0c:cc:8d:df:08:
+ fb:06:09:83:a8:23:51:5b:01:3f:34:da:1f:1f:6e:
+ c1:e3:58:65:cb:0a:f9:f3:87:c8:c8:fa:a1:cc:f5:
+ 74:e2:b8:ae:2d:06:84:80:28:6f:5a:c1:22:5c:92:
+ 94:42:cd:19:02:12:5c:10:62:7e:a2:44:fb:16:5e:
+ 9c:72:b1:ac:ea:04:e6:15:aa:99:e8:5a:f8:58:b9:
+ 87:24:e8:75:cd:25:88:e2:58:92:5e:86:83:7f:8a:
+ 23:53:ae:8a:e8:a3:21:7e:83:af:40:09:18:49:af:
+ e1:d0:5a:b0:4f:6f:e2:31:ad:f4:f1:37:1f:c9:2a:
+ e1:8b:d6:8c:12:31:d4:27:1a:df:ea:6b:9e:78:53:
+ ed:9a:19:b0:ce:45:44:5b:1b:ef:64:59:21:fa:c7:
+ b7:d1:d3:0c:1e:cb:88:da:fd:23:3f:f4:ac:2b:a0:
+ 4d:61:d3:be:ca:de:19:61:61:24:f1:f6:9c:b4:96:
+ bd:9d:eb:17:9f:24:39:78:e9:23:50:d3:01:50:77:
+ d8:52:64:2f:3e:19:4f:75:b9:17:b1:da:8d:e0:d0:
+ ed:db:37:13:dc:2f:e0:5f:80:68:d7:f4:87:ba:c1:
+ 1f:12:78:d0:08:27:17:7a:98:a6:9f:d2:21:ba:4e:
+ 87:bf
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ 5D:D9:96:9A:40:C7:27:CB:2C:9B:A2:EC:CF:19:AB:C8:AF:CC:86:48
+ X509v3 Authority Key Identifier:
+ keyid:42:32:B6:16:FA:04:FD:FE:5D:4B:7A:C3:FD:F7:4C:40:1D:5A:43:AF
+
+ X509v3 Key Usage:
+ Certificate Sign, CRL Sign
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.securetrust.com/STCA.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.30360.3.0
+ Policy: 1.3.6.1.4.1.30360.3.3.3.3.4.4.3
+ CPS: http://www.securetrust.com/legal/
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 53:f1:c8:a0:17:ec:6c:88:82:b1:c0:24:af:d1:08:58:b3:2c:
+ 6f:7b:c1:5c:89:92:6f:88:fc:4b:c0:02:50:93:2f:5a:41:98:
+ 59:b6:e3:7f:8c:14:63:77:7d:45:3c:88:50:5e:a6:81:52:00:
+ c8:c5:fe:48:ee:1f:5d:ad:de:44:0b:42:58:9c:e1:67:5c:43:
+ b6:a0:85:98:ff:16:d4:1a:28:be:76:e1:2f:e1:84:f4:7e:b9:
+ 27:aa:77:cb:36:b3:fe:c3:fa:d2:17:f6:e1:62:4e:d3:cc:cc:
+ b3:19:65:d3:4b:a8:e8:b3:d5:4c:ea:f6:4e:ae:cb:ae:34:48:
+ 1f:60:cc:58:e7:e7:74:c9:01:35:fd:6a:e0:58:8a:d2:16:eb:
+ ec:e9:3e:bb:f0:1d:cf:b6:ff:1e:0c:b7:bb:39:e9:b7:98:1b:
+ c0:52:21:eb:3a:3d:78:38:8c:a9:19:5f:27:a4:d0:7f:36:61:
+ ab:24:7e:9f:f8:2d:3f:92:29:63:be:cb:10:db:0d:40:36:02:
+ a0:d4:17:a2:8d:7f:7e:7c:99:af:45:5a:40:cd:a2:6b:5c:be:
+ 0e:f3:d3:87:fc:a1:10:ca:aa:33:b7:ba:4b:c0:3d:a4:21:8c:
+ 17:9c:cf:d8:bf:e6:57:fe:cd:eb:fa:30:1a:d5:fe:e8:25:97:
+ a9:be:3b:ea
+-----BEGIN CERTIFICATE-----
+MIIEajCCA1KgAwIBAgIER4af5TANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQGEwJV
+UzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNl
+Y3VyZVRydXN0IENBMB4XDTA4MTIyMjIzNDczOVoXDTI4MTIyMjIzNDczOVowga4x
+CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhJbGxpbm9pczEQMA4GA1UEBxMHQ2hpY2Fn
+bzEhMB8GA1UEChMYVHJ1c3R3YXZlIEhvbGRpbmdzLCBJbmMuMTYwNAYDVQQDEy1U
+cnVzdHdhdmUgT3JnYW5pemF0aW9uIFZhbGlkYXRpb24gQ0EsIExldmVsIDIxHzAd
+BgkqhkiG9w0BCQEWEGNhQHRydXN0d2F2ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDoFO6g2pe9ib0MzI3fCPsGCYOoI1FbAT802h8fbsHjWGXL
+Cvnzh8jI+qHM9XTiuK4tBoSAKG9awSJckpRCzRkCElwQYn6iRPsWXpxysazqBOYV
+qpnoWvhYuYck6HXNJYjiWJJehoN/iiNTrorooyF+g69ACRhJr+HQWrBPb+IxrfTx
+Nx/JKuGL1owSMdQnGt/qa554U+2aGbDORURbG+9kWSH6x7fR0wwey4ja/SM/9Kwr
+oE1h077K3hlhYSTx9py0lr2d6xefJDl46SNQ0wFQd9hSZC8+GU91uRex2o3g0O3b
+NxPcL+BfgGjX9Ie6wR8SeNAIJxd6mKaf0iG6Toe/AgMBAAGjgfQwgfEwDwYDVR0T
+AQH/BAUwAwEB/zAdBgNVHQ4EFgQUXdmWmkDHJ8ssm6LszxmryK/MhkgwHwYDVR0j
+BBgwFoAUQjK2FvoE/f5dS3rD/fdMQB1aQ68wCwYDVR0PBAQDAgEGMDQGA1UdHwQt
+MCswKaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NUQ0EuY3JsMFsG
+A1UdIARUMFIwDAYKKwYBBAGB7RgDADBCBg8rBgEEAYHtGAMDAwMEBAMwLzAtBggr
+BgEFBQcCARYhaHR0cDovL3d3dy5zZWN1cmV0cnVzdC5jb20vbGVnYWwvMA0GCSqG
+SIb3DQEBBQUAA4IBAQBT8cigF+xsiIKxwCSv0QhYsyxve8FciZJviPxLwAJQky9a
+QZhZtuN/jBRjd31FPIhQXqaBUgDIxf5I7h9drd5EC0JYnOFnXEO2oIWY/xbUGii+
+duEv4YT0frknqnfLNrP+w/rSF/bhYk7TzMyzGWXTS6jos9VM6vZOrsuuNEgfYMxY
+5+d0yQE1/WrgWIrSFuvs6T678B3Ptv8eDLe7Oem3mBvAUiHrOj14OIypGV8npNB/
+NmGrJH6f+C0/kiljvssQ2w1ANgKg1BeijX9+fJmvRVpAzaJrXL4O89OH/KEQyqoz
+t7pLwD2kIYwXnM/Yv+ZX/s3r+jAa1f7oJZepvjvq
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert33[] = {
+ 0x30, 0x82, 0x04, 0x6a, 0x30, 0x82, 0x03, 0x52, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x47, 0x86, 0x9f, 0xe5, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x48,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x17,
+ 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0e, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x31, 0x32, 0x32, 0x32, 0x32, 0x33,
+ 0x34, 0x37, 0x33, 0x39, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x31, 0x32, 0x32,
+ 0x32, 0x32, 0x33, 0x34, 0x37, 0x33, 0x39, 0x5a, 0x30, 0x81, 0xae, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x08, 0x49,
+ 0x6c, 0x6c, 0x69, 0x6e, 0x6f, 0x69, 0x73, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x43, 0x68, 0x69, 0x63, 0x61, 0x67,
+ 0x6f, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x77, 0x61, 0x76, 0x65, 0x20, 0x48, 0x6f,
+ 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x36, 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x77, 0x61, 0x76, 0x65, 0x20, 0x4f, 0x72, 0x67,
+ 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56, 0x61,
+ 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x2c,
+ 0x20, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x32, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x10, 0x63, 0x61, 0x40, 0x74, 0x72, 0x75, 0x73, 0x74, 0x77, 0x61, 0x76,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xe8, 0x14, 0xee, 0xa0, 0xda, 0x97, 0xbd, 0x89, 0xbd, 0x0c,
+ 0xcc, 0x8d, 0xdf, 0x08, 0xfb, 0x06, 0x09, 0x83, 0xa8, 0x23, 0x51, 0x5b,
+ 0x01, 0x3f, 0x34, 0xda, 0x1f, 0x1f, 0x6e, 0xc1, 0xe3, 0x58, 0x65, 0xcb,
+ 0x0a, 0xf9, 0xf3, 0x87, 0xc8, 0xc8, 0xfa, 0xa1, 0xcc, 0xf5, 0x74, 0xe2,
+ 0xb8, 0xae, 0x2d, 0x06, 0x84, 0x80, 0x28, 0x6f, 0x5a, 0xc1, 0x22, 0x5c,
+ 0x92, 0x94, 0x42, 0xcd, 0x19, 0x02, 0x12, 0x5c, 0x10, 0x62, 0x7e, 0xa2,
+ 0x44, 0xfb, 0x16, 0x5e, 0x9c, 0x72, 0xb1, 0xac, 0xea, 0x04, 0xe6, 0x15,
+ 0xaa, 0x99, 0xe8, 0x5a, 0xf8, 0x58, 0xb9, 0x87, 0x24, 0xe8, 0x75, 0xcd,
+ 0x25, 0x88, 0xe2, 0x58, 0x92, 0x5e, 0x86, 0x83, 0x7f, 0x8a, 0x23, 0x53,
+ 0xae, 0x8a, 0xe8, 0xa3, 0x21, 0x7e, 0x83, 0xaf, 0x40, 0x09, 0x18, 0x49,
+ 0xaf, 0xe1, 0xd0, 0x5a, 0xb0, 0x4f, 0x6f, 0xe2, 0x31, 0xad, 0xf4, 0xf1,
+ 0x37, 0x1f, 0xc9, 0x2a, 0xe1, 0x8b, 0xd6, 0x8c, 0x12, 0x31, 0xd4, 0x27,
+ 0x1a, 0xdf, 0xea, 0x6b, 0x9e, 0x78, 0x53, 0xed, 0x9a, 0x19, 0xb0, 0xce,
+ 0x45, 0x44, 0x5b, 0x1b, 0xef, 0x64, 0x59, 0x21, 0xfa, 0xc7, 0xb7, 0xd1,
+ 0xd3, 0x0c, 0x1e, 0xcb, 0x88, 0xda, 0xfd, 0x23, 0x3f, 0xf4, 0xac, 0x2b,
+ 0xa0, 0x4d, 0x61, 0xd3, 0xbe, 0xca, 0xde, 0x19, 0x61, 0x61, 0x24, 0xf1,
+ 0xf6, 0x9c, 0xb4, 0x96, 0xbd, 0x9d, 0xeb, 0x17, 0x9f, 0x24, 0x39, 0x78,
+ 0xe9, 0x23, 0x50, 0xd3, 0x01, 0x50, 0x77, 0xd8, 0x52, 0x64, 0x2f, 0x3e,
+ 0x19, 0x4f, 0x75, 0xb9, 0x17, 0xb1, 0xda, 0x8d, 0xe0, 0xd0, 0xed, 0xdb,
+ 0x37, 0x13, 0xdc, 0x2f, 0xe0, 0x5f, 0x80, 0x68, 0xd7, 0xf4, 0x87, 0xba,
+ 0xc1, 0x1f, 0x12, 0x78, 0xd0, 0x08, 0x27, 0x17, 0x7a, 0x98, 0xa6, 0x9f,
+ 0xd2, 0x21, 0xba, 0x4e, 0x87, 0xbf, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x81, 0xf4, 0x30, 0x81, 0xf1, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x5d, 0xd9, 0x96,
+ 0x9a, 0x40, 0xc7, 0x27, 0xcb, 0x2c, 0x9b, 0xa2, 0xec, 0xcf, 0x19, 0xab,
+ 0xc8, 0xaf, 0xcc, 0x86, 0x48, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x42, 0x32, 0xb6, 0x16, 0xfa, 0x04,
+ 0xfd, 0xfe, 0x5d, 0x4b, 0x7a, 0xc3, 0xfd, 0xf7, 0x4c, 0x40, 0x1d, 0x5a,
+ 0x43, 0xaf, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d,
+ 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x53, 0x54, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x5b, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x54, 0x30, 0x52, 0x30, 0x0c, 0x06, 0x0a,
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xed, 0x18, 0x03, 0x00, 0x30, 0x42,
+ 0x06, 0x0f, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0xed, 0x18, 0x03, 0x03,
+ 0x03, 0x03, 0x04, 0x04, 0x03, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x21, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x53, 0xf1, 0xc8, 0xa0, 0x17, 0xec, 0x6c, 0x88, 0x82, 0xb1,
+ 0xc0, 0x24, 0xaf, 0xd1, 0x08, 0x58, 0xb3, 0x2c, 0x6f, 0x7b, 0xc1, 0x5c,
+ 0x89, 0x92, 0x6f, 0x88, 0xfc, 0x4b, 0xc0, 0x02, 0x50, 0x93, 0x2f, 0x5a,
+ 0x41, 0x98, 0x59, 0xb6, 0xe3, 0x7f, 0x8c, 0x14, 0x63, 0x77, 0x7d, 0x45,
+ 0x3c, 0x88, 0x50, 0x5e, 0xa6, 0x81, 0x52, 0x00, 0xc8, 0xc5, 0xfe, 0x48,
+ 0xee, 0x1f, 0x5d, 0xad, 0xde, 0x44, 0x0b, 0x42, 0x58, 0x9c, 0xe1, 0x67,
+ 0x5c, 0x43, 0xb6, 0xa0, 0x85, 0x98, 0xff, 0x16, 0xd4, 0x1a, 0x28, 0xbe,
+ 0x76, 0xe1, 0x2f, 0xe1, 0x84, 0xf4, 0x7e, 0xb9, 0x27, 0xaa, 0x77, 0xcb,
+ 0x36, 0xb3, 0xfe, 0xc3, 0xfa, 0xd2, 0x17, 0xf6, 0xe1, 0x62, 0x4e, 0xd3,
+ 0xcc, 0xcc, 0xb3, 0x19, 0x65, 0xd3, 0x4b, 0xa8, 0xe8, 0xb3, 0xd5, 0x4c,
+ 0xea, 0xf6, 0x4e, 0xae, 0xcb, 0xae, 0x34, 0x48, 0x1f, 0x60, 0xcc, 0x58,
+ 0xe7, 0xe7, 0x74, 0xc9, 0x01, 0x35, 0xfd, 0x6a, 0xe0, 0x58, 0x8a, 0xd2,
+ 0x16, 0xeb, 0xec, 0xe9, 0x3e, 0xbb, 0xf0, 0x1d, 0xcf, 0xb6, 0xff, 0x1e,
+ 0x0c, 0xb7, 0xbb, 0x39, 0xe9, 0xb7, 0x98, 0x1b, 0xc0, 0x52, 0x21, 0xeb,
+ 0x3a, 0x3d, 0x78, 0x38, 0x8c, 0xa9, 0x19, 0x5f, 0x27, 0xa4, 0xd0, 0x7f,
+ 0x36, 0x61, 0xab, 0x24, 0x7e, 0x9f, 0xf8, 0x2d, 0x3f, 0x92, 0x29, 0x63,
+ 0xbe, 0xcb, 0x10, 0xdb, 0x0d, 0x40, 0x36, 0x02, 0xa0, 0xd4, 0x17, 0xa2,
+ 0x8d, 0x7f, 0x7e, 0x7c, 0x99, 0xaf, 0x45, 0x5a, 0x40, 0xcd, 0xa2, 0x6b,
+ 0x5c, 0xbe, 0x0e, 0xf3, 0xd3, 0x87, 0xfc, 0xa1, 0x10, 0xca, 0xaa, 0x33,
+ 0xb7, 0xba, 0x4b, 0xc0, 0x3d, 0xa4, 0x21, 0x8c, 0x17, 0x9c, 0xcf, 0xd8,
+ 0xbf, 0xe6, 0x57, 0xfe, 0xcd, 0xeb, 0xfa, 0x30, 0x1a, 0xd5, 0xfe, 0xe8,
+ 0x25, 0x97, 0xa9, 0xbe, 0x3b, 0xea,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4d:5f:2c:34:08:b2:4c:20:cd:6d:50:7e:24:4d:c9:ec
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=Thawte, Inc., CN=Thawte SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:99:e4:85:5b:76:49:7d:2f:05:d8:c5:ac:c8:c8:
+ a9:d3:dc:98:e6:d7:34:a6:2f:0c:f2:22:26:d8:a3:
+ c9:14:4c:8f:05:a4:45:e8:14:0c:58:90:05:1a:b7:
+ c5:c1:06:a5:80:af:bb:1d:49:6b:52:34:88:c3:59:
+ e7:ef:6b:c4:27:41:8c:2b:66:1d:d0:e0:a3:97:98:
+ 19:34:4b:41:d5:98:d5:c7:05:ad:a2:e4:d7:ed:0c:
+ ad:4f:c1:b5:b0:21:fd:3e:50:53:b2:c4:90:d0:d4:
+ 30:67:6c:9a:f1:0e:74:c4:c2:dc:8a:e8:97:ff:c9:
+ 92:ae:01:8a:56:0a:98:32:b0:00:23:ec:90:1a:60:
+ c3:ed:bb:3a:cb:0f:63:9f:0d:44:c9:52:e1:25:96:
+ bf:ed:50:95:89:7f:56:14:b1:b7:61:1d:1c:07:8c:
+ 3a:2c:f7:ff:80:de:39:45:d5:af:1a:d1:78:d8:c7:
+ 71:6a:a3:19:a7:32:50:21:e9:f2:0e:a1:c6:13:03:
+ 44:48:d1:66:a8:52:57:d7:11:b4:93:8b:e5:99:9f:
+ 5d:e7:78:51:e5:4d:f6:b7:59:b4:76:b5:09:37:4d:
+ 06:38:13:7a:1c:08:98:5c:c4:48:4a:cb:52:a0:a9:
+ f8:b1:9d:8e:7b:79:b0:20:2f:3c:96:a8:11:62:47:
+ bb:11
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-9
+ X509v3 Subject Key Identifier:
+ A7:A2:83:BB:34:45:40:3D:FC:D5:30:4F:12:B9:3E:A1:01:9F:F6:DB
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 80:22:80:e0:6c:c8:95:16:d7:57:26:87:f3:72:34:db:c6:72:
+ 56:27:3e:d3:96:f6:2e:25:91:a5:3e:33:97:a7:4b:e5:2f:fb:
+ 25:7d:2f:07:61:fa:6f:83:74:4c:4c:53:72:20:a4:7a:cf:51:
+ 51:56:81:88:b0:6d:1f:36:2c:c8:2b:b1:88:99:c1:fe:44:ab:
+ 48:51:7c:d8:f2:44:64:2a:d8:71:a7:fb:1a:2f:f9:19:8d:34:
+ b2:23:bf:c4:4c:55:1d:8e:44:e8:aa:5d:9a:dd:9f:fd:03:c7:
+ ba:24:43:8d:2d:47:44:db:f6:d8:98:c8:b2:f9:da:ef:ed:29:
+ 5c:69:12:fa:d1:23:96:0f:bf:9c:0d:f2:79:45:53:37:9a:56:
+ 2f:e8:57:10:70:f6:ee:89:0c:49:89:9a:c1:23:f5:c2:2a:cc:
+ 41:cf:22:ab:65:6e:b7:94:82:6d:2f:40:5f:58:de:eb:95:2b:
+ a6:72:68:52:19:91:2a:ae:75:9d:4e:92:e6:ca:de:54:ea:18:
+ ab:25:3c:e6:64:a6:79:1f:26:7d:61:ed:7d:d2:e5:71:55:d8:
+ 93:17:7c:14:38:30:3c:df:86:e3:4c:ad:49:e3:97:59:ce:1b:
+ 9b:2b:ce:dc:65:d4:0b:28:6b:4e:84:46:51:44:f7:33:08:2d:
+ 58:97:21:ae
+-----BEGIN CERTIFICATE-----
+MIIEbDCCA1SgAwIBAgIQTV8sNAiyTCDNbVB+JE3J7DANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTAwMjA4MDAwMDAwWhcNMjAw
+MjA3MjM1OTU5WjA8MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMu
+MRYwFAYDVQQDEw1UaGF3dGUgU1NMIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAmeSFW3ZJfS8F2MWsyMip09yY5tc0pi8M8iIm2KPJFEyPBaRF6BQM
+WJAFGrfFwQalgK+7HUlrUjSIw1nn72vEJ0GMK2Yd0OCjl5gZNEtB1ZjVxwWtouTX
+7QytT8G1sCH9PlBTssSQ0NQwZ2ya8Q50xMLciuiX/8mSrgGKVgqYMrAAI+yQGmDD
+7bs6yw9jnw1EyVLhJZa/7VCViX9WFLG3YR0cB4w6LPf/gN45RdWvGtF42MdxaqMZ
+pzJQIenyDqHGEwNESNFmqFJX1xG0k4vlmZ9d53hR5U32t1m0drUJN00GOBN6HAiY
+XMRISstSoKn4sZ2Oe3mwIC88lqgRYke7EQIDAQABo4H7MIH4MDIGCCsGAQUFBwEB
+BCYwJDAiBggrBgEFBQcwAYYWaHR0cDovL29jc3AudGhhd3RlLmNvbTASBgNVHRMB
+Af8ECDAGAQH/AgEAMDQGA1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudGhhd3Rl
+LmNvbS9UaGF3dGVQQ0EuY3JsMA4GA1UdDwEB/wQEAwIBBjAoBgNVHREEITAfpB0w
+GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItOTAdBgNVHQ4EFgQUp6KDuzRFQD38
+1TBPErk+oQGf9tswHwYDVR0jBBgwFoAUe1tFz6/Oy3r9MZIaarbzRutXSFAwDQYJ
+KoZIhvcNAQEFBQADggEBAIAigOBsyJUW11cmh/NyNNvGclYnPtOW9i4lkaU+M5en
+S+Uv+yV9Lwdh+m+DdExMU3IgpHrPUVFWgYiwbR82LMgrsYiZwf5Eq0hRfNjyRGQq
+2HGn+xov+RmNNLIjv8RMVR2OROiqXZrdn/0Dx7okQ40tR0Tb9tiYyLL52u/tKVxp
+EvrRI5YPv5wN8nlFUzeaVi/oVxBw9u6JDEmJmsEj9cIqzEHPIqtlbreUgm0vQF9Y
+3uuVK6ZyaFIZkSqudZ1OkubK3lTqGKslPOZkpnkfJn1h7X3S5XFV2JMXfBQ4MDzf
+huNMrUnjl1nOG5srztxl1Asoa06ERlFE9zMILViXIa4=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert34[] = {
+ 0x30, 0x82, 0x04, 0x6c, 0x30, 0x82, 0x03, 0x54, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4d, 0x5f, 0x2c, 0x34, 0x08, 0xb2, 0x4c, 0x20, 0xcd,
+ 0x6d, 0x50, 0x7e, 0x24, 0x4d, 0xc9, 0xec, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30,
+ 0x32, 0x30, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x3c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0d, 0x54,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xe4, 0x85,
+ 0x5b, 0x76, 0x49, 0x7d, 0x2f, 0x05, 0xd8, 0xc5, 0xac, 0xc8, 0xc8, 0xa9,
+ 0xd3, 0xdc, 0x98, 0xe6, 0xd7, 0x34, 0xa6, 0x2f, 0x0c, 0xf2, 0x22, 0x26,
+ 0xd8, 0xa3, 0xc9, 0x14, 0x4c, 0x8f, 0x05, 0xa4, 0x45, 0xe8, 0x14, 0x0c,
+ 0x58, 0x90, 0x05, 0x1a, 0xb7, 0xc5, 0xc1, 0x06, 0xa5, 0x80, 0xaf, 0xbb,
+ 0x1d, 0x49, 0x6b, 0x52, 0x34, 0x88, 0xc3, 0x59, 0xe7, 0xef, 0x6b, 0xc4,
+ 0x27, 0x41, 0x8c, 0x2b, 0x66, 0x1d, 0xd0, 0xe0, 0xa3, 0x97, 0x98, 0x19,
+ 0x34, 0x4b, 0x41, 0xd5, 0x98, 0xd5, 0xc7, 0x05, 0xad, 0xa2, 0xe4, 0xd7,
+ 0xed, 0x0c, 0xad, 0x4f, 0xc1, 0xb5, 0xb0, 0x21, 0xfd, 0x3e, 0x50, 0x53,
+ 0xb2, 0xc4, 0x90, 0xd0, 0xd4, 0x30, 0x67, 0x6c, 0x9a, 0xf1, 0x0e, 0x74,
+ 0xc4, 0xc2, 0xdc, 0x8a, 0xe8, 0x97, 0xff, 0xc9, 0x92, 0xae, 0x01, 0x8a,
+ 0x56, 0x0a, 0x98, 0x32, 0xb0, 0x00, 0x23, 0xec, 0x90, 0x1a, 0x60, 0xc3,
+ 0xed, 0xbb, 0x3a, 0xcb, 0x0f, 0x63, 0x9f, 0x0d, 0x44, 0xc9, 0x52, 0xe1,
+ 0x25, 0x96, 0xbf, 0xed, 0x50, 0x95, 0x89, 0x7f, 0x56, 0x14, 0xb1, 0xb7,
+ 0x61, 0x1d, 0x1c, 0x07, 0x8c, 0x3a, 0x2c, 0xf7, 0xff, 0x80, 0xde, 0x39,
+ 0x45, 0xd5, 0xaf, 0x1a, 0xd1, 0x78, 0xd8, 0xc7, 0x71, 0x6a, 0xa3, 0x19,
+ 0xa7, 0x32, 0x50, 0x21, 0xe9, 0xf2, 0x0e, 0xa1, 0xc6, 0x13, 0x03, 0x44,
+ 0x48, 0xd1, 0x66, 0xa8, 0x52, 0x57, 0xd7, 0x11, 0xb4, 0x93, 0x8b, 0xe5,
+ 0x99, 0x9f, 0x5d, 0xe7, 0x78, 0x51, 0xe5, 0x4d, 0xf6, 0xb7, 0x59, 0xb4,
+ 0x76, 0xb5, 0x09, 0x37, 0x4d, 0x06, 0x38, 0x13, 0x7a, 0x1c, 0x08, 0x98,
+ 0x5c, 0xc4, 0x48, 0x4a, 0xcb, 0x52, 0xa0, 0xa9, 0xf8, 0xb1, 0x9d, 0x8e,
+ 0x7b, 0x79, 0xb0, 0x20, 0x2f, 0x3c, 0x96, 0xa8, 0x11, 0x62, 0x47, 0xbb,
+ 0x11, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfb, 0x30, 0x81, 0xf8,
+ 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30,
+ 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x28,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30,
+ 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x39, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0xa7, 0xa2, 0x83, 0xbb, 0x34, 0x45, 0x40, 0x3d, 0xfc,
+ 0xd5, 0x30, 0x4f, 0x12, 0xb9, 0x3e, 0xa1, 0x01, 0x9f, 0xf6, 0xdb, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a,
+ 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x80, 0x22, 0x80, 0xe0, 0x6c, 0xc8, 0x95, 0x16,
+ 0xd7, 0x57, 0x26, 0x87, 0xf3, 0x72, 0x34, 0xdb, 0xc6, 0x72, 0x56, 0x27,
+ 0x3e, 0xd3, 0x96, 0xf6, 0x2e, 0x25, 0x91, 0xa5, 0x3e, 0x33, 0x97, 0xa7,
+ 0x4b, 0xe5, 0x2f, 0xfb, 0x25, 0x7d, 0x2f, 0x07, 0x61, 0xfa, 0x6f, 0x83,
+ 0x74, 0x4c, 0x4c, 0x53, 0x72, 0x20, 0xa4, 0x7a, 0xcf, 0x51, 0x51, 0x56,
+ 0x81, 0x88, 0xb0, 0x6d, 0x1f, 0x36, 0x2c, 0xc8, 0x2b, 0xb1, 0x88, 0x99,
+ 0xc1, 0xfe, 0x44, 0xab, 0x48, 0x51, 0x7c, 0xd8, 0xf2, 0x44, 0x64, 0x2a,
+ 0xd8, 0x71, 0xa7, 0xfb, 0x1a, 0x2f, 0xf9, 0x19, 0x8d, 0x34, 0xb2, 0x23,
+ 0xbf, 0xc4, 0x4c, 0x55, 0x1d, 0x8e, 0x44, 0xe8, 0xaa, 0x5d, 0x9a, 0xdd,
+ 0x9f, 0xfd, 0x03, 0xc7, 0xba, 0x24, 0x43, 0x8d, 0x2d, 0x47, 0x44, 0xdb,
+ 0xf6, 0xd8, 0x98, 0xc8, 0xb2, 0xf9, 0xda, 0xef, 0xed, 0x29, 0x5c, 0x69,
+ 0x12, 0xfa, 0xd1, 0x23, 0x96, 0x0f, 0xbf, 0x9c, 0x0d, 0xf2, 0x79, 0x45,
+ 0x53, 0x37, 0x9a, 0x56, 0x2f, 0xe8, 0x57, 0x10, 0x70, 0xf6, 0xee, 0x89,
+ 0x0c, 0x49, 0x89, 0x9a, 0xc1, 0x23, 0xf5, 0xc2, 0x2a, 0xcc, 0x41, 0xcf,
+ 0x22, 0xab, 0x65, 0x6e, 0xb7, 0x94, 0x82, 0x6d, 0x2f, 0x40, 0x5f, 0x58,
+ 0xde, 0xeb, 0x95, 0x2b, 0xa6, 0x72, 0x68, 0x52, 0x19, 0x91, 0x2a, 0xae,
+ 0x75, 0x9d, 0x4e, 0x92, 0xe6, 0xca, 0xde, 0x54, 0xea, 0x18, 0xab, 0x25,
+ 0x3c, 0xe6, 0x64, 0xa6, 0x79, 0x1f, 0x26, 0x7d, 0x61, 0xed, 0x7d, 0xd2,
+ 0xe5, 0x71, 0x55, 0xd8, 0x93, 0x17, 0x7c, 0x14, 0x38, 0x30, 0x3c, 0xdf,
+ 0x86, 0xe3, 0x4c, 0xad, 0x49, 0xe3, 0x97, 0x59, 0xce, 0x1b, 0x9b, 0x2b,
+ 0xce, 0xdc, 0x65, 0xd4, 0x0b, 0x28, 0x6b, 0x4e, 0x84, 0x46, 0x51, 0x44,
+ 0xf7, 0x33, 0x08, 0x2d, 0x58, 0x97, 0x21, 0xae,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:47:10
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Dec 15 08:00:00 2006 GMT
+ Not After : Jan 28 12:00:00 2028 GMT
+ Subject: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a6:cf:24:0e:be:2e:6f:28:99:45:42:c4:ab:3e:
+ 21:54:9b:0b:d3:7f:84:70:fa:12:b3:cb:bf:87:5f:
+ c6:7f:86:d3:b2:30:5c:d6:fd:ad:f1:7b:dc:e5:f8:
+ 60:96:09:92:10:f5:d0:53:de:fb:7b:7e:73:88:ac:
+ 52:88:7b:4a:a6:ca:49:a6:5e:a8:a7:8c:5a:11:bc:
+ 7a:82:eb:be:8c:e9:b3:ac:96:25:07:97:4a:99:2a:
+ 07:2f:b4:1e:77:bf:8a:0f:b5:02:7c:1b:96:b8:c5:
+ b9:3a:2c:bc:d6:12:b9:eb:59:7d:e2:d0:06:86:5f:
+ 5e:49:6a:b5:39:5e:88:34:ec:bc:78:0c:08:98:84:
+ 6c:a8:cd:4b:b4:a0:7d:0c:79:4d:f0:b8:2d:cb:21:
+ ca:d5:6c:5b:7d:e1:a0:29:84:a1:f9:d3:94:49:cb:
+ 24:62:91:20:bc:dd:0b:d5:d9:cc:f9:ea:27:0a:2b:
+ 73:91:c6:9d:1b:ac:c8:cb:e8:e0:a0:f4:2f:90:8b:
+ 4d:fb:b0:36:1b:f6:19:7a:85:e0:6d:f2:61:13:88:
+ 5c:9f:e0:93:0a:51:97:8a:5a:ce:af:ab:d5:f7:aa:
+ 09:aa:60:bd:dc:d9:5f:df:72:a9:60:13:5e:00:01:
+ c9:4a:fa:3f:a4:ea:07:03:21:02:8e:82:ca:03:c2:
+ 9b:8f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ 9B:E2:07:57:67:1C:1E:C0:6A:06:DE:59:B4:9A:2D:DF:DC:19:86:2E
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Microsoft Server Gated Crypto
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 3a:0f:cd:26:4d:38:30:08:a8:c6:fc:5c:d8:08:7a:ef:fa:1c:
+ 2a:03:ce:32:ae:44:96:e1:52:03:95:0a:52:d6:67:af:5b:96:
+ 7c:dd:19:8b:30:5b:36:3a:6b:6e:a0:15:c6:82:a1:cb:39:66:
+ 00:57:8b:02:a2:6e:85:fb:ac:55:5a:b8:15:50:1a:90:de:09:
+ 48:ec:a8:f6:57:1c:18:31:bd:c6:7d:c8:bd:eb:c2:a7:39:51:
+ 6d:a2:ff:1c:78:de:1c:27:04:e1:cf:24:95:e8:0e:e4:d5:1f:
+ b0:f9:fb:50:ca:cb:6e:9e:62:26:78:86:f5:c4:f5:78:8f:dd:
+ 72:af:6e:2e:d5:9e:dd:ce:3c:cb:b8:c7:2d:54:60:d7:e5:9c:
+ 02:4b:86:44:f0:57:51:2b:cd:0a:9b:3c:b1:f5:3a:4c:1d:8a:
+ c5:f0:30:3e:65:87:c4:0e:5f:6e:4a:ac:8a:a8:1e:e7:fa:e4:
+ 33:80:15:84:56:65:25:9b:fb:9e:30:88:cb:91:16:c1:05:c3:
+ a9:24:ec:21:d2:d5:b0:fc:b7:23:46:a7:9d:f7:f7:c6:53:12:
+ 78:37:b4:13:73:8f:37:97:5e:04:9b:f9:99:8b:93:3e:26:42:
+ 97:9f:fd:1e:b5:d5:cb:88:48:34:a2:66:a0:fa:ac:72:8f:dd:
+ 47:2f:82:74
+-----BEGIN CERTIFICATE-----
+MIIEdzCCA1+gAwIBAgILBAAAAAABL07hRxAwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0wNjEyMTUwODAw
+MDBaFw0yODAxMjgxMjAwMDBaMEwxIDAeBgNVBAsTF0dsb2JhbFNpZ24gUm9vdCBD
+QSAtIFIyMRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAps8kDr4ubyiZRULEqz4h
+VJsL03+EcPoSs8u/h1/Gf4bTsjBc1v2t8Xvc5fhglgmSEPXQU977e35ziKxSiHtK
+pspJpl6op4xaEbx6guu+jOmzrJYlB5dKmSoHL7Qed7+KD7UCfBuWuMW5Oiy81hK5
+61l94tAGhl9eSWq1OV6INOy8eAwImIRsqM1LtKB9DHlN8LgtyyHK1WxbfeGgKYSh
++dOUScskYpEgvN0L1dnM+eonCitzkcadG6zIy+jgoPQvkItN+7A2G/YZeoXgbfJh
+E4hcn+CTClGXilrOr6vV96oJqmC93Nlf33KpYBNeAAHJSvo/pOoHAyECjoLKA8Kb
+jwIDAQABo4IBTTCCAUkwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
+HQYDVR0OBBYEFJviB1dnHB7AagbeWbSaLd/cGYYuMEcGA1UdIARAMD4wPAYEVR0g
+ADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBv
+c2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmdsb2JhbHNpZ24u
+bmV0L3Jvb3QuY3JsMD0GCCsGAQUFBwEBBDEwLzAtBggrBgEFBQcwAYYhaHR0cDov
+L29jc3AuZ2xvYmFsc2lnbi5jb20vcm9vdHIxMCkGA1UdJQQiMCAGCCsGAQUFBwMB
+BggrBgEFBQcDAgYKKwYBBAGCNwoDAzAfBgNVHSMEGDAWgBRge2YaRQ2XyolQL30E
+zTSo//z9SzANBgkqhkiG9w0BAQUFAAOCAQEAOg/NJk04MAioxvxc2Ah67/ocKgPO
+Mq5EluFSA5UKUtZnr1uWfN0ZizBbNjprbqAVxoKhyzlmAFeLAqJuhfusVVq4FVAa
+kN4JSOyo9lccGDG9xn3IvevCpzlRbaL/HHjeHCcE4c8klegO5NUfsPn7UMrLbp5i
+JniG9cT1eI/dcq9uLtWe3c48y7jHLVRg1+WcAkuGRPBXUSvNCps8sfU6TB2KxfAw
+PmWHxA5fbkqsiqge5/rkM4AVhFZlJZv7njCIy5EWwQXDqSTsIdLVsPy3I0annff3
+xlMSeDe0E3OPN5deBJv5mYuTPiZCl5/9HrXVy4hINKJmoPqsco/dRy+CdA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert35[] = {
+ 0x30, 0x82, 0x04, 0x77, 0x30, 0x82, 0x03, 0x5f, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x47, 0x10, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x36, 0x31, 0x32, 0x31, 0x35, 0x30, 0x38, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x38, 0x30, 0x31, 0x32, 0x38, 0x31,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x4c, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x17, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x52, 0x32, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53,
+ 0x69, 0x67, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa6, 0xcf, 0x24,
+ 0x0e, 0xbe, 0x2e, 0x6f, 0x28, 0x99, 0x45, 0x42, 0xc4, 0xab, 0x3e, 0x21,
+ 0x54, 0x9b, 0x0b, 0xd3, 0x7f, 0x84, 0x70, 0xfa, 0x12, 0xb3, 0xcb, 0xbf,
+ 0x87, 0x5f, 0xc6, 0x7f, 0x86, 0xd3, 0xb2, 0x30, 0x5c, 0xd6, 0xfd, 0xad,
+ 0xf1, 0x7b, 0xdc, 0xe5, 0xf8, 0x60, 0x96, 0x09, 0x92, 0x10, 0xf5, 0xd0,
+ 0x53, 0xde, 0xfb, 0x7b, 0x7e, 0x73, 0x88, 0xac, 0x52, 0x88, 0x7b, 0x4a,
+ 0xa6, 0xca, 0x49, 0xa6, 0x5e, 0xa8, 0xa7, 0x8c, 0x5a, 0x11, 0xbc, 0x7a,
+ 0x82, 0xeb, 0xbe, 0x8c, 0xe9, 0xb3, 0xac, 0x96, 0x25, 0x07, 0x97, 0x4a,
+ 0x99, 0x2a, 0x07, 0x2f, 0xb4, 0x1e, 0x77, 0xbf, 0x8a, 0x0f, 0xb5, 0x02,
+ 0x7c, 0x1b, 0x96, 0xb8, 0xc5, 0xb9, 0x3a, 0x2c, 0xbc, 0xd6, 0x12, 0xb9,
+ 0xeb, 0x59, 0x7d, 0xe2, 0xd0, 0x06, 0x86, 0x5f, 0x5e, 0x49, 0x6a, 0xb5,
+ 0x39, 0x5e, 0x88, 0x34, 0xec, 0xbc, 0x78, 0x0c, 0x08, 0x98, 0x84, 0x6c,
+ 0xa8, 0xcd, 0x4b, 0xb4, 0xa0, 0x7d, 0x0c, 0x79, 0x4d, 0xf0, 0xb8, 0x2d,
+ 0xcb, 0x21, 0xca, 0xd5, 0x6c, 0x5b, 0x7d, 0xe1, 0xa0, 0x29, 0x84, 0xa1,
+ 0xf9, 0xd3, 0x94, 0x49, 0xcb, 0x24, 0x62, 0x91, 0x20, 0xbc, 0xdd, 0x0b,
+ 0xd5, 0xd9, 0xcc, 0xf9, 0xea, 0x27, 0x0a, 0x2b, 0x73, 0x91, 0xc6, 0x9d,
+ 0x1b, 0xac, 0xc8, 0xcb, 0xe8, 0xe0, 0xa0, 0xf4, 0x2f, 0x90, 0x8b, 0x4d,
+ 0xfb, 0xb0, 0x36, 0x1b, 0xf6, 0x19, 0x7a, 0x85, 0xe0, 0x6d, 0xf2, 0x61,
+ 0x13, 0x88, 0x5c, 0x9f, 0xe0, 0x93, 0x0a, 0x51, 0x97, 0x8a, 0x5a, 0xce,
+ 0xaf, 0xab, 0xd5, 0xf7, 0xaa, 0x09, 0xaa, 0x60, 0xbd, 0xdc, 0xd9, 0x5f,
+ 0xdf, 0x72, 0xa9, 0x60, 0x13, 0x5e, 0x00, 0x01, 0xc9, 0x4a, 0xfa, 0x3f,
+ 0xa4, 0xea, 0x07, 0x03, 0x21, 0x02, 0x8e, 0x82, 0xca, 0x03, 0xc2, 0x9b,
+ 0x8f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x4d, 0x30, 0x82,
+ 0x01, 0x49, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x9b, 0xe2,
+ 0x07, 0x57, 0x67, 0x1c, 0x1e, 0xc0, 0x6a, 0x06, 0xde, 0x59, 0xb4, 0x9a,
+ 0x2d, 0xdf, 0xdc, 0x19, 0x86, 0x2e, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24,
+ 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f,
+ 0x74, 0x72, 0x31, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x22,
+ 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x0a,
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60,
+ 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04,
+ 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x3a, 0x0f, 0xcd, 0x26, 0x4d, 0x38, 0x30, 0x08, 0xa8,
+ 0xc6, 0xfc, 0x5c, 0xd8, 0x08, 0x7a, 0xef, 0xfa, 0x1c, 0x2a, 0x03, 0xce,
+ 0x32, 0xae, 0x44, 0x96, 0xe1, 0x52, 0x03, 0x95, 0x0a, 0x52, 0xd6, 0x67,
+ 0xaf, 0x5b, 0x96, 0x7c, 0xdd, 0x19, 0x8b, 0x30, 0x5b, 0x36, 0x3a, 0x6b,
+ 0x6e, 0xa0, 0x15, 0xc6, 0x82, 0xa1, 0xcb, 0x39, 0x66, 0x00, 0x57, 0x8b,
+ 0x02, 0xa2, 0x6e, 0x85, 0xfb, 0xac, 0x55, 0x5a, 0xb8, 0x15, 0x50, 0x1a,
+ 0x90, 0xde, 0x09, 0x48, 0xec, 0xa8, 0xf6, 0x57, 0x1c, 0x18, 0x31, 0xbd,
+ 0xc6, 0x7d, 0xc8, 0xbd, 0xeb, 0xc2, 0xa7, 0x39, 0x51, 0x6d, 0xa2, 0xff,
+ 0x1c, 0x78, 0xde, 0x1c, 0x27, 0x04, 0xe1, 0xcf, 0x24, 0x95, 0xe8, 0x0e,
+ 0xe4, 0xd5, 0x1f, 0xb0, 0xf9, 0xfb, 0x50, 0xca, 0xcb, 0x6e, 0x9e, 0x62,
+ 0x26, 0x78, 0x86, 0xf5, 0xc4, 0xf5, 0x78, 0x8f, 0xdd, 0x72, 0xaf, 0x6e,
+ 0x2e, 0xd5, 0x9e, 0xdd, 0xce, 0x3c, 0xcb, 0xb8, 0xc7, 0x2d, 0x54, 0x60,
+ 0xd7, 0xe5, 0x9c, 0x02, 0x4b, 0x86, 0x44, 0xf0, 0x57, 0x51, 0x2b, 0xcd,
+ 0x0a, 0x9b, 0x3c, 0xb1, 0xf5, 0x3a, 0x4c, 0x1d, 0x8a, 0xc5, 0xf0, 0x30,
+ 0x3e, 0x65, 0x87, 0xc4, 0x0e, 0x5f, 0x6e, 0x4a, 0xac, 0x8a, 0xa8, 0x1e,
+ 0xe7, 0xfa, 0xe4, 0x33, 0x80, 0x15, 0x84, 0x56, 0x65, 0x25, 0x9b, 0xfb,
+ 0x9e, 0x30, 0x88, 0xcb, 0x91, 0x16, 0xc1, 0x05, 0xc3, 0xa9, 0x24, 0xec,
+ 0x21, 0xd2, 0xd5, 0xb0, 0xfc, 0xb7, 0x23, 0x46, 0xa7, 0x9d, 0xf7, 0xf7,
+ 0xc6, 0x53, 0x12, 0x78, 0x37, 0xb4, 0x13, 0x73, 0x8f, 0x37, 0x97, 0x5e,
+ 0x04, 0x9b, 0xf9, 0x99, 0x8b, 0x93, 0x3e, 0x26, 0x42, 0x97, 0x9f, 0xfd,
+ 0x1e, 0xb5, 0xd5, 0xcb, 0x88, 0x48, 0x34, 0xa2, 0x66, 0xa0, 0xfa, 0xac,
+ 0x72, 0x8f, 0xdd, 0x47, 0x2f, 0x82, 0x74,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:3f:11
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Domain Validation CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b1:a3:cd:c0:df:33:40:26:eb:de:5a:d7:94:66:
+ d4:01:63:cc:33:44:89:e0:e2:b8:c2:47:0d:8f:ad:
+ 69:86:1c:a8:73:42:0b:f1:72:fb:2d:ac:b5:11:72:
+ 83:22:f6:56:e7:2e:c5:67:71:9d:00:1c:32:bc:e3:
+ ed:2e:08:45:a9:e6:fa:dd:c8:8c:83:05:c1:6f:4b:
+ d0:26:4a:0b:f6:1b:45:c0:4d:7e:93:bc:0d:27:84:
+ ed:30:a3:e9:c6:26:26:dd:2d:1f:d8:8b:c3:ce:19:
+ d0:5b:fc:08:9f:e4:d8:e2:35:e4:a0:68:a6:f6:0d:
+ a3:74:60:42:b2:97:82:24:8e:41:a4:f2:2e:5e:b6:
+ 8e:a7:6e:d9:6c:7f:0d:3b:24:35:6a:d0:ab:5b:6a:
+ f7:97:02:00:3f:51:a6:a7:6e:73:ca:77:0d:76:7c:
+ 9b:b6:30:1a:1a:9c:f7:1f:28:7b:0e:8b:47:1f:e7:
+ 7f:05:8c:c6:c9:c8:bb:cf:e9:dc:7a:41:2e:a1:86:
+ da:d4:39:b2:e2:13:40:a6:a8:3a:fa:0f:53:1e:4f:
+ ec:6e:98:09:1b:ca:9a:77:b3:55:85:85:e9:2e:16:
+ b5:9d:5e:54:f1:4a:7a:6c:39:ba:6e:17:06:34:b3:
+ b2:42:e1:f7:f3:9c:9a:0b:11:44:de:6a:78:8e:b1:
+ 13:4f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 96:AD:FA:B0:5B:B9:83:64:2A:76:C2:1C:8A:69:DA:42:DC:FE:FD:28
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Microsoft Server Gated Crypto
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 7e:9a:13:39:71:69:a0:fc:8c:35:ac:af:b4:d6:de:64:ea:33:
+ 6f:95:53:92:71:ad:4c:c0:fb:d0:6b:ba:80:0e:c2:0a:e6:37:
+ fa:d2:25:a3:22:f7:89:9f:52:12:43:2f:bb:c4:fc:6c:ce:e4:
+ aa:9d:f6:9d:57:7b:cc:2a:ac:75:49:1b:54:66:cf:a7:e9:b9:
+ b0:c2:7c:70:23:fb:9c:97:00:f2:25:a4:d9:a1:0a:5d:85:06:
+ 1d:1a:87:f5:2d:54:c5:64:21:8e:ac:aa:ec:19:3e:9b:ff:c0:
+ 67:a7:2e:00:e3:f1:81:40:00:5b:83:e2:a8:a7:ef:35:50:83:
+ c0:f4:9b:88:2a:89:a9:a9:9c:2f:82:b9:18:9e:fa:eb:47:24:
+ 6e:13:ee:b2:8c:f0:42:37:5e:e6:8f:91:bc:a5:5f:51:2b:ae:
+ bb:8c:76:31:4e:53:11:79:ec:11:4e:38:73:e5:1a:66:70:f4:
+ 82:f7:7b:10:55:f8:bb:a5:c3:1d:e5:d3:f6:bc:fa:28:b6:31:
+ 10:d5:fe:91:23:a4:21:3f:ba:4c:91:8f:87:c7:82:ab:38:c2:
+ 01:73:89:48:1a:f9:0c:91:b9:95:fb:6d:21:5f:03:c8:bf:7b:
+ 74:ef:7b:71:79:b5:3e:73:23:d1:5a:dc:a6:0c:e1:2d:64:65:
+ 91:be:c2:b9
+-----BEGIN CERTIFICATE-----
+MIIEhTCCA22gAwIBAgILBAAAAAABL07hPxEwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
+MDBaFw0yMjA0MTMxMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0
+aW9uIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxo83A
+3zNAJuveWteUZtQBY8wzRIng4rjCRw2PrWmGHKhzQgvxcvstrLURcoMi9lbnLsVn
+cZ0AHDK84+0uCEWp5vrdyIyDBcFvS9AmSgv2G0XATX6TvA0nhO0wo+nGJibdLR/Y
+i8POGdBb/Aif5NjiNeSgaKb2DaN0YEKyl4IkjkGk8i5eto6nbtlsfw07JDVq0Ktb
+aveXAgA/UaanbnPKdw12fJu2MBoanPcfKHsOi0cf538FjMbJyLvP6dx6QS6hhtrU
+ObLiE0CmqDr6D1MeT+xumAkbypp3s1WFhekuFrWdXlTxSnpsObpuFwY0s7JC4ffz
+nJoLEUTeaniOsRNPAgMBAAGjggFQMIIBTDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
+AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlq36sFu5g2QqdsIcimnaQtz+/SgwRwYD
+VR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2Jh
+bHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9j
+cmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEEMTAvMC0GCCsG
+AQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwKQYDVR0l
+BCIwIAYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDMB8GA1UdIwQYMBaA
+FGB7ZhpFDZfKiVAvfQTNNKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQB+mhM5cWmg
+/Iw1rK+01t5k6jNvlVOSca1MwPvQa7qADsIK5jf60iWjIveJn1ISQy+7xPxszuSq
+nfadV3vMKqx1SRtUZs+n6bmwwnxwI/uclwDyJaTZoQpdhQYdGof1LVTFZCGOrKrs
+GT6b/8Bnpy4A4/GBQABbg+Kop+81UIPA9JuIKompqZwvgrkYnvrrRyRuE+6yjPBC
+N17mj5G8pV9RK667jHYxTlMReewRTjhz5RpmcPSC93sQVfi7pcMd5dP2vPootjEQ
+1f6RI6QhP7pMkY+Hx4KrOMIBc4lIGvkMkbmV+20hXwPIv3t073txebU+cyPRWtym
+DOEtZGWRvsK5
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert36[] = {
+ 0x30, 0x82, 0x04, 0x85, 0x30, 0x82, 0x03, 0x6d, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x3f, 0x11, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x44, 0x6f,
+ 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0xa3, 0xcd, 0xc0,
+ 0xdf, 0x33, 0x40, 0x26, 0xeb, 0xde, 0x5a, 0xd7, 0x94, 0x66, 0xd4, 0x01,
+ 0x63, 0xcc, 0x33, 0x44, 0x89, 0xe0, 0xe2, 0xb8, 0xc2, 0x47, 0x0d, 0x8f,
+ 0xad, 0x69, 0x86, 0x1c, 0xa8, 0x73, 0x42, 0x0b, 0xf1, 0x72, 0xfb, 0x2d,
+ 0xac, 0xb5, 0x11, 0x72, 0x83, 0x22, 0xf6, 0x56, 0xe7, 0x2e, 0xc5, 0x67,
+ 0x71, 0x9d, 0x00, 0x1c, 0x32, 0xbc, 0xe3, 0xed, 0x2e, 0x08, 0x45, 0xa9,
+ 0xe6, 0xfa, 0xdd, 0xc8, 0x8c, 0x83, 0x05, 0xc1, 0x6f, 0x4b, 0xd0, 0x26,
+ 0x4a, 0x0b, 0xf6, 0x1b, 0x45, 0xc0, 0x4d, 0x7e, 0x93, 0xbc, 0x0d, 0x27,
+ 0x84, 0xed, 0x30, 0xa3, 0xe9, 0xc6, 0x26, 0x26, 0xdd, 0x2d, 0x1f, 0xd8,
+ 0x8b, 0xc3, 0xce, 0x19, 0xd0, 0x5b, 0xfc, 0x08, 0x9f, 0xe4, 0xd8, 0xe2,
+ 0x35, 0xe4, 0xa0, 0x68, 0xa6, 0xf6, 0x0d, 0xa3, 0x74, 0x60, 0x42, 0xb2,
+ 0x97, 0x82, 0x24, 0x8e, 0x41, 0xa4, 0xf2, 0x2e, 0x5e, 0xb6, 0x8e, 0xa7,
+ 0x6e, 0xd9, 0x6c, 0x7f, 0x0d, 0x3b, 0x24, 0x35, 0x6a, 0xd0, 0xab, 0x5b,
+ 0x6a, 0xf7, 0x97, 0x02, 0x00, 0x3f, 0x51, 0xa6, 0xa7, 0x6e, 0x73, 0xca,
+ 0x77, 0x0d, 0x76, 0x7c, 0x9b, 0xb6, 0x30, 0x1a, 0x1a, 0x9c, 0xf7, 0x1f,
+ 0x28, 0x7b, 0x0e, 0x8b, 0x47, 0x1f, 0xe7, 0x7f, 0x05, 0x8c, 0xc6, 0xc9,
+ 0xc8, 0xbb, 0xcf, 0xe9, 0xdc, 0x7a, 0x41, 0x2e, 0xa1, 0x86, 0xda, 0xd4,
+ 0x39, 0xb2, 0xe2, 0x13, 0x40, 0xa6, 0xa8, 0x3a, 0xfa, 0x0f, 0x53, 0x1e,
+ 0x4f, 0xec, 0x6e, 0x98, 0x09, 0x1b, 0xca, 0x9a, 0x77, 0xb3, 0x55, 0x85,
+ 0x85, 0xe9, 0x2e, 0x16, 0xb5, 0x9d, 0x5e, 0x54, 0xf1, 0x4a, 0x7a, 0x6c,
+ 0x39, 0xba, 0x6e, 0x17, 0x06, 0x34, 0xb3, 0xb2, 0x42, 0xe1, 0xf7, 0xf3,
+ 0x9c, 0x9a, 0x0b, 0x11, 0x44, 0xde, 0x6a, 0x78, 0x8e, 0xb1, 0x13, 0x4f,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x50, 0x30, 0x82, 0x01,
+ 0x4c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x96, 0xad, 0xfa, 0xb0, 0x5b, 0xb9, 0x83, 0x64, 0x2a, 0x76, 0xc2, 0x1c,
+ 0x8a, 0x69, 0xda, 0x42, 0xdc, 0xfe, 0xfd, 0x28, 0x30, 0x47, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26,
+ 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x25,
+ 0x04, 0x22, 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45, 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f,
+ 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff, 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x7e, 0x9a, 0x13, 0x39, 0x71, 0x69, 0xa0,
+ 0xfc, 0x8c, 0x35, 0xac, 0xaf, 0xb4, 0xd6, 0xde, 0x64, 0xea, 0x33, 0x6f,
+ 0x95, 0x53, 0x92, 0x71, 0xad, 0x4c, 0xc0, 0xfb, 0xd0, 0x6b, 0xba, 0x80,
+ 0x0e, 0xc2, 0x0a, 0xe6, 0x37, 0xfa, 0xd2, 0x25, 0xa3, 0x22, 0xf7, 0x89,
+ 0x9f, 0x52, 0x12, 0x43, 0x2f, 0xbb, 0xc4, 0xfc, 0x6c, 0xce, 0xe4, 0xaa,
+ 0x9d, 0xf6, 0x9d, 0x57, 0x7b, 0xcc, 0x2a, 0xac, 0x75, 0x49, 0x1b, 0x54,
+ 0x66, 0xcf, 0xa7, 0xe9, 0xb9, 0xb0, 0xc2, 0x7c, 0x70, 0x23, 0xfb, 0x9c,
+ 0x97, 0x00, 0xf2, 0x25, 0xa4, 0xd9, 0xa1, 0x0a, 0x5d, 0x85, 0x06, 0x1d,
+ 0x1a, 0x87, 0xf5, 0x2d, 0x54, 0xc5, 0x64, 0x21, 0x8e, 0xac, 0xaa, 0xec,
+ 0x19, 0x3e, 0x9b, 0xff, 0xc0, 0x67, 0xa7, 0x2e, 0x00, 0xe3, 0xf1, 0x81,
+ 0x40, 0x00, 0x5b, 0x83, 0xe2, 0xa8, 0xa7, 0xef, 0x35, 0x50, 0x83, 0xc0,
+ 0xf4, 0x9b, 0x88, 0x2a, 0x89, 0xa9, 0xa9, 0x9c, 0x2f, 0x82, 0xb9, 0x18,
+ 0x9e, 0xfa, 0xeb, 0x47, 0x24, 0x6e, 0x13, 0xee, 0xb2, 0x8c, 0xf0, 0x42,
+ 0x37, 0x5e, 0xe6, 0x8f, 0x91, 0xbc, 0xa5, 0x5f, 0x51, 0x2b, 0xae, 0xbb,
+ 0x8c, 0x76, 0x31, 0x4e, 0x53, 0x11, 0x79, 0xec, 0x11, 0x4e, 0x38, 0x73,
+ 0xe5, 0x1a, 0x66, 0x70, 0xf4, 0x82, 0xf7, 0x7b, 0x10, 0x55, 0xf8, 0xbb,
+ 0xa5, 0xc3, 0x1d, 0xe5, 0xd3, 0xf6, 0xbc, 0xfa, 0x28, 0xb6, 0x31, 0x10,
+ 0xd5, 0xfe, 0x91, 0x23, 0xa4, 0x21, 0x3f, 0xba, 0x4c, 0x91, 0x8f, 0x87,
+ 0xc7, 0x82, 0xab, 0x38, 0xc2, 0x01, 0x73, 0x89, 0x48, 0x1a, 0xf9, 0x0c,
+ 0x91, 0xb9, 0x95, 0xfb, 0x6d, 0x21, 0x5f, 0x03, 0xc8, 0xbf, 0x7b, 0x74,
+ 0xef, 0x7b, 0x71, 0x79, 0xb5, 0x3e, 0x73, 0x23, 0xd1, 0x5a, 0xdc, 0xa6,
+ 0x0c, 0xe1, 0x2d, 0x64, 0x65, 0x91, 0xbe, 0xc2, 0xb9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:5d:d4
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: OU=GlobalSign Root CA - R2, O=GlobalSign, CN=GlobalSign
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Extended Validation CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cd:a1:46:cc:52:9a:b8:a5:b4:7f:58:d9:cd:d8:
+ 4b:a0:72:0c:97:5b:a6:88:20:c3:b9:3d:46:dc:d0:
+ 5c:52:30:f6:fa:59:4f:85:5f:b0:db:88:b4:a9:5f:
+ 2b:23:48:ac:ab:f5:92:78:14:b6:32:0f:fb:5c:6a:
+ 85:5b:00:90:e0:bb:65:f5:5a:f9:4f:67:7e:c7:6c:
+ 29:ec:93:c0:2b:ca:c4:5e:d8:b0:db:d6:be:3f:9b:
+ 0b:c0:8f:a9:5d:ae:f7:00:02:a4:fc:ba:66:11:38:
+ 77:fe:23:20:25:55:10:c5:bd:82:b9:4c:b1:68:c6:
+ e2:70:7b:83:5c:13:67:c1:a1:f3:7c:0b:a8:99:9a:
+ d0:e2:9b:25:31:c8:2b:8d:40:f6:52:63:b1:a0:ad:
+ 5a:2e:f5:79:36:6d:35:2c:0e:dd:05:e4:d0:e2:07:
+ 48:b7:28:5e:2b:d5:58:d5:6c:d0:0c:a1:01:46:01:
+ 5a:8f:c6:af:64:c7:55:01:5d:e1:d1:c6:6c:50:25:
+ a0:05:ad:00:ab:0c:8d:65:6b:dd:eb:c2:72:54:c9:
+ 0f:3c:00:17:87:22:ef:db:b9:86:78:16:51:ae:77:
+ d9:a6:28:4d:f3:58:8d:83:67:b9:34:25:9b:1c:51:
+ 80:51:f3:83:92:6a:a3:ae:47:9a:d6:e4:8b:1b:c0:
+ ed:b1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ B0:B0:4A:FD:1C:75:28:F8:1C:61:AA:13:F6:FA:C1:90:3D:6B:16:A3
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root-r2.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/ExtendedSSLCA
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Microsoft Server Gated Crypto
+ X509v3 Authority Key Identifier:
+ keyid:9B:E2:07:57:67:1C:1E:C0:6A:06:DE:59:B4:9A:2D:DF:DC:19:86:2E
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 2f:49:b6:f2:b6:5a:a4:95:ab:9e:5a:e9:2b:82:9b:cc:90:6b:
+ 7c:74:45:20:e7:5e:d8:c7:23:ee:28:35:b1:35:65:2a:a5:51:
+ e0:55:3f:f6:83:67:b4:e4:36:29:b0:da:ec:97:95:a9:8a:05:
+ a3:45:fe:23:2e:52:88:b4:1f:10:80:ad:d2:8b:9f:a3:5f:a8:
+ c5:eb:73:de:79:88:41:98:33:ee:a7:60:18:b1:46:c9:40:10:
+ 07:9c:8f:0a:52:c9:13:1a:06:6e:a0:9b:2d:3a:f6:ae:4f:e7:
+ a3:51:35:2a:5b:18:05:12:e5:51:dc:b6:36:62:f3:e1:a4:0f:
+ fb:e4:cf:c3:94:bf:11:ab:a1:59:31:01:f0:cc:53:ec:8f:63:
+ d7:6c:96:d3:48:2a:8a:23:ed:45:56:a8:66:41:ea:01:b9:47:
+ ee:a1:47:0c:14:f1:23:e1:20:73:ca:7d:50:7c:64:38:57:a3:
+ 8f:4a:9c:9b:e9:6d:45:cf:44:6b:4d:60:20:40:71:25:b5:46:
+ aa:6c:08:7e:df:c8:fa:c8:56:2a:92:cb:83:b8:79:09:97:2d:
+ 5e:a1:01:ce:06:ed:b4:97:c9:04:dc:41:ef:e0:4f:36:4d:e4:
+ 40:73:46:ec:11:12:7c:88:5b:34:26:25:4d:ea:dc:18:be:c5:
+ 1b:cd:64:c0
+-----BEGIN CERTIFICATE-----
+MIIEhjCCA26gAwIBAgILBAAAAAABL07hXdQwDQYJKoZIhvcNAQEFBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTEwNDEzMTAwMDAwWhcNMjIwNDEz
+MTAwMDAwWjBZMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1z
+YTEvMC0GA1UEAxMmR2xvYmFsU2lnbiBFeHRlbmRlZCBWYWxpZGF0aW9uIENBIC0g
+RzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNoUbMUpq4pbR/WNnN
+2EugcgyXW6aIIMO5PUbc0FxSMPb6WU+FX7DbiLSpXysjSKyr9ZJ4FLYyD/tcaoVb
+AJDgu2X1WvlPZ37HbCnsk8ArysRe2LDb1r4/mwvAj6ldrvcAAqT8umYROHf+IyAl
+VRDFvYK5TLFoxuJwe4NcE2fBofN8C6iZmtDimyUxyCuNQPZSY7GgrVou9Xk2bTUs
+Dt0F5NDiB0i3KF4r1VjVbNAMoQFGAVqPxq9kx1UBXeHRxmxQJaAFrQCrDI1la93r
+wnJUyQ88ABeHIu/buYZ4FlGud9mmKE3zWI2DZ7k0JZscUYBR84OSaqOuR5rW5Isb
+wO2xAgMBAAGjggFaMIIBVjAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
+/wIBADAdBgNVHQ4EFgQUsLBK/Rx1KPgcYaoT9vrBkD1rFqMwRwYDVR0gBEAwPjA8
+BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29t
+L3JlcG9zaXRvcnkvMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFs
+c2lnbi5uZXQvcm9vdC1yMi5jcmwwRAYIKwYBBQUHAQEEODA2MDQGCCsGAQUFBzAB
+hihodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9FeHRlbmRlZFNTTENBMCkGA1Ud
+JQQiMCAGCCsGAQUFBwMBBggrBgEFBQcDAgYKKwYBBAGCNwoDAzAfBgNVHSMEGDAW
+gBSb4gdXZxwewGoG3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAL0m28rZa
+pJWrnlrpK4KbzJBrfHRFIOde2Mcj7ig1sTVlKqVR4FU/9oNntOQ2KbDa7JeVqYoF
+o0X+Iy5SiLQfEICt0oufo1+oxetz3nmIQZgz7qdgGLFGyUAQB5yPClLJExoGbqCb
+LTr2rk/no1E1KlsYBRLlUdy2NmLz4aQP++TPw5S/EauhWTEB8MxT7I9j12yW00gq
+iiPtRVaoZkHqAblH7qFHDBTxI+Egc8p9UHxkOFejj0qcm+ltRc9Ea01gIEBxJbVG
+qmwIft/I+shWKpLLg7h5CZctXqEBzgbttJfJBNxB7+BPNk3kQHNG7BESfIhbNCYl
+TercGL7FG81kwA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert37[] = {
+ 0x30, 0x82, 0x04, 0x86, 0x30, 0x82, 0x03, 0x6e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x5d, 0xd4, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4c, 0x31, 0x20, 0x30, 0x1e, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x17, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x52, 0x32, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x0a, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x59, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73,
+ 0x61, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26,
+ 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xcd,
+ 0xa1, 0x46, 0xcc, 0x52, 0x9a, 0xb8, 0xa5, 0xb4, 0x7f, 0x58, 0xd9, 0xcd,
+ 0xd8, 0x4b, 0xa0, 0x72, 0x0c, 0x97, 0x5b, 0xa6, 0x88, 0x20, 0xc3, 0xb9,
+ 0x3d, 0x46, 0xdc, 0xd0, 0x5c, 0x52, 0x30, 0xf6, 0xfa, 0x59, 0x4f, 0x85,
+ 0x5f, 0xb0, 0xdb, 0x88, 0xb4, 0xa9, 0x5f, 0x2b, 0x23, 0x48, 0xac, 0xab,
+ 0xf5, 0x92, 0x78, 0x14, 0xb6, 0x32, 0x0f, 0xfb, 0x5c, 0x6a, 0x85, 0x5b,
+ 0x00, 0x90, 0xe0, 0xbb, 0x65, 0xf5, 0x5a, 0xf9, 0x4f, 0x67, 0x7e, 0xc7,
+ 0x6c, 0x29, 0xec, 0x93, 0xc0, 0x2b, 0xca, 0xc4, 0x5e, 0xd8, 0xb0, 0xdb,
+ 0xd6, 0xbe, 0x3f, 0x9b, 0x0b, 0xc0, 0x8f, 0xa9, 0x5d, 0xae, 0xf7, 0x00,
+ 0x02, 0xa4, 0xfc, 0xba, 0x66, 0x11, 0x38, 0x77, 0xfe, 0x23, 0x20, 0x25,
+ 0x55, 0x10, 0xc5, 0xbd, 0x82, 0xb9, 0x4c, 0xb1, 0x68, 0xc6, 0xe2, 0x70,
+ 0x7b, 0x83, 0x5c, 0x13, 0x67, 0xc1, 0xa1, 0xf3, 0x7c, 0x0b, 0xa8, 0x99,
+ 0x9a, 0xd0, 0xe2, 0x9b, 0x25, 0x31, 0xc8, 0x2b, 0x8d, 0x40, 0xf6, 0x52,
+ 0x63, 0xb1, 0xa0, 0xad, 0x5a, 0x2e, 0xf5, 0x79, 0x36, 0x6d, 0x35, 0x2c,
+ 0x0e, 0xdd, 0x05, 0xe4, 0xd0, 0xe2, 0x07, 0x48, 0xb7, 0x28, 0x5e, 0x2b,
+ 0xd5, 0x58, 0xd5, 0x6c, 0xd0, 0x0c, 0xa1, 0x01, 0x46, 0x01, 0x5a, 0x8f,
+ 0xc6, 0xaf, 0x64, 0xc7, 0x55, 0x01, 0x5d, 0xe1, 0xd1, 0xc6, 0x6c, 0x50,
+ 0x25, 0xa0, 0x05, 0xad, 0x00, 0xab, 0x0c, 0x8d, 0x65, 0x6b, 0xdd, 0xeb,
+ 0xc2, 0x72, 0x54, 0xc9, 0x0f, 0x3c, 0x00, 0x17, 0x87, 0x22, 0xef, 0xdb,
+ 0xb9, 0x86, 0x78, 0x16, 0x51, 0xae, 0x77, 0xd9, 0xa6, 0x28, 0x4d, 0xf3,
+ 0x58, 0x8d, 0x83, 0x67, 0xb9, 0x34, 0x25, 0x9b, 0x1c, 0x51, 0x80, 0x51,
+ 0xf3, 0x83, 0x92, 0x6a, 0xa3, 0xae, 0x47, 0x9a, 0xd6, 0xe4, 0x8b, 0x1b,
+ 0xc0, 0xed, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5a,
+ 0x30, 0x82, 0x01, 0x56, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0xb0, 0xb0, 0x4a, 0xfd, 0x1c, 0x75, 0x28, 0xf8, 0x1c,
+ 0x61, 0xaa, 0x13, 0xf6, 0xfa, 0xc1, 0x90, 0x3d, 0x6b, 0x16, 0xa3, 0x30,
+ 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30, 0x3e, 0x30, 0x3c,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30, 0x32, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x26, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f,
+ 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2f, 0x30, 0x2d, 0x30,
+ 0x2b, 0xa0, 0x29, 0xa0, 0x27, 0x86, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2d, 0x72, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x44, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x38, 0x30, 0x36,
+ 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x28, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65,
+ 0x64, 0x53, 0x53, 0x4c, 0x43, 0x41, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d,
+ 0x25, 0x04, 0x22, 0x30, 0x20, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03,
+ 0x02, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03,
+ 0x03, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x9b, 0xe2, 0x07, 0x57, 0x67, 0x1c, 0x1e, 0xc0, 0x6a, 0x06,
+ 0xde, 0x59, 0xb4, 0x9a, 0x2d, 0xdf, 0xdc, 0x19, 0x86, 0x2e, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x2f, 0x49, 0xb6, 0xf2, 0xb6, 0x5a,
+ 0xa4, 0x95, 0xab, 0x9e, 0x5a, 0xe9, 0x2b, 0x82, 0x9b, 0xcc, 0x90, 0x6b,
+ 0x7c, 0x74, 0x45, 0x20, 0xe7, 0x5e, 0xd8, 0xc7, 0x23, 0xee, 0x28, 0x35,
+ 0xb1, 0x35, 0x65, 0x2a, 0xa5, 0x51, 0xe0, 0x55, 0x3f, 0xf6, 0x83, 0x67,
+ 0xb4, 0xe4, 0x36, 0x29, 0xb0, 0xda, 0xec, 0x97, 0x95, 0xa9, 0x8a, 0x05,
+ 0xa3, 0x45, 0xfe, 0x23, 0x2e, 0x52, 0x88, 0xb4, 0x1f, 0x10, 0x80, 0xad,
+ 0xd2, 0x8b, 0x9f, 0xa3, 0x5f, 0xa8, 0xc5, 0xeb, 0x73, 0xde, 0x79, 0x88,
+ 0x41, 0x98, 0x33, 0xee, 0xa7, 0x60, 0x18, 0xb1, 0x46, 0xc9, 0x40, 0x10,
+ 0x07, 0x9c, 0x8f, 0x0a, 0x52, 0xc9, 0x13, 0x1a, 0x06, 0x6e, 0xa0, 0x9b,
+ 0x2d, 0x3a, 0xf6, 0xae, 0x4f, 0xe7, 0xa3, 0x51, 0x35, 0x2a, 0x5b, 0x18,
+ 0x05, 0x12, 0xe5, 0x51, 0xdc, 0xb6, 0x36, 0x62, 0xf3, 0xe1, 0xa4, 0x0f,
+ 0xfb, 0xe4, 0xcf, 0xc3, 0x94, 0xbf, 0x11, 0xab, 0xa1, 0x59, 0x31, 0x01,
+ 0xf0, 0xcc, 0x53, 0xec, 0x8f, 0x63, 0xd7, 0x6c, 0x96, 0xd3, 0x48, 0x2a,
+ 0x8a, 0x23, 0xed, 0x45, 0x56, 0xa8, 0x66, 0x41, 0xea, 0x01, 0xb9, 0x47,
+ 0xee, 0xa1, 0x47, 0x0c, 0x14, 0xf1, 0x23, 0xe1, 0x20, 0x73, 0xca, 0x7d,
+ 0x50, 0x7c, 0x64, 0x38, 0x57, 0xa3, 0x8f, 0x4a, 0x9c, 0x9b, 0xe9, 0x6d,
+ 0x45, 0xcf, 0x44, 0x6b, 0x4d, 0x60, 0x20, 0x40, 0x71, 0x25, 0xb5, 0x46,
+ 0xaa, 0x6c, 0x08, 0x7e, 0xdf, 0xc8, 0xfa, 0xc8, 0x56, 0x2a, 0x92, 0xcb,
+ 0x83, 0xb8, 0x79, 0x09, 0x97, 0x2d, 0x5e, 0xa1, 0x01, 0xce, 0x06, 0xed,
+ 0xb4, 0x97, 0xc9, 0x04, 0xdc, 0x41, 0xef, 0xe0, 0x4f, 0x36, 0x4d, 0xe4,
+ 0x40, 0x73, 0x46, 0xec, 0x11, 0x12, 0x7c, 0x88, 0x5b, 0x34, 0x26, 0x25,
+ 0x4d, 0xea, 0xdc, 0x18, 0xbe, 0xc5, 0x1b, 0xcd, 0x64, 0xc0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 52:42:06:4a:4f:37:fe:43:69:48:7a:96:67:ff:5d:27
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Jun 7 08:09:10 2005 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b1:f7:c3:38:3f:b4:a8:7f:cf:39:82:51:67:d0:
+ 6d:9f:d2:ff:58:f3:e7:9f:2b:ec:0d:89:54:99:b9:
+ 38:99:16:f7:e0:21:79:48:c2:bb:61:74:12:96:1d:
+ 3c:6a:72:d5:3c:10:67:3a:39:ed:2b:13:cd:66:eb:
+ 95:09:33:a4:6c:97:b1:e8:c6:ec:c1:75:79:9c:46:
+ 5e:8d:ab:d0:6a:fd:b9:2a:55:17:10:54:b3:19:f0:
+ 9a:f6:f1:b1:5d:b6:a7:6d:fb:e0:71:17:6b:a2:88:
+ fb:00:df:fe:1a:31:77:0c:9a:01:7a:b1:32:e3:2b:
+ 01:07:38:6e:c3:a5:5e:23:bc:45:9b:7b:50:c1:c9:
+ 30:8f:db:e5:2b:7a:d3:5b:fb:33:40:1e:a0:d5:98:
+ 17:bc:8b:87:c3:89:d3:5d:a0:8e:b2:aa:aa:f6:8e:
+ 69:88:06:c5:fa:89:21:f3:08:9d:69:2e:09:33:9b:
+ 29:0d:46:0f:8c:cc:49:34:b0:69:51:bd:f9:06:cd:
+ 68:ad:66:4c:bc:3e:ac:61:bd:0a:88:0e:c8:df:3d:
+ ee:7c:04:4c:9d:0a:5e:6b:91:d6:ee:c7:ed:28:8d:
+ ab:4d:87:89:73:d0:6e:a4:d0:1e:16:8b:14:e1:76:
+ 44:03:7f:63:ac:e4:cd:49:9c:c5:92:f4:ab:32:a1:
+ 48:5b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/AddTrustExternalCARoot.crl
+
+ Full Name:
+ URI:http://crl.comodo.net/AddTrustExternalCARoot.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 60:64:39:59:a2:43:65:2e:fd:f9:1f:d6:ae:33:bb:e8:53:13:
+ c4:88:ee:23:1a:6c:ce:d8:64:59:53:53:90:e8:36:df:d4:fc:
+ f3:4e:79:2f:d5:e6:8f:0c:ef:2a:41:6d:71:bd:9b:78:38:23:
+ d3:70:4b:86:0c:fd:12:a7:22:62:12:d8:cc:e0:51:ef:2d:e5:
+ cd:0c:45:a2:ea:da:ed:7e:ec:f7:32:9a:e7:05:35:5e:6e:c2:
+ 2c:68:68:9d:ff:8c:f1:ca:55:87:c4:2f:b1:40:06:dc:84:22:
+ 5c:6d:b3:cd:d1:9b:1a:0a:33:28:66:16:0c:bd:33:c2:f6:07:
+ f1:e3:a1:79:94:e0:f8:d0:d0:d3:df:52:86:3f:a9:e1:c9:1d:
+ 3e:86:84:b1:db:5f:ee:e4:49:43:c1:39:7d:cf:2f:96:a7:75:
+ 5d:7e:67:67:84:e5:59:20:40:bf:37:22:bf:07:43:b4:30:e1:
+ 43:8a:cd:03:5d:6d:b9:29:d9:84:a7:f5:62:63:84:86:d6:37:
+ be:6f:67:bb:ff:62:57:39:9d:0c:4d:b2:2a:61:3d:1d:9c:ef:
+ 9a:77:20:a0:2f:ee:1a:72:9d:b0:9d:bf:78:13:27:07:0a:60:
+ 11:93:f5:0f:2e:c9:ef:6b:24:83:fe:9b:90:b4:4b:68:81:d0:
+ c2:fa:e0:3f
+-----BEGIN CERTIFICATE-----
+MIIEhjCCA26gAwIBAgIQUkIGSk83/kNpSHqWZ/9dJzANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTA1MDYwNzA4MDkxMFoXDTIwMDUzMDEwNDgzOFow
+gZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl
+IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY
+aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR8wHQYDVQQDExZVVE4tVVNFUkZpcnN0
+LUhhcmR3YXJlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsffDOD+0
+qH/POYJRZ9Btn9L/WPPnnyvsDYlUmbk4mRb34CF5SMK7YXQSlh08anLVPBBnOjnt
+KxPNZuuVCTOkbJex6MbswXV5nEZejavQav25KlUXEFSzGfCa9vGxXbanbfvgcRdr
+ooj7AN/+GjF3DJoBerEy4ysBBzhuw6VeI7xFm3tQwckwj9vlK3rTW/szQB6g1ZgX
+vIuHw4nTXaCOsqqq9o5piAbF+okh8widaS4JM5spDUYPjMxJNLBpUb35Bs1orWZM
+vD6sYb0KiA7I3z3ufARMnQpea5HW7sftKI2rTYeJc9BupNAeFosU4XZEA39jrOTN
+SZzFkvSrMqFIWwIDAQABo4H0MIHxMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8D
+veAky1QaMB0GA1UdDgQWBBShcl8mGyiYQ5VdBzfVhZadS9LDRTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAIMAYGBFUdIAAwewYDVR0f
+BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQWRkVHJ1c3RFeHRl
+cm5hbENBUm9vdC5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BZGRU
+cnVzdEV4dGVybmFsQ0FSb290LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAYGQ5WaJD
+ZS79+R/WrjO76FMTxIjuIxpszthkWVNTkOg239T88055L9XmjwzvKkFtcb2beDgj
+03BLhgz9EqciYhLYzOBR7y3lzQxFoura7X7s9zKa5wU1Xm7CLGhonf+M8cpVh8Qv
+sUAG3IQiXG2zzdGbGgozKGYWDL0zwvYH8eOheZTg+NDQ099Shj+p4ckdPoaEsdtf
+7uRJQ8E5fc8vlqd1XX5nZ4TlWSBAvzcivwdDtDDhQ4rNA11tuSnZhKf1YmOEhtY3
+vm9nu/9iVzmdDE2yKmE9HZzvmncgoC/uGnKdsJ2/eBMnBwpgEZP1Dy7J72skg/6b
+kLRLaIHQwvrgPw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert38[] = {
+ 0x30, 0x82, 0x04, 0x86, 0x30, 0x82, 0x03, 0x6e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x52, 0x42, 0x06, 0x4a, 0x4f, 0x37, 0xfe, 0x43, 0x69,
+ 0x48, 0x7a, 0x96, 0x67, 0xff, 0x5d, 0x27, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x35, 0x30, 0x36, 0x30,
+ 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65,
+ 0x20, 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52,
+ 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75,
+ 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55,
+ 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74,
+ 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0xf7, 0xc3, 0x38, 0x3f, 0xb4,
+ 0xa8, 0x7f, 0xcf, 0x39, 0x82, 0x51, 0x67, 0xd0, 0x6d, 0x9f, 0xd2, 0xff,
+ 0x58, 0xf3, 0xe7, 0x9f, 0x2b, 0xec, 0x0d, 0x89, 0x54, 0x99, 0xb9, 0x38,
+ 0x99, 0x16, 0xf7, 0xe0, 0x21, 0x79, 0x48, 0xc2, 0xbb, 0x61, 0x74, 0x12,
+ 0x96, 0x1d, 0x3c, 0x6a, 0x72, 0xd5, 0x3c, 0x10, 0x67, 0x3a, 0x39, 0xed,
+ 0x2b, 0x13, 0xcd, 0x66, 0xeb, 0x95, 0x09, 0x33, 0xa4, 0x6c, 0x97, 0xb1,
+ 0xe8, 0xc6, 0xec, 0xc1, 0x75, 0x79, 0x9c, 0x46, 0x5e, 0x8d, 0xab, 0xd0,
+ 0x6a, 0xfd, 0xb9, 0x2a, 0x55, 0x17, 0x10, 0x54, 0xb3, 0x19, 0xf0, 0x9a,
+ 0xf6, 0xf1, 0xb1, 0x5d, 0xb6, 0xa7, 0x6d, 0xfb, 0xe0, 0x71, 0x17, 0x6b,
+ 0xa2, 0x88, 0xfb, 0x00, 0xdf, 0xfe, 0x1a, 0x31, 0x77, 0x0c, 0x9a, 0x01,
+ 0x7a, 0xb1, 0x32, 0xe3, 0x2b, 0x01, 0x07, 0x38, 0x6e, 0xc3, 0xa5, 0x5e,
+ 0x23, 0xbc, 0x45, 0x9b, 0x7b, 0x50, 0xc1, 0xc9, 0x30, 0x8f, 0xdb, 0xe5,
+ 0x2b, 0x7a, 0xd3, 0x5b, 0xfb, 0x33, 0x40, 0x1e, 0xa0, 0xd5, 0x98, 0x17,
+ 0xbc, 0x8b, 0x87, 0xc3, 0x89, 0xd3, 0x5d, 0xa0, 0x8e, 0xb2, 0xaa, 0xaa,
+ 0xf6, 0x8e, 0x69, 0x88, 0x06, 0xc5, 0xfa, 0x89, 0x21, 0xf3, 0x08, 0x9d,
+ 0x69, 0x2e, 0x09, 0x33, 0x9b, 0x29, 0x0d, 0x46, 0x0f, 0x8c, 0xcc, 0x49,
+ 0x34, 0xb0, 0x69, 0x51, 0xbd, 0xf9, 0x06, 0xcd, 0x68, 0xad, 0x66, 0x4c,
+ 0xbc, 0x3e, 0xac, 0x61, 0xbd, 0x0a, 0x88, 0x0e, 0xc8, 0xdf, 0x3d, 0xee,
+ 0x7c, 0x04, 0x4c, 0x9d, 0x0a, 0x5e, 0x6b, 0x91, 0xd6, 0xee, 0xc7, 0xed,
+ 0x28, 0x8d, 0xab, 0x4d, 0x87, 0x89, 0x73, 0xd0, 0x6e, 0xa4, 0xd0, 0x1e,
+ 0x16, 0x8b, 0x14, 0xe1, 0x76, 0x44, 0x03, 0x7f, 0x63, 0xac, 0xe4, 0xcd,
+ 0x49, 0x9c, 0xc5, 0x92, 0xf4, 0xab, 0x32, 0xa1, 0x48, 0x5b, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x81, 0xf4, 0x30, 0x81, 0xf1, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd,
+ 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03,
+ 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98,
+ 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5, 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3,
+ 0x45, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x74, 0x30, 0x72, 0x30, 0x38, 0xa0, 0x36, 0xa0, 0x34, 0x86, 0x32,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65,
+ 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x36, 0xa0, 0x34, 0xa0, 0x32, 0x86, 0x30, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x6f, 0x64, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x41, 0x64, 0x64, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x60, 0x64, 0x39, 0x59, 0xa2, 0x43,
+ 0x65, 0x2e, 0xfd, 0xf9, 0x1f, 0xd6, 0xae, 0x33, 0xbb, 0xe8, 0x53, 0x13,
+ 0xc4, 0x88, 0xee, 0x23, 0x1a, 0x6c, 0xce, 0xd8, 0x64, 0x59, 0x53, 0x53,
+ 0x90, 0xe8, 0x36, 0xdf, 0xd4, 0xfc, 0xf3, 0x4e, 0x79, 0x2f, 0xd5, 0xe6,
+ 0x8f, 0x0c, 0xef, 0x2a, 0x41, 0x6d, 0x71, 0xbd, 0x9b, 0x78, 0x38, 0x23,
+ 0xd3, 0x70, 0x4b, 0x86, 0x0c, 0xfd, 0x12, 0xa7, 0x22, 0x62, 0x12, 0xd8,
+ 0xcc, 0xe0, 0x51, 0xef, 0x2d, 0xe5, 0xcd, 0x0c, 0x45, 0xa2, 0xea, 0xda,
+ 0xed, 0x7e, 0xec, 0xf7, 0x32, 0x9a, 0xe7, 0x05, 0x35, 0x5e, 0x6e, 0xc2,
+ 0x2c, 0x68, 0x68, 0x9d, 0xff, 0x8c, 0xf1, 0xca, 0x55, 0x87, 0xc4, 0x2f,
+ 0xb1, 0x40, 0x06, 0xdc, 0x84, 0x22, 0x5c, 0x6d, 0xb3, 0xcd, 0xd1, 0x9b,
+ 0x1a, 0x0a, 0x33, 0x28, 0x66, 0x16, 0x0c, 0xbd, 0x33, 0xc2, 0xf6, 0x07,
+ 0xf1, 0xe3, 0xa1, 0x79, 0x94, 0xe0, 0xf8, 0xd0, 0xd0, 0xd3, 0xdf, 0x52,
+ 0x86, 0x3f, 0xa9, 0xe1, 0xc9, 0x1d, 0x3e, 0x86, 0x84, 0xb1, 0xdb, 0x5f,
+ 0xee, 0xe4, 0x49, 0x43, 0xc1, 0x39, 0x7d, 0xcf, 0x2f, 0x96, 0xa7, 0x75,
+ 0x5d, 0x7e, 0x67, 0x67, 0x84, 0xe5, 0x59, 0x20, 0x40, 0xbf, 0x37, 0x22,
+ 0xbf, 0x07, 0x43, 0xb4, 0x30, 0xe1, 0x43, 0x8a, 0xcd, 0x03, 0x5d, 0x6d,
+ 0xb9, 0x29, 0xd9, 0x84, 0xa7, 0xf5, 0x62, 0x63, 0x84, 0x86, 0xd6, 0x37,
+ 0xbe, 0x6f, 0x67, 0xbb, 0xff, 0x62, 0x57, 0x39, 0x9d, 0x0c, 0x4d, 0xb2,
+ 0x2a, 0x61, 0x3d, 0x1d, 0x9c, 0xef, 0x9a, 0x77, 0x20, 0xa0, 0x2f, 0xee,
+ 0x1a, 0x72, 0x9d, 0xb0, 0x9d, 0xbf, 0x78, 0x13, 0x27, 0x07, 0x0a, 0x60,
+ 0x11, 0x93, 0xf5, 0x0f, 0x2e, 0xc9, 0xef, 0x6b, 0x24, 0x83, 0xfe, 0x9b,
+ 0x90, 0xb4, 0x4b, 0x68, 0x81, 0xd0, 0xc2, 0xfa, 0xe0, 0x3f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:2f:4e:e1:42:f9
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA
+ Validity
+ Not Before: Apr 13 10:00:00 2011 GMT
+ Not After : Apr 13 10:00:00 2022 GMT
+ Subject: C=BE, O=GlobalSign nv-sa, CN=GlobalSign Organization Validation CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:dd:35:1d:f2:20:54:26:1a:d0:ef:a5:6f:81:76:
+ 59:70:dc:e7:f4:d4:03:24:1f:24:0e:9d:22:9f:d4:
+ 27:32:7a:2b:7c:ee:8b:e3:61:62:38:17:af:b4:4b:
+ 7a:9f:67:21:1c:2d:95:54:ba:79:ba:b6:c4:f2:0d:
+ 21:74:17:67:74:e2:b1:64:08:99:60:78:fb:67:c2:
+ 4b:f7:27:8d:6f:36:76:cf:31:8c:e5:f1:06:d7:dc:
+ 57:0e:5b:ac:ee:ce:2d:ab:aa:a9:70:2f:02:86:c8:
+ b1:d0:08:07:95:ea:2a:ec:d1:9e:e4:36:5c:3b:a6:
+ 36:b5:43:8b:ab:f7:8e:3e:00:1b:ff:85:59:6b:62:
+ 01:8d:82:e8:4a:ba:38:b3:e0:c3:f4:6d:19:a7:ea:
+ 05:dd:84:67:c2:66:c7:24:02:73:5a:b5:ee:a4:19:
+ d9:fc:00:ce:b6:a4:8d:df:7e:bd:5f:b2:3a:9d:84:
+ 31:4f:c8:63:0c:e4:d8:0d:52:a3:7e:01:1b:d4:67:
+ a5:18:28:eb:01:a7:82:3c:d9:8e:1d:e5:47:0d:ba:
+ 8b:59:14:a3:1f:1f:4b:ea:e2:27:46:86:ce:9d:39:
+ c4:66:41:a7:e2:15:23:6b:56:47:c1:ed:c5:53:e4:
+ d4:80:1f:6b:fa:80:46:98:b2:09:a6:0f:95:be:66:
+ 88:93
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 5D:46:B2:8D:C4:4B:74:1C:BB:ED:F5:73:B6:3A:B7:38:8F:75:9E:7E
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.globalsign.com/repository/
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.globalsign.net/root.crl
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.globalsign.com/rootr1
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Microsoft Server Gated Crypto
+ X509v3 Authority Key Identifier:
+ keyid:60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 73:7a:ec:01:2c:17:22:91:9a:ca:b1:67:18:a2:ba:c8:05:89:
+ 92:24:de:1f:b8:ab:44:9f:f7:40:55:65:f2:e0:f4:2e:c7:de:
+ b0:3f:99:15:1f:95:70:82:e9:9b:4a:64:24:20:16:f0:76:17:
+ d2:1b:fe:ac:fa:06:b4:77:cf:98:d8:2a:ec:57:15:d8:5e:4e:
+ dd:8b:96:e1:53:33:19:91:d5:84:6e:25:ef:0f:cb:ad:bf:db:
+ 4b:6b:56:cc:b5:d4:40:3e:26:5e:b6:59:f4:c5:90:c9:09:c4:
+ 84:df:bc:26:7d:82:e9:eb:f4:5b:fc:c8:15:de:09:18:45:86:
+ b3:8b:4d:c7:6b:35:27:9b:60:f6:a4:5a:2a:58:49:b1:d8:35:
+ 43:c6:32:bb:5e:3b:c4:4a:21:c1:a0:3b:5e:c1:23:a9:ce:db:
+ d5:ba:fe:5d:6d:fd:00:7e:fa:f1:94:37:61:b9:00:39:66:96:
+ a9:9c:b4:1e:11:ef:55:d8:b4:d8:b0:c4:a5:ae:32:0a:2f:f8:
+ 2d:f4:a2:a7:ff:36:d3:5e:63:8b:4e:12:f7:b5:28:80:75:ee:
+ 94:2f:70:a0:56:77:39:aa:39:97:17:fc:00:f3:cf:66:e7:a2:
+ 71:92:ab:05:9b:73:2e:7a:e7:e7:21:59:09:8d:30:a1:ac:5c:
+ ca:19:7a:f8
+-----BEGIN CERTIFICATE-----
+MIIEizCCA3OgAwIBAgILBAAAAAABL07hQvkwDQYJKoZIhvcNAQEFBQAwVzELMAkG
+A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
+b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
+MDBaFw0yMjA0MTMxMDAwMDBaMF0xCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
+YWxTaWduIG52LXNhMTMwMQYDVQQDEypHbG9iYWxTaWduIE9yZ2FuaXphdGlvbiBW
+YWxpZGF0aW9uIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDdNR3yIFQmGtDvpW+Bdllw3Of01AMkHyQOnSKf1Ccyeit87ovjYWI4F6+0S3qf
+ZyEcLZVUunm6tsTyDSF0F2d04rFkCJlgePtnwkv3J41vNnbPMYzl8QbX3FcOW6zu
+zi2rqqlwLwKGyLHQCAeV6irs0Z7kNlw7pja1Q4ur944+ABv/hVlrYgGNguhKujiz
+4MP0bRmn6gXdhGfCZsckAnNate6kGdn8AM62pI3ffr1fsjqdhDFPyGMM5NgNUqN+
+ARvUZ6UYKOsBp4I82Y4d5UcNuotZFKMfH0vq4idGhs6dOcRmQafiFSNrVkfB7cVT
+5NSAH2v6gEaYsgmmD5W+ZoiTAgMBAAGjggFQMIIBTDAOBgNVHQ8BAf8EBAMCAQYw
+EgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUXUayjcRLdBy77fVztjq3OI91
+nn4wRwYDVR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3
+Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0
+dHA6Ly9jcmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEEMTAv
+MC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEw
+KQYDVR0lBCIwIAYIKwYBBQUHAwEGCCsGAQUFBwMCBgorBgEEAYI3CgMDMB8GA1Ud
+IwQYMBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBz
+euwBLBcikZrKsWcYorrIBYmSJN4fuKtEn/dAVWXy4PQux96wP5kVH5VwgumbSmQk
+IBbwdhfSG/6s+ga0d8+Y2CrsVxXYXk7di5bhUzMZkdWEbiXvD8utv9tLa1bMtdRA
+PiZetln0xZDJCcSE37wmfYLp6/Rb/MgV3gkYRYazi03HazUnm2D2pFoqWEmx2DVD
+xjK7XjvESiHBoDtewSOpztvVuv5dbf0AfvrxlDdhuQA5ZpapnLQeEe9V2LTYsMSl
+rjIKL/gt9KKn/zbTXmOLThL3tSiAde6UL3CgVnc5qjmXF/wA889m56JxkqsFm3Mu
+eufnIVkJjTChrFzKGXr4
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert39[] = {
+ 0x30, 0x82, 0x04, 0x8b, 0x30, 0x82, 0x03, 0x73, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x4e, 0xe1,
+ 0x42, 0xf9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x57, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30, 0x17,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61, 0x31,
+ 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x07, 0x52, 0x6f,
+ 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69,
+ 0x67, 0x6e, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x31, 0x30, 0x34, 0x31, 0x33, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x33, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x5d, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x42, 0x45, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x47, 0x6c, 0x6f, 0x62,
+ 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x6e, 0x76, 0x2d, 0x73, 0x61,
+ 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x47,
+ 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x4f, 0x72,
+ 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x41,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xdd, 0x35, 0x1d, 0xf2, 0x20, 0x54, 0x26, 0x1a, 0xd0, 0xef,
+ 0xa5, 0x6f, 0x81, 0x76, 0x59, 0x70, 0xdc, 0xe7, 0xf4, 0xd4, 0x03, 0x24,
+ 0x1f, 0x24, 0x0e, 0x9d, 0x22, 0x9f, 0xd4, 0x27, 0x32, 0x7a, 0x2b, 0x7c,
+ 0xee, 0x8b, 0xe3, 0x61, 0x62, 0x38, 0x17, 0xaf, 0xb4, 0x4b, 0x7a, 0x9f,
+ 0x67, 0x21, 0x1c, 0x2d, 0x95, 0x54, 0xba, 0x79, 0xba, 0xb6, 0xc4, 0xf2,
+ 0x0d, 0x21, 0x74, 0x17, 0x67, 0x74, 0xe2, 0xb1, 0x64, 0x08, 0x99, 0x60,
+ 0x78, 0xfb, 0x67, 0xc2, 0x4b, 0xf7, 0x27, 0x8d, 0x6f, 0x36, 0x76, 0xcf,
+ 0x31, 0x8c, 0xe5, 0xf1, 0x06, 0xd7, 0xdc, 0x57, 0x0e, 0x5b, 0xac, 0xee,
+ 0xce, 0x2d, 0xab, 0xaa, 0xa9, 0x70, 0x2f, 0x02, 0x86, 0xc8, 0xb1, 0xd0,
+ 0x08, 0x07, 0x95, 0xea, 0x2a, 0xec, 0xd1, 0x9e, 0xe4, 0x36, 0x5c, 0x3b,
+ 0xa6, 0x36, 0xb5, 0x43, 0x8b, 0xab, 0xf7, 0x8e, 0x3e, 0x00, 0x1b, 0xff,
+ 0x85, 0x59, 0x6b, 0x62, 0x01, 0x8d, 0x82, 0xe8, 0x4a, 0xba, 0x38, 0xb3,
+ 0xe0, 0xc3, 0xf4, 0x6d, 0x19, 0xa7, 0xea, 0x05, 0xdd, 0x84, 0x67, 0xc2,
+ 0x66, 0xc7, 0x24, 0x02, 0x73, 0x5a, 0xb5, 0xee, 0xa4, 0x19, 0xd9, 0xfc,
+ 0x00, 0xce, 0xb6, 0xa4, 0x8d, 0xdf, 0x7e, 0xbd, 0x5f, 0xb2, 0x3a, 0x9d,
+ 0x84, 0x31, 0x4f, 0xc8, 0x63, 0x0c, 0xe4, 0xd8, 0x0d, 0x52, 0xa3, 0x7e,
+ 0x01, 0x1b, 0xd4, 0x67, 0xa5, 0x18, 0x28, 0xeb, 0x01, 0xa7, 0x82, 0x3c,
+ 0xd9, 0x8e, 0x1d, 0xe5, 0x47, 0x0d, 0xba, 0x8b, 0x59, 0x14, 0xa3, 0x1f,
+ 0x1f, 0x4b, 0xea, 0xe2, 0x27, 0x46, 0x86, 0xce, 0x9d, 0x39, 0xc4, 0x66,
+ 0x41, 0xa7, 0xe2, 0x15, 0x23, 0x6b, 0x56, 0x47, 0xc1, 0xed, 0xc5, 0x53,
+ 0xe4, 0xd4, 0x80, 0x1f, 0x6b, 0xfa, 0x80, 0x46, 0x98, 0xb2, 0x09, 0xa6,
+ 0x0f, 0x95, 0xbe, 0x66, 0x88, 0x93, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x50, 0x30, 0x82, 0x01, 0x4c, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x5d, 0x46, 0xb2, 0x8d, 0xc4, 0x4b,
+ 0x74, 0x1c, 0xbb, 0xed, 0xf5, 0x73, 0xb6, 0x3a, 0xb7, 0x38, 0x8f, 0x75,
+ 0x9e, 0x7e, 0x30, 0x47, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x40, 0x30,
+ 0x3e, 0x30, 0x3c, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x34, 0x30,
+ 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x2f, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c,
+ 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f,
+ 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x72, 0x31, 0x30,
+ 0x29, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x22, 0x30, 0x20, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
+ 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x60, 0x7b, 0x66, 0x1a, 0x45,
+ 0x0d, 0x97, 0xca, 0x89, 0x50, 0x2f, 0x7d, 0x04, 0xcd, 0x34, 0xa8, 0xff,
+ 0xfc, 0xfd, 0x4b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x73,
+ 0x7a, 0xec, 0x01, 0x2c, 0x17, 0x22, 0x91, 0x9a, 0xca, 0xb1, 0x67, 0x18,
+ 0xa2, 0xba, 0xc8, 0x05, 0x89, 0x92, 0x24, 0xde, 0x1f, 0xb8, 0xab, 0x44,
+ 0x9f, 0xf7, 0x40, 0x55, 0x65, 0xf2, 0xe0, 0xf4, 0x2e, 0xc7, 0xde, 0xb0,
+ 0x3f, 0x99, 0x15, 0x1f, 0x95, 0x70, 0x82, 0xe9, 0x9b, 0x4a, 0x64, 0x24,
+ 0x20, 0x16, 0xf0, 0x76, 0x17, 0xd2, 0x1b, 0xfe, 0xac, 0xfa, 0x06, 0xb4,
+ 0x77, 0xcf, 0x98, 0xd8, 0x2a, 0xec, 0x57, 0x15, 0xd8, 0x5e, 0x4e, 0xdd,
+ 0x8b, 0x96, 0xe1, 0x53, 0x33, 0x19, 0x91, 0xd5, 0x84, 0x6e, 0x25, 0xef,
+ 0x0f, 0xcb, 0xad, 0xbf, 0xdb, 0x4b, 0x6b, 0x56, 0xcc, 0xb5, 0xd4, 0x40,
+ 0x3e, 0x26, 0x5e, 0xb6, 0x59, 0xf4, 0xc5, 0x90, 0xc9, 0x09, 0xc4, 0x84,
+ 0xdf, 0xbc, 0x26, 0x7d, 0x82, 0xe9, 0xeb, 0xf4, 0x5b, 0xfc, 0xc8, 0x15,
+ 0xde, 0x09, 0x18, 0x45, 0x86, 0xb3, 0x8b, 0x4d, 0xc7, 0x6b, 0x35, 0x27,
+ 0x9b, 0x60, 0xf6, 0xa4, 0x5a, 0x2a, 0x58, 0x49, 0xb1, 0xd8, 0x35, 0x43,
+ 0xc6, 0x32, 0xbb, 0x5e, 0x3b, 0xc4, 0x4a, 0x21, 0xc1, 0xa0, 0x3b, 0x5e,
+ 0xc1, 0x23, 0xa9, 0xce, 0xdb, 0xd5, 0xba, 0xfe, 0x5d, 0x6d, 0xfd, 0x00,
+ 0x7e, 0xfa, 0xf1, 0x94, 0x37, 0x61, 0xb9, 0x00, 0x39, 0x66, 0x96, 0xa9,
+ 0x9c, 0xb4, 0x1e, 0x11, 0xef, 0x55, 0xd8, 0xb4, 0xd8, 0xb0, 0xc4, 0xa5,
+ 0xae, 0x32, 0x0a, 0x2f, 0xf8, 0x2d, 0xf4, 0xa2, 0xa7, 0xff, 0x36, 0xd3,
+ 0x5e, 0x63, 0x8b, 0x4e, 0x12, 0xf7, 0xb5, 0x28, 0x80, 0x75, 0xee, 0x94,
+ 0x2f, 0x70, 0xa0, 0x56, 0x77, 0x39, 0xaa, 0x39, 0x97, 0x17, 0xfc, 0x00,
+ 0xf3, 0xcf, 0x66, 0xe7, 0xa2, 0x71, 0x92, 0xab, 0x05, 0x9b, 0x73, 0x2e,
+ 0x7a, 0xe7, 0xe7, 0x21, 0x59, 0x09, 0x8d, 0x30, 0xa1, 0xac, 0x5c, 0xca,
+ 0x19, 0x7a, 0xf8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 67109810 (0x40003b2)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Dec 15 20:32:00 2004 GMT
+ Not After : Dec 15 23:59:00 2014 GMT
+ Subject: C=IT, O=I.T. Telecom, OU=Servizi di certificazione, CN=I.T. Telecom Global CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c3:14:14:be:cd:a1:7e:89:2d:ab:8f:e5:ab:5c:
+ 2e:70:a1:e3:cb:08:2e:07:2c:a9:38:f8:9a:1b:3b:
+ 02:32:43:47:e7:fd:98:04:83:2a:23:34:8b:a4:3c:
+ 92:1b:95:fb:ae:bb:5a:70:c9:92:5e:86:09:64:c6:
+ 42:c2:f3:6c:3c:18:35:8b:5f:f1:c9:ed:3d:72:cb:
+ 4b:3f:ed:61:c3:5f:dd:2c:38:27:e9:73:1d:04:e9:
+ 35:c7:7f:4f:92:a9:c2:f6:b9:a9:6d:05:0d:5b:02:
+ bf:c7:0c:a9:0d:a2:5f:15:48:30:79:b7:ab:77:48:
+ 51:13:83:1f:0e:72:07:4a:75:13:be:b0:90:3f:c6:
+ 8f:17:03:32:55:76:1f:2f:3b:c1:ee:53:35:1d:33:
+ 86:25:6c:81:1b:eb:10:fe:d2:ab:b5:1b:3c:38:fd:
+ 90:71:02:70:3b:09:3a:c7:71:4a:c2:51:65:7c:f5:
+ 59:08:e1:4a:83:d4:5f:d0:10:1c:7b:f6:3c:95:24:
+ 99:59:2c:df:14:59:28:83:bf:f8:e8:52:83:c8:12:
+ 65:0f:95:a5:94:1c:02:d8:b7:aa:2d:6c:e5:59:53:
+ 65:f4:51:d7:48:96:2e:60:9c:59:9c:51:d0:97:b3:
+ 9e:38:91:ab:b3:7a:5e:b4:3b:bb:c8:62:3f:c0:b8:
+ 69:27
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ 3E:81:72:D4:41:E6:9F:BC:B0:37:DA:3F:68:0E:7A:86:0B:EF:55:74
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://www.public-trust.com/CPS/OmniRoot.html
+ Policy: 1.3.76.12.1.1.3
+ CPS: https://www.tipki.com/GlobalCA/CPS
+
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 Key Usage: critical
+ Digital Signature, Non Repudiation, Key Encipherment, Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ Signature Algorithm: sha1WithRSAEncryption
+ 84:5b:69:ea:0c:c7:1e:de:b2:ca:fe:e8:26:ae:36:94:00:09:
+ 25:69:07:93:e3:c3:96:4b:97:4c:a6:79:38:d2:df:dc:41:7b:
+ d8:61:70:ec:c0:36:8c:8c:b1:65:ac:d6:e4:32:b9:7c:3c:04:
+ 79:72:af:87:0a:58:19:25:f7:25:b7:1d:49:c9:8b:33:54:43:
+ c4:8b:09:26:1c:a9:0c:50:e3:df:92:c5:68:6c:05:2d:c6:d7:
+ 23:13:9e:bf:46:36:10:8d:f1:27:c1:74:a8:f5:0f:c6:b8:e2:
+ 91:3c:2d:4c:fb:9d:da:1e:e4:a5:87:80:a6:ce:43:b7:14:7d:
+ 9f:38
+-----BEGIN CERTIFICATE-----
+MIIEizCCA/SgAwIBAgIEBAADsjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTA0MTIxNTIwMzIwMFoXDTE0MTIxNTIzNTkwMFowaTELMAkG
+A1UEBhMCSVQxFTATBgNVBAoTDEkuVC4gVGVsZWNvbTEiMCAGA1UECxMZU2Vydml6
+aSBkaSBjZXJ0aWZpY2F6aW9uZTEfMB0GA1UEAxMWSS5ULiBUZWxlY29tIEdsb2Jh
+bCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMUFL7NoX6JLauP
+5atcLnCh48sILgcsqTj4mhs7AjJDR+f9mASDKiM0i6Q8khuV+667WnDJkl6GCWTG
+QsLzbDwYNYtf8cntPXLLSz/tYcNf3Sw4J+lzHQTpNcd/T5Kpwva5qW0FDVsCv8cM
+qQ2iXxVIMHm3q3dIURODHw5yB0p1E76wkD/GjxcDMlV2Hy87we5TNR0zhiVsgRvr
+EP7Sq7UbPDj9kHECcDsJOsdxSsJRZXz1WQjhSoPUX9AQHHv2PJUkmVks3xRZKIO/
++OhSg8gSZQ+VpZQcAti3qi1s5VlTZfRR10iWLmCcWZxR0JeznjiRq7N6XrQ7u8hi
+P8C4aScCAwEAAaOCAa4wggGqMEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cu
+cHVibGljLXRydXN0LmNvbS9jZ2ktYmluL0NSTC8yMDE4L2NkcC5jcmwwHQYDVR0O
+BBYEFD6BctRB5p+8sDfaP2gOeoYL71V0MIGRBgNVHSAEgYkwgYYwSAYJKwYBBAGx
+PgEAMDswOQYIKwYBBQUHAgEWLWh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9D
+UFMvT21uaVJvb3QuaHRtbDA6BgYrTAwBAQMwMDAuBggrBgEFBQcCARYiaHR0cHM6
+Ly93d3cudGlwa2kuY29tL0dsb2JhbENBL0NQUzCBiQYDVR0jBIGBMH+heaR3MHUx
+CzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsT
+HkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5
+YmVyVHJ1c3QgR2xvYmFsIFJvb3SCAgGlMA4GA1UdDwEB/wQEAwIB5jASBgNVHRMB
+Af8ECDAGAQH/AgEBMA0GCSqGSIb3DQEBBQUAA4GBAIRbaeoMxx7essr+6CauNpQA
+CSVpB5Pjw5ZLl0ymeTjS39xBe9hhcOzANoyMsWWs1uQyuXw8BHlyr4cKWBkl9yW3
+HUnJizNUQ8SLCSYcqQxQ49+SxWhsBS3G1yMTnr9GNhCN8SfBdKj1D8a44pE8LUz7
+ndoe5KWHgKbOQ7cUfZ84
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert40[] = {
+ 0x30, 0x82, 0x04, 0x8b, 0x30, 0x82, 0x03, 0xf4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x04, 0x00, 0x03, 0xb2, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x30, 0x34, 0x31, 0x32, 0x31, 0x35, 0x32, 0x30, 0x33, 0x32, 0x30,
+ 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x32, 0x31, 0x35, 0x32, 0x33,
+ 0x35, 0x39, 0x30, 0x30, 0x5a, 0x30, 0x69, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x54, 0x31, 0x15, 0x30, 0x13,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x49, 0x2e, 0x54, 0x2e, 0x20,
+ 0x54, 0x65, 0x6c, 0x65, 0x63, 0x6f, 0x6d, 0x31, 0x22, 0x30, 0x20, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x19, 0x53, 0x65, 0x72, 0x76, 0x69, 0x7a,
+ 0x69, 0x20, 0x64, 0x69, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x7a, 0x69, 0x6f, 0x6e, 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x49, 0x2e, 0x54, 0x2e, 0x20, 0x54,
+ 0x65, 0x6c, 0x65, 0x63, 0x6f, 0x6d, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61,
+ 0x6c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xc3, 0x14, 0x14, 0xbe, 0xcd, 0xa1, 0x7e, 0x89, 0x2d, 0xab, 0x8f,
+ 0xe5, 0xab, 0x5c, 0x2e, 0x70, 0xa1, 0xe3, 0xcb, 0x08, 0x2e, 0x07, 0x2c,
+ 0xa9, 0x38, 0xf8, 0x9a, 0x1b, 0x3b, 0x02, 0x32, 0x43, 0x47, 0xe7, 0xfd,
+ 0x98, 0x04, 0x83, 0x2a, 0x23, 0x34, 0x8b, 0xa4, 0x3c, 0x92, 0x1b, 0x95,
+ 0xfb, 0xae, 0xbb, 0x5a, 0x70, 0xc9, 0x92, 0x5e, 0x86, 0x09, 0x64, 0xc6,
+ 0x42, 0xc2, 0xf3, 0x6c, 0x3c, 0x18, 0x35, 0x8b, 0x5f, 0xf1, 0xc9, 0xed,
+ 0x3d, 0x72, 0xcb, 0x4b, 0x3f, 0xed, 0x61, 0xc3, 0x5f, 0xdd, 0x2c, 0x38,
+ 0x27, 0xe9, 0x73, 0x1d, 0x04, 0xe9, 0x35, 0xc7, 0x7f, 0x4f, 0x92, 0xa9,
+ 0xc2, 0xf6, 0xb9, 0xa9, 0x6d, 0x05, 0x0d, 0x5b, 0x02, 0xbf, 0xc7, 0x0c,
+ 0xa9, 0x0d, 0xa2, 0x5f, 0x15, 0x48, 0x30, 0x79, 0xb7, 0xab, 0x77, 0x48,
+ 0x51, 0x13, 0x83, 0x1f, 0x0e, 0x72, 0x07, 0x4a, 0x75, 0x13, 0xbe, 0xb0,
+ 0x90, 0x3f, 0xc6, 0x8f, 0x17, 0x03, 0x32, 0x55, 0x76, 0x1f, 0x2f, 0x3b,
+ 0xc1, 0xee, 0x53, 0x35, 0x1d, 0x33, 0x86, 0x25, 0x6c, 0x81, 0x1b, 0xeb,
+ 0x10, 0xfe, 0xd2, 0xab, 0xb5, 0x1b, 0x3c, 0x38, 0xfd, 0x90, 0x71, 0x02,
+ 0x70, 0x3b, 0x09, 0x3a, 0xc7, 0x71, 0x4a, 0xc2, 0x51, 0x65, 0x7c, 0xf5,
+ 0x59, 0x08, 0xe1, 0x4a, 0x83, 0xd4, 0x5f, 0xd0, 0x10, 0x1c, 0x7b, 0xf6,
+ 0x3c, 0x95, 0x24, 0x99, 0x59, 0x2c, 0xdf, 0x14, 0x59, 0x28, 0x83, 0xbf,
+ 0xf8, 0xe8, 0x52, 0x83, 0xc8, 0x12, 0x65, 0x0f, 0x95, 0xa5, 0x94, 0x1c,
+ 0x02, 0xd8, 0xb7, 0xaa, 0x2d, 0x6c, 0xe5, 0x59, 0x53, 0x65, 0xf4, 0x51,
+ 0xd7, 0x48, 0x96, 0x2e, 0x60, 0x9c, 0x59, 0x9c, 0x51, 0xd0, 0x97, 0xb3,
+ 0x9e, 0x38, 0x91, 0xab, 0xb3, 0x7a, 0x5e, 0xb4, 0x3b, 0xbb, 0xc8, 0x62,
+ 0x3f, 0xc0, 0xb8, 0x69, 0x27, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0xae, 0x30, 0x82, 0x01, 0xaa, 0x30, 0x45, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38, 0xa0, 0x36, 0x86,
+ 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d, 0x62, 0x69, 0x6e,
+ 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38, 0x2f, 0x63, 0x64,
+ 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x3e, 0x81, 0x72, 0xd4, 0x41, 0xe6, 0x9f, 0xbc,
+ 0xb0, 0x37, 0xda, 0x3f, 0x68, 0x0e, 0x7a, 0x86, 0x0b, 0xef, 0x55, 0x74,
+ 0x30, 0x81, 0x91, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x81, 0x89, 0x30,
+ 0x81, 0x86, 0x30, 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1,
+ 0x3e, 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x2d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43,
+ 0x50, 0x53, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x52, 0x6f, 0x6f, 0x74, 0x2e,
+ 0x68, 0x74, 0x6d, 0x6c, 0x30, 0x3a, 0x06, 0x06, 0x2b, 0x4c, 0x0c, 0x01,
+ 0x01, 0x03, 0x30, 0x30, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x69, 0x70, 0x6b, 0x69, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x43, 0x41,
+ 0x2f, 0x43, 0x50, 0x53, 0x30, 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x81, 0x81, 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47,
+ 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79,
+ 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f,
+ 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0xe6, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x84, 0x5b, 0x69, 0xea, 0x0c,
+ 0xc7, 0x1e, 0xde, 0xb2, 0xca, 0xfe, 0xe8, 0x26, 0xae, 0x36, 0x94, 0x00,
+ 0x09, 0x25, 0x69, 0x07, 0x93, 0xe3, 0xc3, 0x96, 0x4b, 0x97, 0x4c, 0xa6,
+ 0x79, 0x38, 0xd2, 0xdf, 0xdc, 0x41, 0x7b, 0xd8, 0x61, 0x70, 0xec, 0xc0,
+ 0x36, 0x8c, 0x8c, 0xb1, 0x65, 0xac, 0xd6, 0xe4, 0x32, 0xb9, 0x7c, 0x3c,
+ 0x04, 0x79, 0x72, 0xaf, 0x87, 0x0a, 0x58, 0x19, 0x25, 0xf7, 0x25, 0xb7,
+ 0x1d, 0x49, 0xc9, 0x8b, 0x33, 0x54, 0x43, 0xc4, 0x8b, 0x09, 0x26, 0x1c,
+ 0xa9, 0x0c, 0x50, 0xe3, 0xdf, 0x92, 0xc5, 0x68, 0x6c, 0x05, 0x2d, 0xc6,
+ 0xd7, 0x23, 0x13, 0x9e, 0xbf, 0x46, 0x36, 0x10, 0x8d, 0xf1, 0x27, 0xc1,
+ 0x74, 0xa8, 0xf5, 0x0f, 0xc6, 0xb8, 0xe2, 0x91, 0x3c, 0x2d, 0x4c, 0xfb,
+ 0x9d, 0xda, 0x1e, 0xe4, 0xa5, 0x87, 0x80, 0xa6, 0xce, 0x43, 0xb7, 0x14,
+ 0x7d, 0x9f, 0x38,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 76:10:12:8a:17:b6:82:bb:3a:1f:9d:1a:9a:35:c0:92
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Feb 18 00:00:00 2010 GMT
+ Not After : Feb 17 23:59:59 2020 GMT
+ Subject: C=US, O=Thawte, Inc., OU=Domain Validated SSL, CN=Thawte DV SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cb:98:c9:36:3f:d2:9c:d8:16:07:d4:49:63:f9:
+ 83:b0:e8:02:2d:cc:5c:5a:74:97:a6:13:ef:13:13:
+ de:05:7c:a7:e6:ca:00:23:da:39:f9:ef:13:cf:52:
+ c5:af:9a:e3:ca:be:f3:82:d9:8b:3d:aa:e1:cc:ae:
+ 88:50:66:a3:2d:ec:61:14:75:49:ab:0e:24:f1:ac:
+ 44:5b:0b:28:a2:33:20:76:1e:06:60:6a:67:05:71:
+ 8b:ba:66:62:16:7a:b3:6d:0d:c7:d0:94:40:c6:8c:
+ 3d:1e:92:0c:62:34:0d:44:89:d5:f7:89:fe:29:ed:
+ 18:8f:f6:9b:2b:08:f7:6a:ab:d8:48:97:5a:f4:9f:
+ ed:0c:75:52:22:f7:d5:5e:84:00:9f:c0:4a:0d:31:
+ 77:4c:64:d0:12:e6:0f:3a:f0:a1:c0:d5:5c:1d:e7:
+ 5f:2d:c2:f7:d6:36:18:d9:95:6e:44:4e:c9:58:14:
+ 4d:b6:8e:bb:cd:de:62:1e:fa:5b:b5:bd:18:2b:98:
+ ac:ac:93:3f:50:5a:f5:14:0b:a2:cf:b6:f3:9e:4f:
+ 5a:cd:5a:c3:36:23:da:1a:af:b0:4d:d6:4a:22:03:
+ 8f:43:02:19:bd:ea:ac:dd:c4:7a:35:32:14:f1:72:
+ 2e:08:55:40:0c:f4:07:41:41:af:38:37:84:29:42:
+ b2:55
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-11
+ X509v3 Subject Key Identifier:
+ AB:44:E4:5D:EC:83:C7:D9:C0:85:9F:F7:E1:C6:97:90:B0:8C:3F:98
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 04:ba:fb:ac:bb:fc:4b:54:11:a3:2d:88:b3:3c:bd:00:6d:8a:
+ 1a:b6:8d:c4:c1:83:f8:c7:53:2a:c1:32:6e:3a:81:a1:54:7d:
+ da:1a:3f:3a:45:4f:36:e7:42:b0:0a:42:85:97:a0:ac:fb:e5:
+ 87:a7:83:4f:e8:b1:b7:9b:58:65:6e:26:80:0b:92:4d:47:55:
+ b9:61:16:51:65:e9:2b:f1:68:d9:58:b8:03:81:d1:b7:66:1c:
+ d3:bc:c5:a6:7b:5f:3e:c5:38:46:76:e7:75:b4:a0:0c:4b:ce:
+ a2:c2:a9:c1:cc:36:73:7b:fb:b9:24:24:a0:5e:a7:f6:fa:bb:
+ 0c:28:43:9e:1d:f0:4e:f0:3f:d8:24:b0:21:dc:6d:2d:ee:bf:
+ 5a:3b:fa:88:9c:74:6c:af:21:dd:92:ec:c3:15:ef:94:75:26:
+ 46:d6:a6:3f:bf:66:48:aa:1d:ef:dd:27:e6:b7:51:89:38:7d:
+ 13:84:0c:40:fc:d0:b5:f1:e0:db:f9:4f:2f:40:1c:b4:8e:47:
+ 22:61:b8:4c:96:de:f0:5f:11:7e:4f:11:d9:ec:50:47:22:0e:
+ c5:1d:e2:64:49:e7:68:63:45:3a:8a:d9:71:f4:5e:f1:6e:b7:
+ 14:4d:3e:6f:14:1e:dc:52:fe:bc:df:0c:bd:29:3f:76:fb:11:
+ 5f:68:68:15
+-----BEGIN CERTIFICATE-----
+MIIEjzCCA3egAwIBAgIQdhASihe2grs6H50amjXAkjANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMTAwMjE4MDAwMDAwWhcNMjAw
+MjE3MjM1OTU5WjBeMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMu
+MR0wGwYDVQQLExREb21haW4gVmFsaWRhdGVkIFNTTDEZMBcGA1UEAxMQVGhhd3Rl
+IERWIFNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMuYyTY/
+0pzYFgfUSWP5g7DoAi3MXFp0l6YT7xMT3gV8p+bKACPaOfnvE89Sxa+a48q+84LZ
+iz2q4cyuiFBmoy3sYRR1SasOJPGsRFsLKKIzIHYeBmBqZwVxi7pmYhZ6s20Nx9CU
+QMaMPR6SDGI0DUSJ1feJ/intGI/2mysI92qr2EiXWvSf7Qx1UiL31V6EAJ/ASg0x
+d0xk0BLmDzrwocDVXB3nXy3C99Y2GNmVbkROyVgUTbaOu83eYh76W7W9GCuYrKyT
+P1Ba9RQLos+2855PWs1awzYj2hqvsE3WSiIDj0MCGb3qrN3EejUyFPFyLghVQAz0
+B0FBrzg3hClCslUCAwEAAaOB/DCB+TAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUH
+MAGGFmh0dHA6Ly9vY3NwLnRoYXd0ZS5jb20wEgYDVR0TAQH/BAgwBgEB/wIBADA0
+BgNVHR8ELTArMCmgJ6AlhiNodHRwOi8vY3JsLnRoYXd0ZS5jb20vVGhhd3RlUENB
+LmNybDAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVZl
+cmlTaWduTVBLSS0yLTExMB0GA1UdDgQWBBSrRORd7IPH2cCFn/fhxpeQsIw/mDAf
+BgNVHSMEGDAWgBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOC
+AQEABLr7rLv8S1QRoy2Iszy9AG2KGraNxMGD+MdTKsEybjqBoVR92ho/OkVPNudC
+sApChZegrPvlh6eDT+ixt5tYZW4mgAuSTUdVuWEWUWXpK/Fo2Vi4A4HRt2Yc07zF
+pntfPsU4RnbndbSgDEvOosKpwcw2c3v7uSQkoF6n9vq7DChDnh3wTvA/2CSwIdxt
+Le6/Wjv6iJx0bK8h3ZLswxXvlHUmRtamP79mSKod790n5rdRiTh9E4QMQPzQtfHg
+2/lPL0ActI5HImG4TJbe8F8Rfk8R2exQRyIOxR3iZEnnaGNFOorZcfRe8W63FE0+
+bxQe3FL+vN8MvSk/dvsRX2hoFQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert41[] = {
+ 0x30, 0x82, 0x04, 0x8f, 0x30, 0x82, 0x03, 0x77, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x76, 0x10, 0x12, 0x8a, 0x17, 0xb6, 0x82, 0xbb, 0x3a,
+ 0x1f, 0x9d, 0x1a, 0x9a, 0x35, 0xc0, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x31, 0x38,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30,
+ 0x32, 0x31, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x5e,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x14, 0x44,
+ 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61,
+ 0x74, 0x65, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x31, 0x19, 0x30, 0x17, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x20, 0x44, 0x56, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xcb, 0x98, 0xc9, 0x36, 0x3f,
+ 0xd2, 0x9c, 0xd8, 0x16, 0x07, 0xd4, 0x49, 0x63, 0xf9, 0x83, 0xb0, 0xe8,
+ 0x02, 0x2d, 0xcc, 0x5c, 0x5a, 0x74, 0x97, 0xa6, 0x13, 0xef, 0x13, 0x13,
+ 0xde, 0x05, 0x7c, 0xa7, 0xe6, 0xca, 0x00, 0x23, 0xda, 0x39, 0xf9, 0xef,
+ 0x13, 0xcf, 0x52, 0xc5, 0xaf, 0x9a, 0xe3, 0xca, 0xbe, 0xf3, 0x82, 0xd9,
+ 0x8b, 0x3d, 0xaa, 0xe1, 0xcc, 0xae, 0x88, 0x50, 0x66, 0xa3, 0x2d, 0xec,
+ 0x61, 0x14, 0x75, 0x49, 0xab, 0x0e, 0x24, 0xf1, 0xac, 0x44, 0x5b, 0x0b,
+ 0x28, 0xa2, 0x33, 0x20, 0x76, 0x1e, 0x06, 0x60, 0x6a, 0x67, 0x05, 0x71,
+ 0x8b, 0xba, 0x66, 0x62, 0x16, 0x7a, 0xb3, 0x6d, 0x0d, 0xc7, 0xd0, 0x94,
+ 0x40, 0xc6, 0x8c, 0x3d, 0x1e, 0x92, 0x0c, 0x62, 0x34, 0x0d, 0x44, 0x89,
+ 0xd5, 0xf7, 0x89, 0xfe, 0x29, 0xed, 0x18, 0x8f, 0xf6, 0x9b, 0x2b, 0x08,
+ 0xf7, 0x6a, 0xab, 0xd8, 0x48, 0x97, 0x5a, 0xf4, 0x9f, 0xed, 0x0c, 0x75,
+ 0x52, 0x22, 0xf7, 0xd5, 0x5e, 0x84, 0x00, 0x9f, 0xc0, 0x4a, 0x0d, 0x31,
+ 0x77, 0x4c, 0x64, 0xd0, 0x12, 0xe6, 0x0f, 0x3a, 0xf0, 0xa1, 0xc0, 0xd5,
+ 0x5c, 0x1d, 0xe7, 0x5f, 0x2d, 0xc2, 0xf7, 0xd6, 0x36, 0x18, 0xd9, 0x95,
+ 0x6e, 0x44, 0x4e, 0xc9, 0x58, 0x14, 0x4d, 0xb6, 0x8e, 0xbb, 0xcd, 0xde,
+ 0x62, 0x1e, 0xfa, 0x5b, 0xb5, 0xbd, 0x18, 0x2b, 0x98, 0xac, 0xac, 0x93,
+ 0x3f, 0x50, 0x5a, 0xf5, 0x14, 0x0b, 0xa2, 0xcf, 0xb6, 0xf3, 0x9e, 0x4f,
+ 0x5a, 0xcd, 0x5a, 0xc3, 0x36, 0x23, 0xda, 0x1a, 0xaf, 0xb0, 0x4d, 0xd6,
+ 0x4a, 0x22, 0x03, 0x8f, 0x43, 0x02, 0x19, 0xbd, 0xea, 0xac, 0xdd, 0xc4,
+ 0x7a, 0x35, 0x32, 0x14, 0xf1, 0x72, 0x2e, 0x08, 0x55, 0x40, 0x0c, 0xf4,
+ 0x07, 0x41, 0x41, 0xaf, 0x38, 0x37, 0x84, 0x29, 0x42, 0xb2, 0x55, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xfc, 0x30, 0x81, 0xf9, 0x30, 0x32,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x26,
+ 0x30, 0x24, 0x30, 0x22, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x16, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x34,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0,
+ 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, 0x06, 0x03,
+ 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32,
+ 0x2d, 0x31, 0x31, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xab, 0x44, 0xe4, 0x5d, 0xec, 0x83, 0xc7, 0xd9, 0xc0, 0x85,
+ 0x9f, 0xf7, 0xe1, 0xc6, 0x97, 0x90, 0xb0, 0x8c, 0x3f, 0x98, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7b,
+ 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31, 0x92, 0x1a, 0x6a,
+ 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x04, 0xba, 0xfb, 0xac, 0xbb, 0xfc, 0x4b, 0x54, 0x11,
+ 0xa3, 0x2d, 0x88, 0xb3, 0x3c, 0xbd, 0x00, 0x6d, 0x8a, 0x1a, 0xb6, 0x8d,
+ 0xc4, 0xc1, 0x83, 0xf8, 0xc7, 0x53, 0x2a, 0xc1, 0x32, 0x6e, 0x3a, 0x81,
+ 0xa1, 0x54, 0x7d, 0xda, 0x1a, 0x3f, 0x3a, 0x45, 0x4f, 0x36, 0xe7, 0x42,
+ 0xb0, 0x0a, 0x42, 0x85, 0x97, 0xa0, 0xac, 0xfb, 0xe5, 0x87, 0xa7, 0x83,
+ 0x4f, 0xe8, 0xb1, 0xb7, 0x9b, 0x58, 0x65, 0x6e, 0x26, 0x80, 0x0b, 0x92,
+ 0x4d, 0x47, 0x55, 0xb9, 0x61, 0x16, 0x51, 0x65, 0xe9, 0x2b, 0xf1, 0x68,
+ 0xd9, 0x58, 0xb8, 0x03, 0x81, 0xd1, 0xb7, 0x66, 0x1c, 0xd3, 0xbc, 0xc5,
+ 0xa6, 0x7b, 0x5f, 0x3e, 0xc5, 0x38, 0x46, 0x76, 0xe7, 0x75, 0xb4, 0xa0,
+ 0x0c, 0x4b, 0xce, 0xa2, 0xc2, 0xa9, 0xc1, 0xcc, 0x36, 0x73, 0x7b, 0xfb,
+ 0xb9, 0x24, 0x24, 0xa0, 0x5e, 0xa7, 0xf6, 0xfa, 0xbb, 0x0c, 0x28, 0x43,
+ 0x9e, 0x1d, 0xf0, 0x4e, 0xf0, 0x3f, 0xd8, 0x24, 0xb0, 0x21, 0xdc, 0x6d,
+ 0x2d, 0xee, 0xbf, 0x5a, 0x3b, 0xfa, 0x88, 0x9c, 0x74, 0x6c, 0xaf, 0x21,
+ 0xdd, 0x92, 0xec, 0xc3, 0x15, 0xef, 0x94, 0x75, 0x26, 0x46, 0xd6, 0xa6,
+ 0x3f, 0xbf, 0x66, 0x48, 0xaa, 0x1d, 0xef, 0xdd, 0x27, 0xe6, 0xb7, 0x51,
+ 0x89, 0x38, 0x7d, 0x13, 0x84, 0x0c, 0x40, 0xfc, 0xd0, 0xb5, 0xf1, 0xe0,
+ 0xdb, 0xf9, 0x4f, 0x2f, 0x40, 0x1c, 0xb4, 0x8e, 0x47, 0x22, 0x61, 0xb8,
+ 0x4c, 0x96, 0xde, 0xf0, 0x5f, 0x11, 0x7e, 0x4f, 0x11, 0xd9, 0xec, 0x50,
+ 0x47, 0x22, 0x0e, 0xc5, 0x1d, 0xe2, 0x64, 0x49, 0xe7, 0x68, 0x63, 0x45,
+ 0x3a, 0x8a, 0xd9, 0x71, 0xf4, 0x5e, 0xf1, 0x6e, 0xb7, 0x14, 0x4d, 0x3e,
+ 0x6f, 0x14, 0x1e, 0xdc, 0x52, 0xfe, 0xbc, 0xdf, 0x0c, 0xbd, 0x29, 0x3f,
+ 0x76, 0xfb, 0x11, 0x5f, 0x68, 0x68, 0x15,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 1b:09:3b:78:60:96:da:37:bb:a4:51:94:46:c8:96:78
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:cd:7d:1e:f7:c7:75:8d:48:e7:56:34:4c:00:90:75:a9:51:
+ a5:56:c1:6d:bc:fe:f5:53:22:e9:98:a2:ac:9a:7e:70:1e:b3:
+ 8e:3b:45:e3:86:95:31:da:6d:4c:fb:34:50:80:96:cd:24:f2:
+ 40:df:04:3f:e2:65:ce:34:22:61:15:ea:66:70:64:d2:f1:6e:
+ f3:ca:18:59:6a:41:46:7e:82:de:19:b0:70:31:56:69:0d:0c:
+ e6:1d:9d:71:58:dc:cc:de:62:f5:e1:7a:10:02:d8:7a:dc:3b:
+ fa:57:bd:c9:e9:8f:46:21:39:9f:51:65:4c:8e:3a:be:28:41:
+ 70:1d
+-----BEGIN CERTIFICATE-----
+MIIEkDCCA/mgAwIBAgIQGwk7eGCW2je7pFGURsiWeDANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggFbMIIBVzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
+KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
+j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
+L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEAo819HvfHdY1I51Y0
+TACQdalRpVbBbbz+9VMi6ZiirJp+cB6zjjtF44aVMdptTPs0UICWzSTyQN8EP+Jl
+zjQiYRXqZnBk0vFu88oYWWpBRn6C3hmwcDFWaQ0M5h2dcVjczN5i9eF6EALYetw7
++le9yemPRiE5n1FlTI46vihBcB0=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert42[] = {
+ 0x30, 0x82, 0x04, 0x90, 0x30, 0x82, 0x03, 0xf9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x1b, 0x09, 0x3b, 0x78, 0x60, 0x96, 0xda, 0x37, 0xbb,
+ 0xa4, 0x51, 0x94, 0x46, 0xc8, 0x96, 0x78, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x5b, 0x30, 0x82, 0x01, 0x57, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f,
+ 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09,
+ 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30,
+ 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
+ 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80,
+ 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0xa3, 0xcd, 0x7d, 0x1e, 0xf7, 0xc7, 0x75, 0x8d, 0x48, 0xe7, 0x56, 0x34,
+ 0x4c, 0x00, 0x90, 0x75, 0xa9, 0x51, 0xa5, 0x56, 0xc1, 0x6d, 0xbc, 0xfe,
+ 0xf5, 0x53, 0x22, 0xe9, 0x98, 0xa2, 0xac, 0x9a, 0x7e, 0x70, 0x1e, 0xb3,
+ 0x8e, 0x3b, 0x45, 0xe3, 0x86, 0x95, 0x31, 0xda, 0x6d, 0x4c, 0xfb, 0x34,
+ 0x50, 0x80, 0x96, 0xcd, 0x24, 0xf2, 0x40, 0xdf, 0x04, 0x3f, 0xe2, 0x65,
+ 0xce, 0x34, 0x22, 0x61, 0x15, 0xea, 0x66, 0x70, 0x64, 0xd2, 0xf1, 0x6e,
+ 0xf3, 0xca, 0x18, 0x59, 0x6a, 0x41, 0x46, 0x7e, 0x82, 0xde, 0x19, 0xb0,
+ 0x70, 0x31, 0x56, 0x69, 0x0d, 0x0c, 0xe6, 0x1d, 0x9d, 0x71, 0x58, 0xdc,
+ 0xcc, 0xde, 0x62, 0xf5, 0xe1, 0x7a, 0x10, 0x02, 0xd8, 0x7a, 0xdc, 0x3b,
+ 0xfa, 0x57, 0xbd, 0xc9, 0xe9, 0x8f, 0x46, 0x21, 0x39, 0x9f, 0x51, 0x65,
+ 0x4c, 0x8e, 0x3a, 0xbe, 0x28, 0x41, 0x70, 0x1d,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 3d:3a:05:26:09:b6:2e:e5:8c:36:29:38:63:54:e1:24
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Validity
+ Not Before: Dec 1 00:00:00 2006 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d0:40:8b:8b:72:e3:91:1b:f7:51:c1:1b:54:04:
+ 98:d3:a9:bf:c1:e6:8a:5d:3b:87:fb:bb:88:ce:0d:
+ e3:2f:3f:06:96:f0:a2:29:50:99:ae:db:3b:a1:57:
+ b0:74:51:71:cd:ed:42:91:4d:41:fe:a9:c8:d8:6a:
+ 86:77:44:bb:59:66:97:50:5e:b4:d4:2c:70:44:cf:
+ da:37:95:42:69:3c:30:c4:71:b3:52:f0:21:4d:a1:
+ d8:ba:39:7c:1c:9e:a3:24:9d:f2:83:16:98:aa:16:
+ 7c:43:9b:15:5b:b7:ae:34:91:fe:d4:62:26:18:46:
+ 9a:3f:eb:c1:f9:f1:90:57:eb:ac:7a:0d:8b:db:72:
+ 30:6a:66:d5:e0:46:a3:70:dc:68:d9:ff:04:48:89:
+ 77:de:b5:e9:fb:67:6d:41:e9:bc:39:bd:32:d9:62:
+ 02:f1:b1:a8:3d:6e:37:9c:e2:2f:e2:d3:a2:26:8b:
+ c6:b8:55:43:88:e1:23:3e:a5:d2:24:39:6a:47:ab:
+ 00:d4:a1:b3:a9:25:fe:0d:3f:a7:1d:ba:d3:51:c1:
+ 0b:a4:da:ac:38:ef:55:50:24:05:65:46:93:34:4f:
+ 2d:8d:ad:c6:d4:21:19:d2:8e:ca:05:61:71:07:73:
+ 47:e5:8a:19:12:bd:04:4d:ce:4e:9c:a5:48:ac:bb:
+ 26:f7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+
+ X509v3 Subject Key Identifier:
+ 0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/UTN-USERFirst-Hardware.crl
+
+ Full Name:
+ URI:http://crl.comodo.net/UTN-USERFirst-Hardware.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 9e:cf:0c:29:ff:99:5f:85:24:63:19:5a:68:f5:e4:46:ce:53:
+ 57:91:d6:25:fd:ea:ed:64:0f:73:da:aa:1c:25:d9:fb:ee:2c:
+ 03:87:9d:8d:a9:7a:63:5c:50:a8:f7:64:8c:96:2c:ba:0b:f2:
+ 7e:f9:74:29:c6:e5:4b:0b:50:30:af:c3:2e:3e:52:1a:fb:35:
+ 66:59:12:62:e0:68:7a:b0:42:01:a9:16:c3:8a:fa:45:19:7a:
+ f2:e0:2b:bf:78:87:49:6f:7e:ff:d4:7c:bd:e1:8b:a5:7b:43:
+ 9b:2c:42:cf:62:ef:63:1e:1c:85:d5:4c:b0:4a:91:4c:61:66:
+ 5b:26:7e:25:e1:c7:15:c0:54:4b:c9:66:16:29:63:df:71:ab:
+ b6:07:92:fa:f3:4f:f2:31:d6:32:d0:4d:35:db:5b:89:b8:08:
+ e4:68:de:d8:47:cb:d7:5e:e8:16:b2:94:21:9c:6a:5b:bf:b4:
+ 81:86:dd:c5:f2:a8:71:3e:dd:a7:4a:b5:fa:f8:6c:3b:34:9a:
+ 9b:58:7d:4d:d4:d3:5b:53:23:6b:49:38:16:a1:98:9f:84:5e:
+ ab:ae:3f:ae:ce:7f:c8:17:e4:32:ab:c4:d3:2f:9a:90:31:c2:
+ 92:53:96:ed:72:a7:fe:c4:da:39:29:51:68:ed:90:8d:97:8e:
+ fe:45:19:b7
+-----BEGIN CERTIFICATE-----
+MIIEmTCCA4GgAwIBAgIQPToFJgm2LuWMNik4Y1ThJDANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNMDYxMjAxMDAwMDAwWhcNMjAwNTMwMTA0ODM4WjCBgTELMAkG
+A1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMH
+U2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNP
+TU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANBAi4ty45Eb91HBG1QEmNOpv8Hmil07h/u7iM4N4y8/Bpbw
+oilQma7bO6FXsHRRcc3tQpFNQf6pyNhqhndEu1lml1BetNQscETP2jeVQmk8MMRx
+s1LwIU2h2Lo5fByeoySd8oMWmKoWfEObFVu3rjSR/tRiJhhGmj/rwfnxkFfrrHoN
+i9tyMGpm1eBGo3DcaNn/BEiJd9616ftnbUHpvDm9MtliAvGxqD1uN5ziL+LToiaL
+xrhVQ4jhIz6l0iQ5akerANShs6kl/g0/px2601HBC6TarDjvVVAkBWVGkzRPLY2t
+xtQhGdKOygVhcQdzR+WKGRK9BE3OTpylSKy7JvcCAwEAAaOB9DCB8TAfBgNVHSME
+GDAWgBShcl8mGyiYQ5VdBzfVhZadS9LDRTAdBgNVHQ4EFgQUC1jli8ZMFTekQKkw
+qSG+RzZaVv8wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0g
+BAowCDAGBgRVHSAAMHsGA1UdHwR0MHIwOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2Rv
+Y2EuY29tL1VUTi1VU0VSRmlyc3QtSGFyZHdhcmUuY3JsMDagNKAyhjBodHRwOi8v
+Y3JsLmNvbW9kby5uZXQvVVROLVVTRVJGaXJzdC1IYXJkd2FyZS5jcmwwDQYJKoZI
+hvcNAQEFBQADggEBAJ7PDCn/mV+FJGMZWmj15EbOU1eR1iX96u1kD3Paqhwl2fvu
+LAOHnY2pemNcUKj3ZIyWLLoL8n75dCnG5UsLUDCvwy4+Uhr7NWZZEmLgaHqwQgGp
+FsOK+kUZevLgK794h0lvfv/UfL3hi6V7Q5ssQs9i72MeHIXVTLBKkUxhZlsmfiXh
+xxXAVEvJZhYpY99xq7YHkvrzT/Ix1jLQTTXbW4m4CORo3thHy9de6BaylCGcalu/
+tIGG3cXyqHE+3adKtfr4bDs0mptYfU3U01tTI2tJOBahmJ+EXquuP67Of8gX5DKr
+xNMvmpAxwpJTlu1yp/7E2jkpUWjtkI2Xjv5FGbc=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert43[] = {
+ 0x30, 0x82, 0x04, 0x99, 0x30, 0x82, 0x03, 0x81, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x3d, 0x3a, 0x05, 0x26, 0x09, 0xb6, 0x2e, 0xe5, 0x8c,
+ 0x36, 0x29, 0x38, 0x63, 0x54, 0xe1, 0x24, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20,
+ 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54,
+ 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54,
+ 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d,
+ 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x36, 0x31, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34,
+ 0x38, 0x33, 0x38, 0x5a, 0x30, 0x81, 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65,
+ 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07,
+ 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f,
+ 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31,
+ 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f,
+ 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xd0, 0x40, 0x8b, 0x8b, 0x72, 0xe3, 0x91, 0x1b, 0xf7, 0x51, 0xc1,
+ 0x1b, 0x54, 0x04, 0x98, 0xd3, 0xa9, 0xbf, 0xc1, 0xe6, 0x8a, 0x5d, 0x3b,
+ 0x87, 0xfb, 0xbb, 0x88, 0xce, 0x0d, 0xe3, 0x2f, 0x3f, 0x06, 0x96, 0xf0,
+ 0xa2, 0x29, 0x50, 0x99, 0xae, 0xdb, 0x3b, 0xa1, 0x57, 0xb0, 0x74, 0x51,
+ 0x71, 0xcd, 0xed, 0x42, 0x91, 0x4d, 0x41, 0xfe, 0xa9, 0xc8, 0xd8, 0x6a,
+ 0x86, 0x77, 0x44, 0xbb, 0x59, 0x66, 0x97, 0x50, 0x5e, 0xb4, 0xd4, 0x2c,
+ 0x70, 0x44, 0xcf, 0xda, 0x37, 0x95, 0x42, 0x69, 0x3c, 0x30, 0xc4, 0x71,
+ 0xb3, 0x52, 0xf0, 0x21, 0x4d, 0xa1, 0xd8, 0xba, 0x39, 0x7c, 0x1c, 0x9e,
+ 0xa3, 0x24, 0x9d, 0xf2, 0x83, 0x16, 0x98, 0xaa, 0x16, 0x7c, 0x43, 0x9b,
+ 0x15, 0x5b, 0xb7, 0xae, 0x34, 0x91, 0xfe, 0xd4, 0x62, 0x26, 0x18, 0x46,
+ 0x9a, 0x3f, 0xeb, 0xc1, 0xf9, 0xf1, 0x90, 0x57, 0xeb, 0xac, 0x7a, 0x0d,
+ 0x8b, 0xdb, 0x72, 0x30, 0x6a, 0x66, 0xd5, 0xe0, 0x46, 0xa3, 0x70, 0xdc,
+ 0x68, 0xd9, 0xff, 0x04, 0x48, 0x89, 0x77, 0xde, 0xb5, 0xe9, 0xfb, 0x67,
+ 0x6d, 0x41, 0xe9, 0xbc, 0x39, 0xbd, 0x32, 0xd9, 0x62, 0x02, 0xf1, 0xb1,
+ 0xa8, 0x3d, 0x6e, 0x37, 0x9c, 0xe2, 0x2f, 0xe2, 0xd3, 0xa2, 0x26, 0x8b,
+ 0xc6, 0xb8, 0x55, 0x43, 0x88, 0xe1, 0x23, 0x3e, 0xa5, 0xd2, 0x24, 0x39,
+ 0x6a, 0x47, 0xab, 0x00, 0xd4, 0xa1, 0xb3, 0xa9, 0x25, 0xfe, 0x0d, 0x3f,
+ 0xa7, 0x1d, 0xba, 0xd3, 0x51, 0xc1, 0x0b, 0xa4, 0xda, 0xac, 0x38, 0xef,
+ 0x55, 0x50, 0x24, 0x05, 0x65, 0x46, 0x93, 0x34, 0x4f, 0x2d, 0x8d, 0xad,
+ 0xc6, 0xd4, 0x21, 0x19, 0xd2, 0x8e, 0xca, 0x05, 0x61, 0x71, 0x07, 0x73,
+ 0x47, 0xe5, 0x8a, 0x19, 0x12, 0xbd, 0x04, 0x4d, 0xce, 0x4e, 0x9c, 0xa5,
+ 0x48, 0xac, 0xbb, 0x26, 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81,
+ 0xf4, 0x30, 0x81, 0xf1, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98,
+ 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5, 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3,
+ 0x45, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x0b, 0x58, 0xe5, 0x8b, 0xc6, 0x4c, 0x15, 0x37, 0xa4, 0x40, 0xa9, 0x30,
+ 0xa9, 0x21, 0xbe, 0x47, 0x36, 0x5a, 0x56, 0xff, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30,
+ 0x38, 0xa0, 0x36, 0xa0, 0x34, 0x86, 0x32, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f,
+ 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x55,
+ 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72,
+ 0x64, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x36, 0xa0,
+ 0x34, 0xa0, 0x32, 0x86, 0x30, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46,
+ 0x69, 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72,
+ 0x65, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x9e, 0xcf, 0x0c, 0x29, 0xff, 0x99, 0x5f, 0x85, 0x24, 0x63, 0x19,
+ 0x5a, 0x68, 0xf5, 0xe4, 0x46, 0xce, 0x53, 0x57, 0x91, 0xd6, 0x25, 0xfd,
+ 0xea, 0xed, 0x64, 0x0f, 0x73, 0xda, 0xaa, 0x1c, 0x25, 0xd9, 0xfb, 0xee,
+ 0x2c, 0x03, 0x87, 0x9d, 0x8d, 0xa9, 0x7a, 0x63, 0x5c, 0x50, 0xa8, 0xf7,
+ 0x64, 0x8c, 0x96, 0x2c, 0xba, 0x0b, 0xf2, 0x7e, 0xf9, 0x74, 0x29, 0xc6,
+ 0xe5, 0x4b, 0x0b, 0x50, 0x30, 0xaf, 0xc3, 0x2e, 0x3e, 0x52, 0x1a, 0xfb,
+ 0x35, 0x66, 0x59, 0x12, 0x62, 0xe0, 0x68, 0x7a, 0xb0, 0x42, 0x01, 0xa9,
+ 0x16, 0xc3, 0x8a, 0xfa, 0x45, 0x19, 0x7a, 0xf2, 0xe0, 0x2b, 0xbf, 0x78,
+ 0x87, 0x49, 0x6f, 0x7e, 0xff, 0xd4, 0x7c, 0xbd, 0xe1, 0x8b, 0xa5, 0x7b,
+ 0x43, 0x9b, 0x2c, 0x42, 0xcf, 0x62, 0xef, 0x63, 0x1e, 0x1c, 0x85, 0xd5,
+ 0x4c, 0xb0, 0x4a, 0x91, 0x4c, 0x61, 0x66, 0x5b, 0x26, 0x7e, 0x25, 0xe1,
+ 0xc7, 0x15, 0xc0, 0x54, 0x4b, 0xc9, 0x66, 0x16, 0x29, 0x63, 0xdf, 0x71,
+ 0xab, 0xb6, 0x07, 0x92, 0xfa, 0xf3, 0x4f, 0xf2, 0x31, 0xd6, 0x32, 0xd0,
+ 0x4d, 0x35, 0xdb, 0x5b, 0x89, 0xb8, 0x08, 0xe4, 0x68, 0xde, 0xd8, 0x47,
+ 0xcb, 0xd7, 0x5e, 0xe8, 0x16, 0xb2, 0x94, 0x21, 0x9c, 0x6a, 0x5b, 0xbf,
+ 0xb4, 0x81, 0x86, 0xdd, 0xc5, 0xf2, 0xa8, 0x71, 0x3e, 0xdd, 0xa7, 0x4a,
+ 0xb5, 0xfa, 0xf8, 0x6c, 0x3b, 0x34, 0x9a, 0x9b, 0x58, 0x7d, 0x4d, 0xd4,
+ 0xd3, 0x5b, 0x53, 0x23, 0x6b, 0x49, 0x38, 0x16, 0xa1, 0x98, 0x9f, 0x84,
+ 0x5e, 0xab, 0xae, 0x3f, 0xae, 0xce, 0x7f, 0xc8, 0x17, 0xe4, 0x32, 0xab,
+ 0xc4, 0xd3, 0x2f, 0x9a, 0x90, 0x31, 0xc2, 0x92, 0x53, 0x96, 0xed, 0x72,
+ 0xa7, 0xfe, 0xc4, 0xda, 0x39, 0x29, 0x51, 0x68, 0xed, 0x90, 0x8d, 0x97,
+ 0x8e, 0xfe, 0x45, 0x19, 0xb7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1116155212 (0x42872d4c)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
+ Validity
+ Not Before: Jan 5 19:20:39 2007 GMT
+ Not After : Jan 5 19:50:39 2017 GMT
+ Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:95:b6:43:42:fa:c6:6d:2a:6f:48:df:94:4c:
+ 39:57:05:ee:c3:79:11:41:68:36:ed:ec:fe:9a:01:
+ 8f:a1:38:28:fc:f7:10:46:66:2e:4d:1e:1a:b1:1a:
+ 4e:c6:d1:c0:95:88:b0:c9:ff:31:8b:33:03:db:b7:
+ 83:7b:3e:20:84:5e:ed:b2:56:28:a7:f8:e0:b9:40:
+ 71:37:c5:cb:47:0e:97:2a:68:c0:22:95:62:15:db:
+ 47:d9:f5:d0:2b:ff:82:4b:c9:ad:3e:de:4c:db:90:
+ 80:50:3f:09:8a:84:00:ec:30:0a:3d:18:cd:fb:fd:
+ 2a:59:9a:23:95:17:2c:45:9e:1f:6e:43:79:6d:0c:
+ 5c:98:fe:48:a7:c5:23:47:5c:5e:fd:6e:e7:1e:b4:
+ f6:68:45:d1:86:83:5b:a2:8a:8d:b1:e3:29:80:fe:
+ 25:71:88:ad:be:bc:8f:ac:52:96:4b:aa:51:8d:e4:
+ 13:31:19:e8:4e:4d:9f:db:ac:b3:6a:d5:bc:39:54:
+ 71:ca:7a:7a:7f:90:dd:7d:1d:80:d9:81:bb:59:26:
+ c2:11:fe:e6:93:e2:f7:80:e4:65:fb:34:37:0e:29:
+ 80:70:4d:af:38:86:2e:9e:7f:57:af:9e:17:ae:eb:
+ 1c:cb:28:21:5f:b6:1c:d8:e7:a2:04:22:f9:d3:da:
+ d8:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/server1.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/CPS
+
+ X509v3 Subject Key Identifier:
+ 68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D
+ X509v3 Authority Key Identifier:
+ keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
+
+ 1.2.840.113533.7.65.0:
+ 0
+..V7.1....
+ Signature Algorithm: sha1WithRSAEncryption
+ 0c:b0:84:7c:2d:13:fe:9a:3d:bf:18:05:95:3d:20:48:a3:16:
+ 81:87:15:50:15:a4:88:8d:9f:60:d4:3a:6f:eb:2d:6e:3a:86:
+ a4:a9:d2:c1:9d:89:7a:08:1c:a4:2d:b3:47:8e:0f:64:4a:6f:
+ 66:03:83:3f:4f:34:94:36:aa:29:6d:8b:8d:02:22:2b:8c:cd:
+ 77:a5:70:95:86:91:d1:b6:bf:52:be:33:6a:6b:99:f9:6f:e1:
+ 12:be:04:cb:33:bf:f5:12:1a:4e:44:ba:5b:16:4d:30:b9:f3:
+ b4:74:ce:6e:f2:68:56:58:dd:d8:a1:fd:54:05:f4:23:91:85:
+ c9:f9
+-----BEGIN CERTIFICATE-----
+MIIEmzCCBASgAwIBAgIEQoctTDANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
+ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
+ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNzAx
+MDUxOTIwMzlaFw0xNzAxMDUxOTUwMzlaMIGwMQswCQYDVQQGEwJVUzEWMBQGA1UE
+ChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBp
+cyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBF
+bnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2lbZD
+QvrGbSpvSN+UTDlXBe7DeRFBaDbt7P6aAY+hOCj89xBGZi5NHhqxGk7G0cCViLDJ
+/zGLMwPbt4N7PiCEXu2yViin+OC5QHE3xctHDpcqaMAilWIV20fZ9dAr/4JLya0+
+3kzbkIBQPwmKhADsMAo9GM37/SpZmiOVFyxFnh9uQ3ltDFyY/kinxSNHXF79buce
+tPZoRdGGg1uiio2x4ymA/iVxiK2+vI+sUpZLqlGN5BMxGehOTZ/brLNq1bw5VHHK
+enp/kN19HYDZgbtZJsIR/uaT4veA5GX7NDcOKYBwTa84hi6ef1evnheu6xzLKCFf
+thzY56IEIvnT2tjLAgMBAAGjggEnMIIBIzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
+AQH/BAUwAwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9v
+Y3NwLmVudHJ1c3QubmV0MDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50
+cnVzdC5uZXQvc2VydmVyMS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYB
+BQUHAgEWGmh0dHA6Ly93d3cuZW50cnVzdC5uZXQvQ1BTMB0GA1UdDgQWBBRokORn
+pKZTgMeGZqTx90tD+4S9bTAfBgNVHSMEGDAWgBTwF2ITVT2z/woAa/tQhJfz7WLQ
+GjAZBgkqhkiG9n0HQQAEDDAKGwRWNy4xAwIAgTANBgkqhkiG9w0BAQUFAAOBgQAM
+sIR8LRP+mj2/GAWVPSBIoxaBhxVQFaSIjZ9g1Dpv6y1uOoakqdLBnYl6CBykLbNH
+jg9kSm9mA4M/TzSUNqopbYuNAiIrjM13pXCVhpHRtr9SvjNqa5n5b+ESvgTLM7/1
+EhpORLpbFk0wufO0dM5u8mhWWN3Yof1UBfQjkYXJ+Q==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert44[] = {
+ 0x30, 0x82, 0x04, 0x9b, 0x30, 0x82, 0x04, 0x04, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x42, 0x87, 0x2d, 0x4c, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xc3, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e,
+ 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c,
+ 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x31, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x31,
+ 0x30, 0x35, 0x31, 0x39, 0x32, 0x30, 0x33, 0x39, 0x5a, 0x17, 0x0d, 0x31,
+ 0x37, 0x30, 0x31, 0x30, 0x35, 0x31, 0x39, 0x35, 0x30, 0x33, 0x39, 0x5a,
+ 0x30, 0x81, 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69,
+ 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
+ 0x6e, 0x63, 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45,
+ 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, 0x95, 0xb6, 0x43,
+ 0x42, 0xfa, 0xc6, 0x6d, 0x2a, 0x6f, 0x48, 0xdf, 0x94, 0x4c, 0x39, 0x57,
+ 0x05, 0xee, 0xc3, 0x79, 0x11, 0x41, 0x68, 0x36, 0xed, 0xec, 0xfe, 0x9a,
+ 0x01, 0x8f, 0xa1, 0x38, 0x28, 0xfc, 0xf7, 0x10, 0x46, 0x66, 0x2e, 0x4d,
+ 0x1e, 0x1a, 0xb1, 0x1a, 0x4e, 0xc6, 0xd1, 0xc0, 0x95, 0x88, 0xb0, 0xc9,
+ 0xff, 0x31, 0x8b, 0x33, 0x03, 0xdb, 0xb7, 0x83, 0x7b, 0x3e, 0x20, 0x84,
+ 0x5e, 0xed, 0xb2, 0x56, 0x28, 0xa7, 0xf8, 0xe0, 0xb9, 0x40, 0x71, 0x37,
+ 0xc5, 0xcb, 0x47, 0x0e, 0x97, 0x2a, 0x68, 0xc0, 0x22, 0x95, 0x62, 0x15,
+ 0xdb, 0x47, 0xd9, 0xf5, 0xd0, 0x2b, 0xff, 0x82, 0x4b, 0xc9, 0xad, 0x3e,
+ 0xde, 0x4c, 0xdb, 0x90, 0x80, 0x50, 0x3f, 0x09, 0x8a, 0x84, 0x00, 0xec,
+ 0x30, 0x0a, 0x3d, 0x18, 0xcd, 0xfb, 0xfd, 0x2a, 0x59, 0x9a, 0x23, 0x95,
+ 0x17, 0x2c, 0x45, 0x9e, 0x1f, 0x6e, 0x43, 0x79, 0x6d, 0x0c, 0x5c, 0x98,
+ 0xfe, 0x48, 0xa7, 0xc5, 0x23, 0x47, 0x5c, 0x5e, 0xfd, 0x6e, 0xe7, 0x1e,
+ 0xb4, 0xf6, 0x68, 0x45, 0xd1, 0x86, 0x83, 0x5b, 0xa2, 0x8a, 0x8d, 0xb1,
+ 0xe3, 0x29, 0x80, 0xfe, 0x25, 0x71, 0x88, 0xad, 0xbe, 0xbc, 0x8f, 0xac,
+ 0x52, 0x96, 0x4b, 0xaa, 0x51, 0x8d, 0xe4, 0x13, 0x31, 0x19, 0xe8, 0x4e,
+ 0x4d, 0x9f, 0xdb, 0xac, 0xb3, 0x6a, 0xd5, 0xbc, 0x39, 0x54, 0x71, 0xca,
+ 0x7a, 0x7a, 0x7f, 0x90, 0xdd, 0x7d, 0x1d, 0x80, 0xd9, 0x81, 0xbb, 0x59,
+ 0x26, 0xc2, 0x11, 0xfe, 0xe6, 0x93, 0xe2, 0xf7, 0x80, 0xe4, 0x65, 0xfb,
+ 0x34, 0x37, 0x0e, 0x29, 0x80, 0x70, 0x4d, 0xaf, 0x38, 0x86, 0x2e, 0x9e,
+ 0x7f, 0x57, 0xaf, 0x9e, 0x17, 0xae, 0xeb, 0x1c, 0xcb, 0x28, 0x21, 0x5f,
+ 0xb6, 0x1c, 0xd8, 0xe7, 0xa2, 0x04, 0x22, 0xf9, 0xd3, 0xda, 0xd8, 0xcb,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x27, 0x30, 0x82, 0x01,
+ 0x23, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x33,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27,
+ 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c,
+ 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x68, 0x90, 0xe4, 0x67,
+ 0xa4, 0xa6, 0x53, 0x80, 0xc7, 0x86, 0x66, 0xa4, 0xf1, 0xf7, 0x4b, 0x43,
+ 0xfb, 0x84, 0xbd, 0x6d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xf0, 0x17, 0x62, 0x13, 0x55, 0x3d, 0xb3,
+ 0xff, 0x0a, 0x00, 0x6b, 0xfb, 0x50, 0x84, 0x97, 0xf3, 0xed, 0x62, 0xd0,
+ 0x1a, 0x30, 0x19, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf6, 0x7d, 0x07,
+ 0x41, 0x00, 0x04, 0x0c, 0x30, 0x0a, 0x1b, 0x04, 0x56, 0x37, 0x2e, 0x31,
+ 0x03, 0x02, 0x00, 0x81, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x0c,
+ 0xb0, 0x84, 0x7c, 0x2d, 0x13, 0xfe, 0x9a, 0x3d, 0xbf, 0x18, 0x05, 0x95,
+ 0x3d, 0x20, 0x48, 0xa3, 0x16, 0x81, 0x87, 0x15, 0x50, 0x15, 0xa4, 0x88,
+ 0x8d, 0x9f, 0x60, 0xd4, 0x3a, 0x6f, 0xeb, 0x2d, 0x6e, 0x3a, 0x86, 0xa4,
+ 0xa9, 0xd2, 0xc1, 0x9d, 0x89, 0x7a, 0x08, 0x1c, 0xa4, 0x2d, 0xb3, 0x47,
+ 0x8e, 0x0f, 0x64, 0x4a, 0x6f, 0x66, 0x03, 0x83, 0x3f, 0x4f, 0x34, 0x94,
+ 0x36, 0xaa, 0x29, 0x6d, 0x8b, 0x8d, 0x02, 0x22, 0x2b, 0x8c, 0xcd, 0x77,
+ 0xa5, 0x70, 0x95, 0x86, 0x91, 0xd1, 0xb6, 0xbf, 0x52, 0xbe, 0x33, 0x6a,
+ 0x6b, 0x99, 0xf9, 0x6f, 0xe1, 0x12, 0xbe, 0x04, 0xcb, 0x33, 0xbf, 0xf5,
+ 0x12, 0x1a, 0x4e, 0x44, 0xba, 0x5b, 0x16, 0x4d, 0x30, 0xb9, 0xf3, 0xb4,
+ 0x74, 0xce, 0x6e, 0xf2, 0x68, 0x56, 0x58, 0xdd, 0xd8, 0xa1, 0xfd, 0x54,
+ 0x05, 0xf4, 0x23, 0x91, 0x85, 0xc9, 0xf9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 69:48:a2:6b:20:1a:a4:21:e8:98:b1:c4:92:c7:c5:8e
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Primary Certification Authority
+ Validity
+ Not Before: Nov 29 00:00:00 2006 GMT
+ Not After : Nov 28 23:59:59 2016 GMT
+ Subject: C=US, O=GeoTrust Inc, OU=See www.geotrust.com/resources/cps (c)06, CN=GeoTrust Extended Validation SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c2:ef:ed:ec:0b:2d:72:8a:74:68:73:36:6e:10:
+ a8:7e:48:7f:58:bb:78:67:dc:ed:7b:d6:7c:a6:4f:
+ 3d:9f:5d:6f:0a:d0:a0:b4:65:fd:be:d3:bf:77:b6:
+ 94:a5:82:ff:81:95:9d:28:10:06:ec:c2:b4:90:aa:
+ 5a:51:4c:73:d9:6b:74:a8:35:49:f4:a6:36:80:d4:
+ 5c:75:9e:9e:7c:01:c7:8c:9c:81:c8:86:83:1a:8e:
+ bd:00:13:a2:dc:ff:a5:78:aa:77:2c:21:62:08:97:
+ 3f:80:bd:f7:67:a4:79:db:7d:d7:3e:6e:b6:d5:96:
+ b9:98:86:4e:7a:67:e2:93:af:da:a5:d1:27:fb:f1:
+ 66:c3:2a:03:0c:b6:c7:82:1d:39:fb:3c:de:29:36:
+ 71:5d:e1:a8:b5:16:39:7c:1b:ff:7b:86:f5:80:92:
+ 95:e0:03:3b:aa:44:fb:f4:00:b5:e5:a9:e2:fa:18:
+ f9:84:9a:c1:e1:f6:2e:0e:81:8b:14:29:34:ff:1f:
+ 55:60:88:a4:99:c6:6f:6f:04:39:3a:75:a4:a7:1e:
+ 58:df:b7:ff:c9:9a:1d:70:db:83:a0:d3:83:1b:2d:
+ 6d:2a:90:5b:a3:63:91:73:b5:ff:9d:82:7a:41:f3:
+ d3:aa:2f:0b:0d:9f:cf:44:c0:5e:c7:a1:6b:cf:ae:
+ 94:db
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 28:C4:EB:8F:F1:5F:79:90:A3:2B:55:C3:56:4E:7D:6B:53:72:2C:18
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.geotrust.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.geotrust.com/resources/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://EVSecure-crl.geotrust.com/GeoTrustPCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:2C:D5:50:41:97:15:8B:F0:8F:36:61:5B:4A:FB:6B:D9:99:C9:33:92
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 02:60:a3:16:12:9d:d8:1c:19:e4:5a:37:6c:ff:32:98:37:46:
+ 4f:bc:81:7c:80:c3:ca:89:2a:00:fe:5e:3e:ec:ba:8c:2b:1f:
+ ab:95:6b:91:94:21:a0:60:1f:02:06:fa:cf:17:6d:f8:95:ab:
+ cd:78:23:14:96:c0:9d:1f:1b:eb:50:e1:65:42:8a:d2:b3:c9:
+ ad:80:c3:67:cf:b4:58:1b:d5:04:e4:58:fe:34:45:e0:fb:a4:
+ 84:22:8b:e9:e2:37:4c:98:f1:0b:ff:a4:89:53:d1:4d:c0:68:
+ 48:d7:59:87:1a:3b:7d:f5:d0:f9:23:72:ca:60:fd:c3:22:15:
+ f0:9a:95:58:6f:7c:24:93:ec:a5:12:3d:b4:1b:01:e8:ee:69:
+ ed:41:6b:52:cb:9a:b7:5c:15:d1:bd:06:40:7a:e0:0c:97:cb:
+ 60:e7:82:5f:6a:5f:de:49:84:56:6a:af:7c:b0:4b:ad:8c:4f:
+ 0f:79:a0:cc:11:3c:25:e7:46:bf:7a:d0:2f:88:c8:bf:eb:94:
+ 0b:6a:75:33:7f:73:00:b8:12:70:23:5e:55:7f:45:5b:1e:10:
+ b1:02:68:d8:27:40:cf:24:09:e2:65:74:ce:89:44:8d:7b:28:
+ 90:68:ae:ac:c2:38:c8:56:0d:33:88:28:7f:54:fc:3c:3c:50:
+ 09:93:3d:38
+-----BEGIN CERTIFICATE-----
+MIIEnDCCA4SgAwIBAgIQaUiiayAapCHomLHEksfFjjANBgkqhkiG9w0BAQUFADBY
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
+R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
+MjkwMDAwMDBaFw0xNjExMjgyMzU5NTlaMIGFMQswCQYDVQQGEwJVUzEVMBMGA1UE
+ChMMR2VvVHJ1c3QgSW5jMTEwLwYDVQQLEyhTZWUgd3d3Lmdlb3RydXN0LmNvbS9y
+ZXNvdXJjZXMvY3BzIChjKTA2MSwwKgYDVQQDEyNHZW9UcnVzdCBFeHRlbmRlZCBW
+YWxpZGF0aW9uIFNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AMLv7ewLLXKKdGhzNm4QqH5If1i7eGfc7XvWfKZPPZ9dbwrQoLRl/b7Tv3e2lKWC
+/4GVnSgQBuzCtJCqWlFMc9lrdKg1SfSmNoDUXHWennwBx4ycgciGgxqOvQATotz/
+pXiqdywhYgiXP4C992ekedt91z5uttWWuZiGTnpn4pOv2qXRJ/vxZsMqAwy2x4Id
+Ofs83ik2cV3hqLUWOXwb/3uG9YCSleADO6pE+/QAteWp4voY+YSaweH2Lg6BixQp
+NP8fVWCIpJnGb28EOTp1pKceWN+3/8maHXDbg6DTgxstbSqQW6NjkXO1/52CekHz
+06ovCw2fz0TAXseha8+ulNsCAwEAAaOCATIwggEuMB0GA1UdDgQWBBQoxOuP8V95
+kKMrVcNWTn1rU3IsGDA9BggrBgEFBQcBAQQxMC8wLQYIKwYBBQUHMAGGIWh0dHA6
+Ly9FVlNlY3VyZS1vY3NwLmdlb3RydXN0LmNvbTASBgNVHRMBAf8ECDAGAQH/AgEA
+MEYGA1UdIAQ/MD0wOwYEVR0gADAzMDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdl
+b3RydXN0LmNvbS9yZXNvdXJjZXMvY3BzMEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHA6
+Ly9FVlNlY3VyZS1jcmwuZ2VvdHJ1c3QuY29tL0dlb1RydXN0UENBLmNybDAOBgNV
+HQ8BAf8EBAMCAQYwHwYDVR0jBBgwFoAULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
+KoZIhvcNAQEFBQADggEBAAJgoxYSndgcGeRaN2z/Mpg3Rk+8gXyAw8qJKgD+Xj7s
+uowrH6uVa5GUIaBgHwIG+s8XbfiVq814IxSWwJ0fG+tQ4WVCitKzya2Aw2fPtFgb
+1QTkWP40ReD7pIQii+niN0yY8Qv/pIlT0U3AaEjXWYcaO3310Pkjcspg/cMiFfCa
+lVhvfCST7KUSPbQbAejuae1Ba1LLmrdcFdG9BkB64AyXy2Dngl9qX95JhFZqr3yw
+S62MTw95oMwRPCXnRr960C+IyL/rlAtqdTN/cwC4EnAjXlV/RVseELECaNgnQM8k
+CeJldM6JRI17KJBorqzCOMhWDTOIKH9U/Dw8UAmTPTg=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert45[] = {
+ 0x30, 0x82, 0x04, 0x9c, 0x30, 0x82, 0x03, 0x84, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x69, 0x48, 0xa2, 0x6b, 0x20, 0x1a, 0xa4, 0x21, 0xe8,
+ 0x98, 0xb1, 0xc4, 0x92, 0xc7, 0xc5, 0x8e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x58,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28,
+ 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x50, 0x72, 0x69,
+ 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31,
+ 0x32, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31,
+ 0x36, 0x31, 0x31, 0x32, 0x38, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ 0x30, 0x81, 0x85, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0c, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x49, 0x6e, 0x63, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x28, 0x53, 0x65, 0x65, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65,
+ 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73,
+ 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x23, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xc2, 0xef, 0xed, 0xec, 0x0b, 0x2d, 0x72, 0x8a, 0x74, 0x68, 0x73,
+ 0x36, 0x6e, 0x10, 0xa8, 0x7e, 0x48, 0x7f, 0x58, 0xbb, 0x78, 0x67, 0xdc,
+ 0xed, 0x7b, 0xd6, 0x7c, 0xa6, 0x4f, 0x3d, 0x9f, 0x5d, 0x6f, 0x0a, 0xd0,
+ 0xa0, 0xb4, 0x65, 0xfd, 0xbe, 0xd3, 0xbf, 0x77, 0xb6, 0x94, 0xa5, 0x82,
+ 0xff, 0x81, 0x95, 0x9d, 0x28, 0x10, 0x06, 0xec, 0xc2, 0xb4, 0x90, 0xaa,
+ 0x5a, 0x51, 0x4c, 0x73, 0xd9, 0x6b, 0x74, 0xa8, 0x35, 0x49, 0xf4, 0xa6,
+ 0x36, 0x80, 0xd4, 0x5c, 0x75, 0x9e, 0x9e, 0x7c, 0x01, 0xc7, 0x8c, 0x9c,
+ 0x81, 0xc8, 0x86, 0x83, 0x1a, 0x8e, 0xbd, 0x00, 0x13, 0xa2, 0xdc, 0xff,
+ 0xa5, 0x78, 0xaa, 0x77, 0x2c, 0x21, 0x62, 0x08, 0x97, 0x3f, 0x80, 0xbd,
+ 0xf7, 0x67, 0xa4, 0x79, 0xdb, 0x7d, 0xd7, 0x3e, 0x6e, 0xb6, 0xd5, 0x96,
+ 0xb9, 0x98, 0x86, 0x4e, 0x7a, 0x67, 0xe2, 0x93, 0xaf, 0xda, 0xa5, 0xd1,
+ 0x27, 0xfb, 0xf1, 0x66, 0xc3, 0x2a, 0x03, 0x0c, 0xb6, 0xc7, 0x82, 0x1d,
+ 0x39, 0xfb, 0x3c, 0xde, 0x29, 0x36, 0x71, 0x5d, 0xe1, 0xa8, 0xb5, 0x16,
+ 0x39, 0x7c, 0x1b, 0xff, 0x7b, 0x86, 0xf5, 0x80, 0x92, 0x95, 0xe0, 0x03,
+ 0x3b, 0xaa, 0x44, 0xfb, 0xf4, 0x00, 0xb5, 0xe5, 0xa9, 0xe2, 0xfa, 0x18,
+ 0xf9, 0x84, 0x9a, 0xc1, 0xe1, 0xf6, 0x2e, 0x0e, 0x81, 0x8b, 0x14, 0x29,
+ 0x34, 0xff, 0x1f, 0x55, 0x60, 0x88, 0xa4, 0x99, 0xc6, 0x6f, 0x6f, 0x04,
+ 0x39, 0x3a, 0x75, 0xa4, 0xa7, 0x1e, 0x58, 0xdf, 0xb7, 0xff, 0xc9, 0x9a,
+ 0x1d, 0x70, 0xdb, 0x83, 0xa0, 0xd3, 0x83, 0x1b, 0x2d, 0x6d, 0x2a, 0x90,
+ 0x5b, 0xa3, 0x63, 0x91, 0x73, 0xb5, 0xff, 0x9d, 0x82, 0x7a, 0x41, 0xf3,
+ 0xd3, 0xaa, 0x2f, 0x0b, 0x0d, 0x9f, 0xcf, 0x44, 0xc0, 0x5e, 0xc7, 0xa1,
+ 0x6b, 0xcf, 0xae, 0x94, 0xdb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x32, 0x30, 0x82, 0x01, 0x2e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x28, 0xc4, 0xeb, 0x8f, 0xf1, 0x5f, 0x79,
+ 0x90, 0xa3, 0x2b, 0x55, 0xc3, 0x56, 0x4e, 0x7d, 0x6b, 0x53, 0x72, 0x2c,
+ 0x18, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3f, 0x30, 0x3d, 0x30,
+ 0x3b, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x33, 0x30, 0x31, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x67, 0x65,
+ 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2f, 0x63, 0x70, 0x73,
+ 0x30, 0x41, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3a, 0x30, 0x38, 0x30,
+ 0x36, 0xa0, 0x34, 0xa0, 0x32, 0x86, 0x30, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x63,
+ 0x72, 0x6c, 0x2e, 0x67, 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x47, 0x65, 0x6f, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x50, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x2c, 0xd5, 0x50, 0x41, 0x97, 0x15, 0x8b, 0xf0, 0x8f, 0x36, 0x61, 0x5b,
+ 0x4a, 0xfb, 0x6b, 0xd9, 0x99, 0xc9, 0x33, 0x92, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x02, 0x60, 0xa3, 0x16, 0x12, 0x9d, 0xd8, 0x1c,
+ 0x19, 0xe4, 0x5a, 0x37, 0x6c, 0xff, 0x32, 0x98, 0x37, 0x46, 0x4f, 0xbc,
+ 0x81, 0x7c, 0x80, 0xc3, 0xca, 0x89, 0x2a, 0x00, 0xfe, 0x5e, 0x3e, 0xec,
+ 0xba, 0x8c, 0x2b, 0x1f, 0xab, 0x95, 0x6b, 0x91, 0x94, 0x21, 0xa0, 0x60,
+ 0x1f, 0x02, 0x06, 0xfa, 0xcf, 0x17, 0x6d, 0xf8, 0x95, 0xab, 0xcd, 0x78,
+ 0x23, 0x14, 0x96, 0xc0, 0x9d, 0x1f, 0x1b, 0xeb, 0x50, 0xe1, 0x65, 0x42,
+ 0x8a, 0xd2, 0xb3, 0xc9, 0xad, 0x80, 0xc3, 0x67, 0xcf, 0xb4, 0x58, 0x1b,
+ 0xd5, 0x04, 0xe4, 0x58, 0xfe, 0x34, 0x45, 0xe0, 0xfb, 0xa4, 0x84, 0x22,
+ 0x8b, 0xe9, 0xe2, 0x37, 0x4c, 0x98, 0xf1, 0x0b, 0xff, 0xa4, 0x89, 0x53,
+ 0xd1, 0x4d, 0xc0, 0x68, 0x48, 0xd7, 0x59, 0x87, 0x1a, 0x3b, 0x7d, 0xf5,
+ 0xd0, 0xf9, 0x23, 0x72, 0xca, 0x60, 0xfd, 0xc3, 0x22, 0x15, 0xf0, 0x9a,
+ 0x95, 0x58, 0x6f, 0x7c, 0x24, 0x93, 0xec, 0xa5, 0x12, 0x3d, 0xb4, 0x1b,
+ 0x01, 0xe8, 0xee, 0x69, 0xed, 0x41, 0x6b, 0x52, 0xcb, 0x9a, 0xb7, 0x5c,
+ 0x15, 0xd1, 0xbd, 0x06, 0x40, 0x7a, 0xe0, 0x0c, 0x97, 0xcb, 0x60, 0xe7,
+ 0x82, 0x5f, 0x6a, 0x5f, 0xde, 0x49, 0x84, 0x56, 0x6a, 0xaf, 0x7c, 0xb0,
+ 0x4b, 0xad, 0x8c, 0x4f, 0x0f, 0x79, 0xa0, 0xcc, 0x11, 0x3c, 0x25, 0xe7,
+ 0x46, 0xbf, 0x7a, 0xd0, 0x2f, 0x88, 0xc8, 0xbf, 0xeb, 0x94, 0x0b, 0x6a,
+ 0x75, 0x33, 0x7f, 0x73, 0x00, 0xb8, 0x12, 0x70, 0x23, 0x5e, 0x55, 0x7f,
+ 0x45, 0x5b, 0x1e, 0x10, 0xb1, 0x02, 0x68, 0xd8, 0x27, 0x40, 0xcf, 0x24,
+ 0x09, 0xe2, 0x65, 0x74, 0xce, 0x89, 0x44, 0x8d, 0x7b, 0x28, 0x90, 0x68,
+ 0xae, 0xac, 0xc2, 0x38, 0xc8, 0x56, 0x0d, 0x33, 0x88, 0x28, 0x7f, 0x54,
+ 0xfc, 0x3c, 0x3c, 0x50, 0x09, 0x93, 0x3d, 0x38,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1184796954 (0x469e911a)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
+ Validity
+ Not Before: Mar 23 15:18:27 2009 GMT
+ Not After : Mar 23 15:48:27 2019 GMT
+ Subject: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048)
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ad:4d:4b:a9:12:86:b2:ea:a3:20:07:15:16:64:
+ 2a:2b:4b:d1:bf:0b:4a:4d:8e:ed:80:76:a5:67:b7:
+ 78:40:c0:73:42:c8:68:c0:db:53:2b:dd:5e:b8:76:
+ 98:35:93:8b:1a:9d:7c:13:3a:0e:1f:5b:b7:1e:cf:
+ e5:24:14:1e:b1:81:a9:8d:7d:b8:cc:6b:4b:03:f1:
+ 02:0c:dc:ab:a5:40:24:00:7f:74:94:a1:9d:08:29:
+ b3:88:0b:f5:87:77:9d:55:cd:e4:c3:7e:d7:6a:64:
+ ab:85:14:86:95:5b:97:32:50:6f:3d:c8:ba:66:0c:
+ e3:fc:bd:b8:49:c1:76:89:49:19:fd:c0:a8:bd:89:
+ a3:67:2f:c6:9f:bc:71:19:60:b8:2d:e9:2c:c9:90:
+ 76:66:7b:94:e2:af:78:d6:65:53:5d:3c:d6:9c:b2:
+ cf:29:03:f9:2f:a4:50:b2:d4:48:ce:05:32:55:8a:
+ fd:b2:64:4c:0e:e4:98:07:75:db:7f:df:b9:08:55:
+ 60:85:30:29:f9:7b:48:a4:69:86:e3:35:3f:1e:86:
+ 5d:7a:7a:15:bd:ef:00:8e:15:22:54:17:00:90:26:
+ 93:bc:0e:49:68:91:bf:f8:47:d3:9d:95:42:c1:0e:
+ 4d:df:6f:26:cf:c3:18:21:62:66:43:70:d6:d5:c0:
+ 07:e1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/server1.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/CPS
+
+ X509v3 Subject Key Identifier:
+ 55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70
+ X509v3 Authority Key Identifier:
+ keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
+
+ 1.2.840.113533.7.65.0:
+ 0
+..V7.1....
+ Signature Algorithm: sha1WithRSAEncryption
+ 8f:65:a2:30:8e:26:ab:8a:ec:35:16:98:e9:03:f0:8d:17:5f:
+ bc:4c:6c:02:f6:74:52:e0:c2:c6:1f:ce:f2:a6:11:0c:a8:b1:
+ 0e:4d:84:8b:71:36:ef:b3:35:45:f3:c1:f8:96:c5:8b:55:a4:
+ cc:6b:83:16:20:32:da:be:fb:af:9b:b7:9f:e1:7e:84:9f:9e:
+ 3c:50:a7:3f:5c:c2:be:8b:86:b8:08:92:ee:f8:42:2b:0d:13:
+ e3:76:85:48:0a:4a:bf:d0:a5:3b:0a:b0:54:b8:6d:e3:08:f9:
+ 34:8d:0b:8e:8b:12:cc:17:1a:33:87:95:c8:9e:0a:dc:50:53:
+ 17:7b
+-----BEGIN CERTIFICATE-----
+MIIEnzCCBAigAwIBAgIERp6RGjANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
+VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
+ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
+ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wOTAz
+MjMxNTE4MjdaFw0xOTAzMjMxNTQ4MjdaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5l
+dDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkg
+cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5u
+ZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+rU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL
+Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3ed
+Vc3kw37XamSrhRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4
+LeksyZB2ZnuU4q941mVTXTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5
+CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N
+328mz8MYIWJmQ3DW1cAH4QIDAQABo4IBJzCCASMwDgYDVR0PAQH/BAQDAgEGMA8G
+A1UdEwEB/wQFMAMBAf8wMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRw
+Oi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
+LmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMDsGA1UdIAQ0MDIwMAYEVR0gADAoMCYG
+CCsGAQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NQUzAdBgNVHQ4EFgQU
+VeSB0RGAvtiJuQijMfmhJAkWuXAwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX
+8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEFBQAD
+gYEAj2WiMI4mq4rsNRaY6QPwjRdfvExsAvZ0UuDCxh/O8qYRDKixDk2Ei3E277M1
+RfPB+JbFi1WkzGuDFiAy2r77r5u3n+F+hJ+ePFCnP1zCvouGuAiS7vhCKw0T43aF
+SApKv9ClOwqwVLht4wj5NI0LjosSzBcaM4eVyJ4K3FBTF3s=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert46[] = {
+ 0x30, 0x82, 0x04, 0x9f, 0x30, 0x82, 0x04, 0x08, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x46, 0x9e, 0x91, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xc3, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28,
+ 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e,
+ 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c,
+ 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d,
+ 0x69, 0x74, 0x65, 0x64, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x31, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x33,
+ 0x32, 0x33, 0x31, 0x35, 0x31, 0x38, 0x32, 0x37, 0x5a, 0x17, 0x0d, 0x31,
+ 0x39, 0x30, 0x33, 0x32, 0x33, 0x31, 0x35, 0x34, 0x38, 0x32, 0x37, 0x5a,
+ 0x30, 0x81, 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0b, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x31, 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38,
+ 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20,
+ 0x72, 0x65, 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73,
+ 0x20, 0x6c, 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39,
+ 0x39, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33,
+ 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72,
+ 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30,
+ 0x34, 0x38, 0x29, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xad, 0x4d, 0x4b, 0xa9, 0x12, 0x86, 0xb2, 0xea, 0xa3, 0x20, 0x07, 0x15,
+ 0x16, 0x64, 0x2a, 0x2b, 0x4b, 0xd1, 0xbf, 0x0b, 0x4a, 0x4d, 0x8e, 0xed,
+ 0x80, 0x76, 0xa5, 0x67, 0xb7, 0x78, 0x40, 0xc0, 0x73, 0x42, 0xc8, 0x68,
+ 0xc0, 0xdb, 0x53, 0x2b, 0xdd, 0x5e, 0xb8, 0x76, 0x98, 0x35, 0x93, 0x8b,
+ 0x1a, 0x9d, 0x7c, 0x13, 0x3a, 0x0e, 0x1f, 0x5b, 0xb7, 0x1e, 0xcf, 0xe5,
+ 0x24, 0x14, 0x1e, 0xb1, 0x81, 0xa9, 0x8d, 0x7d, 0xb8, 0xcc, 0x6b, 0x4b,
+ 0x03, 0xf1, 0x02, 0x0c, 0xdc, 0xab, 0xa5, 0x40, 0x24, 0x00, 0x7f, 0x74,
+ 0x94, 0xa1, 0x9d, 0x08, 0x29, 0xb3, 0x88, 0x0b, 0xf5, 0x87, 0x77, 0x9d,
+ 0x55, 0xcd, 0xe4, 0xc3, 0x7e, 0xd7, 0x6a, 0x64, 0xab, 0x85, 0x14, 0x86,
+ 0x95, 0x5b, 0x97, 0x32, 0x50, 0x6f, 0x3d, 0xc8, 0xba, 0x66, 0x0c, 0xe3,
+ 0xfc, 0xbd, 0xb8, 0x49, 0xc1, 0x76, 0x89, 0x49, 0x19, 0xfd, 0xc0, 0xa8,
+ 0xbd, 0x89, 0xa3, 0x67, 0x2f, 0xc6, 0x9f, 0xbc, 0x71, 0x19, 0x60, 0xb8,
+ 0x2d, 0xe9, 0x2c, 0xc9, 0x90, 0x76, 0x66, 0x7b, 0x94, 0xe2, 0xaf, 0x78,
+ 0xd6, 0x65, 0x53, 0x5d, 0x3c, 0xd6, 0x9c, 0xb2, 0xcf, 0x29, 0x03, 0xf9,
+ 0x2f, 0xa4, 0x50, 0xb2, 0xd4, 0x48, 0xce, 0x05, 0x32, 0x55, 0x8a, 0xfd,
+ 0xb2, 0x64, 0x4c, 0x0e, 0xe4, 0x98, 0x07, 0x75, 0xdb, 0x7f, 0xdf, 0xb9,
+ 0x08, 0x55, 0x60, 0x85, 0x30, 0x29, 0xf9, 0x7b, 0x48, 0xa4, 0x69, 0x86,
+ 0xe3, 0x35, 0x3f, 0x1e, 0x86, 0x5d, 0x7a, 0x7a, 0x15, 0xbd, 0xef, 0x00,
+ 0x8e, 0x15, 0x22, 0x54, 0x17, 0x00, 0x90, 0x26, 0x93, 0xbc, 0x0e, 0x49,
+ 0x68, 0x91, 0xbf, 0xf8, 0x47, 0xd3, 0x9d, 0x95, 0x42, 0xc1, 0x0e, 0x4d,
+ 0xdf, 0x6f, 0x26, 0xcf, 0xc3, 0x18, 0x21, 0x62, 0x66, 0x43, 0x70, 0xd6,
+ 0xd5, 0xc0, 0x07, 0xe1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0x27, 0x30, 0x82, 0x01, 0x23, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
+ 0x01, 0xff, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x33, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26, 0xa0, 0x24,
+ 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x31, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30,
+ 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50,
+ 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x55, 0xe4, 0x81, 0xd1, 0x11, 0x80, 0xbe, 0xd8, 0x89, 0xb9, 0x08, 0xa3,
+ 0x31, 0xf9, 0xa1, 0x24, 0x09, 0x16, 0xb9, 0x70, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xf0, 0x17, 0x62,
+ 0x13, 0x55, 0x3d, 0xb3, 0xff, 0x0a, 0x00, 0x6b, 0xfb, 0x50, 0x84, 0x97,
+ 0xf3, 0xed, 0x62, 0xd0, 0x1a, 0x30, 0x19, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf6, 0x7d, 0x07, 0x41, 0x00, 0x04, 0x0c, 0x30, 0x0a, 0x1b, 0x04,
+ 0x56, 0x37, 0x2e, 0x31, 0x03, 0x02, 0x00, 0x81, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x81, 0x81, 0x00, 0x8f, 0x65, 0xa2, 0x30, 0x8e, 0x26, 0xab, 0x8a, 0xec,
+ 0x35, 0x16, 0x98, 0xe9, 0x03, 0xf0, 0x8d, 0x17, 0x5f, 0xbc, 0x4c, 0x6c,
+ 0x02, 0xf6, 0x74, 0x52, 0xe0, 0xc2, 0xc6, 0x1f, 0xce, 0xf2, 0xa6, 0x11,
+ 0x0c, 0xa8, 0xb1, 0x0e, 0x4d, 0x84, 0x8b, 0x71, 0x36, 0xef, 0xb3, 0x35,
+ 0x45, 0xf3, 0xc1, 0xf8, 0x96, 0xc5, 0x8b, 0x55, 0xa4, 0xcc, 0x6b, 0x83,
+ 0x16, 0x20, 0x32, 0xda, 0xbe, 0xfb, 0xaf, 0x9b, 0xb7, 0x9f, 0xe1, 0x7e,
+ 0x84, 0x9f, 0x9e, 0x3c, 0x50, 0xa7, 0x3f, 0x5c, 0xc2, 0xbe, 0x8b, 0x86,
+ 0xb8, 0x08, 0x92, 0xee, 0xf8, 0x42, 0x2b, 0x0d, 0x13, 0xe3, 0x76, 0x85,
+ 0x48, 0x0a, 0x4a, 0xbf, 0xd0, 0xa5, 0x3b, 0x0a, 0xb0, 0x54, 0xb8, 0x6d,
+ 0xe3, 0x08, 0xf9, 0x34, 0x8d, 0x0b, 0x8e, 0x8b, 0x12, 0xcc, 0x17, 0x1a,
+ 0x33, 0x87, 0x95, 0xc8, 0x9e, 0x0a, 0xdc, 0x50, 0x53, 0x17, 0x7b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5a:b6:1d:ac:1e:4d:a2:06:14:c7:55:3d:3d:a9:b2:dc
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Validity
+ Not Before: Oct 23 00:00:00 2008 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=FR, O=GANDI SAS, CN=Gandi Standard SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:54:3d:a5:db:0d:22:78:50:6a:5a:23:89:3f:
+ 97:a1:d4:07:1a:a9:58:08:9b:a0:15:c3:32:b6:b7:
+ f1:e8:b9:a5:6f:ad:37:f6:6e:71:1b:b4:75:2d:48:
+ 5e:9f:c6:15:aa:81:ef:e5:c4:88:95:8a:3a:6c:77:
+ cc:b5:cd:65:e4:67:e5:73:c9:50:52:94:c1:27:49:
+ 3e:a0:6b:41:16:41:b6:94:99:41:ae:3e:cb:e2:06:
+ 46:09:e9:4d:be:c9:4c:55:a9:18:7e:a6:df:6e:fd:
+ 4a:b2:cc:6c:4e:d9:c8:50:15:93:b3:f2:e9:e3:c2:
+ 6a:ad:3a:d5:fb:c3:79:50:9f:25:79:29:b2:47:64:
+ 7c:20:3e:e2:08:4d:93:29:14:b6:34:6e:cf:71:46:
+ 7e:76:10:f4:fd:6c:aa:01:d2:c2:06:de:92:83:cc:
+ 58:90:2e:92:de:1e:65:b7:63:2f:3d:b2:eb:70:8c:
+ 4c:e0:be:15:9d:de:c1:4d:56:f8:0b:c6:8e:07:b9:
+ 5d:df:95:f0:7b:40:1f:1a:2c:d7:9c:2b:4b:76:f4:
+ 59:f5:43:c1:2c:66:10:9e:9e:66:96:60:9d:1c:74:
+ 1b:4e:18:5c:08:b0:6e:6c:ca:69:1a:02:e9:bb:ca:
+ 78:ef:66:2e:e3:32:fd:41:5c:95:74:81:4d:f4:da:
+ fe:4b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+
+ X509v3 Subject Key Identifier:
+ B6:A8:FF:A2:A8:2F:D0:A6:CD:4B:B1:68:F3:E7:50:10:31:A7:79:21
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.2.26
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/UTN-USERFirst-Hardware.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/UTNAddTrustServer_CA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 19:53:bf:03:3d:9b:e2:6b:5a:fd:ba:49:1f:4f:ec:e1:c6:82:
+ 39:3c:d2:03:04:0f:ab:7b:3e:82:a9:85:10:1f:f4:de:32:af:
+ 58:3f:ff:70:f3:30:1d:97:2d:4c:9a:e2:ec:0c:3e:14:2d:2f:
+ 98:48:9d:ae:16:6a:ac:2d:42:aa:b5:64:a4:70:bb:eb:73:94:
+ 7b:46:4c:e7:7a:14:76:5b:4c:1d:84:a1:20:74:1f:2e:4b:5c:
+ 70:88:dc:bd:f7:19:3d:ed:59:0d:e2:3f:26:e2:9c:ac:a4:3c:
+ 95:1c:f8:be:8c:03:ae:f0:e5:9c:4d:bc:c7:9b:58:00:bf:af:
+ ad:fa:37:6e:71:6d:18:34:0e:c1:ea:6a:f8:0d:df:69:54:56:
+ 15:f2:28:b3:fe:a4:63:ec:c5:04:64:60:bb:fe:2a:f0:f4:87:
+ a1:b0:ae:bd:aa:e4:2f:e3:03:0b:2f:66:5f:85:a4:32:7b:46:
+ ed:25:0c:e7:f1:b7:e7:19:fd:60:ba:5f:87:77:de:98:07:96:
+ e4:5e:ea:63:7d:a8:de:55:da:61:5c:3c:90:83:43:04:07:3c:
+ dd:f3:f8:9f:06:52:0a:de:c7:b6:7b:8f:e1:11:f7:04:7a:35:
+ ff:6a:bc:5b:c7:50:49:08:70:6f:94:43:cd:9e:c7:70:f1:db:
+ d0:6d:da:8f
+-----BEGIN CERTIFICATE-----
+MIIEozCCA4ugAwIBAgIQWrYdrB5NogYUx1U9Pamy3DANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNMDgxMDIzMDAwMDAwWhcNMjAwNTMwMTA0ODM4WjBBMQswCQYD
+VQQGEwJGUjESMBAGA1UEChMJR0FOREkgU0FTMR4wHAYDVQQDExVHYW5kaSBTdGFu
+ZGFyZCBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2VD2l
+2w0ieFBqWiOJP5eh1AcaqVgIm6AVwzK2t/HouaVvrTf2bnEbtHUtSF6fxhWqge/l
+xIiVijpsd8y1zWXkZ+VzyVBSlMEnST6ga0EWQbaUmUGuPsviBkYJ6U2+yUxVqRh+
+pt9u/UqyzGxO2chQFZOz8unjwmqtOtX7w3lQnyV5KbJHZHwgPuIITZMpFLY0bs9x
+Rn52EPT9bKoB0sIG3pKDzFiQLpLeHmW3Yy89sutwjEzgvhWd3sFNVvgLxo4HuV3f
+lfB7QB8aLNecK0t29Fn1Q8EsZhCenmaWYJ0cdBtOGFwIsG5symkaAum7ynjvZi7j
+Mv1BXJV0gU302v5LAgMBAAGjggE+MIIBOjAfBgNVHSMEGDAWgBShcl8mGyiYQ5Vd
+BzfVhZadS9LDRTAdBgNVHQ4EFgQUtqj/oqgv0KbNS7Fo8+dQEDGneSEwDgYDVR0P
+AQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwGAYDVR0gBBEwDzANBgsrBgEE
+AbIxAQICGjBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vY3JsLnVzZXJ0cnVzdC5j
+b20vVVROLVVTRVJGaXJzdC1IYXJkd2FyZS5jcmwwdAYIKwYBBQUHAQEEaDBmMD0G
+CCsGAQUFBzAChjFodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVROQWRkVHJ1c3RT
+ZXJ2ZXJfQ0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3Qu
+Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAZU78DPZvia1r9ukkfT+zhxoI5PNIDBA+r
+ez6CqYUQH/TeMq9YP/9w8zAdly1MmuLsDD4ULS+YSJ2uFmqsLUKqtWSkcLvrc5R7
+RkznehR2W0wdhKEgdB8uS1xwiNy99xk97VkN4j8m4pyspDyVHPi+jAOu8OWcTbzH
+m1gAv6+t+jducW0YNA7B6mr4Dd9pVFYV8iiz/qRj7MUEZGC7/irw9IehsK69quQv
+4wMLL2ZfhaQye0btJQzn8bfnGf1gul+Hd96YB5bkXupjfajeVdphXDyQg0MEBzzd
+8/ifBlIK3se2e4/hEfcEejX/arxbx1BJCHBvlEPNnsdw8dvQbdqP
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert47[] = {
+ 0x30, 0x82, 0x04, 0xa3, 0x30, 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x5a, 0xb6, 0x1d, 0xac, 0x1e, 0x4d, 0xa2, 0x06, 0x14,
+ 0xc7, 0x55, 0x3d, 0x3d, 0xa9, 0xb2, 0xdc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20,
+ 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54,
+ 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54,
+ 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d,
+ 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x38, 0x31, 0x30, 0x32, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34,
+ 0x38, 0x33, 0x38, 0x5a, 0x30, 0x41, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x47, 0x41, 0x4e, 0x44, 0x49, 0x20,
+ 0x53, 0x41, 0x53, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x15, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x20, 0x53, 0x74, 0x61, 0x6e,
+ 0x64, 0x61, 0x72, 0x64, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, 0x54, 0x3d, 0xa5,
+ 0xdb, 0x0d, 0x22, 0x78, 0x50, 0x6a, 0x5a, 0x23, 0x89, 0x3f, 0x97, 0xa1,
+ 0xd4, 0x07, 0x1a, 0xa9, 0x58, 0x08, 0x9b, 0xa0, 0x15, 0xc3, 0x32, 0xb6,
+ 0xb7, 0xf1, 0xe8, 0xb9, 0xa5, 0x6f, 0xad, 0x37, 0xf6, 0x6e, 0x71, 0x1b,
+ 0xb4, 0x75, 0x2d, 0x48, 0x5e, 0x9f, 0xc6, 0x15, 0xaa, 0x81, 0xef, 0xe5,
+ 0xc4, 0x88, 0x95, 0x8a, 0x3a, 0x6c, 0x77, 0xcc, 0xb5, 0xcd, 0x65, 0xe4,
+ 0x67, 0xe5, 0x73, 0xc9, 0x50, 0x52, 0x94, 0xc1, 0x27, 0x49, 0x3e, 0xa0,
+ 0x6b, 0x41, 0x16, 0x41, 0xb6, 0x94, 0x99, 0x41, 0xae, 0x3e, 0xcb, 0xe2,
+ 0x06, 0x46, 0x09, 0xe9, 0x4d, 0xbe, 0xc9, 0x4c, 0x55, 0xa9, 0x18, 0x7e,
+ 0xa6, 0xdf, 0x6e, 0xfd, 0x4a, 0xb2, 0xcc, 0x6c, 0x4e, 0xd9, 0xc8, 0x50,
+ 0x15, 0x93, 0xb3, 0xf2, 0xe9, 0xe3, 0xc2, 0x6a, 0xad, 0x3a, 0xd5, 0xfb,
+ 0xc3, 0x79, 0x50, 0x9f, 0x25, 0x79, 0x29, 0xb2, 0x47, 0x64, 0x7c, 0x20,
+ 0x3e, 0xe2, 0x08, 0x4d, 0x93, 0x29, 0x14, 0xb6, 0x34, 0x6e, 0xcf, 0x71,
+ 0x46, 0x7e, 0x76, 0x10, 0xf4, 0xfd, 0x6c, 0xaa, 0x01, 0xd2, 0xc2, 0x06,
+ 0xde, 0x92, 0x83, 0xcc, 0x58, 0x90, 0x2e, 0x92, 0xde, 0x1e, 0x65, 0xb7,
+ 0x63, 0x2f, 0x3d, 0xb2, 0xeb, 0x70, 0x8c, 0x4c, 0xe0, 0xbe, 0x15, 0x9d,
+ 0xde, 0xc1, 0x4d, 0x56, 0xf8, 0x0b, 0xc6, 0x8e, 0x07, 0xb9, 0x5d, 0xdf,
+ 0x95, 0xf0, 0x7b, 0x40, 0x1f, 0x1a, 0x2c, 0xd7, 0x9c, 0x2b, 0x4b, 0x76,
+ 0xf4, 0x59, 0xf5, 0x43, 0xc1, 0x2c, 0x66, 0x10, 0x9e, 0x9e, 0x66, 0x96,
+ 0x60, 0x9d, 0x1c, 0x74, 0x1b, 0x4e, 0x18, 0x5c, 0x08, 0xb0, 0x6e, 0x6c,
+ 0xca, 0x69, 0x1a, 0x02, 0xe9, 0xbb, 0xca, 0x78, 0xef, 0x66, 0x2e, 0xe3,
+ 0x32, 0xfd, 0x41, 0x5c, 0x95, 0x74, 0x81, 0x4d, 0xf4, 0xda, 0xfe, 0x4b,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3e, 0x30, 0x82, 0x01,
+ 0x3a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98, 0x43, 0x95, 0x5d,
+ 0x07, 0x37, 0xd5, 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3, 0x45, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb6, 0xa8, 0xff,
+ 0xa2, 0xa8, 0x2f, 0xd0, 0xa6, 0xcd, 0x4b, 0xb1, 0x68, 0xf3, 0xe7, 0x50,
+ 0x10, 0x31, 0xa7, 0x79, 0x21, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04,
+ 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, 0x1a, 0x30, 0x44, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0, 0x35,
+ 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46,
+ 0x69, 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72,
+ 0x65, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x74, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x68, 0x30, 0x66, 0x30, 0x3d, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x31, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x55, 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x53,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x19,
+ 0x53, 0xbf, 0x03, 0x3d, 0x9b, 0xe2, 0x6b, 0x5a, 0xfd, 0xba, 0x49, 0x1f,
+ 0x4f, 0xec, 0xe1, 0xc6, 0x82, 0x39, 0x3c, 0xd2, 0x03, 0x04, 0x0f, 0xab,
+ 0x7b, 0x3e, 0x82, 0xa9, 0x85, 0x10, 0x1f, 0xf4, 0xde, 0x32, 0xaf, 0x58,
+ 0x3f, 0xff, 0x70, 0xf3, 0x30, 0x1d, 0x97, 0x2d, 0x4c, 0x9a, 0xe2, 0xec,
+ 0x0c, 0x3e, 0x14, 0x2d, 0x2f, 0x98, 0x48, 0x9d, 0xae, 0x16, 0x6a, 0xac,
+ 0x2d, 0x42, 0xaa, 0xb5, 0x64, 0xa4, 0x70, 0xbb, 0xeb, 0x73, 0x94, 0x7b,
+ 0x46, 0x4c, 0xe7, 0x7a, 0x14, 0x76, 0x5b, 0x4c, 0x1d, 0x84, 0xa1, 0x20,
+ 0x74, 0x1f, 0x2e, 0x4b, 0x5c, 0x70, 0x88, 0xdc, 0xbd, 0xf7, 0x19, 0x3d,
+ 0xed, 0x59, 0x0d, 0xe2, 0x3f, 0x26, 0xe2, 0x9c, 0xac, 0xa4, 0x3c, 0x95,
+ 0x1c, 0xf8, 0xbe, 0x8c, 0x03, 0xae, 0xf0, 0xe5, 0x9c, 0x4d, 0xbc, 0xc7,
+ 0x9b, 0x58, 0x00, 0xbf, 0xaf, 0xad, 0xfa, 0x37, 0x6e, 0x71, 0x6d, 0x18,
+ 0x34, 0x0e, 0xc1, 0xea, 0x6a, 0xf8, 0x0d, 0xdf, 0x69, 0x54, 0x56, 0x15,
+ 0xf2, 0x28, 0xb3, 0xfe, 0xa4, 0x63, 0xec, 0xc5, 0x04, 0x64, 0x60, 0xbb,
+ 0xfe, 0x2a, 0xf0, 0xf4, 0x87, 0xa1, 0xb0, 0xae, 0xbd, 0xaa, 0xe4, 0x2f,
+ 0xe3, 0x03, 0x0b, 0x2f, 0x66, 0x5f, 0x85, 0xa4, 0x32, 0x7b, 0x46, 0xed,
+ 0x25, 0x0c, 0xe7, 0xf1, 0xb7, 0xe7, 0x19, 0xfd, 0x60, 0xba, 0x5f, 0x87,
+ 0x77, 0xde, 0x98, 0x07, 0x96, 0xe4, 0x5e, 0xea, 0x63, 0x7d, 0xa8, 0xde,
+ 0x55, 0xda, 0x61, 0x5c, 0x3c, 0x90, 0x83, 0x43, 0x04, 0x07, 0x3c, 0xdd,
+ 0xf3, 0xf8, 0x9f, 0x06, 0x52, 0x0a, 0xde, 0xc7, 0xb6, 0x7b, 0x8f, 0xe1,
+ 0x11, 0xf7, 0x04, 0x7a, 0x35, 0xff, 0x6a, 0xbc, 0x5b, 0xc7, 0x50, 0x49,
+ 0x08, 0x70, 0x6f, 0x94, 0x43, 0xcd, 0x9e, 0xc7, 0x70, 0xf1, 0xdb, 0xd0,
+ 0x6d, 0xda, 0x8f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 10:e7:76:e8:a6:5a:6e:37:7e:05:03:06:d4:3c:25:ea
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Validity
+ Not Before: Apr 10 00:00:00 2006 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Network Solutions L.L.C., CN=Network Solutions Certificate Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c3:dd:36:cc:83:c3:18:55:b0:96:d9:13:25:d3:
+ 26:86:48:38:bb:16:7f:f1:9f:29:f6:fd:03:f1:ed:
+ 4d:26:9a:56:f0:b5:1a:1a:cd:e6:cc:85:55:40:a4:
+ b5:d0:0d:ca:22:ef:3d:23:c6:7e:6c:cc:bc:a1:e9:
+ 7c:50:46:e0:bd:14:ad:65:12:c2:0b:11:69:52:0a:
+ 07:92:1f:73:6f:c1:ba:d7:62:f0:ce:00:2e:34:a5:
+ c8:e6:2f:0f:ec:0d:ea:44:61:75:68:e5:e4:dc:80:
+ 36:4f:da:78:5d:53:25:94:94:f5:4f:2e:3a:60:6f:
+ 0c:a6:d9:b3:f6:2a:2e:03:12:d5:26:42:07:51:b2:
+ 64:57:71:dc:21:1c:89:c7:69:a3:e6:fb:c2:7b:6e:
+ ef:0c:87:fb:50:64:e8:4e:4b:ef:e7:71:9b:83:63:
+ 61:c9:32:8d:8c:ec:14:a7:e4:89:ad:3f:2b:26:64:
+ e4:85:42:f2:89:50:e1:3a:be:15:e3:45:25:e2:5a:
+ cb:8c:3f:e0:33:1e:35:09:5a:84:ea:7e:5d:a1:f5:
+ 91:80:0a:28:06:b7:cb:31:41:25:61:8b:01:e9:56:
+ a2:f6:3e:5f:2f:f3:c4:43:f6:19:94:75:83:4c:a1:
+ 82:42:3a:c6:ba:c4:09:30:a6:e1:75:02:51:b9:5e:
+ 64:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+
+ X509v3 Subject Key Identifier:
+ 3C:41:E2:8F:08:08:A9:4C:25:89:8D:6D:C5:38:D0:FC:85:8C:62:17
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.782.1.2.1.3.1
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/UTN-USERFirst-Hardware.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://www.usertrust.com/cacerts/UTNAddTrustServer_CA.crt
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 68:ab:fc:ef:80:6b:18:b2:b0:b3:a3:45:89:cb:53:c5:a2:e6:
+ af:08:a9:fd:ff:0f:49:ac:ff:e4:9f:d7:41:7c:a3:c5:a2:e8:
+ aa:e0:57:21:2d:c3:aa:7c:0c:4c:28:0b:79:f4:ee:4c:32:ad:
+ 79:0e:7e:a2:5e:34:18:4f:df:54:f1:bd:68:7c:e3:d3:d7:46:
+ 5e:6d:64:c2:f7:6d:88:82:73:0c:ef:99:85:ea:a9:ef:32:4a:
+ f0:83:9f:73:91:0c:a4:3e:2b:31:51:a6:62:8f:15:84:f9:a6:
+ 3a:12:30:3f:da:6e:f8:cc:c7:19:92:0f:5c:f4:fe:17:f1:95:
+ 08:47:52:2c:50:8f:e8:9b:a5:ee:ae:70:33:89:91:82:fe:30:
+ aa:76:76:59:d7:6c:18:d3:2b:12:5b:1d:28:1d:78:71:f6:cd:
+ 36:a2:e9:07:48:44:3b:e7:57:6e:82:0a:ad:c5:8a:dd:e8:53:
+ b4:71:af:13:d2:06:9d:37:6d:53:3f:8a:35:08:fa:fe:a2:16:
+ e6:b9:6f:5c:56:39:d6:c6:aa:ef:19:67:ce:13:c5:b8:95:05:
+ fb:0a:44:c9:9f:a9:40:25:4b:32:11:af:07:fe:08:d5:42:71:
+ e9:e1:53:8b:15:1f:dd:2a:07:95:70:24:6f:64:5e:d3:b7:90:
+ 2e:8b:21:d8
+-----BEGIN CERTIFICATE-----
+MIIEpjCCA46gAwIBAgIQEOd26KZabjd+BQMG1Dwl6jANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNMDYwNDEwMDAwMDAwWhcNMjAwNTMwMTA0ODM4WjBiMQswCQYD
+VQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYD
+VQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDD3TbMg8MYVbCW2RMl0yaGSDi7
+Fn/xnyn2/QPx7U0mmlbwtRoazebMhVVApLXQDcoi7z0jxn5szLyh6XxQRuC9FK1l
+EsILEWlSCgeSH3NvwbrXYvDOAC40pcjmLw/sDepEYXVo5eTcgDZP2nhdUyWUlPVP
+Ljpgbwym2bP2Ki4DEtUmQgdRsmRXcdwhHInHaaPm+8J7bu8Mh/tQZOhOS+/ncZuD
+Y2HJMo2M7BSn5ImtPysmZOSFQvKJUOE6vhXjRSXiWsuMP+AzHjUJWoTqfl2h9ZGA
+CigGt8sxQSVhiwHpVqL2Pl8v88RD9hmUdYNMoYJCOsa6xAkwpuF1AlG5XmSLAgMB
+AAGjggEgMIIBHDAfBgNVHSMEGDAWgBShcl8mGyiYQ5VdBzfVhZadS9LDRTAdBgNV
+HQ4EFgQUPEHijwgIqUwliY1txTjQ/IWMYhcwDgYDVR0PAQH/BAQDAgEGMBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwGQYDVR0gBBIwEDAOBgwrBgEEAYYOAQIBAwEwRAYDVR0f
+BD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmly
+c3QtSGFyZHdhcmUuY3JsMFUGCCsGAQUFBwEBBEkwRzBFBggrBgEFBQcwAoY5aHR0
+cDovL3d3dy51c2VydHJ1c3QuY29tL2NhY2VydHMvVVROQWRkVHJ1c3RTZXJ2ZXJf
+Q0EuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQBoq/zvgGsYsrCzo0WJy1PFouavCKn9
+/w9JrP/kn9dBfKPFouiq4FchLcOqfAxMKAt59O5MMq15Dn6iXjQYT99U8b1ofOPT
+10ZebWTC922IgnMM75mF6qnvMkrwg59zkQykPisxUaZijxWE+aY6EjA/2m74zMcZ
+kg9c9P4X8ZUIR1IsUI/om6XurnAziZGC/jCqdnZZ12wY0ysSWx0oHXhx9s02oukH
+SEQ751duggqtxYrd6FO0ca8T0gadN21TP4o1CPr+ohbmuW9cVjnWxqrvGWfOE8W4
+lQX7CkTJn6lAJUsyEa8H/gjVQnHp4VOLFR/dKgeVcCRvZF7Tt5AuiyHY
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert48[] = {
+ 0x30, 0x82, 0x04, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x10, 0xe7, 0x76, 0xe8, 0xa6, 0x5a, 0x6e, 0x37, 0x7e,
+ 0x05, 0x03, 0x06, 0xd4, 0x3c, 0x25, 0xea, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20,
+ 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54,
+ 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54,
+ 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d,
+ 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x36, 0x30, 0x34, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34,
+ 0x38, 0x33, 0x38, 0x5a, 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xc3, 0xdd, 0x36, 0xcc, 0x83, 0xc3, 0x18,
+ 0x55, 0xb0, 0x96, 0xd9, 0x13, 0x25, 0xd3, 0x26, 0x86, 0x48, 0x38, 0xbb,
+ 0x16, 0x7f, 0xf1, 0x9f, 0x29, 0xf6, 0xfd, 0x03, 0xf1, 0xed, 0x4d, 0x26,
+ 0x9a, 0x56, 0xf0, 0xb5, 0x1a, 0x1a, 0xcd, 0xe6, 0xcc, 0x85, 0x55, 0x40,
+ 0xa4, 0xb5, 0xd0, 0x0d, 0xca, 0x22, 0xef, 0x3d, 0x23, 0xc6, 0x7e, 0x6c,
+ 0xcc, 0xbc, 0xa1, 0xe9, 0x7c, 0x50, 0x46, 0xe0, 0xbd, 0x14, 0xad, 0x65,
+ 0x12, 0xc2, 0x0b, 0x11, 0x69, 0x52, 0x0a, 0x07, 0x92, 0x1f, 0x73, 0x6f,
+ 0xc1, 0xba, 0xd7, 0x62, 0xf0, 0xce, 0x00, 0x2e, 0x34, 0xa5, 0xc8, 0xe6,
+ 0x2f, 0x0f, 0xec, 0x0d, 0xea, 0x44, 0x61, 0x75, 0x68, 0xe5, 0xe4, 0xdc,
+ 0x80, 0x36, 0x4f, 0xda, 0x78, 0x5d, 0x53, 0x25, 0x94, 0x94, 0xf5, 0x4f,
+ 0x2e, 0x3a, 0x60, 0x6f, 0x0c, 0xa6, 0xd9, 0xb3, 0xf6, 0x2a, 0x2e, 0x03,
+ 0x12, 0xd5, 0x26, 0x42, 0x07, 0x51, 0xb2, 0x64, 0x57, 0x71, 0xdc, 0x21,
+ 0x1c, 0x89, 0xc7, 0x69, 0xa3, 0xe6, 0xfb, 0xc2, 0x7b, 0x6e, 0xef, 0x0c,
+ 0x87, 0xfb, 0x50, 0x64, 0xe8, 0x4e, 0x4b, 0xef, 0xe7, 0x71, 0x9b, 0x83,
+ 0x63, 0x61, 0xc9, 0x32, 0x8d, 0x8c, 0xec, 0x14, 0xa7, 0xe4, 0x89, 0xad,
+ 0x3f, 0x2b, 0x26, 0x64, 0xe4, 0x85, 0x42, 0xf2, 0x89, 0x50, 0xe1, 0x3a,
+ 0xbe, 0x15, 0xe3, 0x45, 0x25, 0xe2, 0x5a, 0xcb, 0x8c, 0x3f, 0xe0, 0x33,
+ 0x1e, 0x35, 0x09, 0x5a, 0x84, 0xea, 0x7e, 0x5d, 0xa1, 0xf5, 0x91, 0x80,
+ 0x0a, 0x28, 0x06, 0xb7, 0xcb, 0x31, 0x41, 0x25, 0x61, 0x8b, 0x01, 0xe9,
+ 0x56, 0xa2, 0xf6, 0x3e, 0x5f, 0x2f, 0xf3, 0xc4, 0x43, 0xf6, 0x19, 0x94,
+ 0x75, 0x83, 0x4c, 0xa1, 0x82, 0x42, 0x3a, 0xc6, 0xba, 0xc4, 0x09, 0x30,
+ 0xa6, 0xe1, 0x75, 0x02, 0x51, 0xb9, 0x5e, 0x64, 0x8b, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x20, 0x30, 0x82, 0x01, 0x1c, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xa1,
+ 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98, 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5,
+ 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3, 0x45, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08,
+ 0xa9, 0x4c, 0x25, 0x89, 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c,
+ 0x62, 0x17, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x19, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x12, 0x30,
+ 0x10, 0x30, 0x0e, 0x06, 0x0c, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x86, 0x0e,
+ 0x01, 0x02, 0x01, 0x03, 0x01, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0, 0x35, 0x86, 0x33,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x75,
+ 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72,
+ 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x55, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x01, 0x01, 0x04, 0x49, 0x30, 0x47, 0x30, 0x45, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x39, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61,
+ 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x55, 0x54, 0x4e, 0x41, 0x64, 0x64,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x68, 0xab, 0xfc, 0xef, 0x80, 0x6b, 0x18, 0xb2, 0xb0, 0xb3,
+ 0xa3, 0x45, 0x89, 0xcb, 0x53, 0xc5, 0xa2, 0xe6, 0xaf, 0x08, 0xa9, 0xfd,
+ 0xff, 0x0f, 0x49, 0xac, 0xff, 0xe4, 0x9f, 0xd7, 0x41, 0x7c, 0xa3, 0xc5,
+ 0xa2, 0xe8, 0xaa, 0xe0, 0x57, 0x21, 0x2d, 0xc3, 0xaa, 0x7c, 0x0c, 0x4c,
+ 0x28, 0x0b, 0x79, 0xf4, 0xee, 0x4c, 0x32, 0xad, 0x79, 0x0e, 0x7e, 0xa2,
+ 0x5e, 0x34, 0x18, 0x4f, 0xdf, 0x54, 0xf1, 0xbd, 0x68, 0x7c, 0xe3, 0xd3,
+ 0xd7, 0x46, 0x5e, 0x6d, 0x64, 0xc2, 0xf7, 0x6d, 0x88, 0x82, 0x73, 0x0c,
+ 0xef, 0x99, 0x85, 0xea, 0xa9, 0xef, 0x32, 0x4a, 0xf0, 0x83, 0x9f, 0x73,
+ 0x91, 0x0c, 0xa4, 0x3e, 0x2b, 0x31, 0x51, 0xa6, 0x62, 0x8f, 0x15, 0x84,
+ 0xf9, 0xa6, 0x3a, 0x12, 0x30, 0x3f, 0xda, 0x6e, 0xf8, 0xcc, 0xc7, 0x19,
+ 0x92, 0x0f, 0x5c, 0xf4, 0xfe, 0x17, 0xf1, 0x95, 0x08, 0x47, 0x52, 0x2c,
+ 0x50, 0x8f, 0xe8, 0x9b, 0xa5, 0xee, 0xae, 0x70, 0x33, 0x89, 0x91, 0x82,
+ 0xfe, 0x30, 0xaa, 0x76, 0x76, 0x59, 0xd7, 0x6c, 0x18, 0xd3, 0x2b, 0x12,
+ 0x5b, 0x1d, 0x28, 0x1d, 0x78, 0x71, 0xf6, 0xcd, 0x36, 0xa2, 0xe9, 0x07,
+ 0x48, 0x44, 0x3b, 0xe7, 0x57, 0x6e, 0x82, 0x0a, 0xad, 0xc5, 0x8a, 0xdd,
+ 0xe8, 0x53, 0xb4, 0x71, 0xaf, 0x13, 0xd2, 0x06, 0x9d, 0x37, 0x6d, 0x53,
+ 0x3f, 0x8a, 0x35, 0x08, 0xfa, 0xfe, 0xa2, 0x16, 0xe6, 0xb9, 0x6f, 0x5c,
+ 0x56, 0x39, 0xd6, 0xc6, 0xaa, 0xef, 0x19, 0x67, 0xce, 0x13, 0xc5, 0xb8,
+ 0x95, 0x05, 0xfb, 0x0a, 0x44, 0xc9, 0x9f, 0xa9, 0x40, 0x25, 0x4b, 0x32,
+ 0x11, 0xaf, 0x07, 0xfe, 0x08, 0xd5, 0x42, 0x71, 0xe9, 0xe1, 0x53, 0x8b,
+ 0x15, 0x1f, 0xdd, 0x2a, 0x07, 0x95, 0x70, 0x24, 0x6f, 0x64, 0x5e, 0xd3,
+ 0xb7, 0x90, 0x2e, 0x8b, 0x21, 0xd8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 46:ea:f0:96:05:4c:c5:e3:fa:65:ea:6e:9f:42:c6:64
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Jun 7 08:09:10 2005 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:df:ee:58:10:a2:2b:6e:55:c4:8e:bf:2e:46:09:
+ e7:e0:08:0f:2e:2b:7a:13:94:1b:bd:f6:b6:80:8e:
+ 65:05:93:00:1e:bc:af:e2:0f:8e:19:0d:12:47:ec:
+ ac:ad:a3:fa:2e:70:f8:de:6e:fb:56:42:15:9e:2e:
+ 5c:ef:23:de:21:b9:05:76:27:19:0f:4f:d6:c3:9c:
+ b4:be:94:19:63:f2:a6:11:0a:eb:53:48:9c:be:f2:
+ 29:3b:16:e8:1a:a0:4c:a6:c9:f4:18:59:68:c0:70:
+ f2:53:00:c0:5e:50:82:a5:56:6f:36:f9:4a:e0:44:
+ 86:a0:4d:4e:d6:47:6e:49:4a:cb:67:d7:a6:c4:05:
+ b9:8e:1e:f4:fc:ff:cd:e7:36:e0:9c:05:6c:b2:33:
+ 22:15:d0:b4:e0:cc:17:c0:b2:c0:f4:fe:32:3f:29:
+ 2a:95:7b:d8:f2:a7:4e:0f:54:7c:a1:0d:80:b3:09:
+ 03:c1:ff:5c:dd:5e:9a:3e:bc:ae:bc:47:8a:6a:ae:
+ 71:ca:1f:b1:2a:b8:5f:42:05:0b:ec:46:30:d1:72:
+ 0b:ca:e9:56:6d:f5:ef:df:78:be:61:ba:b2:a5:ae:
+ 04:4c:bc:a8:ac:69:15:97:bd:ef:eb:b4:8c:bf:35:
+ f8:d4:c3:d1:28:0e:5c:3a:9f:70:18:33:20:77:c4:
+ a2:af
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 53:32:D1:B3:CF:7F:FA:E0:F1:A0:5D:85:4E:92:D2:9E:45:1D:B4:4F
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Extended Key Usage:
+ Microsoft Server Gated Crypto, Netscape Server Gated Crypto
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/AddTrustExternalCARoot.crl
+
+ Full Name:
+ URI:http://crl.comodo.net/AddTrustExternalCARoot.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 63:86:92:10:b1:13:fa:37:be:8e:2a:b6:1b:8a:43:f5:5c:ae:
+ 0e:14:df:f7:69:40:7f:bf:1a:71:00:09:d8:bf:d4:24:4a:bf:
+ e0:93:ff:01:d8:0b:c6:0f:ec:7e:47:9c:b0:5d:f7:7c:14:9d:
+ fc:c0:33:92:84:5b:d2:83:f4:52:e2:22:58:74:fc:43:1b:3f:
+ a7:a3:58:da:03:fd:bc:f0:3a:e4:ed:cc:12:bb:c9:b9:ae:7b:
+ 04:a0:04:72:bf:e9:de:2d:d2:a7:51:66:00:73:d2:bd:7e:aa:
+ 9e:53:96:7d:69:b2:18:3e:8e:ad:56:50:7e:f7:d5:b0:ff:39:
+ 62:65:82:8c:96:57:c3:8f:f7:60:f6:c2:8d:34:87:fc:4f:43:
+ e5:db:bf:1c:aa:f6:86:cd:e6:df:11:3f:8d:07:f7:6d:83:13:
+ c0:38:88:39:60:a1:7e:30:e1:e3:88:3e:a4:bb:63:6f:2c:e9:
+ 8a:68:2c:ee:96:69:ac:04:61:e1:4f:4e:0e:9d:72:4c:f6:79:
+ 38:c8:c7:48:69:6f:94:0f:74:b4:bc:c8:cf:57:4d:b9:75:71:
+ 96:0d:8a:06:0b:eb:dd:d0:f0:3c:7d:c6:2e:98:46:6a:38:c7:
+ 02:b5:c8:b8:b2:65:75:de:da:90:08:b6:77:b8:53:00:25:cb:
+ 47:ca:73:5f
+-----BEGIN CERTIFICATE-----
+MIIEpjCCA46gAwIBAgIQRurwlgVMxeP6Zepun0LGZDANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTA1MDYwNzA4MDkxMFoXDTIwMDUzMDEwNDgzOFow
+gZMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl
+IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY
+aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMRswGQYDVQQDExJVVE4gLSBEQVRBQ29y
+cCBTR0MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDf7lgQoituVcSO
+vy5GCefgCA8uK3oTlBu99raAjmUFkwAevK/iD44ZDRJH7Kyto/oucPjebvtWQhWe
+LlzvI94huQV2JxkPT9bDnLS+lBlj8qYRCutTSJy+8ik7FugaoEymyfQYWWjAcPJT
+AMBeUIKlVm82+UrgRIagTU7WR25JSstn16bEBbmOHvT8/83nNuCcBWyyMyIV0LTg
+zBfAssD0/jI/KSqVe9jyp04PVHyhDYCzCQPB/1zdXpo+vK68R4pqrnHKH7EquF9C
+BQvsRjDRcgvK6VZt9e/feL5hurKlrgRMvKisaRWXve/rtIy/NfjUw9EoDlw6n3AY
+MyB3xKKvAgMBAAGjggEXMIIBEzAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
+JMtUGjAdBgNVHQ4EFgQUUzLRs89/+uDxoF2FTpLSnkUdtE8wDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZI
+AYb4QgQBMBEGA1UdIAQKMAgwBgYEVR0gADB7BgNVHR8EdDByMDigNqA0hjJodHRw
+Oi8vY3JsLmNvbW9kb2NhLmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDA2
+oDSgMoYwaHR0cDovL2NybC5jb21vZG8ubmV0L0FkZFRydXN0RXh0ZXJuYWxDQVJv
+b3QuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQBjhpIQsRP6N76OKrYbikP1XK4OFN/3
+aUB/vxpxAAnYv9QkSr/gk/8B2AvGD+x+R5ywXfd8FJ38wDOShFvSg/RS4iJYdPxD
+Gz+no1jaA/288Drk7cwSu8m5rnsEoARyv+neLdKnUWYAc9K9fqqeU5Z9abIYPo6t
+VlB+99Ww/zliZYKMllfDj/dg9sKNNIf8T0Pl278cqvaGzebfET+NB/dtgxPAOIg5
+YKF+MOHjiD6ku2NvLOmKaCzulmmsBGHhT04OnXJM9nk4yMdIaW+UD3S0vMjPV025
+dXGWDYoGC+vd0PA8fcYumEZqOMcCtci4smV13tqQCLZ3uFMAJctHynNf
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert49[] = {
+ 0x30, 0x82, 0x04, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x46, 0xea, 0xf0, 0x96, 0x05, 0x4c, 0xc5, 0xe3, 0xfa,
+ 0x65, 0xea, 0x6e, 0x9f, 0x42, 0xc6, 0x64, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x35, 0x30, 0x36, 0x30,
+ 0x37, 0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x93, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65,
+ 0x20, 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52,
+ 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75,
+ 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x55,
+ 0x54, 0x4e, 0x20, 0x2d, 0x20, 0x44, 0x41, 0x54, 0x41, 0x43, 0x6f, 0x72,
+ 0x70, 0x20, 0x53, 0x47, 0x43, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xdf, 0xee, 0x58, 0x10, 0xa2, 0x2b, 0x6e, 0x55, 0xc4, 0x8e,
+ 0xbf, 0x2e, 0x46, 0x09, 0xe7, 0xe0, 0x08, 0x0f, 0x2e, 0x2b, 0x7a, 0x13,
+ 0x94, 0x1b, 0xbd, 0xf6, 0xb6, 0x80, 0x8e, 0x65, 0x05, 0x93, 0x00, 0x1e,
+ 0xbc, 0xaf, 0xe2, 0x0f, 0x8e, 0x19, 0x0d, 0x12, 0x47, 0xec, 0xac, 0xad,
+ 0xa3, 0xfa, 0x2e, 0x70, 0xf8, 0xde, 0x6e, 0xfb, 0x56, 0x42, 0x15, 0x9e,
+ 0x2e, 0x5c, 0xef, 0x23, 0xde, 0x21, 0xb9, 0x05, 0x76, 0x27, 0x19, 0x0f,
+ 0x4f, 0xd6, 0xc3, 0x9c, 0xb4, 0xbe, 0x94, 0x19, 0x63, 0xf2, 0xa6, 0x11,
+ 0x0a, 0xeb, 0x53, 0x48, 0x9c, 0xbe, 0xf2, 0x29, 0x3b, 0x16, 0xe8, 0x1a,
+ 0xa0, 0x4c, 0xa6, 0xc9, 0xf4, 0x18, 0x59, 0x68, 0xc0, 0x70, 0xf2, 0x53,
+ 0x00, 0xc0, 0x5e, 0x50, 0x82, 0xa5, 0x56, 0x6f, 0x36, 0xf9, 0x4a, 0xe0,
+ 0x44, 0x86, 0xa0, 0x4d, 0x4e, 0xd6, 0x47, 0x6e, 0x49, 0x4a, 0xcb, 0x67,
+ 0xd7, 0xa6, 0xc4, 0x05, 0xb9, 0x8e, 0x1e, 0xf4, 0xfc, 0xff, 0xcd, 0xe7,
+ 0x36, 0xe0, 0x9c, 0x05, 0x6c, 0xb2, 0x33, 0x22, 0x15, 0xd0, 0xb4, 0xe0,
+ 0xcc, 0x17, 0xc0, 0xb2, 0xc0, 0xf4, 0xfe, 0x32, 0x3f, 0x29, 0x2a, 0x95,
+ 0x7b, 0xd8, 0xf2, 0xa7, 0x4e, 0x0f, 0x54, 0x7c, 0xa1, 0x0d, 0x80, 0xb3,
+ 0x09, 0x03, 0xc1, 0xff, 0x5c, 0xdd, 0x5e, 0x9a, 0x3e, 0xbc, 0xae, 0xbc,
+ 0x47, 0x8a, 0x6a, 0xae, 0x71, 0xca, 0x1f, 0xb1, 0x2a, 0xb8, 0x5f, 0x42,
+ 0x05, 0x0b, 0xec, 0x46, 0x30, 0xd1, 0x72, 0x0b, 0xca, 0xe9, 0x56, 0x6d,
+ 0xf5, 0xef, 0xdf, 0x78, 0xbe, 0x61, 0xba, 0xb2, 0xa5, 0xae, 0x04, 0x4c,
+ 0xbc, 0xa8, 0xac, 0x69, 0x15, 0x97, 0xbd, 0xef, 0xeb, 0xb4, 0x8c, 0xbf,
+ 0x35, 0xf8, 0xd4, 0xc3, 0xd1, 0x28, 0x0e, 0x5c, 0x3a, 0x9f, 0x70, 0x18,
+ 0x33, 0x20, 0x77, 0xc4, 0xa2, 0xaf, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x17, 0x30, 0x82, 0x01, 0x13, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a,
+ 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0,
+ 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x53, 0x32, 0xd1, 0xb3, 0xcf, 0x7f, 0xfa, 0xe0, 0xf1,
+ 0xa0, 0x5d, 0x85, 0x4e, 0x92, 0xd2, 0x9e, 0x45, 0x1d, 0xb4, 0x4f, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x19, 0x30, 0x17, 0x06, 0x0a, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72,
+ 0x30, 0x38, 0xa0, 0x36, 0xa0, 0x34, 0x86, 0x32, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64,
+ 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x36,
+ 0xa0, 0x34, 0xa0, 0x32, 0x86, 0x30, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x63, 0x86, 0x92, 0x10, 0xb1, 0x13, 0xfa, 0x37, 0xbe, 0x8e,
+ 0x2a, 0xb6, 0x1b, 0x8a, 0x43, 0xf5, 0x5c, 0xae, 0x0e, 0x14, 0xdf, 0xf7,
+ 0x69, 0x40, 0x7f, 0xbf, 0x1a, 0x71, 0x00, 0x09, 0xd8, 0xbf, 0xd4, 0x24,
+ 0x4a, 0xbf, 0xe0, 0x93, 0xff, 0x01, 0xd8, 0x0b, 0xc6, 0x0f, 0xec, 0x7e,
+ 0x47, 0x9c, 0xb0, 0x5d, 0xf7, 0x7c, 0x14, 0x9d, 0xfc, 0xc0, 0x33, 0x92,
+ 0x84, 0x5b, 0xd2, 0x83, 0xf4, 0x52, 0xe2, 0x22, 0x58, 0x74, 0xfc, 0x43,
+ 0x1b, 0x3f, 0xa7, 0xa3, 0x58, 0xda, 0x03, 0xfd, 0xbc, 0xf0, 0x3a, 0xe4,
+ 0xed, 0xcc, 0x12, 0xbb, 0xc9, 0xb9, 0xae, 0x7b, 0x04, 0xa0, 0x04, 0x72,
+ 0xbf, 0xe9, 0xde, 0x2d, 0xd2, 0xa7, 0x51, 0x66, 0x00, 0x73, 0xd2, 0xbd,
+ 0x7e, 0xaa, 0x9e, 0x53, 0x96, 0x7d, 0x69, 0xb2, 0x18, 0x3e, 0x8e, 0xad,
+ 0x56, 0x50, 0x7e, 0xf7, 0xd5, 0xb0, 0xff, 0x39, 0x62, 0x65, 0x82, 0x8c,
+ 0x96, 0x57, 0xc3, 0x8f, 0xf7, 0x60, 0xf6, 0xc2, 0x8d, 0x34, 0x87, 0xfc,
+ 0x4f, 0x43, 0xe5, 0xdb, 0xbf, 0x1c, 0xaa, 0xf6, 0x86, 0xcd, 0xe6, 0xdf,
+ 0x11, 0x3f, 0x8d, 0x07, 0xf7, 0x6d, 0x83, 0x13, 0xc0, 0x38, 0x88, 0x39,
+ 0x60, 0xa1, 0x7e, 0x30, 0xe1, 0xe3, 0x88, 0x3e, 0xa4, 0xbb, 0x63, 0x6f,
+ 0x2c, 0xe9, 0x8a, 0x68, 0x2c, 0xee, 0x96, 0x69, 0xac, 0x04, 0x61, 0xe1,
+ 0x4f, 0x4e, 0x0e, 0x9d, 0x72, 0x4c, 0xf6, 0x79, 0x38, 0xc8, 0xc7, 0x48,
+ 0x69, 0x6f, 0x94, 0x0f, 0x74, 0xb4, 0xbc, 0xc8, 0xcf, 0x57, 0x4d, 0xb9,
+ 0x75, 0x71, 0x96, 0x0d, 0x8a, 0x06, 0x0b, 0xeb, 0xdd, 0xd0, 0xf0, 0x3c,
+ 0x7d, 0xc6, 0x2e, 0x98, 0x46, 0x6a, 0x38, 0xc7, 0x02, 0xb5, 0xc8, 0xb8,
+ 0xb2, 0x65, 0x75, 0xde, 0xda, 0x90, 0x08, 0xb6, 0x77, 0xb8, 0x53, 0x00,
+ 0x25, 0xcb, 0x47, 0xca, 0x73, 0x5f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 2e:79:83:2e:90:88:87:ea:8b:8e:f3:1a:6e:e6:7a:44
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN - DATACorp SGC
+ Validity
+ Not Before: Dec 1 00:00:00 2006 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d0:40:8b:8b:72:e3:91:1b:f7:51:c1:1b:54:04:
+ 98:d3:a9:bf:c1:e6:8a:5d:3b:87:fb:bb:88:ce:0d:
+ e3:2f:3f:06:96:f0:a2:29:50:99:ae:db:3b:a1:57:
+ b0:74:51:71:cd:ed:42:91:4d:41:fe:a9:c8:d8:6a:
+ 86:77:44:bb:59:66:97:50:5e:b4:d4:2c:70:44:cf:
+ da:37:95:42:69:3c:30:c4:71:b3:52:f0:21:4d:a1:
+ d8:ba:39:7c:1c:9e:a3:24:9d:f2:83:16:98:aa:16:
+ 7c:43:9b:15:5b:b7:ae:34:91:fe:d4:62:26:18:46:
+ 9a:3f:eb:c1:f9:f1:90:57:eb:ac:7a:0d:8b:db:72:
+ 30:6a:66:d5:e0:46:a3:70:dc:68:d9:ff:04:48:89:
+ 77:de:b5:e9:fb:67:6d:41:e9:bc:39:bd:32:d9:62:
+ 02:f1:b1:a8:3d:6e:37:9c:e2:2f:e2:d3:a2:26:8b:
+ c6:b8:55:43:88:e1:23:3e:a5:d2:24:39:6a:47:ab:
+ 00:d4:a1:b3:a9:25:fe:0d:3f:a7:1d:ba:d3:51:c1:
+ 0b:a4:da:ac:38:ef:55:50:24:05:65:46:93:34:4f:
+ 2d:8d:ad:c6:d4:21:19:d2:8e:ca:05:61:71:07:73:
+ 47:e5:8a:19:12:bd:04:4d:ce:4e:9c:a5:48:ac:bb:
+ 26:f7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:53:32:D1:B3:CF:7F:FA:E0:F1:A0:5D:85:4E:92:D2:9E:45:1D:B4:4F
+
+ X509v3 Subject Key Identifier:
+ 0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Extended Key Usage:
+ Microsoft Server Gated Crypto, Netscape Server Gated Crypto
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/UTN-DATACorpSGC.crl
+
+ Full Name:
+ URI:http://crl.comodo.net/UTN-DATACorpSGC.crl
+
+ Signature Algorithm: sha1WithRSAEncryption
+ d8:5e:92:c4:ae:14:dc:43:ad:c2:a4:c3:67:45:07:1d:f9:37:
+ a2:19:c7:1c:37:35:91:13:1c:07:c4:7d:42:a6:0e:f0:86:5c:
+ 43:6b:0e:44:cf:be:24:61:3a:42:a9:ce:9d:4c:af:79:39:70:
+ dd:0e:04:20:4e:95:9c:3c:de:b7:60:ba:63:43:40:ed:6a:0f:
+ 81:49:46:bb:1e:93:c0:4b:f3:f8:e1:36:49:1b:6f:b6:0c:0d:
+ f2:90:57:8a:fc:6d:93:f2:28:c7:fa:86:0a:28:b3:17:0e:59:
+ 8a:2e:b6:bf:cd:e1:ac:4c:66:6c:f2:55:91:56:b7:32:bf:b1:
+ e4:7d:b5:e8:3a:b6:2f:db:b2:9c:da:50:93:8e:4e:c5:ac:9a:
+ 7e:5c:9e:12:3c:3b:4d:c6:50:70:b3:65:2b:8e:f7:6b:a1:bb:
+ 25:c0:00:bb:f5:ec:16:65:81:0e:fb:d4:a3:21:96:77:9a:a8:
+ 74:bc:53:aa:c2:39:50:ff:0b:02:09:61:cc:95:b7:d7:88:6a:
+ f6:5c:c5:68:d3:14:95:1a:47:5f:d9:fb:2d:e4:2f:8f:13:86:
+ ab:31:13:40:13:ac:6e:ed:b5:10:30:8b:1b:50:a9:ce:ee:8c:
+ ca:eb:7c:b5:b9:16:3d:d4:fa:6f:92:6d:1e:a2:bd:fb:02:4a:
+ c5:70:be:f1
+-----BEGIN CERTIFICATE-----
+MIIEqzCCA5OgAwIBAgIQLnmDLpCIh+qLjvMabuZ6RDANBgkqhkiG9w0BAQUFADCB
+kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
+IFNHQzAeFw0wNjEyMDEwMDAwMDBaFw0yMDA1MzAxMDQ4MzhaMIGBMQswCQYDVQQG
+EwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxm
+b3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RP
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZ
+rts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAh
+TaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23Iw
+ambV4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVD
+iOEjPqXSJDlqR6sA1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ
+0o7KBWFxB3NH5YoZEr0ETc5OnKVIrLsm9wIDAQABo4IBCTCCAQUwHwYDVR0jBBgw
+FoAUUzLRs89/+uDxoF2FTpLSnkUdtE8wHQYDVR0OBBYEFAtY5YvGTBU3pECpMKkh
+vkc2Wlb/MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MCAGA1UdJQQZ
+MBcGCisGAQQBgjcKAwMGCWCGSAGG+EIEATARBgNVHSAECjAIMAYGBFUdIAAwbQYD
+VR0fBGYwZDAxoC+gLYYraHR0cDovL2NybC5jb21vZG9jYS5jb20vVVROLURBVEFD
+b3JwU0dDLmNybDAvoC2gK4YpaHR0cDovL2NybC5jb21vZG8ubmV0L1VUTi1EQVRB
+Q29ycFNHQy5jcmwwDQYJKoZIhvcNAQEFBQADggEBANheksSuFNxDrcKkw2dFBx35
+N6IZxxw3NZETHAfEfUKmDvCGXENrDkTPviRhOkKpzp1Mr3k5cN0OBCBOlZw83rdg
+umNDQO1qD4FJRrsek8BL8/jhNkkbb7YMDfKQV4r8bZPyKMf6hgoosxcOWYoutr/N
+4axMZmzyVZFWtzK/seR9teg6ti/bspzaUJOOTsWsmn5cnhI8O03GUHCzZSuO92uh
+uyXAALv17BZlgQ771KMhlneaqHS8U6rCOVD/CwIJYcyVt9eIavZcxWjTFJUaR1/Z
++y3kL48ThqsxE0ATrG7ttRAwixtQqc7ujMrrfLW5Fj3U+m+SbR6ivfsCSsVwvvE=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert50[] = {
+ 0x30, 0x82, 0x04, 0xab, 0x30, 0x82, 0x03, 0x93, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x2e, 0x79, 0x83, 0x2e, 0x90, 0x88, 0x87, 0xea, 0x8b,
+ 0x8e, 0xf3, 0x1a, 0x6e, 0xe6, 0x7a, 0x44, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x93, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20,
+ 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54,
+ 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31,
+ 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x55, 0x54,
+ 0x4e, 0x20, 0x2d, 0x20, 0x44, 0x41, 0x54, 0x41, 0x43, 0x6f, 0x72, 0x70,
+ 0x20, 0x53, 0x47, 0x43, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x32,
+ 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32,
+ 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a,
+ 0x30, 0x81, 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04,
+ 0x08, 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d,
+ 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66,
+ 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20,
+ 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0x40, 0x8b,
+ 0x8b, 0x72, 0xe3, 0x91, 0x1b, 0xf7, 0x51, 0xc1, 0x1b, 0x54, 0x04, 0x98,
+ 0xd3, 0xa9, 0xbf, 0xc1, 0xe6, 0x8a, 0x5d, 0x3b, 0x87, 0xfb, 0xbb, 0x88,
+ 0xce, 0x0d, 0xe3, 0x2f, 0x3f, 0x06, 0x96, 0xf0, 0xa2, 0x29, 0x50, 0x99,
+ 0xae, 0xdb, 0x3b, 0xa1, 0x57, 0xb0, 0x74, 0x51, 0x71, 0xcd, 0xed, 0x42,
+ 0x91, 0x4d, 0x41, 0xfe, 0xa9, 0xc8, 0xd8, 0x6a, 0x86, 0x77, 0x44, 0xbb,
+ 0x59, 0x66, 0x97, 0x50, 0x5e, 0xb4, 0xd4, 0x2c, 0x70, 0x44, 0xcf, 0xda,
+ 0x37, 0x95, 0x42, 0x69, 0x3c, 0x30, 0xc4, 0x71, 0xb3, 0x52, 0xf0, 0x21,
+ 0x4d, 0xa1, 0xd8, 0xba, 0x39, 0x7c, 0x1c, 0x9e, 0xa3, 0x24, 0x9d, 0xf2,
+ 0x83, 0x16, 0x98, 0xaa, 0x16, 0x7c, 0x43, 0x9b, 0x15, 0x5b, 0xb7, 0xae,
+ 0x34, 0x91, 0xfe, 0xd4, 0x62, 0x26, 0x18, 0x46, 0x9a, 0x3f, 0xeb, 0xc1,
+ 0xf9, 0xf1, 0x90, 0x57, 0xeb, 0xac, 0x7a, 0x0d, 0x8b, 0xdb, 0x72, 0x30,
+ 0x6a, 0x66, 0xd5, 0xe0, 0x46, 0xa3, 0x70, 0xdc, 0x68, 0xd9, 0xff, 0x04,
+ 0x48, 0x89, 0x77, 0xde, 0xb5, 0xe9, 0xfb, 0x67, 0x6d, 0x41, 0xe9, 0xbc,
+ 0x39, 0xbd, 0x32, 0xd9, 0x62, 0x02, 0xf1, 0xb1, 0xa8, 0x3d, 0x6e, 0x37,
+ 0x9c, 0xe2, 0x2f, 0xe2, 0xd3, 0xa2, 0x26, 0x8b, 0xc6, 0xb8, 0x55, 0x43,
+ 0x88, 0xe1, 0x23, 0x3e, 0xa5, 0xd2, 0x24, 0x39, 0x6a, 0x47, 0xab, 0x00,
+ 0xd4, 0xa1, 0xb3, 0xa9, 0x25, 0xfe, 0x0d, 0x3f, 0xa7, 0x1d, 0xba, 0xd3,
+ 0x51, 0xc1, 0x0b, 0xa4, 0xda, 0xac, 0x38, 0xef, 0x55, 0x50, 0x24, 0x05,
+ 0x65, 0x46, 0x93, 0x34, 0x4f, 0x2d, 0x8d, 0xad, 0xc6, 0xd4, 0x21, 0x19,
+ 0xd2, 0x8e, 0xca, 0x05, 0x61, 0x71, 0x07, 0x73, 0x47, 0xe5, 0x8a, 0x19,
+ 0x12, 0xbd, 0x04, 0x4d, 0xce, 0x4e, 0x9c, 0xa5, 0x48, 0xac, 0xbb, 0x26,
+ 0xf7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x09, 0x30, 0x82,
+ 0x01, 0x05, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0x53, 0x32, 0xd1, 0xb3, 0xcf, 0x7f, 0xfa, 0xe0, 0xf1,
+ 0xa0, 0x5d, 0x85, 0x4e, 0x92, 0xd2, 0x9e, 0x45, 0x1d, 0xb4, 0x4f, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x58,
+ 0xe5, 0x8b, 0xc6, 0x4c, 0x15, 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21,
+ 0xbe, 0x47, 0x36, 0x5a, 0x56, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x19,
+ 0x30, 0x17, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0a,
+ 0x03, 0x03, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04,
+ 0x01, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08,
+ 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x6d, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x66, 0x30, 0x64, 0x30, 0x31, 0xa0, 0x2f, 0xa0,
+ 0x2d, 0x86, 0x2b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x44, 0x41, 0x54, 0x41, 0x43,
+ 0x6f, 0x72, 0x70, 0x53, 0x47, 0x43, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x2f,
+ 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x44, 0x41, 0x54, 0x41,
+ 0x43, 0x6f, 0x72, 0x70, 0x53, 0x47, 0x43, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xd8, 0x5e, 0x92, 0xc4, 0xae,
+ 0x14, 0xdc, 0x43, 0xad, 0xc2, 0xa4, 0xc3, 0x67, 0x45, 0x07, 0x1d, 0xf9,
+ 0x37, 0xa2, 0x19, 0xc7, 0x1c, 0x37, 0x35, 0x91, 0x13, 0x1c, 0x07, 0xc4,
+ 0x7d, 0x42, 0xa6, 0x0e, 0xf0, 0x86, 0x5c, 0x43, 0x6b, 0x0e, 0x44, 0xcf,
+ 0xbe, 0x24, 0x61, 0x3a, 0x42, 0xa9, 0xce, 0x9d, 0x4c, 0xaf, 0x79, 0x39,
+ 0x70, 0xdd, 0x0e, 0x04, 0x20, 0x4e, 0x95, 0x9c, 0x3c, 0xde, 0xb7, 0x60,
+ 0xba, 0x63, 0x43, 0x40, 0xed, 0x6a, 0x0f, 0x81, 0x49, 0x46, 0xbb, 0x1e,
+ 0x93, 0xc0, 0x4b, 0xf3, 0xf8, 0xe1, 0x36, 0x49, 0x1b, 0x6f, 0xb6, 0x0c,
+ 0x0d, 0xf2, 0x90, 0x57, 0x8a, 0xfc, 0x6d, 0x93, 0xf2, 0x28, 0xc7, 0xfa,
+ 0x86, 0x0a, 0x28, 0xb3, 0x17, 0x0e, 0x59, 0x8a, 0x2e, 0xb6, 0xbf, 0xcd,
+ 0xe1, 0xac, 0x4c, 0x66, 0x6c, 0xf2, 0x55, 0x91, 0x56, 0xb7, 0x32, 0xbf,
+ 0xb1, 0xe4, 0x7d, 0xb5, 0xe8, 0x3a, 0xb6, 0x2f, 0xdb, 0xb2, 0x9c, 0xda,
+ 0x50, 0x93, 0x8e, 0x4e, 0xc5, 0xac, 0x9a, 0x7e, 0x5c, 0x9e, 0x12, 0x3c,
+ 0x3b, 0x4d, 0xc6, 0x50, 0x70, 0xb3, 0x65, 0x2b, 0x8e, 0xf7, 0x6b, 0xa1,
+ 0xbb, 0x25, 0xc0, 0x00, 0xbb, 0xf5, 0xec, 0x16, 0x65, 0x81, 0x0e, 0xfb,
+ 0xd4, 0xa3, 0x21, 0x96, 0x77, 0x9a, 0xa8, 0x74, 0xbc, 0x53, 0xaa, 0xc2,
+ 0x39, 0x50, 0xff, 0x0b, 0x02, 0x09, 0x61, 0xcc, 0x95, 0xb7, 0xd7, 0x88,
+ 0x6a, 0xf6, 0x5c, 0xc5, 0x68, 0xd3, 0x14, 0x95, 0x1a, 0x47, 0x5f, 0xd9,
+ 0xfb, 0x2d, 0xe4, 0x2f, 0x8f, 0x13, 0x86, 0xab, 0x31, 0x13, 0x40, 0x13,
+ 0xac, 0x6e, 0xed, 0xb5, 0x10, 0x30, 0x8b, 0x1b, 0x50, 0xa9, 0xce, 0xee,
+ 0x8c, 0xca, 0xeb, 0x7c, 0xb5, 0xb9, 0x16, 0x3d, 0xd4, 0xfa, 0x6f, 0x92,
+ 0x6d, 0x1e, 0xa2, 0xbd, 0xfb, 0x02, 0x4a, 0xc5, 0x70, 0xbe, 0xf1,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7f:71:c1:d3:a2:26:b0:d2:b1:13:f3:e6:81:67:64:3e
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Dec 7 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Internet2, OU=InCommon, CN=InCommon Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:97:7c:c7:c8:fe:b3:e9:20:6a:a3:a4:4f:8e:8e:
+ 34:56:06:b3:7a:6c:aa:10:9b:48:61:2b:36:90:69:
+ e3:34:0a:47:a7:bb:7b:de:aa:6a:fb:eb:82:95:8f:
+ ca:1d:7f:af:75:a6:a8:4c:da:20:67:61:1a:0d:86:
+ c1:ca:c1:87:af:ac:4e:e4:de:62:1b:2f:9d:b1:98:
+ af:c6:01:fb:17:70:db:ac:14:59:ec:6f:3f:33:7f:
+ a6:98:0b:e4:e2:38:af:f5:7f:85:6d:0e:74:04:9d:
+ f6:27:86:c7:9b:8f:e7:71:2a:08:f4:03:02:40:63:
+ 24:7d:40:57:8f:54:e0:54:7e:b6:13:48:61:f1:de:
+ ce:0e:bd:b6:fa:4d:98:b2:d9:0d:8d:79:a6:e0:aa:
+ cd:0c:91:9a:a5:df:ab:73:bb:ca:14:78:5c:47:29:
+ a1:ca:c5:ba:9f:c7:da:60:f7:ff:e7:7f:f2:d9:da:
+ a1:2d:0f:49:16:a7:d3:00:92:cf:8a:47:d9:4d:f8:
+ d5:95:66:d3:74:f9:80:63:00:4f:4c:84:16:1f:b3:
+ f5:24:1f:a1:4e:de:e8:95:d6:b2:0b:09:8b:2c:6b:
+ c7:5c:2f:8c:63:c9:99:cb:52:b1:62:7b:73:01:62:
+ 7f:63:6c:d8:68:a0:ee:6a:a8:8d:1f:29:f3:d0:18:
+ ac:ad
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 48:4F:5A:FA:2F:4A:9A:5E:E0:50:F3:6B:7B:55:A5:DE:F5:BE:34:5D
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 93:66:21:80:74:45:85:4b:c2:ab:ce:32:b0:29:fe:dd:df:d6:
+ 24:5b:bf:03:6a:6f:50:3e:0e:1b:b3:0d:88:a3:5b:ee:c4:a4:
+ 12:3b:56:ef:06:7f:cf:7f:21:95:56:3b:41:31:fe:e1:aa:93:
+ d2:95:f3:95:0d:3c:47:ab:ca:5c:26:ad:3e:f1:f9:8c:34:6e:
+ 11:be:f4:67:e3:02:49:f9:a6:7c:7b:64:25:dd:17:46:f2:50:
+ e3:e3:0a:21:3a:49:24:cd:c6:84:65:68:67:68:b0:45:2d:47:
+ 99:cd:9c:ab:86:29:11:72:dc:d6:9c:36:43:74:f3:d4:97:9e:
+ 56:a0:fe:5f:40:58:d2:d5:d7:7e:7c:c5:8e:1a:b2:04:5c:92:
+ 66:0e:85:ad:2e:06:ce:c8:a3:d8:eb:14:27:91:de:cf:17:30:
+ 81:53:b6:66:12:ad:37:e4:f5:ef:96:5c:20:0e:36:e9:ac:62:
+ 7d:19:81:8a:f5:90:61:a6:49:ab:ce:3c:df:e6:ca:64:ee:82:
+ 65:39:45:95:16:ba:41:06:00:98:ba:0c:56:61:e4:c6:c6:86:
+ 01:cf:66:a9:22:29:02:d6:3d:cf:c4:2a:8d:99:de:fb:09:14:
+ 9e:0e:d1:d5:c6:d7:81:dd:ad:24:ab:ac:07:05:e2:1d:68:c3:
+ 70:66:5f:d3
+-----BEGIN CERTIFICATE-----
+MIIEwzCCA6ugAwIBAgIQf3HB06ImsNKxE/PmgWdkPjANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMTIwNzAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+UTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUludGVybmV0MjERMA8GA1UECxMISW5D
+b21tb24xGzAZBgNVBAMTEkluQ29tbW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAJd8x8j+s+kgaqOkT46ONFYGs3psqhCbSGErNpBp
+4zQKR6e7e96qavvrgpWPyh1/r3WmqEzaIGdhGg2GwcrBh6+sTuTeYhsvnbGYr8YB
++xdw26wUWexvPzN/ppgL5OI4r/V/hW0OdASd9ieGx5uP53EqCPQDAkBjJH1AV49U
+4FR+thNIYfHezg69tvpNmLLZDY15puCqzQyRmqXfq3O7yhR4XEcpocrFup/H2mD3
+/+d/8tnaoS0PSRan0wCSz4pH2U341ZVm03T5gGMAT0yEFh+z9SQfoU7e6JXWsgsJ
+iyxrx1wvjGPJmctSsWJ7cwFif2Ns2Gig7mqojR8p89AYrK0CAwEAAaOCAXcwggFz
+MB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8DveAky1QaMB0GA1UdDgQWBBRIT1r6
+L0qaXuBQ82t7VaXe9b40XTAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
+/wIBADARBgNVHSAECjAIMAYGBFUdIAAwRAYDVR0fBD0wOzA5oDegNYYzaHR0cDov
+L2NybC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMIGz
+BggrBgEFBQcBAQSBpjCBozA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1
+c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsGAQUFBzAChi1o
+dHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0NDQS5jcnQwJQYI
+KwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEF
+BQADggEBAJNmIYB0RYVLwqvOMrAp/t3f1iRbvwNqb1A+DhuzDYijW+7EpBI7Vu8G
+f89/IZVWO0Ex/uGqk9KV85UNPEerylwmrT7x+Yw0bhG+9GfjAkn5pnx7ZCXdF0by
+UOPjCiE6SSTNxoRlaGdosEUtR5nNnKuGKRFy3NacNkN089SXnlag/l9AWNLV1358
+xY4asgRckmYOha0uBs7Io9jrFCeR3s8XMIFTtmYSrTfk9e+WXCAONumsYn0ZgYr1
+kGGmSavOPN/mymTugmU5RZUWukEGAJi6DFZh5MbGhgHPZqkiKQLWPc/EKo2Z3vsJ
+FJ4O0dXG14HdrSSrrAcF4h1ow3BmX9M=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert51[] = {
+ 0x30, 0x82, 0x04, 0xc3, 0x30, 0x82, 0x03, 0xab, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x7f, 0x71, 0xc1, 0xd3, 0xa2, 0x26, 0xb0, 0xd2, 0xb1,
+ 0x13, 0xf3, 0xe6, 0x81, 0x67, 0x64, 0x3e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x31, 0x32, 0x30,
+ 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x51, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x09, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x32, 0x31, 0x11,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x08, 0x49, 0x6e, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x12, 0x49, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+ 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x97, 0x7c, 0xc7, 0xc8, 0xfe,
+ 0xb3, 0xe9, 0x20, 0x6a, 0xa3, 0xa4, 0x4f, 0x8e, 0x8e, 0x34, 0x56, 0x06,
+ 0xb3, 0x7a, 0x6c, 0xaa, 0x10, 0x9b, 0x48, 0x61, 0x2b, 0x36, 0x90, 0x69,
+ 0xe3, 0x34, 0x0a, 0x47, 0xa7, 0xbb, 0x7b, 0xde, 0xaa, 0x6a, 0xfb, 0xeb,
+ 0x82, 0x95, 0x8f, 0xca, 0x1d, 0x7f, 0xaf, 0x75, 0xa6, 0xa8, 0x4c, 0xda,
+ 0x20, 0x67, 0x61, 0x1a, 0x0d, 0x86, 0xc1, 0xca, 0xc1, 0x87, 0xaf, 0xac,
+ 0x4e, 0xe4, 0xde, 0x62, 0x1b, 0x2f, 0x9d, 0xb1, 0x98, 0xaf, 0xc6, 0x01,
+ 0xfb, 0x17, 0x70, 0xdb, 0xac, 0x14, 0x59, 0xec, 0x6f, 0x3f, 0x33, 0x7f,
+ 0xa6, 0x98, 0x0b, 0xe4, 0xe2, 0x38, 0xaf, 0xf5, 0x7f, 0x85, 0x6d, 0x0e,
+ 0x74, 0x04, 0x9d, 0xf6, 0x27, 0x86, 0xc7, 0x9b, 0x8f, 0xe7, 0x71, 0x2a,
+ 0x08, 0xf4, 0x03, 0x02, 0x40, 0x63, 0x24, 0x7d, 0x40, 0x57, 0x8f, 0x54,
+ 0xe0, 0x54, 0x7e, 0xb6, 0x13, 0x48, 0x61, 0xf1, 0xde, 0xce, 0x0e, 0xbd,
+ 0xb6, 0xfa, 0x4d, 0x98, 0xb2, 0xd9, 0x0d, 0x8d, 0x79, 0xa6, 0xe0, 0xaa,
+ 0xcd, 0x0c, 0x91, 0x9a, 0xa5, 0xdf, 0xab, 0x73, 0xbb, 0xca, 0x14, 0x78,
+ 0x5c, 0x47, 0x29, 0xa1, 0xca, 0xc5, 0xba, 0x9f, 0xc7, 0xda, 0x60, 0xf7,
+ 0xff, 0xe7, 0x7f, 0xf2, 0xd9, 0xda, 0xa1, 0x2d, 0x0f, 0x49, 0x16, 0xa7,
+ 0xd3, 0x00, 0x92, 0xcf, 0x8a, 0x47, 0xd9, 0x4d, 0xf8, 0xd5, 0x95, 0x66,
+ 0xd3, 0x74, 0xf9, 0x80, 0x63, 0x00, 0x4f, 0x4c, 0x84, 0x16, 0x1f, 0xb3,
+ 0xf5, 0x24, 0x1f, 0xa1, 0x4e, 0xde, 0xe8, 0x95, 0xd6, 0xb2, 0x0b, 0x09,
+ 0x8b, 0x2c, 0x6b, 0xc7, 0x5c, 0x2f, 0x8c, 0x63, 0xc9, 0x99, 0xcb, 0x52,
+ 0xb1, 0x62, 0x7b, 0x73, 0x01, 0x62, 0x7f, 0x63, 0x6c, 0xd8, 0x68, 0xa0,
+ 0xee, 0x6a, 0xa8, 0x8d, 0x1f, 0x29, 0xf3, 0xd0, 0x18, 0xac, 0xad, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26,
+ 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, 0x4f, 0x5a, 0xfa,
+ 0x2f, 0x4a, 0x9a, 0x5e, 0xe0, 0x50, 0xf3, 0x6b, 0x7b, 0x55, 0xa5, 0xde,
+ 0xf5, 0xbe, 0x34, 0x5d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x0a, 0x30, 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30,
+ 0x44, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39,
+ 0xa0, 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43,
+ 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81,
+ 0xa6, 0x30, 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43,
+ 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53,
+ 0x47, 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x93, 0x66, 0x21, 0x80, 0x74,
+ 0x45, 0x85, 0x4b, 0xc2, 0xab, 0xce, 0x32, 0xb0, 0x29, 0xfe, 0xdd, 0xdf,
+ 0xd6, 0x24, 0x5b, 0xbf, 0x03, 0x6a, 0x6f, 0x50, 0x3e, 0x0e, 0x1b, 0xb3,
+ 0x0d, 0x88, 0xa3, 0x5b, 0xee, 0xc4, 0xa4, 0x12, 0x3b, 0x56, 0xef, 0x06,
+ 0x7f, 0xcf, 0x7f, 0x21, 0x95, 0x56, 0x3b, 0x41, 0x31, 0xfe, 0xe1, 0xaa,
+ 0x93, 0xd2, 0x95, 0xf3, 0x95, 0x0d, 0x3c, 0x47, 0xab, 0xca, 0x5c, 0x26,
+ 0xad, 0x3e, 0xf1, 0xf9, 0x8c, 0x34, 0x6e, 0x11, 0xbe, 0xf4, 0x67, 0xe3,
+ 0x02, 0x49, 0xf9, 0xa6, 0x7c, 0x7b, 0x64, 0x25, 0xdd, 0x17, 0x46, 0xf2,
+ 0x50, 0xe3, 0xe3, 0x0a, 0x21, 0x3a, 0x49, 0x24, 0xcd, 0xc6, 0x84, 0x65,
+ 0x68, 0x67, 0x68, 0xb0, 0x45, 0x2d, 0x47, 0x99, 0xcd, 0x9c, 0xab, 0x86,
+ 0x29, 0x11, 0x72, 0xdc, 0xd6, 0x9c, 0x36, 0x43, 0x74, 0xf3, 0xd4, 0x97,
+ 0x9e, 0x56, 0xa0, 0xfe, 0x5f, 0x40, 0x58, 0xd2, 0xd5, 0xd7, 0x7e, 0x7c,
+ 0xc5, 0x8e, 0x1a, 0xb2, 0x04, 0x5c, 0x92, 0x66, 0x0e, 0x85, 0xad, 0x2e,
+ 0x06, 0xce, 0xc8, 0xa3, 0xd8, 0xeb, 0x14, 0x27, 0x91, 0xde, 0xcf, 0x17,
+ 0x30, 0x81, 0x53, 0xb6, 0x66, 0x12, 0xad, 0x37, 0xe4, 0xf5, 0xef, 0x96,
+ 0x5c, 0x20, 0x0e, 0x36, 0xe9, 0xac, 0x62, 0x7d, 0x19, 0x81, 0x8a, 0xf5,
+ 0x90, 0x61, 0xa6, 0x49, 0xab, 0xce, 0x3c, 0xdf, 0xe6, 0xca, 0x64, 0xee,
+ 0x82, 0x65, 0x39, 0x45, 0x95, 0x16, 0xba, 0x41, 0x06, 0x00, 0x98, 0xba,
+ 0x0c, 0x56, 0x61, 0xe4, 0xc6, 0xc6, 0x86, 0x01, 0xcf, 0x66, 0xa9, 0x22,
+ 0x29, 0x02, 0xd6, 0x3d, 0xcf, 0xc4, 0x2a, 0x8d, 0x99, 0xde, 0xfb, 0x09,
+ 0x14, 0x9e, 0x0e, 0xd1, 0xd5, 0xc6, 0xd7, 0x81, 0xdd, 0xad, 0x24, 0xab,
+ 0xac, 0x07, 0x05, 0xe2, 0x1d, 0x68, 0xc3, 0x70, 0x66, 0x5f, 0xd3,
+};
diff --git a/net/quic/crypto/common_cert_set_51_100.inc b/net/quic/crypto/common_cert_set_51_100.inc
new file mode 100644
index 0000000..3c79622
--- /dev/null
+++ b/net/quic/crypto/common_cert_set_51_100.inc
@@ -0,0 +1,11308 @@
+/* Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/* This file contains common certificates. It's designed to be #included in
+ * another file, in a namespace. */
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 35:97:31:87:f3:87:3a:07:32:7e:ce:58:0c:9b:7e:da
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 0f:25:ae:48:ed:1b:33:85:4c:0c:b5:c2:d7:fe:4d:d6:83:28:
+ 4c:41:65:60:00:0b:77:48:71:82:fe:7f:db:5a:0e:20:cc:d2:
+ ea:47:bc:64:42:61:44:34:74:30:81:81:26:8a:4a:f7:44:5d:
+ 7e:34:80:a8:b8:83:e2:09:d7:6d:23:dd:89:ed:28:08:bd:63:
+ 5a:11:57:08:c4:9e:da:e2:68:28:af:dd:50:3c:ec:82:21:d8:
+ 00:c2:55:44:50:70:41:ad:83:17:79:ba:08:f3:2b:de:ed:34:
+ 1d:44:9e:d2:04:93:f4:cb:05:17:2d:09:2d:2d:63:ef:f6:26:
+ 0b:7b
+-----BEGIN CERTIFICATE-----
+MIIExjCCBC+gAwIBAgIQNZcxh/OHOgcyfs5YDJt+2jANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggGRMIIBjTAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwNAYD
+VR0lBC0wKwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBBggrBgEFBQcDAQYIKwYBBQUH
+AwIwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUr
+DgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNp
+Z24uY29tL3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhho
+dHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wDQYJKoZIhvcNAQEFBQADgYEADyWuSO0b
+M4VMDLXC1/5N1oMoTEFlYAALd0hxgv5/21oOIMzS6ke8ZEJhRDR0MIGBJopK90Rd
+fjSAqLiD4gnXbSPdie0oCL1jWhFXCMSe2uJoKK/dUDzsgiHYAMJVRFBwQa2DF3m6
+CPMr3u00HUSe0gST9MsFFy0JLS1j7/YmC3s=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert52[] = {
+ 0x30, 0x82, 0x04, 0xc6, 0x30, 0x82, 0x04, 0x2f, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x35, 0x97, 0x31, 0x87, 0xf3, 0x87, 0x3a, 0x07, 0x32,
+ 0x7e, 0xce, 0x58, 0x0c, 0x9b, 0x7e, 0xda, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x91, 0x30, 0x82, 0x01, 0x8d, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x34, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x02, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59,
+ 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
+ 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac,
+ 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b,
+ 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x0f, 0x25, 0xae, 0x48, 0xed, 0x1b,
+ 0x33, 0x85, 0x4c, 0x0c, 0xb5, 0xc2, 0xd7, 0xfe, 0x4d, 0xd6, 0x83, 0x28,
+ 0x4c, 0x41, 0x65, 0x60, 0x00, 0x0b, 0x77, 0x48, 0x71, 0x82, 0xfe, 0x7f,
+ 0xdb, 0x5a, 0x0e, 0x20, 0xcc, 0xd2, 0xea, 0x47, 0xbc, 0x64, 0x42, 0x61,
+ 0x44, 0x34, 0x74, 0x30, 0x81, 0x81, 0x26, 0x8a, 0x4a, 0xf7, 0x44, 0x5d,
+ 0x7e, 0x34, 0x80, 0xa8, 0xb8, 0x83, 0xe2, 0x09, 0xd7, 0x6d, 0x23, 0xdd,
+ 0x89, 0xed, 0x28, 0x08, 0xbd, 0x63, 0x5a, 0x11, 0x57, 0x08, 0xc4, 0x9e,
+ 0xda, 0xe2, 0x68, 0x28, 0xaf, 0xdd, 0x50, 0x3c, 0xec, 0x82, 0x21, 0xd8,
+ 0x00, 0xc2, 0x55, 0x44, 0x50, 0x70, 0x41, 0xad, 0x83, 0x17, 0x79, 0xba,
+ 0x08, 0xf3, 0x2b, 0xde, 0xed, 0x34, 0x1d, 0x44, 0x9e, 0xd2, 0x04, 0x93,
+ 0xf4, 0xcb, 0x05, 0x17, 0x2d, 0x09, 0x2d, 0x2d, 0x63, 0xef, 0xf6, 0x26,
+ 0x0b, 0x7b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 18:a2:23:6c:d7:27:c7:52:8d:f6:7b:4b:85:6e:ff:ed
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Jul 29 00:00:00 2010 GMT
+ Not After : Jul 28 23:59:59 2020 GMT
+ Subject: C=US, O=Thawte, Inc., CN=Thawte SGC CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cd:d9:e9:5c:55:4c:c6:fd:26:0d:3c:9d:56:3a:
+ 7a:46:02:05:eb:f0:c2:ad:be:12:2f:59:ff:67:35:
+ 29:d9:69:d9:4d:37:3e:6d:87:49:bc:bb:d5:16:62:
+ 44:29:71:96:5c:a6:27:e8:c5:9c:fc:19:0b:29:af:
+ 2e:5c:da:0b:8f:bf:ed:53:15:a7:82:35:30:5e:08:
+ 36:32:24:36:36:1a:e4:72:2b:c4:68:48:a4:78:1f:
+ 33:34:20:fe:97:6e:9c:ac:3a:fd:e6:fd:83:5f:75:
+ 83:71:5d:90:df:bd:48:57:6d:10:26:af:6f:41:d8:
+ cc:78:9e:3d:9c:85:28:89:43:31:ab:a7:6e:a1:bc:
+ 02:e6:be:8f:c3:63:a4:64:68:3b:1b:c3:da:33:c8:
+ 7b:5a:1f:d6:08:72:b2:36:34:18:d3:20:4f:98:e8:
+ 02:93:df:50:b2:67:c8:3d:96:64:55:c7:69:25:0a:
+ ba:21:36:70:d3:59:a8:82:d2:54:6d:4e:06:5a:e1:
+ d8:07:8d:35:b8:d0:16:a1:74:fe:4a:1b:70:a8:a9:
+ 43:9a:80:27:a0:40:b7:6f:f9:e3:a8:a8:1e:8a:93:
+ 3c:96:36:a7:88:e9:36:9d:c1:e3:ef:b6:7e:02:37:
+ 62:09:d7:8b:c6:70:d9:32:50:9a:b1:a7:1e:54:21:
+ 1e:49
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g5.crl
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-17
+ X509v3 Subject Key Identifier:
+ 24:C0:C0:A4:49:3C:52:0B:12:D8:92:0C:51:D1:87:A7:4D:54:75:2C
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 38:da:76:35:18:49:32:34:f0:b4:e8:28:08:45:eb:8f:62:3e:
+ 99:21:72:77:95:e0:36:82:b3:ff:ab:7f:12:6c:e1:1c:10:c9:
+ 54:98:e5:0c:31:74:cc:80:7a:a0:26:a7:45:c8:11:4c:76:e4:
+ d0:a9:b1:c8:92:a3:80:79:26:0d:8d:cf:c8:47:63:2d:13:3c:
+ c2:96:34:d7:00:42:3a:4a:8b:9e:17:a9:dc:c9:50:c5:40:e1:
+ 29:45:61:22:f5:b3:b0:88:78:8d:ae:a1:8d:50:6f:44:82:74:
+ 52:87:15:0c:1c:4e:f2:16:37:da:c1:05:69:d9:01:54:ee:cd:
+ 71:49:f6:6c:56:7c:75:73:e2:8a:9f:a6:69:d7:60:9f:04:c3:
+ a3:9f:81:60:b3:c5:bd:a5:55:d0:69:db:45:98:64:20:f2:c0:
+ 8b:8c:4e:e9:57:52:36:ab:bb:53:67:30:89:63:13:28:f3:44:
+ d1:43:76:b4:81:68:2a:07:21:3f:8f:f4:67:d3:08:a0:79:de:
+ cc:b9:53:2d:1f:44:d3:54:9c:a3:07:4d:8a:08:34:4d:dd:17:
+ 7a:fe:ad:6b:4b:99:b6:00:c9:62:76:7e:98:9a:a2:49:1c:86:
+ be:b2:55:95:2c:2d:27:21:bc:19:b0:f1:3e:ad:b6:d1:1a:de:
+ ed:b6:ee:35
+-----BEGIN CERTIFICATE-----
+MIIEyzCCA7OgAwIBAgIQGKIjbNcnx1KN9ntLhW7/7TANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTAwNzI5MDAwMDAwWhcNMjAwNzI4MjM1OTU5WjBBMQsw
+CQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMuMRswGQYDVQQDExJUaGF3
+dGUgU0dDIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN
+2elcVUzG/SYNPJ1WOnpGAgXr8MKtvhIvWf9nNSnZadlNNz5th0m8u9UWYkQpcZZc
+pifoxZz8GQspry5c2guPv+1TFaeCNTBeCDYyJDY2GuRyK8RoSKR4HzM0IP6Xbpys
+Ov3m/YNfdYNxXZDfvUhXbRAmr29B2Mx4nj2chSiJQzGrp26hvALmvo/DY6RkaDsb
+w9ozyHtaH9YIcrI2NBjTIE+Y6AKT31CyZ8g9lmRVx2klCrohNnDTWaiC0lRtTgZa
+4dgHjTW40BahdP5KG3CoqUOagCegQLdv+eOoqB6KkzyWNqeI6TadwePvtn4CN2IJ
+14vGcNkyUJqxpx5UIR5JAgMBAAGjggEzMIIBLzAyBggrBgEFBQcBAQQmMCQwIgYI
+KwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0ZS5jb20wEgYDVR0TAQH/BAgwBgEB
+/wIBADA0BgNVHR8ELTArMCmgJ6AlhiNodHRwOi8vY3JsLnZlcmlzaWduLmNvbS9w
+Y2EzLWc1LmNybDA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIGCWCGSAGG
++EIEAQYKYIZIAYb4RQEIATAOBgNVHQ8BAf8EBAMCAQYwKQYDVR0RBCIwIKQeMBwx
+GjAYBgNVBAMTEVZlcmlTaWduTVBLSS0yLTE3MB0GA1UdDgQWBBQkwMCkSTxSCxLY
+kgxR0YenTVR1LDAfBgNVHSMEGDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzANBgkq
+hkiG9w0BAQUFAAOCAQEAONp2NRhJMjTwtOgoCEXrj2I+mSFyd5XgNoKz/6t/Emzh
+HBDJVJjlDDF0zIB6oCanRcgRTHbk0KmxyJKjgHkmDY3PyEdjLRM8wpY01wBCOkqL
+nhep3MlQxUDhKUVhIvWzsIh4ja6hjVBvRIJ0UocVDBxO8hY32sEFadkBVO7NcUn2
+bFZ8dXPiip+maddgnwTDo5+BYLPFvaVV0GnbRZhkIPLAi4xO6VdSNqu7U2cwiWMT
+KPNE0UN2tIFoKgchP4/0Z9MIoHnezLlTLR9E01ScowdNigg0Td0Xev6ta0uZtgDJ
+YnZ+mJqiSRyGvrJVlSwtJyG8GbDxPq220Rre7bbuNQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert53[] = {
+ 0x30, 0x82, 0x04, 0xcb, 0x30, 0x82, 0x03, 0xb3, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x18, 0xa2, 0x23, 0x6c, 0xd7, 0x27, 0xc7, 0x52, 0x8d,
+ 0xf6, 0x7b, 0x4b, 0x85, 0x6e, 0xff, 0xed, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x37, 0x32, 0x39, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x41, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x15,
+ 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x54, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1b, 0x30,
+ 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x20, 0x53, 0x47, 0x43, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xcd,
+ 0xd9, 0xe9, 0x5c, 0x55, 0x4c, 0xc6, 0xfd, 0x26, 0x0d, 0x3c, 0x9d, 0x56,
+ 0x3a, 0x7a, 0x46, 0x02, 0x05, 0xeb, 0xf0, 0xc2, 0xad, 0xbe, 0x12, 0x2f,
+ 0x59, 0xff, 0x67, 0x35, 0x29, 0xd9, 0x69, 0xd9, 0x4d, 0x37, 0x3e, 0x6d,
+ 0x87, 0x49, 0xbc, 0xbb, 0xd5, 0x16, 0x62, 0x44, 0x29, 0x71, 0x96, 0x5c,
+ 0xa6, 0x27, 0xe8, 0xc5, 0x9c, 0xfc, 0x19, 0x0b, 0x29, 0xaf, 0x2e, 0x5c,
+ 0xda, 0x0b, 0x8f, 0xbf, 0xed, 0x53, 0x15, 0xa7, 0x82, 0x35, 0x30, 0x5e,
+ 0x08, 0x36, 0x32, 0x24, 0x36, 0x36, 0x1a, 0xe4, 0x72, 0x2b, 0xc4, 0x68,
+ 0x48, 0xa4, 0x78, 0x1f, 0x33, 0x34, 0x20, 0xfe, 0x97, 0x6e, 0x9c, 0xac,
+ 0x3a, 0xfd, 0xe6, 0xfd, 0x83, 0x5f, 0x75, 0x83, 0x71, 0x5d, 0x90, 0xdf,
+ 0xbd, 0x48, 0x57, 0x6d, 0x10, 0x26, 0xaf, 0x6f, 0x41, 0xd8, 0xcc, 0x78,
+ 0x9e, 0x3d, 0x9c, 0x85, 0x28, 0x89, 0x43, 0x31, 0xab, 0xa7, 0x6e, 0xa1,
+ 0xbc, 0x02, 0xe6, 0xbe, 0x8f, 0xc3, 0x63, 0xa4, 0x64, 0x68, 0x3b, 0x1b,
+ 0xc3, 0xda, 0x33, 0xc8, 0x7b, 0x5a, 0x1f, 0xd6, 0x08, 0x72, 0xb2, 0x36,
+ 0x34, 0x18, 0xd3, 0x20, 0x4f, 0x98, 0xe8, 0x02, 0x93, 0xdf, 0x50, 0xb2,
+ 0x67, 0xc8, 0x3d, 0x96, 0x64, 0x55, 0xc7, 0x69, 0x25, 0x0a, 0xba, 0x21,
+ 0x36, 0x70, 0xd3, 0x59, 0xa8, 0x82, 0xd2, 0x54, 0x6d, 0x4e, 0x06, 0x5a,
+ 0xe1, 0xd8, 0x07, 0x8d, 0x35, 0xb8, 0xd0, 0x16, 0xa1, 0x74, 0xfe, 0x4a,
+ 0x1b, 0x70, 0xa8, 0xa9, 0x43, 0x9a, 0x80, 0x27, 0xa0, 0x40, 0xb7, 0x6f,
+ 0xf9, 0xe3, 0xa8, 0xa8, 0x1e, 0x8a, 0x93, 0x3c, 0x96, 0x36, 0xa7, 0x88,
+ 0xe9, 0x36, 0x9d, 0xc1, 0xe3, 0xef, 0xb6, 0x7e, 0x02, 0x37, 0x62, 0x09,
+ 0xd7, 0x8b, 0xc6, 0x70, 0xd9, 0x32, 0x50, 0x9a, 0xb1, 0xa7, 0x1e, 0x54,
+ 0x21, 0x1e, 0x49, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x33,
+ 0x30, 0x82, 0x01, 0x2f, 0x30, 0x32, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x26, 0x30, 0x24, 0x30, 0x22, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x16, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x74, 0x68,
+ 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70,
+ 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x34,
+ 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x45, 0x01, 0x08, 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x29, 0x06, 0x03,
+ 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31,
+ 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32,
+ 0x2d, 0x31, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x24, 0xc0, 0xc0, 0xa4, 0x49, 0x3c, 0x52, 0x0b, 0x12, 0xd8,
+ 0x92, 0x0c, 0x51, 0xd1, 0x87, 0xa7, 0x4d, 0x54, 0x75, 0x2c, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f,
+ 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43,
+ 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x38, 0xda, 0x76, 0x35, 0x18, 0x49, 0x32, 0x34, 0xf0,
+ 0xb4, 0xe8, 0x28, 0x08, 0x45, 0xeb, 0x8f, 0x62, 0x3e, 0x99, 0x21, 0x72,
+ 0x77, 0x95, 0xe0, 0x36, 0x82, 0xb3, 0xff, 0xab, 0x7f, 0x12, 0x6c, 0xe1,
+ 0x1c, 0x10, 0xc9, 0x54, 0x98, 0xe5, 0x0c, 0x31, 0x74, 0xcc, 0x80, 0x7a,
+ 0xa0, 0x26, 0xa7, 0x45, 0xc8, 0x11, 0x4c, 0x76, 0xe4, 0xd0, 0xa9, 0xb1,
+ 0xc8, 0x92, 0xa3, 0x80, 0x79, 0x26, 0x0d, 0x8d, 0xcf, 0xc8, 0x47, 0x63,
+ 0x2d, 0x13, 0x3c, 0xc2, 0x96, 0x34, 0xd7, 0x00, 0x42, 0x3a, 0x4a, 0x8b,
+ 0x9e, 0x17, 0xa9, 0xdc, 0xc9, 0x50, 0xc5, 0x40, 0xe1, 0x29, 0x45, 0x61,
+ 0x22, 0xf5, 0xb3, 0xb0, 0x88, 0x78, 0x8d, 0xae, 0xa1, 0x8d, 0x50, 0x6f,
+ 0x44, 0x82, 0x74, 0x52, 0x87, 0x15, 0x0c, 0x1c, 0x4e, 0xf2, 0x16, 0x37,
+ 0xda, 0xc1, 0x05, 0x69, 0xd9, 0x01, 0x54, 0xee, 0xcd, 0x71, 0x49, 0xf6,
+ 0x6c, 0x56, 0x7c, 0x75, 0x73, 0xe2, 0x8a, 0x9f, 0xa6, 0x69, 0xd7, 0x60,
+ 0x9f, 0x04, 0xc3, 0xa3, 0x9f, 0x81, 0x60, 0xb3, 0xc5, 0xbd, 0xa5, 0x55,
+ 0xd0, 0x69, 0xdb, 0x45, 0x98, 0x64, 0x20, 0xf2, 0xc0, 0x8b, 0x8c, 0x4e,
+ 0xe9, 0x57, 0x52, 0x36, 0xab, 0xbb, 0x53, 0x67, 0x30, 0x89, 0x63, 0x13,
+ 0x28, 0xf3, 0x44, 0xd1, 0x43, 0x76, 0xb4, 0x81, 0x68, 0x2a, 0x07, 0x21,
+ 0x3f, 0x8f, 0xf4, 0x67, 0xd3, 0x08, 0xa0, 0x79, 0xde, 0xcc, 0xb9, 0x53,
+ 0x2d, 0x1f, 0x44, 0xd3, 0x54, 0x9c, 0xa3, 0x07, 0x4d, 0x8a, 0x08, 0x34,
+ 0x4d, 0xdd, 0x17, 0x7a, 0xfe, 0xad, 0x6b, 0x4b, 0x99, 0xb6, 0x00, 0xc9,
+ 0x62, 0x76, 0x7e, 0x98, 0x9a, 0xa2, 0x49, 0x1c, 0x86, 0xbe, 0xb2, 0x55,
+ 0x95, 0x2c, 0x2d, 0x27, 0x21, 0xbc, 0x19, 0xb0, 0xf1, 0x3e, 0xad, 0xb6,
+ 0xd1, 0x1a, 0xde, 0xed, 0xb6, 0xee, 0x35,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 25:0c:e8:e0:30:61:2e:9f:2b:89:f7:05:4d:7c:f8:fd
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ Signature Algorithm: sha1WithRSAEncryption
+ 13:02:dd:f8:e8:86:00:f2:5a:f8:f8:20:0c:59:88:62:07:ce:
+ ce:f7:4e:f9:bb:59:a1:98:e5:e1:38:dd:4e:bc:66:18:d3:ad:
+ eb:18:f2:0d:c9:6d:3e:4a:94:20:c3:3c:ba:bd:65:54:c6:af:
+ 44:b3:10:ad:2c:6b:3e:ab:d7:07:b6:b8:81:63:c5:f9:5e:2e:
+ e5:2a:67:ce:cd:33:0c:2a:d7:89:56:03:23:1f:b3:be:e8:3a:
+ 08:59:b4:ec:45:35:f7:8a:5b:ff:66:cf:50:af:c6:6d:57:8d:
+ 19:78:b7:b9:a2:d1:57:ea:1f:9a:4b:af:ba:c9:8e:12:7e:c6:
+ bd:ff
+-----BEGIN CERTIFICATE-----
+MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
+BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
+aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
+KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
+j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
+L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
+b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC
+BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA
+A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
+lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
+tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert54[] = {
+ 0x30, 0x82, 0x04, 0xd0, 0x30, 0x82, 0x04, 0x39, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x25, 0x0c, 0xe8, 0xe0, 0x30, 0x61, 0x2e, 0x9f, 0x2b,
+ 0x89, 0xf7, 0x05, 0x4d, 0x7c, 0xf8, 0xfd, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x9b, 0x30, 0x82, 0x01, 0x97, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x3d,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x6d, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f,
+ 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09,
+ 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30,
+ 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
+ 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80,
+ 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x25,
+ 0x04, 0x37, 0x30, 0x35, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x81, 0x81, 0x00, 0x13, 0x02, 0xdd, 0xf8, 0xe8, 0x86, 0x00, 0xf2,
+ 0x5a, 0xf8, 0xf8, 0x20, 0x0c, 0x59, 0x88, 0x62, 0x07, 0xce, 0xce, 0xf7,
+ 0x4e, 0xf9, 0xbb, 0x59, 0xa1, 0x98, 0xe5, 0xe1, 0x38, 0xdd, 0x4e, 0xbc,
+ 0x66, 0x18, 0xd3, 0xad, 0xeb, 0x18, 0xf2, 0x0d, 0xc9, 0x6d, 0x3e, 0x4a,
+ 0x94, 0x20, 0xc3, 0x3c, 0xba, 0xbd, 0x65, 0x54, 0xc6, 0xaf, 0x44, 0xb3,
+ 0x10, 0xad, 0x2c, 0x6b, 0x3e, 0xab, 0xd7, 0x07, 0xb6, 0xb8, 0x81, 0x63,
+ 0xc5, 0xf9, 0x5e, 0x2e, 0xe5, 0x2a, 0x67, 0xce, 0xcd, 0x33, 0x0c, 0x2a,
+ 0xd7, 0x89, 0x56, 0x03, 0x23, 0x1f, 0xb3, 0xbe, 0xe8, 0x3a, 0x08, 0x59,
+ 0xb4, 0xec, 0x45, 0x35, 0xf7, 0x8a, 0x5b, 0xff, 0x66, 0xcf, 0x50, 0xaf,
+ 0xc6, 0x6d, 0x57, 0x8d, 0x19, 0x78, 0xb7, 0xb9, 0xa2, 0xd1, 0x57, 0xea,
+ 0x1f, 0x9a, 0x4b, 0xaf, 0xba, 0xc9, 0x8e, 0x12, 0x7e, 0xc6, 0xbd, 0xff,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 48:fc:4b:0a:37:06:ff:46:fe:d3:de:5d:4c:1e:ca:62
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Nov 26 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Network Solutions L.L.C., CN=Network Solutions DV Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a7:57:66:35:dc:90:4e:78:74:65:a8:da:ac:e8:
+ e9:62:65:46:ca:64:f0:d0:c3:a7:42:5b:8c:e1:8e:
+ 00:05:2b:53:e4:1b:84:22:b4:df:57:b0:40:8f:17:
+ 92:7e:31:97:1e:f5:ad:f0:80:99:db:97:30:95:35:
+ 0d:64:41:df:c4:b3:82:79:cd:bc:96:e2:fd:00:29:
+ c5:3e:be:7c:08:6c:be:fe:90:b1:15:39:21:86:34:
+ 40:bd:9c:9d:fa:6a:e5:2a:68:45:0e:68:e0:e8:b0:
+ 08:65:84:36:31:9c:46:e1:4e:cb:3f:58:83:f3:6c:
+ 8e:34:19:82:53:26:2c:8d:ab:92:22:5f:05:a1:3d:
+ 9b:ae:67:b4:56:c0:f9:97:78:c0:b5:98:15:0c:ad:
+ 03:ad:ff:78:8f:2f:26:7c:3a:dc:94:00:87:c3:7e:
+ c2:b6:a8:8c:0b:1d:1d:0f:8c:b5:d0:fb:93:3a:38:
+ f6:08:fe:3b:8d:66:6b:45:c6:5f:b2:7b:f0:14:f9:
+ 81:75:de:0b:4b:83:cb:ee:77:bb:9c:7e:9b:9d:27:
+ d8:90:06:9d:cf:4b:3c:2b:fa:bf:01:0a:c5:6d:1c:
+ 5a:60:68:92:f9:0e:43:fb:f2:88:78:96:e5:53:4b:
+ 51:f6:b1:e7:6d:f7:c6:ff:4f:d7:03:7b:73:f2:60:
+ 0a:21
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 58:D8:25:92:A4:55:5A:6E:D9:A3:D1:A3:7C:0C:AA:04:21:71:2E:60
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.782.1.2.1.9.1
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 27:94:9f:7e:7f:fc:73:48:f2:38:1f:b0:05:bf:71:dc:3b:a9:
+ 7e:c3:10:86:25:2d:14:ee:44:9d:4a:8d:f2:b3:3a:c5:66:fa:
+ 02:bf:d5:f0:00:16:77:c9:74:d7:88:c0:b1:18:7a:f3:4e:13:
+ 31:70:6f:46:70:41:e1:1a:42:3e:aa:5f:46:18:2d:85:0c:3b:
+ bb:fe:cf:02:d6:cf:ae:db:1a:93:52:74:6c:9e:fa:b2:ee:af:
+ 2f:7d:07:42:17:7d:31:e5:6a:36:28:2b:fd:d4:72:f1:fe:b9:
+ c5:f7:f0:72:61:e0:9d:bc:ca:eb:45:0b:b8:68:09:01:1b:4d:
+ 73:7f:df:e6:93:ba:1d:fc:6b:28:b3:64:30:bb:d0:3a:aa:35:
+ 6b:0b:83:61:68:d7:32:5a:49:de:1a:d1:fc:6d:8b:a0:dc:fa:
+ 7a:a4:92:7f:74:e2:0d:92:a0:9e:b8:46:1c:62:63:b0:b8:08:
+ c4:fd:b0:b4:9f:24:09:b3:2d:9c:75:14:77:4a:6e:c4:63:c1:
+ 4d:13:86:ce:98:72:1d:3d:b9:c6:4e:73:30:e4:c6:73:a2:d1:
+ f7:90:e4:90:cc:e1:3a:37:d6:53:02:5f:45:2d:2f:a6:4f:49:
+ 41:ea:df:8f:2f:97:1c:76:db:78:40:63:cb:e4:d5:d7:53:38:
+ 0e:11:10:38
+-----BEGIN CERTIFICATE-----
+MIIE0zCCA7ugAwIBAgIQSPxLCjcG/0b+095dTB7KYjANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMTEyNjAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+WTELMAkGA1UEBhMCVVMxITAfBgNVBAoTGE5ldHdvcmsgU29sdXRpb25zIEwuTC5D
+LjEnMCUGA1UEAxMeTmV0d29yayBTb2x1dGlvbnMgRFYgU2VydmVyIENBMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp1dmNdyQTnh0ZajarOjpYmVGymTw
+0MOnQluM4Y4ABStT5BuEIrTfV7BAjxeSfjGXHvWt8ICZ25cwlTUNZEHfxLOCec28
+luL9ACnFPr58CGy+/pCxFTkhhjRAvZyd+mrlKmhFDmjg6LAIZYQ2MZxG4U7LP1iD
+82yONBmCUyYsjauSIl8FoT2brme0VsD5l3jAtZgVDK0Drf94jy8mfDrclACHw37C
+tqiMCx0dD4y10PuTOjj2CP47jWZrRcZfsnvwFPmBdd4LS4PL7ne7nH6bnSfYkAad
+z0s8K/q/AQrFbRxaYGiS+Q5D+/KIeJblU0tR9rHnbffG/0/XA3tz8mAKIQIDAQAB
+o4IBfzCCAXswHwYDVR0jBBgwFoAUrb2YejS0Jvf6xCZU7wO94CTLVBowHQYDVR0O
+BBYEFFjYJZKkVVpu2aPRo3wMqgQhcS5gMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMBkGA1UdIAQSMBAwDgYMKwYBBAGGDgECAQkBMEQGA1UdHwQ9
+MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
+bmFsQ0FSb290LmNybDCBswYIKwYBBQUHAQEEgaYwgaMwPwYIKwYBBQUHMAKGM2h0
+dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LnA3
+YzA5BggrBgEFBQcwAoYtaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0
+VVROU0dDQ0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3Qu
+Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAnlJ9+f/xzSPI4H7AFv3HcO6l+wxCGJS0U
+7kSdSo3yszrFZvoCv9XwABZ3yXTXiMCxGHrzThMxcG9GcEHhGkI+ql9GGC2FDDu7
+/s8C1s+u2xqTUnRsnvqy7q8vfQdCF30x5Wo2KCv91HLx/rnF9/ByYeCdvMrrRQu4
+aAkBG01zf9/mk7od/Gsos2Qwu9A6qjVrC4NhaNcyWkneGtH8bYug3Pp6pJJ/dOIN
+kqCeuEYcYmOwuAjE/bC0nyQJsy2cdRR3Sm7EY8FNE4bOmHIdPbnGTnMw5MZzotH3
+kOSQzOE6N9ZTAl9FLS+mT0lB6t+PL5ccdtt4QGPL5NXXUzgOERA4
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert55[] = {
+ 0x30, 0x82, 0x04, 0xd3, 0x30, 0x82, 0x03, 0xbb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x48, 0xfc, 0x4b, 0x0a, 0x37, 0x06, 0xff, 0x46, 0xfe,
+ 0xd3, 0xde, 0x5d, 0x4c, 0x1e, 0xca, 0x62, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x31, 0x31, 0x32,
+ 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x59, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c,
+ 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43,
+ 0x2e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x44, 0x56, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0xa7, 0x57, 0x66, 0x35, 0xdc, 0x90, 0x4e, 0x78, 0x74,
+ 0x65, 0xa8, 0xda, 0xac, 0xe8, 0xe9, 0x62, 0x65, 0x46, 0xca, 0x64, 0xf0,
+ 0xd0, 0xc3, 0xa7, 0x42, 0x5b, 0x8c, 0xe1, 0x8e, 0x00, 0x05, 0x2b, 0x53,
+ 0xe4, 0x1b, 0x84, 0x22, 0xb4, 0xdf, 0x57, 0xb0, 0x40, 0x8f, 0x17, 0x92,
+ 0x7e, 0x31, 0x97, 0x1e, 0xf5, 0xad, 0xf0, 0x80, 0x99, 0xdb, 0x97, 0x30,
+ 0x95, 0x35, 0x0d, 0x64, 0x41, 0xdf, 0xc4, 0xb3, 0x82, 0x79, 0xcd, 0xbc,
+ 0x96, 0xe2, 0xfd, 0x00, 0x29, 0xc5, 0x3e, 0xbe, 0x7c, 0x08, 0x6c, 0xbe,
+ 0xfe, 0x90, 0xb1, 0x15, 0x39, 0x21, 0x86, 0x34, 0x40, 0xbd, 0x9c, 0x9d,
+ 0xfa, 0x6a, 0xe5, 0x2a, 0x68, 0x45, 0x0e, 0x68, 0xe0, 0xe8, 0xb0, 0x08,
+ 0x65, 0x84, 0x36, 0x31, 0x9c, 0x46, 0xe1, 0x4e, 0xcb, 0x3f, 0x58, 0x83,
+ 0xf3, 0x6c, 0x8e, 0x34, 0x19, 0x82, 0x53, 0x26, 0x2c, 0x8d, 0xab, 0x92,
+ 0x22, 0x5f, 0x05, 0xa1, 0x3d, 0x9b, 0xae, 0x67, 0xb4, 0x56, 0xc0, 0xf9,
+ 0x97, 0x78, 0xc0, 0xb5, 0x98, 0x15, 0x0c, 0xad, 0x03, 0xad, 0xff, 0x78,
+ 0x8f, 0x2f, 0x26, 0x7c, 0x3a, 0xdc, 0x94, 0x00, 0x87, 0xc3, 0x7e, 0xc2,
+ 0xb6, 0xa8, 0x8c, 0x0b, 0x1d, 0x1d, 0x0f, 0x8c, 0xb5, 0xd0, 0xfb, 0x93,
+ 0x3a, 0x38, 0xf6, 0x08, 0xfe, 0x3b, 0x8d, 0x66, 0x6b, 0x45, 0xc6, 0x5f,
+ 0xb2, 0x7b, 0xf0, 0x14, 0xf9, 0x81, 0x75, 0xde, 0x0b, 0x4b, 0x83, 0xcb,
+ 0xee, 0x77, 0xbb, 0x9c, 0x7e, 0x9b, 0x9d, 0x27, 0xd8, 0x90, 0x06, 0x9d,
+ 0xcf, 0x4b, 0x3c, 0x2b, 0xfa, 0xbf, 0x01, 0x0a, 0xc5, 0x6d, 0x1c, 0x5a,
+ 0x60, 0x68, 0x92, 0xf9, 0x0e, 0x43, 0xfb, 0xf2, 0x88, 0x78, 0x96, 0xe5,
+ 0x53, 0x4b, 0x51, 0xf6, 0xb1, 0xe7, 0x6d, 0xf7, 0xc6, 0xff, 0x4f, 0xd7,
+ 0x03, 0x7b, 0x73, 0xf2, 0x60, 0x0a, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x01, 0x7f, 0x30, 0x82, 0x01, 0x7b, 0x30, 0x1f, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98,
+ 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd,
+ 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x58, 0xd8, 0x25, 0x92, 0xa4, 0x55, 0x5a, 0x6e,
+ 0xd9, 0xa3, 0xd1, 0xa3, 0x7c, 0x0c, 0xaa, 0x04, 0x21, 0x71, 0x2e, 0x60,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x19, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x12, 0x30, 0x10, 0x30,
+ 0x0e, 0x06, 0x0c, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x86, 0x0e, 0x01, 0x02,
+ 0x01, 0x09, 0x01, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d,
+ 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41,
+ 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x33, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41,
+ 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37,
+ 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x27,
+ 0x94, 0x9f, 0x7e, 0x7f, 0xfc, 0x73, 0x48, 0xf2, 0x38, 0x1f, 0xb0, 0x05,
+ 0xbf, 0x71, 0xdc, 0x3b, 0xa9, 0x7e, 0xc3, 0x10, 0x86, 0x25, 0x2d, 0x14,
+ 0xee, 0x44, 0x9d, 0x4a, 0x8d, 0xf2, 0xb3, 0x3a, 0xc5, 0x66, 0xfa, 0x02,
+ 0xbf, 0xd5, 0xf0, 0x00, 0x16, 0x77, 0xc9, 0x74, 0xd7, 0x88, 0xc0, 0xb1,
+ 0x18, 0x7a, 0xf3, 0x4e, 0x13, 0x31, 0x70, 0x6f, 0x46, 0x70, 0x41, 0xe1,
+ 0x1a, 0x42, 0x3e, 0xaa, 0x5f, 0x46, 0x18, 0x2d, 0x85, 0x0c, 0x3b, 0xbb,
+ 0xfe, 0xcf, 0x02, 0xd6, 0xcf, 0xae, 0xdb, 0x1a, 0x93, 0x52, 0x74, 0x6c,
+ 0x9e, 0xfa, 0xb2, 0xee, 0xaf, 0x2f, 0x7d, 0x07, 0x42, 0x17, 0x7d, 0x31,
+ 0xe5, 0x6a, 0x36, 0x28, 0x2b, 0xfd, 0xd4, 0x72, 0xf1, 0xfe, 0xb9, 0xc5,
+ 0xf7, 0xf0, 0x72, 0x61, 0xe0, 0x9d, 0xbc, 0xca, 0xeb, 0x45, 0x0b, 0xb8,
+ 0x68, 0x09, 0x01, 0x1b, 0x4d, 0x73, 0x7f, 0xdf, 0xe6, 0x93, 0xba, 0x1d,
+ 0xfc, 0x6b, 0x28, 0xb3, 0x64, 0x30, 0xbb, 0xd0, 0x3a, 0xaa, 0x35, 0x6b,
+ 0x0b, 0x83, 0x61, 0x68, 0xd7, 0x32, 0x5a, 0x49, 0xde, 0x1a, 0xd1, 0xfc,
+ 0x6d, 0x8b, 0xa0, 0xdc, 0xfa, 0x7a, 0xa4, 0x92, 0x7f, 0x74, 0xe2, 0x0d,
+ 0x92, 0xa0, 0x9e, 0xb8, 0x46, 0x1c, 0x62, 0x63, 0xb0, 0xb8, 0x08, 0xc4,
+ 0xfd, 0xb0, 0xb4, 0x9f, 0x24, 0x09, 0xb3, 0x2d, 0x9c, 0x75, 0x14, 0x77,
+ 0x4a, 0x6e, 0xc4, 0x63, 0xc1, 0x4d, 0x13, 0x86, 0xce, 0x98, 0x72, 0x1d,
+ 0x3d, 0xb9, 0xc6, 0x4e, 0x73, 0x30, 0xe4, 0xc6, 0x73, 0xa2, 0xd1, 0xf7,
+ 0x90, 0xe4, 0x90, 0xcc, 0xe1, 0x3a, 0x37, 0xd6, 0x53, 0x02, 0x5f, 0x45,
+ 0x2d, 0x2f, 0xa6, 0x4f, 0x49, 0x41, 0xea, 0xdf, 0x8f, 0x2f, 0x97, 0x1c,
+ 0x76, 0xdb, 0x78, 0x40, 0x63, 0xcb, 0xe4, 0xd5, 0xd7, 0x53, 0x38, 0x0e,
+ 0x11, 0x10, 0x38,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 769 (0x301)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority
+ Validity
+ Not Before: Nov 16 01:54:37 2006 GMT
+ Not After : Nov 16 01:54:37 2026 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=GoDaddy.com, Inc., OU=http://certificates.godaddy.com/repository, CN=Go Daddy Secure Certification Authority/serialNumber=07969287
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c4:2d:d5:15:8c:9c:26:4c:ec:32:35:eb:5f:b8:
+ 59:01:5a:a6:61:81:59:3b:70:63:ab:e3:dc:3d:c7:
+ 2a:b8:c9:33:d3:79:e4:3a:ed:3c:30:23:84:8e:b3:
+ 30:14:b6:b2:87:c3:3d:95:54:04:9e:df:99:dd:0b:
+ 25:1e:21:de:65:29:7e:35:a8:a9:54:eb:f6:f7:32:
+ 39:d4:26:55:95:ad:ef:fb:fe:58:86:d7:9e:f4:00:
+ 8d:8c:2a:0c:bd:42:04:ce:a7:3f:04:f6:ee:80:f2:
+ aa:ef:52:a1:69:66:da:be:1a:ad:5d:da:2c:66:ea:
+ 1a:6b:bb:e5:1a:51:4a:00:2f:48:c7:98:75:d8:b9:
+ 29:c8:ee:f8:66:6d:0a:9c:b3:f3:fc:78:7c:a2:f8:
+ a3:f2:b5:c3:f3:b9:7a:91:c1:a7:e6:25:2e:9c:a8:
+ ed:12:65:6e:6a:f6:12:44:53:70:30:95:c3:9c:2b:
+ 58:2b:3d:08:74:4a:f2:be:51:b0:bf:87:d0:4c:27:
+ 58:6b:b5:35:c5:9d:af:17:31:f8:0b:8f:ee:ad:81:
+ 36:05:89:08:98:cf:3a:af:25:87:c0:49:ea:a7:fd:
+ 67:f7:45:8e:97:cc:14:39:e2:36:85:b5:7e:1a:37:
+ fd:16:f6:71:11:9a:74:30:16:fe:13:94:a3:3f:84:
+ 0d:4f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ FD:AC:61:32:93:6C:45:D6:E2:EE:85:5F:9A:BA:E7:76:99:68:CC:E7
+ X509v3 Authority Key Identifier:
+ keyid:D2:C4:B0:D2:91:D4:4C:11:71:B3:61:CB:3D:A1:FE:DD:A8:6A:D4:E3
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.godaddy.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://certificates.godaddy.com/repository/gdroot.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://certificates.godaddy.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ d2:86:c0:ec:bd:f9:a1:b6:67:ee:66:0b:a2:06:3a:04:50:8e:
+ 15:72:ac:4a:74:95:53:cb:37:cb:44:49:ef:07:90:6b:33:d9:
+ 96:f0:94:56:a5:13:30:05:3c:85:32:21:7b:c9:c7:0a:a8:24:
+ a4:90:de:46:d3:25:23:14:03:67:c2:10:d6:6f:0f:5d:7b:7a:
+ cc:9f:c5:58:2a:c1:c4:9e:21:a8:5a:f3:ac:a4:46:f3:9e:e4:
+ 63:cb:2f:90:a4:29:29:01:d9:72:2c:29:df:37:01:27:bc:4f:
+ ee:68:d3:21:8f:c0:b3:e4:f5:09:ed:d2:10:aa:53:b4:be:f0:
+ cc:59:0b:d6:3b:96:1c:95:24:49:df:ce:ec:fd:a7:48:91:14:
+ 45:0e:3a:36:6f:da:45:b3:45:a2:41:c9:d4:d7:44:4e:3e:b9:
+ 74:76:d5:a2:13:55:2c:c6:87:a3:b5:99:ac:06:84:87:7f:75:
+ 06:fc:bf:14:4c:0e:cc:6e:c4:df:3d:b7:12:71:f4:e8:f1:51:
+ 40:22:28:49:e0:1d:4b:87:a8:34:cc:06:a2:dd:12:5a:d1:86:
+ 36:64:03:35:6f:6f:77:6e:eb:f2:85:50:98:5e:ab:03:53:ad:
+ 91:23:63:1f:16:9c:cd:b9:b2:05:63:3a:e1:f4:68:1b:17:05:
+ 35:95:53:ee
+-----BEGIN CERTIFICATE-----
+MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
+ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
+RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
+MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
+QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
+b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
+b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
+YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
+KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
+VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
+SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
+cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
+6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
+MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
+kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
+BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
+BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
+c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
+AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
+BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
+OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
+A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
+0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
+RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
+qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
+U+4=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert56[] = {
+ 0x30, 0x82, 0x04, 0xde, 0x30, 0x82, 0x03, 0xc6, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x03, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x63, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x54, 0x68,
+ 0x65, 0x20, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x47,
+ 0x72, 0x6f, 0x75, 0x70, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31,
+ 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x28, 0x47, 0x6f, 0x20,
+ 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, 0x36, 0x30,
+ 0x31, 0x35, 0x34, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x36, 0x31, 0x31,
+ 0x31, 0x36, 0x30, 0x31, 0x35, 0x34, 0x33, 0x37, 0x5a, 0x30, 0x81, 0xca,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x07,
+ 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, 0x6f, 0x74, 0x74, 0x73,
+ 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x33, 0x30, 0x31,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27,
+ 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x05,
+ 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, 0x38, 0x37, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
+ 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c,
+ 0x9c, 0x26, 0x4c, 0xec, 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a,
+ 0xa6, 0x61, 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7,
+ 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c, 0x30, 0x23,
+ 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87, 0xc3, 0x3d, 0x95, 0x54,
+ 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b, 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29,
+ 0x7e, 0x35, 0xa8, 0xa9, 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26,
+ 0x55, 0x95, 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00,
+ 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f, 0x04, 0xf6,
+ 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69, 0x66, 0xda, 0xbe, 0x1a,
+ 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea, 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51,
+ 0x4a, 0x00, 0x2f, 0x48, 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee,
+ 0xf8, 0x66, 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8,
+ 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7, 0xe6, 0x25,
+ 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a, 0xf6, 0x12, 0x44, 0x53,
+ 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b, 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a,
+ 0xf2, 0xbe, 0x51, 0xb0, 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5,
+ 0x35, 0xc5, 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81,
+ 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87, 0xc0, 0x49,
+ 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97, 0xcc, 0x14, 0x39, 0xe2,
+ 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37, 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a,
+ 0x74, 0x30, 0x16, 0xfe, 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f, 0x02,
+ 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x32, 0x30, 0x82, 0x01, 0x2e,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xfd,
+ 0xac, 0x61, 0x32, 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a,
+ 0xba, 0xe7, 0x76, 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xd2, 0xc4, 0xb0, 0xd2,
+ 0x91, 0xd4, 0x4c, 0x11, 0x71, 0xb3, 0x61, 0xcb, 0x3d, 0xa1, 0xfe, 0xdd,
+ 0xa8, 0x6a, 0xd4, 0xe3, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00,
+ 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64,
+ 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x46, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x3f, 0x30, 0x3d, 0x30, 0x3b, 0xa0, 0x39, 0xa0, 0x37, 0x86, 0x35,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61,
+ 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x72, 0x6f, 0x6f,
+ 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4b, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e,
+ 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xd2, 0x86,
+ 0xc0, 0xec, 0xbd, 0xf9, 0xa1, 0xb6, 0x67, 0xee, 0x66, 0x0b, 0xa2, 0x06,
+ 0x3a, 0x04, 0x50, 0x8e, 0x15, 0x72, 0xac, 0x4a, 0x74, 0x95, 0x53, 0xcb,
+ 0x37, 0xcb, 0x44, 0x49, 0xef, 0x07, 0x90, 0x6b, 0x33, 0xd9, 0x96, 0xf0,
+ 0x94, 0x56, 0xa5, 0x13, 0x30, 0x05, 0x3c, 0x85, 0x32, 0x21, 0x7b, 0xc9,
+ 0xc7, 0x0a, 0xa8, 0x24, 0xa4, 0x90, 0xde, 0x46, 0xd3, 0x25, 0x23, 0x14,
+ 0x03, 0x67, 0xc2, 0x10, 0xd6, 0x6f, 0x0f, 0x5d, 0x7b, 0x7a, 0xcc, 0x9f,
+ 0xc5, 0x58, 0x2a, 0xc1, 0xc4, 0x9e, 0x21, 0xa8, 0x5a, 0xf3, 0xac, 0xa4,
+ 0x46, 0xf3, 0x9e, 0xe4, 0x63, 0xcb, 0x2f, 0x90, 0xa4, 0x29, 0x29, 0x01,
+ 0xd9, 0x72, 0x2c, 0x29, 0xdf, 0x37, 0x01, 0x27, 0xbc, 0x4f, 0xee, 0x68,
+ 0xd3, 0x21, 0x8f, 0xc0, 0xb3, 0xe4, 0xf5, 0x09, 0xed, 0xd2, 0x10, 0xaa,
+ 0x53, 0xb4, 0xbe, 0xf0, 0xcc, 0x59, 0x0b, 0xd6, 0x3b, 0x96, 0x1c, 0x95,
+ 0x24, 0x49, 0xdf, 0xce, 0xec, 0xfd, 0xa7, 0x48, 0x91, 0x14, 0x45, 0x0e,
+ 0x3a, 0x36, 0x6f, 0xda, 0x45, 0xb3, 0x45, 0xa2, 0x41, 0xc9, 0xd4, 0xd7,
+ 0x44, 0x4e, 0x3e, 0xb9, 0x74, 0x76, 0xd5, 0xa2, 0x13, 0x55, 0x2c, 0xc6,
+ 0x87, 0xa3, 0xb5, 0x99, 0xac, 0x06, 0x84, 0x87, 0x7f, 0x75, 0x06, 0xfc,
+ 0xbf, 0x14, 0x4c, 0x0e, 0xcc, 0x6e, 0xc4, 0xdf, 0x3d, 0xb7, 0x12, 0x71,
+ 0xf4, 0xe8, 0xf1, 0x51, 0x40, 0x22, 0x28, 0x49, 0xe0, 0x1d, 0x4b, 0x87,
+ 0xa8, 0x34, 0xcc, 0x06, 0xa2, 0xdd, 0x12, 0x5a, 0xd1, 0x86, 0x36, 0x64,
+ 0x03, 0x35, 0x6f, 0x6f, 0x77, 0x6e, 0xeb, 0xf2, 0x85, 0x50, 0x98, 0x5e,
+ 0xab, 0x03, 0x53, 0xad, 0x91, 0x23, 0x63, 0x1f, 0x16, 0x9c, 0xcd, 0xb9,
+ 0xb2, 0x05, 0x63, 0x3a, 0xe1, 0xf4, 0x68, 0x1b, 0x17, 0x05, 0x35, 0x95,
+ 0x53, 0xee,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6e:ba:f0:8f:79:83:fa:9d:e1:b2:6f:96:fc:6e:98:bf
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Aug 23 00:00:00 2011 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d4:2b:2e:1c:d2:a3:f8:7f:55:14:40:de:f7:44:
+ dd:84:55:f7:85:7b:55:66:69:a7:e5:59:eb:65:83:
+ f4:f3:76:b1:66:c3:4f:4e:98:93:09:b7:40:b3:d1:
+ 17:a0:12:09:a8:80:e1:29:63:97:02:8c:31:9d:0a:
+ 02:e0:59:5b:bb:ed:30:b5:ef:7e:5d:af:08:4e:8d:
+ 8b:c2:39:56:16:98:73:94:78:0a:c9:a6:4f:28:b7:
+ a8:34:37:db:25:21:b1:3c:99:f6:e0:12:3e:73:ea:
+ 64:32:9f:42:06:3c:19:d8:0a:04:7a:4c:57:49:2b:
+ d2:77:7a:d0:00:bc:5e:fa:8e:ee:cc:c2:e4:13:6e:
+ 25:5f:dc:3c:a4:88:a3:dc:49:c7:bc:c7:0f:dd:19:
+ c0:b1:72:ed:78:ef:38:83:0a:45:17:1b:c9:7d:9d:
+ ed:df:ab:2c:2c:a3:75:ae:5b:82:1d:88:83:8d:ce:
+ 08:65:0c:66:26:57:05:a1:0c:df:e6:07:84:0b:84:
+ a3:c8:ab:d5:95:47:bf:dc:dc:fe:1d:fc:02:93:44:
+ 01:ca:e6:b5:b7:6b:16:30:01:5d:e9:89:09:95:9e:
+ f8:5e:29:5c:dd:c7:55:8c:f2:8e:20:4e:40:7a:e4:
+ f5:45:03:b4:98:2b:c4:80:7e:53:87:6f:c2:d2:57:
+ b0:e9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 1B:6B:BD:1F:8A:49:18:94:54:37:55:B4:20:17:ED:37:B9:77:18:7D
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 43:25:39:23:07:04:ac:99:5d:59:67:3d:e6:2f:61:7d:5a:56:
+ 7b:fc:06:8d:b3:4b:9d:fa:d5:05:4c:0d:66:b5:bd:3c:c7:a2:
+ 2a:6b:b5:cf:e6:ba:83:3e:60:90:36:0c:d5:c2:ed:8a:95:d9:
+ 92:42:23:1c:03:76:3e:c2:48:f1:75:72:9d:b3:8c:cf:b3:58:
+ 34:56:49:1d:a1:2e:2b:3d:b2:e8:5a:10:46:de:64:b5:4d:ae:
+ 4b:6e:fc:01:b7:21:10:d5:95:b7:eb:2c:be:14:06:cc:41:2e:
+ e4:6c:e2:46:90:ff:c6:28:7e:73:fe:e5:17:ba:82:c3:10:05:
+ 81:66:c2:8b:28:38:a0:44:3e:e9:e4:ce:33:b0:7c:f8:e1:53:
+ 9d:b8:b4:cb:da:c9:2e:d9:93:70:8e:7c:0b:e3:73:3e:99:99:
+ 8f:eb:e1:11:44:35:d8:60:81:62:45:d4:de:45:5b:90:2e:49:
+ 1b:1b:db:a4:0f:80:62:21:73:69:f1:e3:de:6d:d8:48:7c:56:
+ 12:26:22:11:47:01:c6:5e:19:c2:b4:95:97:ee:61:00:55:f1:
+ 04:38:fc:84:e6:78:b4:0d:43:be:43:33:dd:68:d3:22:5b:00:
+ fb:14:82:e8:4b:62:79:30:cf:d3:95:9f:b3:b9:84:01:d4:dd:
+ cf:23:12:f8
+-----BEGIN CERTIFICATE-----
+MIIE4jCCA8qgAwIBAgIQbrrwj3mD+p3hsm+W/G6YvzANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTExMDgyMzAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+cDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxFjAUBgNV
+BAMTDUNPTU9ETyBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDUKy4c0qP4f1UUQN73RN2EVfeFe1VmaaflWetlg/TzdrFmw09OmJMJt0Cz0Reg
+EgmogOEpY5cCjDGdCgLgWVu77TC1735drwhOjYvCOVYWmHOUeArJpk8ot6g0N9sl
+IbE8mfbgEj5z6mQyn0IGPBnYCgR6TFdJK9J3etAAvF76ju7MwuQTbiVf3DykiKPc
+Sce8xw/dGcCxcu147ziDCkUXG8l9ne3fqywso3WuW4IdiIONzghlDGYmVwWhDN/m
+B4QLhKPIq9WVR7/c3P4d/AKTRAHK5rW3axYwAV3piQmVnvheKVzdx1WM8o4gTkB6
+5PVFA7SYK8SAflOHb8LSV7DpAgMBAAGjggF3MIIBczAfBgNVHSMEGDAWgBStvZh6
+NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUG2u9H4pJGJRUN1W0IBftN7l3GH0w
+DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEQYDVR0gBAowCDAG
+BgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNv
+bS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYIKwYBBQUHAQEEgaYwgaMw
+PwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4
+dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0cDovL2NydC51c2VydHJ1
+c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8v
+b2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQBDJTkjBwSsmV1Z
+Zz3mL2F9WlZ7/AaNs0ud+tUFTA1mtb08x6Iqa7XP5rqDPmCQNgzVwu2KldmSQiMc
+A3Y+wkjxdXKds4zPs1g0VkkdoS4rPbLoWhBG3mS1Ta5LbvwBtyEQ1ZW36yy+FAbM
+QS7kbOJGkP/GKH5z/uUXuoLDEAWBZsKLKDigRD7p5M4zsHz44VOduLTL2sku2ZNw
+jnwL43M+mZmP6+ERRDXYYIFiRdTeRVuQLkkbG9ukD4BiIXNp8ePebdhIfFYSJiIR
+RwHGXhnCtJWX7mEAVfEEOPyE5ni0DUO+QzPdaNMiWwD7FILoS2J5MM/TlZ+zuYQB
+1N3PIxL4
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert57[] = {
+ 0x30, 0x82, 0x04, 0xe2, 0x30, 0x82, 0x03, 0xca, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6e, 0xba, 0xf0, 0x8f, 0x79, 0x83, 0xfa, 0x9d, 0xe1,
+ 0xb2, 0x6f, 0x96, 0xfc, 0x6e, 0x98, 0xbf, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x38, 0x32,
+ 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x70, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0d, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x53,
+ 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xd4, 0x2b, 0x2e, 0x1c, 0xd2, 0xa3, 0xf8, 0x7f, 0x55, 0x14,
+ 0x40, 0xde, 0xf7, 0x44, 0xdd, 0x84, 0x55, 0xf7, 0x85, 0x7b, 0x55, 0x66,
+ 0x69, 0xa7, 0xe5, 0x59, 0xeb, 0x65, 0x83, 0xf4, 0xf3, 0x76, 0xb1, 0x66,
+ 0xc3, 0x4f, 0x4e, 0x98, 0x93, 0x09, 0xb7, 0x40, 0xb3, 0xd1, 0x17, 0xa0,
+ 0x12, 0x09, 0xa8, 0x80, 0xe1, 0x29, 0x63, 0x97, 0x02, 0x8c, 0x31, 0x9d,
+ 0x0a, 0x02, 0xe0, 0x59, 0x5b, 0xbb, 0xed, 0x30, 0xb5, 0xef, 0x7e, 0x5d,
+ 0xaf, 0x08, 0x4e, 0x8d, 0x8b, 0xc2, 0x39, 0x56, 0x16, 0x98, 0x73, 0x94,
+ 0x78, 0x0a, 0xc9, 0xa6, 0x4f, 0x28, 0xb7, 0xa8, 0x34, 0x37, 0xdb, 0x25,
+ 0x21, 0xb1, 0x3c, 0x99, 0xf6, 0xe0, 0x12, 0x3e, 0x73, 0xea, 0x64, 0x32,
+ 0x9f, 0x42, 0x06, 0x3c, 0x19, 0xd8, 0x0a, 0x04, 0x7a, 0x4c, 0x57, 0x49,
+ 0x2b, 0xd2, 0x77, 0x7a, 0xd0, 0x00, 0xbc, 0x5e, 0xfa, 0x8e, 0xee, 0xcc,
+ 0xc2, 0xe4, 0x13, 0x6e, 0x25, 0x5f, 0xdc, 0x3c, 0xa4, 0x88, 0xa3, 0xdc,
+ 0x49, 0xc7, 0xbc, 0xc7, 0x0f, 0xdd, 0x19, 0xc0, 0xb1, 0x72, 0xed, 0x78,
+ 0xef, 0x38, 0x83, 0x0a, 0x45, 0x17, 0x1b, 0xc9, 0x7d, 0x9d, 0xed, 0xdf,
+ 0xab, 0x2c, 0x2c, 0xa3, 0x75, 0xae, 0x5b, 0x82, 0x1d, 0x88, 0x83, 0x8d,
+ 0xce, 0x08, 0x65, 0x0c, 0x66, 0x26, 0x57, 0x05, 0xa1, 0x0c, 0xdf, 0xe6,
+ 0x07, 0x84, 0x0b, 0x84, 0xa3, 0xc8, 0xab, 0xd5, 0x95, 0x47, 0xbf, 0xdc,
+ 0xdc, 0xfe, 0x1d, 0xfc, 0x02, 0x93, 0x44, 0x01, 0xca, 0xe6, 0xb5, 0xb7,
+ 0x6b, 0x16, 0x30, 0x01, 0x5d, 0xe9, 0x89, 0x09, 0x95, 0x9e, 0xf8, 0x5e,
+ 0x29, 0x5c, 0xdd, 0xc7, 0x55, 0x8c, 0xf2, 0x8e, 0x20, 0x4e, 0x40, 0x7a,
+ 0xe4, 0xf5, 0x45, 0x03, 0xb4, 0x98, 0x2b, 0xc4, 0x80, 0x7e, 0x53, 0x87,
+ 0x6f, 0xc2, 0xd2, 0x57, 0xb0, 0xe9, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a,
+ 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0,
+ 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x1b, 0x6b, 0xbd, 0x1f, 0x8a, 0x49, 0x18, 0x94, 0x54,
+ 0x37, 0x55, 0xb4, 0x20, 0x17, 0xed, 0x37, 0xb9, 0x77, 0x18, 0x7d, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08, 0x30, 0x06,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d,
+ 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0, 0x35, 0x86,
+ 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e,
+ 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x45, 0x78,
+ 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81, 0xa3, 0x30,
+ 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86,
+ 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e,
+ 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x45, 0x78,
+ 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x43, 0x25, 0x39, 0x23, 0x07, 0x04, 0xac, 0x99, 0x5d, 0x59,
+ 0x67, 0x3d, 0xe6, 0x2f, 0x61, 0x7d, 0x5a, 0x56, 0x7b, 0xfc, 0x06, 0x8d,
+ 0xb3, 0x4b, 0x9d, 0xfa, 0xd5, 0x05, 0x4c, 0x0d, 0x66, 0xb5, 0xbd, 0x3c,
+ 0xc7, 0xa2, 0x2a, 0x6b, 0xb5, 0xcf, 0xe6, 0xba, 0x83, 0x3e, 0x60, 0x90,
+ 0x36, 0x0c, 0xd5, 0xc2, 0xed, 0x8a, 0x95, 0xd9, 0x92, 0x42, 0x23, 0x1c,
+ 0x03, 0x76, 0x3e, 0xc2, 0x48, 0xf1, 0x75, 0x72, 0x9d, 0xb3, 0x8c, 0xcf,
+ 0xb3, 0x58, 0x34, 0x56, 0x49, 0x1d, 0xa1, 0x2e, 0x2b, 0x3d, 0xb2, 0xe8,
+ 0x5a, 0x10, 0x46, 0xde, 0x64, 0xb5, 0x4d, 0xae, 0x4b, 0x6e, 0xfc, 0x01,
+ 0xb7, 0x21, 0x10, 0xd5, 0x95, 0xb7, 0xeb, 0x2c, 0xbe, 0x14, 0x06, 0xcc,
+ 0x41, 0x2e, 0xe4, 0x6c, 0xe2, 0x46, 0x90, 0xff, 0xc6, 0x28, 0x7e, 0x73,
+ 0xfe, 0xe5, 0x17, 0xba, 0x82, 0xc3, 0x10, 0x05, 0x81, 0x66, 0xc2, 0x8b,
+ 0x28, 0x38, 0xa0, 0x44, 0x3e, 0xe9, 0xe4, 0xce, 0x33, 0xb0, 0x7c, 0xf8,
+ 0xe1, 0x53, 0x9d, 0xb8, 0xb4, 0xcb, 0xda, 0xc9, 0x2e, 0xd9, 0x93, 0x70,
+ 0x8e, 0x7c, 0x0b, 0xe3, 0x73, 0x3e, 0x99, 0x99, 0x8f, 0xeb, 0xe1, 0x11,
+ 0x44, 0x35, 0xd8, 0x60, 0x81, 0x62, 0x45, 0xd4, 0xde, 0x45, 0x5b, 0x90,
+ 0x2e, 0x49, 0x1b, 0x1b, 0xdb, 0xa4, 0x0f, 0x80, 0x62, 0x21, 0x73, 0x69,
+ 0xf1, 0xe3, 0xde, 0x6d, 0xd8, 0x48, 0x7c, 0x56, 0x12, 0x26, 0x22, 0x11,
+ 0x47, 0x01, 0xc6, 0x5e, 0x19, 0xc2, 0xb4, 0x95, 0x97, 0xee, 0x61, 0x00,
+ 0x55, 0xf1, 0x04, 0x38, 0xfc, 0x84, 0xe6, 0x78, 0xb4, 0x0d, 0x43, 0xbe,
+ 0x43, 0x33, 0xdd, 0x68, 0xd3, 0x22, 0x5b, 0x00, 0xfb, 0x14, 0x82, 0xe8,
+ 0x4b, 0x62, 0x79, 0x30, 0xcf, 0xd3, 0x95, 0x9f, 0xb3, 0xb9, 0x84, 0x01,
+ 0xd4, 0xdd, 0xcf, 0x23, 0x12, 0xf8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4e:6c:48:88:36:bb:28:ce:2b:e3:5a:c3:79:8f:4a:24
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Nov 14 00:00:00 2012 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO SSL CA 2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:90:3d:54:2c:85:28:dd:9b:d0:b8:2b:8d:cc:31:
+ a5:96:97:0c:58:92:24:77:84:ad:9e:8a:92:e3:87:
+ d9:8a:55:63:ea:ea:9e:e7:08:9d:bf:e5:8a:e9:60:
+ 53:3e:80:6f:86:52:49:10:91:70:cf:10:b3:eb:08:
+ 58:25:48:5d:5d:eb:b7:ab:de:26:c1:5b:e1:9b:04:
+ de:5d:19:3a:be:40:17:d7:4e:dd:f9:d1:83:ca:36:
+ 36:2c:48:08:71:5c:eb:f2:0f:af:12:7a:4e:ad:2b:
+ b7:5d:8b:4b:ec:ee:fe:df:34:69:2c:fc:73:af:b1:
+ ce:0f:79:79:db:0a:90:02:fd:ca:33:b4:a2:d5:9d:
+ 79:5f:7f:b3:a4:59:a8:28:aa:78:e5:54:0a:18:d0:
+ 2e:6a:94:26:10:18:2b:7e:b3:cf:dd:28:28:bd:f8:
+ 8b:6b:ca:05:df:7a:50:ba:b8:4c:55:f6:79:ef:4f:
+ c4:4c:0f:8b:dc:79:a5:be:49:9d:7a:18:aa:f1:a6:
+ 6c:f8:59:e0:41:c2:e7:7c:1d:0c:ea:be:8d:e9:c8:
+ 0f:55:22:f5:71:42:a9:d0:81:ba:92:58:95:f8:c2:
+ ad:5a:7b:2f:00:81:d7:70:8d:b6:d7:45:f6:08:c0:
+ 8d:cb:5d:48:db:63:65:97:31:d1:15:9a:03:4f:1e:
+ 76:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ BF:D4:7D:6F:AF:74:93:90:88:A8:43:C6:1E:F7:13:6C:AE:B5:CC:AF
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.comodoca3.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 3d:6d:89:77:8f:fe:66:51:37:17:ea:1e:f1:51:44:78:2a:f2:
+ a5:69:a9:40:0d:29:cf:35:5e:4d:bc:9e:7f:13:09:ce:cc:0e:
+ be:e0:b8:62:be:a1:18:cd:7e:91:78:ea:9b:46:dd:f7:0e:f8:
+ 4c:7d:2e:48:6d:a8:6a:94:a4:73:7d:de:94:90:b0:f7:9c:ab:
+ 6a:98:3c:08:45:4d:81:c6:0f:dd:9c:2b:3e:5b:b3:39:07:c4:
+ 9d:34:ce:4a:c3:47:30:85:24:36:d6:48:59:84:e4:d7:06:ed:
+ a9:dc:c7:0c:ca:7b:33:ea:bf:d4:4b:88:fc:3b:3b:4a:84:8d:
+ bd:f8:ff:ae:71:43:ff:98:d3:e9:7e:7d:59:9c:98:1d:14:00:
+ 4b:dc:ce:72:ce:1c:6f:dc:e7:33:d1:ca:3f:f7:8c:1e:d9:89:
+ 39:52:77:86:ea:cf:66:6c:1b:6b:38:c2:cb:f8:a7:47:60:87:
+ 18:d2:c0:ff:a9:b6:0a:22:41:4b:bf:55:78:ec:c1:95:2c:3c:
+ f1:7e:5b:58:d0:6c:29:f6:36:ba:dc:cb:99:49:75:4d:2a:9d:
+ a5:b5:33:5d:35:db:9b:d5:f5:b3:67:a3:db:c0:85:5f:11:33:
+ 09:8f:e1:8a:42:f7:a0:da:a3:b9:7f:35:d4:6c:74:8f:df:f8:
+ ff:be:bf:8e
+-----BEGIN CERTIFICATE-----
+MIIE5DCCA8ygAwIBAgIQTmxIiDa7KM4r41rDeY9KJDANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEyMTExNDAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+cjELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxGDAWBgNV
+BAMTD0NPTU9ETyBTU0wgQ0EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAJA9VCyFKN2b0LgrjcwxpZaXDFiSJHeErZ6KkuOH2YpVY+rqnucInb/liulg
+Uz6Ab4ZSSRCRcM8Qs+sIWCVIXV3rt6veJsFb4ZsE3l0ZOr5AF9dO3fnRg8o2NixI
+CHFc6/IPrxJ6Tq0rt12LS+zu/t80aSz8c6+xzg95edsKkAL9yjO0otWdeV9/s6RZ
+qCiqeOVUChjQLmqUJhAYK36zz90oKL34i2vKBd96ULq4TFX2ee9PxEwPi9x5pb5J
+nXoYqvGmbPhZ4EHC53wdDOq+jenID1Ui9XFCqdCBupJYlfjCrVp7LwCB13CNttdF
+9gjAjctdSNtjZZcx0RWaA08edp0CAwEAAaOCAXcwggFzMB8GA1UdIwQYMBaAFK29
+mHo0tCb3+sQmVO8DveAky1QaMB0GA1UdDgQWBBS/1H1vr3STkIioQ8Ye9xNsrrXM
+rzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADARBgNVHSAECjAI
+MAYGBFUdIAAwRAYDVR0fBD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3Qu
+Y29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMIGzBggrBgEFBQcBAQSBpjCB
+ozA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0
+RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsGAQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0
+cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0NDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLmNvbW9kb2NhMy5jb20wDQYJKoZIhvcNAQEFBQADggEBAD1tiXeP/mZR
+NxfqHvFRRHgq8qVpqUANKc81Xk28nn8TCc7MDr7guGK+oRjNfpF46ptG3fcO+Ex9
+LkhtqGqUpHN93pSQsPecq2qYPAhFTYHGD92cKz5bszkHxJ00zkrDRzCFJDbWSFmE
+5NcG7ancxwzKezPqv9RLiPw7O0qEjb34/65xQ/+Y0+l+fVmcmB0UAEvcznLOHG/c
+5zPRyj/3jB7ZiTlSd4bqz2ZsG2s4wsv4p0dghxjSwP+ptgoiQUu/VXjswZUsPPF+
+W1jQbCn2Nrrcy5lJdU0qnaW1M10125vV9bNno9vAhV8RMwmP4YpC96Dao7l/NdRs
+dI/f+P++v44=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert58[] = {
+ 0x30, 0x82, 0x04, 0xe4, 0x30, 0x82, 0x03, 0xcc, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4e, 0x6c, 0x48, 0x88, 0x36, 0xbb, 0x28, 0xce, 0x2b,
+ 0xe3, 0x5a, 0xc3, 0x79, 0x8f, 0x4a, 0x24, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31, 0x31, 0x31,
+ 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x72, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0f, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x53,
+ 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0x90, 0x3d, 0x54, 0x2c, 0x85, 0x28, 0xdd, 0x9b,
+ 0xd0, 0xb8, 0x2b, 0x8d, 0xcc, 0x31, 0xa5, 0x96, 0x97, 0x0c, 0x58, 0x92,
+ 0x24, 0x77, 0x84, 0xad, 0x9e, 0x8a, 0x92, 0xe3, 0x87, 0xd9, 0x8a, 0x55,
+ 0x63, 0xea, 0xea, 0x9e, 0xe7, 0x08, 0x9d, 0xbf, 0xe5, 0x8a, 0xe9, 0x60,
+ 0x53, 0x3e, 0x80, 0x6f, 0x86, 0x52, 0x49, 0x10, 0x91, 0x70, 0xcf, 0x10,
+ 0xb3, 0xeb, 0x08, 0x58, 0x25, 0x48, 0x5d, 0x5d, 0xeb, 0xb7, 0xab, 0xde,
+ 0x26, 0xc1, 0x5b, 0xe1, 0x9b, 0x04, 0xde, 0x5d, 0x19, 0x3a, 0xbe, 0x40,
+ 0x17, 0xd7, 0x4e, 0xdd, 0xf9, 0xd1, 0x83, 0xca, 0x36, 0x36, 0x2c, 0x48,
+ 0x08, 0x71, 0x5c, 0xeb, 0xf2, 0x0f, 0xaf, 0x12, 0x7a, 0x4e, 0xad, 0x2b,
+ 0xb7, 0x5d, 0x8b, 0x4b, 0xec, 0xee, 0xfe, 0xdf, 0x34, 0x69, 0x2c, 0xfc,
+ 0x73, 0xaf, 0xb1, 0xce, 0x0f, 0x79, 0x79, 0xdb, 0x0a, 0x90, 0x02, 0xfd,
+ 0xca, 0x33, 0xb4, 0xa2, 0xd5, 0x9d, 0x79, 0x5f, 0x7f, 0xb3, 0xa4, 0x59,
+ 0xa8, 0x28, 0xaa, 0x78, 0xe5, 0x54, 0x0a, 0x18, 0xd0, 0x2e, 0x6a, 0x94,
+ 0x26, 0x10, 0x18, 0x2b, 0x7e, 0xb3, 0xcf, 0xdd, 0x28, 0x28, 0xbd, 0xf8,
+ 0x8b, 0x6b, 0xca, 0x05, 0xdf, 0x7a, 0x50, 0xba, 0xb8, 0x4c, 0x55, 0xf6,
+ 0x79, 0xef, 0x4f, 0xc4, 0x4c, 0x0f, 0x8b, 0xdc, 0x79, 0xa5, 0xbe, 0x49,
+ 0x9d, 0x7a, 0x18, 0xaa, 0xf1, 0xa6, 0x6c, 0xf8, 0x59, 0xe0, 0x41, 0xc2,
+ 0xe7, 0x7c, 0x1d, 0x0c, 0xea, 0xbe, 0x8d, 0xe9, 0xc8, 0x0f, 0x55, 0x22,
+ 0xf5, 0x71, 0x42, 0xa9, 0xd0, 0x81, 0xba, 0x92, 0x58, 0x95, 0xf8, 0xc2,
+ 0xad, 0x5a, 0x7b, 0x2f, 0x00, 0x81, 0xd7, 0x70, 0x8d, 0xb6, 0xd7, 0x45,
+ 0xf6, 0x08, 0xc0, 0x8d, 0xcb, 0x5d, 0x48, 0xdb, 0x63, 0x65, 0x97, 0x31,
+ 0xd1, 0x15, 0x9a, 0x03, 0x4f, 0x1e, 0x76, 0x9d, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd,
+ 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03,
+ 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xbf, 0xd4, 0x7d, 0x6f, 0xaf, 0x74, 0x93,
+ 0x90, 0x88, 0xa8, 0x43, 0xc6, 0x1e, 0xf7, 0x13, 0x6c, 0xae, 0xb5, 0xcc,
+ 0xaf, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08,
+ 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0,
+ 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81,
+ 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64,
+ 0x6f, 0x63, 0x61, 0x33, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x3d, 0x6d, 0x89, 0x77, 0x8f, 0xfe, 0x66, 0x51,
+ 0x37, 0x17, 0xea, 0x1e, 0xf1, 0x51, 0x44, 0x78, 0x2a, 0xf2, 0xa5, 0x69,
+ 0xa9, 0x40, 0x0d, 0x29, 0xcf, 0x35, 0x5e, 0x4d, 0xbc, 0x9e, 0x7f, 0x13,
+ 0x09, 0xce, 0xcc, 0x0e, 0xbe, 0xe0, 0xb8, 0x62, 0xbe, 0xa1, 0x18, 0xcd,
+ 0x7e, 0x91, 0x78, 0xea, 0x9b, 0x46, 0xdd, 0xf7, 0x0e, 0xf8, 0x4c, 0x7d,
+ 0x2e, 0x48, 0x6d, 0xa8, 0x6a, 0x94, 0xa4, 0x73, 0x7d, 0xde, 0x94, 0x90,
+ 0xb0, 0xf7, 0x9c, 0xab, 0x6a, 0x98, 0x3c, 0x08, 0x45, 0x4d, 0x81, 0xc6,
+ 0x0f, 0xdd, 0x9c, 0x2b, 0x3e, 0x5b, 0xb3, 0x39, 0x07, 0xc4, 0x9d, 0x34,
+ 0xce, 0x4a, 0xc3, 0x47, 0x30, 0x85, 0x24, 0x36, 0xd6, 0x48, 0x59, 0x84,
+ 0xe4, 0xd7, 0x06, 0xed, 0xa9, 0xdc, 0xc7, 0x0c, 0xca, 0x7b, 0x33, 0xea,
+ 0xbf, 0xd4, 0x4b, 0x88, 0xfc, 0x3b, 0x3b, 0x4a, 0x84, 0x8d, 0xbd, 0xf8,
+ 0xff, 0xae, 0x71, 0x43, 0xff, 0x98, 0xd3, 0xe9, 0x7e, 0x7d, 0x59, 0x9c,
+ 0x98, 0x1d, 0x14, 0x00, 0x4b, 0xdc, 0xce, 0x72, 0xce, 0x1c, 0x6f, 0xdc,
+ 0xe7, 0x33, 0xd1, 0xca, 0x3f, 0xf7, 0x8c, 0x1e, 0xd9, 0x89, 0x39, 0x52,
+ 0x77, 0x86, 0xea, 0xcf, 0x66, 0x6c, 0x1b, 0x6b, 0x38, 0xc2, 0xcb, 0xf8,
+ 0xa7, 0x47, 0x60, 0x87, 0x18, 0xd2, 0xc0, 0xff, 0xa9, 0xb6, 0x0a, 0x22,
+ 0x41, 0x4b, 0xbf, 0x55, 0x78, 0xec, 0xc1, 0x95, 0x2c, 0x3c, 0xf1, 0x7e,
+ 0x5b, 0x58, 0xd0, 0x6c, 0x29, 0xf6, 0x36, 0xba, 0xdc, 0xcb, 0x99, 0x49,
+ 0x75, 0x4d, 0x2a, 0x9d, 0xa5, 0xb5, 0x33, 0x5d, 0x35, 0xdb, 0x9b, 0xd5,
+ 0xf5, 0xb3, 0x67, 0xa3, 0xdb, 0xc0, 0x85, 0x5f, 0x11, 0x33, 0x09, 0x8f,
+ 0xe1, 0x8a, 0x42, 0xf7, 0xa0, 0xda, 0xa3, 0xb9, 0x7f, 0x35, 0xd4, 0x6c,
+ 0x74, 0x8f, 0xdf, 0xf8, 0xff, 0xbe, 0xbf, 0x8e,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4f:e3:e2:65:21:07:ab:20:37:41:6e:48:70:ce:d2:c2
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: May 25 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Trusted Secure Certificate Authority, CN=Trusted Secure Certificate Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:80:0b:42:c6:06:6c:cf:22:b3:1a:9e:11:2e:42:
+ 6e:39:bf:e8:12:af:3c:42:21:12:95:40:5d:32:b1:
+ 6d:1c:21:d1:34:e5:4f:a8:d1:43:a2:26:4e:30:7d:
+ 73:44:2c:73:aa:c5:4d:66:01:19:d2:ea:50:59:65:
+ d0:68:9d:05:a0:7c:a1:79:53:d0:21:90:59:0e:37:
+ db:1e:dc:92:a7:8b:0d:c4:f5:f8:e6:ff:b5:35:1a:
+ da:a8:b6:9b:20:85:65:c4:a2:4d:df:f3:94:4d:63:
+ 7e:ee:89:07:af:fe:e1:ba:00:15:2d:c6:77:8e:a3:
+ fe:ad:cf:26:54:5a:df:fc:d2:de:c2:ad:f6:b2:23:
+ fd:a8:83:e5:65:bd:27:f7:27:1a:18:59:6a:9e:14:
+ f6:b4:86:ff:1c:58:14:43:73:96:24:bf:10:43:d5:
+ 5c:89:f0:ce:f7:e1:96:16:5e:18:4a:27:28:90:80:
+ 18:fc:32:fe:f4:c7:b8:d6:82:3d:35:af:bb:4a:1c:
+ 5b:05:78:f6:fd:55:3e:82:74:b2:73:b8:89:4e:f7:
+ 1b:85:9a:d8:ca:b1:5a:b1:00:20:41:14:30:2b:14:
+ 24:ed:37:0e:32:3e:23:88:39:7e:b9:d9:38:03:e2:
+ 4c:d9:0d:43:41:33:10:eb:30:72:53:88:f7:52:9b:
+ 4f:81
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ CC:03:5B:96:5A:9E:16:CC:26:1E:BD:A3:70:FB:E3:CB:79:19:FC:4D
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.2.8
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 7b:f0:fc:a1:28:47:bc:2b:b4:04:73:3f:4b:dd:1e:d1:b9:cd:
+ 1c:ed:7d:e5:e8:cb:51:f4:92:bf:dd:9c:0d:5c:6e:1d:95:ed:
+ 5b:70:50:89:d4:67:9a:15:54:d1:90:0a:fa:09:68:06:18:bb:
+ d7:27:e4:93:ff:43:48:81:3b:c8:59:49:35:ea:ac:b6:ae:46:
+ b5:d4:f3:b8:c3:c6:e4:91:bf:c9:34:fd:7e:d0:59:6e:61:a1:
+ 1f:48:63:54:b2:7d:46:bf:c8:fa:c3:bf:48:58:98:f6:69:84:
+ a7:16:69:08:27:a4:22:cb:a2:2c:c8:df:6e:a9:ee:f8:41:df:
+ 1b:a8:b7:f3:e3:ae:ce:a3:fe:d9:27:60:50:3f:04:7d:7a:44:
+ ea:76:42:5c:d3:55:46:ef:27:c5:6a:4a:80:e7:35:a0:91:c6:
+ 1b:a6:86:9c:5a:3b:04:83:54:34:d7:d1:88:a6:36:e9:7f:40:
+ 27:da:56:0a:50:21:9d:29:8b:a0:84:ec:fe:71:23:53:04:18:
+ 19:70:67:86:44:95:72:40:55:f6:dd:a3:b4:3d:2d:09:60:a5:
+ e7:5f:fc:ac:3b:ec:0c:91:9f:f8:ee:6a:ba:b2:3c:fd:95:7d:
+ 9a:07:f4:b0:65:43:a2:f6:df:7d:b8:21:49:84:04:ee:bd:ce:
+ 53:8f:0f:29
+-----BEGIN CERTIFICATE-----
+MIIE5DCCA8ygAwIBAgIQT+PiZSEHqyA3QW5IcM7SwjANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMDUyNTAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+azELMAkGA1UEBhMCVVMxLTArBgNVBAoTJFRydXN0ZWQgU2VjdXJlIENlcnRpZmlj
+YXRlIEF1dGhvcml0eTEtMCsGA1UEAxMkVHJ1c3RlZCBTZWN1cmUgQ2VydGlmaWNh
+dGUgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgAtC
+xgZszyKzGp4RLkJuOb/oEq88QiESlUBdMrFtHCHRNOVPqNFDoiZOMH1zRCxzqsVN
+ZgEZ0upQWWXQaJ0FoHyheVPQIZBZDjfbHtySp4sNxPX45v+1NRraqLabIIVlxKJN
+3/OUTWN+7okHr/7hugAVLcZ3jqP+rc8mVFrf/NLewq32siP9qIPlZb0n9ycaGFlq
+nhT2tIb/HFgUQ3OWJL8QQ9VcifDO9+GWFl4YSicokIAY/DL+9Me41oI9Na+7Shxb
+BXj2/VU+gnSyc7iJTvcbhZrYyrFasQAgQRQwKxQk7TcOMj4jiDl+udk4A+JM2Q1D
+QTMQ6zByU4j3UptPgQIDAQABo4IBfjCCAXowHwYDVR0jBBgwFoAUrb2YejS0Jvf6
+xCZU7wO94CTLVBowHQYDVR0OBBYEFMwDW5ZanhbMJh69o3D748t5GfxNMA4GA1Ud
+DwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMBgGA1UdIAQRMA8wDQYLKwYB
+BAGyMQECAggwRAYDVR0fBD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3Qu
+Y29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMIGzBggrBgEFBQcBAQSBpjCB
+ozA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0
+RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsGAQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0
+cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0NDQS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEFBQADggEBAHvw/KEoR7wr
+tARzP0vdHtG5zRztfeXoy1H0kr/dnA1cbh2V7VtwUInUZ5oVVNGQCvoJaAYYu9cn
+5JP/Q0iBO8hZSTXqrLauRrXU87jDxuSRv8k0/X7QWW5hoR9IY1SyfUa/yPrDv0hY
+mPZphKcWaQgnpCLLoizI326p7vhB3xuot/Pjrs6j/tknYFA/BH16ROp2QlzTVUbv
+J8VqSoDnNaCRxhumhpxaOwSDVDTX0YimNul/QCfaVgpQIZ0pi6CE7P5xI1MEGBlw
+Z4ZElXJAVfbdo7Q9LQlgpedf/Kw77AyRn/juarqyPP2VfZoH9LBlQ6L23324IUmE
+BO69zlOPDyk=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert59[] = {
+ 0x30, 0x82, 0x04, 0xe4, 0x30, 0x82, 0x03, 0xcc, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4f, 0xe3, 0xe2, 0x65, 0x21, 0x07, 0xab, 0x20, 0x37,
+ 0x41, 0x6e, 0x48, 0x70, 0xce, 0xd2, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x35, 0x32,
+ 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x24, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00,
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x80, 0x0b, 0x42,
+ 0xc6, 0x06, 0x6c, 0xcf, 0x22, 0xb3, 0x1a, 0x9e, 0x11, 0x2e, 0x42, 0x6e,
+ 0x39, 0xbf, 0xe8, 0x12, 0xaf, 0x3c, 0x42, 0x21, 0x12, 0x95, 0x40, 0x5d,
+ 0x32, 0xb1, 0x6d, 0x1c, 0x21, 0xd1, 0x34, 0xe5, 0x4f, 0xa8, 0xd1, 0x43,
+ 0xa2, 0x26, 0x4e, 0x30, 0x7d, 0x73, 0x44, 0x2c, 0x73, 0xaa, 0xc5, 0x4d,
+ 0x66, 0x01, 0x19, 0xd2, 0xea, 0x50, 0x59, 0x65, 0xd0, 0x68, 0x9d, 0x05,
+ 0xa0, 0x7c, 0xa1, 0x79, 0x53, 0xd0, 0x21, 0x90, 0x59, 0x0e, 0x37, 0xdb,
+ 0x1e, 0xdc, 0x92, 0xa7, 0x8b, 0x0d, 0xc4, 0xf5, 0xf8, 0xe6, 0xff, 0xb5,
+ 0x35, 0x1a, 0xda, 0xa8, 0xb6, 0x9b, 0x20, 0x85, 0x65, 0xc4, 0xa2, 0x4d,
+ 0xdf, 0xf3, 0x94, 0x4d, 0x63, 0x7e, 0xee, 0x89, 0x07, 0xaf, 0xfe, 0xe1,
+ 0xba, 0x00, 0x15, 0x2d, 0xc6, 0x77, 0x8e, 0xa3, 0xfe, 0xad, 0xcf, 0x26,
+ 0x54, 0x5a, 0xdf, 0xfc, 0xd2, 0xde, 0xc2, 0xad, 0xf6, 0xb2, 0x23, 0xfd,
+ 0xa8, 0x83, 0xe5, 0x65, 0xbd, 0x27, 0xf7, 0x27, 0x1a, 0x18, 0x59, 0x6a,
+ 0x9e, 0x14, 0xf6, 0xb4, 0x86, 0xff, 0x1c, 0x58, 0x14, 0x43, 0x73, 0x96,
+ 0x24, 0xbf, 0x10, 0x43, 0xd5, 0x5c, 0x89, 0xf0, 0xce, 0xf7, 0xe1, 0x96,
+ 0x16, 0x5e, 0x18, 0x4a, 0x27, 0x28, 0x90, 0x80, 0x18, 0xfc, 0x32, 0xfe,
+ 0xf4, 0xc7, 0xb8, 0xd6, 0x82, 0x3d, 0x35, 0xaf, 0xbb, 0x4a, 0x1c, 0x5b,
+ 0x05, 0x78, 0xf6, 0xfd, 0x55, 0x3e, 0x82, 0x74, 0xb2, 0x73, 0xb8, 0x89,
+ 0x4e, 0xf7, 0x1b, 0x85, 0x9a, 0xd8, 0xca, 0xb1, 0x5a, 0xb1, 0x00, 0x20,
+ 0x41, 0x14, 0x30, 0x2b, 0x14, 0x24, 0xed, 0x37, 0x0e, 0x32, 0x3e, 0x23,
+ 0x88, 0x39, 0x7e, 0xb9, 0xd9, 0x38, 0x03, 0xe2, 0x4c, 0xd9, 0x0d, 0x43,
+ 0x41, 0x33, 0x10, 0xeb, 0x30, 0x72, 0x53, 0x88, 0xf7, 0x52, 0x9b, 0x4f,
+ 0x81, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x7e, 0x30, 0x82,
+ 0x01, 0x7a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30,
+ 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa,
+ 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcc, 0x03,
+ 0x5b, 0x96, 0x5a, 0x9e, 0x16, 0xcc, 0x26, 0x1e, 0xbd, 0xa3, 0x70, 0xfb,
+ 0xe3, 0xcb, 0x79, 0x19, 0xfc, 0x4d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, 0x08, 0x30, 0x44, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0,
+ 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81,
+ 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x7b, 0xf0, 0xfc, 0xa1, 0x28, 0x47, 0xbc, 0x2b,
+ 0xb4, 0x04, 0x73, 0x3f, 0x4b, 0xdd, 0x1e, 0xd1, 0xb9, 0xcd, 0x1c, 0xed,
+ 0x7d, 0xe5, 0xe8, 0xcb, 0x51, 0xf4, 0x92, 0xbf, 0xdd, 0x9c, 0x0d, 0x5c,
+ 0x6e, 0x1d, 0x95, 0xed, 0x5b, 0x70, 0x50, 0x89, 0xd4, 0x67, 0x9a, 0x15,
+ 0x54, 0xd1, 0x90, 0x0a, 0xfa, 0x09, 0x68, 0x06, 0x18, 0xbb, 0xd7, 0x27,
+ 0xe4, 0x93, 0xff, 0x43, 0x48, 0x81, 0x3b, 0xc8, 0x59, 0x49, 0x35, 0xea,
+ 0xac, 0xb6, 0xae, 0x46, 0xb5, 0xd4, 0xf3, 0xb8, 0xc3, 0xc6, 0xe4, 0x91,
+ 0xbf, 0xc9, 0x34, 0xfd, 0x7e, 0xd0, 0x59, 0x6e, 0x61, 0xa1, 0x1f, 0x48,
+ 0x63, 0x54, 0xb2, 0x7d, 0x46, 0xbf, 0xc8, 0xfa, 0xc3, 0xbf, 0x48, 0x58,
+ 0x98, 0xf6, 0x69, 0x84, 0xa7, 0x16, 0x69, 0x08, 0x27, 0xa4, 0x22, 0xcb,
+ 0xa2, 0x2c, 0xc8, 0xdf, 0x6e, 0xa9, 0xee, 0xf8, 0x41, 0xdf, 0x1b, 0xa8,
+ 0xb7, 0xf3, 0xe3, 0xae, 0xce, 0xa3, 0xfe, 0xd9, 0x27, 0x60, 0x50, 0x3f,
+ 0x04, 0x7d, 0x7a, 0x44, 0xea, 0x76, 0x42, 0x5c, 0xd3, 0x55, 0x46, 0xef,
+ 0x27, 0xc5, 0x6a, 0x4a, 0x80, 0xe7, 0x35, 0xa0, 0x91, 0xc6, 0x1b, 0xa6,
+ 0x86, 0x9c, 0x5a, 0x3b, 0x04, 0x83, 0x54, 0x34, 0xd7, 0xd1, 0x88, 0xa6,
+ 0x36, 0xe9, 0x7f, 0x40, 0x27, 0xda, 0x56, 0x0a, 0x50, 0x21, 0x9d, 0x29,
+ 0x8b, 0xa0, 0x84, 0xec, 0xfe, 0x71, 0x23, 0x53, 0x04, 0x18, 0x19, 0x70,
+ 0x67, 0x86, 0x44, 0x95, 0x72, 0x40, 0x55, 0xf6, 0xdd, 0xa3, 0xb4, 0x3d,
+ 0x2d, 0x09, 0x60, 0xa5, 0xe7, 0x5f, 0xfc, 0xac, 0x3b, 0xec, 0x0c, 0x91,
+ 0x9f, 0xf8, 0xee, 0x6a, 0xba, 0xb2, 0x3c, 0xfd, 0x95, 0x7d, 0x9a, 0x07,
+ 0xf4, 0xb0, 0x65, 0x43, 0xa2, 0xf6, 0xdf, 0x7d, 0xb8, 0x21, 0x49, 0x84,
+ 0x04, 0xee, 0xbd, 0xce, 0x53, 0x8f, 0x0f, 0x29,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 07:6f:12:46:81:45:9c:28:d5:48:d6:97:c4:0e:00:1b
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Feb 16 00:00:00 2012 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=PositiveSSL CA 2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e8:ea:39:e3:22:a6:aa:b9:c4:00:d0:e7:aa:67:
+ 3b:43:07:bd:4f:92:eb:bc:be:01:a3:40:ad:e0:ef:
+ 44:28:b5:d0:3a:be:80:54:17:85:7a:6b:84:6c:36:
+ 36:e5:a3:24:e2:fe:28:01:90:bc:d7:dd:0f:b9:2b:
+ 4e:48:77:05:69:af:de:57:30:b1:e8:fb:1a:03:f6:
+ 3c:5b:53:1e:a1:01:49:68:72:73:d6:33:2b:43:a9:
+ 37:32:52:0f:ae:27:56:31:30:60:ad:c9:bd:73:2c:
+ 39:ee:90:d8:75:b0:25:21:60:7b:2a:7f:02:fd:82:
+ 85:1f:74:4f:92:34:73:5c:1d:00:a0:b0:c0:ea:98:
+ e2:be:01:14:58:17:28:22:8a:77:5d:50:25:cd:9a:
+ 6c:a6:e5:0c:e5:ab:28:c3:b2:20:89:f0:07:24:1e:
+ 95:c2:2e:c0:e5:e9:ec:f6:3d:12:07:48:3d:d2:c3:
+ 23:56:41:ec:d3:df:35:4b:c8:e7:f6:86:05:52:10:
+ 43:9a:8c:17:7c:8b:aa:bc:78:e0:f0:45:3b:ac:80:
+ 55:fe:28:93:e1:0a:11:68:f4:52:57:6f:fe:48:0b:
+ 5b:5d:1a:6a:67:73:99:82:b4:9e:43:60:3e:c7:5b:
+ 2a:12:6e:1a:ee:cb:39:ae:c3:35:9d:a8:bc:5d:b0:
+ 2f:c3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 99:E4:40:5F:6B:14:5E:3E:05:D9:DD:D3:63:54:FC:62:B8:F7:00:AC
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 9c:36:e3:4e:ae:f1:8a:bb:6c:97:8c:8f:4b:67:d0:9f:d8:84:
+ aa:9f:21:5f:35:a1:5b:c4:2b:63:0d:e8:bc:77:5d:a7:c4:37:
+ fd:4b:2d:9e:e8:1d:69:a1:c0:84:cc:d1:6d:8b:f3:81:cb:9f:
+ 4b:74:b0:49:2a:31:e8:37:40:eb:1f:d9:97:a3:1a:11:d5:26:
+ a7:6e:0f:ba:d5:be:2c:fd:b4:91:64:dc:be:3b:19:50:0d:7a:
+ 95:f3:04:13:a9:bb:47:0f:8b:5c:d1:ac:c2:7b:77:21:50:dd:
+ 5b:ab:ee:f4:a6:d8:d4:4a:53:6b:4d:ad:b8:c8:e7:e6:52:58:
+ 4d:43:4c:c2:a2:23:4f:0e:c0:20:39:af:df:4f:42:5b:1e:d3:
+ 09:f4:18:09:59:2a:d9:e8:4a:18:bf:32:fb:fa:2d:64:8b:87:
+ ca:5b:2b:e8:b8:0b:7e:be:17:12:c7:03:82:29:af:58:af:85:
+ 84:5d:3d:0a:df:23:51:c3:cd:af:10:bf:80:69:77:91:0a:4f:
+ e5:ba:e1:ad:9b:ce:df:33:4e:30:3b:e9:8f:66:7f:82:fa:6b:
+ fa:db:a3:c0:73:00:e3:d6:12:af:4d:f2:0f:5a:14:51:1f:6d:
+ b8:86:81:62:07:ce:5c:72:c2:4f:f3:57:2a:71:d9:d4:97:85:
+ e6:18:53:b7
+-----BEGIN CERTIFICATE-----
+MIIE5TCCA82gAwIBAgIQB28SRoFFnCjVSNaXxA4AGzANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEyMDIxNjAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+czELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxGTAXBgNV
+BAMTEFBvc2l0aXZlU1NMIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDo6jnjIqaqucQA0OeqZztDB71Pkuu8vgGjQK3g70QotdA6voBUF4V6a4Rs
+NjbloyTi/igBkLzX3Q+5K05IdwVpr95XMLHo+xoD9jxbUx6hAUlocnPWMytDqTcy
+Ug+uJ1YxMGCtyb1zLDnukNh1sCUhYHsqfwL9goUfdE+SNHNcHQCgsMDqmOK+ARRY
+FygiinddUCXNmmym5QzlqyjDsiCJ8AckHpXCLsDl6ez2PRIHSD3SwyNWQezT3zVL
+yOf2hgVSEEOajBd8i6q8eODwRTusgFX+KJPhChFo9FJXb/5IC1tdGmpnc5mCtJ5D
+YD7HWyoSbhruyzmuwzWdqLxdsC/DAgMBAAGjggF3MIIBczAfBgNVHSMEGDAWgBSt
+vZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUmeRAX2sUXj4F2d3TY1T8Yrj3
+AKwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEQYDVR0gBAow
+CDAGBgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0
+LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYIKwYBBQUHAQEEgaYw
+gaMwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9BZGRUcnVz
+dEV4dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0cDovL2NydC51c2Vy
+dHJ1c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsGAQUFBzABhhlodHRw
+Oi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQCcNuNOrvGK
+u2yXjI9LZ9Cf2ISqnyFfNaFbxCtjDei8d12nxDf9Sy2e6B1pocCEzNFti/OBy59L
+dLBJKjHoN0DrH9mXoxoR1Sanbg+61b4s/bSRZNy+OxlQDXqV8wQTqbtHD4tc0azC
+e3chUN1bq+70ptjUSlNrTa24yOfmUlhNQ0zCoiNPDsAgOa/fT0JbHtMJ9BgJWSrZ
+6EoYvzL7+i1ki4fKWyvouAt+vhcSxwOCKa9Yr4WEXT0K3yNRw82vEL+AaXeRCk/l
+uuGtm87fM04wO+mPZn+C+mv626PAcwDj1hKvTfIPWhRRH224hoFiB85ccsJP81cq
+cdnUl4XmGFO3
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert60[] = {
+ 0x30, 0x82, 0x04, 0xe5, 0x30, 0x82, 0x03, 0xcd, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x07, 0x6f, 0x12, 0x46, 0x81, 0x45, 0x9c, 0x28, 0xd5,
+ 0x48, 0xd6, 0x97, 0xc4, 0x0e, 0x00, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x30, 0x32, 0x31,
+ 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x73, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x10, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65,
+ 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x20, 0x32, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xe8, 0xea, 0x39, 0xe3, 0x22, 0xa6, 0xaa,
+ 0xb9, 0xc4, 0x00, 0xd0, 0xe7, 0xaa, 0x67, 0x3b, 0x43, 0x07, 0xbd, 0x4f,
+ 0x92, 0xeb, 0xbc, 0xbe, 0x01, 0xa3, 0x40, 0xad, 0xe0, 0xef, 0x44, 0x28,
+ 0xb5, 0xd0, 0x3a, 0xbe, 0x80, 0x54, 0x17, 0x85, 0x7a, 0x6b, 0x84, 0x6c,
+ 0x36, 0x36, 0xe5, 0xa3, 0x24, 0xe2, 0xfe, 0x28, 0x01, 0x90, 0xbc, 0xd7,
+ 0xdd, 0x0f, 0xb9, 0x2b, 0x4e, 0x48, 0x77, 0x05, 0x69, 0xaf, 0xde, 0x57,
+ 0x30, 0xb1, 0xe8, 0xfb, 0x1a, 0x03, 0xf6, 0x3c, 0x5b, 0x53, 0x1e, 0xa1,
+ 0x01, 0x49, 0x68, 0x72, 0x73, 0xd6, 0x33, 0x2b, 0x43, 0xa9, 0x37, 0x32,
+ 0x52, 0x0f, 0xae, 0x27, 0x56, 0x31, 0x30, 0x60, 0xad, 0xc9, 0xbd, 0x73,
+ 0x2c, 0x39, 0xee, 0x90, 0xd8, 0x75, 0xb0, 0x25, 0x21, 0x60, 0x7b, 0x2a,
+ 0x7f, 0x02, 0xfd, 0x82, 0x85, 0x1f, 0x74, 0x4f, 0x92, 0x34, 0x73, 0x5c,
+ 0x1d, 0x00, 0xa0, 0xb0, 0xc0, 0xea, 0x98, 0xe2, 0xbe, 0x01, 0x14, 0x58,
+ 0x17, 0x28, 0x22, 0x8a, 0x77, 0x5d, 0x50, 0x25, 0xcd, 0x9a, 0x6c, 0xa6,
+ 0xe5, 0x0c, 0xe5, 0xab, 0x28, 0xc3, 0xb2, 0x20, 0x89, 0xf0, 0x07, 0x24,
+ 0x1e, 0x95, 0xc2, 0x2e, 0xc0, 0xe5, 0xe9, 0xec, 0xf6, 0x3d, 0x12, 0x07,
+ 0x48, 0x3d, 0xd2, 0xc3, 0x23, 0x56, 0x41, 0xec, 0xd3, 0xdf, 0x35, 0x4b,
+ 0xc8, 0xe7, 0xf6, 0x86, 0x05, 0x52, 0x10, 0x43, 0x9a, 0x8c, 0x17, 0x7c,
+ 0x8b, 0xaa, 0xbc, 0x78, 0xe0, 0xf0, 0x45, 0x3b, 0xac, 0x80, 0x55, 0xfe,
+ 0x28, 0x93, 0xe1, 0x0a, 0x11, 0x68, 0xf4, 0x52, 0x57, 0x6f, 0xfe, 0x48,
+ 0x0b, 0x5b, 0x5d, 0x1a, 0x6a, 0x67, 0x73, 0x99, 0x82, 0xb4, 0x9e, 0x43,
+ 0x60, 0x3e, 0xc7, 0x5b, 0x2a, 0x12, 0x6e, 0x1a, 0xee, 0xcb, 0x39, 0xae,
+ 0xc3, 0x35, 0x9d, 0xa8, 0xbc, 0x5d, 0xb0, 0x2f, 0xc3, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad,
+ 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef,
+ 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x99, 0xe4, 0x40, 0x5f, 0x6b, 0x14,
+ 0x5e, 0x3e, 0x05, 0xd9, 0xdd, 0xd3, 0x63, 0x54, 0xfc, 0x62, 0xb8, 0xf7,
+ 0x00, 0xac, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30,
+ 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37,
+ 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30,
+ 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x9c, 0x36, 0xe3, 0x4e, 0xae, 0xf1, 0x8a,
+ 0xbb, 0x6c, 0x97, 0x8c, 0x8f, 0x4b, 0x67, 0xd0, 0x9f, 0xd8, 0x84, 0xaa,
+ 0x9f, 0x21, 0x5f, 0x35, 0xa1, 0x5b, 0xc4, 0x2b, 0x63, 0x0d, 0xe8, 0xbc,
+ 0x77, 0x5d, 0xa7, 0xc4, 0x37, 0xfd, 0x4b, 0x2d, 0x9e, 0xe8, 0x1d, 0x69,
+ 0xa1, 0xc0, 0x84, 0xcc, 0xd1, 0x6d, 0x8b, 0xf3, 0x81, 0xcb, 0x9f, 0x4b,
+ 0x74, 0xb0, 0x49, 0x2a, 0x31, 0xe8, 0x37, 0x40, 0xeb, 0x1f, 0xd9, 0x97,
+ 0xa3, 0x1a, 0x11, 0xd5, 0x26, 0xa7, 0x6e, 0x0f, 0xba, 0xd5, 0xbe, 0x2c,
+ 0xfd, 0xb4, 0x91, 0x64, 0xdc, 0xbe, 0x3b, 0x19, 0x50, 0x0d, 0x7a, 0x95,
+ 0xf3, 0x04, 0x13, 0xa9, 0xbb, 0x47, 0x0f, 0x8b, 0x5c, 0xd1, 0xac, 0xc2,
+ 0x7b, 0x77, 0x21, 0x50, 0xdd, 0x5b, 0xab, 0xee, 0xf4, 0xa6, 0xd8, 0xd4,
+ 0x4a, 0x53, 0x6b, 0x4d, 0xad, 0xb8, 0xc8, 0xe7, 0xe6, 0x52, 0x58, 0x4d,
+ 0x43, 0x4c, 0xc2, 0xa2, 0x23, 0x4f, 0x0e, 0xc0, 0x20, 0x39, 0xaf, 0xdf,
+ 0x4f, 0x42, 0x5b, 0x1e, 0xd3, 0x09, 0xf4, 0x18, 0x09, 0x59, 0x2a, 0xd9,
+ 0xe8, 0x4a, 0x18, 0xbf, 0x32, 0xfb, 0xfa, 0x2d, 0x64, 0x8b, 0x87, 0xca,
+ 0x5b, 0x2b, 0xe8, 0xb8, 0x0b, 0x7e, 0xbe, 0x17, 0x12, 0xc7, 0x03, 0x82,
+ 0x29, 0xaf, 0x58, 0xaf, 0x85, 0x84, 0x5d, 0x3d, 0x0a, 0xdf, 0x23, 0x51,
+ 0xc3, 0xcd, 0xaf, 0x10, 0xbf, 0x80, 0x69, 0x77, 0x91, 0x0a, 0x4f, 0xe5,
+ 0xba, 0xe1, 0xad, 0x9b, 0xce, 0xdf, 0x33, 0x4e, 0x30, 0x3b, 0xe9, 0x8f,
+ 0x66, 0x7f, 0x82, 0xfa, 0x6b, 0xfa, 0xdb, 0xa3, 0xc0, 0x73, 0x00, 0xe3,
+ 0xd6, 0x12, 0xaf, 0x4d, 0xf2, 0x0f, 0x5a, 0x14, 0x51, 0x1f, 0x6d, 0xb8,
+ 0x86, 0x81, 0x62, 0x07, 0xce, 0x5c, 0x72, 0xc2, 0x4f, 0xf3, 0x57, 0x2a,
+ 0x71, 0xd9, 0xd4, 0x97, 0x85, 0xe6, 0x18, 0x53, 0xb7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 74:86:21:96:95:10:c9:29:26:29:4b:cc:8b:f8:29:2c
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Jun 22 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Globe Hosting, Inc., OU=GlobeSSL DV Certification Authority, CN=GlobeSSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a0:47:04:ce:a8:35:ab:ff:18:63:88:8e:c1:89:
+ fb:2d:03:0f:cd:2d:28:97:a1:da:d2:10:5b:83:4f:
+ 2c:46:98:0b:12:98:f4:b3:39:b7:97:a3:86:5d:80:
+ 22:02:33:c8:9d:ba:9b:ac:ba:d9:7c:7e:de:f7:a3:
+ b9:2a:69:e8:17:7b:fc:56:2f:99:87:da:2a:a2:77:
+ a4:ac:56:8c:ac:f5:1e:df:38:4f:97:d2:3a:03:6a:
+ f6:49:c3:2a:7b:b3:26:54:4c:15:e1:00:f3:02:90:
+ 38:a4:99:57:db:fd:8a:91:01:f1:71:96:75:df:21:
+ f9:15:19:5d:18:2b:0e:73:10:2d:0e:5b:56:28:cb:
+ fd:61:ec:b6:f0:5b:f9:3c:14:f6:42:0b:ca:cd:17:
+ df:d9:76:5f:d6:54:29:40:d1:79:15:fb:f5:45:a9:
+ 2d:6f:54:35:d7:5e:39:e6:a6:b5:04:5b:90:d9:6f:
+ 5c:2f:58:85:00:00:f0:68:11:08:19:40:50:49:8c:
+ da:87:e9:82:99:d3:86:ae:d4:c1:36:a2:56:0e:08:
+ c3:b7:36:7a:91:f0:24:0c:79:8f:30:a5:e2:4c:99:
+ 9d:7e:76:dd:98:81:e8:49:46:ac:01:c8:25:f7:7e:
+ 04:c5:9e:fa:0d:e2:f7:b8:40:f1:45:fc:e6:c2:c9:
+ 6f:c7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ C3:AB:A0:02:F0:9B:F5:66:7F:28:15:92:22:95:DB:B8:4E:D3:93:08
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.2.27
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 66:9c:13:6d:d2:7e:2c:dd:6b:d6:1a:91:37:85:86:51:23:4d:
+ 64:63:f5:5e:83:3e:88:fe:6f:67:87:0e:ca:85:6d:bb:3b:3c:
+ d5:ad:fc:ba:4d:ba:8b:bb:c8:c1:ed:2c:d4:6d:cb:10:c2:33:
+ e3:e7:66:97:8f:2b:e5:8f:81:8f:ed:bc:dd:87:b5:db:dc:23:
+ 5f:ae:0f:40:91:29:9e:07:d4:b1:ce:d0:82:1b:6e:1d:d1:a4:
+ 08:50:12:ae:8f:0f:79:67:a7:00:67:de:ba:90:9b:48:bc:5f:
+ 90:c3:1b:fe:cc:b6:3a:1e:db:15:15:b5:de:ab:78:e3:41:aa:
+ 93:8a:e1:bf:43:15:ec:c9:6b:21:fe:ed:a1:df:e9:0b:2d:cb:
+ a0:73:1f:d6:3e:f8:98:9b:46:78:e4:ad:25:20:41:86:28:d0:
+ de:7d:14:96:04:47:ac:c8:b9:6b:dd:00:f0:47:11:9f:8b:7e:
+ b1:a2:ed:47:e9:17:23:34:e6:bd:8b:67:41:64:60:0a:1a:cd:
+ 75:69:89:39:66:95:e1:32:87:73:91:d0:9b:83:8d:b8:c7:e0:
+ bc:22:8f:2c:24:13:c8:c2:94:97:fa:31:26:22:82:2b:b5:ef:
+ 05:a6:a0:7e:9a:00:b4:6b:e3:9e:59:43:bc:76:98:f3:3c:30:
+ db:1c:30:2e
+-----BEGIN CERTIFICATE-----
+MIIE6DCCA9CgAwIBAgIQdIYhlpUQySkmKUvMi/gpLDANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMDYyMjAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+bzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0dsb2JlIEhvc3RpbmcsIEluYy4xLDAq
+BgNVBAsTI0dsb2JlU1NMIERWIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRQwEgYD
+VQQDEwtHbG9iZVNTTCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AKBHBM6oNav/GGOIjsGJ+y0DD80tKJeh2tIQW4NPLEaYCxKY9LM5t5ejhl2AIgIz
+yJ26m6y62Xx+3vejuSpp6Bd7/FYvmYfaKqJ3pKxWjKz1Ht84T5fSOgNq9knDKnuz
+JlRMFeEA8wKQOKSZV9v9ipEB8XGWdd8h+RUZXRgrDnMQLQ5bVijL/WHstvBb+TwU
+9kILys0X39l2X9ZUKUDReRX79UWpLW9UNddeOeamtQRbkNlvXC9YhQAA8GgRCBlA
+UEmM2ofpgpnThq7UwTaiVg4Iw7c2epHwJAx5jzCl4kyZnX523ZiB6ElGrAHIJfd+
+BMWe+g3i97hA8UX85sLJb8cCAwEAAaOCAX4wggF6MB8GA1UdIwQYMBaAFK29mHo0
+tCb3+sQmVO8DveAky1QaMB0GA1UdDgQWBBTDq6AC8Jv1Zn8oFZIildu4TtOTCDAO
+BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAYBgNVHSAEETAPMA0G
+CysGAQQBsjEBAgIbMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRy
+dXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYIKwYBBQUHAQEE
+gaYwgaMwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9BZGRU
+cnVzdEV4dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0cDovL2NydC51
+c2VydHJ1c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsGAQUFBzABhhlo
+dHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQBmnBNt
+0n4s3WvWGpE3hYZRI01kY/Vegz6I/m9nhw7KhW27OzzVrfy6TbqLu8jB7SzUbcsQ
+wjPj52aXjyvlj4GP7bzdh7Xb3CNfrg9AkSmeB9SxztCCG24d0aQIUBKujw95Z6cA
+Z966kJtIvF+Qwxv+zLY6HtsVFbXeq3jjQaqTiuG/QxXsyWsh/u2h3+kLLcugcx/W
+PviYm0Z45K0lIEGGKNDefRSWBEesyLlr3QDwRxGfi36xou1H6RcjNOa9i2dBZGAK
+Gs11aYk5ZpXhModzkdCbg424x+C8Io8sJBPIwpSX+jEmIoIrte8FpqB+mgC0a+Oe
+WUO8dpjzPDDbHDAu
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert61[] = {
+ 0x30, 0x82, 0x04, 0xe8, 0x30, 0x82, 0x03, 0xd0, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x74, 0x86, 0x21, 0x96, 0x95, 0x10, 0xc9, 0x29, 0x26,
+ 0x29, 0x4b, 0xcc, 0x8b, 0xf8, 0x29, 0x2c, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x36, 0x32,
+ 0x32, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x13, 0x47, 0x6c, 0x6f, 0x62, 0x65, 0x20, 0x48, 0x6f, 0x73, 0x74, 0x69,
+ 0x6e, 0x67, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2c, 0x30, 0x2a,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x23, 0x47, 0x6c, 0x6f, 0x62, 0x65,
+ 0x53, 0x53, 0x4c, 0x20, 0x44, 0x56, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x0b, 0x47, 0x6c, 0x6f, 0x62, 0x65, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xa0, 0x47, 0x04, 0xce, 0xa8, 0x35, 0xab, 0xff, 0x18, 0x63, 0x88,
+ 0x8e, 0xc1, 0x89, 0xfb, 0x2d, 0x03, 0x0f, 0xcd, 0x2d, 0x28, 0x97, 0xa1,
+ 0xda, 0xd2, 0x10, 0x5b, 0x83, 0x4f, 0x2c, 0x46, 0x98, 0x0b, 0x12, 0x98,
+ 0xf4, 0xb3, 0x39, 0xb7, 0x97, 0xa3, 0x86, 0x5d, 0x80, 0x22, 0x02, 0x33,
+ 0xc8, 0x9d, 0xba, 0x9b, 0xac, 0xba, 0xd9, 0x7c, 0x7e, 0xde, 0xf7, 0xa3,
+ 0xb9, 0x2a, 0x69, 0xe8, 0x17, 0x7b, 0xfc, 0x56, 0x2f, 0x99, 0x87, 0xda,
+ 0x2a, 0xa2, 0x77, 0xa4, 0xac, 0x56, 0x8c, 0xac, 0xf5, 0x1e, 0xdf, 0x38,
+ 0x4f, 0x97, 0xd2, 0x3a, 0x03, 0x6a, 0xf6, 0x49, 0xc3, 0x2a, 0x7b, 0xb3,
+ 0x26, 0x54, 0x4c, 0x15, 0xe1, 0x00, 0xf3, 0x02, 0x90, 0x38, 0xa4, 0x99,
+ 0x57, 0xdb, 0xfd, 0x8a, 0x91, 0x01, 0xf1, 0x71, 0x96, 0x75, 0xdf, 0x21,
+ 0xf9, 0x15, 0x19, 0x5d, 0x18, 0x2b, 0x0e, 0x73, 0x10, 0x2d, 0x0e, 0x5b,
+ 0x56, 0x28, 0xcb, 0xfd, 0x61, 0xec, 0xb6, 0xf0, 0x5b, 0xf9, 0x3c, 0x14,
+ 0xf6, 0x42, 0x0b, 0xca, 0xcd, 0x17, 0xdf, 0xd9, 0x76, 0x5f, 0xd6, 0x54,
+ 0x29, 0x40, 0xd1, 0x79, 0x15, 0xfb, 0xf5, 0x45, 0xa9, 0x2d, 0x6f, 0x54,
+ 0x35, 0xd7, 0x5e, 0x39, 0xe6, 0xa6, 0xb5, 0x04, 0x5b, 0x90, 0xd9, 0x6f,
+ 0x5c, 0x2f, 0x58, 0x85, 0x00, 0x00, 0xf0, 0x68, 0x11, 0x08, 0x19, 0x40,
+ 0x50, 0x49, 0x8c, 0xda, 0x87, 0xe9, 0x82, 0x99, 0xd3, 0x86, 0xae, 0xd4,
+ 0xc1, 0x36, 0xa2, 0x56, 0x0e, 0x08, 0xc3, 0xb7, 0x36, 0x7a, 0x91, 0xf0,
+ 0x24, 0x0c, 0x79, 0x8f, 0x30, 0xa5, 0xe2, 0x4c, 0x99, 0x9d, 0x7e, 0x76,
+ 0xdd, 0x98, 0x81, 0xe8, 0x49, 0x46, 0xac, 0x01, 0xc8, 0x25, 0xf7, 0x7e,
+ 0x04, 0xc5, 0x9e, 0xfa, 0x0d, 0xe2, 0xf7, 0xb8, 0x40, 0xf1, 0x45, 0xfc,
+ 0xe6, 0xc2, 0xc9, 0x6f, 0xc7, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0x7e, 0x30, 0x82, 0x01, 0x7a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34,
+ 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24,
+ 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xc3, 0xab, 0xa0, 0x02, 0xf0, 0x9b, 0xf5, 0x66, 0x7f, 0x28,
+ 0x15, 0x92, 0x22, 0x95, 0xdb, 0xb8, 0x4e, 0xd3, 0x93, 0x08, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x18,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06,
+ 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, 0x1b,
+ 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30,
+ 0x39, 0xa0, 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81,
+ 0xb3, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x81, 0xa6, 0x30, 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x43, 0x41, 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75,
+ 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e,
+ 0x53, 0x47, 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75,
+ 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x9c, 0x13, 0x6d,
+ 0xd2, 0x7e, 0x2c, 0xdd, 0x6b, 0xd6, 0x1a, 0x91, 0x37, 0x85, 0x86, 0x51,
+ 0x23, 0x4d, 0x64, 0x63, 0xf5, 0x5e, 0x83, 0x3e, 0x88, 0xfe, 0x6f, 0x67,
+ 0x87, 0x0e, 0xca, 0x85, 0x6d, 0xbb, 0x3b, 0x3c, 0xd5, 0xad, 0xfc, 0xba,
+ 0x4d, 0xba, 0x8b, 0xbb, 0xc8, 0xc1, 0xed, 0x2c, 0xd4, 0x6d, 0xcb, 0x10,
+ 0xc2, 0x33, 0xe3, 0xe7, 0x66, 0x97, 0x8f, 0x2b, 0xe5, 0x8f, 0x81, 0x8f,
+ 0xed, 0xbc, 0xdd, 0x87, 0xb5, 0xdb, 0xdc, 0x23, 0x5f, 0xae, 0x0f, 0x40,
+ 0x91, 0x29, 0x9e, 0x07, 0xd4, 0xb1, 0xce, 0xd0, 0x82, 0x1b, 0x6e, 0x1d,
+ 0xd1, 0xa4, 0x08, 0x50, 0x12, 0xae, 0x8f, 0x0f, 0x79, 0x67, 0xa7, 0x00,
+ 0x67, 0xde, 0xba, 0x90, 0x9b, 0x48, 0xbc, 0x5f, 0x90, 0xc3, 0x1b, 0xfe,
+ 0xcc, 0xb6, 0x3a, 0x1e, 0xdb, 0x15, 0x15, 0xb5, 0xde, 0xab, 0x78, 0xe3,
+ 0x41, 0xaa, 0x93, 0x8a, 0xe1, 0xbf, 0x43, 0x15, 0xec, 0xc9, 0x6b, 0x21,
+ 0xfe, 0xed, 0xa1, 0xdf, 0xe9, 0x0b, 0x2d, 0xcb, 0xa0, 0x73, 0x1f, 0xd6,
+ 0x3e, 0xf8, 0x98, 0x9b, 0x46, 0x78, 0xe4, 0xad, 0x25, 0x20, 0x41, 0x86,
+ 0x28, 0xd0, 0xde, 0x7d, 0x14, 0x96, 0x04, 0x47, 0xac, 0xc8, 0xb9, 0x6b,
+ 0xdd, 0x00, 0xf0, 0x47, 0x11, 0x9f, 0x8b, 0x7e, 0xb1, 0xa2, 0xed, 0x47,
+ 0xe9, 0x17, 0x23, 0x34, 0xe6, 0xbd, 0x8b, 0x67, 0x41, 0x64, 0x60, 0x0a,
+ 0x1a, 0xcd, 0x75, 0x69, 0x89, 0x39, 0x66, 0x95, 0xe1, 0x32, 0x87, 0x73,
+ 0x91, 0xd0, 0x9b, 0x83, 0x8d, 0xb8, 0xc7, 0xe0, 0xbc, 0x22, 0x8f, 0x2c,
+ 0x24, 0x13, 0xc8, 0xc2, 0x94, 0x97, 0xfa, 0x31, 0x26, 0x22, 0x82, 0x2b,
+ 0xb5, 0xef, 0x05, 0xa6, 0xa0, 0x7e, 0x9a, 0x00, 0xb4, 0x6b, 0xe3, 0x9e,
+ 0x59, 0x43, 0xbc, 0x76, 0x98, 0xf3, 0x3c, 0x30, 0xdb, 0x1c, 0x30, 0x2e,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7a:ac:a2:1d:53:9d:14:54:11:3c:04:5e:d8:35:f8:ea
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Network Solutions L.L.C., CN=Network Solutions Certificate Authority
+ Validity
+ Not Before: Nov 26 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Network Solutions L.L.C., CN=Network Solutions EV Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d0:35:5c:e2:e7:95:1a:98:8f:d8:4f:d6:d5:dc:
+ 7e:5c:82:bf:9f:cc:4b:fa:3c:4a:81:bc:da:c5:a7:
+ e9:ad:9a:26:8f:dc:19:2c:63:12:3e:56:df:75:e6:
+ 48:ac:e3:47:90:7f:5f:08:f1:a3:80:d1:d0:cd:25:
+ cd:59:f3:ad:2e:c3:eb:06:09:fe:39:24:39:a2:a1:
+ ec:c4:c4:9a:d7:a0:08:55:fe:c8:c5:64:2e:fc:e7:
+ 06:88:95:c1:3e:31:5a:55:f0:1d:98:04:94:b4:7f:
+ 5e:dc:90:a9:a1:85:c7:aa:12:b9:87:d1:a3:71:11:
+ 02:6c:7e:9b:c9:39:eb:ec:b5:58:27:8b:a3:98:11:
+ a0:ab:83:fb:24:30:00:ae:02:57:fe:80:e2:ca:8f:
+ 48:60:63:39:db:af:96:74:83:bb:3b:6c:ef:b3:33:
+ c6:a6:dc:31:e9:f9:bc:aa:b7:1e:c8:f4:7f:58:69:
+ 72:ee:5a:8f:36:0a:fe:32:11:1c:34:3d:79:88:69:
+ d7:da:30:73:36:68:e1:fc:10:28:41:ee:6c:7f:88:
+ 08:3e:93:77:63:8a:aa:c8:a8:7b:cb:34:70:04:a1:
+ 6c:3b:6d:48:27:d4:3d:17:ba:0c:a3:e1:8a:5a:ab:
+ 1f:e1:72:26:c3:8e:26:32:28:d9:72:49:0e:ee:e5:
+ 75:43
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:21:30:C9:FB:00:D7:4E:98:DA:87:AA:2A:D0:A7:2E:B1:40:31:A7:4C
+
+ X509v3 Subject Key Identifier:
+ 8A:35:E4:35:3A:BC:11:A1:9E:FB:F5:4F:34:66:D5:4B:AC:4C:62:68
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.networksolutions.com/legal/SSL-legal-repository-ev-cps.jsp
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.netsolssl.com/NetworkSolutionsCertificateAuthority.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/NetworkSolutionsAddTrustEVServerCA.crt
+ OCSP - URI:http://ocsp.netsolssl.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 3b:41:a7:b0:f6:24:18:e5:c8:77:0e:a8:05:bc:e8:48:57:ce:
+ 81:23:ff:17:98:68:01:89:c5:69:9e:c2:ab:45:ab:73:4c:25:
+ c9:6f:77:05:72:10:eb:9e:5e:72:0c:f7:d3:7f:bc:63:1c:b0:
+ e5:4c:44:01:99:1f:e1:de:fc:70:e3:77:e5:d8:e9:a9:2d:95:
+ dd:05:cf:6e:c5:c7:d9:dc:2f:d1:40:7e:8f:e9:47:8b:87:d9:
+ 81:33:a5:2b:4c:b9:2e:a4:e1:a8:cc:1c:6b:cf:04:36:5a:aa:
+ a4:a0:74:30:1b:51:20:c7:61:b9:50:18:e4:bf:2b:c3:f8:a6:
+ fa:8c:89:16:21:99:a7:5a:43:99:03:6d:74:e0:8b:ea:b0:78:
+ 8e:20:01:d2:29:b2:8c:f1:7b:2a:08:b2:62:6a:30:36:5d:5c:
+ a7:3b:4a:ee:f7:07:32:47:2d:f6:88:62:0c:a9:24:e0:70:df:
+ a2:a6:42:0c:7b:7d:28:05:d7:0b:6d:e5:84:fb:f0:c9:88:b3:
+ a9:d9:01:c3:9c:98:dc:cb:83:47:ec:f9:d1:9e:a0:5c:5d:a7:
+ 31:52:b8:5d:b0:91:03:6f:1e:6a:ef:e3:36:02:e3:1a:5d:31:
+ 4a:90:16:1b:d7:33:05:30:fb:00:aa:28:eb:5f:0d:e7:14:56:
+ 27:5d:7c:b4
+-----BEGIN CERTIFICATE-----
+MIIE8DCCA9igAwIBAgIQeqyiHVOdFFQRPARe2DX46jANBgkqhkiG9w0BAQUFADBi
+MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
+MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
+dHkwHhcNMTAxMTI2MDAwMDAwWhcNMjAwNTMwMTA0ODM4WjBZMQswCQYDVQQGEwJV
+UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMScwJQYDVQQDEx5O
+ZXR3b3JrIFNvbHV0aW9ucyBFViBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDQNVzi55UamI/YT9bV3H5cgr+fzEv6PEqBvNrFp+mtmiaP
+3BksYxI+Vt915kis40eQf18I8aOA0dDNJc1Z860uw+sGCf45JDmioezExJrXoAhV
+/sjFZC785waIlcE+MVpV8B2YBJS0f17ckKmhhceqErmH0aNxEQJsfpvJOevstVgn
+i6OYEaCrg/skMACuAlf+gOLKj0hgYznbr5Z0g7s7bO+zM8am3DHp+byqtx7I9H9Y
+aXLuWo82Cv4yERw0PXmIadfaMHM2aOH8EChB7mx/iAg+k3djiqrIqHvLNHAEoWw7
+bUgn1D0Xugyj4Ypaqx/hcibDjiYyKNlySQ7u5XVDAgMBAAGjggGpMIIBpTAfBgNV
+HSMEGDAWgBQhMMn7ANdOmNqHqirQpy6xQDGnTDAdBgNVHQ4EFgQUijXkNTq8EaGe
++/VPNGbVS6xMYmgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+ZgYDVR0gBF8wXTBbBgRVHSAAMFMwUQYIKwYBBQUHAgEWRWh0dHA6Ly93d3cubmV0
+d29ya3NvbHV0aW9ucy5jb20vbGVnYWwvU1NMLWxlZ2FsLXJlcG9zaXRvcnktZXYt
+Y3BzLmpzcDBSBgNVHR8ESzBJMEegRaBDhkFodHRwOi8vY3JsLm5ldHNvbHNzbC5j
+b20vTmV0d29ya1NvbHV0aW9uc0NlcnRpZmljYXRlQXV0aG9yaXR5LmNybDCBggYI
+KwYBBQUHAQEEdjB0MEsGCCsGAQUFBzAChj9odHRwOi8vY3J0LnVzZXJ0cnVzdC5j
+b20vTmV0d29ya1NvbHV0aW9uc0FkZFRydXN0RVZTZXJ2ZXJDQS5jcnQwJQYIKwYB
+BQUHMAGGGWh0dHA6Ly9vY3NwLm5ldHNvbHNzbC5jb20wDQYJKoZIhvcNAQEFBQAD
+ggEBADtBp7D2JBjlyHcOqAW86EhXzoEj/xeYaAGJxWmewqtFq3NMJclvdwVyEOue
+XnIM99N/vGMcsOVMRAGZH+He/HDjd+XY6aktld0Fz27Fx9ncL9FAfo/pR4uH2YEz
+pStMuS6k4ajMHGvPBDZaqqSgdDAbUSDHYblQGOS/K8P4pvqMiRYhmadaQ5kDbXTg
+i+qweI4gAdIpsozxeyoIsmJqMDZdXKc7Su73BzJHLfaIYgypJOBw36KmQgx7fSgF
+1wtt5YT78MmIs6nZAcOcmNzLg0fs+dGeoFxdpzFSuF2wkQNvHmrv4zYC4xpdMUqQ
+FhvXMwUw+wCqKOtfDecUViddfLQ=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert62[] = {
+ 0x30, 0x82, 0x04, 0xf0, 0x30, 0x82, 0x03, 0xd8, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x7a, 0xac, 0xa2, 0x1d, 0x53, 0x9d, 0x14, 0x54, 0x11,
+ 0x3c, 0x04, 0x5e, 0xd8, 0x35, 0xf8, 0xea, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x62,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
+ 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, 0x4e,
+ 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+ 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x31, 0x31, 0x32, 0x36,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30,
+ 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30, 0x59,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e,
+ 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1e, 0x4e,
+ 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x45, 0x56, 0x20, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xd0, 0x35, 0x5c, 0xe2, 0xe7, 0x95, 0x1a, 0x98, 0x8f, 0xd8,
+ 0x4f, 0xd6, 0xd5, 0xdc, 0x7e, 0x5c, 0x82, 0xbf, 0x9f, 0xcc, 0x4b, 0xfa,
+ 0x3c, 0x4a, 0x81, 0xbc, 0xda, 0xc5, 0xa7, 0xe9, 0xad, 0x9a, 0x26, 0x8f,
+ 0xdc, 0x19, 0x2c, 0x63, 0x12, 0x3e, 0x56, 0xdf, 0x75, 0xe6, 0x48, 0xac,
+ 0xe3, 0x47, 0x90, 0x7f, 0x5f, 0x08, 0xf1, 0xa3, 0x80, 0xd1, 0xd0, 0xcd,
+ 0x25, 0xcd, 0x59, 0xf3, 0xad, 0x2e, 0xc3, 0xeb, 0x06, 0x09, 0xfe, 0x39,
+ 0x24, 0x39, 0xa2, 0xa1, 0xec, 0xc4, 0xc4, 0x9a, 0xd7, 0xa0, 0x08, 0x55,
+ 0xfe, 0xc8, 0xc5, 0x64, 0x2e, 0xfc, 0xe7, 0x06, 0x88, 0x95, 0xc1, 0x3e,
+ 0x31, 0x5a, 0x55, 0xf0, 0x1d, 0x98, 0x04, 0x94, 0xb4, 0x7f, 0x5e, 0xdc,
+ 0x90, 0xa9, 0xa1, 0x85, 0xc7, 0xaa, 0x12, 0xb9, 0x87, 0xd1, 0xa3, 0x71,
+ 0x11, 0x02, 0x6c, 0x7e, 0x9b, 0xc9, 0x39, 0xeb, 0xec, 0xb5, 0x58, 0x27,
+ 0x8b, 0xa3, 0x98, 0x11, 0xa0, 0xab, 0x83, 0xfb, 0x24, 0x30, 0x00, 0xae,
+ 0x02, 0x57, 0xfe, 0x80, 0xe2, 0xca, 0x8f, 0x48, 0x60, 0x63, 0x39, 0xdb,
+ 0xaf, 0x96, 0x74, 0x83, 0xbb, 0x3b, 0x6c, 0xef, 0xb3, 0x33, 0xc6, 0xa6,
+ 0xdc, 0x31, 0xe9, 0xf9, 0xbc, 0xaa, 0xb7, 0x1e, 0xc8, 0xf4, 0x7f, 0x58,
+ 0x69, 0x72, 0xee, 0x5a, 0x8f, 0x36, 0x0a, 0xfe, 0x32, 0x11, 0x1c, 0x34,
+ 0x3d, 0x79, 0x88, 0x69, 0xd7, 0xda, 0x30, 0x73, 0x36, 0x68, 0xe1, 0xfc,
+ 0x10, 0x28, 0x41, 0xee, 0x6c, 0x7f, 0x88, 0x08, 0x3e, 0x93, 0x77, 0x63,
+ 0x8a, 0xaa, 0xc8, 0xa8, 0x7b, 0xcb, 0x34, 0x70, 0x04, 0xa1, 0x6c, 0x3b,
+ 0x6d, 0x48, 0x27, 0xd4, 0x3d, 0x17, 0xba, 0x0c, 0xa3, 0xe1, 0x8a, 0x5a,
+ 0xab, 0x1f, 0xe1, 0x72, 0x26, 0xc3, 0x8e, 0x26, 0x32, 0x28, 0xd9, 0x72,
+ 0x49, 0x0e, 0xee, 0xe5, 0x75, 0x43, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0xa9, 0x30, 0x82, 0x01, 0xa5, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x30, 0xc9, 0xfb,
+ 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87, 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1,
+ 0x40, 0x31, 0xa7, 0x4c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x8a, 0x35, 0xe4, 0x35, 0x3a, 0xbc, 0x11, 0xa1, 0x9e,
+ 0xfb, 0xf5, 0x4f, 0x34, 0x66, 0xd5, 0x4b, 0xac, 0x4c, 0x62, 0x68, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x66, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x5f, 0x30, 0x5d, 0x30, 0x5b,
+ 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x53, 0x30, 0x51, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x45, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6e, 0x65, 0x74,
+ 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2f,
+ 0x53, 0x53, 0x4c, 0x2d, 0x6c, 0x65, 0x67, 0x61, 0x6c, 0x2d, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2d, 0x65, 0x76, 0x2d,
+ 0x63, 0x70, 0x73, 0x2e, 0x6a, 0x73, 0x70, 0x30, 0x52, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x4b, 0x30, 0x49, 0x30, 0x47, 0xa0, 0x45, 0xa0, 0x43,
+ 0x86, 0x41, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2e, 0x6e, 0x65, 0x74, 0x73, 0x6f, 0x6c, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x6f,
+ 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x82, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x76, 0x30, 0x74,
+ 0x30, 0x4b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02,
+ 0x86, 0x3f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74,
+ 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x53, 0x6f,
+ 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x41, 0x64, 0x64, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x45, 0x56, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x6e, 0x65, 0x74, 0x73, 0x6f,
+ 0x6c, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x3b, 0x41, 0xa7, 0xb0, 0xf6, 0x24, 0x18, 0xe5,
+ 0xc8, 0x77, 0x0e, 0xa8, 0x05, 0xbc, 0xe8, 0x48, 0x57, 0xce, 0x81, 0x23,
+ 0xff, 0x17, 0x98, 0x68, 0x01, 0x89, 0xc5, 0x69, 0x9e, 0xc2, 0xab, 0x45,
+ 0xab, 0x73, 0x4c, 0x25, 0xc9, 0x6f, 0x77, 0x05, 0x72, 0x10, 0xeb, 0x9e,
+ 0x5e, 0x72, 0x0c, 0xf7, 0xd3, 0x7f, 0xbc, 0x63, 0x1c, 0xb0, 0xe5, 0x4c,
+ 0x44, 0x01, 0x99, 0x1f, 0xe1, 0xde, 0xfc, 0x70, 0xe3, 0x77, 0xe5, 0xd8,
+ 0xe9, 0xa9, 0x2d, 0x95, 0xdd, 0x05, 0xcf, 0x6e, 0xc5, 0xc7, 0xd9, 0xdc,
+ 0x2f, 0xd1, 0x40, 0x7e, 0x8f, 0xe9, 0x47, 0x8b, 0x87, 0xd9, 0x81, 0x33,
+ 0xa5, 0x2b, 0x4c, 0xb9, 0x2e, 0xa4, 0xe1, 0xa8, 0xcc, 0x1c, 0x6b, 0xcf,
+ 0x04, 0x36, 0x5a, 0xaa, 0xa4, 0xa0, 0x74, 0x30, 0x1b, 0x51, 0x20, 0xc7,
+ 0x61, 0xb9, 0x50, 0x18, 0xe4, 0xbf, 0x2b, 0xc3, 0xf8, 0xa6, 0xfa, 0x8c,
+ 0x89, 0x16, 0x21, 0x99, 0xa7, 0x5a, 0x43, 0x99, 0x03, 0x6d, 0x74, 0xe0,
+ 0x8b, 0xea, 0xb0, 0x78, 0x8e, 0x20, 0x01, 0xd2, 0x29, 0xb2, 0x8c, 0xf1,
+ 0x7b, 0x2a, 0x08, 0xb2, 0x62, 0x6a, 0x30, 0x36, 0x5d, 0x5c, 0xa7, 0x3b,
+ 0x4a, 0xee, 0xf7, 0x07, 0x32, 0x47, 0x2d, 0xf6, 0x88, 0x62, 0x0c, 0xa9,
+ 0x24, 0xe0, 0x70, 0xdf, 0xa2, 0xa6, 0x42, 0x0c, 0x7b, 0x7d, 0x28, 0x05,
+ 0xd7, 0x0b, 0x6d, 0xe5, 0x84, 0xfb, 0xf0, 0xc9, 0x88, 0xb3, 0xa9, 0xd9,
+ 0x01, 0xc3, 0x9c, 0x98, 0xdc, 0xcb, 0x83, 0x47, 0xec, 0xf9, 0xd1, 0x9e,
+ 0xa0, 0x5c, 0x5d, 0xa7, 0x31, 0x52, 0xb8, 0x5d, 0xb0, 0x91, 0x03, 0x6f,
+ 0x1e, 0x6a, 0xef, 0xe3, 0x36, 0x02, 0xe3, 0x1a, 0x5d, 0x31, 0x4a, 0x90,
+ 0x16, 0x1b, 0xd7, 0x33, 0x05, 0x30, 0xfb, 0x00, 0xaa, 0x28, 0xeb, 0x5f,
+ 0x0d, 0xe7, 0x14, 0x56, 0x27, 0x5d, 0x7c, 0xb4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4b:75:57:82:69:39:0c:9b:e3:2f:12:ec:5f:6d:94:5e
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Feb 11 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d0:40:8b:8b:72:e3:91:1b:f7:51:c1:1b:54:04:
+ 98:d3:a9:bf:c1:e6:8a:5d:3b:87:fb:bb:88:ce:0d:
+ e3:2f:3f:06:96:f0:a2:29:50:99:ae:db:3b:a1:57:
+ b0:74:51:71:cd:ed:42:91:4d:41:fe:a9:c8:d8:6a:
+ 86:77:44:bb:59:66:97:50:5e:b4:d4:2c:70:44:cf:
+ da:37:95:42:69:3c:30:c4:71:b3:52:f0:21:4d:a1:
+ d8:ba:39:7c:1c:9e:a3:24:9d:f2:83:16:98:aa:16:
+ 7c:43:9b:15:5b:b7:ae:34:91:fe:d4:62:26:18:46:
+ 9a:3f:eb:c1:f9:f1:90:57:eb:ac:7a:0d:8b:db:72:
+ 30:6a:66:d5:e0:46:a3:70:dc:68:d9:ff:04:48:89:
+ 77:de:b5:e9:fb:67:6d:41:e9:bc:39:bd:32:d9:62:
+ 02:f1:b1:a8:3d:6e:37:9c:e2:2f:e2:d3:a2:26:8b:
+ c6:b8:55:43:88:e1:23:3e:a5:d2:24:39:6a:47:ab:
+ 00:d4:a1:b3:a9:25:fe:0d:3f:a7:1d:ba:d3:51:c1:
+ 0b:a4:da:ac:38:ef:55:50:24:05:65:46:93:34:4f:
+ 2d:8d:ad:c6:d4:21:19:d2:8e:ca:05:61:71:07:73:
+ 47:e5:8a:19:12:bd:04:4d:ce:4e:9c:a5:48:ac:bb:
+ 26:f7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 4d:87:0d:50:30:f3:82:5d:c4:3f:d4:ef:ee:8d:48:e3:e7:bd:
+ 90:6b:c4:32:38:c6:5e:28:ab:5c:a5:ad:61:f9:8e:bb:85:14:
+ 39:21:51:5b:8e:8c:dc:17:92:80:2f:83:94:69:88:c1:be:27:
+ 8e:4f:9f:a9:83:d8:be:d7:87:92:71:a3:b6:fd:11:74:b8:95:
+ 81:28:20:77:0d:43:77:75:76:38:1d:4d:1b:2e:97:89:8c:0a:
+ 1b:66:16:52:d4:14:9a:6f:80:48:16:de:30:c0:42:68:ea:bf:
+ a2:ba:2a:44:4d:ac:89:e2:f3:cc:53:9b:e3:e6:1d:6e:4f:98:
+ 9f:d9:0e:51:50:86:e0:1a:34:32:24:80:7d:3a:87:f3:3c:e5:
+ 5a:4d:b7:8b:bd:0a:24:0d:ae:db:f4:8f:5c:d2:66:0c:82:1c:
+ 72:37:b6:d1:b9:d0:98:34:1b:27:6d:8b:5e:1e:40:73:18:fa:
+ a8:e4:c6:e8:90:c3:ab:19:e4:c1:a1:cd:4c:d4:3a:b6:88:c8:
+ f3:d0:65:61:3a:bf:18:f4:af:1c:56:a9:eb:97:38:d9:20:29:
+ 1f:3f:2a:29:47:9d:8a:0f:6a:12:81:44:02:21:d4:3b:3a:1a:
+ 2b:1e:40:43:7d:94:a0:69:0e:fc:2e:fb:52:f6:fd:2e:32:d8:
+ cb:6b:bd:eb
+-----BEGIN CERTIFICATE-----
+MIIE8TCCA9mgAwIBAgIQS3VXgmk5DJvjLxLsX22UXjANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMDIxMTAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+gYExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMScwJQYD
+VQQDEx5DT01PRE8gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDQQIuLcuORG/dRwRtUBJjTqb/B5opdO4f7u4jO
+DeMvPwaW8KIpUJmu2zuhV7B0UXHN7UKRTUH+qcjYaoZ3RLtZZpdQXrTULHBEz9o3
+lUJpPDDEcbNS8CFNodi6OXwcnqMknfKDFpiqFnxDmxVbt640kf7UYiYYRpo/68H5
+8ZBX66x6DYvbcjBqZtXgRqNw3GjZ/wRIiXfeten7Z21B6bw5vTLZYgLxsag9bjec
+4i/i06Imi8a4VUOI4SM+pdIkOWpHqwDUobOpJf4NP6cdutNRwQuk2qw471VQJAVl
+RpM0Ty2NrcbUIRnSjsoFYXEHc0flihkSvQRNzk6cpUisuyb3AgMBAAGjggF0MIIB
+cDAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUC1jl
+i8ZMFTekQKkwqSG+RzZaVv8wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB
+Af8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9j
+cmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYI
+KwYBBQUHAQEEgaYwgaMwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0
+LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0
+cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsG
+AQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUA
+A4IBAQBNhw1QMPOCXcQ/1O/ujUjj572Qa8QyOMZeKKtcpa1h+Y67hRQ5IVFbjozc
+F5KAL4OUaYjBvieOT5+pg9i+14eScaO2/RF0uJWBKCB3DUN3dXY4HU0bLpeJjAob
+ZhZS1BSab4BIFt4wwEJo6r+iuipETayJ4vPMU5vj5h1uT5if2Q5RUIbgGjQyJIB9
+OofzPOVaTbeLvQokDa7b9I9c0mYMghxyN7bRudCYNBsnbYteHkBzGPqo5MbokMOr
+GeTBoc1M1Dq2iMjz0GVhOr8Y9K8cVqnrlzjZICkfPyopR52KD2oSgUQCIdQ7Ohor
+HkBDfZSgaQ78LvtS9v0uMtjLa73r
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert63[] = {
+ 0x30, 0x82, 0x04, 0xf1, 0x30, 0x82, 0x03, 0xd9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4b, 0x75, 0x57, 0x82, 0x69, 0x39, 0x0c, 0x9b, 0xe3,
+ 0x2f, 0x12, 0xec, 0x5f, 0x6d, 0x94, 0x5e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x31,
+ 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61,
+ 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f,
+ 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0x40, 0x8b, 0x8b,
+ 0x72, 0xe3, 0x91, 0x1b, 0xf7, 0x51, 0xc1, 0x1b, 0x54, 0x04, 0x98, 0xd3,
+ 0xa9, 0xbf, 0xc1, 0xe6, 0x8a, 0x5d, 0x3b, 0x87, 0xfb, 0xbb, 0x88, 0xce,
+ 0x0d, 0xe3, 0x2f, 0x3f, 0x06, 0x96, 0xf0, 0xa2, 0x29, 0x50, 0x99, 0xae,
+ 0xdb, 0x3b, 0xa1, 0x57, 0xb0, 0x74, 0x51, 0x71, 0xcd, 0xed, 0x42, 0x91,
+ 0x4d, 0x41, 0xfe, 0xa9, 0xc8, 0xd8, 0x6a, 0x86, 0x77, 0x44, 0xbb, 0x59,
+ 0x66, 0x97, 0x50, 0x5e, 0xb4, 0xd4, 0x2c, 0x70, 0x44, 0xcf, 0xda, 0x37,
+ 0x95, 0x42, 0x69, 0x3c, 0x30, 0xc4, 0x71, 0xb3, 0x52, 0xf0, 0x21, 0x4d,
+ 0xa1, 0xd8, 0xba, 0x39, 0x7c, 0x1c, 0x9e, 0xa3, 0x24, 0x9d, 0xf2, 0x83,
+ 0x16, 0x98, 0xaa, 0x16, 0x7c, 0x43, 0x9b, 0x15, 0x5b, 0xb7, 0xae, 0x34,
+ 0x91, 0xfe, 0xd4, 0x62, 0x26, 0x18, 0x46, 0x9a, 0x3f, 0xeb, 0xc1, 0xf9,
+ 0xf1, 0x90, 0x57, 0xeb, 0xac, 0x7a, 0x0d, 0x8b, 0xdb, 0x72, 0x30, 0x6a,
+ 0x66, 0xd5, 0xe0, 0x46, 0xa3, 0x70, 0xdc, 0x68, 0xd9, 0xff, 0x04, 0x48,
+ 0x89, 0x77, 0xde, 0xb5, 0xe9, 0xfb, 0x67, 0x6d, 0x41, 0xe9, 0xbc, 0x39,
+ 0xbd, 0x32, 0xd9, 0x62, 0x02, 0xf1, 0xb1, 0xa8, 0x3d, 0x6e, 0x37, 0x9c,
+ 0xe2, 0x2f, 0xe2, 0xd3, 0xa2, 0x26, 0x8b, 0xc6, 0xb8, 0x55, 0x43, 0x88,
+ 0xe1, 0x23, 0x3e, 0xa5, 0xd2, 0x24, 0x39, 0x6a, 0x47, 0xab, 0x00, 0xd4,
+ 0xa1, 0xb3, 0xa9, 0x25, 0xfe, 0x0d, 0x3f, 0xa7, 0x1d, 0xba, 0xd3, 0x51,
+ 0xc1, 0x0b, 0xa4, 0xda, 0xac, 0x38, 0xef, 0x55, 0x50, 0x24, 0x05, 0x65,
+ 0x46, 0x93, 0x34, 0x4f, 0x2d, 0x8d, 0xad, 0xc6, 0xd4, 0x21, 0x19, 0xd2,
+ 0x8e, 0xca, 0x05, 0x61, 0x71, 0x07, 0x73, 0x47, 0xe5, 0x8a, 0x19, 0x12,
+ 0xbd, 0x04, 0x4d, 0xce, 0x4e, 0x9c, 0xa5, 0x48, 0xac, 0xbb, 0x26, 0xf7,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x74, 0x30, 0x82, 0x01,
+ 0x70, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4,
+ 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x58, 0xe5,
+ 0x8b, 0xc6, 0x4c, 0x15, 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21, 0xbe,
+ 0x47, 0x36, 0x5a, 0x56, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
+ 0x01, 0xff, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30,
+ 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37,
+ 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30,
+ 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x4d, 0x87, 0x0d, 0x50, 0x30, 0xf3, 0x82,
+ 0x5d, 0xc4, 0x3f, 0xd4, 0xef, 0xee, 0x8d, 0x48, 0xe3, 0xe7, 0xbd, 0x90,
+ 0x6b, 0xc4, 0x32, 0x38, 0xc6, 0x5e, 0x28, 0xab, 0x5c, 0xa5, 0xad, 0x61,
+ 0xf9, 0x8e, 0xbb, 0x85, 0x14, 0x39, 0x21, 0x51, 0x5b, 0x8e, 0x8c, 0xdc,
+ 0x17, 0x92, 0x80, 0x2f, 0x83, 0x94, 0x69, 0x88, 0xc1, 0xbe, 0x27, 0x8e,
+ 0x4f, 0x9f, 0xa9, 0x83, 0xd8, 0xbe, 0xd7, 0x87, 0x92, 0x71, 0xa3, 0xb6,
+ 0xfd, 0x11, 0x74, 0xb8, 0x95, 0x81, 0x28, 0x20, 0x77, 0x0d, 0x43, 0x77,
+ 0x75, 0x76, 0x38, 0x1d, 0x4d, 0x1b, 0x2e, 0x97, 0x89, 0x8c, 0x0a, 0x1b,
+ 0x66, 0x16, 0x52, 0xd4, 0x14, 0x9a, 0x6f, 0x80, 0x48, 0x16, 0xde, 0x30,
+ 0xc0, 0x42, 0x68, 0xea, 0xbf, 0xa2, 0xba, 0x2a, 0x44, 0x4d, 0xac, 0x89,
+ 0xe2, 0xf3, 0xcc, 0x53, 0x9b, 0xe3, 0xe6, 0x1d, 0x6e, 0x4f, 0x98, 0x9f,
+ 0xd9, 0x0e, 0x51, 0x50, 0x86, 0xe0, 0x1a, 0x34, 0x32, 0x24, 0x80, 0x7d,
+ 0x3a, 0x87, 0xf3, 0x3c, 0xe5, 0x5a, 0x4d, 0xb7, 0x8b, 0xbd, 0x0a, 0x24,
+ 0x0d, 0xae, 0xdb, 0xf4, 0x8f, 0x5c, 0xd2, 0x66, 0x0c, 0x82, 0x1c, 0x72,
+ 0x37, 0xb6, 0xd1, 0xb9, 0xd0, 0x98, 0x34, 0x1b, 0x27, 0x6d, 0x8b, 0x5e,
+ 0x1e, 0x40, 0x73, 0x18, 0xfa, 0xa8, 0xe4, 0xc6, 0xe8, 0x90, 0xc3, 0xab,
+ 0x19, 0xe4, 0xc1, 0xa1, 0xcd, 0x4c, 0xd4, 0x3a, 0xb6, 0x88, 0xc8, 0xf3,
+ 0xd0, 0x65, 0x61, 0x3a, 0xbf, 0x18, 0xf4, 0xaf, 0x1c, 0x56, 0xa9, 0xeb,
+ 0x97, 0x38, 0xd9, 0x20, 0x29, 0x1f, 0x3f, 0x2a, 0x29, 0x47, 0x9d, 0x8a,
+ 0x0f, 0x6a, 0x12, 0x81, 0x44, 0x02, 0x21, 0xd4, 0x3b, 0x3a, 0x1a, 0x2b,
+ 0x1e, 0x40, 0x43, 0x7d, 0x94, 0xa0, 0x69, 0x0e, 0xfc, 0x2e, 0xfb, 0x52,
+ 0xf6, 0xfd, 0x2e, 0x32, 0xd8, 0xcb, 0x6b, 0xbd, 0xeb,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6f:25:dc:15:af:df:5e:a3:08:56:0c:3b:7a:4f:c7:f8
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: May 30 10:48:38 2000 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d0:40:8b:8b:72:e3:91:1b:f7:51:c1:1b:54:04:
+ 98:d3:a9:bf:c1:e6:8a:5d:3b:87:fb:bb:88:ce:0d:
+ e3:2f:3f:06:96:f0:a2:29:50:99:ae:db:3b:a1:57:
+ b0:74:51:71:cd:ed:42:91:4d:41:fe:a9:c8:d8:6a:
+ 86:77:44:bb:59:66:97:50:5e:b4:d4:2c:70:44:cf:
+ da:37:95:42:69:3c:30:c4:71:b3:52:f0:21:4d:a1:
+ d8:ba:39:7c:1c:9e:a3:24:9d:f2:83:16:98:aa:16:
+ 7c:43:9b:15:5b:b7:ae:34:91:fe:d4:62:26:18:46:
+ 9a:3f:eb:c1:f9:f1:90:57:eb:ac:7a:0d:8b:db:72:
+ 30:6a:66:d5:e0:46:a3:70:dc:68:d9:ff:04:48:89:
+ 77:de:b5:e9:fb:67:6d:41:e9:bc:39:bd:32:d9:62:
+ 02:f1:b1:a8:3d:6e:37:9c:e2:2f:e2:d3:a2:26:8b:
+ c6:b8:55:43:88:e1:23:3e:a5:d2:24:39:6a:47:ab:
+ 00:d4:a1:b3:a9:25:fe:0d:3f:a7:1d:ba:d3:51:c1:
+ 0b:a4:da:ac:38:ef:55:50:24:05:65:46:93:34:4f:
+ 2d:8d:ad:c6:d4:21:19:d2:8e:ca:05:61:71:07:73:
+ 47:e5:8a:19:12:bd:04:4d:ce:4e:9c:a5:48:ac:bb:
+ 26:f7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 07:60:93:99:aa:ce:d0:d3:47:d0:37:33:de:3f:64:b7:e5:2e:
+ a3:25:0c:d5:33:1d:0d:8d:ab:f6:7e:46:7b:59:06:92:e3:82:
+ c4:e7:f5:f6:f3:d9:05:cf:49:34:2d:37:5f:f4:25:c7:f0:fb:
+ 6b:23:77:f1:f1:40:d7:4c:bb:49:45:31:dd:00:28:67:b7:29:
+ 4c:75:a8:1f:79:31:c9:36:37:0f:ca:35:4f:8c:f1:7e:de:fc:
+ 46:ab:bf:68:9b:70:23:30:2e:b7:c5:5c:7b:8a:fb:18:13:79:
+ 4b:92:42:8c:dc:2c:ab:6c:22:b7:28:53:b3:1a:4a:ce:1b:fb:
+ 28:0e:b7:3a:a4:da:0d:f7:40:32:4f:df:6f:bb:01:50:fc:87:
+ d3:76:d9:fc:fb:b6:84:03:ca:c9:36:18:f7:dd:6c:db:bb:ba:
+ 81:1c:a6:ad:fe:28:f9:cf:b9:a2:71:5d:19:05:ea:4a:46:dc:
+ 73:41:ef:89:94:42:b1:43:88:6f:35:17:af:1e:60:83:ac:7a:
+ 8c:10:7b:9f:c9:f6:83:6d:9e:fa:88:ee:3e:dd:ee:9e:b0:bf:
+ e0:6a:b9:d0:9f:07:b2:09:13:9a:f5:a4:e5:c8:5b:79:a7:47:
+ 35:33:68:e5:55:9e:aa:5b:cb:30:0b:9d:c7:0f:bf:68:44:81:
+ 97:8b:51:4a
+-----BEGIN CERTIFICATE-----
+MIIE8TCCA9mgAwIBAgIQbyXcFa/fXqMIVgw7ek/H+DANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
+gYExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMScwJQYD
+VQQDEx5DT01PRE8gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDQQIuLcuORG/dRwRtUBJjTqb/B5opdO4f7u4jO
+DeMvPwaW8KIpUJmu2zuhV7B0UXHN7UKRTUH+qcjYaoZ3RLtZZpdQXrTULHBEz9o3
+lUJpPDDEcbNS8CFNodi6OXwcnqMknfKDFpiqFnxDmxVbt640kf7UYiYYRpo/68H5
+8ZBX66x6DYvbcjBqZtXgRqNw3GjZ/wRIiXfeten7Z21B6bw5vTLZYgLxsag9bjec
+4i/i06Imi8a4VUOI4SM+pdIkOWpHqwDUobOpJf4NP6cdutNRwQuk2qw471VQJAVl
+RpM0Ty2NrcbUIRnSjsoFYXEHc0flihkSvQRNzk6cpUisuyb3AgMBAAGjggF0MIIB
+cDAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUC1jl
+i8ZMFTekQKkwqSG+RzZaVv8wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB
+Af8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9j
+cmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYI
+KwYBBQUHAQEEgaYwgaMwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0
+LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0
+cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsG
+AQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUA
+A4IBAQAHYJOZqs7Q00fQNzPeP2S35S6jJQzVMx0Njav2fkZ7WQaS44LE5/X289kF
+z0k0LTdf9CXH8PtrI3fx8UDXTLtJRTHdAChntylMdagfeTHJNjcPyjVPjPF+3vxG
+q79om3AjMC63xVx7ivsYE3lLkkKM3CyrbCK3KFOzGkrOG/soDrc6pNoN90AyT99v
+uwFQ/IfTdtn8+7aEA8rJNhj33Wzbu7qBHKat/ij5z7micV0ZBepKRtxzQe+JlEKx
+Q4hvNRevHmCDrHqMEHufyfaDbZ76iO4+3e6esL/garnQnweyCROa9aTlyFt5p0c1
+M2jlVZ6qW8swC53HD79oRIGXi1FK
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert64[] = {
+ 0x30, 0x82, 0x04, 0xf1, 0x30, 0x82, 0x03, 0xd9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6f, 0x25, 0xdc, 0x15, 0xaf, 0xdf, 0x5e, 0xa3, 0x08,
+ 0x56, 0x0c, 0x3b, 0x7a, 0x4f, 0xc7, 0xf8, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x35, 0x33,
+ 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61,
+ 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f,
+ 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0x40, 0x8b, 0x8b,
+ 0x72, 0xe3, 0x91, 0x1b, 0xf7, 0x51, 0xc1, 0x1b, 0x54, 0x04, 0x98, 0xd3,
+ 0xa9, 0xbf, 0xc1, 0xe6, 0x8a, 0x5d, 0x3b, 0x87, 0xfb, 0xbb, 0x88, 0xce,
+ 0x0d, 0xe3, 0x2f, 0x3f, 0x06, 0x96, 0xf0, 0xa2, 0x29, 0x50, 0x99, 0xae,
+ 0xdb, 0x3b, 0xa1, 0x57, 0xb0, 0x74, 0x51, 0x71, 0xcd, 0xed, 0x42, 0x91,
+ 0x4d, 0x41, 0xfe, 0xa9, 0xc8, 0xd8, 0x6a, 0x86, 0x77, 0x44, 0xbb, 0x59,
+ 0x66, 0x97, 0x50, 0x5e, 0xb4, 0xd4, 0x2c, 0x70, 0x44, 0xcf, 0xda, 0x37,
+ 0x95, 0x42, 0x69, 0x3c, 0x30, 0xc4, 0x71, 0xb3, 0x52, 0xf0, 0x21, 0x4d,
+ 0xa1, 0xd8, 0xba, 0x39, 0x7c, 0x1c, 0x9e, 0xa3, 0x24, 0x9d, 0xf2, 0x83,
+ 0x16, 0x98, 0xaa, 0x16, 0x7c, 0x43, 0x9b, 0x15, 0x5b, 0xb7, 0xae, 0x34,
+ 0x91, 0xfe, 0xd4, 0x62, 0x26, 0x18, 0x46, 0x9a, 0x3f, 0xeb, 0xc1, 0xf9,
+ 0xf1, 0x90, 0x57, 0xeb, 0xac, 0x7a, 0x0d, 0x8b, 0xdb, 0x72, 0x30, 0x6a,
+ 0x66, 0xd5, 0xe0, 0x46, 0xa3, 0x70, 0xdc, 0x68, 0xd9, 0xff, 0x04, 0x48,
+ 0x89, 0x77, 0xde, 0xb5, 0xe9, 0xfb, 0x67, 0x6d, 0x41, 0xe9, 0xbc, 0x39,
+ 0xbd, 0x32, 0xd9, 0x62, 0x02, 0xf1, 0xb1, 0xa8, 0x3d, 0x6e, 0x37, 0x9c,
+ 0xe2, 0x2f, 0xe2, 0xd3, 0xa2, 0x26, 0x8b, 0xc6, 0xb8, 0x55, 0x43, 0x88,
+ 0xe1, 0x23, 0x3e, 0xa5, 0xd2, 0x24, 0x39, 0x6a, 0x47, 0xab, 0x00, 0xd4,
+ 0xa1, 0xb3, 0xa9, 0x25, 0xfe, 0x0d, 0x3f, 0xa7, 0x1d, 0xba, 0xd3, 0x51,
+ 0xc1, 0x0b, 0xa4, 0xda, 0xac, 0x38, 0xef, 0x55, 0x50, 0x24, 0x05, 0x65,
+ 0x46, 0x93, 0x34, 0x4f, 0x2d, 0x8d, 0xad, 0xc6, 0xd4, 0x21, 0x19, 0xd2,
+ 0x8e, 0xca, 0x05, 0x61, 0x71, 0x07, 0x73, 0x47, 0xe5, 0x8a, 0x19, 0x12,
+ 0xbd, 0x04, 0x4d, 0xce, 0x4e, 0x9c, 0xa5, 0x48, 0xac, 0xbb, 0x26, 0xf7,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x74, 0x30, 0x82, 0x01,
+ 0x70, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4,
+ 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x0b, 0x58, 0xe5,
+ 0x8b, 0xc6, 0x4c, 0x15, 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21, 0xbe,
+ 0x47, 0x36, 0x5a, 0x56, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01,
+ 0x01, 0xff, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30,
+ 0x08, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37,
+ 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30,
+ 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43,
+ 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x07, 0x60, 0x93, 0x99, 0xaa, 0xce, 0xd0,
+ 0xd3, 0x47, 0xd0, 0x37, 0x33, 0xde, 0x3f, 0x64, 0xb7, 0xe5, 0x2e, 0xa3,
+ 0x25, 0x0c, 0xd5, 0x33, 0x1d, 0x0d, 0x8d, 0xab, 0xf6, 0x7e, 0x46, 0x7b,
+ 0x59, 0x06, 0x92, 0xe3, 0x82, 0xc4, 0xe7, 0xf5, 0xf6, 0xf3, 0xd9, 0x05,
+ 0xcf, 0x49, 0x34, 0x2d, 0x37, 0x5f, 0xf4, 0x25, 0xc7, 0xf0, 0xfb, 0x6b,
+ 0x23, 0x77, 0xf1, 0xf1, 0x40, 0xd7, 0x4c, 0xbb, 0x49, 0x45, 0x31, 0xdd,
+ 0x00, 0x28, 0x67, 0xb7, 0x29, 0x4c, 0x75, 0xa8, 0x1f, 0x79, 0x31, 0xc9,
+ 0x36, 0x37, 0x0f, 0xca, 0x35, 0x4f, 0x8c, 0xf1, 0x7e, 0xde, 0xfc, 0x46,
+ 0xab, 0xbf, 0x68, 0x9b, 0x70, 0x23, 0x30, 0x2e, 0xb7, 0xc5, 0x5c, 0x7b,
+ 0x8a, 0xfb, 0x18, 0x13, 0x79, 0x4b, 0x92, 0x42, 0x8c, 0xdc, 0x2c, 0xab,
+ 0x6c, 0x22, 0xb7, 0x28, 0x53, 0xb3, 0x1a, 0x4a, 0xce, 0x1b, 0xfb, 0x28,
+ 0x0e, 0xb7, 0x3a, 0xa4, 0xda, 0x0d, 0xf7, 0x40, 0x32, 0x4f, 0xdf, 0x6f,
+ 0xbb, 0x01, 0x50, 0xfc, 0x87, 0xd3, 0x76, 0xd9, 0xfc, 0xfb, 0xb6, 0x84,
+ 0x03, 0xca, 0xc9, 0x36, 0x18, 0xf7, 0xdd, 0x6c, 0xdb, 0xbb, 0xba, 0x81,
+ 0x1c, 0xa6, 0xad, 0xfe, 0x28, 0xf9, 0xcf, 0xb9, 0xa2, 0x71, 0x5d, 0x19,
+ 0x05, 0xea, 0x4a, 0x46, 0xdc, 0x73, 0x41, 0xef, 0x89, 0x94, 0x42, 0xb1,
+ 0x43, 0x88, 0x6f, 0x35, 0x17, 0xaf, 0x1e, 0x60, 0x83, 0xac, 0x7a, 0x8c,
+ 0x10, 0x7b, 0x9f, 0xc9, 0xf6, 0x83, 0x6d, 0x9e, 0xfa, 0x88, 0xee, 0x3e,
+ 0xdd, 0xee, 0x9e, 0xb0, 0xbf, 0xe0, 0x6a, 0xb9, 0xd0, 0x9f, 0x07, 0xb2,
+ 0x09, 0x13, 0x9a, 0xf5, 0xa4, 0xe5, 0xc8, 0x5b, 0x79, 0xa7, 0x47, 0x35,
+ 0x33, 0x68, 0xe5, 0x55, 0x9e, 0xaa, 0x5b, 0xcb, 0x30, 0x0b, 0x9d, 0xc7,
+ 0x0f, 0xbf, 0x68, 0x44, 0x81, 0x97, 0x8b, 0x51, 0x4a,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 946072060 (0x3863e9fc)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048)
+ Validity
+ Not Before: Dec 10 20:43:54 2009 GMT
+ Not After : Dec 10 21:13:54 2019 GMT
+ Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/rpa is incorporated by reference, OU=(c) 2009 Entrust, Inc., CN=Entrust Certification Authority - L1C
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:97:a3:2d:3c:9e:de:05:da:13:c2:11:8d:9d:8e:
+ e3:7f:c7:4b:7e:5a:9f:b3:ff:62:ab:73:c8:28:6b:
+ ba:10:64:82:87:13:cd:57:18:ff:28:ce:c0:e6:0e:
+ 06:91:50:29:83:d1:f2:c3:2a:db:d8:db:4e:04:cc:
+ 00:eb:8b:b6:96:dc:bc:aa:fa:52:77:04:c1:db:19:
+ e4:ae:9c:fd:3c:8b:03:ef:4d:bc:1a:03:65:f9:c1:
+ b1:3f:72:86:f2:38:aa:19:ae:10:88:78:28:da:75:
+ c3:3d:02:82:02:9c:b9:c1:65:77:76:24:4c:98:f7:
+ 6d:31:38:fb:db:fe:db:37:02:76:a1:18:97:a6:cc:
+ de:20:09:49:36:24:69:42:f6:e4:37:62:f1:59:6d:
+ a9:3c:ed:34:9c:a3:8e:db:dc:3a:d7:f7:0a:6f:ef:
+ 2e:d8:d5:93:5a:7a:ed:08:49:68:e2:41:e3:5a:90:
+ c1:86:55:fc:51:43:9d:e0:b2:c4:67:b4:cb:32:31:
+ 25:f0:54:9f:4b:d1:6f:db:d4:dd:fc:af:5e:6c:78:
+ 90:95:de:ca:3a:48:b9:79:3c:9b:19:d6:75:05:a0:
+ f9:88:d7:c1:e8:a5:09:e4:1a:15:dc:87:23:aa:b2:
+ 75:8c:63:25:87:d8:f8:3d:a6:c2:cc:66:ff:a5:66:
+ 68:55
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/2048ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/rpa
+
+ X509v3 Subject Key Identifier:
+ 1E:F1:AB:89:06:F8:49:0F:01:33:77:EE:14:7A:EE:19:7C:93:28:4D
+ X509v3 Authority Key Identifier:
+ keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 07:f6:5f:82:84:7f:80:40:c7:90:34:46:42:24:03:ce:2f:ab:
+ ba:83:9e:25:73:0d:ed:ac:05:69:c6:87:ed:a3:5c:f2:57:c1:
+ b1:49:76:9a:4d:f2:3f:dd:e4:0e:fe:0b:3e:b9:98:d9:32:95:
+ 1d:32:f4:01:ee:9c:c8:c8:e5:3f:e0:53:76:62:fc:dd:ab:6d:
+ 3d:94:90:f2:c0:b3:3c:98:27:36:5e:28:97:22:fc:1b:40:d3:
+ 2b:0d:ad:b5:57:6d:df:0f:e3:4b:ef:73:02:10:65:fa:1b:d0:
+ ac:31:d5:e3:0f:e8:ba:32:30:83:ee:4a:d0:bf:df:22:90:7a:
+ be:ec:3a:1b:c4:49:04:1d:f1:ae:80:77:3c:42:08:db:a7:3b:
+ 28:a6:80:01:03:e6:39:a3:eb:df:80:59:1b:f3:2c:be:dc:72:
+ 44:79:a0:6c:07:a5:6d:4d:44:8e:42:68:ca:94:7c:2e:36:ba:
+ 85:9e:cd:aa:c4:5e:3c:54:be:fe:2f:ea:69:9d:1c:1e:29:9b:
+ 96:d8:c8:fe:51:90:f1:24:a6:90:06:b3:f0:29:a2:ff:78:2e:
+ 77:5c:45:21:d9:44:00:31:f3:be:32:4f:f5:0a:32:0d:fc:fc:
+ ba:16:76:56:b2:d6:48:92:f2:8b:a6:3e:b7:ac:5c:69:ea:0b:
+ 3f:66:45:b9
+-----BEGIN CERTIFICATE-----
+MIIE8jCCA9qgAwIBAgIEOGPp/DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0wOTEyMTAyMDQzNTRaFw0xOTEy
+MTAyMTEzNTRaMIGxMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5j
+LjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L3JwYSBpcyBpbmNvcnBvcmF0ZWQg
+YnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwOSBFbnRydXN0LCBJbmMuMS4w
+LAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gTDFDMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl6MtPJ7eBdoTwhGNnY7jf8dL
+flqfs/9iq3PIKGu6EGSChxPNVxj/KM7A5g4GkVApg9Hywyrb2NtOBMwA64u2lty8
+qvpSdwTB2xnkrpz9PIsD7028GgNl+cGxP3KG8jiqGa4QiHgo2nXDPQKCApy5wWV3
+diRMmPdtMTj72/7bNwJ2oRiXpszeIAlJNiRpQvbkN2LxWW2pPO00nKOO29w61/cK
+b+8u2NWTWnrtCElo4kHjWpDBhlX8UUOd4LLEZ7TLMjEl8FSfS9Fv29Td/K9ebHiQ
+ld7KOki5eTybGdZ1BaD5iNfB6KUJ5BoV3IcjqrJ1jGMlh9j4PabCzGb/pWZoVQID
+AQABo4IBCzCCAQcwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wMwYI
+KwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5l
+dDAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmVudHJ1c3QubmV0LzIwNDhj
+YS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly93
+d3cuZW50cnVzdC5uZXQvcnBhMB0GA1UdDgQWBBQe8auJBvhJDwEzd+4Ueu4ZfJMo
+TTAfBgNVHSMEGDAWgBRV5IHREYC+2Im5CKMx+aEkCRa5cDANBgkqhkiG9w0BAQUF
+AAOCAQEAB/ZfgoR/gEDHkDRGQiQDzi+ruoOeJXMN7awFacaH7aNc8lfBsUl2mk3y
+P93kDv4LPrmY2TKVHTL0Ae6cyMjlP+BTdmL83attPZSQ8sCzPJgnNl4olyL8G0DT
+Kw2ttVdt3w/jS+9zAhBl+hvQrDHV4w/oujIwg+5K0L/fIpB6vuw6G8RJBB3xroB3
+PEII26c7KKaAAQPmOaPr34BZG/MsvtxyRHmgbAelbU1EjkJoypR8Lja6hZ7NqsRe
+PFS+/i/qaZ0cHimbltjI/lGQ8SSmkAaz8Cmi/3gud1xFIdlEADHzvjJP9QoyDfz8
+uhZ2VrLWSJLyi6Y+t6xcaeoLP2ZFuQ==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert65[] = {
+ 0x30, 0x82, 0x04, 0xf2, 0x30, 0x82, 0x03, 0xda, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x38, 0x63, 0xe9, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31,
+ 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77,
+ 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65,
+ 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c,
+ 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39,
+ 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33, 0x30, 0x31,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38,
+ 0x29, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x31, 0x32, 0x31, 0x30, 0x32,
+ 0x30, 0x34, 0x33, 0x35, 0x34, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32,
+ 0x31, 0x30, 0x32, 0x31, 0x31, 0x33, 0x35, 0x34, 0x5a, 0x30, 0x81, 0xb1,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x20, 0x69, 0x73, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2e, 0x30,
+ 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x4c, 0x31, 0x43, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde,
+ 0x05, 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, 0x7f, 0xc7, 0x4b,
+ 0x7e, 0x5a, 0x9f, 0xb3, 0xff, 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba,
+ 0x10, 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, 0x28, 0xce, 0xc0,
+ 0xe6, 0x0e, 0x06, 0x91, 0x50, 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb,
+ 0xd8, 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, 0x96, 0xdc, 0xbc,
+ 0xaa, 0xfa, 0x52, 0x77, 0x04, 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd,
+ 0x3c, 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, 0xf9, 0xc1, 0xb1,
+ 0x3f, 0x72, 0x86, 0xf2, 0x38, 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28,
+ 0xda, 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, 0xc1, 0x65, 0x77,
+ 0x76, 0x24, 0x4c, 0x98, 0xf7, 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb,
+ 0x37, 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, 0x20, 0x09, 0x49,
+ 0x36, 0x24, 0x69, 0x42, 0xf6, 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9,
+ 0x3c, 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, 0xd7, 0xf7, 0x0a,
+ 0x6f, 0xef, 0x2e, 0xd8, 0xd5, 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68,
+ 0xe2, 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, 0x51, 0x43, 0x9d,
+ 0xe0, 0xb2, 0xc4, 0x67, 0xb4, 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f,
+ 0x4b, 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, 0x6c, 0x78, 0x90,
+ 0x95, 0xde, 0xca, 0x3a, 0x48, 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75,
+ 0x05, 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, 0xe4, 0x1a, 0x15,
+ 0xdc, 0x87, 0x23, 0xaa, 0xb2, 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8,
+ 0x3d, 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, 0x55, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0b, 0x30, 0x82, 0x01, 0x07, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x33, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25,
+ 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29,
+ 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x32, 0x30, 0x34, 0x38, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x1e, 0xf1, 0xab, 0x89, 0x06, 0xf8, 0x49,
+ 0x0f, 0x01, 0x33, 0x77, 0xee, 0x14, 0x7a, 0xee, 0x19, 0x7c, 0x93, 0x28,
+ 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x55, 0xe4, 0x81, 0xd1, 0x11, 0x80, 0xbe, 0xd8, 0x89, 0xb9,
+ 0x08, 0xa3, 0x31, 0xf9, 0xa1, 0x24, 0x09, 0x16, 0xb9, 0x70, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x07, 0xf6, 0x5f, 0x82, 0x84, 0x7f,
+ 0x80, 0x40, 0xc7, 0x90, 0x34, 0x46, 0x42, 0x24, 0x03, 0xce, 0x2f, 0xab,
+ 0xba, 0x83, 0x9e, 0x25, 0x73, 0x0d, 0xed, 0xac, 0x05, 0x69, 0xc6, 0x87,
+ 0xed, 0xa3, 0x5c, 0xf2, 0x57, 0xc1, 0xb1, 0x49, 0x76, 0x9a, 0x4d, 0xf2,
+ 0x3f, 0xdd, 0xe4, 0x0e, 0xfe, 0x0b, 0x3e, 0xb9, 0x98, 0xd9, 0x32, 0x95,
+ 0x1d, 0x32, 0xf4, 0x01, 0xee, 0x9c, 0xc8, 0xc8, 0xe5, 0x3f, 0xe0, 0x53,
+ 0x76, 0x62, 0xfc, 0xdd, 0xab, 0x6d, 0x3d, 0x94, 0x90, 0xf2, 0xc0, 0xb3,
+ 0x3c, 0x98, 0x27, 0x36, 0x5e, 0x28, 0x97, 0x22, 0xfc, 0x1b, 0x40, 0xd3,
+ 0x2b, 0x0d, 0xad, 0xb5, 0x57, 0x6d, 0xdf, 0x0f, 0xe3, 0x4b, 0xef, 0x73,
+ 0x02, 0x10, 0x65, 0xfa, 0x1b, 0xd0, 0xac, 0x31, 0xd5, 0xe3, 0x0f, 0xe8,
+ 0xba, 0x32, 0x30, 0x83, 0xee, 0x4a, 0xd0, 0xbf, 0xdf, 0x22, 0x90, 0x7a,
+ 0xbe, 0xec, 0x3a, 0x1b, 0xc4, 0x49, 0x04, 0x1d, 0xf1, 0xae, 0x80, 0x77,
+ 0x3c, 0x42, 0x08, 0xdb, 0xa7, 0x3b, 0x28, 0xa6, 0x80, 0x01, 0x03, 0xe6,
+ 0x39, 0xa3, 0xeb, 0xdf, 0x80, 0x59, 0x1b, 0xf3, 0x2c, 0xbe, 0xdc, 0x72,
+ 0x44, 0x79, 0xa0, 0x6c, 0x07, 0xa5, 0x6d, 0x4d, 0x44, 0x8e, 0x42, 0x68,
+ 0xca, 0x94, 0x7c, 0x2e, 0x36, 0xba, 0x85, 0x9e, 0xcd, 0xaa, 0xc4, 0x5e,
+ 0x3c, 0x54, 0xbe, 0xfe, 0x2f, 0xea, 0x69, 0x9d, 0x1c, 0x1e, 0x29, 0x9b,
+ 0x96, 0xd8, 0xc8, 0xfe, 0x51, 0x90, 0xf1, 0x24, 0xa6, 0x90, 0x06, 0xb3,
+ 0xf0, 0x29, 0xa2, 0xff, 0x78, 0x2e, 0x77, 0x5c, 0x45, 0x21, 0xd9, 0x44,
+ 0x00, 0x31, 0xf3, 0xbe, 0x32, 0x4f, 0xf5, 0x0a, 0x32, 0x0d, 0xfc, 0xfc,
+ 0xba, 0x16, 0x76, 0x56, 0xb2, 0xd6, 0x48, 0x92, 0xf2, 0x8b, 0xa6, 0x3e,
+ 0xb7, 0xac, 0x5c, 0x69, 0xea, 0x0b, 0x3f, 0x66, 0x45, 0xb9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1276021817 (0x4c0e8c39)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048)
+ Validity
+ Not Before: Nov 11 15:40:40 2011 GMT
+ Not After : Nov 12 02:51:17 2021 GMT
+ Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/rpa is incorporated by reference, OU=(c) 2009 Entrust, Inc., CN=Entrust Certification Authority - L1C
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:97:a3:2d:3c:9e:de:05:da:13:c2:11:8d:9d:8e:
+ e3:7f:c7:4b:7e:5a:9f:b3:ff:62:ab:73:c8:28:6b:
+ ba:10:64:82:87:13:cd:57:18:ff:28:ce:c0:e6:0e:
+ 06:91:50:29:83:d1:f2:c3:2a:db:d8:db:4e:04:cc:
+ 00:eb:8b:b6:96:dc:bc:aa:fa:52:77:04:c1:db:19:
+ e4:ae:9c:fd:3c:8b:03:ef:4d:bc:1a:03:65:f9:c1:
+ b1:3f:72:86:f2:38:aa:19:ae:10:88:78:28:da:75:
+ c3:3d:02:82:02:9c:b9:c1:65:77:76:24:4c:98:f7:
+ 6d:31:38:fb:db:fe:db:37:02:76:a1:18:97:a6:cc:
+ de:20:09:49:36:24:69:42:f6:e4:37:62:f1:59:6d:
+ a9:3c:ed:34:9c:a3:8e:db:dc:3a:d7:f7:0a:6f:ef:
+ 2e:d8:d5:93:5a:7a:ed:08:49:68:e2:41:e3:5a:90:
+ c1:86:55:fc:51:43:9d:e0:b2:c4:67:b4:cb:32:31:
+ 25:f0:54:9f:4b:d1:6f:db:d4:dd:fc:af:5e:6c:78:
+ 90:95:de:ca:3a:48:b9:79:3c:9b:19:d6:75:05:a0:
+ f9:88:d7:c1:e8:a5:09:e4:1a:15:dc:87:23:aa:b2:
+ 75:8c:63:25:87:d8:f8:3d:a6:c2:cc:66:ff:a5:66:
+ 68:55
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/2048ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/rpa
+
+ X509v3 Subject Key Identifier:
+ 1E:F1:AB:89:06:F8:49:0F:01:33:77:EE:14:7A:EE:19:7C:93:28:4D
+ X509v3 Authority Key Identifier:
+ keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 40:9a:87:7e:88:d4:cc:26:a7:4b:fa:78:4a:20:d5:f9:a2:36:
+ 21:bb:ee:5b:a0:4f:44:8d:cf:aa:f9:97:17:96:84:a9:c8:67:
+ 9b:bb:e6:10:de:79:d6:56:6a:a4:78:14:49:d9:7c:ed:30:5e:
+ 69:ea:6d:24:46:5a:88:34:3d:26:27:cf:69:41:84:1c:04:da:
+ 19:38:2e:db:89:41:39:7e:65:1f:9d:5a:3a:cc:e1:0c:4c:37:
+ a1:ce:60:93:a8:b5:8c:ca:3f:ba:2b:5d:4c:1b:81:89:7a:ca:
+ 36:30:9c:ff:84:e3:fe:3a:f1:f7:79:71:c9:b5:d3:33:03:ca:
+ 77:ce:b0:ba:29:d2:34:5d:73:ff:a4:fd:f2:25:b8:35:45:79:
+ 7a:1f:97:ae:c9:be:0a:68:84:99:74:39:a8:4e:7a:26:f5:cd:
+ de:25:e2:37:85:65:07:a7:ca:c5:05:b7:13:38:0d:2d:f0:6d:
+ 19:ce:de:99:61:27:ee:45:6e:c7:39:ff:f6:c5:8b:e0:cb:7c:
+ 8a:1e:d5:7a:07:31:2a:52:5c:3a:50:19:38:a9:44:fa:3c:a8:
+ cf:ef:79:9d:6a:d9:e5:2e:a1:8f:29:28:d7:ec:aa:c1:fb:26:
+ e6:9f:46:24:a6:b1:07:cd:b9:0c:e8:0d:82:16:00:1d:96:92:
+ fc:a6:08:a0
+-----BEGIN CERTIFICATE-----
+MIIE9TCCA92gAwIBAgIETA6MOTANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0xMTExMTExNTQwNDBaFw0yMTEx
+MTIwMjUxMTdaMIGxMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5j
+LjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L3JwYSBpcyBpbmNvcnBvcmF0ZWQg
+YnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwOSBFbnRydXN0LCBJbmMuMS4w
+LAYDVQQDEyVFbnRydXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gTDFDMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl6MtPJ7eBdoTwhGNnY7jf8dL
+flqfs/9iq3PIKGu6EGSChxPNVxj/KM7A5g4GkVApg9Hywyrb2NtOBMwA64u2lty8
+qvpSdwTB2xnkrpz9PIsD7028GgNl+cGxP3KG8jiqGa4QiHgo2nXDPQKCApy5wWV3
+diRMmPdtMTj72/7bNwJ2oRiXpszeIAlJNiRpQvbkN2LxWW2pPO00nKOO29w61/cK
+b+8u2NWTWnrtCElo4kHjWpDBhlX8UUOd4LLEZ7TLMjEl8FSfS9Fv29Td/K9ebHiQ
+ld7KOki5eTybGdZ1BaD5iNfB6KUJ5BoV3IcjqrJ1jGMlh9j4PabCzGb/pWZoVQID
+AQABo4IBDjCCAQowDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
+MwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0
+Lm5ldDAyBgNVHR8EKzApMCegJaAjhiFodHRwOi8vY3JsLmVudHJ1c3QubmV0LzIw
+NDhjYS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6
+Ly93d3cuZW50cnVzdC5uZXQvcnBhMB0GA1UdDgQWBBQe8auJBvhJDwEzd+4Ueu4Z
+fJMoTTAfBgNVHSMEGDAWgBRV5IHREYC+2Im5CKMx+aEkCRa5cDANBgkqhkiG9w0B
+AQUFAAOCAQEAQJqHfojUzCanS/p4SiDV+aI2IbvuW6BPRI3PqvmXF5aEqchnm7vm
+EN551lZqpHgUSdl87TBeaeptJEZaiDQ9JifPaUGEHATaGTgu24lBOX5lH51aOszh
+DEw3oc5gk6i1jMo/uitdTBuBiXrKNjCc/4Tj/jrx93lxybXTMwPKd86wuinSNF1z
+/6T98iW4NUV5eh+Xrsm+CmiEmXQ5qE56JvXN3iXiN4VlB6fKxQW3EzgNLfBtGc7e
+mWEn7kVuxzn/9sWL4Mt8ih7VegcxKlJcOlAZOKlE+jyoz+95nWrZ5S6hjyko1+yq
+wfsm5p9GJKaxB825DOgNghYAHZaS/KYIoA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert66[] = {
+ 0x30, 0x82, 0x04, 0xf5, 0x30, 0x82, 0x03, 0xdd, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x4c, 0x0e, 0x8c, 0x39, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31,
+ 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77,
+ 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65,
+ 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c,
+ 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39,
+ 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33, 0x30, 0x31,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38,
+ 0x29, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x35, 0x34, 0x30, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31,
+ 0x31, 0x32, 0x30, 0x32, 0x35, 0x31, 0x31, 0x37, 0x5a, 0x30, 0x81, 0xb1,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30,
+ 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x20, 0x69, 0x73, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20,
+ 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2e, 0x30,
+ 0x2c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x4c, 0x31, 0x43, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x97, 0xa3, 0x2d, 0x3c, 0x9e, 0xde,
+ 0x05, 0xda, 0x13, 0xc2, 0x11, 0x8d, 0x9d, 0x8e, 0xe3, 0x7f, 0xc7, 0x4b,
+ 0x7e, 0x5a, 0x9f, 0xb3, 0xff, 0x62, 0xab, 0x73, 0xc8, 0x28, 0x6b, 0xba,
+ 0x10, 0x64, 0x82, 0x87, 0x13, 0xcd, 0x57, 0x18, 0xff, 0x28, 0xce, 0xc0,
+ 0xe6, 0x0e, 0x06, 0x91, 0x50, 0x29, 0x83, 0xd1, 0xf2, 0xc3, 0x2a, 0xdb,
+ 0xd8, 0xdb, 0x4e, 0x04, 0xcc, 0x00, 0xeb, 0x8b, 0xb6, 0x96, 0xdc, 0xbc,
+ 0xaa, 0xfa, 0x52, 0x77, 0x04, 0xc1, 0xdb, 0x19, 0xe4, 0xae, 0x9c, 0xfd,
+ 0x3c, 0x8b, 0x03, 0xef, 0x4d, 0xbc, 0x1a, 0x03, 0x65, 0xf9, 0xc1, 0xb1,
+ 0x3f, 0x72, 0x86, 0xf2, 0x38, 0xaa, 0x19, 0xae, 0x10, 0x88, 0x78, 0x28,
+ 0xda, 0x75, 0xc3, 0x3d, 0x02, 0x82, 0x02, 0x9c, 0xb9, 0xc1, 0x65, 0x77,
+ 0x76, 0x24, 0x4c, 0x98, 0xf7, 0x6d, 0x31, 0x38, 0xfb, 0xdb, 0xfe, 0xdb,
+ 0x37, 0x02, 0x76, 0xa1, 0x18, 0x97, 0xa6, 0xcc, 0xde, 0x20, 0x09, 0x49,
+ 0x36, 0x24, 0x69, 0x42, 0xf6, 0xe4, 0x37, 0x62, 0xf1, 0x59, 0x6d, 0xa9,
+ 0x3c, 0xed, 0x34, 0x9c, 0xa3, 0x8e, 0xdb, 0xdc, 0x3a, 0xd7, 0xf7, 0x0a,
+ 0x6f, 0xef, 0x2e, 0xd8, 0xd5, 0x93, 0x5a, 0x7a, 0xed, 0x08, 0x49, 0x68,
+ 0xe2, 0x41, 0xe3, 0x5a, 0x90, 0xc1, 0x86, 0x55, 0xfc, 0x51, 0x43, 0x9d,
+ 0xe0, 0xb2, 0xc4, 0x67, 0xb4, 0xcb, 0x32, 0x31, 0x25, 0xf0, 0x54, 0x9f,
+ 0x4b, 0xd1, 0x6f, 0xdb, 0xd4, 0xdd, 0xfc, 0xaf, 0x5e, 0x6c, 0x78, 0x90,
+ 0x95, 0xde, 0xca, 0x3a, 0x48, 0xb9, 0x79, 0x3c, 0x9b, 0x19, 0xd6, 0x75,
+ 0x05, 0xa0, 0xf9, 0x88, 0xd7, 0xc1, 0xe8, 0xa5, 0x09, 0xe4, 0x1a, 0x15,
+ 0xdc, 0x87, 0x23, 0xaa, 0xb2, 0x75, 0x8c, 0x63, 0x25, 0x87, 0xd8, 0xf8,
+ 0x3d, 0xa6, 0xc2, 0xcc, 0x66, 0xff, 0xa5, 0x66, 0x68, 0x55, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x0e, 0x30, 0x82, 0x01, 0x0a, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x2b, 0x30, 0x29, 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x32, 0x30,
+ 0x34, 0x38, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x1e, 0xf1, 0xab, 0x89,
+ 0x06, 0xf8, 0x49, 0x0f, 0x01, 0x33, 0x77, 0xee, 0x14, 0x7a, 0xee, 0x19,
+ 0x7c, 0x93, 0x28, 0x4d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x55, 0xe4, 0x81, 0xd1, 0x11, 0x80, 0xbe,
+ 0xd8, 0x89, 0xb9, 0x08, 0xa3, 0x31, 0xf9, 0xa1, 0x24, 0x09, 0x16, 0xb9,
+ 0x70, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x40, 0x9a, 0x87,
+ 0x7e, 0x88, 0xd4, 0xcc, 0x26, 0xa7, 0x4b, 0xfa, 0x78, 0x4a, 0x20, 0xd5,
+ 0xf9, 0xa2, 0x36, 0x21, 0xbb, 0xee, 0x5b, 0xa0, 0x4f, 0x44, 0x8d, 0xcf,
+ 0xaa, 0xf9, 0x97, 0x17, 0x96, 0x84, 0xa9, 0xc8, 0x67, 0x9b, 0xbb, 0xe6,
+ 0x10, 0xde, 0x79, 0xd6, 0x56, 0x6a, 0xa4, 0x78, 0x14, 0x49, 0xd9, 0x7c,
+ 0xed, 0x30, 0x5e, 0x69, 0xea, 0x6d, 0x24, 0x46, 0x5a, 0x88, 0x34, 0x3d,
+ 0x26, 0x27, 0xcf, 0x69, 0x41, 0x84, 0x1c, 0x04, 0xda, 0x19, 0x38, 0x2e,
+ 0xdb, 0x89, 0x41, 0x39, 0x7e, 0x65, 0x1f, 0x9d, 0x5a, 0x3a, 0xcc, 0xe1,
+ 0x0c, 0x4c, 0x37, 0xa1, 0xce, 0x60, 0x93, 0xa8, 0xb5, 0x8c, 0xca, 0x3f,
+ 0xba, 0x2b, 0x5d, 0x4c, 0x1b, 0x81, 0x89, 0x7a, 0xca, 0x36, 0x30, 0x9c,
+ 0xff, 0x84, 0xe3, 0xfe, 0x3a, 0xf1, 0xf7, 0x79, 0x71, 0xc9, 0xb5, 0xd3,
+ 0x33, 0x03, 0xca, 0x77, 0xce, 0xb0, 0xba, 0x29, 0xd2, 0x34, 0x5d, 0x73,
+ 0xff, 0xa4, 0xfd, 0xf2, 0x25, 0xb8, 0x35, 0x45, 0x79, 0x7a, 0x1f, 0x97,
+ 0xae, 0xc9, 0xbe, 0x0a, 0x68, 0x84, 0x99, 0x74, 0x39, 0xa8, 0x4e, 0x7a,
+ 0x26, 0xf5, 0xcd, 0xde, 0x25, 0xe2, 0x37, 0x85, 0x65, 0x07, 0xa7, 0xca,
+ 0xc5, 0x05, 0xb7, 0x13, 0x38, 0x0d, 0x2d, 0xf0, 0x6d, 0x19, 0xce, 0xde,
+ 0x99, 0x61, 0x27, 0xee, 0x45, 0x6e, 0xc7, 0x39, 0xff, 0xf6, 0xc5, 0x8b,
+ 0xe0, 0xcb, 0x7c, 0x8a, 0x1e, 0xd5, 0x7a, 0x07, 0x31, 0x2a, 0x52, 0x5c,
+ 0x3a, 0x50, 0x19, 0x38, 0xa9, 0x44, 0xfa, 0x3c, 0xa8, 0xcf, 0xef, 0x79,
+ 0x9d, 0x6a, 0xd9, 0xe5, 0x2e, 0xa1, 0x8f, 0x29, 0x28, 0xd7, 0xec, 0xaa,
+ 0xc1, 0xfb, 0x26, 0xe6, 0x9f, 0x46, 0x24, 0xa6, 0xb1, 0x07, 0xcd, 0xb9,
+ 0x0c, 0xe8, 0x0d, 0x82, 0x16, 0x00, 0x1d, 0x96, 0x92, 0xfc, 0xa6, 0x08,
+ 0xa0,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 40:89:95:44:7e:5f:b1:19:d8:65:73:70:2f:8d:64:fc
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Validity
+ Not Before: Dec 1 00:00:00 2006 GMT
+ Not After : Dec 31 23:59:59 2019 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=EssentialSSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ad:f0:08:b0:72:c6:ab:83:12:31:17:70:89:85:
+ a9:20:12:d4:98:6a:ed:80:d4:d1:df:e4:8e:59:2d:
+ d3:96:21:8d:76:d2:3f:18:0b:46:19:63:0b:c7:20:
+ f3:e5:0b:dd:80:1a:f1:5a:a0:bd:1d:76:cd:b7:23:
+ 3a:74:5e:61:1b:75:aa:9b:d4:85:f4:e1:78:91:d3:
+ 2d:e1:af:fc:98:2e:06:d2:79:3d:5a:c0:1f:21:2d:
+ 1c:ae:21:53:c6:3a:a7:21:7e:be:ed:67:6f:75:1d:
+ 1a:9f:6a:5b:06:b3:6a:e3:b1:0b:aa:6a:0e:e7:6d:
+ 6c:c3:ca:95:8c:37:ce:21:1f:35:90:7d:db:da:1a:
+ 5c:a8:88:14:b2:0f:c8:12:20:5f:c5:d3:7f:e8:e1:
+ 38:e0:db:bc:f9:1f:a1:aa:d6:1b:90:07:21:fa:45:
+ 24:50:5d:27:2a:a0:28:41:45:5b:7d:bc:a0:a2:2f:
+ aa:9b:7e:5b:53:c5:f1:05:16:57:7e:11:d7:3b:b4:
+ d9:01:76:dc:df:7d:10:cf:51:a9:e5:38:f2:7b:14:
+ 00:75:59:f9:f0:59:db:17:3e:f7:af:e6:02:2d:a4:
+ 79:c1:5d:a2:1c:c3:9a:c8:a7:a8:0b:48:0a:6a:2e:
+ 7f:2d:97:65:f6:c5:04:9c:44:c8:99:96:7e:7e:a4:
+ dd:2f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+
+ X509v3 Subject Key Identifier:
+ DA:CB:EA:AD:5B:08:5D:CC:FF:FC:26:54:CE:49:E5:55:C6:38:F4:F8
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://secure.comodo.net/CPS
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/COMODOCertificationAuthority.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.comodoca.com/ComodoUTNServerCA.crt
+ CA Issuers - URI:http://crt.comodo.net/ComodoUTNServerCA.crt
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 6d:c3:6b:56:47:47:6b:3d:36:c2:db:51:ca:75:d0:5a:aa:62:
+ 40:42:07:61:d8:fe:6b:2c:d8:03:e1:54:f6:9e:5c:16:e3:17:
+ 37:ec:a0:f6:2b:7a:5f:2e:c3:50:01:d7:33:0e:0a:3e:ad:b3:
+ 33:c7:4f:9e:45:26:c8:f1:ee:bd:64:62:ff:88:1f:c4:59:f7:
+ 92:15:c8:e7:f7:38:ab:1f:00:ec:4c:f1:27:aa:01:0d:34:c7:
+ 04:5a:b4:79:b2:9c:e4:31:61:ef:9a:12:33:69:d4:e0:30:6e:
+ 1c:67:5d:f9:68:d6:31:37:6a:49:c5:0b:75:99:54:65:1e:5f:
+ 2d:99:cb:a7:41:a4:fa:d2:b5:f0:d4:1e:48:ec:90:3f:d3:7d:
+ b1:ff:23:96:6b:23:35:b0:ed:9e:5f:3d:31:74:48:80:7d:90:
+ 56:6d:10:fe:63:7c:ee:9a:d3:fd:9f:5f:21:09:0d:5e:cc:b3:
+ 8d:5a:8f:d8:a0:41:35:a3:86:73:05:ae:d9:19:7a:3a:cb:20:
+ af:51:91:a3:cc:46:4d:47:50:c6:fb:dc:15:2c:54:71:bf:fe:
+ 57:fb:89:ac:ff:d0:bb:8f:66:3e:ef:e4:21:af:80:47:ff:86:
+ db:39:11:c8:e6:50:cd:45:6d:59:96:ca:55:76:6d:b5:8e:b0:
+ de:09:68:00
+-----BEGIN CERTIFICATE-----
+MIIE+DCCA+CgAwIBAgIQQImVRH5fsRnYZXNwL41k/DANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0xOTEyMzEyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh
+dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
+TyBDQSBMaW1pdGVkMRgwFgYDVQQDEw9Fc3NlbnRpYWxTU0wgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt8AiwcsargxIxF3CJhakgEtSYau2A1NHf
+5I5ZLdOWIY120j8YC0YZYwvHIPPlC92AGvFaoL0dds23Izp0XmEbdaqb1IX04XiR
+0y3hr/yYLgbSeT1awB8hLRyuIVPGOqchfr7tZ291HRqfalsGs2rjsQuqag7nbWzD
+ypWMN84hHzWQfdvaGlyoiBSyD8gSIF/F03/o4Tjg27z5H6Gq1huQByH6RSRQXScq
+oChBRVt9vKCiL6qbfltTxfEFFld+Edc7tNkBdtzffRDPUanlOPJ7FAB1WfnwWdsX
+Pvev5gItpHnBXaIcw5rIp6gLSApqLn8tl2X2xQScRMiZln5+pN0vAgMBAAGjggF4
+MIIBdDAfBgNVHSMEGDAWgBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQU
+2svqrVsIXcz//CZUzknlVcY49PgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwPgYDVR0gBDcwNTAzBgRVHSAAMCswKQYIKwYBBQUHAgEWHWh0dHBz
+Oi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6
+Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NlcnRpZmljYXRpb25BdXRob3JpdHku
+Y3JsMIGCBggrBgEFBQcBAQR2MHQwOQYIKwYBBQUHMAKGLWh0dHA6Ly9jcnQuY29t
+b2RvY2EuY29tL0NvbW9kb1VUTlNlcnZlckNBLmNydDA3BggrBgEFBQcwAoYraHR0
+cDovL2NydC5jb21vZG8ubmV0L0NvbW9kb1VUTlNlcnZlckNBLmNydDANBgkqhkiG
+9w0BAQUFAAOCAQEAbcNrVkdHaz02wttRynXQWqpiQEIHYdj+ayzYA+FU9p5cFuMX
+N+yg9it6Xy7DUAHXMw4KPq2zM8dPnkUmyPHuvWRi/4gfxFn3khXI5/c4qx8A7Ezx
+J6oBDTTHBFq0ebKc5DFh75oSM2nU4DBuHGdd+WjWMTdqScULdZlUZR5fLZnLp0Gk
++tK18NQeSOyQP9N9sf8jlmsjNbDtnl89MXRIgH2QVm0Q/mN87prT/Z9fIQkNXsyz
+jVqP2KBBNaOGcwWu2Rl6Ossgr1GRo8xGTUdQxvvcFSxUcb/+V/uJrP/Qu49mPu/k
+Ia+AR/+G2zkRyOZQzUVtWZbKVXZttY6w3gloAA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert67[] = {
+ 0x30, 0x82, 0x04, 0xf8, 0x30, 0x82, 0x03, 0xe0, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x40, 0x89, 0x95, 0x44, 0x7e, 0x5f, 0xb1, 0x19, 0xd8,
+ 0x65, 0x73, 0x70, 0x2f, 0x8d, 0x64, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x36, 0x31, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32,
+ 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x72, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30,
+ 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x47, 0x72, 0x65, 0x61,
+ 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74,
+ 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+ 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44,
+ 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, 0x45,
+ 0x73, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x53, 0x4c, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xad,
+ 0xf0, 0x08, 0xb0, 0x72, 0xc6, 0xab, 0x83, 0x12, 0x31, 0x17, 0x70, 0x89,
+ 0x85, 0xa9, 0x20, 0x12, 0xd4, 0x98, 0x6a, 0xed, 0x80, 0xd4, 0xd1, 0xdf,
+ 0xe4, 0x8e, 0x59, 0x2d, 0xd3, 0x96, 0x21, 0x8d, 0x76, 0xd2, 0x3f, 0x18,
+ 0x0b, 0x46, 0x19, 0x63, 0x0b, 0xc7, 0x20, 0xf3, 0xe5, 0x0b, 0xdd, 0x80,
+ 0x1a, 0xf1, 0x5a, 0xa0, 0xbd, 0x1d, 0x76, 0xcd, 0xb7, 0x23, 0x3a, 0x74,
+ 0x5e, 0x61, 0x1b, 0x75, 0xaa, 0x9b, 0xd4, 0x85, 0xf4, 0xe1, 0x78, 0x91,
+ 0xd3, 0x2d, 0xe1, 0xaf, 0xfc, 0x98, 0x2e, 0x06, 0xd2, 0x79, 0x3d, 0x5a,
+ 0xc0, 0x1f, 0x21, 0x2d, 0x1c, 0xae, 0x21, 0x53, 0xc6, 0x3a, 0xa7, 0x21,
+ 0x7e, 0xbe, 0xed, 0x67, 0x6f, 0x75, 0x1d, 0x1a, 0x9f, 0x6a, 0x5b, 0x06,
+ 0xb3, 0x6a, 0xe3, 0xb1, 0x0b, 0xaa, 0x6a, 0x0e, 0xe7, 0x6d, 0x6c, 0xc3,
+ 0xca, 0x95, 0x8c, 0x37, 0xce, 0x21, 0x1f, 0x35, 0x90, 0x7d, 0xdb, 0xda,
+ 0x1a, 0x5c, 0xa8, 0x88, 0x14, 0xb2, 0x0f, 0xc8, 0x12, 0x20, 0x5f, 0xc5,
+ 0xd3, 0x7f, 0xe8, 0xe1, 0x38, 0xe0, 0xdb, 0xbc, 0xf9, 0x1f, 0xa1, 0xaa,
+ 0xd6, 0x1b, 0x90, 0x07, 0x21, 0xfa, 0x45, 0x24, 0x50, 0x5d, 0x27, 0x2a,
+ 0xa0, 0x28, 0x41, 0x45, 0x5b, 0x7d, 0xbc, 0xa0, 0xa2, 0x2f, 0xaa, 0x9b,
+ 0x7e, 0x5b, 0x53, 0xc5, 0xf1, 0x05, 0x16, 0x57, 0x7e, 0x11, 0xd7, 0x3b,
+ 0xb4, 0xd9, 0x01, 0x76, 0xdc, 0xdf, 0x7d, 0x10, 0xcf, 0x51, 0xa9, 0xe5,
+ 0x38, 0xf2, 0x7b, 0x14, 0x00, 0x75, 0x59, 0xf9, 0xf0, 0x59, 0xdb, 0x17,
+ 0x3e, 0xf7, 0xaf, 0xe6, 0x02, 0x2d, 0xa4, 0x79, 0xc1, 0x5d, 0xa2, 0x1c,
+ 0xc3, 0x9a, 0xc8, 0xa7, 0xa8, 0x0b, 0x48, 0x0a, 0x6a, 0x2e, 0x7f, 0x2d,
+ 0x97, 0x65, 0xf6, 0xc5, 0x04, 0x9c, 0x44, 0xc8, 0x99, 0x96, 0x7e, 0x7e,
+ 0xa4, 0xdd, 0x2f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x78,
+ 0x30, 0x82, 0x01, 0x74, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x0b, 0x58, 0xe5, 0x8b, 0xc6, 0x4c, 0x15,
+ 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21, 0xbe, 0x47, 0x36, 0x5a, 0x56,
+ 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xda, 0xcb, 0xea, 0xad, 0x5b, 0x08, 0x5d, 0xcc, 0xff, 0xfc, 0x26, 0x54,
+ 0xce, 0x49, 0xe5, 0x55, 0xc6, 0x38, 0xf4, 0xf8, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3e, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x37, 0x30, 0x35, 0x30, 0x33, 0x06, 0x04, 0x55,
+ 0x1d, 0x20, 0x00, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x6f, 0x64, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53,
+ 0x30, 0x49, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x42, 0x30, 0x40, 0x30,
+ 0x3e, 0xa0, 0x3c, 0xa0, 0x3a, 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f,
+ 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x4f, 0x4d, 0x4f, 0x44,
+ 0x4f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x81, 0x82, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x76, 0x30, 0x74, 0x30, 0x39, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f,
+ 0x6d, 0x6f, 0x64, 0x6f, 0x55, 0x54, 0x4e, 0x53, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x37, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2b, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f,
+ 0x64, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64,
+ 0x6f, 0x55, 0x54, 0x4e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+ 0x6d, 0xc3, 0x6b, 0x56, 0x47, 0x47, 0x6b, 0x3d, 0x36, 0xc2, 0xdb, 0x51,
+ 0xca, 0x75, 0xd0, 0x5a, 0xaa, 0x62, 0x40, 0x42, 0x07, 0x61, 0xd8, 0xfe,
+ 0x6b, 0x2c, 0xd8, 0x03, 0xe1, 0x54, 0xf6, 0x9e, 0x5c, 0x16, 0xe3, 0x17,
+ 0x37, 0xec, 0xa0, 0xf6, 0x2b, 0x7a, 0x5f, 0x2e, 0xc3, 0x50, 0x01, 0xd7,
+ 0x33, 0x0e, 0x0a, 0x3e, 0xad, 0xb3, 0x33, 0xc7, 0x4f, 0x9e, 0x45, 0x26,
+ 0xc8, 0xf1, 0xee, 0xbd, 0x64, 0x62, 0xff, 0x88, 0x1f, 0xc4, 0x59, 0xf7,
+ 0x92, 0x15, 0xc8, 0xe7, 0xf7, 0x38, 0xab, 0x1f, 0x00, 0xec, 0x4c, 0xf1,
+ 0x27, 0xaa, 0x01, 0x0d, 0x34, 0xc7, 0x04, 0x5a, 0xb4, 0x79, 0xb2, 0x9c,
+ 0xe4, 0x31, 0x61, 0xef, 0x9a, 0x12, 0x33, 0x69, 0xd4, 0xe0, 0x30, 0x6e,
+ 0x1c, 0x67, 0x5d, 0xf9, 0x68, 0xd6, 0x31, 0x37, 0x6a, 0x49, 0xc5, 0x0b,
+ 0x75, 0x99, 0x54, 0x65, 0x1e, 0x5f, 0x2d, 0x99, 0xcb, 0xa7, 0x41, 0xa4,
+ 0xfa, 0xd2, 0xb5, 0xf0, 0xd4, 0x1e, 0x48, 0xec, 0x90, 0x3f, 0xd3, 0x7d,
+ 0xb1, 0xff, 0x23, 0x96, 0x6b, 0x23, 0x35, 0xb0, 0xed, 0x9e, 0x5f, 0x3d,
+ 0x31, 0x74, 0x48, 0x80, 0x7d, 0x90, 0x56, 0x6d, 0x10, 0xfe, 0x63, 0x7c,
+ 0xee, 0x9a, 0xd3, 0xfd, 0x9f, 0x5f, 0x21, 0x09, 0x0d, 0x5e, 0xcc, 0xb3,
+ 0x8d, 0x5a, 0x8f, 0xd8, 0xa0, 0x41, 0x35, 0xa3, 0x86, 0x73, 0x05, 0xae,
+ 0xd9, 0x19, 0x7a, 0x3a, 0xcb, 0x20, 0xaf, 0x51, 0x91, 0xa3, 0xcc, 0x46,
+ 0x4d, 0x47, 0x50, 0xc6, 0xfb, 0xdc, 0x15, 0x2c, 0x54, 0x71, 0xbf, 0xfe,
+ 0x57, 0xfb, 0x89, 0xac, 0xff, 0xd0, 0xbb, 0x8f, 0x66, 0x3e, 0xef, 0xe4,
+ 0x21, 0xaf, 0x80, 0x47, 0xff, 0x86, 0xdb, 0x39, 0x11, 0xc8, 0xe6, 0x50,
+ 0xcd, 0x45, 0x6d, 0x59, 0x96, 0xca, 0x55, 0x76, 0x6d, 0xb5, 0x8e, 0xb0,
+ 0xde, 0x09, 0x68, 0x00,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 269 (0x10d)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com
+ Validity
+ Not Before: Jun 29 17:06:20 2004 GMT
+ Not After : Jun 29 17:06:20 2024 GMT
+ Subject: C=US, O=The Go Daddy Group, Inc., OU=Go Daddy Class 2 Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:de:9d:d7:ea:57:18:49:a1:5b:eb:d7:5f:48:86:
+ ea:be:dd:ff:e4:ef:67:1c:f4:65:68:b3:57:71:a0:
+ 5e:77:bb:ed:9b:49:e9:70:80:3d:56:18:63:08:6f:
+ da:f2:cc:d0:3f:7f:02:54:22:54:10:d8:b2:81:d4:
+ c0:75:3d:4b:7f:c7:77:c3:3e:78:ab:1a:03:b5:20:
+ 6b:2f:6a:2b:b1:c5:88:7e:c4:bb:1e:b0:c1:d8:45:
+ 27:6f:aa:37:58:f7:87:26:d7:d8:2d:f6:a9:17:b7:
+ 1f:72:36:4e:a6:17:3f:65:98:92:db:2a:6e:5d:a2:
+ fe:88:e0:0b:de:7f:e5:8d:15:e1:eb:cb:3a:d5:e2:
+ 12:a2:13:2d:d8:8e:af:5f:12:3d:a0:08:05:08:b6:
+ 5c:a5:65:38:04:45:99:1e:a3:60:60:74:c5:41:a5:
+ 72:62:1b:62:c5:1f:6f:5f:1a:42:be:02:51:65:a8:
+ ae:23:18:6a:fc:78:03:a9:4d:7f:80:c3:fa:ab:5a:
+ fc:a1:40:a4:ca:19:16:fe:b2:c8:ef:5e:73:0d:ee:
+ 77:bd:9a:f6:79:98:bc:b1:07:67:a2:15:0d:dd:a0:
+ 58:c6:44:7b:0a:3e:62:28:5f:ba:41:07:53:58:cf:
+ 11:7e:38:74:c5:f8:ff:b5:69:90:8f:84:74:ea:97:
+ 1b:af
+ Exponent: 3 (0x3)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ D2:C4:B0:D2:91:D4:4C:11:71:B3:61:CB:3D:A1:FE:DD:A8:6A:D4:E3
+ X509v3 Authority Key Identifier:
+ DirName:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
+ serial:01
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.godaddy.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://certificates.godaddy.com/repository/root.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://certificates.godaddy.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ b5:40:f9:a7:1d:f6:ea:fe:a4:1a:42:5a:44:f7:15:d4:85:46:
+ 89:c0:be:9e:e3:e3:eb:c5:e3:58:89:8f:92:9f:57:a8:71:2c:
+ 48:d1:81:b2:79:1f:ac:06:35:19:b0:4e:0e:58:1b:14:b3:98:
+ 81:d1:04:1e:c8:07:c9:83:9f:78:44:0a:18:0b:98:dc:76:7a:
+ 65:0d:0d:6d:80:c4:0b:01:1c:cb:ad:47:3e:71:be:77:4b:cc:
+ 06:77:d0:f4:56:6b:1f:4b:13:9a:14:8a:88:23:a8:51:f0:83:
+ 4c:ab:35:bf:46:7e:39:dc:75:a4:ae:e8:29:fb:ef:39:8f:4f:
+ 55:67
+-----BEGIN CERTIFICATE-----
+MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh
+bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe
+BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDYyMFoX
+DTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBE
+YWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgC
+ggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
+2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+q
+N1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiO
+r18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN
+f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEH
+U1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ4EFgQU0sSw0pHU
+TBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBuzEkMCIGA1UEBxMb
+VmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQKEw5WYWxpQ2VydCwg
+SW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2xpY3kgVmFsaWRhdGlv
+biBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudmFsaWNlcnQuY29tLzEg
+MB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CAQEwDwYDVR0TAQH/BAUw
+AwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmdv
+ZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jZXJ0aWZpY2F0ZXMu
+Z29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybDBLBgNVHSAERDBCMEAGBFUd
+IAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv
+bS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQC1
+QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+Sn1eocSxI0YGyeR+sBjUZsE4O
+WBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgMQLARzLrUc+cb53S8wGd9D0Vmsf
+SxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j09VZw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert68[] = {
+ 0x30, 0x82, 0x04, 0xfb, 0x30, 0x82, 0x04, 0x64, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x01, 0x0d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0xbb, 0x31,
+ 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x1b, 0x56, 0x61,
+ 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x35, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2c, 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
+ 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x11, 0x69, 0x6e, 0x66, 0x6f, 0x40, 0x76, 0x61, 0x6c, 0x69, 0x63, 0x65,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x34,
+ 0x30, 0x36, 0x32, 0x39, 0x31, 0x37, 0x30, 0x36, 0x32, 0x30, 0x5a, 0x17,
+ 0x0d, 0x32, 0x34, 0x30, 0x36, 0x32, 0x39, 0x31, 0x37, 0x30, 0x36, 0x32,
+ 0x30, 0x5a, 0x30, 0x63, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x18, 0x54, 0x68, 0x65, 0x20, 0x47, 0x6f, 0x20, 0x44,
+ 0x61, 0x64, 0x64, 0x79, 0x20, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x28, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, 0x20,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x20, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0d, 0x00, 0x30, 0x82, 0x01, 0x08, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xde, 0x9d, 0xd7, 0xea, 0x57, 0x18, 0x49, 0xa1,
+ 0x5b, 0xeb, 0xd7, 0x5f, 0x48, 0x86, 0xea, 0xbe, 0xdd, 0xff, 0xe4, 0xef,
+ 0x67, 0x1c, 0xf4, 0x65, 0x68, 0xb3, 0x57, 0x71, 0xa0, 0x5e, 0x77, 0xbb,
+ 0xed, 0x9b, 0x49, 0xe9, 0x70, 0x80, 0x3d, 0x56, 0x18, 0x63, 0x08, 0x6f,
+ 0xda, 0xf2, 0xcc, 0xd0, 0x3f, 0x7f, 0x02, 0x54, 0x22, 0x54, 0x10, 0xd8,
+ 0xb2, 0x81, 0xd4, 0xc0, 0x75, 0x3d, 0x4b, 0x7f, 0xc7, 0x77, 0xc3, 0x3e,
+ 0x78, 0xab, 0x1a, 0x03, 0xb5, 0x20, 0x6b, 0x2f, 0x6a, 0x2b, 0xb1, 0xc5,
+ 0x88, 0x7e, 0xc4, 0xbb, 0x1e, 0xb0, 0xc1, 0xd8, 0x45, 0x27, 0x6f, 0xaa,
+ 0x37, 0x58, 0xf7, 0x87, 0x26, 0xd7, 0xd8, 0x2d, 0xf6, 0xa9, 0x17, 0xb7,
+ 0x1f, 0x72, 0x36, 0x4e, 0xa6, 0x17, 0x3f, 0x65, 0x98, 0x92, 0xdb, 0x2a,
+ 0x6e, 0x5d, 0xa2, 0xfe, 0x88, 0xe0, 0x0b, 0xde, 0x7f, 0xe5, 0x8d, 0x15,
+ 0xe1, 0xeb, 0xcb, 0x3a, 0xd5, 0xe2, 0x12, 0xa2, 0x13, 0x2d, 0xd8, 0x8e,
+ 0xaf, 0x5f, 0x12, 0x3d, 0xa0, 0x08, 0x05, 0x08, 0xb6, 0x5c, 0xa5, 0x65,
+ 0x38, 0x04, 0x45, 0x99, 0x1e, 0xa3, 0x60, 0x60, 0x74, 0xc5, 0x41, 0xa5,
+ 0x72, 0x62, 0x1b, 0x62, 0xc5, 0x1f, 0x6f, 0x5f, 0x1a, 0x42, 0xbe, 0x02,
+ 0x51, 0x65, 0xa8, 0xae, 0x23, 0x18, 0x6a, 0xfc, 0x78, 0x03, 0xa9, 0x4d,
+ 0x7f, 0x80, 0xc3, 0xfa, 0xab, 0x5a, 0xfc, 0xa1, 0x40, 0xa4, 0xca, 0x19,
+ 0x16, 0xfe, 0xb2, 0xc8, 0xef, 0x5e, 0x73, 0x0d, 0xee, 0x77, 0xbd, 0x9a,
+ 0xf6, 0x79, 0x98, 0xbc, 0xb1, 0x07, 0x67, 0xa2, 0x15, 0x0d, 0xdd, 0xa0,
+ 0x58, 0xc6, 0x44, 0x7b, 0x0a, 0x3e, 0x62, 0x28, 0x5f, 0xba, 0x41, 0x07,
+ 0x53, 0x58, 0xcf, 0x11, 0x7e, 0x38, 0x74, 0xc5, 0xf8, 0xff, 0xb5, 0x69,
+ 0x90, 0x8f, 0x84, 0x74, 0xea, 0x97, 0x1b, 0xaf, 0x02, 0x01, 0x03, 0xa3,
+ 0x82, 0x01, 0xe1, 0x30, 0x82, 0x01, 0xdd, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xd2, 0xc4, 0xb0, 0xd2, 0x91, 0xd4,
+ 0x4c, 0x11, 0x71, 0xb3, 0x61, 0xcb, 0x3d, 0xa1, 0xfe, 0xdd, 0xa8, 0x6a,
+ 0xd4, 0xe3, 0x30, 0x81, 0xd2, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81,
+ 0xca, 0x30, 0x81, 0xc7, 0xa1, 0x81, 0xc1, 0xa4, 0x81, 0xbe, 0x30, 0x81,
+ 0xbb, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x1b,
+ 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x56, 0x61, 0x6c,
+ 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x35, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x2c, 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x6f, 0x6c, 0x69,
+ 0x63, 0x79, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
+ 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x61, 0x6c,
+ 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x20,
+ 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
+ 0x01, 0x16, 0x11, 0x69, 0x6e, 0x66, 0x6f, 0x40, 0x76, 0x61, 0x6c, 0x69,
+ 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x82, 0x01, 0x01, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f,
+ 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x44, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37,
+ 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e,
+ 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x72,
+ 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x4b, 0x06, 0x03, 0x55,
+ 0x1d, 0x20, 0x04, 0x44, 0x30, 0x42, 0x30, 0x40, 0x06, 0x04, 0x55, 0x1d,
+ 0x20, 0x00, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+ 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
+ 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xb5,
+ 0x40, 0xf9, 0xa7, 0x1d, 0xf6, 0xea, 0xfe, 0xa4, 0x1a, 0x42, 0x5a, 0x44,
+ 0xf7, 0x15, 0xd4, 0x85, 0x46, 0x89, 0xc0, 0xbe, 0x9e, 0xe3, 0xe3, 0xeb,
+ 0xc5, 0xe3, 0x58, 0x89, 0x8f, 0x92, 0x9f, 0x57, 0xa8, 0x71, 0x2c, 0x48,
+ 0xd1, 0x81, 0xb2, 0x79, 0x1f, 0xac, 0x06, 0x35, 0x19, 0xb0, 0x4e, 0x0e,
+ 0x58, 0x1b, 0x14, 0xb3, 0x98, 0x81, 0xd1, 0x04, 0x1e, 0xc8, 0x07, 0xc9,
+ 0x83, 0x9f, 0x78, 0x44, 0x0a, 0x18, 0x0b, 0x98, 0xdc, 0x76, 0x7a, 0x65,
+ 0x0d, 0x0d, 0x6d, 0x80, 0xc4, 0x0b, 0x01, 0x1c, 0xcb, 0xad, 0x47, 0x3e,
+ 0x71, 0xbe, 0x77, 0x4b, 0xcc, 0x06, 0x77, 0xd0, 0xf4, 0x56, 0x6b, 0x1f,
+ 0x4b, 0x13, 0x9a, 0x14, 0x8a, 0x88, 0x23, 0xa8, 0x51, 0xf0, 0x83, 0x4c,
+ 0xab, 0x35, 0xbf, 0x46, 0x7e, 0x39, 0xdc, 0x75, 0xa4, 0xae, 0xe8, 0x29,
+ 0xfb, 0xef, 0x39, 0x8f, 0x4f, 0x55, 0x67,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 16:90:c3:29:b6:78:06:07:51:1f:05:b0:34:48:46:cb
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Apr 16 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO High-Assurance Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e7:87:da:c0:77:e4:bb:3a:fa:6a:24:c8:80:41:
+ ac:d2:16:13:15:3d:fa:f7:f8:2a:76:dc:a8:2d:39:
+ 08:ce:48:4a:be:0f:7d:f0:de:ba:bb:47:d5:bd:2d:
+ d7:1b:ab:0f:20:81:23:08:72:b1:c0:11:95:0d:e6:
+ ea:a9:87:ff:c7:6e:1e:4f:66:32:ba:53:bc:05:aa:
+ 1c:2c:0c:ef:4d:37:47:6b:10:0c:db:c5:a0:98:7e:
+ 58:db:37:d6:ae:e9:06:bd:d7:a8:65:f3:37:b9:c7:
+ 6d:ce:77:c7:26:e0:d7:74:1f:a6:98:16:bb:0c:6b:
+ c8:be:77:d0:ef:58:a7:29:a0:b9:b8:69:05:36:cb:
+ b2:da:58:a3:0b:75:ad:3d:8b:22:82:20:3e:70:86:
+ 99:1c:b9:4f:cf:77:a4:07:1a:23:63:d1:38:56:84:
+ ec:bf:8f:c5:4e:f4:18:96:9b:1a:e8:93:ec:8d:af:
+ 15:9c:24:f0:5a:3b:e8:0f:b9:a8:5a:01:d3:b2:1c:
+ 60:c9:9c:52:04:dd:92:a7:fe:0c:ac:e2:45:8d:03:
+ 61:bc:79:e0:77:2e:87:41:3c:58:5f:cb:f5:c5:77:
+ f2:58:c8:4d:28:d0:9a:fa:f3:73:09:24:68:74:bc:
+ 20:4c:d8:2c:b0:aa:e8:d9:4e:6d:f2:8c:24:d3:93:
+ 5d:91
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 3F:D5:B5:D0:D6:44:79:50:4A:17:A3:9B:8C:4A:DC:B8:B0:22:64:6B
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 13:85:1f:52:80:18:c9:53:f7:fe:2e:1a:af:cc:d9:0b:3c:c2:
+ d3:85:81:10:f0:28:8d:b9:40:7e:2c:9e:8f:d6:36:86:0a:4c:
+ 14:2d:d6:97:43:92:41:19:37:4b:96:9e:eb:a9:30:79:12:95:
+ b3:02:36:57:ed:2b:b9:1d:98:1a:a3:18:0a:3f:9b:39:8b:cd:
+ a1:49:29:4c:2f:f9:d0:95:8c:c8:4d:95:ba:a8:43:cf:33:aa:
+ 25:2a:5a:0e:aa:27:c9:4e:6b:b1:e6:73:1f:b3:74:04:c3:f3:
+ 4c:e2:a8:eb:67:b7:5d:b8:08:05:1a:56:9a:54:29:85:f5:29:
+ 4e:80:3b:95:d0:7b:53:96:11:56:c1:02:d3:ea:b2:7f:ca:8f:
+ 9c:70:4a:14:8d:5a:b9:16:60:75:d6:cd:27:1e:16:cd:5b:33:
+ 8e:79:40:cf:28:48:e7:dc:71:16:4e:74:91:75:b9:2a:8c:f1:
+ 70:ac:26:dd:04:b9:40:c2:85:de:1c:93:40:d0:cc:6e:c3:9b:
+ aa:ef:60:65:df:60:22:f0:5a:a5:7a:a2:2f:e4:70:73:ee:3c:
+ d4:26:2b:68:07:c1:20:7a:e8:98:5a:3e:7b:9f:02:8b:62:c0:
+ 85:81:80:60:35:7e:a5:1d:0c:d2:9c:df:62:45:0d:db:fc:37:
+ fb:f5:25:22
+-----BEGIN CERTIFICATE-----
+MIIE/DCCA+SgAwIBAgIQFpDDKbZ4BgdRHwWwNEhGyzANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTEwMDQxNjAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+gYkxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMS8wLQYD
+VQQDEyZDT01PRE8gSGlnaC1Bc3N1cmFuY2UgU2VjdXJlIFNlcnZlciBDQTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOeH2sB35Ls6+mokyIBBrNIWExU9
++vf4KnbcqC05CM5ISr4PffDeurtH1b0t1xurDyCBIwhyscARlQ3m6qmH/8duHk9m
+MrpTvAWqHCwM7003R2sQDNvFoJh+WNs31q7pBr3XqGXzN7nHbc53xybg13QfppgW
+uwxryL530O9YpymgubhpBTbLstpYowt1rT2LIoIgPnCGmRy5T893pAcaI2PROFaE
+7L+PxU70GJabGuiT7I2vFZwk8Fo76A+5qFoB07IcYMmcUgTdkqf+DKziRY0DYbx5
+4Hcuh0E8WF/L9cV38ljITSjQmvrzcwkkaHS8IEzYLLCq6NlObfKMJNOTXZECAwEA
+AaOCAXcwggFzMB8GA1UdIwQYMBaAFK29mHo0tCb3+sQmVO8DveAky1QaMB0GA1Ud
+DgQWBBQ/1bXQ1kR5UEoXo5uMSty4sCJkazAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
+AQH/BAgwBgEB/wIBADARBgNVHSAECjAIMAYGBFUdIAAwRAYDVR0fBD0wOzA5oDeg
+NYYzaHR0cDovL2NybC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJv
+b3QuY3JsMIGzBggrBgEFBQcBAQSBpjCBozA/BggrBgEFBQcwAoYzaHR0cDovL2Ny
+dC51c2VydHJ1c3QuY29tL0FkZFRydXN0RXh0ZXJuYWxDQVJvb3QucDdjMDkGCCsG
+AQUFBzAChi1odHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RVVE5TR0ND
+QS5jcnQwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJ
+KoZIhvcNAQEFBQADggEBABOFH1KAGMlT9/4uGq/M2Qs8wtOFgRDwKI25QH4sno/W
+NoYKTBQt1pdDkkEZN0uWnuupMHkSlbMCNlftK7kdmBqjGAo/mzmLzaFJKUwv+dCV
+jMhNlbqoQ88zqiUqWg6qJ8lOa7Hmcx+zdATD80ziqOtnt124CAUaVppUKYX1KU6A
+O5XQe1OWEVbBAtPqsn/Kj5xwShSNWrkWYHXWzSceFs1bM455QM8oSOfccRZOdJF1
+uSqM8XCsJt0EuUDChd4ck0DQzG7Dm6rvYGXfYCLwWqV6oi/kcHPuPNQmK2gHwSB6
+6JhaPnufAotiwIWBgGA1fqUdDNKc32JFDdv8N/v1JSI=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert69[] = {
+ 0x30, 0x82, 0x04, 0xfc, 0x30, 0x82, 0x03, 0xe4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x16, 0x90, 0xc3, 0x29, 0xb6, 0x78, 0x06, 0x07, 0x51,
+ 0x1f, 0x05, 0xb0, 0x34, 0x48, 0x46, 0xcb, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x34, 0x31,
+ 0x36, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x89, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61,
+ 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f,
+ 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x26, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20,
+ 0x48, 0x69, 0x67, 0x68, 0x2d, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e,
+ 0x63, 0x65, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xe7, 0x87, 0xda, 0xc0, 0x77, 0xe4, 0xbb, 0x3a,
+ 0xfa, 0x6a, 0x24, 0xc8, 0x80, 0x41, 0xac, 0xd2, 0x16, 0x13, 0x15, 0x3d,
+ 0xfa, 0xf7, 0xf8, 0x2a, 0x76, 0xdc, 0xa8, 0x2d, 0x39, 0x08, 0xce, 0x48,
+ 0x4a, 0xbe, 0x0f, 0x7d, 0xf0, 0xde, 0xba, 0xbb, 0x47, 0xd5, 0xbd, 0x2d,
+ 0xd7, 0x1b, 0xab, 0x0f, 0x20, 0x81, 0x23, 0x08, 0x72, 0xb1, 0xc0, 0x11,
+ 0x95, 0x0d, 0xe6, 0xea, 0xa9, 0x87, 0xff, 0xc7, 0x6e, 0x1e, 0x4f, 0x66,
+ 0x32, 0xba, 0x53, 0xbc, 0x05, 0xaa, 0x1c, 0x2c, 0x0c, 0xef, 0x4d, 0x37,
+ 0x47, 0x6b, 0x10, 0x0c, 0xdb, 0xc5, 0xa0, 0x98, 0x7e, 0x58, 0xdb, 0x37,
+ 0xd6, 0xae, 0xe9, 0x06, 0xbd, 0xd7, 0xa8, 0x65, 0xf3, 0x37, 0xb9, 0xc7,
+ 0x6d, 0xce, 0x77, 0xc7, 0x26, 0xe0, 0xd7, 0x74, 0x1f, 0xa6, 0x98, 0x16,
+ 0xbb, 0x0c, 0x6b, 0xc8, 0xbe, 0x77, 0xd0, 0xef, 0x58, 0xa7, 0x29, 0xa0,
+ 0xb9, 0xb8, 0x69, 0x05, 0x36, 0xcb, 0xb2, 0xda, 0x58, 0xa3, 0x0b, 0x75,
+ 0xad, 0x3d, 0x8b, 0x22, 0x82, 0x20, 0x3e, 0x70, 0x86, 0x99, 0x1c, 0xb9,
+ 0x4f, 0xcf, 0x77, 0xa4, 0x07, 0x1a, 0x23, 0x63, 0xd1, 0x38, 0x56, 0x84,
+ 0xec, 0xbf, 0x8f, 0xc5, 0x4e, 0xf4, 0x18, 0x96, 0x9b, 0x1a, 0xe8, 0x93,
+ 0xec, 0x8d, 0xaf, 0x15, 0x9c, 0x24, 0xf0, 0x5a, 0x3b, 0xe8, 0x0f, 0xb9,
+ 0xa8, 0x5a, 0x01, 0xd3, 0xb2, 0x1c, 0x60, 0xc9, 0x9c, 0x52, 0x04, 0xdd,
+ 0x92, 0xa7, 0xfe, 0x0c, 0xac, 0xe2, 0x45, 0x8d, 0x03, 0x61, 0xbc, 0x79,
+ 0xe0, 0x77, 0x2e, 0x87, 0x41, 0x3c, 0x58, 0x5f, 0xcb, 0xf5, 0xc5, 0x77,
+ 0xf2, 0x58, 0xc8, 0x4d, 0x28, 0xd0, 0x9a, 0xfa, 0xf3, 0x73, 0x09, 0x24,
+ 0x68, 0x74, 0xbc, 0x20, 0x4c, 0xd8, 0x2c, 0xb0, 0xaa, 0xe8, 0xd9, 0x4e,
+ 0x6d, 0xf2, 0x8c, 0x24, 0xd3, 0x93, 0x5d, 0x91, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x77, 0x30, 0x82, 0x01, 0x73, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd,
+ 0x98, 0x7a, 0x34, 0xb4, 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03,
+ 0xbd, 0xe0, 0x24, 0xcb, 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x3f, 0xd5, 0xb5, 0xd0, 0xd6, 0x44, 0x79,
+ 0x50, 0x4a, 0x17, 0xa3, 0x9b, 0x8c, 0x4a, 0xdc, 0xb8, 0xb0, 0x22, 0x64,
+ 0x6b, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x11, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x0a, 0x30, 0x08,
+ 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x44, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, 0x37, 0xa0,
+ 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6, 0x30, 0x81,
+ 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41, 0x52, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x13, 0x85, 0x1f, 0x52, 0x80, 0x18, 0xc9, 0x53,
+ 0xf7, 0xfe, 0x2e, 0x1a, 0xaf, 0xcc, 0xd9, 0x0b, 0x3c, 0xc2, 0xd3, 0x85,
+ 0x81, 0x10, 0xf0, 0x28, 0x8d, 0xb9, 0x40, 0x7e, 0x2c, 0x9e, 0x8f, 0xd6,
+ 0x36, 0x86, 0x0a, 0x4c, 0x14, 0x2d, 0xd6, 0x97, 0x43, 0x92, 0x41, 0x19,
+ 0x37, 0x4b, 0x96, 0x9e, 0xeb, 0xa9, 0x30, 0x79, 0x12, 0x95, 0xb3, 0x02,
+ 0x36, 0x57, 0xed, 0x2b, 0xb9, 0x1d, 0x98, 0x1a, 0xa3, 0x18, 0x0a, 0x3f,
+ 0x9b, 0x39, 0x8b, 0xcd, 0xa1, 0x49, 0x29, 0x4c, 0x2f, 0xf9, 0xd0, 0x95,
+ 0x8c, 0xc8, 0x4d, 0x95, 0xba, 0xa8, 0x43, 0xcf, 0x33, 0xaa, 0x25, 0x2a,
+ 0x5a, 0x0e, 0xaa, 0x27, 0xc9, 0x4e, 0x6b, 0xb1, 0xe6, 0x73, 0x1f, 0xb3,
+ 0x74, 0x04, 0xc3, 0xf3, 0x4c, 0xe2, 0xa8, 0xeb, 0x67, 0xb7, 0x5d, 0xb8,
+ 0x08, 0x05, 0x1a, 0x56, 0x9a, 0x54, 0x29, 0x85, 0xf5, 0x29, 0x4e, 0x80,
+ 0x3b, 0x95, 0xd0, 0x7b, 0x53, 0x96, 0x11, 0x56, 0xc1, 0x02, 0xd3, 0xea,
+ 0xb2, 0x7f, 0xca, 0x8f, 0x9c, 0x70, 0x4a, 0x14, 0x8d, 0x5a, 0xb9, 0x16,
+ 0x60, 0x75, 0xd6, 0xcd, 0x27, 0x1e, 0x16, 0xcd, 0x5b, 0x33, 0x8e, 0x79,
+ 0x40, 0xcf, 0x28, 0x48, 0xe7, 0xdc, 0x71, 0x16, 0x4e, 0x74, 0x91, 0x75,
+ 0xb9, 0x2a, 0x8c, 0xf1, 0x70, 0xac, 0x26, 0xdd, 0x04, 0xb9, 0x40, 0xc2,
+ 0x85, 0xde, 0x1c, 0x93, 0x40, 0xd0, 0xcc, 0x6e, 0xc3, 0x9b, 0xaa, 0xef,
+ 0x60, 0x65, 0xdf, 0x60, 0x22, 0xf0, 0x5a, 0xa5, 0x7a, 0xa2, 0x2f, 0xe4,
+ 0x70, 0x73, 0xee, 0x3c, 0xd4, 0x26, 0x2b, 0x68, 0x07, 0xc1, 0x20, 0x7a,
+ 0xe8, 0x98, 0x5a, 0x3e, 0x7b, 0x9f, 0x02, 0x8b, 0x62, 0xc0, 0x85, 0x81,
+ 0x80, 0x60, 0x35, 0x7e, 0xa5, 0x1d, 0x0c, 0xd2, 0x9c, 0xdf, 0x62, 0x45,
+ 0x0d, 0xdb, 0xfc, 0x37, 0xfb, 0xf5, 0x25, 0x22,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 45:4f:a2:0d:78:11:74:59:f8:c6:ab:3c:7b:cd:03:0e
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: Jul 29 00:00:00 2011 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=BR, O=TrustSign Certificadora Digital, OU=Security Dept., CN=TrustSign BR Certification Authority (OV)
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:fb:cb:bf:d7:c0:51:de:8e:5f:6e:76:a7:1f:
+ c3:30:67:68:ef:63:a9:0d:3c:7a:0e:f6:dd:26:ff:
+ bc:7a:26:d5:b6:22:91:08:b8:38:43:e1:15:3d:0b:
+ f0:b0:df:9d:34:40:06:5e:ea:3b:e9:9b:2e:23:d3:
+ eb:1f:17:1c:16:ad:5a:78:41:e6:2d:61:ed:7d:2c:
+ 5a:5d:8a:6c:0e:70:6b:ff:ce:c4:80:50:14:cc:2b:
+ 60:0f:19:3d:f1:6d:e0:a7:a3:59:22:97:73:ec:4a:
+ d6:ba:f0:9e:a9:0b:f4:66:3e:13:5f:c8:72:5f:04:
+ 4a:13:21:e6:80:85:1f:63:5a:26:f0:c4:25:9b:07:
+ 33:71:ea:32:26:04:b2:d8:68:ff:6a:e6:09:5f:09:
+ 83:0c:c3:a8:e5:5a:bf:6c:86:b2:28:6a:94:a3:74:
+ 99:4a:d8:d0:11:e5:e4:ee:8c:b2:2c:4d:72:86:1b:
+ 17:4d:ee:cc:9b:5a:b2:36:7d:05:92:17:47:9b:f7:
+ 06:9d:96:be:ac:da:c4:db:97:c0:d4:10:cb:11:26:
+ 62:59:17:05:14:63:cb:81:4f:5a:a3:9d:cf:50:e7:
+ 4a:b6:e3:30:d5:29:44:ac:99:45:8c:0d:d0:97:66:
+ bb:38:f6:ad:0f:4d:ea:bb:0a:8b:02:27:11:37:eb:
+ 42:bb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 22:82:72:0D:B6:41:E3:DC:E4:81:8B:FB:86:02:11:71:93:FA:9B:4D
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.2.38
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 3e:d9:cd:2e:64:0f:59:01:5c:22:22:b1:71:44:9d:59:4d:4c:
+ 8c:8f:10:59:5e:f6:ba:f8:5a:e3:4c:62:ea:09:8d:d7:9b:69:
+ a1:af:90:1d:de:f9:18:d6:83:c2:a5:e9:a8:ac:b6:ba:bf:db:
+ 4d:0a:50:f1:78:a3:13:91:6b:26:d8:e9:94:bb:2c:fb:0d:6f:
+ 7e:a7:f5:16:e2:99:77:d3:c6:52:3f:06:6e:18:f0:2e:28:c7:
+ 72:01:d7:9c:0b:12:40:76:08:7a:5c:69:eb:a1:d9:82:97:26:
+ c2:76:40:47:0f:56:54:2d:2c:92:65:98:28:b7:10:b2:dc:9c:
+ 26:b5:54:11:d6:be:b8:b6:03:9c:f0:b5:a5:f7:6e:f1:97:9c:
+ bd:88:23:a5:48:c7:8c:ca:0d:26:fd:48:09:a7:22:92:04:2a:
+ 3a:7e:97:a4:86:ef:9f:ff:2c:4b:e1:00:14:c1:55:6d:c1:5c:
+ 91:57:ec:8d:43:c5:f9:7d:b4:0c:06:34:c3:b4:a7:67:87:b6:
+ c4:cf:1a:35:08:95:11:20:21:a1:77:bf:2f:19:8a:73:81:c2:
+ 87:23:eb:d3:99:b2:7f:27:89:d2:8e:40:fa:3d:ee:06:49:0b:
+ fe:f4:a4:34:49:77:88:3d:42:91:96:b4:2c:9c:0b:e6:d4:41:
+ c4:df:ad:90
+-----BEGIN CERTIFICATE-----
+MIIE/jCCA+agAwIBAgIQRU+iDXgRdFn4xqs8e80DDjANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTExMDcyOTAwMDAwMFoXDTIwMDUzMDEwNDgzOFow
+gYQxCzAJBgNVBAYTAkJSMSgwJgYDVQQKEx9UcnVzdFNpZ24gQ2VydGlmaWNhZG9y
+YSBEaWdpdGFsMRcwFQYDVQQLEw5TZWN1cml0eSBEZXB0LjEyMDAGA1UEAxMpVHJ1
+c3RTaWduIEJSIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IChPVikwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCj+8u/18BR3o5fbnanH8MwZ2jvY6kNPHoO
+9t0m/7x6JtW2IpEIuDhD4RU9C/Cw3500QAZe6jvpmy4j0+sfFxwWrVp4QeYtYe19
+LFpdimwOcGv/zsSAUBTMK2APGT3xbeCno1kil3PsSta68J6pC/RmPhNfyHJfBEoT
+IeaAhR9jWibwxCWbBzNx6jImBLLYaP9q5glfCYMMw6jlWr9shrIoapSjdJlK2NAR
+5eTujLIsTXKGGxdN7sybWrI2fQWSF0eb9wadlr6s2sTbl8DUEMsRJmJZFwUUY8uB
+T1qjnc9Q50q24zDVKUSsmUWMDdCXZrs49q0PTeq7CosCJxE360K7AgMBAAGjggF+
+MIIBejAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQU
+IoJyDbZB49zkgYv7hgIRcZP6m00wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwGAYDVR0gBBEwDzANBgsrBgEEAbIxAQICJjBEBgNVHR8EPTA7MDmg
+N6A1hjNodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RFeHRlcm5hbENB
+Um9vdC5jcmwwgbMGCCsGAQUFBwEBBIGmMIGjMD8GCCsGAQUFBzAChjNodHRwOi8v
+Y3J0LnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5wN2MwOQYI
+KwYBBQUHMAKGLWh0dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9BZGRUcnVzdFVUTlNH
+Q0NBLmNydDAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTAN
+BgkqhkiG9w0BAQUFAAOCAQEAPtnNLmQPWQFcIiKxcUSdWU1MjI8QWV72uvha40xi
+6gmN15tpoa+QHd75GNaDwqXpqKy2ur/bTQpQ8XijE5FrJtjplLss+w1vfqf1FuKZ
+d9PGUj8GbhjwLijHcgHXnAsSQHYIelxp66HZgpcmwnZARw9WVC0skmWYKLcQstyc
+JrVUEda+uLYDnPC1pfdu8ZecvYgjpUjHjMoNJv1ICacikgQqOn6XpIbvn/8sS+EA
+FMFVbcFckVfsjUPF+X20DAY0w7SnZ4e2xM8aNQiVESAhoXe/LxmKc4HChyPr05my
+fyeJ0o5A+j3uBkkL/vSkNEl3iD1CkZa0LJwL5tRBxN+tkA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert70[] = {
+ 0x30, 0x82, 0x04, 0xfe, 0x30, 0x82, 0x03, 0xe6, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x45, 0x4f, 0xa2, 0x0d, 0x78, 0x11, 0x74, 0x59, 0xf8,
+ 0xc6, 0xab, 0x3c, 0x7b, 0xcd, 0x03, 0x0e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x31, 0x30, 0x37, 0x32,
+ 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x81, 0x84, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x42, 0x52, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x1f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x64, 0x6f, 0x72,
+ 0x61, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x31, 0x17, 0x30,
+ 0x15, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0e, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x20, 0x44, 0x65, 0x70, 0x74, 0x2e, 0x31, 0x32,
+ 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x29, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x42, 0x52, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x4f,
+ 0x56, 0x29, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa3,
+ 0xfb, 0xcb, 0xbf, 0xd7, 0xc0, 0x51, 0xde, 0x8e, 0x5f, 0x6e, 0x76, 0xa7,
+ 0x1f, 0xc3, 0x30, 0x67, 0x68, 0xef, 0x63, 0xa9, 0x0d, 0x3c, 0x7a, 0x0e,
+ 0xf6, 0xdd, 0x26, 0xff, 0xbc, 0x7a, 0x26, 0xd5, 0xb6, 0x22, 0x91, 0x08,
+ 0xb8, 0x38, 0x43, 0xe1, 0x15, 0x3d, 0x0b, 0xf0, 0xb0, 0xdf, 0x9d, 0x34,
+ 0x40, 0x06, 0x5e, 0xea, 0x3b, 0xe9, 0x9b, 0x2e, 0x23, 0xd3, 0xeb, 0x1f,
+ 0x17, 0x1c, 0x16, 0xad, 0x5a, 0x78, 0x41, 0xe6, 0x2d, 0x61, 0xed, 0x7d,
+ 0x2c, 0x5a, 0x5d, 0x8a, 0x6c, 0x0e, 0x70, 0x6b, 0xff, 0xce, 0xc4, 0x80,
+ 0x50, 0x14, 0xcc, 0x2b, 0x60, 0x0f, 0x19, 0x3d, 0xf1, 0x6d, 0xe0, 0xa7,
+ 0xa3, 0x59, 0x22, 0x97, 0x73, 0xec, 0x4a, 0xd6, 0xba, 0xf0, 0x9e, 0xa9,
+ 0x0b, 0xf4, 0x66, 0x3e, 0x13, 0x5f, 0xc8, 0x72, 0x5f, 0x04, 0x4a, 0x13,
+ 0x21, 0xe6, 0x80, 0x85, 0x1f, 0x63, 0x5a, 0x26, 0xf0, 0xc4, 0x25, 0x9b,
+ 0x07, 0x33, 0x71, 0xea, 0x32, 0x26, 0x04, 0xb2, 0xd8, 0x68, 0xff, 0x6a,
+ 0xe6, 0x09, 0x5f, 0x09, 0x83, 0x0c, 0xc3, 0xa8, 0xe5, 0x5a, 0xbf, 0x6c,
+ 0x86, 0xb2, 0x28, 0x6a, 0x94, 0xa3, 0x74, 0x99, 0x4a, 0xd8, 0xd0, 0x11,
+ 0xe5, 0xe4, 0xee, 0x8c, 0xb2, 0x2c, 0x4d, 0x72, 0x86, 0x1b, 0x17, 0x4d,
+ 0xee, 0xcc, 0x9b, 0x5a, 0xb2, 0x36, 0x7d, 0x05, 0x92, 0x17, 0x47, 0x9b,
+ 0xf7, 0x06, 0x9d, 0x96, 0xbe, 0xac, 0xda, 0xc4, 0xdb, 0x97, 0xc0, 0xd4,
+ 0x10, 0xcb, 0x11, 0x26, 0x62, 0x59, 0x17, 0x05, 0x14, 0x63, 0xcb, 0x81,
+ 0x4f, 0x5a, 0xa3, 0x9d, 0xcf, 0x50, 0xe7, 0x4a, 0xb6, 0xe3, 0x30, 0xd5,
+ 0x29, 0x44, 0xac, 0x99, 0x45, 0x8c, 0x0d, 0xd0, 0x97, 0x66, 0xbb, 0x38,
+ 0xf6, 0xad, 0x0f, 0x4d, 0xea, 0xbb, 0x0a, 0x8b, 0x02, 0x27, 0x11, 0x37,
+ 0xeb, 0x42, 0xbb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x7e,
+ 0x30, 0x82, 0x01, 0x7a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4, 0x26,
+ 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb, 0x54,
+ 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x22, 0x82, 0x72, 0x0d, 0xb6, 0x41, 0xe3, 0xdc, 0xe4, 0x81, 0x8b, 0xfb,
+ 0x86, 0x02, 0x11, 0x71, 0x93, 0xfa, 0x9b, 0x4d, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, 0x26, 0x30, 0x44,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0,
+ 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41,
+ 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6,
+ 0x30, 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41,
+ 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41,
+ 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47,
+ 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3e, 0xd9, 0xcd, 0x2e, 0x64, 0x0f,
+ 0x59, 0x01, 0x5c, 0x22, 0x22, 0xb1, 0x71, 0x44, 0x9d, 0x59, 0x4d, 0x4c,
+ 0x8c, 0x8f, 0x10, 0x59, 0x5e, 0xf6, 0xba, 0xf8, 0x5a, 0xe3, 0x4c, 0x62,
+ 0xea, 0x09, 0x8d, 0xd7, 0x9b, 0x69, 0xa1, 0xaf, 0x90, 0x1d, 0xde, 0xf9,
+ 0x18, 0xd6, 0x83, 0xc2, 0xa5, 0xe9, 0xa8, 0xac, 0xb6, 0xba, 0xbf, 0xdb,
+ 0x4d, 0x0a, 0x50, 0xf1, 0x78, 0xa3, 0x13, 0x91, 0x6b, 0x26, 0xd8, 0xe9,
+ 0x94, 0xbb, 0x2c, 0xfb, 0x0d, 0x6f, 0x7e, 0xa7, 0xf5, 0x16, 0xe2, 0x99,
+ 0x77, 0xd3, 0xc6, 0x52, 0x3f, 0x06, 0x6e, 0x18, 0xf0, 0x2e, 0x28, 0xc7,
+ 0x72, 0x01, 0xd7, 0x9c, 0x0b, 0x12, 0x40, 0x76, 0x08, 0x7a, 0x5c, 0x69,
+ 0xeb, 0xa1, 0xd9, 0x82, 0x97, 0x26, 0xc2, 0x76, 0x40, 0x47, 0x0f, 0x56,
+ 0x54, 0x2d, 0x2c, 0x92, 0x65, 0x98, 0x28, 0xb7, 0x10, 0xb2, 0xdc, 0x9c,
+ 0x26, 0xb5, 0x54, 0x11, 0xd6, 0xbe, 0xb8, 0xb6, 0x03, 0x9c, 0xf0, 0xb5,
+ 0xa5, 0xf7, 0x6e, 0xf1, 0x97, 0x9c, 0xbd, 0x88, 0x23, 0xa5, 0x48, 0xc7,
+ 0x8c, 0xca, 0x0d, 0x26, 0xfd, 0x48, 0x09, 0xa7, 0x22, 0x92, 0x04, 0x2a,
+ 0x3a, 0x7e, 0x97, 0xa4, 0x86, 0xef, 0x9f, 0xff, 0x2c, 0x4b, 0xe1, 0x00,
+ 0x14, 0xc1, 0x55, 0x6d, 0xc1, 0x5c, 0x91, 0x57, 0xec, 0x8d, 0x43, 0xc5,
+ 0xf9, 0x7d, 0xb4, 0x0c, 0x06, 0x34, 0xc3, 0xb4, 0xa7, 0x67, 0x87, 0xb6,
+ 0xc4, 0xcf, 0x1a, 0x35, 0x08, 0x95, 0x11, 0x20, 0x21, 0xa1, 0x77, 0xbf,
+ 0x2f, 0x19, 0x8a, 0x73, 0x81, 0xc2, 0x87, 0x23, 0xeb, 0xd3, 0x99, 0xb2,
+ 0x7f, 0x27, 0x89, 0xd2, 0x8e, 0x40, 0xfa, 0x3d, 0xee, 0x06, 0x49, 0x0b,
+ 0xfe, 0xf4, 0xa4, 0x34, 0x49, 0x77, 0x88, 0x3d, 0x42, 0x91, 0x96, 0xb4,
+ 0x2c, 0x9c, 0x0b, 0xe6, 0xd4, 0x41, 0xc4, 0xdf, 0xad, 0x90,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120025006 (0x7276fae)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IE, O=Baltimore, OU=CyberTrust, CN=Baltimore CyberTrust Root
+ Validity
+ Not Before: Apr 25 17:41:36 2012 GMT
+ Not After : Apr 25 17:40:55 2020 GMT
+ Subject: CN=Microsoft Internet Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:be:40:54:11:aa:28:79:66:5e:a9:bb:b0:93:62:
+ 4b:73:19:aa:e9:2f:91:4c:6d:97:37:25:68:b8:b0:
+ 36:50:94:5c:ef:ff:c4:c4:17:76:43:57:fc:a0:e1:
+ c6:33:ec:30:62:be:35:0f:23:7a:96:4d:6c:74:62:
+ 09:7e:31:48:4e:9f:4d:aa:5b:b3:16:b7:fe:a0:29:
+ 2f:65:a5:4b:ac:6a:56:8a:bf:28:70:6a:df:37:18:
+ df:ec:c6:87:22:3e:16:dd:38:1c:0f:e2:05:be:77:
+ ab:ee:85:8a:1e:0d:23:3f:e9:24:e1:90:47:b2:67:
+ 3b:15:3e:55:20:3c:f9:f5:42:d7:af:c5:66:0d:9a:
+ 94:17:b9:6a:67:7c:a8:fa:b1:26:00:04:23:d5:4d:
+ a3:ae:8d:79:60:b1:8e:83:4f:cd:bb:77:78:27:b2:
+ e8:74:ad:1f:4c:34:80:31:7f:8b:e4:50:7e:6f:7f:
+ cf:55:da:c6:fb:b6:5a:f5:5d:1d:38:94:a5:fe:b0:
+ 8d:22:63:23:e7:70:40:40:bf:89:aa:54:46:25:03:
+ ec:a4:f2:ad:c0:40:b3:72:fe:94:b5:d9:96:b2:1b:
+ 73:5e:d5:f4:6b:47:41:79:a4:da:f3:8e:2e:8d:38:
+ 4d:c9:5e:17:1c:ae:b5:4c:36:6b:e8:7f:d9:24:c7:
+ 28:f0:a7:86:be:e8:1d:08:b4:db:72:23:64:d1:42:
+ f3:f5:4c:da:ac:f3:b6:a5:75:68:fc:f0:bb:02:61:
+ 5d:1e:7a:07:52:29:be:74:12:42:53:9c:8d:cc:e9:
+ 88:7b:a4:55:36:08:58:8c:21:89:c5:ba:13:e8:6d:
+ 9b:81:8a:77:c6:38:6c:f5:3a:1d:91:37:57:2b:a9:
+ eb:2b:46:4a:a8:97:28:8b:a3:7e:4a:dd:8d:a7:d3:
+ 02:10:7c:96:b0:c8:bc:86:28:06:d6:57:a2:a7:66:
+ f1:17:d6:cc:5f:55:12:3d:59:03:a2:c7:d2:d6:69:
+ b4:0d:25:f9:f1:d3:94:56:16:2e:26:bc:96:f9:ba:
+ 1c:51:9b:81:f1:37:bc:77:ee:7e:d9:9a:78:f6:41:
+ b4:71:df:10:25:5b:b2:e1:3a:c7:7b:f2:6c:b3:19:
+ b7:20:e7:89:e9:c4:a5:6a:a4:7c:39:24:ee:e7:5d:
+ 2b:2b:c9:91:fe:a1:3c:33:25:bd:c5:41:14:92:ee:
+ cf:56:3a:26:13:75:ca:11:5c:c6:27:ab:49:88:ab:
+ 55:72:fc:65:48:dc:ae:cd:f1:8f:8b:10:66:2a:90:
+ 2e:0b:8b:b7:fe:38:75:cd:b1:75:80:b6:8f:cf:43:
+ 1c:21:29:93:0f:e3:13:b9:e2:b0:c8:b2:46:2a:5a:
+ 14:0c:1d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ keyid:E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://cdp1.public-trust.com/CRL/Omniroot2025.crl
+
+ X509v3 Subject Key Identifier:
+ 2A:4D:97:95:5D:34:7E:9D:B6:E6:33:BE:9C:27:C1:70:7E:67:DB:C1
+ Signature Algorithm: sha1WithRSAEncryption
+ 23:3e:87:6e:d3:ca:d6:0e:b0:36:25:8d:cc:0a:ce:dc:5d:74:
+ f3:42:1b:ef:aa:c0:49:73:85:cc:7d:2f:77:09:23:c5:2c:50:
+ 1d:46:c3:68:68:ef:10:32:d9:4d:7e:e3:5a:e3:1d:18:3c:d2:
+ 40:54:cd:ef:59:ee:df:fe:fb:70:4a:bc:d8:74:37:2b:dc:a4:
+ 68:71:50:ba:63:cb:44:dd:11:48:ff:f8:f1:ff:60:ba:7c:aa:
+ 30:7d:dd:17:b1:77:ea:50:90:20:00:0b:a3:3d:5d:98:71:51:
+ 9f:dd:2d:c0:78:5e:0d:55:b1:83:45:de:e5:59:98:6d:a4:e1:
+ 69:7c:32:d0:04:7b:f7:a9:1d:97:3b:d5:59:bf:cb:6f:9d:a4:
+ b6:a5:bb:41:11:ed:c8:91:83:15:55:ae:59:36:b7:9f:6a:f0:
+ b8:38:f9:7c:32:25:95:cc:33:f1:31:e7:df:cb:78:4b:36:1f:
+ f4:55:e0:bd:28:f9:ca:b9:64:99:ce:eb:61:e9:81:72:94:d3:
+ 9b:cd:0a:2b:2a:a4:94:83:ae:6a:0c:23:44:0e:35:ad:a1:e9:
+ ec:d8:d7:75:90:8d:e1:d6:b3:c5:50:fc:5d:d5:fb:6f:92:e1:
+ f4:7e:f0:ae:af:f9:39:b3:ce:4b:01:9c:bd:4e:f7:f1:f2:6f:
+ ce:c0:36:d8
+-----BEGIN CERTIFICATE-----
+MIIFATCCA+mgAwIBAgIEBydvrjANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEyMDQyNTE3NDEzNloX
+DTIwMDQyNTE3NDA1NVowJzElMCMGA1UEAxMcTWljcm9zb2Z0IEludGVybmV0IEF1
+dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL5AVBGqKHlm
+Xqm7sJNiS3MZqukvkUxtlzclaLiwNlCUXO//xMQXdkNX/KDhxjPsMGK+NQ8jepZN
+bHRiCX4xSE6fTapbsxa3/qApL2WlS6xqVoq/KHBq3zcY3+zGhyI+Ft04HA/iBb53
+q+6Fih4NIz/pJOGQR7JnOxU+VSA8+fVC16/FZg2alBe5amd8qPqxJgAEI9VNo66N
+eWCxjoNPzbt3eCey6HStH0w0gDF/i+RQfm9/z1Xaxvu2WvVdHTiUpf6wjSJjI+dw
+QEC/iapURiUD7KTyrcBAs3L+lLXZlrIbc17V9GtHQXmk2vOOLo04TcleFxyutUw2
+a+h/2STHKPCnhr7oHQi023IjZNFC8/VM2qzztqV1aPzwuwJhXR56B1IpvnQSQlOc
+jczpiHukVTYIWIwhicW6E+htm4GKd8Y4bPU6HZE3Vyup6ytGSqiXKIujfkrdjafT
+AhB8lrDIvIYoBtZXoqdm8RfWzF9VEj1ZA6LH0tZptA0l+fHTlFYWLia8lvm6HFGb
+gfE3vHfuftmaePZBtHHfECVbsuE6x3vybLMZtyDnienEpWqkfDkk7uddKyvJkf6h
+PDMlvcVBFJLuz1Y6JhN1yhFcxierSYirVXL8ZUjcrs3xj4sQZiqQLguLt/44dc2x
+dYC2j89DHCEpkw/jE7nisMiyRipaFAwdAgMBAAGjggEAMIH9MBIGA1UdEwEB/wQI
+MAYBAf8CAQEwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEFBQcCARYt
+aHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnkuY2ZtMA4G
+A1UdDwEB/wQEAwIBhjAfBgNVHSMEGDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DBC
+BgNVHR8EOzA5MDegNaAzhjFodHRwOi8vY2RwMS5wdWJsaWMtdHJ1c3QuY29tL0NS
+TC9PbW5pcm9vdDIwMjUuY3JsMB0GA1UdDgQWBBQqTZeVXTR+nbbmM76cJ8Fwfmfb
+wTANBgkqhkiG9w0BAQUFAAOCAQEAIz6HbtPK1g6wNiWNzArO3F1080Ib76rASXOF
+zH0vdwkjxSxQHUbDaGjvEDLZTX7jWuMdGDzSQFTN71nu3/77cEq82HQ3K9ykaHFQ
+umPLRN0RSP/48f9gunyqMH3dF7F36lCQIAALoz1dmHFRn90twHheDVWxg0Xe5VmY
+baThaXwy0AR796kdlzvVWb/Lb52ktqW7QRHtyJGDFVWuWTa3n2rwuDj5fDIllcwz
+8THn38t4SzYf9FXgvSj5yrlkmc7rYemBcpTTm80KKyqklIOuagwjRA41raHp7NjX
+dZCN4dazxVD8XdX7b5Lh9H7wrq/5ObPOSwGcvU738fJvzsA22A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert71[] = {
+ 0x30, 0x82, 0x05, 0x01, 0x30, 0x82, 0x03, 0xe9, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x6f, 0xae, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5a,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49,
+ 0x45, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09,
+ 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f, 0x72, 0x65, 0x31, 0x13, 0x30,
+ 0x11, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0a, 0x43, 0x79, 0x62, 0x65,
+ 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x19, 0x42, 0x61, 0x6c, 0x74, 0x69, 0x6d, 0x6f,
+ 0x72, 0x65, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32,
+ 0x30, 0x34, 0x32, 0x35, 0x31, 0x37, 0x34, 0x31, 0x33, 0x36, 0x5a, 0x17,
+ 0x0d, 0x32, 0x30, 0x30, 0x34, 0x32, 0x35, 0x31, 0x37, 0x34, 0x30, 0x35,
+ 0x35, 0x5a, 0x30, 0x27, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x1c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82, 0x02, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02,
+ 0x82, 0x02, 0x01, 0x00, 0xbe, 0x40, 0x54, 0x11, 0xaa, 0x28, 0x79, 0x66,
+ 0x5e, 0xa9, 0xbb, 0xb0, 0x93, 0x62, 0x4b, 0x73, 0x19, 0xaa, 0xe9, 0x2f,
+ 0x91, 0x4c, 0x6d, 0x97, 0x37, 0x25, 0x68, 0xb8, 0xb0, 0x36, 0x50, 0x94,
+ 0x5c, 0xef, 0xff, 0xc4, 0xc4, 0x17, 0x76, 0x43, 0x57, 0xfc, 0xa0, 0xe1,
+ 0xc6, 0x33, 0xec, 0x30, 0x62, 0xbe, 0x35, 0x0f, 0x23, 0x7a, 0x96, 0x4d,
+ 0x6c, 0x74, 0x62, 0x09, 0x7e, 0x31, 0x48, 0x4e, 0x9f, 0x4d, 0xaa, 0x5b,
+ 0xb3, 0x16, 0xb7, 0xfe, 0xa0, 0x29, 0x2f, 0x65, 0xa5, 0x4b, 0xac, 0x6a,
+ 0x56, 0x8a, 0xbf, 0x28, 0x70, 0x6a, 0xdf, 0x37, 0x18, 0xdf, 0xec, 0xc6,
+ 0x87, 0x22, 0x3e, 0x16, 0xdd, 0x38, 0x1c, 0x0f, 0xe2, 0x05, 0xbe, 0x77,
+ 0xab, 0xee, 0x85, 0x8a, 0x1e, 0x0d, 0x23, 0x3f, 0xe9, 0x24, 0xe1, 0x90,
+ 0x47, 0xb2, 0x67, 0x3b, 0x15, 0x3e, 0x55, 0x20, 0x3c, 0xf9, 0xf5, 0x42,
+ 0xd7, 0xaf, 0xc5, 0x66, 0x0d, 0x9a, 0x94, 0x17, 0xb9, 0x6a, 0x67, 0x7c,
+ 0xa8, 0xfa, 0xb1, 0x26, 0x00, 0x04, 0x23, 0xd5, 0x4d, 0xa3, 0xae, 0x8d,
+ 0x79, 0x60, 0xb1, 0x8e, 0x83, 0x4f, 0xcd, 0xbb, 0x77, 0x78, 0x27, 0xb2,
+ 0xe8, 0x74, 0xad, 0x1f, 0x4c, 0x34, 0x80, 0x31, 0x7f, 0x8b, 0xe4, 0x50,
+ 0x7e, 0x6f, 0x7f, 0xcf, 0x55, 0xda, 0xc6, 0xfb, 0xb6, 0x5a, 0xf5, 0x5d,
+ 0x1d, 0x38, 0x94, 0xa5, 0xfe, 0xb0, 0x8d, 0x22, 0x63, 0x23, 0xe7, 0x70,
+ 0x40, 0x40, 0xbf, 0x89, 0xaa, 0x54, 0x46, 0x25, 0x03, 0xec, 0xa4, 0xf2,
+ 0xad, 0xc0, 0x40, 0xb3, 0x72, 0xfe, 0x94, 0xb5, 0xd9, 0x96, 0xb2, 0x1b,
+ 0x73, 0x5e, 0xd5, 0xf4, 0x6b, 0x47, 0x41, 0x79, 0xa4, 0xda, 0xf3, 0x8e,
+ 0x2e, 0x8d, 0x38, 0x4d, 0xc9, 0x5e, 0x17, 0x1c, 0xae, 0xb5, 0x4c, 0x36,
+ 0x6b, 0xe8, 0x7f, 0xd9, 0x24, 0xc7, 0x28, 0xf0, 0xa7, 0x86, 0xbe, 0xe8,
+ 0x1d, 0x08, 0xb4, 0xdb, 0x72, 0x23, 0x64, 0xd1, 0x42, 0xf3, 0xf5, 0x4c,
+ 0xda, 0xac, 0xf3, 0xb6, 0xa5, 0x75, 0x68, 0xfc, 0xf0, 0xbb, 0x02, 0x61,
+ 0x5d, 0x1e, 0x7a, 0x07, 0x52, 0x29, 0xbe, 0x74, 0x12, 0x42, 0x53, 0x9c,
+ 0x8d, 0xcc, 0xe9, 0x88, 0x7b, 0xa4, 0x55, 0x36, 0x08, 0x58, 0x8c, 0x21,
+ 0x89, 0xc5, 0xba, 0x13, 0xe8, 0x6d, 0x9b, 0x81, 0x8a, 0x77, 0xc6, 0x38,
+ 0x6c, 0xf5, 0x3a, 0x1d, 0x91, 0x37, 0x57, 0x2b, 0xa9, 0xeb, 0x2b, 0x46,
+ 0x4a, 0xa8, 0x97, 0x28, 0x8b, 0xa3, 0x7e, 0x4a, 0xdd, 0x8d, 0xa7, 0xd3,
+ 0x02, 0x10, 0x7c, 0x96, 0xb0, 0xc8, 0xbc, 0x86, 0x28, 0x06, 0xd6, 0x57,
+ 0xa2, 0xa7, 0x66, 0xf1, 0x17, 0xd6, 0xcc, 0x5f, 0x55, 0x12, 0x3d, 0x59,
+ 0x03, 0xa2, 0xc7, 0xd2, 0xd6, 0x69, 0xb4, 0x0d, 0x25, 0xf9, 0xf1, 0xd3,
+ 0x94, 0x56, 0x16, 0x2e, 0x26, 0xbc, 0x96, 0xf9, 0xba, 0x1c, 0x51, 0x9b,
+ 0x81, 0xf1, 0x37, 0xbc, 0x77, 0xee, 0x7e, 0xd9, 0x9a, 0x78, 0xf6, 0x41,
+ 0xb4, 0x71, 0xdf, 0x10, 0x25, 0x5b, 0xb2, 0xe1, 0x3a, 0xc7, 0x7b, 0xf2,
+ 0x6c, 0xb3, 0x19, 0xb7, 0x20, 0xe7, 0x89, 0xe9, 0xc4, 0xa5, 0x6a, 0xa4,
+ 0x7c, 0x39, 0x24, 0xee, 0xe7, 0x5d, 0x2b, 0x2b, 0xc9, 0x91, 0xfe, 0xa1,
+ 0x3c, 0x33, 0x25, 0xbd, 0xc5, 0x41, 0x14, 0x92, 0xee, 0xcf, 0x56, 0x3a,
+ 0x26, 0x13, 0x75, 0xca, 0x11, 0x5c, 0xc6, 0x27, 0xab, 0x49, 0x88, 0xab,
+ 0x55, 0x72, 0xfc, 0x65, 0x48, 0xdc, 0xae, 0xcd, 0xf1, 0x8f, 0x8b, 0x10,
+ 0x66, 0x2a, 0x90, 0x2e, 0x0b, 0x8b, 0xb7, 0xfe, 0x38, 0x75, 0xcd, 0xb1,
+ 0x75, 0x80, 0xb6, 0x8f, 0xcf, 0x43, 0x1c, 0x21, 0x29, 0x93, 0x0f, 0xe3,
+ 0x13, 0xb9, 0xe2, 0xb0, 0xc8, 0xb2, 0x46, 0x2a, 0x5a, 0x14, 0x0c, 0x1d,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x00, 0x30, 0x81, 0xfd,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x53, 0x06, 0x03,
+ 0x55, 0x1d, 0x20, 0x04, 0x4c, 0x30, 0x4a, 0x30, 0x48, 0x06, 0x09, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00, 0x30, 0x3b, 0x30, 0x39,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2d,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x79, 0x62, 0x65, 0x72,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d, 0x6e, 0x69, 0x72, 0x6f,
+ 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66, 0x6d, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x86, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xe5, 0x9d, 0x59, 0x30, 0x82, 0x47, 0x58, 0xcc, 0xac, 0xfa,
+ 0x08, 0x54, 0x36, 0x86, 0x7b, 0x3a, 0xb5, 0x04, 0x4d, 0xf0, 0x30, 0x42,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0,
+ 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x64, 0x70, 0x31, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x52,
+ 0x4c, 0x2f, 0x4f, 0x6d, 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x32, 0x30,
+ 0x32, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x2a, 0x4d, 0x97, 0x95, 0x5d, 0x34, 0x7e,
+ 0x9d, 0xb6, 0xe6, 0x33, 0xbe, 0x9c, 0x27, 0xc1, 0x70, 0x7e, 0x67, 0xdb,
+ 0xc1, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x23, 0x3e, 0x87,
+ 0x6e, 0xd3, 0xca, 0xd6, 0x0e, 0xb0, 0x36, 0x25, 0x8d, 0xcc, 0x0a, 0xce,
+ 0xdc, 0x5d, 0x74, 0xf3, 0x42, 0x1b, 0xef, 0xaa, 0xc0, 0x49, 0x73, 0x85,
+ 0xcc, 0x7d, 0x2f, 0x77, 0x09, 0x23, 0xc5, 0x2c, 0x50, 0x1d, 0x46, 0xc3,
+ 0x68, 0x68, 0xef, 0x10, 0x32, 0xd9, 0x4d, 0x7e, 0xe3, 0x5a, 0xe3, 0x1d,
+ 0x18, 0x3c, 0xd2, 0x40, 0x54, 0xcd, 0xef, 0x59, 0xee, 0xdf, 0xfe, 0xfb,
+ 0x70, 0x4a, 0xbc, 0xd8, 0x74, 0x37, 0x2b, 0xdc, 0xa4, 0x68, 0x71, 0x50,
+ 0xba, 0x63, 0xcb, 0x44, 0xdd, 0x11, 0x48, 0xff, 0xf8, 0xf1, 0xff, 0x60,
+ 0xba, 0x7c, 0xaa, 0x30, 0x7d, 0xdd, 0x17, 0xb1, 0x77, 0xea, 0x50, 0x90,
+ 0x20, 0x00, 0x0b, 0xa3, 0x3d, 0x5d, 0x98, 0x71, 0x51, 0x9f, 0xdd, 0x2d,
+ 0xc0, 0x78, 0x5e, 0x0d, 0x55, 0xb1, 0x83, 0x45, 0xde, 0xe5, 0x59, 0x98,
+ 0x6d, 0xa4, 0xe1, 0x69, 0x7c, 0x32, 0xd0, 0x04, 0x7b, 0xf7, 0xa9, 0x1d,
+ 0x97, 0x3b, 0xd5, 0x59, 0xbf, 0xcb, 0x6f, 0x9d, 0xa4, 0xb6, 0xa5, 0xbb,
+ 0x41, 0x11, 0xed, 0xc8, 0x91, 0x83, 0x15, 0x55, 0xae, 0x59, 0x36, 0xb7,
+ 0x9f, 0x6a, 0xf0, 0xb8, 0x38, 0xf9, 0x7c, 0x32, 0x25, 0x95, 0xcc, 0x33,
+ 0xf1, 0x31, 0xe7, 0xdf, 0xcb, 0x78, 0x4b, 0x36, 0x1f, 0xf4, 0x55, 0xe0,
+ 0xbd, 0x28, 0xf9, 0xca, 0xb9, 0x64, 0x99, 0xce, 0xeb, 0x61, 0xe9, 0x81,
+ 0x72, 0x94, 0xd3, 0x9b, 0xcd, 0x0a, 0x2b, 0x2a, 0xa4, 0x94, 0x83, 0xae,
+ 0x6a, 0x0c, 0x23, 0x44, 0x0e, 0x35, 0xad, 0xa1, 0xe9, 0xec, 0xd8, 0xd7,
+ 0x75, 0x90, 0x8d, 0xe1, 0xd6, 0xb3, 0xc5, 0x50, 0xfc, 0x5d, 0xd5, 0xfb,
+ 0x6f, 0x92, 0xe1, 0xf4, 0x7e, 0xf0, 0xae, 0xaf, 0xf9, 0x39, 0xb3, 0xce,
+ 0x4b, 0x01, 0x9c, 0xbd, 0x4e, 0xf7, 0xf1, 0xf2, 0x6f, 0xce, 0xc0, 0x36,
+ 0xd8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 20:93:42:da:a7:64:7c:05:c5:f5:fd:93:76:a4:42:8c
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Validity
+ Not Before: Jan 17 00:00:00 2007 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=RU, O=RBC Hosting Center, CN=RBC HC High Assurance Services CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c7:16:e6:21:49:a0:b4:13:ca:18:12:2c:a8:aa:
+ fa:46:9c:d1:5b:5a:1d:26:a3:e6:11:68:3b:02:00:
+ ea:a5:be:95:f7:4c:d7:a1:bc:70:9a:a2:ec:9f:de:
+ d1:b3:1a:c8:16:e0:68:cf:8a:23:3a:6b:1b:59:94:
+ 49:7d:60:cf:51:c6:e3:66:45:f9:6a:5d:f7:88:e9:
+ a1:b1:ab:96:17:3c:46:72:a9:4f:8b:a0:58:cb:bf:
+ c3:9d:91:c4:67:c6:33:ef:fb:16:8b:94:0a:0a:a6:
+ 52:b9:fe:c5:3e:a4:37:c5:ca:ce:b1:bf:a2:db:49:
+ 63:3e:cd:21:75:fa:28:8f:e0:67:4e:32:b2:c3:f3:
+ ea:b2:8b:e5:6a:93:2d:90:dd:19:45:12:f3:ce:16:
+ 7d:1c:ca:b1:93:c0:02:41:97:d0:b7:fc:ac:4f:86:
+ 2c:61:de:cd:ad:ac:4f:19:6b:e2:9e:91:3e:9b:3d:
+ e5:ed:4f:df:d1:10:59:c4:18:f5:25:2c:de:73:ad:
+ 7b:25:31:b5:5d:c5:91:95:f3:6b:9f:fa:d2:b8:ad:
+ 23:7c:9e:45:86:72:aa:09:c0:86:29:e4:75:63:5b:
+ 7d:87:77:d2:89:51:91:27:3a:08:97:25:0d:83:21:
+ 9f:a7:39:fc:43:41:74:c9:ed:c7:00:9d:eb:3c:41:
+ f7:d5
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+
+ X509v3 Subject Key Identifier:
+ 66:8F:F1:9F:4D:DB:8E:DD:FA:EC:1B:99:13:6A:B8:82:68:85:06:ED
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6449.1.2.2.16
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/UTN-USERFirst-Hardware.crl
+
+ Full Name:
+ URI:http://crl.comodo.net/UTN-USERFirst-Hardware.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.comodoca.com/UTNAddTrustServerCA.crt
+ CA Issuers - URI:http://crt.comodo.net/UTNAddTrustServerCA.crt
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 65:2c:c5:19:68:1d:3c:1c:e9:15:9f:a5:b4:fe:32:19:d0:76:
+ f3:b3:f8:58:b5:10:e1:4e:44:f3:c1:e1:b5:98:81:41:69:d7:
+ 89:d4:05:49:e3:15:30:4a:24:c4:1e:67:e7:3d:ab:99:b9:85:
+ 91:04:2f:15:3e:42:a6:e6:e6:25:ac:6e:30:91:3f:03:3f:78:
+ f9:2f:ad:15:fa:e6:ad:1e:57:ea:7b:cc:e4:ce:3a:89:5d:3d:
+ 89:d1:ba:8a:fa:2d:f5:eb:a9:31:95:50:06:f4:34:f3:95:a6:
+ 9b:43:41:20:65:3b:ab:85:d7:e0:53:53:df:54:89:f7:10:c1:
+ 67:79:b0:2e:a2:8b:c8:cd:02:c7:92:5d:e1:4c:e7:d1:87:bb:
+ 8f:82:ee:ce:b7:0c:7c:68:76:da:f3:1f:e5:96:71:e8:77:97:
+ 34:06:13:55:28:81:58:40:75:74:47:28:97:14:0a:2b:53:b0:
+ eb:6e:42:68:76:ef:67:21:61:b6:64:74:08:70:6d:11:c6:e7:
+ 7f:e5:36:1c:66:3a:fd:4e:7a:63:9e:15:ce:cf:a9:04:e6:30:
+ ea:10:d2:5a:81:24:4b:c2:f2:3b:fb:34:e6:69:7b:ff:5d:9b:
+ 30:46:59:69:2d:0d:f2:7f:fc:fb:0a:29:ea:35:14:d0:bb:3c:
+ a3:b1:10:41
+-----BEGIN CERTIFICATE-----
+MIIFAjCCA+qgAwIBAgIQIJNC2qdkfAXF9f2TdqRCjDANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNMDcwMTE3MDAwMDAwWhcNMjAwNTMwMTA0ODM4WjBWMQswCQYD
+VQQGEwJSVTEbMBkGA1UEChMSUkJDIEhvc3RpbmcgQ2VudGVyMSowKAYDVQQDEyFS
+QkMgSEMgSGlnaCBBc3N1cmFuY2UgU2VydmljZXMgQ0EwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDHFuYhSaC0E8oYEiyoqvpGnNFbWh0mo+YRaDsCAOql
+vpX3TNehvHCaouyf3tGzGsgW4GjPiiM6axtZlEl9YM9RxuNmRflqXfeI6aGxq5YX
+PEZyqU+LoFjLv8OdkcRnxjPv+xaLlAoKplK5/sU+pDfFys6xv6LbSWM+zSF1+iiP
+4GdOMrLD8+qyi+Vqky2Q3RlFEvPOFn0cyrGTwAJBl9C3/KxPhixh3s2trE8Za+Ke
+kT6bPeXtT9/REFnEGPUlLN5zrXslMbVdxZGV82uf+tK4rSN8nkWGcqoJwIYp5HVj
+W32Hd9KJUZEnOgiXJQ2DIZ+nOfxDQXTJ7ccAnes8QffVAgMBAAGjggGIMIIBhDAf
+BgNVHSMEGDAWgBShcl8mGyiYQ5VdBzfVhZadS9LDRTAdBgNVHQ4EFgQUZo/xn03b
+jt367BuZE2q4gmiFBu0wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwGAYDVR0gBBEwDzANBgsrBgEEAbIxAQICEDB7BgNVHR8EdDByMDigNqA0hjJo
+dHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
+bDA2oDSgMoYwaHR0cDovL2NybC5jb21vZG8ubmV0L1VUTi1VU0VSRmlyc3QtSGFy
+ZHdhcmUuY3JsMIGGBggrBgEFBQcBAQR6MHgwOwYIKwYBBQUHMAKGL2h0dHA6Ly9j
+cnQuY29tb2RvY2EuY29tL1VUTkFkZFRydXN0U2VydmVyQ0EuY3J0MDkGCCsGAQUF
+BzAChi1odHRwOi8vY3J0LmNvbW9kby5uZXQvVVROQWRkVHJ1c3RTZXJ2ZXJDQS5j
+cnQwDQYJKoZIhvcNAQEFBQADggEBAGUsxRloHTwc6RWfpbT+MhnQdvOz+Fi1EOFO
+RPPB4bWYgUFp14nUBUnjFTBKJMQeZ+c9q5m5hZEELxU+Qqbm5iWsbjCRPwM/ePkv
+rRX65q0eV+p7zOTOOoldPYnRuor6LfXrqTGVUAb0NPOVpptDQSBlO6uF1+BTU99U
+ifcQwWd5sC6ii8jNAseSXeFM59GHu4+C7s63DHxodtrzH+WWceh3lzQGE1UogVhA
+dXRHKJcUCitTsOtuQmh272chYbZkdAhwbRHG53/lNhxmOv1OemOeFc7PqQTmMOoQ
+0lqBJEvC8jv7NOZpe/9dmzBGWWktDfJ//PsKKeo1FNC7PKOxEEE=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert72[] = {
+ 0x30, 0x82, 0x05, 0x02, 0x30, 0x82, 0x03, 0xea, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x20, 0x93, 0x42, 0xda, 0xa7, 0x64, 0x7c, 0x05, 0xc5,
+ 0xf5, 0xfd, 0x93, 0x76, 0xa4, 0x42, 0x8c, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20,
+ 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54,
+ 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54,
+ 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d,
+ 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x37, 0x30, 0x31, 0x31, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34,
+ 0x38, 0x33, 0x38, 0x5a, 0x30, 0x56, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x52, 0x55, 0x31, 0x1b, 0x30, 0x19, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x12, 0x52, 0x42, 0x43, 0x20, 0x48, 0x6f,
+ 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72,
+ 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x52,
+ 0x42, 0x43, 0x20, 0x48, 0x43, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41,
+ 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xc7, 0x16, 0xe6, 0x21, 0x49, 0xa0, 0xb4,
+ 0x13, 0xca, 0x18, 0x12, 0x2c, 0xa8, 0xaa, 0xfa, 0x46, 0x9c, 0xd1, 0x5b,
+ 0x5a, 0x1d, 0x26, 0xa3, 0xe6, 0x11, 0x68, 0x3b, 0x02, 0x00, 0xea, 0xa5,
+ 0xbe, 0x95, 0xf7, 0x4c, 0xd7, 0xa1, 0xbc, 0x70, 0x9a, 0xa2, 0xec, 0x9f,
+ 0xde, 0xd1, 0xb3, 0x1a, 0xc8, 0x16, 0xe0, 0x68, 0xcf, 0x8a, 0x23, 0x3a,
+ 0x6b, 0x1b, 0x59, 0x94, 0x49, 0x7d, 0x60, 0xcf, 0x51, 0xc6, 0xe3, 0x66,
+ 0x45, 0xf9, 0x6a, 0x5d, 0xf7, 0x88, 0xe9, 0xa1, 0xb1, 0xab, 0x96, 0x17,
+ 0x3c, 0x46, 0x72, 0xa9, 0x4f, 0x8b, 0xa0, 0x58, 0xcb, 0xbf, 0xc3, 0x9d,
+ 0x91, 0xc4, 0x67, 0xc6, 0x33, 0xef, 0xfb, 0x16, 0x8b, 0x94, 0x0a, 0x0a,
+ 0xa6, 0x52, 0xb9, 0xfe, 0xc5, 0x3e, 0xa4, 0x37, 0xc5, 0xca, 0xce, 0xb1,
+ 0xbf, 0xa2, 0xdb, 0x49, 0x63, 0x3e, 0xcd, 0x21, 0x75, 0xfa, 0x28, 0x8f,
+ 0xe0, 0x67, 0x4e, 0x32, 0xb2, 0xc3, 0xf3, 0xea, 0xb2, 0x8b, 0xe5, 0x6a,
+ 0x93, 0x2d, 0x90, 0xdd, 0x19, 0x45, 0x12, 0xf3, 0xce, 0x16, 0x7d, 0x1c,
+ 0xca, 0xb1, 0x93, 0xc0, 0x02, 0x41, 0x97, 0xd0, 0xb7, 0xfc, 0xac, 0x4f,
+ 0x86, 0x2c, 0x61, 0xde, 0xcd, 0xad, 0xac, 0x4f, 0x19, 0x6b, 0xe2, 0x9e,
+ 0x91, 0x3e, 0x9b, 0x3d, 0xe5, 0xed, 0x4f, 0xdf, 0xd1, 0x10, 0x59, 0xc4,
+ 0x18, 0xf5, 0x25, 0x2c, 0xde, 0x73, 0xad, 0x7b, 0x25, 0x31, 0xb5, 0x5d,
+ 0xc5, 0x91, 0x95, 0xf3, 0x6b, 0x9f, 0xfa, 0xd2, 0xb8, 0xad, 0x23, 0x7c,
+ 0x9e, 0x45, 0x86, 0x72, 0xaa, 0x09, 0xc0, 0x86, 0x29, 0xe4, 0x75, 0x63,
+ 0x5b, 0x7d, 0x87, 0x77, 0xd2, 0x89, 0x51, 0x91, 0x27, 0x3a, 0x08, 0x97,
+ 0x25, 0x0d, 0x83, 0x21, 0x9f, 0xa7, 0x39, 0xfc, 0x43, 0x41, 0x74, 0xc9,
+ 0xed, 0xc7, 0x00, 0x9d, 0xeb, 0x3c, 0x41, 0xf7, 0xd5, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x88, 0x30, 0x82, 0x01, 0x84, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xa1,
+ 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98, 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5,
+ 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3, 0x45, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x66, 0x8f, 0xf1, 0x9f, 0x4d, 0xdb,
+ 0x8e, 0xdd, 0xfa, 0xec, 0x1b, 0x99, 0x13, 0x6a, 0xb8, 0x82, 0x68, 0x85,
+ 0x06, 0xed, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02,
+ 0x01, 0x00, 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x11, 0x30,
+ 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb2, 0x31,
+ 0x01, 0x02, 0x02, 0x10, 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x74, 0x30, 0x72, 0x30, 0x38, 0xa0, 0x36, 0xa0, 0x34, 0x86, 0x32, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55,
+ 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74,
+ 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x36, 0xa0, 0x34, 0xa0, 0x32, 0x86, 0x30, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f,
+ 0x64, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x55, 0x54, 0x4e, 0x2d, 0x55,
+ 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72,
+ 0x64, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x86,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x7a,
+ 0x30, 0x78, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x55, 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x55, 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x65, 0x2c,
+ 0xc5, 0x19, 0x68, 0x1d, 0x3c, 0x1c, 0xe9, 0x15, 0x9f, 0xa5, 0xb4, 0xfe,
+ 0x32, 0x19, 0xd0, 0x76, 0xf3, 0xb3, 0xf8, 0x58, 0xb5, 0x10, 0xe1, 0x4e,
+ 0x44, 0xf3, 0xc1, 0xe1, 0xb5, 0x98, 0x81, 0x41, 0x69, 0xd7, 0x89, 0xd4,
+ 0x05, 0x49, 0xe3, 0x15, 0x30, 0x4a, 0x24, 0xc4, 0x1e, 0x67, 0xe7, 0x3d,
+ 0xab, 0x99, 0xb9, 0x85, 0x91, 0x04, 0x2f, 0x15, 0x3e, 0x42, 0xa6, 0xe6,
+ 0xe6, 0x25, 0xac, 0x6e, 0x30, 0x91, 0x3f, 0x03, 0x3f, 0x78, 0xf9, 0x2f,
+ 0xad, 0x15, 0xfa, 0xe6, 0xad, 0x1e, 0x57, 0xea, 0x7b, 0xcc, 0xe4, 0xce,
+ 0x3a, 0x89, 0x5d, 0x3d, 0x89, 0xd1, 0xba, 0x8a, 0xfa, 0x2d, 0xf5, 0xeb,
+ 0xa9, 0x31, 0x95, 0x50, 0x06, 0xf4, 0x34, 0xf3, 0x95, 0xa6, 0x9b, 0x43,
+ 0x41, 0x20, 0x65, 0x3b, 0xab, 0x85, 0xd7, 0xe0, 0x53, 0x53, 0xdf, 0x54,
+ 0x89, 0xf7, 0x10, 0xc1, 0x67, 0x79, 0xb0, 0x2e, 0xa2, 0x8b, 0xc8, 0xcd,
+ 0x02, 0xc7, 0x92, 0x5d, 0xe1, 0x4c, 0xe7, 0xd1, 0x87, 0xbb, 0x8f, 0x82,
+ 0xee, 0xce, 0xb7, 0x0c, 0x7c, 0x68, 0x76, 0xda, 0xf3, 0x1f, 0xe5, 0x96,
+ 0x71, 0xe8, 0x77, 0x97, 0x34, 0x06, 0x13, 0x55, 0x28, 0x81, 0x58, 0x40,
+ 0x75, 0x74, 0x47, 0x28, 0x97, 0x14, 0x0a, 0x2b, 0x53, 0xb0, 0xeb, 0x6e,
+ 0x42, 0x68, 0x76, 0xef, 0x67, 0x21, 0x61, 0xb6, 0x64, 0x74, 0x08, 0x70,
+ 0x6d, 0x11, 0xc6, 0xe7, 0x7f, 0xe5, 0x36, 0x1c, 0x66, 0x3a, 0xfd, 0x4e,
+ 0x7a, 0x63, 0x9e, 0x15, 0xce, 0xcf, 0xa9, 0x04, 0xe6, 0x30, 0xea, 0x10,
+ 0xd2, 0x5a, 0x81, 0x24, 0x4b, 0xc2, 0xf2, 0x3b, 0xfb, 0x34, 0xe6, 0x69,
+ 0x7b, 0xff, 0x5d, 0x9b, 0x30, 0x46, 0x59, 0x69, 0x2d, 0x0d, 0xf2, 0x7f,
+ 0xfc, 0xfb, 0x0a, 0x29, 0xea, 0x35, 0x14, 0xd0, 0xbb, 0x3c, 0xa3, 0xb1,
+ 0x10, 0x41,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 18:b2:cb:ba:a3:04:f1:a0:0f:c1:f2:f3:26:46:2a:4a
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Validity
+ Not Before: Dec 1 00:00:00 2006 GMT
+ Not After : Dec 31 23:59:59 2019 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=EssentialSSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ad:f0:08:b0:72:c6:ab:83:12:31:17:70:89:85:
+ a9:20:12:d4:98:6a:ed:80:d4:d1:df:e4:8e:59:2d:
+ d3:96:21:8d:76:d2:3f:18:0b:46:19:63:0b:c7:20:
+ f3:e5:0b:dd:80:1a:f1:5a:a0:bd:1d:76:cd:b7:23:
+ 3a:74:5e:61:1b:75:aa:9b:d4:85:f4:e1:78:91:d3:
+ 2d:e1:af:fc:98:2e:06:d2:79:3d:5a:c0:1f:21:2d:
+ 1c:ae:21:53:c6:3a:a7:21:7e:be:ed:67:6f:75:1d:
+ 1a:9f:6a:5b:06:b3:6a:e3:b1:0b:aa:6a:0e:e7:6d:
+ 6c:c3:ca:95:8c:37:ce:21:1f:35:90:7d:db:da:1a:
+ 5c:a8:88:14:b2:0f:c8:12:20:5f:c5:d3:7f:e8:e1:
+ 38:e0:db:bc:f9:1f:a1:aa:d6:1b:90:07:21:fa:45:
+ 24:50:5d:27:2a:a0:28:41:45:5b:7d:bc:a0:a2:2f:
+ aa:9b:7e:5b:53:c5:f1:05:16:57:7e:11:d7:3b:b4:
+ d9:01:76:dc:df:7d:10:cf:51:a9:e5:38:f2:7b:14:
+ 00:75:59:f9:f0:59:db:17:3e:f7:af:e6:02:2d:a4:
+ 79:c1:5d:a2:1c:c3:9a:c8:a7:a8:0b:48:0a:6a:2e:
+ 7f:2d:97:65:f6:c5:04:9c:44:c8:99:96:7e:7e:a4:
+ dd:2f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+
+ X509v3 Subject Key Identifier:
+ DA:CB:EA:AD:5B:08:5D:CC:FF:FC:26:54:CE:49:E5:55:C6:38:F4:F8
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Extended Key Usage:
+ Microsoft Server Gated Crypto, Netscape Server Gated Crypto
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://secure.comodo.com/CPS
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/COMODOCertificationAuthority.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.comodoca.com/ComodoUTNSGCCA.crt
+ OCSP - URI:http://ocsp.comodoca.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 2d:97:34:7a:40:32:ea:70:97:2f:81:3b:4b:79:12:77:ae:fb:
+ aa:d7:1a:a8:da:5f:f3:a1:db:9e:4d:96:cb:37:7a:a8:ea:ee:
+ 9b:95:db:9d:bb:e1:27:9e:fd:45:ed:0e:52:96:ac:f4:27:bf:
+ 74:aa:92:f4:a5:c4:43:00:1f:0e:b5:78:f9:8a:c5:8c:70:bd:
+ 9a:7a:31:a3:29:d0:59:6b:4c:33:b5:2c:f8:8b:0f:92:63:57:
+ 56:ac:24:67:8a:5b:2f:29:c2:b1:b9:da:24:c5:e4:62:0e:7e:
+ 79:c3:fe:b9:83:ea:27:3b:bc:1d:43:b5:6e:17:aa:fb:c8:98:
+ 88:6a:d9:f2:7c:a1:f6:71:ba:19:4f:b8:38:e3:42:d7:f0:da:
+ b1:c0:23:df:dd:d7:f1:a7:ed:09:8f:56:a0:ab:c3:0b:cb:a4:
+ 92:80:81:92:1f:a9:6f:f9:6c:33:dc:3e:57:c6:a7:f2:1f:cc:
+ 2a:7c:e4:2c:4c:46:5f:eb:f3:61:f7:2b:c4:35:9f:8d:58:f5:
+ 3a:83:44:0e:d8:93:ac:4c:6b:cc:77:f4:03:cd:cc:dc:e0:1c:
+ 4b:5d:25:da:3d:5e:ce:77:8a:e1:3e:c6:d7:94:cd:70:49:3c:
+ ff:0e:bd:08:48:ab:e5:52:14:15:9d:0e:9c:1a:87:56:68:ad:
+ 9c:09:00:64
+-----BEGIN CERTIFICATE-----
+MIIFAzCCA+ugAwIBAgIQGLLLuqME8aAPwfLzJkYqSjANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0xOTEyMzEyMzU5NTlaMHIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh
+dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
+TyBDQSBMaW1pdGVkMRgwFgYDVQQDEw9Fc3NlbnRpYWxTU0wgQ0EwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCt8AiwcsargxIxF3CJhakgEtSYau2A1NHf
+5I5ZLdOWIY120j8YC0YZYwvHIPPlC92AGvFaoL0dds23Izp0XmEbdaqb1IX04XiR
+0y3hr/yYLgbSeT1awB8hLRyuIVPGOqchfr7tZ291HRqfalsGs2rjsQuqag7nbWzD
+ypWMN84hHzWQfdvaGlyoiBSyD8gSIF/F03/o4Tjg27z5H6Gq1huQByH6RSRQXScq
+oChBRVt9vKCiL6qbfltTxfEFFld+Edc7tNkBdtzffRDPUanlOPJ7FAB1WfnwWdsX
+Pvev5gItpHnBXaIcw5rIp6gLSApqLn8tl2X2xQScRMiZln5+pN0vAgMBAAGjggGD
+MIIBfzAfBgNVHSMEGDAWgBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQU
+2svqrVsIXcz//CZUzknlVcY49PgwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwIAYDVR0lBBkwFwYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMD4GA1Ud
+IAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21v
+ZG8uY29tL0NQUzBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9kb2Nh
+LmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBsBggrBgEFBQcB
+AQRgMF4wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NvbW9k
+b1VUTlNHQ0NBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2Eu
+Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQAtlzR6QDLqcJcvgTtLeRJ3rvuq1xqo2l/z
+odueTZbLN3qo6u6bldudu+Ennv1F7Q5Slqz0J790qpL0pcRDAB8OtXj5isWMcL2a
+ejGjKdBZa0wztSz4iw+SY1dWrCRnilsvKcKxudokxeRiDn55w/65g+onO7wdQ7Vu
+F6r7yJiIatnyfKH2cboZT7g440LX8NqxwCPf3dfxp+0Jj1agq8MLy6SSgIGSH6lv
++Wwz3D5XxqfyH8wqfOQsTEZf6/Nh9yvENZ+NWPU6g0QO2JOsTGvMd/QDzczc4BxL
+XSXaPV7Od4rhPsbXlM1wSTz/Dr0ISKvlUhQVnQ6cGodWaK2cCQBk
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert73[] = {
+ 0x30, 0x82, 0x05, 0x03, 0x30, 0x82, 0x03, 0xeb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x18, 0xb2, 0xcb, 0xba, 0xa3, 0x04, 0xf1, 0xa0, 0x0f,
+ 0xc1, 0xf2, 0xf3, 0x26, 0x46, 0x2a, 0x4a, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x36, 0x31, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32,
+ 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x72, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30,
+ 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x47, 0x72, 0x65, 0x61,
+ 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74,
+ 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
+ 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, 0x4f, 0x4d, 0x4f, 0x44,
+ 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, 0x45,
+ 0x73, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x53, 0x4c, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xad,
+ 0xf0, 0x08, 0xb0, 0x72, 0xc6, 0xab, 0x83, 0x12, 0x31, 0x17, 0x70, 0x89,
+ 0x85, 0xa9, 0x20, 0x12, 0xd4, 0x98, 0x6a, 0xed, 0x80, 0xd4, 0xd1, 0xdf,
+ 0xe4, 0x8e, 0x59, 0x2d, 0xd3, 0x96, 0x21, 0x8d, 0x76, 0xd2, 0x3f, 0x18,
+ 0x0b, 0x46, 0x19, 0x63, 0x0b, 0xc7, 0x20, 0xf3, 0xe5, 0x0b, 0xdd, 0x80,
+ 0x1a, 0xf1, 0x5a, 0xa0, 0xbd, 0x1d, 0x76, 0xcd, 0xb7, 0x23, 0x3a, 0x74,
+ 0x5e, 0x61, 0x1b, 0x75, 0xaa, 0x9b, 0xd4, 0x85, 0xf4, 0xe1, 0x78, 0x91,
+ 0xd3, 0x2d, 0xe1, 0xaf, 0xfc, 0x98, 0x2e, 0x06, 0xd2, 0x79, 0x3d, 0x5a,
+ 0xc0, 0x1f, 0x21, 0x2d, 0x1c, 0xae, 0x21, 0x53, 0xc6, 0x3a, 0xa7, 0x21,
+ 0x7e, 0xbe, 0xed, 0x67, 0x6f, 0x75, 0x1d, 0x1a, 0x9f, 0x6a, 0x5b, 0x06,
+ 0xb3, 0x6a, 0xe3, 0xb1, 0x0b, 0xaa, 0x6a, 0x0e, 0xe7, 0x6d, 0x6c, 0xc3,
+ 0xca, 0x95, 0x8c, 0x37, 0xce, 0x21, 0x1f, 0x35, 0x90, 0x7d, 0xdb, 0xda,
+ 0x1a, 0x5c, 0xa8, 0x88, 0x14, 0xb2, 0x0f, 0xc8, 0x12, 0x20, 0x5f, 0xc5,
+ 0xd3, 0x7f, 0xe8, 0xe1, 0x38, 0xe0, 0xdb, 0xbc, 0xf9, 0x1f, 0xa1, 0xaa,
+ 0xd6, 0x1b, 0x90, 0x07, 0x21, 0xfa, 0x45, 0x24, 0x50, 0x5d, 0x27, 0x2a,
+ 0xa0, 0x28, 0x41, 0x45, 0x5b, 0x7d, 0xbc, 0xa0, 0xa2, 0x2f, 0xaa, 0x9b,
+ 0x7e, 0x5b, 0x53, 0xc5, 0xf1, 0x05, 0x16, 0x57, 0x7e, 0x11, 0xd7, 0x3b,
+ 0xb4, 0xd9, 0x01, 0x76, 0xdc, 0xdf, 0x7d, 0x10, 0xcf, 0x51, 0xa9, 0xe5,
+ 0x38, 0xf2, 0x7b, 0x14, 0x00, 0x75, 0x59, 0xf9, 0xf0, 0x59, 0xdb, 0x17,
+ 0x3e, 0xf7, 0xaf, 0xe6, 0x02, 0x2d, 0xa4, 0x79, 0xc1, 0x5d, 0xa2, 0x1c,
+ 0xc3, 0x9a, 0xc8, 0xa7, 0xa8, 0x0b, 0x48, 0x0a, 0x6a, 0x2e, 0x7f, 0x2d,
+ 0x97, 0x65, 0xf6, 0xc5, 0x04, 0x9c, 0x44, 0xc8, 0x99, 0x96, 0x7e, 0x7e,
+ 0xa4, 0xdd, 0x2f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x83,
+ 0x30, 0x82, 0x01, 0x7f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x0b, 0x58, 0xe5, 0x8b, 0xc6, 0x4c, 0x15,
+ 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21, 0xbe, 0x47, 0x36, 0x5a, 0x56,
+ 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0xda, 0xcb, 0xea, 0xad, 0x5b, 0x08, 0x5d, 0xcc, 0xff, 0xfc, 0x26, 0x54,
+ 0xce, 0x49, 0xe5, 0x55, 0xc6, 0x38, 0xf4, 0xf8, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x19, 0x30, 0x17, 0x06, 0x0a, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x37, 0x30, 0x35, 0x30, 0x33, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6f,
+ 0x64, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x49,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x42, 0x30, 0x40, 0x30, 0x3e, 0xa0,
+ 0x3c, 0xa0, 0x3a, 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x6c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x60, 0x30, 0x5e, 0x30, 0x36, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f,
+ 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64,
+ 0x6f, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72,
+ 0x74, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x2d,
+ 0x97, 0x34, 0x7a, 0x40, 0x32, 0xea, 0x70, 0x97, 0x2f, 0x81, 0x3b, 0x4b,
+ 0x79, 0x12, 0x77, 0xae, 0xfb, 0xaa, 0xd7, 0x1a, 0xa8, 0xda, 0x5f, 0xf3,
+ 0xa1, 0xdb, 0x9e, 0x4d, 0x96, 0xcb, 0x37, 0x7a, 0xa8, 0xea, 0xee, 0x9b,
+ 0x95, 0xdb, 0x9d, 0xbb, 0xe1, 0x27, 0x9e, 0xfd, 0x45, 0xed, 0x0e, 0x52,
+ 0x96, 0xac, 0xf4, 0x27, 0xbf, 0x74, 0xaa, 0x92, 0xf4, 0xa5, 0xc4, 0x43,
+ 0x00, 0x1f, 0x0e, 0xb5, 0x78, 0xf9, 0x8a, 0xc5, 0x8c, 0x70, 0xbd, 0x9a,
+ 0x7a, 0x31, 0xa3, 0x29, 0xd0, 0x59, 0x6b, 0x4c, 0x33, 0xb5, 0x2c, 0xf8,
+ 0x8b, 0x0f, 0x92, 0x63, 0x57, 0x56, 0xac, 0x24, 0x67, 0x8a, 0x5b, 0x2f,
+ 0x29, 0xc2, 0xb1, 0xb9, 0xda, 0x24, 0xc5, 0xe4, 0x62, 0x0e, 0x7e, 0x79,
+ 0xc3, 0xfe, 0xb9, 0x83, 0xea, 0x27, 0x3b, 0xbc, 0x1d, 0x43, 0xb5, 0x6e,
+ 0x17, 0xaa, 0xfb, 0xc8, 0x98, 0x88, 0x6a, 0xd9, 0xf2, 0x7c, 0xa1, 0xf6,
+ 0x71, 0xba, 0x19, 0x4f, 0xb8, 0x38, 0xe3, 0x42, 0xd7, 0xf0, 0xda, 0xb1,
+ 0xc0, 0x23, 0xdf, 0xdd, 0xd7, 0xf1, 0xa7, 0xed, 0x09, 0x8f, 0x56, 0xa0,
+ 0xab, 0xc3, 0x0b, 0xcb, 0xa4, 0x92, 0x80, 0x81, 0x92, 0x1f, 0xa9, 0x6f,
+ 0xf9, 0x6c, 0x33, 0xdc, 0x3e, 0x57, 0xc6, 0xa7, 0xf2, 0x1f, 0xcc, 0x2a,
+ 0x7c, 0xe4, 0x2c, 0x4c, 0x46, 0x5f, 0xeb, 0xf3, 0x61, 0xf7, 0x2b, 0xc4,
+ 0x35, 0x9f, 0x8d, 0x58, 0xf5, 0x3a, 0x83, 0x44, 0x0e, 0xd8, 0x93, 0xac,
+ 0x4c, 0x6b, 0xcc, 0x77, 0xf4, 0x03, 0xcd, 0xcc, 0xdc, 0xe0, 0x1c, 0x4b,
+ 0x5d, 0x25, 0xda, 0x3d, 0x5e, 0xce, 0x77, 0x8a, 0xe1, 0x3e, 0xc6, 0xd7,
+ 0x94, 0xcd, 0x70, 0x49, 0x3c, 0xff, 0x0e, 0xbd, 0x08, 0x48, 0xab, 0xe5,
+ 0x52, 0x14, 0x15, 0x9d, 0x0e, 0x9c, 0x1a, 0x87, 0x56, 0x68, 0xad, 0x9c,
+ 0x09, 0x00, 0x64,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4c:cd:4a:9a:5b:45:13:21:8c:cf:90:2f:8b:2b:51:71
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, ST=UT, L=Salt Lake City, O=The USERTRUST Network, OU=http://www.usertrust.com, CN=UTN-USERFirst-Hardware
+ Validity
+ Not Before: Sep 18 00:00:00 2006 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=Comodo CA Limited, CN=PositiveSSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:4f:79:58:22:93:c9:28:3e:52:11:00:2f:c0:
+ a9:20:8a:d7:2d:55:1e:10:e7:b8:7f:e2:86:2a:a4:
+ ec:5e:e9:e4:92:58:96:54:0d:f3:17:ba:41:9e:15:
+ 91:55:ef:c2:ee:00:20:18:45:83:26:df:20:cc:3d:
+ b3:a1:13:31:0a:21:5c:79:83:79:ab:24:15:5c:56:
+ f0:b4:95:98:a1:da:d2:1b:ea:16:b5:cb:b7:b0:c1:
+ 53:f9:a4:46:da:f0:2e:24:b8:62:9e:8e:8b:5e:2b:
+ 5a:96:a0:ea:50:e9:88:fb:28:2a:4d:9a:9c:48:4f:
+ 83:b6:87:ae:4f:c5:c8:b5:d9:fd:be:3f:d1:a7:9d:
+ c6:2c:13:0d:c0:01:c7:b3:70:f3:8f:69:bb:b0:3c:
+ 10:df:eb:09:00:84:3f:6e:ef:fc:e3:2d:b4:c7:5d:
+ 11:cc:f7:f2:f1:f6:e2:e3:00:7e:12:0e:de:8d:7d:
+ 00:ca:3a:3d:f6:72:e8:79:25:a6:08:16:f7:ab:88:
+ ff:56:5e:09:17:c0:5a:82:05:62:34:2b:28:48:32:
+ 97:d0:84:0c:ac:13:18:db:9e:66:c7:aa:14:8b:11:
+ 69:4d:f1:09:d3:ba:5d:a9:88:37:62:d8:bf:03:9b:
+ 9d:d7:e6:05:7e:c2:6a:52:aa:8d:07:80:ea:8e:0e:
+ f3:1d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:A1:72:5F:26:1B:28:98:43:95:5D:07:37:D5:85:96:9D:4B:D2:C3:45
+
+ X509v3 Subject Key Identifier:
+ B8:CA:11:E9:06:31:79:DB:C3:94:C6:E8:19:2A:BC:BB:35:16:31:A4
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/UTN-USERFirst-Hardware.crl
+
+ Full Name:
+ URI:http://crl.comodo.net/UTN-USERFirst-Hardware.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.comodoca.com/UTNAddTrustServerCA.crt
+ CA Issuers - URI:http://crt.comodo.net/UTNAddTrustServerCA.crt
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 1d:b4:e7:f9:18:48:5d:ed:fa:5a:c3:1d:e3:b7:ef:86:15:c9:
+ 6c:13:49:16:0d:31:8c:31:5c:e7:da:87:64:8e:af:12:16:a4:
+ 5a:2d:92:1a:3b:3e:21:65:aa:17:c3:95:7e:40:1c:fd:14:69:
+ 06:18:cd:ac:31:ec:6a:f1:16:9e:89:26:3d:5b:21:8d:e8:e8:
+ f9:ab:3d:aa:3c:cb:99:f2:86:5f:88:53:15:05:17:e5:a8:d2:
+ 85:a7:4e:49:7d:d6:dd:c7:8f:ce:88:bf:65:05:e0:14:b5:31:
+ 77:ee:bd:2a:86:e8:e8:a7:6a:2d:da:65:19:8c:67:e2:6d:f6:
+ 4a:85:66:83:b6:32:4d:9f:42:23:17:d7:45:41:6a:76:04:d4:
+ ad:b9:8f:6e:dc:c2:3e:e9:51:f2:9e:d8:f3:7f:fb:50:2a:f0:
+ 8b:fd:6f:0d:22:36:2e:ce:0e:46:f2:de:8f:da:3b:7d:1e:93:
+ ac:fc:f4:32:2b:0f:ab:01:1f:a5:40:8f:e3:24:99:1f:5d:b2:
+ aa:0c:b9:e2:a1:e7:92:0c:90:4b:53:7d:1f:28:ee:56:3d:af:
+ 18:67:49:df:d5:1e:c2:a8:98:2b:4c:47:83:81:4c:2e:44:c2:
+ ef:c8:63:ee:8b:7b:5b:31:f6:26:61:bf:79:1c:b0:a9:4e:a9:
+ c9:50:7b:e2
+-----BEGIN CERTIFICATE-----
+MIIFAzCCA+ugAwIBAgIQTM1KmltFEyGMz5AviytRcTANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNMDYwOTE4MDAwMDAwWhcNMjAwNTMwMTA0ODM4WjBxMQswCQYD
+VQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdT
+YWxmb3JkMRowGAYDVQQKExFDb21vZG8gQ0EgTGltaXRlZDEXMBUGA1UEAxMOUG9z
+aXRpdmVTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9T3lY
+IpPJKD5SEQAvwKkgitctVR4Q57h/4oYqpOxe6eSSWJZUDfMXukGeFZFV78LuACAY
+RYMm3yDMPbOhEzEKIVx5g3mrJBVcVvC0lZih2tIb6ha1y7ewwVP5pEba8C4kuGKe
+joteK1qWoOpQ6Yj7KCpNmpxIT4O2h65Pxci12f2+P9GnncYsEw3AAcezcPOPabuw
+PBDf6wkAhD9u7/zjLbTHXRHM9/Lx9uLjAH4SDt6NfQDKOj32cuh5JaYIFveriP9W
+XgkXwFqCBWI0KyhIMpfQhAysExjbnmbHqhSLEWlN8QnTul2piDdi2L8Dm53X5gV+
+wmpSqo0HgOqODvMdAgMBAAGjggFuMIIBajAfBgNVHSMEGDAWgBShcl8mGyiYQ5Vd
+BzfVhZadS9LDRTAdBgNVHQ4EFgQUuMoR6QYxedvDlMboGSq8uzUWMaQwDgYDVR0P
+AQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwewYDVR0fBHQwcjA4oDagNIYy
+aHR0cDovL2NybC5jb21vZG9jYS5jb20vVVROLVVTRVJGaXJzdC1IYXJkd2FyZS5j
+cmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9VVE4tVVNFUkZpcnN0LUhh
+cmR3YXJlLmNybDCBhgYIKwYBBQUHAQEEejB4MDsGCCsGAQUFBzAChi9odHRwOi8v
+Y3J0LmNvbW9kb2NhLmNvbS9VVE5BZGRUcnVzdFNlcnZlckNBLmNydDA5BggrBgEF
+BQcwAoYtaHR0cDovL2NydC5jb21vZG8ubmV0L1VUTkFkZFRydXN0U2VydmVyQ0Eu
+Y3J0MA0GCSqGSIb3DQEBBQUAA4IBAQAdtOf5GEhd7fpawx3jt++GFclsE0kWDTGM
+MVzn2odkjq8SFqRaLZIaOz4hZaoXw5V+QBz9FGkGGM2sMexq8RaeiSY9WyGN6Oj5
+qz2qPMuZ8oZfiFMVBRflqNKFp05Jfdbdx4/OiL9lBeAUtTF37r0qhujop2ot2mUZ
+jGfibfZKhWaDtjJNn0IjF9dFQWp2BNStuY9u3MI+6VHyntjzf/tQKvCL/W8NIjYu
+zg5G8t6P2jt9HpOs/PQyKw+rAR+lQI/jJJkfXbKqDLnioeeSDJBLU30fKO5WPa8Y
+Z0nf1R7CqJgrTEeDgUwuRMLvyGPui3tbMfYmYb95HLCpTqnJUHvi
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert74[] = {
+ 0x30, 0x82, 0x05, 0x03, 0x30, 0x82, 0x03, 0xeb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x4c, 0xcd, 0x4a, 0x9a, 0x5b, 0x45, 0x13, 0x21, 0x8c,
+ 0xcf, 0x90, 0x2f, 0x8b, 0x2b, 0x51, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x97, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x02, 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, 0x6b, 0x65, 0x20,
+ 0x43, 0x69, 0x74, 0x79, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54,
+ 0x52, 0x55, 0x53, 0x54, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73,
+ 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x31,
+ 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54,
+ 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d,
+ 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d,
+ 0x30, 0x36, 0x30, 0x39, 0x31, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34,
+ 0x38, 0x33, 0x38, 0x5a, 0x30, 0x71, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53,
+ 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03,
+ 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x20,
+ 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x17,
+ 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0e, 0x50, 0x6f, 0x73,
+ 0x69, 0x74, 0x69, 0x76, 0x65, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0x4f, 0x79, 0x58,
+ 0x22, 0x93, 0xc9, 0x28, 0x3e, 0x52, 0x11, 0x00, 0x2f, 0xc0, 0xa9, 0x20,
+ 0x8a, 0xd7, 0x2d, 0x55, 0x1e, 0x10, 0xe7, 0xb8, 0x7f, 0xe2, 0x86, 0x2a,
+ 0xa4, 0xec, 0x5e, 0xe9, 0xe4, 0x92, 0x58, 0x96, 0x54, 0x0d, 0xf3, 0x17,
+ 0xba, 0x41, 0x9e, 0x15, 0x91, 0x55, 0xef, 0xc2, 0xee, 0x00, 0x20, 0x18,
+ 0x45, 0x83, 0x26, 0xdf, 0x20, 0xcc, 0x3d, 0xb3, 0xa1, 0x13, 0x31, 0x0a,
+ 0x21, 0x5c, 0x79, 0x83, 0x79, 0xab, 0x24, 0x15, 0x5c, 0x56, 0xf0, 0xb4,
+ 0x95, 0x98, 0xa1, 0xda, 0xd2, 0x1b, 0xea, 0x16, 0xb5, 0xcb, 0xb7, 0xb0,
+ 0xc1, 0x53, 0xf9, 0xa4, 0x46, 0xda, 0xf0, 0x2e, 0x24, 0xb8, 0x62, 0x9e,
+ 0x8e, 0x8b, 0x5e, 0x2b, 0x5a, 0x96, 0xa0, 0xea, 0x50, 0xe9, 0x88, 0xfb,
+ 0x28, 0x2a, 0x4d, 0x9a, 0x9c, 0x48, 0x4f, 0x83, 0xb6, 0x87, 0xae, 0x4f,
+ 0xc5, 0xc8, 0xb5, 0xd9, 0xfd, 0xbe, 0x3f, 0xd1, 0xa7, 0x9d, 0xc6, 0x2c,
+ 0x13, 0x0d, 0xc0, 0x01, 0xc7, 0xb3, 0x70, 0xf3, 0x8f, 0x69, 0xbb, 0xb0,
+ 0x3c, 0x10, 0xdf, 0xeb, 0x09, 0x00, 0x84, 0x3f, 0x6e, 0xef, 0xfc, 0xe3,
+ 0x2d, 0xb4, 0xc7, 0x5d, 0x11, 0xcc, 0xf7, 0xf2, 0xf1, 0xf6, 0xe2, 0xe3,
+ 0x00, 0x7e, 0x12, 0x0e, 0xde, 0x8d, 0x7d, 0x00, 0xca, 0x3a, 0x3d, 0xf6,
+ 0x72, 0xe8, 0x79, 0x25, 0xa6, 0x08, 0x16, 0xf7, 0xab, 0x88, 0xff, 0x56,
+ 0x5e, 0x09, 0x17, 0xc0, 0x5a, 0x82, 0x05, 0x62, 0x34, 0x2b, 0x28, 0x48,
+ 0x32, 0x97, 0xd0, 0x84, 0x0c, 0xac, 0x13, 0x18, 0xdb, 0x9e, 0x66, 0xc7,
+ 0xaa, 0x14, 0x8b, 0x11, 0x69, 0x4d, 0xf1, 0x09, 0xd3, 0xba, 0x5d, 0xa9,
+ 0x88, 0x37, 0x62, 0xd8, 0xbf, 0x03, 0x9b, 0x9d, 0xd7, 0xe6, 0x05, 0x7e,
+ 0xc2, 0x6a, 0x52, 0xaa, 0x8d, 0x07, 0x80, 0xea, 0x8e, 0x0e, 0xf3, 0x1d,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x6e, 0x30, 0x82, 0x01,
+ 0x6a, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98, 0x43, 0x95, 0x5d,
+ 0x07, 0x37, 0xd5, 0x85, 0x96, 0x9d, 0x4b, 0xd2, 0xc3, 0x45, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb8, 0xca, 0x11,
+ 0xe9, 0x06, 0x31, 0x79, 0xdb, 0xc3, 0x94, 0xc6, 0xe8, 0x19, 0x2a, 0xbc,
+ 0xbb, 0x35, 0x16, 0x31, 0xa4, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x74, 0x30, 0x72, 0x30, 0x38, 0xa0, 0x36, 0xa0, 0x34, 0x86, 0x32,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x55, 0x54, 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73,
+ 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x36, 0xa0, 0x34, 0xa0, 0x32, 0x86, 0x30, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x6f, 0x64, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x55, 0x54, 0x4e, 0x2d,
+ 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61,
+ 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81,
+ 0x86, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x7a, 0x30, 0x78, 0x30, 0x3b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x02, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55, 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x2e,
+ 0x6e, 0x65, 0x74, 0x2f, 0x55, 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54, 0x72,
+ 0x75, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41, 0x2e,
+ 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x1d,
+ 0xb4, 0xe7, 0xf9, 0x18, 0x48, 0x5d, 0xed, 0xfa, 0x5a, 0xc3, 0x1d, 0xe3,
+ 0xb7, 0xef, 0x86, 0x15, 0xc9, 0x6c, 0x13, 0x49, 0x16, 0x0d, 0x31, 0x8c,
+ 0x31, 0x5c, 0xe7, 0xda, 0x87, 0x64, 0x8e, 0xaf, 0x12, 0x16, 0xa4, 0x5a,
+ 0x2d, 0x92, 0x1a, 0x3b, 0x3e, 0x21, 0x65, 0xaa, 0x17, 0xc3, 0x95, 0x7e,
+ 0x40, 0x1c, 0xfd, 0x14, 0x69, 0x06, 0x18, 0xcd, 0xac, 0x31, 0xec, 0x6a,
+ 0xf1, 0x16, 0x9e, 0x89, 0x26, 0x3d, 0x5b, 0x21, 0x8d, 0xe8, 0xe8, 0xf9,
+ 0xab, 0x3d, 0xaa, 0x3c, 0xcb, 0x99, 0xf2, 0x86, 0x5f, 0x88, 0x53, 0x15,
+ 0x05, 0x17, 0xe5, 0xa8, 0xd2, 0x85, 0xa7, 0x4e, 0x49, 0x7d, 0xd6, 0xdd,
+ 0xc7, 0x8f, 0xce, 0x88, 0xbf, 0x65, 0x05, 0xe0, 0x14, 0xb5, 0x31, 0x77,
+ 0xee, 0xbd, 0x2a, 0x86, 0xe8, 0xe8, 0xa7, 0x6a, 0x2d, 0xda, 0x65, 0x19,
+ 0x8c, 0x67, 0xe2, 0x6d, 0xf6, 0x4a, 0x85, 0x66, 0x83, 0xb6, 0x32, 0x4d,
+ 0x9f, 0x42, 0x23, 0x17, 0xd7, 0x45, 0x41, 0x6a, 0x76, 0x04, 0xd4, 0xad,
+ 0xb9, 0x8f, 0x6e, 0xdc, 0xc2, 0x3e, 0xe9, 0x51, 0xf2, 0x9e, 0xd8, 0xf3,
+ 0x7f, 0xfb, 0x50, 0x2a, 0xf0, 0x8b, 0xfd, 0x6f, 0x0d, 0x22, 0x36, 0x2e,
+ 0xce, 0x0e, 0x46, 0xf2, 0xde, 0x8f, 0xda, 0x3b, 0x7d, 0x1e, 0x93, 0xac,
+ 0xfc, 0xf4, 0x32, 0x2b, 0x0f, 0xab, 0x01, 0x1f, 0xa5, 0x40, 0x8f, 0xe3,
+ 0x24, 0x99, 0x1f, 0x5d, 0xb2, 0xaa, 0x0c, 0xb9, 0xe2, 0xa1, 0xe7, 0x92,
+ 0x0c, 0x90, 0x4b, 0x53, 0x7d, 0x1f, 0x28, 0xee, 0x56, 0x3d, 0xaf, 0x18,
+ 0x67, 0x49, 0xdf, 0xd5, 0x1e, 0xc2, 0xa8, 0x98, 0x2b, 0x4c, 0x47, 0x83,
+ 0x81, 0x4c, 0x2e, 0x44, 0xc2, 0xef, 0xc8, 0x63, 0xee, 0x8b, 0x7b, 0x5b,
+ 0x31, 0xf6, 0x26, 0x61, 0xbf, 0x79, 0x1c, 0xb0, 0xa9, 0x4e, 0xa9, 0xc9,
+ 0x50, 0x7b, 0xe2,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 11:a3:b4:d0:ec:8d:b7:7f:9d:a0:cd:5d:2d:51:2f:42
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Validity
+ Not Before: May 24 00:00:00 2010 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Extended Validation Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cc:4a:96:33:cd:25:8d:67:ee:28:96:37:87:46:
+ f0:f6:04:a2:84:7f:53:aa:96:e6:1f:b1:02:1c:6e:
+ ed:7d:21:d4:d7:3c:1e:a2:d8:69:2f:a8:b7:f5:a2:
+ ed:64:58:64:e1:44:65:36:49:41:20:01:8d:3b:13:
+ e2:08:f3:0c:f2:57:39:93:37:b7:1c:93:44:83:8e:
+ bf:2d:f1:a1:05:75:da:6e:ee:7b:6f:1b:ea:76:83:
+ 28:74:4a:1c:2b:d3:f5:c4:03:72:93:af:86:ce:09:
+ 8c:3c:75:d4:c9:0a:2f:72:f3:ad:bd:0e:30:3c:84:
+ a1:73:1f:03:25:14:a5:8f:c3:d6:f4:b5:e4:dd:86:
+ 7a:f5:19:ba:68:f2:85:54:a2:30:11:ca:d1:92:cb:
+ 3b:74:06:12:a0:37:ab:6a:d8:54:11:df:6c:9a:16:
+ 94:b9:b4:a7:65:c6:74:2d:31:f3:4d:52:e9:55:51:
+ 9f:cb:3e:a2:8d:76:98:70:d2:6f:a6:65:45:2f:1b:
+ 85:bb:5b:6d:f9:f2:c0:04:66:13:84:7a:9d:ce:27:
+ d8:f4:44:9e:bf:ac:be:99:db:6b:4f:db:58:21:b0:
+ 89:27:b4:8f:32:d6:4b:5e:72:91:5e:df:05:9d:d9:
+ 49:2f:f4:b6:6f:50:1f:75:cb:80:9d:e6:d3:e4:d1:
+ f2:d3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+
+ X509v3 Subject Key Identifier:
+ 88:44:51:FF:50:2A:69:5E:2D:88:F4:21:BA:D9:0C:F2:CE:CB:EA:7C
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://secure.comodo.com/CPS
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/COMODOCertificationAuthority.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.comodoca.com/COMODOAddTrustServerCA.crt
+ OCSP - URI:http://ocsp.comodoca.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 9a:43:bf:af:a4:72:5e:cd:7d:6f:7f:f4:fc:3d:8c:bb:70:e6:
+ 1e:dd:04:fd:3f:dc:9d:9f:bf:89:76:9b:f2:86:31:fc:7f:b3:
+ ed:2a:91:53:2c:e2:aa:b0:e3:c8:2c:71:f7:15:8a:23:1c:f1:
+ 69:2e:81:fb:b1:bc:62:0b:ab:1a:54:1c:d9:22:5e:34:4c:a5:
+ f6:23:0f:5d:7a:3d:db:43:cd:69:7e:17:37:52:cd:53:a1:c2:
+ 11:d4:53:78:27:64:d5:89:41:4d:16:55:bb:90:cb:f0:d8:e4:
+ dd:dd:d3:09:64:48:28:ff:32:23:84:2f:8c:7b:55:2f:cf:29:
+ 88:37:34:78:0f:33:aa:ff:b7:f2:96:a4:9b:44:80:b5:be:6c:
+ 56:54:ab:a4:81:9e:25:18:28:54:3a:7f:2c:63:cf:59:20:8c:
+ 18:6b:38:2c:b4:dd:ed:e3:40:de:0c:36:25:57:9a:c0:d1:60:
+ 9e:5e:03:68:97:ae:1a:3b:ea:45:d7:51:99:49:ee:44:59:56:
+ 0b:5e:b1:8f:68:ea:8a:9e:ca:d2:c9:a0:03:7e:70:25:f4:32:
+ c9:4e:50:83:87:a2:34:48:3d:4f:35:77:fc:d8:88:ea:f6:7d:
+ 1e:ce:43:b6:d5:c2:6a:7e:38:66:63:4d:e7:ee:32:ef:0f:24:
+ e8:2a:67:fa
+-----BEGIN CERTIFICATE-----
+MIIFBjCCA+6gAwIBAgIQEaO00OyNt3+doM1dLVEvQjANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMDA1MjQwMDAw
+MDBaFw0yMDA1MzAxMDQ4MzhaMIGOMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
+YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
+RE8gQ0EgTGltaXRlZDE0MDIGA1UEAxMrQ09NT0RPIEV4dGVuZGVkIFZhbGlkYXRp
+b24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAMxKljPNJY1n7iiWN4dG8PYEooR/U6qW5h+xAhxu7X0h1Nc8HqLYaS+ot/Wi
+7WRYZOFEZTZJQSABjTsT4gjzDPJXOZM3txyTRIOOvy3xoQV12m7ue28b6naDKHRK
+HCvT9cQDcpOvhs4JjDx11MkKL3Lzrb0OMDyEoXMfAyUUpY/D1vS15N2GevUZumjy
+hVSiMBHK0ZLLO3QGEqA3q2rYVBHfbJoWlLm0p2XGdC0x801S6VVRn8s+oo12mHDS
+b6ZlRS8bhbtbbfnywARmE4R6nc4n2PREnr+svpnba0/bWCGwiSe0jzLWS15ykV7f
+BZ3ZSS/0tm9QH3XLgJ3m0+TR8tMCAwEAAaOCAWkwggFlMB8GA1UdIwQYMBaAFAtY
+5YvGTBU3pECpMKkhvkc2Wlb/MB0GA1UdDgQWBBSIRFH/UCppXi2I9CG62Qzyzsvq
+fDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADA+BgNVHSAENzA1
+MDMGBFUdIAAwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNv
+bS9DUFMwSQYDVR0fBEIwQDA+oDygOoY4aHR0cDovL2NybC5jb21vZG9jYS5jb20v
+Q09NT0RPQ2VydGlmaWNhdGlvbkF1dGhvcml0eS5jcmwwdAYIKwYBBQUHAQEEaDBm
+MD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9BZGRU
+cnVzdFNlcnZlckNBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2Rv
+Y2EuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQCaQ7+vpHJezX1vf/T8PYy7cOYe3QT9
+P9ydn7+JdpvyhjH8f7PtKpFTLOKqsOPILHH3FYojHPFpLoH7sbxiC6saVBzZIl40
+TKX2Iw9dej3bQ81pfhc3Us1TocIR1FN4J2TViUFNFlW7kMvw2OTd3dMJZEgo/zIj
+hC+Me1UvzymINzR4DzOq/7fylqSbRIC1vmxWVKukgZ4lGChUOn8sY89ZIIwYazgs
+tN3t40DeDDYlV5rA0WCeXgNol64aO+pF11GZSe5EWVYLXrGPaOqKnsrSyaADfnAl
+9DLJTlCDh6I0SD1PNXf82Ijq9n0ezkO21cJqfjhmY03n7jLvDyToKmf6
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert75[] = {
+ 0x30, 0x82, 0x05, 0x06, 0x30, 0x82, 0x03, 0xee, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x11, 0xa3, 0xb4, 0xd0, 0xec, 0x8d, 0xb7, 0x7f, 0x9d,
+ 0xa0, 0xcd, 0x5d, 0x2d, 0x51, 0x2f, 0x42, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e,
+ 0x17, 0x0d, 0x31, 0x30, 0x30, 0x35, 0x32, 0x34, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x35, 0x33, 0x30, 0x31,
+ 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30, 0x81, 0x8e, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b,
+ 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x47, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73,
+ 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30,
+ 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, 0x4f, 0x4d, 0x4f,
+ 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65,
+ 0x64, 0x31, 0x34, 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e,
+ 0x64, 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xcc, 0x4a, 0x96, 0x33, 0xcd, 0x25, 0x8d, 0x67,
+ 0xee, 0x28, 0x96, 0x37, 0x87, 0x46, 0xf0, 0xf6, 0x04, 0xa2, 0x84, 0x7f,
+ 0x53, 0xaa, 0x96, 0xe6, 0x1f, 0xb1, 0x02, 0x1c, 0x6e, 0xed, 0x7d, 0x21,
+ 0xd4, 0xd7, 0x3c, 0x1e, 0xa2, 0xd8, 0x69, 0x2f, 0xa8, 0xb7, 0xf5, 0xa2,
+ 0xed, 0x64, 0x58, 0x64, 0xe1, 0x44, 0x65, 0x36, 0x49, 0x41, 0x20, 0x01,
+ 0x8d, 0x3b, 0x13, 0xe2, 0x08, 0xf3, 0x0c, 0xf2, 0x57, 0x39, 0x93, 0x37,
+ 0xb7, 0x1c, 0x93, 0x44, 0x83, 0x8e, 0xbf, 0x2d, 0xf1, 0xa1, 0x05, 0x75,
+ 0xda, 0x6e, 0xee, 0x7b, 0x6f, 0x1b, 0xea, 0x76, 0x83, 0x28, 0x74, 0x4a,
+ 0x1c, 0x2b, 0xd3, 0xf5, 0xc4, 0x03, 0x72, 0x93, 0xaf, 0x86, 0xce, 0x09,
+ 0x8c, 0x3c, 0x75, 0xd4, 0xc9, 0x0a, 0x2f, 0x72, 0xf3, 0xad, 0xbd, 0x0e,
+ 0x30, 0x3c, 0x84, 0xa1, 0x73, 0x1f, 0x03, 0x25, 0x14, 0xa5, 0x8f, 0xc3,
+ 0xd6, 0xf4, 0xb5, 0xe4, 0xdd, 0x86, 0x7a, 0xf5, 0x19, 0xba, 0x68, 0xf2,
+ 0x85, 0x54, 0xa2, 0x30, 0x11, 0xca, 0xd1, 0x92, 0xcb, 0x3b, 0x74, 0x06,
+ 0x12, 0xa0, 0x37, 0xab, 0x6a, 0xd8, 0x54, 0x11, 0xdf, 0x6c, 0x9a, 0x16,
+ 0x94, 0xb9, 0xb4, 0xa7, 0x65, 0xc6, 0x74, 0x2d, 0x31, 0xf3, 0x4d, 0x52,
+ 0xe9, 0x55, 0x51, 0x9f, 0xcb, 0x3e, 0xa2, 0x8d, 0x76, 0x98, 0x70, 0xd2,
+ 0x6f, 0xa6, 0x65, 0x45, 0x2f, 0x1b, 0x85, 0xbb, 0x5b, 0x6d, 0xf9, 0xf2,
+ 0xc0, 0x04, 0x66, 0x13, 0x84, 0x7a, 0x9d, 0xce, 0x27, 0xd8, 0xf4, 0x44,
+ 0x9e, 0xbf, 0xac, 0xbe, 0x99, 0xdb, 0x6b, 0x4f, 0xdb, 0x58, 0x21, 0xb0,
+ 0x89, 0x27, 0xb4, 0x8f, 0x32, 0xd6, 0x4b, 0x5e, 0x72, 0x91, 0x5e, 0xdf,
+ 0x05, 0x9d, 0xd9, 0x49, 0x2f, 0xf4, 0xb6, 0x6f, 0x50, 0x1f, 0x75, 0xcb,
+ 0x80, 0x9d, 0xe6, 0xd3, 0xe4, 0xd1, 0xf2, 0xd3, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x01, 0x69, 0x30, 0x82, 0x01, 0x65, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x0b, 0x58,
+ 0xe5, 0x8b, 0xc6, 0x4c, 0x15, 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21,
+ 0xbe, 0x47, 0x36, 0x5a, 0x56, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x88, 0x44, 0x51, 0xff, 0x50, 0x2a, 0x69,
+ 0x5e, 0x2d, 0x88, 0xf4, 0x21, 0xba, 0xd9, 0x0c, 0xf2, 0xce, 0xcb, 0xea,
+ 0x7c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x37, 0x30, 0x35,
+ 0x30, 0x33, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2b, 0x30, 0x29,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1d,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x73, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x49, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x42, 0x30, 0x40, 0x30, 0x3e, 0xa0, 0x3c, 0xa0, 0x3a, 0x86, 0x38,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x74, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x68, 0x30, 0x66,
+ 0x30, 0x3e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02,
+ 0x86, 0x32, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x41, 0x64, 0x64, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x74, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f,
+ 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x9a, 0x43, 0xbf, 0xaf, 0xa4, 0x72, 0x5e, 0xcd, 0x7d, 0x6f,
+ 0x7f, 0xf4, 0xfc, 0x3d, 0x8c, 0xbb, 0x70, 0xe6, 0x1e, 0xdd, 0x04, 0xfd,
+ 0x3f, 0xdc, 0x9d, 0x9f, 0xbf, 0x89, 0x76, 0x9b, 0xf2, 0x86, 0x31, 0xfc,
+ 0x7f, 0xb3, 0xed, 0x2a, 0x91, 0x53, 0x2c, 0xe2, 0xaa, 0xb0, 0xe3, 0xc8,
+ 0x2c, 0x71, 0xf7, 0x15, 0x8a, 0x23, 0x1c, 0xf1, 0x69, 0x2e, 0x81, 0xfb,
+ 0xb1, 0xbc, 0x62, 0x0b, 0xab, 0x1a, 0x54, 0x1c, 0xd9, 0x22, 0x5e, 0x34,
+ 0x4c, 0xa5, 0xf6, 0x23, 0x0f, 0x5d, 0x7a, 0x3d, 0xdb, 0x43, 0xcd, 0x69,
+ 0x7e, 0x17, 0x37, 0x52, 0xcd, 0x53, 0xa1, 0xc2, 0x11, 0xd4, 0x53, 0x78,
+ 0x27, 0x64, 0xd5, 0x89, 0x41, 0x4d, 0x16, 0x55, 0xbb, 0x90, 0xcb, 0xf0,
+ 0xd8, 0xe4, 0xdd, 0xdd, 0xd3, 0x09, 0x64, 0x48, 0x28, 0xff, 0x32, 0x23,
+ 0x84, 0x2f, 0x8c, 0x7b, 0x55, 0x2f, 0xcf, 0x29, 0x88, 0x37, 0x34, 0x78,
+ 0x0f, 0x33, 0xaa, 0xff, 0xb7, 0xf2, 0x96, 0xa4, 0x9b, 0x44, 0x80, 0xb5,
+ 0xbe, 0x6c, 0x56, 0x54, 0xab, 0xa4, 0x81, 0x9e, 0x25, 0x18, 0x28, 0x54,
+ 0x3a, 0x7f, 0x2c, 0x63, 0xcf, 0x59, 0x20, 0x8c, 0x18, 0x6b, 0x38, 0x2c,
+ 0xb4, 0xdd, 0xed, 0xe3, 0x40, 0xde, 0x0c, 0x36, 0x25, 0x57, 0x9a, 0xc0,
+ 0xd1, 0x60, 0x9e, 0x5e, 0x03, 0x68, 0x97, 0xae, 0x1a, 0x3b, 0xea, 0x45,
+ 0xd7, 0x51, 0x99, 0x49, 0xee, 0x44, 0x59, 0x56, 0x0b, 0x5e, 0xb1, 0x8f,
+ 0x68, 0xea, 0x8a, 0x9e, 0xca, 0xd2, 0xc9, 0xa0, 0x03, 0x7e, 0x70, 0x25,
+ 0xf4, 0x32, 0xc9, 0x4e, 0x50, 0x83, 0x87, 0xa2, 0x34, 0x48, 0x3d, 0x4f,
+ 0x35, 0x77, 0xfc, 0xd8, 0x88, 0xea, 0xf6, 0x7d, 0x1e, 0xce, 0x43, 0xb6,
+ 0xd5, 0xc2, 0x6a, 0x7e, 0x38, 0x66, 0x63, 0x4d, 0xe7, 0xee, 0x32, 0xef,
+ 0x0f, 0x24, 0xe8, 0x2a, 0x67, 0xfa,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 513 (0x201)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority
+ Validity
+ Not Before: Nov 16 01:15:40 2006 GMT
+ Not After : Nov 16 01:15:40 2026 GMT
+ Subject: C=US, ST=Arizona, L=Scottsdale, O=Starfield Technologies, Inc., OU=http://certificates.starfieldtech.com/repository, CN=Starfield Secure Certification Authority/serialNumber=10688435
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e2:a7:5d:a3:ed:66:ef:6a:2f:2b:36:1f:dd:8d:
+ d3:05:02:a0:ca:0f:5e:19:ae:38:72:cf:16:da:54:
+ 4a:cb:48:0a:f4:a1:73:11:65:85:43:c9:5b:17:0c:
+ 9a:2b:be:0f:98:51:7a:60:29:0d:6c:de:e2:e8:e5:
+ 15:4d:56:ff:90:d1:a7:a6:04:3f:60:07:4a:ca:6f:
+ a5:10:e7:b3:f8:5c:b1:bc:2b:2a:dc:01:79:f5:1d:
+ 35:f5:7a:28:83:f2:93:73:82:89:ac:60:6d:cb:c2:
+ 48:c2:1d:d4:06:44:17:3c:ac:01:47:ab:3e:70:84:
+ 09:0b:b8:20:08:40:20:87:a1:63:1a:ca:3e:83:d2:
+ 37:b3:98:8d:32:3f:37:bf:a1:b7:5b:5f:de:5c:33:
+ 92:cf:3e:07:ce:b9:48:4b:e2:f0:55:50:2f:f8:70:
+ 42:89:d1:93:96:8a:63:d9:66:0d:e6:58:6e:b9:6d:
+ 90:bd:ca:dc:84:66:f2:39:8e:5b:a6:58:55:73:cb:
+ 62:6c:1b:d7:20:16:3b:2c:59:f5:cb:c8:56:32:4a:
+ 50:27:ba:55:d3:a8:01:cb:72:a9:74:8b:0c:ad:3a:
+ e5:15:b6:2a:df:65:f8:de:8a:f5:ef:84:3b:f9:e7:
+ 54:65:0b:80:bd:47:45:a5:f0:44:d8:53:3b:be:80:
+ f1:2f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 49:4B:52:27:D1:1B:BC:F2:A1:21:6A:62:7B:51:42:7A:8A:D7:D5:56
+ X509v3 Authority Key Identifier:
+ keyid:BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.starfieldtech.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://certificates.starfieldtech.com/repository/sfroot.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://certificates.starfieldtech.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ 86:52:ba:b3:1f:a6:5e:6b:90:a6:64:2a:fc:45:b2:ae:9f:3e:
+ b3:62:af:db:1f:67:c4:bd:ca:a1:2f:c7:9c:0d:21:57:d0:f8:
+ 36:21:ce:3a:25:3e:78:76:b3:d9:dd:bc:de:fb:6c:84:5f:0c:
+ a3:0d:12:eb:11:3b:71:5f:80:1e:f1:1f:6d:0e:5f:c1:ec:d4:
+ a5:f7:65:bb:1f:4c:95:01:13:b2:6a:9c:0b:eb:1f:9d:b1:e7:
+ ed:19:0d:bc:85:7c:f3:17:bd:59:63:ae:a7:1a:05:cd:47:e3:
+ 2d:96:62:51:32:0a:08:68:4b:22:77:5f:f7:45:dc:61:de:f4:
+ cb:2b:22:29:44:25:d2:9f:0b:77:7a:a1:26:7c:4a:d7:0f:c2:
+ d1:3c:ba:0e:a7:95:9a:5b:05:0a:10:f9:55:5f:c1:97:8b:74:
+ cc:5e:28:69:13:7e:d0:0a:8d:9d:0f:60:54:7a:c4:8c:1b:35:
+ 0f:74:7a:70:b2:82:cf:1d:b5:e2:8a:db:2a:c6:b2:51:69:bf:
+ 12:17:92:60:17:aa:3d:5b:09:f8:87:65:1d:a7:a4:28:e5:22:
+ 02:03:82:44:9a:34:63:9e:fb:28:cf:e8:cd:2e:0e:52:20:ed:
+ 4a:cb:38:7c:9d:ae:6e:79:d7:95:2c:a8:91:f3:86:01:21:91:
+ 4b:b5:40:a4
+-----BEGIN CERTIFICATE-----
+MIIFBzCCA++gAwIBAgICAgEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCVVMx
+JTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsT
+KVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2
+MTExNjAxMTU0MFoXDTI2MTExNjAxMTU0MFowgdwxCzAJBgNVBAYTAlVTMRAwDgYD
+VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
+ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTkwNwYDVQQLEzBodHRwOi8vY2VydGlm
+aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkxMTAvBgNVBAMTKFN0
+YXJmaWVsZCBTZWN1cmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxETAPBgNVBAUT
+CDEwNjg4NDM1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4qddo+1m
+72ovKzYf3Y3TBQKgyg9eGa44cs8W2lRKy0gK9KFzEWWFQ8lbFwyaK74PmFF6YCkN
+bN7i6OUVTVb/kNGnpgQ/YAdKym+lEOez+FyxvCsq3AF59R019Xoog/KTc4KJrGBt
+y8JIwh3UBkQXPKwBR6s+cIQJC7ggCEAgh6FjGso+g9I3s5iNMj83v6G3W1/eXDOS
+zz4HzrlIS+LwVVAv+HBCidGTlopj2WYN5lhuuW2QvcrchGbyOY5bplhVc8tibBvX
+IBY7LFn1y8hWMkpQJ7pV06gBy3KpdIsMrTrlFbYq32X43or174Q7+edUZQuAvUdF
+pfBE2FM7voDxLwIDAQABo4IBRDCCAUAwHQYDVR0OBBYEFElLUifRG7zyoSFqYntR
+QnqK19VWMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtVrNzXEMIOqYjnMBIGA1UdEwEB
+/wQIMAYBAf8CAQAwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8v
+b2NzcC5zdGFyZmllbGR0ZWNoLmNvbTBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8v
+Y2VydGlmaWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvc2Zyb290
+LmNybDBRBgNVHSAESjBIMEYGBFUdIAAwPjA8BggrBgEFBQcCARYwaHR0cDovL2Nl
+cnRpZmljYXRlcy5zdGFyZmllbGR0ZWNoLmNvbS9yZXBvc2l0b3J5MA4GA1UdDwEB
+/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAhlK6sx+mXmuQpmQq/EWyrp8+s2Kv
+2x9nxL3KoS/HnA0hV9D4NiHOOiU+eHaz2d283vtshF8Mow0S6xE7cV+AHvEfbQ5f
+wezUpfdlux9MlQETsmqcC+sfnbHn7RkNvIV88xe9WWOupxoFzUfjLZZiUTIKCGhL
+Indf90XcYd70yysiKUQl0p8Ld3qhJnxK1w/C0Ty6DqeVmlsFChD5VV/Bl4t0zF4o
+aRN+0AqNnQ9gVHrEjBs1D3R6cLKCzx214orbKsayUWm/EheSYBeqPVsJ+IdlHaek
+KOUiAgOCRJo0Y577KM/ozS4OUiDtSss4fJ2ubnnXlSyokfOGASGRS7VApA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert76[] = {
+ 0x30, 0x82, 0x05, 0x07, 0x30, 0x82, 0x03, 0xef, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x68, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74,
+ 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68,
+ 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x29, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36,
+ 0x31, 0x31, 0x31, 0x36, 0x30, 0x31, 0x31, 0x35, 0x34, 0x30, 0x5a, 0x17,
+ 0x0d, 0x32, 0x36, 0x31, 0x31, 0x31, 0x36, 0x30, 0x31, 0x31, 0x35, 0x34,
+ 0x30, 0x5a, 0x30, 0x81, 0xdc, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61,
+ 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53,
+ 0x63, 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x25, 0x30,
+ 0x23, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72,
+ 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f,
+ 0x6c, 0x6f, 0x67, 0x69, 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31,
+ 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x28, 0x53, 0x74,
+ 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x05, 0x13,
+ 0x08, 0x31, 0x30, 0x36, 0x38, 0x38, 0x34, 0x33, 0x35, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0xa7, 0x5d, 0xa3, 0xed, 0x66,
+ 0xef, 0x6a, 0x2f, 0x2b, 0x36, 0x1f, 0xdd, 0x8d, 0xd3, 0x05, 0x02, 0xa0,
+ 0xca, 0x0f, 0x5e, 0x19, 0xae, 0x38, 0x72, 0xcf, 0x16, 0xda, 0x54, 0x4a,
+ 0xcb, 0x48, 0x0a, 0xf4, 0xa1, 0x73, 0x11, 0x65, 0x85, 0x43, 0xc9, 0x5b,
+ 0x17, 0x0c, 0x9a, 0x2b, 0xbe, 0x0f, 0x98, 0x51, 0x7a, 0x60, 0x29, 0x0d,
+ 0x6c, 0xde, 0xe2, 0xe8, 0xe5, 0x15, 0x4d, 0x56, 0xff, 0x90, 0xd1, 0xa7,
+ 0xa6, 0x04, 0x3f, 0x60, 0x07, 0x4a, 0xca, 0x6f, 0xa5, 0x10, 0xe7, 0xb3,
+ 0xf8, 0x5c, 0xb1, 0xbc, 0x2b, 0x2a, 0xdc, 0x01, 0x79, 0xf5, 0x1d, 0x35,
+ 0xf5, 0x7a, 0x28, 0x83, 0xf2, 0x93, 0x73, 0x82, 0x89, 0xac, 0x60, 0x6d,
+ 0xcb, 0xc2, 0x48, 0xc2, 0x1d, 0xd4, 0x06, 0x44, 0x17, 0x3c, 0xac, 0x01,
+ 0x47, 0xab, 0x3e, 0x70, 0x84, 0x09, 0x0b, 0xb8, 0x20, 0x08, 0x40, 0x20,
+ 0x87, 0xa1, 0x63, 0x1a, 0xca, 0x3e, 0x83, 0xd2, 0x37, 0xb3, 0x98, 0x8d,
+ 0x32, 0x3f, 0x37, 0xbf, 0xa1, 0xb7, 0x5b, 0x5f, 0xde, 0x5c, 0x33, 0x92,
+ 0xcf, 0x3e, 0x07, 0xce, 0xb9, 0x48, 0x4b, 0xe2, 0xf0, 0x55, 0x50, 0x2f,
+ 0xf8, 0x70, 0x42, 0x89, 0xd1, 0x93, 0x96, 0x8a, 0x63, 0xd9, 0x66, 0x0d,
+ 0xe6, 0x58, 0x6e, 0xb9, 0x6d, 0x90, 0xbd, 0xca, 0xdc, 0x84, 0x66, 0xf2,
+ 0x39, 0x8e, 0x5b, 0xa6, 0x58, 0x55, 0x73, 0xcb, 0x62, 0x6c, 0x1b, 0xd7,
+ 0x20, 0x16, 0x3b, 0x2c, 0x59, 0xf5, 0xcb, 0xc8, 0x56, 0x32, 0x4a, 0x50,
+ 0x27, 0xba, 0x55, 0xd3, 0xa8, 0x01, 0xcb, 0x72, 0xa9, 0x74, 0x8b, 0x0c,
+ 0xad, 0x3a, 0xe5, 0x15, 0xb6, 0x2a, 0xdf, 0x65, 0xf8, 0xde, 0x8a, 0xf5,
+ 0xef, 0x84, 0x3b, 0xf9, 0xe7, 0x54, 0x65, 0x0b, 0x80, 0xbd, 0x47, 0x45,
+ 0xa5, 0xf0, 0x44, 0xd8, 0x53, 0x3b, 0xbe, 0x80, 0xf1, 0x2f, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x44, 0x30, 0x82, 0x01, 0x40, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x49, 0x4b,
+ 0x52, 0x27, 0xd1, 0x1b, 0xbc, 0xf2, 0xa1, 0x21, 0x6a, 0x62, 0x7b, 0x51,
+ 0x42, 0x7a, 0x8a, 0xd7, 0xd5, 0x56, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xbf, 0x5f, 0xb7, 0xd1, 0xce,
+ 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, 0xac, 0xdc, 0xd7, 0x10, 0xc2, 0x0e,
+ 0xa9, 0x88, 0xe7, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01,
+ 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30,
+ 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x2d, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x1d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65,
+ 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4c,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x45, 0x30, 0x43, 0x30, 0x41, 0xa0,
+ 0x3f, 0xa0, 0x3d, 0x86, 0x3b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73,
+ 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65,
+ 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x73, 0x66, 0x72, 0x6f, 0x6f, 0x74,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x51, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x4a, 0x30, 0x48, 0x30, 0x46, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30,
+ 0x3e, 0x30, 0x3c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x01, 0x16, 0x30, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x86, 0x52, 0xba, 0xb3, 0x1f, 0xa6, 0x5e, 0x6b, 0x90,
+ 0xa6, 0x64, 0x2a, 0xfc, 0x45, 0xb2, 0xae, 0x9f, 0x3e, 0xb3, 0x62, 0xaf,
+ 0xdb, 0x1f, 0x67, 0xc4, 0xbd, 0xca, 0xa1, 0x2f, 0xc7, 0x9c, 0x0d, 0x21,
+ 0x57, 0xd0, 0xf8, 0x36, 0x21, 0xce, 0x3a, 0x25, 0x3e, 0x78, 0x76, 0xb3,
+ 0xd9, 0xdd, 0xbc, 0xde, 0xfb, 0x6c, 0x84, 0x5f, 0x0c, 0xa3, 0x0d, 0x12,
+ 0xeb, 0x11, 0x3b, 0x71, 0x5f, 0x80, 0x1e, 0xf1, 0x1f, 0x6d, 0x0e, 0x5f,
+ 0xc1, 0xec, 0xd4, 0xa5, 0xf7, 0x65, 0xbb, 0x1f, 0x4c, 0x95, 0x01, 0x13,
+ 0xb2, 0x6a, 0x9c, 0x0b, 0xeb, 0x1f, 0x9d, 0xb1, 0xe7, 0xed, 0x19, 0x0d,
+ 0xbc, 0x85, 0x7c, 0xf3, 0x17, 0xbd, 0x59, 0x63, 0xae, 0xa7, 0x1a, 0x05,
+ 0xcd, 0x47, 0xe3, 0x2d, 0x96, 0x62, 0x51, 0x32, 0x0a, 0x08, 0x68, 0x4b,
+ 0x22, 0x77, 0x5f, 0xf7, 0x45, 0xdc, 0x61, 0xde, 0xf4, 0xcb, 0x2b, 0x22,
+ 0x29, 0x44, 0x25, 0xd2, 0x9f, 0x0b, 0x77, 0x7a, 0xa1, 0x26, 0x7c, 0x4a,
+ 0xd7, 0x0f, 0xc2, 0xd1, 0x3c, 0xba, 0x0e, 0xa7, 0x95, 0x9a, 0x5b, 0x05,
+ 0x0a, 0x10, 0xf9, 0x55, 0x5f, 0xc1, 0x97, 0x8b, 0x74, 0xcc, 0x5e, 0x28,
+ 0x69, 0x13, 0x7e, 0xd0, 0x0a, 0x8d, 0x9d, 0x0f, 0x60, 0x54, 0x7a, 0xc4,
+ 0x8c, 0x1b, 0x35, 0x0f, 0x74, 0x7a, 0x70, 0xb2, 0x82, 0xcf, 0x1d, 0xb5,
+ 0xe2, 0x8a, 0xdb, 0x2a, 0xc6, 0xb2, 0x51, 0x69, 0xbf, 0x12, 0x17, 0x92,
+ 0x60, 0x17, 0xaa, 0x3d, 0x5b, 0x09, 0xf8, 0x87, 0x65, 0x1d, 0xa7, 0xa4,
+ 0x28, 0xe5, 0x22, 0x02, 0x03, 0x82, 0x44, 0x9a, 0x34, 0x63, 0x9e, 0xfb,
+ 0x28, 0xcf, 0xe8, 0xcd, 0x2e, 0x0e, 0x52, 0x20, 0xed, 0x4a, 0xcb, 0x38,
+ 0x7c, 0x9d, 0xae, 0x6e, 0x79, 0xd7, 0x95, 0x2c, 0xa8, 0x91, 0xf3, 0x86,
+ 0x01, 0x21, 0x91, 0x4b, 0xb5, 0x40, 0xa4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1276028635 (0x4c0ea6db)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048)
+ Validity
+ Not Before: Oct 1 19:42:24 2006 GMT
+ Not After : Nov 4 03:38:44 2016 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df:
+ e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67:
+ a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98:
+ a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a:
+ cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71:
+ 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be:
+ f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41:
+ 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d:
+ be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7:
+ ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41:
+ 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a:
+ 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4:
+ 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e:
+ 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79:
+ 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a:
+ a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e:
+ 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8:
+ 4b:cb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/2048ca.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+
+ X509v3 Subject Key Identifier:
+ B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+ X509v3 Authority Key Identifier:
+ keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70
+
+ 1.2.840.113533.7.65.0:
+ 0
+..V8.1....
+ Signature Algorithm: sha1WithRSAEncryption
+ 59:e1:94:14:89:c6:72:3c:e7:6b:75:4b:25:7a:2d:3e:a3:db:
+ ac:3c:72:4f:9b:30:b0:a2:5e:d6:62:5d:8f:36:6b:e7:dd:23:
+ 59:c1:80:2c:a0:ed:7e:11:a0:c9:a3:bb:f6:96:b8:34:c9:fe:
+ c6:d7:58:b4:bb:27:7f:e5:6b:23:04:68:61:4b:16:57:df:e1:
+ 7e:c0:c5:36:8f:0c:04:de:ef:77:68:68:83:6d:7c:05:fb:45:
+ dd:ce:16:56:91:39:d2:58:91:51:95:87:9e:4d:b4:0a:d7:05:
+ 63:83:43:26:de:08:a6:19:77:9d:fe:59:a2:5f:db:32:33:4a:
+ 65:10:c4:47:ef:ba:57:07:1f:4c:9f:af:68:65:ef:67:6d:9a:
+ de:1e:5e:4e:87:85:ee:9d:0d:7b:3d:d2:03:a9:dd:b7:05:04:
+ 9e:95:0d:c1:b2:11:fd:5a:77:c4:1f:98:9f:2e:a0:d0:c9:7c:
+ d3:34:62:f5:2f:96:37:48:48:b4:21:fb:2f:ad:53:65:34:c2:
+ 7b:4a:7c:fc:90:49:9f:f3:f7:37:08:9e:41:00:b2:63:1b:4b:
+ b9:f6:c1:7d:59:66:ab:d1:f3:8a:30:05:18:7a:41:47:ab:c7:
+ 67:14:3a:7c:60:b1:08:4e:d0:ce:c7:e1:ad:a6:4d:ee:ae:32:
+ ac:ac:c6:5a
+-----BEGIN CERTIFICATE-----
+MIIFBzCCA++gAwIBAgIETA6m2zANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
+RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
+IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
+ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0wNjEwMDExOTQyMjRaFw0xNjEx
+MDQwMzM4NDRaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx
+GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhp
+Z2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD1ZQ0Z6IKHLBfaaZAscS3
+so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80ltcZF+Y7arpl/DpIT4T2JR
+vvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46OFBbdzEbjbPHJEWap6xt
+ABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZdHFMsfpjNGgYWpGhz0DQE
+E1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdmt4i3ePLKCqg4qwpkwr9m
+XZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggFmMIIBYjAOBgNVHQ8BAf8E
+BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAnBgNVHSUEIDAeBggrBgEFBQcDAQYI
+KwYBBQUHAwIGCCsGAQUFBwMEMDMGCCsGAQUFBwEBBCcwJTAjBggrBgEFBQcwAYYX
+aHR0cDovL29jc3AuZW50cnVzdC5uZXQwMgYDVR0fBCswKTAnoCWgI4YhaHR0cDov
+L2NybC5lbnRydXN0Lm5ldC8yMDQ4Y2EuY3JsME8GA1UdIARIMEYwRAYEVR0gADA8
+MDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJl
+cG9zaXRvcnkuaHRtMB0GA1UdDgQWBBSxPsNpA/i/RwHUmCYaCALvY2QrwzAfBgNV
+HSMEGDAWgBRV5IHREYC+2Im5CKMx+aEkCRa5cDAZBgkqhkiG9n0HQQAEDDAKGwRW
+OC4xAwIAgTANBgkqhkiG9w0BAQUFAAOCAQEAWeGUFInGcjzna3VLJXotPqPbrDxy
+T5swsKJe1mJdjzZr590jWcGALKDtfhGgyaO79pa4NMn+xtdYtLsnf+VrIwRoYUsW
+V9/hfsDFNo8MBN7vd2hog218BftF3c4WVpE50liRUZWHnk20CtcFY4NDJt4Iphl3
+nf5Zol/bMjNKZRDER++6VwcfTJ+vaGXvZ22a3h5eToeF7p0Nez3SA6ndtwUEnpUN
+wbIR/Vp3xB+Yny6g0Ml80zRi9S+WN0hItCH7L61TZTTCe0p8/JBJn/P3NwieQQCy
+YxtLufbBfVlmq9HzijAFGHpBR6vHZxQ6fGCxCE7QzsfhraZN7q4yrKzGWg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert77[] = {
+ 0x30, 0x82, 0x05, 0x07, 0x30, 0x82, 0x03, 0xef, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x4c, 0x0e, 0xa6, 0xdb, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xb4, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x31,
+ 0x40, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x14, 0x37, 0x77, 0x77,
+ 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65,
+ 0x74, 0x2f, 0x43, 0x50, 0x53, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x20, 0x69,
+ 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x20, 0x62, 0x79, 0x20, 0x72, 0x65,
+ 0x66, 0x2e, 0x20, 0x28, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x6c,
+ 0x69, 0x61, 0x62, 0x2e, 0x29, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x1c, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39,
+ 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x33, 0x30, 0x31,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x45, 0x6e, 0x74, 0x72, 0x75,
+ 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x28, 0x32, 0x30, 0x34, 0x38,
+ 0x29, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x30, 0x30, 0x31, 0x31,
+ 0x39, 0x34, 0x32, 0x32, 0x34, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31,
+ 0x30, 0x34, 0x30, 0x33, 0x33, 0x38, 0x34, 0x34, 0x5a, 0x30, 0x6c, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44,
+ 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31,
+ 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77,
+ 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69,
+ 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65,
+ 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xc6, 0xcc, 0xe5, 0x73,
+ 0xe6, 0xfb, 0xd4, 0xbb, 0xe5, 0x2d, 0x2d, 0x32, 0xa6, 0xdf, 0xe5, 0x81,
+ 0x3f, 0xc9, 0xcd, 0x25, 0x49, 0xb6, 0x71, 0x2a, 0xc3, 0xd5, 0x94, 0x34,
+ 0x67, 0xa2, 0x0a, 0x1c, 0xb0, 0x5f, 0x69, 0xa6, 0x40, 0xb1, 0xc4, 0xb7,
+ 0xb2, 0x8f, 0xd0, 0x98, 0xa4, 0xa9, 0x41, 0x59, 0x3a, 0xd3, 0xdc, 0x94,
+ 0xd6, 0x3c, 0xdb, 0x74, 0x38, 0xa4, 0x4a, 0xcc, 0x4d, 0x25, 0x82, 0xf7,
+ 0x4a, 0xa5, 0x53, 0x12, 0x38, 0xee, 0xf3, 0x49, 0x6d, 0x71, 0x91, 0x7e,
+ 0x63, 0xb6, 0xab, 0xa6, 0x5f, 0xc3, 0xa4, 0x84, 0xf8, 0x4f, 0x62, 0x51,
+ 0xbe, 0xf8, 0xc5, 0xec, 0xdb, 0x38, 0x92, 0xe3, 0x06, 0xe5, 0x08, 0x91,
+ 0x0c, 0xc4, 0x28, 0x41, 0x55, 0xfb, 0xcb, 0x5a, 0x89, 0x15, 0x7e, 0x71,
+ 0xe8, 0x35, 0xbf, 0x4d, 0x72, 0x09, 0x3d, 0xbe, 0x3a, 0x38, 0x50, 0x5b,
+ 0x77, 0x31, 0x1b, 0x8d, 0xb3, 0xc7, 0x24, 0x45, 0x9a, 0xa7, 0xac, 0x6d,
+ 0x00, 0x14, 0x5a, 0x04, 0xb7, 0xba, 0x13, 0xeb, 0x51, 0x0a, 0x98, 0x41,
+ 0x41, 0x22, 0x4e, 0x65, 0x61, 0x87, 0x81, 0x41, 0x50, 0xa6, 0x79, 0x5c,
+ 0x89, 0xde, 0x19, 0x4a, 0x57, 0xd5, 0x2e, 0xe6, 0x5d, 0x1c, 0x53, 0x2c,
+ 0x7e, 0x98, 0xcd, 0x1a, 0x06, 0x16, 0xa4, 0x68, 0x73, 0xd0, 0x34, 0x04,
+ 0x13, 0x5c, 0xa1, 0x71, 0xd3, 0x5a, 0x7c, 0x55, 0xdb, 0x5e, 0x64, 0xe1,
+ 0x37, 0x87, 0x30, 0x56, 0x04, 0xe5, 0x11, 0xb4, 0x29, 0x80, 0x12, 0xf1,
+ 0x79, 0x39, 0x88, 0xa2, 0x02, 0x11, 0x7c, 0x27, 0x66, 0xb7, 0x88, 0xb7,
+ 0x78, 0xf2, 0xca, 0x0a, 0xa8, 0x38, 0xab, 0x0a, 0x64, 0xc2, 0xbf, 0x66,
+ 0x5d, 0x95, 0x84, 0xc1, 0xa1, 0x25, 0x1e, 0x87, 0x5d, 0x1a, 0x50, 0x0b,
+ 0x20, 0x12, 0xcc, 0x41, 0xbb, 0x6e, 0x0b, 0x51, 0x38, 0xb8, 0x4b, 0xcb,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x66, 0x30, 0x82, 0x01,
+ 0x62, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x01, 0x30, 0x27, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x20, 0x30, 0x1e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x04, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+ 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30,
+ 0x32, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2b, 0x30, 0x29, 0x30, 0x27,
+ 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x32, 0x30, 0x34, 0x38, 0x63, 0x61, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x4f, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x48,
+ 0x30, 0x46, 0x30, 0x44, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3c,
+ 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68, 0x74, 0x6d,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1,
+ 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a,
+ 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55,
+ 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x55, 0xe4, 0x81, 0xd1,
+ 0x11, 0x80, 0xbe, 0xd8, 0x89, 0xb9, 0x08, 0xa3, 0x31, 0xf9, 0xa1, 0x24,
+ 0x09, 0x16, 0xb9, 0x70, 0x30, 0x19, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf6, 0x7d, 0x07, 0x41, 0x00, 0x04, 0x0c, 0x30, 0x0a, 0x1b, 0x04, 0x56,
+ 0x38, 0x2e, 0x31, 0x03, 0x02, 0x00, 0x81, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x01, 0x00, 0x59, 0xe1, 0x94, 0x14, 0x89, 0xc6, 0x72, 0x3c, 0xe7,
+ 0x6b, 0x75, 0x4b, 0x25, 0x7a, 0x2d, 0x3e, 0xa3, 0xdb, 0xac, 0x3c, 0x72,
+ 0x4f, 0x9b, 0x30, 0xb0, 0xa2, 0x5e, 0xd6, 0x62, 0x5d, 0x8f, 0x36, 0x6b,
+ 0xe7, 0xdd, 0x23, 0x59, 0xc1, 0x80, 0x2c, 0xa0, 0xed, 0x7e, 0x11, 0xa0,
+ 0xc9, 0xa3, 0xbb, 0xf6, 0x96, 0xb8, 0x34, 0xc9, 0xfe, 0xc6, 0xd7, 0x58,
+ 0xb4, 0xbb, 0x27, 0x7f, 0xe5, 0x6b, 0x23, 0x04, 0x68, 0x61, 0x4b, 0x16,
+ 0x57, 0xdf, 0xe1, 0x7e, 0xc0, 0xc5, 0x36, 0x8f, 0x0c, 0x04, 0xde, 0xef,
+ 0x77, 0x68, 0x68, 0x83, 0x6d, 0x7c, 0x05, 0xfb, 0x45, 0xdd, 0xce, 0x16,
+ 0x56, 0x91, 0x39, 0xd2, 0x58, 0x91, 0x51, 0x95, 0x87, 0x9e, 0x4d, 0xb4,
+ 0x0a, 0xd7, 0x05, 0x63, 0x83, 0x43, 0x26, 0xde, 0x08, 0xa6, 0x19, 0x77,
+ 0x9d, 0xfe, 0x59, 0xa2, 0x5f, 0xdb, 0x32, 0x33, 0x4a, 0x65, 0x10, 0xc4,
+ 0x47, 0xef, 0xba, 0x57, 0x07, 0x1f, 0x4c, 0x9f, 0xaf, 0x68, 0x65, 0xef,
+ 0x67, 0x6d, 0x9a, 0xde, 0x1e, 0x5e, 0x4e, 0x87, 0x85, 0xee, 0x9d, 0x0d,
+ 0x7b, 0x3d, 0xd2, 0x03, 0xa9, 0xdd, 0xb7, 0x05, 0x04, 0x9e, 0x95, 0x0d,
+ 0xc1, 0xb2, 0x11, 0xfd, 0x5a, 0x77, 0xc4, 0x1f, 0x98, 0x9f, 0x2e, 0xa0,
+ 0xd0, 0xc9, 0x7c, 0xd3, 0x34, 0x62, 0xf5, 0x2f, 0x96, 0x37, 0x48, 0x48,
+ 0xb4, 0x21, 0xfb, 0x2f, 0xad, 0x53, 0x65, 0x34, 0xc2, 0x7b, 0x4a, 0x7c,
+ 0xfc, 0x90, 0x49, 0x9f, 0xf3, 0xf7, 0x37, 0x08, 0x9e, 0x41, 0x00, 0xb2,
+ 0x63, 0x1b, 0x4b, 0xb9, 0xf6, 0xc1, 0x7d, 0x59, 0x66, 0xab, 0xd1, 0xf3,
+ 0x8a, 0x30, 0x05, 0x18, 0x7a, 0x41, 0x47, 0xab, 0xc7, 0x67, 0x14, 0x3a,
+ 0x7c, 0x60, 0xb1, 0x08, 0x4e, 0xd0, 0xce, 0xc7, 0xe1, 0xad, 0xa6, 0x4d,
+ 0xee, 0xae, 0x32, 0xac, 0xac, 0xc6, 0x5a,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1164679900 (0x456b9adc)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority
+ Validity
+ Not Before: Dec 10 20:55:43 2009 GMT
+ Not After : Dec 10 21:25:43 2019 GMT
+ Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/rpa is incorporated by reference, OU=(c) 2009 Entrust, Inc., CN=Entrust Certification Authority - L1E
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:5b:04:54:77:dd:0e:24:66:dc:2a:a1:db:80:
+ cc:5d:c7:5f:fd:52:16:58:da:5f:94:06:a9:b8:b6:
+ b9:63:0c:47:20:82:ec:c7:95:4e:8b:b8:77:52:6a:
+ 3d:b5:87:a9:d6:e1:cc:74:e5:a6:c8:c0:d4:56:4f:
+ 8d:2e:d6:08:3e:0c:4c:43:3e:f0:41:93:5e:46:ef:
+ 39:e7:d9:65:2a:0c:76:50:27:bd:5b:0d:33:33:07:
+ e0:f7:a2:a9:9c:e1:11:33:ad:66:fc:d2:2c:7a:aa:
+ a3:73:16:be:93:85:75:0f:d7:37:8c:fa:23:b7:64:
+ f8:e3:4c:6e:ed:b3:05:bd:e2:36:db:7c:de:76:44:
+ da:82:72:76:b6:6e:ff:94:a1:d0:86:f7:10:cd:4a:
+ 5a:8b:b0:75:8c:66:52:80:4e:48:4c:49:83:a6:40:
+ d7:77:81:13:4d:5e:72:7e:48:46:22:aa:0f:e2:3e:
+ 65:94:38:e1:72:71:fe:4a:71:09:ba:35:7f:55:89:
+ 3d:81:d5:b8:28:01:10:77:36:5a:10:85:d2:bd:60:
+ 84:2b:49:61:94:0c:de:4c:40:6a:2a:c4:79:60:84:
+ 24:82:32:69:4a:98:4b:e2:56:10:ba:03:45:51:20:
+ d3:cf:da:8e:54:1b:45:b6:7a:ba:97:9a:5a:d8:c6:
+ d1:5f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/rootca1.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/CPS
+
+ X509v3 Subject Key Identifier:
+ 5B:41:8A:B2:C4:43:C1:BD:BF:C8:54:41:55:9D:E0:96:AD:FF:B9:A1
+ X509v3 Authority Key Identifier:
+ keyid:68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D
+
+ 1.2.840.113533.7.65.0:
+ 0
+..V7.1....
+ Signature Algorithm: sha1WithRSAEncryption
+ b2:3b:d2:9e:c1:bc:3b:48:b6:dc:d8:5a:18:66:53:c3:bd:35:
+ 0d:48:42:2c:35:01:d8:10:a2:e2:e3:8d:2c:ba:a6:03:11:ed:
+ 6b:b1:49:cb:5f:cd:ec:60:b3:ba:d4:02:eb:61:4f:4e:7e:f8:
+ df:90:5f:4e:d3:90:02:1c:52:da:12:00:2f:9b:71:da:04:12:
+ 14:c1:90:83:2e:28:d2:10:40:11:8b:26:2d:eb:99:55:54:6f:
+ 60:8e:c5:83:1d:c0:a3:3f:d5:8a:14:39:6a:1b:0d:ef:d3:5a:
+ 77:39:cf:69:b4:bd:69:6f:4f:78:d3:a1:86:a3:9b:b7:d7:fb:
+ aa:2d:f0:fa:26:a1:f9:67:2c:88:4b:a5:34:d5:83:fb:4c:f1:
+ 5b:70:22:66:1b:9b:59:4f:4d:ce:98:db:41:a4:fe:1a:a3:eb:
+ 38:e6:f9:f1:39:02:9d:46:b6:c9:c2:9e:3e:82:b6:1f:9f:ca:
+ 4a:a8:b1:06:5f:10:34:3b:fd:da:7b:ac:33:4e:ed:a6:b7:4b:
+ f3:91:f5:9c:0b:11:92:dc:13:6a:c8:d5:f1:3b:6d:96:6b:01:
+ e4:23:4c:b1:c1:e0:d2:12:21:9f:29:d4:ad:95:3d:a6:f7:e7:
+ 32:c5:75:b7:0b:57:d8:a4:f9:c0:ec:ec:32:33:0c:4d:ae:e8:
+ 08:d5:ec:aa
+-----BEGIN CERTIFICATE-----
+MIIFCjCCA/KgAwIBAgIERWua3DANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA5MTIxMDIwNTU0M1oXDTE5MTIxMDIx
+MjU0M1owgbExCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
+NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvcnBhIGlzIGluY29ycG9yYXRlZCBieSBy
+ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA5IEVudHJ1c3QsIEluYy4xLjAsBgNV
+BAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMUUwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2WwRUd90OJGbcKqHbgMxdx1/9UhZY
+2l+UBqm4trljDEcgguzHlU6LuHdSaj21h6nW4cx05abIwNRWT40u1gg+DExDPvBB
+k15G7znn2WUqDHZQJ71bDTMzB+D3oqmc4REzrWb80ix6qqNzFr6ThXUP1zeM+iO3
+ZPjjTG7tswW94jbbfN52RNqCcna2bv+UodCG9xDNSlqLsHWMZlKATkhMSYOmQNd3
+gRNNXnJ+SEYiqg/iPmWUOOFycf5KcQm6NX9ViT2B1bgoARB3NloQhdK9YIQrSWGU
+DN5MQGoqxHlghCSCMmlKmEviVhC6A0VRINPP2o5UG0W2erqXmlrYxtFfAgMBAAGj
+ggEnMIIBIzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAzBggrBgEF
+BQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDMG
+A1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvcm9vdGNhMS5j
+cmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cu
+ZW50cnVzdC5uZXQvQ1BTMB0GA1UdDgQWBBRbQYqyxEPBvb/IVEFVneCWrf+5oTAf
+BgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAZBgkqhkiG9n0HQQAEDDAK
+GwRWNy4xAwIAgTANBgkqhkiG9w0BAQUFAAOCAQEAsjvSnsG8O0i23NhaGGZTw701
+DUhCLDUB2BCi4uONLLqmAxHta7FJy1/N7GCzutQC62FPTn7435BfTtOQAhxS2hIA
+L5tx2gQSFMGQgy4o0hBAEYsmLeuZVVRvYI7Fgx3Aoz/VihQ5ahsN79NadznPabS9
+aW9PeNOhhqObt9f7qi3w+iah+WcsiEulNNWD+0zxW3AiZhubWU9NzpjbQaT+GqPr
+OOb58TkCnUa2ycKePoK2H5/KSqixBl8QNDv92nusM07tprdL85H1nAsRktwTasjV
+8TttlmsB5CNMscHg0hIhnynUrZU9pvfnMsV1twtX2KT5wOzsMjMMTa7oCNXsqg==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert78[] = {
+ 0x30, 0x82, 0x05, 0x0a, 0x30, 0x82, 0x03, 0xf2, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x45, 0x6b, 0x9a, 0xdc, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x73, 0x20,
+ 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16,
+ 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d,
+ 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17,
+ 0x0d, 0x30, 0x39, 0x31, 0x32, 0x31, 0x30, 0x32, 0x30, 0x35, 0x35, 0x34,
+ 0x33, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x31, 0x30, 0x32, 0x31,
+ 0x32, 0x35, 0x34, 0x33, 0x5a, 0x30, 0x81, 0xb1, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30,
+ 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x39, 0x30,
+ 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x77, 0x77, 0x77, 0x2e,
+ 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x72, 0x70, 0x61, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x2d, 0x20, 0x4c, 0x31, 0x45, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xb6, 0x5b, 0x04, 0x54, 0x77, 0xdd, 0x0e, 0x24, 0x66, 0xdc,
+ 0x2a, 0xa1, 0xdb, 0x80, 0xcc, 0x5d, 0xc7, 0x5f, 0xfd, 0x52, 0x16, 0x58,
+ 0xda, 0x5f, 0x94, 0x06, 0xa9, 0xb8, 0xb6, 0xb9, 0x63, 0x0c, 0x47, 0x20,
+ 0x82, 0xec, 0xc7, 0x95, 0x4e, 0x8b, 0xb8, 0x77, 0x52, 0x6a, 0x3d, 0xb5,
+ 0x87, 0xa9, 0xd6, 0xe1, 0xcc, 0x74, 0xe5, 0xa6, 0xc8, 0xc0, 0xd4, 0x56,
+ 0x4f, 0x8d, 0x2e, 0xd6, 0x08, 0x3e, 0x0c, 0x4c, 0x43, 0x3e, 0xf0, 0x41,
+ 0x93, 0x5e, 0x46, 0xef, 0x39, 0xe7, 0xd9, 0x65, 0x2a, 0x0c, 0x76, 0x50,
+ 0x27, 0xbd, 0x5b, 0x0d, 0x33, 0x33, 0x07, 0xe0, 0xf7, 0xa2, 0xa9, 0x9c,
+ 0xe1, 0x11, 0x33, 0xad, 0x66, 0xfc, 0xd2, 0x2c, 0x7a, 0xaa, 0xa3, 0x73,
+ 0x16, 0xbe, 0x93, 0x85, 0x75, 0x0f, 0xd7, 0x37, 0x8c, 0xfa, 0x23, 0xb7,
+ 0x64, 0xf8, 0xe3, 0x4c, 0x6e, 0xed, 0xb3, 0x05, 0xbd, 0xe2, 0x36, 0xdb,
+ 0x7c, 0xde, 0x76, 0x44, 0xda, 0x82, 0x72, 0x76, 0xb6, 0x6e, 0xff, 0x94,
+ 0xa1, 0xd0, 0x86, 0xf7, 0x10, 0xcd, 0x4a, 0x5a, 0x8b, 0xb0, 0x75, 0x8c,
+ 0x66, 0x52, 0x80, 0x4e, 0x48, 0x4c, 0x49, 0x83, 0xa6, 0x40, 0xd7, 0x77,
+ 0x81, 0x13, 0x4d, 0x5e, 0x72, 0x7e, 0x48, 0x46, 0x22, 0xaa, 0x0f, 0xe2,
+ 0x3e, 0x65, 0x94, 0x38, 0xe1, 0x72, 0x71, 0xfe, 0x4a, 0x71, 0x09, 0xba,
+ 0x35, 0x7f, 0x55, 0x89, 0x3d, 0x81, 0xd5, 0xb8, 0x28, 0x01, 0x10, 0x77,
+ 0x36, 0x5a, 0x10, 0x85, 0xd2, 0xbd, 0x60, 0x84, 0x2b, 0x49, 0x61, 0x94,
+ 0x0c, 0xde, 0x4c, 0x40, 0x6a, 0x2a, 0xc4, 0x79, 0x60, 0x84, 0x24, 0x82,
+ 0x32, 0x69, 0x4a, 0x98, 0x4b, 0xe2, 0x56, 0x10, 0xba, 0x03, 0x45, 0x51,
+ 0x20, 0xd3, 0xcf, 0xda, 0x8e, 0x54, 0x1b, 0x45, 0xb6, 0x7a, 0xba, 0x97,
+ 0x9a, 0x5a, 0xd8, 0xc6, 0xd1, 0x5f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x27, 0x30, 0x82, 0x01, 0x23, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x33, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30, 0x23, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x65, 0x6e,
+ 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x33, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30, 0x28, 0xa0, 0x26,
+ 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61, 0x31, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30,
+ 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30,
+ 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x5b, 0x41, 0x8a, 0xb2, 0xc4, 0x43, 0xc1, 0xbd, 0xbf, 0xc8,
+ 0x54, 0x41, 0x55, 0x9d, 0xe0, 0x96, 0xad, 0xff, 0xb9, 0xa1, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x68,
+ 0x90, 0xe4, 0x67, 0xa4, 0xa6, 0x53, 0x80, 0xc7, 0x86, 0x66, 0xa4, 0xf1,
+ 0xf7, 0x4b, 0x43, 0xfb, 0x84, 0xbd, 0x6d, 0x30, 0x19, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf6, 0x7d, 0x07, 0x41, 0x00, 0x04, 0x0c, 0x30, 0x0a,
+ 0x1b, 0x04, 0x56, 0x37, 0x2e, 0x31, 0x03, 0x02, 0x00, 0x81, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xb2, 0x3b, 0xd2, 0x9e, 0xc1, 0xbc,
+ 0x3b, 0x48, 0xb6, 0xdc, 0xd8, 0x5a, 0x18, 0x66, 0x53, 0xc3, 0xbd, 0x35,
+ 0x0d, 0x48, 0x42, 0x2c, 0x35, 0x01, 0xd8, 0x10, 0xa2, 0xe2, 0xe3, 0x8d,
+ 0x2c, 0xba, 0xa6, 0x03, 0x11, 0xed, 0x6b, 0xb1, 0x49, 0xcb, 0x5f, 0xcd,
+ 0xec, 0x60, 0xb3, 0xba, 0xd4, 0x02, 0xeb, 0x61, 0x4f, 0x4e, 0x7e, 0xf8,
+ 0xdf, 0x90, 0x5f, 0x4e, 0xd3, 0x90, 0x02, 0x1c, 0x52, 0xda, 0x12, 0x00,
+ 0x2f, 0x9b, 0x71, 0xda, 0x04, 0x12, 0x14, 0xc1, 0x90, 0x83, 0x2e, 0x28,
+ 0xd2, 0x10, 0x40, 0x11, 0x8b, 0x26, 0x2d, 0xeb, 0x99, 0x55, 0x54, 0x6f,
+ 0x60, 0x8e, 0xc5, 0x83, 0x1d, 0xc0, 0xa3, 0x3f, 0xd5, 0x8a, 0x14, 0x39,
+ 0x6a, 0x1b, 0x0d, 0xef, 0xd3, 0x5a, 0x77, 0x39, 0xcf, 0x69, 0xb4, 0xbd,
+ 0x69, 0x6f, 0x4f, 0x78, 0xd3, 0xa1, 0x86, 0xa3, 0x9b, 0xb7, 0xd7, 0xfb,
+ 0xaa, 0x2d, 0xf0, 0xfa, 0x26, 0xa1, 0xf9, 0x67, 0x2c, 0x88, 0x4b, 0xa5,
+ 0x34, 0xd5, 0x83, 0xfb, 0x4c, 0xf1, 0x5b, 0x70, 0x22, 0x66, 0x1b, 0x9b,
+ 0x59, 0x4f, 0x4d, 0xce, 0x98, 0xdb, 0x41, 0xa4, 0xfe, 0x1a, 0xa3, 0xeb,
+ 0x38, 0xe6, 0xf9, 0xf1, 0x39, 0x02, 0x9d, 0x46, 0xb6, 0xc9, 0xc2, 0x9e,
+ 0x3e, 0x82, 0xb6, 0x1f, 0x9f, 0xca, 0x4a, 0xa8, 0xb1, 0x06, 0x5f, 0x10,
+ 0x34, 0x3b, 0xfd, 0xda, 0x7b, 0xac, 0x33, 0x4e, 0xed, 0xa6, 0xb7, 0x4b,
+ 0xf3, 0x91, 0xf5, 0x9c, 0x0b, 0x11, 0x92, 0xdc, 0x13, 0x6a, 0xc8, 0xd5,
+ 0xf1, 0x3b, 0x6d, 0x96, 0x6b, 0x01, 0xe4, 0x23, 0x4c, 0xb1, 0xc1, 0xe0,
+ 0xd2, 0x12, 0x21, 0x9f, 0x29, 0xd4, 0xad, 0x95, 0x3d, 0xa6, 0xf7, 0xe7,
+ 0x32, 0xc5, 0x75, 0xb7, 0x0b, 0x57, 0xd8, 0xa4, 0xf9, 0xc0, 0xec, 0xec,
+ 0x32, 0x33, 0x0c, 0x4d, 0xae, 0xe8, 0x08, 0xd5, 0xec, 0xaa,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7b:11:55:eb:78:9a:90:85:b5:8c:92:ff:42:b7:fe:56
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Validity
+ Not Before: Nov 17 00:00:00 2006 GMT
+ Not After : Nov 16 23:59:59 2016 GMT
+ Subject: C=US, O=thawte, Inc., OU=Terms of use at https://www.thawte.com/cps (c)06, CN=thawte Extended Validation SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b5:8d:47:f7:b0:48:76:9b:bd:fb:a9:cb:bf:04:
+ 31:a2:3d:9a:7e:30:29:d3:28:b8:fe:68:ce:cf:e9:
+ 30:6a:53:95:0e:50:65:80:26:c9:98:bf:f2:14:ff:
+ 06:7c:6a:7b:dc:50:07:e2:98:fa:df:cf:30:5d:ca:
+ a8:b9:8a:9b:2d:2d:7e:59:8b:1a:f7:b3:c9:c3:69:
+ 80:0f:89:19:08:77:b2:52:55:ad:78:83:9d:6b:b9:
+ 87:e4:53:24:37:2c:fc:19:0e:8b:79:14:4d:be:80:
+ 9e:b4:9b:73:74:31:f2:38:ec:8a:af:2a:36:8e:64:
+ ce:31:26:14:03:54:53:8e:fb:84:08:c1:7e:47:32:
+ 3d:71:e0:ba:ba:8c:82:58:96:4d:68:43:56:1a:f3:
+ 46:5a:32:99:95:b0:60:6f:e9:41:8a:48:cc:16:0d:
+ 44:68:b1:8a:dd:dd:17:3d:a4:9b:78:7f:2e:29:06:
+ f0:dc:d5:d2:13:3f:c0:36:05:fd:c7:b5:b9:80:1b:
+ 8a:46:74:2f:f1:ab:79:9e:97:6e:f8:a5:13:5a:f3:
+ fc:b5:d7:c8:96:19:37:ee:06:bc:c6:27:14:81:05:
+ 14:33:38:16:9f:4b:e2:0f:db:38:bb:f3:01:ef:35:
+ 2e:de:af:f1:e4:6f:6f:f7:96:00:56:5e:8f:60:94:
+ 1d:2f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.thawte.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePCA.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Alternative Name:
+ DirName:/CN=PrivateLabel3-2048-234
+ X509v3 Subject Key Identifier:
+ CD:32:E2:F2:5D:25:47:02:AA:8F:79:4B:32:EE:03:99:FD:30:49:D1
+ X509v3 Authority Key Identifier:
+ keyid:7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 0b:b4:96:ce:03:0c:d1:9d:af:cb:e3:39:56:0d:c6:22:a0:c9:
+ 71:7d:ea:65:95:31:f1:dc:b6:1e:f2:8d:31:5d:61:b3:54:84:
+ 13:cc:2b:3f:02:5c:c7:1f:15:01:82:90:1e:31:25:06:e3:32:
+ 0c:87:f0:c3:be:9a:c4:00:41:f6:c6:91:e5:6c:3e:92:5d:a3:
+ e4:3d:1f:32:2d:31:1e:50:c1:02:21:b4:23:e3:07:75:9a:52:
+ 45:51:fa:d3:1d:fd:01:6f:60:6d:25:d9:bf:43:b1:a7:43:6c:
+ ad:8c:bb:bc:f7:99:41:eb:d6:95:cf:20:5c:7e:6f:c4:2a:da:
+ 4b:4d:1b:5b:c2:9f:b0:94:d4:bf:47:97:fd:9d:49:79:60:8e:
+ ae:96:19:a1:b0:eb:e8:df:42:c7:22:74:61:0c:25:a3:7f:8f:
+ 45:d2:7e:e7:4a:6e:1d:4f:48:bb:c2:da:1a:7e:4a:59:81:fa:
+ 1c:e3:fb:14:73:41:03:a1:77:fa:9b:06:fc:7c:33:bd:46:3d:
+ 0c:06:17:85:7b:2a:7b:e3:36:e8:83:df:fa:aa:cb:32:0c:79:
+ aa:86:74:6c:44:54:f6:d8:07:9e:cd:98:f4:23:05:09:2f:a2:
+ 53:b5:db:0a:81:cc:5f:23:cb:79:11:c5:11:5b:85:6b:27:01:
+ 89:f3:0e:bb
+-----BEGIN CERTIFICATE-----
+MIIFCjCCA/KgAwIBAgIQexFV63iakIW1jJL/Qrf+VjANBgkqhkiG9w0BAQUFADCB
+qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
+Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
+MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
+BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMTYx
+MTE2MjM1OTU5WjCBizELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
+LjE5MDcGA1UECxMwVGVybXMgb2YgdXNlIGF0IGh0dHBzOi8vd3d3LnRoYXd0ZS5j
+b20vY3BzIChjKTA2MSowKAYDVQQDEyF0aGF3dGUgRXh0ZW5kZWQgVmFsaWRhdGlv
+biBTU0wgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1jUf3sEh2
+m737qcu/BDGiPZp+MCnTKLj+aM7P6TBqU5UOUGWAJsmYv/IU/wZ8anvcUAfimPrf
+zzBdyqi5ipstLX5Zixr3s8nDaYAPiRkId7JSVa14g51ruYfkUyQ3LPwZDot5FE2+
+gJ60m3N0MfI47IqvKjaOZM4xJhQDVFOO+4QIwX5HMj1x4Lq6jIJYlk1oQ1Ya80Za
+MpmVsGBv6UGKSMwWDURosYrd3Rc9pJt4fy4pBvDc1dITP8A2Bf3HtbmAG4pGdC/x
+q3mel274pRNa8/y118iWGTfuBrzGJxSBBRQzOBafS+IP2zi78wHvNS7er/Hkb2/3
+lgBWXo9glB0vAgMBAAGjggFIMIIBRDA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUH
+MAGGH2h0dHA6Ly9FVlNlY3VyZS1vY3NwLnRoYXd0ZS5jb20wEgYDVR0TAQH/BAgw
+BgEB/wIBADA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggrBgEFBQcCARYaaHR0cHM6
+Ly93d3cudGhhd3RlLmNvbS9jcHMwNAYDVR0fBC0wKzApoCegJYYjaHR0cDovL2Ny
+bC50aGF3dGUuY29tL1RoYXd0ZVBDQS5jcmwwDgYDVR0PAQH/BAQDAgEGMC4GA1Ud
+EQQnMCWkIzAhMR8wHQYDVQQDExZQcml2YXRlTGFiZWwzLTIwNDgtMjM0MB0GA1Ud
+DgQWBBTNMuLyXSVHAqqPeUsy7gOZ/TBJ0TAfBgNVHSMEGDAWgBR7W0XPr87Lev0x
+khpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAC7SWzgMM0Z2vy+M5Vg3GIqDJ
+cX3qZZUx8dy2HvKNMV1hs1SEE8wrPwJcxx8VAYKQHjElBuMyDIfww76axABB9saR
+5Ww+kl2j5D0fMi0xHlDBAiG0I+MHdZpSRVH60x39AW9gbSXZv0Oxp0NsrYy7vPeZ
+QevWlc8gXH5vxCraS00bW8KfsJTUv0eX/Z1JeWCOrpYZobDr6N9CxyJ0YQwlo3+P
+RdJ+50puHU9Iu8LaGn5KWYH6HOP7FHNBA6F3+psG/HwzvUY9DAYXhXsqe+M26IPf
++qrLMgx5qoZ0bERU9tgHns2Y9CMFCS+iU7XbCoHMXyPLeRHFEVuFaycBifMOuw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert79[] = {
+ 0x30, 0x82, 0x05, 0x0a, 0x30, 0x82, 0x03, 0xf2, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x7b, 0x11, 0x55, 0xeb, 0x78, 0x9a, 0x90, 0x85, 0xb5,
+ 0x8c, 0x92, 0xff, 0x42, 0xb7, 0xfe, 0x56, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1f,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20, 0x44,
+ 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32, 0x30,
+ 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
+ 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, 0x37,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31,
+ 0x31, 0x31, 0x36, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81,
+ 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30,
+ 0x54, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x61, 0x74, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f,
+ 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36,
+ 0x31, 0x2a, 0x30, 0x28, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x74,
+ 0x68, 0x61, 0x77, 0x74, 0x65, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64,
+ 0x65, 0x64, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xb5, 0x8d, 0x47, 0xf7, 0xb0, 0x48, 0x76,
+ 0x9b, 0xbd, 0xfb, 0xa9, 0xcb, 0xbf, 0x04, 0x31, 0xa2, 0x3d, 0x9a, 0x7e,
+ 0x30, 0x29, 0xd3, 0x28, 0xb8, 0xfe, 0x68, 0xce, 0xcf, 0xe9, 0x30, 0x6a,
+ 0x53, 0x95, 0x0e, 0x50, 0x65, 0x80, 0x26, 0xc9, 0x98, 0xbf, 0xf2, 0x14,
+ 0xff, 0x06, 0x7c, 0x6a, 0x7b, 0xdc, 0x50, 0x07, 0xe2, 0x98, 0xfa, 0xdf,
+ 0xcf, 0x30, 0x5d, 0xca, 0xa8, 0xb9, 0x8a, 0x9b, 0x2d, 0x2d, 0x7e, 0x59,
+ 0x8b, 0x1a, 0xf7, 0xb3, 0xc9, 0xc3, 0x69, 0x80, 0x0f, 0x89, 0x19, 0x08,
+ 0x77, 0xb2, 0x52, 0x55, 0xad, 0x78, 0x83, 0x9d, 0x6b, 0xb9, 0x87, 0xe4,
+ 0x53, 0x24, 0x37, 0x2c, 0xfc, 0x19, 0x0e, 0x8b, 0x79, 0x14, 0x4d, 0xbe,
+ 0x80, 0x9e, 0xb4, 0x9b, 0x73, 0x74, 0x31, 0xf2, 0x38, 0xec, 0x8a, 0xaf,
+ 0x2a, 0x36, 0x8e, 0x64, 0xce, 0x31, 0x26, 0x14, 0x03, 0x54, 0x53, 0x8e,
+ 0xfb, 0x84, 0x08, 0xc1, 0x7e, 0x47, 0x32, 0x3d, 0x71, 0xe0, 0xba, 0xba,
+ 0x8c, 0x82, 0x58, 0x96, 0x4d, 0x68, 0x43, 0x56, 0x1a, 0xf3, 0x46, 0x5a,
+ 0x32, 0x99, 0x95, 0xb0, 0x60, 0x6f, 0xe9, 0x41, 0x8a, 0x48, 0xcc, 0x16,
+ 0x0d, 0x44, 0x68, 0xb1, 0x8a, 0xdd, 0xdd, 0x17, 0x3d, 0xa4, 0x9b, 0x78,
+ 0x7f, 0x2e, 0x29, 0x06, 0xf0, 0xdc, 0xd5, 0xd2, 0x13, 0x3f, 0xc0, 0x36,
+ 0x05, 0xfd, 0xc7, 0xb5, 0xb9, 0x80, 0x1b, 0x8a, 0x46, 0x74, 0x2f, 0xf1,
+ 0xab, 0x79, 0x9e, 0x97, 0x6e, 0xf8, 0xa5, 0x13, 0x5a, 0xf3, 0xfc, 0xb5,
+ 0xd7, 0xc8, 0x96, 0x19, 0x37, 0xee, 0x06, 0xbc, 0xc6, 0x27, 0x14, 0x81,
+ 0x05, 0x14, 0x33, 0x38, 0x16, 0x9f, 0x4b, 0xe2, 0x0f, 0xdb, 0x38, 0xbb,
+ 0xf3, 0x01, 0xef, 0x35, 0x2e, 0xde, 0xaf, 0xf1, 0xe4, 0x6f, 0x6f, 0xf7,
+ 0x96, 0x00, 0x56, 0x5e, 0x8f, 0x60, 0x94, 0x1d, 0x2f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0x48, 0x30, 0x82, 0x01, 0x44, 0x30, 0x3b,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2f,
+ 0x30, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x01, 0x86, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45,
+ 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3b, 0x06, 0x03, 0x55,
+ 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d,
+ 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30, 0x34, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0,
+ 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x27, 0x30, 0x25, 0xa4, 0x23, 0x30, 0x21, 0x31, 0x1f, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x50, 0x72, 0x69, 0x76,
+ 0x61, 0x74, 0x65, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x33, 0x2d, 0x32, 0x30,
+ 0x34, 0x38, 0x2d, 0x32, 0x33, 0x34, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcd, 0x32, 0xe2, 0xf2, 0x5d, 0x25, 0x47,
+ 0x02, 0xaa, 0x8f, 0x79, 0x4b, 0x32, 0xee, 0x03, 0x99, 0xfd, 0x30, 0x49,
+ 0xd1, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31,
+ 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x0b, 0xb4, 0x96, 0xce, 0x03, 0x0c,
+ 0xd1, 0x9d, 0xaf, 0xcb, 0xe3, 0x39, 0x56, 0x0d, 0xc6, 0x22, 0xa0, 0xc9,
+ 0x71, 0x7d, 0xea, 0x65, 0x95, 0x31, 0xf1, 0xdc, 0xb6, 0x1e, 0xf2, 0x8d,
+ 0x31, 0x5d, 0x61, 0xb3, 0x54, 0x84, 0x13, 0xcc, 0x2b, 0x3f, 0x02, 0x5c,
+ 0xc7, 0x1f, 0x15, 0x01, 0x82, 0x90, 0x1e, 0x31, 0x25, 0x06, 0xe3, 0x32,
+ 0x0c, 0x87, 0xf0, 0xc3, 0xbe, 0x9a, 0xc4, 0x00, 0x41, 0xf6, 0xc6, 0x91,
+ 0xe5, 0x6c, 0x3e, 0x92, 0x5d, 0xa3, 0xe4, 0x3d, 0x1f, 0x32, 0x2d, 0x31,
+ 0x1e, 0x50, 0xc1, 0x02, 0x21, 0xb4, 0x23, 0xe3, 0x07, 0x75, 0x9a, 0x52,
+ 0x45, 0x51, 0xfa, 0xd3, 0x1d, 0xfd, 0x01, 0x6f, 0x60, 0x6d, 0x25, 0xd9,
+ 0xbf, 0x43, 0xb1, 0xa7, 0x43, 0x6c, 0xad, 0x8c, 0xbb, 0xbc, 0xf7, 0x99,
+ 0x41, 0xeb, 0xd6, 0x95, 0xcf, 0x20, 0x5c, 0x7e, 0x6f, 0xc4, 0x2a, 0xda,
+ 0x4b, 0x4d, 0x1b, 0x5b, 0xc2, 0x9f, 0xb0, 0x94, 0xd4, 0xbf, 0x47, 0x97,
+ 0xfd, 0x9d, 0x49, 0x79, 0x60, 0x8e, 0xae, 0x96, 0x19, 0xa1, 0xb0, 0xeb,
+ 0xe8, 0xdf, 0x42, 0xc7, 0x22, 0x74, 0x61, 0x0c, 0x25, 0xa3, 0x7f, 0x8f,
+ 0x45, 0xd2, 0x7e, 0xe7, 0x4a, 0x6e, 0x1d, 0x4f, 0x48, 0xbb, 0xc2, 0xda,
+ 0x1a, 0x7e, 0x4a, 0x59, 0x81, 0xfa, 0x1c, 0xe3, 0xfb, 0x14, 0x73, 0x41,
+ 0x03, 0xa1, 0x77, 0xfa, 0x9b, 0x06, 0xfc, 0x7c, 0x33, 0xbd, 0x46, 0x3d,
+ 0x0c, 0x06, 0x17, 0x85, 0x7b, 0x2a, 0x7b, 0xe3, 0x36, 0xe8, 0x83, 0xdf,
+ 0xfa, 0xaa, 0xcb, 0x32, 0x0c, 0x79, 0xaa, 0x86, 0x74, 0x6c, 0x44, 0x54,
+ 0xf6, 0xd8, 0x07, 0x9e, 0xcd, 0x98, 0xf4, 0x23, 0x05, 0x09, 0x2f, 0xa2,
+ 0x53, 0xb5, 0xdb, 0x0a, 0x81, 0xcc, 0x5f, 0x23, 0xcb, 0x79, 0x11, 0xc5,
+ 0x11, 0x5b, 0x85, 0x6b, 0x27, 0x01, 0x89, 0xf3, 0x0e, 0xbb,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1276037400 (0x4c0ec918)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority
+ Validity
+ Not Before: Nov 11 14:57:22 2011 GMT
+ Not After : Nov 12 08:12:31 2021 GMT
+ Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/rpa is incorporated by reference, OU=(c) 2009 Entrust, Inc., CN=Entrust Certification Authority - L1E
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:5b:04:54:77:dd:0e:24:66:dc:2a:a1:db:80:
+ cc:5d:c7:5f:fd:52:16:58:da:5f:94:06:a9:b8:b6:
+ b9:63:0c:47:20:82:ec:c7:95:4e:8b:b8:77:52:6a:
+ 3d:b5:87:a9:d6:e1:cc:74:e5:a6:c8:c0:d4:56:4f:
+ 8d:2e:d6:08:3e:0c:4c:43:3e:f0:41:93:5e:46:ef:
+ 39:e7:d9:65:2a:0c:76:50:27:bd:5b:0d:33:33:07:
+ e0:f7:a2:a9:9c:e1:11:33:ad:66:fc:d2:2c:7a:aa:
+ a3:73:16:be:93:85:75:0f:d7:37:8c:fa:23:b7:64:
+ f8:e3:4c:6e:ed:b3:05:bd:e2:36:db:7c:de:76:44:
+ da:82:72:76:b6:6e:ff:94:a1:d0:86:f7:10:cd:4a:
+ 5a:8b:b0:75:8c:66:52:80:4e:48:4c:49:83:a6:40:
+ d7:77:81:13:4d:5e:72:7e:48:46:22:aa:0f:e2:3e:
+ 65:94:38:e1:72:71:fe:4a:71:09:ba:35:7f:55:89:
+ 3d:81:d5:b8:28:01:10:77:36:5a:10:85:d2:bd:60:
+ 84:2b:49:61:94:0c:de:4c:40:6a:2a:c4:79:60:84:
+ 24:82:32:69:4a:98:4b:e2:56:10:ba:03:45:51:20:
+ d3:cf:da:8e:54:1b:45:b6:7a:ba:97:9a:5a:d8:c6:
+ d1:5f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.entrust.net
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.entrust.net/rootca1.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://www.entrust.net/CPS
+
+ X509v3 Subject Key Identifier:
+ 5B:41:8A:B2:C4:43:C1:BD:BF:C8:54:41:55:9D:E0:96:AD:FF:B9:A1
+ X509v3 Authority Key Identifier:
+ keyid:68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D
+
+ 1.2.840.113533.7.65.0:
+ 0
+..V8.1....
+ Signature Algorithm: sha1WithRSAEncryption
+ a1:f1:a8:10:e8:e6:29:b9:22:6c:61:5b:2a:3f:3c:01:c7:82:
+ 21:0b:e8:4e:0f:c4:c9:c6:bc:99:9d:f6:ef:5b:c7:69:b2:d9:
+ 9e:ac:52:42:e9:8a:b8:31:c4:13:96:03:8f:65:93:06:69:fe:
+ 28:b6:a6:fd:ad:87:8c:d5:cc:a6:e7:f9:1a:37:ef:32:2d:05:
+ 2d:1e:4e:b9:d5:d5:d1:0f:9b:7f:24:4e:b8:90:ec:e6:69:bf:
+ 9f:2a:3c:63:02:e1:69:a3:6e:a0:34:72:c8:50:50:b6:da:8e:
+ 92:2e:b8:4b:28:fe:f4:92:f0:04:b6:d6:9d:3d:07:66:11:75:
+ 6d:85:71:5e:32:f2:d7:0c:db:30:21:15:e1:74:b7:b5:eb:6b:
+ f9:73:ea:0a:49:ad:48:f6:23:23:8c:60:47:2c:51:96:b1:cc:
+ 23:77:cd:96:c5:c6:cd:b5:4c:2c:95:f7:22:45:f8:b6:ad:84:
+ 0c:08:ca:13:b0:a8:9d:35:6f:8b:48:d8:5f:b6:2b:a7:a8:27:
+ 44:c3:0c:8e:a6:0d:e3:64:26:61:92:97:13:5e:80:31:0c:b7:
+ 9e:90:20:87:0b:d0:aa:0a:06:04:27:3c:86:6a:20:0d:9d:bb:
+ ce:7d:57:c9:59:93:a2:03:3b:8c:b3:6f:42:fd:a4:d5:9b:ca:
+ 01:aa:04:0c
+-----BEGIN CERTIFICATE-----
+MIIFDTCCA/WgAwIBAgIETA7JGDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
+VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
+Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
+KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTExMTExMTE0NTcyMloXDTIxMTExMjA4
+MTIzMVowgbExCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
+NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvcnBhIGlzIGluY29ycG9yYXRlZCBieSBy
+ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA5IEVudHJ1c3QsIEluYy4xLjAsBgNV
+BAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBMMUUwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2WwRUd90OJGbcKqHbgMxdx1/9UhZY
+2l+UBqm4trljDEcgguzHlU6LuHdSaj21h6nW4cx05abIwNRWT40u1gg+DExDPvBB
+k15G7znn2WUqDHZQJ71bDTMzB+D3oqmc4REzrWb80ix6qqNzFr6ThXUP1zeM+iO3
+ZPjjTG7tswW94jbbfN52RNqCcna2bv+UodCG9xDNSlqLsHWMZlKATkhMSYOmQNd3
+gRNNXnJ+SEYiqg/iPmWUOOFycf5KcQm6NX9ViT2B1bgoARB3NloQhdK9YIQrSWGU
+DN5MQGoqxHlghCSCMmlKmEviVhC6A0VRINPP2o5UG0W2erqXmlrYxtFfAgMBAAGj
+ggEqMIIBJjAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAzBggr
+BgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0
+MDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvcm9vdGNh
+MS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly93
+d3cuZW50cnVzdC5uZXQvQ1BTMB0GA1UdDgQWBBRbQYqyxEPBvb/IVEFVneCWrf+5
+oTAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAZBgkqhkiG9n0HQQAE
+DDAKGwRWOC4xAwIAgTANBgkqhkiG9w0BAQUFAAOCAQEAofGoEOjmKbkibGFbKj88
+AceCIQvoTg/Eyca8mZ3271vHabLZnqxSQumKuDHEE5YDj2WTBmn+KLam/a2HjNXM
+puf5GjfvMi0FLR5OudXV0Q+bfyROuJDs5mm/nyo8YwLhaaNuoDRyyFBQttqOki64
+Syj+9JLwBLbWnT0HZhF1bYVxXjLy1wzbMCEV4XS3tetr+XPqCkmtSPYjI4xgRyxR
+lrHMI3fNlsXGzbVMLJX3IkX4tq2EDAjKE7ConTVvi0jYX7Yrp6gnRMMMjqYN42Qm
+YZKXE16AMQy3npAghwvQqgoGBCc8hmogDZ27zn1XyVmTogM7jLNvQv2k1ZvKAaoE
+DA==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert80[] = {
+ 0x30, 0x82, 0x05, 0x0d, 0x30, 0x82, 0x03, 0xf5, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x4c, 0x0e, 0xc9, 0x18, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xb0, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x30, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74,
+ 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x20, 0x69, 0x73, 0x20,
+ 0x69, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64,
+ 0x20, 0x62, 0x79, 0x20, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16,
+ 0x28, 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2d,
+ 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x45, 0x6e, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x34, 0x35, 0x37, 0x32,
+ 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x31, 0x32, 0x30, 0x38,
+ 0x31, 0x32, 0x33, 0x31, 0x5a, 0x30, 0x81, 0xb1, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30,
+ 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x45, 0x6e, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x39, 0x30,
+ 0x37, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x30, 0x77, 0x77, 0x77, 0x2e,
+ 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f,
+ 0x72, 0x70, 0x61, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x72,
+ 0x70, 0x6f, 0x72, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x72,
+ 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x39, 0x20, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2c,
+ 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x2e, 0x30, 0x2c, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x25, 0x45, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x2d, 0x20, 0x4c, 0x31, 0x45, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xb6, 0x5b, 0x04, 0x54, 0x77, 0xdd, 0x0e, 0x24, 0x66, 0xdc,
+ 0x2a, 0xa1, 0xdb, 0x80, 0xcc, 0x5d, 0xc7, 0x5f, 0xfd, 0x52, 0x16, 0x58,
+ 0xda, 0x5f, 0x94, 0x06, 0xa9, 0xb8, 0xb6, 0xb9, 0x63, 0x0c, 0x47, 0x20,
+ 0x82, 0xec, 0xc7, 0x95, 0x4e, 0x8b, 0xb8, 0x77, 0x52, 0x6a, 0x3d, 0xb5,
+ 0x87, 0xa9, 0xd6, 0xe1, 0xcc, 0x74, 0xe5, 0xa6, 0xc8, 0xc0, 0xd4, 0x56,
+ 0x4f, 0x8d, 0x2e, 0xd6, 0x08, 0x3e, 0x0c, 0x4c, 0x43, 0x3e, 0xf0, 0x41,
+ 0x93, 0x5e, 0x46, 0xef, 0x39, 0xe7, 0xd9, 0x65, 0x2a, 0x0c, 0x76, 0x50,
+ 0x27, 0xbd, 0x5b, 0x0d, 0x33, 0x33, 0x07, 0xe0, 0xf7, 0xa2, 0xa9, 0x9c,
+ 0xe1, 0x11, 0x33, 0xad, 0x66, 0xfc, 0xd2, 0x2c, 0x7a, 0xaa, 0xa3, 0x73,
+ 0x16, 0xbe, 0x93, 0x85, 0x75, 0x0f, 0xd7, 0x37, 0x8c, 0xfa, 0x23, 0xb7,
+ 0x64, 0xf8, 0xe3, 0x4c, 0x6e, 0xed, 0xb3, 0x05, 0xbd, 0xe2, 0x36, 0xdb,
+ 0x7c, 0xde, 0x76, 0x44, 0xda, 0x82, 0x72, 0x76, 0xb6, 0x6e, 0xff, 0x94,
+ 0xa1, 0xd0, 0x86, 0xf7, 0x10, 0xcd, 0x4a, 0x5a, 0x8b, 0xb0, 0x75, 0x8c,
+ 0x66, 0x52, 0x80, 0x4e, 0x48, 0x4c, 0x49, 0x83, 0xa6, 0x40, 0xd7, 0x77,
+ 0x81, 0x13, 0x4d, 0x5e, 0x72, 0x7e, 0x48, 0x46, 0x22, 0xaa, 0x0f, 0xe2,
+ 0x3e, 0x65, 0x94, 0x38, 0xe1, 0x72, 0x71, 0xfe, 0x4a, 0x71, 0x09, 0xba,
+ 0x35, 0x7f, 0x55, 0x89, 0x3d, 0x81, 0xd5, 0xb8, 0x28, 0x01, 0x10, 0x77,
+ 0x36, 0x5a, 0x10, 0x85, 0xd2, 0xbd, 0x60, 0x84, 0x2b, 0x49, 0x61, 0x94,
+ 0x0c, 0xde, 0x4c, 0x40, 0x6a, 0x2a, 0xc4, 0x79, 0x60, 0x84, 0x24, 0x82,
+ 0x32, 0x69, 0x4a, 0x98, 0x4b, 0xe2, 0x56, 0x10, 0xba, 0x03, 0x45, 0x51,
+ 0x20, 0xd3, 0xcf, 0xda, 0x8e, 0x54, 0x1b, 0x45, 0xb6, 0x7a, 0xba, 0x97,
+ 0x9a, 0x5a, 0xd8, 0xc6, 0xd1, 0x5f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x01, 0x2a, 0x30, 0x82, 0x01, 0x26, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30,
+ 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x33, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x27, 0x30, 0x25, 0x30,
+ 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86,
+ 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70,
+ 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e, 0x65, 0x74,
+ 0x30, 0x33, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2c, 0x30, 0x2a, 0x30,
+ 0x28, 0xa0, 0x26, 0xa0, 0x24, 0x86, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x63, 0x61,
+ 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x20,
+ 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00,
+ 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6e,
+ 0x65, 0x74, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x5b, 0x41, 0x8a, 0xb2, 0xc4, 0x43, 0xc1,
+ 0xbd, 0xbf, 0xc8, 0x54, 0x41, 0x55, 0x9d, 0xe0, 0x96, 0xad, 0xff, 0xb9,
+ 0xa1, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x68, 0x90, 0xe4, 0x67, 0xa4, 0xa6, 0x53, 0x80, 0xc7, 0x86,
+ 0x66, 0xa4, 0xf1, 0xf7, 0x4b, 0x43, 0xfb, 0x84, 0xbd, 0x6d, 0x30, 0x19,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf6, 0x7d, 0x07, 0x41, 0x00, 0x04,
+ 0x0c, 0x30, 0x0a, 0x1b, 0x04, 0x56, 0x38, 0x2e, 0x31, 0x03, 0x02, 0x00,
+ 0x81, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xa1, 0xf1, 0xa8,
+ 0x10, 0xe8, 0xe6, 0x29, 0xb9, 0x22, 0x6c, 0x61, 0x5b, 0x2a, 0x3f, 0x3c,
+ 0x01, 0xc7, 0x82, 0x21, 0x0b, 0xe8, 0x4e, 0x0f, 0xc4, 0xc9, 0xc6, 0xbc,
+ 0x99, 0x9d, 0xf6, 0xef, 0x5b, 0xc7, 0x69, 0xb2, 0xd9, 0x9e, 0xac, 0x52,
+ 0x42, 0xe9, 0x8a, 0xb8, 0x31, 0xc4, 0x13, 0x96, 0x03, 0x8f, 0x65, 0x93,
+ 0x06, 0x69, 0xfe, 0x28, 0xb6, 0xa6, 0xfd, 0xad, 0x87, 0x8c, 0xd5, 0xcc,
+ 0xa6, 0xe7, 0xf9, 0x1a, 0x37, 0xef, 0x32, 0x2d, 0x05, 0x2d, 0x1e, 0x4e,
+ 0xb9, 0xd5, 0xd5, 0xd1, 0x0f, 0x9b, 0x7f, 0x24, 0x4e, 0xb8, 0x90, 0xec,
+ 0xe6, 0x69, 0xbf, 0x9f, 0x2a, 0x3c, 0x63, 0x02, 0xe1, 0x69, 0xa3, 0x6e,
+ 0xa0, 0x34, 0x72, 0xc8, 0x50, 0x50, 0xb6, 0xda, 0x8e, 0x92, 0x2e, 0xb8,
+ 0x4b, 0x28, 0xfe, 0xf4, 0x92, 0xf0, 0x04, 0xb6, 0xd6, 0x9d, 0x3d, 0x07,
+ 0x66, 0x11, 0x75, 0x6d, 0x85, 0x71, 0x5e, 0x32, 0xf2, 0xd7, 0x0c, 0xdb,
+ 0x30, 0x21, 0x15, 0xe1, 0x74, 0xb7, 0xb5, 0xeb, 0x6b, 0xf9, 0x73, 0xea,
+ 0x0a, 0x49, 0xad, 0x48, 0xf6, 0x23, 0x23, 0x8c, 0x60, 0x47, 0x2c, 0x51,
+ 0x96, 0xb1, 0xcc, 0x23, 0x77, 0xcd, 0x96, 0xc5, 0xc6, 0xcd, 0xb5, 0x4c,
+ 0x2c, 0x95, 0xf7, 0x22, 0x45, 0xf8, 0xb6, 0xad, 0x84, 0x0c, 0x08, 0xca,
+ 0x13, 0xb0, 0xa8, 0x9d, 0x35, 0x6f, 0x8b, 0x48, 0xd8, 0x5f, 0xb6, 0x2b,
+ 0xa7, 0xa8, 0x27, 0x44, 0xc3, 0x0c, 0x8e, 0xa6, 0x0d, 0xe3, 0x64, 0x26,
+ 0x61, 0x92, 0x97, 0x13, 0x5e, 0x80, 0x31, 0x0c, 0xb7, 0x9e, 0x90, 0x20,
+ 0x87, 0x0b, 0xd0, 0xaa, 0x0a, 0x06, 0x04, 0x27, 0x3c, 0x86, 0x6a, 0x20,
+ 0x0d, 0x9d, 0xbb, 0xce, 0x7d, 0x57, 0xc9, 0x59, 0x93, 0xa2, 0x03, 0x3b,
+ 0x8c, 0xb3, 0x6f, 0x42, 0xfd, 0xa4, 0xd5, 0x9b, 0xca, 0x01, 0xaa, 0x04,
+ 0x0c,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 268 (0x10c)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com
+ Validity
+ Not Before: Jun 29 17:39:16 2004 GMT
+ Not After : Jun 29 17:39:16 2024 GMT
+ Subject: C=US, O=Starfield Technologies, Inc., OU=Starfield Class 2 Certification Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b7:32:c8:fe:e9:71:a6:04:85:ad:0c:11:64:df:
+ ce:4d:ef:c8:03:18:87:3f:a1:ab:fb:3c:a6:9f:f0:
+ c3:a1:da:d4:d8:6e:2b:53:90:fb:24:a4:3e:84:f0:
+ 9e:e8:5f:ec:e5:27:44:f5:28:a6:3f:7b:de:e0:2a:
+ f0:c8:af:53:2f:9e:ca:05:01:93:1e:8f:66:1c:39:
+ a7:4d:fa:5a:b6:73:04:25:66:eb:77:7f:e7:59:c6:
+ 4a:99:25:14:54:eb:26:c7:f3:7f:19:d5:30:70:8f:
+ af:b0:46:2a:ff:ad:eb:29:ed:d7:9f:aa:04:87:a3:
+ d4:f9:89:a5:34:5f:db:43:91:82:36:d9:66:3c:b1:
+ b8:b9:82:fd:9c:3a:3e:10:c8:3b:ef:06:65:66:7a:
+ 9b:19:18:3d:ff:71:51:3c:30:2e:5f:be:3d:77:73:
+ b2:5d:06:6c:c3:23:56:9a:2b:85:26:92:1c:a7:02:
+ b3:e4:3f:0d:af:08:79:82:b8:36:3d:ea:9c:d3:35:
+ b3:bc:69:ca:f5:cc:9d:e8:fd:64:8d:17:80:33:6e:
+ 5e:4a:5d:99:c9:1e:87:b4:9d:1a:c0:d5:6e:13:35:
+ 23:5e:df:9b:5f:3d:ef:d6:f7:76:c2:ea:3e:bb:78:
+ 0d:1c:42:67:6b:04:d8:f8:d6:da:6f:8b:f2:44:a0:
+ 01:ab
+ Exponent: 3 (0x3)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ BF:5F:B7:D1:CE:DD:1F:86:F4:5B:55:AC:DC:D7:10:C2:0E:A9:88:E7
+ X509v3 Authority Key Identifier:
+ DirName:/L=ValiCert Validation Network/O=ValiCert, Inc./OU=ValiCert Class 2 Policy Validation Authority/CN=http://www.valicert.com//emailAddress=info@valicert.com
+ serial:01
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.starfieldtech.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://certificates.starfieldtech.com/repository/root.crl
+
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: http://certificates.starfieldtech.com/repository
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ a5:62:f1:a7:c2:5d:25:a5:70:3d:bc:e2:2a:71:b3:7d:e4:0d:
+ 37:1d:55:6a:6d:a1:b0:ab:98:00:e4:85:60:4a:20:cb:a1:f0:
+ 3d:75:f7:94:da:43:7f:68:5c:25:08:b8:d2:7d:33:10:7b:dc:
+ 76:67:f1:b8:e1:53:7c:e1:c6:83:5b:29:7b:8d:4a:6f:2e:7f:
+ 44:a8:1a:45:6b:32:07:c7:78:ca:64:92:c2:b4:84:0c:dc:dd:
+ 2d:5f:4d:bb:dd:8a:ea:38:dc:d9:66:a2:ec:41:ba:55:6d:5a:
+ 64:3d:b7:03:cc:1c:e2:91:50:9e:e3:09:44:95:17:17:73:3d:
+ cc:25
+-----BEGIN CERTIFICATE-----
+MIIFEjCCBHugAwIBAgICAQwwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh
+bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe
+BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MzkxNloX
+DTI0MDYyOTE3MzkxNlowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVs
+ZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAy
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0A
+MIIBCAKCAQEAtzLI/ulxpgSFrQwRZN/OTe/IAxiHP6Gr+zymn/DDodrU2G4rU5D7
+JKQ+hPCe6F/s5SdE9SimP3ve4CrwyK9TL57KBQGTHo9mHDmnTfpatnMEJWbrd3/n
+WcZKmSUUVOsmx/N/GdUwcI+vsEYq/63rKe3Xn6oEh6PU+YmlNF/bQ5GCNtlmPLG4
+uYL9nDo+EMg77wZlZnqbGRg9/3FRPDAuX749d3OyXQZswyNWmiuFJpIcpwKz5D8N
+rwh5grg2Peqc0zWzvGnK9cyd6P1kjReAM25eSl2ZyR6HtJ0awNVuEzUjXt+bXz3v
+1vd2wuo+u3gNHEJnawTY+Nbab4vyRKABqwIBA6OCAfMwggHvMB0GA1UdDgQWBBS/
+X7fRzt0fhvRbVazc1xDCDqmI5zCB0gYDVR0jBIHKMIHHoYHBpIG+MIG7MSQwIgYD
+VQQHExtWYWxpQ2VydCBWYWxpZGF0aW9uIE5ldHdvcmsxFzAVBgNVBAoTDlZhbGlD
+ZXJ0LCBJbmMuMTUwMwYDVQQLEyxWYWxpQ2VydCBDbGFzcyAyIFBvbGljeSBWYWxp
+ZGF0aW9uIEF1dGhvcml0eTEhMB8GA1UEAxMYaHR0cDovL3d3dy52YWxpY2VydC5j
+b20vMSAwHgYJKoZIhvcNAQkBFhFpbmZvQHZhbGljZXJ0LmNvbYIBATAPBgNVHRMB
+Af8EBTADAQH/MDkGCCsGAQUFBwEBBC0wKzApBggrBgEFBQcwAYYdaHR0cDovL29j
+c3Auc3RhcmZpZWxkdGVjaC5jb20wSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2Nl
+cnRpZmljYXRlcy5zdGFyZmllbGR0ZWNoLmNvbS9yZXBvc2l0b3J5L3Jvb3QuY3Js
+MFEGA1UdIARKMEgwRgYEVR0gADA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY2VydGlm
+aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkwDgYDVR0PAQH/BAQD
+AgEGMA0GCSqGSIb3DQEBBQUAA4GBAKVi8afCXSWlcD284ipxs33kDTcdVWptobCr
+mADkhWBKIMuh8D1195TaQ39oXCUIuNJ9MxB73HZn8bjhU3zhxoNbKXuNSm8uf0So
+GkVrMgfHeMpkksK0hAzc3S1fTbvdiuo43NlmouxBulVtWmQ9twPMHOKRUJ7jCUSV
+FxdzPcwl
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert81[] = {
+ 0x30, 0x82, 0x05, 0x12, 0x30, 0x82, 0x04, 0x7b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x02, 0x01, 0x0c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0xbb, 0x31,
+ 0x24, 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x1b, 0x56, 0x61,
+ 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x35, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2c, 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
+ 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x31, 0x20, 0x30, 0x1e,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+ 0x11, 0x69, 0x6e, 0x66, 0x6f, 0x40, 0x76, 0x61, 0x6c, 0x69, 0x63, 0x65,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x34,
+ 0x30, 0x36, 0x32, 0x39, 0x31, 0x37, 0x33, 0x39, 0x31, 0x36, 0x5a, 0x17,
+ 0x0d, 0x32, 0x34, 0x30, 0x36, 0x32, 0x39, 0x31, 0x37, 0x33, 0x39, 0x31,
+ 0x36, 0x5a, 0x30, 0x68, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x1c, 0x53, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c,
+ 0x64, 0x20, 0x54, 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0x6f, 0x67, 0x69,
+ 0x65, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x32, 0x30, 0x30,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x29, 0x53, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x30, 0x82, 0x01, 0x20, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0d, 0x00,
+ 0x30, 0x82, 0x01, 0x08, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb7, 0x32, 0xc8,
+ 0xfe, 0xe9, 0x71, 0xa6, 0x04, 0x85, 0xad, 0x0c, 0x11, 0x64, 0xdf, 0xce,
+ 0x4d, 0xef, 0xc8, 0x03, 0x18, 0x87, 0x3f, 0xa1, 0xab, 0xfb, 0x3c, 0xa6,
+ 0x9f, 0xf0, 0xc3, 0xa1, 0xda, 0xd4, 0xd8, 0x6e, 0x2b, 0x53, 0x90, 0xfb,
+ 0x24, 0xa4, 0x3e, 0x84, 0xf0, 0x9e, 0xe8, 0x5f, 0xec, 0xe5, 0x27, 0x44,
+ 0xf5, 0x28, 0xa6, 0x3f, 0x7b, 0xde, 0xe0, 0x2a, 0xf0, 0xc8, 0xaf, 0x53,
+ 0x2f, 0x9e, 0xca, 0x05, 0x01, 0x93, 0x1e, 0x8f, 0x66, 0x1c, 0x39, 0xa7,
+ 0x4d, 0xfa, 0x5a, 0xb6, 0x73, 0x04, 0x25, 0x66, 0xeb, 0x77, 0x7f, 0xe7,
+ 0x59, 0xc6, 0x4a, 0x99, 0x25, 0x14, 0x54, 0xeb, 0x26, 0xc7, 0xf3, 0x7f,
+ 0x19, 0xd5, 0x30, 0x70, 0x8f, 0xaf, 0xb0, 0x46, 0x2a, 0xff, 0xad, 0xeb,
+ 0x29, 0xed, 0xd7, 0x9f, 0xaa, 0x04, 0x87, 0xa3, 0xd4, 0xf9, 0x89, 0xa5,
+ 0x34, 0x5f, 0xdb, 0x43, 0x91, 0x82, 0x36, 0xd9, 0x66, 0x3c, 0xb1, 0xb8,
+ 0xb9, 0x82, 0xfd, 0x9c, 0x3a, 0x3e, 0x10, 0xc8, 0x3b, 0xef, 0x06, 0x65,
+ 0x66, 0x7a, 0x9b, 0x19, 0x18, 0x3d, 0xff, 0x71, 0x51, 0x3c, 0x30, 0x2e,
+ 0x5f, 0xbe, 0x3d, 0x77, 0x73, 0xb2, 0x5d, 0x06, 0x6c, 0xc3, 0x23, 0x56,
+ 0x9a, 0x2b, 0x85, 0x26, 0x92, 0x1c, 0xa7, 0x02, 0xb3, 0xe4, 0x3f, 0x0d,
+ 0xaf, 0x08, 0x79, 0x82, 0xb8, 0x36, 0x3d, 0xea, 0x9c, 0xd3, 0x35, 0xb3,
+ 0xbc, 0x69, 0xca, 0xf5, 0xcc, 0x9d, 0xe8, 0xfd, 0x64, 0x8d, 0x17, 0x80,
+ 0x33, 0x6e, 0x5e, 0x4a, 0x5d, 0x99, 0xc9, 0x1e, 0x87, 0xb4, 0x9d, 0x1a,
+ 0xc0, 0xd5, 0x6e, 0x13, 0x35, 0x23, 0x5e, 0xdf, 0x9b, 0x5f, 0x3d, 0xef,
+ 0xd6, 0xf7, 0x76, 0xc2, 0xea, 0x3e, 0xbb, 0x78, 0x0d, 0x1c, 0x42, 0x67,
+ 0x6b, 0x04, 0xd8, 0xf8, 0xd6, 0xda, 0x6f, 0x8b, 0xf2, 0x44, 0xa0, 0x01,
+ 0xab, 0x02, 0x01, 0x03, 0xa3, 0x82, 0x01, 0xf3, 0x30, 0x82, 0x01, 0xef,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xbf,
+ 0x5f, 0xb7, 0xd1, 0xce, 0xdd, 0x1f, 0x86, 0xf4, 0x5b, 0x55, 0xac, 0xdc,
+ 0xd7, 0x10, 0xc2, 0x0e, 0xa9, 0x88, 0xe7, 0x30, 0x81, 0xd2, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x81, 0xca, 0x30, 0x81, 0xc7, 0xa1, 0x81, 0xc1,
+ 0xa4, 0x81, 0xbe, 0x30, 0x81, 0xbb, 0x31, 0x24, 0x30, 0x22, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x13, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x43, 0x65, 0x72,
+ 0x74, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x17, 0x30, 0x15,
+ 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x43,
+ 0x65, 0x72, 0x74, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x35, 0x30,
+ 0x33, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2c, 0x56, 0x61, 0x6c, 0x69,
+ 0x43, 0x65, 0x72, 0x74, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32,
+ 0x20, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x20, 0x56, 0x61, 0x6c, 0x69,
+ 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x11, 0x69, 0x6e, 0x66, 0x6f,
+ 0x40, 0x76, 0x61, 0x6c, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x82, 0x01, 0x01, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x39, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x2d, 0x30,
+ 0x2b, 0x30, 0x29, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x1d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64,
+ 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f, 0xa0, 0x3d, 0xa0,
+ 0x3b, 0x86, 0x39, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65,
+ 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x2f, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x51, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4a, 0x30, 0x48, 0x30,
+ 0x46, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x3e, 0x30, 0x3c, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x30, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x66,
+ 0x69, 0x65, 0x6c, 0x64, 0x74, 0x65, 0x63, 0x68, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xa5, 0x62,
+ 0xf1, 0xa7, 0xc2, 0x5d, 0x25, 0xa5, 0x70, 0x3d, 0xbc, 0xe2, 0x2a, 0x71,
+ 0xb3, 0x7d, 0xe4, 0x0d, 0x37, 0x1d, 0x55, 0x6a, 0x6d, 0xa1, 0xb0, 0xab,
+ 0x98, 0x00, 0xe4, 0x85, 0x60, 0x4a, 0x20, 0xcb, 0xa1, 0xf0, 0x3d, 0x75,
+ 0xf7, 0x94, 0xda, 0x43, 0x7f, 0x68, 0x5c, 0x25, 0x08, 0xb8, 0xd2, 0x7d,
+ 0x33, 0x10, 0x7b, 0xdc, 0x76, 0x67, 0xf1, 0xb8, 0xe1, 0x53, 0x7c, 0xe1,
+ 0xc6, 0x83, 0x5b, 0x29, 0x7b, 0x8d, 0x4a, 0x6f, 0x2e, 0x7f, 0x44, 0xa8,
+ 0x1a, 0x45, 0x6b, 0x32, 0x07, 0xc7, 0x78, 0xca, 0x64, 0x92, 0xc2, 0xb4,
+ 0x84, 0x0c, 0xdc, 0xdd, 0x2d, 0x5f, 0x4d, 0xbb, 0xdd, 0x8a, 0xea, 0x38,
+ 0xdc, 0xd9, 0x66, 0xa2, 0xec, 0x41, 0xba, 0x55, 0x6d, 0x5a, 0x64, 0x3d,
+ 0xb7, 0x03, 0xcc, 0x1c, 0xe2, 0x91, 0x50, 0x9e, 0xe3, 0x09, 0x44, 0x95,
+ 0x17, 0x17, 0x73, 0x3d, 0xcc, 0x25,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 120021506 (0x7276202)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root
+ Validity
+ Not Before: Apr 14 18:12:26 2010 GMT
+ Not After : Apr 14 18:12:14 2018 GMT
+ Subject: CN=Microsoft Internet Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:bd:f4:cd:27:a5:4a:d8:90:19:db:b2:0a:7b:60:
+ a4:4e:8f:0e:98:a3:9c:7c:50:76:eb:b3:4a:8c:9f:
+ 18:b0:e7:9a:c5:2b:82:86:09:28:ac:11:12:32:26:
+ f5:19:ea:f0:b8:67:68:c5:06:fd:f4:19:ae:d9:13:
+ a0:d0:81:27:08:a6:79:9c:04:f5:48:32:2e:36:1f:
+ ab:6b:26:ec:a1:43:b4:9d:80:b0:49:03:ea:82:49:
+ 5f:05:13:c5:a0:83:5f:e1:2a:f4:04:19:4b:7e:c8:
+ da:88:bc:b5:5d:03:bc:78:56:a1:e9:7f:c5:6a:ef:
+ b6:ff:1d:01:59:b7:1f:53:5a:5f:c6:f8:91:6d:c5:
+ 7d:43:93:18:74:45:ed:15:ba:b2:7c:c8:3a:34:14:
+ 1e:aa:63:f7:e5:d4:4b:c8:23:2b:87:69:95:13:99:
+ 09:14:ef:7a:01:20:4e:b7:c6:48:41:ae:c9:87:01:
+ 29:d9:c2:87:38:7f:b6:42:a4:f0:b2:ce:2d:fd:b4:
+ 4c:57:f0:a8:d6:cb:4e:fa:5f:5d:fd:b9:fb:09:dc:
+ 16:85:64:e5:71:9c:d5:f1:33:97:38:67:2e:9b:bc:
+ 17:36:05:7e:10:36:7f:7e:eb:98:5a:5b:1c:ad:a5:
+ e7:09:10:7d:f9:4a:2f:b3:8f:37:15:d6:6f:b9:5b:
+ 37:dc:b7:9f:7f:8e:66:7f:23:5c:ed:12:7f:8c:07:
+ f0:fe:19:f9:b8:34:43:7b:b2:ea:85:fb:8c:a9:aa:
+ df:fd:91:0d:2c:f5:fb:af:97:89:f1:06:8a:af:49:
+ f6:3c:2e:23:f6:44:16:25:91:11:e2:23:c3:ca:85:
+ 55:49:2a:c8:21:af:7d:11:26:86:b0:28:45:ba:87:
+ ee:36:13:81:d5:4b:47:1a:8e:db:09:f1:d1:97:29:
+ 50:14:32:99:09:e3:f2:c0:e7:53:8f:6b:f4:fa:13:
+ 5c:3c:8d:ee:54:99:0f:27:47:4e:3c:12:f3:8f:12:
+ 17:46:f0:89:6a:45:b3:b5:3c:0c:77:45:04:2f:bd:
+ be:b5:9e:98:3c:05:3b:bb:41:39:84:20:bc:79:04:
+ d6:42:cd:3e:89:e9:e7:7a:37:49:10:b4:cc:9f:24:
+ 5c:23:a6:48:6e:fb:e3:d4:ee:21:29:93:e4:fd:80:
+ 1a:1b:3a:6c:c1:f7:eb:d9:d4:4d:be:f1:11:f6:a2:
+ 8e:42:24:a1:4f:69:b5:d2:68:14:89:d9:9f:90:d8:
+ 1f:9e:1b:e6:6d:64:25:29:b6:34:43:a4:5b:f5:0d:
+ eb:74:06:7e:9f:f1:63:dc:45:a7:7c:3a:9a:5c:6b:
+ 73:d8:c3:58:04:8e:88:6f:13:d0:e6:d0:df:cd:c4:
+ 0a:0e:07
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.6334.1.0
+ CPS: http://cybertrust.omniroot.com/repository.cfm
+ Policy: X509v3 Any Policy
+
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc./CN=GTE CyberTrust Global Root
+ serial:01:A5
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl
+
+ X509v3 Subject Key Identifier:
+ 33:21:F0:CB:FE:A2:A0:44:92:DE:F6:3B:33:D8:5F:01:4B:97:78:5D
+ Signature Algorithm: sha1WithRSAEncryption
+ 2b:48:f3:94:fb:44:c5:93:6a:d6:4d:fe:b4:13:4e:12:26:17:
+ ca:b2:5a:ab:09:b9:56:a4:6f:7f:57:9e:64:b2:f5:e4:d3:35:
+ ef:63:65:cb:e5:2c:15:9c:ef:ce:f8:2a:c5:92:64:2b:49:3e:
+ 3c:36:6c:bd:18:9b:64:67:97:3f:ed:68:d0:16:c1:13:3c:f2:
+ 51:a0:57:de:24:ce:35:ab:69:90:4e:2b:0c:3a:f9:b4:f1:80:
+ fa:6d:00:79:a6:3a:96:99:4e:3a:6e:54:d0:a3:59:6e:8b:1d:
+ 95:49:bb:95:d8:75:b8:e1:12:33:ac:5c:27:bb:cb:55:71:d5:
+ fa:ed
+-----BEGIN CERTIFICATE-----
+MIIFEjCCBHugAwIBAgIEBydiAjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
+UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
+cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
+b2JhbCBSb290MB4XDTEwMDQxNDE4MTIyNloXDTE4MDQxNDE4MTIxNFowJzElMCMG
+A1UEAxMcTWljcm9zb2Z0IEludGVybmV0IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcN
+AQEBBQADggIPADCCAgoCggIBAL30zSelStiQGduyCntgpE6PDpijnHxQduuzSoyf
+GLDnmsUrgoYJKKwREjIm9Rnq8LhnaMUG/fQZrtkToNCBJwimeZwE9UgyLjYfq2sm
+7KFDtJ2AsEkD6oJJXwUTxaCDX+Eq9AQZS37I2oi8tV0DvHhWoel/xWrvtv8dAVm3
+H1NaX8b4kW3FfUOTGHRF7RW6snzIOjQUHqpj9+XUS8gjK4dplROZCRTvegEgTrfG
+SEGuyYcBKdnChzh/tkKk8LLOLf20TFfwqNbLTvpfXf25+wncFoVk5XGc1fEzlzhn
+Lpu8FzYFfhA2f37rmFpbHK2l5wkQfflKL7OPNxXWb7lbN9y3n3+OZn8jXO0Sf4wH
+8P4Z+bg0Q3uy6oX7jKmq3/2RDSz1+6+XifEGiq9J9jwuI/ZEFiWREeIjw8qFVUkq
+yCGvfREmhrAoRbqH7jYTgdVLRxqO2wnx0ZcpUBQymQnj8sDnU49r9PoTXDyN7lSZ
+DydHTjwS848SF0bwiWpFs7U8DHdFBC+9vrWemDwFO7tBOYQgvHkE1kLNPonp53o3
+SRC0zJ8kXCOmSG7749TuISmT5P2AGhs6bMH369nUTb7xEfaijkIkoU9ptdJoFInZ
+n5DYH54b5m1kJSm2NEOkW/UN63QGfp/xY9xFp3w6mlxrc9jDWASOiG8T0ObQ383E
+Cg4HAgMBAAGjggF3MIIBczASBgNVHRMBAf8ECDAGAQH/AgEBMFsGA1UdIARUMFIw
+SAYJKwYBBAGxPgEAMDswOQYIKwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9t
+bmlyb290LmNvbS9yZXBvc2l0b3J5LmNmbTAGBgRVHSAAMA4GA1UdDwEB/wQEAwIB
+hjCBiQYDVR0jBIGBMH+heaR3MHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg
+Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywg
+SW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3SCAgGlMEUG
+A1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9jZ2kt
+YmluL0NSTC8yMDE4L2NkcC5jcmwwHQYDVR0OBBYEFDMh8Mv+oqBEkt72OzPYXwFL
+l3hdMA0GCSqGSIb3DQEBBQUAA4GBACtI85T7RMWTatZN/rQTThImF8qyWqsJuVak
+b39XnmSy9eTTNe9jZcvlLBWc7874KsWSZCtJPjw2bL0Ym2Rnlz/taNAWwRM88lGg
+V94kzjWraZBOKww6+bTxgPptAHmmOpaZTjpuVNCjWW6LHZVJu5XYdbjhEjOsXCe7
+y1Vx1frt
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert82[] = {
+ 0x30, 0x82, 0x05, 0x12, 0x30, 0x82, 0x04, 0x7b, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x04, 0x07, 0x27, 0x62, 0x02, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x75,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f,
+ 0x47, 0x54, 0x45, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x1e, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43,
+ 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c,
+ 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x30, 0x34, 0x31, 0x34, 0x31, 0x38, 0x31, 0x32, 0x32,
+ 0x36, 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x34, 0x31, 0x34, 0x31, 0x38,
+ 0x31, 0x32, 0x31, 0x34, 0x5a, 0x30, 0x27, 0x31, 0x25, 0x30, 0x23, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+ 0x6f, 0x66, 0x74, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x82,
+ 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82,
+ 0x02, 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xbd, 0xf4, 0xcd, 0x27, 0xa5,
+ 0x4a, 0xd8, 0x90, 0x19, 0xdb, 0xb2, 0x0a, 0x7b, 0x60, 0xa4, 0x4e, 0x8f,
+ 0x0e, 0x98, 0xa3, 0x9c, 0x7c, 0x50, 0x76, 0xeb, 0xb3, 0x4a, 0x8c, 0x9f,
+ 0x18, 0xb0, 0xe7, 0x9a, 0xc5, 0x2b, 0x82, 0x86, 0x09, 0x28, 0xac, 0x11,
+ 0x12, 0x32, 0x26, 0xf5, 0x19, 0xea, 0xf0, 0xb8, 0x67, 0x68, 0xc5, 0x06,
+ 0xfd, 0xf4, 0x19, 0xae, 0xd9, 0x13, 0xa0, 0xd0, 0x81, 0x27, 0x08, 0xa6,
+ 0x79, 0x9c, 0x04, 0xf5, 0x48, 0x32, 0x2e, 0x36, 0x1f, 0xab, 0x6b, 0x26,
+ 0xec, 0xa1, 0x43, 0xb4, 0x9d, 0x80, 0xb0, 0x49, 0x03, 0xea, 0x82, 0x49,
+ 0x5f, 0x05, 0x13, 0xc5, 0xa0, 0x83, 0x5f, 0xe1, 0x2a, 0xf4, 0x04, 0x19,
+ 0x4b, 0x7e, 0xc8, 0xda, 0x88, 0xbc, 0xb5, 0x5d, 0x03, 0xbc, 0x78, 0x56,
+ 0xa1, 0xe9, 0x7f, 0xc5, 0x6a, 0xef, 0xb6, 0xff, 0x1d, 0x01, 0x59, 0xb7,
+ 0x1f, 0x53, 0x5a, 0x5f, 0xc6, 0xf8, 0x91, 0x6d, 0xc5, 0x7d, 0x43, 0x93,
+ 0x18, 0x74, 0x45, 0xed, 0x15, 0xba, 0xb2, 0x7c, 0xc8, 0x3a, 0x34, 0x14,
+ 0x1e, 0xaa, 0x63, 0xf7, 0xe5, 0xd4, 0x4b, 0xc8, 0x23, 0x2b, 0x87, 0x69,
+ 0x95, 0x13, 0x99, 0x09, 0x14, 0xef, 0x7a, 0x01, 0x20, 0x4e, 0xb7, 0xc6,
+ 0x48, 0x41, 0xae, 0xc9, 0x87, 0x01, 0x29, 0xd9, 0xc2, 0x87, 0x38, 0x7f,
+ 0xb6, 0x42, 0xa4, 0xf0, 0xb2, 0xce, 0x2d, 0xfd, 0xb4, 0x4c, 0x57, 0xf0,
+ 0xa8, 0xd6, 0xcb, 0x4e, 0xfa, 0x5f, 0x5d, 0xfd, 0xb9, 0xfb, 0x09, 0xdc,
+ 0x16, 0x85, 0x64, 0xe5, 0x71, 0x9c, 0xd5, 0xf1, 0x33, 0x97, 0x38, 0x67,
+ 0x2e, 0x9b, 0xbc, 0x17, 0x36, 0x05, 0x7e, 0x10, 0x36, 0x7f, 0x7e, 0xeb,
+ 0x98, 0x5a, 0x5b, 0x1c, 0xad, 0xa5, 0xe7, 0x09, 0x10, 0x7d, 0xf9, 0x4a,
+ 0x2f, 0xb3, 0x8f, 0x37, 0x15, 0xd6, 0x6f, 0xb9, 0x5b, 0x37, 0xdc, 0xb7,
+ 0x9f, 0x7f, 0x8e, 0x66, 0x7f, 0x23, 0x5c, 0xed, 0x12, 0x7f, 0x8c, 0x07,
+ 0xf0, 0xfe, 0x19, 0xf9, 0xb8, 0x34, 0x43, 0x7b, 0xb2, 0xea, 0x85, 0xfb,
+ 0x8c, 0xa9, 0xaa, 0xdf, 0xfd, 0x91, 0x0d, 0x2c, 0xf5, 0xfb, 0xaf, 0x97,
+ 0x89, 0xf1, 0x06, 0x8a, 0xaf, 0x49, 0xf6, 0x3c, 0x2e, 0x23, 0xf6, 0x44,
+ 0x16, 0x25, 0x91, 0x11, 0xe2, 0x23, 0xc3, 0xca, 0x85, 0x55, 0x49, 0x2a,
+ 0xc8, 0x21, 0xaf, 0x7d, 0x11, 0x26, 0x86, 0xb0, 0x28, 0x45, 0xba, 0x87,
+ 0xee, 0x36, 0x13, 0x81, 0xd5, 0x4b, 0x47, 0x1a, 0x8e, 0xdb, 0x09, 0xf1,
+ 0xd1, 0x97, 0x29, 0x50, 0x14, 0x32, 0x99, 0x09, 0xe3, 0xf2, 0xc0, 0xe7,
+ 0x53, 0x8f, 0x6b, 0xf4, 0xfa, 0x13, 0x5c, 0x3c, 0x8d, 0xee, 0x54, 0x99,
+ 0x0f, 0x27, 0x47, 0x4e, 0x3c, 0x12, 0xf3, 0x8f, 0x12, 0x17, 0x46, 0xf0,
+ 0x89, 0x6a, 0x45, 0xb3, 0xb5, 0x3c, 0x0c, 0x77, 0x45, 0x04, 0x2f, 0xbd,
+ 0xbe, 0xb5, 0x9e, 0x98, 0x3c, 0x05, 0x3b, 0xbb, 0x41, 0x39, 0x84, 0x20,
+ 0xbc, 0x79, 0x04, 0xd6, 0x42, 0xcd, 0x3e, 0x89, 0xe9, 0xe7, 0x7a, 0x37,
+ 0x49, 0x10, 0xb4, 0xcc, 0x9f, 0x24, 0x5c, 0x23, 0xa6, 0x48, 0x6e, 0xfb,
+ 0xe3, 0xd4, 0xee, 0x21, 0x29, 0x93, 0xe4, 0xfd, 0x80, 0x1a, 0x1b, 0x3a,
+ 0x6c, 0xc1, 0xf7, 0xeb, 0xd9, 0xd4, 0x4d, 0xbe, 0xf1, 0x11, 0xf6, 0xa2,
+ 0x8e, 0x42, 0x24, 0xa1, 0x4f, 0x69, 0xb5, 0xd2, 0x68, 0x14, 0x89, 0xd9,
+ 0x9f, 0x90, 0xd8, 0x1f, 0x9e, 0x1b, 0xe6, 0x6d, 0x64, 0x25, 0x29, 0xb6,
+ 0x34, 0x43, 0xa4, 0x5b, 0xf5, 0x0d, 0xeb, 0x74, 0x06, 0x7e, 0x9f, 0xf1,
+ 0x63, 0xdc, 0x45, 0xa7, 0x7c, 0x3a, 0x9a, 0x5c, 0x6b, 0x73, 0xd8, 0xc3,
+ 0x58, 0x04, 0x8e, 0x88, 0x6f, 0x13, 0xd0, 0xe6, 0xd0, 0xdf, 0xcd, 0xc4,
+ 0x0a, 0x0e, 0x07, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x77,
+ 0x30, 0x82, 0x01, 0x73, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
+ 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01,
+ 0x30, 0x5b, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x54, 0x30, 0x52, 0x30,
+ 0x48, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xb1, 0x3e, 0x01, 0x00,
+ 0x30, 0x3b, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x79, 0x62, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x6f, 0x6d,
+ 0x6e, 0x69, 0x72, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x63, 0x66,
+ 0x6d, 0x30, 0x06, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x86, 0x30, 0x81, 0x89, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x81,
+ 0x30, 0x7f, 0xa1, 0x79, 0xa4, 0x77, 0x30, 0x75, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x18, 0x30,
+ 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47, 0x54, 0x45, 0x20,
+ 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31,
+ 0x27, 0x30, 0x25, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1e, 0x47, 0x54,
+ 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72, 0x54, 0x72, 0x75, 0x73, 0x74,
+ 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x1a, 0x47, 0x54, 0x45, 0x20, 0x43, 0x79, 0x62, 0x65, 0x72,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
+ 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x82, 0x02, 0x01, 0xa5, 0x30, 0x45, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3e, 0x30, 0x3c, 0x30, 0x3a, 0xa0, 0x38,
+ 0xa0, 0x36, 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2d, 0x74, 0x72,
+ 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x67, 0x69, 0x2d,
+ 0x62, 0x69, 0x6e, 0x2f, 0x43, 0x52, 0x4c, 0x2f, 0x32, 0x30, 0x31, 0x38,
+ 0x2f, 0x63, 0x64, 0x70, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x33, 0x21, 0xf0, 0xcb, 0xfe,
+ 0xa2, 0xa0, 0x44, 0x92, 0xde, 0xf6, 0x3b, 0x33, 0xd8, 0x5f, 0x01, 0x4b,
+ 0x97, 0x78, 0x5d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x2b, 0x48,
+ 0xf3, 0x94, 0xfb, 0x44, 0xc5, 0x93, 0x6a, 0xd6, 0x4d, 0xfe, 0xb4, 0x13,
+ 0x4e, 0x12, 0x26, 0x17, 0xca, 0xb2, 0x5a, 0xab, 0x09, 0xb9, 0x56, 0xa4,
+ 0x6f, 0x7f, 0x57, 0x9e, 0x64, 0xb2, 0xf5, 0xe4, 0xd3, 0x35, 0xef, 0x63,
+ 0x65, 0xcb, 0xe5, 0x2c, 0x15, 0x9c, 0xef, 0xce, 0xf8, 0x2a, 0xc5, 0x92,
+ 0x64, 0x2b, 0x49, 0x3e, 0x3c, 0x36, 0x6c, 0xbd, 0x18, 0x9b, 0x64, 0x67,
+ 0x97, 0x3f, 0xed, 0x68, 0xd0, 0x16, 0xc1, 0x13, 0x3c, 0xf2, 0x51, 0xa0,
+ 0x57, 0xde, 0x24, 0xce, 0x35, 0xab, 0x69, 0x90, 0x4e, 0x2b, 0x0c, 0x3a,
+ 0xf9, 0xb4, 0xf1, 0x80, 0xfa, 0x6d, 0x00, 0x79, 0xa6, 0x3a, 0x96, 0x99,
+ 0x4e, 0x3a, 0x6e, 0x54, 0xd0, 0xa3, 0x59, 0x6e, 0x8b, 0x1d, 0x95, 0x49,
+ 0xbb, 0x95, 0xd8, 0x75, 0xb8, 0xe1, 0x12, 0x33, 0xac, 0x5c, 0x27, 0xbb,
+ 0xcb, 0x55, 0x71, 0xd5, 0xfa, 0xed,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 57:bf:fb:03:fb:2c:46:d4:e1:9e:ce:e0:d7:43:7f:13
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2021 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:af:24:08:08:29:7a:35:9e:60:0c:aa:e7:4b:3b:
+ 4e:dc:7c:bc:3c:45:1c:bb:2b:e0:fe:29:02:f9:57:
+ 08:a3:64:85:15:27:f5:f1:ad:c8:31:89:5d:22:e8:
+ 2a:aa:a6:42:b3:8f:f8:b9:55:b7:b1:b7:4b:b3:fe:
+ 8f:7e:07:57:ec:ef:43:db:66:62:15:61:cf:60:0d:
+ a4:d8:de:f8:e0:c3:62:08:3d:54:13:eb:49:ca:59:
+ 54:85:26:e5:2b:8f:1b:9f:eb:f5:a1:91:c2:33:49:
+ d8:43:63:6a:52:4b:d2:8f:e8:70:51:4d:d1:89:69:
+ 7b:c7:70:f6:b3:dc:12:74:db:7b:5d:4b:56:d3:96:
+ bf:15:77:a1:b0:f4:a2:25:f2:af:1c:92:67:18:e5:
+ f4:06:04:ef:90:b9:e4:00:e4:dd:3a:b5:19:ff:02:
+ ba:f4:3c:ee:e0:8b:eb:37:8b:ec:f4:d7:ac:f2:f6:
+ f0:3d:af:dd:75:91:33:19:1d:1c:40:cb:74:24:19:
+ 21:93:d9:14:fe:ac:2a:52:c7:8f:d5:04:49:e4:8d:
+ 63:47:88:3c:69:83:cb:fe:47:bd:2b:7e:4f:c5:95:
+ ae:0e:9d:d4:d1:43:c0:67:73:e3:14:08:7e:e5:3f:
+ 9f:73:b8:33:0a:cf:5d:3f:34:87:96:8a:ee:53:e8:
+ 25:15
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 Subject Key Identifier:
+ 7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
+ serial:70:BA:E4:1D:10:D9:29:34:B6:38:CA:7B:03:CC:BA:BF
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a9:7b:66:29:30:f7:d5:b4:a6:96:12:d0:ee:72:f0:58:11:69:
+ 15:55:5f:41:ff:d2:12:84:13:a4:d9:03:66:ff:a9:e0:4c:c9:
+ ed:8c:72:8b:b4:d7:55:3b:29:15:60:c8:3c:21:ef:44:2e:93:
+ 3d:c6:0b:0c:8d:24:3f:1e:fb:01:5a:7a:dd:83:66:14:d1:c7:
+ fd:30:53:48:51:85:85:13:a8:54:e1:ee:76:a2:89:18:d3:97:
+ 89:7a:c6:fd:b3:bd:94:61:5a:3a:08:cf:14:93:bd:93:fd:09:
+ a9:7b:56:c8:00:b8:44:58:e9:de:5b:77:bd:07:1c:6c:0b:30:
+ 30:c7
+-----BEGIN CERTIFICATE-----
+MIIFEzCCBHygAwIBAgIQV7/7A/ssRtThns7g10N/EzANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
+LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
+HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
+dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
+ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
+RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
+ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
+TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
+Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
+iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
+AAGjggHeMIIB2jAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
+dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjBt
+BggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9naWYwITAfMAcGBSsOAwIa
+BBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8vbG9nby52ZXJpc2lnbi5j
+b20vdnNsb2dvLmdpZjA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7Lvw
+MAnzQzn6Aq8zMTMwNAYDVR0lBC0wKwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBBggr
+BgEFBQcDAQYIKwYBBQUHAwIwgYAGA1UdIwR5MHehY6RhMF8xCzAJBgNVBAYTAlVT
+MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMyBQdWJs
+aWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eYIQcLrkHRDZKTS2OMp7
+A8y6vzANBgkqhkiG9w0BAQUFAAOBgQCpe2YpMPfVtKaWEtDucvBYEWkVVV9B/9IS
+hBOk2QNm/6ngTMntjHKLtNdVOykVYMg8Ie9ELpM9xgsMjSQ/HvsBWnrdg2YU0cf9
+MFNIUYWFE6hU4e52ookY05eJesb9s72UYVo6CM8Uk72T/Qmpe1bIALhEWOneW3e9
+BxxsCzAwxw==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert83[] = {
+ 0x30, 0x82, 0x05, 0x13, 0x30, 0x82, 0x04, 0x7c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x57, 0xbf, 0xfb, 0x03, 0xfb, 0x2c, 0x46, 0xd4, 0xe1,
+ 0x9e, 0xce, 0xe0, 0xd7, 0x43, 0x7f, 0x13, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x5f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x2e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xca, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30,
+ 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20,
+ 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f,
+ 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30,
+ 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d,
+ 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xaf, 0x24, 0x08, 0x08, 0x29, 0x7a, 0x35,
+ 0x9e, 0x60, 0x0c, 0xaa, 0xe7, 0x4b, 0x3b, 0x4e, 0xdc, 0x7c, 0xbc, 0x3c,
+ 0x45, 0x1c, 0xbb, 0x2b, 0xe0, 0xfe, 0x29, 0x02, 0xf9, 0x57, 0x08, 0xa3,
+ 0x64, 0x85, 0x15, 0x27, 0xf5, 0xf1, 0xad, 0xc8, 0x31, 0x89, 0x5d, 0x22,
+ 0xe8, 0x2a, 0xaa, 0xa6, 0x42, 0xb3, 0x8f, 0xf8, 0xb9, 0x55, 0xb7, 0xb1,
+ 0xb7, 0x4b, 0xb3, 0xfe, 0x8f, 0x7e, 0x07, 0x57, 0xec, 0xef, 0x43, 0xdb,
+ 0x66, 0x62, 0x15, 0x61, 0xcf, 0x60, 0x0d, 0xa4, 0xd8, 0xde, 0xf8, 0xe0,
+ 0xc3, 0x62, 0x08, 0x3d, 0x54, 0x13, 0xeb, 0x49, 0xca, 0x59, 0x54, 0x85,
+ 0x26, 0xe5, 0x2b, 0x8f, 0x1b, 0x9f, 0xeb, 0xf5, 0xa1, 0x91, 0xc2, 0x33,
+ 0x49, 0xd8, 0x43, 0x63, 0x6a, 0x52, 0x4b, 0xd2, 0x8f, 0xe8, 0x70, 0x51,
+ 0x4d, 0xd1, 0x89, 0x69, 0x7b, 0xc7, 0x70, 0xf6, 0xb3, 0xdc, 0x12, 0x74,
+ 0xdb, 0x7b, 0x5d, 0x4b, 0x56, 0xd3, 0x96, 0xbf, 0x15, 0x77, 0xa1, 0xb0,
+ 0xf4, 0xa2, 0x25, 0xf2, 0xaf, 0x1c, 0x92, 0x67, 0x18, 0xe5, 0xf4, 0x06,
+ 0x04, 0xef, 0x90, 0xb9, 0xe4, 0x00, 0xe4, 0xdd, 0x3a, 0xb5, 0x19, 0xff,
+ 0x02, 0xba, 0xf4, 0x3c, 0xee, 0xe0, 0x8b, 0xeb, 0x37, 0x8b, 0xec, 0xf4,
+ 0xd7, 0xac, 0xf2, 0xf6, 0xf0, 0x3d, 0xaf, 0xdd, 0x75, 0x91, 0x33, 0x19,
+ 0x1d, 0x1c, 0x40, 0xcb, 0x74, 0x24, 0x19, 0x21, 0x93, 0xd9, 0x14, 0xfe,
+ 0xac, 0x2a, 0x52, 0xc7, 0x8f, 0xd5, 0x04, 0x49, 0xe4, 0x8d, 0x63, 0x47,
+ 0x88, 0x3c, 0x69, 0x83, 0xcb, 0xfe, 0x47, 0xbd, 0x2b, 0x7e, 0x4f, 0xc5,
+ 0x95, 0xae, 0x0e, 0x9d, 0xd4, 0xd1, 0x43, 0xc0, 0x67, 0x73, 0xe3, 0x14,
+ 0x08, 0x7e, 0xe5, 0x3f, 0x9f, 0x73, 0xb8, 0x33, 0x0a, 0xcf, 0x5d, 0x3f,
+ 0x34, 0x87, 0x96, 0x8a, 0xee, 0x53, 0xe8, 0x25, 0x15, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0xde, 0x30, 0x82, 0x01, 0xda, 0x30, 0x0f,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03,
+ 0x01, 0x01, 0xff, 0x30, 0x31, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2a,
+ 0x30, 0x28, 0x30, 0x26, 0xa0, 0x24, 0xa0, 0x22, 0x86, 0x20, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63,
+ 0x61, 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61,
+ 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55,
+ 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30,
+ 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+ 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3,
+ 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25,
+ 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69,
+ 0x66, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34,
+ 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0,
+ 0x30, 0x09, 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30,
+ 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x81, 0x80, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x79, 0x30, 0x77, 0xa1, 0x63, 0xa4, 0x61, 0x30, 0x5f, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63,
+ 0x2e, 0x31, 0x37, 0x30, 0x35, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2e,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c,
+ 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x82, 0x10,
+ 0x70, 0xba, 0xe4, 0x1d, 0x10, 0xd9, 0x29, 0x34, 0xb6, 0x38, 0xca, 0x7b,
+ 0x03, 0xcc, 0xba, 0xbf, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xa9,
+ 0x7b, 0x66, 0x29, 0x30, 0xf7, 0xd5, 0xb4, 0xa6, 0x96, 0x12, 0xd0, 0xee,
+ 0x72, 0xf0, 0x58, 0x11, 0x69, 0x15, 0x55, 0x5f, 0x41, 0xff, 0xd2, 0x12,
+ 0x84, 0x13, 0xa4, 0xd9, 0x03, 0x66, 0xff, 0xa9, 0xe0, 0x4c, 0xc9, 0xed,
+ 0x8c, 0x72, 0x8b, 0xb4, 0xd7, 0x55, 0x3b, 0x29, 0x15, 0x60, 0xc8, 0x3c,
+ 0x21, 0xef, 0x44, 0x2e, 0x93, 0x3d, 0xc6, 0x0b, 0x0c, 0x8d, 0x24, 0x3f,
+ 0x1e, 0xfb, 0x01, 0x5a, 0x7a, 0xdd, 0x83, 0x66, 0x14, 0xd1, 0xc7, 0xfd,
+ 0x30, 0x53, 0x48, 0x51, 0x85, 0x85, 0x13, 0xa8, 0x54, 0xe1, 0xee, 0x76,
+ 0xa2, 0x89, 0x18, 0xd3, 0x97, 0x89, 0x7a, 0xc6, 0xfd, 0xb3, 0xbd, 0x94,
+ 0x61, 0x5a, 0x3a, 0x08, 0xcf, 0x14, 0x93, 0xbd, 0x93, 0xfd, 0x09, 0xa9,
+ 0x7b, 0x56, 0xc8, 0x00, 0xb8, 0x44, 0x58, 0xe9, 0xde, 0x5b, 0x77, 0xbd,
+ 0x07, 0x1c, 0x6c, 0x0b, 0x30, 0x30, 0xc7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 08:0a:57:82:2c:c6:f5:e1:4f:19:b7:09:55:c8:03:42
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO Certification Authority
+ Validity
+ Not Before: Dec 1 00:00:00 2006 GMT
+ Not After : Dec 31 23:59:59 2019 GMT
+ Subject: C=GB, ST=Greater Manchester, L=Salford, O=COMODO CA Limited, CN=COMODO High Assurance Secure Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b8:4c:b0:aa:8e:06:df:37:4a:a4:f2:08:ff:e2:
+ b9:92:d6:6f:eb:9e:35:4e:ec:e5:65:08:8b:13:c0:
+ bc:38:7b:11:12:1c:0b:4b:f4:37:22:15:cc:60:d1:
+ c5:1e:28:d7:2f:9a:97:d7:1c:04:8d:9b:63:7d:6e:
+ 2f:ee:f8:1f:4b:33:3e:d8:4d:86:61:0b:5a:9b:d8:
+ 96:3b:05:76:0b:2b:cb:d7:85:21:bc:19:a7:c6:68:
+ 44:83:18:b2:17:44:b5:90:9c:65:6f:71:92:50:71:
+ a0:55:72:26:92:5e:d3:69:eb:08:3f:f2:7e:a7:a0:
+ b3:eb:ab:e1:03:b9:88:7a:81:3f:a5:84:dc:92:43:
+ 4e:3b:57:70:00:1e:6b:99:50:0d:53:e8:e2:b6:18:
+ 92:1a:cd:b8:4c:5e:d1:a0:c4:a0:f1:c6:ec:fc:dd:
+ d1:7c:91:1a:14:91:32:9d:79:46:ab:f1:f0:48:60:
+ 28:55:b4:4c:e6:16:0e:bc:ef:5e:ca:d0:fe:ec:91:
+ f0:d5:11:18:5c:aa:c3:86:67:c4:11:43:08:69:55:
+ 80:b5:b0:20:48:da:78:89:09:04:57:37:f7:5d:28:
+ f3:47:fb:18:c9:be:be:78:b0:32:74:da:55:da:d6:
+ 54:86:3e:95:2b:15:1a:ed:94:5b:96:6a:f8:e3:c5:
+ 9d:ab
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:0B:58:E5:8B:C6:4C:15:37:A4:40:A9:30:A9:21:BE:47:36:5A:56:FF
+
+ X509v3 Subject Key Identifier:
+ 60:59:CD:80:C7:C5:E3:AB:8C:2F:FC:6B:E5:5B:0A:F5:0F:DE:4B:FF
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Extended Key Usage:
+ Microsoft Server Gated Crypto, Netscape Server Gated Crypto
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://secure.comodo.com/CPS
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.comodoca.com/COMODOCertificationAuthority.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.comodoca.com/ComodoUTNSGCCA.crt
+ OCSP - URI:http://ocsp.comodoca.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 32:86:37:17:d4:67:f2:ee:2d:7f:a8:03:f6:df:22:c7:ee:4c:
+ 0d:b1:4c:c1:27:b4:2c:5d:0d:30:07:8a:b3:41:51:e4:47:b0:
+ ca:a2:1d:6d:b9:a4:fb:96:23:87:03:b6:27:29:25:51:fe:48:
+ b5:98:c3:dc:34:47:e5:40:43:31:14:33:ef:bd:7f:99:43:ff:
+ 48:68:01:de:88:44:63:27:f1:22:be:c0:2f:74:d6:57:63:d6:
+ 30:c7:3f:3b:cc:bb:d2:e5:33:72:99:5a:bf:d9:33:59:b6:41:
+ 83:b4:98:3a:9c:77:00:a4:f1:c4:30:e8:1d:af:e2:d6:f8:7e:
+ 2a:66:45:58:81:21:8f:50:60:15:ef:60:62:d4:ab:3a:b9:f0:
+ fa:5c:e7:3c:3d:9d:b9:5f:75:c9:c8:73:af:5e:fe:03:6c:4c:
+ e6:ea:28:14:54:21:8e:99:4c:db:25:7c:cc:03:d5:81:26:fa:
+ 57:42:8b:79:10:03:70:f5:6a:43:82:5c:5b:5c:1f:85:09:20:
+ 42:66:71:59:d5:2f:49:b5:ab:29:63:54:e3:03:99:8d:8e:38:
+ 28:44:ff:b5:3e:c3:cd:43:73:3d:d9:3a:3b:0d:bc:f6:88:21:
+ 34:99:d9:99:e8:56:7c:27:84:ae:d3:c8:bd:b7:82:fa:74:2c:
+ e0:33:a6:8f
+-----BEGIN CERTIFICATE-----
+MIIFGzCCBAOgAwIBAgIQCApXgizG9eFPGbcJVcgDQjANBgkqhkiG9w0BAQUFADCB
+gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
+BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
+MDBaFw0xOTEyMzEyMzU5NTlaMIGJMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
+YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
+RE8gQ0EgTGltaXRlZDEvMC0GA1UEAxMmQ09NT0RPIEhpZ2ggQXNzdXJhbmNlIFNl
+Y3VyZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4
+TLCqjgbfN0qk8gj/4rmS1m/rnjVO7OVlCIsTwLw4exESHAtL9DciFcxg0cUeKNcv
+mpfXHASNm2N9bi/u+B9LMz7YTYZhC1qb2JY7BXYLK8vXhSG8GafGaESDGLIXRLWQ
+nGVvcZJQcaBVciaSXtNp6wg/8n6noLPrq+EDuYh6gT+lhNySQ047V3AAHmuZUA1T
+6OK2GJIazbhMXtGgxKDxxuz83dF8kRoUkTKdeUar8fBIYChVtEzmFg68717K0P7s
+kfDVERhcqsOGZ8QRQwhpVYC1sCBI2niJCQRXN/ddKPNH+xjJvr54sDJ02lXa1lSG
+PpUrFRrtlFuWavjjxZ2rAgMBAAGjggGDMIIBfzAfBgNVHSMEGDAWgBQLWOWLxkwV
+N6RAqTCpIb5HNlpW/zAdBgNVHQ4EFgQUYFnNgMfF46uML/xr5VsK9Q/eS/8wDgYD
+VR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwIAYDVR0lBBkwFwYKKwYB
+BAGCNwoDAwYJYIZIAYb4QgQBMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUF
+BwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzBJBgNVHR8EQjBAMD6g
+PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
+QXV0aG9yaXR5LmNybDBsBggrBgEFBQcBAQRgMF4wNgYIKwYBBQUHMAKGKmh0dHA6
+Ly9jcnQuY29tb2RvY2EuY29tL0NvbW9kb1VUTlNHQ0NBLmNydDAkBggrBgEFBQcw
+AYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQAy
+hjcX1Gfy7i1/qAP23yLH7kwNsUzBJ7QsXQ0wB4qzQVHkR7DKoh1tuaT7liOHA7Yn
+KSVR/ki1mMPcNEflQEMxFDPvvX+ZQ/9IaAHeiERjJ/EivsAvdNZXY9Ywxz87zLvS
+5TNymVq/2TNZtkGDtJg6nHcApPHEMOgdr+LW+H4qZkVYgSGPUGAV72Bi1Ks6ufD6
+XOc8PZ25X3XJyHOvXv4DbEzm6igUVCGOmUzbJXzMA9WBJvpXQot5EANw9WpDglxb
+XB+FCSBCZnFZ1S9JtaspY1TjA5mNjjgoRP+1PsPNQ3M92To7Dbz2iCE0mdmZ6FZ8
+J4Su08i9t4L6dCzgM6aP
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert84[] = {
+ 0x30, 0x82, 0x05, 0x1b, 0x30, 0x82, 0x04, 0x03, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x08, 0x0a, 0x57, 0x82, 0x2c, 0xc6, 0xf5, 0xe1, 0x4f,
+ 0x19, 0xb7, 0x09, 0x55, 0xc8, 0x03, 0x42, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0x81, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x47, 0x42, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x12, 0x47, 0x72, 0x65, 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e,
+ 0x63, 0x68, 0x65, 0x73, 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x04, 0x07, 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72,
+ 0x64, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69,
+ 0x6d, 0x69, 0x74, 0x65, 0x64, 0x31, 0x27, 0x30, 0x25, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x1e, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e,
+ 0x17, 0x0d, 0x30, 0x36, 0x31, 0x32, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32,
+ 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0x89, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x47, 0x42, 0x31, 0x1b,
+ 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x47, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x63, 0x68, 0x65, 0x73,
+ 0x74, 0x65, 0x72, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x13, 0x07, 0x53, 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x64, 0x31, 0x1a, 0x30,
+ 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x43, 0x4f, 0x4d, 0x4f,
+ 0x44, 0x4f, 0x20, 0x43, 0x41, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65,
+ 0x64, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26,
+ 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20,
+ 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x53, 0x65,
+ 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20,
+ 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb8,
+ 0x4c, 0xb0, 0xaa, 0x8e, 0x06, 0xdf, 0x37, 0x4a, 0xa4, 0xf2, 0x08, 0xff,
+ 0xe2, 0xb9, 0x92, 0xd6, 0x6f, 0xeb, 0x9e, 0x35, 0x4e, 0xec, 0xe5, 0x65,
+ 0x08, 0x8b, 0x13, 0xc0, 0xbc, 0x38, 0x7b, 0x11, 0x12, 0x1c, 0x0b, 0x4b,
+ 0xf4, 0x37, 0x22, 0x15, 0xcc, 0x60, 0xd1, 0xc5, 0x1e, 0x28, 0xd7, 0x2f,
+ 0x9a, 0x97, 0xd7, 0x1c, 0x04, 0x8d, 0x9b, 0x63, 0x7d, 0x6e, 0x2f, 0xee,
+ 0xf8, 0x1f, 0x4b, 0x33, 0x3e, 0xd8, 0x4d, 0x86, 0x61, 0x0b, 0x5a, 0x9b,
+ 0xd8, 0x96, 0x3b, 0x05, 0x76, 0x0b, 0x2b, 0xcb, 0xd7, 0x85, 0x21, 0xbc,
+ 0x19, 0xa7, 0xc6, 0x68, 0x44, 0x83, 0x18, 0xb2, 0x17, 0x44, 0xb5, 0x90,
+ 0x9c, 0x65, 0x6f, 0x71, 0x92, 0x50, 0x71, 0xa0, 0x55, 0x72, 0x26, 0x92,
+ 0x5e, 0xd3, 0x69, 0xeb, 0x08, 0x3f, 0xf2, 0x7e, 0xa7, 0xa0, 0xb3, 0xeb,
+ 0xab, 0xe1, 0x03, 0xb9, 0x88, 0x7a, 0x81, 0x3f, 0xa5, 0x84, 0xdc, 0x92,
+ 0x43, 0x4e, 0x3b, 0x57, 0x70, 0x00, 0x1e, 0x6b, 0x99, 0x50, 0x0d, 0x53,
+ 0xe8, 0xe2, 0xb6, 0x18, 0x92, 0x1a, 0xcd, 0xb8, 0x4c, 0x5e, 0xd1, 0xa0,
+ 0xc4, 0xa0, 0xf1, 0xc6, 0xec, 0xfc, 0xdd, 0xd1, 0x7c, 0x91, 0x1a, 0x14,
+ 0x91, 0x32, 0x9d, 0x79, 0x46, 0xab, 0xf1, 0xf0, 0x48, 0x60, 0x28, 0x55,
+ 0xb4, 0x4c, 0xe6, 0x16, 0x0e, 0xbc, 0xef, 0x5e, 0xca, 0xd0, 0xfe, 0xec,
+ 0x91, 0xf0, 0xd5, 0x11, 0x18, 0x5c, 0xaa, 0xc3, 0x86, 0x67, 0xc4, 0x11,
+ 0x43, 0x08, 0x69, 0x55, 0x80, 0xb5, 0xb0, 0x20, 0x48, 0xda, 0x78, 0x89,
+ 0x09, 0x04, 0x57, 0x37, 0xf7, 0x5d, 0x28, 0xf3, 0x47, 0xfb, 0x18, 0xc9,
+ 0xbe, 0xbe, 0x78, 0xb0, 0x32, 0x74, 0xda, 0x55, 0xda, 0xd6, 0x54, 0x86,
+ 0x3e, 0x95, 0x2b, 0x15, 0x1a, 0xed, 0x94, 0x5b, 0x96, 0x6a, 0xf8, 0xe3,
+ 0xc5, 0x9d, 0xab, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x83,
+ 0x30, 0x82, 0x01, 0x7f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x0b, 0x58, 0xe5, 0x8b, 0xc6, 0x4c, 0x15,
+ 0x37, 0xa4, 0x40, 0xa9, 0x30, 0xa9, 0x21, 0xbe, 0x47, 0x36, 0x5a, 0x56,
+ 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x60, 0x59, 0xcd, 0x80, 0xc7, 0xc5, 0xe3, 0xab, 0x8c, 0x2f, 0xfc, 0x6b,
+ 0xe5, 0x5b, 0x0a, 0xf5, 0x0f, 0xde, 0x4b, 0xff, 0x30, 0x0e, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x20, 0x06, 0x03,
+ 0x55, 0x1d, 0x25, 0x04, 0x19, 0x30, 0x17, 0x06, 0x0a, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0x37, 0x0a, 0x03, 0x03, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x37, 0x30, 0x35, 0x30, 0x33, 0x06, 0x04, 0x55, 0x1d, 0x20,
+ 0x00, 0x30, 0x2b, 0x30, 0x29, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x01, 0x16, 0x1d, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6f,
+ 0x64, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x49,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x42, 0x30, 0x40, 0x30, 0x3e, 0xa0,
+ 0x3c, 0xa0, 0x3a, 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x4f, 0x4d, 0x4f, 0x44, 0x4f, 0x43,
+ 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x6c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x60, 0x30, 0x5e, 0x30, 0x36, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f,
+ 0x63, 0x61, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x6f, 0x6d, 0x6f, 0x64,
+ 0x6f, 0x55, 0x54, 0x4e, 0x53, 0x47, 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72,
+ 0x74, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x6f, 0x64, 0x6f, 0x63, 0x61, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x32,
+ 0x86, 0x37, 0x17, 0xd4, 0x67, 0xf2, 0xee, 0x2d, 0x7f, 0xa8, 0x03, 0xf6,
+ 0xdf, 0x22, 0xc7, 0xee, 0x4c, 0x0d, 0xb1, 0x4c, 0xc1, 0x27, 0xb4, 0x2c,
+ 0x5d, 0x0d, 0x30, 0x07, 0x8a, 0xb3, 0x41, 0x51, 0xe4, 0x47, 0xb0, 0xca,
+ 0xa2, 0x1d, 0x6d, 0xb9, 0xa4, 0xfb, 0x96, 0x23, 0x87, 0x03, 0xb6, 0x27,
+ 0x29, 0x25, 0x51, 0xfe, 0x48, 0xb5, 0x98, 0xc3, 0xdc, 0x34, 0x47, 0xe5,
+ 0x40, 0x43, 0x31, 0x14, 0x33, 0xef, 0xbd, 0x7f, 0x99, 0x43, 0xff, 0x48,
+ 0x68, 0x01, 0xde, 0x88, 0x44, 0x63, 0x27, 0xf1, 0x22, 0xbe, 0xc0, 0x2f,
+ 0x74, 0xd6, 0x57, 0x63, 0xd6, 0x30, 0xc7, 0x3f, 0x3b, 0xcc, 0xbb, 0xd2,
+ 0xe5, 0x33, 0x72, 0x99, 0x5a, 0xbf, 0xd9, 0x33, 0x59, 0xb6, 0x41, 0x83,
+ 0xb4, 0x98, 0x3a, 0x9c, 0x77, 0x00, 0xa4, 0xf1, 0xc4, 0x30, 0xe8, 0x1d,
+ 0xaf, 0xe2, 0xd6, 0xf8, 0x7e, 0x2a, 0x66, 0x45, 0x58, 0x81, 0x21, 0x8f,
+ 0x50, 0x60, 0x15, 0xef, 0x60, 0x62, 0xd4, 0xab, 0x3a, 0xb9, 0xf0, 0xfa,
+ 0x5c, 0xe7, 0x3c, 0x3d, 0x9d, 0xb9, 0x5f, 0x75, 0xc9, 0xc8, 0x73, 0xaf,
+ 0x5e, 0xfe, 0x03, 0x6c, 0x4c, 0xe6, 0xea, 0x28, 0x14, 0x54, 0x21, 0x8e,
+ 0x99, 0x4c, 0xdb, 0x25, 0x7c, 0xcc, 0x03, 0xd5, 0x81, 0x26, 0xfa, 0x57,
+ 0x42, 0x8b, 0x79, 0x10, 0x03, 0x70, 0xf5, 0x6a, 0x43, 0x82, 0x5c, 0x5b,
+ 0x5c, 0x1f, 0x85, 0x09, 0x20, 0x42, 0x66, 0x71, 0x59, 0xd5, 0x2f, 0x49,
+ 0xb5, 0xab, 0x29, 0x63, 0x54, 0xe3, 0x03, 0x99, 0x8d, 0x8e, 0x38, 0x28,
+ 0x44, 0xff, 0xb5, 0x3e, 0xc3, 0xcd, 0x43, 0x73, 0x3d, 0xd9, 0x3a, 0x3b,
+ 0x0d, 0xbc, 0xf6, 0x88, 0x21, 0x34, 0x99, 0xd9, 0x99, 0xe8, 0x56, 0x7c,
+ 0x27, 0x84, 0xae, 0xd3, 0xc8, 0xbd, 0xb7, 0x82, 0xfa, 0x74, 0x2c, 0xe0,
+ 0x33, 0xa6, 0x8f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 03:0e:95:29:4d:ae:c1:2c:03:cf:31:ab:5b:02:71:d7
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
+ Validity
+ Not Before: May 30 10:48:38 2000 GMT
+ Not After : May 30 10:48:38 2020 GMT
+ Subject: C=US, O=Network Solutions L.L.C., CN=Network Solutions Certificate Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e4:bc:7e:92:30:6d:c6:d8:8e:2b:0b:bc:46:ce:
+ e0:27:96:de:de:f9:fa:12:d3:3c:33:73:b3:04:2f:
+ bc:71:8c:e5:9f:b6:22:60:3e:5f:5d:ce:09:ff:82:
+ 0c:1b:9a:51:50:1a:26:89:dd:d5:61:5d:19:dc:12:
+ 0f:2d:0a:a2:43:5d:17:d0:34:92:20:ea:73:cf:38:
+ 2c:06:26:09:7a:72:f7:fa:50:32:f8:c2:93:d3:69:
+ a2:23:ce:41:b1:cc:e4:d5:1f:36:d1:8a:3a:f8:8c:
+ 63:e2:14:59:69:ed:0d:d3:7f:6b:e8:b8:03:e5:4f:
+ 6a:e5:98:63:69:48:05:be:2e:ff:33:b6:e9:97:59:
+ 69:f8:67:19:ae:93:61:96:44:15:d3:72:b0:3f:bc:
+ 6a:7d:ec:48:7f:8d:c3:ab:aa:71:2b:53:69:41:53:
+ 34:b5:b0:b9:c5:06:0a:c4:b0:45:f5:41:5d:6e:89:
+ 45:7b:3d:3b:26:8c:74:c2:e5:d2:d1:7d:b2:11:d4:
+ fb:58:32:22:9a:80:c9:dc:fd:0c:e9:7f:5e:03:97:
+ ce:3b:00:14:87:27:70:38:a9:8e:6e:b3:27:76:98:
+ 51:e0:05:e3:21:ab:1a:d5:85:22:3c:29:b5:9a:16:
+ c5:80:a8:f4:bb:6b:30:8f:2f:46:02:a2:b1:0c:22:
+ e0:d3
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Authority Key Identifier:
+ keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
+
+ X509v3 Subject Key Identifier:
+ 21:30:C9:FB:00:D7:4E:98:DA:87:AA:2A:D0:A7:2E:B1:40:31:A7:4C
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.782.1.2.1.8.1
+ CPS: http://www.networksolutions.com/legal/SSL-legal-repository-ev-cps.jsp
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.usertrust.com/AddTrustExternalCARoot.crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustExternalCARoot.p7c
+ CA Issuers - URI:http://crt.usertrust.com/AddTrustUTNSGCCA.crt
+ OCSP - URI:http://ocsp.usertrust.com
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 56:45:d5:72:11:a7:cc:89:b3:32:5a:90:8c:53:73:05:36:c4:
+ 33:27:af:3e:41:c0:e9:72:2d:49:9d:52:1e:55:5a:9e:87:d0:
+ a9:4e:f0:5c:0c:9d:d2:3a:0f:8a:e7:4a:cc:5e:ba:a5:b5:9f:
+ c9:ab:3d:99:4c:46:86:7c:c0:35:8e:17:7b:5d:4a:21:2b:6e:
+ 2c:67:fd:1f:66:3e:c8:5d:4b:0c:d3:cd:ef:51:74:c1:00:b7:
+ c7:ac:b0:12:d5:77:20:f9:f0:f3:e2:f4:e7:e2:f6:07:fd:1d:
+ 94:f8:e5:3d:83:1d:37:2a:93:aa:2c:7a:99:d6:62:90:11:80:
+ 23:08:f8:62:cc:1d:27:47:d4:53:97:1c:17:52:10:70:07:22:
+ 3a:ec:9a:37:d1:19:1e:d4:20:8e:6f:9f:44:7f:3a:11:ab:6b:
+ 9b:ce:81:4f:c4:8e:ee:3c:b0:27:4a:1f:9c:79:d1:91:bf:73:
+ f0:dd:b2:00:5c:33:ee:59:61:fd:ae:27:72:f3:b1:3d:7a:ff:
+ 4c:41:92:e4:83:e2:56:a2:44:e3:03:56:f6:34:9a:36:7b:62:
+ 96:22:f3:6f:d3:4e:7f:2c:b2:b0:b2:5b:b1:2a:06:0e:36:5b:
+ 63:34:c8:3b:69:b3:ef:51:ac:9a:68:85:ed:2e:2d:44:fe:9e:
+ 09:d7:26:f8
+-----BEGIN CERTIFICATE-----
+MIIFLjCCBBagAwIBAgIQAw6VKU2uwSwDzzGrWwJx1zANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
+YjELMAkGA1UEBhMCVVMxITAfBgNVBAoTGE5ldHdvcmsgU29sdXRpb25zIEwuTC5D
+LjEwMC4GA1UEAxMnTmV0d29yayBTb2x1dGlvbnMgQ2VydGlmaWNhdGUgQXV0aG9y
+aXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5Lx+kjBtxtiOKwu8
+Rs7gJ5be3vn6EtM8M3OzBC+8cYzln7YiYD5fXc4J/4IMG5pRUBomid3VYV0Z3BIP
+LQqiQ10X0DSSIOpzzzgsBiYJenL3+lAy+MKT02miI85Bsczk1R820Yo6+Ixj4hRZ
+ae0N039r6LgD5U9q5ZhjaUgFvi7/M7bpl1lp+GcZrpNhlkQV03KwP7xqfexIf43D
+q6pxK1NpQVM0tbC5xQYKxLBF9UFdbolFez07Jox0wuXS0X2yEdT7WDIimoDJ3P0M
+6X9eA5fOOwAUhydwOKmObrMndphR4AXjIasa1YUiPCm1mhbFgKj0u2swjy9GAqKx
+DCLg0wIDAQABo4IB0TCCAc0wHwYDVR0jBBgwFoAUrb2YejS0Jvf6xCZU7wO94CTL
+VBowHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
+BjAPBgNVHRMBAf8EBTADAQH/MG4GA1UdIARnMGUwYwYMKwYBBAGGDgECAQgBMFMw
+UQYIKwYBBQUHAgEWRWh0dHA6Ly93d3cubmV0d29ya3NvbHV0aW9ucy5jb20vbGVn
+YWwvU1NMLWxlZ2FsLXJlcG9zaXRvcnktZXYtY3BzLmpzcDBEBgNVHR8EPTA7MDmg
+N6A1hjNodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RFeHRlcm5hbENB
+Um9vdC5jcmwwgbMGCCsGAQUFBwEBBIGmMIGjMD8GCCsGAQUFBzAChjNodHRwOi8v
+Y3J0LnVzZXJ0cnVzdC5jb20vQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5wN2MwOQYI
+KwYBBQUHMAKGLWh0dHA6Ly9jcnQudXNlcnRydXN0LmNvbS9BZGRUcnVzdFVUTlNH
+Q0NBLmNydDAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTAN
+BgkqhkiG9w0BAQUFAAOCAQEAVkXVchGnzImzMlqQjFNzBTbEMyevPkHA6XItSZ1S
+HlVanofQqU7wXAyd0joPiudKzF66pbWfyas9mUxGhnzANY4Xe11KIStuLGf9H2Y+
+yF1LDNPN71F0wQC3x6ywEtV3IPnw8+L05+L2B/0dlPjlPYMdNyqTqix6mdZikBGA
+Iwj4YswdJ0fUU5ccF1IQcAciOuyaN9EZHtQgjm+fRH86Eatrm86BT8SO7jywJ0of
+nHnRkb9z8N2yAFwz7llh/a4ncvOxPXr/TEGS5IPiVqJE4wNW9jSaNntiliLzb9NO
+fyyysLJbsSoGDjZbYzTIO2mz71GsmmiF7S4tRP6eCdcm+A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert85[] = {
+ 0x30, 0x82, 0x05, 0x2e, 0x30, 0x82, 0x04, 0x16, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x03, 0x0e, 0x95, 0x29, 0x4d, 0xae, 0xc1, 0x2c, 0x03,
+ 0xcf, 0x31, 0xab, 0x5b, 0x02, 0x71, 0xd7, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6f,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x53,
+ 0x45, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b,
+ 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x42, 0x31,
+ 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d, 0x41, 0x64,
+ 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45, 0x78, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x20, 0x54, 0x54, 0x50, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x19, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x43, 0x41, 0x20, 0x52,
+ 0x6f, 0x6f, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x35, 0x33,
+ 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, 0x33, 0x38, 0x5a, 0x30,
+ 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x18, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c,
+ 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43,
+ 0x2e, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27,
+ 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xe4, 0xbc, 0x7e, 0x92, 0x30, 0x6d, 0xc6, 0xd8, 0x8e, 0x2b, 0x0b, 0xbc,
+ 0x46, 0xce, 0xe0, 0x27, 0x96, 0xde, 0xde, 0xf9, 0xfa, 0x12, 0xd3, 0x3c,
+ 0x33, 0x73, 0xb3, 0x04, 0x2f, 0xbc, 0x71, 0x8c, 0xe5, 0x9f, 0xb6, 0x22,
+ 0x60, 0x3e, 0x5f, 0x5d, 0xce, 0x09, 0xff, 0x82, 0x0c, 0x1b, 0x9a, 0x51,
+ 0x50, 0x1a, 0x26, 0x89, 0xdd, 0xd5, 0x61, 0x5d, 0x19, 0xdc, 0x12, 0x0f,
+ 0x2d, 0x0a, 0xa2, 0x43, 0x5d, 0x17, 0xd0, 0x34, 0x92, 0x20, 0xea, 0x73,
+ 0xcf, 0x38, 0x2c, 0x06, 0x26, 0x09, 0x7a, 0x72, 0xf7, 0xfa, 0x50, 0x32,
+ 0xf8, 0xc2, 0x93, 0xd3, 0x69, 0xa2, 0x23, 0xce, 0x41, 0xb1, 0xcc, 0xe4,
+ 0xd5, 0x1f, 0x36, 0xd1, 0x8a, 0x3a, 0xf8, 0x8c, 0x63, 0xe2, 0x14, 0x59,
+ 0x69, 0xed, 0x0d, 0xd3, 0x7f, 0x6b, 0xe8, 0xb8, 0x03, 0xe5, 0x4f, 0x6a,
+ 0xe5, 0x98, 0x63, 0x69, 0x48, 0x05, 0xbe, 0x2e, 0xff, 0x33, 0xb6, 0xe9,
+ 0x97, 0x59, 0x69, 0xf8, 0x67, 0x19, 0xae, 0x93, 0x61, 0x96, 0x44, 0x15,
+ 0xd3, 0x72, 0xb0, 0x3f, 0xbc, 0x6a, 0x7d, 0xec, 0x48, 0x7f, 0x8d, 0xc3,
+ 0xab, 0xaa, 0x71, 0x2b, 0x53, 0x69, 0x41, 0x53, 0x34, 0xb5, 0xb0, 0xb9,
+ 0xc5, 0x06, 0x0a, 0xc4, 0xb0, 0x45, 0xf5, 0x41, 0x5d, 0x6e, 0x89, 0x45,
+ 0x7b, 0x3d, 0x3b, 0x26, 0x8c, 0x74, 0xc2, 0xe5, 0xd2, 0xd1, 0x7d, 0xb2,
+ 0x11, 0xd4, 0xfb, 0x58, 0x32, 0x22, 0x9a, 0x80, 0xc9, 0xdc, 0xfd, 0x0c,
+ 0xe9, 0x7f, 0x5e, 0x03, 0x97, 0xce, 0x3b, 0x00, 0x14, 0x87, 0x27, 0x70,
+ 0x38, 0xa9, 0x8e, 0x6e, 0xb3, 0x27, 0x76, 0x98, 0x51, 0xe0, 0x05, 0xe3,
+ 0x21, 0xab, 0x1a, 0xd5, 0x85, 0x22, 0x3c, 0x29, 0xb5, 0x9a, 0x16, 0xc5,
+ 0x80, 0xa8, 0xf4, 0xbb, 0x6b, 0x30, 0x8f, 0x2f, 0x46, 0x02, 0xa2, 0xb1,
+ 0x0c, 0x22, 0xe0, 0xd3, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0xd1, 0x30, 0x82, 0x01, 0xcd, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xad, 0xbd, 0x98, 0x7a, 0x34, 0xb4,
+ 0x26, 0xf7, 0xfa, 0xc4, 0x26, 0x54, 0xef, 0x03, 0xbd, 0xe0, 0x24, 0xcb,
+ 0x54, 0x1a, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87, 0xaa,
+ 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x06, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x6e, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x67, 0x30, 0x65, 0x30, 0x63, 0x06, 0x0c, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x86, 0x0e, 0x01, 0x02, 0x01, 0x08, 0x01, 0x30, 0x53, 0x30,
+ 0x51, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16,
+ 0x45, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x6f, 0x6c, 0x75, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x65, 0x67,
+ 0x61, 0x6c, 0x2f, 0x53, 0x53, 0x4c, 0x2d, 0x6c, 0x65, 0x67, 0x61, 0x6c,
+ 0x2d, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2d,
+ 0x65, 0x76, 0x2d, 0x63, 0x70, 0x73, 0x2e, 0x6a, 0x73, 0x70, 0x30, 0x44,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0,
+ 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41,
+ 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xb3, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0xa6,
+ 0x30, 0x81, 0xa3, 0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x02, 0x86, 0x33, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75,
+ 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x43, 0x41,
+ 0x52, 0x6f, 0x6f, 0x74, 0x2e, 0x70, 0x37, 0x63, 0x30, 0x39, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x2d, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x41,
+ 0x64, 0x64, 0x54, 0x72, 0x75, 0x73, 0x74, 0x55, 0x54, 0x4e, 0x53, 0x47,
+ 0x43, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x25, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x75, 0x73, 0x65,
+ 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x56, 0x45, 0xd5, 0x72, 0x11, 0xa7,
+ 0xcc, 0x89, 0xb3, 0x32, 0x5a, 0x90, 0x8c, 0x53, 0x73, 0x05, 0x36, 0xc4,
+ 0x33, 0x27, 0xaf, 0x3e, 0x41, 0xc0, 0xe9, 0x72, 0x2d, 0x49, 0x9d, 0x52,
+ 0x1e, 0x55, 0x5a, 0x9e, 0x87, 0xd0, 0xa9, 0x4e, 0xf0, 0x5c, 0x0c, 0x9d,
+ 0xd2, 0x3a, 0x0f, 0x8a, 0xe7, 0x4a, 0xcc, 0x5e, 0xba, 0xa5, 0xb5, 0x9f,
+ 0xc9, 0xab, 0x3d, 0x99, 0x4c, 0x46, 0x86, 0x7c, 0xc0, 0x35, 0x8e, 0x17,
+ 0x7b, 0x5d, 0x4a, 0x21, 0x2b, 0x6e, 0x2c, 0x67, 0xfd, 0x1f, 0x66, 0x3e,
+ 0xc8, 0x5d, 0x4b, 0x0c, 0xd3, 0xcd, 0xef, 0x51, 0x74, 0xc1, 0x00, 0xb7,
+ 0xc7, 0xac, 0xb0, 0x12, 0xd5, 0x77, 0x20, 0xf9, 0xf0, 0xf3, 0xe2, 0xf4,
+ 0xe7, 0xe2, 0xf6, 0x07, 0xfd, 0x1d, 0x94, 0xf8, 0xe5, 0x3d, 0x83, 0x1d,
+ 0x37, 0x2a, 0x93, 0xaa, 0x2c, 0x7a, 0x99, 0xd6, 0x62, 0x90, 0x11, 0x80,
+ 0x23, 0x08, 0xf8, 0x62, 0xcc, 0x1d, 0x27, 0x47, 0xd4, 0x53, 0x97, 0x1c,
+ 0x17, 0x52, 0x10, 0x70, 0x07, 0x22, 0x3a, 0xec, 0x9a, 0x37, 0xd1, 0x19,
+ 0x1e, 0xd4, 0x20, 0x8e, 0x6f, 0x9f, 0x44, 0x7f, 0x3a, 0x11, 0xab, 0x6b,
+ 0x9b, 0xce, 0x81, 0x4f, 0xc4, 0x8e, 0xee, 0x3c, 0xb0, 0x27, 0x4a, 0x1f,
+ 0x9c, 0x79, 0xd1, 0x91, 0xbf, 0x73, 0xf0, 0xdd, 0xb2, 0x00, 0x5c, 0x33,
+ 0xee, 0x59, 0x61, 0xfd, 0xae, 0x27, 0x72, 0xf3, 0xb1, 0x3d, 0x7a, 0xff,
+ 0x4c, 0x41, 0x92, 0xe4, 0x83, 0xe2, 0x56, 0xa2, 0x44, 0xe3, 0x03, 0x56,
+ 0xf6, 0x34, 0x9a, 0x36, 0x7b, 0x62, 0x96, 0x22, 0xf3, 0x6f, 0xd3, 0x4e,
+ 0x7f, 0x2c, 0xb2, 0xb0, 0xb2, 0x5b, 0xb1, 0x2a, 0x06, 0x0e, 0x36, 0x5b,
+ 0x63, 0x34, 0xc8, 0x3b, 0x69, 0xb3, 0xef, 0x51, 0xac, 0x9a, 0x68, 0x85,
+ 0xed, 0x2e, 0x2d, 0x44, 0xfe, 0x9e, 0x09, 0xd7, 0x26, 0xf8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5f:a6:be:80:b6:86:c6:2f:01:ed:0c:ab:b1:96:a1:05
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=ZA, ST=Western Cape, L=Cape Town, O=Thawte Consulting cc, OU=Certification Services Division, CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+ Validity
+ Not Before: Nov 17 00:00:00 2006 GMT
+ Not After : Dec 30 23:59:59 2020 GMT
+ Subject: C=US, O=thawte, Inc., OU=Certification Services Division, OU=(c) 2006 thawte, Inc. - For authorized use only, CN=thawte Primary Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ac:a0:f0:fb:80:59:d4:9c:c7:a4:cf:9d:a1:59:
+ 73:09:10:45:0c:0d:2c:6e:68:f1:6c:5b:48:68:49:
+ 59:37:fc:0b:33:19:c2:77:7f:cc:10:2d:95:34:1c:
+ e6:eb:4d:09:a7:1c:d2:b8:c9:97:36:02:b7:89:d4:
+ 24:5f:06:c0:cc:44:94:94:8d:02:62:6f:eb:5a:dd:
+ 11:8d:28:9a:5c:84:90:10:7a:0d:bd:74:66:2f:6a:
+ 38:a0:e2:d5:54:44:eb:1d:07:9f:07:ba:6f:ee:e9:
+ fd:4e:0b:29:f5:3e:84:a0:01:f1:9c:ab:f8:1c:7e:
+ 89:a4:e8:a1:d8:71:65:0d:a3:51:7b:ee:bc:d2:22:
+ 60:0d:b9:5b:9d:df:ba:fc:51:5b:0b:af:98:b2:e9:
+ 2e:e9:04:e8:62:87:de:2b:c8:d7:4e:c1:4c:64:1e:
+ dd:cf:87:58:ba:4a:4f:ca:68:07:1d:1c:9d:4a:c6:
+ d5:2f:91:cc:7c:71:72:1c:c5:c0:67:eb:32:fd:c9:
+ 92:5c:94:da:85:c0:9b:bf:53:7d:2b:09:f4:8c:9d:
+ 91:1f:97:6a:52:cb:de:09:36:a4:77:d8:7b:87:50:
+ 44:d5:3e:6e:29:69:fb:39:49:26:1e:09:a5:80:7b:
+ 40:2d:eb:e8:27:85:c9:fe:61:fd:7e:e6:7c:97:1d:
+ d5:9d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.thawte.com/cps
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 7B:5B:45:CF:AF:CE:CB:7A:FD:31:92:1A:6A:B6:F3:46:EB:57:48:50
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.thawte.com/ThawtePremiumServerCA.crl
+
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ X509v3 Authority Key Identifier:
+ DirName:/C=ZA/ST=Western Cape/L=Cape Town/O=Thawte Consulting cc/OU=Certification Services Division/CN=Thawte Premium Server CA/emailAddress=premium-server@thawte.com
+ serial:01
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 2b:ca:12:c9:dd:d7:cc:63:1c:9b:31:35:4a:dd:e4:b7:f6:9d:
+ d1:a4:fb:1e:f8:47:f9:ae:07:8e:0d:58:12:fb:da:ed:b5:cc:
+ 33:e5:97:68:47:61:42:d5:66:a9:6e:1e:47:bf:85:db:7d:58:
+ d1:77:5a:cc:90:61:98:9a:29:f5:9d:b1:cf:b8:dc:f3:7b:80:
+ 47:48:d1:7d:f4:68:8c:c4:41:cb:b4:e9:fd:f0:23:e0:b1:9b:
+ 76:2a:6d:28:56:a3:8c:cd:e9:ec:21:00:71:f0:5f:dd:50:a5:
+ 69:42:1b:83:11:5d:84:28:d3:27:ae:ec:2a:ab:2f:60:42:c5:
+ c4:78
+-----BEGIN CERTIFICATE-----
+MIIFUTCCBLqgAwIBAgIQX6a+gLaGxi8B7QyrsZahBTANBgkqhkiG9w0BAQUFADCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTA2MTExNzAwMDAwMFoXDTIwMTIzMDIzNTk1OVow
+gakxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwx0aGF3dGUsIEluYy4xKDAmBgNVBAsT
+H0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xODA2BgNVBAsTLyhjKSAy
+MDA2IHRoYXd0ZSwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD
+VQQDExZ0aGF3dGUgUHJpbWFyeSBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEArKDw+4BZ1JzHpM+doVlzCRBFDA0sbmjxbFtIaElZN/wLMxnC
+d3/MEC2VNBzm600JpxzSuMmXNgK3idQkXwbAzESUlI0CYm/rWt0RjSiaXISQEHoN
+vXRmL2o4oOLVVETrHQefB7pv7un9Tgsp9T6EoAHxnKv4HH6JpOih2HFlDaNRe+68
+0iJgDblbnd+6/FFbC6+Ysuku6QToYofeK8jXTsFMZB7dz4dYukpPymgHHRydSsbV
+L5HMfHFyHMXAZ+sy/cmSXJTahcCbv1N9Kwn0jJ2RH5dqUsveCTakd9h7h1BE1T5u
+KWn7OUkmHgmlgHtALevoJ4XJ/mH9fuZ8lx3VnQIDAQABo4IBzTCCAckwDwYDVR0T
+AQH/BAUwAwEB/zA7BgNVHSAENDAyMDAGBFUdIAAwKDAmBggrBgEFBQcCARYaaHR0
+cHM6Ly93d3cudGhhd3RlLmNvbS9jcHMwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQW
+BBR7W0XPr87Lev0xkhpqtvNG61dIUDBABgNVHR8EOTA3MDWgM6Axhi9odHRwOi8v
+Y3JsLnRoYXd0ZS5jb20vVGhhd3RlUHJlbWl1bVNlcnZlckNBLmNybDAgBgNVHSUE
+GTAXBglghkgBhvhCBAEGCmCGSAGG+EUBCAEwgeUGA1UdIwSB3TCB2qGB1KSB0TCB
+zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
+Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
+d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tggEBMA0GCSqGSIb3DQEBBQUAA4GBACvKEsnd18xjHJsx
+NUrd5Lf2ndGk+x74R/muB44NWBL72u21zDPll2hHYULVZqluHke/hdt9WNF3WsyQ
+YZiaKfWdsc+43PN7gEdI0X30aIzEQcu06f3wI+Cxm3YqbShWo4zN6ewhAHHwX91Q
+pWlCG4MRXYQo0yeu7CqrL2BCxcR4
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert86[] = {
+ 0x30, 0x82, 0x05, 0x51, 0x30, 0x82, 0x04, 0xba, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x5f, 0xa6, 0xbe, 0x80, 0xb6, 0x86, 0xc6, 0x2f, 0x01,
+ 0xed, 0x0c, 0xab, 0xb1, 0x96, 0xa1, 0x05, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70,
+ 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09,
+ 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31,
+ 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30,
+ 0x31, 0x32, 0x33, 0x30, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30,
+ 0x81, 0xa9, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0c, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20, 0x49, 0x6e,
+ 0x63, 0x2e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x20,
+ 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x38, 0x30, 0x36,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2f, 0x28, 0x63, 0x29, 0x20, 0x32,
+ 0x30, 0x30, 0x36, 0x20, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73,
+ 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x16, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x20,
+ 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x52, 0x6f, 0x6f, 0x74,
+ 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xac, 0xa0, 0xf0, 0xfb, 0x80, 0x59, 0xd4, 0x9c, 0xc7, 0xa4, 0xcf, 0x9d,
+ 0xa1, 0x59, 0x73, 0x09, 0x10, 0x45, 0x0c, 0x0d, 0x2c, 0x6e, 0x68, 0xf1,
+ 0x6c, 0x5b, 0x48, 0x68, 0x49, 0x59, 0x37, 0xfc, 0x0b, 0x33, 0x19, 0xc2,
+ 0x77, 0x7f, 0xcc, 0x10, 0x2d, 0x95, 0x34, 0x1c, 0xe6, 0xeb, 0x4d, 0x09,
+ 0xa7, 0x1c, 0xd2, 0xb8, 0xc9, 0x97, 0x36, 0x02, 0xb7, 0x89, 0xd4, 0x24,
+ 0x5f, 0x06, 0xc0, 0xcc, 0x44, 0x94, 0x94, 0x8d, 0x02, 0x62, 0x6f, 0xeb,
+ 0x5a, 0xdd, 0x11, 0x8d, 0x28, 0x9a, 0x5c, 0x84, 0x90, 0x10, 0x7a, 0x0d,
+ 0xbd, 0x74, 0x66, 0x2f, 0x6a, 0x38, 0xa0, 0xe2, 0xd5, 0x54, 0x44, 0xeb,
+ 0x1d, 0x07, 0x9f, 0x07, 0xba, 0x6f, 0xee, 0xe9, 0xfd, 0x4e, 0x0b, 0x29,
+ 0xf5, 0x3e, 0x84, 0xa0, 0x01, 0xf1, 0x9c, 0xab, 0xf8, 0x1c, 0x7e, 0x89,
+ 0xa4, 0xe8, 0xa1, 0xd8, 0x71, 0x65, 0x0d, 0xa3, 0x51, 0x7b, 0xee, 0xbc,
+ 0xd2, 0x22, 0x60, 0x0d, 0xb9, 0x5b, 0x9d, 0xdf, 0xba, 0xfc, 0x51, 0x5b,
+ 0x0b, 0xaf, 0x98, 0xb2, 0xe9, 0x2e, 0xe9, 0x04, 0xe8, 0x62, 0x87, 0xde,
+ 0x2b, 0xc8, 0xd7, 0x4e, 0xc1, 0x4c, 0x64, 0x1e, 0xdd, 0xcf, 0x87, 0x58,
+ 0xba, 0x4a, 0x4f, 0xca, 0x68, 0x07, 0x1d, 0x1c, 0x9d, 0x4a, 0xc6, 0xd5,
+ 0x2f, 0x91, 0xcc, 0x7c, 0x71, 0x72, 0x1c, 0xc5, 0xc0, 0x67, 0xeb, 0x32,
+ 0xfd, 0xc9, 0x92, 0x5c, 0x94, 0xda, 0x85, 0xc0, 0x9b, 0xbf, 0x53, 0x7d,
+ 0x2b, 0x09, 0xf4, 0x8c, 0x9d, 0x91, 0x1f, 0x97, 0x6a, 0x52, 0xcb, 0xde,
+ 0x09, 0x36, 0xa4, 0x77, 0xd8, 0x7b, 0x87, 0x50, 0x44, 0xd5, 0x3e, 0x6e,
+ 0x29, 0x69, 0xfb, 0x39, 0x49, 0x26, 0x1e, 0x09, 0xa5, 0x80, 0x7b, 0x40,
+ 0x2d, 0xeb, 0xe8, 0x27, 0x85, 0xc9, 0xfe, 0x61, 0xfd, 0x7e, 0xe6, 0x7c,
+ 0x97, 0x1d, 0xd5, 0x9d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0xcd, 0x30, 0x82, 0x01, 0xc9, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x3b,
+ 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x34, 0x30, 0x32, 0x30, 0x30, 0x06,
+ 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x28, 0x30, 0x26, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1a, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x74, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30,
+ 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03,
+ 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0x7b, 0x5b, 0x45, 0xcf, 0xaf, 0xce, 0xcb, 0x7a, 0xfd, 0x31,
+ 0x92, 0x1a, 0x6a, 0xb6, 0xf3, 0x46, 0xeb, 0x57, 0x48, 0x50, 0x30, 0x40,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x39, 0x30, 0x37, 0x30, 0x35, 0xa0,
+ 0x33, 0xa0, 0x31, 0x86, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2e, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x54, 0x68, 0x61, 0x77, 0x74, 0x65, 0x50, 0x72, 0x65,
+ 0x6d, 0x69, 0x75, 0x6d, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x20, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x19, 0x30, 0x17, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42,
+ 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01,
+ 0x08, 0x01, 0x30, 0x81, 0xe5, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81,
+ 0xdd, 0x30, 0x81, 0xda, 0xa1, 0x81, 0xd4, 0xa4, 0x81, 0xd1, 0x30, 0x81,
+ 0xce, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x5a, 0x41, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
+ 0x0c, 0x57, 0x65, 0x73, 0x74, 0x65, 0x72, 0x6e, 0x20, 0x43, 0x61, 0x70,
+ 0x65, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09,
+ 0x43, 0x61, 0x70, 0x65, 0x20, 0x54, 0x6f, 0x77, 0x6e, 0x31, 0x1d, 0x30,
+ 0x1b, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x14, 0x54, 0x68, 0x61, 0x77,
+ 0x74, 0x65, 0x20, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x6e,
+ 0x67, 0x20, 0x63, 0x63, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x1f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x73, 0x20, 0x44, 0x69, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x21,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x18, 0x54, 0x68, 0x61,
+ 0x77, 0x74, 0x65, 0x20, 0x50, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x28, 0x30,
+ 0x26, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01,
+ 0x16, 0x19, 0x70, 0x72, 0x65, 0x6d, 0x69, 0x75, 0x6d, 0x2d, 0x73, 0x65,
+ 0x72, 0x76, 0x65, 0x72, 0x40, 0x74, 0x68, 0x61, 0x77, 0x74, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x82, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81,
+ 0x00, 0x2b, 0xca, 0x12, 0xc9, 0xdd, 0xd7, 0xcc, 0x63, 0x1c, 0x9b, 0x31,
+ 0x35, 0x4a, 0xdd, 0xe4, 0xb7, 0xf6, 0x9d, 0xd1, 0xa4, 0xfb, 0x1e, 0xf8,
+ 0x47, 0xf9, 0xae, 0x07, 0x8e, 0x0d, 0x58, 0x12, 0xfb, 0xda, 0xed, 0xb5,
+ 0xcc, 0x33, 0xe5, 0x97, 0x68, 0x47, 0x61, 0x42, 0xd5, 0x66, 0xa9, 0x6e,
+ 0x1e, 0x47, 0xbf, 0x85, 0xdb, 0x7d, 0x58, 0xd1, 0x77, 0x5a, 0xcc, 0x90,
+ 0x61, 0x98, 0x9a, 0x29, 0xf5, 0x9d, 0xb1, 0xcf, 0xb8, 0xdc, 0xf3, 0x7b,
+ 0x80, 0x47, 0x48, 0xd1, 0x7d, 0xf4, 0x68, 0x8c, 0xc4, 0x41, 0xcb, 0xb4,
+ 0xe9, 0xfd, 0xf0, 0x23, 0xe0, 0xb1, 0x9b, 0x76, 0x2a, 0x6d, 0x28, 0x56,
+ 0xa3, 0x8c, 0xcd, 0xe9, 0xec, 0x21, 0x00, 0x71, 0xf0, 0x5f, 0xdd, 0x50,
+ 0xa5, 0x69, 0x42, 0x1b, 0x83, 0x11, 0x5d, 0x84, 0x28, 0xd3, 0x27, 0xae,
+ 0xec, 0x2a, 0xab, 0x2f, 0x60, 0x42, 0xc5, 0xc4, 0x78,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5b:77:59:c6:17:84:e1:5e:c7:27:c0:32:95:29:28:6b
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2016 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)06, CN=VeriSign Class 3 Extended Validation SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:98:db:a0:55:eb:9c:fd:17:79:e3:9a:6e:14:1d:
+ b1:5b:98:23:87:16:6e:87:76:9c:b5:38:3b:b5:a0:
+ 7a:b4:07:63:09:19:e6:2a:88:48:a9:e7:9d:b6:30:
+ 5a:08:97:0c:ec:aa:e4:16:69:72:62:23:9a:fb:7a:
+ 54:28:98:c5:0c:2d:b7:d7:22:b6:c8:f9:38:17:c7:
+ dd:da:31:46:9a:94:14:8e:9e:ee:78:a0:b7:22:d4:
+ 49:54:97:4d:e5:74:5b:92:bc:ec:6c:2c:df:e7:c1:
+ b6:1b:1a:55:6b:66:08:03:7f:45:af:9a:33:f1:10:
+ c0:6c:99:4a:92:24:31:08:6d:dd:02:3e:61:76:78:
+ 78:b6:ed:7e:37:ae:6c:f3:89:e1:b7:e1:dc:15:cc:
+ b7:56:9f:80:a0:b1:05:7f:4e:37:15:ff:b7:2f:1e:
+ 8f:06:38:3f:50:b7:69:28:a3:b5:66:5f:36:1a:52:
+ 48:43:66:52:df:a2:92:4f:d3:18:60:be:e3:ea:5e:
+ 19:71:05:bf:9e:1c:6c:68:72:25:6f:b3:7b:73:c9:
+ 6d:bd:12:ff:9b:41:32:5e:f4:e8:7e:c5:0b:a3:4c:
+ 64:d1:4e:bc:26:08:65:fb:19:97:58:78:e1:33:bf:
+ ed:68:3e:b1:27:45:6f:c0:e2:ec:97:69:f7:5c:d3:
+ f7:51
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ FC:8A:50:BA:9E:B9:25:5A:7B:55:85:4F:95:00:63:8F:E9:58:6B:43
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://EVSecure-crl.verisign.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Netscape Cert Type:
+ SSL CA, S/MIME CA
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=Class3CA2048-1-47
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.verisign.com
+
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 96:a2:fa:7f:e6:3d:ed:d4:2b:ce:b7:15:3f:c0:72:03:5f:8b:
+ ba:16:90:25:f7:c2:83:d8:c7:75:34:63:68:12:53:0c:53:89:
+ 7b:c9:56:09:a7:c3:36:44:4e:0e:d0:62:62:b3:86:fa:e8:a1:
+ 9b:34:67:8d:53:22:17:3e:fd:ac:ee:67:2e:43:e2:5d:7f:33:
+ 84:f2:a2:70:c0:6e:82:97:c0:34:fd:25:c6:23:7f:ed:e6:b0:
+ c5:57:43:84:b2:de:2d:f1:d0:f6:48:1f:14:71:57:b2:ac:31:
+ e1:97:24:23:c9:13:5d:74:e5:46:ef:09:7c:9e:e1:99:31:0a:
+ 08:79:1b:8f:71:9f:17:66:c8:38:cf:ee:8c:97:b6:06:b9:73:
+ 46:e4:d3:94:c1:e5:60:b5:25:75:2d:d9:69:31:ec:cd:96:c3:
+ a3:76:fd:e8:74:44:ac:12:b9:4d:bf:51:e8:b9:d4:44:4e:27:
+ cb:ae:20:d1:7e:2a:7c:b6:63:47:9e:76:ba:97:d0:16:e7:0b:
+ 6c:6d:f7:43:6f:33:0b:29:30:77:fa:9d:f9:f5:4e:b8:76:b3:
+ cd:18:b4:f9:20:ef:3d:db:e6:ca:ad:9b:d0:4e:d2:87:a9:0d:
+ a6:44:73:50:dd:70:5b:ed:ad:7e:4a:bc:22:d5:a8:26:e4:c2:
+ 85:20:0d:d9
+-----BEGIN CERTIFICATE-----
+MIIF5DCCBMygAwIBAgIQW3dZxheE4V7HJ8AylSkoazANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMTYxMTA3MjM1OTU5WjCBujEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE0MDIGA1UEAxMrVmVy
+aVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBDQTCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJjboFXrnP0XeeOabhQdsVuYI4cWbod2
+nLU4O7WgerQHYwkZ5iqISKnnnbYwWgiXDOyq5BZpcmIjmvt6VCiYxQwtt9citsj5
+OBfH3doxRpqUFI6e7nigtyLUSVSXTeV0W5K87Gws3+fBthsaVWtmCAN/Ra+aM/EQ
+wGyZSpIkMQht3QI+YXZ4eLbtfjeubPOJ4bfh3BXMt1afgKCxBX9ONxX/ty8ejwY4
+P1C3aSijtWZfNhpSSENmUt+ikk/TGGC+4+peGXEFv54cbGhyJW+ze3PJbb0S/5tB
+Ml706H7FC6NMZNFOvCYIZfsZl1h44TO/7Wg+sSdFb8Di7Jdp91zT91ECAwEAAaOC
+AdIwggHOMB0GA1UdDgQWBBT8ilC6nrklWntVhU+VAGOP6VhrQzASBgNVHRMBAf8E
+CDAGAQH/AgEAMD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRw
+czovL3d3dy52ZXJpc2lnbi5jb20vY3BzMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6
+Ly9FVlNlY3VyZS1jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUuY3JsMA4GA1UdDwEB
+/wQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZ
+MFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7
+GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwKQYDVR0R
+BCIwIKQeMBwxGjAYBgNVBAMTEUNsYXNzM0NBMjA0OC0xLTQ3MD0GCCsGAQUFBwEB
+BDEwLzAtBggrBgEFBQcwAYYhaHR0cDovL0VWU2VjdXJlLW9jc3AudmVyaXNpZ24u
+Y29tMB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqGSIb3DQEB
+BQUAA4IBAQCWovp/5j3t1CvOtxU/wHIDX4u6FpAl98KD2Md1NGNoElMMU4l7yVYJ
+p8M2RE4O0GJis4b66KGbNGeNUyIXPv2s7mcuQ+JdfzOE8qJwwG6Cl8A0/SXGI3/t
+5rDFV0OEst4t8dD2SB8UcVeyrDHhlyQjyRNddOVG7wl8nuGZMQoIeRuPcZ8XZsg4
+z+6Ml7YGuXNG5NOUweVgtSV1LdlpMezNlsOjdv3odESsErlNv1HoudRETifLriDR
+fip8tmNHnna6l9AW5wtsbfdDbzMLKTB3+p359U64drPNGLT5IO892+bKrZvQTtKH
+qQ2mRHNQ3XBb7a1+Srwi1agm5MKFIA3Z
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert87[] = {
+ 0x30, 0x82, 0x05, 0xe4, 0x30, 0x82, 0x04, 0xcc, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x5b, 0x77, 0x59, 0xc6, 0x17, 0x84, 0xe1, 0x5e, 0xc7,
+ 0x27, 0xc0, 0x32, 0x95, 0x29, 0x28, 0x6b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xba, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, 0x31, 0x34,
+ 0x30, 0x32, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2b, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x98, 0xdb, 0xa0, 0x55, 0xeb, 0x9c, 0xfd, 0x17, 0x79, 0xe3, 0x9a,
+ 0x6e, 0x14, 0x1d, 0xb1, 0x5b, 0x98, 0x23, 0x87, 0x16, 0x6e, 0x87, 0x76,
+ 0x9c, 0xb5, 0x38, 0x3b, 0xb5, 0xa0, 0x7a, 0xb4, 0x07, 0x63, 0x09, 0x19,
+ 0xe6, 0x2a, 0x88, 0x48, 0xa9, 0xe7, 0x9d, 0xb6, 0x30, 0x5a, 0x08, 0x97,
+ 0x0c, 0xec, 0xaa, 0xe4, 0x16, 0x69, 0x72, 0x62, 0x23, 0x9a, 0xfb, 0x7a,
+ 0x54, 0x28, 0x98, 0xc5, 0x0c, 0x2d, 0xb7, 0xd7, 0x22, 0xb6, 0xc8, 0xf9,
+ 0x38, 0x17, 0xc7, 0xdd, 0xda, 0x31, 0x46, 0x9a, 0x94, 0x14, 0x8e, 0x9e,
+ 0xee, 0x78, 0xa0, 0xb7, 0x22, 0xd4, 0x49, 0x54, 0x97, 0x4d, 0xe5, 0x74,
+ 0x5b, 0x92, 0xbc, 0xec, 0x6c, 0x2c, 0xdf, 0xe7, 0xc1, 0xb6, 0x1b, 0x1a,
+ 0x55, 0x6b, 0x66, 0x08, 0x03, 0x7f, 0x45, 0xaf, 0x9a, 0x33, 0xf1, 0x10,
+ 0xc0, 0x6c, 0x99, 0x4a, 0x92, 0x24, 0x31, 0x08, 0x6d, 0xdd, 0x02, 0x3e,
+ 0x61, 0x76, 0x78, 0x78, 0xb6, 0xed, 0x7e, 0x37, 0xae, 0x6c, 0xf3, 0x89,
+ 0xe1, 0xb7, 0xe1, 0xdc, 0x15, 0xcc, 0xb7, 0x56, 0x9f, 0x80, 0xa0, 0xb1,
+ 0x05, 0x7f, 0x4e, 0x37, 0x15, 0xff, 0xb7, 0x2f, 0x1e, 0x8f, 0x06, 0x38,
+ 0x3f, 0x50, 0xb7, 0x69, 0x28, 0xa3, 0xb5, 0x66, 0x5f, 0x36, 0x1a, 0x52,
+ 0x48, 0x43, 0x66, 0x52, 0xdf, 0xa2, 0x92, 0x4f, 0xd3, 0x18, 0x60, 0xbe,
+ 0xe3, 0xea, 0x5e, 0x19, 0x71, 0x05, 0xbf, 0x9e, 0x1c, 0x6c, 0x68, 0x72,
+ 0x25, 0x6f, 0xb3, 0x7b, 0x73, 0xc9, 0x6d, 0xbd, 0x12, 0xff, 0x9b, 0x41,
+ 0x32, 0x5e, 0xf4, 0xe8, 0x7e, 0xc5, 0x0b, 0xa3, 0x4c, 0x64, 0xd1, 0x4e,
+ 0xbc, 0x26, 0x08, 0x65, 0xfb, 0x19, 0x97, 0x58, 0x78, 0xe1, 0x33, 0xbf,
+ 0xed, 0x68, 0x3e, 0xb1, 0x27, 0x45, 0x6f, 0xc0, 0xe2, 0xec, 0x97, 0x69,
+ 0xf7, 0x5c, 0xd3, 0xf7, 0x51, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0xd2, 0x30, 0x82, 0x01, 0xce, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0xfc, 0x8a, 0x50, 0xba, 0x9e, 0xb9, 0x25,
+ 0x5a, 0x7b, 0x55, 0x85, 0x4f, 0x95, 0x00, 0x63, 0x8f, 0xe9, 0x58, 0x6b,
+ 0x43, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3d, 0x06,
+ 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34, 0x30, 0x32, 0x06, 0x04,
+ 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73,
+ 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x36, 0x30, 0x34, 0x30,
+ 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x63,
+ 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x11, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59,
+ 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
+ 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac,
+ 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b,
+ 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11,
+ 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x33, 0x43, 0x41, 0x32, 0x30, 0x34, 0x38, 0x2d, 0x31, 0x2d, 0x34, 0x37,
+ 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb,
+ 0xf0, 0x30, 0x09, 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x96, 0xa2, 0xfa, 0x7f,
+ 0xe6, 0x3d, 0xed, 0xd4, 0x2b, 0xce, 0xb7, 0x15, 0x3f, 0xc0, 0x72, 0x03,
+ 0x5f, 0x8b, 0xba, 0x16, 0x90, 0x25, 0xf7, 0xc2, 0x83, 0xd8, 0xc7, 0x75,
+ 0x34, 0x63, 0x68, 0x12, 0x53, 0x0c, 0x53, 0x89, 0x7b, 0xc9, 0x56, 0x09,
+ 0xa7, 0xc3, 0x36, 0x44, 0x4e, 0x0e, 0xd0, 0x62, 0x62, 0xb3, 0x86, 0xfa,
+ 0xe8, 0xa1, 0x9b, 0x34, 0x67, 0x8d, 0x53, 0x22, 0x17, 0x3e, 0xfd, 0xac,
+ 0xee, 0x67, 0x2e, 0x43, 0xe2, 0x5d, 0x7f, 0x33, 0x84, 0xf2, 0xa2, 0x70,
+ 0xc0, 0x6e, 0x82, 0x97, 0xc0, 0x34, 0xfd, 0x25, 0xc6, 0x23, 0x7f, 0xed,
+ 0xe6, 0xb0, 0xc5, 0x57, 0x43, 0x84, 0xb2, 0xde, 0x2d, 0xf1, 0xd0, 0xf6,
+ 0x48, 0x1f, 0x14, 0x71, 0x57, 0xb2, 0xac, 0x31, 0xe1, 0x97, 0x24, 0x23,
+ 0xc9, 0x13, 0x5d, 0x74, 0xe5, 0x46, 0xef, 0x09, 0x7c, 0x9e, 0xe1, 0x99,
+ 0x31, 0x0a, 0x08, 0x79, 0x1b, 0x8f, 0x71, 0x9f, 0x17, 0x66, 0xc8, 0x38,
+ 0xcf, 0xee, 0x8c, 0x97, 0xb6, 0x06, 0xb9, 0x73, 0x46, 0xe4, 0xd3, 0x94,
+ 0xc1, 0xe5, 0x60, 0xb5, 0x25, 0x75, 0x2d, 0xd9, 0x69, 0x31, 0xec, 0xcd,
+ 0x96, 0xc3, 0xa3, 0x76, 0xfd, 0xe8, 0x74, 0x44, 0xac, 0x12, 0xb9, 0x4d,
+ 0xbf, 0x51, 0xe8, 0xb9, 0xd4, 0x44, 0x4e, 0x27, 0xcb, 0xae, 0x20, 0xd1,
+ 0x7e, 0x2a, 0x7c, 0xb6, 0x63, 0x47, 0x9e, 0x76, 0xba, 0x97, 0xd0, 0x16,
+ 0xe7, 0x0b, 0x6c, 0x6d, 0xf7, 0x43, 0x6f, 0x33, 0x0b, 0x29, 0x30, 0x77,
+ 0xfa, 0x9d, 0xf9, 0xf5, 0x4e, 0xb8, 0x76, 0xb3, 0xcd, 0x18, 0xb4, 0xf9,
+ 0x20, 0xef, 0x3d, 0xdb, 0xe6, 0xca, 0xad, 0x9b, 0xd0, 0x4e, 0xd2, 0x87,
+ 0xa9, 0x0d, 0xa6, 0x44, 0x73, 0x50, 0xdd, 0x70, 0x5b, 0xed, 0xad, 0x7e,
+ 0x4a, 0xbc, 0x22, 0xd5, 0xa8, 0x26, 0xe4, 0xc2, 0x85, 0x20, 0x0d, 0xd9,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6e:cc:7a:a5:a7:03:20:09:b8:ce:bc:f4:e9:52:d4:91
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)10, CN=VeriSign Class 3 Secure Server CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b1:87:84:1f:c2:0c:45:f5:bc:ab:25:97:a7:ad:
+ a2:3e:9c:ba:f6:c1:39:b8:8b:ca:c2:ac:56:c6:e5:
+ bb:65:8e:44:4f:4d:ce:6f:ed:09:4a:d4:af:4e:10:
+ 9c:68:8b:2e:95:7b:89:9b:13:ca:e2:34:34:c1:f3:
+ 5b:f3:49:7b:62:83:48:81:74:d1:88:78:6c:02:53:
+ f9:bc:7f:43:26:57:58:33:83:3b:33:0a:17:b0:d0:
+ 4e:91:24:ad:86:7d:64:12:dc:74:4a:34:a1:1d:0a:
+ ea:96:1d:0b:15:fc:a3:4b:3b:ce:63:88:d0:f8:2d:
+ 0c:94:86:10:ca:b6:9a:3d:ca:eb:37:9c:00:48:35:
+ 86:29:50:78:e8:45:63:cd:19:41:4f:f5:95:ec:7b:
+ 98:d4:c4:71:b3:50:be:28:b3:8f:a0:b9:53:9c:f5:
+ ca:2c:23:a9:fd:14:06:e8:18:b4:9a:e8:3c:6e:81:
+ fd:e4:cd:35:36:b3:51:d3:69:ec:12:ba:56:6e:6f:
+ 9b:57:c5:8b:14:e7:0e:c7:9c:ed:4a:54:6a:c9:4d:
+ c5:bf:11:b1:ae:1c:67:81:cb:44:55:33:99:7f:24:
+ 9b:3f:53:45:7f:86:1a:f3:3c:fa:6d:7f:81:f5:b8:
+ 4a:d3:f5:85:37:1c:b5:a6:d0:09:e4:18:7b:38:4e:
+ fa:0f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.23.3
+ CPS: https://www.verisign.com/cps
+ User Notice:
+ Explicit Text: https://www.verisign.com/rpa
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-6
+ X509v3 Subject Key Identifier:
+ 0D:44:5C:16:53:44:C1:82:7E:1D:20:AB:25:F4:01:63:D8:BE:79:A5
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 0c:83:24:ef:dd:c3:0c:d9:58:9c:fe:36:b6:eb:8a:80:4b:d1:
+ a3:f7:9d:f3:cc:53:ef:82:9e:a3:a1:e6:97:c1:58:9d:75:6c:
+ e0:1d:1b:4c:fa:d1:c1:2d:05:c0:ea:6e:b2:22:70:55:d9:20:
+ 33:40:33:07:c2:65:83:fa:8f:43:37:9b:ea:0e:9a:6c:70:ee:
+ f6:9c:80:3b:d9:37:f4:7a:6d:ec:d0:18:7d:49:4a:ca:99:c7:
+ 19:28:a2:be:d8:77:24:f7:85:26:86:6d:87:05:40:41:67:d1:
+ 27:3a:ed:dc:48:1d:22:cd:0b:0b:8b:bc:f4:b1:7b:fd:b4:99:
+ a8:e9:76:2a:e1:1a:2d:87:6e:74:d3:88:dd:1e:22:c6:df:16:
+ b6:2b:82:14:0a:94:5c:f2:50:ec:af:ce:ff:62:37:0d:ad:65:
+ d3:06:41:53:ed:02:14:c8:b5:58:28:a1:ac:e0:5b:ec:b3:7f:
+ 95:4a:fb:03:c8:ad:26:db:e6:66:78:12:4a:d9:9f:42:fb:e1:
+ 98:e6:42:83:9b:8f:8f:67:24:e8:61:19:b5:dd:cd:b5:0b:26:
+ 05:8e:c3:6e:c4:c8:75:b8:46:cf:e2:18:06:5e:a9:ae:a8:81:
+ 9a:47:16:de:0c:28:6c:25:27:b9:de:b7:84:58:c6:1f:38:1e:
+ a4:c4:cb:66
+-----BEGIN CERTIFICATE-----
+MIIF7DCCBNSgAwIBAgIQbsx6pacDIAm4zrz06VLUkTANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
+aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
+5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
+f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
+tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
+GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
+M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggHfMIIB
+2zA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlz
+aWduLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4
+RQEHFwMwVjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nw
+czAqBggrBgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQG
+A1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUu
+Y3JsMA4GA1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglp
+bWFnZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNo
+dHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0w
+GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+
+HSCrJfQBY9i+eaUwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJ
+KoZIhvcNAQEFBQADggEBAAyDJO/dwwzZWJz+NrbrioBL0aP3nfPMU++CnqOh5pfB
+WJ11bOAdG0z60cEtBcDqbrIicFXZIDNAMwfCZYP6j0M3m+oOmmxw7vacgDvZN/R6
+bezQGH1JSsqZxxkoor7YdyT3hSaGbYcFQEFn0Sc67dxIHSLNCwuLvPSxe/20majp
+dirhGi2HbnTTiN0eIsbfFrYrghQKlFzyUOyvzv9iNw2tZdMGQVPtAhTItVgooazg
+W+yzf5VK+wPIrSbb5mZ4EkrZn0L74ZjmQoObj49nJOhhGbXdzbULJgWOw27EyHW4
+Rs/iGAZeqa6ogZpHFt4MKGwlJ7net4RYxh84HqTEy2Y=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert88[] = {
+ 0x30, 0x82, 0x05, 0xec, 0x30, 0x82, 0x04, 0xd4, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6e, 0xcc, 0x7a, 0xa5, 0xa7, 0x03, 0x20, 0x09, 0xb8,
+ 0xce, 0xbc, 0xf4, 0xe9, 0x52, 0xd4, 0x91, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x32, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xb5, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x31, 0x30, 0x31, 0x2f,
+ 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72,
+ 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb1, 0x87, 0x84, 0x1f,
+ 0xc2, 0x0c, 0x45, 0xf5, 0xbc, 0xab, 0x25, 0x97, 0xa7, 0xad, 0xa2, 0x3e,
+ 0x9c, 0xba, 0xf6, 0xc1, 0x39, 0xb8, 0x8b, 0xca, 0xc2, 0xac, 0x56, 0xc6,
+ 0xe5, 0xbb, 0x65, 0x8e, 0x44, 0x4f, 0x4d, 0xce, 0x6f, 0xed, 0x09, 0x4a,
+ 0xd4, 0xaf, 0x4e, 0x10, 0x9c, 0x68, 0x8b, 0x2e, 0x95, 0x7b, 0x89, 0x9b,
+ 0x13, 0xca, 0xe2, 0x34, 0x34, 0xc1, 0xf3, 0x5b, 0xf3, 0x49, 0x7b, 0x62,
+ 0x83, 0x48, 0x81, 0x74, 0xd1, 0x88, 0x78, 0x6c, 0x02, 0x53, 0xf9, 0xbc,
+ 0x7f, 0x43, 0x26, 0x57, 0x58, 0x33, 0x83, 0x3b, 0x33, 0x0a, 0x17, 0xb0,
+ 0xd0, 0x4e, 0x91, 0x24, 0xad, 0x86, 0x7d, 0x64, 0x12, 0xdc, 0x74, 0x4a,
+ 0x34, 0xa1, 0x1d, 0x0a, 0xea, 0x96, 0x1d, 0x0b, 0x15, 0xfc, 0xa3, 0x4b,
+ 0x3b, 0xce, 0x63, 0x88, 0xd0, 0xf8, 0x2d, 0x0c, 0x94, 0x86, 0x10, 0xca,
+ 0xb6, 0x9a, 0x3d, 0xca, 0xeb, 0x37, 0x9c, 0x00, 0x48, 0x35, 0x86, 0x29,
+ 0x50, 0x78, 0xe8, 0x45, 0x63, 0xcd, 0x19, 0x41, 0x4f, 0xf5, 0x95, 0xec,
+ 0x7b, 0x98, 0xd4, 0xc4, 0x71, 0xb3, 0x50, 0xbe, 0x28, 0xb3, 0x8f, 0xa0,
+ 0xb9, 0x53, 0x9c, 0xf5, 0xca, 0x2c, 0x23, 0xa9, 0xfd, 0x14, 0x06, 0xe8,
+ 0x18, 0xb4, 0x9a, 0xe8, 0x3c, 0x6e, 0x81, 0xfd, 0xe4, 0xcd, 0x35, 0x36,
+ 0xb3, 0x51, 0xd3, 0x69, 0xec, 0x12, 0xba, 0x56, 0x6e, 0x6f, 0x9b, 0x57,
+ 0xc5, 0x8b, 0x14, 0xe7, 0x0e, 0xc7, 0x9c, 0xed, 0x4a, 0x54, 0x6a, 0xc9,
+ 0x4d, 0xc5, 0xbf, 0x11, 0xb1, 0xae, 0x1c, 0x67, 0x81, 0xcb, 0x44, 0x55,
+ 0x33, 0x99, 0x7f, 0x24, 0x9b, 0x3f, 0x53, 0x45, 0x7f, 0x86, 0x1a, 0xf3,
+ 0x3c, 0xfa, 0x6d, 0x7f, 0x81, 0xf5, 0xb8, 0x4a, 0xd3, 0xf5, 0x85, 0x37,
+ 0x1c, 0xb5, 0xa6, 0xd0, 0x09, 0xe4, 0x18, 0x7b, 0x38, 0x4e, 0xfa, 0x0f,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xdf, 0x30, 0x82, 0x01,
+ 0xdb, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff,
+ 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x69,
+ 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8,
+ 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74,
+ 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72,
+ 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70,
+ 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02,
+ 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f,
+ 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x34, 0x06,
+ 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27,
+ 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e,
+ 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01,
+ 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1,
+ 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69,
+ 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f,
+ 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f,
+ 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a,
+ 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x28,
+ 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d, 0x30,
+ 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49,
+ 0x2d, 0x32, 0x2d, 0x36, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
+ 0x16, 0x04, 0x14, 0x0d, 0x44, 0x5c, 0x16, 0x53, 0x44, 0xc1, 0x82, 0x7e,
+ 0x1d, 0x20, 0xab, 0x25, 0xf4, 0x01, 0x63, 0xd8, 0xbe, 0x79, 0xa5, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14,
+ 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3,
+ 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x0c, 0x83, 0x24, 0xef, 0xdd, 0xc3, 0x0c, 0xd9,
+ 0x58, 0x9c, 0xfe, 0x36, 0xb6, 0xeb, 0x8a, 0x80, 0x4b, 0xd1, 0xa3, 0xf7,
+ 0x9d, 0xf3, 0xcc, 0x53, 0xef, 0x82, 0x9e, 0xa3, 0xa1, 0xe6, 0x97, 0xc1,
+ 0x58, 0x9d, 0x75, 0x6c, 0xe0, 0x1d, 0x1b, 0x4c, 0xfa, 0xd1, 0xc1, 0x2d,
+ 0x05, 0xc0, 0xea, 0x6e, 0xb2, 0x22, 0x70, 0x55, 0xd9, 0x20, 0x33, 0x40,
+ 0x33, 0x07, 0xc2, 0x65, 0x83, 0xfa, 0x8f, 0x43, 0x37, 0x9b, 0xea, 0x0e,
+ 0x9a, 0x6c, 0x70, 0xee, 0xf6, 0x9c, 0x80, 0x3b, 0xd9, 0x37, 0xf4, 0x7a,
+ 0x6d, 0xec, 0xd0, 0x18, 0x7d, 0x49, 0x4a, 0xca, 0x99, 0xc7, 0x19, 0x28,
+ 0xa2, 0xbe, 0xd8, 0x77, 0x24, 0xf7, 0x85, 0x26, 0x86, 0x6d, 0x87, 0x05,
+ 0x40, 0x41, 0x67, 0xd1, 0x27, 0x3a, 0xed, 0xdc, 0x48, 0x1d, 0x22, 0xcd,
+ 0x0b, 0x0b, 0x8b, 0xbc, 0xf4, 0xb1, 0x7b, 0xfd, 0xb4, 0x99, 0xa8, 0xe9,
+ 0x76, 0x2a, 0xe1, 0x1a, 0x2d, 0x87, 0x6e, 0x74, 0xd3, 0x88, 0xdd, 0x1e,
+ 0x22, 0xc6, 0xdf, 0x16, 0xb6, 0x2b, 0x82, 0x14, 0x0a, 0x94, 0x5c, 0xf2,
+ 0x50, 0xec, 0xaf, 0xce, 0xff, 0x62, 0x37, 0x0d, 0xad, 0x65, 0xd3, 0x06,
+ 0x41, 0x53, 0xed, 0x02, 0x14, 0xc8, 0xb5, 0x58, 0x28, 0xa1, 0xac, 0xe0,
+ 0x5b, 0xec, 0xb3, 0x7f, 0x95, 0x4a, 0xfb, 0x03, 0xc8, 0xad, 0x26, 0xdb,
+ 0xe6, 0x66, 0x78, 0x12, 0x4a, 0xd9, 0x9f, 0x42, 0xfb, 0xe1, 0x98, 0xe6,
+ 0x42, 0x83, 0x9b, 0x8f, 0x8f, 0x67, 0x24, 0xe8, 0x61, 0x19, 0xb5, 0xdd,
+ 0xcd, 0xb5, 0x0b, 0x26, 0x05, 0x8e, 0xc3, 0x6e, 0xc4, 0xc8, 0x75, 0xb8,
+ 0x46, 0xcf, 0xe2, 0x18, 0x06, 0x5e, 0xa9, 0xae, 0xa8, 0x81, 0x9a, 0x47,
+ 0x16, 0xde, 0x0c, 0x28, 0x6c, 0x25, 0x27, 0xb9, 0xde, 0xb7, 0x84, 0x58,
+ 0xc6, 0x1f, 0x38, 0x1e, 0xa4, 0xc4, 0xcb, 0x66,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 71:63:66:35:eb:f3:82:3d:7e:13:09:59:a2:d8:e5:de
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 1999 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G3
+ Validity
+ Not Before: Oct 12 00:00:00 2010 GMT
+ Not After : Oct 11 23:59:59 2020 GMT
+ Subject: C=US, O=Oracle Corporation, OU=VeriSign Trust Network, OU=Class 3 MPKI Secure Server CA, CN=Oracle SSL CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a4:3e:5a:70:29:b8:f1:9f:22:b9:21:8e:c5:52:
+ ac:32:aa:bf:1a:1b:29:f3:3d:79:ea:6a:1c:29:48:
+ f4:6f:fe:86:31:23:3d:07:23:17:05:3b:5e:04:fd:
+ 3e:5c:1a:6d:63:61:64:90:49:67:de:69:8f:f4:72:
+ a7:23:87:1a:66:98:da:07:3c:21:2c:ac:86:8d:11:
+ 7d:40:98:40:26:59:a9:c5:af:aa:f2:e9:d7:11:91:
+ 9b:f1:d6:6f:cd:65:63:3d:8f:76:a1:99:2f:c6:3f:
+ 9d:fa:57:82:b1:ff:11:0b:c4:ec:84:d2:d4:47:ef:
+ 2c:bf:90:eb:61:95:ee:eb:17:c0:43:d6:83:67:7b:
+ 54:80:f4:0d:06:9f:0a:ed:d9:de:5c:66:fd:49:a6:
+ e8:3f:96:3c:fa:c9:46:96:65:af:82:73:26:e0:94:
+ 0b:bd:99:c0:b5:61:a6:ec:dc:be:57:d4:57:91:ca:
+ 18:0e:2c:cc:0c:8a:e0:a4:7c:a3:e5:7c:0c:3e:97:
+ df:62:b9:80:a5:11:35:db:6b:fb:91:45:3c:2f:48:
+ e9:58:05:6d:8e:cd:04:72:2e:04:a2:ae:18:66:79:
+ e9:38:f7:78:ec:62:af:eb:a6:f8:c5:4a:7c:58:85:
+ 60:7d:20:6a:7d:84:c6:32:3a:66:ea:33:ec:e8:4d:
+ 93:f1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:1
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.23.3
+ CPS: https://www.verisign.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g3.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-20
+ X509v3 Subject Key Identifier:
+ CC:F8:BB:65:47:6A:52:16:C4:EC:7E:9B:27:9C:FC:2E:A9:C2:F0:0F
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 1999 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G3
+ serial:9B:7E:06:49:A3:3E:62:B9:D5:EE:90:48:71:29:EF:57
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 47:8b:0e:12:54:a1:ce:ca:59:ad:c1:81:65:66:2f:8f:5b:71:
+ c5:86:a3:90:a3:7b:e0:7c:f1:60:1a:81:87:7f:df:c1:7e:9c:
+ a0:5b:d4:db:c0:bc:ac:78:4d:51:59:9d:28:24:db:46:a1:74:
+ e2:d7:2e:2b:7f:74:09:03:d3:aa:31:f1:47:fb:b5:a3:e5:5e:
+ 40:d1:b6:a3:c5:e5:cf:5f:26:7b:ab:17:ab:91:8a:2f:f9:d8:
+ 0a:34:54:f6:6a:63:52:9b:d7:70:8b:34:46:14:2a:7b:09:40:
+ 04:a0:1a:6f:d3:4b:2a:f5:12:9c:22:db:3b:ce:b2:7e:15:f0:
+ f3:4e:3e:e4:7f:b6:8a:bf:68:04:b5:9c:5d:bb:8f:a5:c8:29:
+ 95:5f:5b:c6:e4:df:84:9c:80:74:1a:35:1d:fd:94:ac:86:85:
+ 89:2e:90:7f:59:b6:9c:06:e5:35:ff:ff:ff:b5:53:d9:3e:b5:
+ dd:ae:fe:06:4f:66:71:e0:4f:f7:fc:c1:85:b5:7b:85:43:22:
+ cf:5b:f6:94:85:a6:59:b2:5d:fe:29:4f:8c:9c:1e:92:ce:0f:
+ 33:20:19:49:59:54:36:6c:c4:e9:f9:66:1b:20:6c:b2:6f:3e:
+ 24:39:6f:91:fb:b4:d8:93:50:c0:c2:97:de:e9:93:5e:97:20:
+ 05:4a:09:13
+-----BEGIN CERTIFICATE-----
+MIIF+zCCBOOgAwIBAgIQcWNmNevzgj1+EwlZotjl3jANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMTk5OSBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzMwHhcNMTAxMDEyMDAwMDAwWhcNMjAxMDExMjM1OTU5WjCBizEL
+MAkGA1UEBhMCVVMxGzAZBgNVBAoTEk9yYWNsZSBDb3Jwb3JhdGlvbjEfMB0GA1UE
+CxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazEmMCQGA1UECxMdQ2xhc3MgMyBNUEtJ
+IFNlY3VyZSBTZXJ2ZXIgQ0ExFjAUBgNVBAMTDU9yYWNsZSBTU0wgQ0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkPlpwKbjxnyK5IY7FUqwyqr8aGynz
+PXnqahwpSPRv/oYxIz0HIxcFO14E/T5cGm1jYWSQSWfeaY/0cqcjhxpmmNoHPCEs
+rIaNEX1AmEAmWanFr6ry6dcRkZvx1m/NZWM9j3ahmS/GP536V4Kx/xELxOyE0tRH
+7yy/kOthle7rF8BD1oNne1SA9A0Gnwrt2d5cZv1Jpug/ljz6yUaWZa+CcybglAu9
+mcC1Yabs3L5X1FeRyhgOLMwMiuCkfKPlfAw+l99iuYClETXba/uRRTwvSOlYBW2O
+zQRyLgSirhhmeek493jsYq/rpvjFSnxYhWB9IGp9hMYyOmbqM+zoTZPxAgMBAAGj
+ggIYMIICFDAOBgNVHQ8BAf8EBAMCAQYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUF
+BzABhhhodHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wEgYDVR0TAQH/BAgwBgEB/wIB
+ATBEBgNVHSAEPTA7MDkGC2CGSAGG+EUBBxcDMCowKAYIKwYBBQUHAgEWHGh0dHBz
+Oi8vd3d3LnZlcmlzaWduLmNvbS9jcHMwNAYDVR0fBC0wKzApoCegJYYjaHR0cDov
+L2NybC52ZXJpc2lnbi5jb20vcGNhMy1nMy5jcmwwKQYDVR0RBCIwIKQeMBwxGjAY
+BgNVBAMTEVZlcmlTaWduTVBLSS0yLTIwMB0GA1UdDgQWBBTM+LtlR2pSFsTsfpsn
+nPwuqcLwDzCB8QYDVR0jBIHpMIHmoYHQpIHNMIHKMQswCQYDVQQGEwJVUzEXMBUG
+A1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5l
+dHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1
+dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVi
+bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHM4IRAJt+Bkmj
+PmK51e6QSHEp71cwDQYJKoZIhvcNAQEFBQADggEBAEeLDhJUoc7KWa3BgWVmL49b
+ccWGo5Cje+B88WAagYd/38F+nKBb1NvAvKx4TVFZnSgk20ahdOLXLit/dAkD06ox
+8Uf7taPlXkDRtqPF5c9fJnurF6uRii/52Ao0VPZqY1Kb13CLNEYUKnsJQASgGm/T
+Syr1Epwi2zvOsn4V8PNOPuR/toq/aAS1nF27j6XIKZVfW8bk34ScgHQaNR39lKyG
+hYkukH9ZtpwG5TX///+1U9k+td2u/gZPZnHgT/f8wYW1e4VDIs9b9pSFplmyXf4p
+T4ycHpLODzMgGUlZVDZsxOn5ZhsgbLJvPiQ5b5H7tNiTUMDCl97pk16XIAVKCRM=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert89[] = {
+ 0x30, 0x82, 0x05, 0xfb, 0x30, 0x82, 0x04, 0xe3, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x71, 0x63, 0x66, 0x35, 0xeb, 0xf3, 0x82, 0x3d, 0x7e,
+ 0x13, 0x09, 0x59, 0xa2, 0xd8, 0xe5, 0xde, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x31, 0x30, 0x31, 0x32, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x31,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x12, 0x4f, 0x72,
+ 0x61, 0x63, 0x6c, 0x65, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20,
+ 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
+ 0x6b, 0x31, 0x26, 0x30, 0x24, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1d,
+ 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x4d, 0x50, 0x4b, 0x49,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x43, 0x41, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x0d, 0x4f, 0x72, 0x61, 0x63, 0x6c, 0x65, 0x20, 0x53,
+ 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xa4, 0x3e, 0x5a, 0x70, 0x29, 0xb8, 0xf1, 0x9f, 0x22, 0xb9,
+ 0x21, 0x8e, 0xc5, 0x52, 0xac, 0x32, 0xaa, 0xbf, 0x1a, 0x1b, 0x29, 0xf3,
+ 0x3d, 0x79, 0xea, 0x6a, 0x1c, 0x29, 0x48, 0xf4, 0x6f, 0xfe, 0x86, 0x31,
+ 0x23, 0x3d, 0x07, 0x23, 0x17, 0x05, 0x3b, 0x5e, 0x04, 0xfd, 0x3e, 0x5c,
+ 0x1a, 0x6d, 0x63, 0x61, 0x64, 0x90, 0x49, 0x67, 0xde, 0x69, 0x8f, 0xf4,
+ 0x72, 0xa7, 0x23, 0x87, 0x1a, 0x66, 0x98, 0xda, 0x07, 0x3c, 0x21, 0x2c,
+ 0xac, 0x86, 0x8d, 0x11, 0x7d, 0x40, 0x98, 0x40, 0x26, 0x59, 0xa9, 0xc5,
+ 0xaf, 0xaa, 0xf2, 0xe9, 0xd7, 0x11, 0x91, 0x9b, 0xf1, 0xd6, 0x6f, 0xcd,
+ 0x65, 0x63, 0x3d, 0x8f, 0x76, 0xa1, 0x99, 0x2f, 0xc6, 0x3f, 0x9d, 0xfa,
+ 0x57, 0x82, 0xb1, 0xff, 0x11, 0x0b, 0xc4, 0xec, 0x84, 0xd2, 0xd4, 0x47,
+ 0xef, 0x2c, 0xbf, 0x90, 0xeb, 0x61, 0x95, 0xee, 0xeb, 0x17, 0xc0, 0x43,
+ 0xd6, 0x83, 0x67, 0x7b, 0x54, 0x80, 0xf4, 0x0d, 0x06, 0x9f, 0x0a, 0xed,
+ 0xd9, 0xde, 0x5c, 0x66, 0xfd, 0x49, 0xa6, 0xe8, 0x3f, 0x96, 0x3c, 0xfa,
+ 0xc9, 0x46, 0x96, 0x65, 0xaf, 0x82, 0x73, 0x26, 0xe0, 0x94, 0x0b, 0xbd,
+ 0x99, 0xc0, 0xb5, 0x61, 0xa6, 0xec, 0xdc, 0xbe, 0x57, 0xd4, 0x57, 0x91,
+ 0xca, 0x18, 0x0e, 0x2c, 0xcc, 0x0c, 0x8a, 0xe0, 0xa4, 0x7c, 0xa3, 0xe5,
+ 0x7c, 0x0c, 0x3e, 0x97, 0xdf, 0x62, 0xb9, 0x80, 0xa5, 0x11, 0x35, 0xdb,
+ 0x6b, 0xfb, 0x91, 0x45, 0x3c, 0x2f, 0x48, 0xe9, 0x58, 0x05, 0x6d, 0x8e,
+ 0xcd, 0x04, 0x72, 0x2e, 0x04, 0xa2, 0xae, 0x18, 0x66, 0x79, 0xe9, 0x38,
+ 0xf7, 0x78, 0xec, 0x62, 0xaf, 0xeb, 0xa6, 0xf8, 0xc5, 0x4a, 0x7c, 0x58,
+ 0x85, 0x60, 0x7d, 0x20, 0x6a, 0x7d, 0x84, 0xc6, 0x32, 0x3a, 0x66, 0xea,
+ 0x33, 0xec, 0xe8, 0x4d, 0x93, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x02, 0x18, 0x30, 0x82, 0x02, 0x14, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x01, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x3d, 0x30, 0x3b,
+ 0x30, 0x39, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01,
+ 0x07, 0x17, 0x03, 0x30, 0x2a, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x70, 0x73, 0x30,
+ 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29,
+ 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67,
+ 0x33, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11,
+ 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b, 0x49, 0x2d, 0x32, 0x2d, 0x32, 0x30,
+ 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xcc,
+ 0xf8, 0xbb, 0x65, 0x47, 0x6a, 0x52, 0x16, 0xc4, 0xec, 0x7e, 0x9b, 0x27,
+ 0x9c, 0xfc, 0x2e, 0xa9, 0xc2, 0xf0, 0x0f, 0x30, 0x81, 0xf1, 0x06, 0x03,
+ 0x55, 0x1d, 0x23, 0x04, 0x81, 0xe9, 0x30, 0x81, 0xe6, 0xa1, 0x81, 0xd0,
+ 0xa4, 0x81, 0xcd, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06,
+ 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69,
+ 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53,
+ 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65,
+ 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x39,
+ 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x13, 0x3c, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20,
+ 0x2d, 0x20, 0x47, 0x33, 0x82, 0x11, 0x00, 0x9b, 0x7e, 0x06, 0x49, 0xa3,
+ 0x3e, 0x62, 0xb9, 0xd5, 0xee, 0x90, 0x48, 0x71, 0x29, 0xef, 0x57, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x8b, 0x0e, 0x12, 0x54,
+ 0xa1, 0xce, 0xca, 0x59, 0xad, 0xc1, 0x81, 0x65, 0x66, 0x2f, 0x8f, 0x5b,
+ 0x71, 0xc5, 0x86, 0xa3, 0x90, 0xa3, 0x7b, 0xe0, 0x7c, 0xf1, 0x60, 0x1a,
+ 0x81, 0x87, 0x7f, 0xdf, 0xc1, 0x7e, 0x9c, 0xa0, 0x5b, 0xd4, 0xdb, 0xc0,
+ 0xbc, 0xac, 0x78, 0x4d, 0x51, 0x59, 0x9d, 0x28, 0x24, 0xdb, 0x46, 0xa1,
+ 0x74, 0xe2, 0xd7, 0x2e, 0x2b, 0x7f, 0x74, 0x09, 0x03, 0xd3, 0xaa, 0x31,
+ 0xf1, 0x47, 0xfb, 0xb5, 0xa3, 0xe5, 0x5e, 0x40, 0xd1, 0xb6, 0xa3, 0xc5,
+ 0xe5, 0xcf, 0x5f, 0x26, 0x7b, 0xab, 0x17, 0xab, 0x91, 0x8a, 0x2f, 0xf9,
+ 0xd8, 0x0a, 0x34, 0x54, 0xf6, 0x6a, 0x63, 0x52, 0x9b, 0xd7, 0x70, 0x8b,
+ 0x34, 0x46, 0x14, 0x2a, 0x7b, 0x09, 0x40, 0x04, 0xa0, 0x1a, 0x6f, 0xd3,
+ 0x4b, 0x2a, 0xf5, 0x12, 0x9c, 0x22, 0xdb, 0x3b, 0xce, 0xb2, 0x7e, 0x15,
+ 0xf0, 0xf3, 0x4e, 0x3e, 0xe4, 0x7f, 0xb6, 0x8a, 0xbf, 0x68, 0x04, 0xb5,
+ 0x9c, 0x5d, 0xbb, 0x8f, 0xa5, 0xc8, 0x29, 0x95, 0x5f, 0x5b, 0xc6, 0xe4,
+ 0xdf, 0x84, 0x9c, 0x80, 0x74, 0x1a, 0x35, 0x1d, 0xfd, 0x94, 0xac, 0x86,
+ 0x85, 0x89, 0x2e, 0x90, 0x7f, 0x59, 0xb6, 0x9c, 0x06, 0xe5, 0x35, 0xff,
+ 0xff, 0xff, 0xb5, 0x53, 0xd9, 0x3e, 0xb5, 0xdd, 0xae, 0xfe, 0x06, 0x4f,
+ 0x66, 0x71, 0xe0, 0x4f, 0xf7, 0xfc, 0xc1, 0x85, 0xb5, 0x7b, 0x85, 0x43,
+ 0x22, 0xcf, 0x5b, 0xf6, 0x94, 0x85, 0xa6, 0x59, 0xb2, 0x5d, 0xfe, 0x29,
+ 0x4f, 0x8c, 0x9c, 0x1e, 0x92, 0xce, 0x0f, 0x33, 0x20, 0x19, 0x49, 0x59,
+ 0x54, 0x36, 0x6c, 0xc4, 0xe9, 0xf9, 0x66, 0x1b, 0x20, 0x6c, 0xb2, 0x6f,
+ 0x3e, 0x24, 0x39, 0x6f, 0x91, 0xfb, 0xb4, 0xd8, 0x93, 0x50, 0xc0, 0xc2,
+ 0x97, 0xde, 0xe9, 0x93, 0x5e, 0x97, 0x20, 0x05, 0x4a, 0x09, 0x13,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 61:5d:aa:d2:00:06:00:00:00:40
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=Microsoft Internet Authority
+ Validity
+ Not Before: May 15 20:40:55 2012 GMT
+ Not After : May 15 20:50:55 2016 GMT
+ Subject: DC=com, DC=microsoft, DC=corp, DC=redmond, CN=MSIT Machine Auth CA 2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:c8:e8:00:eb:58:69:29:11:84:87:1c:9f:87:
+ 4d:44:3d:38:1b:c7:13:93:e7:14:4c:17:2b:db:75:
+ 08:c0:c9:21:ca:ae:e0:1f:e8:8c:fe:a1:df:24:8c:
+ bf:02:9c:ed:99:be:3a:53:2a:45:4e:b0:48:78:37:
+ dc:a1:63:ef:03:b7:94:29:66:6c:66:d7:6c:6a:48:
+ 65:b2:dd:47:21:23:1b:b8:41:74:2f:96:dd:98:22:
+ b9:fa:f3:0e:4a:b0:2f:0b:a2:de:b7:02:13:42:70:
+ 3f:78:04:14:72:e3:3a:2b:7e:28:48:1d:96:b4:db:
+ 16:39:8d:b3:c4:59:a1:d7:a2:d2:64:61:33:2b:41:
+ 18:c5:ab:95:6f:5d:22:07:77:cd:53:9d:03:49:65:
+ d5:88:f5:5f:9d:f0:c4:69:4f:91:08:a5:39:07:96:
+ 36:af:2d:64:dc:26:5a:c1:13:ee:31:39:d6:5f:dc:
+ 97:fc:27:aa:05:78:47:c5:22:26:63:53:7e:37:c2:
+ 7b:64:3d:69:cb:f0:fb:8a:15:3e:52:b3:86:6a:b4:
+ c1:1c:3b:b2:f5:c7:3e:c5:76:dd:74:68:76:7a:55:
+ e6:80:7b:2e:8c:a6:da:bb:91:5a:07:cd:19:4a:ea:
+ 08:5c:ff:c1:49:d6:7b:06:bf:eb:b7:4a:9f:b4:27:
+ 9c:17
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ 1.3.6.1.4.1.311.21.1:
+ .....
+ 1.3.6.1.4.1.311.21.2:
+ ..#...h..f...@z.g.3...
+ X509v3 Subject Key Identifier:
+ EB:DB:11:5E:F8:09:9E:D8:D6:62:9C:FD:62:9D:E3:84:4A:28:E1:27
+ 1.3.6.1.4.1.311.20.2:
+ .
+.S.u.b.C.A
+ X509v3 Key Usage:
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Authority Key Identifier:
+ keyid:2A:4D:97:95:5D:34:7E:9D:B6:E6:33:BE:9C:27:C1:70:7E:67:DB:C1
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://mscrl.microsoft.com/pki/mscorp/crl/mswww(6).crl
+ URI:http://crl.microsoft.com/pki/mscorp/crl/mswww(6).crl
+ URI:http://corppki/crl/mswww(6).crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://www.microsoft.com/pki/mscorp/mswww(6).crt
+ CA Issuers - URI:http://corppki/aia/mswww(6).crt
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:36:72:f7:45:0b:a7:86:37:de:20:8d:f5:d7:8e:da:89:00:
+ 7a:52:4b:85:32:b2:32:d7:ed:80:57:fd:0d:51:1c:d1:1e:3d:
+ 0c:3e:91:2a:30:e2:53:bc:61:07:89:02:11:b9:1b:83:48:d3:
+ 1a:c7:47:96:62:b7:cf:de:d7:0d:b8:9d:84:8e:de:d5:e6:e4:
+ b9:c0:06:e3:1c:f4:31:43:1f:53:fc:4a:42:b5:9b:23:cd:b2:
+ ec:0d:0f:81:89:ff:59:a9:00:d3:04:4a:0b:af:5b:84:f5:3b:
+ 4f:b9:37:91:88:21:e4:9c:ca:52:76:63:7e:88:7a:65:b4:8e:
+ 16:6a:f4:60:bd:2c:0e:ef:17:86:2b:75:09:58:73:c5:4f:b9:
+ a8:1b:ef:2a:f4:b6:a3:b9:07:f0:a4:90:39:53:df:e1:ba:02:
+ 98:a5:a5:82:11:a1:06:53:b2:c2:eb:fd:ea:67:72:37:63:d0:
+ 85:cf:86:05:c8:c3:73:c1:db:f3:c4:d2:55:ff:a0:1d:e7:72:
+ 14:1a:ff:b2:ff:08:42:48:40:0a:19:82:cf:22:f3:05:c5:d5:
+ df:0b:29:c4:3a:0e:c5:32:38:ef:2f:94:9a:0c:f2:d4:ee:bf:
+ 62:c7:e2:a4:5f:3d:6e:78:bc:10:c1:2e:4a:30:f2:87:db:89:
+ 38:be:fe:cc:80:8f:f5:f9:5c:cb:5c:f1:ea:02:08:6b:a5:0b:
+ ef:20:5c:a3:34:cc:f0:80:b6:1f:63:b9:44:32:1c:26:41:9b:
+ dd:93:a3:64:01:57:11:21:43:94:2a:57:2d:8a:4a:cb:8a:28:
+ 40:0a:fb:50:f9:af:26:74:13:97:82:4e:6c:64:eb:d1:c6:bf:
+ 5e:fd:25:da:05:46:4a:ae:c7:2f:c7:04:ef:2e:71:2e:e2:a8:
+ 5b:a4:ea:b2:6f:a8:91:35:c4:b7:63:17:62:0e:27:8e:3c:24:
+ cd:3d:45:69:05:dc:52:c5:35:f8:11:c0:1d:df:62:60:f4:e1:
+ a6:5e:70:b8:45:74:03:ab:d1:16:74:e3:9e:d3:c1:a3:e8:90:
+ 96:8a:8a:c2:46:46:9d:b9:5c:6c:02:1d:32:84:eb:14:85:76:
+ 95:aa:4d:2d:69:b6:02:f6:fe:ed:34:d5:8c:e6:fa:ac:5d:dc:
+ 03:40:e6:cf:77:89:ff:b1:28:ca:86:8c:c8:e7:31:47:fc:16:
+ fe:54:0c:f5:26:b1:7e:dc:98:26:70:58:26:13:5c:c7:75:db:
+ 12:de:4c:ac:ff:9a:0c:ea:a2:c2:1c:41:04:8c:e6:47:97:47:
+ 6f:89:c5:48:de:37:0d:6a:d9:f0:68:24:5c:ff:19:59:e6:e1:
+ 70:37:38:0d:db:ee:b0:e2
+-----BEGIN CERTIFICATE-----
+MIIGCDCCA/CgAwIBAgIKYV2q0gAGAAAAQDANBgkqhkiG9w0BAQUFADAnMSUwIwYD
+VQQDExxNaWNyb3NvZnQgSW50ZXJuZXQgQXV0aG9yaXR5MB4XDTEyMDUxNTIwNDA1
+NVoXDTE2MDUxNTIwNTA1NVowgYAxEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
+kiaJk/IsZAEZFgltaWNyb3NvZnQxFDASBgoJkiaJk/IsZAEZFgRjb3JwMRcwFQYK
+CZImiZPyLGQBGRYHcmVkbW9uZDEfMB0GA1UEAxMWTVNJVCBNYWNoaW5lIEF1dGgg
+Q0EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3I6ADrWGkpEYSH
+HJ+HTUQ9OBvHE5PnFEwXK9t1CMDJIcqu4B/ojP6h3ySMvwKc7Zm+OlMqRU6wSHg3
+3KFj7wO3lClmbGbXbGpIZbLdRyEjG7hBdC+W3ZgiufrzDkqwLwui3rcCE0JwP3gE
+FHLjOit+KEgdlrTbFjmNs8RZodei0mRhMytBGMWrlW9dIgd3zVOdA0ll1Yj1X53w
+xGlPkQilOQeWNq8tZNwmWsET7jE51l/cl/wnqgV4R8UiJmNTfjfCe2Q9acvw+4oV
+PlKzhmq0wRw7svXHPsV23XRodnpV5oB7Loym2ruRWgfNGUrqCFz/wUnWewa/67dK
+n7QnnBcCAwEAAaOCAdowggHWMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGC
+NxUCBBYEFCO30O1oke9mm+EFQHq+Z8oz1ga9MB0GA1UdDgQWBBTr2xFe+Ame2NZi
+nP1ineOESijhJzAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
+AYYwEgYDVR0TAQH/BAgwBgEB/wIBADAfBgNVHSMEGDAWgBQqTZeVXTR+nbbmM76c
+J8FwfmfbwTCBowYDVR0fBIGbMIGYMIGVoIGSoIGPhjZodHRwOi8vbXNjcmwubWlj
+cm9zb2Z0LmNvbS9wa2kvbXNjb3JwL2NybC9tc3d3dyg2KS5jcmyGNGh0dHA6Ly9j
+cmwubWljcm9zb2Z0LmNvbS9wa2kvbXNjb3JwL2NybC9tc3d3dyg2KS5jcmyGH2h0
+dHA6Ly9jb3JwcGtpL2NybC9tc3d3dyg2KS5jcmwweQYIKwYBBQUHAQEEbTBrMDwG
+CCsGAQUFBzAChjBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL21zY29ycC9t
+c3d3dyg2KS5jcnQwKwYIKwYBBQUHMAKGH2h0dHA6Ly9jb3JwcGtpL2FpYS9tc3d3
+dyg2KS5jcnQwDQYJKoZIhvcNAQEFBQADggIBAKM2cvdFC6eGN94gjfXXjtqJAHpS
+S4UysjLX7YBX/Q1RHNEePQw+kSow4lO8YQeJAhG5G4NI0xrHR5Zit8/e1w24nYSO
+3tXm5LnABuMc9DFDH1P8SkK1myPNsuwND4GJ/1mpANMESguvW4T1O0+5N5GIIeSc
+ylJ2Y36IemW0jhZq9GC9LA7vF4YrdQlYc8VPuagb7yr0tqO5B/CkkDlT3+G6Apil
+pYIRoQZTssLr/epncjdj0IXPhgXIw3PB2/PE0lX/oB3nchQa/7L/CEJIQAoZgs8i
+8wXF1d8LKcQ6DsUyOO8vlJoM8tTuv2LH4qRfPW54vBDBLkow8ofbiTi+/syAj/X5
+XMtc8eoCCGulC+8gXKM0zPCAth9juUQyHCZBm92To2QBVxEhQ5QqVy2KSsuKKEAK
++1D5ryZ0E5eCTmxk69HGv179JdoFRkquxy/HBO8ucS7iqFuk6rJvqJE1xLdjF2IO
+J448JM09RWkF3FLFNfgRwB3fYmD04aZecLhFdAOr0RZ0457TwaPokJaKisJGRp25
+XGwCHTKE6xSFdpWqTS1ptgL2/u001Yzm+qxd3ANA5s93if+xKMqGjMjnMUf8Fv5U
+DPUmsX7cmCZwWCYTXMd12xLeTKz/mgzqosIcQQSM5keXR2+JxUjeNw1q2fBoJFz/
+GVnm4XA3OA3b7rDi
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert90[] = {
+ 0x30, 0x82, 0x06, 0x08, 0x30, 0x82, 0x03, 0xf0, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0a, 0x61, 0x5d, 0xaa, 0xd2, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x40, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x30, 0x27, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x1c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f,
+ 0x66, 0x74, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x32, 0x30, 0x35, 0x31, 0x35, 0x32, 0x30, 0x34, 0x30, 0x35,
+ 0x35, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x30, 0x35, 0x31, 0x35, 0x32, 0x30,
+ 0x35, 0x30, 0x35, 0x35, 0x5a, 0x30, 0x81, 0x80, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19,
+ 0x16, 0x03, 0x63, 0x6f, 0x6d, 0x31, 0x19, 0x30, 0x17, 0x06, 0x0a, 0x09,
+ 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x09, 0x6d,
+ 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x31, 0x14, 0x30, 0x12,
+ 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19,
+ 0x16, 0x04, 0x63, 0x6f, 0x72, 0x70, 0x31, 0x17, 0x30, 0x15, 0x06, 0x0a,
+ 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x07,
+ 0x72, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x1f, 0x30, 0x1d, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x4d, 0x53, 0x49, 0x54, 0x20, 0x4d,
+ 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x20,
+ 0x43, 0x41, 0x20, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xbd, 0xc8, 0xe8, 0x00, 0xeb, 0x58, 0x69, 0x29, 0x11, 0x84, 0x87,
+ 0x1c, 0x9f, 0x87, 0x4d, 0x44, 0x3d, 0x38, 0x1b, 0xc7, 0x13, 0x93, 0xe7,
+ 0x14, 0x4c, 0x17, 0x2b, 0xdb, 0x75, 0x08, 0xc0, 0xc9, 0x21, 0xca, 0xae,
+ 0xe0, 0x1f, 0xe8, 0x8c, 0xfe, 0xa1, 0xdf, 0x24, 0x8c, 0xbf, 0x02, 0x9c,
+ 0xed, 0x99, 0xbe, 0x3a, 0x53, 0x2a, 0x45, 0x4e, 0xb0, 0x48, 0x78, 0x37,
+ 0xdc, 0xa1, 0x63, 0xef, 0x03, 0xb7, 0x94, 0x29, 0x66, 0x6c, 0x66, 0xd7,
+ 0x6c, 0x6a, 0x48, 0x65, 0xb2, 0xdd, 0x47, 0x21, 0x23, 0x1b, 0xb8, 0x41,
+ 0x74, 0x2f, 0x96, 0xdd, 0x98, 0x22, 0xb9, 0xfa, 0xf3, 0x0e, 0x4a, 0xb0,
+ 0x2f, 0x0b, 0xa2, 0xde, 0xb7, 0x02, 0x13, 0x42, 0x70, 0x3f, 0x78, 0x04,
+ 0x14, 0x72, 0xe3, 0x3a, 0x2b, 0x7e, 0x28, 0x48, 0x1d, 0x96, 0xb4, 0xdb,
+ 0x16, 0x39, 0x8d, 0xb3, 0xc4, 0x59, 0xa1, 0xd7, 0xa2, 0xd2, 0x64, 0x61,
+ 0x33, 0x2b, 0x41, 0x18, 0xc5, 0xab, 0x95, 0x6f, 0x5d, 0x22, 0x07, 0x77,
+ 0xcd, 0x53, 0x9d, 0x03, 0x49, 0x65, 0xd5, 0x88, 0xf5, 0x5f, 0x9d, 0xf0,
+ 0xc4, 0x69, 0x4f, 0x91, 0x08, 0xa5, 0x39, 0x07, 0x96, 0x36, 0xaf, 0x2d,
+ 0x64, 0xdc, 0x26, 0x5a, 0xc1, 0x13, 0xee, 0x31, 0x39, 0xd6, 0x5f, 0xdc,
+ 0x97, 0xfc, 0x27, 0xaa, 0x05, 0x78, 0x47, 0xc5, 0x22, 0x26, 0x63, 0x53,
+ 0x7e, 0x37, 0xc2, 0x7b, 0x64, 0x3d, 0x69, 0xcb, 0xf0, 0xfb, 0x8a, 0x15,
+ 0x3e, 0x52, 0xb3, 0x86, 0x6a, 0xb4, 0xc1, 0x1c, 0x3b, 0xb2, 0xf5, 0xc7,
+ 0x3e, 0xc5, 0x76, 0xdd, 0x74, 0x68, 0x76, 0x7a, 0x55, 0xe6, 0x80, 0x7b,
+ 0x2e, 0x8c, 0xa6, 0xda, 0xbb, 0x91, 0x5a, 0x07, 0xcd, 0x19, 0x4a, 0xea,
+ 0x08, 0x5c, 0xff, 0xc1, 0x49, 0xd6, 0x7b, 0x06, 0xbf, 0xeb, 0xb7, 0x4a,
+ 0x9f, 0xb4, 0x27, 0x9c, 0x17, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x01, 0xda, 0x30, 0x82, 0x01, 0xd6, 0x30, 0x12, 0x06, 0x09, 0x2b, 0x06,
+ 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01, 0x04, 0x05, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0x30, 0x23, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
+ 0x37, 0x15, 0x02, 0x04, 0x16, 0x04, 0x14, 0x23, 0xb7, 0xd0, 0xed, 0x68,
+ 0x91, 0xef, 0x66, 0x9b, 0xe1, 0x05, 0x40, 0x7a, 0xbe, 0x67, 0xca, 0x33,
+ 0xd6, 0x06, 0xbd, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+ 0x04, 0x14, 0xeb, 0xdb, 0x11, 0x5e, 0xf8, 0x09, 0x9e, 0xd8, 0xd6, 0x62,
+ 0x9c, 0xfd, 0x62, 0x9d, 0xe3, 0x84, 0x4a, 0x28, 0xe1, 0x27, 0x30, 0x19,
+ 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04,
+ 0x0c, 0x1e, 0x0a, 0x00, 0x53, 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00,
+ 0x41, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x86, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x1f,
+ 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x2a,
+ 0x4d, 0x97, 0x95, 0x5d, 0x34, 0x7e, 0x9d, 0xb6, 0xe6, 0x33, 0xbe, 0x9c,
+ 0x27, 0xc1, 0x70, 0x7e, 0x67, 0xdb, 0xc1, 0x30, 0x81, 0xa3, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x9b, 0x30, 0x81, 0x98, 0x30, 0x81, 0x95,
+ 0xa0, 0x81, 0x92, 0xa0, 0x81, 0x8f, 0x86, 0x36, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6d, 0x73, 0x63, 0x72, 0x6c, 0x2e, 0x6d, 0x69, 0x63,
+ 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70,
+ 0x6b, 0x69, 0x2f, 0x6d, 0x73, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2f, 0x6d, 0x73, 0x77, 0x77, 0x77, 0x28, 0x36, 0x29, 0x2e, 0x63,
+ 0x72, 0x6c, 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x6d, 0x73, 0x63,
+ 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x6d, 0x73, 0x77, 0x77,
+ 0x77, 0x28, 0x36, 0x29, 0x2e, 0x63, 0x72, 0x6c, 0x86, 0x1f, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x6f, 0x72, 0x70, 0x70, 0x6b, 0x69,
+ 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x6d, 0x73, 0x77, 0x77, 0x77, 0x28, 0x36,
+ 0x29, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x79, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x6d, 0x30, 0x6b, 0x30, 0x3c, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x30, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69,
+ 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x70, 0x6b, 0x69, 0x2f, 0x6d, 0x73, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x6d,
+ 0x73, 0x77, 0x77, 0x77, 0x28, 0x36, 0x29, 0x2e, 0x63, 0x72, 0x74, 0x30,
+ 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86,
+ 0x1f, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x6f, 0x72, 0x70,
+ 0x70, 0x6b, 0x69, 0x2f, 0x61, 0x69, 0x61, 0x2f, 0x6d, 0x73, 0x77, 0x77,
+ 0x77, 0x28, 0x36, 0x29, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x02, 0x01, 0x00, 0xa3, 0x36, 0x72, 0xf7, 0x45, 0x0b, 0xa7, 0x86,
+ 0x37, 0xde, 0x20, 0x8d, 0xf5, 0xd7, 0x8e, 0xda, 0x89, 0x00, 0x7a, 0x52,
+ 0x4b, 0x85, 0x32, 0xb2, 0x32, 0xd7, 0xed, 0x80, 0x57, 0xfd, 0x0d, 0x51,
+ 0x1c, 0xd1, 0x1e, 0x3d, 0x0c, 0x3e, 0x91, 0x2a, 0x30, 0xe2, 0x53, 0xbc,
+ 0x61, 0x07, 0x89, 0x02, 0x11, 0xb9, 0x1b, 0x83, 0x48, 0xd3, 0x1a, 0xc7,
+ 0x47, 0x96, 0x62, 0xb7, 0xcf, 0xde, 0xd7, 0x0d, 0xb8, 0x9d, 0x84, 0x8e,
+ 0xde, 0xd5, 0xe6, 0xe4, 0xb9, 0xc0, 0x06, 0xe3, 0x1c, 0xf4, 0x31, 0x43,
+ 0x1f, 0x53, 0xfc, 0x4a, 0x42, 0xb5, 0x9b, 0x23, 0xcd, 0xb2, 0xec, 0x0d,
+ 0x0f, 0x81, 0x89, 0xff, 0x59, 0xa9, 0x00, 0xd3, 0x04, 0x4a, 0x0b, 0xaf,
+ 0x5b, 0x84, 0xf5, 0x3b, 0x4f, 0xb9, 0x37, 0x91, 0x88, 0x21, 0xe4, 0x9c,
+ 0xca, 0x52, 0x76, 0x63, 0x7e, 0x88, 0x7a, 0x65, 0xb4, 0x8e, 0x16, 0x6a,
+ 0xf4, 0x60, 0xbd, 0x2c, 0x0e, 0xef, 0x17, 0x86, 0x2b, 0x75, 0x09, 0x58,
+ 0x73, 0xc5, 0x4f, 0xb9, 0xa8, 0x1b, 0xef, 0x2a, 0xf4, 0xb6, 0xa3, 0xb9,
+ 0x07, 0xf0, 0xa4, 0x90, 0x39, 0x53, 0xdf, 0xe1, 0xba, 0x02, 0x98, 0xa5,
+ 0xa5, 0x82, 0x11, 0xa1, 0x06, 0x53, 0xb2, 0xc2, 0xeb, 0xfd, 0xea, 0x67,
+ 0x72, 0x37, 0x63, 0xd0, 0x85, 0xcf, 0x86, 0x05, 0xc8, 0xc3, 0x73, 0xc1,
+ 0xdb, 0xf3, 0xc4, 0xd2, 0x55, 0xff, 0xa0, 0x1d, 0xe7, 0x72, 0x14, 0x1a,
+ 0xff, 0xb2, 0xff, 0x08, 0x42, 0x48, 0x40, 0x0a, 0x19, 0x82, 0xcf, 0x22,
+ 0xf3, 0x05, 0xc5, 0xd5, 0xdf, 0x0b, 0x29, 0xc4, 0x3a, 0x0e, 0xc5, 0x32,
+ 0x38, 0xef, 0x2f, 0x94, 0x9a, 0x0c, 0xf2, 0xd4, 0xee, 0xbf, 0x62, 0xc7,
+ 0xe2, 0xa4, 0x5f, 0x3d, 0x6e, 0x78, 0xbc, 0x10, 0xc1, 0x2e, 0x4a, 0x30,
+ 0xf2, 0x87, 0xdb, 0x89, 0x38, 0xbe, 0xfe, 0xcc, 0x80, 0x8f, 0xf5, 0xf9,
+ 0x5c, 0xcb, 0x5c, 0xf1, 0xea, 0x02, 0x08, 0x6b, 0xa5, 0x0b, 0xef, 0x20,
+ 0x5c, 0xa3, 0x34, 0xcc, 0xf0, 0x80, 0xb6, 0x1f, 0x63, 0xb9, 0x44, 0x32,
+ 0x1c, 0x26, 0x41, 0x9b, 0xdd, 0x93, 0xa3, 0x64, 0x01, 0x57, 0x11, 0x21,
+ 0x43, 0x94, 0x2a, 0x57, 0x2d, 0x8a, 0x4a, 0xcb, 0x8a, 0x28, 0x40, 0x0a,
+ 0xfb, 0x50, 0xf9, 0xaf, 0x26, 0x74, 0x13, 0x97, 0x82, 0x4e, 0x6c, 0x64,
+ 0xeb, 0xd1, 0xc6, 0xbf, 0x5e, 0xfd, 0x25, 0xda, 0x05, 0x46, 0x4a, 0xae,
+ 0xc7, 0x2f, 0xc7, 0x04, 0xef, 0x2e, 0x71, 0x2e, 0xe2, 0xa8, 0x5b, 0xa4,
+ 0xea, 0xb2, 0x6f, 0xa8, 0x91, 0x35, 0xc4, 0xb7, 0x63, 0x17, 0x62, 0x0e,
+ 0x27, 0x8e, 0x3c, 0x24, 0xcd, 0x3d, 0x45, 0x69, 0x05, 0xdc, 0x52, 0xc5,
+ 0x35, 0xf8, 0x11, 0xc0, 0x1d, 0xdf, 0x62, 0x60, 0xf4, 0xe1, 0xa6, 0x5e,
+ 0x70, 0xb8, 0x45, 0x74, 0x03, 0xab, 0xd1, 0x16, 0x74, 0xe3, 0x9e, 0xd3,
+ 0xc1, 0xa3, 0xe8, 0x90, 0x96, 0x8a, 0x8a, 0xc2, 0x46, 0x46, 0x9d, 0xb9,
+ 0x5c, 0x6c, 0x02, 0x1d, 0x32, 0x84, 0xeb, 0x14, 0x85, 0x76, 0x95, 0xaa,
+ 0x4d, 0x2d, 0x69, 0xb6, 0x02, 0xf6, 0xfe, 0xed, 0x34, 0xd5, 0x8c, 0xe6,
+ 0xfa, 0xac, 0x5d, 0xdc, 0x03, 0x40, 0xe6, 0xcf, 0x77, 0x89, 0xff, 0xb1,
+ 0x28, 0xca, 0x86, 0x8c, 0xc8, 0xe7, 0x31, 0x47, 0xfc, 0x16, 0xfe, 0x54,
+ 0x0c, 0xf5, 0x26, 0xb1, 0x7e, 0xdc, 0x98, 0x26, 0x70, 0x58, 0x26, 0x13,
+ 0x5c, 0xc7, 0x75, 0xdb, 0x12, 0xde, 0x4c, 0xac, 0xff, 0x9a, 0x0c, 0xea,
+ 0xa2, 0xc2, 0x1c, 0x41, 0x04, 0x8c, 0xe6, 0x47, 0x97, 0x47, 0x6f, 0x89,
+ 0xc5, 0x48, 0xde, 0x37, 0x0d, 0x6a, 0xd9, 0xf0, 0x68, 0x24, 0x5c, 0xff,
+ 0x19, 0x59, 0xe6, 0xe1, 0x70, 0x37, 0x38, 0x0d, 0xdb, 0xee, 0xb0, 0xe2,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 11:2a:00:6d:37:e5:10:6f:d6:ca:7c:c3:ef:ba:cc:18
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2016 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)06, CN=VeriSign Class 3 Extended Validation SSL SGC CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:56:88:ba:88:34:64:64:cf:cd:ca:b0:ee:e7:
+ 19:73:c5:72:d9:bb:45:bc:b5:a8:ff:83:be:1c:03:
+ db:ed:89:b7:2e:10:1a:25:bc:55:ca:41:a1:9f:0b:
+ cf:19:5e:70:b9:5e:39:4b:9e:31:1c:5f:87:ae:2a:
+ aa:a8:2b:a2:1b:3b:10:23:5f:13:b1:dd:08:8c:4e:
+ 14:da:83:81:e3:b5:8c:e3:68:ed:24:67:ce:56:b6:
+ ac:9b:73:96:44:db:8a:8c:b3:d6:f0:71:93:8e:db:
+ 71:54:4a:eb:73:59:6a:8f:70:51:2c:03:9f:97:d1:
+ cc:11:7a:bc:62:0d:95:2a:c9:1c:75:57:e9:f5:c7:
+ ea:ba:84:35:cb:c7:85:5a:7e:e4:4d:e1:11:97:7d:
+ 0e:20:34:45:db:f1:a2:09:eb:eb:3d:9e:b8:96:43:
+ 5e:34:4b:08:25:1e:43:1a:a2:d9:b7:8a:01:34:3d:
+ c3:f8:e5:af:4f:8c:ff:cd:65:f0:23:4e:c5:97:b3:
+ 5c:da:90:1c:82:85:0d:06:0d:c1:22:b6:7b:28:a4:
+ 03:c3:4c:53:d1:58:bc:72:bc:08:39:fc:a0:76:a8:
+ a8:e9:4b:6e:88:3d:e3:b3:31:25:8c:73:29:48:0e:
+ 32:79:06:ed:3d:43:f4:f6:e4:e9:fc:7d:be:8e:08:
+ d5:1f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 4E:43:C8:1D:76:EF:37:53:7A:4F:F2:58:6F:94:F3:38:E2:D5:BD:DF
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://EVSecure-crl.verisign.com/pca3-g5.crl
+
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Netscape Cert Type:
+ SSL CA, S/MIME CA
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=Class3CA2048-1-48
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.verisign.com
+
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 5a:a2:b1:bf:eb:8d:d4:38:a8:80:72:c2:dc:38:2e:ac:a7:71:
+ f9:2b:a3:bb:47:bb:6d:69:6f:10:36:98:8c:c7:56:2e:bb:bc:
+ ab:4a:9b:7a:d6:f2:82:93:e0:14:fe:8a:ce:83:b7:83:db:93:
+ 87:ab:ac:65:79:49:fd:57:a9:b1:ce:09:1f:ba:10:15:c4:09:
+ 0e:62:e3:f9:0a:25:d5:64:98:f0:f2:a8:0f:76:32:7e:91:e6:
+ 18:ee:bc:e7:da:d0:4e:8d:78:bb:e2:9d:c0:59:2b:c0:ce:95:
+ 0d:24:0c:72:ca:34:5e:70:22:89:2b:4a:b0:f1:68:87:f3:ee:
+ 44:8d:28:40:77:39:6e:48:72:45:31:5d:6b:39:0e:86:02:ea:
+ 66:99:93:31:0f:df:67:de:a6:9f:8c:9d:4c:ce:71:6f:3a:21:
+ f6:b9:34:3f:f9:6e:d8:9a:f7:3e:da:f3:81:5f:7a:5c:6d:8f:
+ 7c:f6:99:74:b7:ff:e4:17:5d:ed:61:5e:ab:48:bb:96:8d:66:
+ 45:39:b4:12:0a:f6:70:e9:9c:76:22:4b:60:e9:2a:1b:34:49:
+ f7:a2:d4:67:c0:b1:26:ad:13:ba:d9:84:01:c1:ab:e1:8e:6d:
+ 70:16:3b:77:ac:91:9a:bb:1a:1f:da:58:a7:e4:4f:c1:61:ae:
+ bc:a2:fe:4b
+-----BEGIN CERTIFICATE-----
+MIIGCjCCBPKgAwIBAgIQESoAbTflEG/WynzD77rMGDANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMTYxMTA3MjM1OTU5WjCBvjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMvVmVy
+aVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9Voi6iDRkZM/NyrDu5xlzxXLZ
+u0W8taj/g74cA9vtibcuEBolvFXKQaGfC88ZXnC5XjlLnjEcX4euKqqoK6IbOxAj
+XxOx3QiMThTag4HjtYzjaO0kZ85Wtqybc5ZE24qMs9bwcZOO23FUSutzWWqPcFEs
+A5+X0cwRerxiDZUqyRx1V+n1x+q6hDXLx4VafuRN4RGXfQ4gNEXb8aIJ6+s9nriW
+Q140SwglHkMaotm3igE0PcP45a9PjP/NZfAjTsWXs1zakByChQ0GDcEitnsopAPD
+TFPRWLxyvAg5/KB2qKjpS26IPeOzMSWMcylIDjJ5Bu09Q/T25On8fb6OCNUfAgMB
+AAGjggH0MIIB8DAdBgNVHQ4EFgQUTkPIHXbvN1N6T/JYb5TzOOLVvd8wEgYDVR0T
+AQH/BAgwBgEB/wIBADA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczA9BgNVHR8ENjA0MDKgMKAuhixo
+dHRwOi8vRVZTZWN1cmUtY3JsLnZlcmlzaWduLmNvbS9wY2EzLWc1LmNybDAgBgNV
+HSUEGTAXBglghkgBhvhCBAEGCmCGSAGG+EUBCAEwDgYDVR0PAQH/BAQDAgEGMBEG
+CWCGSAGG+EIBAQQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFn
+ZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRw
+Oi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjApBgNVHREEIjAgpB4wHDEa
+MBgGA1UEAxMRQ2xhc3MzQ0EyMDQ4LTEtNDgwPQYIKwYBBQUHAQEEMTAvMC0GCCsG
+AQUFBzABhiFodHRwOi8vRVZTZWN1cmUtb2NzcC52ZXJpc2lnbi5jb20wHwYDVR0j
+BBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJKoZIhvcNAQEFBQADggEBAFqi
+sb/rjdQ4qIBywtw4Lqyncfkro7tHu21pbxA2mIzHVi67vKtKm3rW8oKT4BT+is6D
+t4Pbk4errGV5Sf1XqbHOCR+6EBXECQ5i4/kKJdVkmPDyqA92Mn6R5hjuvOfa0E6N
+eLvincBZK8DOlQ0kDHLKNF5wIokrSrDxaIfz7kSNKEB3OW5IckUxXWs5DoYC6maZ
+kzEP32fepp+MnUzOcW86Ifa5ND/5btia9z7a84Ffelxtj3z2mXS3/+QXXe1hXqtI
+u5aNZkU5tBIK9nDpnHYiS2DpKhs0Sfei1GfAsSatE7rZhAHBq+GObXAWO3eskZq7
+Gh/aWKfkT8Fhrryi/ks=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert91[] = {
+ 0x30, 0x82, 0x06, 0x0a, 0x30, 0x82, 0x04, 0xf2, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x11, 0x2a, 0x00, 0x6d, 0x37, 0xe5, 0x10, 0x6f, 0xd6,
+ 0xca, 0x7c, 0xc3, 0xef, 0xba, 0xcc, 0x18, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, 0x31, 0x38,
+ 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x53, 0x47, 0x43, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0x56, 0x88, 0xba, 0x88, 0x34, 0x64,
+ 0x64, 0xcf, 0xcd, 0xca, 0xb0, 0xee, 0xe7, 0x19, 0x73, 0xc5, 0x72, 0xd9,
+ 0xbb, 0x45, 0xbc, 0xb5, 0xa8, 0xff, 0x83, 0xbe, 0x1c, 0x03, 0xdb, 0xed,
+ 0x89, 0xb7, 0x2e, 0x10, 0x1a, 0x25, 0xbc, 0x55, 0xca, 0x41, 0xa1, 0x9f,
+ 0x0b, 0xcf, 0x19, 0x5e, 0x70, 0xb9, 0x5e, 0x39, 0x4b, 0x9e, 0x31, 0x1c,
+ 0x5f, 0x87, 0xae, 0x2a, 0xaa, 0xa8, 0x2b, 0xa2, 0x1b, 0x3b, 0x10, 0x23,
+ 0x5f, 0x13, 0xb1, 0xdd, 0x08, 0x8c, 0x4e, 0x14, 0xda, 0x83, 0x81, 0xe3,
+ 0xb5, 0x8c, 0xe3, 0x68, 0xed, 0x24, 0x67, 0xce, 0x56, 0xb6, 0xac, 0x9b,
+ 0x73, 0x96, 0x44, 0xdb, 0x8a, 0x8c, 0xb3, 0xd6, 0xf0, 0x71, 0x93, 0x8e,
+ 0xdb, 0x71, 0x54, 0x4a, 0xeb, 0x73, 0x59, 0x6a, 0x8f, 0x70, 0x51, 0x2c,
+ 0x03, 0x9f, 0x97, 0xd1, 0xcc, 0x11, 0x7a, 0xbc, 0x62, 0x0d, 0x95, 0x2a,
+ 0xc9, 0x1c, 0x75, 0x57, 0xe9, 0xf5, 0xc7, 0xea, 0xba, 0x84, 0x35, 0xcb,
+ 0xc7, 0x85, 0x5a, 0x7e, 0xe4, 0x4d, 0xe1, 0x11, 0x97, 0x7d, 0x0e, 0x20,
+ 0x34, 0x45, 0xdb, 0xf1, 0xa2, 0x09, 0xeb, 0xeb, 0x3d, 0x9e, 0xb8, 0x96,
+ 0x43, 0x5e, 0x34, 0x4b, 0x08, 0x25, 0x1e, 0x43, 0x1a, 0xa2, 0xd9, 0xb7,
+ 0x8a, 0x01, 0x34, 0x3d, 0xc3, 0xf8, 0xe5, 0xaf, 0x4f, 0x8c, 0xff, 0xcd,
+ 0x65, 0xf0, 0x23, 0x4e, 0xc5, 0x97, 0xb3, 0x5c, 0xda, 0x90, 0x1c, 0x82,
+ 0x85, 0x0d, 0x06, 0x0d, 0xc1, 0x22, 0xb6, 0x7b, 0x28, 0xa4, 0x03, 0xc3,
+ 0x4c, 0x53, 0xd1, 0x58, 0xbc, 0x72, 0xbc, 0x08, 0x39, 0xfc, 0xa0, 0x76,
+ 0xa8, 0xa8, 0xe9, 0x4b, 0x6e, 0x88, 0x3d, 0xe3, 0xb3, 0x31, 0x25, 0x8c,
+ 0x73, 0x29, 0x48, 0x0e, 0x32, 0x79, 0x06, 0xed, 0x3d, 0x43, 0xf4, 0xf6,
+ 0xe4, 0xe9, 0xfc, 0x7d, 0xbe, 0x8e, 0x08, 0xd5, 0x1f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x01, 0xf4, 0x30, 0x82, 0x01, 0xf0, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x4e, 0x43, 0xc8,
+ 0x1d, 0x76, 0xef, 0x37, 0x53, 0x7a, 0x4f, 0xf2, 0x58, 0x6f, 0x94, 0xf3,
+ 0x38, 0xe2, 0xd5, 0xbd, 0xdf, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34,
+ 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x36, 0x30, 0x34, 0x30, 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33,
+ 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x20, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x19, 0x30, 0x17, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
+ 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x11, 0x06,
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01, 0x04, 0x04,
+ 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b,
+ 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67,
+ 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06,
+ 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a,
+ 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18,
+ 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c,
+ 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a,
+ 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x43, 0x6c, 0x61,
+ 0x73, 0x73, 0x33, 0x43, 0x41, 0x32, 0x30, 0x34, 0x38, 0x2d, 0x31, 0x2d,
+ 0x34, 0x38, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23,
+ 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd,
+ 0xec, 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33,
+ 0x31, 0x33, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x5a, 0xa2,
+ 0xb1, 0xbf, 0xeb, 0x8d, 0xd4, 0x38, 0xa8, 0x80, 0x72, 0xc2, 0xdc, 0x38,
+ 0x2e, 0xac, 0xa7, 0x71, 0xf9, 0x2b, 0xa3, 0xbb, 0x47, 0xbb, 0x6d, 0x69,
+ 0x6f, 0x10, 0x36, 0x98, 0x8c, 0xc7, 0x56, 0x2e, 0xbb, 0xbc, 0xab, 0x4a,
+ 0x9b, 0x7a, 0xd6, 0xf2, 0x82, 0x93, 0xe0, 0x14, 0xfe, 0x8a, 0xce, 0x83,
+ 0xb7, 0x83, 0xdb, 0x93, 0x87, 0xab, 0xac, 0x65, 0x79, 0x49, 0xfd, 0x57,
+ 0xa9, 0xb1, 0xce, 0x09, 0x1f, 0xba, 0x10, 0x15, 0xc4, 0x09, 0x0e, 0x62,
+ 0xe3, 0xf9, 0x0a, 0x25, 0xd5, 0x64, 0x98, 0xf0, 0xf2, 0xa8, 0x0f, 0x76,
+ 0x32, 0x7e, 0x91, 0xe6, 0x18, 0xee, 0xbc, 0xe7, 0xda, 0xd0, 0x4e, 0x8d,
+ 0x78, 0xbb, 0xe2, 0x9d, 0xc0, 0x59, 0x2b, 0xc0, 0xce, 0x95, 0x0d, 0x24,
+ 0x0c, 0x72, 0xca, 0x34, 0x5e, 0x70, 0x22, 0x89, 0x2b, 0x4a, 0xb0, 0xf1,
+ 0x68, 0x87, 0xf3, 0xee, 0x44, 0x8d, 0x28, 0x40, 0x77, 0x39, 0x6e, 0x48,
+ 0x72, 0x45, 0x31, 0x5d, 0x6b, 0x39, 0x0e, 0x86, 0x02, 0xea, 0x66, 0x99,
+ 0x93, 0x31, 0x0f, 0xdf, 0x67, 0xde, 0xa6, 0x9f, 0x8c, 0x9d, 0x4c, 0xce,
+ 0x71, 0x6f, 0x3a, 0x21, 0xf6, 0xb9, 0x34, 0x3f, 0xf9, 0x6e, 0xd8, 0x9a,
+ 0xf7, 0x3e, 0xda, 0xf3, 0x81, 0x5f, 0x7a, 0x5c, 0x6d, 0x8f, 0x7c, 0xf6,
+ 0x99, 0x74, 0xb7, 0xff, 0xe4, 0x17, 0x5d, 0xed, 0x61, 0x5e, 0xab, 0x48,
+ 0xbb, 0x96, 0x8d, 0x66, 0x45, 0x39, 0xb4, 0x12, 0x0a, 0xf6, 0x70, 0xe9,
+ 0x9c, 0x76, 0x22, 0x4b, 0x60, 0xe9, 0x2a, 0x1b, 0x34, 0x49, 0xf7, 0xa2,
+ 0xd4, 0x67, 0xc0, 0xb1, 0x26, 0xad, 0x13, 0xba, 0xd9, 0x84, 0x01, 0xc1,
+ 0xab, 0xe1, 0x8e, 0x6d, 0x70, 0x16, 0x3b, 0x77, 0xac, 0x91, 0x9a, 0xbb,
+ 0x1a, 0x1f, 0xda, 0x58, 0xa7, 0xe4, 0x4f, 0xc1, 0x61, 0xae, 0xbc, 0xa2,
+ 0xfe, 0x4b,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 61:03:33:36:00:05:00:00:00:30
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=Microsoft Internet Authority
+ Validity
+ Not Before: May 19 22:13:30 2010 GMT
+ Not After : May 19 22:23:30 2014 GMT
+ Subject: DC=com, DC=microsoft, DC=corp, DC=redmond, CN=Microsoft Secure Server Authority
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ea:9f:5f:91:0b:cd:19:82:5f:91:ea:ab:f5:8b:
+ 28:d8:8b:f5:1c:e0:91:c9:bc:cd:02:10:50:22:b7:
+ 38:0a:5c:cf:71:0c:58:2d:88:6c:a8:b8:3c:33:63:
+ f9:73:9d:3c:e9:c3:79:ed:f2:fe:c9:cb:c3:6e:24:
+ e2:3c:42:70:d8:5f:b7:5b:f7:9b:5f:f5:27:6f:78:
+ 00:eb:96:5d:b7:6f:cf:e4:41:04:f0:bb:43:bd:6f:
+ 5f:26:0f:b7:8e:37:41:13:54:67:1b:90:00:27:38:
+ b8:1a:c3:96:6d:1c:31:35:35:49:c5:46:1e:e7:73:
+ a4:ca:03:11:79:41:81:af:d3:8e:46:a2:c5:be:00:
+ 53:05:b9:38:9c:b7:60:29:b3:ca:52:9a:92:c5:53:
+ 27:b6:41:0d:40:f8:2f:9b:e7:81:49:1a:5a:6a:a8:
+ 4f:71:c7:e8:6d:81:be:27:ef:c9:d6:c6:92:2b:10:
+ e4:36:35:40:08:d0:4d:70:fd:70:9b:20:1c:b3:b9:
+ df:75:9d:2b:77:d0:c4:cd:6a:71:ef:5a:58:0b:f9:
+ 70:85:88:05:89:6d:66:92:30:ab:af:88:39:d7:d4:
+ 2d:0b:96:9c:78:24:af:00:ab:cf:09:3e:13:ae:6b:
+ c3:e0:e1:cf:60:7f:8b:53:dc:02:d0:f3:b0:86:11:
+ de:bd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 08:42:E3:DB:4E:11:66:F3:B5:08:C5:40:DB:55:7C:33:46:11:83:38
+ X509v3 Key Usage:
+ Digital Signature, Certificate Sign, CRL Sign
+ 1.3.6.1.4.1.311.21.1:
+ .....
+ 1.3.6.1.4.1.311.21.2:
+ ..~...Z2..q..Oup......
+ 1.3.6.1.4.1.311.20.2:
+ .
+.S.u.b.C.A
+ X509v3 Authority Key Identifier:
+ keyid:33:21:F0:CB:FE:A2:A0:44:92:DE:F6:3B:33:D8:5F:01:4B:97:78:5D
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://mscrl.microsoft.com/pki/mscorp/crl/mswww(5).crl
+ URI:http://crl.microsoft.com/pki/mscorp/crl/mswww(5).crl
+ URI:http://corppki/crl/mswww(5).crl
+
+ Authority Information Access:
+ CA Issuers - URI:http://www.microsoft.com/pki/mscorp/mswww(5).crt
+ CA Issuers - URI:http://corppki/aia/mswww(5).crt
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 8f:c2:d1:5c:ef:14:11:77:17:63:07:3c:4c:7c:68:da:fe:86:
+ 4a:e2:20:cc:3f:b0:27:3d:d1:e2:ac:c8:8b:48:a6:e4:59:f7:
+ 3a:06:ad:7d:52:f1:f6:65:61:96:21:22:ae:68:be:2f:7a:de:
+ b3:0c:f5:e9:c5:dd:f8:65:82:5d:cb:6c:3e:0c:37:11:74:15:
+ 09:78:55:bd:26:12:bb:d6:95:74:d3:bc:f5:76:09:2a:6a:df:
+ 36:c4:8e:56:d5:1f:20:df:7f:82:30:d7:43:ab:68:22:8b:6a:
+ 5a:c5:9b:d0:9d:8d:0b:0c:50:85:7e:cc:5a:80:07:8b:03:4e:
+ bf:bd:5f:6c:56:0f:05:a9:e2:54:c3:a5:d3:52:5c:5f:4d:0b:
+ dd:05:f8:51:12:03:21:6f:9c:6c:97:98:2a:c1:c1:11:bc:bd:
+ 1b:ae:fb:e3:57:5f:4f:1f:00:9e:e2:a4:51:d3:f7:ac:09:37:
+ 58:a5:09:21:d1:72:d0:b2:c1:8b:db:4d:dc:13:d1:54:58:4d:
+ 2b:c0:ad:fa:53:19:35:b1:15:a8:42:64:b7:ed:c7:1f:a5:79:
+ a8:0d:38:d4:50:bf:f4:5a:ff:2f:e9:bf:3f:7d:38:e5:fb:20:
+ 0c:d4:4e:e0:2f:1d:45:7a:fb:28:2f:31:48:6f:cc:6e:5c:68:
+ 42:fa:ea:c8:0b:01:30:ec:10:26:42:38:23:a9:c3:19:b8:d9:
+ 70:1a:68:2c:92:cb:9f:73:e6:cc:ff:33:23:ee:db:5e:b5:7f:
+ 05:58:3f:50:c5:1c:08:18:f4:eb:2f:62:aa:53:f7:a1:cd:de:
+ e3:eb:82:1c:1a:67:6b:a1:4c:a7:68:71:40:d1:65:3b:41:18:
+ 9c:49:e3:71:fb:eb:4d:83:93:d3:47:e6:64:42:cb:b6:35:1c:
+ fb:34:0e:a1:28:fb:8c:a1:a7:1f:01:28:51:e5:71:94:37:9c:
+ dc:41:5b:7c:7e:e9:2c:23:67:94:9d:73:df:5f:40:79:a3:8d:
+ 95:30:cc:53:17:08:bc:50:86:f3:fc:10:19:81:fc:f4:5a:6e:
+ f3:dc:a2:9a:75:7b:c3:ac:a0:51:ed:32:b6:58:df:4f:8e:91:
+ 53:6a:d2:aa:1b:5d:e6:53:b8:89:a3:9e:89:a1:e3:29:e0:b3:
+ 6c:eb:1a:cc:6f:5a:aa:c2:e2:f6:1e:45:29:ef:d6:c2:43:b1:
+ 3b:ad:3e:26:fc:81:97:5c:48:fd:62:59:34:92:c9:fb:b9:a1:
+ d7:42:05:fb:19:f6:7e:32:fb:29:34:d5:87:66:e5:04:1d:c8:
+ 3e:10:fa:a6:78:f5:1e:7d:de:1a:3a:78:7c:dc:2a:71:06:a3:
+ 2d:6f:05:55:23:8b:90:ef
+-----BEGIN CERTIFICATE-----
+MIIGEzCCA/ugAwIBAgIKYQMzNgAFAAAAMDANBgkqhkiG9w0BAQUFADAnMSUwIwYD
+VQQDExxNaWNyb3NvZnQgSW50ZXJuZXQgQXV0aG9yaXR5MB4XDTEwMDUxOTIyMTMz
+MFoXDTE0MDUxOTIyMjMzMFowgYsxEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ
+kiaJk/IsZAEZFgltaWNyb3NvZnQxFDASBgoJkiaJk/IsZAEZFgRjb3JwMRcwFQYK
+CZImiZPyLGQBGRYHcmVkbW9uZDEqMCgGA1UEAxMhTWljcm9zb2Z0IFNlY3VyZSBT
+ZXJ2ZXIgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+6p9fkQvNGYJfkeqr9Yso2Iv1HOCRybzNAhBQIrc4ClzPcQxYLYhsqLg8M2P5c508
+6cN57fL+ycvDbiTiPEJw2F+3W/ebX/Unb3gA65Zdt2/P5EEE8LtDvW9fJg+3jjdB
+E1RnG5AAJzi4GsOWbRwxNTVJxUYe53OkygMReUGBr9OORqLFvgBTBbk4nLdgKbPK
+UpqSxVMntkENQPgvm+eBSRpaaqhPccfobYG+J+/J1saSKxDkNjVACNBNcP1wmyAc
+s7nfdZ0rd9DEzWpx71pYC/lwhYgFiW1mkjCrr4g519QtC5aceCSvAKvPCT4TrmvD
+4OHPYH+LU9wC0POwhhHevQIDAQABo4IB2jCCAdYwEgYDVR0TAQH/BAgwBgEB/wIB
+ADAdBgNVHQ4EFgQUCELj204RZvO1CMVA21V8M0YRgzgwCwYDVR0PBAQDAgGGMBIG
+CSsGAQQBgjcVAQQFAgMIAAgwIwYJKwYBBAGCNxUCBBYEFH6KwpxaMozCcaLZT3Vw
+96kb9pQFMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMB8GA1UdIwQYMBaAFDMh
+8Mv+oqBEkt72OzPYXwFLl3hdMIGjBgNVHR8EgZswgZgwgZWggZKggY+GNmh0dHA6
+Ly9tc2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3KDUpLmNy
+bIY0aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3
+KDUpLmNybIYfaHR0cDovL2NvcnBwa2kvY3JsL21zd3d3KDUpLmNybDB5BggrBgEF
+BQcBAQRtMGswPAYIKwYBBQUHMAKGMGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
+a2kvbXNjb3JwL21zd3d3KDUpLmNydDArBggrBgEFBQcwAoYfaHR0cDovL2NvcnBw
+a2kvYWlhL21zd3d3KDUpLmNydDANBgkqhkiG9w0BAQUFAAOCAgEAj8LRXO8UEXcX
+Ywc8THxo2v6GSuIgzD+wJz3R4qzIi0im5Fn3OgatfVLx9mVhliEirmi+L3reswz1
+6cXd+GWCXctsPgw3EXQVCXhVvSYSu9aVdNO89XYJKmrfNsSOVtUfIN9/gjDXQ6to
+IotqWsWb0J2NCwxQhX7MWoAHiwNOv71fbFYPBaniVMOl01JcX00L3QX4URIDIW+c
+bJeYKsHBEby9G67741dfTx8AnuKkUdP3rAk3WKUJIdFy0LLBi9tN3BPRVFhNK8Ct
++lMZNbEVqEJkt+3HH6V5qA041FC/9Fr/L+m/P3045fsgDNRO4C8dRXr7KC8xSG/M
+blxoQvrqyAsBMOwQJkI4I6nDGbjZcBpoLJLLn3PmzP8zI+7bXrV/BVg/UMUcCBj0
+6y9iqlP3oc3e4+uCHBpna6FMp2hxQNFlO0EYnEnjcfvrTYOT00fmZELLtjUc+zQO
+oSj7jKGnHwEoUeVxlDec3EFbfH7pLCNnlJ1z319AeaONlTDMUxcIvFCG8/wQGYH8
+9Fpu89yimnV7w6ygUe0ytljfT46RU2rSqhtd5lO4iaOeiaHjKeCzbOsazG9aqsLi
+9h5FKe/WwkOxO60+JvyBl1xI/WJZNJLJ+7mh10IF+xn2fjL7KTTVh2blBB3IPhD6
+pnj1Hn3eGjp4fNwqcQajLW8FVSOLkO8=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert92[] = {
+ 0x30, 0x82, 0x06, 0x13, 0x30, 0x82, 0x03, 0xfb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0a, 0x61, 0x03, 0x33, 0x36, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x30, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x05, 0x05, 0x00, 0x30, 0x27, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x13, 0x1c, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f,
+ 0x66, 0x74, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20,
+ 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17,
+ 0x0d, 0x31, 0x30, 0x30, 0x35, 0x31, 0x39, 0x32, 0x32, 0x31, 0x33, 0x33,
+ 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, 0x31, 0x39, 0x32, 0x32,
+ 0x32, 0x33, 0x33, 0x30, 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19,
+ 0x16, 0x03, 0x63, 0x6f, 0x6d, 0x31, 0x19, 0x30, 0x17, 0x06, 0x0a, 0x09,
+ 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x09, 0x6d,
+ 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x31, 0x14, 0x30, 0x12,
+ 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19,
+ 0x16, 0x04, 0x63, 0x6f, 0x72, 0x70, 0x31, 0x17, 0x30, 0x15, 0x06, 0x0a,
+ 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x07,
+ 0x72, 0x65, 0x64, 0x6d, 0x6f, 0x6e, 0x64, 0x31, 0x2a, 0x30, 0x28, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x21, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
+ 0x6f, 0x66, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x74, 0x79, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+ 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xea, 0x9f, 0x5f, 0x91, 0x0b, 0xcd, 0x19, 0x82, 0x5f, 0x91, 0xea, 0xab,
+ 0xf5, 0x8b, 0x28, 0xd8, 0x8b, 0xf5, 0x1c, 0xe0, 0x91, 0xc9, 0xbc, 0xcd,
+ 0x02, 0x10, 0x50, 0x22, 0xb7, 0x38, 0x0a, 0x5c, 0xcf, 0x71, 0x0c, 0x58,
+ 0x2d, 0x88, 0x6c, 0xa8, 0xb8, 0x3c, 0x33, 0x63, 0xf9, 0x73, 0x9d, 0x3c,
+ 0xe9, 0xc3, 0x79, 0xed, 0xf2, 0xfe, 0xc9, 0xcb, 0xc3, 0x6e, 0x24, 0xe2,
+ 0x3c, 0x42, 0x70, 0xd8, 0x5f, 0xb7, 0x5b, 0xf7, 0x9b, 0x5f, 0xf5, 0x27,
+ 0x6f, 0x78, 0x00, 0xeb, 0x96, 0x5d, 0xb7, 0x6f, 0xcf, 0xe4, 0x41, 0x04,
+ 0xf0, 0xbb, 0x43, 0xbd, 0x6f, 0x5f, 0x26, 0x0f, 0xb7, 0x8e, 0x37, 0x41,
+ 0x13, 0x54, 0x67, 0x1b, 0x90, 0x00, 0x27, 0x38, 0xb8, 0x1a, 0xc3, 0x96,
+ 0x6d, 0x1c, 0x31, 0x35, 0x35, 0x49, 0xc5, 0x46, 0x1e, 0xe7, 0x73, 0xa4,
+ 0xca, 0x03, 0x11, 0x79, 0x41, 0x81, 0xaf, 0xd3, 0x8e, 0x46, 0xa2, 0xc5,
+ 0xbe, 0x00, 0x53, 0x05, 0xb9, 0x38, 0x9c, 0xb7, 0x60, 0x29, 0xb3, 0xca,
+ 0x52, 0x9a, 0x92, 0xc5, 0x53, 0x27, 0xb6, 0x41, 0x0d, 0x40, 0xf8, 0x2f,
+ 0x9b, 0xe7, 0x81, 0x49, 0x1a, 0x5a, 0x6a, 0xa8, 0x4f, 0x71, 0xc7, 0xe8,
+ 0x6d, 0x81, 0xbe, 0x27, 0xef, 0xc9, 0xd6, 0xc6, 0x92, 0x2b, 0x10, 0xe4,
+ 0x36, 0x35, 0x40, 0x08, 0xd0, 0x4d, 0x70, 0xfd, 0x70, 0x9b, 0x20, 0x1c,
+ 0xb3, 0xb9, 0xdf, 0x75, 0x9d, 0x2b, 0x77, 0xd0, 0xc4, 0xcd, 0x6a, 0x71,
+ 0xef, 0x5a, 0x58, 0x0b, 0xf9, 0x70, 0x85, 0x88, 0x05, 0x89, 0x6d, 0x66,
+ 0x92, 0x30, 0xab, 0xaf, 0x88, 0x39, 0xd7, 0xd4, 0x2d, 0x0b, 0x96, 0x9c,
+ 0x78, 0x24, 0xaf, 0x00, 0xab, 0xcf, 0x09, 0x3e, 0x13, 0xae, 0x6b, 0xc3,
+ 0xe0, 0xe1, 0xcf, 0x60, 0x7f, 0x8b, 0x53, 0xdc, 0x02, 0xd0, 0xf3, 0xb0,
+ 0x86, 0x11, 0xde, 0xbd, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01,
+ 0xda, 0x30, 0x82, 0x01, 0xd6, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x08, 0x42, 0xe3, 0xdb, 0x4e, 0x11, 0x66, 0xf3, 0xb5, 0x08, 0xc5, 0x40,
+ 0xdb, 0x55, 0x7c, 0x33, 0x46, 0x11, 0x83, 0x38, 0x30, 0x0b, 0x06, 0x03,
+ 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x12, 0x06,
+ 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01, 0x04, 0x05,
+ 0x02, 0x03, 0x08, 0x00, 0x08, 0x30, 0x23, 0x06, 0x09, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x82, 0x37, 0x15, 0x02, 0x04, 0x16, 0x04, 0x14, 0x7e, 0x8a,
+ 0xc2, 0x9c, 0x5a, 0x32, 0x8c, 0xc2, 0x71, 0xa2, 0xd9, 0x4f, 0x75, 0x70,
+ 0xf7, 0xa9, 0x1b, 0xf6, 0x94, 0x05, 0x30, 0x19, 0x06, 0x09, 0x2b, 0x06,
+ 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02, 0x04, 0x0c, 0x1e, 0x0a, 0x00,
+ 0x53, 0x00, 0x75, 0x00, 0x62, 0x00, 0x43, 0x00, 0x41, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x33, 0x21,
+ 0xf0, 0xcb, 0xfe, 0xa2, 0xa0, 0x44, 0x92, 0xde, 0xf6, 0x3b, 0x33, 0xd8,
+ 0x5f, 0x01, 0x4b, 0x97, 0x78, 0x5d, 0x30, 0x81, 0xa3, 0x06, 0x03, 0x55,
+ 0x1d, 0x1f, 0x04, 0x81, 0x9b, 0x30, 0x81, 0x98, 0x30, 0x81, 0x95, 0xa0,
+ 0x81, 0x92, 0xa0, 0x81, 0x8f, 0x86, 0x36, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x6d, 0x73, 0x63, 0x72, 0x6c, 0x2e, 0x6d, 0x69, 0x63, 0x72,
+ 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b,
+ 0x69, 0x2f, 0x6d, 0x73, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x72, 0x6c,
+ 0x2f, 0x6d, 0x73, 0x77, 0x77, 0x77, 0x28, 0x35, 0x29, 0x2e, 0x63, 0x72,
+ 0x6c, 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6b, 0x69, 0x2f, 0x6d, 0x73, 0x63, 0x6f,
+ 0x72, 0x70, 0x2f, 0x63, 0x72, 0x6c, 0x2f, 0x6d, 0x73, 0x77, 0x77, 0x77,
+ 0x28, 0x35, 0x29, 0x2e, 0x63, 0x72, 0x6c, 0x86, 0x1f, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x6f, 0x72, 0x70, 0x70, 0x6b, 0x69, 0x2f,
+ 0x63, 0x72, 0x6c, 0x2f, 0x6d, 0x73, 0x77, 0x77, 0x77, 0x28, 0x35, 0x29,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x79, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x6d, 0x30, 0x6b, 0x30, 0x3c, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x30, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63,
+ 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70,
+ 0x6b, 0x69, 0x2f, 0x6d, 0x73, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x6d, 0x73,
+ 0x77, 0x77, 0x77, 0x28, 0x35, 0x29, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x2b,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x1f,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x6f, 0x72, 0x70, 0x70,
+ 0x6b, 0x69, 0x2f, 0x61, 0x69, 0x61, 0x2f, 0x6d, 0x73, 0x77, 0x77, 0x77,
+ 0x28, 0x35, 0x29, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82,
+ 0x02, 0x01, 0x00, 0x8f, 0xc2, 0xd1, 0x5c, 0xef, 0x14, 0x11, 0x77, 0x17,
+ 0x63, 0x07, 0x3c, 0x4c, 0x7c, 0x68, 0xda, 0xfe, 0x86, 0x4a, 0xe2, 0x20,
+ 0xcc, 0x3f, 0xb0, 0x27, 0x3d, 0xd1, 0xe2, 0xac, 0xc8, 0x8b, 0x48, 0xa6,
+ 0xe4, 0x59, 0xf7, 0x3a, 0x06, 0xad, 0x7d, 0x52, 0xf1, 0xf6, 0x65, 0x61,
+ 0x96, 0x21, 0x22, 0xae, 0x68, 0xbe, 0x2f, 0x7a, 0xde, 0xb3, 0x0c, 0xf5,
+ 0xe9, 0xc5, 0xdd, 0xf8, 0x65, 0x82, 0x5d, 0xcb, 0x6c, 0x3e, 0x0c, 0x37,
+ 0x11, 0x74, 0x15, 0x09, 0x78, 0x55, 0xbd, 0x26, 0x12, 0xbb, 0xd6, 0x95,
+ 0x74, 0xd3, 0xbc, 0xf5, 0x76, 0x09, 0x2a, 0x6a, 0xdf, 0x36, 0xc4, 0x8e,
+ 0x56, 0xd5, 0x1f, 0x20, 0xdf, 0x7f, 0x82, 0x30, 0xd7, 0x43, 0xab, 0x68,
+ 0x22, 0x8b, 0x6a, 0x5a, 0xc5, 0x9b, 0xd0, 0x9d, 0x8d, 0x0b, 0x0c, 0x50,
+ 0x85, 0x7e, 0xcc, 0x5a, 0x80, 0x07, 0x8b, 0x03, 0x4e, 0xbf, 0xbd, 0x5f,
+ 0x6c, 0x56, 0x0f, 0x05, 0xa9, 0xe2, 0x54, 0xc3, 0xa5, 0xd3, 0x52, 0x5c,
+ 0x5f, 0x4d, 0x0b, 0xdd, 0x05, 0xf8, 0x51, 0x12, 0x03, 0x21, 0x6f, 0x9c,
+ 0x6c, 0x97, 0x98, 0x2a, 0xc1, 0xc1, 0x11, 0xbc, 0xbd, 0x1b, 0xae, 0xfb,
+ 0xe3, 0x57, 0x5f, 0x4f, 0x1f, 0x00, 0x9e, 0xe2, 0xa4, 0x51, 0xd3, 0xf7,
+ 0xac, 0x09, 0x37, 0x58, 0xa5, 0x09, 0x21, 0xd1, 0x72, 0xd0, 0xb2, 0xc1,
+ 0x8b, 0xdb, 0x4d, 0xdc, 0x13, 0xd1, 0x54, 0x58, 0x4d, 0x2b, 0xc0, 0xad,
+ 0xfa, 0x53, 0x19, 0x35, 0xb1, 0x15, 0xa8, 0x42, 0x64, 0xb7, 0xed, 0xc7,
+ 0x1f, 0xa5, 0x79, 0xa8, 0x0d, 0x38, 0xd4, 0x50, 0xbf, 0xf4, 0x5a, 0xff,
+ 0x2f, 0xe9, 0xbf, 0x3f, 0x7d, 0x38, 0xe5, 0xfb, 0x20, 0x0c, 0xd4, 0x4e,
+ 0xe0, 0x2f, 0x1d, 0x45, 0x7a, 0xfb, 0x28, 0x2f, 0x31, 0x48, 0x6f, 0xcc,
+ 0x6e, 0x5c, 0x68, 0x42, 0xfa, 0xea, 0xc8, 0x0b, 0x01, 0x30, 0xec, 0x10,
+ 0x26, 0x42, 0x38, 0x23, 0xa9, 0xc3, 0x19, 0xb8, 0xd9, 0x70, 0x1a, 0x68,
+ 0x2c, 0x92, 0xcb, 0x9f, 0x73, 0xe6, 0xcc, 0xff, 0x33, 0x23, 0xee, 0xdb,
+ 0x5e, 0xb5, 0x7f, 0x05, 0x58, 0x3f, 0x50, 0xc5, 0x1c, 0x08, 0x18, 0xf4,
+ 0xeb, 0x2f, 0x62, 0xaa, 0x53, 0xf7, 0xa1, 0xcd, 0xde, 0xe3, 0xeb, 0x82,
+ 0x1c, 0x1a, 0x67, 0x6b, 0xa1, 0x4c, 0xa7, 0x68, 0x71, 0x40, 0xd1, 0x65,
+ 0x3b, 0x41, 0x18, 0x9c, 0x49, 0xe3, 0x71, 0xfb, 0xeb, 0x4d, 0x83, 0x93,
+ 0xd3, 0x47, 0xe6, 0x64, 0x42, 0xcb, 0xb6, 0x35, 0x1c, 0xfb, 0x34, 0x0e,
+ 0xa1, 0x28, 0xfb, 0x8c, 0xa1, 0xa7, 0x1f, 0x01, 0x28, 0x51, 0xe5, 0x71,
+ 0x94, 0x37, 0x9c, 0xdc, 0x41, 0x5b, 0x7c, 0x7e, 0xe9, 0x2c, 0x23, 0x67,
+ 0x94, 0x9d, 0x73, 0xdf, 0x5f, 0x40, 0x79, 0xa3, 0x8d, 0x95, 0x30, 0xcc,
+ 0x53, 0x17, 0x08, 0xbc, 0x50, 0x86, 0xf3, 0xfc, 0x10, 0x19, 0x81, 0xfc,
+ 0xf4, 0x5a, 0x6e, 0xf3, 0xdc, 0xa2, 0x9a, 0x75, 0x7b, 0xc3, 0xac, 0xa0,
+ 0x51, 0xed, 0x32, 0xb6, 0x58, 0xdf, 0x4f, 0x8e, 0x91, 0x53, 0x6a, 0xd2,
+ 0xaa, 0x1b, 0x5d, 0xe6, 0x53, 0xb8, 0x89, 0xa3, 0x9e, 0x89, 0xa1, 0xe3,
+ 0x29, 0xe0, 0xb3, 0x6c, 0xeb, 0x1a, 0xcc, 0x6f, 0x5a, 0xaa, 0xc2, 0xe2,
+ 0xf6, 0x1e, 0x45, 0x29, 0xef, 0xd6, 0xc2, 0x43, 0xb1, 0x3b, 0xad, 0x3e,
+ 0x26, 0xfc, 0x81, 0x97, 0x5c, 0x48, 0xfd, 0x62, 0x59, 0x34, 0x92, 0xc9,
+ 0xfb, 0xb9, 0xa1, 0xd7, 0x42, 0x05, 0xfb, 0x19, 0xf6, 0x7e, 0x32, 0xfb,
+ 0x29, 0x34, 0xd5, 0x87, 0x66, 0xe5, 0x04, 0x1d, 0xc8, 0x3e, 0x10, 0xfa,
+ 0xa6, 0x78, 0xf5, 0x1e, 0x7d, 0xde, 0x1a, 0x3a, 0x78, 0x7c, 0xdc, 0x2a,
+ 0x71, 0x06, 0xa3, 0x2d, 0x6f, 0x05, 0x55, 0x23, 0x8b, 0x90, 0xef,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 2c:48:dd:93:0d:f5:59:8e:f9:3c:99:54:7a:60:ed:43
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Nov 8 00:00:00 2006 GMT
+ Not After : Nov 7 23:59:59 2016 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)06, CN=VeriSign Class 3 Extended Validation SSL SGC CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bd:56:88:ba:88:34:64:64:cf:cd:ca:b0:ee:e7:
+ 19:73:c5:72:d9:bb:45:bc:b5:a8:ff:83:be:1c:03:
+ db:ed:89:b7:2e:10:1a:25:bc:55:ca:41:a1:9f:0b:
+ cf:19:5e:70:b9:5e:39:4b:9e:31:1c:5f:87:ae:2a:
+ aa:a8:2b:a2:1b:3b:10:23:5f:13:b1:dd:08:8c:4e:
+ 14:da:83:81:e3:b5:8c:e3:68:ed:24:67:ce:56:b6:
+ ac:9b:73:96:44:db:8a:8c:b3:d6:f0:71:93:8e:db:
+ 71:54:4a:eb:73:59:6a:8f:70:51:2c:03:9f:97:d1:
+ cc:11:7a:bc:62:0d:95:2a:c9:1c:75:57:e9:f5:c7:
+ ea:ba:84:35:cb:c7:85:5a:7e:e4:4d:e1:11:97:7d:
+ 0e:20:34:45:db:f1:a2:09:eb:eb:3d:9e:b8:96:43:
+ 5e:34:4b:08:25:1e:43:1a:a2:d9:b7:8a:01:34:3d:
+ c3:f8:e5:af:4f:8c:ff:cd:65:f0:23:4e:c5:97:b3:
+ 5c:da:90:1c:82:85:0d:06:0d:c1:22:b6:7b:28:a4:
+ 03:c3:4c:53:d1:58:bc:72:bc:08:39:fc:a0:76:a8:
+ a8:e9:4b:6e:88:3d:e3:b3:31:25:8c:73:29:48:0e:
+ 32:79:06:ed:3d:43:f4:f6:e4:e9:fc:7d:be:8e:08:
+ d5:1f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 4E:43:C8:1D:76:EF:37:53:7A:4F:F2:58:6F:94:F3:38:E2:D5:BD:DF
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: X509v3 Any Policy
+ CPS: https://www.verisign.com/cps
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://EVSecure-crl.verisign.com/pca3-g5.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Netscape Cert Type:
+ SSL CA, S/MIME CA
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=Class3CA2048-1-48
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Authority Information Access:
+ OCSP - URI:http://EVSecure-ocsp.verisign.com
+
+ X509v3 Extended Key Usage:
+ Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1, TLS Web Server Authentication, TLS Web Client Authentication
+ Signature Algorithm: sha1WithRSAEncryption
+ 27:74:a6:34:ea:1d:9d:e1:53:d6:1c:9d:0c:a7:5b:4c:a9:67:
+ f2:f0:32:b7:01:0f:fb:42:18:38:de:e4:ee:49:c8:13:c9:0b:
+ ec:04:c3:40:71:18:72:76:43:02:23:5d:ab:7b:c8:48:14:1a:
+ c8:7b:1d:fc:f6:0a:9f:36:a1:d2:09:73:71:66:96:75:51:34:
+ bf:99:30:51:67:9d:54:b7:26:45:ac:73:08:23:86:26:99:71:
+ f4:8e:d7:ea:39:9b:06:09:23:bf:62:dd:a8:c4:b6:7d:a4:89:
+ 07:3e:f3:6d:ae:40:59:50:79:97:37:3d:32:78:7d:b2:63:4b:
+ f9:ea:08:69:0e:13:ed:e8:cf:bb:ac:05:86:ca:22:cf:88:62:
+ 5d:3c:22:49:d8:63:d5:24:a6:bd:ef:5c:e3:cc:20:3b:22:ea:
+ fc:44:c6:a8:e5:1f:e1:86:cd:0c:4d:8f:93:53:d9:7f:ee:a1:
+ 08:a7:b3:30:96:49:70:6e:a3:6c:3d:d0:63:ef:25:66:63:cc:
+ aa:b7:18:17:4e:ea:70:76:f6:ba:42:a6:80:37:09:4e:9f:66:
+ 88:2e:6b:33:66:c8:c0:71:a4:41:eb:5a:e3:fc:14:2e:4b:88:
+ fd:ae:6e:5b:65:e9:27:e4:bf:e4:b0:23:c1:b2:7d:5b:62:25:
+ d7:3e:10:d4
+-----BEGIN CERTIFICATE-----
+MIIGHjCCBQagAwIBAgIQLEjdkw31WY75PJlUemDtQzANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMTYxMTA3MjM1OTU5WjCBvjEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMvVmVy
+aVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9Voi6iDRkZM/NyrDu5xlzxXLZ
+u0W8taj/g74cA9vtibcuEBolvFXKQaGfC88ZXnC5XjlLnjEcX4euKqqoK6IbOxAj
+XxOx3QiMThTag4HjtYzjaO0kZ85Wtqybc5ZE24qMs9bwcZOO23FUSutzWWqPcFEs
+A5+X0cwRerxiDZUqyRx1V+n1x+q6hDXLx4VafuRN4RGXfQ4gNEXb8aIJ6+s9nriW
+Q140SwglHkMaotm3igE0PcP45a9PjP/NZfAjTsWXs1zakByChQ0GDcEitnsopAPD
+TFPRWLxyvAg5/KB2qKjpS26IPeOzMSWMcylIDjJ5Bu09Q/T25On8fb6OCNUfAgMB
+AAGjggIIMIICBDAdBgNVHQ4EFgQUTkPIHXbvN1N6T/JYb5TzOOLVvd8wEgYDVR0T
+AQH/BAgwBgEB/wIBADA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczA9BgNVHR8ENjA0MDKgMKAuhixo
+dHRwOi8vRVZTZWN1cmUtY3JsLnZlcmlzaWduLmNvbS9wY2EzLWc1LmNybDAOBgNV
+HQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgEGMG0GCCsGAQUFBwEMBGEwX6Fd
+oFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrU
+SBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMCkG
+A1UdEQQiMCCkHjAcMRowGAYDVQQDExFDbGFzczNDQTIwNDgtMS00ODAfBgNVHSME
+GDAWgBR/02Wnwt3su/AwCfNDOfoCrzMxMzA9BggrBgEFBQcBAQQxMC8wLQYIKwYB
+BQUHMAGGIWh0dHA6Ly9FVlNlY3VyZS1vY3NwLnZlcmlzaWduLmNvbTA0BgNVHSUE
+LTArBglghkgBhvhCBAEGCmCGSAGG+EUBCAEGCCsGAQUFBwMBBggrBgEFBQcDAjAN
+BgkqhkiG9w0BAQUFAAOCAQEAJ3SmNOodneFT1hydDKdbTKln8vAytwEP+0IYON7k
+7knIE8kL7ATDQHEYcnZDAiNdq3vISBQayHsd/PYKnzah0glzcWaWdVE0v5kwUWed
+VLcmRaxzCCOGJplx9I7X6jmbBgkjv2LdqMS2faSJBz7zba5AWVB5lzc9Mnh9smNL
++eoIaQ4T7ejPu6wFhsoiz4hiXTwiSdhj1SSmve9c48wgOyLq/ETGqOUf4YbNDE2P
+k1PZf+6hCKezMJZJcG6jbD3QY+8lZmPMqrcYF07qcHb2ukKmgDcJTp9miC5rM2bI
+wHGkQeta4/wULkuI/a5uW2XpJ+S/5LAjwbJ9W2Il1z4Q1A==
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert93[] = {
+ 0x30, 0x82, 0x06, 0x1e, 0x30, 0x82, 0x05, 0x06, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x2c, 0x48, 0xdd, 0x93, 0x0d, 0xf5, 0x59, 0x8e, 0xf9,
+ 0x3c, 0x99, 0x54, 0x7a, 0x60, 0xed, 0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbe, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30, 0x36, 0x31, 0x38,
+ 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x56,
+ 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x53,
+ 0x4c, 0x20, 0x53, 0x47, 0x43, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0x56, 0x88, 0xba, 0x88, 0x34, 0x64,
+ 0x64, 0xcf, 0xcd, 0xca, 0xb0, 0xee, 0xe7, 0x19, 0x73, 0xc5, 0x72, 0xd9,
+ 0xbb, 0x45, 0xbc, 0xb5, 0xa8, 0xff, 0x83, 0xbe, 0x1c, 0x03, 0xdb, 0xed,
+ 0x89, 0xb7, 0x2e, 0x10, 0x1a, 0x25, 0xbc, 0x55, 0xca, 0x41, 0xa1, 0x9f,
+ 0x0b, 0xcf, 0x19, 0x5e, 0x70, 0xb9, 0x5e, 0x39, 0x4b, 0x9e, 0x31, 0x1c,
+ 0x5f, 0x87, 0xae, 0x2a, 0xaa, 0xa8, 0x2b, 0xa2, 0x1b, 0x3b, 0x10, 0x23,
+ 0x5f, 0x13, 0xb1, 0xdd, 0x08, 0x8c, 0x4e, 0x14, 0xda, 0x83, 0x81, 0xe3,
+ 0xb5, 0x8c, 0xe3, 0x68, 0xed, 0x24, 0x67, 0xce, 0x56, 0xb6, 0xac, 0x9b,
+ 0x73, 0x96, 0x44, 0xdb, 0x8a, 0x8c, 0xb3, 0xd6, 0xf0, 0x71, 0x93, 0x8e,
+ 0xdb, 0x71, 0x54, 0x4a, 0xeb, 0x73, 0x59, 0x6a, 0x8f, 0x70, 0x51, 0x2c,
+ 0x03, 0x9f, 0x97, 0xd1, 0xcc, 0x11, 0x7a, 0xbc, 0x62, 0x0d, 0x95, 0x2a,
+ 0xc9, 0x1c, 0x75, 0x57, 0xe9, 0xf5, 0xc7, 0xea, 0xba, 0x84, 0x35, 0xcb,
+ 0xc7, 0x85, 0x5a, 0x7e, 0xe4, 0x4d, 0xe1, 0x11, 0x97, 0x7d, 0x0e, 0x20,
+ 0x34, 0x45, 0xdb, 0xf1, 0xa2, 0x09, 0xeb, 0xeb, 0x3d, 0x9e, 0xb8, 0x96,
+ 0x43, 0x5e, 0x34, 0x4b, 0x08, 0x25, 0x1e, 0x43, 0x1a, 0xa2, 0xd9, 0xb7,
+ 0x8a, 0x01, 0x34, 0x3d, 0xc3, 0xf8, 0xe5, 0xaf, 0x4f, 0x8c, 0xff, 0xcd,
+ 0x65, 0xf0, 0x23, 0x4e, 0xc5, 0x97, 0xb3, 0x5c, 0xda, 0x90, 0x1c, 0x82,
+ 0x85, 0x0d, 0x06, 0x0d, 0xc1, 0x22, 0xb6, 0x7b, 0x28, 0xa4, 0x03, 0xc3,
+ 0x4c, 0x53, 0xd1, 0x58, 0xbc, 0x72, 0xbc, 0x08, 0x39, 0xfc, 0xa0, 0x76,
+ 0xa8, 0xa8, 0xe9, 0x4b, 0x6e, 0x88, 0x3d, 0xe3, 0xb3, 0x31, 0x25, 0x8c,
+ 0x73, 0x29, 0x48, 0x0e, 0x32, 0x79, 0x06, 0xed, 0x3d, 0x43, 0xf4, 0xf6,
+ 0xe4, 0xe9, 0xfc, 0x7d, 0xbe, 0x8e, 0x08, 0xd5, 0x1f, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x82, 0x02, 0x08, 0x30, 0x82, 0x02, 0x04, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x4e, 0x43, 0xc8,
+ 0x1d, 0x76, 0xef, 0x37, 0x53, 0x7a, 0x4f, 0xf2, 0x58, 0x6f, 0x94, 0xf3,
+ 0x38, 0xe2, 0xd5, 0xbd, 0xdf, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13,
+ 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01,
+ 0x00, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, 0x30, 0x34,
+ 0x30, 0x32, 0x06, 0x04, 0x55, 0x1d, 0x20, 0x00, 0x30, 0x2a, 0x30, 0x28,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04,
+ 0x36, 0x30, 0x34, 0x30, 0x32, 0xa0, 0x30, 0xa0, 0x2e, 0x86, 0x2c, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x65, 0x2d, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73,
+ 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33,
+ 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30,
+ 0x11, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01,
+ 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d,
+ 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d,
+ 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30,
+ 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5,
+ 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4,
+ 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76,
+ 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x29, 0x06,
+ 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20, 0xa4, 0x1e, 0x30, 0x1c,
+ 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x11, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x33, 0x43, 0x41, 0x32, 0x30, 0x34, 0x38, 0x2d,
+ 0x31, 0x2d, 0x34, 0x38, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec,
+ 0xbb, 0xf0, 0x30, 0x09, 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31,
+ 0x33, 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01,
+ 0x01, 0x04, 0x31, 0x30, 0x2f, 0x30, 0x2d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x45, 0x56, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2d, 0x6f,
+ 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04,
+ 0x2d, 0x30, 0x2b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42,
+ 0x04, 0x01, 0x06, 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01,
+ 0x08, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x27, 0x74, 0xa6, 0x34, 0xea, 0x1d,
+ 0x9d, 0xe1, 0x53, 0xd6, 0x1c, 0x9d, 0x0c, 0xa7, 0x5b, 0x4c, 0xa9, 0x67,
+ 0xf2, 0xf0, 0x32, 0xb7, 0x01, 0x0f, 0xfb, 0x42, 0x18, 0x38, 0xde, 0xe4,
+ 0xee, 0x49, 0xc8, 0x13, 0xc9, 0x0b, 0xec, 0x04, 0xc3, 0x40, 0x71, 0x18,
+ 0x72, 0x76, 0x43, 0x02, 0x23, 0x5d, 0xab, 0x7b, 0xc8, 0x48, 0x14, 0x1a,
+ 0xc8, 0x7b, 0x1d, 0xfc, 0xf6, 0x0a, 0x9f, 0x36, 0xa1, 0xd2, 0x09, 0x73,
+ 0x71, 0x66, 0x96, 0x75, 0x51, 0x34, 0xbf, 0x99, 0x30, 0x51, 0x67, 0x9d,
+ 0x54, 0xb7, 0x26, 0x45, 0xac, 0x73, 0x08, 0x23, 0x86, 0x26, 0x99, 0x71,
+ 0xf4, 0x8e, 0xd7, 0xea, 0x39, 0x9b, 0x06, 0x09, 0x23, 0xbf, 0x62, 0xdd,
+ 0xa8, 0xc4, 0xb6, 0x7d, 0xa4, 0x89, 0x07, 0x3e, 0xf3, 0x6d, 0xae, 0x40,
+ 0x59, 0x50, 0x79, 0x97, 0x37, 0x3d, 0x32, 0x78, 0x7d, 0xb2, 0x63, 0x4b,
+ 0xf9, 0xea, 0x08, 0x69, 0x0e, 0x13, 0xed, 0xe8, 0xcf, 0xbb, 0xac, 0x05,
+ 0x86, 0xca, 0x22, 0xcf, 0x88, 0x62, 0x5d, 0x3c, 0x22, 0x49, 0xd8, 0x63,
+ 0xd5, 0x24, 0xa6, 0xbd, 0xef, 0x5c, 0xe3, 0xcc, 0x20, 0x3b, 0x22, 0xea,
+ 0xfc, 0x44, 0xc6, 0xa8, 0xe5, 0x1f, 0xe1, 0x86, 0xcd, 0x0c, 0x4d, 0x8f,
+ 0x93, 0x53, 0xd9, 0x7f, 0xee, 0xa1, 0x08, 0xa7, 0xb3, 0x30, 0x96, 0x49,
+ 0x70, 0x6e, 0xa3, 0x6c, 0x3d, 0xd0, 0x63, 0xef, 0x25, 0x66, 0x63, 0xcc,
+ 0xaa, 0xb7, 0x18, 0x17, 0x4e, 0xea, 0x70, 0x76, 0xf6, 0xba, 0x42, 0xa6,
+ 0x80, 0x37, 0x09, 0x4e, 0x9f, 0x66, 0x88, 0x2e, 0x6b, 0x33, 0x66, 0xc8,
+ 0xc0, 0x71, 0xa4, 0x41, 0xeb, 0x5a, 0xe3, 0xfc, 0x14, 0x2e, 0x4b, 0x88,
+ 0xfd, 0xae, 0x6e, 0x5b, 0x65, 0xe9, 0x27, 0xe4, 0xbf, 0xe4, 0xb0, 0x23,
+ 0xc1, 0xb2, 0x7d, 0x5b, 0x62, 0x25, 0xd7, 0x3e, 0x10, 0xd4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 64:1b:e8:20:ce:02:08:13:f3:2d:4d:2d:95:d6:7e:67
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=(c) 2006 VeriSign, Inc. - For authorized use only, CN=VeriSign Class 3 Public Primary Certification Authority - G5
+ Validity
+ Not Before: Feb 8 00:00:00 2010 GMT
+ Not After : Feb 7 23:59:59 2020 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)10, CN=VeriSign Class 3 International Server CA - G3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:99:d6:9c:62:f0:15:f4:81:9a:41:08:59:8f:13:
+ 9d:17:c9:9f:51:dc:da:b1:52:ef:ff:e3:41:dd:e0:
+ df:c4:28:c6:e3:ad:79:1f:27:10:98:b8:bb:20:97:
+ c1:28:44:41:0f:ea:a9:a8:52:cf:4d:4e:1b:8b:bb:
+ b5:c4:76:d9:cc:56:06:ee:b3:55:20:2a:de:15:8d:
+ 71:cb:54:c8:6f:17:cd:89:00:e4:dc:ff:e1:c0:1f:
+ 68:71:e9:c7:29:2e:7e:bc:3b:fc:e5:bb:ab:26:54:
+ 8b:66:90:cd:f6:92:b9:31:24:80:bc:9e:6c:d5:fc:
+ 7e:d2:e1:4b:8c:dc:42:fa:44:4b:5f:f8:18:b5:2e:
+ 30:f4:3d:12:98:d3:62:05:73:54:a6:9c:a2:1d:be:
+ 52:83:3a:07:46:c4:3b:02:56:21:bf:f2:51:4f:d0:
+ a6:99:39:e9:ae:a5:3f:89:9b:9c:7d:fe:4d:60:07:
+ 25:20:f7:bb:d7:69:83:2b:82:93:43:37:d9:83:41:
+ 1b:6b:0b:ab:4a:66:84:4f:4a:8e:de:7e:34:99:8e:
+ 68:d6:ca:39:06:9b:4c:b3:9a:48:4d:13:46:b4:58:
+ 21:04:c4:fb:a0:4d:ac:2e:4b:62:12:e3:fb:4d:f6:
+ c9:51:00:01:1f:fc:1e:6a:81:2a:38:e0:b9:4f:d6:
+ 2d:45
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.23.3
+ CPS: https://www.verisign.com/cps
+ User Notice:
+ Explicit Text: https://www.verisign.com/rpa
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, 2.16.840.1.113733.1.8.1
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g5.crl
+
+ X509v3 Subject Alternative Name:
+ DirName:/CN=VeriSignMPKI-2-7
+ X509v3 Subject Key Identifier:
+ D7:9B:7C:D8:22:A0:15:F7:DD:AD:5F:CE:29:9B:58:C3:BC:46:00:B5
+ X509v3 Authority Key Identifier:
+ keyid:7F:D3:65:A7:C2:DD:EC:BB:F0:30:09:F3:43:39:FA:02:AF:33:31:33
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 71:b5:7d:73:52:4a:dd:d7:4d:34:2b:2e:af:94:46:a5:49:50:
+ 02:4f:f8:2f:17:70:f2:13:dc:1f:21:86:aa:c2:4f:7c:37:3c:
+ d4:46:78:ae:5d:78:6f:d1:ba:5a:bc:10:ab:58:36:c5:8c:62:
+ 15:45:60:17:21:e2:d5:42:a8:77:a1:55:d8:43:04:51:f6:6e:
+ ba:48:e6:5d:4c:b7:44:d3:3e:a4:d5:d6:33:9a:9f:0d:e6:d7:
+ 4e:96:44:95:5a:6c:d6:a3:16:53:0e:98:43:ce:a4:b8:c3:66:
+ 7a:05:5c:62:10:e8:1b:12:db:7d:2e:76:50:ff:df:d7:6b:1b:
+ cc:8a:cc:71:fa:b3:40:56:7c:33:7a:77:94:5b:f5:0b:53:fb:
+ 0e:5f:bc:68:fb:af:2a:ee:30:37:79:16:93:25:7f:4d:10:ff:
+ 57:fb:bf:6e:3b:33:21:de:79:dc:86:17:59:2d:43:64:b7:a6:
+ 66:87:ea:bc:96:46:19:1a:86:8b:6f:d7:b7:49:00:5b:db:a3:
+ bf:29:9a:ee:f7:d3:33:ae:a3:f4:9e:4c:ca:5e:69:d4:1b:ad:
+ b7:90:77:6a:d8:59:6f:79:ab:01:fa:55:f0:8a:21:66:e5:65:
+ 6e:fd:7c:d3:df:1e:eb:7e:3f:06:90:fb:19:0b:d3:06:02:1b:
+ 78:43:99:a8
+-----BEGIN CERTIFICATE-----
+MIIGKTCCBRGgAwIBAgIQZBvoIM4CCBPzLU0tldZ+ZzANBgkqhkiG9w0BAQUFADCB
+yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
+ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
+U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
+ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
+aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBvDEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
+ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
+aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDE2MDQGA1UEAxMtVmVy
+aVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZlciBDQSAtIEczMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmdacYvAV9IGaQQhZjxOdF8mfUdza
+sVLv/+NB3eDfxCjG4615HycQmLi7IJfBKERBD+qpqFLPTU4bi7u1xHbZzFYG7rNV
+ICreFY1xy1TIbxfNiQDk3P/hwB9ocenHKS5+vDv85burJlSLZpDN9pK5MSSAvJ5s
+1fx+0uFLjNxC+kRLX/gYtS4w9D0SmNNiBXNUppyiHb5SgzoHRsQ7AlYhv/JRT9Cm
+mTnprqU/iZucff5NYAclIPe712mDK4KTQzfZg0EbawurSmaET0qO3n40mY5o1so5
+BptMs5pITRNGtFghBMT7oE2sLktiEuP7TfbJUQABH/weaoEqOOC5T9YtRQIDAQAB
+o4ICFTCCAhEwEgYDVR0TAQH/BAgwBgEB/wIBADBwBgNVHSAEaTBnMGUGC2CGSAGG
++EUBBxcDMFYwKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9j
+cHMwKgYIKwYBBQUHAgIwHhocaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYTAO
+BgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv
+Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDov
+L2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwNAYDVR0lBC0wKwYIKwYBBQUH
+AwEGCCsGAQUFBwMCBglghkgBhvhCBAEGCmCGSAGG+EUBCAEwNAYIKwYBBQUHAQEE
+KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC52ZXJpc2lnbi5jb20wNAYDVR0f
+BC0wKzApoCegJYYjaHR0cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy1nNS5jcmww
+KAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFZlcmlTaWduTVBLSS0yLTcwHQYDVR0O
+BBYEFNebfNgioBX33a1fzimbWMO8RgC1MB8GA1UdIwQYMBaAFH/TZafC3ey78DAJ
+80M5+gKvMzEzMA0GCSqGSIb3DQEBBQUAA4IBAQBxtX1zUkrd1000Ky6vlEalSVAC
+T/gvF3DyE9wfIYaqwk98NzzURniuXXhv0bpavBCrWDbFjGIVRWAXIeLVQqh3oVXY
+QwRR9m66SOZdTLdE0z6k1dYzmp8N5tdOlkSVWmzWoxZTDphDzqS4w2Z6BVxiEOgb
+Ett9LnZQ/9/XaxvMisxx+rNAVnwzeneUW/ULU/sOX7xo+68q7jA3eRaTJX9NEP9X
++79uOzMh3nnchhdZLUNkt6Zmh+q8lkYZGoaLb9e3SQBb26O/KZru99MzrqP0nkzK
+XmnUG623kHdq2FlveasB+lXwiiFm5WVu/XzT3x7rfj8GkPsZC9MGAht4Q5mo
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert94[] = {
+ 0x30, 0x82, 0x06, 0x29, 0x30, 0x82, 0x05, 0x11, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x64, 0x1b, 0xe8, 0x20, 0xce, 0x02, 0x08, 0x13, 0xf3,
+ 0x2d, 0x4d, 0x2d, 0x95, 0xd6, 0x7e, 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28,
+ 0x63, 0x29, 0x20, 0x32, 0x30, 0x30, 0x36, 0x20, 0x56, 0x65, 0x72, 0x69,
+ 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d,
+ 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
+ 0x31, 0x45, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x3c, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73,
+ 0x73, 0x20, 0x33, 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50,
+ 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x20, 0x2d, 0x20, 0x47, 0x35, 0x30,
+ 0x1e, 0x17, 0x0d, 0x31, 0x30, 0x30, 0x32, 0x30, 0x38, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x32, 0x30, 0x37,
+ 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81, 0xbc, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0e, 0x56, 0x65,
+ 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e,
+ 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56,
+ 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73,
+ 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, 0x3b, 0x30,
+ 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54, 0x65, 0x72, 0x6d,
+ 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20, 0x61, 0x74, 0x20,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x31, 0x30, 0x31, 0x36,
+ 0x30, 0x34, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2d, 0x56, 0x65, 0x72,
+ 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20,
+ 0x33, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43,
+ 0x41, 0x20, 0x2d, 0x20, 0x47, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82,
+ 0x01, 0x01, 0x00, 0x99, 0xd6, 0x9c, 0x62, 0xf0, 0x15, 0xf4, 0x81, 0x9a,
+ 0x41, 0x08, 0x59, 0x8f, 0x13, 0x9d, 0x17, 0xc9, 0x9f, 0x51, 0xdc, 0xda,
+ 0xb1, 0x52, 0xef, 0xff, 0xe3, 0x41, 0xdd, 0xe0, 0xdf, 0xc4, 0x28, 0xc6,
+ 0xe3, 0xad, 0x79, 0x1f, 0x27, 0x10, 0x98, 0xb8, 0xbb, 0x20, 0x97, 0xc1,
+ 0x28, 0x44, 0x41, 0x0f, 0xea, 0xa9, 0xa8, 0x52, 0xcf, 0x4d, 0x4e, 0x1b,
+ 0x8b, 0xbb, 0xb5, 0xc4, 0x76, 0xd9, 0xcc, 0x56, 0x06, 0xee, 0xb3, 0x55,
+ 0x20, 0x2a, 0xde, 0x15, 0x8d, 0x71, 0xcb, 0x54, 0xc8, 0x6f, 0x17, 0xcd,
+ 0x89, 0x00, 0xe4, 0xdc, 0xff, 0xe1, 0xc0, 0x1f, 0x68, 0x71, 0xe9, 0xc7,
+ 0x29, 0x2e, 0x7e, 0xbc, 0x3b, 0xfc, 0xe5, 0xbb, 0xab, 0x26, 0x54, 0x8b,
+ 0x66, 0x90, 0xcd, 0xf6, 0x92, 0xb9, 0x31, 0x24, 0x80, 0xbc, 0x9e, 0x6c,
+ 0xd5, 0xfc, 0x7e, 0xd2, 0xe1, 0x4b, 0x8c, 0xdc, 0x42, 0xfa, 0x44, 0x4b,
+ 0x5f, 0xf8, 0x18, 0xb5, 0x2e, 0x30, 0xf4, 0x3d, 0x12, 0x98, 0xd3, 0x62,
+ 0x05, 0x73, 0x54, 0xa6, 0x9c, 0xa2, 0x1d, 0xbe, 0x52, 0x83, 0x3a, 0x07,
+ 0x46, 0xc4, 0x3b, 0x02, 0x56, 0x21, 0xbf, 0xf2, 0x51, 0x4f, 0xd0, 0xa6,
+ 0x99, 0x39, 0xe9, 0xae, 0xa5, 0x3f, 0x89, 0x9b, 0x9c, 0x7d, 0xfe, 0x4d,
+ 0x60, 0x07, 0x25, 0x20, 0xf7, 0xbb, 0xd7, 0x69, 0x83, 0x2b, 0x82, 0x93,
+ 0x43, 0x37, 0xd9, 0x83, 0x41, 0x1b, 0x6b, 0x0b, 0xab, 0x4a, 0x66, 0x84,
+ 0x4f, 0x4a, 0x8e, 0xde, 0x7e, 0x34, 0x99, 0x8e, 0x68, 0xd6, 0xca, 0x39,
+ 0x06, 0x9b, 0x4c, 0xb3, 0x9a, 0x48, 0x4d, 0x13, 0x46, 0xb4, 0x58, 0x21,
+ 0x04, 0xc4, 0xfb, 0xa0, 0x4d, 0xac, 0x2e, 0x4b, 0x62, 0x12, 0xe3, 0xfb,
+ 0x4d, 0xf6, 0xc9, 0x51, 0x00, 0x01, 0x1f, 0xfc, 0x1e, 0x6a, 0x81, 0x2a,
+ 0x38, 0xe0, 0xb9, 0x4f, 0xd6, 0x2d, 0x45, 0x02, 0x03, 0x01, 0x00, 0x01,
+ 0xa3, 0x82, 0x02, 0x15, 0x30, 0x82, 0x02, 0x11, 0x30, 0x12, 0x06, 0x03,
+ 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01,
+ 0xff, 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x69, 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86,
+ 0xf8, 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74,
+ 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+ 0x70, 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x30, 0x0e,
+ 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02,
+ 0x01, 0x06, 0x30, 0x6d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x01, 0x0c, 0x04, 0x61, 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59,
+ 0x30, 0x57, 0x30, 0x55, 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f,
+ 0x67, 0x69, 0x66, 0x30, 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac,
+ 0x8d, 0x8e, 0x6b, 0xc3, 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b,
+ 0x19, 0x2e, 0x30, 0x25, 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x67, 0x69, 0x66, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x25,
+ 0x04, 0x2d, 0x30, 0x2b, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02,
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01, 0x06,
+ 0x0a, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45, 0x01, 0x08, 0x01, 0x30,
+ 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67,
+ 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x2d, 0x30, 0x2b, 0x30, 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76,
+ 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x70, 0x63, 0x61, 0x33, 0x2d, 0x67, 0x35, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x28, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x21, 0x30, 0x1f, 0xa4, 0x1d,
+ 0x30, 0x1b, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x10, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x4d, 0x50, 0x4b,
+ 0x49, 0x2d, 0x32, 0x2d, 0x37, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xd7, 0x9b, 0x7c, 0xd8, 0x22, 0xa0, 0x15, 0xf7,
+ 0xdd, 0xad, 0x5f, 0xce, 0x29, 0x9b, 0x58, 0xc3, 0xbc, 0x46, 0x00, 0xb5,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0x7f, 0xd3, 0x65, 0xa7, 0xc2, 0xdd, 0xec, 0xbb, 0xf0, 0x30, 0x09,
+ 0xf3, 0x43, 0x39, 0xfa, 0x02, 0xaf, 0x33, 0x31, 0x33, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x71, 0xb5, 0x7d, 0x73, 0x52, 0x4a, 0xdd,
+ 0xd7, 0x4d, 0x34, 0x2b, 0x2e, 0xaf, 0x94, 0x46, 0xa5, 0x49, 0x50, 0x02,
+ 0x4f, 0xf8, 0x2f, 0x17, 0x70, 0xf2, 0x13, 0xdc, 0x1f, 0x21, 0x86, 0xaa,
+ 0xc2, 0x4f, 0x7c, 0x37, 0x3c, 0xd4, 0x46, 0x78, 0xae, 0x5d, 0x78, 0x6f,
+ 0xd1, 0xba, 0x5a, 0xbc, 0x10, 0xab, 0x58, 0x36, 0xc5, 0x8c, 0x62, 0x15,
+ 0x45, 0x60, 0x17, 0x21, 0xe2, 0xd5, 0x42, 0xa8, 0x77, 0xa1, 0x55, 0xd8,
+ 0x43, 0x04, 0x51, 0xf6, 0x6e, 0xba, 0x48, 0xe6, 0x5d, 0x4c, 0xb7, 0x44,
+ 0xd3, 0x3e, 0xa4, 0xd5, 0xd6, 0x33, 0x9a, 0x9f, 0x0d, 0xe6, 0xd7, 0x4e,
+ 0x96, 0x44, 0x95, 0x5a, 0x6c, 0xd6, 0xa3, 0x16, 0x53, 0x0e, 0x98, 0x43,
+ 0xce, 0xa4, 0xb8, 0xc3, 0x66, 0x7a, 0x05, 0x5c, 0x62, 0x10, 0xe8, 0x1b,
+ 0x12, 0xdb, 0x7d, 0x2e, 0x76, 0x50, 0xff, 0xdf, 0xd7, 0x6b, 0x1b, 0xcc,
+ 0x8a, 0xcc, 0x71, 0xfa, 0xb3, 0x40, 0x56, 0x7c, 0x33, 0x7a, 0x77, 0x94,
+ 0x5b, 0xf5, 0x0b, 0x53, 0xfb, 0x0e, 0x5f, 0xbc, 0x68, 0xfb, 0xaf, 0x2a,
+ 0xee, 0x30, 0x37, 0x79, 0x16, 0x93, 0x25, 0x7f, 0x4d, 0x10, 0xff, 0x57,
+ 0xfb, 0xbf, 0x6e, 0x3b, 0x33, 0x21, 0xde, 0x79, 0xdc, 0x86, 0x17, 0x59,
+ 0x2d, 0x43, 0x64, 0xb7, 0xa6, 0x66, 0x87, 0xea, 0xbc, 0x96, 0x46, 0x19,
+ 0x1a, 0x86, 0x8b, 0x6f, 0xd7, 0xb7, 0x49, 0x00, 0x5b, 0xdb, 0xa3, 0xbf,
+ 0x29, 0x9a, 0xee, 0xf7, 0xd3, 0x33, 0xae, 0xa3, 0xf4, 0x9e, 0x4c, 0xca,
+ 0x5e, 0x69, 0xd4, 0x1b, 0xad, 0xb7, 0x90, 0x77, 0x6a, 0xd8, 0x59, 0x6f,
+ 0x79, 0xab, 0x01, 0xfa, 0x55, 0xf0, 0x8a, 0x21, 0x66, 0xe5, 0x65, 0x6e,
+ 0xfd, 0x7c, 0xd3, 0xdf, 0x1e, 0xeb, 0x7e, 0x3f, 0x06, 0x90, 0xfb, 0x19,
+ 0x0b, 0xd3, 0x06, 0x02, 0x1b, 0x78, 0x43, 0x99, 0xa8,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 6e:4f:fa:b3:c5:e6:69:c4:d1:67:c9:92:ab:e8:58:c4
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority - G2, OU=(c) 1998 VeriSign, Inc. - For authorized use only, OU=VeriSign Trust Network
+ Validity
+ Not Before: Mar 25 00:00:00 2009 GMT
+ Not After : Mar 24 23:59:59 2019 GMT
+ Subject: C=US, O=VeriSign, Inc., OU=VeriSign Trust Network, OU=Terms of use at https://www.verisign.com/rpa (c)09, CN=VeriSign Class 3 Secure Server CA - G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:d4:56:8f:57:3b:37:28:a6:40:63:d2:95:d5:05:
+ 74:da:b5:19:6a:96:d6:71:57:2f:e2:c0:34:8c:a0:
+ 95:b3:8c:e1:37:24:f3:2e:ed:43:45:05:8e:89:d7:
+ fa:da:4a:b5:f8:3e:8d:4e:c7:f9:49:50:45:37:40:
+ 9f:74:aa:a0:51:55:61:f1:60:84:89:a5:9e:80:8d:
+ 2f:b0:21:aa:45:82:c4:cf:b4:14:7f:47:15:20:28:
+ 82:b0:68:12:c0:ae:5c:07:d7:f6:59:cc:cb:62:56:
+ 5c:4d:49:ff:26:88:ab:54:51:3a:2f:4a:da:0e:98:
+ e2:89:72:b9:fc:f7:68:3c:c4:1f:39:7a:cb:17:81:
+ f3:0c:ad:0f:dc:61:62:1b:10:0b:04:1e:29:18:71:
+ 5e:62:cb:43:de:be:31:ba:71:02:19:4e:26:a9:51:
+ da:8c:64:69:03:de:9c:fd:7d:fd:7b:61:bc:fc:84:
+ 7c:88:5c:b4:c3:7b:ed:5f:2b:46:12:f1:fd:00:01:
+ 9a:8b:5b:e9:a3:05:2e:8f:2e:5b:de:f3:1b:78:f8:
+ 66:91:08:c0:5e:ce:d5:b0:36:ca:d4:a8:7b:a0:7d:
+ f9:30:7a:bf:f8:dd:19:51:2b:20:ba:fe:a7:cf:a1:
+ 4e:b0:67:f5:80:aa:2b:83:2e:d2:8e:54:89:8e:1e:
+ 29:0b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ Authority Information Access:
+ OCSP - URI:http://ocsp.verisign.com
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.113733.1.7.23.3
+ CPS: https://www.verisign.com/cps
+ User Notice:
+ Explicit Text: https://www.verisign.com/rpa
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl.verisign.com/pca3-g2.crl
+
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ 1.3.6.1.5.5.7.1.12:
+ 0_.].[0Y0W0U..image/gif0!0.0...+..............k...j.H.,{..0%.#http://logo.verisign.com/vslogo.gif
+ X509v3 Subject Alternative Name:
+ DirName:/CN=Class3CA2048-1-52
+ X509v3 Subject Key Identifier:
+ A5:EF:0B:11:CE:C0:41:03:A3:4A:65:90:48:B2:1C:E0:57:2D:7D:47
+ X509v3 Authority Key Identifier:
+ DirName:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority - G2/OU=(c) 1998 VeriSign, Inc. - For authorized use only/OU=VeriSign Trust Network
+ serial:7D:D9:FE:07:CF:A8:1E:B7:10:79:67:FB:A7:89:34:C6
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 63:74:2f:3d:53:aa:2f:97:ec:26:11:66:1a:fe:f1:de:41:27:
+ 19:d2:7f:d8:c1:1c:f9:e2:38:56:3a:1f:90:ae:39:c5:20:75:
+ ab:f8:6c:2d:67:1f:29:c2:21:d7:14:88:63:4b:b0:9b:27:63:
+ 91:f8:f0:a3:01:24:b6:fb:8f:e3:3d:02:0b:6f:54:fe:d4:cc:
+ db:d6:85:bf:7c:95:1e:5e:62:11:c1:d9:09:9c:42:b9:b2:d4:
+ aa:2d:98:3a:23:60:cc:a2:9a:f1:6e:e8:cf:8e:d1:1a:3c:5e:
+ 19:c5:d7:9b:35:b0:02:23:24:e5:05:b8:d5:88:e3:e0:fa:b9:
+ f4:5f
+-----BEGIN CERTIFICATE-----
+MIIGLDCCBZWgAwIBAgIQbk/6s8XmacTRZ8mSq+hYxDANBgkqhkiG9w0BAQUFADCB
+wTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTwwOgYDVQQL
+EzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1
+dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv
+cmswHhcNMDkwMzI1MDAwMDAwWhcNMTkwMzI0MjM1OTU5WjCBtTELMAkGA1UEBhMC
+VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6Ly93
+d3cudmVyaXNpZ24uY29tL3JwYSAoYykwOTEvMC0GA1UEAxMmVmVyaVNpZ24gQ2xh
+c3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDUVo9XOzcopkBj0pXVBXTatRlqltZxVy/iwDSMoJWzjOE3JPMu
+7UNFBY6J1/raSrX4Po1Ox/lJUEU3QJ90qqBRVWHxYISJpZ6AjS+wIapFgsTPtBR/
+RxUgKIKwaBLArlwH1/ZZzMtiVlxNSf8miKtUUTovStoOmOKJcrn892g8xB85essX
+gfMMrQ/cYWIbEAsEHikYcV5iy0PevjG6cQIZTiapUdqMZGkD3pz9ff17Ybz8hHyI
+XLTDe+1fK0YS8f0AAZqLW+mjBS6PLlve8xt4+GaRCMBeztWwNsrUqHugffkwer/4
+3RlRKyC6/qfPoU6wZ/WAqiuDLtKOVImOHikLAgMBAAGjggKpMIICpTA0BggrBgEF
+BQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlzaWduLmNvbTAS
+BgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4RQEHFwMwVjAo
+BggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2NwczAqBggrBgEF
+BQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQGA1UdHwQtMCsw
+KaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzIuY3JsMA4GA1Ud
+DwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglpbWFnZS9naWYw
+ITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNodHRwOi8vbG9n
+by52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjApBgNVHREEIjAgpB4wHDEaMBgGA1UE
+AxMRQ2xhc3MzQ0EyMDQ4LTEtNTIwHQYDVR0OBBYEFKXvCxHOwEEDo0plkEiyHOBX
+LX1HMIHnBgNVHSMEgd8wgdyhgcekgcQwgcExCzAJBgNVBAYTAlVTMRcwFQYDVQQK
+Ew5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy
+eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5
+OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYD
+VQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrghB92f4Hz6getxB5Z/uniTTGMA0G
+CSqGSIb3DQEBBQUAA4GBAGN0Lz1Tqi+X7CYRZhr+8d5BJxnSf9jBHPniOFY6H5Cu
+OcUgdav4bC1nHynCIdcUiGNLsJsnY5H48KMBJLb7j+M9AgtvVP7UzNvWhb98lR5e
+YhHB2QmcQrmy1KotmDojYMyimvFu6M+O0Ro8XhnF15s1sAIjJOUFuNWI4+D6ufRf
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert95[] = {
+ 0x30, 0x82, 0x06, 0x2c, 0x30, 0x82, 0x05, 0x95, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x6e, 0x4f, 0xfa, 0xb3, 0xc5, 0xe6, 0x69, 0xc4, 0xd1,
+ 0x67, 0xc9, 0x92, 0xab, 0xe8, 0x58, 0xc4, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81,
+ 0xc1, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x3c, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x33, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79,
+ 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+ 0x20, 0x2d, 0x20, 0x47, 0x32, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x38,
+ 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73, 0x65,
+ 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e,
+ 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f,
+ 0x72, 0x6b, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30, 0x33, 0x32, 0x35,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x30,
+ 0x33, 0x32, 0x34, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x81,
+ 0xb5, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20, 0x49,
+ 0x6e, 0x63, 0x2e, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x0b,
+ 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x54,
+ 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
+ 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x32, 0x54,
+ 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x20,
+ 0x61, 0x74, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x20, 0x28, 0x63, 0x29, 0x30,
+ 0x39, 0x31, 0x2f, 0x30, 0x2d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x26,
+ 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x20, 0x43, 0x6c, 0x61,
+ 0x73, 0x73, 0x20, 0x33, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x20, 0x2d, 0x20,
+ 0x47, 0x32, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd4,
+ 0x56, 0x8f, 0x57, 0x3b, 0x37, 0x28, 0xa6, 0x40, 0x63, 0xd2, 0x95, 0xd5,
+ 0x05, 0x74, 0xda, 0xb5, 0x19, 0x6a, 0x96, 0xd6, 0x71, 0x57, 0x2f, 0xe2,
+ 0xc0, 0x34, 0x8c, 0xa0, 0x95, 0xb3, 0x8c, 0xe1, 0x37, 0x24, 0xf3, 0x2e,
+ 0xed, 0x43, 0x45, 0x05, 0x8e, 0x89, 0xd7, 0xfa, 0xda, 0x4a, 0xb5, 0xf8,
+ 0x3e, 0x8d, 0x4e, 0xc7, 0xf9, 0x49, 0x50, 0x45, 0x37, 0x40, 0x9f, 0x74,
+ 0xaa, 0xa0, 0x51, 0x55, 0x61, 0xf1, 0x60, 0x84, 0x89, 0xa5, 0x9e, 0x80,
+ 0x8d, 0x2f, 0xb0, 0x21, 0xaa, 0x45, 0x82, 0xc4, 0xcf, 0xb4, 0x14, 0x7f,
+ 0x47, 0x15, 0x20, 0x28, 0x82, 0xb0, 0x68, 0x12, 0xc0, 0xae, 0x5c, 0x07,
+ 0xd7, 0xf6, 0x59, 0xcc, 0xcb, 0x62, 0x56, 0x5c, 0x4d, 0x49, 0xff, 0x26,
+ 0x88, 0xab, 0x54, 0x51, 0x3a, 0x2f, 0x4a, 0xda, 0x0e, 0x98, 0xe2, 0x89,
+ 0x72, 0xb9, 0xfc, 0xf7, 0x68, 0x3c, 0xc4, 0x1f, 0x39, 0x7a, 0xcb, 0x17,
+ 0x81, 0xf3, 0x0c, 0xad, 0x0f, 0xdc, 0x61, 0x62, 0x1b, 0x10, 0x0b, 0x04,
+ 0x1e, 0x29, 0x18, 0x71, 0x5e, 0x62, 0xcb, 0x43, 0xde, 0xbe, 0x31, 0xba,
+ 0x71, 0x02, 0x19, 0x4e, 0x26, 0xa9, 0x51, 0xda, 0x8c, 0x64, 0x69, 0x03,
+ 0xde, 0x9c, 0xfd, 0x7d, 0xfd, 0x7b, 0x61, 0xbc, 0xfc, 0x84, 0x7c, 0x88,
+ 0x5c, 0xb4, 0xc3, 0x7b, 0xed, 0x5f, 0x2b, 0x46, 0x12, 0xf1, 0xfd, 0x00,
+ 0x01, 0x9a, 0x8b, 0x5b, 0xe9, 0xa3, 0x05, 0x2e, 0x8f, 0x2e, 0x5b, 0xde,
+ 0xf3, 0x1b, 0x78, 0xf8, 0x66, 0x91, 0x08, 0xc0, 0x5e, 0xce, 0xd5, 0xb0,
+ 0x36, 0xca, 0xd4, 0xa8, 0x7b, 0xa0, 0x7d, 0xf9, 0x30, 0x7a, 0xbf, 0xf8,
+ 0xdd, 0x19, 0x51, 0x2b, 0x20, 0xba, 0xfe, 0xa7, 0xcf, 0xa1, 0x4e, 0xb0,
+ 0x67, 0xf5, 0x80, 0xaa, 0x2b, 0x83, 0x2e, 0xd2, 0x8e, 0x54, 0x89, 0x8e,
+ 0x1e, 0x29, 0x0b, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x02, 0xa9,
+ 0x30, 0x82, 0x02, 0xa5, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x76, 0x65,
+ 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x12,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06,
+ 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x69, 0x30, 0x67, 0x30, 0x65, 0x06, 0x0b, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xf8, 0x45, 0x01, 0x07, 0x17, 0x03, 0x30, 0x56, 0x30, 0x28,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c,
+ 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e,
+ 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x63, 0x70, 0x73, 0x30, 0x2a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x02, 0x30, 0x1e, 0x1a, 0x1c, 0x68, 0x74, 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x65, 0x72, 0x69,
+ 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61,
+ 0x30, 0x34, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2d, 0x30, 0x2b, 0x30,
+ 0x29, 0xa0, 0x27, 0xa0, 0x25, 0x86, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69,
+ 0x67, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x63, 0x61, 0x33, 0x2d,
+ 0x67, 0x32, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x6d,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c, 0x04, 0x61,
+ 0x30, 0x5f, 0xa1, 0x5d, 0xa0, 0x5b, 0x30, 0x59, 0x30, 0x57, 0x30, 0x55,
+ 0x16, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, 0x69, 0x66, 0x30,
+ 0x21, 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+ 0x04, 0x14, 0x8f, 0xe5, 0xd3, 0x1a, 0x86, 0xac, 0x8d, 0x8e, 0x6b, 0xc3,
+ 0xcf, 0x80, 0x6a, 0xd4, 0x48, 0x18, 0x2c, 0x7b, 0x19, 0x2e, 0x30, 0x25,
+ 0x16, 0x23, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x6f, 0x67,
+ 0x6f, 0x2e, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x76, 0x73, 0x6c, 0x6f, 0x67, 0x6f, 0x2e, 0x67, 0x69,
+ 0x66, 0x30, 0x29, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x22, 0x30, 0x20,
+ 0xa4, 0x1e, 0x30, 0x1c, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x13, 0x11, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x33, 0x43, 0x41, 0x32,
+ 0x30, 0x34, 0x38, 0x2d, 0x31, 0x2d, 0x35, 0x32, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa5, 0xef, 0x0b, 0x11, 0xce,
+ 0xc0, 0x41, 0x03, 0xa3, 0x4a, 0x65, 0x90, 0x48, 0xb2, 0x1c, 0xe0, 0x57,
+ 0x2d, 0x7d, 0x47, 0x30, 0x81, 0xe7, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x81, 0xdf, 0x30, 0x81, 0xdc, 0xa1, 0x81, 0xc7, 0xa4, 0x81, 0xc4, 0x30,
+ 0x81, 0xc1, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x13, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x31, 0x3c, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x04,
+ 0x0b, 0x13, 0x33, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72,
+ 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x20, 0x2d, 0x20, 0x47, 0x32, 0x31, 0x3a, 0x30, 0x38, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x31, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39,
+ 0x38, 0x20, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67, 0x6e, 0x2c, 0x20,
+ 0x49, 0x6e, 0x63, 0x2e, 0x20, 0x2d, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x75, 0x73,
+ 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x16, 0x56, 0x65, 0x72, 0x69, 0x53, 0x69, 0x67,
+ 0x6e, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x4e, 0x65, 0x74, 0x77,
+ 0x6f, 0x72, 0x6b, 0x82, 0x10, 0x7d, 0xd9, 0xfe, 0x07, 0xcf, 0xa8, 0x1e,
+ 0xb7, 0x10, 0x79, 0x67, 0xfb, 0xa7, 0x89, 0x34, 0xc6, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00,
+ 0x03, 0x81, 0x81, 0x00, 0x63, 0x74, 0x2f, 0x3d, 0x53, 0xaa, 0x2f, 0x97,
+ 0xec, 0x26, 0x11, 0x66, 0x1a, 0xfe, 0xf1, 0xde, 0x41, 0x27, 0x19, 0xd2,
+ 0x7f, 0xd8, 0xc1, 0x1c, 0xf9, 0xe2, 0x38, 0x56, 0x3a, 0x1f, 0x90, 0xae,
+ 0x39, 0xc5, 0x20, 0x75, 0xab, 0xf8, 0x6c, 0x2d, 0x67, 0x1f, 0x29, 0xc2,
+ 0x21, 0xd7, 0x14, 0x88, 0x63, 0x4b, 0xb0, 0x9b, 0x27, 0x63, 0x91, 0xf8,
+ 0xf0, 0xa3, 0x01, 0x24, 0xb6, 0xfb, 0x8f, 0xe3, 0x3d, 0x02, 0x0b, 0x6f,
+ 0x54, 0xfe, 0xd4, 0xcc, 0xdb, 0xd6, 0x85, 0xbf, 0x7c, 0x95, 0x1e, 0x5e,
+ 0x62, 0x11, 0xc1, 0xd9, 0x09, 0x9c, 0x42, 0xb9, 0xb2, 0xd4, 0xaa, 0x2d,
+ 0x98, 0x3a, 0x23, 0x60, 0xcc, 0xa2, 0x9a, 0xf1, 0x6e, 0xe8, 0xcf, 0x8e,
+ 0xd1, 0x1a, 0x3c, 0x5e, 0x19, 0xc5, 0xd7, 0x9b, 0x35, 0xb0, 0x02, 0x23,
+ 0x24, 0xe5, 0x05, 0xb8, 0xd5, 0x88, 0xe3, 0xe0, 0xfa, 0xb9, 0xf4, 0x5f,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 24 (0x18)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+ Validity
+ Not Before: Oct 24 20:54:17 2007 GMT
+ Not After : Oct 24 20:54:17 2017 GMT
+ Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 1 Primary Intermediate Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b6:89:c6:ac:ef:09:52:78:07:ac:92:63:d0:f4:
+ 44:18:18:84:80:56:1f:91:ae:e1:87:fa:32:50:b4:
+ d3:47:06:f0:e6:07:5f:70:0e:10:f7:1d:c0:ce:10:
+ 36:34:85:5a:0f:92:ac:83:c6:ac:58:52:3f:ba:38:
+ e8:fc:e7:a7:24:e2:40:a6:08:76:c0:92:6e:9e:2a:
+ 6d:4d:3f:6e:61:20:0a:db:59:de:d2:7d:63:b3:3e:
+ 46:fe:fa:21:51:18:d7:cd:30:a6:ed:07:6e:3b:70:
+ 87:b4:f9:fa:eb:ee:82:3c:05:6f:92:f7:a4:dc:0a:
+ 30:1e:93:73:fe:07:ca:d7:5f:80:9d:22:58:52:ae:
+ 06:da:8b:87:23:69:b0:e4:2a:d8:ea:83:d2:bd:f3:
+ 71:db:70:5a:28:0f:af:5a:38:70:45:12:3f:30:4d:
+ cd:3b:af:17:e5:0f:cb:a0:a9:5d:48:aa:b1:61:50:
+ cb:34:cd:3c:5c:c3:0b:e8:10:c0:8c:9b:f0:03:03:
+ 62:fe:b2:6c:3e:72:0e:ee:1c:43:2a:c9:48:0e:57:
+ 39:c4:31:21:c8:10:c1:2c:87:fe:54:95:52:1f:52:
+ 3c:31:12:9b:7f:e7:c0:a0:a5:59:d5:e2:8f:3e:f0:
+ d5:a8:e1:d7:70:31:a9:c4:b3:cf:af:6d:53:2f:06:
+ f4:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ EB:42:34:D0:98:B0:AB:9F:F4:1B:6B:08:F7:CC:64:2E:EF:0E:2C:45
+ X509v3 Authority Key Identifier:
+ keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.startssl.com/ca
+ CA Issuers - URI:http://www.startssl.com/sfsca.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.startssl.com/sfsca.crl
+
+ Full Name:
+ URI:http://crl.startssl.com/sfsca.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.23223.1.2.1
+ CPS: http://www.startssl.com/policy.pdf
+ CPS: http://www.startssl.com/intermediate.pdf
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 21:09:49:3e:a5:88:6e:e0:0b:8b:48:da:31:4d:8f:f7:56:57:
+ a2:e1:d3:62:57:e9:b5:56:f3:85:45:75:3b:e5:50:1f:04:8b:
+ e6:a0:5a:3e:e7:00:ae:85:d0:fb:ff:20:03:64:cb:ad:02:e1:
+ c6:91:72:f8:a3:4d:d6:de:e8:cc:3f:a1:8a:a2:e3:7c:37:a7:
+ c6:4f:8f:35:d6:f4:d6:6e:06:7b:dd:21:d9:cf:56:ff:cb:30:
+ 22:49:fe:89:04:f3:85:e5:aa:f1:e7:1f:e8:75:90:4d:dd:f9:
+ 46:f7:42:34:f7:45:58:0c:11:0d:84:b0:c6:da:5d:3e:f9:01:
+ 9e:e7:e1:da:55:95:be:74:1c:7b:fc:4d:14:4f:ac:7e:55:47:
+ 7d:7b:f4:a5:0d:49:1e:95:e8:f7:12:c1:cc:ff:76:a6:25:47:
+ d0:f3:75:35:be:97:b7:58:16:eb:aa:5c:78:6f:ec:53:30:af:
+ ea:04:4d:cc:a9:02:e3:f0:b6:04:12:f6:30:b1:11:3d:90:4e:
+ 56:64:d7:dc:3c:43:5f:73:39:ef:4b:af:87:eb:f6:fe:68:88:
+ 44:72:ea:d2:07:c6:69:b0:c1:a1:8b:ef:17:49:d7:61:b1:45:
+ 48:5f:3b:20:21:e9:5b:b2:cc:f4:d7:e9:31:f5:0b:15:61:3b:
+ 7a:94:e3:eb:d9:bc:7f:94:ae:6a:e3:62:62:96:a8:64:7c:b8:
+ 87:f3:99:32:7e:92:a2:52:be:bb:f8:65:cf:c9:f2:30:fc:8b:
+ c1:c2:a6:96:d7:5f:89:e1:5c:34:80:f5:8f:47:07:2f:b4:91:
+ bf:b1:a2:7e:5f:4b:5a:d0:5b:9f:24:86:05:51:5a:69:03:65:
+ 43:49:71:c5:e0:6f:94:34:6b:f6:1b:d8:a9:b0:4c:7e:53:eb:
+ 8f:48:df:ca:33:b5:48:fa:36:4a:1a:53:a6:33:0c:d0:89:cd:
+ 49:15:cd:89:31:3c:90:c0:72:d7:65:4b:52:35:8a:46:11:44:
+ b9:3d:8e:28:65:a6:3e:79:9e:5c:08:44:29:ad:b0:35:11:2e:
+ 21:4e:b8:d2:e7:10:3e:5d:84:83:b3:c3:c2:e4:d2:c6:fd:09:
+ 4b:74:09:dd:f1:b3:d3:19:3e:80:0d:a2:0b:19:f0:38:e7:c5:
+ c2:af:e2:23:db:61:e2:9d:5c:6e:20:89:49:2e:23:6a:b2:62:
+ c1:45:b4:9f:af:8b:a7:f1:22:3b:f8:7d:e2:90:d0:7a:19:fb:
+ 4a:4c:e3:d2:7d:5f:4a:83:03:ed:27:d6:23:9e:6b:8d:b4:59:
+ a2:d9:ef:6c:82:29:dd:75:19:3c:3f:4c:10:8d:ef:bb:75:27:
+ d2:ae:83:a7:a8:ce:5b:a7
+-----BEGIN CERTIFICATE-----
+MIIGNDCCBBygAwIBAgIBGDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NDE3WhcNMTcxMDI0MjA1NDE3WjCB
+jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
+IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
+YXJ0Q29tIENsYXNzIDEgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtonGrO8JUngHrJJj0PREGBiE
+gFYfka7hh/oyULTTRwbw5gdfcA4Q9x3AzhA2NIVaD5Ksg8asWFI/ujjo/OenJOJA
+pgh2wJJuniptTT9uYSAK21ne0n1jsz5G/vohURjXzTCm7QduO3CHtPn66+6CPAVv
+kvek3AowHpNz/gfK11+AnSJYUq4G2ouHI2mw5CrY6oPSvfNx23BaKA+vWjhwRRI/
+ME3NO68X5Q/LoKldSKqxYVDLNM08XMML6BDAjJvwAwNi/rJsPnIO7hxDKslIDlc5
+xDEhyBDBLIf+VJVSH1I8MRKbf+fAoKVZ1eKPPvDVqOHXcDGpxLPPr21TLwb0pwID
+AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFOtCNNCYsKuf9BtrCPfMZC7vDixFMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
+L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
+YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
+c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
+BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
+BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
+LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAIQlJPqWIbuALi0jaMU2P91ZXouHTYlfp
+tVbzhUV1O+VQHwSL5qBaPucAroXQ+/8gA2TLrQLhxpFy+KNN1t7ozD+hiqLjfDen
+xk+PNdb01m4Ge90h2c9W/8swIkn+iQTzheWq8ecf6HWQTd35RvdCNPdFWAwRDYSw
+xtpdPvkBnufh2lWVvnQce/xNFE+sflVHfXv0pQ1JHpXo9xLBzP92piVH0PN1Nb6X
+t1gW66pceG/sUzCv6gRNzKkC4/C2BBL2MLERPZBOVmTX3DxDX3M570uvh+v2/miI
+RHLq0gfGabDBoYvvF0nXYbFFSF87ICHpW7LM9NfpMfULFWE7epTj69m8f5SuauNi
+YpaoZHy4h/OZMn6SolK+u/hlz8nyMPyLwcKmltdfieFcNID1j0cHL7SRv7Gifl9L
+WtBbnySGBVFaaQNlQ0lxxeBvlDRr9hvYqbBMflPrj0jfyjO1SPo2ShpTpjMM0InN
+SRXNiTE8kMBy12VLUjWKRhFEuT2OKGWmPnmeXAhEKa2wNREuIU640ucQPl2Eg7PD
+wuTSxv0JS3QJ3fGz0xk+gA2iCxnwOOfFwq/iI9th4p1cbiCJSS4jarJiwUW0n6+L
+p/EiO/h94pDQehn7Skzj0n1fSoMD7SfWI55rjbRZotnvbIIp3XUZPD9MEI3vu3Un
+0q6Dp6jOW6c=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert96[] = {
+ 0x30, 0x82, 0x06, 0x34, 0x30, 0x82, 0x04, 0x1c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x18, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16,
+ 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61,
+ 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b,
+ 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43,
+ 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x30, 0x32, 0x34,
+ 0x32, 0x30, 0x35, 0x34, 0x31, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x31,
+ 0x30, 0x32, 0x34, 0x32, 0x30, 0x35, 0x34, 0x31, 0x37, 0x5a, 0x30, 0x81,
+ 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74,
+ 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31,
+ 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x20, 0x31, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, 0x89, 0xc6, 0xac, 0xef, 0x09,
+ 0x52, 0x78, 0x07, 0xac, 0x92, 0x63, 0xd0, 0xf4, 0x44, 0x18, 0x18, 0x84,
+ 0x80, 0x56, 0x1f, 0x91, 0xae, 0xe1, 0x87, 0xfa, 0x32, 0x50, 0xb4, 0xd3,
+ 0x47, 0x06, 0xf0, 0xe6, 0x07, 0x5f, 0x70, 0x0e, 0x10, 0xf7, 0x1d, 0xc0,
+ 0xce, 0x10, 0x36, 0x34, 0x85, 0x5a, 0x0f, 0x92, 0xac, 0x83, 0xc6, 0xac,
+ 0x58, 0x52, 0x3f, 0xba, 0x38, 0xe8, 0xfc, 0xe7, 0xa7, 0x24, 0xe2, 0x40,
+ 0xa6, 0x08, 0x76, 0xc0, 0x92, 0x6e, 0x9e, 0x2a, 0x6d, 0x4d, 0x3f, 0x6e,
+ 0x61, 0x20, 0x0a, 0xdb, 0x59, 0xde, 0xd2, 0x7d, 0x63, 0xb3, 0x3e, 0x46,
+ 0xfe, 0xfa, 0x21, 0x51, 0x18, 0xd7, 0xcd, 0x30, 0xa6, 0xed, 0x07, 0x6e,
+ 0x3b, 0x70, 0x87, 0xb4, 0xf9, 0xfa, 0xeb, 0xee, 0x82, 0x3c, 0x05, 0x6f,
+ 0x92, 0xf7, 0xa4, 0xdc, 0x0a, 0x30, 0x1e, 0x93, 0x73, 0xfe, 0x07, 0xca,
+ 0xd7, 0x5f, 0x80, 0x9d, 0x22, 0x58, 0x52, 0xae, 0x06, 0xda, 0x8b, 0x87,
+ 0x23, 0x69, 0xb0, 0xe4, 0x2a, 0xd8, 0xea, 0x83, 0xd2, 0xbd, 0xf3, 0x71,
+ 0xdb, 0x70, 0x5a, 0x28, 0x0f, 0xaf, 0x5a, 0x38, 0x70, 0x45, 0x12, 0x3f,
+ 0x30, 0x4d, 0xcd, 0x3b, 0xaf, 0x17, 0xe5, 0x0f, 0xcb, 0xa0, 0xa9, 0x5d,
+ 0x48, 0xaa, 0xb1, 0x61, 0x50, 0xcb, 0x34, 0xcd, 0x3c, 0x5c, 0xc3, 0x0b,
+ 0xe8, 0x10, 0xc0, 0x8c, 0x9b, 0xf0, 0x03, 0x03, 0x62, 0xfe, 0xb2, 0x6c,
+ 0x3e, 0x72, 0x0e, 0xee, 0x1c, 0x43, 0x2a, 0xc9, 0x48, 0x0e, 0x57, 0x39,
+ 0xc4, 0x31, 0x21, 0xc8, 0x10, 0xc1, 0x2c, 0x87, 0xfe, 0x54, 0x95, 0x52,
+ 0x1f, 0x52, 0x3c, 0x31, 0x12, 0x9b, 0x7f, 0xe7, 0xc0, 0xa0, 0xa5, 0x59,
+ 0xd5, 0xe2, 0x8f, 0x3e, 0xf0, 0xd5, 0xa8, 0xe1, 0xd7, 0x70, 0x31, 0xa9,
+ 0xc4, 0xb3, 0xcf, 0xaf, 0x6d, 0x53, 0x2f, 0x06, 0xf4, 0xa7, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xad, 0x30, 0x82, 0x01, 0xa9, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98,
+ 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef,
+ 0x0e, 0x2c, 0x45, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, 0x1a, 0xa4, 0x40, 0x5b, 0xa5,
+ 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, 0x43, 0xd0, 0x41, 0xae, 0xf2,
+ 0x30, 0x66, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x5a, 0x30, 0x58, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73,
+ 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x30, 0x2d, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
+ 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x5b, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x54, 0x30, 0x52, 0x30, 0x27, 0xa0, 0x25, 0xa0,
+ 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x80, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x79, 0x30, 0x77, 0x30, 0x75, 0x06, 0x0b, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x81, 0xb5, 0x37, 0x01, 0x02, 0x01, 0x30, 0x66, 0x30, 0x2e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x22,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x64, 0x66, 0x30, 0x34,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x28,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65,
+ 0x2e, 0x70, 0x64, 0x66, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00,
+ 0x21, 0x09, 0x49, 0x3e, 0xa5, 0x88, 0x6e, 0xe0, 0x0b, 0x8b, 0x48, 0xda,
+ 0x31, 0x4d, 0x8f, 0xf7, 0x56, 0x57, 0xa2, 0xe1, 0xd3, 0x62, 0x57, 0xe9,
+ 0xb5, 0x56, 0xf3, 0x85, 0x45, 0x75, 0x3b, 0xe5, 0x50, 0x1f, 0x04, 0x8b,
+ 0xe6, 0xa0, 0x5a, 0x3e, 0xe7, 0x00, 0xae, 0x85, 0xd0, 0xfb, 0xff, 0x20,
+ 0x03, 0x64, 0xcb, 0xad, 0x02, 0xe1, 0xc6, 0x91, 0x72, 0xf8, 0xa3, 0x4d,
+ 0xd6, 0xde, 0xe8, 0xcc, 0x3f, 0xa1, 0x8a, 0xa2, 0xe3, 0x7c, 0x37, 0xa7,
+ 0xc6, 0x4f, 0x8f, 0x35, 0xd6, 0xf4, 0xd6, 0x6e, 0x06, 0x7b, 0xdd, 0x21,
+ 0xd9, 0xcf, 0x56, 0xff, 0xcb, 0x30, 0x22, 0x49, 0xfe, 0x89, 0x04, 0xf3,
+ 0x85, 0xe5, 0xaa, 0xf1, 0xe7, 0x1f, 0xe8, 0x75, 0x90, 0x4d, 0xdd, 0xf9,
+ 0x46, 0xf7, 0x42, 0x34, 0xf7, 0x45, 0x58, 0x0c, 0x11, 0x0d, 0x84, 0xb0,
+ 0xc6, 0xda, 0x5d, 0x3e, 0xf9, 0x01, 0x9e, 0xe7, 0xe1, 0xda, 0x55, 0x95,
+ 0xbe, 0x74, 0x1c, 0x7b, 0xfc, 0x4d, 0x14, 0x4f, 0xac, 0x7e, 0x55, 0x47,
+ 0x7d, 0x7b, 0xf4, 0xa5, 0x0d, 0x49, 0x1e, 0x95, 0xe8, 0xf7, 0x12, 0xc1,
+ 0xcc, 0xff, 0x76, 0xa6, 0x25, 0x47, 0xd0, 0xf3, 0x75, 0x35, 0xbe, 0x97,
+ 0xb7, 0x58, 0x16, 0xeb, 0xaa, 0x5c, 0x78, 0x6f, 0xec, 0x53, 0x30, 0xaf,
+ 0xea, 0x04, 0x4d, 0xcc, 0xa9, 0x02, 0xe3, 0xf0, 0xb6, 0x04, 0x12, 0xf6,
+ 0x30, 0xb1, 0x11, 0x3d, 0x90, 0x4e, 0x56, 0x64, 0xd7, 0xdc, 0x3c, 0x43,
+ 0x5f, 0x73, 0x39, 0xef, 0x4b, 0xaf, 0x87, 0xeb, 0xf6, 0xfe, 0x68, 0x88,
+ 0x44, 0x72, 0xea, 0xd2, 0x07, 0xc6, 0x69, 0xb0, 0xc1, 0xa1, 0x8b, 0xef,
+ 0x17, 0x49, 0xd7, 0x61, 0xb1, 0x45, 0x48, 0x5f, 0x3b, 0x20, 0x21, 0xe9,
+ 0x5b, 0xb2, 0xcc, 0xf4, 0xd7, 0xe9, 0x31, 0xf5, 0x0b, 0x15, 0x61, 0x3b,
+ 0x7a, 0x94, 0xe3, 0xeb, 0xd9, 0xbc, 0x7f, 0x94, 0xae, 0x6a, 0xe3, 0x62,
+ 0x62, 0x96, 0xa8, 0x64, 0x7c, 0xb8, 0x87, 0xf3, 0x99, 0x32, 0x7e, 0x92,
+ 0xa2, 0x52, 0xbe, 0xbb, 0xf8, 0x65, 0xcf, 0xc9, 0xf2, 0x30, 0xfc, 0x8b,
+ 0xc1, 0xc2, 0xa6, 0x96, 0xd7, 0x5f, 0x89, 0xe1, 0x5c, 0x34, 0x80, 0xf5,
+ 0x8f, 0x47, 0x07, 0x2f, 0xb4, 0x91, 0xbf, 0xb1, 0xa2, 0x7e, 0x5f, 0x4b,
+ 0x5a, 0xd0, 0x5b, 0x9f, 0x24, 0x86, 0x05, 0x51, 0x5a, 0x69, 0x03, 0x65,
+ 0x43, 0x49, 0x71, 0xc5, 0xe0, 0x6f, 0x94, 0x34, 0x6b, 0xf6, 0x1b, 0xd8,
+ 0xa9, 0xb0, 0x4c, 0x7e, 0x53, 0xeb, 0x8f, 0x48, 0xdf, 0xca, 0x33, 0xb5,
+ 0x48, 0xfa, 0x36, 0x4a, 0x1a, 0x53, 0xa6, 0x33, 0x0c, 0xd0, 0x89, 0xcd,
+ 0x49, 0x15, 0xcd, 0x89, 0x31, 0x3c, 0x90, 0xc0, 0x72, 0xd7, 0x65, 0x4b,
+ 0x52, 0x35, 0x8a, 0x46, 0x11, 0x44, 0xb9, 0x3d, 0x8e, 0x28, 0x65, 0xa6,
+ 0x3e, 0x79, 0x9e, 0x5c, 0x08, 0x44, 0x29, 0xad, 0xb0, 0x35, 0x11, 0x2e,
+ 0x21, 0x4e, 0xb8, 0xd2, 0xe7, 0x10, 0x3e, 0x5d, 0x84, 0x83, 0xb3, 0xc3,
+ 0xc2, 0xe4, 0xd2, 0xc6, 0xfd, 0x09, 0x4b, 0x74, 0x09, 0xdd, 0xf1, 0xb3,
+ 0xd3, 0x19, 0x3e, 0x80, 0x0d, 0xa2, 0x0b, 0x19, 0xf0, 0x38, 0xe7, 0xc5,
+ 0xc2, 0xaf, 0xe2, 0x23, 0xdb, 0x61, 0xe2, 0x9d, 0x5c, 0x6e, 0x20, 0x89,
+ 0x49, 0x2e, 0x23, 0x6a, 0xb2, 0x62, 0xc1, 0x45, 0xb4, 0x9f, 0xaf, 0x8b,
+ 0xa7, 0xf1, 0x22, 0x3b, 0xf8, 0x7d, 0xe2, 0x90, 0xd0, 0x7a, 0x19, 0xfb,
+ 0x4a, 0x4c, 0xe3, 0xd2, 0x7d, 0x5f, 0x4a, 0x83, 0x03, 0xed, 0x27, 0xd6,
+ 0x23, 0x9e, 0x6b, 0x8d, 0xb4, 0x59, 0xa2, 0xd9, 0xef, 0x6c, 0x82, 0x29,
+ 0xdd, 0x75, 0x19, 0x3c, 0x3f, 0x4c, 0x10, 0x8d, 0xef, 0xbb, 0x75, 0x27,
+ 0xd2, 0xae, 0x83, 0xa7, 0xa8, 0xce, 0x5b, 0xa7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 26 (0x1a)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Certification Authority
+ Validity
+ Not Before: Oct 24 20:57:09 2007 GMT
+ Not After : Oct 24 20:57:09 2017 GMT
+ Subject: C=IL, O=StartCom Ltd., OU=Secure Digital Certificate Signing, CN=StartCom Class 2 Primary Intermediate Server CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:e2:4f:39:2f:a1:8c:9a:85:ad:08:0e:08:3e:57:
+ f2:88:01:21:1b:94:a9:6c:e2:b8:db:aa:19:18:46:
+ 3a:52:a1:f5:0f:f4:6e:8c:ea:96:8c:96:87:79:13:
+ 40:51:2f:22:f2:0c:8b:87:0f:65:df:71:74:34:43:
+ 55:b1:35:09:9b:d9:bc:1f:fa:eb:42:d0:97:40:72:
+ b7:43:96:3d:ba:96:9d:5d:50:02:1c:9b:91:8d:9c:
+ c0:ac:d7:bb:2f:17:d7:cb:3e:82:9d:73:eb:07:42:
+ 92:b2:cd:64:b3:74:55:1b:b4:4b:86:21:2c:f7:78:
+ 87:32:e0:16:e4:da:bd:4c:95:ea:a4:0a:7e:b6:0a:
+ 0d:2e:8a:cf:55:ab:c3:e5:dd:41:8a:4e:e6:6f:65:
+ 6c:b2:40:cf:17:5d:b9:c3:6a:0b:27:11:84:77:61:
+ f6:c2:7c:ed:c0:8d:78:14:18:99:81:99:75:63:b7:
+ e8:53:d3:ba:61:e9:0e:fa:a2:30:f3:46:a2:b9:c9:
+ 1f:6c:80:5a:40:ac:27:ed:48:47:33:b0:54:c6:46:
+ 1a:f3:35:61:c1:02:29:90:54:7e:64:4d:c4:30:52:
+ 02:82:d7:df:ce:21:6e:18:91:d7:b8:ab:8c:27:17:
+ b5:f0:a3:01:2f:8e:d2:2e:87:3a:3d:b4:29:67:8a:
+ c4:03
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 11:DB:23:45:FD:54:CC:6A:71:6F:84:8A:03:D7:BE:F7:01:2F:26:86
+ X509v3 Authority Key Identifier:
+ keyid:4E:0B:EF:1A:A4:40:5B:A5:17:69:87:30:CA:34:68:43:D0:41:AE:F2
+
+ Authority Information Access:
+ OCSP - URI:http://ocsp.startssl.com/ca
+ CA Issuers - URI:http://www.startssl.com/sfsca.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://www.startssl.com/sfsca.crl
+
+ Full Name:
+ URI:http://crl.startssl.com/sfsca.crl
+
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.23223.1.2.1
+ CPS: http://www.startssl.com/policy.pdf
+ CPS: http://www.startssl.com/intermediate.pdf
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 9d:07:e1:ee:90:76:31:67:16:45:70:8c:cb:84:8b:4b:57:68:
+ 44:a5:89:c1:f2:7e:cb:28:8b:f5:e7:70:77:d5:b6:f4:0b:21:
+ 60:a5:a1:74:73:24:22:80:d6:d8:ba:8d:a2:62:5d:09:35:42:
+ 29:fb:39:63:45:0b:a4:b0:38:1a:68:f4:95:13:cc:e0:43:94:
+ ec:eb:39:1a:ec:57:29:d9:99:6d:f5:84:cd:8e:73:ae:c9:dc:
+ 6a:fa:9e:9d:16:64:93:08:c7:1c:c2:89:54:9e:77:80:90:f6:
+ b9:29:76:eb:13:67:48:59:f8:2e:3a:31:b8:c9:d3:88:e5:5f:
+ 4e:d2:19:3d:43:8e:d7:92:ff:cf:38:b6:e1:5b:8a:53:1d:ce:
+ ac:b4:76:2f:d8:f7:40:63:d5:ee:69:f3:45:7d:a0:62:c1:61:
+ c3:75:ed:b2:7b:4d:ac:21:27:30:4e:59:46:6a:93:17:ca:c8:
+ 39:2d:01:73:65:5b:e9:41:9b:11:17:9c:c8:c8:4a:ef:a1:76:
+ 60:2d:ae:93:ff:0c:d5:33:13:9f:4f:13:ce:dd:86:f1:fc:f8:
+ 35:54:15:a8:5b:e7:85:7e:fa:37:09:ff:8b:b8:31:49:9e:0d:
+ 6e:de:b4:d2:12:2d:b8:ed:c8:c3:f1:b6:42:a0:4c:97:79:df:
+ fe:c3:a3:9f:a1:f4:6d:2c:84:77:a4:a2:05:e1:17:ff:31:dd:
+ 9a:f3:b8:7a:c3:52:c2:11:11:b7:50:31:8a:7f:cc:e7:5a:89:
+ cc:f7:86:9a:61:92:4f:2f:94:b6:98:c7:78:e0:62:4b:43:7d:
+ 3c:de:d6:9a:b4:10:a1:40:9c:4b:2a:dc:b8:d0:d4:9e:fd:f1:
+ 84:78:1b:0e:57:8f:69:54:42:68:7b:ea:a0:ef:75:0f:07:a2:
+ 8c:73:99:ab:55:f5:07:09:d2:af:38:03:6a:90:03:0c:2f:8f:
+ e2:e8:43:c2:31:e9:6f:ad:87:e5:8d:bd:4e:2c:89:4b:51:e6:
+ 9c:4c:54:76:c0:12:81:53:9b:ec:a0:fc:2c:9c:da:18:95:6e:
+ 1e:38:26:42:27:78:60:08:df:7f:6d:32:e8:d8:c0:6f:1f:eb:
+ 26:75:9f:93:fc:7b:1b:fe:35:90:dc:53:a3:07:a6:3f:83:55:
+ 0a:2b:4e:62:82:25:ce:66:30:5d:2c:e0:f9:19:1b:75:b9:9d:
+ 98:56:a6:83:27:7a:d1:8f:8d:59:93:fc:3f:73:d7:2e:b4:2c:
+ 95:d8:8b:f7:c9:7e:c7:fc:9d:ac:72:04:1f:d2:cc:17:f4:ed:
+ 34:60:9b:9e:4a:97:04:fe:dd:72:0e:57:54:51:06:70:4d:ef:
+ aa:1c:a4:82:e0:33:c7:f4
+-----BEGIN CERTIFICATE-----
+MIIGNDCCBBygAwIBAgIBGjANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
+MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
+Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjA1NzA5WhcNMTcxMDI0MjA1NzA5WjCB
+jDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsT
+IlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0
+YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgU2VydmVyIENBMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4k85L6GMmoWtCA4IPlfyiAEh
+G5SpbOK426oZGEY6UqH1D/RujOqWjJaHeRNAUS8i8gyLhw9l33F0NENVsTUJm9m8
+H/rrQtCXQHK3Q5Y9upadXVACHJuRjZzArNe7LxfXyz6CnXPrB0KSss1ks3RVG7RL
+hiEs93iHMuAW5Nq9TJXqpAp+tgoNLorPVavD5d1Bik7mb2VsskDPF125w2oLJxGE
+d2H2wnztwI14FBiZgZl1Y7foU9O6YekO+qIw80aiuckfbIBaQKwn7UhHM7BUxkYa
+8zVhwQIpkFR+ZE3EMFICgtffziFuGJHXuKuMJxe18KMBL47SLoc6PbQpZ4rEAwID
+AQABo4IBrTCCAakwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
+VR0OBBYEFBHbI0X9VMxqcW+EigPXvvcBLyaGMB8GA1UdIwQYMBaAFE4L7xqkQFul
+F2mHMMo0aEPQQa7yMGYGCCsGAQUFBwEBBFowWDAnBggrBgEFBQcwAYYbaHR0cDov
+L29jc3Auc3RhcnRzc2wuY29tL2NhMC0GCCsGAQUFBzAChiFodHRwOi8vd3d3LnN0
+YXJ0c3NsLmNvbS9zZnNjYS5jcnQwWwYDVR0fBFQwUjAnoCWgI4YhaHR0cDovL3d3
+dy5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMCegJaAjhiFodHRwOi8vY3JsLnN0YXJ0
+c3NsLmNvbS9zZnNjYS5jcmwwgYAGA1UdIAR5MHcwdQYLKwYBBAGBtTcBAgEwZjAu
+BggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9saWN5LnBkZjA0
+BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50ZXJtZWRpYXRl
+LnBkZjANBgkqhkiG9w0BAQUFAAOCAgEAnQfh7pB2MWcWRXCMy4SLS1doRKWJwfJ+
+yyiL9edwd9W29AshYKWhdHMkIoDW2LqNomJdCTVCKfs5Y0ULpLA4Gmj0lRPM4EOU
+7Os5GuxXKdmZbfWEzY5zrsncavqenRZkkwjHHMKJVJ53gJD2uSl26xNnSFn4Ljox
+uMnTiOVfTtIZPUOO15L/zzi24VuKUx3OrLR2L9j3QGPV7mnzRX2gYsFhw3XtsntN
+rCEnME5ZRmqTF8rIOS0Bc2Vb6UGbERecyMhK76F2YC2uk/8M1TMTn08Tzt2G8fz4
+NVQVqFvnhX76Nwn/i7gxSZ4Nbt600hItuO3Iw/G2QqBMl3nf/sOjn6H0bSyEd6Si
+BeEX/zHdmvO4esNSwhERt1Axin/M51qJzPeGmmGSTy+UtpjHeOBiS0N9PN7WmrQQ
+oUCcSyrcuNDUnv3xhHgbDlePaVRCaHvqoO91DweijHOZq1X1BwnSrzgDapADDC+P
+4uhDwjHpb62H5Y29TiyJS1HmnExUdsASgVOb7KD8LJzaGJVuHjgmQid4YAjff20y
+6NjAbx/rJnWfk/x7G/41kNxTowemP4NVCitOYoIlzmYwXSzg+RkbdbmdmFamgyd6
+0Y+NWZP8P3PXLrQsldiL98l+x/ydrHIEH9LMF/TtNGCbnkqXBP7dcg5XVFEGcE3v
+qhykguAzx/Q=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert97[] = {
+ 0x30, 0x82, 0x06, 0x34, 0x30, 0x82, 0x04, 0x1c, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x01, 0x1a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x7d, 0x31, 0x0b, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16,
+ 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61,
+ 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b,
+ 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63,
+ 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20,
+ 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+ 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x29, 0x30, 0x27, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43,
+ 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+ 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x30, 0x32, 0x34,
+ 0x32, 0x30, 0x35, 0x37, 0x30, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x31,
+ 0x30, 0x32, 0x34, 0x32, 0x30, 0x35, 0x37, 0x30, 0x39, 0x5a, 0x30, 0x81,
+ 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+ 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74,
+ 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
+ 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+ 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31,
+ 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73,
+ 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49,
+ 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20,
+ 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01,
+ 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0x4f, 0x39, 0x2f, 0xa1, 0x8c,
+ 0x9a, 0x85, 0xad, 0x08, 0x0e, 0x08, 0x3e, 0x57, 0xf2, 0x88, 0x01, 0x21,
+ 0x1b, 0x94, 0xa9, 0x6c, 0xe2, 0xb8, 0xdb, 0xaa, 0x19, 0x18, 0x46, 0x3a,
+ 0x52, 0xa1, 0xf5, 0x0f, 0xf4, 0x6e, 0x8c, 0xea, 0x96, 0x8c, 0x96, 0x87,
+ 0x79, 0x13, 0x40, 0x51, 0x2f, 0x22, 0xf2, 0x0c, 0x8b, 0x87, 0x0f, 0x65,
+ 0xdf, 0x71, 0x74, 0x34, 0x43, 0x55, 0xb1, 0x35, 0x09, 0x9b, 0xd9, 0xbc,
+ 0x1f, 0xfa, 0xeb, 0x42, 0xd0, 0x97, 0x40, 0x72, 0xb7, 0x43, 0x96, 0x3d,
+ 0xba, 0x96, 0x9d, 0x5d, 0x50, 0x02, 0x1c, 0x9b, 0x91, 0x8d, 0x9c, 0xc0,
+ 0xac, 0xd7, 0xbb, 0x2f, 0x17, 0xd7, 0xcb, 0x3e, 0x82, 0x9d, 0x73, 0xeb,
+ 0x07, 0x42, 0x92, 0xb2, 0xcd, 0x64, 0xb3, 0x74, 0x55, 0x1b, 0xb4, 0x4b,
+ 0x86, 0x21, 0x2c, 0xf7, 0x78, 0x87, 0x32, 0xe0, 0x16, 0xe4, 0xda, 0xbd,
+ 0x4c, 0x95, 0xea, 0xa4, 0x0a, 0x7e, 0xb6, 0x0a, 0x0d, 0x2e, 0x8a, 0xcf,
+ 0x55, 0xab, 0xc3, 0xe5, 0xdd, 0x41, 0x8a, 0x4e, 0xe6, 0x6f, 0x65, 0x6c,
+ 0xb2, 0x40, 0xcf, 0x17, 0x5d, 0xb9, 0xc3, 0x6a, 0x0b, 0x27, 0x11, 0x84,
+ 0x77, 0x61, 0xf6, 0xc2, 0x7c, 0xed, 0xc0, 0x8d, 0x78, 0x14, 0x18, 0x99,
+ 0x81, 0x99, 0x75, 0x63, 0xb7, 0xe8, 0x53, 0xd3, 0xba, 0x61, 0xe9, 0x0e,
+ 0xfa, 0xa2, 0x30, 0xf3, 0x46, 0xa2, 0xb9, 0xc9, 0x1f, 0x6c, 0x80, 0x5a,
+ 0x40, 0xac, 0x27, 0xed, 0x48, 0x47, 0x33, 0xb0, 0x54, 0xc6, 0x46, 0x1a,
+ 0xf3, 0x35, 0x61, 0xc1, 0x02, 0x29, 0x90, 0x54, 0x7e, 0x64, 0x4d, 0xc4,
+ 0x30, 0x52, 0x02, 0x82, 0xd7, 0xdf, 0xce, 0x21, 0x6e, 0x18, 0x91, 0xd7,
+ 0xb8, 0xab, 0x8c, 0x27, 0x17, 0xb5, 0xf0, 0xa3, 0x01, 0x2f, 0x8e, 0xd2,
+ 0x2e, 0x87, 0x3a, 0x3d, 0xb4, 0x29, 0x67, 0x8a, 0xc4, 0x03, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xad, 0x30, 0x82, 0x01, 0xa9, 0x30,
+ 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05, 0x30,
+ 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01,
+ 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03,
+ 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x11, 0xdb, 0x23, 0x45, 0xfd,
+ 0x54, 0xcc, 0x6a, 0x71, 0x6f, 0x84, 0x8a, 0x03, 0xd7, 0xbe, 0xf7, 0x01,
+ 0x2f, 0x26, 0x86, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
+ 0x30, 0x16, 0x80, 0x14, 0x4e, 0x0b, 0xef, 0x1a, 0xa4, 0x40, 0x5b, 0xa5,
+ 0x17, 0x69, 0x87, 0x30, 0xca, 0x34, 0x68, 0x43, 0xd0, 0x41, 0xae, 0xf2,
+ 0x30, 0x66, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01,
+ 0x04, 0x5a, 0x30, 0x58, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x30, 0x01, 0x86, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73,
+ 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x30, 0x2d, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x21, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73,
+ 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x5b, 0x06, 0x03,
+ 0x55, 0x1d, 0x1f, 0x04, 0x54, 0x30, 0x52, 0x30, 0x27, 0xa0, 0x25, 0xa0,
+ 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77,
+ 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x6c,
+ 0x30, 0x27, 0xa0, 0x25, 0xa0, 0x23, 0x86, 0x21, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x66, 0x73, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x80, 0x06, 0x03, 0x55, 0x1d,
+ 0x20, 0x04, 0x79, 0x30, 0x77, 0x30, 0x75, 0x06, 0x0b, 0x2b, 0x06, 0x01,
+ 0x04, 0x01, 0x81, 0xb5, 0x37, 0x01, 0x02, 0x01, 0x30, 0x66, 0x30, 0x2e,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x22,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70, 0x64, 0x66, 0x30, 0x34,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x28,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65,
+ 0x2e, 0x70, 0x64, 0x66, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00,
+ 0x9d, 0x07, 0xe1, 0xee, 0x90, 0x76, 0x31, 0x67, 0x16, 0x45, 0x70, 0x8c,
+ 0xcb, 0x84, 0x8b, 0x4b, 0x57, 0x68, 0x44, 0xa5, 0x89, 0xc1, 0xf2, 0x7e,
+ 0xcb, 0x28, 0x8b, 0xf5, 0xe7, 0x70, 0x77, 0xd5, 0xb6, 0xf4, 0x0b, 0x21,
+ 0x60, 0xa5, 0xa1, 0x74, 0x73, 0x24, 0x22, 0x80, 0xd6, 0xd8, 0xba, 0x8d,
+ 0xa2, 0x62, 0x5d, 0x09, 0x35, 0x42, 0x29, 0xfb, 0x39, 0x63, 0x45, 0x0b,
+ 0xa4, 0xb0, 0x38, 0x1a, 0x68, 0xf4, 0x95, 0x13, 0xcc, 0xe0, 0x43, 0x94,
+ 0xec, 0xeb, 0x39, 0x1a, 0xec, 0x57, 0x29, 0xd9, 0x99, 0x6d, 0xf5, 0x84,
+ 0xcd, 0x8e, 0x73, 0xae, 0xc9, 0xdc, 0x6a, 0xfa, 0x9e, 0x9d, 0x16, 0x64,
+ 0x93, 0x08, 0xc7, 0x1c, 0xc2, 0x89, 0x54, 0x9e, 0x77, 0x80, 0x90, 0xf6,
+ 0xb9, 0x29, 0x76, 0xeb, 0x13, 0x67, 0x48, 0x59, 0xf8, 0x2e, 0x3a, 0x31,
+ 0xb8, 0xc9, 0xd3, 0x88, 0xe5, 0x5f, 0x4e, 0xd2, 0x19, 0x3d, 0x43, 0x8e,
+ 0xd7, 0x92, 0xff, 0xcf, 0x38, 0xb6, 0xe1, 0x5b, 0x8a, 0x53, 0x1d, 0xce,
+ 0xac, 0xb4, 0x76, 0x2f, 0xd8, 0xf7, 0x40, 0x63, 0xd5, 0xee, 0x69, 0xf3,
+ 0x45, 0x7d, 0xa0, 0x62, 0xc1, 0x61, 0xc3, 0x75, 0xed, 0xb2, 0x7b, 0x4d,
+ 0xac, 0x21, 0x27, 0x30, 0x4e, 0x59, 0x46, 0x6a, 0x93, 0x17, 0xca, 0xc8,
+ 0x39, 0x2d, 0x01, 0x73, 0x65, 0x5b, 0xe9, 0x41, 0x9b, 0x11, 0x17, 0x9c,
+ 0xc8, 0xc8, 0x4a, 0xef, 0xa1, 0x76, 0x60, 0x2d, 0xae, 0x93, 0xff, 0x0c,
+ 0xd5, 0x33, 0x13, 0x9f, 0x4f, 0x13, 0xce, 0xdd, 0x86, 0xf1, 0xfc, 0xf8,
+ 0x35, 0x54, 0x15, 0xa8, 0x5b, 0xe7, 0x85, 0x7e, 0xfa, 0x37, 0x09, 0xff,
+ 0x8b, 0xb8, 0x31, 0x49, 0x9e, 0x0d, 0x6e, 0xde, 0xb4, 0xd2, 0x12, 0x2d,
+ 0xb8, 0xed, 0xc8, 0xc3, 0xf1, 0xb6, 0x42, 0xa0, 0x4c, 0x97, 0x79, 0xdf,
+ 0xfe, 0xc3, 0xa3, 0x9f, 0xa1, 0xf4, 0x6d, 0x2c, 0x84, 0x77, 0xa4, 0xa2,
+ 0x05, 0xe1, 0x17, 0xff, 0x31, 0xdd, 0x9a, 0xf3, 0xb8, 0x7a, 0xc3, 0x52,
+ 0xc2, 0x11, 0x11, 0xb7, 0x50, 0x31, 0x8a, 0x7f, 0xcc, 0xe7, 0x5a, 0x89,
+ 0xcc, 0xf7, 0x86, 0x9a, 0x61, 0x92, 0x4f, 0x2f, 0x94, 0xb6, 0x98, 0xc7,
+ 0x78, 0xe0, 0x62, 0x4b, 0x43, 0x7d, 0x3c, 0xde, 0xd6, 0x9a, 0xb4, 0x10,
+ 0xa1, 0x40, 0x9c, 0x4b, 0x2a, 0xdc, 0xb8, 0xd0, 0xd4, 0x9e, 0xfd, 0xf1,
+ 0x84, 0x78, 0x1b, 0x0e, 0x57, 0x8f, 0x69, 0x54, 0x42, 0x68, 0x7b, 0xea,
+ 0xa0, 0xef, 0x75, 0x0f, 0x07, 0xa2, 0x8c, 0x73, 0x99, 0xab, 0x55, 0xf5,
+ 0x07, 0x09, 0xd2, 0xaf, 0x38, 0x03, 0x6a, 0x90, 0x03, 0x0c, 0x2f, 0x8f,
+ 0xe2, 0xe8, 0x43, 0xc2, 0x31, 0xe9, 0x6f, 0xad, 0x87, 0xe5, 0x8d, 0xbd,
+ 0x4e, 0x2c, 0x89, 0x4b, 0x51, 0xe6, 0x9c, 0x4c, 0x54, 0x76, 0xc0, 0x12,
+ 0x81, 0x53, 0x9b, 0xec, 0xa0, 0xfc, 0x2c, 0x9c, 0xda, 0x18, 0x95, 0x6e,
+ 0x1e, 0x38, 0x26, 0x42, 0x27, 0x78, 0x60, 0x08, 0xdf, 0x7f, 0x6d, 0x32,
+ 0xe8, 0xd8, 0xc0, 0x6f, 0x1f, 0xeb, 0x26, 0x75, 0x9f, 0x93, 0xfc, 0x7b,
+ 0x1b, 0xfe, 0x35, 0x90, 0xdc, 0x53, 0xa3, 0x07, 0xa6, 0x3f, 0x83, 0x55,
+ 0x0a, 0x2b, 0x4e, 0x62, 0x82, 0x25, 0xce, 0x66, 0x30, 0x5d, 0x2c, 0xe0,
+ 0xf9, 0x19, 0x1b, 0x75, 0xb9, 0x9d, 0x98, 0x56, 0xa6, 0x83, 0x27, 0x7a,
+ 0xd1, 0x8f, 0x8d, 0x59, 0x93, 0xfc, 0x3f, 0x73, 0xd7, 0x2e, 0xb4, 0x2c,
+ 0x95, 0xd8, 0x8b, 0xf7, 0xc9, 0x7e, 0xc7, 0xfc, 0x9d, 0xac, 0x72, 0x04,
+ 0x1f, 0xd2, 0xcc, 0x17, 0xf4, 0xed, 0x34, 0x60, 0x9b, 0x9e, 0x4a, 0x97,
+ 0x04, 0xfe, 0xdd, 0x72, 0x0e, 0x57, 0x54, 0x51, 0x06, 0x70, 0x4d, 0xef,
+ 0xaa, 0x1c, 0xa4, 0x82, 0xe0, 0x33, 0xc7, 0xf4,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 08:51:f9:59:81:41:45:ca:bd:e0:24:e2:12:c9:c2:0e
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Apr 3 00:00:00 2007 GMT
+ Not After : Apr 3 00:00:00 2022 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e:
+ fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9:
+ 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29:
+ 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f:
+ 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f:
+ a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf:
+ 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42:
+ 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12:
+ a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a:
+ 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03:
+ af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a:
+ aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd:
+ 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f:
+ e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed:
+ cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28:
+ 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c:
+ 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e:
+ 5e:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.1.3.0.2
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+ User Notice:
+ Explicit Text:
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ X509v3 Subject Key Identifier:
+ 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7
+ Signature Algorithm: sha1WithRSAEncryption
+ 5d:4f:84:f1:a8:88:d3:a3:b2:bc:9c:6d:e5:29:49:77:e1:e7:
+ d6:dc:a9:d8:35:ae:c9:71:dc:e5:db:dc:9d:24:21:90:a6:cf:
+ b7:01:1c:9b:d4:57:97:91:d7:75:16:a5:12:d7:b9:3d:2e:89:
+ 3d:39:69:8a:d6:35:37:f9:f1:21:c4:5b:40:ad:59:a9:2f:5f:
+ 3a:00:29:43:27:71:03:e4:bd:30:32:55:a6:fe:84:0e:0b:9b:
+ 38:19:2c:43:7c:ac:43:bf:75:31:e5:23:1c:45:55:b7:69:08:
+ 91:b5:cf:d7:d5:b1:5e:ee:9f:94:e4:d6:7a:b9:18:c3:b8:d6:
+ 52:63:1c:10:ba:8b:2f:6d:5d:cc:05:38:f4:56:05:6d:ef:9e:
+ ec:e8:61:36:0c:14:4b:85:14:5a:0c:83:4f:22:5c:59:cb:8c:
+ 8a:71:da:fa:c5:10:84:58:cf:07:ee:e3:90:c2:f5:f9:29:c7:
+ 5a:23:71:f9:59:b4:64:2b:88:b0:a7:36:c7:9a:20:61:eb:fa:
+ 4e:b5:ae:6b:1b:e4:e3:ec:e2:d9:3c:41:49:a8:20:a4:54:f5:
+ 92:8d:bb:c0:55:20:04:a6:d8:b0:17:16:cc:e3:d0:c8:b4:3d:
+ e5:d9:84:c6:d3:f6:6e:6d:78:c9:79:43:e8:7a:37:ff:5c:35:
+ 49:bf:a1:c5
+-----BEGIN CERTIFICATE-----
+MIIGVTCCBT2gAwIBAgIQCFH5WYFBRcq94CTiEsnCDjANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA3MDQwMzAwMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR
+CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv
+KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5
+BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf
+1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs
+zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d
+32duXvsCAwEAAaOCAvcwggLzMA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w
+ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3
+LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH
+AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy
+AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj
+AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg
+AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ
+AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt
+AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj
+AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl
+AHIAZQBuAGMAZQAuMA8GA1UdEwEB/wQFMAMBAf8wNAYIKwYBBQUHAQEEKDAmMCQG
+CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSBhzCB
+hDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFz
+c3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQu
+Y29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSMEGDAW
+gBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUBINTe
+eZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAF1PhPGoiNOjsrycbeUpSXfh59bcqdg1
+rslx3OXb3J0kIZCmz7cBHJvUV5eR13UWpRLXuT0uiT05aYrWNTf58SHEW0CtWakv
+XzoAKUMncQPkvTAyVab+hA4LmzgZLEN8rEO/dTHlIxxFVbdpCJG1z9fVsV7un5Tk
+1nq5GMO41lJjHBC6iy9tXcwFOPRWBW3vnuzoYTYMFEuFFFoMg08iXFnLjIpx2vrF
+EIRYzwfu45DC9fkpx1ojcflZtGQriLCnNseaIGHr+k61rmsb5OPs4tk8QUmoIKRU
+9ZKNu8BVIASm2LAXFszj0Mi0PeXZhMbT9m5teMl5Q+h6N/9cNUm/ocU=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert98[] = {
+ 0x30, 0x82, 0x06, 0x55, 0x30, 0x82, 0x05, 0x3d, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x08, 0x51, 0xf9, 0x59, 0x81, 0x41, 0x45, 0xca, 0xbd,
+ 0xe0, 0x24, 0xe2, 0x12, 0xc9, 0xc2, 0x0e, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x30, 0x34, 0x30, 0x33, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x30,
+ 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67,
+ 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x43, 0x41, 0x2d, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xbf, 0x61, 0x0a, 0x29, 0x10, 0x1f, 0x5e, 0xfe, 0x34, 0x37, 0x51,
+ 0x08, 0xf8, 0x1e, 0xfb, 0x22, 0xed, 0x61, 0xbe, 0x0b, 0x0d, 0x70, 0x4c,
+ 0x50, 0x63, 0x26, 0x75, 0x15, 0xb9, 0x41, 0x88, 0x97, 0xb6, 0xf0, 0xa0,
+ 0x15, 0xbb, 0x08, 0x60, 0xe0, 0x42, 0xe8, 0x05, 0x29, 0x10, 0x87, 0x36,
+ 0x8a, 0x28, 0x65, 0xa8, 0xef, 0x31, 0x07, 0x74, 0x6d, 0x36, 0x97, 0x2f,
+ 0x28, 0x46, 0x66, 0x04, 0xc7, 0x2a, 0x79, 0x26, 0x7a, 0x99, 0xd5, 0x8e,
+ 0xc3, 0x6d, 0x4f, 0xa0, 0x5e, 0xad, 0xbc, 0x3d, 0x91, 0xc2, 0x59, 0x7b,
+ 0x5e, 0x36, 0x6c, 0xc0, 0x53, 0xcf, 0x00, 0x08, 0x32, 0x3e, 0x10, 0x64,
+ 0x58, 0x10, 0x13, 0x69, 0xc7, 0x0c, 0xee, 0x9c, 0x42, 0x51, 0x00, 0xf9,
+ 0x05, 0x44, 0xee, 0x24, 0xce, 0x7a, 0x1f, 0xed, 0x8c, 0x11, 0xbd, 0x12,
+ 0xa8, 0xf3, 0x15, 0xf4, 0x1c, 0x7a, 0x31, 0x69, 0x01, 0x1b, 0xa7, 0xe6,
+ 0x5d, 0xc0, 0x9a, 0x6c, 0x7e, 0x09, 0x9e, 0xe7, 0x52, 0x44, 0x4a, 0x10,
+ 0x3a, 0x23, 0xe4, 0x9b, 0xb6, 0x03, 0xaf, 0xa8, 0x9c, 0xb4, 0x5b, 0x9f,
+ 0xd4, 0x4b, 0xad, 0x92, 0x8c, 0xce, 0xb5, 0x11, 0x2a, 0xaa, 0x37, 0x18,
+ 0x8d, 0xb4, 0xc2, 0xb8, 0xd8, 0x5c, 0x06, 0x8c, 0xf8, 0xff, 0x23, 0xbd,
+ 0x35, 0x5e, 0xd4, 0x7c, 0x3e, 0x7e, 0x83, 0x0e, 0x91, 0x96, 0x05, 0x98,
+ 0xc3, 0xb2, 0x1f, 0xe3, 0xc8, 0x65, 0xeb, 0xa9, 0x7b, 0x5d, 0xa0, 0x2c,
+ 0xcc, 0xfc, 0x3c, 0xd9, 0x6d, 0xed, 0xcc, 0xfa, 0x4b, 0x43, 0x8c, 0xc9,
+ 0xd4, 0xb8, 0xa5, 0x61, 0x1c, 0xb2, 0x40, 0xb6, 0x28, 0x12, 0xdf, 0xb9,
+ 0xf8, 0x5f, 0xfe, 0xd3, 0xb2, 0xc9, 0xef, 0x3d, 0xb4, 0x1e, 0x4b, 0x7c,
+ 0x1c, 0x4c, 0x99, 0x36, 0x9e, 0x3d, 0xeb, 0xec, 0xa7, 0x68, 0x5e, 0x1d,
+ 0xdf, 0x67, 0x6e, 0x5e, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x02, 0xf7, 0x30, 0x82, 0x02, 0xf3, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x82,
+ 0x01, 0xc6, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x82, 0x01, 0xbd, 0x30,
+ 0x82, 0x01, 0xb9, 0x30, 0x82, 0x01, 0xb5, 0x06, 0x0b, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xfd, 0x6c, 0x01, 0x03, 0x00, 0x02, 0x30, 0x82, 0x01, 0xa4,
+ 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68, 0x74, 0x6d,
+ 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52, 0x00, 0x41,
+ 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65,
+ 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68,
+ 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72,
+ 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e,
+ 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75, 0x00, 0x74,
+ 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67,
+ 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x20,
+ 0x00, 0x43, 0x00, 0x50, 0x00, 0x2f, 0x00, 0x43, 0x00, 0x50, 0x00, 0x53,
+ 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6c,
+ 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x50,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x41,
+ 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65,
+ 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68, 0x00, 0x69,
+ 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x6d,
+ 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x61,
+ 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79,
+ 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x61,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x63,
+ 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x62,
+ 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x2e,
+ 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x05,
+ 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06,
+ 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68,
+ 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64,
+ 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30,
+ 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x87, 0x30, 0x81,
+ 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67,
+ 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73,
+ 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f,
+ 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40, 0xa0, 0x3e, 0xa0,
+ 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72,
+ 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74,
+ 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72,
+ 0x6c, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4,
+ 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x1d,
+ 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x50, 0xea, 0x73,
+ 0x89, 0xdb, 0x29, 0xfb, 0x10, 0x8f, 0x9e, 0xe5, 0x01, 0x20, 0xd4, 0xde,
+ 0x79, 0x99, 0x48, 0x83, 0xf7, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01,
+ 0x00, 0x5d, 0x4f, 0x84, 0xf1, 0xa8, 0x88, 0xd3, 0xa3, 0xb2, 0xbc, 0x9c,
+ 0x6d, 0xe5, 0x29, 0x49, 0x77, 0xe1, 0xe7, 0xd6, 0xdc, 0xa9, 0xd8, 0x35,
+ 0xae, 0xc9, 0x71, 0xdc, 0xe5, 0xdb, 0xdc, 0x9d, 0x24, 0x21, 0x90, 0xa6,
+ 0xcf, 0xb7, 0x01, 0x1c, 0x9b, 0xd4, 0x57, 0x97, 0x91, 0xd7, 0x75, 0x16,
+ 0xa5, 0x12, 0xd7, 0xb9, 0x3d, 0x2e, 0x89, 0x3d, 0x39, 0x69, 0x8a, 0xd6,
+ 0x35, 0x37, 0xf9, 0xf1, 0x21, 0xc4, 0x5b, 0x40, 0xad, 0x59, 0xa9, 0x2f,
+ 0x5f, 0x3a, 0x00, 0x29, 0x43, 0x27, 0x71, 0x03, 0xe4, 0xbd, 0x30, 0x32,
+ 0x55, 0xa6, 0xfe, 0x84, 0x0e, 0x0b, 0x9b, 0x38, 0x19, 0x2c, 0x43, 0x7c,
+ 0xac, 0x43, 0xbf, 0x75, 0x31, 0xe5, 0x23, 0x1c, 0x45, 0x55, 0xb7, 0x69,
+ 0x08, 0x91, 0xb5, 0xcf, 0xd7, 0xd5, 0xb1, 0x5e, 0xee, 0x9f, 0x94, 0xe4,
+ 0xd6, 0x7a, 0xb9, 0x18, 0xc3, 0xb8, 0xd6, 0x52, 0x63, 0x1c, 0x10, 0xba,
+ 0x8b, 0x2f, 0x6d, 0x5d, 0xcc, 0x05, 0x38, 0xf4, 0x56, 0x05, 0x6d, 0xef,
+ 0x9e, 0xec, 0xe8, 0x61, 0x36, 0x0c, 0x14, 0x4b, 0x85, 0x14, 0x5a, 0x0c,
+ 0x83, 0x4f, 0x22, 0x5c, 0x59, 0xcb, 0x8c, 0x8a, 0x71, 0xda, 0xfa, 0xc5,
+ 0x10, 0x84, 0x58, 0xcf, 0x07, 0xee, 0xe3, 0x90, 0xc2, 0xf5, 0xf9, 0x29,
+ 0xc7, 0x5a, 0x23, 0x71, 0xf9, 0x59, 0xb4, 0x64, 0x2b, 0x88, 0xb0, 0xa7,
+ 0x36, 0xc7, 0x9a, 0x20, 0x61, 0xeb, 0xfa, 0x4e, 0xb5, 0xae, 0x6b, 0x1b,
+ 0xe4, 0xe3, 0xec, 0xe2, 0xd9, 0x3c, 0x41, 0x49, 0xa8, 0x20, 0xa4, 0x54,
+ 0xf5, 0x92, 0x8d, 0xbb, 0xc0, 0x55, 0x20, 0x04, 0xa6, 0xd8, 0xb0, 0x17,
+ 0x16, 0xcc, 0xe3, 0xd0, 0xc8, 0xb4, 0x3d, 0xe5, 0xd9, 0x84, 0xc6, 0xd3,
+ 0xf6, 0x6e, 0x6d, 0x78, 0xc9, 0x79, 0x43, 0xe8, 0x7a, 0x37, 0xff, 0x5c,
+ 0x35, 0x49, 0xbf, 0xa1, 0xc5,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Apr 2 12:00:00 2008 GMT
+ Not After : Apr 3 00:00:00 2022 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e:
+ fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9:
+ 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29:
+ 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f:
+ 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f:
+ a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf:
+ 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42:
+ 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12:
+ a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a:
+ 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03:
+ af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a:
+ aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd:
+ 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f:
+ e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed:
+ cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28:
+ 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c:
+ 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e:
+ 5e:fb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.1.3.0.2
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+ User Notice:
+ Explicit Text:
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ X509v3 Subject Key Identifier:
+ 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7
+ Signature Algorithm: sha1WithRSAEncryption
+ 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43:
+ ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7:
+ fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3:
+ 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a:
+ 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4:
+ 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a:
+ 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d:
+ 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d:
+ f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e:
+ 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd:
+ 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d:
+ 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4:
+ 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d:
+ c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3:
+ 5a:76:f7:61
+-----BEGIN CERTIFICATE-----
+MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR
+CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv
+KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5
+BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf
+1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs
+zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d
+32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w
+ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3
+LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH
+AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy
+AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj
+AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg
+AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ
+AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt
+AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj
+AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl
+AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm
+MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB
+hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln
+aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl
+cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME
+GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB
+INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a
+vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j
+CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X
+dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE
+JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY
+Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E=
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert99[] = {
+ 0x30, 0x82, 0x06, 0x58, 0x30, 0x82, 0x05, 0x40, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x0a, 0x5f, 0x11, 0x4d, 0x03, 0x5b, 0x17, 0x91, 0x17,
+ 0xd2, 0xef, 0xd4, 0x03, 0x8c, 0x3f, 0x3b, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x34, 0x30, 0x32, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x30,
+ 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x66, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67,
+ 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x43, 0x41, 0x2d, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xbf, 0x61, 0x0a, 0x29, 0x10, 0x1f, 0x5e, 0xfe, 0x34, 0x37, 0x51,
+ 0x08, 0xf8, 0x1e, 0xfb, 0x22, 0xed, 0x61, 0xbe, 0x0b, 0x0d, 0x70, 0x4c,
+ 0x50, 0x63, 0x26, 0x75, 0x15, 0xb9, 0x41, 0x88, 0x97, 0xb6, 0xf0, 0xa0,
+ 0x15, 0xbb, 0x08, 0x60, 0xe0, 0x42, 0xe8, 0x05, 0x29, 0x10, 0x87, 0x36,
+ 0x8a, 0x28, 0x65, 0xa8, 0xef, 0x31, 0x07, 0x74, 0x6d, 0x36, 0x97, 0x2f,
+ 0x28, 0x46, 0x66, 0x04, 0xc7, 0x2a, 0x79, 0x26, 0x7a, 0x99, 0xd5, 0x8e,
+ 0xc3, 0x6d, 0x4f, 0xa0, 0x5e, 0xad, 0xbc, 0x3d, 0x91, 0xc2, 0x59, 0x7b,
+ 0x5e, 0x36, 0x6c, 0xc0, 0x53, 0xcf, 0x00, 0x08, 0x32, 0x3e, 0x10, 0x64,
+ 0x58, 0x10, 0x13, 0x69, 0xc7, 0x0c, 0xee, 0x9c, 0x42, 0x51, 0x00, 0xf9,
+ 0x05, 0x44, 0xee, 0x24, 0xce, 0x7a, 0x1f, 0xed, 0x8c, 0x11, 0xbd, 0x12,
+ 0xa8, 0xf3, 0x15, 0xf4, 0x1c, 0x7a, 0x31, 0x69, 0x01, 0x1b, 0xa7, 0xe6,
+ 0x5d, 0xc0, 0x9a, 0x6c, 0x7e, 0x09, 0x9e, 0xe7, 0x52, 0x44, 0x4a, 0x10,
+ 0x3a, 0x23, 0xe4, 0x9b, 0xb6, 0x03, 0xaf, 0xa8, 0x9c, 0xb4, 0x5b, 0x9f,
+ 0xd4, 0x4b, 0xad, 0x92, 0x8c, 0xce, 0xb5, 0x11, 0x2a, 0xaa, 0x37, 0x18,
+ 0x8d, 0xb4, 0xc2, 0xb8, 0xd8, 0x5c, 0x06, 0x8c, 0xf8, 0xff, 0x23, 0xbd,
+ 0x35, 0x5e, 0xd4, 0x7c, 0x3e, 0x7e, 0x83, 0x0e, 0x91, 0x96, 0x05, 0x98,
+ 0xc3, 0xb2, 0x1f, 0xe3, 0xc8, 0x65, 0xeb, 0xa9, 0x7b, 0x5d, 0xa0, 0x2c,
+ 0xcc, 0xfc, 0x3c, 0xd9, 0x6d, 0xed, 0xcc, 0xfa, 0x4b, 0x43, 0x8c, 0xc9,
+ 0xd4, 0xb8, 0xa5, 0x61, 0x1c, 0xb2, 0x40, 0xb6, 0x28, 0x12, 0xdf, 0xb9,
+ 0xf8, 0x5f, 0xfe, 0xd3, 0xb2, 0xc9, 0xef, 0x3d, 0xb4, 0x1e, 0x4b, 0x7c,
+ 0x1c, 0x4c, 0x99, 0x36, 0x9e, 0x3d, 0xeb, 0xec, 0xa7, 0x68, 0x5e, 0x1d,
+ 0xdf, 0x67, 0x6e, 0x5e, 0xfb, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82,
+ 0x02, 0xfa, 0x30, 0x82, 0x02, 0xf6, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x82,
+ 0x01, 0xc6, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x82, 0x01, 0xbd, 0x30,
+ 0x82, 0x01, 0xb9, 0x30, 0x82, 0x01, 0xb5, 0x06, 0x0b, 0x60, 0x86, 0x48,
+ 0x01, 0x86, 0xfd, 0x6c, 0x01, 0x03, 0x00, 0x02, 0x30, 0x82, 0x01, 0xa4,
+ 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01,
+ 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d, 0x72, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68, 0x74, 0x6d,
+ 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52, 0x00, 0x41,
+ 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x65,
+ 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68,
+ 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72,
+ 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63, 0x00, 0x61,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6e,
+ 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75, 0x00, 0x74,
+ 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63,
+ 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69, 0x00, 0x67,
+ 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74, 0x00, 0x20,
+ 0x00, 0x43, 0x00, 0x50, 0x00, 0x2f, 0x00, 0x43, 0x00, 0x50, 0x00, 0x53,
+ 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6c,
+ 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x50,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20, 0x00, 0x41,
+ 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65,
+ 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68, 0x00, 0x69,
+ 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x6d,
+ 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x61,
+ 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79,
+ 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20, 0x00, 0x61,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x63,
+ 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20, 0x00, 0x62,
+ 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65, 0x00, 0x2e,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08,
+ 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x34, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26,
+ 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81,
+ 0x87, 0x30, 0x81, 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e,
+ 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67,
+ 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56,
+ 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40,
+ 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43,
+ 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72,
+ 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41,
+ 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+ 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03, 0xf8, 0xbf,
+ 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63, 0x64, 0x2b,
+ 0xc3, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+ 0x50, 0xea, 0x73, 0x89, 0xdb, 0x29, 0xfb, 0x10, 0x8f, 0x9e, 0xe5, 0x01,
+ 0x20, 0xd4, 0xde, 0x79, 0x99, 0x48, 0x83, 0xf7, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03,
+ 0x82, 0x01, 0x01, 0x00, 0x1e, 0xe2, 0xa5, 0x48, 0x9e, 0x6c, 0xdb, 0x53,
+ 0x38, 0x0f, 0xef, 0xa6, 0x1a, 0x2a, 0xac, 0xe2, 0x03, 0x43, 0xed, 0x9a,
+ 0xbc, 0x3e, 0x8e, 0x75, 0x1b, 0xf0, 0xfd, 0x2e, 0x22, 0x59, 0xac, 0x13,
+ 0xc0, 0x61, 0xe2, 0xe7, 0xfa, 0xe9, 0x99, 0xcd, 0x87, 0x09, 0x75, 0x54,
+ 0x28, 0xbf, 0x46, 0x60, 0xdc, 0xbe, 0x51, 0x2c, 0x92, 0xf3, 0x1b, 0x91,
+ 0x7c, 0x31, 0x08, 0x70, 0xe2, 0x37, 0xb9, 0xc1, 0x5b, 0xa8, 0xbd, 0xa3,
+ 0x0b, 0x00, 0xfb, 0x1a, 0x15, 0xfd, 0x03, 0xad, 0x58, 0x6a, 0xc5, 0xc7,
+ 0x24, 0x99, 0x48, 0x47, 0x46, 0x31, 0x1e, 0x92, 0xef, 0xb4, 0x5f, 0x4e,
+ 0x34, 0xc7, 0x90, 0xbf, 0x31, 0xc1, 0xf8, 0xb1, 0x84, 0x86, 0xd0, 0x9c,
+ 0x01, 0xaa, 0xdf, 0x8a, 0x56, 0x06, 0xce, 0x3a, 0xe9, 0x0e, 0xae, 0x97,
+ 0x74, 0x5d, 0xd7, 0x71, 0x9a, 0x42, 0x74, 0x5f, 0xde, 0x8d, 0x43, 0x7c,
+ 0xde, 0xe9, 0x55, 0xed, 0x69, 0x00, 0xcb, 0x05, 0xe0, 0x7a, 0x61, 0x61,
+ 0x33, 0xd1, 0x19, 0x4d, 0xf9, 0x08, 0xee, 0xa0, 0x39, 0xc5, 0x25, 0x35,
+ 0xb7, 0x2b, 0xc4, 0x0f, 0xb2, 0xdd, 0xf1, 0xa5, 0xb7, 0x0e, 0x24, 0xc4,
+ 0x26, 0x28, 0x8d, 0x79, 0x77, 0xf5, 0x2f, 0xf0, 0x57, 0xba, 0x7c, 0x07,
+ 0xd4, 0xe1, 0xfc, 0xcd, 0x5a, 0x30, 0x57, 0x7e, 0x86, 0x10, 0x47, 0xdd,
+ 0x31, 0x1f, 0xd7, 0xfc, 0xa2, 0xc2, 0xbf, 0x30, 0x7c, 0x5d, 0x24, 0xaa,
+ 0xe8, 0xf9, 0xae, 0x5f, 0x6a, 0x74, 0xc2, 0xce, 0x6b, 0xb3, 0x46, 0xd8,
+ 0x21, 0xbe, 0x29, 0xd4, 0x8e, 0x5e, 0x15, 0xd6, 0x42, 0x4a, 0xe7, 0x32,
+ 0x6f, 0xa4, 0xb1, 0x6b, 0x51, 0x83, 0x58, 0xbe, 0x3f, 0x6d, 0xc7, 0xfb,
+ 0xda, 0x03, 0x21, 0xcb, 0x6a, 0x16, 0x19, 0x4e, 0x0a, 0xf0, 0xad, 0x84,
+ 0xca, 0x5d, 0x94, 0xb3, 0x5a, 0x76, 0xf7, 0x61,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 08:bb:b0:25:47:13:4b:c9:b1:10:d7:c1:a2:12:59:c5
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Nov 10 00:00:00 2006 GMT
+ Not After : Nov 10 00:00:00 2021 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31:
+ 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af:
+ 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01:
+ 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10:
+ 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a:
+ 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a:
+ db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0:
+ 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db:
+ 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58:
+ 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3:
+ ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47:
+ f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8:
+ 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11:
+ 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df:
+ ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32:
+ b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88:
+ 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0:
+ 3e:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.2.1
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+ User Notice:
+ Explicit Text:
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+ CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Subject Key Identifier:
+ 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 50:1e:43:b0:f7:4d:29:96:5b:bb:a7:d3:0a:b5:b5:d5:d0:27:
+ aa:f9:af:c7:25:d1:95:d5:2f:5a:53:bd:42:07:7e:78:49:ca:
+ 0b:eb:4c:55:e2:ea:2f:7f:49:ad:c7:ff:d1:2d:3e:9c:a0:64:
+ 2b:51:9e:91:26:28:bb:87:bb:75:7c:bc:a1:fd:66:68:2e:4c:
+ 4a:16:cc:fe:06:cf:31:ea:80:6e:e4:bd:e8:03:72:f6:25:b5:
+ 41:83:61:d0:97:0a:27:1d:b3:f7:2b:32:84:8f:5b:e7:cc:3f:
+ e2:2c:67:86:94:f4:b2:2b:6c:52:3b:67:2a:8d:58:95:00:14:
+ 46:24:ac:0b:fa:c9:8e:c7:26:80:df:d1:e1:97:e3:f8:bb:68:
+ c6:9c:bd:be:08:54:3b:10:32:7c:81:1f:2b:28:95:a8:41:0a:
+ c6:d0:30:66:b4:e9:f2:a2:00:69:20:07:ca:82:4c:1e:cf:a7:
+ 98:b8:0c:ee:cd:16:1c:be:1a:63:d4:c0:99:f6:67:b2:f0:8e:
+ 17:2d:58:c2:80:aa:5d:96:c7:b3:28:ed:f0:da:8e:b6:47:1b:
+ 8f:4e:15:f1:97:4c:0b:4b:af:81:d4:46:94:62:2c:43:a7:3c:
+ 25:48:19:63:f2:5c:aa:15:89:76:84:85:73:91:7d:28:3c:09:
+ 83:82:bc:f7
+-----BEGIN CERTIFICATE-----
+MIIG4zCCBcugAwIBAgIQCLuwJUcTS8mxENfBohJZxTANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/
+PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC
+7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw
+PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6
+4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo
+LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U
+pPKwcNSgPqcCAwEAAaOCA4IwggN+MA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy
+BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH
+AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH
+AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
+dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
+AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
+AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
+AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
+AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
+AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
+AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
+AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wDwYDVR0TAQH/BAUwAwEB/zCBgwYI
+KwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
+b20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2VydHMv
+RGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcwgYQw
+QKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1
+cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNv
+bS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYEFExY
+yyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoIAu9j
+ZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBQHkOw900pllu7p9MKtbXV0Ceq+a/HJdGV
+1S9aU71CB354ScoL60xV4uovf0mtx//RLT6coGQrUZ6RJii7h7t1fLyh/WZoLkxK
+Fsz+Bs8x6oBu5L3oA3L2JbVBg2HQlwonHbP3KzKEj1vnzD/iLGeGlPSyK2xSO2cq
+jViVABRGJKwL+smOxyaA39Hhl+P4u2jGnL2+CFQ7EDJ8gR8rKJWoQQrG0DBmtOny
+ogBpIAfKgkwez6eYuAzuzRYcvhpj1MCZ9mey8I4XLVjCgKpdlsezKO3w2o62RxuP
+ThXxl0wLS6+B1EaUYixDpzwlSBlj8lyqFYl2hIVzkX0oPAmDgrz3
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert100[] = {
+ 0x30, 0x82, 0x06, 0xe3, 0x30, 0x82, 0x05, 0xcb, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x08, 0xbb, 0xb0, 0x25, 0x47, 0x13, 0x4b, 0xc9, 0xb1,
+ 0x10, 0xd7, 0xc1, 0xa2, 0x12, 0x59, 0xc5, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x69, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1f,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67,
+ 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x45, 0x56, 0x20, 0x43, 0x41, 0x2d, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xf3, 0x96, 0x62, 0xd8, 0x75, 0x6e, 0x19, 0xff,
+ 0x3f, 0x34, 0x7c, 0x49, 0x4f, 0x31, 0x7e, 0x0d, 0x04, 0x4e, 0x99, 0x81,
+ 0xe2, 0xb3, 0x85, 0x55, 0x91, 0x30, 0xb1, 0xc0, 0xaf, 0x70, 0xbb, 0x2c,
+ 0xa8, 0xe7, 0x18, 0xaa, 0x3f, 0x78, 0xf7, 0x90, 0x68, 0x52, 0x86, 0x01,
+ 0x88, 0x97, 0xe2, 0x3b, 0x06, 0x65, 0x90, 0xaa, 0xbd, 0x65, 0x76, 0xc2,
+ 0xec, 0xbe, 0x10, 0x5b, 0x37, 0x78, 0x83, 0x60, 0x75, 0x45, 0xc6, 0xbd,
+ 0x74, 0xaa, 0xb6, 0x9f, 0xa4, 0x3a, 0x01, 0x50, 0x17, 0xc4, 0x39, 0x69,
+ 0xb9, 0xf1, 0x4f, 0xef, 0x82, 0xc1, 0xca, 0xf3, 0x4a, 0xdb, 0xcc, 0x9e,
+ 0x50, 0x4f, 0x4d, 0x40, 0xa3, 0x3a, 0x90, 0xe7, 0x86, 0x66, 0xbc, 0xf0,
+ 0x3e, 0x76, 0x28, 0x4c, 0xd1, 0x75, 0x80, 0x9e, 0x6a, 0x35, 0x14, 0x35,
+ 0x03, 0x9e, 0xdb, 0x0c, 0x8c, 0xc2, 0x28, 0xad, 0x50, 0xb2, 0xce, 0xf6,
+ 0x91, 0xa3, 0xc3, 0xa5, 0x0a, 0x58, 0x49, 0xf6, 0x75, 0x44, 0x6c, 0xba,
+ 0xf9, 0xce, 0xe9, 0xab, 0x3a, 0x02, 0xe0, 0x4d, 0xf3, 0xac, 0xe2, 0x7a,
+ 0xe0, 0x60, 0x22, 0x05, 0x3c, 0x82, 0xd3, 0x52, 0xe2, 0xf3, 0x9c, 0x47,
+ 0xf8, 0x3b, 0xd8, 0xb2, 0x4b, 0x93, 0x56, 0x4a, 0xbf, 0x70, 0xab, 0x3e,
+ 0xe9, 0x68, 0xc8, 0x1d, 0x8f, 0x58, 0x1d, 0x2a, 0x4d, 0x5e, 0x27, 0x3d,
+ 0xad, 0x0a, 0x59, 0x2f, 0x5a, 0x11, 0x20, 0x40, 0xd9, 0x68, 0x04, 0x68,
+ 0x2d, 0xf4, 0xc0, 0x84, 0x0b, 0x0a, 0x1b, 0x78, 0xdf, 0xed, 0x1a, 0x58,
+ 0xdc, 0xfb, 0x41, 0x5a, 0x6d, 0x6b, 0xf2, 0xed, 0x1c, 0xee, 0x5c, 0x32,
+ 0xb6, 0x5c, 0xec, 0xd7, 0xa6, 0x03, 0x32, 0xa6, 0xe8, 0xde, 0xb7, 0x28,
+ 0x27, 0x59, 0x88, 0x80, 0xff, 0x7b, 0xad, 0x89, 0x58, 0xd5, 0x1e, 0x14,
+ 0xa4, 0xf2, 0xb0, 0x70, 0xd4, 0xa0, 0x3e, 0xa7, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x03, 0x82, 0x30, 0x82, 0x03, 0x7e, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x86, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x34, 0x30, 0x32,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x04, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x08, 0x30, 0x82, 0x01, 0xc4, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x82, 0x01, 0xbb, 0x30, 0x82, 0x01, 0xb7, 0x30, 0x82, 0x01, 0xb3, 0x06,
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6c, 0x02, 0x01, 0x30, 0x82,
+ 0x01, 0xa4, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d,
+ 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68,
+ 0x74, 0x6d, 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52,
+ 0x00, 0x41, 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73,
+ 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63,
+ 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f,
+ 0x00, 0x6e, 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63,
+ 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e,
+ 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20,
+ 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69,
+ 0x00, 0x67, 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74,
+ 0x00, 0x20, 0x00, 0x45, 0x00, 0x56, 0x00, 0x20, 0x00, 0x43, 0x00, 0x50,
+ 0x00, 0x53, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20,
+ 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65,
+ 0x00, 0x6c, 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20,
+ 0x00, 0x50, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20,
+ 0x00, 0x41, 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d,
+ 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68,
+ 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69,
+ 0x00, 0x6d, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69,
+ 0x00, 0x61, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74,
+ 0x00, 0x79, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e,
+ 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72,
+ 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68,
+ 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20,
+ 0x00, 0x62, 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66,
+ 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65,
+ 0x00, 0x2e, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x81, 0x83, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x77, 0x30, 0x75,
+ 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01,
+ 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73,
+ 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x30, 0x4d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x30, 0x02, 0x86, 0x41, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x41, 0x43, 0x65, 0x72, 0x74, 0x73, 0x2f,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68,
+ 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52,
+ 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x81, 0x8f,
+ 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x87, 0x30, 0x81, 0x84, 0x30,
+ 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63,
+ 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69,
+ 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75,
+ 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43,
+ 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86,
+ 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x34,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69,
+ 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45,
+ 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30,
+ 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x4c, 0x58,
+ 0xcb, 0x25, 0xf0, 0x41, 0x4f, 0x52, 0xf4, 0x28, 0xc8, 0x81, 0x43, 0x9b,
+ 0xa6, 0xa8, 0xa0, 0xe6, 0x92, 0xe5, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
+ 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e, 0xc3, 0x69, 0x03,
+ 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08, 0x02, 0xef, 0x63,
+ 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x50,
+ 0x1e, 0x43, 0xb0, 0xf7, 0x4d, 0x29, 0x96, 0x5b, 0xbb, 0xa7, 0xd3, 0x0a,
+ 0xb5, 0xb5, 0xd5, 0xd0, 0x27, 0xaa, 0xf9, 0xaf, 0xc7, 0x25, 0xd1, 0x95,
+ 0xd5, 0x2f, 0x5a, 0x53, 0xbd, 0x42, 0x07, 0x7e, 0x78, 0x49, 0xca, 0x0b,
+ 0xeb, 0x4c, 0x55, 0xe2, 0xea, 0x2f, 0x7f, 0x49, 0xad, 0xc7, 0xff, 0xd1,
+ 0x2d, 0x3e, 0x9c, 0xa0, 0x64, 0x2b, 0x51, 0x9e, 0x91, 0x26, 0x28, 0xbb,
+ 0x87, 0xbb, 0x75, 0x7c, 0xbc, 0xa1, 0xfd, 0x66, 0x68, 0x2e, 0x4c, 0x4a,
+ 0x16, 0xcc, 0xfe, 0x06, 0xcf, 0x31, 0xea, 0x80, 0x6e, 0xe4, 0xbd, 0xe8,
+ 0x03, 0x72, 0xf6, 0x25, 0xb5, 0x41, 0x83, 0x61, 0xd0, 0x97, 0x0a, 0x27,
+ 0x1d, 0xb3, 0xf7, 0x2b, 0x32, 0x84, 0x8f, 0x5b, 0xe7, 0xcc, 0x3f, 0xe2,
+ 0x2c, 0x67, 0x86, 0x94, 0xf4, 0xb2, 0x2b, 0x6c, 0x52, 0x3b, 0x67, 0x2a,
+ 0x8d, 0x58, 0x95, 0x00, 0x14, 0x46, 0x24, 0xac, 0x0b, 0xfa, 0xc9, 0x8e,
+ 0xc7, 0x26, 0x80, 0xdf, 0xd1, 0xe1, 0x97, 0xe3, 0xf8, 0xbb, 0x68, 0xc6,
+ 0x9c, 0xbd, 0xbe, 0x08, 0x54, 0x3b, 0x10, 0x32, 0x7c, 0x81, 0x1f, 0x2b,
+ 0x28, 0x95, 0xa8, 0x41, 0x0a, 0xc6, 0xd0, 0x30, 0x66, 0xb4, 0xe9, 0xf2,
+ 0xa2, 0x00, 0x69, 0x20, 0x07, 0xca, 0x82, 0x4c, 0x1e, 0xcf, 0xa7, 0x98,
+ 0xb8, 0x0c, 0xee, 0xcd, 0x16, 0x1c, 0xbe, 0x1a, 0x63, 0xd4, 0xc0, 0x99,
+ 0xf6, 0x67, 0xb2, 0xf0, 0x8e, 0x17, 0x2d, 0x58, 0xc2, 0x80, 0xaa, 0x5d,
+ 0x96, 0xc7, 0xb3, 0x28, 0xed, 0xf0, 0xda, 0x8e, 0xb6, 0x47, 0x1b, 0x8f,
+ 0x4e, 0x15, 0xf1, 0x97, 0x4c, 0x0b, 0x4b, 0xaf, 0x81, 0xd4, 0x46, 0x94,
+ 0x62, 0x2c, 0x43, 0xa7, 0x3c, 0x25, 0x48, 0x19, 0x63, 0xf2, 0x5c, 0xaa,
+ 0x15, 0x89, 0x76, 0x84, 0x85, 0x73, 0x91, 0x7d, 0x28, 0x3c, 0x09, 0x83,
+ 0x82, 0xbc, 0xf7,
+};
+
+#if 0
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA
+ Validity
+ Not Before: Nov 9 12:00:00 2007 GMT
+ Not After : Nov 10 00:00:00 2021 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31:
+ 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af:
+ 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01:
+ 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10:
+ 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a:
+ 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a:
+ db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0:
+ 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db:
+ 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58:
+ 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3:
+ ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47:
+ f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8:
+ 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11:
+ 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df:
+ ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32:
+ b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88:
+ 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0:
+ 3e:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.2.1
+ CPS: http://www.digicert.com/ssl-cps-repository.htm
+ User Notice:
+ Explicit Text:
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+ CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt
+
+ X509v3 CRL Distribution Points:
+
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ Full Name:
+ URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl
+
+ X509v3 Subject Key Identifier:
+ 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5
+ X509v3 Authority Key Identifier:
+ keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3
+
+ Signature Algorithm: sha1WithRSAEncryption
+ 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d:
+ f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9:
+ 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7:
+ dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81:
+ 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df:
+ 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e:
+ 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07:
+ 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60:
+ 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4:
+ f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c:
+ a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25:
+ 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53:
+ 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32:
+ 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c:
+ 36:8f:1c:80
+-----BEGIN CERTIFICATE-----
+MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
+ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL
+MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
+LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
+RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/
+PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC
+7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw
+PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6
+4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo
+LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U
+pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy
+BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH
+AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH
+AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
+dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
+AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
+AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
+AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
+AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
+AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
+AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
+AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB
+gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
+dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy
+dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw
+gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB
+c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
+LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE
+FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI
+Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU
+nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/
+roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU
+xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+
+BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu
+zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA
+-----END CERTIFICATE-----
+#endif
+static const unsigned char kDERCert101[] = {
+ 0x30, 0x82, 0x06, 0xe6, 0x30, 0x82, 0x05, 0xce, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x10, 0x03, 0x37, 0xb9, 0x28, 0x34, 0x7c, 0x60, 0xa6, 0xae,
+ 0xc5, 0xad, 0xb1, 0x21, 0x7f, 0x38, 0x60, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x6c,
+ 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63,
+ 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x03,
+ 0x13, 0x22, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48,
+ 0x69, 0x67, 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63,
+ 0x65, 0x20, 0x45, 0x56, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x37, 0x31, 0x31, 0x30, 0x39, 0x31, 0x32,
+ 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x31, 0x31, 0x31, 0x31,
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x69, 0x31, 0x0b,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, 0x69,
+ 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, 0x19,
+ 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, 0x77,
+ 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1f,
+ 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x48, 0x69, 0x67,
+ 0x68, 0x20, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x20,
+ 0x45, 0x56, 0x20, 0x43, 0x41, 0x2d, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0xf3, 0x96, 0x62, 0xd8, 0x75, 0x6e, 0x19, 0xff,
+ 0x3f, 0x34, 0x7c, 0x49, 0x4f, 0x31, 0x7e, 0x0d, 0x04, 0x4e, 0x99, 0x81,
+ 0xe2, 0xb3, 0x85, 0x55, 0x91, 0x30, 0xb1, 0xc0, 0xaf, 0x70, 0xbb, 0x2c,
+ 0xa8, 0xe7, 0x18, 0xaa, 0x3f, 0x78, 0xf7, 0x90, 0x68, 0x52, 0x86, 0x01,
+ 0x88, 0x97, 0xe2, 0x3b, 0x06, 0x65, 0x90, 0xaa, 0xbd, 0x65, 0x76, 0xc2,
+ 0xec, 0xbe, 0x10, 0x5b, 0x37, 0x78, 0x83, 0x60, 0x75, 0x45, 0xc6, 0xbd,
+ 0x74, 0xaa, 0xb6, 0x9f, 0xa4, 0x3a, 0x01, 0x50, 0x17, 0xc4, 0x39, 0x69,
+ 0xb9, 0xf1, 0x4f, 0xef, 0x82, 0xc1, 0xca, 0xf3, 0x4a, 0xdb, 0xcc, 0x9e,
+ 0x50, 0x4f, 0x4d, 0x40, 0xa3, 0x3a, 0x90, 0xe7, 0x86, 0x66, 0xbc, 0xf0,
+ 0x3e, 0x76, 0x28, 0x4c, 0xd1, 0x75, 0x80, 0x9e, 0x6a, 0x35, 0x14, 0x35,
+ 0x03, 0x9e, 0xdb, 0x0c, 0x8c, 0xc2, 0x28, 0xad, 0x50, 0xb2, 0xce, 0xf6,
+ 0x91, 0xa3, 0xc3, 0xa5, 0x0a, 0x58, 0x49, 0xf6, 0x75, 0x44, 0x6c, 0xba,
+ 0xf9, 0xce, 0xe9, 0xab, 0x3a, 0x02, 0xe0, 0x4d, 0xf3, 0xac, 0xe2, 0x7a,
+ 0xe0, 0x60, 0x22, 0x05, 0x3c, 0x82, 0xd3, 0x52, 0xe2, 0xf3, 0x9c, 0x47,
+ 0xf8, 0x3b, 0xd8, 0xb2, 0x4b, 0x93, 0x56, 0x4a, 0xbf, 0x70, 0xab, 0x3e,
+ 0xe9, 0x68, 0xc8, 0x1d, 0x8f, 0x58, 0x1d, 0x2a, 0x4d, 0x5e, 0x27, 0x3d,
+ 0xad, 0x0a, 0x59, 0x2f, 0x5a, 0x11, 0x20, 0x40, 0xd9, 0x68, 0x04, 0x68,
+ 0x2d, 0xf4, 0xc0, 0x84, 0x0b, 0x0a, 0x1b, 0x78, 0xdf, 0xed, 0x1a, 0x58,
+ 0xdc, 0xfb, 0x41, 0x5a, 0x6d, 0x6b, 0xf2, 0xed, 0x1c, 0xee, 0x5c, 0x32,
+ 0xb6, 0x5c, 0xec, 0xd7, 0xa6, 0x03, 0x32, 0xa6, 0xe8, 0xde, 0xb7, 0x28,
+ 0x27, 0x59, 0x88, 0x80, 0xff, 0x7b, 0xad, 0x89, 0x58, 0xd5, 0x1e, 0x14,
+ 0xa4, 0xf2, 0xb0, 0x70, 0xd4, 0xa0, 0x3e, 0xa7, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x82, 0x03, 0x85, 0x30, 0x82, 0x03, 0x81, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01,
+ 0x86, 0x30, 0x3b, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x34, 0x30, 0x32,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06,
+ 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x04, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x08, 0x30, 0x82, 0x01, 0xc4, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x82, 0x01, 0xbb, 0x30, 0x82, 0x01, 0xb7, 0x30, 0x82, 0x01, 0xb3, 0x06,
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, 0x6c, 0x02, 0x01, 0x30, 0x82,
+ 0x01, 0xa4, 0x30, 0x3a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x73, 0x6c, 0x2d, 0x63, 0x70, 0x73, 0x2d,
+ 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x68,
+ 0x74, 0x6d, 0x30, 0x82, 0x01, 0x64, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x02, 0x02, 0x30, 0x82, 0x01, 0x56, 0x1e, 0x82, 0x01, 0x52,
+ 0x00, 0x41, 0x00, 0x6e, 0x00, 0x79, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73,
+ 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x74,
+ 0x00, 0x68, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x43, 0x00, 0x65,
+ 0x00, 0x72, 0x00, 0x74, 0x00, 0x69, 0x00, 0x66, 0x00, 0x69, 0x00, 0x63,
+ 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6f,
+ 0x00, 0x6e, 0x00, 0x73, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x75,
+ 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x63,
+ 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x61, 0x00, 0x6e,
+ 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20,
+ 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x44, 0x00, 0x69,
+ 0x00, 0x67, 0x00, 0x69, 0x00, 0x43, 0x00, 0x65, 0x00, 0x72, 0x00, 0x74,
+ 0x00, 0x20, 0x00, 0x45, 0x00, 0x56, 0x00, 0x20, 0x00, 0x43, 0x00, 0x50,
+ 0x00, 0x53, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20,
+ 0x00, 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65,
+ 0x00, 0x6c, 0x00, 0x79, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20,
+ 0x00, 0x50, 0x00, 0x61, 0x00, 0x72, 0x00, 0x74, 0x00, 0x79, 0x00, 0x20,
+ 0x00, 0x41, 0x00, 0x67, 0x00, 0x72, 0x00, 0x65, 0x00, 0x65, 0x00, 0x6d,
+ 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x20, 0x00, 0x77, 0x00, 0x68,
+ 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69,
+ 0x00, 0x6d, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6c, 0x00, 0x69,
+ 0x00, 0x61, 0x00, 0x62, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74,
+ 0x00, 0x79, 0x00, 0x20, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x20,
+ 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x69, 0x00, 0x6e,
+ 0x00, 0x63, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72,
+ 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x68,
+ 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x20,
+ 0x00, 0x62, 0x00, 0x79, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x66,
+ 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65,
+ 0x00, 0x2e, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
+ 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x81,
+ 0x83, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04,
+ 0x77, 0x30, 0x75, 0x30, 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+ 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
+ 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4d, 0x06, 0x08, 0x2b, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x41, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65,
+ 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x41, 0x43, 0x65, 0x72,
+ 0x74, 0x73, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48,
+ 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65,
+ 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74,
+ 0x30, 0x81, 0x8f, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x87, 0x30,
+ 0x81, 0x84, 0x30, 0x40, 0xa0, 0x3e, 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74,
+ 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69,
+ 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44,
+ 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x48, 0x69, 0x67, 0x68, 0x41,
+ 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f,
+ 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x40, 0xa0, 0x3e,
+ 0xa0, 0x3c, 0x86, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63,
+ 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72,
+ 0x74, 0x48, 0x69, 0x67, 0x68, 0x41, 0x73, 0x73, 0x75, 0x72, 0x61, 0x6e,
+ 0x63, 0x65, 0x45, 0x56, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, 0x63,
+ 0x72, 0x6c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x4c, 0x58, 0xcb, 0x25, 0xf0, 0x41, 0x4f, 0x52, 0xf4, 0x28, 0xc8,
+ 0x81, 0x43, 0x9b, 0xa6, 0xa8, 0xa0, 0xe6, 0x92, 0xe5, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb1, 0x3e,
+ 0xc3, 0x69, 0x03, 0xf8, 0xbf, 0x47, 0x01, 0xd4, 0x98, 0x26, 0x1a, 0x08,
+ 0x02, 0xef, 0x63, 0x64, 0x2b, 0xc3, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x4c, 0x7a, 0x17, 0x87, 0x28, 0x5d, 0x17, 0xbc, 0xb2, 0x32,
+ 0x73, 0xbf, 0xcd, 0x2e, 0xf5, 0x58, 0x31, 0x1d, 0xf0, 0xb1, 0x71, 0x54,
+ 0x9c, 0xd6, 0x9b, 0x67, 0x93, 0xdb, 0x2f, 0x03, 0x3e, 0x16, 0x6f, 0x1e,
+ 0x03, 0xc9, 0x53, 0x84, 0xa3, 0x56, 0x60, 0x1e, 0x78, 0x94, 0x1b, 0xa2,
+ 0xa8, 0x6f, 0xa3, 0xa4, 0x8b, 0x52, 0x91, 0xd7, 0xdd, 0x5c, 0x95, 0xbb,
+ 0xef, 0xb5, 0x16, 0x49, 0xe9, 0xa5, 0x42, 0x4f, 0x34, 0xf2, 0x47, 0xff,
+ 0xae, 0x81, 0x7f, 0x13, 0x54, 0xb7, 0x20, 0xc4, 0x70, 0x15, 0xcb, 0x81,
+ 0x0a, 0x81, 0xcb, 0x74, 0x57, 0xdc, 0x9c, 0xdf, 0x24, 0xa4, 0x29, 0x0c,
+ 0x18, 0xf0, 0x1c, 0xe4, 0xae, 0x07, 0x33, 0xec, 0xf1, 0x49, 0x3e, 0x55,
+ 0xcf, 0x6e, 0x4f, 0x0d, 0x54, 0x7b, 0xd3, 0xc9, 0xe8, 0x15, 0x48, 0xd4,
+ 0xc5, 0xbb, 0xdc, 0x35, 0x1c, 0x77, 0x45, 0x07, 0x48, 0x45, 0x85, 0xbd,
+ 0xd7, 0x7e, 0x53, 0xb8, 0xc0, 0x16, 0xd9, 0x95, 0xcd, 0x8b, 0x8d, 0x7d,
+ 0xc9, 0x60, 0x4f, 0xd1, 0xa2, 0x9b, 0xe3, 0xd0, 0x30, 0xd6, 0xb4, 0x73,
+ 0x36, 0xe6, 0xd2, 0xf9, 0x03, 0xb2, 0xe3, 0xa4, 0xf5, 0xe5, 0xb8, 0x3e,
+ 0x04, 0x49, 0x00, 0xba, 0x2e, 0xa6, 0x4a, 0x72, 0x83, 0x72, 0x9d, 0xf7,
+ 0x0b, 0x8c, 0xa9, 0x89, 0xe7, 0xb3, 0xd7, 0x64, 0x1f, 0xd6, 0xe3, 0x60,
+ 0xcb, 0x03, 0xc4, 0xdc, 0x88, 0xe9, 0x9d, 0x25, 0x01, 0x00, 0x71, 0xcb,
+ 0x03, 0xb4, 0x29, 0x60, 0x25, 0x8f, 0xf9, 0x46, 0xd1, 0x7b, 0x71, 0xae,
+ 0xcd, 0x53, 0x12, 0x5b, 0x84, 0x8e, 0xc2, 0x0f, 0xc7, 0xed, 0x93, 0x19,
+ 0xd9, 0xc9, 0xfa, 0x8f, 0x58, 0x34, 0x76, 0x32, 0x2f, 0xae, 0xe1, 0x50,
+ 0x14, 0x61, 0xd4, 0xa8, 0x58, 0xa3, 0xc8, 0x30, 0x13, 0x23, 0xef, 0xc6,
+ 0x25, 0x8c, 0x36, 0x8f, 0x1c, 0x80,
+};
diff --git a/net/quic/crypto/common_cert_set_test.cc b/net/quic/crypto/common_cert_set_test.cc
new file mode 100644
index 0000000..e15a10e
--- /dev/null
+++ b/net/quic/crypto/common_cert_set_test.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/common_cert_set.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+static const unsigned char kGIACertificate[] = {
+ 0x30, 0x82, 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x03, 0x0b, 0x67, 0x71, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45,
+ 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78,
+ 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74,
+ 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x39, 0x30,
+ 0x36, 0x30, 0x38, 0x32, 0x30, 0x34, 0x33, 0x32, 0x37, 0x5a, 0x17, 0x0d,
+ 0x31, 0x33, 0x30, 0x36, 0x30, 0x37, 0x31, 0x39, 0x34, 0x33, 0x32, 0x37,
+ 0x5a, 0x30, 0x46, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
+ 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x13, 0x0a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e,
+ 0x63, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x65, 0x74, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+ 0x79, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+ 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xc9, 0xed, 0xb7, 0xa4, 0x8b, 0x9c,
+ 0x57, 0xe7, 0x84, 0x3e, 0x40, 0x7d, 0x84, 0xf4, 0x8f, 0xd1, 0x71, 0x63,
+ 0x53, 0x99, 0xe7, 0x79, 0x74, 0x14, 0xaf, 0x44, 0x99, 0x33, 0x20, 0x92,
+ 0x8d, 0x7b, 0xe5, 0x28, 0x0c, 0xba, 0xad, 0x6c, 0x49, 0x7e, 0x83, 0x5f,
+ 0x34, 0x59, 0x4e, 0x0a, 0x7a, 0x30, 0xcd, 0xd0, 0xd7, 0xc4, 0x57, 0x45,
+ 0xed, 0xd5, 0xaa, 0xd6, 0x73, 0x26, 0xce, 0xad, 0x32, 0x13, 0xb8, 0xd7,
+ 0x0f, 0x1d, 0x3b, 0xdf, 0xdd, 0xdc, 0x08, 0x36, 0xa8, 0x6f, 0x51, 0x44,
+ 0x9b, 0xca, 0xd6, 0x20, 0x52, 0x73, 0xb7, 0x26, 0x87, 0x35, 0x6a, 0xdb,
+ 0xa9, 0xe5, 0xd4, 0x59, 0xa5, 0x2b, 0xfc, 0x67, 0x19, 0x39, 0xfa, 0x93,
+ 0x18, 0x18, 0x6c, 0xde, 0xdd, 0x25, 0x8a, 0x0e, 0x33, 0x14, 0x47, 0xc2,
+ 0xef, 0x01, 0x50, 0x79, 0xe4, 0xfd, 0x69, 0xd1, 0xa7, 0xc0, 0xac, 0xe2,
+ 0x57, 0x6f, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa3, 0x30, 0x81,
+ 0xa0, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0xbf, 0xc0, 0x30, 0xeb, 0xf5, 0x43, 0x11, 0x3e,
+ 0x67, 0xba, 0x9e, 0x91, 0xfb, 0xfc, 0x6a, 0xda, 0xe3, 0x6b, 0x12, 0x24,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8,
+ 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x12, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01,
+ 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x3a, 0x06, 0x03, 0x55, 0x1d, 0x1f,
+ 0x04, 0x33, 0x30, 0x31, 0x30, 0x2f, 0xa0, 0x2d, 0xa0, 0x2b, 0x86, 0x29,
+ 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67,
+ 0x65, 0x6f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
+ 0x63, 0x72, 0x6c, 0x73, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63,
+ 0x61, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+ 0xb8, 0x8a, 0x23, 0xc6, 0x48, 0x96, 0xb1, 0x11, 0x7c, 0x60, 0x77, 0x5e,
+ 0x05, 0x9a, 0xab, 0xa1, 0xc6, 0xfa, 0x82, 0x1c, 0x18, 0x07, 0xc4, 0xeb,
+ 0x81, 0xb0, 0xa8, 0x66, 0xeb, 0x49, 0xa8, 0xe9, 0x0c, 0xd3, 0x29, 0xad,
+ 0xf5, 0xef, 0x24, 0x4c, 0xfd, 0xe4, 0x4b, 0xca, 0x7f, 0x5e, 0x63, 0xab,
+ 0x99, 0x27, 0xcb, 0x9f, 0x36, 0x21, 0x2c, 0xb9, 0x10, 0x60, 0x67, 0xcd,
+ 0xd2, 0xb4, 0xf0, 0xf0, 0xab, 0x71, 0xe5, 0x8b, 0x5a, 0x89, 0x27, 0x11,
+ 0x84, 0xaa, 0x8e, 0xbf, 0x99, 0xf0, 0x9d, 0x09, 0x21, 0x0a, 0x52, 0x19,
+ 0x9a, 0x5a, 0x09, 0xd2, 0x90, 0xb7, 0xfa, 0x0c, 0xf8, 0x7e, 0x78, 0xa2,
+ 0xb0, 0x85, 0xaf, 0x5c, 0x4c, 0x99, 0xd9, 0x5c, 0x55, 0x29, 0xf9, 0xa5,
+ 0x51, 0x42, 0x2e, 0x3a, 0xcb, 0x38, 0x8c, 0x78, 0x3b, 0xcb, 0xf8, 0xfb,
+ 0x95, 0x87, 0xbc, 0xbc, 0x90, 0xf9, 0x50, 0x32,
+};
+
+TEST(CommonCertSets, FindGIA) {
+ StringPiece gia(reinterpret_cast<const char*>(kGIACertificate),
+ sizeof(kGIACertificate));
+
+ const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC());
+
+ const uint64 in_hash = GG_UINT64_C(0xc9fef74053f99f39);
+ uint64 hash;
+ uint32 index;
+ ASSERT_TRUE(sets->MatchCert(
+ gia,
+ StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
+ &hash, &index));
+ EXPECT_EQ(in_hash, hash);
+
+ StringPiece gia_copy = sets->GetCert(hash, index);
+ EXPECT_FALSE(gia_copy.empty());
+ ASSERT_EQ(gia.size(), gia_copy.size());
+ EXPECT_EQ(0, memcmp(gia.data(), gia_copy.data(), gia.size()));
+}
+
+TEST(CommonCertSets, NonMatch) {
+ const CommonCertSets* sets(CommonCertSets::GetInstanceQUIC());
+ StringPiece not_a_cert("hello");
+ const uint64 in_hash = GG_UINT64_C(0xc9fef74053f99f39);
+ uint64 hash;
+ uint32 index;
+ EXPECT_FALSE(sets->MatchCert(
+ not_a_cert,
+ StringPiece(reinterpret_cast<const char*>(&in_hash), sizeof(in_hash)),
+ &hash, &index));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc
new file mode 100644
index 0000000..bc5c8b7
--- /dev/null
+++ b/net/quic/crypto/crypto_framer.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_framer.h"
+
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/quic/quic_data_writer.h"
+
+using base::StringPiece;
+using std::make_pair;
+using std::pair;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+const size_t kQuicTagSize = sizeof(uint32);
+const size_t kCryptoEndOffsetSize = sizeof(uint32);
+const size_t kNumEntriesSize = sizeof(uint16);
+
+// OneShotVisitor is a framer visitor that records a single handshake message.
+class OneShotVisitor : public CryptoFramerVisitorInterface {
+ public:
+ OneShotVisitor() : error_(false) {}
+
+ virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
+
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE {
+ out_.reset(new CryptoHandshakeMessage(message));
+ }
+
+ bool error() const { return error_; }
+
+ CryptoHandshakeMessage* release() { return out_.release(); }
+
+ private:
+ scoped_ptr<CryptoHandshakeMessage> out_;
+ bool error_;
+};
+
+} // namespace
+
+CryptoFramer::CryptoFramer()
+ : visitor_(nullptr),
+ num_entries_(0),
+ values_len_(0) {
+ Clear();
+}
+
+CryptoFramer::~CryptoFramer() {}
+
+// static
+CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) {
+ OneShotVisitor visitor;
+ CryptoFramer framer;
+
+ framer.set_visitor(&visitor);
+ if (!framer.ProcessInput(in) || visitor.error() ||
+ framer.InputBytesRemaining()) {
+ return nullptr;
+ }
+
+ return visitor.release();
+}
+
+bool CryptoFramer::ProcessInput(StringPiece input) {
+ DCHECK_EQ(QUIC_NO_ERROR, error_);
+ if (error_ != QUIC_NO_ERROR) {
+ return false;
+ }
+ error_ = Process(input);
+ if (error_ != QUIC_NO_ERROR) {
+ visitor_->OnError(this);
+ return false;
+ }
+
+ return true;
+}
+
+// static
+QuicData* CryptoFramer::ConstructHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ size_t num_entries = message.tag_value_map().size();
+ size_t pad_length = 0;
+ bool need_pad_tag = false;
+ bool need_pad_value = false;
+
+ size_t len = message.size();
+ if (len < message.minimum_size()) {
+ need_pad_tag = true;
+ need_pad_value = true;
+ num_entries++;
+
+ size_t delta = message.minimum_size() - len;
+ const size_t overhead = kQuicTagSize + kCryptoEndOffsetSize;
+ if (delta > overhead) {
+ pad_length = delta - overhead;
+ }
+ len += overhead + pad_length;
+ }
+
+ if (num_entries > kMaxEntries) {
+ return nullptr;
+ }
+
+
+ QuicDataWriter writer(len);
+ if (!writer.WriteUInt32(message.tag())) {
+ DCHECK(false) << "Failed to write message tag.";
+ return nullptr;
+ }
+ if (!writer.WriteUInt16(num_entries)) {
+ DCHECK(false) << "Failed to write size.";
+ return nullptr;
+ }
+ if (!writer.WriteUInt16(0)) {
+ DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+
+ uint32 end_offset = 0;
+ // Tags and offsets
+ for (QuicTagValueMap::const_iterator it = message.tag_value_map().begin();
+ it != message.tag_value_map().end(); ++it) {
+ if (it->first == kPAD && need_pad_tag) {
+ // Existing PAD tags are only checked when padding needs to be added
+ // because parts of the code may need to reserialize received messages
+ // and those messages may, legitimately include padding.
+ DCHECK(false) << "Message needed padding but already contained a PAD tag";
+ return nullptr;
+ }
+
+ if (it->first > kPAD && need_pad_tag) {
+ need_pad_tag = false;
+ if (!WritePadTag(&writer, pad_length, &end_offset)) {
+ return nullptr;
+ }
+ }
+
+ if (!writer.WriteUInt32(it->first)) {
+ DCHECK(false) << "Failed to write tag.";
+ return nullptr;
+ }
+ end_offset += it->second.length();
+ if (!writer.WriteUInt32(end_offset)) {
+ DCHECK(false) << "Failed to write end offset.";
+ return nullptr;
+ }
+ }
+
+ if (need_pad_tag) {
+ if (!WritePadTag(&writer, pad_length, &end_offset)) {
+ return nullptr;
+ }
+ }
+
+ // Values
+ for (QuicTagValueMap::const_iterator it = message.tag_value_map().begin();
+ it != message.tag_value_map().end(); ++it) {
+ if (it->first > kPAD && need_pad_value) {
+ need_pad_value = false;
+ if (!writer.WriteRepeatedByte('-', pad_length)) {
+ DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+ }
+
+ if (!writer.WriteBytes(it->second.data(), it->second.length())) {
+ DCHECK(false) << "Failed to write value.";
+ return nullptr;
+ }
+ }
+
+ if (need_pad_value) {
+ if (!writer.WriteRepeatedByte('-', pad_length)) {
+ DCHECK(false) << "Failed to write padding.";
+ return nullptr;
+ }
+ }
+
+ return new QuicData(writer.take(), len, true);
+}
+
+void CryptoFramer::Clear() {
+ message_.Clear();
+ tags_and_lengths_.clear();
+ error_ = QUIC_NO_ERROR;
+ state_ = STATE_READING_TAG;
+}
+
+QuicErrorCode CryptoFramer::Process(StringPiece input) {
+ // Add this data to the buffer.
+ buffer_.append(input.data(), input.length());
+ QuicDataReader reader(buffer_.data(), buffer_.length());
+
+ switch (state_) {
+ case STATE_READING_TAG:
+ if (reader.BytesRemaining() < kQuicTagSize) {
+ break;
+ }
+ QuicTag message_tag;
+ reader.ReadUInt32(&message_tag);
+ message_.set_tag(message_tag);
+ state_ = STATE_READING_NUM_ENTRIES;
+ case STATE_READING_NUM_ENTRIES:
+ if (reader.BytesRemaining() < kNumEntriesSize + sizeof(uint16)) {
+ break;
+ }
+ reader.ReadUInt16(&num_entries_);
+ if (num_entries_ > kMaxEntries) {
+ return QUIC_CRYPTO_TOO_MANY_ENTRIES;
+ }
+ uint16 padding;
+ reader.ReadUInt16(&padding);
+
+ tags_and_lengths_.reserve(num_entries_);
+ state_ = STATE_READING_TAGS_AND_LENGTHS;
+ values_len_ = 0;
+ case STATE_READING_TAGS_AND_LENGTHS: {
+ if (reader.BytesRemaining() <
+ num_entries_ * (kQuicTagSize + kCryptoEndOffsetSize)) {
+ break;
+ }
+
+ uint32 last_end_offset = 0;
+ for (unsigned i = 0; i < num_entries_; ++i) {
+ QuicTag tag;
+ reader.ReadUInt32(&tag);
+ if (i > 0 && tag <= tags_and_lengths_[i-1].first) {
+ if (tag == tags_and_lengths_[i-1].first) {
+ return QUIC_CRYPTO_DUPLICATE_TAG;
+ }
+ return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
+ }
+
+ uint32 end_offset;
+ reader.ReadUInt32(&end_offset);
+
+ if (end_offset < last_end_offset) {
+ return QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
+ }
+ tags_and_lengths_.push_back(
+ make_pair(tag, static_cast<size_t>(end_offset - last_end_offset)));
+ last_end_offset = end_offset;
+ }
+ values_len_ = last_end_offset;
+ state_ = STATE_READING_VALUES;
+ }
+ case STATE_READING_VALUES:
+ if (reader.BytesRemaining() < values_len_) {
+ break;
+ }
+ for (vector<pair<QuicTag, size_t> >::const_iterator
+ it = tags_and_lengths_.begin(); it != tags_and_lengths_.end();
+ it++) {
+ StringPiece value;
+ reader.ReadStringPiece(&value, it->second);
+ message_.SetStringPiece(it->first, value);
+ }
+ visitor_->OnHandshakeMessage(message_);
+ Clear();
+ state_ = STATE_READING_TAG;
+ break;
+ }
+ // Save any remaining data.
+ buffer_ = reader.PeekRemainingPayload().as_string();
+ return QUIC_NO_ERROR;
+}
+
+// static
+bool CryptoFramer::WritePadTag(QuicDataWriter* writer,
+ size_t pad_length,
+ uint32* end_offset) {
+ if (!writer->WriteUInt32(kPAD)) {
+ DCHECK(false) << "Failed to write tag.";
+ return false;
+ }
+ *end_offset += pad_length;
+ if (!writer->WriteUInt32(*end_offset)) {
+ DCHECK(false) << "Failed to write end offset.";
+ return false;
+ }
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h
new file mode 100644
index 0000000..a200fd6
--- /dev/null
+++ b/net/quic/crypto/crypto_framer.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_
+#define NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class CryptoFramer;
+class QuicData;
+class QuicDataReader;
+class QuicDataWriter;
+
+class NET_EXPORT_PRIVATE CryptoFramerVisitorInterface {
+ public:
+ virtual ~CryptoFramerVisitorInterface() {}
+
+ // Called if an error is detected.
+ virtual void OnError(CryptoFramer* framer) = 0;
+
+ // Called when a complete handshake message has been parsed.
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) = 0;
+};
+
+// A class for framing the crypto messages that are exchanged in a QUIC
+// session.
+class NET_EXPORT_PRIVATE CryptoFramer {
+ public:
+ CryptoFramer();
+
+ virtual ~CryptoFramer();
+
+ // ParseMessage parses exactly one message from the given StringPiece. If
+ // there is an error, the message is truncated, or the message has trailing
+ // garbage then nullptr will be returned.
+ static CryptoHandshakeMessage* ParseMessage(base::StringPiece in);
+
+ // Set callbacks to be called from the framer. A visitor must be set, or
+ // else the framer will crash. It is acceptable for the visitor to do
+ // nothing. If this is called multiple times, only the last visitor
+ // will be used. |visitor| will be owned by the framer.
+ void set_visitor(CryptoFramerVisitorInterface* visitor) {
+ visitor_ = visitor;
+ }
+
+ QuicErrorCode error() const { return error_; }
+
+ // Processes input data, which must be delivered in order. Returns
+ // false if there was an error, and true otherwise.
+ bool ProcessInput(base::StringPiece input);
+
+ // Returns the number of bytes of buffered input data remaining to be
+ // parsed.
+ size_t InputBytesRemaining() const { return buffer_.length(); }
+
+ // Returns a new QuicData owned by the caller that contains a serialized
+ // |message|, or nullptr if there was an error.
+ static QuicData* ConstructHandshakeMessage(
+ const CryptoHandshakeMessage& message);
+
+ private:
+ // Clears per-message state. Does not clear the visitor.
+ void Clear();
+
+ // Process does does the work of |ProcessInput|, but returns an error code,
+ // doesn't set error_ and doesn't call |visitor_->OnError()|.
+ QuicErrorCode Process(base::StringPiece input);
+
+ static bool WritePadTag(QuicDataWriter* writer,
+ size_t pad_length,
+ uint32* end_offset);
+
+ // Represents the current state of the parsing state machine.
+ enum CryptoFramerState {
+ STATE_READING_TAG,
+ STATE_READING_NUM_ENTRIES,
+ STATE_READING_TAGS_AND_LENGTHS,
+ STATE_READING_VALUES
+ };
+
+ // Visitor to invoke when messages are parsed.
+ CryptoFramerVisitorInterface* visitor_;
+ // Last error.
+ QuicErrorCode error_;
+ // Remaining unparsed data.
+ std::string buffer_;
+ // Current state of the parsing.
+ CryptoFramerState state_;
+ // The message currently being parsed.
+ CryptoHandshakeMessage message_;
+ // Number of entires in the message currently being parsed.
+ uint16 num_entries_;
+ // tags_and_lengths_ contains the tags that are currently being parsed and
+ // their lengths.
+ std::vector<std::pair<QuicTag, size_t> > tags_and_lengths_;
+ // Cumulative length of all values in the message currently being parsed.
+ size_t values_len_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_
diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc
new file mode 100644
index 0000000..01e9157
--- /dev/null
+++ b/net/quic/crypto/crypto_framer_test.cc
@@ -0,0 +1,485 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <map>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+char* AsChars(unsigned char* data) { return reinterpret_cast<char*>(data); }
+
+} // namespace
+
+namespace test {
+
+class TestCryptoVisitor : public ::net::CryptoFramerVisitorInterface {
+ public:
+ TestCryptoVisitor() : error_count_(0) {}
+
+ virtual void OnError(CryptoFramer* framer) OVERRIDE {
+ DLOG(ERROR) << "CryptoFramer Error: " << framer->error();
+ ++error_count_;
+ }
+
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE {
+ messages_.push_back(message);
+ }
+
+ // Counters from the visitor callbacks.
+ int error_count_;
+
+ vector<CryptoHandshakeMessage> messages_;
+};
+
+TEST(CryptoFramerTest, ConstructHandshakeMessage) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "abcdef");
+ message.SetStringPiece(0x12345679, "ghijk");
+ message.SetStringPiece(0x1234567A, "lmnopqr");
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x03, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // tag 3
+ 0x7A, 0x56, 0x34, 0x12,
+ // end offset 3
+ 0x12, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd',
+ 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j',
+ 'k',
+ // value 3
+ 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r',
+ };
+
+ CryptoFramer framer;
+ scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message));
+ ASSERT_TRUE(data.get() != nullptr);
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "abcdef");
+ message.SetStringPiece(0x12345679, "ghijk");
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd',
+ 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j',
+ 'k',
+ };
+
+ CryptoFramer framer;
+ scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message));
+ ASSERT_TRUE(data.get() != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageZeroLength) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x12345678, "");
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ CryptoFramer framer;
+ scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message));
+ ASSERT_TRUE(data.get() != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ for (uint32 key = 1; key <= kMaxEntries + 1; ++key) {
+ message.SetStringPiece(key, "abcdef");
+ }
+
+ CryptoFramer framer;
+ scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message));
+ EXPECT_TRUE(data.get() == nullptr);
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSize) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(0x01020304, "test");
+ message.set_minimum_size(64);
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 'P', 'A', 'D', 0,
+ // end offset 1
+ 0x24, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x04, 0x03, 0x02, 0x01,
+ // end offset 2
+ 0x28, 0x00, 0x00, 0x00,
+ // 36 bytes of padding.
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-',
+ // value 2
+ 't', 'e', 's', 't',
+ };
+
+ CryptoFramer framer;
+ scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message));
+ ASSERT_TRUE(data.get() != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST(CryptoFramerTest, ConstructHandshakeMessageMinimumSizePadLast) {
+ CryptoHandshakeMessage message;
+ message.set_tag(0xFFAA7733);
+ message.SetStringPiece(1, "");
+ message.set_minimum_size(64);
+
+ unsigned char packet[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x01, 0x00, 0x00, 0x00,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ // tag 2
+ 'P', 'A', 'D', 0,
+ // end offset 2
+ 0x28, 0x00, 0x00, 0x00,
+ // 40 bytes of padding.
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ '-', '-', '-', '-', '-', '-', '-', '-',
+ };
+
+ CryptoFramer framer;
+ scoped_ptr<QuicData> data(framer.ConstructHandshakeMessage(message));
+ ASSERT_TRUE(data.get() != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST(CryptoFramerTest, ProcessInput) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd',
+ 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j',
+ 'k',
+ };
+
+ EXPECT_TRUE(
+ framer.ProcessInput(StringPiece(AsChars(input), arraysize(input))));
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ EXPECT_EQ(0, visitor.error_count_);
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", CryptoTestUtils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", CryptoTestUtils::GetValueForTag(message, 0x12345679));
+}
+
+TEST(CryptoFramerTest, ProcessInputWithThreeKeys) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x03, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // tag 3
+ 0x7A, 0x56, 0x34, 0x12,
+ // end offset 3
+ 0x12, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd',
+ 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j',
+ 'k',
+ // value 3
+ 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r',
+ };
+
+ EXPECT_TRUE(
+ framer.ProcessInput(StringPiece(AsChars(input), arraysize(input))));
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ EXPECT_EQ(0, visitor.error_count_);
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(3u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", CryptoTestUtils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", CryptoTestUtils::GetValueForTag(message, 0x12345679));
+ EXPECT_EQ("lmnopqr", CryptoTestUtils::GetValueForTag(message, 0x1234567A));
+}
+
+TEST(CryptoFramerTest, ProcessInputIncrementally) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x06, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x0b, 0x00, 0x00, 0x00,
+ // value 1
+ 'a', 'b', 'c', 'd',
+ 'e', 'f',
+ // value 2
+ 'g', 'h', 'i', 'j',
+ 'k',
+ };
+
+ for (size_t i = 0; i < arraysize(input); i++) {
+ EXPECT_TRUE(framer.ProcessInput(StringPiece(AsChars(input) + i, 1)));
+ }
+ EXPECT_EQ(0u, framer.InputBytesRemaining());
+ ASSERT_EQ(1u, visitor.messages_.size());
+ const CryptoHandshakeMessage& message = visitor.messages_[0];
+ EXPECT_EQ(0xFFAA7733, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abcdef", CryptoTestUtils::GetValueForTag(message, 0x12345678));
+ EXPECT_EQ("ghijk", CryptoTestUtils::GetValueForTag(message, 0x12345679));
+}
+
+TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x13,
+ // end offset 1
+ 0x01, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x02, 0x00, 0x00, 0x00,
+ };
+
+ EXPECT_FALSE(
+ framer.ProcessInput(StringPiece(AsChars(input), arraysize(input))));
+ EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error());
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessEndOffsetsOutOfOrder) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x01, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x78, 0x56, 0x34, 0x13,
+ // end offset 2
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ EXPECT_FALSE(
+ framer.ProcessInput(StringPiece(AsChars(input), arraysize(input))));
+ EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error());
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessInputTooManyEntries) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0xA0, 0x00,
+ // padding
+ 0x00, 0x00,
+ };
+
+ EXPECT_FALSE(
+ framer.ProcessInput(StringPiece(AsChars(input), arraysize(input))));
+ EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error());
+ EXPECT_EQ(1, visitor.error_count_);
+}
+
+TEST(CryptoFramerTest, ProcessInputZeroLength) {
+ test::TestCryptoVisitor visitor;
+ CryptoFramer framer;
+ framer.set_visitor(&visitor);
+
+ unsigned char input[] = {
+ // tag
+ 0x33, 0x77, 0xAA, 0xFF,
+ // num entries
+ 0x02, 0x00,
+ // padding
+ 0x00, 0x00,
+ // tag 1
+ 0x78, 0x56, 0x34, 0x12,
+ // end offset 1
+ 0x00, 0x00, 0x00, 0x00,
+ // tag 2
+ 0x79, 0x56, 0x34, 0x12,
+ // end offset 2
+ 0x05, 0x00, 0x00, 0x00,
+ };
+
+ EXPECT_TRUE(
+ framer.ProcessInput(StringPiece(AsChars(input), arraysize(input))));
+ EXPECT_EQ(0, visitor.error_count_);
+}
+
+} // namespace test
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc
new file mode 100644
index 0000000..5a928b6
--- /dev/null
+++ b/net/quic/crypto/crypto_handshake.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_handshake.h"
+
+#include "net/quic/crypto/common_cert_set.h"
+#include "net/quic/crypto/key_exchange.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+
+namespace net {
+
+QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters()
+ : key_exchange(0),
+ aead(0),
+ x509_ecdsa_supported(false) {
+}
+
+QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {}
+
+CrypterPair::CrypterPair() {}
+
+CrypterPair::~CrypterPair() {}
+
+// static
+const char QuicCryptoConfig::kInitialLabel[] = "QUIC key expansion";
+
+// static
+const char QuicCryptoConfig::kCETVLabel[] = "QUIC CETV block";
+
+// static
+const char QuicCryptoConfig::kForwardSecureLabel[] =
+ "QUIC forward secure key expansion";
+
+QuicCryptoConfig::QuicCryptoConfig()
+ : common_cert_sets(CommonCertSets::GetInstanceQUIC()) {
+}
+
+QuicCryptoConfig::~QuicCryptoConfig() {}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h
new file mode 100644
index 0000000..bfd2f34
--- /dev/null
+++ b/net/quic/crypto/crypto_handshake.h
@@ -0,0 +1,170 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
+#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class CommonCertSets;
+class KeyExchange;
+class QuicDecrypter;
+class QuicEncrypter;
+
+// HandshakeFailureReason enum values are uploaded to UMA, they cannot be
+// changed.
+enum HandshakeFailureReason {
+ HANDSHAKE_OK = 0,
+
+ // Failure reasons for an invalid client nonce in CHLO.
+ //
+ // The default error value for nonce verification failures from strike
+ // register (covers old strike registers and unknown failures).
+ CLIENT_NONCE_UNKNOWN_FAILURE = 1,
+ // Client nonce had incorrect length.
+ CLIENT_NONCE_INVALID_FAILURE = 2,
+ // Client nonce is not unique.
+ CLIENT_NONCE_NOT_UNIQUE_FAILURE = 3,
+ // Client orbit is invalid or incorrect.
+ CLIENT_NONCE_INVALID_ORBIT_FAILURE = 4,
+ // Client nonce's timestamp is not in the strike register's valid time range.
+ CLIENT_NONCE_INVALID_TIME_FAILURE = 5,
+ // Strike register's RPC call timed out, client nonce couldn't be verified.
+ CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT = 6,
+ // Strike register is down, client nonce couldn't be verified.
+ CLIENT_NONCE_STRIKE_REGISTER_FAILURE = 7,
+
+ // Failure reasons for an invalid server nonce in CHLO.
+ //
+ // Unbox of server nonce failed.
+ SERVER_NONCE_DECRYPTION_FAILURE = 8,
+ // Decrypted server nonce had incorrect length.
+ SERVER_NONCE_INVALID_FAILURE = 9,
+ // Server nonce is not unique.
+ SERVER_NONCE_NOT_UNIQUE_FAILURE = 10,
+ // Server nonce's timestamp is not in the strike register's valid time range.
+ SERVER_NONCE_INVALID_TIME_FAILURE = 11,
+
+ // Failure reasons for an invalid server config in CHLO.
+ //
+ // Missing Server config id (kSCID) tag.
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE = 12,
+ // Couldn't find the Server config id (kSCID).
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE = 13,
+
+ // Failure reasons for an invalid source-address token.
+ //
+ // Missing Source-address token (kSourceAddressTokenTag) tag.
+ SOURCE_ADDRESS_TOKEN_INVALID_FAILURE = 14,
+ // Unbox of Source-address token failed.
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE = 15,
+ // Couldn't parse the unbox'ed Source-address token.
+ SOURCE_ADDRESS_TOKEN_PARSE_FAILURE = 16,
+ // Source-address token is for a different IP address.
+ SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE = 17,
+ // The source-address token has a timestamp in the future.
+ SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE = 18,
+ // The source-address token has expired.
+ SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE = 19,
+
+ MAX_FAILURE_REASON,
+};
+
+// These errors will be packed into an uint32 and we don't want to set the most
+// significant bit, which may be misinterpreted as the sign bit.
+COMPILE_ASSERT(MAX_FAILURE_REASON <= 32, failure_reason_out_of_sync);
+
+// A CrypterPair contains the encrypter and decrypter for an encryption level.
+struct NET_EXPORT_PRIVATE CrypterPair {
+ CrypterPair();
+ ~CrypterPair();
+ scoped_ptr<QuicEncrypter> encrypter;
+ scoped_ptr<QuicDecrypter> decrypter;
+};
+
+// Parameters negotiated by the crypto handshake.
+struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters {
+ // Initializes the members to 0 or empty values.
+ QuicCryptoNegotiatedParameters();
+ ~QuicCryptoNegotiatedParameters();
+
+ QuicTag key_exchange;
+ QuicTag aead;
+ std::string initial_premaster_secret;
+ std::string forward_secure_premaster_secret;
+ // subkey_secret is used as the PRK input to the HKDF used for key extraction.
+ std::string subkey_secret;
+ CrypterPair initial_crypters;
+ CrypterPair forward_secure_crypters;
+ // Normalized SNI: converted to lower case and trailing '.' removed.
+ std::string sni;
+ std::string client_nonce;
+ std::string server_nonce;
+ // hkdf_input_suffix contains the HKDF input following the label: the
+ // ConnectionId, client hello and server config. This is only populated in the
+ // client because only the client needs to derive the forward secure keys at a
+ // later time from the initial keys.
+ std::string hkdf_input_suffix;
+ // cached_certs contains the cached certificates that a client used when
+ // sending a client hello.
+ std::vector<std::string> cached_certs;
+ // client_key_exchange is used by clients to store the ephemeral KeyExchange
+ // for the connection.
+ scoped_ptr<KeyExchange> client_key_exchange;
+ // channel_id is set by servers to a ChannelID key when the client correctly
+ // proves possession of the corresponding private key. It consists of 32
+ // bytes of x coordinate, followed by 32 bytes of y coordinate. Both values
+ // are big-endian and the pair is a P-256 public key.
+ std::string channel_id;
+
+ // Used when generating proof signature when sending server config updates.
+ bool x509_ecdsa_supported;
+
+ // Used to generate cert chain when sending server config updates.
+ std::string client_common_set_hashes;
+ std::string client_cached_cert_hashes;
+};
+
+// QuicCryptoConfig contains common configuration between clients and servers.
+class NET_EXPORT_PRIVATE QuicCryptoConfig {
+ public:
+ // kInitialLabel is a constant that is used when deriving the initial
+ // (non-forward secure) keys for the connection in order to tie the resulting
+ // key to this protocol.
+ static const char kInitialLabel[];
+
+ // kCETVLabel is a constant that is used when deriving the keys for the
+ // encrypted tag/value block in the client hello.
+ static const char kCETVLabel[];
+
+ // kForwardSecureLabel is a constant that is used when deriving the forward
+ // secure keys for the connection in order to tie the resulting key to this
+ // protocol.
+ static const char kForwardSecureLabel[];
+
+ QuicCryptoConfig();
+ ~QuicCryptoConfig();
+
+ // Key exchange methods. The following two members' values correspond by
+ // index.
+ QuicTagVector kexs;
+ // Authenticated encryption with associated data (AEAD) algorithms.
+ QuicTagVector aead;
+
+ const CommonCertSets* common_cert_sets;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoConfig);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_
diff --git a/net/quic/crypto/crypto_handshake_message.cc b/net/quic/crypto/crypto_handshake_message.cc
new file mode 100644
index 0000000..9125968
--- /dev/null
+++ b/net/quic/crypto/crypto_handshake_message.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_handshake_message.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_socket_address_coder.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace net {
+
+CryptoHandshakeMessage::CryptoHandshakeMessage()
+ : tag_(0),
+ minimum_size_(0) {}
+
+CryptoHandshakeMessage::CryptoHandshakeMessage(
+ const CryptoHandshakeMessage& other)
+ : tag_(other.tag_),
+ tag_value_map_(other.tag_value_map_),
+ minimum_size_(other.minimum_size_) {
+ // Don't copy serialized_. scoped_ptr doesn't have a copy constructor.
+ // The new object can lazily reconstruct serialized_.
+}
+
+CryptoHandshakeMessage::~CryptoHandshakeMessage() {}
+
+CryptoHandshakeMessage& CryptoHandshakeMessage::operator=(
+ const CryptoHandshakeMessage& other) {
+ tag_ = other.tag_;
+ tag_value_map_ = other.tag_value_map_;
+ // Don't copy serialized_. scoped_ptr doesn't have an assignment operator.
+ // However, invalidate serialized_.
+ serialized_.reset();
+ minimum_size_ = other.minimum_size_;
+ return *this;
+}
+
+void CryptoHandshakeMessage::Clear() {
+ tag_ = 0;
+ tag_value_map_.clear();
+ minimum_size_ = 0;
+ serialized_.reset();
+}
+
+const QuicData& CryptoHandshakeMessage::GetSerialized() const {
+ if (!serialized_.get()) {
+ serialized_.reset(CryptoFramer::ConstructHandshakeMessage(*this));
+ }
+ return *serialized_.get();
+}
+
+void CryptoHandshakeMessage::MarkDirty() {
+ serialized_.reset();
+}
+
+void CryptoHandshakeMessage::SetTaglist(QuicTag tag, ...) {
+ // Warning, if sizeof(QuicTag) > sizeof(int) then this function will break
+ // because the terminating 0 will only be promoted to int.
+ COMPILE_ASSERT(sizeof(QuicTag) <= sizeof(int),
+ crypto_tag_may_not_be_larger_than_int_or_varargs_will_break);
+
+ vector<QuicTag> tags;
+ va_list ap;
+
+ va_start(ap, tag);
+ for (;;) {
+ QuicTag list_item = va_arg(ap, QuicTag);
+ if (list_item == 0) {
+ break;
+ }
+ tags.push_back(list_item);
+ }
+
+ // Because of the way that we keep tags in memory, we can copy the contents
+ // of the vector and get the correct bytes in wire format. See
+ // crypto_protocol.h. This assumes that the system is little-endian.
+ SetVector(tag, tags);
+
+ va_end(ap);
+}
+
+void CryptoHandshakeMessage::SetStringPiece(QuicTag tag, StringPiece value) {
+ tag_value_map_[tag] = value.as_string();
+}
+
+void CryptoHandshakeMessage::Erase(QuicTag tag) {
+ tag_value_map_.erase(tag);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetTaglist(QuicTag tag,
+ const QuicTag** out_tags,
+ size_t* out_len) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() % sizeof(QuicTag) != 0) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ *out_tags = nullptr;
+ *out_len = 0;
+ return ret;
+ }
+
+ *out_tags = reinterpret_cast<const QuicTag*>(it->second.data());
+ *out_len = it->second.size() / sizeof(QuicTag);
+ return ret;
+}
+
+bool CryptoHandshakeMessage::GetStringPiece(QuicTag tag,
+ StringPiece* out) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ if (it == tag_value_map_.end()) {
+ return false;
+ }
+ *out = it->second;
+ return true;
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetNthValue24(QuicTag tag,
+ unsigned index,
+ StringPiece* out) const {
+ StringPiece value;
+ if (!GetStringPiece(tag, &value)) {
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ for (unsigned i = 0;; i++) {
+ if (value.empty()) {
+ return QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND;
+ }
+ if (value.size() < 3) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ const unsigned char* data =
+ reinterpret_cast<const unsigned char*>(value.data());
+ size_t size = static_cast<size_t>(data[0]) |
+ (static_cast<size_t>(data[1]) << 8) |
+ (static_cast<size_t>(data[2]) << 16);
+ value.remove_prefix(3);
+
+ if (value.size() < size) {
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (i == index) {
+ *out = StringPiece(value.data(), size);
+ return QUIC_NO_ERROR;
+ }
+
+ value.remove_prefix(size);
+ }
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint16(QuicTag tag,
+ uint16* out) const {
+ return GetPOD(tag, out, sizeof(uint16));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint32(QuicTag tag,
+ uint32* out) const {
+ return GetPOD(tag, out, sizeof(uint32));
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetUint64(QuicTag tag,
+ uint64* out) const {
+ return GetPOD(tag, out, sizeof(uint64));
+}
+
+size_t CryptoHandshakeMessage::size() const {
+ size_t ret = sizeof(QuicTag) +
+ sizeof(uint16) /* number of entries */ +
+ sizeof(uint16) /* padding */;
+ ret += (sizeof(QuicTag) + sizeof(uint32) /* end offset */) *
+ tag_value_map_.size();
+ for (QuicTagValueMap::const_iterator i = tag_value_map_.begin();
+ i != tag_value_map_.end(); ++i) {
+ ret += i->second.size();
+ }
+
+ return ret;
+}
+
+void CryptoHandshakeMessage::set_minimum_size(size_t min_bytes) {
+ if (min_bytes == minimum_size_) {
+ return;
+ }
+ serialized_.reset();
+ minimum_size_ = min_bytes;
+}
+
+size_t CryptoHandshakeMessage::minimum_size() const {
+ return minimum_size_;
+}
+
+string CryptoHandshakeMessage::DebugString() const {
+ return DebugStringInternal(0);
+}
+
+QuicErrorCode CryptoHandshakeMessage::GetPOD(
+ QuicTag tag, void* out, size_t len) const {
+ QuicTagValueMap::const_iterator it = tag_value_map_.find(tag);
+ QuicErrorCode ret = QUIC_NO_ERROR;
+
+ if (it == tag_value_map_.end()) {
+ ret = QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ } else if (it->second.size() != len) {
+ ret = QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (ret != QUIC_NO_ERROR) {
+ memset(out, 0, len);
+ return ret;
+ }
+
+ memcpy(out, it->second.data(), len);
+ return ret;
+}
+
+string CryptoHandshakeMessage::DebugStringInternal(size_t indent) const {
+ string ret = string(2 * indent, ' ') + QuicUtils::TagToString(tag_) + "<\n";
+ ++indent;
+ for (QuicTagValueMap::const_iterator it = tag_value_map_.begin();
+ it != tag_value_map_.end(); ++it) {
+ ret += string(2 * indent, ' ') + QuicUtils::TagToString(it->first) + ": ";
+
+ bool done = false;
+ switch (it->first) {
+ case kICSL:
+ case kIFCW:
+ case kCFCW:
+ case kSFCW:
+ case kIRTT:
+ case kKATO:
+ case kMSPC:
+ case kSWND:
+ // uint32 value
+ if (it->second.size() == 4) {
+ uint32 value;
+ memcpy(&value, it->second.data(), sizeof(value));
+ ret += base::UintToString(value);
+ done = true;
+ }
+ break;
+ case kKEXS:
+ case kAEAD:
+ case kCGST:
+ case kCOPT:
+ case kPDMD:
+ case kVER:
+ // tag lists
+ if (it->second.size() % sizeof(QuicTag) == 0) {
+ for (size_t j = 0; j < it->second.size(); j += sizeof(QuicTag)) {
+ QuicTag tag;
+ memcpy(&tag, it->second.data() + j, sizeof(tag));
+ if (j > 0) {
+ ret += ",";
+ }
+ ret += "'" + QuicUtils::TagToString(tag) + "'";
+ }
+ done = true;
+ }
+ break;
+ case kCADR:
+ // IP address and port
+ if (!it->second.empty()) {
+ QuicSocketAddressCoder decoder;
+ if (decoder.Decode(it->second.data(), it->second.size())) {
+ ret += IPAddressToStringWithPort(decoder.ip(), decoder.port());
+ done = true;
+ }
+ }
+ break;
+ case kSCFG:
+ // nested messages.
+ if (!it->second.empty()) {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(it->second));
+ if (msg.get()) {
+ ret += "\n";
+ ret += msg->DebugStringInternal(indent + 1);
+
+ done = true;
+ }
+ }
+ break;
+ case kPAD:
+ ret += StringPrintf("(%d bytes of padding)",
+ static_cast<int>(it->second.size()));
+ done = true;
+ break;
+ case kUAID:
+ ret += it->second;
+ done = true;
+ break;
+ }
+
+ if (!done) {
+ // If there's no specific format for this tag, or the value is invalid,
+ // then just use hex.
+ ret += "0x" + base::HexEncode(it->second.data(), it->second.size());
+ }
+ ret += "\n";
+ }
+ --indent;
+ ret += string(2 * indent, ' ') + ">";
+ return ret;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_handshake_message.h b/net/quic/crypto/crypto_handshake_message.h
new file mode 100644
index 0000000..fcb3930
--- /dev/null
+++ b/net/quic/crypto/crypto_handshake_message.h
@@ -0,0 +1,135 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+#define NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// An intermediate format of a handshake message that's convenient for a
+// CryptoFramer to serialize from or parse into.
+class NET_EXPORT_PRIVATE CryptoHandshakeMessage {
+ public:
+ CryptoHandshakeMessage();
+ CryptoHandshakeMessage(const CryptoHandshakeMessage& other);
+ ~CryptoHandshakeMessage();
+
+ CryptoHandshakeMessage& operator=(const CryptoHandshakeMessage& other);
+
+ // Clears state.
+ void Clear();
+
+ // GetSerialized returns the serialized form of this message and caches the
+ // result. Subsequently altering the message does not invalidate the cache.
+ const QuicData& GetSerialized() const;
+
+ // MarkDirty invalidates the cache created by |GetSerialized|.
+ void MarkDirty();
+
+ // SetValue sets an element with the given tag to the raw, memory contents of
+ // |v|.
+ template<class T> void SetValue(QuicTag tag, const T& v) {
+ tag_value_map_[tag] =
+ std::string(reinterpret_cast<const char*>(&v), sizeof(v));
+ }
+
+ // SetVector sets an element with the given tag to the raw contents of an
+ // array of elements in |v|.
+ template<class T> void SetVector(QuicTag tag, const std::vector<T>& v) {
+ if (v.empty()) {
+ tag_value_map_[tag] = std::string();
+ } else {
+ tag_value_map_[tag] = std::string(reinterpret_cast<const char*>(&v[0]),
+ v.size() * sizeof(T));
+ }
+ }
+
+ // Returns the message tag.
+ QuicTag tag() const { return tag_; }
+ // Sets the message tag.
+ void set_tag(QuicTag tag) { tag_ = tag; }
+
+ const QuicTagValueMap& tag_value_map() const { return tag_value_map_; }
+
+ // SetTaglist sets an element with the given tag to contain a list of tags,
+ // passed as varargs. The argument list must be terminated with a 0 element.
+ void SetTaglist(QuicTag tag, ...);
+
+ void SetStringPiece(QuicTag tag, base::StringPiece value);
+
+ // Erase removes a tag/value, if present, from the message.
+ void Erase(QuicTag tag);
+
+ // GetTaglist finds an element with the given tag containing zero or more
+ // tags. If such a tag doesn't exist, it returns false. Otherwise it sets
+ // |out_tags| and |out_len| to point to the array of tags and returns true.
+ // The array points into the CryptoHandshakeMessage and is valid only for as
+ // long as the CryptoHandshakeMessage exists and is not modified.
+ QuicErrorCode GetTaglist(QuicTag tag, const QuicTag** out_tags,
+ size_t* out_len) const;
+
+ bool GetStringPiece(QuicTag tag, base::StringPiece* out) const;
+
+ // GetNthValue24 interprets the value with the given tag to be a series of
+ // 24-bit, length prefixed values and it returns the subvalue with the given
+ // index.
+ QuicErrorCode GetNthValue24(QuicTag tag,
+ unsigned index,
+ base::StringPiece* out) const;
+ QuicErrorCode GetUint16(QuicTag tag, uint16* out) const;
+ QuicErrorCode GetUint32(QuicTag tag, uint32* out) const;
+ QuicErrorCode GetUint64(QuicTag tag, uint64* out) const;
+
+ // size returns 4 (message tag) + 2 (uint16, number of entries) +
+ // (4 (tag) + 4 (end offset))*tag_value_map_.size() + ∑ value sizes.
+ size_t size() const;
+
+ // set_minimum_size sets the minimum number of bytes that the message should
+ // consume. The CryptoFramer will add a PAD tag as needed when serializing in
+ // order to ensure this. Setting a value of 0 disables padding.
+ //
+ // Padding is useful in order to ensure that messages are a minimum size. A
+ // QUIC server can require a minimum size in order to reduce the
+ // amplification factor of any mirror DoS attack.
+ void set_minimum_size(size_t min_bytes);
+
+ size_t minimum_size() const;
+
+ // DebugString returns a multi-line, string representation of the message
+ // suitable for including in debug output.
+ std::string DebugString() const;
+
+ private:
+ // GetPOD is a utility function for extracting a plain-old-data value. If
+ // |tag| exists in the message, and has a value of exactly |len| bytes then
+ // it copies |len| bytes of data into |out|. Otherwise |len| bytes at |out|
+ // are zeroed out.
+ //
+ // If used to copy integers then this assumes that the machine is
+ // little-endian.
+ QuicErrorCode GetPOD(QuicTag tag, void* out, size_t len) const;
+
+ std::string DebugStringInternal(size_t indent) const;
+
+ QuicTag tag_;
+ QuicTagValueMap tag_value_map_;
+
+ size_t minimum_size_;
+
+ // The serialized form of the handshake message. This member is constructed
+ // lasily.
+ mutable scoped_ptr<QuicData> serialized_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_MESSAGE_H_
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
new file mode 100644
index 0000000..0dd3b34
--- /dev/null
+++ b/net/quic/crypto/crypto_protocol.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
+#define NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
+
+#include <string>
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+// Version and Crypto tags are written to the wire with a big-endian
+// representation of the name of the tag. For example
+// the client hello tag (CHLO) will be written as the
+// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is
+// stored in memory as a little endian uint32, we need
+// to reverse the order of the bytes.
+//
+// We use a macro to ensure that no static initialisers are created. Use the
+// MakeQuicTag function in normal code.
+#define TAG(a, b, c, d) \
+ static_cast<QuicTag>((d << 24) + (c << 16) + (b << 8) + a)
+
+namespace net {
+
+typedef std::string ServerConfigID;
+
+const QuicTag kCHLO = TAG('C', 'H', 'L', 'O'); // Client hello
+const QuicTag kSHLO = TAG('S', 'H', 'L', 'O'); // Server hello
+const QuicTag kSCFG = TAG('S', 'C', 'F', 'G'); // Server config
+const QuicTag kREJ = TAG('R', 'E', 'J', '\0'); // Reject
+const QuicTag kCETV = TAG('C', 'E', 'T', 'V'); // Client encrypted tag-value
+ // pairs
+const QuicTag kPRST = TAG('P', 'R', 'S', 'T'); // Public reset
+const QuicTag kSCUP = TAG('S', 'C', 'U', 'P'); // Server config update.
+
+// Key exchange methods
+const QuicTag kP256 = TAG('P', '2', '5', '6'); // ECDH, Curve P-256
+const QuicTag kC255 = TAG('C', '2', '5', '5'); // ECDH, Curve25519
+
+// AEAD algorithms
+const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm
+const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12
+const QuicTag kCC12 = TAG('C', 'C', '1', '2'); // ChaCha20 + Poly1305
+
+// Socket receive buffer
+const QuicTag kSRBF = TAG('S', 'R', 'B', 'F'); // Socket receive buffer
+
+// Congestion control feedback types
+const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic
+
+// Connection options (COPT) values
+const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP
+const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control
+const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10
+const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic
+const QuicTag k1CON = TAG('1', 'C', 'O', 'N'); // Emulate a single connection
+
+// Loss detection algorithm types
+const QuicTag kNACK = TAG('N', 'A', 'C', 'K'); // TCP style nack counting
+const QuicTag kTIME = TAG('T', 'I', 'M', 'E'); // Time based
+
+// FEC options
+const QuicTag kFHDR = TAG('F', 'H', 'D', 'R'); // FEC protect headers
+
+// Proof types (i.e. certificate types)
+// NOTE: although it would be silly to do so, specifying both kX509 and kX59R
+// is allowed and is equivalent to specifying only kX509.
+const QuicTag kX509 = TAG('X', '5', '0', '9'); // X.509 certificate, all key
+ // types
+const QuicTag kX59R = TAG('X', '5', '9', 'R'); // X.509 certificate, RSA keys
+ // only
+const QuicTag kCHID = TAG('C', 'H', 'I', 'D'); // Channel ID.
+
+// Client hello tags
+const QuicTag kVER = TAG('V', 'E', 'R', '\0'); // Version (new)
+const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce
+const QuicTag kKEXS = TAG('K', 'E', 'X', 'S'); // Key exchange methods
+const QuicTag kAEAD = TAG('A', 'E', 'A', 'D'); // Authenticated
+ // encryption algorithms
+const QuicTag kCGST = TAG('C', 'G', 'S', 'T'); // Congestion control
+ // feedback types
+const QuicTag kCOPT = TAG('C', 'O', 'P', 'T'); // Connection options
+const QuicTag kICSL = TAG('I', 'C', 'S', 'L'); // Idle connection state
+ // lifetime
+const QuicTag kKATO = TAG('K', 'A', 'T', 'O'); // Keepalive timeout
+const QuicTag kMSPC = TAG('M', 'S', 'P', 'C'); // Max streams per connection.
+const QuicTag kIRTT = TAG('I', 'R', 'T', 'T'); // Estimated initial RTT in us.
+const QuicTag kSWND = TAG('S', 'W', 'N', 'D'); // Server's Initial congestion
+ // window.
+const QuicTag kSNI = TAG('S', 'N', 'I', '\0'); // Server name
+ // indication
+const QuicTag kPUBS = TAG('P', 'U', 'B', 'S'); // Public key values
+const QuicTag kSCID = TAG('S', 'C', 'I', 'D'); // Server config id
+const QuicTag kORBT = TAG('O', 'B', 'I', 'T'); // Server orbit.
+const QuicTag kPDMD = TAG('P', 'D', 'M', 'D'); // Proof demand.
+const QuicTag kPROF = TAG('P', 'R', 'O', 'F'); // Proof (signature).
+const QuicTag kCCS = TAG('C', 'C', 'S', 0); // Common certificate set
+const QuicTag kCCRT = TAG('C', 'C', 'R', 'T'); // Cached certificate
+const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y'); // Expiry
+// TODO(rjshade): Remove kIFCW when removing QUIC_VERSION_19.
+const QuicTag kIFCW = TAG('I', 'F', 'C', 'W'); // Initial flow control receive
+ // window.
+const QuicTag kSFCW = TAG('S', 'F', 'C', 'W'); // Initial stream flow control
+ // receive window.
+const QuicTag kCFCW = TAG('C', 'F', 'C', 'W'); // Initial session/connection
+ // flow control receive window.
+const QuicTag kUAID = TAG('U', 'A', 'I', 'D'); // Client's User Agent ID.
+
+// Rejection tags
+const QuicTag kRREJ = TAG('R', 'R', 'E', 'J'); // Reasons for server sending
+ // rejection message tag.
+
+// Server hello tags
+const QuicTag kCADR = TAG('C', 'A', 'D', 'R'); // Client IP address and port
+
+// CETV tags
+const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key
+const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature
+
+// Public reset tags
+const QuicTag kRNON = TAG('R', 'N', 'O', 'N'); // Public reset nonce proof
+const QuicTag kRSEQ = TAG('R', 'S', 'E', 'Q'); // Rejected sequence number
+
+// Universal tags
+const QuicTag kPAD = TAG('P', 'A', 'D', '\0'); // Padding
+
+// These tags have a special form so that they appear either at the beginning
+// or the end of a handshake message. Since handshake messages are sorted by
+// tag value, the tags with 0 at the end will sort first and those with 255 at
+// the end will sort last.
+//
+// The certificate chain should have a tag that will cause it to be sorted at
+// the end of any handshake messages because it's likely to be large and the
+// client might be able to get everything that it needs from the small values at
+// the beginning.
+//
+// Likewise tags with random values should be towards the beginning of the
+// message because the server mightn't hold state for a rejected client hello
+// and therefore the client may have issues reassembling the rejection message
+// in the event that it sent two client hellos.
+const QuicTag kServerNonceTag =
+ TAG('S', 'N', 'O', 0); // The server's nonce
+const QuicTag kSourceAddressTokenTag =
+ TAG('S', 'T', 'K', 0); // Source-address token
+const QuicTag kCertificateTag =
+ TAG('C', 'R', 'T', 255); // Certificate chain
+
+#undef TAG
+
+const size_t kMaxEntries = 128; // Max number of entries in a message.
+
+const size_t kNonceSize = 32; // Size in bytes of the connection nonce.
+
+const size_t kOrbitSize = 8; // Number of bytes in an orbit value.
+
+// kProofSignatureLabel is prepended to server configs before signing to avoid
+// any cross-protocol attacks on the signature.
+const char kProofSignatureLabel[] = "QUIC server config signature";
+
+// kClientHelloMinimumSize is the minimum size of a client hello. Client hellos
+// will have PAD tags added in order to ensure this minimum is met and client
+// hellos smaller than this will be an error. This minimum size reduces the
+// amplification factor of any mirror DoS attack.
+//
+// A client may pad an inchoate client hello to a size larger than
+// kClientHelloMinimumSize to make it more likely to receive a complete
+// rejection message.
+const size_t kClientHelloMinimumSize = 1024;
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_
diff --git a/net/quic/crypto/crypto_secret_boxer.cc b/net/quic/crypto/crypto_secret_boxer.cc
new file mode 100644
index 0000000..5d8a11b
--- /dev/null
+++ b/net/quic/crypto/crypto_secret_boxer.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_secret_boxer.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+// Defined kKeySize for GetKeySize() and SetKey().
+static const size_t kKeySize = 16;
+
+// kBoxNonceSize contains the number of bytes of nonce that we use in each box.
+// TODO(rtenneti): Add support for kBoxNonceSize to be 16 bytes.
+//
+// From agl@:
+// 96-bit nonces are on the edge. An attacker who can collect 2^41
+// source-address tokens has a 1% chance of finding a duplicate.
+//
+// The "average" DDoS is now 32.4M PPS. That's 2^25 source-address tokens
+// per second. So one day of that DDoS botnot would reach the 1% mark.
+//
+// It's not terrible, but it's not a "forget about it" margin.
+static const size_t kBoxNonceSize = 12;
+
+// static
+size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; }
+
+void CryptoSecretBoxer::SetKey(StringPiece key) {
+ DCHECK_EQ(kKeySize, key.size());
+ key_ = key.as_string();
+}
+
+string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const {
+ scoped_ptr<QuicEncrypter> encrypter(QuicEncrypter::Create(kAESG));
+ if (!encrypter->SetKey(key_)) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's encrypter->SetKey failed.";
+ return string();
+ }
+ size_t ciphertext_size = encrypter->GetCiphertextSize(plaintext.length());
+
+ string ret;
+ const size_t len = kBoxNonceSize + ciphertext_size;
+ ret.resize(len);
+ char* data = &ret[0];
+
+ // Generate nonce.
+ rand->RandBytes(data, kBoxNonceSize);
+ memcpy(data + kBoxNonceSize, plaintext.data(), plaintext.size());
+
+ if (!encrypter->Encrypt(StringPiece(data, kBoxNonceSize), StringPiece(),
+ plaintext, reinterpret_cast<unsigned char*>(
+ data + kBoxNonceSize))) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's Encrypt failed.";
+ return string();
+ }
+
+ return ret;
+}
+
+bool CryptoSecretBoxer::Unbox(StringPiece ciphertext,
+ string* out_storage,
+ StringPiece* out) const {
+ if (ciphertext.size() < kBoxNonceSize) {
+ return false;
+ }
+
+ char nonce[kBoxNonceSize];
+ memcpy(nonce, ciphertext.data(), kBoxNonceSize);
+ ciphertext.remove_prefix(kBoxNonceSize);
+
+ size_t len = ciphertext.size();
+ out_storage->resize(len);
+ char* data = const_cast<char*>(out_storage->data());
+
+ scoped_ptr<QuicDecrypter> decrypter(QuicDecrypter::Create(kAESG));
+ if (!decrypter->SetKey(key_)) {
+ DLOG(DFATAL) << "CryptoSecretBoxer's decrypter->SetKey failed.";
+ return false;
+ }
+ if (!decrypter->Decrypt(StringPiece(nonce, kBoxNonceSize), StringPiece(),
+ ciphertext, reinterpret_cast<unsigned char*>(data),
+ &len)) {
+ return false;
+ }
+
+ out->set(data, len);
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_secret_boxer.h b/net/quic/crypto/crypto_secret_boxer.h
new file mode 100644
index 0000000..38b8fb3
--- /dev/null
+++ b/net/quic/crypto/crypto_secret_boxer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_SECRET_BOXER_H_
+#define NET_QUIC_CRYPTO_CRYPTO_SECRET_BOXER_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class QuicRandom;
+
+// CryptoSecretBoxer encrypts small chunks of plaintext (called 'boxing') and
+// then, later, can authenticate+decrypt the resulting boxes. This object is
+// thread-safe.
+class NET_EXPORT_PRIVATE CryptoSecretBoxer {
+ public:
+ CryptoSecretBoxer() {}
+
+ // GetKeySize returns the number of bytes in a key.
+ static size_t GetKeySize();
+
+ // SetKey sets the key for this object. This must be done before |Box| or
+ // |Unbox| are called. |key| must be |GetKeySize()| bytes long.
+ void SetKey(base::StringPiece key);
+
+ // Box encrypts |plaintext| using a random nonce generated from |rand| and
+ // returns the resulting ciphertext. Since an authenticator and nonce are
+ // included, the result will be slightly larger than |plaintext|.
+ std::string Box(QuicRandom* rand, base::StringPiece plaintext) const;
+
+ // Unbox takes the result of a previous call to |Box| in |ciphertext| and
+ // authenticates+decrypts it. If |ciphertext| is not authentic then it
+ // returns false. Otherwise, |out_storage| is used to store the result and
+ // |out| is set to point into |out_storage| and contains the original
+ // plaintext.
+ bool Unbox(base::StringPiece ciphertext,
+ std::string* out_storage,
+ base::StringPiece* out) const;
+
+ private:
+ std::string key_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoSecretBoxer);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_SECRET_BOXER_H_
diff --git a/net/quic/crypto/crypto_secret_boxer_test.cc b/net/quic/crypto/crypto_secret_boxer_test.cc
new file mode 100644
index 0000000..427d052
--- /dev/null
+++ b/net/quic/crypto/crypto_secret_boxer_test.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_secret_boxer.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/crypto/quic_random.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+TEST(CryptoSecretBoxerTest, BoxAndUnbox) {
+ StringPiece message("hello world");
+ const size_t key_size = CryptoSecretBoxer::GetKeySize();
+ scoped_ptr<uint8[]> key(new uint8[key_size]);
+ memset(key.get(), 0x11, key_size);
+
+ CryptoSecretBoxer boxer;
+ boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size));
+
+ const string box = boxer.Box(QuicRandom::GetInstance(), message);
+
+ string storage;
+ StringPiece result;
+ EXPECT_TRUE(boxer.Unbox(box, &storage, &result));
+ EXPECT_EQ(result, message);
+
+ EXPECT_FALSE(boxer.Unbox(string(1, 'X') + box, &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(box.substr(1, string::npos), &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(string(), &storage, &result));
+ EXPECT_FALSE(boxer.Unbox(string(1, box[0]^0x80) + box.substr(1, string::npos),
+ &storage, &result));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/crypto_server_config_protobuf.cc b/net/quic/crypto/crypto_server_config_protobuf.cc
new file mode 100644
index 0000000..d292f9e
--- /dev/null
+++ b/net/quic/crypto/crypto_server_config_protobuf.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_server_config_protobuf.h"
+
+#include "base/stl_util.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+QuicServerConfigProtobuf::QuicServerConfigProtobuf()
+ : primary_time_(QuicWallTime::Zero().ToUNIXSeconds()),
+ priority_(0) {
+}
+
+QuicServerConfigProtobuf::~QuicServerConfigProtobuf() {
+ STLDeleteElements(&keys_);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_server_config_protobuf.h b/net/quic/crypto/crypto_server_config_protobuf.h
new file mode 100644
index 0000000..57ebfb0
--- /dev/null
+++ b/net/quic/crypto/crypto_server_config_protobuf.h
@@ -0,0 +1,139 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_
+#define NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+namespace net {
+
+// QuicServerConfigProtobuf contains QUIC server config block and the private
+// keys needed to prove ownership.
+// TODO(rch): sync with server more rationally.
+class NET_EXPORT_PRIVATE QuicServerConfigProtobuf {
+ public:
+ // PrivateKey contains a QUIC tag of a key exchange algorithm and a
+ // serialised private key for that algorithm. The format of the serialised
+ // private key is specific to the algorithm in question.
+ class NET_EXPORT_PRIVATE PrivateKey {
+ public:
+ QuicTag tag() const {
+ return tag_;
+ }
+ void set_tag(QuicTag tag) {
+ tag_ = tag;
+ }
+ std::string private_key() const {
+ return private_key_;
+ }
+ void set_private_key(std::string key) {
+ private_key_ = key;
+ }
+
+ private:
+ QuicTag tag_;
+ std::string private_key_;
+ };
+
+ QuicServerConfigProtobuf();
+ ~QuicServerConfigProtobuf();
+
+ size_t key_size() const {
+ return keys_.size();
+ }
+
+ const PrivateKey& key(size_t i) const {
+ DCHECK_GT(keys_.size(), i);
+ return *keys_[i];
+ }
+
+ std::string config() const {
+ return config_;
+ }
+
+ void set_config(base::StringPiece config) {
+ config.CopyToString(&config_);
+ }
+
+ QuicServerConfigProtobuf::PrivateKey* add_key() {
+ keys_.push_back(new PrivateKey);
+ return keys_.back();
+ }
+
+ void clear_key() {
+ STLDeleteElements(&keys_);
+ }
+
+ bool has_primary_time() const {
+ return primary_time_ > 0;
+ }
+
+ int64 primary_time() const {
+ return primary_time_;
+ }
+
+ void set_primary_time(int64 primary_time) {
+ primary_time_ = primary_time;
+ }
+
+ bool has_priority() const {
+ return priority_ > 0;
+ }
+
+ uint64 priority() const {
+ return priority_;
+ }
+
+ void set_priority(int64 priority) {
+ priority_ = priority;
+ }
+
+ bool has_source_address_token_secret_override() const {
+ return !source_address_token_secret_override_.empty();
+ }
+
+ std::string source_address_token_secret_override() const {
+ return source_address_token_secret_override_;
+ }
+
+ void set_source_address_token_secret_override(
+ base::StringPiece source_address_token_secret_override) {
+ source_address_token_secret_override.CopyToString(
+ &source_address_token_secret_override_);
+ }
+
+ private:
+ std::vector<PrivateKey*> keys_;
+
+ // config_ is a serialised config in QUIC wire format.
+ std::string config_;
+
+ // primary_time_ contains a UNIX epoch seconds value that indicates when this
+ // config should become primary.
+ int64 primary_time_;
+
+ // Relative priority of this config vs other configs with the same
+ // primary time. For use as a secondary sort key when selecting the
+ // primary config.
+ uint64 priority_;
+
+ // Optional override to the secret used to box/unbox source address
+ // tokens when talking to clients that select this server config.
+ // It can be of any length as it is fed into a KDF before use.
+ std::string source_address_token_secret_override_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerConfigProtobuf);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_
diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc
new file mode 100644
index 0000000..bb47cbf
--- /dev/null
+++ b/net/quic/crypto/crypto_server_test.cc
@@ -0,0 +1,727 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ostream>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/secure_hash.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_socket_address_coder.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/delayed_verify_strike_register_client.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::ostream;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+class QuicCryptoServerConfigPeer {
+ public:
+ explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
+ : server_config_(server_config) {}
+
+ base::Lock* GetStrikeRegisterClientLock() {
+ return &server_config_->strike_register_client_lock_;
+ }
+
+ private:
+ QuicCryptoServerConfig* server_config_;
+};
+
+// Run tests with combinations of
+// {FLAGS_use_early_return_when_verifying_chlo,
+// FLAGS_send_quic_crypto_reject_reason}.
+struct TestParams {
+ TestParams(bool use_early_return_when_verifying_chlo,
+ bool send_quic_crypto_reject_reason)
+ : use_early_return_when_verifying_chlo(
+ use_early_return_when_verifying_chlo),
+ send_quic_crypto_reject_reason(send_quic_crypto_reject_reason) {
+ }
+
+ friend ostream& operator<<(ostream& os, const TestParams& p) {
+ os << "{ use_early_return_when_verifying_chlo: "
+ << p.use_early_return_when_verifying_chlo
+ << " send_quic_crypto_reject_reason: "
+ << p.send_quic_crypto_reject_reason << " }";
+ return os;
+ }
+
+ bool use_early_return_when_verifying_chlo;
+ bool send_quic_crypto_reject_reason;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+ vector<TestParams> params;
+ params.push_back(TestParams(false, false));
+ params.push_back(TestParams(false, true));
+ params.push_back(TestParams(true, false));
+ params.push_back(TestParams(true, true));
+ return params;
+}
+
+class CryptoServerTest : public ::testing::TestWithParam<TestParams> {
+ public:
+ CryptoServerTest()
+ : rand_(QuicRandom::GetInstance()),
+ client_address_(Loopback4(), 1234),
+ config_(QuicCryptoServerConfig::TESTING, rand_) {
+ config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting());
+ supported_versions_ = QuicSupportedVersions();
+ client_version_ = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.front()));
+
+ FLAGS_use_early_return_when_verifying_chlo =
+ GetParam().use_early_return_when_verifying_chlo;
+ FLAGS_send_quic_crypto_reject_reason =
+ GetParam().send_quic_crypto_reject_reason;
+ }
+
+ virtual void SetUp() {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ config_.AddDefaultConfig(rand_, &clock_,
+ config_options_));
+
+ StringPiece orbit;
+ CHECK(msg->GetStringPiece(kORBT, &orbit));
+ CHECK_EQ(sizeof(orbit_), orbit.size());
+ memcpy(orbit_, orbit.data(), orbit.size());
+
+ char public_value[32];
+ memset(public_value, 42, sizeof(public_value));
+
+ const string nonce_str = GenerateNonce();
+ nonce_hex_ = "#" + base::HexEncode(nonce_str.data(), nonce_str.size());
+ pub_hex_ = "#" + base::HexEncode(public_value, sizeof(public_value));
+
+ CryptoHandshakeMessage client_hello = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+ ShouldSucceed(client_hello);
+ // The message should be rejected because the source-address token is
+ // missing.
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+
+ StringPiece srct;
+ ASSERT_TRUE(out_.GetStringPiece(kSourceAddressTokenTag, &srct));
+ srct_hex_ = "#" + base::HexEncode(srct.data(), srct.size());
+
+ StringPiece scfg;
+ ASSERT_TRUE(out_.GetStringPiece(kSCFG, &scfg));
+ server_config_.reset(CryptoFramer::ParseMessage(scfg));
+
+ StringPiece scid;
+ ASSERT_TRUE(server_config_->GetStringPiece(kSCID, &scid));
+ scid_hex_ = "#" + base::HexEncode(scid.data(), scid.size());
+ }
+
+ // Helper used to accept the result of ValidateClientHello and pass
+ // it on to ProcessClientHello.
+ class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ ValidateCallback(CryptoServerTest* test,
+ bool should_succeed,
+ const char* error_substr,
+ bool* called)
+ : test_(test),
+ should_succeed_(should_succeed),
+ error_substr_(error_substr),
+ called_(called) {
+ *called_ = false;
+ }
+
+ virtual void RunImpl(const CryptoHandshakeMessage& client_hello,
+ const Result& result) OVERRIDE {
+ {
+ // Ensure that the strike register client lock is not held.
+ QuicCryptoServerConfigPeer peer(&test_->config_);
+ base::Lock* m = peer.GetStrikeRegisterClientLock();
+ // In Chromium, we will dead lock if the lock is held by the current
+ // thread. Chromium doesn't have AssertNotHeld API call.
+ // m->AssertNotHeld();
+ base::AutoLock lock(*m);
+ }
+ ASSERT_FALSE(*called_);
+ test_->ProcessValidationResult(
+ client_hello, result, should_succeed_, error_substr_);
+ *called_ = true;
+ }
+
+ private:
+ CryptoServerTest* test_;
+ bool should_succeed_;
+ const char* error_substr_;
+ bool* called_;
+ };
+
+ void CheckServerHello(const CryptoHandshakeMessage& server_hello) {
+ const QuicTag* versions;
+ size_t num_versions;
+ server_hello.GetTaglist(kVER, &versions, &num_versions);
+ ASSERT_EQ(QuicSupportedVersions().size(), num_versions);
+ for (size_t i = 0; i < num_versions; ++i) {
+ EXPECT_EQ(QuicVersionToQuicTag(QuicSupportedVersions()[i]), versions[i]);
+ }
+
+ StringPiece address;
+ ASSERT_TRUE(server_hello.GetStringPiece(kCADR, &address));
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(address.data(), address.size()));
+ EXPECT_EQ(client_address_.address(), decoder.ip());
+ EXPECT_EQ(client_address_.port(), decoder.port());
+ }
+
+ void ShouldSucceed(const CryptoHandshakeMessage& message) {
+ bool called = false;
+ RunValidate(message, new ValidateCallback(this, true, "", &called));
+ EXPECT_TRUE(called);
+ }
+
+ void RunValidate(
+ const CryptoHandshakeMessage& message,
+ ValidateClientHelloResultCallback* cb) {
+ config_.ValidateClientHello(message, client_address_, &clock_, cb);
+ }
+
+ void ShouldFailMentioning(const char* error_substr,
+ const CryptoHandshakeMessage& message) {
+ bool called = false;
+ ShouldFailMentioning(error_substr, message, &called);
+ EXPECT_TRUE(called);
+ }
+
+ void ShouldFailMentioning(const char* error_substr,
+ const CryptoHandshakeMessage& message,
+ bool* called) {
+ config_.ValidateClientHello(
+ message, client_address_, &clock_,
+ new ValidateCallback(this, false, error_substr, called));
+ }
+
+ void ProcessValidationResult(const CryptoHandshakeMessage& message,
+ const ValidateCallback::Result& result,
+ bool should_succeed,
+ const char* error_substr) {
+ string error_details;
+ QuicErrorCode error = config_.ProcessClientHello(
+ result, 1 /* ConnectionId */, client_address_,
+ supported_versions_.front(), supported_versions_, &clock_, rand_,
+ ¶ms_, &out_, &error_details);
+
+ if (should_succeed) {
+ ASSERT_EQ(error, QUIC_NO_ERROR)
+ << "Message failed with error " << error_details << ": "
+ << message.DebugString();
+ } else {
+ ASSERT_NE(error, QUIC_NO_ERROR)
+ << "Message didn't fail: " << message.DebugString();
+
+ EXPECT_TRUE(error_details.find(error_substr) != string::npos)
+ << error_substr << " not in " << error_details;
+ }
+ }
+
+ CryptoHandshakeMessage InchoateClientHello(const char* message_tag, ...) {
+ va_list ap;
+ va_start(ap, message_tag);
+
+ CryptoHandshakeMessage message =
+ CryptoTestUtils::BuildMessage(message_tag, ap);
+ va_end(ap);
+
+ message.SetStringPiece(kPAD, string(kClientHelloMinimumSize, '-'));
+ return message;
+ }
+
+ string GenerateNonce() {
+ string nonce;
+ CryptoUtils::GenerateNonce(
+ clock_.WallNow(), rand_,
+ StringPiece(reinterpret_cast<const char*>(orbit_), sizeof(orbit_)),
+ &nonce);
+ return nonce;
+ }
+
+ void CheckRejectReasons(
+ const HandshakeFailureReason* expected_handshake_failures,
+ size_t expected_count) {
+ const uint32* reject_reasons;
+ size_t num_reject_reasons;
+ COMPILE_ASSERT(sizeof(QuicTag) == sizeof(uint32), header_out_of_sync);
+ QuicErrorCode error_code = out_.GetTaglist(kRREJ, &reject_reasons,
+ &num_reject_reasons);
+ if (!FLAGS_send_quic_crypto_reject_reason) {
+ ASSERT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error_code);
+ return;
+ }
+ ASSERT_EQ(QUIC_NO_ERROR, error_code);
+
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ EXPECT_EQ(1u, num_reject_reasons);
+ } else {
+ EXPECT_EQ(expected_count, num_reject_reasons);
+ }
+ for (size_t i = 0; i < num_reject_reasons; ++i) {
+ EXPECT_EQ(expected_handshake_failures[i], reject_reasons[i]);
+ }
+ }
+
+ protected:
+ QuicRandom* const rand_;
+ MockClock clock_;
+ const IPEndPoint client_address_;
+ QuicVersionVector supported_versions_;
+ string client_version_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfig::ConfigOptions config_options_;
+ QuicCryptoNegotiatedParameters params_;
+ CryptoHandshakeMessage out_;
+ uint8 orbit_[kOrbitSize];
+
+ // These strings contain hex escaped values from the server suitable for
+ // passing to |InchoateClientHello| when constructing client hello messages.
+ string nonce_hex_, pub_hex_, srct_hex_, scid_hex_;
+ scoped_ptr<CryptoHandshakeMessage> server_config_;
+};
+
+// Run all CryptoServerTest with all combinations of
+// FLAGS_use_early_return_when_verifying_chlo and
+// FLAGS_send_quic_crypto_reject_reason.
+INSTANTIATE_TEST_CASE_P(CryptoServerTests,
+ CryptoServerTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(CryptoServerTest, BadSNI) {
+ static const char* kBadSNIs[] = {
+ "",
+ "foo",
+ "#00",
+ "#ff00",
+ "127.0.0.1",
+ "ffee::1",
+ };
+
+ string client_version = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.front()));
+
+ for (size_t i = 0; i < arraysize(kBadSNIs); i++) {
+ ShouldFailMentioning("SNI", InchoateClientHello(
+ "CHLO",
+ "SNI", kBadSNIs[i],
+ "VER\0", client_version.data(),
+ nullptr));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+ }
+}
+
+// TODO(rtenneti): Enable the DefaultCert test after implementing ProofSource.
+TEST_F(CryptoServerTest, DISABLED_DefaultCert) {
+ // Check that the server replies with a default certificate when no SNI is
+ // specified.
+ ShouldSucceed(InchoateClientHello(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", srct_hex_.c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ "PDMD", "X509",
+ "VER\0", client_version_.data(),
+ nullptr));
+
+ StringPiece cert, proof;
+ EXPECT_TRUE(out_.GetStringPiece(kCertificateTag, &cert));
+ EXPECT_TRUE(out_.GetStringPiece(kPROF, &proof));
+ EXPECT_NE(0u, cert.size());
+ EXPECT_NE(0u, proof.size());
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_INVALID_TIME_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, TooSmall) {
+ ShouldFailMentioning("too small", CryptoTestUtils::Message(
+ "CHLO",
+ "VER\0", client_version_.data(),
+ nullptr));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, BadSourceAddressToken) {
+ // Invalid source-address tokens should be ignored.
+ static const char* kBadSourceAddressTokens[] = {
+ "",
+ "foo",
+ "#0000",
+ "#0000000000000000000000000000000000000000",
+ };
+
+ for (size_t i = 0; i < arraysize(kBadSourceAddressTokens); i++) {
+ ShouldSucceed(InchoateClientHello(
+ "CHLO",
+ "STK", kBadSourceAddressTokens[i],
+ "VER\0", client_version_.data(),
+ nullptr));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+ }
+}
+
+TEST_P(CryptoServerTest, BadClientNonce) {
+ // Invalid nonces should be ignored.
+ static const char* kBadNonces[] = {
+ "",
+ "#0000",
+ "#0000000000000000000000000000000000000000",
+ };
+
+ for (size_t i = 0; i < arraysize(kBadNonces); i++) {
+ ShouldSucceed(InchoateClientHello(
+ "CHLO",
+ "NONC", kBadNonces[i],
+ "VER\0", client_version_.data(),
+ nullptr));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+ }
+}
+
+TEST_P(CryptoServerTest, DowngradeAttack) {
+ if (supported_versions_.size() == 1) {
+ // No downgrade attack is possible if the server only supports one version.
+ return;
+ }
+ // Set the client's preferred version to a supported version that
+ // is not the "current" version (supported_versions_.front()).
+ string bad_version = QuicUtils::TagToString(
+ QuicVersionToQuicTag(supported_versions_.back()));
+
+ ShouldFailMentioning("Downgrade", InchoateClientHello(
+ "CHLO",
+ "VER\0", bad_version.data(),
+ nullptr));
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptServerConfig) {
+ // This tests corrupted server config.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", (string(1, 'X') + scid_hex_).c_str(),
+ "#004b5453", srct_hex_.c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptSourceAddressToken) {
+ // This tests corrupted source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptClientNonceAndSourceAddressToken) {
+ // This test corrupts client nonce and source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", (string(1, 'X') + nonce_hex_).c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ CLIENT_NONCE_INVALID_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, CorruptMultipleTags) {
+ // This test corrupts client nonce, server nonce and source address token.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", (string(1, 'X') + srct_hex_).c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", (string(1, 'X') + nonce_hex_).c_str(),
+ "SNO\0", (string(1, 'X') + nonce_hex_).c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+ ShouldSucceed(msg);
+ ASSERT_EQ(kREJ, out_.tag());
+ const HandshakeFailureReason kRejectReasons[] = {
+ SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ CLIENT_NONCE_INVALID_FAILURE,
+ SERVER_NONCE_DECRYPTION_FAILURE,
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+TEST_P(CryptoServerTest, ReplayProtection) {
+ // This tests that disabling replay protection works.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", srct_hex_.c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+ ShouldSucceed(msg);
+ // The message should be rejected because the strike-register is still
+ // quiescent.
+ ASSERT_EQ(kREJ, out_.tag());
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ CLIENT_NONCE_INVALID_TIME_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+
+ config_.set_replay_protection(false);
+
+ ShouldSucceed(msg);
+ // The message should be accepted now.
+ ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
+
+ ShouldSucceed(msg);
+ // The message should accepted twice when replay protection is off.
+ ASSERT_EQ(kSHLO, out_.tag());
+ CheckServerHello(out_);
+}
+
+TEST(CryptoServerConfigGenerationTest, Determinism) {
+ // Test that using a deterministic PRNG causes the server-config to be
+ // deterministic.
+
+ MockRandom rand_a, rand_b;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a);
+ QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b);
+ scoped_ptr<CryptoHandshakeMessage> scfg_a(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+ scoped_ptr<CryptoHandshakeMessage> scfg_b(
+ b.AddDefaultConfig(&rand_b, &clock, options));
+
+ ASSERT_EQ(scfg_a->DebugString(), scfg_b->DebugString());
+}
+
+TEST(CryptoServerConfigGenerationTest, SCIDVaries) {
+ // This test ensures that the server config ID varies for different server
+ // configs.
+
+ MockRandom rand_a, rand_b;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a);
+ rand_b.ChangeValue();
+ QuicCryptoServerConfig b(QuicCryptoServerConfig::TESTING, &rand_b);
+ scoped_ptr<CryptoHandshakeMessage> scfg_a(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+ scoped_ptr<CryptoHandshakeMessage> scfg_b(
+ b.AddDefaultConfig(&rand_b, &clock, options));
+
+ StringPiece scid_a, scid_b;
+ EXPECT_TRUE(scfg_a->GetStringPiece(kSCID, &scid_a));
+ EXPECT_TRUE(scfg_b->GetStringPiece(kSCID, &scid_b));
+
+ EXPECT_NE(scid_a, scid_b);
+}
+
+
+TEST(CryptoServerConfigGenerationTest, SCIDIsHashOfServerConfig) {
+ MockRandom rand_a;
+ const QuicCryptoServerConfig::ConfigOptions options;
+ MockClock clock;
+
+ QuicCryptoServerConfig a(QuicCryptoServerConfig::TESTING, &rand_a);
+ scoped_ptr<CryptoHandshakeMessage> scfg(
+ a.AddDefaultConfig(&rand_a, &clock, options));
+
+ StringPiece scid;
+ EXPECT_TRUE(scfg->GetStringPiece(kSCID, &scid));
+ // Need to take a copy of |scid| has we're about to call |Erase|.
+ const string scid_str(scid.as_string());
+
+ scfg->Erase(kSCID);
+ scfg->MarkDirty();
+ const QuicData& serialized(scfg->GetSerialized());
+
+ scoped_ptr<crypto::SecureHash> hash(
+ crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+ hash->Update(serialized.data(), serialized.length());
+ uint8 digest[16];
+ hash->Finish(digest, sizeof(digest));
+
+ ASSERT_EQ(scid.size(), sizeof(digest));
+ EXPECT_EQ(0, memcmp(digest, scid_str.data(), sizeof(digest)));
+}
+
+class CryptoServerTestNoConfig : public CryptoServerTest {
+ public:
+ virtual void SetUp() {
+ // Deliberately don't add a config so that we can test this situation.
+ }
+};
+
+TEST_P(CryptoServerTestNoConfig, DontCrash) {
+ ShouldFailMentioning("No config", InchoateClientHello(
+ "CHLO",
+ "VER\0", client_version_.data(),
+ nullptr));
+
+ const HandshakeFailureReason kRejectReasons[] = {
+ SERVER_CONFIG_INCHOATE_HELLO_FAILURE
+ };
+ CheckRejectReasons(kRejectReasons, arraysize(kRejectReasons));
+}
+
+class AsyncStrikeServerVerificationTest : public CryptoServerTest {
+ protected:
+ AsyncStrikeServerVerificationTest() {
+ }
+
+ virtual void SetUp() {
+ const string kOrbit = "12345678";
+ config_options_.orbit = kOrbit;
+ strike_register_client_ = new DelayedVerifyStrikeRegisterClient(
+ 10000, // strike_register_max_entries
+ static_cast<uint32>(clock_.WallNow().ToUNIXSeconds()),
+ 60, // strike_register_window_secs
+ reinterpret_cast<const uint8 *>(kOrbit.data()),
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED);
+ config_.SetStrikeRegisterClient(strike_register_client_);
+ CryptoServerTest::SetUp();
+ strike_register_client_->StartDelayingVerification();
+ }
+
+ DelayedVerifyStrikeRegisterClient* strike_register_client_;
+};
+
+TEST_P(AsyncStrikeServerVerificationTest, AsyncReplayProtection) {
+ // This tests async validation with a strike register works.
+ CryptoHandshakeMessage msg = CryptoTestUtils::Message(
+ "CHLO",
+ "AEAD", "AESG",
+ "KEXS", "C255",
+ "SCID", scid_hex_.c_str(),
+ "#004b5453", srct_hex_.c_str(),
+ "PUBS", pub_hex_.c_str(),
+ "NONC", nonce_hex_.c_str(),
+ "VER\0", client_version_.data(),
+ "$padding", static_cast<int>(kClientHelloMinimumSize),
+ nullptr);
+
+ // Clear the message tag.
+ out_.set_tag(0);
+
+ bool called = false;
+ RunValidate(msg, new ValidateCallback(this, true, "", &called));
+ // The verification request was queued.
+ ASSERT_FALSE(called);
+ EXPECT_EQ(0u, out_.tag());
+ EXPECT_EQ(1, strike_register_client_->PendingVerifications());
+
+ // Continue processing the verification request.
+ strike_register_client_->RunPendingVerifications();
+ ASSERT_TRUE(called);
+ EXPECT_EQ(0, strike_register_client_->PendingVerifications());
+ // The message should be accepted now.
+ EXPECT_EQ(kSHLO, out_.tag());
+
+ // Rejected if replayed.
+ RunValidate(msg, new ValidateCallback(this, true, "", &called));
+ // The verification request was queued.
+ ASSERT_FALSE(called);
+ EXPECT_EQ(1, strike_register_client_->PendingVerifications());
+
+ strike_register_client_->RunPendingVerifications();
+ ASSERT_TRUE(called);
+ EXPECT_EQ(0, strike_register_client_->PendingVerifications());
+ // The message should be rejected now.
+ EXPECT_EQ(kREJ, out_.tag());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/crypto_utils.cc b/net/quic/crypto/crypto_utils.cc
new file mode 100644
index 0000000..7e0ab75
--- /dev/null
+++ b/net/quic/crypto/crypto_utils.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_utils.h"
+
+#include "crypto/hkdf.h"
+#include "net/base/net_util.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_time.h"
+#include "url/url_canon.h"
+
+using base::StringPiece;
+using std::numeric_limits;
+using std::string;
+
+namespace net {
+
+// static
+void CryptoUtils::GenerateNonce(QuicWallTime now,
+ QuicRandom* random_generator,
+ StringPiece orbit,
+ string* nonce) {
+ // a 4-byte timestamp + 28 random bytes.
+ nonce->reserve(kNonceSize);
+ nonce->resize(kNonceSize);
+ uint32 gmt_unix_time = now.ToUNIXSeconds();
+ // The time in the nonce must be encoded in big-endian because the
+ // strike-register depends on the nonces being ordered by time.
+ (*nonce)[0] = static_cast<char>(gmt_unix_time >> 24);
+ (*nonce)[1] = static_cast<char>(gmt_unix_time >> 16);
+ (*nonce)[2] = static_cast<char>(gmt_unix_time >> 8);
+ (*nonce)[3] = static_cast<char>(gmt_unix_time);
+
+ size_t bytes_written = sizeof(gmt_unix_time);
+ if (orbit.size() == 8) {
+ memcpy(&(*nonce)[bytes_written], orbit.data(), orbit.size());
+ bytes_written += orbit.size();
+ }
+ random_generator->RandBytes(&(*nonce)[bytes_written],
+ kNonceSize - bytes_written);
+}
+
+// static
+bool CryptoUtils::IsValidSNI(StringPiece sni) {
+ // TODO(rtenneti): Support RFC2396 hostname.
+ // NOTE: Microsoft does NOT enforce this spec, so if we throw away hostnames
+ // based on the above spec, we may be losing some hostnames that windows
+ // would consider valid. By far the most common hostname character NOT
+ // accepted by the above spec is '_'.
+ url::CanonHostInfo host_info;
+ string canonicalized_host(CanonicalizeHost(sni.as_string(), &host_info));
+ return !host_info.IsIPAddress() &&
+ IsCanonicalizedHostCompliant(canonicalized_host, std::string()) &&
+ sni.find_last_of('.') != string::npos;
+}
+
+// static
+string CryptoUtils::NormalizeHostname(const char* hostname) {
+ url::CanonHostInfo host_info;
+ string host(CanonicalizeHost(hostname, &host_info));
+
+ // Walk backwards over the string, stopping at the first trailing dot.
+ size_t host_end = host.length();
+ while (host_end != 0 && host[host_end - 1] == '.') {
+ host_end--;
+ }
+
+ // Erase the trailing dots.
+ if (host_end != host.length()) {
+ host.erase(host_end, host.length() - host_end);
+ }
+ return host;
+}
+
+// static
+bool CryptoUtils::DeriveKeys(StringPiece premaster_secret,
+ QuicTag aead,
+ StringPiece client_nonce,
+ StringPiece server_nonce,
+ const string& hkdf_input,
+ Perspective perspective,
+ CrypterPair* crypters,
+ string* subkey_secret) {
+ crypters->encrypter.reset(QuicEncrypter::Create(aead));
+ crypters->decrypter.reset(QuicDecrypter::Create(aead));
+ size_t key_bytes = crypters->encrypter->GetKeySize();
+ size_t nonce_prefix_bytes = crypters->encrypter->GetNoncePrefixSize();
+ size_t subkey_secret_bytes =
+ subkey_secret == nullptr ? 0 : premaster_secret.length();
+
+ StringPiece nonce = client_nonce;
+ string nonce_storage;
+ if (!server_nonce.empty()) {
+ nonce_storage = client_nonce.as_string() + server_nonce.as_string();
+ nonce = nonce_storage;
+ }
+
+ crypto::HKDF hkdf(premaster_secret, nonce, hkdf_input, key_bytes,
+ nonce_prefix_bytes, subkey_secret_bytes);
+ if (perspective == SERVER) {
+ if (!crypters->encrypter->SetKey(hkdf.server_write_key()) ||
+ !crypters->encrypter->SetNoncePrefix(hkdf.server_write_iv()) ||
+ !crypters->decrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->decrypter->SetNoncePrefix(hkdf.client_write_iv())) {
+ return false;
+ }
+ } else {
+ if (!crypters->encrypter->SetKey(hkdf.client_write_key()) ||
+ !crypters->encrypter->SetNoncePrefix(hkdf.client_write_iv()) ||
+ !crypters->decrypter->SetKey(hkdf.server_write_key()) ||
+ !crypters->decrypter->SetNoncePrefix(hkdf.server_write_iv())) {
+ return false;
+ }
+ }
+ if (subkey_secret != nullptr) {
+ hkdf.subkey_secret().CopyToString(subkey_secret);
+ }
+
+ return true;
+}
+
+// static
+bool CryptoUtils::ExportKeyingMaterial(StringPiece subkey_secret,
+ StringPiece label,
+ StringPiece context,
+ size_t result_len,
+ string* result) {
+ for (size_t i = 0; i < label.length(); i++) {
+ if (label[i] == '\0') {
+ LOG(ERROR) << "ExportKeyingMaterial label may not contain NULs";
+ return false;
+ }
+ }
+ // Create HKDF info input: null-terminated label + length-prefixed context
+ if (context.length() >= numeric_limits<uint32>::max()) {
+ LOG(ERROR) << "Context value longer than 2^32";
+ return false;
+ }
+ uint32 context_length = static_cast<uint32>(context.length());
+ string info = label.as_string();
+ info.push_back('\0');
+ info.append(reinterpret_cast<char*>(&context_length), sizeof(context_length));
+ info.append(context.data(), context.length());
+
+ crypto::HKDF hkdf(subkey_secret,
+ StringPiece() /* no salt */,
+ info,
+ result_len,
+ 0 /* no fixed IV */,
+ 0 /* no subkey secret */);
+ hkdf.client_write_key().CopyToString(result);
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/crypto_utils.h b/net/quic/crypto/crypto_utils.h
new file mode 100644
index 0000000..af3ecbb
--- /dev/null
+++ b/net/quic/crypto/crypto_utils.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Some helpers for quic crypto
+
+#ifndef NET_QUIC_CRYPTO_CRYPTO_UTILS_H_
+#define NET_QUIC_CRYPTO_CRYPTO_UTILS_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class QuicTime;
+class QuicRandom;
+struct QuicCryptoNegotiatedParameters;
+
+class NET_EXPORT_PRIVATE CryptoUtils {
+ public:
+ enum Perspective {
+ SERVER,
+ CLIENT,
+ };
+
+ // Generates the connection nonce. The nonce is formed as:
+ // <4 bytes> current time
+ // <8 bytes> |orbit| (or random if |orbit| is empty)
+ // <20 bytes> random
+ static void GenerateNonce(QuicWallTime now,
+ QuicRandom* random_generator,
+ base::StringPiece orbit,
+ std::string* nonce);
+
+ // Returns true if the sni is valid, false otherwise.
+ // (1) disallow IP addresses;
+ // (2) check that the hostname contains valid characters only; and
+ // (3) contains at least one dot.
+ static bool IsValidSNI(base::StringPiece sni);
+
+ // Convert hostname to lowercase and remove the trailing '.'.
+ // Returns |hostname|. NormalizeHostname() doesn't support IP address
+ // literals. IsValidSNI() should be called before calling NormalizeHostname().
+ static std::string NormalizeHostname(const char* hostname);
+
+ // DeriveKeys populates |crypters->encrypter|, |crypters->decrypter|, and
+ // |subkey_secret| (optional -- may be null) given the contents of
+ // |premaster_secret|, |client_nonce|, |server_nonce| and |hkdf_input|. |aead|
+ // determines which cipher will be used. |perspective| controls whether the
+ // server's keys are assigned to |encrypter| or |decrypter|. |server_nonce| is
+ // optional and, if non-empty, is mixed into the key derivation.
+ // |subkey_secret| will have the same length as |premaster_secret|.
+ static bool DeriveKeys(base::StringPiece premaster_secret,
+ QuicTag aead,
+ base::StringPiece client_nonce,
+ base::StringPiece server_nonce,
+ const std::string& hkdf_input,
+ Perspective perspective,
+ CrypterPair* crypters,
+ std::string* subkey_secret);
+
+ // Performs key extraction to derive a new secret of |result_len| bytes
+ // dependent on |subkey_secret|, |label|, and |context|. Returns false if the
+ // parameters are invalid (e.g. |label| contains null bytes); returns true on
+ // success.
+ static bool ExportKeyingMaterial(base::StringPiece subkey_secret,
+ base::StringPiece label,
+ base::StringPiece context,
+ size_t result_len,
+ std::string* result);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CryptoUtils);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CRYPTO_UTILS_H_
diff --git a/net/quic/crypto/crypto_utils_test.cc b/net/quic/crypto/crypto_utils_test.cc
new file mode 100644
index 0000000..fd9f005
--- /dev/null
+++ b/net/quic/crypto/crypto_utils_test.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/crypto_utils.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(CryptoUtilsTest, IsValidSNI) {
+ // IP as SNI.
+ EXPECT_FALSE(CryptoUtils::IsValidSNI("192.168.0.1"));
+ // SNI without any dot.
+ EXPECT_FALSE(CryptoUtils::IsValidSNI("somedomain"));
+ // Invalid RFC2396 hostname
+ // TODO(rtenneti): Support RFC2396 hostname.
+ // EXPECT_FALSE(CryptoUtils::IsValidSNI("some_domain.com"));
+ // An empty string must be invalid otherwise the QUIC client will try sending
+ // it.
+ EXPECT_FALSE(CryptoUtils::IsValidSNI(""));
+
+ // Valid SNI
+ EXPECT_TRUE(CryptoUtils::IsValidSNI("test.google.com"));
+}
+
+TEST(CryptoUtilsTest, NormalizeHostname) {
+ struct {
+ const char *input, *expected;
+ } tests[] = {
+ { "www.google.com", "www.google.com", },
+ { "WWW.GOOGLE.COM", "www.google.com", },
+ { "www.google.com.", "www.google.com", },
+ { "www.google.COM.", "www.google.com", },
+ { "www.google.com..", "www.google.com", },
+ { "www.google.com........", "www.google.com", },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ EXPECT_EQ(std::string(tests[i].expected),
+ CryptoUtils::NormalizeHostname(tests[i].input));
+ }
+}
+
+TEST(CryptoUtilsTest, TestExportKeyingMaterial) {
+ const struct TestVector {
+ // Input (strings of hexadecimal digits):
+ const char* subkey_secret;
+ const char* label;
+ const char* context;
+ size_t result_len;
+
+ // Expected output (string of hexadecimal digits):
+ const char* expected; // Null if it should fail.
+ } test_vector[] = {
+ // Try a typical input
+ { "4823c1189ecc40fce888fbb4cf9ae6254f19ba12e6d9af54788f195a6f509ca3",
+ "e934f78d7a71dd85420fceeb8cea0317",
+ "b8d766b5d3c8aba0009c7ed3de553eba53b4de1030ea91383dcdf724cd8b7217",
+ 32,
+ "a9979da0d5f1c1387d7cbe68f5c4163ddb445a03c4ad6ee72cb49d56726d679e"
+ },
+ // Don't let the label contain nulls
+ { "14fe51e082ffee7d1b4d8d4ab41f8c55",
+ "3132333435363700",
+ "58585858585858585858585858585858",
+ 16,
+ nullptr
+ },
+ // Make sure nulls in the context are fine
+ { "d862c2e36b0a42f7827c67ebc8d44df7",
+ "7a5b95e4e8378123",
+ "4142434445464700",
+ 16,
+ "12d418c6d0738a2e4d85b2d0170f76e1"
+ },
+ // ... and give a different result than without
+ { "d862c2e36b0a42f7827c67ebc8d44df7",
+ "7a5b95e4e8378123",
+ "41424344454647",
+ 16,
+ "abfa1c479a6e3ffb98a11dee7d196408"
+ },
+ // Try weird lengths
+ { "d0ec8a34f6cc9a8c96",
+ "49711798cc6251",
+ "933d4a2f30d22f089cfba842791116adc121e0",
+ 23,
+ "c9a46ed0757bd1812f1f21b4d41e62125fec8364a21db7"
+ },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_vector); i++) {
+ // Decode the test vector.
+ string subkey_secret;
+ string label;
+ string context;
+ ASSERT_TRUE(DecodeHexString(test_vector[i].subkey_secret, &subkey_secret));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].label, &label));
+ ASSERT_TRUE(DecodeHexString(test_vector[i].context, &context));
+ size_t result_len = test_vector[i].result_len;
+ bool expect_ok = test_vector[i].expected != nullptr;
+ string expected;
+ if (expect_ok) {
+ ASSERT_TRUE(DecodeHexString(test_vector[i].expected, &expected));
+ }
+
+ string result;
+ bool ok = CryptoUtils::ExportKeyingMaterial(subkey_secret,
+ label,
+ context,
+ result_len,
+ &result);
+ EXPECT_EQ(expect_ok, ok);
+ if (expect_ok) {
+ EXPECT_EQ(result_len, result.length());
+ test::CompareCharArraysWithHexError("HKDF output",
+ result.data(),
+ result.length(),
+ expected.data(),
+ expected.length());
+ }
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/curve25519_key_exchange.cc b/net/quic/crypto/curve25519_key_exchange.cc
new file mode 100644
index 0000000..88eb053
--- /dev/null
+++ b/net/quic/crypto/curve25519_key_exchange.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/curve25519_key_exchange.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "crypto/curve25519.h"
+#include "net/quic/crypto/quic_random.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+Curve25519KeyExchange::Curve25519KeyExchange() {}
+
+Curve25519KeyExchange::~Curve25519KeyExchange() {}
+
+// static
+Curve25519KeyExchange* Curve25519KeyExchange::New(
+ const StringPiece& private_key) {
+ Curve25519KeyExchange* ka;
+ // We don't want to #include the NaCl headers in the public header file, so
+ // we use literals for the sizes of private_key_ and public_key_. Here we
+ // assert that those values are equal to the values from the NaCl header.
+ COMPILE_ASSERT(
+ sizeof(ka->private_key_) == crypto::curve25519::kScalarBytes,
+ header_out_of_sync);
+ COMPILE_ASSERT(sizeof(ka->public_key_) == crypto::curve25519::kBytes,
+ header_out_of_sync);
+
+ if (private_key.size() != crypto::curve25519::kScalarBytes) {
+ return nullptr;
+ }
+
+ ka = new Curve25519KeyExchange();
+ memcpy(ka->private_key_, private_key.data(),
+ crypto::curve25519::kScalarBytes);
+ crypto::curve25519::ScalarBaseMult(ka->private_key_, ka->public_key_);
+ return ka;
+}
+
+// static
+string Curve25519KeyExchange::NewPrivateKey(QuicRandom* rand) {
+ uint8 private_key[crypto::curve25519::kScalarBytes];
+ rand->RandBytes(private_key, sizeof(private_key));
+
+ // This makes |private_key| a valid scalar, as specified on
+ // http://cr.yp.to/ecdh.html
+ private_key[0] &= 248;
+ private_key[31] &= 127;
+ private_key[31] |= 64;
+ return string(reinterpret_cast<char*>(private_key), sizeof(private_key));
+}
+
+KeyExchange* Curve25519KeyExchange::NewKeyPair(QuicRandom* rand) const {
+ const string private_value = NewPrivateKey(rand);
+ return Curve25519KeyExchange::New(private_value);
+}
+
+bool Curve25519KeyExchange::CalculateSharedKey(
+ const StringPiece& peer_public_value,
+ string* out_result) const {
+ if (peer_public_value.size() != crypto::curve25519::kBytes) {
+ return false;
+ }
+
+ uint8 result[crypto::curve25519::kBytes];
+ crypto::curve25519::ScalarMult(
+ private_key_,
+ reinterpret_cast<const uint8*>(peer_public_value.data()),
+ result);
+ out_result->assign(reinterpret_cast<char*>(result), sizeof(result));
+
+ return true;
+}
+
+StringPiece Curve25519KeyExchange::public_value() const {
+ return StringPiece(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+QuicTag Curve25519KeyExchange::tag() const { return kC255; }
+
+} // namespace net
diff --git a/net/quic/crypto/curve25519_key_exchange.h b/net/quic/crypto/curve25519_key_exchange.h
new file mode 100644
index 0000000..93288f6
--- /dev/null
+++ b/net/quic/crypto/curve25519_key_exchange.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
+#define NET_QUIC_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/key_exchange.h"
+
+namespace net {
+
+class QuicRandom;
+
+// Curve25519KeyExchange implements a KeyExchange using elliptic-curve
+// Diffie-Hellman on curve25519. See http://cr.yp.to/ecdh.html
+class NET_EXPORT_PRIVATE Curve25519KeyExchange : public KeyExchange {
+ public:
+ virtual ~Curve25519KeyExchange();
+
+ // New creates a new object from a private key. If the private key is
+ // invalid, nullptr is returned.
+ static Curve25519KeyExchange* New(const base::StringPiece& private_key);
+
+ // NewPrivateKey returns a private key, generated from |rand|, suitable for
+ // passing to |New|.
+ static std::string NewPrivateKey(QuicRandom* rand);
+
+ // KeyExchange interface.
+ virtual KeyExchange* NewKeyPair(QuicRandom* rand) const OVERRIDE;
+ virtual bool CalculateSharedKey(const base::StringPiece& peer_public_value,
+ std::string* shared_key) const OVERRIDE;
+ virtual base::StringPiece public_value() const OVERRIDE;
+ virtual QuicTag tag() const OVERRIDE;
+
+ private:
+ Curve25519KeyExchange();
+
+ uint8 private_key_[32];
+ uint8 public_key_[32];
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_CURVE25519_KEY_EXCHANGE_H_
diff --git a/net/quic/crypto/curve25519_key_exchange_test.cc b/net/quic/crypto/curve25519_key_exchange_test.cc
new file mode 100644
index 0000000..93ef630
--- /dev/null
+++ b/net/quic/crypto/curve25519_key_exchange_test.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/curve25519_key_exchange.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/quic/crypto/quic_random.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+
+// SharedKey just tests that the basic key exchange identity holds: that both
+// parties end up with the same key.
+TEST(Curve25519KeyExchange, SharedKey) {
+ QuicRandom* const rand = QuicRandom::GetInstance();
+
+ for (int i = 0; i < 5; i++) {
+ const string alice_key(Curve25519KeyExchange::NewPrivateKey(rand));
+ const string bob_key(Curve25519KeyExchange::NewPrivateKey(rand));
+
+ scoped_ptr<Curve25519KeyExchange> alice(
+ Curve25519KeyExchange::New(alice_key));
+ scoped_ptr<Curve25519KeyExchange> bob(Curve25519KeyExchange::New(bob_key));
+
+ const StringPiece alice_public(alice->public_value());
+ const StringPiece bob_public(bob->public_value());
+
+ string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKey(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKey(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/ephemeral_key_source.h b/net/quic/crypto/ephemeral_key_source.h
new file mode 100644
index 0000000..2700be0
--- /dev/null
+++ b/net/quic/crypto/ephemeral_key_source.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_EPHEMERAL_KEY_SOURCE_H_
+#define NET_QUIC_CRYPTO_EPHEMERAL_KEY_SOURCE_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class KeyExchange;
+class QuicRandom;
+
+// EphemeralKeySource manages and rotates ephemeral keys as they can be reused
+// for several connections in a short space of time. Since the implementation
+// of this may involve locking or thread-local data, this interface abstracts
+// that away.
+class NET_EXPORT_PRIVATE EphemeralKeySource {
+ public:
+ virtual ~EphemeralKeySource() {}
+
+ // CalculateForwardSecureKey generates an ephemeral public/private key pair
+ // using the algorithm |key_exchange|, sets |*public_value| to the public key
+ // and returns the shared key between |peer_public_value| and the private
+ // key. |*public_value| will be sent to the peer to be used with the peer's
+ // private key.
+ virtual std::string CalculateForwardSecureKey(
+ const KeyExchange* key_exchange,
+ QuicRandom* rand,
+ QuicTime now,
+ base::StringPiece peer_public_value,
+ std::string* public_value) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_EPHEMERAL_KEY_SOURCE_H_
diff --git a/net/quic/crypto/key_exchange.h b/net/quic/crypto/key_exchange.h
new file mode 100644
index 0000000..8690f0e
--- /dev/null
+++ b/net/quic/crypto/key_exchange.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_KEY_EXCHANGE_H_
+#define NET_QUIC_CRYPTO_KEY_EXCHANGE_H_
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_protocol.h"
+
+namespace net {
+
+class QuicRandom;
+
+// KeyExchange is an abstract class that provides an interface to a
+// key-exchange primitive.
+class NET_EXPORT_PRIVATE KeyExchange {
+ public:
+ virtual ~KeyExchange() {}
+
+ // NewKeyPair generates a new public, private key pair. The caller takes
+ // ownership of the return value. (This is intended for servers that need to
+ // generate forward-secure keys.)
+ virtual KeyExchange* NewKeyPair(QuicRandom* rand) const = 0;
+
+ // CalculateSharedKey computes the shared key between the local private key
+ // (which is implicitly known by a KeyExchange object) and a public value
+ // from the peer.
+ virtual bool CalculateSharedKey(const base::StringPiece& peer_public_value,
+ std::string* shared_key) const = 0;
+
+ // public_value returns the local public key which can be sent to a peer in
+ // order to complete a key exchange. The returned StringPiece is a reference
+ // to a member of the KeyExchange and is only valid for as long as the
+ // KeyExchange exists.
+ virtual base::StringPiece public_value() const = 0;
+
+ // tag returns the tag value that identifies this key exchange function.
+ virtual QuicTag tag() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_KEY_EXCHANGE_H_
diff --git a/net/quic/crypto/local_strike_register_client.cc b/net/quic/crypto/local_strike_register_client.cc
new file mode 100644
index 0000000..71d2224
--- /dev/null
+++ b/net/quic/crypto/local_strike_register_client.cc
@@ -0,0 +1,50 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/local_strike_register_client.h"
+
+#include "net/quic/crypto/crypto_protocol.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+LocalStrikeRegisterClient::LocalStrikeRegisterClient(
+ unsigned max_entries,
+ uint32 current_time_external,
+ uint32 window_secs,
+ const uint8 orbit[8],
+ StrikeRegister::StartupType startup)
+ : strike_register_(max_entries, current_time_external, window_secs,
+ orbit, startup) {
+}
+
+bool LocalStrikeRegisterClient::IsKnownOrbit(StringPiece orbit) const {
+ base::AutoLock lock(m_);
+ if (orbit.length() != kOrbitSize) {
+ return false;
+ }
+ return memcmp(orbit.data(), strike_register_.orbit(), kOrbitSize) == 0;
+}
+
+void LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(
+ StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) {
+ InsertStatus nonce_error;
+ if (nonce.length() != kNonceSize) {
+ nonce_error = NONCE_INVALID_FAILURE;
+ } else {
+ base::AutoLock lock(m_);
+ nonce_error = strike_register_.Insert(
+ reinterpret_cast<const uint8*>(nonce.data()),
+ static_cast<uint32>(now.ToUNIXSeconds()));
+ }
+
+ // m_ must not be held when the ResultCallback runs.
+ cb->Run((nonce_error == NONCE_OK), nonce_error);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/local_strike_register_client.h b/net/quic/crypto/local_strike_register_client.h
new file mode 100644
index 0000000..fe8ae93
--- /dev/null
+++ b/net/quic/crypto/local_strike_register_client.h
@@ -0,0 +1,43 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
+#define NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/strike_register.h"
+#include "net/quic/crypto/strike_register_client.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+// StrikeRegisterClient implementation that wraps a local in-memory
+// strike register.
+class NET_EXPORT_PRIVATE LocalStrikeRegisterClient
+ : public StrikeRegisterClient {
+ public:
+ LocalStrikeRegisterClient(unsigned max_entries,
+ uint32 current_time_external,
+ uint32 window_secs,
+ const uint8 orbit[8],
+ StrikeRegister::StartupType startup);
+
+ virtual bool IsKnownOrbit(base::StringPiece orbit) const OVERRIDE;
+ virtual void VerifyNonceIsValidAndUnique(base::StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) OVERRIDE;
+
+ private:
+ mutable base::Lock m_;
+ StrikeRegister strike_register_;
+
+ DISALLOW_COPY_AND_ASSIGN(LocalStrikeRegisterClient);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_LOCAL_STRIKE_REGISTER_CLIENT_H_
diff --git a/net/quic/crypto/local_strike_register_client_test.cc b/net/quic/crypto/local_strike_register_client_test.cc
new file mode 100644
index 0000000..1a3b6f9
--- /dev/null
+++ b/net/quic/crypto/local_strike_register_client_test.cc
@@ -0,0 +1,138 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/local_strike_register_client.h"
+
+#include <memory>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/sys_byteorder.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class RecordResultCallback : public StrikeRegisterClient::ResultCallback {
+ public:
+ // RecordResultCallback stores the argument to RunImpl in
+ // |*saved_value| and sets |*called| to true. The callback is self
+ // deleting.
+ RecordResultCallback(bool* called,
+ bool* saved_value,
+ InsertStatus* saved_nonce_error)
+ : called_(called),
+ saved_value_(saved_value),
+ saved_nonce_error_(saved_nonce_error) {
+ *called_ = false;
+ }
+
+ protected:
+ virtual void RunImpl(bool nonce_is_valid_and_unique,
+ InsertStatus nonce_error) OVERRIDE {
+ *called_ = true;
+ *saved_value_ = nonce_is_valid_and_unique;
+ *saved_nonce_error_ = nonce_error;
+ }
+
+ private:
+ bool* called_;
+ bool* saved_value_;
+ InsertStatus* saved_nonce_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecordResultCallback);
+};
+
+const uint8 kOrbit[] = "\x12\x34\x56\x78\x9A\xBC\xDE\xF0";
+const uint32 kCurrentTimeExternalSecs = 12345678;
+size_t kMaxEntries = 100;
+uint32 kWindowSecs = 60;
+
+class LocalStrikeRegisterClientTest : public ::testing::Test {
+ protected:
+ LocalStrikeRegisterClientTest() {
+ }
+
+ virtual void SetUp() {
+ strike_register_.reset(new LocalStrikeRegisterClient(
+ kMaxEntries, kCurrentTimeExternalSecs, kWindowSecs, kOrbit,
+ net::StrikeRegister::NO_STARTUP_PERIOD_NEEDED));
+ }
+
+ scoped_ptr<LocalStrikeRegisterClient> strike_register_;
+};
+
+TEST_F(LocalStrikeRegisterClientTest, CheckOrbit) {
+ EXPECT_TRUE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize - 1)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit), kOrbitSize + 1)));
+ EXPECT_FALSE(strike_register_->IsKnownOrbit(
+ StringPiece(reinterpret_cast<const char*>(kOrbit) + 1, kOrbitSize)));
+}
+
+TEST_F(LocalStrikeRegisterClientTest, IncorrectNonceLength) {
+ string valid_nonce;
+ uint32 norder = htonl(kCurrentTimeExternalSecs);
+ valid_nonce.assign(reinterpret_cast<const char*>(&norder), sizeof(norder));
+ valid_nonce.append(string(reinterpret_cast<const char*>(kOrbit), kOrbitSize));
+ valid_nonce.append(string(20, '\x17')); // 20 'random' bytes.
+
+ {
+ // Validation fails if you remove a byte from the nonce.
+ bool called = false;
+ bool is_valid = false;
+ InsertStatus nonce_error = NONCE_UNKNOWN_FAILURE;
+ string short_nonce = valid_nonce.substr(0, valid_nonce.length() - 1);
+ strike_register_->VerifyNonceIsValidAndUnique(
+ short_nonce,
+ QuicWallTime::FromUNIXSeconds(kCurrentTimeExternalSecs),
+ new RecordResultCallback(&called, &is_valid, &nonce_error));
+ EXPECT_TRUE(called);
+ EXPECT_FALSE(is_valid);
+ EXPECT_EQ(NONCE_INVALID_FAILURE, nonce_error);
+ }
+
+ {
+ // Validation fails if you add a byte to the nonce.
+ bool called = false;
+ bool is_valid = false;
+ InsertStatus nonce_error = NONCE_UNKNOWN_FAILURE;
+ string long_nonce(valid_nonce);
+ long_nonce.append("a");
+ strike_register_->VerifyNonceIsValidAndUnique(
+ long_nonce,
+ QuicWallTime::FromUNIXSeconds(kCurrentTimeExternalSecs),
+ new RecordResultCallback(&called, &is_valid, &nonce_error));
+ EXPECT_TRUE(called);
+ EXPECT_FALSE(is_valid);
+ EXPECT_EQ(NONCE_INVALID_FAILURE, nonce_error);
+ }
+
+ {
+ // Verify that the base nonce validates was valid.
+ bool called = false;
+ bool is_valid = false;
+ InsertStatus nonce_error = NONCE_UNKNOWN_FAILURE;
+ strike_register_->VerifyNonceIsValidAndUnique(
+ valid_nonce,
+ QuicWallTime::FromUNIXSeconds(kCurrentTimeExternalSecs),
+ new RecordResultCallback(&called, &is_valid, &nonce_error));
+ EXPECT_TRUE(called);
+ EXPECT_TRUE(is_valid);
+ EXPECT_EQ(NONCE_OK, nonce_error);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/null_decrypter.cc b/net/quic/crypto/null_decrypter.cc
new file mode 100644
index 0000000..77aee4c
--- /dev/null
+++ b/net/quic/crypto/null_decrypter.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/null_decrypter.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/quic_data_reader.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+NullDecrypter::NullDecrypter() {}
+
+bool NullDecrypter::SetKey(StringPiece key) { return key.empty(); }
+
+bool NullDecrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+bool NullDecrypter::Decrypt(StringPiece /*nonce*/,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) {
+ QuicDataReader reader(ciphertext.data(), ciphertext.length());
+
+ uint128 hash;
+ if (!ReadHash(&reader, &hash)) {
+ return false;
+ }
+
+ StringPiece plaintext = reader.ReadRemainingPayload();
+
+ // TODO(rch): avoid buffer copy here
+ string buffer = associated_data.as_string();
+ plaintext.AppendToString(&buffer);
+ if (hash != ComputeHash(buffer)) {
+ return false;
+ }
+ memcpy(output, plaintext.data(), plaintext.length());
+ *output_length = plaintext.length();
+ return true;
+}
+
+QuicData* NullDecrypter::DecryptPacket(QuicPacketSequenceNumber /*seq_number*/,
+ StringPiece associated_data,
+ StringPiece ciphertext) {
+ // It's worth duplicating |Decrypt|, above, in order to save a copy by using
+ // the shared-data QuicData constructor directly.
+ QuicDataReader reader(ciphertext.data(), ciphertext.length());
+
+ uint128 hash;
+ if (!ReadHash(&reader, &hash)) {
+ return nullptr;
+ }
+
+ StringPiece plaintext = reader.ReadRemainingPayload();
+
+ // TODO(rch): avoid buffer copy here
+ string buffer = associated_data.as_string();
+ plaintext.AppendToString(&buffer);
+
+ if (hash != ComputeHash(buffer)) {
+ return nullptr;
+ }
+ return new QuicData(plaintext.data(), plaintext.length());
+}
+
+StringPiece NullDecrypter::GetKey() const { return StringPiece(); }
+
+StringPiece NullDecrypter::GetNoncePrefix() const { return StringPiece(); }
+
+bool NullDecrypter::ReadHash(QuicDataReader* reader, uint128* hash) {
+ uint64 lo;
+ uint32 hi;
+ if (!reader->ReadUInt64(&lo) ||
+ !reader->ReadUInt32(&hi)) {
+ return false;
+ }
+ *hash = hi;
+ *hash <<= 64;
+ *hash += lo;
+ return true;
+}
+
+uint128 NullDecrypter::ComputeHash(const string& data) const {
+ uint128 correct_hash = QuicUtils::FNV1a_128_Hash(data.data(), data.length());
+ uint128 mask(GG_UINT64_C(0x0), GG_UINT64_C(0xffffffff));
+ mask <<= 96;
+ correct_hash &= ~mask;
+ return correct_hash;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/null_decrypter.h b/net/quic/crypto/null_decrypter.h
new file mode 100644
index 0000000..2bc2fe8
--- /dev/null
+++ b/net/quic/crypto/null_decrypter.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_NULL_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_NULL_DECRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/quic_decrypter.h"
+
+namespace net {
+
+class QuicDataReader;
+
+// A NullDecrypter is a QuicDecrypter used before a crypto negotiation
+// has occurred. It does not actually decrypt the payload, but does
+// verify a hash (fnv128) over both the payload and associated data.
+class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter {
+ public:
+ NullDecrypter();
+ virtual ~NullDecrypter() {}
+
+ // QuicDecrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Decrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) OVERRIDE;
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext) OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ private:
+ bool ReadHash(QuicDataReader* reader, uint128* hash);
+ uint128 ComputeHash(const std::string& data) const;
+
+ DISALLOW_COPY_AND_ASSIGN(NullDecrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_NULL_DECRYPTER_H_
diff --git a/net/quic/crypto/null_decrypter_test.cc b/net/quic/crypto/null_decrypter_test.cc
new file mode 100644
index 0000000..173ecde
--- /dev/null
+++ b/net/quic/crypto/null_decrypter_test.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/null_decrypter.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+class NullDecrypterTest : public ::testing::TestWithParam<bool> {
+};
+
+TEST_F(NullDecrypterTest, Decrypt) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0xa0, 0x6f, 0x44, 0x8a,
+ 0x44, 0xf8, 0x18, 0x3b,
+ 0x47, 0x91, 0xb2, 0x13,
+ // payload
+ 'g', 'o', 'o', 'd',
+ 'b', 'y', 'e', '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = arraysize(expected);
+ NullDecrypter decrypter;
+ scoped_ptr<QuicData> decrypted(
+ decrypter.DecryptPacket(0, "hello world!", StringPiece(data, len)));
+ ASSERT_TRUE(decrypted.get());
+ EXPECT_EQ("goodbye!", decrypted->AsStringPiece());
+}
+
+TEST_F(NullDecrypterTest, BadHash) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0x46, 0x11, 0xea, 0x5f,
+ 0xcf, 0x1d, 0x66, 0x5b,
+ 0xba, 0xf0, 0xbc, 0xfd,
+ // payload
+ 'g', 'o', 'o', 'd',
+ 'b', 'y', 'e', '!',
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = arraysize(expected);
+ NullDecrypter decrypter;
+ scoped_ptr<QuicData> decrypted(
+ decrypter.DecryptPacket(0, "hello world!", StringPiece(data, len)));
+ ASSERT_FALSE(decrypted.get());
+}
+
+TEST_F(NullDecrypterTest, ShortInput) {
+ unsigned char expected[] = {
+ // fnv hash (truncated)
+ 0x46, 0x11, 0xea, 0x5f,
+ 0xcf, 0x1d, 0x66, 0x5b,
+ 0xba, 0xf0, 0xbc,
+ };
+ const char* data = reinterpret_cast<const char*>(expected);
+ size_t len = arraysize(expected);
+ NullDecrypter decrypter;
+ scoped_ptr<QuicData> decrypted(
+ decrypter.DecryptPacket(0, "hello world!", StringPiece(data, len)));
+ ASSERT_FALSE(decrypted.get());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/null_encrypter.cc b/net/quic/crypto/null_encrypter.cc
new file mode 100644
index 0000000..5f1a85b
--- /dev/null
+++ b/net/quic/crypto/null_encrypter.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/quic_data_writer.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+const size_t kHashSizeShort = 12; // size of uint128 serialized short
+
+NullEncrypter::NullEncrypter() {}
+
+bool NullEncrypter::SetKey(StringPiece key) { return key.empty(); }
+
+bool NullEncrypter::SetNoncePrefix(StringPiece nonce_prefix) {
+ return nonce_prefix.empty();
+}
+
+bool NullEncrypter::Encrypt(
+ StringPiece /*nonce*/,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) {
+ string buffer = associated_data.as_string();
+ plaintext.AppendToString(&buffer);
+ uint128 hash = QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length());
+ QuicUtils::SerializeUint128Short(hash, output);
+ memcpy(output + GetHashLength(), plaintext.data(), plaintext.size());
+ return true;
+}
+
+QuicData* NullEncrypter::EncryptPacket(
+ QuicPacketSequenceNumber /*sequence_number*/,
+ StringPiece associated_data,
+ StringPiece plaintext) {
+ const size_t len = plaintext.size() + GetHashLength();
+ uint8* buffer = new uint8[len];
+ Encrypt(StringPiece(), associated_data, plaintext, buffer);
+ return new QuicData(reinterpret_cast<char*>(buffer), len, true);
+}
+
+size_t NullEncrypter::GetKeySize() const { return 0; }
+
+size_t NullEncrypter::GetNoncePrefixSize() const { return 0; }
+
+size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const {
+ return ciphertext_size - GetHashLength();
+}
+
+size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
+ return plaintext_size + GetHashLength();
+}
+
+StringPiece NullEncrypter::GetKey() const { return StringPiece(); }
+
+StringPiece NullEncrypter::GetNoncePrefix() const { return StringPiece(); }
+
+size_t NullEncrypter::GetHashLength() const {
+ return kHashSizeShort;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/null_encrypter.h b/net/quic/crypto/null_encrypter.h
new file mode 100644
index 0000000..1bcdff5
--- /dev/null
+++ b/net/quic/crypto/null_encrypter.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_
+
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/quic_encrypter.h"
+
+namespace net {
+
+// A NullEncrypter is a QuicEncrypter used before a crypto negotiation
+// has occurred. It does not actually encrypt the payload, but does
+// generate a MAC (fnv128) over both the payload and associated data.
+class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter {
+ public:
+ NullEncrypter();
+ virtual ~NullEncrypter() {}
+
+ // QuicEncrypter implementation
+ virtual bool SetKey(base::StringPiece key) OVERRIDE;
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) OVERRIDE;
+ virtual bool Encrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext,
+ unsigned char* output) OVERRIDE;
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext) OVERRIDE;
+ virtual size_t GetKeySize() const OVERRIDE;
+ virtual size_t GetNoncePrefixSize() const OVERRIDE;
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE;
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE;
+ virtual base::StringPiece GetKey() const OVERRIDE;
+ virtual base::StringPiece GetNoncePrefix() const OVERRIDE;
+
+ private:
+ size_t GetHashLength() const;
+
+ DISALLOW_COPY_AND_ASSIGN(NullEncrypter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_
diff --git a/net/quic/crypto/null_encrypter_test.cc b/net/quic/crypto/null_encrypter_test.cc
new file mode 100644
index 0000000..4f4dae7
--- /dev/null
+++ b/net/quic/crypto/null_encrypter_test.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using base::StringPiece;
+
+namespace net {
+namespace test {
+
+class NullEncrypterTest : public ::testing::TestWithParam<bool> {
+};
+
+TEST_F(NullEncrypterTest, Encrypt) {
+ unsigned char expected[] = {
+ // fnv hash
+ 0xa0, 0x6f, 0x44, 0x8a,
+ 0x44, 0xf8, 0x18, 0x3b,
+ 0x47, 0x91, 0xb2, 0x13,
+ // payload
+ 'g', 'o', 'o', 'd',
+ 'b', 'y', 'e', '!',
+ };
+ NullEncrypter encrypter;
+ scoped_ptr<QuicData> encrypted(
+ encrypter.EncryptPacket(0, "hello world!", "goodbye!"));
+ ASSERT_TRUE(encrypted.get());
+ test::CompareCharArraysWithHexError(
+ "encrypted data", encrypted->data(), encrypted->length(),
+ reinterpret_cast<const char*>(expected),
+ arraysize(expected));
+}
+
+TEST_F(NullEncrypterTest, GetMaxPlaintextSize) {
+ NullEncrypter encrypter;
+ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012));
+ EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112));
+ EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22));
+}
+
+TEST_F(NullEncrypterTest, GetCiphertextSize) {
+ NullEncrypter encrypter;
+ EXPECT_EQ(1012u, encrypter.GetCiphertextSize(1000));
+ EXPECT_EQ(112u, encrypter.GetCiphertextSize(100));
+ EXPECT_EQ(22u, encrypter.GetCiphertextSize(10));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/p256_key_exchange.h b/net/quic/crypto/p256_key_exchange.h
new file mode 100644
index 0000000..ce5a0d4
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_
+#define NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/key_exchange.h"
+
+#if defined(USE_OPENSSL)
+#include "crypto/openssl_util.h"
+#include "crypto/scoped_openssl_types.h"
+#else
+#include "crypto/ec_private_key.h"
+#include "crypto/scoped_nss_types.h"
+#endif
+
+namespace net {
+
+// P256KeyExchange implements a KeyExchange using elliptic-curve
+// Diffie-Hellman on NIST P-256.
+class NET_EXPORT_PRIVATE P256KeyExchange : public KeyExchange {
+ public:
+ virtual ~P256KeyExchange();
+
+ // New creates a new key exchange object from a private key. If
+ // |private_key| is invalid, nullptr is returned.
+ static P256KeyExchange* New(base::StringPiece private_key);
+
+ // |NewPrivateKey| returns a private key, suitable for passing to |New|.
+ // If |NewPrivateKey| can't generate a private key, it returns an empty
+ // string.
+ static std::string NewPrivateKey();
+
+ // KeyExchange interface.
+ virtual KeyExchange* NewKeyPair(QuicRandom* rand) const OVERRIDE;
+ virtual bool CalculateSharedKey(const base::StringPiece& peer_public_value,
+ std::string* shared_key) const OVERRIDE;
+ virtual base::StringPiece public_value() const OVERRIDE;
+ virtual QuicTag tag() const OVERRIDE;
+
+ private:
+ enum {
+ // A P-256 field element consists of 32 bytes.
+ kP256FieldBytes = 32,
+ // A P-256 point in uncompressed form consists of 0x04 (to denote
+ // that the point is uncompressed) followed by two, 32-byte field
+ // elements.
+ kUncompressedP256PointBytes = 1 + 2 * kP256FieldBytes,
+ // The first byte in an uncompressed P-256 point.
+ kUncompressedECPointForm = 0x04,
+ };
+
+#if defined(USE_OPENSSL)
+ // P256KeyExchange takes ownership of |private_key|, and expects
+ // |public_key| consists of |kUncompressedP256PointBytes| bytes.
+ P256KeyExchange(EC_KEY* private_key, const uint8* public_key);
+
+ crypto::ScopedEC_KEY private_key_;
+#else
+ // P256KeyExchange takes ownership of |key_pair|, and expects
+ // |public_key| consists of |kUncompressedP256PointBytes| bytes.
+ P256KeyExchange(crypto::ECPrivateKey* key_pair, const uint8* public_key);
+
+ scoped_ptr<crypto::ECPrivateKey> key_pair_;
+#endif
+ // The public key stored as an uncompressed P-256 point.
+ uint8 public_key_[kUncompressedP256PointBytes];
+
+ DISALLOW_COPY_AND_ASSIGN(P256KeyExchange);
+};
+
+} // namespace net
+#endif // NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_
diff --git a/net/quic/crypto/p256_key_exchange_nss.cc b/net/quic/crypto/p256_key_exchange_nss.cc
new file mode 100644
index 0000000..73aa03d
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange_nss.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/p256_key_exchange.h"
+
+#include "base/logging.h"
+#include "base/sys_byteorder.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+// Password used by |NewPrivateKey| to encrypt exported EC private keys.
+// This is not used to provide any security, but to workaround NSS being
+// unwilling to export unencrypted EC keys. Note that SPDY and ChannelID
+// use the same approach.
+const char kExportPassword[] = "";
+
+// Convert StringPiece to vector of uint8.
+static vector<uint8> StringPieceToVector(StringPiece piece) {
+ return vector<uint8>(piece.data(), piece.data() + piece.length());
+}
+
+} // namespace
+
+P256KeyExchange::P256KeyExchange(crypto::ECPrivateKey* key_pair,
+ const uint8* public_key)
+ : key_pair_(key_pair) {
+ memcpy(public_key_, public_key, sizeof(public_key_));
+}
+
+P256KeyExchange::~P256KeyExchange() {
+}
+
+// static
+P256KeyExchange* P256KeyExchange::New(StringPiece key) {
+ if (key.size() < 2) {
+ DVLOG(1) << "Key pair is too small.";
+ return nullptr;
+ }
+
+ const uint8* data = reinterpret_cast<const uint8*>(key.data());
+ size_t size = static_cast<size_t>(data[0]) |
+ (static_cast<size_t>(data[1]) << 8);
+ key.remove_prefix(2);
+ if (key.size() < size) {
+ DVLOG(1) << "Key pair does not contain key material.";
+ return nullptr;
+ }
+
+ StringPiece private_piece(key.data(), size);
+ key.remove_prefix(size);
+ if (key.empty()) {
+ DVLOG(1) << "Key pair does not contain public key.";
+ return nullptr;
+ }
+
+ StringPiece public_piece(key);
+
+ scoped_ptr<crypto::ECPrivateKey> key_pair(
+ crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo(
+ kExportPassword,
+ // TODO(thaidn): fix this interface to avoid copying secrets.
+ StringPieceToVector(private_piece),
+ StringPieceToVector(public_piece)));
+
+ if (!key_pair.get()) {
+ DVLOG(1) << "Can't decrypt private key.";
+ return nullptr;
+ }
+
+ // Perform some sanity checks on the public key.
+ SECKEYPublicKey* public_key = key_pair->public_key();
+ if (public_key->keyType != ecKey ||
+ public_key->u.ec.publicValue.len != kUncompressedP256PointBytes ||
+ !public_key->u.ec.publicValue.data ||
+ public_key->u.ec.publicValue.data[0] != kUncompressedECPointForm) {
+ DVLOG(1) << "Key is invalid.";
+ return nullptr;
+ }
+
+ // Ensure that the key is using the correct curve, i.e., NIST P-256.
+ const SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
+ if (!oid_data) {
+ DVLOG(1) << "Can't get P-256's OID.";
+ return nullptr;
+ }
+
+ if (public_key->u.ec.DEREncodedParams.len != oid_data->oid.len + 2 ||
+ !public_key->u.ec.DEREncodedParams.data ||
+ public_key->u.ec.DEREncodedParams.data[0] != SEC_ASN1_OBJECT_ID ||
+ public_key->u.ec.DEREncodedParams.data[1] != oid_data->oid.len ||
+ memcmp(public_key->u.ec.DEREncodedParams.data + 2,
+ oid_data->oid.data, oid_data->oid.len) != 0) {
+ DVLOG(1) << "Key is invalid.";
+ }
+
+ return new P256KeyExchange(key_pair.release(),
+ public_key->u.ec.publicValue.data);
+}
+
+// static
+string P256KeyExchange::NewPrivateKey() {
+ scoped_ptr<crypto::ECPrivateKey> key_pair(crypto::ECPrivateKey::Create());
+
+ if (!key_pair.get()) {
+ DVLOG(1) << "Can't generate new key pair.";
+ return string();
+ }
+
+ vector<uint8> private_key;
+ if (!key_pair->ExportEncryptedPrivateKey(kExportPassword,
+ 1 /* iteration */,
+ &private_key)) {
+ DVLOG(1) << "Can't export private key.";
+ return string();
+ }
+
+ // NSS lacks the ability to import an ECC private key without
+ // also importing the public key, so it is necessary to also
+ // store the public key.
+ vector<uint8> public_key;
+ if (!key_pair->ExportPublicKey(&public_key)) {
+ DVLOG(1) << "Can't export public key.";
+ return string();
+ }
+
+ // TODO(thaidn): determine how large encrypted private key can be
+ uint16 private_key_size = private_key.size();
+ const size_t result_size = sizeof(private_key_size) +
+ private_key_size +
+ public_key.size();
+ vector<char> result(result_size);
+ char* resultp = &result[0];
+ // Export the key string.
+ // The first two bytes are the private key's size in little endian.
+ private_key_size = base::ByteSwapToLE16(private_key_size);
+ memcpy(resultp, &private_key_size, sizeof(private_key_size));
+ resultp += sizeof(private_key_size);
+ memcpy(resultp, &private_key[0], private_key.size());
+ resultp += private_key.size();
+ memcpy(resultp, &public_key[0], public_key.size());
+
+ return string(&result[0], result_size);
+}
+
+KeyExchange* P256KeyExchange::NewKeyPair(QuicRandom* /*rand*/) const {
+ // TODO(agl): avoid the serialisation/deserialisation in this function.
+ const string private_value = NewPrivateKey();
+ return P256KeyExchange::New(private_value);
+}
+
+bool P256KeyExchange::CalculateSharedKey(const StringPiece& peer_public_value,
+ string* out_result) const {
+ if (peer_public_value.size() != kUncompressedP256PointBytes ||
+ peer_public_value[0] != kUncompressedECPointForm) {
+ DVLOG(1) << "Peer public value is invalid.";
+ return false;
+ }
+
+ DCHECK(key_pair_.get());
+ DCHECK(key_pair_->public_key());
+
+ SECKEYPublicKey peer_public_key;
+ memset(&peer_public_key, 0, sizeof(peer_public_key));
+
+ peer_public_key.keyType = ecKey;
+ // Both sides of a ECDH key exchange need to use the same EC params.
+ peer_public_key.u.ec.DEREncodedParams.len =
+ key_pair_->public_key()->u.ec.DEREncodedParams.len;
+ peer_public_key.u.ec.DEREncodedParams.data =
+ key_pair_->public_key()->u.ec.DEREncodedParams.data;
+
+ peer_public_key.u.ec.publicValue.type = siBuffer;
+ peer_public_key.u.ec.publicValue.data =
+ reinterpret_cast<uint8*>(const_cast<char*>(peer_public_value.data()));
+ peer_public_key.u.ec.publicValue.len = peer_public_value.size();
+
+ // The NSS function performing ECDH key exchange is PK11_PubDeriveWithKDF.
+ // As this function is used for SSL/TLS's ECDH key exchanges it has many
+ // arguments, most of which are not required in QUIC.
+ // Key derivation function CKD_NULL is used because the return value of
+ // |CalculateSharedKey| is the actual ECDH shared key, not any derived keys
+ // from it.
+ crypto::ScopedPK11SymKey premaster_secret(
+ PK11_PubDeriveWithKDF(
+ key_pair_->key(),
+ &peer_public_key,
+ PR_FALSE,
+ nullptr,
+ nullptr,
+ CKM_ECDH1_DERIVE, /* mechanism */
+ CKM_GENERIC_SECRET_KEY_GEN, /* target */
+ CKA_DERIVE,
+ 0,
+ CKD_NULL, /* kdf */
+ nullptr,
+ nullptr));
+
+ if (!premaster_secret.get()) {
+ DVLOG(1) << "Can't derive ECDH shared key.";
+ return false;
+ }
+
+ if (PK11_ExtractKeyValue(premaster_secret.get()) != SECSuccess) {
+ DVLOG(1) << "Can't extract raw ECDH shared key.";
+ return false;
+ }
+
+ SECItem* key_data = PK11_GetKeyData(premaster_secret.get());
+ if (!key_data || !key_data->data || key_data->len != kP256FieldBytes) {
+ DVLOG(1) << "ECDH shared key is invalid.";
+ return false;
+ }
+
+ out_result->assign(reinterpret_cast<char*>(key_data->data), key_data->len);
+ return true;
+}
+
+StringPiece P256KeyExchange::public_value() const {
+ return StringPiece(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+QuicTag P256KeyExchange::tag() const { return kP256; }
+
+} // namespace net
diff --git a/net/quic/crypto/p256_key_exchange_openssl.cc b/net/quic/crypto/p256_key_exchange_openssl.cc
new file mode 100644
index 0000000..f6d88b6
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange_openssl.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/p256_key_exchange.h"
+
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/evp.h>
+
+#include "base/logging.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+P256KeyExchange::P256KeyExchange(EC_KEY* private_key, const uint8* public_key)
+ : private_key_(private_key) {
+ memcpy(public_key_, public_key, sizeof(public_key_));
+}
+
+P256KeyExchange::~P256KeyExchange() {}
+
+// static
+P256KeyExchange* P256KeyExchange::New(StringPiece key) {
+ if (key.empty()) {
+ DVLOG(1) << "Private key is empty";
+ return nullptr;
+ }
+
+ const uint8* keyp = reinterpret_cast<const uint8*>(key.data());
+ crypto::ScopedEC_KEY private_key(d2i_ECPrivateKey(nullptr, &keyp,
+ key.size()));
+ if (!private_key.get() || !EC_KEY_check_key(private_key.get())) {
+ DVLOG(1) << "Private key is invalid.";
+ return nullptr;
+ }
+
+ uint8 public_key[kUncompressedP256PointBytes];
+ if (EC_POINT_point2oct(EC_KEY_get0_group(private_key.get()),
+ EC_KEY_get0_public_key(private_key.get()),
+ POINT_CONVERSION_UNCOMPRESSED, public_key,
+ sizeof(public_key), nullptr) != sizeof(public_key)) {
+ DVLOG(1) << "Can't get public key.";
+ return nullptr;
+ }
+
+ return new P256KeyExchange(private_key.release(), public_key);
+}
+
+// static
+string P256KeyExchange::NewPrivateKey() {
+ crypto::ScopedEC_KEY key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (!key.get() || !EC_KEY_generate_key(key.get())) {
+ DVLOG(1) << "Can't generate a new private key.";
+ return string();
+ }
+
+ int key_len = i2d_ECPrivateKey(key.get(), nullptr);
+ if (key_len <= 0) {
+ DVLOG(1) << "Can't convert private key to string";
+ return string();
+ }
+ scoped_ptr<uint8[]> private_key(new uint8[key_len]);
+ uint8* keyp = private_key.get();
+ if (!i2d_ECPrivateKey(key.get(), &keyp)) {
+ DVLOG(1) << "Can't convert private key to string.";
+ return string();
+ }
+ return string(reinterpret_cast<char*>(private_key.get()), key_len);
+}
+
+KeyExchange* P256KeyExchange::NewKeyPair(QuicRandom* /*rand*/) const {
+ // TODO(agl): avoid the serialisation/deserialisation in this function.
+ const string private_value = NewPrivateKey();
+ return P256KeyExchange::New(private_value);
+}
+
+bool P256KeyExchange::CalculateSharedKey(const StringPiece& peer_public_value,
+ string* out_result) const {
+ if (peer_public_value.size() != kUncompressedP256PointBytes) {
+ DVLOG(1) << "Peer public value is invalid";
+ return false;
+ }
+
+ crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free>::Type point(
+ EC_POINT_new(EC_KEY_get0_group(private_key_.get())));
+ if (!point.get() ||
+ !EC_POINT_oct2point( /* also test if point is on curve */
+ EC_KEY_get0_group(private_key_.get()),
+ point.get(),
+ reinterpret_cast<const uint8*>(peer_public_value.data()),
+ peer_public_value.size(), nullptr)) {
+ DVLOG(1) << "Can't convert peer public value to curve point.";
+ return false;
+ }
+
+ uint8 result[kP256FieldBytes];
+ if (ECDH_compute_key(result, sizeof(result), point.get(), private_key_.get(),
+ nullptr) != sizeof(result)) {
+ DVLOG(1) << "Can't compute ECDH shared key.";
+ return false;
+ }
+
+ out_result->assign(reinterpret_cast<char*>(result), sizeof(result));
+ return true;
+}
+
+StringPiece P256KeyExchange::public_value() const {
+ return StringPiece(reinterpret_cast<const char*>(public_key_),
+ sizeof(public_key_));
+}
+
+QuicTag P256KeyExchange::tag() const { return kP256; }
+
+} // namespace net
diff --git a/net/quic/crypto/p256_key_exchange_test.cc b/net/quic/crypto/p256_key_exchange_test.cc
new file mode 100644
index 0000000..54c99a0
--- /dev/null
+++ b/net/quic/crypto/p256_key_exchange_test.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/p256_key_exchange.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+// SharedKey just tests that the basic key exchange identity holds: that both
+// parties end up with the same key.
+TEST(P256KeyExchange, SharedKey) {
+ for (int i = 0; i < 5; i++) {
+ string alice_private(P256KeyExchange::NewPrivateKey());
+ string bob_private(P256KeyExchange::NewPrivateKey());
+
+ ASSERT_FALSE(alice_private.empty());
+ ASSERT_FALSE(bob_private.empty());
+ ASSERT_NE(alice_private, bob_private);
+
+ scoped_ptr<P256KeyExchange> alice(P256KeyExchange::New(alice_private));
+ scoped_ptr<P256KeyExchange> bob(P256KeyExchange::New(bob_private));
+
+ ASSERT_TRUE(alice.get() != nullptr);
+ ASSERT_TRUE(bob.get() != nullptr);
+
+ const base::StringPiece alice_public(alice->public_value());
+ const base::StringPiece bob_public(bob->public_value());
+
+ std::string alice_shared, bob_shared;
+ ASSERT_TRUE(alice->CalculateSharedKey(bob_public, &alice_shared));
+ ASSERT_TRUE(bob->CalculateSharedKey(alice_public, &bob_shared));
+ ASSERT_EQ(alice_shared, bob_shared);
+ }
+}
+
+} // namespace test
+} // namespace net
+
diff --git a/net/quic/crypto/proof_source.h b/net/quic/crypto/proof_source.h
new file mode 100644
index 0000000..4482dd9
--- /dev/null
+++ b/net/quic/crypto/proof_source.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_PROOF_SOURCE_H_
+#define NET_QUIC_CRYPTO_PROOF_SOURCE_H_
+
+#include <string>
+#include <vector>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// ProofSource is an interface by which a QUIC server can obtain certificate
+// chains and signatures that prove its identity.
+class NET_EXPORT_PRIVATE ProofSource {
+ public:
+ virtual ~ProofSource() {}
+
+ // GetProof finds a certificate chain for |hostname|, sets |out_certs| to
+ // point to it (in leaf-first order), calculates a signature of
+ // |server_config| using that chain and puts the result in |out_signature|.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding when the
+ // key is RSA.
+ //
+ // The signature uses SHA-256 as the hash function when the key is ECDSA.
+ //
+ // If |ecdsa_ok| is true, the signature may use an ECDSA key. Otherwise, the
+ // signature must use an RSA key.
+ //
+ // |out_certs| is a pointer to a pointer, not a pointer to an array.
+ //
+ // The number of certificate chains is expected to be small and fixed thus
+ // the ProofSource retains ownership of the contents of |out_certs|. The
+ // expectation is that they will be cached forever.
+ //
+ // The signature values should be cached because |server_config| will be
+ // somewhat static. However, since they aren't bounded, the ProofSource may
+ // wish to evicit entries from that cache, thus the caller takes ownership of
+ // |*out_signature|.
+ //
+ // |hostname| may be empty to signify that a default certificate should be
+ // used.
+ //
+ // This function may be called concurrently.
+ virtual bool GetProof(const std::string& hostname,
+ const std::string& server_config,
+ bool ecdsa_ok,
+ const std::vector<std::string>** out_certs,
+ std::string* out_signature) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_PROOF_SOURCE_H_
diff --git a/net/quic/crypto/proof_source_chromium.cc b/net/quic/crypto/proof_source_chromium.cc
new file mode 100644
index 0000000..7522631
--- /dev/null
+++ b/net/quic/crypto/proof_source_chromium.cc
@@ -0,0 +1,23 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/proof_source_chromium.h"
+
+using std::string;
+using std::vector;
+
+namespace net {
+
+ProofSourceChromium::ProofSourceChromium() {
+}
+
+bool ProofSourceChromium::GetProof(const string& hostname,
+ const string& server_config,
+ bool ecdsa_ok,
+ const vector<string>** out_certs,
+ string* out_signature) {
+ return false;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/proof_source_chromium.h b/net/quic/crypto/proof_source_chromium.h
new file mode 100644
index 0000000..70ab92d
--- /dev/null
+++ b/net/quic/crypto/proof_source_chromium.h
@@ -0,0 +1,38 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_PROOF_SOURCE_CHROMIUM_H_
+#define NET_QUIC_CRYPTO_PROOF_SOURCE_CHROMIUM_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/proof_source.h"
+
+namespace net {
+
+// ProofSourceChromium implements the QUIC ProofSource interface.
+// TODO(rtenneti): implement details of this class.
+class NET_EXPORT_PRIVATE ProofSourceChromium : public ProofSource {
+ public:
+ ProofSourceChromium();
+ virtual ~ProofSourceChromium() {}
+
+ // ProofSource interface
+ virtual bool GetProof(const std::string& hostname,
+ const std::string& server_config,
+ bool ecdsa_ok,
+ const std::vector<std::string>** out_certs,
+ std::string* out_signature) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProofSourceChromium);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_PROOF_SOURCE_CHROMIUM_H_
diff --git a/net/quic/crypto/proof_test.cc b/net/quic/crypto/proof_test.cc
new file mode 100644
index 0000000..031cba2
--- /dev/null
+++ b/net/quic/crypto/proof_test.cc
@@ -0,0 +1,371 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/quic/crypto/proof_source.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+namespace {
+
+// TestProofVerifierCallback is a simple callback for a ProofVerifier that
+// signals a TestCompletionCallback when called and stores the results from the
+// ProofVerifier in pointers passed to the constructor.
+class TestProofVerifierCallback : public ProofVerifierCallback {
+ public:
+ TestProofVerifierCallback(TestCompletionCallback* comp_callback,
+ bool* ok,
+ string* error_details)
+ : comp_callback_(comp_callback),
+ ok_(ok),
+ error_details_(error_details) {}
+
+ virtual void Run(bool ok,
+ const string& error_details,
+ scoped_ptr<ProofVerifyDetails>* details) OVERRIDE {
+ *ok_ = ok;
+ *error_details_ = error_details;
+
+ comp_callback_->callback().Run(0);
+ }
+
+ private:
+ TestCompletionCallback* const comp_callback_;
+ bool* const ok_;
+ string* const error_details_;
+};
+
+// RunVerification runs |verifier->VerifyProof| and asserts that the result
+// matches |expected_ok|.
+void RunVerification(ProofVerifier* verifier,
+ const string& hostname,
+ const string& server_config,
+ const vector<string>& certs,
+ const string& proof,
+ bool expected_ok) {
+ scoped_ptr<ProofVerifyDetails> details;
+ TestCompletionCallback comp_callback;
+ bool ok;
+ string error_details;
+ scoped_ptr<ProofVerifyContext> verify_context(
+ CryptoTestUtils::ProofVerifyContextForTesting());
+ TestProofVerifierCallback* callback =
+ new TestProofVerifierCallback(&comp_callback, &ok, &error_details);
+
+ QuicAsyncStatus status = verifier->VerifyProof(
+ hostname, server_config, certs, proof, verify_context.get(),
+ &error_details, &details, callback);
+
+ switch (status) {
+ case QUIC_FAILURE:
+ delete callback;
+ ASSERT_FALSE(expected_ok);
+ ASSERT_NE("", error_details);
+ return;
+ case QUIC_SUCCESS:
+ delete callback;
+ ASSERT_TRUE(expected_ok);
+ ASSERT_EQ("", error_details);
+ return;
+ case QUIC_PENDING:
+ comp_callback.WaitForResult();
+ ASSERT_EQ(expected_ok, ok);
+ break;
+ }
+}
+
+// Reads the certificate named "quic_" + |file_name| in the test data directory.
+// The certificate must be PEM encoded. Returns the DER-encoded certificate.
+string LoadTestCert(const string& file_name) {
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> cert =
+ ImportCertFromFile(certs_dir, "quic_" + file_name);
+ CHECK_NE(static_cast<X509Certificate*>(nullptr), cert.get());
+
+ string der_bytes;
+ CHECK(X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_bytes));
+ return der_bytes;
+}
+
+} // namespace
+
+// TODO(rtenneti): Enable testing of ProofVerifier.
+TEST(ProofTest, DISABLED_Verify) {
+ scoped_ptr<ProofSource> source(CryptoTestUtils::ProofSourceForTesting());
+ scoped_ptr<ProofVerifier> verifier(
+ CryptoTestUtils::ProofVerifierForTesting());
+
+ const string server_config = "server config bytes";
+ const string hostname = "test.example.com";
+ const vector<string>* certs;
+ const vector<string>* first_certs;
+ string error_details, signature, first_signature;
+
+ ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
+ &first_certs, &first_signature));
+ ASSERT_TRUE(source->GetProof(hostname, server_config, false /* no ECDSA */,
+ &certs, &signature));
+
+ // Check that the proof source is caching correctly:
+ ASSERT_EQ(first_certs, certs);
+ ASSERT_EQ(signature, first_signature);
+
+ RunVerification(
+ verifier.get(), hostname, server_config, *certs, signature, true);
+
+ RunVerification(
+ verifier.get(), "foo.com", server_config, *certs, signature, false);
+
+ RunVerification(
+ verifier.get(), server_config.substr(1, string::npos), server_config,
+ *certs, signature, false);
+
+ const string corrupt_signature = "1" + signature;
+ RunVerification(
+ verifier.get(), hostname, server_config, *certs, corrupt_signature,
+ false);
+
+ vector<string> wrong_certs;
+ for (size_t i = 1; i < certs->size(); i++) {
+ wrong_certs.push_back((*certs)[i]);
+ }
+ RunVerification(
+ verifier.get(), "foo.com", server_config, wrong_certs, corrupt_signature,
+ false);
+}
+
+// A known answer test that allows us to test ProofVerifier without a working
+// ProofSource.
+TEST(ProofTest, VerifyRSAKnownAnswerTest) {
+ // These sample signatures were generated by running the Proof.Verify test
+ // and dumping the bytes of the |signature| output of ProofSource::GetProof().
+ static const unsigned char signature_data_0[] = {
+ 0x31, 0xd5, 0xfb, 0x40, 0x30, 0x75, 0xd2, 0x7d, 0x61, 0xf9, 0xd7, 0x54,
+ 0x30, 0x06, 0xaf, 0x54, 0x0d, 0xb0, 0x0a, 0xda, 0x63, 0xca, 0x7e, 0x9e,
+ 0xce, 0xba, 0x10, 0x05, 0x1b, 0xa6, 0x7f, 0xef, 0x2b, 0xa3, 0xff, 0x3c,
+ 0xbb, 0x9a, 0xe4, 0xbf, 0xb8, 0x0c, 0xc1, 0xbd, 0xed, 0xc2, 0x90, 0x68,
+ 0xeb, 0x45, 0x48, 0xea, 0x3c, 0x95, 0xf8, 0xa2, 0xb9, 0xe7, 0x62, 0x29,
+ 0x00, 0xc3, 0x18, 0xb4, 0x16, 0x6f, 0x5e, 0xb0, 0xc1, 0x26, 0xc0, 0x4b,
+ 0x84, 0xf5, 0x97, 0xfc, 0x17, 0xf9, 0x1c, 0x43, 0xb8, 0xf2, 0x3f, 0x38,
+ 0x32, 0xad, 0x36, 0x52, 0x2c, 0x26, 0x92, 0x7a, 0xea, 0x2c, 0xa2, 0xf4,
+ 0x28, 0x2f, 0x19, 0x4d, 0x1f, 0x11, 0x46, 0x82, 0xd0, 0xc4, 0x86, 0x56,
+ 0x5c, 0x97, 0x9e, 0xc6, 0x37, 0x8e, 0xaf, 0x9d, 0x69, 0xe9, 0x4f, 0x5a,
+ 0x6d, 0x70, 0x75, 0xc7, 0x41, 0x95, 0x68, 0x53, 0x94, 0xca, 0x31, 0x63,
+ 0x61, 0x9f, 0xb8, 0x8c, 0x3b, 0x75, 0x36, 0x8b, 0x69, 0xa2, 0x35, 0xc0,
+ 0x4b, 0x77, 0x55, 0x08, 0xc2, 0xb4, 0x56, 0xd2, 0x81, 0xce, 0x9e, 0x25,
+ 0xdb, 0x50, 0x74, 0xb3, 0x8a, 0xd9, 0x20, 0x42, 0x3f, 0x85, 0x2d, 0xaa,
+ 0xfd, 0x66, 0xfa, 0xd6, 0x95, 0x55, 0x6b, 0x63, 0x63, 0x04, 0xf8, 0x6c,
+ 0x3e, 0x08, 0x22, 0x39, 0xb9, 0x9a, 0xe0, 0xd7, 0x01, 0xff, 0xeb, 0x8a,
+ 0xb9, 0xe2, 0x34, 0xa5, 0xa0, 0x51, 0xe9, 0xbe, 0x15, 0x12, 0xbf, 0xbe,
+ 0x64, 0x3d, 0x3f, 0x98, 0xce, 0xc1, 0xa6, 0x33, 0x32, 0xd3, 0x5c, 0xa8,
+ 0x39, 0x93, 0xdc, 0x1c, 0xb9, 0xab, 0x3c, 0x80, 0x62, 0xb3, 0x76, 0x21,
+ 0xdf, 0x47, 0x1e, 0xa9, 0x0e, 0x5e, 0x8a, 0xbe, 0x66, 0x5b, 0x7c, 0x21,
+ 0xfa, 0x78, 0x2d, 0xd1, 0x1d, 0x5c, 0x35, 0x8a, 0x34, 0xb2, 0x1a, 0xc2,
+ 0xc4, 0x4b, 0x53, 0x54,
+ };
+ static const unsigned char signature_data_1[] = {
+ 0x01, 0x7b, 0x52, 0x35, 0xe3, 0x51, 0xdd, 0xf1, 0x67, 0x8d, 0x31, 0x5e,
+ 0xa3, 0x75, 0x1f, 0x68, 0x6c, 0xdd, 0x41, 0x7a, 0x18, 0x25, 0xe0, 0x12,
+ 0x6e, 0x84, 0x46, 0x5e, 0xb2, 0x98, 0xd7, 0x84, 0xe1, 0x62, 0xe0, 0xc1,
+ 0xc4, 0xd7, 0x4f, 0x4f, 0x80, 0xc1, 0x92, 0xd6, 0x02, 0xaf, 0xca, 0x28,
+ 0x9f, 0xe0, 0xf3, 0x74, 0xd7, 0xf1, 0x44, 0x67, 0x59, 0x27, 0xc8, 0xc2,
+ 0x8b, 0xd4, 0xe5, 0x4a, 0x07, 0xfd, 0x00, 0xd6, 0x8a, 0xbf, 0x8b, 0xcd,
+ 0x6a, 0xe0, 0x1d, 0xf6, 0x4b, 0x68, 0x0f, 0xcf, 0xb9, 0xd0, 0xa1, 0xbc,
+ 0x2e, 0xcf, 0x7c, 0x03, 0x47, 0x11, 0xe4, 0x4c, 0xbc, 0x1b, 0x6b, 0xa5,
+ 0x2a, 0x82, 0x86, 0xa4, 0x7f, 0x1d, 0x85, 0x64, 0x21, 0x10, 0xd2, 0xb2,
+ 0xa0, 0x31, 0xa2, 0x78, 0xe6, 0xf2, 0xea, 0x96, 0x38, 0x8c, 0x9a, 0xe1,
+ 0x01, 0xab, 0x8e, 0x95, 0x66, 0xc8, 0xe5, 0xcc, 0x80, 0xa3, 0xbd, 0x16,
+ 0xa7, 0x79, 0x19, 0x39, 0x61, 0x3d, 0xff, 0x37, 0xca, 0x9f, 0x97, 0x05,
+ 0xc7, 0xcb, 0xf0, 0xea, 0xaf, 0x64, 0x07, 0xc0, 0xed, 0x2a, 0x98, 0xa4,
+ 0xaf, 0x04, 0x6f, 0xf2, 0xc9, 0xb2, 0x73, 0x9a, 0x56, 0x85, 0x43, 0x64,
+ 0x5f, 0xaa, 0xb7, 0xff, 0x31, 0x4c, 0x2e, 0x6c, 0x17, 0xcf, 0xe5, 0xbe,
+ 0x7f, 0x7e, 0xad, 0xf5, 0x6f, 0x84, 0x50, 0x20, 0x29, 0xb3, 0x57, 0xe7,
+ 0xb1, 0xdc, 0x2c, 0x95, 0x48, 0xfe, 0xb0, 0xc1, 0x92, 0xda, 0xc5, 0x58,
+ 0x95, 0xb0, 0x1a, 0x3a, 0x05, 0x71, 0x3c, 0x6d, 0x20, 0x01, 0x4c, 0xa9,
+ 0xe4, 0x38, 0x08, 0x65, 0xb4, 0xbd, 0x86, 0x76, 0xbd, 0xad, 0x25, 0x06,
+ 0x74, 0x0b, 0xca, 0x95, 0x27, 0x0c, 0x13, 0x08, 0x7e, 0x30, 0xcf, 0xf6,
+ 0xb5, 0xc1, 0x2a, 0x08, 0xfc, 0x4b, 0xc6, 0xb5, 0x2f, 0x23, 0x27, 0x32,
+ 0x89, 0xdb, 0x0e, 0x4a,
+ };
+ static const unsigned char signature_data_2[] = {
+ 0x6d, 0x7d, 0x22, 0x8c, 0x85, 0xc4, 0x8a, 0x80, 0x05, 0xe4, 0x3c, 0xaf,
+ 0x10, 0x3b, 0xe3, 0x51, 0xb1, 0x86, 0x52, 0x63, 0xb6, 0x17, 0x33, 0xbd,
+ 0x1b, 0x1e, 0xc4, 0x50, 0x10, 0xfc, 0xcc, 0xea, 0x6b, 0x11, 0xeb, 0x6d,
+ 0x5e, 0x00, 0xe7, 0xf3, 0x67, 0x99, 0x74, 0x53, 0x12, 0x8f, 0xe4, 0x3e,
+ 0x20, 0x17, 0x8e, 0x83, 0xe6, 0xdc, 0x83, 0x91, 0x0e, 0xf3, 0x69, 0x22,
+ 0x95, 0x14, 0xdf, 0xc1, 0xda, 0xb5, 0xdb, 0x6a, 0x1a, 0xb4, 0x4f, 0x26,
+ 0xd0, 0x32, 0x1d, 0x73, 0x95, 0x1f, 0x39, 0x1d, 0x00, 0xcb, 0xc3, 0x92,
+ 0x49, 0x53, 0xcb, 0x5c, 0x36, 0x70, 0x19, 0xd9, 0x64, 0x36, 0xda, 0xfb,
+ 0x20, 0xe5, 0x47, 0xd9, 0x08, 0xc6, 0x5a, 0x9e, 0x87, 0x1a, 0xdb, 0x11,
+ 0x7b, 0x17, 0xfc, 0x53, 0x7b, 0xc1, 0xa0, 0xc0, 0x33, 0xcf, 0x96, 0xba,
+ 0x03, 0x79, 0x8e, 0xc6, 0x05, 0xd2, 0xb7, 0xa2, 0xe2, 0xc1, 0x67, 0xb7,
+ 0x6a, 0xeb, 0xb1, 0x40, 0xbb, 0x7d, 0x57, 0xcb, 0xc2, 0x60, 0x9f, 0xf1,
+ 0x72, 0xe5, 0xad, 0xce, 0x95, 0x45, 0x7c, 0xbc, 0x75, 0x81, 0x45, 0x19,
+ 0xe1, 0xa7, 0x2f, 0x05, 0x52, 0xeb, 0xed, 0xdd, 0x19, 0xd9, 0x1a, 0xc9,
+ 0x5a, 0x06, 0x8e, 0x29, 0x54, 0xb5, 0x4f, 0x80, 0xaa, 0x36, 0x36, 0xc0,
+ 0xff, 0x64, 0xac, 0xe8, 0x0f, 0x99, 0x35, 0x5e, 0xc6, 0x72, 0x1f, 0x8c,
+ 0xc4, 0x2b, 0x7d, 0xc1, 0xfb, 0xf0, 0x12, 0x61, 0xb1, 0x18, 0x65, 0xdd,
+ 0xc2, 0x38, 0x92, 0xba, 0x84, 0xf8, 0xc8, 0x5e, 0x17, 0x63, 0xe0, 0x9c,
+ 0x2c, 0xe6, 0x70, 0x71, 0xdc, 0xe5, 0xc1, 0xea, 0xb3, 0x9a, 0xb6, 0x91,
+ 0xdc, 0xc5, 0x56, 0x84, 0x8a, 0x31, 0x31, 0x23, 0x61, 0x94, 0x7e, 0x01,
+ 0x22, 0x49, 0xf3, 0xcb, 0x0e, 0x31, 0x03, 0x04, 0x1b, 0x14, 0x43, 0x7c,
+ 0xad, 0x42, 0xe5, 0x55,
+ };
+
+ scoped_ptr<ProofVerifier> verifier(
+ CryptoTestUtils::ProofVerifierForTesting());
+
+ const string server_config = "server config bytes";
+ const string hostname = "test.example.com";
+
+ vector<string> certs(2);
+ certs[0] = LoadTestCert("test.example.com.crt");
+ certs[1] = LoadTestCert("intermediate.crt");
+
+ // Signatures are nondeterministic, so we test multiple signatures on the
+ // same server_config.
+ vector<string> signatures(3);
+ signatures[0].assign(reinterpret_cast<const char*>(signature_data_0),
+ sizeof(signature_data_0));
+ signatures[1].assign(reinterpret_cast<const char*>(signature_data_1),
+ sizeof(signature_data_1));
+ signatures[2].assign(reinterpret_cast<const char*>(signature_data_2),
+ sizeof(signature_data_2));
+
+ for (size_t i = 0; i < signatures.size(); i++) {
+ const string& signature = signatures[i];
+
+ RunVerification(
+ verifier.get(), hostname, server_config, certs, signature, true);
+ RunVerification(
+ verifier.get(), "foo.com", server_config, certs, signature, false);
+ RunVerification(
+ verifier.get(), hostname, server_config.substr(1, string::npos),
+ certs, signature, false);
+
+ const string corrupt_signature = "1" + signature;
+ RunVerification(
+ verifier.get(), hostname, server_config, certs, corrupt_signature,
+ false);
+
+ vector<string> wrong_certs;
+ for (size_t i = 1; i < certs.size(); i++) {
+ wrong_certs.push_back(certs[i]);
+ }
+ RunVerification(verifier.get(), hostname, server_config, wrong_certs,
+ signature, false);
+ }
+}
+
+// A known answer test that allows us to test ProofVerifier without a working
+// ProofSource.
+TEST(ProofTest, VerifyECDSAKnownAnswerTest) {
+ // Disable this test on platforms that do not support ECDSA certificates.
+#if defined(OS_WIN)
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return;
+#endif
+
+ // These sample signatures were generated by running the Proof.Verify test
+ // (modified to use ECDSA for signing proofs) and dumping the bytes of the
+ // |signature| output of ProofSource::GetProof().
+ static const unsigned char signature_data_0[] = {
+ 0x30, 0x45, 0x02, 0x21, 0x00, 0x89, 0xc4, 0x7d, 0x08, 0xd1, 0x49, 0x19,
+ 0x6c, 0xd1, 0x7c, 0xb9, 0x25, 0xe0, 0xe3, 0xbd, 0x6a, 0x5c, 0xd7, 0xaa,
+ 0x0c, 0xdc, 0x4f, 0x8e, 0xeb, 0xde, 0xbf, 0x32, 0xf8, 0xd1, 0x84, 0x95,
+ 0x97, 0x02, 0x20, 0x29, 0x3d, 0x49, 0x22, 0x73, 0xed, 0x8b, 0xde, 0x3d,
+ 0xc2, 0xa4, 0x20, 0xcc, 0xe7, 0xc8, 0x2a, 0x85, 0x20, 0x9b, 0x5b, 0xda,
+ 0xcd, 0x58, 0x23, 0xbe, 0x89, 0x73, 0x31, 0x87, 0x51, 0xd1, 0x01,
+ };
+ static const unsigned char signature_data_1[] = {
+ 0x30, 0x46, 0x02, 0x21, 0x00, 0xec, 0xdf, 0x69, 0xc8, 0x24, 0x59, 0x93,
+ 0xda, 0x49, 0xee, 0x37, 0x28, 0xaf, 0xeb, 0x0e, 0x2f, 0x80, 0x17, 0x4b,
+ 0x3b, 0xf6, 0x54, 0xcd, 0x3b, 0x86, 0xc5, 0x98, 0x0d, 0xff, 0xc6, 0xb1,
+ 0xe7, 0x02, 0x21, 0x00, 0xe1, 0x36, 0x8c, 0xc0, 0xf4, 0x50, 0x5f, 0xba,
+ 0xfb, 0xe2, 0xff, 0x1d, 0x5d, 0x64, 0xe4, 0x07, 0xbb, 0x5a, 0x4b, 0x19,
+ 0xb6, 0x39, 0x7a, 0xc4, 0x12, 0xc6, 0xe5, 0x42, 0xc8, 0x78, 0x33, 0xcd,
+ };
+ static const unsigned char signature_data_2[] = {
+ 0x30, 0x45, 0x02, 0x20, 0x09, 0x51, 0xe9, 0xde, 0xdb, 0x01, 0xfd, 0xb4,
+ 0xd8, 0x20, 0xbb, 0xad, 0x41, 0xe3, 0xaa, 0xe7, 0xa3, 0xc3, 0x32, 0x10,
+ 0x9d, 0xfa, 0x37, 0xce, 0x17, 0xd1, 0x29, 0xf9, 0xd4, 0x1d, 0x0d, 0x19,
+ 0x02, 0x21, 0x00, 0xc6, 0x20, 0xd4, 0x28, 0xf9, 0x70, 0xb5, 0xb4, 0xff,
+ 0x4a, 0x35, 0xba, 0xa0, 0xf2, 0x8e, 0x00, 0xf7, 0xcb, 0x43, 0xaf, 0x2d,
+ 0x1f, 0xce, 0x92, 0x05, 0xca, 0x29, 0xfe, 0xd2, 0x8f, 0xd9, 0x31,
+ };
+
+ scoped_ptr<ProofVerifier> verifier(
+ CryptoTestUtils::ProofVerifierForTesting());
+
+ const string server_config = "server config bytes";
+ const string hostname = "test.example.com";
+
+ vector<string> certs(2);
+ certs[0] = LoadTestCert("test_ecc.example.com.crt");
+ certs[1] = LoadTestCert("intermediate.crt");
+
+ // Signatures are nondeterministic, so we test multiple signatures on the
+ // same server_config.
+ vector<string> signatures(3);
+ signatures[0].assign(reinterpret_cast<const char*>(signature_data_0),
+ sizeof(signature_data_0));
+ signatures[1].assign(reinterpret_cast<const char*>(signature_data_1),
+ sizeof(signature_data_1));
+ signatures[2].assign(reinterpret_cast<const char*>(signature_data_2),
+ sizeof(signature_data_2));
+
+ for (size_t i = 0; i < signatures.size(); i++) {
+ const string& signature = signatures[i];
+
+ RunVerification(
+ verifier.get(), hostname, server_config, certs, signature, true);
+ RunVerification(
+ verifier.get(), "foo.com", server_config, certs, signature, false);
+ RunVerification(
+ verifier.get(), hostname, server_config.substr(1, string::npos),
+ certs, signature, false);
+
+ // An ECDSA signature is DER-encoded. Corrupt the last byte so that the
+ // signature can still be DER-decoded correctly.
+ string corrupt_signature = signature;
+ corrupt_signature[corrupt_signature.size() - 1] += 1;
+ RunVerification(
+ verifier.get(), hostname, server_config, certs, corrupt_signature,
+ false);
+
+ // Prepending a "1" makes the DER invalid.
+ const string bad_der_signature1 = "1" + signature;
+ RunVerification(
+ verifier.get(), hostname, server_config, certs, bad_der_signature1,
+ false);
+
+ vector<string> wrong_certs;
+ for (size_t i = 1; i < certs.size(); i++) {
+ wrong_certs.push_back(certs[i]);
+ }
+ RunVerification(
+ verifier.get(), hostname, server_config, wrong_certs, signature,
+ false);
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/proof_verifier.h b/net/quic/crypto/proof_verifier.h
new file mode 100644
index 0000000..87339da
--- /dev/null
+++ b/net/quic/crypto/proof_verifier.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_H_
+#define NET_QUIC_CRYPTO_PROOF_VERIFIER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_types.h"
+
+namespace net {
+
+// ProofVerifyDetails is an abstract class that acts as a container for any
+// implementation specific details that a ProofVerifier wishes to return. These
+// details are saved in the CachedState for the origin in question.
+class NET_EXPORT_PRIVATE ProofVerifyDetails {
+ public:
+ virtual ~ProofVerifyDetails() {}
+
+ // Returns an new ProofVerifyDetails object with the same contents
+ // as this one.
+ virtual ProofVerifyDetails* Clone() const = 0;
+};
+
+// ProofVerifyContext is an abstract class that acts as a container for any
+// implementation specific context that a ProofVerifier needs.
+class NET_EXPORT_PRIVATE ProofVerifyContext {
+ public:
+ virtual ~ProofVerifyContext() {}
+};
+
+// ProofVerifierCallback provides a generic mechanism for a ProofVerifier to
+// call back after an asynchronous verification.
+class NET_EXPORT_PRIVATE ProofVerifierCallback {
+ public:
+ virtual ~ProofVerifierCallback() {}
+
+ // Run is called on the original thread to mark the completion of an
+ // asynchonous verification. If |ok| is true then the certificate is valid
+ // and |error_details| is unused. Otherwise, |error_details| contains a
+ // description of the error. |details| contains implementation-specific
+ // details of the verification. |Run| may take ownership of |details| by
+ // calling |release| on it.
+ virtual void Run(bool ok,
+ const std::string& error_details,
+ scoped_ptr<ProofVerifyDetails>* details) = 0;
+};
+
+// A ProofVerifier checks the signature on a server config, and the certificate
+// chain that backs the public key.
+class NET_EXPORT_PRIVATE ProofVerifier {
+ public:
+ virtual ~ProofVerifier() {}
+
+ // VerifyProof checks that |signature| is a valid signature of
+ // |server_config| by the public key in the leaf certificate of |certs|, and
+ // that |certs| is a valid chain for |hostname|. On success, it returns
+ // QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and sets |*error_details|
+ // to a description of the problem. In either case it may set |*details|,
+ // which the caller takes ownership of.
+ //
+ // |context| specifies an implementation specific struct (which may be nullptr
+ // for some implementations) that provides useful information for the
+ // verifier, e.g. logging handles.
+ //
+ // This function may also return QUIC_PENDING, in which case the ProofVerifier
+ // will call back, on the original thread, via |callback| when complete.
+ // In this case, the ProofVerifier will take ownership of |callback|.
+ //
+ // The signature uses SHA-256 as the hash function and PSS padding in the
+ // case of RSA.
+ virtual QuicAsyncStatus VerifyProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* details,
+ ProofVerifierCallback* callback) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_PROOF_VERIFIER_H_
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
new file mode 100644
index 0000000..775e374
--- /dev/null
+++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -0,0 +1,385 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/proof_verifier_chromium.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "crypto/signature_verifier.h"
+#include "net/base/net_errors.h"
+#include "net/base/net_log.h"
+#include "net/cert/asn1_util.h"
+#include "net/cert/cert_status_flags.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/single_request_cert_verifier.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/http/transport_security_state.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/ssl/ssl_config_service.h"
+
+using base::StringPiece;
+using base::StringPrintf;
+using std::string;
+using std::vector;
+
+namespace net {
+
+ProofVerifyDetails* ProofVerifyDetailsChromium::Clone() const {
+ ProofVerifyDetailsChromium* other = new ProofVerifyDetailsChromium;
+ other->cert_verify_result = cert_verify_result;
+ return other;
+}
+
+// A Job handles the verification of a single proof. It is owned by the
+// ProofVerifier. If the verification can not complete synchronously, it
+// will notify the ProofVerifier upon completion.
+class ProofVerifierChromium::Job {
+ public:
+ Job(ProofVerifierChromium* proof_verifier,
+ CertVerifier* cert_verifier,
+ TransportSecurityState* transport_security_state,
+ const BoundNetLog& net_log);
+
+ // Starts the proof verification. If |QUIC_PENDING| is returned, then
+ // |callback| will be invoked asynchronously when the verification completes.
+ QuicAsyncStatus VerifyProof(const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback);
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_VERIFY_CERT,
+ STATE_VERIFY_CERT_COMPLETE,
+ };
+
+ int DoLoop(int last_io_result);
+ void OnIOComplete(int result);
+ int DoVerifyCert(int result);
+ int DoVerifyCertComplete(int result);
+
+ bool VerifySignature(const std::string& signed_data,
+ const std::string& signature,
+ const std::string& cert);
+
+ // Proof verifier to notify when this jobs completes.
+ ProofVerifierChromium* proof_verifier_;
+
+ // The underlying verifier used for verifying certificates.
+ scoped_ptr<SingleRequestCertVerifier> verifier_;
+
+ TransportSecurityState* transport_security_state_;
+
+ // |hostname| specifies the hostname for which |certs| is a valid chain.
+ std::string hostname_;
+
+ scoped_ptr<ProofVerifierCallback> callback_;
+ scoped_ptr<ProofVerifyDetailsChromium> verify_details_;
+ std::string error_details_;
+
+ // X509Certificate from a chain of DER encoded certificates.
+ scoped_refptr<X509Certificate> cert_;
+
+ State next_state_;
+
+ BoundNetLog net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+ProofVerifierChromium::Job::Job(
+ ProofVerifierChromium* proof_verifier,
+ CertVerifier* cert_verifier,
+ TransportSecurityState* transport_security_state,
+ const BoundNetLog& net_log)
+ : proof_verifier_(proof_verifier),
+ verifier_(new SingleRequestCertVerifier(cert_verifier)),
+ transport_security_state_(transport_security_state),
+ next_state_(STATE_NONE),
+ net_log_(net_log) {
+}
+
+QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof(
+ const string& hostname,
+ const string& server_config,
+ const vector<string>& certs,
+ const string& signature,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) {
+ DCHECK(error_details);
+ DCHECK(verify_details);
+ DCHECK(callback);
+
+ error_details->clear();
+
+ if (STATE_NONE != next_state_) {
+ *error_details = "Certificate is already set and VerifyProof has begun";
+ DLOG(DFATAL) << *error_details;
+ return QUIC_FAILURE;
+ }
+
+ verify_details_.reset(new ProofVerifyDetailsChromium);
+
+ if (certs.empty()) {
+ *error_details = "Failed to create certificate chain. Certs are empty.";
+ DLOG(WARNING) << *error_details;
+ verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
+ *verify_details = verify_details_.Pass();
+ return QUIC_FAILURE;
+ }
+
+ // Convert certs to X509Certificate.
+ vector<StringPiece> cert_pieces(certs.size());
+ for (unsigned i = 0; i < certs.size(); i++) {
+ cert_pieces[i] = base::StringPiece(certs[i]);
+ }
+ cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces);
+ if (!cert_.get()) {
+ *error_details = "Failed to create certificate chain";
+ DLOG(WARNING) << *error_details;
+ verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
+ *verify_details = verify_details_.Pass();
+ return QUIC_FAILURE;
+ }
+
+ // We call VerifySignature first to avoid copying of server_config and
+ // signature.
+ if (!VerifySignature(server_config, signature, certs[0])) {
+ *error_details = "Failed to verify signature of server config";
+ DLOG(WARNING) << *error_details;
+ verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
+ *verify_details = verify_details_.Pass();
+ return QUIC_FAILURE;
+ }
+
+ hostname_ = hostname;
+
+ next_state_ = STATE_VERIFY_CERT;
+ switch (DoLoop(OK)) {
+ case OK:
+ *verify_details = verify_details_.Pass();
+ return QUIC_SUCCESS;
+ case ERR_IO_PENDING:
+ callback_.reset(callback);
+ return QUIC_PENDING;
+ default:
+ *error_details = error_details_;
+ *verify_details = verify_details_.Pass();
+ return QUIC_FAILURE;
+ }
+}
+
+int ProofVerifierChromium::Job::DoLoop(int last_result) {
+ int rv = last_result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_VERIFY_CERT:
+ DCHECK(rv == OK);
+ rv = DoVerifyCert(rv);
+ break;
+ case STATE_VERIFY_CERT_COMPLETE:
+ rv = DoVerifyCertComplete(rv);
+ break;
+ case STATE_NONE:
+ default:
+ rv = ERR_UNEXPECTED;
+ LOG(DFATAL) << "unexpected state " << state;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+ return rv;
+}
+
+void ProofVerifierChromium::Job::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING) {
+ scoped_ptr<ProofVerifierCallback> callback(callback_.Pass());
+ // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium.
+ scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.Pass());
+ callback->Run(rv == OK, error_details_, &verify_details);
+ // Will delete |this|.
+ proof_verifier_->OnJobComplete(this);
+ }
+}
+
+int ProofVerifierChromium::Job::DoVerifyCert(int result) {
+ next_state_ = STATE_VERIFY_CERT_COMPLETE;
+
+ int flags = 0;
+ return verifier_->Verify(
+ cert_.get(),
+ hostname_,
+ flags,
+ SSLConfigService::GetCRLSet().get(),
+ &verify_details_->cert_verify_result,
+ base::Bind(&ProofVerifierChromium::Job::OnIOComplete,
+ base::Unretained(this)),
+ net_log_);
+}
+
+int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) {
+ verifier_.reset();
+
+ const CertVerifyResult& cert_verify_result =
+ verify_details_->cert_verify_result;
+ const CertStatus cert_status = cert_verify_result.cert_status;
+ if (transport_security_state_ &&
+ (result == OK ||
+ (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) &&
+ !transport_security_state_->CheckPublicKeyPins(
+ hostname_,
+ cert_verify_result.is_issued_by_known_root,
+ cert_verify_result.public_key_hashes,
+ &verify_details_->pinning_failure_log)) {
+ result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
+ }
+
+ if (result != OK) {
+ std::string error_string = ErrorToString(result);
+ error_details_ = StringPrintf("Failed to verify certificate chain: %s",
+ error_string.c_str());
+ DLOG(WARNING) << error_details_;
+ }
+
+ // Exit DoLoop and return the result to the caller to VerifyProof.
+ DCHECK_EQ(STATE_NONE, next_state_);
+ return result;
+}
+
+bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data,
+ const string& signature,
+ const string& cert) {
+ StringPiece spki;
+ if (!asn1::ExtractSPKIFromDERCert(cert, &spki)) {
+ DLOG(WARNING) << "ExtractSPKIFromDERCert failed";
+ return false;
+ }
+
+ crypto::SignatureVerifier verifier;
+
+ size_t size_bits;
+ X509Certificate::PublicKeyType type;
+ X509Certificate::GetPublicKeyInfo(cert_->os_cert_handle(), &size_bits,
+ &type);
+ if (type == X509Certificate::kPublicKeyTypeRSA) {
+ crypto::SignatureVerifier::HashAlgorithm hash_alg =
+ crypto::SignatureVerifier::SHA256;
+ crypto::SignatureVerifier::HashAlgorithm mask_hash_alg = hash_alg;
+ unsigned int hash_len = 32; // 32 is the length of a SHA-256 hash.
+
+ bool ok = verifier.VerifyInitRSAPSS(
+ hash_alg, mask_hash_alg, hash_len,
+ reinterpret_cast<const uint8*>(signature.data()), signature.size(),
+ reinterpret_cast<const uint8*>(spki.data()), spki.size());
+ if (!ok) {
+ DLOG(WARNING) << "VerifyInitRSAPSS failed";
+ return false;
+ }
+ } else if (type == X509Certificate::kPublicKeyTypeECDSA) {
+ // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
+ // RFC 5758:
+ // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
+ // ...
+ // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
+ // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
+ // as an AlgorithmIdentifier, the encoding MUST omit the parameters
+ // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
+ // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
+ // SHA384, or ecdsa-with-SHA512.
+ // See also RFC 5480, Appendix A.
+ static const uint8 kECDSAWithSHA256AlgorithmID[] = {
+ 0x30, 0x0a,
+ 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
+ };
+
+ if (!verifier.VerifyInit(
+ kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID),
+ reinterpret_cast<const uint8*>(signature.data()),
+ signature.size(),
+ reinterpret_cast<const uint8*>(spki.data()),
+ spki.size())) {
+ DLOG(WARNING) << "VerifyInit failed";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unsupported public key type " << type;
+ return false;
+ }
+
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(kProofSignatureLabel),
+ sizeof(kProofSignatureLabel));
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()),
+ signed_data.size());
+
+ if (!verifier.VerifyFinal()) {
+ DLOG(WARNING) << "VerifyFinal failed";
+ return false;
+ }
+
+ DVLOG(1) << "VerifyFinal success";
+ return true;
+}
+
+ProofVerifierChromium::ProofVerifierChromium(
+ CertVerifier* cert_verifier,
+ TransportSecurityState* transport_security_state)
+ : cert_verifier_(cert_verifier),
+ transport_security_state_(transport_security_state) {
+}
+
+ProofVerifierChromium::~ProofVerifierChromium() {
+ STLDeleteElements(&active_jobs_);
+}
+
+QuicAsyncStatus ProofVerifierChromium::VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) {
+ if (!verify_context) {
+ *error_details = "Missing context";
+ return QUIC_FAILURE;
+ }
+ const ProofVerifyContextChromium* chromium_context =
+ reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
+ scoped_ptr<Job> job(new Job(this,
+ cert_verifier_,
+ transport_security_state_,
+ chromium_context->net_log));
+ QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs,
+ signature, error_details,
+ verify_details, callback);
+ if (status == QUIC_PENDING) {
+ active_jobs_.insert(job.release());
+ }
+ return status;
+}
+
+void ProofVerifierChromium::OnJobComplete(Job* job) {
+ active_jobs_.erase(job);
+ delete job;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/proof_verifier_chromium.h b/net/quic/crypto/proof_verifier_chromium.h
new file mode 100644
index 0000000..7b305e2
--- /dev/null
+++ b/net/quic/crypto/proof_verifier_chromium.h
@@ -0,0 +1,91 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
+#define NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/base/net_log.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/quic/crypto/proof_verifier.h"
+
+namespace net {
+
+class CertVerifier;
+class TransportSecurityState;
+
+// ProofVerifyDetailsChromium is the implementation-specific information that a
+// ProofVerifierChromium returns about a certificate verification.
+class NET_EXPORT_PRIVATE ProofVerifyDetailsChromium
+ : public ProofVerifyDetails {
+ public:
+
+ // ProofVerifyDetails implementation
+ virtual ProofVerifyDetails* Clone() const OVERRIDE;
+
+ CertVerifyResult cert_verify_result;
+
+ // pinning_failure_log contains a message produced by
+ // TransportSecurityState::DomainState::CheckPublicKeyPins in the event of a
+ // pinning failure. It is a (somewhat) human-readable string.
+ std::string pinning_failure_log;
+};
+
+// ProofVerifyContextChromium is the implementation-specific information that a
+// ProofVerifierChromium needs in order to log correctly.
+struct ProofVerifyContextChromium : public ProofVerifyContext {
+ public:
+ explicit ProofVerifyContextChromium(const BoundNetLog& net_log)
+ : net_log(net_log) {}
+
+ BoundNetLog net_log;
+};
+
+// ProofVerifierChromium implements the QUIC ProofVerifier interface. It is
+// capable of handling multiple simultaneous requests.
+class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier {
+ public:
+ ProofVerifierChromium(CertVerifier* cert_verifier,
+ TransportSecurityState* transport_security_state);
+ virtual ~ProofVerifierChromium();
+
+ // ProofVerifier interface
+ virtual QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) OVERRIDE;
+
+ private:
+ class Job;
+ typedef std::set<Job*> JobSet;
+
+ void OnJobComplete(Job* job);
+
+ // Set owning pointers to active jobs.
+ JobSet active_jobs_;
+
+ // Underlying verifier used to verify certificates.
+ CertVerifier* const cert_verifier_;
+
+ TransportSecurityState* const transport_security_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProofVerifierChromium);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_
diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc
new file mode 100644
index 0000000..90dc265
--- /dev/null
+++ b/net/quic/crypto/quic_crypto_client_config.cc
@@ -0,0 +1,866 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_crypto_client_config.h"
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+#include "net/quic/crypto/channel_id.h"
+#include "net/quic/crypto/common_cert_set.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/curve25519_key_exchange.h"
+#include "net/quic/crypto/key_exchange.h"
+#include "net/quic/crypto/p256_key_exchange.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::find;
+using std::make_pair;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+enum ServerConfigState {
+ // WARNING: Do not change the numerical values of any of server config state.
+ // Do not remove deprecated server config states - just comment them as
+ // deprecated.
+ SERVER_CONFIG_EMPTY = 0,
+ SERVER_CONFIG_INVALID = 1,
+ SERVER_CONFIG_CORRUPTED = 2,
+ SERVER_CONFIG_EXPIRED = 3,
+ SERVER_CONFIG_INVALID_EXPIRY = 4,
+
+ // NOTE: Add new server config states only immediately above this line. Make
+ // sure to update the QuicServerConfigState enum in
+ // tools/metrics/histograms/histograms.xml accordingly.
+ SERVER_CONFIG_COUNT
+};
+
+void RecordServerConfigState(ServerConfigState server_config_state) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicClientHelloServerConfigState",
+ server_config_state, SERVER_CONFIG_COUNT);
+}
+
+} // namespace
+
+QuicCryptoClientConfig::QuicCryptoClientConfig()
+ : disable_ecdsa_(false) {}
+
+QuicCryptoClientConfig::~QuicCryptoClientConfig() {
+ STLDeleteValues(&cached_states_);
+}
+
+QuicCryptoClientConfig::CachedState::CachedState()
+ : server_config_valid_(false),
+ generation_counter_(0) {}
+
+QuicCryptoClientConfig::CachedState::~CachedState() {}
+
+bool QuicCryptoClientConfig::CachedState::IsComplete(QuicWallTime now) const {
+ if (server_config_.empty()) {
+ RecordServerConfigState(SERVER_CONFIG_EMPTY);
+ return false;
+ }
+
+ if (!server_config_valid_) {
+ RecordServerConfigState(SERVER_CONFIG_INVALID);
+ return false;
+ }
+
+ const CryptoHandshakeMessage* scfg = GetServerConfig();
+ if (!scfg) {
+ // Should be impossible short of cache corruption.
+ DCHECK(false);
+ RecordServerConfigState(SERVER_CONFIG_CORRUPTED);
+ return false;
+ }
+
+ uint64 expiry_seconds;
+ if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ RecordServerConfigState(SERVER_CONFIG_INVALID_EXPIRY);
+ return false;
+ }
+ if (now.ToUNIXSeconds() >= expiry_seconds) {
+ UMA_HISTOGRAM_CUSTOM_TIMES(
+ "Net.QuicClientHelloServerConfig.InvalidDuration",
+ base::TimeDelta::FromSeconds(now.ToUNIXSeconds() - expiry_seconds),
+ base::TimeDelta::FromMinutes(1), base::TimeDelta::FromDays(20), 50);
+ RecordServerConfigState(SERVER_CONFIG_EXPIRED);
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicCryptoClientConfig::CachedState::IsEmpty() const {
+ return server_config_.empty();
+}
+
+const CryptoHandshakeMessage*
+QuicCryptoClientConfig::CachedState::GetServerConfig() const {
+ if (server_config_.empty()) {
+ return nullptr;
+ }
+
+ if (!scfg_.get()) {
+ scfg_.reset(CryptoFramer::ParseMessage(server_config_));
+ DCHECK(scfg_.get());
+ }
+ return scfg_.get();
+}
+
+QuicErrorCode QuicCryptoClientConfig::CachedState::SetServerConfig(
+ StringPiece server_config, QuicWallTime now, string* error_details) {
+ const bool matches_existing = server_config == server_config_;
+
+ // Even if the new server config matches the existing one, we still wish to
+ // reject it if it has expired.
+ scoped_ptr<CryptoHandshakeMessage> new_scfg_storage;
+ const CryptoHandshakeMessage* new_scfg;
+
+ if (!matches_existing) {
+ new_scfg_storage.reset(CryptoFramer::ParseMessage(server_config));
+ new_scfg = new_scfg_storage.get();
+ } else {
+ new_scfg = GetServerConfig();
+ }
+
+ if (!new_scfg) {
+ *error_details = "SCFG invalid";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ uint64 expiry_seconds;
+ if (new_scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR) {
+ *error_details = "SCFG missing EXPY";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (now.ToUNIXSeconds() >= expiry_seconds) {
+ *error_details = "SCFG has expired";
+ return QUIC_CRYPTO_SERVER_CONFIG_EXPIRED;
+ }
+
+ if (!matches_existing) {
+ server_config_ = server_config.as_string();
+ SetProofInvalid();
+ scfg_.reset(new_scfg_storage.release());
+ }
+ return QUIC_NO_ERROR;
+}
+
+void QuicCryptoClientConfig::CachedState::InvalidateServerConfig() {
+ server_config_.clear();
+ scfg_.reset();
+ SetProofInvalid();
+}
+
+void QuicCryptoClientConfig::CachedState::SetProof(const vector<string>& certs,
+ StringPiece signature) {
+ bool has_changed =
+ signature != server_config_sig_ || certs_.size() != certs.size();
+
+ if (!has_changed) {
+ for (size_t i = 0; i < certs_.size(); i++) {
+ if (certs_[i] != certs[i]) {
+ has_changed = true;
+ break;
+ }
+ }
+ }
+
+ if (!has_changed) {
+ return;
+ }
+
+ // If the proof has changed then it needs to be revalidated.
+ SetProofInvalid();
+ certs_ = certs;
+ server_config_sig_ = signature.as_string();
+}
+
+void QuicCryptoClientConfig::CachedState::Clear() {
+ server_config_.clear();
+ source_address_token_.clear();
+ certs_.clear();
+ server_config_sig_.clear();
+ server_config_valid_ = false;
+ proof_verify_details_.reset();
+ scfg_.reset();
+ ++generation_counter_;
+}
+
+void QuicCryptoClientConfig::CachedState::ClearProof() {
+ SetProofInvalid();
+ certs_.clear();
+ server_config_sig_.clear();
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofValid() {
+ server_config_valid_ = true;
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofInvalid() {
+ server_config_valid_ = false;
+ ++generation_counter_;
+}
+
+bool QuicCryptoClientConfig::CachedState::Initialize(
+ StringPiece server_config,
+ StringPiece source_address_token,
+ const vector<string>& certs,
+ StringPiece signature,
+ QuicWallTime now) {
+ DCHECK(server_config_.empty());
+
+ if (server_config.empty()) {
+ return false;
+ }
+
+ string error_details;
+ QuicErrorCode error = SetServerConfig(server_config, now,
+ &error_details);
+ if (error != QUIC_NO_ERROR) {
+ DVLOG(1) << "SetServerConfig failed with " << error_details;
+ return false;
+ }
+
+ signature.CopyToString(&server_config_sig_);
+ source_address_token.CopyToString(&source_address_token_);
+ certs_ = certs;
+ return true;
+}
+
+const string& QuicCryptoClientConfig::CachedState::server_config() const {
+ return server_config_;
+}
+
+const string&
+QuicCryptoClientConfig::CachedState::source_address_token() const {
+ return source_address_token_;
+}
+
+const vector<string>& QuicCryptoClientConfig::CachedState::certs() const {
+ return certs_;
+}
+
+const string& QuicCryptoClientConfig::CachedState::signature() const {
+ return server_config_sig_;
+}
+
+bool QuicCryptoClientConfig::CachedState::proof_valid() const {
+ return server_config_valid_;
+}
+
+uint64 QuicCryptoClientConfig::CachedState::generation_counter() const {
+ return generation_counter_;
+}
+
+const ProofVerifyDetails*
+QuicCryptoClientConfig::CachedState::proof_verify_details() const {
+ return proof_verify_details_.get();
+}
+
+void QuicCryptoClientConfig::CachedState::set_source_address_token(
+ StringPiece token) {
+ source_address_token_ = token.as_string();
+}
+
+void QuicCryptoClientConfig::CachedState::SetProofVerifyDetails(
+ ProofVerifyDetails* details) {
+ proof_verify_details_.reset(details);
+}
+
+void QuicCryptoClientConfig::CachedState::InitializeFrom(
+ const QuicCryptoClientConfig::CachedState& other) {
+ DCHECK(server_config_.empty());
+ DCHECK(!server_config_valid_);
+ server_config_ = other.server_config_;
+ source_address_token_ = other.source_address_token_;
+ certs_ = other.certs_;
+ server_config_sig_ = other.server_config_sig_;
+ server_config_valid_ = other.server_config_valid_;
+ if (other.proof_verify_details_.get() != nullptr) {
+ proof_verify_details_.reset(other.proof_verify_details_->Clone());
+ }
+ ++generation_counter_;
+}
+
+void QuicCryptoClientConfig::SetDefaults() {
+ // Key exchange methods.
+ kexs.resize(2);
+ kexs[0] = kC255;
+ kexs[1] = kP256;
+
+ // Authenticated encryption algorithms. Prefer ChaCha20 by default.
+ aead.clear();
+ if (ChaCha20Poly1305Encrypter::IsSupported()) {
+ aead.push_back(kCC12);
+ }
+ aead.push_back(kAESG);
+
+ disable_ecdsa_ = false;
+}
+
+QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate(
+ const QuicServerId& server_id) {
+ CachedStateMap::const_iterator it = cached_states_.find(server_id);
+ if (it != cached_states_.end()) {
+ return it->second;
+ }
+
+ CachedState* cached = new CachedState;
+ cached_states_.insert(make_pair(server_id, cached));
+ PopulateFromCanonicalConfig(server_id, cached);
+ return cached;
+}
+
+void QuicCryptoClientConfig::ClearCachedStates() {
+ for (CachedStateMap::const_iterator it = cached_states_.begin();
+ it != cached_states_.end(); ++it) {
+ it->second->Clear();
+ }
+}
+
+void QuicCryptoClientConfig::FillInchoateClientHello(
+ const QuicServerId& server_id,
+ const QuicVersion preferred_version,
+ const CachedState* cached,
+ QuicCryptoNegotiatedParameters* out_params,
+ CryptoHandshakeMessage* out) const {
+ out->set_tag(kCHLO);
+ out->set_minimum_size(kClientHelloMinimumSize);
+
+ // Server name indication. We only send SNI if it's a valid domain name, as
+ // per the spec.
+ if (CryptoUtils::IsValidSNI(server_id.host())) {
+ out->SetStringPiece(kSNI, server_id.host());
+ }
+ out->SetValue(kVER, QuicVersionToQuicTag(preferred_version));
+
+ if (!user_agent_id_.empty()) {
+ out->SetStringPiece(kUAID, user_agent_id_);
+ }
+
+ if (!cached->source_address_token().empty()) {
+ out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token());
+ }
+
+ if (server_id.is_https()) {
+ if (disable_ecdsa_) {
+ out->SetTaglist(kPDMD, kX59R, 0);
+ } else {
+ out->SetTaglist(kPDMD, kX509, 0);
+ }
+ }
+
+ if (common_cert_sets) {
+ out->SetStringPiece(kCCS, common_cert_sets->GetCommonHashes());
+ }
+
+ const vector<string>& certs = cached->certs();
+ // We save |certs| in the QuicCryptoNegotiatedParameters so that, if the
+ // client config is being used for multiple connections, another connection
+ // doesn't update the cached certificates and cause us to be unable to
+ // process the server's compressed certificate chain.
+ out_params->cached_certs = certs;
+ if (!certs.empty()) {
+ vector<uint64> hashes;
+ hashes.reserve(certs.size());
+ for (vector<string>::const_iterator i = certs.begin();
+ i != certs.end(); ++i) {
+ hashes.push_back(QuicUtils::FNV1a_64_Hash(i->data(), i->size()));
+ }
+ out->SetVector(kCCRT, hashes);
+ }
+}
+
+QuicErrorCode QuicCryptoClientConfig::FillClientHello(
+ const QuicServerId& server_id,
+ QuicConnectionId connection_id,
+ const QuicVersion preferred_version,
+ const CachedState* cached,
+ QuicWallTime now,
+ QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
+ QuicCryptoNegotiatedParameters* out_params,
+ CryptoHandshakeMessage* out,
+ string* error_details) const {
+ DCHECK(error_details != nullptr);
+
+ FillInchoateClientHello(server_id, preferred_version, cached,
+ out_params, out);
+
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (!scfg) {
+ // This should never happen as our caller should have checked
+ // cached->IsComplete() before calling this function.
+ *error_details = "Handshake not ready";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ StringPiece scid;
+ if (!scfg->GetStringPiece(kSCID, &scid)) {
+ *error_details = "SCFG missing SCID";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ out->SetStringPiece(kSCID, scid);
+
+ const QuicTag* their_aeads;
+ const QuicTag* their_key_exchanges;
+ size_t num_their_aeads, num_their_key_exchanges;
+ if (scfg->GetTaglist(kAEAD, &their_aeads,
+ &num_their_aeads) != QUIC_NO_ERROR ||
+ scfg->GetTaglist(kKEXS, &their_key_exchanges,
+ &num_their_key_exchanges) != QUIC_NO_ERROR) {
+ *error_details = "Missing AEAD or KEXS";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ // AEAD: the work loads on the client and server are symmetric. Since the
+ // client is more likely to be CPU-constrained, break the tie by favoring
+ // the client's preference.
+ // Key exchange: the client does more work than the server, so favor the
+ // client's preference.
+ size_t key_exchange_index;
+ if (!QuicUtils::FindMutualTag(
+ aead, their_aeads, num_their_aeads, QuicUtils::LOCAL_PRIORITY,
+ &out_params->aead, nullptr) ||
+ !QuicUtils::FindMutualTag(
+ kexs, their_key_exchanges, num_their_key_exchanges,
+ QuicUtils::LOCAL_PRIORITY, &out_params->key_exchange,
+ &key_exchange_index)) {
+ *error_details = "Unsupported AEAD or KEXS";
+ return QUIC_CRYPTO_NO_SUPPORT;
+ }
+ out->SetTaglist(kAEAD, out_params->aead, 0);
+ out->SetTaglist(kKEXS, out_params->key_exchange, 0);
+
+ StringPiece public_value;
+ if (scfg->GetNthValue24(kPUBS, key_exchange_index, &public_value) !=
+ QUIC_NO_ERROR) {
+ *error_details = "Missing public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ StringPiece orbit;
+ if (!scfg->GetStringPiece(kORBT, &orbit) || orbit.size() != kOrbitSize) {
+ *error_details = "SCFG missing OBIT";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ CryptoUtils::GenerateNonce(now, rand, orbit, &out_params->client_nonce);
+ out->SetStringPiece(kNONC, out_params->client_nonce);
+ if (!out_params->server_nonce.empty()) {
+ out->SetStringPiece(kServerNonceTag, out_params->server_nonce);
+ }
+
+ switch (out_params->key_exchange) {
+ case kC255:
+ out_params->client_key_exchange.reset(Curve25519KeyExchange::New(
+ Curve25519KeyExchange::NewPrivateKey(rand)));
+ break;
+ case kP256:
+ out_params->client_key_exchange.reset(P256KeyExchange::New(
+ P256KeyExchange::NewPrivateKey()));
+ break;
+ default:
+ DCHECK(false);
+ *error_details = "Configured to support an unknown key exchange";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ if (!out_params->client_key_exchange->CalculateSharedKey(
+ public_value, &out_params->initial_premaster_secret)) {
+ *error_details = "Key exchange failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ out->SetStringPiece(kPUBS, out_params->client_key_exchange->public_value());
+
+ if (channel_id_key) {
+ // In order to calculate the encryption key for the CETV block we need to
+ // serialise the client hello as it currently is (i.e. without the CETV
+ // block). For this, the client hello is serialized without padding.
+ const size_t orig_min_size = out->minimum_size();
+ out->set_minimum_size(0);
+
+ CryptoHandshakeMessage cetv;
+ cetv.set_tag(kCETV);
+
+ string hkdf_input;
+ const QuicData& client_hello_serialized = out->GetSerialized();
+ hkdf_input.append(QuicCryptoConfig::kCETVLabel,
+ strlen(QuicCryptoConfig::kCETVLabel) + 1);
+ hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
+ hkdf_input.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_input.append(cached->server_config());
+
+ string key = channel_id_key->SerializeKey();
+ string signature;
+ if (!channel_id_key->Sign(hkdf_input, &signature)) {
+ *error_details = "Channel ID signature failed";
+ return QUIC_INVALID_CHANNEL_ID_SIGNATURE;
+ }
+
+ cetv.SetStringPiece(kCIDK, key);
+ cetv.SetStringPiece(kCIDS, signature);
+
+ CrypterPair crypters;
+ if (!CryptoUtils::DeriveKeys(out_params->initial_premaster_secret,
+ out_params->aead, out_params->client_nonce,
+ out_params->server_nonce, hkdf_input,
+ CryptoUtils::CLIENT, &crypters,
+ nullptr /* subkey secret */)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ const QuicData& cetv_plaintext = cetv.GetSerialized();
+ scoped_ptr<QuicData> cetv_ciphertext(crypters.encrypter->EncryptPacket(
+ 0 /* sequence number */,
+ StringPiece() /* associated data */,
+ cetv_plaintext.AsStringPiece()));
+ if (!cetv_ciphertext.get()) {
+ *error_details = "Packet encryption failed";
+ return QUIC_ENCRYPTION_FAILURE;
+ }
+
+ out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece());
+ out->MarkDirty();
+
+ out->set_minimum_size(orig_min_size);
+ }
+
+ // Derive the symmetric keys and set up the encrypters and decrypters.
+ // Set the following members of out_params:
+ // out_params->hkdf_input_suffix
+ // out_params->initial_crypters
+ out_params->hkdf_input_suffix.clear();
+ out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
+ const QuicData& client_hello_serialized = out->GetSerialized();
+ out_params->hkdf_input_suffix.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ out_params->hkdf_input_suffix.append(cached->server_config());
+
+ string hkdf_input;
+ const size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
+ hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
+ hkdf_input.append(out_params->hkdf_input_suffix);
+
+ if (!CryptoUtils::DeriveKeys(
+ out_params->initial_premaster_secret, out_params->aead,
+ out_params->client_nonce, out_params->server_nonce, hkdf_input,
+ CryptoUtils::CLIENT, &out_params->initial_crypters,
+ nullptr /* subkey secret */)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig(
+ const CryptoHandshakeMessage& message,
+ QuicWallTime now,
+ const vector<string>& cached_certs,
+ CachedState* cached,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ StringPiece scfg;
+ if (!message.GetStringPiece(kSCFG, &scfg)) {
+ *error_details = "Missing SCFG";
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND;
+ }
+
+ QuicErrorCode error = cached->SetServerConfig(scfg, now, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ StringPiece token;
+ if (message.GetStringPiece(kSourceAddressTokenTag, &token)) {
+ cached->set_source_address_token(token);
+ }
+
+ StringPiece proof, cert_bytes;
+ bool has_proof = message.GetStringPiece(kPROF, &proof);
+ bool has_cert = message.GetStringPiece(kCertificateTag, &cert_bytes);
+ if (has_proof && has_cert) {
+ vector<string> certs;
+ if (!CertCompressor::DecompressChain(cert_bytes, cached_certs,
+ common_cert_sets, &certs)) {
+ *error_details = "Certificate data invalid";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ cached->SetProof(certs, proof);
+ } else {
+ if (proof_verifier() != nullptr) {
+ // Secure QUIC: clear existing proof as we have been sent a new SCFG
+ // without matching proof/certs.
+ cached->ClearProof();
+ }
+
+ if (has_proof && !has_cert) {
+ *error_details = "Certificate missing";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!has_proof && has_cert) {
+ *error_details = "Proof missing";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessRejection(
+ const CryptoHandshakeMessage& rej,
+ QuicWallTime now,
+ CachedState* cached,
+ bool is_https,
+ QuicCryptoNegotiatedParameters* out_params,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ if (rej.tag() != kREJ) {
+ *error_details = "Message is not REJ";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ QuicErrorCode error = CacheNewServerConfig(rej, now, out_params->cached_certs,
+ cached, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ StringPiece nonce;
+ if (rej.GetStringPiece(kServerNonceTag, &nonce)) {
+ out_params->server_nonce = nonce.as_string();
+ }
+
+ const uint32* reject_reasons;
+ size_t num_reject_reasons;
+ COMPILE_ASSERT(sizeof(QuicTag) == sizeof(uint32), header_out_of_sync);
+ if (rej.GetTaglist(kRREJ, &reject_reasons,
+ &num_reject_reasons) == QUIC_NO_ERROR) {
+ uint32 packed_error = 0;
+ for (size_t i = 0; i < num_reject_reasons; ++i) {
+ // HANDSHAKE_OK is 0 and don't report that as error.
+ if (reject_reasons[i] == HANDSHAKE_OK || reject_reasons[i] >= 32) {
+ continue;
+ }
+ HandshakeFailureReason reason =
+ static_cast<HandshakeFailureReason>(reject_reasons[i]);
+ packed_error |= 1 << (reason - 1);
+ }
+ DVLOG(1) << "Reasons for rejection: " << packed_error;
+ if (is_https) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Secure",
+ packed_error);
+ } else {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicClientHelloRejectReasons.Insecure",
+ packed_error);
+ }
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessServerHello(
+ const CryptoHandshakeMessage& server_hello,
+ QuicConnectionId connection_id,
+ const QuicVersionVector& negotiated_versions,
+ CachedState* cached,
+ QuicCryptoNegotiatedParameters* out_params,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ if (server_hello.tag() != kSHLO) {
+ *error_details = "Bad tag";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+
+ const QuicTag* supported_version_tags;
+ size_t num_supported_versions;
+
+ if (server_hello.GetTaglist(kVER, &supported_version_tags,
+ &num_supported_versions) != QUIC_NO_ERROR) {
+ *error_details = "server hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ if (!negotiated_versions.empty()) {
+ bool mismatch = num_supported_versions != negotiated_versions.size();
+ for (size_t i = 0; i < num_supported_versions && !mismatch; ++i) {
+ mismatch = QuicTagToQuicVersion(supported_version_tags[i]) !=
+ negotiated_versions[i];
+ }
+ // The server sent a list of supported versions, and the connection
+ // reports that there was a version negotiation during the handshake.
+ // Ensure that these two lists are identical.
+ if (mismatch) {
+ *error_details = "Downgrade attack detected";
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
+ }
+ }
+
+ // Learn about updated source address tokens.
+ StringPiece token;
+ if (server_hello.GetStringPiece(kSourceAddressTokenTag, &token)) {
+ cached->set_source_address_token(token);
+ }
+
+ // TODO(agl):
+ // learn about updated SCFGs.
+
+ StringPiece public_value;
+ if (!server_hello.GetStringPiece(kPUBS, &public_value)) {
+ *error_details = "server hello missing forward secure public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!out_params->client_key_exchange->CalculateSharedKey(
+ public_value, &out_params->forward_secure_premaster_secret)) {
+ *error_details = "Key exchange failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ string hkdf_input;
+ const size_t label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
+ hkdf_input.reserve(label_len + out_params->hkdf_input_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel, label_len);
+ hkdf_input.append(out_params->hkdf_input_suffix);
+
+ if (!CryptoUtils::DeriveKeys(
+ out_params->forward_secure_premaster_secret, out_params->aead,
+ out_params->client_nonce, out_params->server_nonce, hkdf_input,
+ CryptoUtils::CLIENT, &out_params->forward_secure_crypters,
+ &out_params->subkey_secret)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ return QUIC_NO_ERROR;
+}
+
+QuicErrorCode QuicCryptoClientConfig::ProcessServerConfigUpdate(
+ const CryptoHandshakeMessage& server_config_update,
+ QuicWallTime now,
+ CachedState* cached,
+ QuicCryptoNegotiatedParameters* out_params,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ if (server_config_update.tag() != kSCUP) {
+ *error_details = "ServerConfigUpdate must have kSCUP tag.";
+ return QUIC_INVALID_CRYPTO_MESSAGE_TYPE;
+ }
+
+ return CacheNewServerConfig(server_config_update, now,
+ out_params->cached_certs, cached, error_details);
+}
+
+ProofVerifier* QuicCryptoClientConfig::proof_verifier() const {
+ return proof_verifier_.get();
+}
+
+void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) {
+ proof_verifier_.reset(verifier);
+}
+
+ChannelIDSource* QuicCryptoClientConfig::channel_id_source() const {
+ return channel_id_source_.get();
+}
+
+void QuicCryptoClientConfig::SetChannelIDSource(ChannelIDSource* source) {
+ channel_id_source_.reset(source);
+}
+
+void QuicCryptoClientConfig::InitializeFrom(
+ const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
+ QuicCryptoClientConfig* canonical_crypto_config) {
+ CachedState* canonical_cached =
+ canonical_crypto_config->LookupOrCreate(canonical_server_id);
+ if (!canonical_cached->proof_valid()) {
+ return;
+ }
+ CachedState* cached = LookupOrCreate(server_id);
+ cached->InitializeFrom(*canonical_cached);
+}
+
+void QuicCryptoClientConfig::AddCanonicalSuffix(const string& suffix) {
+ canoncial_suffixes_.push_back(suffix);
+}
+
+void QuicCryptoClientConfig::PreferAesGcm() {
+ DCHECK(!aead.empty());
+ if (aead.size() <= 1) {
+ return;
+ }
+ QuicTagVector::iterator pos = find(aead.begin(), aead.end(), kAESG);
+ if (pos != aead.end()) {
+ aead.erase(pos);
+ aead.insert(aead.begin(), kAESG);
+ }
+}
+
+void QuicCryptoClientConfig::DisableEcdsa() {
+ disable_ecdsa_ = true;
+}
+
+void QuicCryptoClientConfig::PopulateFromCanonicalConfig(
+ const QuicServerId& server_id,
+ CachedState* server_state) {
+ DCHECK(server_state->IsEmpty());
+ size_t i = 0;
+ for (; i < canoncial_suffixes_.size(); ++i) {
+ if (EndsWith(server_id.host(), canoncial_suffixes_[i], false)) {
+ break;
+ }
+ }
+ if (i == canoncial_suffixes_.size())
+ return;
+
+ QuicServerId suffix_server_id(canoncial_suffixes_[i], server_id.port(),
+ server_id.is_https(),
+ server_id.privacy_mode());
+ if (!ContainsKey(canonical_server_map_, suffix_server_id)) {
+ // This is the first host we've seen which matches the suffix, so make it
+ // canonical.
+ canonical_server_map_[suffix_server_id] = server_id;
+ return;
+ }
+
+ const QuicServerId& canonical_server_id =
+ canonical_server_map_[suffix_server_id];
+ CachedState* canonical_state = cached_states_[canonical_server_id];
+ if (!canonical_state->proof_valid()) {
+ return;
+ }
+
+ // Update canonical version to point at the "most recent" entry.
+ canonical_server_map_[suffix_server_id] = server_id;
+
+ server_state->InitializeFrom(*canonical_state);
+}
+
+} // namespace net
diff --git a/net/quic/crypto/quic_crypto_client_config.h b/net/quic/crypto/quic_crypto_client_config.h
new file mode 100644
index 0000000..39c2508
--- /dev/null
+++ b/net/quic/crypto/quic_crypto_client_config.h
@@ -0,0 +1,317 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
+#define NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
+
+namespace net {
+
+class ChannelIDKey;
+class ChannelIDSource;
+class CryptoHandshakeMessage;
+class ProofVerifier;
+class ProofVerifyDetails;
+class QuicRandom;
+
+// QuicCryptoClientConfig contains crypto-related configuration settings for a
+// client. Note that this object isn't thread-safe. It's designed to be used on
+// a single thread at a time.
+class NET_EXPORT_PRIVATE QuicCryptoClientConfig : public QuicCryptoConfig {
+ public:
+ // A CachedState contains the information that the client needs in order to
+ // perform a 0-RTT handshake with a server. This information can be reused
+ // over several connections to the same server.
+ class NET_EXPORT_PRIVATE CachedState {
+ public:
+ CachedState();
+ ~CachedState();
+
+ // IsComplete returns true if this object contains enough information to
+ // perform a handshake with the server. |now| is used to judge whether any
+ // cached server config has expired.
+ bool IsComplete(QuicWallTime now) const;
+
+ // IsEmpty returns true if |server_config_| is empty.
+ bool IsEmpty() const;
+
+ // GetServerConfig returns the parsed contents of |server_config|, or
+ // nullptr if |server_config| is empty. The return value is owned by this
+ // object and is destroyed when this object is.
+ const CryptoHandshakeMessage* GetServerConfig() const;
+
+ // SetServerConfig checks that |server_config| parses correctly and stores
+ // it in |server_config_|. |now| is used to judge whether |server_config|
+ // has expired.
+ QuicErrorCode SetServerConfig(base::StringPiece server_config,
+ QuicWallTime now,
+ std::string* error_details);
+
+ // InvalidateServerConfig clears the cached server config (if any).
+ void InvalidateServerConfig();
+
+ // SetProof stores a certificate chain and signature.
+ void SetProof(const std::vector<std::string>& certs,
+ base::StringPiece signature);
+
+ // Clears all the data.
+ void Clear();
+
+ // Clears the certificate chain and signature and invalidates the proof.
+ void ClearProof();
+
+ // SetProofValid records that the certificate chain and signature have been
+ // validated and that it's safe to assume that the server is legitimate.
+ // (Note: this does not check the chain or signature.)
+ void SetProofValid();
+
+ // If the server config or the proof has changed then it needs to be
+ // revalidated. Helper function to keep server_config_valid_ and
+ // generation_counter_ in sync.
+ void SetProofInvalid();
+
+ const std::string& server_config() const;
+ const std::string& source_address_token() const;
+ const std::vector<std::string>& certs() const;
+ const std::string& signature() const;
+ bool proof_valid() const;
+ uint64 generation_counter() const;
+ const ProofVerifyDetails* proof_verify_details() const;
+
+ void set_source_address_token(base::StringPiece token);
+
+ // SetProofVerifyDetails takes ownership of |details|.
+ void SetProofVerifyDetails(ProofVerifyDetails* details);
+
+ // Copy the |server_config_|, |source_address_token_|, |certs_| and
+ // |server_config_sig_| from the |other|. The remaining fields,
+ // |generation_counter_|, |proof_verify_details_|, and |scfg_| remain
+ // unchanged.
+ void InitializeFrom(const CachedState& other);
+
+ // Initializes this cached state based on the arguments provided.
+ // Returns false if there is a problem parsing the server config.
+ bool Initialize(base::StringPiece server_config,
+ base::StringPiece source_address_token,
+ const std::vector<std::string>& certs,
+ base::StringPiece signature,
+ QuicWallTime now);
+
+ private:
+ std::string server_config_; // A serialized handshake message.
+ std::string source_address_token_; // An opaque proof of IP ownership.
+ std::vector<std::string> certs_; // A list of certificates in leaf-first
+ // order.
+ std::string server_config_sig_; // A signature of |server_config_|.
+ bool server_config_valid_; // True if |server_config_| is correctly
+ // signed and |certs_| has been
+ // validated.
+ // Generation counter associated with the |server_config_|, |certs_| and
+ // |server_config_sig_| combination. It is incremented whenever we set
+ // server_config_valid_ to false.
+ uint64 generation_counter_;
+
+ scoped_ptr<ProofVerifyDetails> proof_verify_details_;
+
+ // scfg contains the cached, parsed value of |server_config|.
+ mutable scoped_ptr<CryptoHandshakeMessage> scfg_;
+
+ DISALLOW_COPY_AND_ASSIGN(CachedState);
+ };
+
+ QuicCryptoClientConfig();
+ ~QuicCryptoClientConfig();
+
+ // Sets the members to reasonable, default values.
+ void SetDefaults();
+
+ // LookupOrCreate returns a CachedState for the given |server_id|. If no such
+ // CachedState currently exists, it will be created and cached.
+ CachedState* LookupOrCreate(const QuicServerId& server_id);
+
+ // Delete all CachedState objects from cached_states_.
+ void ClearCachedStates();
+
+ // FillInchoateClientHello sets |out| to be a CHLO message that elicits a
+ // source-address token or SCFG from a server. If |cached| is non-nullptr, the
+ // source-address token will be taken from it. |out_params| is used in order
+ // to store the cached certs that were sent as hints to the server in
+ // |out_params->cached_certs|. |preferred_version| is the version of the
+ // QUIC protocol that this client chose to use initially. This allows the
+ // server to detect downgrade attacks.
+ void FillInchoateClientHello(const QuicServerId& server_id,
+ const QuicVersion preferred_version,
+ const CachedState* cached,
+ QuicCryptoNegotiatedParameters* out_params,
+ CryptoHandshakeMessage* out) const;
+
+ // FillClientHello sets |out| to be a CHLO message based on the configuration
+ // of this object. This object must have cached enough information about
+ // the server's hostname in order to perform a handshake. This can be checked
+ // with the |IsComplete| member of |CachedState|.
+ //
+ // |now| and |rand| are used to generate the nonce and |out_params| is
+ // filled with the results of the handshake that the server is expected to
+ // accept. |preferred_version| is the version of the QUIC protocol that this
+ // client chose to use initially. This allows the server to detect downgrade
+ // attacks.
+ //
+ // If |channel_id_key| is not null, it is used to sign a secret value derived
+ // from the client and server's keys, and the Channel ID public key and the
+ // signature are placed in the CETV value of the CHLO.
+ QuicErrorCode FillClientHello(const QuicServerId& server_id,
+ QuicConnectionId connection_id,
+ const QuicVersion preferred_version,
+ const CachedState* cached,
+ QuicWallTime now,
+ QuicRandom* rand,
+ const ChannelIDKey* channel_id_key,
+ QuicCryptoNegotiatedParameters* out_params,
+ CryptoHandshakeMessage* out,
+ std::string* error_details) const;
+
+ // ProcessRejection processes a REJ message from a server and updates the
+ // cached information about that server. After this, |IsComplete| may return
+ // true for that server's CachedState. If the rejection message contains
+ // state about a future handshake (i.e. an nonce value from the server), then
+ // it will be saved in |out_params|. |now| is used to judge whether the
+ // server config in the rejection message has expired. |is_https| is used to
+ // track reject reason for secure vs insecure QUIC.
+ QuicErrorCode ProcessRejection(const CryptoHandshakeMessage& rej,
+ QuicWallTime now,
+ CachedState* cached,
+ bool is_https,
+ QuicCryptoNegotiatedParameters* out_params,
+ std::string* error_details);
+
+ // ProcessServerHello processes the message in |server_hello|, updates the
+ // cached information about that server, writes the negotiated parameters to
+ // |out_params| and returns QUIC_NO_ERROR. If |server_hello| is unacceptable
+ // then it puts an error message in |error_details| and returns an error
+ // code. |negotiated_versions| contains the list of version, if any, that were
+ // present in a version negotiation packet previously recevied from the
+ // server. The contents of this list will be compared against the list of
+ // versions provided in the VER tag of the server hello.
+ QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello,
+ QuicConnectionId connection_id,
+ const QuicVersionVector& negotiated_versions,
+ CachedState* cached,
+ QuicCryptoNegotiatedParameters* out_params,
+ std::string* error_details);
+
+ // Processes the message in |server_update|, updating the cached source
+ // address token, and server config.
+ // If |server_update| is invalid then |error_details| will contain an error
+ // message, and an error code will be returned. If all has gone well
+ // QUIC_NO_ERROR is returned.
+ QuicErrorCode ProcessServerConfigUpdate(
+ const CryptoHandshakeMessage& server_update,
+ QuicWallTime now,
+ CachedState* cached,
+ QuicCryptoNegotiatedParameters* out_params,
+ std::string* error_details);
+
+ ProofVerifier* proof_verifier() const;
+
+ // SetProofVerifier takes ownership of a |ProofVerifier| that clients are
+ // free to use in order to verify certificate chains from servers. If a
+ // ProofVerifier is set then the client will request a certificate chain from
+ // the server.
+ void SetProofVerifier(ProofVerifier* verifier);
+
+ ChannelIDSource* channel_id_source() const;
+
+ // SetChannelIDSource sets a ChannelIDSource that will be called, when the
+ // server supports channel IDs, to obtain a channel ID for signing a message
+ // proving possession of the channel ID. This object takes ownership of
+ // |source|.
+ void SetChannelIDSource(ChannelIDSource* source);
+
+ // Initialize the CachedState from |canonical_crypto_config| for the
+ // |canonical_server_id| as the initial CachedState for |server_id|. We will
+ // copy config data only if |canonical_crypto_config| has valid proof.
+ void InitializeFrom(const QuicServerId& server_id,
+ const QuicServerId& canonical_server_id,
+ QuicCryptoClientConfig* canonical_crypto_config);
+
+ // Adds |suffix| as a domain suffix for which the server's crypto config
+ // is expected to be shared among servers with the domain suffix. If a server
+ // matches this suffix, then the server config from another server with the
+ // suffix will be used to initialize the cached state for this server.
+ void AddCanonicalSuffix(const std::string& suffix);
+
+ // Prefers AES-GCM (kAESG) over other AEAD algorithms. Call this method if
+ // the CPU has hardware acceleration for AES-GCM. This method can only be
+ // called after SetDefaults().
+ void PreferAesGcm();
+
+ // Disables the use of ECDSA for proof verification.
+ // Call this method on platforms that do not support ECDSA.
+ // TODO(rch): remove this method when we drop support for Windows XP.
+ void DisableEcdsa();
+
+ // Saves the |user_agent_id| that will be passed in QUIC's CHLO message.
+ void set_user_agent_id(const std::string& user_agent_id) {
+ user_agent_id_ = user_agent_id;
+ }
+
+ private:
+ typedef std::map<QuicServerId, CachedState*> CachedStateMap;
+
+ // CacheNewServerConfig checks for SCFG, STK, PROF, and CRT tags in |message|,
+ // verifies them, and stores them in the cached state if they validate.
+ // This is used on receipt of a REJ from a server, or when a server sends
+ // updated server config during a connection.
+ QuicErrorCode CacheNewServerConfig(
+ const CryptoHandshakeMessage& message,
+ QuicWallTime now,
+ const std::vector<std::string>& cached_certs,
+ CachedState* cached,
+ std::string* error_details);
+
+ // If the suffix of the hostname in |server_id| is in |canoncial_suffixes_|,
+ // then populate |cached| with the canonical cached state from
+ // |canonical_server_map_| for that suffix.
+ void PopulateFromCanonicalConfig(const QuicServerId& server_id,
+ CachedState* cached);
+
+ // cached_states_ maps from the server_id to the cached information about
+ // that server.
+ CachedStateMap cached_states_;
+
+ // Contains a map of servers which could share the same server config. Map
+ // from a canonical host suffix/port/scheme to a representative server with
+ // the canonical suffix, which has a plausible set of initial certificates
+ // (or at least server public key).
+ std::map<QuicServerId, QuicServerId> canonical_server_map_;
+
+ // Contains list of suffixes (for exmaple ".c.youtube.com",
+ // ".googlevideo.com") of canoncial hostnames.
+ std::vector<std::string> canoncial_suffixes_;
+
+ scoped_ptr<ProofVerifier> proof_verifier_;
+ scoped_ptr<ChannelIDSource> channel_id_source_;
+
+ // True if ECDSA should be disabled.
+ bool disable_ecdsa_;
+
+ // The |user_agent_id_| passed in QUIC's CHLO message.
+ std::string user_agent_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_CLIENT_CONFIG_H_
diff --git a/net/quic/crypto/quic_crypto_client_config_test.cc b/net/quic/crypto/quic_crypto_client_config_test.cc
new file mode 100644
index 0000000..b02243b
--- /dev/null
+++ b/net/quic/crypto/quic_crypto_client_config_test.cc
@@ -0,0 +1,271 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_crypto_client_config.h"
+
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+namespace {
+
+class TestProofVerifyDetails : public ProofVerifyDetails {
+ virtual ~TestProofVerifyDetails() {}
+
+ // ProofVerifyDetails implementation
+ virtual ProofVerifyDetails* Clone() const OVERRIDE {
+ return new TestProofVerifyDetails;
+ }
+};
+
+} // namespace
+
+TEST(QuicCryptoClientConfigTest, CachedState_IsEmpty) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_IsComplete) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_FALSE(state.IsComplete(QuicWallTime::FromUNIXSeconds(0)));
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_GenerationCounter) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_EQ(0u, state.generation_counter());
+ state.SetProofInvalid();
+ EXPECT_EQ(1u, state.generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_SetProofVerifyDetails) {
+ QuicCryptoClientConfig::CachedState state;
+ EXPECT_TRUE(state.proof_verify_details() == nullptr);
+ ProofVerifyDetails* details = new TestProofVerifyDetails;
+ state.SetProofVerifyDetails(details);
+ EXPECT_EQ(details, state.proof_verify_details());
+}
+
+TEST(QuicCryptoClientConfigTest, CachedState_InitializeFrom) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig::CachedState other;
+ state.set_source_address_token("TOKEN");
+ // TODO(rch): Populate other fields of |state|.
+ other.InitializeFrom(state);
+ EXPECT_EQ(state.server_config(), other.server_config());
+ EXPECT_EQ(state.source_address_token(), other.source_address_token());
+ EXPECT_EQ(state.certs(), other.certs());
+ EXPECT_EQ(1u, other.generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChlo) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ ¶ms, &msg);
+
+ QuicTag cver;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kVER, &cver));
+ EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
+}
+
+TEST(QuicCryptoClientConfigTest, PreferAesGcm) {
+ QuicCryptoClientConfig config;
+ config.SetDefaults();
+ if (config.aead.size() > 1)
+ EXPECT_NE(kAESG, config.aead[0]);
+ config.PreferAesGcm();
+ EXPECT_EQ(kAESG, config.aead[0]);
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChloSecure) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ ¶ms, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX509, pdmd);
+}
+
+TEST(QuicCryptoClientConfigTest, InchoateChloSecureNoEcdsa) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ config.DisableEcdsa();
+ QuicCryptoNegotiatedParameters params;
+ CryptoHandshakeMessage msg;
+ QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED);
+ config.FillInchoateClientHello(server_id, QuicVersionMax(), &state,
+ ¶ms, &msg);
+
+ QuicTag pdmd;
+ EXPECT_EQ(QUIC_NO_ERROR, msg.GetUint32(kPDMD, &pdmd));
+ EXPECT_EQ(kX59R, pdmd);
+}
+
+TEST(QuicCryptoClientConfigTest, FillClientHello) {
+ QuicCryptoClientConfig::CachedState state;
+ QuicCryptoClientConfig config;
+ QuicCryptoNegotiatedParameters params;
+ QuicConnectionId kConnectionId = 1234;
+ string error_details;
+ MockRandom rand;
+ CryptoHandshakeMessage chlo;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ config.FillClientHello(server_id,
+ kConnectionId,
+ QuicVersionMax(),
+ &state,
+ QuicWallTime::Zero(),
+ &rand,
+ nullptr, // channel_id_key
+ ¶ms,
+ &chlo,
+ &error_details);
+
+ // Verify that certain QuicTags have been set correctly in the CHLO.
+ QuicTag cver;
+ EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kVER, &cver));
+ EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
+}
+
+TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
+ QuicVersionVector supported_versions = QuicSupportedVersions();
+ if (supported_versions.size() == 1) {
+ // No downgrade attack is possible if the client only supports one version.
+ return;
+ }
+ QuicTagVector supported_version_tags;
+ for (size_t i = supported_versions.size(); i > 0; --i) {
+ supported_version_tags.push_back(
+ QuicVersionToQuicTag(supported_versions[i - 1]));
+ }
+ CryptoHandshakeMessage msg;
+ msg.set_tag(kSHLO);
+ msg.SetVector(kVER, supported_version_tags);
+
+ QuicCryptoClientConfig::CachedState cached;
+ QuicCryptoNegotiatedParameters out_params;
+ string error;
+ QuicCryptoClientConfig config;
+ EXPECT_EQ(QUIC_VERSION_NEGOTIATION_MISMATCH,
+ config.ProcessServerHello(msg, 0, supported_versions,
+ &cached, &out_params, &error));
+ EXPECT_EQ("Downgrade attack detected", error);
+}
+
+TEST(QuicCryptoClientConfigTest, InitializeFrom) {
+ QuicCryptoClientConfig config;
+ QuicServerId canonical_server_id("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_server_id);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicServerId other_server_id("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ config.InitializeFrom(other_server_id, canonical_server_id, &config);
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(other_server_id);
+
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+}
+
+TEST(QuicCryptoClientConfigTest, Canonical) {
+ QuicCryptoClientConfig config;
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId canonical_id2("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+
+ QuicCryptoClientConfig::CachedState* other =
+ config.LookupOrCreate(canonical_id2);
+
+ EXPECT_TRUE(state->IsEmpty());
+ EXPECT_EQ(state->server_config(), other->server_config());
+ EXPECT_EQ(state->source_address_token(), other->source_address_token());
+ EXPECT_EQ(state->certs(), other->certs());
+ EXPECT_EQ(1u, other->generation_counter());
+
+ QuicServerId different_id("mail.google.org", 80, false,
+ PRIVACY_MODE_DISABLED);
+ EXPECT_TRUE(config.LookupOrCreate(different_id)->IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, CanonicalNotUsedIfNotValid) {
+ QuicCryptoClientConfig config;
+ config.AddCanonicalSuffix(".google.com");
+ QuicServerId canonical_id1("www.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId canonical_id2("mail.google.com", 80, false,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state =
+ config.LookupOrCreate(canonical_id1);
+ // TODO(rch): Populate other fields of |state|.
+ state->set_source_address_token("TOKEN");
+
+ // Do not set the proof as valid, and check that it is not used
+ // as a canonical entry.
+ EXPECT_TRUE(config.LookupOrCreate(canonical_id2)->IsEmpty());
+}
+
+TEST(QuicCryptoClientConfigTest, ClearCachedStates) {
+ QuicCryptoClientConfig config;
+ QuicServerId server_id("www.google.com", 80, false, PRIVACY_MODE_DISABLED);
+ QuicCryptoClientConfig::CachedState* state = config.LookupOrCreate(server_id);
+ // TODO(rch): Populate other fields of |state|.
+ vector<string> certs(1);
+ certs[0] = "Hello Cert";
+ state->SetProof(certs, "signature");
+ state->set_source_address_token("TOKEN");
+ state->SetProofValid();
+ EXPECT_EQ(1u, state->generation_counter());
+
+ // Verify LookupOrCreate returns the same data.
+ QuicCryptoClientConfig::CachedState* other = config.LookupOrCreate(server_id);
+
+ EXPECT_EQ(state, other);
+ EXPECT_EQ(1u, other->generation_counter());
+
+ // Clear the cached states.
+ config.ClearCachedStates();
+
+ // Verify LookupOrCreate doesn't have any data.
+ QuicCryptoClientConfig::CachedState* cleared_cache =
+ config.LookupOrCreate(server_id);
+
+ EXPECT_EQ(state, cleared_cache);
+ EXPECT_FALSE(cleared_cache->proof_valid());
+ EXPECT_TRUE(cleared_cache->server_config().empty());
+ EXPECT_TRUE(cleared_cache->certs().empty());
+ EXPECT_TRUE(cleared_cache->signature().empty());
+ EXPECT_EQ(2u, cleared_cache->generation_counter());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc
new file mode 100644
index 0000000..6281043
--- /dev/null
+++ b/net/quic/crypto/quic_crypto_server_config.cc
@@ -0,0 +1,1571 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_crypto_server_config.h"
+
+#include <stdlib.h>
+#include <algorithm>
+
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "crypto/hkdf.h"
+#include "crypto/secure_hash.h"
+#include "net/base/net_util.h"
+#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/cert_compressor.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+#include "net/quic/crypto/channel_id.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_server_config_protobuf.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/curve25519_key_exchange.h"
+#include "net/quic/crypto/ephemeral_key_source.h"
+#include "net/quic/crypto/key_exchange.h"
+#include "net/quic/crypto/local_strike_register_client.h"
+#include "net/quic/crypto/p256_key_exchange.h"
+#include "net/quic/crypto/proof_source.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/source_address_token.h"
+#include "net/quic/crypto/strike_register.h"
+#include "net/quic/crypto/strike_register_client.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_socket_address_coder.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using crypto::SecureHash;
+using std::map;
+using std::sort;
+using std::string;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+string DeriveSourceAddressTokenKey(StringPiece source_address_token_secret) {
+ crypto::HKDF hkdf(source_address_token_secret,
+ StringPiece() /* no salt */,
+ "QUIC source address token key",
+ CryptoSecretBoxer::GetKeySize(),
+ 0 /* no fixed IV needed */,
+ 0 /* no subkey secret */);
+ return hkdf.server_write_key().as_string();
+}
+
+} // namespace
+
+// ClientHelloInfo contains information about a client hello message that is
+// only kept for as long as it's being processed.
+struct ClientHelloInfo {
+ ClientHelloInfo(const IPEndPoint& in_client_ip, QuicWallTime in_now)
+ : client_ip(in_client_ip),
+ now(in_now),
+ valid_source_address_token(false),
+ client_nonce_well_formed(false),
+ unique(false) {}
+
+ // Inputs to EvaluateClientHello.
+ const IPEndPoint client_ip;
+ const QuicWallTime now;
+
+ // Outputs from EvaluateClientHello.
+ bool valid_source_address_token;
+ bool client_nonce_well_formed;
+ bool unique;
+ StringPiece sni;
+ StringPiece client_nonce;
+ StringPiece server_nonce;
+ StringPiece user_agent_id;
+
+ // Errors from EvaluateClientHello.
+ vector<uint32> reject_reasons;
+ COMPILE_ASSERT(sizeof(QuicTag) == sizeof(uint32), header_out_of_sync);
+};
+
+struct ValidateClientHelloResultCallback::Result {
+ Result(const CryptoHandshakeMessage& in_client_hello,
+ IPEndPoint in_client_ip,
+ QuicWallTime in_now)
+ : client_hello(in_client_hello),
+ info(in_client_ip, in_now),
+ error_code(QUIC_NO_ERROR) {
+ }
+
+ CryptoHandshakeMessage client_hello;
+ ClientHelloInfo info;
+ QuicErrorCode error_code;
+ string error_details;
+};
+
+class ValidateClientHelloHelper {
+ public:
+ ValidateClientHelloHelper(ValidateClientHelloResultCallback::Result* result,
+ ValidateClientHelloResultCallback* done_cb)
+ : result_(result), done_cb_(done_cb) {
+ }
+
+ ~ValidateClientHelloHelper() {
+ LOG_IF(DFATAL, done_cb_ != nullptr)
+ << "Deleting ValidateClientHelloHelper with a pending callback.";
+ }
+
+ void ValidationComplete(QuicErrorCode error_code, const char* error_details) {
+ result_->error_code = error_code;
+ result_->error_details = error_details;
+ done_cb_->Run(result_);
+ DetachCallback();
+ }
+
+ void StartedAsyncCallback() {
+ DetachCallback();
+ }
+
+ private:
+ void DetachCallback() {
+ LOG_IF(DFATAL, done_cb_ == nullptr) << "Callback already detached.";
+ done_cb_ = nullptr;
+ }
+
+ ValidateClientHelloResultCallback::Result* result_;
+ ValidateClientHelloResultCallback* done_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValidateClientHelloHelper);
+};
+
+class VerifyNonceIsValidAndUniqueCallback
+ : public StrikeRegisterClient::ResultCallback {
+ public:
+ VerifyNonceIsValidAndUniqueCallback(
+ ValidateClientHelloResultCallback::Result* result,
+ ValidateClientHelloResultCallback* done_cb)
+ : result_(result), done_cb_(done_cb) {
+ }
+
+ protected:
+ virtual void RunImpl(bool nonce_is_valid_and_unique,
+ InsertStatus nonce_error) OVERRIDE {
+ DVLOG(1) << "Using client nonce, unique: " << nonce_is_valid_and_unique
+ << " nonce_error: " << nonce_error;
+ result_->info.unique = nonce_is_valid_and_unique;
+ if (!nonce_is_valid_and_unique) {
+ HandshakeFailureReason client_nonce_error;
+ switch (nonce_error) {
+ case NONCE_INVALID_FAILURE:
+ client_nonce_error = CLIENT_NONCE_INVALID_FAILURE;
+ break;
+ case NONCE_NOT_UNIQUE_FAILURE:
+ client_nonce_error = CLIENT_NONCE_NOT_UNIQUE_FAILURE;
+ break;
+ case NONCE_INVALID_ORBIT_FAILURE:
+ client_nonce_error = CLIENT_NONCE_INVALID_ORBIT_FAILURE;
+ break;
+ case NONCE_INVALID_TIME_FAILURE:
+ client_nonce_error = CLIENT_NONCE_INVALID_TIME_FAILURE;
+ break;
+ case STRIKE_REGISTER_TIMEOUT:
+ client_nonce_error = CLIENT_NONCE_STRIKE_REGISTER_TIMEOUT;
+ break;
+ case STRIKE_REGISTER_FAILURE:
+ client_nonce_error = CLIENT_NONCE_STRIKE_REGISTER_FAILURE;
+ break;
+ case NONCE_UNKNOWN_FAILURE:
+ client_nonce_error = CLIENT_NONCE_UNKNOWN_FAILURE;
+ break;
+ case NONCE_OK:
+ default:
+ LOG(DFATAL) << "Unexpected client nonce error: " << nonce_error;
+ client_nonce_error = CLIENT_NONCE_UNKNOWN_FAILURE;
+ break;
+ }
+ result_->info.reject_reasons.push_back(client_nonce_error);
+ }
+ done_cb_->Run(result_);
+ }
+
+ private:
+ ValidateClientHelloResultCallback::Result* result_;
+ ValidateClientHelloResultCallback* done_cb_;
+
+ DISALLOW_COPY_AND_ASSIGN(VerifyNonceIsValidAndUniqueCallback);
+};
+
+// static
+const char QuicCryptoServerConfig::TESTING[] = "secret string for testing";
+
+PrimaryConfigChangedCallback::PrimaryConfigChangedCallback() {
+}
+
+PrimaryConfigChangedCallback::~PrimaryConfigChangedCallback() {
+}
+
+ValidateClientHelloResultCallback::ValidateClientHelloResultCallback() {
+}
+
+ValidateClientHelloResultCallback::~ValidateClientHelloResultCallback() {
+}
+
+void ValidateClientHelloResultCallback::Run(const Result* result) {
+ RunImpl(result->client_hello, *result);
+ delete result;
+ delete this;
+}
+
+QuicCryptoServerConfig::ConfigOptions::ConfigOptions()
+ : expiry_time(QuicWallTime::Zero()),
+ channel_id_enabled(false),
+ p256(false) {}
+
+QuicCryptoServerConfig::QuicCryptoServerConfig(
+ StringPiece source_address_token_secret,
+ QuicRandom* rand)
+ : replay_protection_(true),
+ configs_lock_(),
+ primary_config_(nullptr),
+ next_config_promotion_time_(QuicWallTime::Zero()),
+ server_nonce_strike_register_lock_(),
+ strike_register_no_startup_period_(false),
+ strike_register_max_entries_(1 << 10),
+ strike_register_window_secs_(600),
+ source_address_token_future_secs_(3600),
+ source_address_token_lifetime_secs_(86400),
+ server_nonce_strike_register_max_entries_(1 << 10),
+ server_nonce_strike_register_window_secs_(120) {
+ default_source_address_token_boxer_.SetKey(
+ DeriveSourceAddressTokenKey(source_address_token_secret));
+
+ // Generate a random key and orbit for server nonces.
+ rand->RandBytes(server_nonce_orbit_, sizeof(server_nonce_orbit_));
+ const size_t key_size = server_nonce_boxer_.GetKeySize();
+ scoped_ptr<uint8[]> key_bytes(new uint8[key_size]);
+ rand->RandBytes(key_bytes.get(), key_size);
+
+ server_nonce_boxer_.SetKey(
+ StringPiece(reinterpret_cast<char*>(key_bytes.get()), key_size));
+}
+
+QuicCryptoServerConfig::~QuicCryptoServerConfig() {
+ primary_config_ = nullptr;
+}
+
+// static
+QuicServerConfigProtobuf* QuicCryptoServerConfig::GenerateConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options) {
+ CryptoHandshakeMessage msg;
+
+ const string curve25519_private_key =
+ Curve25519KeyExchange::NewPrivateKey(rand);
+ scoped_ptr<Curve25519KeyExchange> curve25519(
+ Curve25519KeyExchange::New(curve25519_private_key));
+ StringPiece curve25519_public_value = curve25519->public_value();
+
+ string encoded_public_values;
+ // First three bytes encode the length of the public value.
+ encoded_public_values.push_back(curve25519_public_value.size());
+ encoded_public_values.push_back(curve25519_public_value.size() >> 8);
+ encoded_public_values.push_back(curve25519_public_value.size() >> 16);
+ encoded_public_values.append(curve25519_public_value.data(),
+ curve25519_public_value.size());
+
+ string p256_private_key;
+ if (options.p256) {
+ p256_private_key = P256KeyExchange::NewPrivateKey();
+ scoped_ptr<P256KeyExchange> p256(P256KeyExchange::New(p256_private_key));
+ StringPiece p256_public_value = p256->public_value();
+
+ encoded_public_values.push_back(p256_public_value.size());
+ encoded_public_values.push_back(p256_public_value.size() >> 8);
+ encoded_public_values.push_back(p256_public_value.size() >> 16);
+ encoded_public_values.append(p256_public_value.data(),
+ p256_public_value.size());
+ }
+
+ msg.set_tag(kSCFG);
+ if (options.p256) {
+ msg.SetTaglist(kKEXS, kC255, kP256, 0);
+ } else {
+ msg.SetTaglist(kKEXS, kC255, 0);
+ }
+ if (ChaCha20Poly1305Encrypter::IsSupported()) {
+ msg.SetTaglist(kAEAD, kAESG, kCC12, 0);
+ } else {
+ msg.SetTaglist(kAEAD, kAESG, 0);
+ }
+ msg.SetStringPiece(kPUBS, encoded_public_values);
+
+ if (options.expiry_time.IsZero()) {
+ const QuicWallTime now = clock->WallNow();
+ const QuicWallTime expiry = now.Add(QuicTime::Delta::FromSeconds(
+ 60 * 60 * 24 * 180 /* 180 days, ~six months */));
+ const uint64 expiry_seconds = expiry.ToUNIXSeconds();
+ msg.SetValue(kEXPY, expiry_seconds);
+ } else {
+ msg.SetValue(kEXPY, options.expiry_time.ToUNIXSeconds());
+ }
+
+ char orbit_bytes[kOrbitSize];
+ if (options.orbit.size() == sizeof(orbit_bytes)) {
+ memcpy(orbit_bytes, options.orbit.data(), sizeof(orbit_bytes));
+ } else {
+ DCHECK(options.orbit.empty());
+ rand->RandBytes(orbit_bytes, sizeof(orbit_bytes));
+ }
+ msg.SetStringPiece(kORBT, StringPiece(orbit_bytes, sizeof(orbit_bytes)));
+
+ if (options.channel_id_enabled) {
+ msg.SetTaglist(kPDMD, kCHID, 0);
+ }
+
+ if (options.id.empty()) {
+ // We need to ensure that the SCID changes whenever the server config does
+ // thus we make it a hash of the rest of the server config.
+ scoped_ptr<QuicData> serialized(
+ CryptoFramer::ConstructHandshakeMessage(msg));
+ scoped_ptr<SecureHash> hash(SecureHash::Create(SecureHash::SHA256));
+ hash->Update(serialized->data(), serialized->length());
+
+ char scid_bytes[16];
+ hash->Finish(scid_bytes, sizeof(scid_bytes));
+ msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes)));
+ } else {
+ msg.SetStringPiece(kSCID, options.id);
+ }
+ // Don't put new tags below this point. The SCID generation should hash over
+ // everything but itself and so extra tags should be added prior to the
+ // preceeding if block.
+
+ scoped_ptr<QuicData> serialized(CryptoFramer::ConstructHandshakeMessage(msg));
+
+ scoped_ptr<QuicServerConfigProtobuf> config(new QuicServerConfigProtobuf);
+ config->set_config(serialized->AsStringPiece());
+ QuicServerConfigProtobuf::PrivateKey* curve25519_key = config->add_key();
+ curve25519_key->set_tag(kC255);
+ curve25519_key->set_private_key(curve25519_private_key);
+
+ if (options.p256) {
+ QuicServerConfigProtobuf::PrivateKey* p256_key = config->add_key();
+ p256_key->set_tag(kP256);
+ p256_key->set_private_key(p256_private_key);
+ }
+
+ return config.release();
+}
+
+CryptoHandshakeMessage* QuicCryptoServerConfig::AddConfig(
+ QuicServerConfigProtobuf* protobuf,
+ const QuicWallTime now) {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(protobuf->config()));
+
+ if (!msg.get()) {
+ LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ scoped_refptr<Config> config(ParseConfigProtobuf(protobuf));
+ if (!config.get()) {
+ LOG(WARNING) << "Failed to parse server config message";
+ return nullptr;
+ }
+
+ {
+ base::AutoLock locked(configs_lock_);
+ if (configs_.find(config->id) != configs_.end()) {
+ LOG(WARNING) << "Failed to add config because another with the same "
+ "server config id already exists: "
+ << base::HexEncode(config->id.data(), config->id.size());
+ return nullptr;
+ }
+
+ configs_[config->id] = config;
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
+ }
+
+ return msg.release();
+}
+
+CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options) {
+ scoped_ptr<QuicServerConfigProtobuf> config(
+ GenerateConfig(rand, clock, options));
+ return AddConfig(config.get(), clock->WallNow());
+}
+
+bool QuicCryptoServerConfig::SetConfigs(
+ const vector<QuicServerConfigProtobuf*>& protobufs,
+ const QuicWallTime now) {
+ vector<scoped_refptr<Config> > parsed_configs;
+ bool ok = true;
+
+ for (vector<QuicServerConfigProtobuf*>::const_iterator i = protobufs.begin();
+ i != protobufs.end(); ++i) {
+ scoped_refptr<Config> config(ParseConfigProtobuf(*i));
+ if (!config.get()) {
+ ok = false;
+ break;
+ }
+
+ parsed_configs.push_back(config);
+ }
+
+ if (parsed_configs.empty()) {
+ LOG(WARNING) << "New config list is empty.";
+ ok = false;
+ }
+
+ if (!ok) {
+ LOG(WARNING) << "Rejecting QUIC configs because of above errors";
+ } else {
+ VLOG(1) << "Updating configs:";
+
+ base::AutoLock locked(configs_lock_);
+ ConfigMap new_configs;
+
+ for (vector<scoped_refptr<Config> >::const_iterator i =
+ parsed_configs.begin();
+ i != parsed_configs.end(); ++i) {
+ scoped_refptr<Config> config = *i;
+
+ ConfigMap::iterator it = configs_.find(config->id);
+ if (it != configs_.end()) {
+ VLOG(1)
+ << "Keeping scid: " << base::HexEncode(
+ config->id.data(), config->id.size())
+ << " orbit: " << base::HexEncode(
+ reinterpret_cast<const char *>(config->orbit), kOrbitSize)
+ << " new primary_time " << config->primary_time.ToUNIXSeconds()
+ << " old primary_time " << it->second->primary_time.ToUNIXSeconds()
+ << " new priority " << config->priority
+ << " old priority " << it->second->priority;
+ // Update primary_time and priority.
+ it->second->primary_time = config->primary_time;
+ it->second->priority = config->priority;
+ new_configs.insert(*it);
+ } else {
+ VLOG(1) << "Adding scid: " << base::HexEncode(
+ config->id.data(), config->id.size())
+ << " orbit: " << base::HexEncode(
+ reinterpret_cast<const char *>(config->orbit), kOrbitSize)
+ << " primary_time " << config->primary_time.ToUNIXSeconds()
+ << " priority " << config->priority;
+ new_configs.insert(make_pair(config->id, config));
+ }
+ }
+
+ configs_.swap(new_configs);
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
+ }
+
+ return ok;
+}
+
+void QuicCryptoServerConfig::GetConfigIds(vector<string>* scids) const {
+ base::AutoLock locked(configs_lock_);
+ for (ConfigMap::const_iterator it = configs_.begin();
+ it != configs_.end(); ++it) {
+ scids->push_back(it->first);
+ }
+}
+
+void QuicCryptoServerConfig::ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ IPEndPoint client_ip,
+ const QuicClock* clock,
+ ValidateClientHelloResultCallback* done_cb) const {
+ const QuicWallTime now(clock->WallNow());
+
+ ValidateClientHelloResultCallback::Result* result =
+ new ValidateClientHelloResultCallback::Result(
+ client_hello, client_ip, now);
+
+ StringPiece requested_scid;
+ client_hello.GetStringPiece(kSCID, &requested_scid);
+
+ uint8 primary_orbit[kOrbitSize];
+ scoped_refptr<Config> requested_config;
+ {
+ base::AutoLock locked(configs_lock_);
+
+ if (!primary_config_.get()) {
+ result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
+ result->error_details = "No configurations loaded";
+ } else {
+ if (!next_config_promotion_time_.IsZero() &&
+ next_config_promotion_time_.IsAfter(now)) {
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
+ }
+
+ memcpy(primary_orbit, primary_config_->orbit, sizeof(primary_orbit));
+ }
+
+ requested_config = GetConfigWithScid(requested_scid);
+ }
+
+ if (result->error_code == QUIC_NO_ERROR) {
+ EvaluateClientHello(primary_orbit, requested_config, result, done_cb);
+ } else {
+ done_cb->Run(result);
+ }
+}
+
+QuicErrorCode QuicCryptoServerConfig::ProcessClientHello(
+ const ValidateClientHelloResultCallback::Result& validate_chlo_result,
+ QuicConnectionId connection_id,
+ IPEndPoint client_address,
+ QuicVersion version,
+ const QuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoNegotiatedParameters *params,
+ CryptoHandshakeMessage* out,
+ string* error_details) const {
+ DCHECK(error_details);
+
+ const CryptoHandshakeMessage& client_hello =
+ validate_chlo_result.client_hello;
+ const ClientHelloInfo& info = validate_chlo_result.info;
+
+ // If the client's preferred version is not the version we are currently
+ // speaking, then the client went through a version negotiation. In this
+ // case, we need to make sure that we actually do not support this version
+ // and that it wasn't a downgrade attack.
+ QuicTag client_version_tag;
+ if (client_hello.GetUint32(kVER, &client_version_tag) != QUIC_NO_ERROR) {
+ *error_details = "client hello missing version list";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ QuicVersion client_version = QuicTagToQuicVersion(client_version_tag);
+ if (client_version != version) {
+ // Just because client_version is a valid version enum doesn't mean that
+ // this server actually supports that version, so we check to see if
+ // it's actually in the supported versions list.
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ if (client_version == supported_versions[i]) {
+ *error_details = "Downgrade attack detected";
+ return QUIC_VERSION_NEGOTIATION_MISMATCH;
+ }
+ }
+ }
+
+ StringPiece requested_scid;
+ client_hello.GetStringPiece(kSCID, &requested_scid);
+ const QuicWallTime now(clock->WallNow());
+
+ scoped_refptr<Config> requested_config;
+ scoped_refptr<Config> primary_config;
+ {
+ base::AutoLock locked(configs_lock_);
+
+ if (!primary_config_.get()) {
+ *error_details = "No configurations loaded";
+ return QUIC_CRYPTO_INTERNAL_ERROR;
+ }
+
+ if (!next_config_promotion_time_.IsZero() &&
+ next_config_promotion_time_.IsAfter(now)) {
+ SelectNewPrimaryConfig(now);
+ DCHECK(primary_config_.get());
+ DCHECK_EQ(configs_.find(primary_config_->id)->second, primary_config_);
+ }
+
+ // We'll use the config that the client requested in order to do
+ // key-agreement. Otherwise we'll give it a copy of |primary_config_|
+ // to use.
+ primary_config = primary_config_;
+
+ requested_config = GetConfigWithScid(requested_scid);
+ }
+
+ if (validate_chlo_result.error_code != QUIC_NO_ERROR) {
+ *error_details = validate_chlo_result.error_details;
+ return validate_chlo_result.error_code;
+ }
+
+ out->Clear();
+
+ if (!info.valid_source_address_token ||
+ !info.client_nonce_well_formed ||
+ !info.unique ||
+ !requested_config.get()) {
+ BuildRejection(
+ *primary_config.get(), client_hello, info, rand, params, out);
+ return QUIC_NO_ERROR;
+ }
+
+ const QuicTag* their_aeads;
+ const QuicTag* their_key_exchanges;
+ size_t num_their_aeads, num_their_key_exchanges;
+ if (client_hello.GetTaglist(kAEAD, &their_aeads,
+ &num_their_aeads) != QUIC_NO_ERROR ||
+ client_hello.GetTaglist(kKEXS, &their_key_exchanges,
+ &num_their_key_exchanges) != QUIC_NO_ERROR ||
+ num_their_aeads != 1 ||
+ num_their_key_exchanges != 1) {
+ *error_details = "Missing or invalid AEAD or KEXS";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ size_t key_exchange_index;
+ if (!QuicUtils::FindMutualTag(requested_config->aead, their_aeads,
+ num_their_aeads, QuicUtils::LOCAL_PRIORITY,
+ ¶ms->aead, nullptr) ||
+ !QuicUtils::FindMutualTag(
+ requested_config->kexs, their_key_exchanges, num_their_key_exchanges,
+ QuicUtils::LOCAL_PRIORITY, ¶ms->key_exchange,
+ &key_exchange_index)) {
+ *error_details = "Unsupported AEAD or KEXS";
+ return QUIC_CRYPTO_NO_SUPPORT;
+ }
+
+ StringPiece public_value;
+ if (!client_hello.GetStringPiece(kPUBS, &public_value)) {
+ *error_details = "Missing public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ const KeyExchange* key_exchange =
+ requested_config->key_exchanges[key_exchange_index];
+ if (!key_exchange->CalculateSharedKey(public_value,
+ ¶ms->initial_premaster_secret)) {
+ *error_details = "Invalid public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ if (!info.sni.empty()) {
+ scoped_ptr<char[]> sni_tmp(new char[info.sni.length() + 1]);
+ memcpy(sni_tmp.get(), info.sni.data(), info.sni.length());
+ sni_tmp[info.sni.length()] = 0;
+ params->sni = CryptoUtils::NormalizeHostname(sni_tmp.get());
+ }
+
+ string hkdf_suffix;
+ const QuicData& client_hello_serialized = client_hello.GetSerialized();
+ hkdf_suffix.reserve(sizeof(connection_id) + client_hello_serialized.length() +
+ requested_config->serialized.size());
+ hkdf_suffix.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
+ hkdf_suffix.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_suffix.append(requested_config->serialized);
+
+ StringPiece cetv_ciphertext;
+ if (requested_config->channel_id_enabled &&
+ client_hello.GetStringPiece(kCETV, &cetv_ciphertext)) {
+ CryptoHandshakeMessage client_hello_copy(client_hello);
+ client_hello_copy.Erase(kCETV);
+ client_hello_copy.Erase(kPAD);
+
+ const QuicData& client_hello_serialized = client_hello_copy.GetSerialized();
+ string hkdf_input;
+ hkdf_input.append(QuicCryptoConfig::kCETVLabel,
+ strlen(QuicCryptoConfig::kCETVLabel) + 1);
+ hkdf_input.append(reinterpret_cast<char*>(&connection_id),
+ sizeof(connection_id));
+ hkdf_input.append(client_hello_serialized.data(),
+ client_hello_serialized.length());
+ hkdf_input.append(requested_config->serialized);
+
+ CrypterPair crypters;
+ if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
+ info.client_nonce, info.server_nonce,
+ hkdf_input, CryptoUtils::SERVER, &crypters,
+ nullptr /* subkey secret */)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ scoped_ptr<QuicData> cetv_plaintext(crypters.decrypter->DecryptPacket(
+ 0 /* sequence number */, StringPiece() /* associated data */,
+ cetv_ciphertext));
+ if (!cetv_plaintext.get()) {
+ *error_details = "CETV decryption failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ scoped_ptr<CryptoHandshakeMessage> cetv(CryptoFramer::ParseMessage(
+ cetv_plaintext->AsStringPiece()));
+ if (!cetv.get()) {
+ *error_details = "CETV parse error";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ StringPiece key, signature;
+ if (cetv->GetStringPiece(kCIDK, &key) &&
+ cetv->GetStringPiece(kCIDS, &signature)) {
+ if (!ChannelIDVerifier::Verify(key, hkdf_input, signature)) {
+ *error_details = "ChannelID signature failure";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+
+ params->channel_id = key.as_string();
+ }
+ }
+
+ string hkdf_input;
+ size_t label_len = strlen(QuicCryptoConfig::kInitialLabel) + 1;
+ hkdf_input.reserve(label_len + hkdf_suffix.size());
+ hkdf_input.append(QuicCryptoConfig::kInitialLabel, label_len);
+ hkdf_input.append(hkdf_suffix);
+
+ if (!CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead,
+ info.client_nonce, info.server_nonce, hkdf_input,
+ CryptoUtils::SERVER,
+ ¶ms->initial_crypters,
+ nullptr /* subkey secret */)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ string forward_secure_public_value;
+ if (ephemeral_key_source_.get()) {
+ params->forward_secure_premaster_secret =
+ ephemeral_key_source_->CalculateForwardSecureKey(
+ key_exchange, rand, clock->ApproximateNow(), public_value,
+ &forward_secure_public_value);
+ } else {
+ scoped_ptr<KeyExchange> forward_secure_key_exchange(
+ key_exchange->NewKeyPair(rand));
+ forward_secure_public_value =
+ forward_secure_key_exchange->public_value().as_string();
+ if (!forward_secure_key_exchange->CalculateSharedKey(
+ public_value, ¶ms->forward_secure_premaster_secret)) {
+ *error_details = "Invalid public value";
+ return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER;
+ }
+ }
+
+ string forward_secure_hkdf_input;
+ label_len = strlen(QuicCryptoConfig::kForwardSecureLabel) + 1;
+ forward_secure_hkdf_input.reserve(label_len + hkdf_suffix.size());
+ forward_secure_hkdf_input.append(QuicCryptoConfig::kForwardSecureLabel,
+ label_len);
+ forward_secure_hkdf_input.append(hkdf_suffix);
+
+ if (!CryptoUtils::DeriveKeys(
+ params->forward_secure_premaster_secret, params->aead,
+ info.client_nonce, info.server_nonce, forward_secure_hkdf_input,
+ CryptoUtils::SERVER, ¶ms->forward_secure_crypters,
+ ¶ms->subkey_secret)) {
+ *error_details = "Symmetric key setup failed";
+ return QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED;
+ }
+
+ out->set_tag(kSHLO);
+ QuicTagVector supported_version_tags;
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ supported_version_tags.push_back
+ (QuicVersionToQuicTag(supported_versions[i]));
+ }
+ out->SetVector(kVER, supported_version_tags);
+ out->SetStringPiece(
+ kSourceAddressTokenTag,
+ NewSourceAddressToken(*requested_config.get(),
+ client_address,
+ rand,
+ info.now,
+ nullptr));
+ QuicSocketAddressCoder address_coder(client_address);
+ out->SetStringPiece(kCADR, address_coder.Encode());
+ out->SetStringPiece(kPUBS, forward_secure_public_value);
+
+ return QUIC_NO_ERROR;
+}
+
+scoped_refptr<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::GetConfigWithScid(StringPiece requested_scid) const {
+ // In Chromium, we will dead lock if the lock is held by the current thread.
+ // Chromium doesn't have AssertReaderHeld API call.
+ // configs_lock_.AssertReaderHeld();
+
+ if (!requested_scid.empty()) {
+ ConfigMap::const_iterator it = configs_.find(requested_scid.as_string());
+ if (it != configs_.end()) {
+ // We'll use the config that the client requested in order to do
+ // key-agreement.
+ return scoped_refptr<Config>(it->second);
+ }
+ }
+
+ return scoped_refptr<Config>();
+}
+
+// ConfigPrimaryTimeLessThan is a comparator that implements "less than" for
+// Config's based on their primary_time.
+// static
+bool QuicCryptoServerConfig::ConfigPrimaryTimeLessThan(
+ const scoped_refptr<Config>& a,
+ const scoped_refptr<Config>& b) {
+ if (a->primary_time.IsBefore(b->primary_time) ||
+ b->primary_time.IsBefore(a->primary_time)) {
+ // Primary times differ.
+ return a->primary_time.IsBefore(b->primary_time);
+ } else if (a->priority != b->priority) {
+ // Primary times are equal, sort backwards by priority.
+ return a->priority < b->priority;
+ } else {
+ // Primary times and priorities are equal, sort by config id.
+ return a->id < b->id;
+ }
+}
+
+void QuicCryptoServerConfig::SelectNewPrimaryConfig(
+ const QuicWallTime now) const {
+ vector<scoped_refptr<Config> > configs;
+ configs.reserve(configs_.size());
+
+ for (ConfigMap::const_iterator it = configs_.begin();
+ it != configs_.end(); ++it) {
+ // TODO(avd) Exclude expired configs?
+ configs.push_back(it->second);
+ }
+
+ if (configs.empty()) {
+ if (primary_config_.get()) {
+ LOG(DFATAL) << "No valid QUIC server config. Keeping the current config.";
+ } else {
+ LOG(DFATAL) << "No valid QUIC server config.";
+ }
+ return;
+ }
+
+ sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan);
+
+ Config* best_candidate = configs[0].get();
+
+ for (size_t i = 0; i < configs.size(); ++i) {
+ const scoped_refptr<Config> config(configs[i]);
+ if (!config->primary_time.IsAfter(now)) {
+ if (config->primary_time.IsAfter(best_candidate->primary_time)) {
+ best_candidate = config.get();
+ }
+ continue;
+ }
+
+ // This is the first config with a primary_time in the future. Thus the
+ // previous Config should be the primary and this one should determine the
+ // next_config_promotion_time_.
+ scoped_refptr<Config> new_primary(best_candidate);
+ if (i == 0) {
+ // We need the primary_time of the next config.
+ if (configs.size() > 1) {
+ next_config_promotion_time_ = configs[1]->primary_time;
+ } else {
+ next_config_promotion_time_ = QuicWallTime::Zero();
+ }
+ } else {
+ next_config_promotion_time_ = config->primary_time;
+ }
+
+ if (primary_config_.get()) {
+ primary_config_->is_primary = false;
+ }
+ primary_config_ = new_primary;
+ new_primary->is_primary = true;
+ DVLOG(1) << "New primary config. orbit: "
+ << base::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize);
+ if (primary_config_changed_cb_.get() != nullptr) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
+
+ return;
+ }
+
+ // All config's primary times are in the past. We should make the most recent
+ // and highest priority candidate primary.
+ scoped_refptr<Config> new_primary(best_candidate);
+ if (primary_config_.get()) {
+ primary_config_->is_primary = false;
+ }
+ primary_config_ = new_primary;
+ new_primary->is_primary = true;
+ DVLOG(1) << "New primary config. orbit: "
+ << base::HexEncode(
+ reinterpret_cast<const char*>(primary_config_->orbit),
+ kOrbitSize)
+ << " scid: " << base::HexEncode(primary_config_->id.data(),
+ primary_config_->id.size());
+ next_config_promotion_time_ = QuicWallTime::Zero();
+ if (primary_config_changed_cb_.get() != nullptr) {
+ primary_config_changed_cb_->Run(primary_config_->id);
+ }
+}
+
+void QuicCryptoServerConfig::EvaluateClientHello(
+ const uint8* primary_orbit,
+ scoped_refptr<Config> requested_config,
+ ValidateClientHelloResultCallback::Result* client_hello_state,
+ ValidateClientHelloResultCallback* done_cb) const {
+ ValidateClientHelloHelper helper(client_hello_state, done_cb);
+
+ const CryptoHandshakeMessage& client_hello =
+ client_hello_state->client_hello;
+ ClientHelloInfo* info = &(client_hello_state->info);
+
+ if (client_hello.size() < kClientHelloMinimumSize) {
+ helper.ValidationComplete(QUIC_CRYPTO_INVALID_VALUE_LENGTH,
+ "Client hello too small");
+ return;
+ }
+
+ if (client_hello.GetStringPiece(kSNI, &info->sni) &&
+ !CryptoUtils::IsValidSNI(info->sni)) {
+ helper.ValidationComplete(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
+ "Invalid SNI name");
+ return;
+ }
+
+ client_hello.GetStringPiece(kUAID, &info->user_agent_id);
+
+ if (!requested_config.get()) {
+ StringPiece requested_scid;
+ if (client_hello.GetStringPiece(kSCID, &requested_scid)) {
+ info->reject_reasons.push_back(SERVER_CONFIG_UNKNOWN_CONFIG_FAILURE);
+ } else {
+ info->reject_reasons.push_back(SERVER_CONFIG_INCHOATE_HELLO_FAILURE);
+ }
+ // No server config with the requested ID.
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
+ HandshakeFailureReason source_address_token_error;
+ StringPiece srct;
+ if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct)) {
+ source_address_token_error = ValidateSourceAddressToken(
+ *requested_config.get(), srct, info->client_ip, info->now);
+ info->valid_source_address_token =
+ (source_address_token_error == HANDSHAKE_OK);
+ } else {
+ source_address_token_error = SOURCE_ADDRESS_TOKEN_INVALID_FAILURE;
+ }
+
+ bool found_error = false;
+ if (source_address_token_error != HANDSHAKE_OK) {
+ info->reject_reasons.push_back(source_address_token_error);
+ // No valid source address token.
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+ found_error = true;
+ }
+
+ if (client_hello.GetStringPiece(kNONC, &info->client_nonce) &&
+ info->client_nonce.size() == kNonceSize) {
+ info->client_nonce_well_formed = true;
+ } else {
+ info->reject_reasons.push_back(CLIENT_NONCE_INVALID_FAILURE);
+ // Invalid client nonce.
+ DVLOG(1) << "Invalid client nonce.";
+ if (FLAGS_use_early_return_when_verifying_chlo) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+ found_error = true;
+ }
+
+ if (!replay_protection_) {
+ if (!found_error) {
+ info->unique = true;
+ }
+ DVLOG(1) << "No replay protection.";
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
+ client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce);
+ if (!info->server_nonce.empty()) {
+ // If the server nonce is present, use it to establish uniqueness.
+ HandshakeFailureReason server_nonce_error =
+ ValidateServerNonce(info->server_nonce, info->now);
+ if (server_nonce_error == HANDSHAKE_OK) {
+ info->unique = true;
+ } else {
+ info->reject_reasons.push_back(server_nonce_error);
+ info->unique = false;
+ }
+ DVLOG(1) << "Using server nonce, unique: " << info->unique;
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
+ // We want to contact strike register only if there are no errors because it
+ // is a RPC call and is expensive.
+ if (found_error) {
+ helper.ValidationComplete(QUIC_NO_ERROR, "");
+ return;
+ }
+
+ // Use the client nonce to establish uniqueness.
+ StrikeRegisterClient* strike_register_client;
+ {
+ base::AutoLock locked(strike_register_client_lock_);
+
+ if (strike_register_client_.get() == nullptr) {
+ strike_register_client_.reset(new LocalStrikeRegisterClient(
+ strike_register_max_entries_,
+ static_cast<uint32>(info->now.ToUNIXSeconds()),
+ strike_register_window_secs_,
+ primary_orbit,
+ strike_register_no_startup_period_ ?
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED :
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ }
+ strike_register_client = strike_register_client_.get();
+ }
+
+ strike_register_client->VerifyNonceIsValidAndUnique(
+ info->client_nonce,
+ info->now,
+ new VerifyNonceIsValidAndUniqueCallback(client_hello_state, done_cb));
+ helper.StartedAsyncCallback();
+}
+
+bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage(
+ const IPEndPoint& client_ip,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ const QuicCryptoNegotiatedParameters& params,
+ const CachedNetworkParameters* cached_network_params,
+ CryptoHandshakeMessage* out) const {
+ base::AutoLock locked(configs_lock_);
+ out->set_tag(kSCUP);
+ out->SetStringPiece(kSCFG, primary_config_->serialized);
+ out->SetStringPiece(kSourceAddressTokenTag,
+ NewSourceAddressToken(*primary_config_.get(),
+ client_ip,
+ rand,
+ clock->WallNow(),
+ cached_network_params));
+
+ if (proof_source_ == nullptr) {
+ // Insecure QUIC, can send SCFG without proof.
+ return true;
+ }
+
+ const vector<string>* certs;
+ string signature;
+ if (!proof_source_->GetProof(params.sni, primary_config_->serialized,
+ params.x509_ecdsa_supported, &certs,
+ &signature)) {
+ DVLOG(1) << "Server: failed to get proof.";
+ return false;
+ }
+
+ const string compressed = CertCompressor::CompressChain(
+ *certs, params.client_common_set_hashes, params.client_cached_cert_hashes,
+ primary_config_->common_cert_sets);
+
+ out->SetStringPiece(kCertificateTag, compressed);
+ out->SetStringPiece(kPROF, signature);
+ return true;
+}
+
+void QuicCryptoServerConfig::BuildRejection(
+ const Config& config,
+ const CryptoHandshakeMessage& client_hello,
+ const ClientHelloInfo& info,
+ QuicRandom* rand,
+ QuicCryptoNegotiatedParameters *params,
+ CryptoHandshakeMessage* out) const {
+ out->set_tag(kREJ);
+ out->SetStringPiece(kSCFG, config.serialized);
+ out->SetStringPiece(kSourceAddressTokenTag,
+ NewSourceAddressToken(
+ config,
+ info.client_ip,
+ rand,
+ info.now,
+ nullptr));
+ if (replay_protection_) {
+ out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now));
+ }
+
+ if (FLAGS_send_quic_crypto_reject_reason) {
+ // Send client the reject reason for debugging purposes.
+ DCHECK_LT(0u, info.reject_reasons.size());
+ out->SetVector(kRREJ, info.reject_reasons);
+ }
+
+ // The client may have requested a certificate chain.
+ const QuicTag* their_proof_demands;
+ size_t num_their_proof_demands;
+
+ if (proof_source_.get() == nullptr ||
+ client_hello.GetTaglist(kPDMD, &their_proof_demands,
+ &num_their_proof_demands) !=
+ QUIC_NO_ERROR) {
+ return;
+ }
+
+ bool x509_supported = false;
+ for (size_t i = 0; i < num_their_proof_demands; i++) {
+ switch (their_proof_demands[i]) {
+ case kX509:
+ x509_supported = true;
+ params->x509_ecdsa_supported = true;
+ break;
+ case kX59R:
+ x509_supported = true;
+ break;
+ }
+ }
+
+ if (!x509_supported) {
+ return;
+ }
+
+ const vector<string>* certs;
+ string signature;
+ if (!proof_source_->GetProof(info.sni.as_string(), config.serialized,
+ params->x509_ecdsa_supported, &certs,
+ &signature)) {
+ return;
+ }
+
+ StringPiece client_common_set_hashes;
+ if (client_hello.GetStringPiece(kCCS, &client_common_set_hashes)) {
+ params->client_common_set_hashes = client_common_set_hashes.as_string();
+ }
+
+ StringPiece client_cached_cert_hashes;
+ if (client_hello.GetStringPiece(kCCRT, &client_cached_cert_hashes)) {
+ params->client_cached_cert_hashes = client_cached_cert_hashes.as_string();
+ }
+
+ const string compressed = CertCompressor::CompressChain(
+ *certs, params->client_common_set_hashes,
+ params->client_cached_cert_hashes, config.common_cert_sets);
+
+ // kREJOverheadBytes is a very rough estimate of how much of a REJ
+ // message is taken up by things other than the certificates.
+ // STK: 56 bytes
+ // SNO: 56 bytes
+ // SCFG
+ // SCID: 16 bytes
+ // PUBS: 38 bytes
+ const size_t kREJOverheadBytes = 166;
+ // kMultiplier is the multiple of the CHLO message size that a REJ message
+ // must stay under when the client doesn't present a valid source-address
+ // token.
+ const size_t kMultiplier = 2;
+ // max_unverified_size is the number of bytes that the certificate chain
+ // and signature can consume before we will demand a valid source-address
+ // token.
+ const size_t max_unverified_size =
+ client_hello.size() * kMultiplier - kREJOverheadBytes;
+ COMPILE_ASSERT(kClientHelloMinimumSize * kMultiplier >= kREJOverheadBytes,
+ overhead_calculation_may_underflow);
+ if (info.valid_source_address_token ||
+ signature.size() + compressed.size() < max_unverified_size) {
+ out->SetStringPiece(kCertificateTag, compressed);
+ out->SetStringPiece(kPROF, signature);
+ }
+}
+
+scoped_refptr<QuicCryptoServerConfig::Config>
+QuicCryptoServerConfig::ParseConfigProtobuf(
+ QuicServerConfigProtobuf* protobuf) {
+ scoped_ptr<CryptoHandshakeMessage> msg(
+ CryptoFramer::ParseMessage(protobuf->config()));
+
+ if (msg->tag() != kSCFG) {
+ LOG(WARNING) << "Server config message has tag " << msg->tag()
+ << " expected " << kSCFG;
+ return nullptr;
+ }
+
+ scoped_refptr<Config> config(new Config);
+ config->serialized = protobuf->config();
+
+ if (!protobuf->has_source_address_token_secret_override()) {
+ // Use the default boxer.
+ config->source_address_token_boxer = &default_source_address_token_boxer_;
+ } else {
+ // Create override boxer instance.
+ CryptoSecretBoxer* boxer = new CryptoSecretBoxer;
+ boxer->SetKey(DeriveSourceAddressTokenKey(
+ protobuf->source_address_token_secret_override()));
+ config->source_address_token_boxer_storage.reset(boxer);
+ config->source_address_token_boxer = boxer;
+ }
+
+ if (protobuf->has_primary_time()) {
+ config->primary_time =
+ QuicWallTime::FromUNIXSeconds(protobuf->primary_time());
+ }
+
+ config->priority = protobuf->priority();
+
+ StringPiece scid;
+ if (!msg->GetStringPiece(kSCID, &scid)) {
+ LOG(WARNING) << "Server config message is missing SCID";
+ return nullptr;
+ }
+ config->id = scid.as_string();
+
+ const QuicTag* aead_tags;
+ size_t aead_len;
+ if (msg->GetTaglist(kAEAD, &aead_tags, &aead_len) != QUIC_NO_ERROR) {
+ LOG(WARNING) << "Server config message is missing AEAD";
+ return nullptr;
+ }
+ config->aead = vector<QuicTag>(aead_tags, aead_tags + aead_len);
+
+ const QuicTag* kexs_tags;
+ size_t kexs_len;
+ if (msg->GetTaglist(kKEXS, &kexs_tags, &kexs_len) != QUIC_NO_ERROR) {
+ LOG(WARNING) << "Server config message is missing KEXS";
+ return nullptr;
+ }
+
+ StringPiece orbit;
+ if (!msg->GetStringPiece(kORBT, &orbit)) {
+ LOG(WARNING) << "Server config message is missing ORBT";
+ return nullptr;
+ }
+
+ if (orbit.size() != kOrbitSize) {
+ LOG(WARNING) << "Orbit value in server config is the wrong length."
+ " Got " << orbit.size() << " want " << kOrbitSize;
+ return nullptr;
+ }
+ COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size);
+ memcpy(config->orbit, orbit.data(), sizeof(config->orbit));
+
+ {
+ StrikeRegisterClient* strike_register_client;
+ {
+ base::AutoLock locked(strike_register_client_lock_);
+ strike_register_client = strike_register_client_.get();
+ }
+
+ if (strike_register_client != nullptr &&
+ !strike_register_client->IsKnownOrbit(orbit)) {
+ LOG(WARNING)
+ << "Rejecting server config with orbit that the strike register "
+ "client doesn't know about.";
+ return nullptr;
+ }
+ }
+
+ if (kexs_len != protobuf->key_size()) {
+ LOG(WARNING) << "Server config has " << kexs_len
+ << " key exchange methods configured, but "
+ << protobuf->key_size() << " private keys";
+ return nullptr;
+ }
+
+ const QuicTag* proof_demand_tags;
+ size_t num_proof_demand_tags;
+ if (msg->GetTaglist(kPDMD, &proof_demand_tags, &num_proof_demand_tags) ==
+ QUIC_NO_ERROR) {
+ for (size_t i = 0; i < num_proof_demand_tags; i++) {
+ if (proof_demand_tags[i] == kCHID) {
+ config->channel_id_enabled = true;
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < kexs_len; i++) {
+ const QuicTag tag = kexs_tags[i];
+ string private_key;
+
+ config->kexs.push_back(tag);
+
+ for (size_t j = 0; j < protobuf->key_size(); j++) {
+ const QuicServerConfigProtobuf::PrivateKey& key = protobuf->key(i);
+ if (key.tag() == tag) {
+ private_key = key.private_key();
+ break;
+ }
+ }
+
+ if (private_key.empty()) {
+ LOG(WARNING) << "Server config contains key exchange method without "
+ "corresponding private key: " << tag;
+ return nullptr;
+ }
+
+ scoped_ptr<KeyExchange> ka;
+ switch (tag) {
+ case kC255:
+ ka.reset(Curve25519KeyExchange::New(private_key));
+ if (!ka.get()) {
+ LOG(WARNING) << "Server config contained an invalid curve25519"
+ " private key.";
+ return nullptr;
+ }
+ break;
+ case kP256:
+ ka.reset(P256KeyExchange::New(private_key));
+ if (!ka.get()) {
+ LOG(WARNING) << "Server config contained an invalid P-256"
+ " private key.";
+ return nullptr;
+ }
+ break;
+ default:
+ LOG(WARNING) << "Server config message contains unknown key exchange "
+ "method: " << tag;
+ return nullptr;
+ }
+
+ for (vector<KeyExchange*>::const_iterator i = config->key_exchanges.begin();
+ i != config->key_exchanges.end(); ++i) {
+ if ((*i)->tag() == tag) {
+ LOG(WARNING) << "Duplicate key exchange in config: " << tag;
+ return nullptr;
+ }
+ }
+
+ config->key_exchanges.push_back(ka.release());
+ }
+
+ return config;
+}
+
+void QuicCryptoServerConfig::SetProofSource(ProofSource* proof_source) {
+ proof_source_.reset(proof_source);
+}
+
+void QuicCryptoServerConfig::SetEphemeralKeySource(
+ EphemeralKeySource* ephemeral_key_source) {
+ ephemeral_key_source_.reset(ephemeral_key_source);
+}
+
+void QuicCryptoServerConfig::SetStrikeRegisterClient(
+ StrikeRegisterClient* strike_register_client) {
+ base::AutoLock locker(strike_register_client_lock_);
+ DCHECK(!strike_register_client_.get());
+ strike_register_client_.reset(strike_register_client);
+}
+
+void QuicCryptoServerConfig::set_replay_protection(bool on) {
+ replay_protection_ = on;
+}
+
+void QuicCryptoServerConfig::set_strike_register_no_startup_period() {
+ base::AutoLock locker(strike_register_client_lock_);
+ DCHECK(!strike_register_client_.get());
+ strike_register_no_startup_period_ = true;
+}
+
+void QuicCryptoServerConfig::set_strike_register_max_entries(
+ uint32 max_entries) {
+ base::AutoLock locker(strike_register_client_lock_);
+ DCHECK(!strike_register_client_.get());
+ strike_register_max_entries_ = max_entries;
+}
+
+void QuicCryptoServerConfig::set_strike_register_window_secs(
+ uint32 window_secs) {
+ base::AutoLock locker(strike_register_client_lock_);
+ DCHECK(!strike_register_client_.get());
+ strike_register_window_secs_ = window_secs;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_future_secs(
+ uint32 future_secs) {
+ source_address_token_future_secs_ = future_secs;
+}
+
+void QuicCryptoServerConfig::set_source_address_token_lifetime_secs(
+ uint32 lifetime_secs) {
+ source_address_token_lifetime_secs_ = lifetime_secs;
+}
+
+void QuicCryptoServerConfig::set_server_nonce_strike_register_max_entries(
+ uint32 max_entries) {
+ DCHECK(!server_nonce_strike_register_.get());
+ server_nonce_strike_register_max_entries_ = max_entries;
+}
+
+void QuicCryptoServerConfig::set_server_nonce_strike_register_window_secs(
+ uint32 window_secs) {
+ DCHECK(!server_nonce_strike_register_.get());
+ server_nonce_strike_register_window_secs_ = window_secs;
+}
+
+void QuicCryptoServerConfig::AcquirePrimaryConfigChangedCb(
+ PrimaryConfigChangedCallback* cb) {
+ base::AutoLock locked(configs_lock_);
+ primary_config_changed_cb_.reset(cb);
+}
+
+string QuicCryptoServerConfig::NewSourceAddressToken(
+ const Config& config,
+ const IPEndPoint& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ const CachedNetworkParameters* cached_network_params) const {
+ IPAddressNumber ip_address = ip.address();
+ if (ip.GetSockAddrFamily() == AF_INET) {
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address);
+ }
+ SourceAddressToken source_address_token;
+ source_address_token.set_ip(IPAddressToPackedString(ip_address));
+ source_address_token.set_timestamp(now.ToUNIXSeconds());
+ if (cached_network_params != nullptr) {
+ source_address_token.set_cached_network_parameters(*cached_network_params);
+ }
+
+ return config.source_address_token_boxer->Box(
+ rand, source_address_token.SerializeAsString());
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ValidateSourceAddressToken(
+ const Config& config,
+ StringPiece token,
+ const IPEndPoint& ip,
+ QuicWallTime now) const {
+ string storage;
+ StringPiece plaintext;
+ if (!config.source_address_token_boxer->Unbox(token, &storage, &plaintext)) {
+ return SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE;
+ }
+
+ SourceAddressToken source_address_token;
+ if (!source_address_token.ParseFromArray(plaintext.data(),
+ plaintext.size())) {
+ return SOURCE_ADDRESS_TOKEN_PARSE_FAILURE;
+ }
+
+ IPAddressNumber ip_address = ip.address();
+ if (ip.GetSockAddrFamily() == AF_INET) {
+ ip_address = ConvertIPv4NumberToIPv6Number(ip_address);
+ }
+ if (source_address_token.ip() != IPAddressToPackedString(ip_address)) {
+ // It's for a different IP address.
+ return SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE;
+ }
+
+ const QuicWallTime timestamp(
+ QuicWallTime::FromUNIXSeconds(source_address_token.timestamp()));
+ const QuicTime::Delta delta(now.AbsoluteDifference(timestamp));
+
+ if (now.IsBefore(timestamp) &&
+ delta.ToSeconds() > source_address_token_future_secs_) {
+ return SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE;
+ }
+
+ if (now.IsAfter(timestamp) &&
+ delta.ToSeconds() > source_address_token_lifetime_secs_) {
+ return SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE;
+ }
+
+ return HANDSHAKE_OK;
+}
+
+// kServerNoncePlaintextSize is the number of bytes in an unencrypted server
+// nonce.
+static const size_t kServerNoncePlaintextSize =
+ 4 /* timestamp */ + 20 /* random bytes */;
+
+string QuicCryptoServerConfig::NewServerNonce(QuicRandom* rand,
+ QuicWallTime now) const {
+ const uint32 timestamp = static_cast<uint32>(now.ToUNIXSeconds());
+
+ uint8 server_nonce[kServerNoncePlaintextSize];
+ COMPILE_ASSERT(sizeof(server_nonce) > sizeof(timestamp), nonce_too_small);
+ server_nonce[0] = static_cast<uint8>(timestamp >> 24);
+ server_nonce[1] = static_cast<uint8>(timestamp >> 16);
+ server_nonce[2] = static_cast<uint8>(timestamp >> 8);
+ server_nonce[3] = static_cast<uint8>(timestamp);
+ rand->RandBytes(&server_nonce[sizeof(timestamp)],
+ sizeof(server_nonce) - sizeof(timestamp));
+
+ return server_nonce_boxer_.Box(
+ rand,
+ StringPiece(reinterpret_cast<char*>(server_nonce), sizeof(server_nonce)));
+}
+
+HandshakeFailureReason QuicCryptoServerConfig::ValidateServerNonce(
+ StringPiece token,
+ QuicWallTime now) const {
+ string storage;
+ StringPiece plaintext;
+ if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) {
+ return SERVER_NONCE_DECRYPTION_FAILURE;
+ }
+
+ // plaintext contains:
+ // uint32 timestamp
+ // uint8[20] random bytes
+
+ if (plaintext.size() != kServerNoncePlaintextSize) {
+ // This should never happen because the value decrypted correctly.
+ LOG(DFATAL) << "Seemingly valid server nonce had incorrect length.";
+ return SERVER_NONCE_INVALID_FAILURE;
+ }
+
+ uint8 server_nonce[32];
+ memcpy(server_nonce, plaintext.data(), 4);
+ memcpy(server_nonce + 4, server_nonce_orbit_, sizeof(server_nonce_orbit_));
+ memcpy(server_nonce + 4 + sizeof(server_nonce_orbit_), plaintext.data() + 4,
+ 20);
+ COMPILE_ASSERT(4 + sizeof(server_nonce_orbit_) + 20 == sizeof(server_nonce),
+ bad_nonce_buffer_length);
+
+ InsertStatus nonce_error;
+ {
+ base::AutoLock auto_lock(server_nonce_strike_register_lock_);
+ if (server_nonce_strike_register_.get() == nullptr) {
+ server_nonce_strike_register_.reset(new StrikeRegister(
+ server_nonce_strike_register_max_entries_,
+ static_cast<uint32>(now.ToUNIXSeconds()),
+ server_nonce_strike_register_window_secs_, server_nonce_orbit_,
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED));
+ }
+ nonce_error = server_nonce_strike_register_->Insert(
+ server_nonce, static_cast<uint32>(now.ToUNIXSeconds()));
+ }
+
+ switch (nonce_error) {
+ case NONCE_OK:
+ return HANDSHAKE_OK;
+ case NONCE_INVALID_FAILURE:
+ case NONCE_INVALID_ORBIT_FAILURE:
+ return SERVER_NONCE_INVALID_FAILURE;
+ case NONCE_NOT_UNIQUE_FAILURE:
+ return SERVER_NONCE_NOT_UNIQUE_FAILURE;
+ case NONCE_INVALID_TIME_FAILURE:
+ return SERVER_NONCE_INVALID_TIME_FAILURE;
+ case NONCE_UNKNOWN_FAILURE:
+ case STRIKE_REGISTER_TIMEOUT:
+ case STRIKE_REGISTER_FAILURE:
+ default:
+ LOG(DFATAL) << "Unexpected server nonce error: " << nonce_error;
+ return SERVER_NONCE_NOT_UNIQUE_FAILURE;
+ }
+}
+
+QuicCryptoServerConfig::Config::Config()
+ : channel_id_enabled(false),
+ is_primary(false),
+ primary_time(QuicWallTime::Zero()),
+ priority(0),
+ source_address_token_boxer(nullptr) {}
+
+QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); }
+
+} // namespace net
diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h
new file mode 100644
index 0000000..ef8f6e9
--- /dev/null
+++ b/net/quic/crypto/quic_crypto_server_config.h
@@ -0,0 +1,497 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
+#define NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/synchronization/lock.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_secret_boxer.h"
+#include "net/quic/crypto/source_address_token.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class CryptoHandshakeMessage;
+class EphemeralKeySource;
+class KeyExchange;
+class ProofSource;
+class QuicClock;
+class QuicDecrypter;
+class QuicEncrypter;
+class QuicRandom;
+class QuicServerConfigProtobuf;
+class StrikeRegister;
+class StrikeRegisterClient;
+
+struct ClientHelloInfo;
+
+namespace test {
+class QuicCryptoServerConfigPeer;
+} // namespace test
+
+// Hook that allows application code to subscribe to primary config changes.
+class PrimaryConfigChangedCallback {
+ public:
+ PrimaryConfigChangedCallback();
+ virtual ~PrimaryConfigChangedCallback();
+ virtual void Run(const std::string& scid) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrimaryConfigChangedCallback);
+};
+
+// Callback used to accept the result of the |client_hello| validation step.
+class NET_EXPORT_PRIVATE ValidateClientHelloResultCallback {
+ public:
+ // Opaque token that holds information about the client_hello and
+ // its validity. Can be interpreted by calling ProcessClientHello.
+ struct Result;
+
+ ValidateClientHelloResultCallback();
+ virtual ~ValidateClientHelloResultCallback();
+ void Run(const Result* result);
+
+ protected:
+ virtual void RunImpl(const CryptoHandshakeMessage& client_hello,
+ const Result& result) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ValidateClientHelloResultCallback);
+};
+
+// QuicCryptoServerConfig contains the crypto configuration of a QUIC server.
+// Unlike a client, a QUIC server can have multiple configurations active in
+// order to support clients resuming with a previous configuration.
+// TODO(agl): when adding configurations at runtime is added, this object will
+// need to consider locking.
+class NET_EXPORT_PRIVATE QuicCryptoServerConfig {
+ public:
+ // ConfigOptions contains options for generating server configs.
+ struct NET_EXPORT_PRIVATE ConfigOptions {
+ ConfigOptions();
+
+ // expiry_time is the time, in UNIX seconds, when the server config will
+ // expire. If unset, it defaults to the current time plus six months.
+ QuicWallTime expiry_time;
+ // channel_id_enabled controls whether the server config will indicate
+ // support for ChannelIDs.
+ bool channel_id_enabled;
+ // id contains the server config id for the resulting config. If empty, a
+ // random id is generated.
+ std::string id;
+ // orbit contains the kOrbitSize bytes of the orbit value for the server
+ // config. If |orbit| is empty then a random orbit is generated.
+ std::string orbit;
+ // p256 determines whether a P-256 public key will be included in the
+ // server config. Note that this breaks deterministic server-config
+ // generation since P-256 key generation doesn't use the QuicRandom given
+ // to DefaultConfig().
+ bool p256;
+ };
+
+ // |source_address_token_secret|: secret key material used for encrypting and
+ // decrypting source address tokens. It can be of any length as it is fed
+ // into a KDF before use. In tests, use TESTING.
+ // |server_nonce_entropy|: an entropy source used to generate the orbit and
+ // key for server nonces, which are always local to a given instance of a
+ // server.
+ QuicCryptoServerConfig(base::StringPiece source_address_token_secret,
+ QuicRandom* server_nonce_entropy);
+ ~QuicCryptoServerConfig();
+
+ // TESTING is a magic parameter for passing to the constructor in tests.
+ static const char TESTING[];
+
+ // Generates a QuicServerConfigProtobuf protobuf suitable for
+ // AddConfig and SetConfigs.
+ static QuicServerConfigProtobuf* GenerateConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options);
+
+ // AddConfig adds a QuicServerConfigProtobuf to the availible configurations.
+ // It returns the SCFG message from the config if successful. The caller
+ // takes ownership of the CryptoHandshakeMessage. |now| is used in
+ // conjunction with |protobuf->primary_time()| to determine whether the
+ // config should be made primary.
+ CryptoHandshakeMessage* AddConfig(QuicServerConfigProtobuf* protobuf,
+ QuicWallTime now);
+
+ // AddDefaultConfig calls DefaultConfig to create a config and then calls
+ // AddConfig to add it. See the comment for |DefaultConfig| for details of
+ // the arguments.
+ CryptoHandshakeMessage* AddDefaultConfig(
+ QuicRandom* rand,
+ const QuicClock* clock,
+ const ConfigOptions& options);
+
+ // SetConfigs takes a vector of config protobufs and the current time.
+ // Configs are assumed to be uniquely identified by their server config ID.
+ // Previously unknown configs are added and possibly made the primary config
+ // depending on their |primary_time| and the value of |now|. Configs that are
+ // known, but are missing from the protobufs are deleted, unless they are
+ // currently the primary config. SetConfigs returns false if any errors were
+ // encountered and no changes to the QuicCryptoServerConfig will occur.
+ bool SetConfigs(const std::vector<QuicServerConfigProtobuf*>& protobufs,
+ QuicWallTime now);
+
+ // Get the server config ids for all known configs.
+ void GetConfigIds(std::vector<std::string>* scids) const;
+
+ // Checks |client_hello| for gross errors and determines whether it
+ // can be shown to be fresh (i.e. not a replay). The result of the
+ // validation step must be interpreted by calling
+ // QuicCryptoServerConfig::ProcessClientHello from the done_cb.
+ //
+ // ValidateClientHello may invoke the done_cb before unrolling the
+ // stack if it is able to assess the validity of the client_nonce
+ // without asynchronous operations.
+ //
+ // client_hello: the incoming client hello message.
+ // client_ip: the IP address of the client, which is used to generate and
+ // validate source-address tokens.
+ // clock: used to validate client nonces and ephemeral keys.
+ // done_cb: single-use callback that accepts an opaque
+ // ValidatedClientHelloMsg token that holds information about
+ // the client hello. The callback will always be called exactly
+ // once, either under the current call stack, or after the
+ // completion of an asynchronous operation.
+ void ValidateClientHello(
+ const CryptoHandshakeMessage& client_hello,
+ IPEndPoint client_ip,
+ const QuicClock* clock,
+ ValidateClientHelloResultCallback* done_cb) const;
+
+ // ProcessClientHello processes |client_hello| and decides whether to accept
+ // or reject the connection. If the connection is to be accepted, |out| is
+ // set to the contents of the ServerHello, |out_params| is completed and
+ // QUIC_NO_ERROR is returned. Otherwise |out| is set to be a REJ message and
+ // an error code is returned.
+ //
+ // validate_chlo_result: Output from the asynchronous call to
+ // ValidateClientHello. Contains the client hello message and
+ // information about it.
+ // connection_id: the ConnectionId for the connection, which is used in key
+ // derivation.
+ // client_address: the IP address and port of the client. The IP address is
+ // used to generate and validate source-address tokens.
+ // version: version of the QUIC protocol in use for this connection
+ // supported_versions: versions of the QUIC protocol that this server
+ // supports.
+ // initial_flow_control_window: size of initial flow control window this
+ // server uses for new streams.
+ // clock: used to validate client nonces and ephemeral keys.
+ // rand: an entropy source
+ // params: the state of the handshake. This may be updated with a server
+ // nonce when we send a rejection. After a successful handshake, this will
+ // contain the state of the connection.
+ // out: the resulting handshake message (either REJ or SHLO)
+ // error_details: used to store a string describing any error.
+ QuicErrorCode ProcessClientHello(
+ const ValidateClientHelloResultCallback::Result& validate_chlo_result,
+ QuicConnectionId connection_id,
+ IPEndPoint client_address,
+ QuicVersion version,
+ const QuicVersionVector& supported_versions,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicCryptoNegotiatedParameters* params,
+ CryptoHandshakeMessage* out,
+ std::string* error_details) const;
+
+ // BuildServerConfigUpdateMessage sets |out| to be a SCUP message containing
+ // the current primary config, an up to date source-address token, and cert
+ // chain and proof in the case of secure QUIC. Returns true if successfully
+ // filled |out|.
+ //
+ // |cached_network_params| is optional, and can be nullptr.
+ bool BuildServerConfigUpdateMessage(
+ const IPEndPoint& client_ip,
+ const QuicClock* clock,
+ QuicRandom* rand,
+ const QuicCryptoNegotiatedParameters& params,
+ const CachedNetworkParameters* cached_network_params,
+ CryptoHandshakeMessage* out) const;
+
+ // SetProofSource installs |proof_source| as the ProofSource for handshakes.
+ // This object takes ownership of |proof_source|.
+ void SetProofSource(ProofSource* proof_source);
+
+ // SetEphemeralKeySource installs an object that can cache ephemeral keys for
+ // a short period of time. This object takes ownership of
+ // |ephemeral_key_source|. If not set then ephemeral keys will be generated
+ // per-connection.
+ void SetEphemeralKeySource(EphemeralKeySource* ephemeral_key_source);
+
+ // Install an externall created StrikeRegisterClient for use to
+ // interact with the strike register. This object takes ownership
+ // of the |strike_register_client|.
+ void SetStrikeRegisterClient(StrikeRegisterClient* strike_register_client);
+
+ // set_replay_protection controls whether replay protection is enabled. If
+ // replay protection is disabled then no strike registers are needed and
+ // frontends can share an orbit value without a shared strike-register.
+ // However, an attacker can duplicate a handshake and cause a client's
+ // request to be processed twice.
+ void set_replay_protection(bool on);
+
+ // set_strike_register_no_startup_period configures the strike register to
+ // not have a startup period.
+ void set_strike_register_no_startup_period();
+
+ // set_strike_register_max_entries sets the maximum number of entries that
+ // the internal strike register will hold. If the strike register fills up
+ // then the oldest entries (by the client's clock) will be dropped.
+ void set_strike_register_max_entries(uint32 max_entries);
+
+ // set_strike_register_window_secs sets the number of seconds around the
+ // current time that the strike register will attempt to be authoritative
+ // for. Setting a larger value allows for greater client clock-skew, but
+ // means that the quiescent startup period must be longer.
+ void set_strike_register_window_secs(uint32 window_secs);
+
+ // set_source_address_token_future_secs sets the number of seconds into the
+ // future that source-address tokens will be accepted from. Since
+ // source-address tokens are authenticated, this should only happen if
+ // another, valid server has clock-skew.
+ void set_source_address_token_future_secs(uint32 future_secs);
+
+ // set_source_address_token_lifetime_secs sets the number of seconds that a
+ // source-address token will be valid for.
+ void set_source_address_token_lifetime_secs(uint32 lifetime_secs);
+
+ // set_server_nonce_strike_register_max_entries sets the number of entries in
+ // the server-nonce strike-register. This is used to record that server nonce
+ // values have been used. If the number of entries is too small then clients
+ // which are depending on server nonces may fail to handshake because their
+ // nonce has expired in the amount of time it took to go from the server to
+ // the client and back.
+ void set_server_nonce_strike_register_max_entries(uint32 max_entries);
+
+ // set_server_nonce_strike_register_window_secs sets the number of seconds
+ // around the current time that the server-nonce strike-register will accept
+ // nonces from. Setting a larger value allows for clients to delay follow-up
+ // client hellos for longer and still use server nonces as proofs of
+ // uniqueness.
+ void set_server_nonce_strike_register_window_secs(uint32 window_secs);
+
+ // Set and take ownership of the callback to invoke on primary config changes.
+ void AcquirePrimaryConfigChangedCb(PrimaryConfigChangedCallback* cb);
+
+ private:
+ friend class test::QuicCryptoServerConfigPeer;
+
+ // Config represents a server config: a collection of preferences and
+ // Diffie-Hellman public values.
+ class NET_EXPORT_PRIVATE Config : public QuicCryptoConfig,
+ public base::RefCounted<Config> {
+ public:
+ Config();
+
+ // TODO(rtenneti): since this is a class, we should probably do
+ // getters/setters here.
+ // |serialized| contains the bytes of this server config, suitable for
+ // sending on the wire.
+ std::string serialized;
+ // id contains the SCID of this server config.
+ std::string id;
+ // orbit contains the orbit value for this config: an opaque identifier
+ // used to identify clusters of server frontends.
+ unsigned char orbit[kOrbitSize];
+
+ // key_exchanges contains key exchange objects with the private keys
+ // already loaded. The values correspond, one-to-one, with the tags in
+ // |kexs| from the parent class.
+ std::vector<KeyExchange*> key_exchanges;
+
+ // tag_value_map contains the raw key/value pairs for the config.
+ QuicTagValueMap tag_value_map;
+
+ // channel_id_enabled is true if the config in |serialized| specifies that
+ // ChannelIDs are supported.
+ bool channel_id_enabled;
+
+ // is_primary is true if this config is the one that we'll give out to
+ // clients as the current one.
+ bool is_primary;
+
+ // primary_time contains the timestamp when this config should become the
+ // primary config. A value of QuicWallTime::Zero() means that this config
+ // will not be promoted at a specific time.
+ QuicWallTime primary_time;
+
+ // Secondary sort key for use when selecting primary configs and
+ // there are multiple configs with the same primary time.
+ // Smaller numbers mean higher priority.
+ uint64 priority;
+
+ // source_address_token_boxer_ is used to protect the
+ // source-address tokens that are given to clients.
+ // Points to either source_address_token_boxer_storage or the
+ // default boxer provided by QuicCryptoServerConfig.
+ const CryptoSecretBoxer* source_address_token_boxer;
+
+ // Holds the override source_address_token_boxer instance if the
+ // Config is not using the default source address token boxer
+ // instance provided by QuicCryptoServerConfig.
+ scoped_ptr<CryptoSecretBoxer> source_address_token_boxer_storage;
+
+ private:
+ friend class base::RefCounted<Config>;
+
+ virtual ~Config();
+
+ DISALLOW_COPY_AND_ASSIGN(Config);
+ };
+
+ typedef std::map<ServerConfigID, scoped_refptr<Config> > ConfigMap;
+
+ // Get a ref to the config with a given server config id.
+ scoped_refptr<Config> GetConfigWithScid(
+ base::StringPiece requested_scid) const;
+
+ // ConfigPrimaryTimeLessThan returns true if a->primary_time <
+ // b->primary_time.
+ static bool ConfigPrimaryTimeLessThan(const scoped_refptr<Config>& a,
+ const scoped_refptr<Config>& b);
+
+ // SelectNewPrimaryConfig reevaluates the primary config based on the
+ // "primary_time" deadlines contained in each.
+ void SelectNewPrimaryConfig(QuicWallTime now) const;
+
+ // EvaluateClientHello checks |client_hello| for gross errors and determines
+ // whether it can be shown to be fresh (i.e. not a replay). The results are
+ // written to |info|.
+ void EvaluateClientHello(
+ const uint8* primary_orbit,
+ scoped_refptr<Config> requested_config,
+ ValidateClientHelloResultCallback::Result* client_hello_state,
+ ValidateClientHelloResultCallback* done_cb) const;
+
+ // BuildRejection sets |out| to be a REJ message in reply to |client_hello|.
+ void BuildRejection(
+ const Config& config,
+ const CryptoHandshakeMessage& client_hello,
+ const ClientHelloInfo& info,
+ QuicRandom* rand,
+ QuicCryptoNegotiatedParameters *params,
+ CryptoHandshakeMessage* out) const;
+
+ // ParseConfigProtobuf parses the given config protobuf and returns a
+ // scoped_refptr<Config> if successful. The caller adopts the reference to the
+ // Config. On error, ParseConfigProtobuf returns nullptr.
+ scoped_refptr<Config> ParseConfigProtobuf(QuicServerConfigProtobuf* protobuf);
+
+ // NewSourceAddressToken returns a fresh source address token for the given
+ // IP address. |cached_network_params| is optional, and can be nullptr.
+ std::string NewSourceAddressToken(
+ const Config& config,
+ const IPEndPoint& ip,
+ QuicRandom* rand,
+ QuicWallTime now,
+ const CachedNetworkParameters* cached_network_params) const;
+
+ // ValidateSourceAddressToken returns HANDSHAKE_OK if the source address token
+ // in |token| is a valid and timely token for the IP address |ip| given that
+ // the current time is |now|. Otherwise it returns the reason for failure.
+ HandshakeFailureReason ValidateSourceAddressToken(const Config& config,
+ base::StringPiece token,
+ const IPEndPoint& ip,
+ QuicWallTime now) const;
+
+ // NewServerNonce generates and encrypts a random nonce.
+ std::string NewServerNonce(QuicRandom* rand, QuicWallTime now) const;
+
+ // ValidateServerNonce decrypts |token| and verifies that it hasn't been
+ // previously used and is recent enough that it is plausible that it was part
+ // of a very recently provided rejection ("recent" will be on the order of
+ // 10-30 seconds). If so, it records that it has been used and returns
+ // HANDSHAKE_OK. Otherwise it returns the reason for failure.
+ HandshakeFailureReason ValidateServerNonce(
+ base::StringPiece echoed_server_nonce,
+ QuicWallTime now) const;
+
+ // replay_protection_ controls whether the server enforces that handshakes
+ // aren't replays.
+ bool replay_protection_;
+
+ // configs_ satisfies the following invariants:
+ // 1) configs_.empty() <-> primary_config_ == nullptr
+ // 2) primary_config_ != nullptr -> primary_config_->is_primary
+ // 3) ∀ c∈configs_, c->is_primary <-> c == primary_config_
+ mutable base::Lock configs_lock_;
+ // configs_ contains all active server configs. It's expected that there are
+ // about half-a-dozen configs active at any one time.
+ ConfigMap configs_;
+ // primary_config_ points to a Config (which is also in |configs_|) which is
+ // the primary config - i.e. the one that we'll give out to new clients.
+ mutable scoped_refptr<Config> primary_config_;
+ // next_config_promotion_time_ contains the nearest, future time when an
+ // active config will be promoted to primary.
+ mutable QuicWallTime next_config_promotion_time_;
+ // Callback to invoke when the primary config changes.
+ scoped_ptr<PrimaryConfigChangedCallback> primary_config_changed_cb_;
+
+ // Protects access to the pointer held by strike_register_client_.
+ mutable base::Lock strike_register_client_lock_;
+ // strike_register_ contains a data structure that keeps track of previously
+ // observed client nonces in order to prevent replay attacks.
+ mutable scoped_ptr<StrikeRegisterClient> strike_register_client_;
+
+ // Default source_address_token_boxer_ used to protect the
+ // source-address tokens that are given to clients. Individual
+ // configs may use boxers with alternate secrets.
+ CryptoSecretBoxer default_source_address_token_boxer_;
+
+ // server_nonce_boxer_ is used to encrypt and validate suggested server
+ // nonces.
+ CryptoSecretBoxer server_nonce_boxer_;
+
+ // server_nonce_orbit_ contains the random, per-server orbit values that this
+ // server will use to generate server nonces (the moral equivalent of a SYN
+ // cookies).
+ uint8 server_nonce_orbit_[8];
+
+ mutable base::Lock server_nonce_strike_register_lock_;
+ // server_nonce_strike_register_ contains a data structure that keeps track of
+ // previously observed server nonces from this server, in order to prevent
+ // replay attacks.
+ mutable scoped_ptr<StrikeRegister> server_nonce_strike_register_;
+
+ // proof_source_ contains an object that can provide certificate chains and
+ // signatures.
+ scoped_ptr<ProofSource> proof_source_;
+
+ // ephemeral_key_source_ contains an object that caches ephemeral keys for a
+ // short period of time.
+ scoped_ptr<EphemeralKeySource> ephemeral_key_source_;
+
+ // These fields store configuration values. See the comments for their
+ // respective setter functions.
+ bool strike_register_no_startup_period_;
+ uint32 strike_register_max_entries_;
+ uint32 strike_register_window_secs_;
+ uint32 source_address_token_future_secs_;
+ uint32 source_address_token_lifetime_secs_;
+ uint32 server_nonce_strike_register_max_entries_;
+ uint32 server_nonce_strike_register_window_secs_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerConfig);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_CRYPTO_SERVER_CONFIG_H_
diff --git a/net/quic/crypto/quic_crypto_server_config_test.cc b/net/quic/crypto/quic_crypto_server_config_test.cc
new file mode 100644
index 0000000..f44dc5c
--- /dev/null
+++ b/net/quic/crypto/quic_crypto_server_config_test.cc
@@ -0,0 +1,669 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_crypto_server_config.h"
+
+#include <stdarg.h>
+
+#include "base/stl_util.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_secret_boxer.h"
+#include "net/quic/crypto/crypto_server_config_protobuf.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/strike_register_client.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::make_pair;
+using std::map;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+class QuicCryptoServerConfigPeer {
+ public:
+ explicit QuicCryptoServerConfigPeer(QuicCryptoServerConfig* server_config)
+ : server_config_(server_config) {}
+
+ scoped_refptr<QuicCryptoServerConfig::Config> GetConfig(string config_id) {
+ base::AutoLock locked(server_config_->configs_lock_);
+ if (config_id == "<primary>") {
+ return scoped_refptr<QuicCryptoServerConfig::Config>(
+ server_config_->primary_config_);
+ } else {
+ return server_config_->GetConfigWithScid(config_id);
+ }
+ }
+
+ bool ConfigHasDefaultSourceAddressTokenBoxer(string config_id) {
+ scoped_refptr<QuicCryptoServerConfig::Config> config = GetConfig(config_id);
+ return config->source_address_token_boxer ==
+ &(server_config_->default_source_address_token_boxer_);
+ }
+
+ string NewSourceAddressToken(
+ string config_id,
+ IPEndPoint ip,
+ QuicRandom* rand,
+ QuicWallTime now) {
+ return server_config_->NewSourceAddressToken(
+ *GetConfig(config_id), ip, rand, now, nullptr);
+ }
+
+ HandshakeFailureReason ValidateSourceAddressToken(string config_id,
+ StringPiece srct,
+ IPEndPoint ip,
+ QuicWallTime now) {
+ return server_config_->ValidateSourceAddressToken(
+ *GetConfig(config_id), srct, ip, now);
+ }
+
+ string NewServerNonce(QuicRandom* rand, QuicWallTime now) const {
+ return server_config_->NewServerNonce(rand, now);
+ }
+
+ HandshakeFailureReason ValidateServerNonce(StringPiece token,
+ QuicWallTime now) {
+ return server_config_->ValidateServerNonce(token, now);
+ }
+
+ base::Lock* GetStrikeRegisterClientLock() {
+ return &server_config_->strike_register_client_lock_;
+ }
+
+ // CheckConfigs compares the state of the Configs in |server_config_| to the
+ // description given as arguments. The arguments are given as
+ // nullptr-terminated pairs. The first of each pair is the server config ID of
+ // a Config. The second is a boolean describing whether the config is the
+ // primary. For example:
+ // CheckConfigs(nullptr); // checks that no Configs are loaded.
+ //
+ // // Checks that exactly three Configs are loaded with the given IDs and
+ // // status.
+ // CheckConfigs(
+ // "id1", false,
+ // "id2", true,
+ // "id3", false,
+ // nullptr);
+ void CheckConfigs(const char* server_config_id1, ...) {
+ va_list ap;
+ va_start(ap, server_config_id1);
+
+ vector<pair<ServerConfigID, bool> > expected;
+ bool first = true;
+ for (;;) {
+ const char* server_config_id;
+ if (first) {
+ server_config_id = server_config_id1;
+ first = false;
+ } else {
+ server_config_id = va_arg(ap, const char*);
+ }
+
+ if (!server_config_id) {
+ break;
+ }
+
+ // varargs will promote the value to an int so we have to read that from
+ // the stack and cast down.
+ const bool is_primary = static_cast<bool>(va_arg(ap, int));
+ expected.push_back(make_pair(server_config_id, is_primary));
+ }
+
+ va_end(ap);
+
+ base::AutoLock locked(server_config_->configs_lock_);
+
+ ASSERT_EQ(expected.size(), server_config_->configs_.size())
+ << ConfigsDebug();
+
+ for (QuicCryptoServerConfig::ConfigMap::const_iterator
+ i = server_config_->configs_.begin();
+ i != server_config_->configs_.end(); ++i) {
+ bool found = false;
+ for (vector<pair<ServerConfigID, bool> >::iterator j = expected.begin();
+ j != expected.end(); ++j) {
+ if (i->first == j->first && i->second->is_primary == j->second) {
+ found = true;
+ j->first.clear();
+ break;
+ }
+ }
+
+ ASSERT_TRUE(found) << "Failed to find match for " << i->first
+ << " in configs:\n" << ConfigsDebug();
+ }
+ }
+
+ // ConfigsDebug returns a string that contains debugging information about
+ // the set of Configs loaded in |server_config_| and their status.
+ // ConfigsDebug() should be called after acquiring
+ // server_config_->configs_lock_.
+ string ConfigsDebug() {
+ if (server_config_->configs_.empty()) {
+ return "No Configs in QuicCryptoServerConfig";
+ }
+
+ string s;
+
+ for (QuicCryptoServerConfig::ConfigMap::const_iterator
+ i = server_config_->configs_.begin();
+ i != server_config_->configs_.end(); ++i) {
+ const scoped_refptr<QuicCryptoServerConfig::Config> config = i->second;
+ if (config->is_primary) {
+ s += "(primary) ";
+ } else {
+ s += " ";
+ }
+ s += config->id;
+ s += "\n";
+ }
+
+ return s;
+ }
+
+ void SelectNewPrimaryConfig(int seconds) {
+ base::AutoLock locked(server_config_->configs_lock_);
+ server_config_->SelectNewPrimaryConfig(
+ QuicWallTime::FromUNIXSeconds(seconds));
+ }
+
+ private:
+ const QuicCryptoServerConfig* server_config_;
+};
+
+class TestStrikeRegisterClient : public StrikeRegisterClient {
+ public:
+ explicit TestStrikeRegisterClient(QuicCryptoServerConfig* config)
+ : config_(config),
+ is_known_orbit_called_(false) {
+ }
+
+ virtual bool IsKnownOrbit(StringPiece orbit) const OVERRIDE {
+ // Ensure that the strike register client lock is not held.
+ QuicCryptoServerConfigPeer peer(config_);
+ base::Lock* m = peer.GetStrikeRegisterClientLock();
+ // In Chromium, we will dead lock if the lock is held by the current thread.
+ // Chromium doesn't have AssertNotHeld API call.
+ // m->AssertNotHeld();
+ base::AutoLock lock(*m);
+
+ is_known_orbit_called_ = true;
+ return true;
+ }
+
+ virtual void VerifyNonceIsValidAndUnique(
+ StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) OVERRIDE {
+ LOG(FATAL) << "Not implemented";
+ }
+
+ bool is_known_orbit_called() { return is_known_orbit_called_; }
+
+ private:
+ QuicCryptoServerConfig* config_;
+ mutable bool is_known_orbit_called_;
+};
+
+TEST(QuicCryptoServerConfigTest, ServerConfig) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ MockClock clock;
+
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock,
+ QuicCryptoServerConfig::ConfigOptions()));
+}
+
+TEST(QuicCryptoServerConfigTest, GetOrbitIsCalledWithoutTheStrikeRegisterLock) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ MockClock clock;
+
+ TestStrikeRegisterClient* strike_register =
+ new TestStrikeRegisterClient(&server);
+ server.SetStrikeRegisterClient(strike_register);
+
+ QuicCryptoServerConfig::ConfigOptions options;
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock, options));
+ EXPECT_TRUE(strike_register->is_known_orbit_called());
+}
+
+TEST(QuicCryptoServerConfigTest, SourceAddressTokens) {
+ const string kPrimary = "<primary>";
+ const string kOverride = "Config with custom source address token key";
+
+ MockClock clock;
+ clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000));
+
+ QuicWallTime now = clock.WallNow();
+ const QuicWallTime original_time = now;
+
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ QuicCryptoServerConfigPeer peer(&server);
+
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddDefaultConfig(rand, &clock,
+ QuicCryptoServerConfig::ConfigOptions()));
+
+ // Add a config that overrides the default boxer.
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.id = kOverride;
+ scoped_ptr<QuicServerConfigProtobuf> protobuf(
+ QuicCryptoServerConfig::GenerateConfig(rand, &clock, options));
+ protobuf->set_source_address_token_secret_override("a secret key");
+ // Lower priority than the default config.
+ protobuf->set_priority(1);
+ scoped_ptr<CryptoHandshakeMessage>(
+ server.AddConfig(protobuf.get(), now));
+
+ EXPECT_TRUE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kPrimary));
+ EXPECT_FALSE(peer.ConfigHasDefaultSourceAddressTokenBoxer(kOverride));
+
+ IPEndPoint ip4 = IPEndPoint(Loopback4(), 1);
+ IPEndPoint ip4d = IPEndPoint(ConvertIPv4NumberToIPv6Number(ip4.address()), 1);
+ IPEndPoint ip6 = IPEndPoint(Loopback6(), 2);
+
+ // Primary config generates configs that validate successfully.
+ const string token4 = peer.NewSourceAddressToken(kPrimary, ip4, rand, now);
+ const string token4d = peer.NewSourceAddressToken(kPrimary, ip4d, rand, now);
+ const string token6 = peer.NewSourceAddressToken(kPrimary, ip6, rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4, ip4, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4, ip4d, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip6, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4d, ip4, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token4d, ip4d, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4d, ip6, now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kPrimary, token6, ip6, now));
+
+ // Override config generates configs that validate successfully.
+ const string override_token4 = peer.NewSourceAddressToken(
+ kOverride, ip4, rand, now);
+ const string override_token6 = peer.NewSourceAddressToken(
+ kOverride, ip6, rand, now);
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kOverride, override_token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DIFFERENT_IP_ADDRESS_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, override_token4, ip6,
+ now));
+ DCHECK_EQ(HANDSHAKE_OK, peer.ValidateSourceAddressToken(
+ kOverride, override_token6, ip6, now));
+
+ // Tokens generated by the primary config do not validate
+ // successfully against the override config, and vice versa.
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, token4, ip4, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kOverride, token6, ip6, now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, override_token4, ip4,
+ now));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_DECRYPTION_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, override_token6, ip6,
+ now));
+
+ // Validation fails after tokens expire.
+ now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_EXPIRED_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
+
+ now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2));
+ DCHECK_EQ(SOURCE_ADDRESS_TOKEN_CLOCK_SKEW_FAILURE,
+ peer.ValidateSourceAddressToken(kPrimary, token4, ip4, now));
+}
+
+TEST(QuicCryptoServerConfigTest, ValidateServerNonce) {
+ QuicRandom* rand = QuicRandom::GetInstance();
+ QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand);
+ QuicCryptoServerConfigPeer peer(&server);
+
+ StringPiece message("hello world");
+ const size_t key_size = CryptoSecretBoxer::GetKeySize();
+ scoped_ptr<uint8[]> key(new uint8[key_size]);
+ memset(key.get(), 0x11, key_size);
+
+ CryptoSecretBoxer boxer;
+ boxer.SetKey(StringPiece(reinterpret_cast<char*>(key.get()), key_size));
+ const string box = boxer.Box(rand, message);
+ MockClock clock;
+ QuicWallTime now = clock.WallNow();
+ const QuicWallTime original_time = now;
+ EXPECT_EQ(SERVER_NONCE_DECRYPTION_FAILURE,
+ peer.ValidateServerNonce(box, now));
+
+ string server_nonce = peer.NewServerNonce(rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
+ EXPECT_EQ(SERVER_NONCE_NOT_UNIQUE_FAILURE,
+ peer.ValidateServerNonce(server_nonce, now));
+
+ now = original_time.Add(QuicTime::Delta::FromSeconds(1000 * 7));
+ server_nonce = peer.NewServerNonce(rand, now);
+ EXPECT_EQ(HANDSHAKE_OK, peer.ValidateServerNonce(server_nonce, now));
+}
+
+class CryptoServerConfigsTest : public ::testing::Test {
+ public:
+ CryptoServerConfigsTest()
+ : rand_(QuicRandom::GetInstance()),
+ config_(QuicCryptoServerConfig::TESTING, rand_),
+ test_peer_(&config_) {}
+
+ virtual void SetUp() {
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1000));
+ }
+
+ // SetConfigs constructs suitable config protobufs and calls SetConfigs on
+ // |config_|. The arguments are given as nullptr-terminated pairs. The first
+ // of each pair is the server config ID of a Config. The second is the
+ // |primary_time| of that Config, given in epoch seconds. (Although note that,
+ // in these tests, time is set to 1000 seconds since the epoch.) For example:
+ // SetConfigs(nullptr); // calls |config_.SetConfigs| with no protobufs.
+ //
+ // // Calls |config_.SetConfigs| with two protobufs: one for a Config with
+ // // a |primary_time| of 900 and priority 1, and another with
+ // // a |primary_time| of 1000 and priority 2.
+
+ // CheckConfigs(
+ // "id1", 900, 1,
+ // "id2", 1000, 2,
+ // nullptr);
+ //
+ // If the server config id starts with "INVALID" then the generated protobuf
+ // will be invalid.
+ void SetConfigs(const char* server_config_id1, ...) {
+ const char kOrbit[] = "12345678";
+
+ va_list ap;
+ va_start(ap, server_config_id1);
+ bool has_invalid = false;
+ bool is_empty = true;
+
+ vector<QuicServerConfigProtobuf*> protobufs;
+ bool first = true;
+ for (;;) {
+ const char* server_config_id;
+ if (first) {
+ server_config_id = server_config_id1;
+ first = false;
+ } else {
+ server_config_id = va_arg(ap, const char*);
+ }
+
+ if (!server_config_id) {
+ break;
+ }
+
+ is_empty = false;
+ int primary_time = va_arg(ap, int);
+ int priority = va_arg(ap, int);
+
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.id = server_config_id;
+ options.orbit = kOrbit;
+ QuicServerConfigProtobuf* protobuf(
+ QuicCryptoServerConfig::GenerateConfig(rand_, &clock_, options));
+ protobuf->set_primary_time(primary_time);
+ protobuf->set_priority(priority);
+ if (string(server_config_id).find("INVALID") == 0) {
+ protobuf->clear_key();
+ has_invalid = true;
+ }
+ protobufs.push_back(protobuf);
+ }
+
+ ASSERT_EQ(!has_invalid && !is_empty,
+ config_.SetConfigs(protobufs, clock_.WallNow()));
+ STLDeleteElements(&protobufs);
+ }
+
+ protected:
+ QuicRandom* const rand_;
+ MockClock clock_;
+ QuicCryptoServerConfig config_;
+ QuicCryptoServerConfigPeer test_peer_;
+};
+
+TEST_F(CryptoServerConfigsTest, NoConfigs) {
+ test_peer_.CheckConfigs(nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) {
+ // Make sure that "b" is primary even though "a" comes first.
+ SetConfigs("a", 1100, 1,
+ "b", 900, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, MakePrimarySecond) {
+ // Make sure that a remains primary after b is added.
+ SetConfigs("a", 900, 1,
+ "b", 1100, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, Delete) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ nullptr);
+ SetConfigs("b", 900, 1,
+ "c", 1100, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "b", true,
+ "c", false,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, DeletePrimary) {
+ // Ensure that deleting the primary config works.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ nullptr);
+ SetConfigs("a", 800, 1,
+ "c", 1100, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "c", false,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, FailIfDeletingAllConfigs) {
+ // Ensure that configs get deleted when removed.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ nullptr);
+ SetConfigs(nullptr);
+ // Config change is rejected, still using old configs.
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, ChangePrimaryTime) {
+ // Check that updates to primary time get picked up.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ nullptr);
+ SetConfigs("a", 1200, 1,
+ "b", 800, 1,
+ "c", 400, 1,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(500);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInThePast) {
+ // Check that the most recent config is selected.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(1500);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, AllConfigsInTheFuture) {
+ // Check that the first config is selected.
+ SetConfigs("a", 400, 1,
+ "b", 800, 1,
+ "c", 1200, 1,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(100);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, SortByPriority) {
+ // Check that priority is used to decide on a primary config when
+ // configs have the same primary time.
+ SetConfigs("a", 900, 1,
+ "b", 900, 2,
+ "c", 900, 3,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ "c", false,
+ nullptr);
+
+ // Change priorities and expect sort order to change.
+ SetConfigs("a", 900, 2,
+ "b", 900, 1,
+ "c", 900, 0,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(800);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", false,
+ "c", true,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, AdvancePrimary) {
+ // Check that a new primary config is enabled at the right time.
+ SetConfigs("a", 900, 1,
+ "b", 1100, 1,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(1000);
+ test_peer_.CheckConfigs(
+ "a", true,
+ "b", false,
+ nullptr);
+ test_peer_.SelectNewPrimaryConfig(1101);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ nullptr);
+}
+
+TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
+ // Ensure that invalid configs don't change anything.
+ SetConfigs("a", 800, 1,
+ "b", 900, 1,
+ "c", 1100, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ nullptr);
+ SetConfigs("a", 800, 1,
+ "c", 1100, 1,
+ "INVALID1", 1000, 1,
+ nullptr);
+ test_peer_.CheckConfigs(
+ "a", false,
+ "b", true,
+ "c", false,
+ nullptr);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/quic_decrypter.cc b/net/quic/crypto/quic_decrypter.cc
new file mode 100644
index 0000000..b7cb089
--- /dev/null
+++ b/net/quic/crypto/quic_decrypter.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_decrypter.h"
+
+#include "net/quic/crypto/aes_128_gcm_12_decrypter.h"
+#include "net/quic/crypto/chacha20_poly1305_decrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/null_decrypter.h"
+
+namespace net {
+
+// static
+QuicDecrypter* QuicDecrypter::Create(QuicTag algorithm) {
+ switch (algorithm) {
+ case kAESG:
+ return new Aes128Gcm12Decrypter();
+ case kCC12:
+ return new ChaCha20Poly1305Decrypter();
+ case kNULL:
+ return new NullDecrypter();
+ default:
+ LOG(FATAL) << "Unsupported algorithm: " << algorithm;
+ return nullptr;
+ }
+}
+
+} // namespace net
diff --git a/net/quic/crypto/quic_decrypter.h b/net/quic/crypto/quic_decrypter.h
new file mode 100644
index 0000000..06c7637
--- /dev/null
+++ b/net/quic/crypto/quic_decrypter.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_
+#define NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicDecrypter {
+ public:
+ virtual ~QuicDecrypter() {}
+
+ static QuicDecrypter* Create(QuicTag algorithm);
+
+ // Sets the encryption key. Returns true on success, false on failure.
+ //
+ // NOTE: The key is the client_write_key or server_write_key derived from
+ // the master secret.
+ virtual bool SetKey(base::StringPiece key) = 0;
+
+ // Sets the fixed initial bytes of the nonce. Returns true on success,
+ // false on failure.
+ //
+ // NOTE: The nonce prefix is the client_write_iv or server_write_iv
+ // derived from the master secret. A 64-bit packet sequence number will
+ // be appended to form the nonce.
+ //
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | Fixed prefix | Packet sequence number |
+ // +---------------------+----------------------------------+
+ // Nonce format
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet sequence number, even when retransmitting a lost packet.
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0;
+
+ // Decrypt authenticates |associated_data| and |ciphertext| and then decrypts
+ // |ciphertext| into |output|, using |nonce|. |nonce| must be 8 bytes longer
+ // than the nonce prefix length returned by GetNoncePrefixSize() (of the
+ // encrypter). |output| must be as long as |ciphertext| on entry and, on
+ // successful return, the true length of the plaintext will be written to
+ // |*output_length|.
+ virtual bool Decrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) = 0;
+
+ // Returns a newly created QuicData object containing the decrypted
+ // |ciphertext| or nullptr if there is an error. |sequence_number| is
+ // appended to the |nonce_prefix| value provided in SetNoncePrefix()
+ // to form the nonce.
+ // TODO(wtc): add a way for DecryptPacket to report decryption failure due
+ // to non-authentic inputs, as opposed to other reasons for failure.
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece ciphertext) = 0;
+
+ // For use by unit tests only.
+ virtual base::StringPiece GetKey() const = 0;
+ virtual base::StringPiece GetNoncePrefix() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_
diff --git a/net/quic/crypto/quic_encrypter.cc b/net/quic/crypto/quic_encrypter.cc
new file mode 100644
index 0000000..298a15a
--- /dev/null
+++ b/net/quic/crypto/quic_encrypter.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_encrypter.h"
+
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/chacha20_poly1305_encrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/null_encrypter.h"
+
+namespace net {
+
+// static
+QuicEncrypter* QuicEncrypter::Create(QuicTag algorithm) {
+ switch (algorithm) {
+ case kAESG:
+ return new Aes128Gcm12Encrypter();
+ case kCC12:
+ return new ChaCha20Poly1305Encrypter();
+ case kNULL:
+ return new NullEncrypter();
+ default:
+ LOG(FATAL) << "Unsupported algorithm: " << algorithm;
+ return nullptr;
+ }
+}
+
+} // namespace net
diff --git a/net/quic/crypto/quic_encrypter.h b/net/quic/crypto/quic_encrypter.h
new file mode 100644
index 0000000..5cb40a1
--- /dev/null
+++ b/net/quic/crypto/quic_encrypter.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_
+#define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicEncrypter {
+ public:
+ virtual ~QuicEncrypter() {}
+
+ static QuicEncrypter* Create(QuicTag algorithm);
+
+ // Sets the encryption key. Returns true on success, false on failure.
+ //
+ // NOTE: The key is the client_write_key or server_write_key derived from
+ // the master secret.
+ virtual bool SetKey(base::StringPiece key) = 0;
+
+ // Sets the fixed initial bytes of the nonce. Returns true on success,
+ // false on failure.
+ //
+ // NOTE: The nonce prefix is the client_write_iv or server_write_iv
+ // derived from the master secret. A 64-bit packet sequence number will
+ // be appended to form the nonce.
+ //
+ // <------------ 64 bits ----------->
+ // +---------------------+----------------------------------+
+ // | Fixed prefix | Packet sequence number |
+ // +---------------------+----------------------------------+
+ // Nonce format
+ //
+ // The security of the nonce format requires that QUIC never reuse a
+ // packet sequence number, even when retransmitting a lost packet.
+ virtual bool SetNoncePrefix(base::StringPiece nonce_prefix) = 0;
+
+ // Encrypt encrypts |plaintext| and writes the ciphertext, plus a MAC over
+ // both |associated_data| and |plaintext| to |output|, using |nonce| as the
+ // nonce. |nonce| must be |8+GetNoncePrefixSize()| bytes long and |output|
+ // must point to a buffer that is at least
+ // |GetCiphertextSize(plaintext.size()| bytes long.
+ virtual bool Encrypt(base::StringPiece nonce,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext,
+ unsigned char* output) = 0;
+
+ // Returns a newly created QuicData object containing the encrypted
+ // |plaintext| as well as a MAC over both |plaintext| and |associated_data|,
+ // or nullptr if there is an error. |sequence_number| is appended to the
+ // |nonce_prefix| value provided in SetNoncePrefix() to form the nonce.
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ base::StringPiece associated_data,
+ base::StringPiece plaintext) = 0;
+
+ // GetKeySize() and GetNoncePrefixSize() tell the HKDF class how many bytes
+ // of key material needs to be derived from the master secret.
+ // NOTE: the sizes returned by GetKeySize() and GetNoncePrefixSize() are
+ // also correct for the QuicDecrypter of the same algorithm. So only
+ // QuicEncrypter has these two methods.
+
+ // Returns the size in bytes of a key for the algorithm.
+ virtual size_t GetKeySize() const = 0;
+ // Returns the size in bytes of the fixed initial part of the nonce.
+ virtual size_t GetNoncePrefixSize() const = 0;
+
+ // Returns the maximum length of plaintext that can be encrypted
+ // to ciphertext no larger than |ciphertext_size|.
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const = 0;
+
+ // Returns the length of the ciphertext that would be generated by encrypting
+ // to plaintext of size |plaintext_size|.
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const = 0;
+
+ // For use by unit tests only.
+ virtual base::StringPiece GetKey() const = 0;
+ virtual base::StringPiece GetNoncePrefix() const = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_
diff --git a/net/quic/crypto/quic_random.cc b/net/quic/crypto/quic_random.cc
new file mode 100644
index 0000000..6f46013
--- /dev/null
+++ b/net/quic/crypto/quic_random.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_random.h"
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "crypto/random.h"
+
+namespace net {
+
+namespace {
+
+class DefaultRandom : public QuicRandom {
+ public:
+ static DefaultRandom* GetInstance();
+
+ // QuicRandom implementation
+ virtual void RandBytes(void* data, size_t len) OVERRIDE;
+ virtual uint64 RandUint64() OVERRIDE;
+ virtual void Reseed(const void* additional_entropy,
+ size_t entropy_len) OVERRIDE;
+
+ private:
+ DefaultRandom() {};
+ virtual ~DefaultRandom() {}
+
+ friend struct DefaultSingletonTraits<DefaultRandom>;
+ DISALLOW_COPY_AND_ASSIGN(DefaultRandom);
+};
+
+DefaultRandom* DefaultRandom::GetInstance() {
+ return Singleton<DefaultRandom>::get();
+}
+
+void DefaultRandom::RandBytes(void* data, size_t len) {
+ crypto::RandBytes(data, len);
+}
+
+uint64 DefaultRandom::RandUint64() {
+ uint64 value;
+ RandBytes(&value, sizeof(value));
+ return value;
+}
+
+void DefaultRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
+ // No such function exists in crypto/random.h.
+}
+
+} // namespace
+
+// static
+QuicRandom* QuicRandom::GetInstance() { return DefaultRandom::GetInstance(); }
+
+} // namespace net
diff --git a/net/quic/crypto/quic_random.h b/net/quic/crypto/quic_random.h
new file mode 100644
index 0000000..ac69b85
--- /dev/null
+++ b/net/quic/crypto/quic_random.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_RANDOM_H_
+#define NET_QUIC_CRYPTO_QUIC_RANDOM_H_
+
+#include <stddef.h>
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// The interface for a random number generator.
+class NET_EXPORT_PRIVATE QuicRandom {
+ public:
+ virtual ~QuicRandom() {}
+
+ // Returns the default random number generator, which is cryptographically
+ // secure and thread-safe.
+ static QuicRandom* GetInstance();
+
+ // Generates |len| random bytes in the |data| buffer.
+ virtual void RandBytes(void* data, size_t len) = 0;
+
+ // Returns a random number in the range [0, kuint64max].
+ virtual uint64 RandUint64() = 0;
+
+ // Reseeds the random number generator with additional entropy input.
+ // NOTE: the constructor of a QuicRandom object is responsible for seeding
+ // itself with enough entropy input.
+ virtual void Reseed(const void* additional_entropy, size_t entropy_len) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_RANDOM_H_
diff --git a/net/quic/crypto/quic_random_test.cc b/net/quic/crypto/quic_random_test.cc
new file mode 100644
index 0000000..a762625
--- /dev/null
+++ b/net/quic/crypto/quic_random_test.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_random.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+TEST(QuicRandomTest, RandBytes) {
+ unsigned char buf1[16];
+ unsigned char buf2[16];
+ memset(buf1, 0xaf, sizeof(buf1));
+ memset(buf2, 0xaf, sizeof(buf2));
+ ASSERT_EQ(0, memcmp(buf1, buf2, sizeof(buf1)));
+
+ QuicRandom* rng = QuicRandom::GetInstance();
+ rng->RandBytes(buf1, sizeof(buf1));
+ EXPECT_NE(0, memcmp(buf1, buf2, sizeof(buf1)));
+}
+
+TEST(QuicRandomTest, RandUint64) {
+ QuicRandom* rng = QuicRandom::GetInstance();
+ uint64 value1 = rng->RandUint64();
+ uint64 value2 = rng->RandUint64();
+ EXPECT_NE(value1, value2);
+}
+
+TEST(QuicRandomTest, Reseed) {
+ char buf[1024];
+ memset(buf, 0xaf, sizeof(buf));
+
+ QuicRandom* rng = QuicRandom::GetInstance();
+ rng->Reseed(buf, sizeof(buf));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/crypto/quic_server_info.cc b/net/quic/crypto/quic_server_info.cc
new file mode 100644
index 0000000..519d7b5
--- /dev/null
+++ b/net/quic/crypto/quic_server_info.cc
@@ -0,0 +1,141 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/quic_server_info.h"
+
+#include <limits>
+
+#include "base/pickle.h"
+
+using std::string;
+
+namespace {
+
+const int kQuicCryptoConfigVersion = 1;
+
+} // namespace
+
+namespace net {
+
+QuicServerInfo::State::State() {}
+
+QuicServerInfo::State::~State() {}
+
+void QuicServerInfo::State::Clear() {
+ server_config.clear();
+ source_address_token.clear();
+ server_config_sig.clear();
+ certs.clear();
+}
+
+QuicServerInfo::QuicServerInfo(const QuicServerId& server_id)
+ : server_id_(server_id) {
+}
+
+QuicServerInfo::~QuicServerInfo() {
+}
+
+const QuicServerInfo::State& QuicServerInfo::state() const {
+ return state_;
+}
+
+QuicServerInfo::State* QuicServerInfo::mutable_state() {
+ return &state_;
+}
+
+bool QuicServerInfo::Parse(const string& data) {
+ State* state = mutable_state();
+
+ state->Clear();
+
+ bool r = ParseInner(data);
+ if (!r)
+ state->Clear();
+ return r;
+}
+
+bool QuicServerInfo::ParseInner(const string& data) {
+ State* state = mutable_state();
+
+ // No data was read from the disk cache.
+ if (data.empty()) {
+ return false;
+ }
+
+ Pickle p(data.data(), data.size());
+ PickleIterator iter(p);
+
+ int version = -1;
+ if (!p.ReadInt(&iter, &version)) {
+ DVLOG(1) << "Missing version";
+ return false;
+ }
+
+ if (version != kQuicCryptoConfigVersion) {
+ DVLOG(1) << "Unsupported version";
+ return false;
+ }
+
+ if (!p.ReadString(&iter, &state->server_config)) {
+ DVLOG(1) << "Malformed server_config";
+ return false;
+ }
+ if (!p.ReadString(&iter, &state->source_address_token)) {
+ DVLOG(1) << "Malformed source_address_token";
+ return false;
+ }
+ if (!p.ReadString(&iter, &state->server_config_sig)) {
+ DVLOG(1) << "Malformed server_config_sig";
+ return false;
+ }
+
+ // Read certs.
+ uint32 num_certs;
+ if (!p.ReadUInt32(&iter, &num_certs)) {
+ DVLOG(1) << "Malformed num_certs";
+ return false;
+ }
+
+ for (uint32 i = 0; i < num_certs; i++) {
+ string cert;
+ if (!p.ReadString(&iter, &cert)) {
+ DVLOG(1) << "Malformed cert";
+ return false;
+ }
+ state->certs.push_back(cert);
+ }
+
+ return true;
+}
+
+string QuicServerInfo::Serialize() {
+ string pickled_data = SerializeInner();
+ state_.Clear();
+ return pickled_data;
+}
+
+string QuicServerInfo::SerializeInner() const {
+ Pickle p(sizeof(Pickle::Header));
+
+ if (!p.WriteInt(kQuicCryptoConfigVersion) ||
+ !p.WriteString(state_.server_config) ||
+ !p.WriteString(state_.source_address_token) ||
+ !p.WriteString(state_.server_config_sig) ||
+ state_.certs.size() > std::numeric_limits<uint32>::max() ||
+ !p.WriteUInt32(state_.certs.size())) {
+ return string();
+ }
+
+ for (size_t i = 0; i < state_.certs.size(); i++) {
+ if (!p.WriteString(state_.certs[i])) {
+ return string();
+ }
+ }
+
+ return string(reinterpret_cast<const char *>(p.data()), p.size());
+}
+
+QuicServerInfoFactory::~QuicServerInfoFactory() {}
+
+} // namespace net
diff --git a/net/quic/crypto/quic_server_info.h b/net/quic/crypto/quic_server_info.h
new file mode 100644
index 0000000..fd54b94
--- /dev/null
+++ b/net/quic/crypto/quic_server_info.h
@@ -0,0 +1,118 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
+#define NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "net/base/completion_callback.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_server_id.h"
+
+namespace net {
+
+class X509Certificate;
+
+// QuicServerInfo is an interface for fetching information about a QUIC server.
+// This information may be stored on disk so does not include keys or other
+// sensitive information. Primarily it's intended for caching the QUIC server's
+// crypto config.
+class NET_EXPORT_PRIVATE QuicServerInfo {
+ public:
+ QuicServerInfo(const QuicServerId& server_id);
+ virtual ~QuicServerInfo();
+
+ // Start will commence the lookup. This must be called before any other
+ // methods. By opportunistically calling this early, it may be possible to
+ // overlap this object's lookup and reduce latency.
+ virtual void Start() = 0;
+
+ // WaitForDataReady returns OK if the fetch of the requested data has
+ // completed. Otherwise it returns ERR_IO_PENDING and will call |callback| on
+ // the current thread when ready.
+ //
+ // Only a single callback can be outstanding at a given time and, in the
+ // event that WaitForDataReady returns OK, it's the caller's responsibility
+ // to delete |callback|.
+ //
+ // |callback| may be NULL, in which case ERR_IO_PENDING may still be returned
+ // but, obviously, a callback will never be made.
+ virtual int WaitForDataReady(const CompletionCallback& callback) = 0;
+
+ // Returns true if data is loaded from disk cache and ready (WaitForDataReady
+ // doesn't have a pending callback).
+ virtual bool IsDataReady() = 0;
+
+ // Returns true if the object is ready to persist data, in other words, if
+ // data is loaded from disk cache and ready and there are no pending writes.
+ virtual bool IsReadyToPersist() = 0;
+
+ // Persist allows for the server information to be updated for future users.
+ // This is a fire and forget operation: the caller may drop its reference
+ // from this object and the store operation will still complete. This can
+ // only be called once WaitForDataReady has returned OK or called its
+ // callback.
+ virtual void Persist() = 0;
+
+ struct State {
+ State();
+ ~State();
+
+ void Clear();
+
+ // This class matches QuicClientCryptoConfig::CachedState.
+ std::string server_config; // A serialized handshake message.
+ std::string source_address_token; // An opaque proof of IP ownership.
+ std::vector<std::string> certs; // A list of certificates in leaf-first
+ // order.
+ std::string server_config_sig; // A signature of |server_config_|.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(State);
+ };
+
+ // Once the data is ready, it can be read using the following members. These
+ // members can then be updated before calling |Persist|.
+ const State& state() const;
+ State* mutable_state();
+
+ protected:
+ // Parse parses pickled data and fills out the public member fields of this
+ // object. It returns true iff the parse was successful. The public member
+ // fields will be set to something sane in any case.
+ bool Parse(const std::string& data);
+ std::string Serialize();
+ State state_;
+
+ private:
+ // ParseInner is a helper function for Parse.
+ bool ParseInner(const std::string& data);
+
+ // SerializeInner is a helper function for Serialize.
+ std::string SerializeInner() const;
+
+ // This is the QUIC server (hostname, port, is_https, privacy_mode) tuple for
+ // which we restore the crypto_config.
+ const QuicServerId server_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerInfo);
+};
+
+class QuicServerInfoFactory {
+ public:
+ virtual ~QuicServerInfoFactory();
+
+ // GetForServer returns a fresh, allocated QuicServerInfo for the given
+ // |server_id| or NULL on failure.
+ virtual QuicServerInfo* GetForServer(const QuicServerId& server_id) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_QUIC_SERVER_INFO_H_
diff --git a/net/quic/crypto/scoped_evp_aead_ctx.cc b/net/quic/crypto/scoped_evp_aead_ctx.cc
new file mode 100644
index 0000000..7facf35
--- /dev/null
+++ b/net/quic/crypto/scoped_evp_aead_ctx.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/scoped_evp_aead_ctx.h"
+
+namespace net {
+
+ScopedEVPAEADCtx::ScopedEVPAEADCtx() {
+ ctx_.aead = nullptr;
+}
+
+ScopedEVPAEADCtx::~ScopedEVPAEADCtx() {
+ if (ctx_.aead != nullptr) {
+ EVP_AEAD_CTX_cleanup(&ctx_);
+ }
+}
+
+EVP_AEAD_CTX* ScopedEVPAEADCtx::get() {
+ return &ctx_;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/scoped_evp_aead_ctx.h b/net/quic/crypto/scoped_evp_aead_ctx.h
new file mode 100644
index 0000000..f0f04c1
--- /dev/null
+++ b/net/quic/crypto/scoped_evp_aead_ctx.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
+#define NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
+
+#include <openssl/evp.h>
+
+#include "base/basictypes.h"
+
+namespace net {
+
+// ScopedEVPAEADCtx manages an EVP_AEAD_CTX object and calls the needed cleanup
+// functions when it goes out of scope.
+class ScopedEVPAEADCtx {
+ public:
+ ScopedEVPAEADCtx();
+ ~ScopedEVPAEADCtx();
+
+ EVP_AEAD_CTX* get();
+
+ private:
+ EVP_AEAD_CTX ctx_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedEVPAEADCtx);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_SCOPED_EVP_AEAD_CTX_H_
diff --git a/net/quic/crypto/source_address_token.cc b/net/quic/crypto/source_address_token.cc
new file mode 100644
index 0000000..f20c343
--- /dev/null
+++ b/net/quic/crypto/source_address_token.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/source_address_token.h"
+
+#include <vector>
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+
+using std::string;
+using std::vector;
+
+namespace net {
+
+CachedNetworkParameters::CachedNetworkParameters() {
+}
+
+CachedNetworkParameters::~CachedNetworkParameters() {
+}
+
+SourceAddressToken::SourceAddressToken() {
+}
+
+SourceAddressToken::~SourceAddressToken() {
+}
+
+string SourceAddressToken::SerializeAsString() const {
+ string out;
+ out.push_back(ip_.size());
+ out.append(ip_);
+ string time_str = base::Int64ToString(timestamp_);
+ out.push_back(time_str.size());
+ out.append(time_str);
+ // TODO(rtenneti): Implement serialization of optional CachedNetworkParameters
+ // when they are used.
+ return out;
+}
+
+bool SourceAddressToken::ParseFromArray(const char* plaintext,
+ size_t plaintext_length) {
+ if (plaintext_length == 0) {
+ return false;
+ }
+ size_t ip_len = plaintext[0];
+ if (plaintext_length <= 1 + ip_len) {
+ return false;
+ }
+ size_t time_len = plaintext[1 + ip_len];
+ if (plaintext_length != 1 + ip_len + 1 + time_len) {
+ return false;
+ }
+
+ string time_str(&plaintext[1 + ip_len + 1], time_len);
+ int64 timestamp;
+ if (!base::StringToInt64(time_str, ×tamp)) {
+ return false;
+ }
+
+ ip_.assign(&plaintext[1], ip_len);
+ timestamp_ = timestamp;
+
+ // TODO(rtenneti): Implement parsing of optional CachedNetworkParameters when
+ // they are used.
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/crypto/source_address_token.h b/net/quic/crypto/source_address_token.h
new file mode 100644
index 0000000..1101351
--- /dev/null
+++ b/net/quic/crypto/source_address_token.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_
+#define NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// TODO(rtenneti): sync with server more rationally.
+// CachedNetworkParameters contains data that can be used to choose appropriate
+// connection parameters (initial RTT, initial CWND, etc.) in new connections.
+class NET_EXPORT_PRIVATE CachedNetworkParameters {
+ public:
+ // Describes the state of the connection during which the supplied network
+ // parameters were calculated.
+ enum PreviousConnectionState {
+ SLOW_START = 0,
+ CONGESTION_AVOIDANCE = 1,
+ };
+
+ CachedNetworkParameters();
+ ~CachedNetworkParameters();
+
+ std::string serving_region() const {
+ return serving_region_;
+ }
+ void set_serving_region(base::StringPiece serving_region) {
+ serving_region_ = serving_region.as_string();
+ }
+
+ int32 bandwidth_estimate_bytes_per_second() const {
+ return bandwidth_estimate_bytes_per_second_;
+ }
+ void set_bandwidth_estimate_bytes_per_second(
+ int32 bandwidth_estimate_bytes_per_second) {
+ bandwidth_estimate_bytes_per_second_ = bandwidth_estimate_bytes_per_second;
+ }
+
+ int32 max_bandwidth_estimate_bytes_per_second() const {
+ return max_bandwidth_estimate_bytes_per_second_;
+ }
+ void set_max_bandwidth_estimate_bytes_per_second(
+ int32 max_bandwidth_estimate_bytes_per_second) {
+ max_bandwidth_estimate_bytes_per_second_ =
+ max_bandwidth_estimate_bytes_per_second;
+ }
+
+ int64 max_bandwidth_timestamp_seconds() const {
+ return max_bandwidth_timestamp_seconds_;
+ }
+ void set_max_bandwidth_timestamp_seconds(
+ int64 max_bandwidth_timestamp_seconds) {
+ max_bandwidth_timestamp_seconds_ = max_bandwidth_timestamp_seconds;
+ }
+
+ int32 min_rtt_ms() const {
+ return min_rtt_ms_;
+ }
+ void set_min_rtt_ms(int32 min_rtt_ms) {
+ min_rtt_ms_ = min_rtt_ms;
+ }
+
+ int32 previous_connection_state() const {
+ return previous_connection_state_;
+ }
+ void set_previous_connection_state(int32 previous_connection_state) {
+ previous_connection_state_ = previous_connection_state;
+ }
+
+ int64 timestamp() const { return timestamp_; }
+ void set_timestamp(int64 timestamp) { timestamp_ = timestamp; }
+
+ private:
+ // serving_region_ is used to decide whether or not the bandwidth estimate and
+ // min RTT are reasonable and if they should be used.
+ // For example a group of geographically close servers may share the same
+ // serving_region_ string if they are expected to have similar network
+ // performance.
+ std::string serving_region_;
+ // The server can supply a bandwidth estimate (in bytes/s) which it may re-use
+ // on receipt of a source-address token with this field set.
+ int32 bandwidth_estimate_bytes_per_second_;
+ // The maximum bandwidth seen by the client, not necessarily the latest.
+ int32 max_bandwidth_estimate_bytes_per_second_;
+ // Timestamp (seconds since UNIX epoch) that indicates when the max bandwidth
+ // was seen by the server.
+ int64 max_bandwidth_timestamp_seconds_;
+ // The min RTT seen on a previous connection can be used by the server to
+ // inform initial connection parameters for new connections.
+ int32 min_rtt_ms_;
+ // Encodes the PreviousConnectionState enum.
+ int32 previous_connection_state_;
+ // UNIX timestamp when this bandwidth estimate was created.
+ int64 timestamp_;
+};
+
+// TODO(rtenneti): sync with server more rationally.
+// A SourceAddressToken is serialised, encrypted and sent to clients so that
+// they can prove ownership of an IP address.
+class NET_EXPORT_PRIVATE SourceAddressToken {
+ public:
+ SourceAddressToken();
+ ~SourceAddressToken();
+
+ std::string SerializeAsString() const;
+
+ bool ParseFromArray(const char* plaintext, size_t plaintext_length);
+
+ std::string ip() const {
+ return ip_;
+ }
+ void set_ip(base::StringPiece ip) {
+ ip_ = ip.as_string();
+ }
+
+ int64 timestamp() const {
+ return timestamp_;
+ }
+ void set_timestamp(int64 timestamp) {
+ timestamp_ = timestamp;
+ }
+
+ const CachedNetworkParameters& cached_network_parameters() const {
+ return cached_network_parameters_;
+ }
+ void set_cached_network_parameters(
+ const CachedNetworkParameters& cached_network_parameters) {
+ cached_network_parameters_ = cached_network_parameters;
+ }
+
+ private:
+ // ip_ contains either 4 (IPv4) or 16 (IPv6) bytes of IP address in network
+ // byte order.
+ std::string ip_;
+ // timestamp_ contains a UNIX timestamp value of the time when the token was
+ // created.
+ int64 timestamp_;
+
+ // The server can provide estimated network parameters to be used for
+ // initial parameter selection in future connections.
+ CachedNetworkParameters cached_network_parameters_;
+
+ DISALLOW_COPY_AND_ASSIGN(SourceAddressToken);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_
diff --git a/net/quic/crypto/strike_register.cc b/net/quic/crypto/strike_register.cc
new file mode 100644
index 0000000..d727ece
--- /dev/null
+++ b/net/quic/crypto/strike_register.cc
@@ -0,0 +1,520 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/strike_register.h"
+
+#include <limits>
+
+#include "base/logging.h"
+
+using std::make_pair;
+using std::max;
+using std::min;
+using std::pair;
+using std::set;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+uint32 GetInitialHorizon(uint32 current_time_internal,
+ uint32 window_secs,
+ StrikeRegister::StartupType startup) {
+ if (startup == StrikeRegister::DENY_REQUESTS_AT_STARTUP) {
+ // The horizon is initially set |window_secs| into the future because, if
+ // we just crashed, then we may have accepted nonces in the span
+ // [current_time...current_time+window_secs] and so we conservatively
+ // reject the whole timespan unless |startup| tells us otherwise.
+ return current_time_internal + window_secs + 1;
+ } else { // startup == StrikeRegister::NO_STARTUP_PERIOD_NEEDED
+ // The orbit can be assumed to be globally unique. Use a horizon
+ // in the past.
+ return 0;
+ }
+}
+
+} // namespace
+
+// static
+const uint32 StrikeRegister::kExternalNodeSize = 24;
+// static
+const uint32 StrikeRegister::kNil = (1u << 31) | 1;
+// static
+const uint32 StrikeRegister::kExternalFlag = 1 << 23;
+
+// InternalNode represents a non-leaf node in the critbit tree. See the comment
+// in the .h file for details.
+class StrikeRegister::InternalNode {
+ public:
+ void SetChild(unsigned direction, uint32 child) {
+ data_[direction] = (data_[direction] & 0xff) | (child << 8);
+ }
+
+ void SetCritByte(uint8 critbyte) {
+ data_[0] = (data_[0] & 0xffffff00) | critbyte;
+ }
+
+ void SetOtherBits(uint8 otherbits) {
+ data_[1] = (data_[1] & 0xffffff00) | otherbits;
+ }
+
+ void SetNextPtr(uint32 next) { data_[0] = next; }
+
+ uint32 next() const { return data_[0]; }
+
+ uint32 child(unsigned n) const { return data_[n] >> 8; }
+
+ uint8 critbyte() const { return data_[0]; }
+
+ uint8 otherbits() const { return data_[1]; }
+
+ // These bytes are organised thus:
+ // <24 bits> left child
+ // <8 bits> crit-byte
+ // <24 bits> right child
+ // <8 bits> other-bits
+ uint32 data_[2];
+};
+
+// kCreationTimeFromInternalEpoch contains the number of seconds between the
+// start of the internal epoch and the creation time. This allows us
+// to consider times that are before the creation time.
+static const uint32 kCreationTimeFromInternalEpoch = 63115200; // 2 years.
+
+void StrikeRegister::ValidateStrikeRegisterConfig(unsigned max_entries) {
+ // We only have 23 bits of index available.
+ CHECK_LT(max_entries, 1u << 23);
+ CHECK_GT(max_entries, 1u); // There must be at least two entries.
+ CHECK_EQ(sizeof(InternalNode), 8u); // in case of compiler changes.
+}
+
+StrikeRegister::StrikeRegister(unsigned max_entries,
+ uint32 current_time,
+ uint32 window_secs,
+ const uint8 orbit[8],
+ StartupType startup)
+ : max_entries_(max_entries),
+ window_secs_(window_secs),
+ internal_epoch_(current_time > kCreationTimeFromInternalEpoch
+ ? current_time - kCreationTimeFromInternalEpoch
+ : 0),
+ horizon_(GetInitialHorizon(
+ ExternalTimeToInternal(current_time), window_secs, startup)) {
+ memcpy(orbit_, orbit, sizeof(orbit_));
+
+ ValidateStrikeRegisterConfig(max_entries);
+ internal_nodes_ = new InternalNode[max_entries];
+ external_nodes_.reset(new uint8[kExternalNodeSize * max_entries]);
+
+ Reset();
+}
+
+StrikeRegister::~StrikeRegister() { delete[] internal_nodes_; }
+
+void StrikeRegister::Reset() {
+ // Thread a free list through all of the internal nodes.
+ internal_node_free_head_ = 0;
+ for (unsigned i = 0; i < max_entries_ - 1; i++)
+ internal_nodes_[i].SetNextPtr(i + 1);
+ internal_nodes_[max_entries_ - 1].SetNextPtr(kNil);
+
+ // Also thread a free list through the external nodes.
+ external_node_free_head_ = 0;
+ for (unsigned i = 0; i < max_entries_ - 1; i++)
+ external_node_next_ptr(i) = i + 1;
+ external_node_next_ptr(max_entries_ - 1) = kNil;
+
+ // This is the root of the tree.
+ internal_node_head_ = kNil;
+}
+
+InsertStatus StrikeRegister::Insert(const uint8 nonce[32],
+ uint32 current_time_external) {
+ // Make space for the insertion if the strike register is full.
+ while (external_node_free_head_ == kNil ||
+ internal_node_free_head_ == kNil) {
+ DropOldestNode();
+ }
+
+ const uint32 current_time = ExternalTimeToInternal(current_time_external);
+
+ // Check to see if the orbit is correct.
+ if (memcmp(nonce + sizeof(current_time), orbit_, sizeof(orbit_))) {
+ return NONCE_INVALID_ORBIT_FAILURE;
+ }
+
+ const uint32 nonce_time = ExternalTimeToInternal(TimeFromBytes(nonce));
+
+ // Check that the timestamp is in the valid range.
+ pair<uint32, uint32> valid_range =
+ StrikeRegister::GetValidRange(current_time);
+ if (nonce_time < valid_range.first || nonce_time > valid_range.second) {
+ return NONCE_INVALID_TIME_FAILURE;
+ }
+
+ // We strip the orbit out of the nonce.
+ uint8 value[24];
+ memcpy(value, nonce, sizeof(nonce_time));
+ memcpy(value + sizeof(nonce_time),
+ nonce + sizeof(nonce_time) + sizeof(orbit_),
+ sizeof(value) - sizeof(nonce_time));
+
+ // Find the best match to |value| in the crit-bit tree. The best match is
+ // simply the value which /could/ match |value|, if any does, so we still
+ // need a memcmp to check.
+ uint32 best_match_index = BestMatch(value);
+ if (best_match_index == kNil) {
+ // Empty tree. Just insert the new value at the root.
+ uint32 index = GetFreeExternalNode();
+ memcpy(external_node(index), value, sizeof(value));
+ internal_node_head_ = (index | kExternalFlag) << 8;
+ DCHECK_LE(horizon_, nonce_time);
+ return NONCE_OK;
+ }
+
+ const uint8* best_match = external_node(best_match_index);
+ if (memcmp(best_match, value, sizeof(value)) == 0) {
+ // We found the value in the tree.
+ return NONCE_NOT_UNIQUE_FAILURE;
+ }
+
+ // We are going to insert a new entry into the tree, so get the nodes now.
+ uint32 internal_node_index = GetFreeInternalNode();
+ uint32 external_node_index = GetFreeExternalNode();
+
+ // If we just evicted the best match, then we have to try and match again.
+ // We know that we didn't just empty the tree because we require that
+ // max_entries_ >= 2. Also, we know that it doesn't match because, if it
+ // did, it would have been returned previously.
+ if (external_node_index == best_match_index) {
+ best_match_index = BestMatch(value);
+ best_match = external_node(best_match_index);
+ }
+
+ // Now we need to find the first bit where we differ from |best_match|.
+ unsigned differing_byte;
+ uint8 new_other_bits;
+ for (differing_byte = 0; differing_byte < sizeof(value); differing_byte++) {
+ new_other_bits = value[differing_byte] ^ best_match[differing_byte];
+ if (new_other_bits) {
+ break;
+ }
+ }
+
+ // Once we have the XOR the of first differing byte in new_other_bits we need
+ // to find the most significant differing bit. We could do this with a simple
+ // for loop, testing bits 7..0. Instead we fold the bits so that we end up
+ // with a byte where all the bits below the most significant one, are set.
+ new_other_bits |= new_other_bits >> 1;
+ new_other_bits |= new_other_bits >> 2;
+ new_other_bits |= new_other_bits >> 4;
+ // Now this bit trick results in all the bits set, except the original
+ // most-significant one.
+ new_other_bits = (new_other_bits & ~(new_other_bits >> 1)) ^ 255;
+
+ // Consider the effect of ORing against |new_other_bits|. If |value| did not
+ // have the critical bit set, the result is the same as |new_other_bits|. If
+ // it did, the result is all ones.
+
+ unsigned newdirection;
+ if ((new_other_bits | value[differing_byte]) == 0xff) {
+ newdirection = 1;
+ } else {
+ newdirection = 0;
+ }
+
+ memcpy(external_node(external_node_index), value, sizeof(value));
+ InternalNode* inode = &internal_nodes_[internal_node_index];
+
+ inode->SetChild(newdirection, external_node_index | kExternalFlag);
+ inode->SetCritByte(differing_byte);
+ inode->SetOtherBits(new_other_bits);
+
+ // |where_index| is a pointer to the uint32 which needs to be updated in
+ // order to insert the new internal node into the tree. The internal nodes
+ // store the child indexes in the top 24-bits of a 32-bit word and, to keep
+ // the code simple, we define that |internal_node_head_| is organised the
+ // same way.
+ DCHECK_EQ(internal_node_head_ & 0xff, 0u);
+ uint32* where_index = &internal_node_head_;
+ while (((*where_index >> 8) & kExternalFlag) == 0) {
+ InternalNode* node = &internal_nodes_[*where_index >> 8];
+ if (node->critbyte() > differing_byte) {
+ break;
+ }
+ if (node->critbyte() == differing_byte &&
+ node->otherbits() > new_other_bits) {
+ break;
+ }
+ if (node->critbyte() == differing_byte &&
+ node->otherbits() == new_other_bits) {
+ CHECK(false);
+ }
+
+ uint8 c = value[node->critbyte()];
+ const int direction =
+ (1 + static_cast<unsigned>(node->otherbits() | c)) >> 8;
+ where_index = &node->data_[direction];
+ }
+
+ inode->SetChild(newdirection ^ 1, *where_index >> 8);
+ *where_index = (*where_index & 0xff) | (internal_node_index << 8);
+
+ DCHECK_LE(horizon_, nonce_time);
+ return NONCE_OK;
+}
+
+const uint8* StrikeRegister::orbit() const {
+ return orbit_;
+}
+
+uint32 StrikeRegister::GetCurrentValidWindowSecs(
+ uint32 current_time_external) const {
+ uint32 current_time = ExternalTimeToInternal(current_time_external);
+ pair<uint32, uint32> valid_range = StrikeRegister::GetValidRange(
+ current_time);
+ if (valid_range.second >= valid_range.first) {
+ return valid_range.second - current_time + 1;
+ } else {
+ return 0;
+ }
+}
+
+void StrikeRegister::Validate() {
+ set<uint32> free_internal_nodes;
+ for (uint32 i = internal_node_free_head_; i != kNil;
+ i = internal_nodes_[i].next()) {
+ CHECK_LT(i, max_entries_);
+ CHECK_EQ(free_internal_nodes.count(i), 0u);
+ free_internal_nodes.insert(i);
+ }
+
+ set<uint32> free_external_nodes;
+ for (uint32 i = external_node_free_head_; i != kNil;
+ i = external_node_next_ptr(i)) {
+ CHECK_LT(i, max_entries_);
+ CHECK_EQ(free_external_nodes.count(i), 0u);
+ free_external_nodes.insert(i);
+ }
+
+ set<uint32> used_external_nodes;
+ set<uint32> used_internal_nodes;
+
+ if (internal_node_head_ != kNil &&
+ ((internal_node_head_ >> 8) & kExternalFlag) == 0) {
+ vector<pair<unsigned, bool> > bits;
+ ValidateTree(internal_node_head_ >> 8, -1, bits, free_internal_nodes,
+ free_external_nodes, &used_internal_nodes,
+ &used_external_nodes);
+ }
+}
+
+// static
+uint32 StrikeRegister::TimeFromBytes(const uint8 d[4]) {
+ return static_cast<uint32>(d[0]) << 24 |
+ static_cast<uint32>(d[1]) << 16 |
+ static_cast<uint32>(d[2]) << 8 |
+ static_cast<uint32>(d[3]);
+}
+
+pair<uint32, uint32> StrikeRegister::GetValidRange(
+ uint32 current_time_internal) const {
+ if (current_time_internal < horizon_) {
+ // Empty valid range.
+ return make_pair(std::numeric_limits<uint32>::max(), 0);
+ }
+
+ uint32 lower_bound;
+ if (current_time_internal >= window_secs_) {
+ lower_bound = max(horizon_, current_time_internal - window_secs_);
+ } else {
+ lower_bound = horizon_;
+ }
+
+ // Also limit the upper range based on horizon_. This makes the
+ // strike register reject inserts that are far in the future and
+ // would consume strike register resources for a long time. This
+ // allows the strike server to degrade optimally in cases where the
+ // insert rate exceeds |max_entries_ / (2 * window_secs_)| entries
+ // per second.
+ uint32 upper_bound =
+ current_time_internal + min(current_time_internal - horizon_,
+ window_secs_);
+
+ return make_pair(lower_bound, upper_bound);
+}
+
+uint32 StrikeRegister::ExternalTimeToInternal(uint32 external_time) const {
+ return external_time - internal_epoch_;
+}
+
+uint32 StrikeRegister::BestMatch(const uint8 v[24]) const {
+ if (internal_node_head_ == kNil) {
+ return kNil;
+ }
+
+ uint32 next = internal_node_head_ >> 8;
+ while ((next & kExternalFlag) == 0) {
+ InternalNode* node = &internal_nodes_[next];
+ uint8 b = v[node->critbyte()];
+ unsigned direction =
+ (1 + static_cast<unsigned>(node->otherbits() | b)) >> 8;
+ next = node->child(direction);
+ }
+
+ return next & ~kExternalFlag;
+}
+
+uint32& StrikeRegister::external_node_next_ptr(unsigned i) {
+ return *reinterpret_cast<uint32*>(&external_nodes_[i * kExternalNodeSize]);
+}
+
+uint8* StrikeRegister::external_node(unsigned i) {
+ return &external_nodes_[i * kExternalNodeSize];
+}
+
+uint32 StrikeRegister::GetFreeExternalNode() {
+ uint32 index = external_node_free_head_;
+ DCHECK(index != kNil);
+ external_node_free_head_ = external_node_next_ptr(index);
+ return index;
+}
+
+uint32 StrikeRegister::GetFreeInternalNode() {
+ uint32 index = internal_node_free_head_;
+ DCHECK(index != kNil);
+ internal_node_free_head_ = internal_nodes_[index].next();
+ return index;
+}
+
+void StrikeRegister::DropOldestNode() {
+ // DropOldestNode should never be called on an empty tree.
+ DCHECK(internal_node_head_ != kNil);
+
+ // An internal node in a crit-bit tree always has exactly two children.
+ // This means that, if we are removing an external node (which is one of
+ // those children), then we also need to remove an internal node. In order
+ // to do that we keep pointers to the parent (wherep) and grandparent
+ // (whereq) when walking down the tree.
+
+ uint32 p = internal_node_head_ >> 8, *wherep = &internal_node_head_,
+ *whereq = nullptr;
+ while ((p & kExternalFlag) == 0) {
+ whereq = wherep;
+ InternalNode* inode = &internal_nodes_[p];
+ // We always go left, towards the smallest element, exploiting the fact
+ // that the timestamp is big-endian and at the start of the value.
+ wherep = &inode->data_[0];
+ p = (*wherep) >> 8;
+ }
+
+ const uint32 ext_index = p & ~kExternalFlag;
+ const uint8* ext_node = external_node(ext_index);
+ uint32 new_horizon = ExternalTimeToInternal(TimeFromBytes(ext_node)) + 1;
+ DCHECK_LE(horizon_, new_horizon);
+ horizon_ = new_horizon;
+
+ if (!whereq) {
+ // We are removing the last element in a tree.
+ internal_node_head_ = kNil;
+ FreeExternalNode(ext_index);
+ return;
+ }
+
+ // |wherep| points to the left child pointer in the parent so we can add
+ // one and dereference to get the right child.
+ const uint32 other_child = wherep[1];
+ FreeInternalNode((*whereq) >> 8);
+ *whereq = (*whereq & 0xff) | (other_child & 0xffffff00);
+ FreeExternalNode(ext_index);
+}
+
+void StrikeRegister::FreeExternalNode(uint32 index) {
+ external_node_next_ptr(index) = external_node_free_head_;
+ external_node_free_head_ = index;
+}
+
+void StrikeRegister::FreeInternalNode(uint32 index) {
+ internal_nodes_[index].SetNextPtr(internal_node_free_head_);
+ internal_node_free_head_ = index;
+}
+
+void StrikeRegister::ValidateTree(
+ uint32 internal_node,
+ int last_bit,
+ const vector<pair<unsigned, bool> >& bits,
+ const set<uint32>& free_internal_nodes,
+ const set<uint32>& free_external_nodes,
+ set<uint32>* used_internal_nodes,
+ set<uint32>* used_external_nodes) {
+ CHECK_LT(internal_node, max_entries_);
+ const InternalNode* i = &internal_nodes_[internal_node];
+ unsigned bit = 0;
+ switch (i->otherbits()) {
+ case 0xff & ~(1 << 7):
+ bit = 0;
+ break;
+ case 0xff & ~(1 << 6):
+ bit = 1;
+ break;
+ case 0xff & ~(1 << 5):
+ bit = 2;
+ break;
+ case 0xff & ~(1 << 4):
+ bit = 3;
+ break;
+ case 0xff & ~(1 << 3):
+ bit = 4;
+ break;
+ case 0xff & ~(1 << 2):
+ bit = 5;
+ break;
+ case 0xff & ~(1 << 1):
+ bit = 6;
+ break;
+ case 0xff & ~1:
+ bit = 7;
+ break;
+ default:
+ CHECK(false);
+ }
+
+ bit += 8 * i->critbyte();
+ if (last_bit > -1) {
+ CHECK_GT(bit, static_cast<unsigned>(last_bit));
+ }
+
+ CHECK_EQ(free_internal_nodes.count(internal_node), 0u);
+
+ for (unsigned child = 0; child < 2; child++) {
+ if (i->child(child) & kExternalFlag) {
+ uint32 ext = i->child(child) & ~kExternalFlag;
+ CHECK_EQ(free_external_nodes.count(ext), 0u);
+ CHECK_EQ(used_external_nodes->count(ext), 0u);
+ used_external_nodes->insert(ext);
+ const uint8* bytes = external_node(ext);
+ for (vector<pair<unsigned, bool> >::const_iterator i = bits.begin();
+ i != bits.end(); i++) {
+ unsigned byte = i->first / 8;
+ DCHECK_LE(byte, 0xffu);
+ unsigned bit = i->first % 8;
+ static const uint8 kMasks[8] =
+ {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
+ CHECK_EQ((bytes[byte] & kMasks[bit]) != 0, i->second);
+ }
+ } else {
+ uint32 inter = i->child(child);
+ vector<pair<unsigned, bool> > new_bits(bits);
+ new_bits.push_back(pair<unsigned, bool>(bit, child != 0));
+ CHECK_EQ(free_internal_nodes.count(inter), 0u);
+ CHECK_EQ(used_internal_nodes->count(inter), 0u);
+ used_internal_nodes->insert(inter);
+ ValidateTree(inter, bit, bits, free_internal_nodes, free_external_nodes,
+ used_internal_nodes, used_external_nodes);
+ }
+ }
+}
+
+} // namespace net
diff --git a/net/quic/crypto/strike_register.h b/net/quic/crypto/strike_register.h
new file mode 100644
index 0000000..8acc272
--- /dev/null
+++ b/net/quic/crypto/strike_register.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_STRIKE_REGISTER_H_
+#define NET_QUIC_CRYPTO_STRIKE_REGISTER_H_
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// InsertStatus enum values cannot be changed, they need to be stable.
+enum InsertStatus {
+ NONCE_OK = 0,
+ // The default error value for nonce verification failures from strike
+ // register (covers old strike registers and unknown failures).
+ NONCE_UNKNOWN_FAILURE = 1,
+ // Decrypted nonce had incorrect length.
+ NONCE_INVALID_FAILURE = 2,
+ // Nonce is not unique.
+ NONCE_NOT_UNIQUE_FAILURE = 3,
+ // Nonce's orbit is invalid or incorrect.
+ NONCE_INVALID_ORBIT_FAILURE = 4,
+ // Nonce's timestamp is not in the strike register's valid time range.
+ NONCE_INVALID_TIME_FAILURE = 5,
+ // Strike register's RPC call timed out, nonce couldn't be verified.
+ STRIKE_REGISTER_TIMEOUT = 6,
+ // Strike register is down, nonce couldn't be verified.
+ STRIKE_REGISTER_FAILURE = 7,
+};
+
+// A StrikeRegister is critbit tree which stores a set of observed nonces.
+// We use a critbit tree because:
+// 1) It's immune to algorithmic complexity attacks. If we had used a hash
+// tree, an attacker could send us a series of values which stretch out one
+// of the hash chains, causing us to do much more work than normal.
+// 2) We can write it to use a fixed block of memory: avoiding fragmentation
+// issues and so forth. (We might be able to do that with the STL
+// algorithms and a custom allocator, but I don't want to go there.)
+// 3) It's simple (compared to balanced binary trees) and doesn't involve
+// bouncing nearly as many cache lines around.
+// 4) It allows us to query for the oldest element in log(n) time.
+//
+// This code is based on djb's public domain critbit tree from qhasm.
+//
+// A critbit tree has external and internal nodes. External nodes are just the
+// nonce values (which are stored with internal times, see below, and without
+// the orbit values included). Internal nodes contain the bit number at which
+// the tree is branching and exactly two children. The critical bit is stored
+// as a byte number and a byte (|otherbits|) which has all the bits set
+// /except/ the one in question.
+//
+// Internal nodes have exactly two children: an internal node with only a
+// single child would be useless.
+//
+// The branching bit number (considering the MSB to be the 1st bit) is
+// monotonically increasing as you go down the tree.
+//
+// There are two distinct time representations used. External times are those
+// which are exposed to the users of this class. They are expected to be a
+// count of the number of seconds since the UNIX epoch. Internal times are a
+// count of the number of seconds since a point in time a couple of years
+// before the creation time given to the constructor. (See
+// |ExternalTimeToInternal|) This avoids having to worry about overflow since
+// we assume that no process will run for 130 years.
+class NET_EXPORT_PRIVATE StrikeRegister {
+ public:
+ enum StartupType {
+ // DENY_REQUESTS_AT_STARTUP is the typical mode for a strike register.
+ // Because servers can crash and the strike-register memory-based, the
+ // state of the strike-register may be lost at any time. Thus the previous
+ // instance of the server may have accepted an nonce with time
+ // now+window_secs, which was forgotten in the crash. Therefore
+ // DENY_REQUESTS_AT_STARTUP causes the strike-register to reject all
+ // requests timestampped before window_secs + the creation time (the
+ // quiescent period).
+ DENY_REQUESTS_AT_STARTUP,
+ // NO_STARTUP_PERIOD_NEEDED indicates that no quiescent period is required.
+ // This may be because the strike-register is using an orbit randomly
+ // generated at startup and therefore nonces accepted by the previous
+ // instance of the strike-register are invalid for that reason.
+ NO_STARTUP_PERIOD_NEEDED,
+ };
+
+ // An external node takes 24 bytes as we don't record the orbit.
+ static const uint32 kExternalNodeSize;
+
+ // We address the nodes by their index in the array. This means that 0 is a
+ // valid index. Therefore this is our invalid index. It also has a one bit
+ // in the LSB position because we tend to store indexes shifted up 8 bits
+ // and this distinguishes kNil from (kExternalFlag | 0) << 8.
+ static const uint32 kNil;
+
+ // Our pointers from internal nodes can either point to an internal or
+ // external node. We flag the 24th bit to mark a pointer as external.
+ static const uint32 kExternalFlag;
+
+ // Allows early validation before a strike register is created.
+ static void ValidateStrikeRegisterConfig(unsigned max_entries);
+
+ // Construct a new set which can hold, at most, |max_entries| (which must be
+ // less than 2**23). See the comments around StartupType about initial
+ // behaviour. Otherwise, all nonces that are outside +/- |window_secs| from
+ // the current time will be rejected. Additionally, all nonces that have an
+ // orbit value other than |orbit| will be rejected.
+ //
+ // (Note that this code is independent of the actual units of time used, but
+ // you should use seconds.)
+ StrikeRegister(unsigned max_entries,
+ uint32 current_time_external,
+ uint32 window_secs,
+ const uint8 orbit[8],
+ StartupType startup);
+
+ ~StrikeRegister();
+
+ void Reset();
+
+ // |Insert| queries to see if |nonce| is
+ // a) for the wrong orbit
+ // b) before the current horizon
+ // c) outside of the valid time window
+ // d) already in the set of observed nonces
+ // and returns the failure reason if any of these are true. It is also free to
+ // return failure reason for other reasons as it's always safe to reject an
+ // nonce.
+ //
+ // nonces are:
+ // 4 bytes of timestamp (UNIX epoch seconds)
+ // 8 bytes of orbit value (a cluster id)
+ // 20 bytes of random data
+ //
+ // Otherwise, it inserts |nonce| into the observed set and returns NONCE_OK.
+ InsertStatus Insert(const uint8 nonce[32], uint32 current_time);
+
+ // orbit returns a pointer to the 8-byte orbit value for this
+ // strike-register.
+ const uint8* orbit() const;
+
+ // Time window for which the strike register has complete information.
+ uint32 GetCurrentValidWindowSecs(uint32 current_time_external) const;
+
+ // This is a debugging aid which checks the tree for sanity.
+ void Validate();
+
+ private:
+ class InternalNode;
+
+ // TimeFromBytes returns a big-endian uint32 from |d|.
+ static uint32 TimeFromBytes(const uint8 d[4]);
+
+ // Range of internal times for which the strike register has
+ // complete information. A nonce is within the valid range of the
+ // strike register if:
+ // valid_range.first <= nonce_time_internal <= valid_range.second
+ std::pair<uint32, uint32> GetValidRange(uint32 current_time_internal) const;
+
+ // ExternalTimeToInternal converts an external time value into an internal
+ // time value using |internal_epoch_|.
+ uint32 ExternalTimeToInternal(uint32 external_time) const;
+
+ // BestMatch returns either kNil, or an external node index which could
+ // possibly match |v|.
+ uint32 BestMatch(const uint8 v[24]) const;
+
+ // external_node_next_ptr returns the 'next' pointer embedded in external
+ // node |i|. This is used to thread a free list through the external nodes.
+ uint32& external_node_next_ptr(unsigned i);
+
+ uint8* external_node(unsigned i);
+
+ uint32 GetFreeExternalNode();
+
+ uint32 GetFreeInternalNode();
+
+ // DropOldestNode removes the oldest node in the tree and updates |horizon_|
+ // accordingly.
+ void DropOldestNode();
+
+ void FreeExternalNode(uint32 index);
+
+ void FreeInternalNode(uint32 index);
+
+ void ValidateTree(uint32 internal_node,
+ int last_bit,
+ const std::vector<std::pair<unsigned, bool> >& bits,
+ const std::set<uint32>& free_internal_nodes,
+ const std::set<uint32>& free_external_nodes,
+ std::set<uint32>* used_internal_nodes,
+ std::set<uint32>* used_external_nodes);
+
+ const uint32 max_entries_;
+ const uint32 window_secs_;
+ // internal_epoch_ contains the external time value of the start of internal
+ // time.
+ const uint32 internal_epoch_;
+ uint8 orbit_[8];
+ // The strike register will reject nonces with internal times < |horizon_| .
+ uint32 horizon_;
+
+ uint32 internal_node_free_head_;
+ uint32 external_node_free_head_;
+ uint32 internal_node_head_;
+ // internal_nodes_ can't be a scoped_ptr because the type isn't defined in
+ // this header.
+ InternalNode* internal_nodes_;
+ scoped_ptr<uint8[]> external_nodes_;
+
+ DISALLOW_COPY_AND_ASSIGN(StrikeRegister);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_STRIKE_REGISTER_H_
diff --git a/net/quic/crypto/strike_register_client.h b/net/quic/crypto/strike_register_client.h
new file mode 100644
index 0000000..dae3519
--- /dev/null
+++ b/net/quic/crypto/strike_register_client.h
@@ -0,0 +1,61 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_CRYPTO_STRIKE_REGISTER_CLIENT_H_
+#define NET_QUIC_CRYPTO_STRIKE_REGISTER_CLIENT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/crypto/strike_register.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+// Interface implemented by clients that talk to strike registers
+// implemented as local or remote services.
+class NET_EXPORT_PRIVATE StrikeRegisterClient {
+ public:
+ // Single use callback that will be invoked once the validation
+ // operation is complete.
+ class NET_EXPORT_PRIVATE ResultCallback {
+ public:
+ ResultCallback() {}
+ virtual ~ResultCallback() {}
+ void Run(bool nonce_is_valid_and_unique, InsertStatus nonce_error) {
+ RunImpl(nonce_is_valid_and_unique, nonce_error);
+ delete this;
+ }
+
+ protected:
+ virtual void RunImpl(bool nonce_is_valid_and_unique,
+ InsertStatus nonce_error) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResultCallback);
+ };
+
+ StrikeRegisterClient() {}
+ virtual ~StrikeRegisterClient() {}
+
+ // Returns true iff the strike register knows about the given orbit.
+ virtual bool IsKnownOrbit(base::StringPiece orbit) const = 0;
+ // Validate a nonce for freshness and uniqueness.
+ // Will invoke cb->Run(ValidateResponse::nonce_is_valid_and_unique(),
+ // ValidateResponse::nonce_error())
+ // once the asynchronous operation is complete.
+ virtual void VerifyNonceIsValidAndUnique(
+ base::StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StrikeRegisterClient);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_CRYPTO_STRIKE_REGISTER_CLIENT_H_
diff --git a/net/quic/crypto/strike_register_test.cc b/net/quic/crypto/strike_register_test.cc
new file mode 100644
index 0000000..df72357
--- /dev/null
+++ b/net/quic/crypto/strike_register_test.cc
@@ -0,0 +1,408 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/crypto/strike_register.h"
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/rand_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using net::InsertStatus;
+using net::StrikeRegister;
+using std::make_pair;
+using std::min;
+using std::pair;
+using std::set;
+using std::string;
+
+const uint8 kOrbit[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+// StrikeRegisterTests don't look at the random bytes so this function can
+// simply set the random bytes to 0.
+void SetNonce(uint8 nonce[32], unsigned time, const uint8 orbit[8]) {
+ nonce[0] = time >> 24;
+ nonce[1] = time >> 16;
+ nonce[2] = time >> 8;
+ nonce[3] = time;
+ memcpy(nonce + 4, orbit, 8);
+ memset(nonce + 12, 0, 20);
+}
+
+TEST(StrikeRegisterTest, SimpleHorizon) {
+ // The set must reject values created on or before its own creation time.
+ StrikeRegister set(10 /* max size */, 1000 /* current time */,
+ 100 /* window secs */, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP);
+ uint8 nonce[32];
+ SetNonce(nonce, 999, kOrbit);
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1000));
+ SetNonce(nonce, 1000, kOrbit);
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1000));
+
+ EXPECT_EQ(0u, set.GetCurrentValidWindowSecs(1000 /* current time */));
+ EXPECT_EQ(0u, set.GetCurrentValidWindowSecs(1100 /* current time */));
+ EXPECT_EQ(1u, set.GetCurrentValidWindowSecs(1101 /* current time */));
+ EXPECT_EQ(50u, set.GetCurrentValidWindowSecs(1150 /* current time */));
+ EXPECT_EQ(100u, set.GetCurrentValidWindowSecs(1200 /* current time */));
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1300 /* current time */));
+}
+
+TEST(StrikeRegisterTest, NoStartupMode) {
+ // Check that a strike register works immediately if NO_STARTUP_PERIOD_NEEDED
+ // is specified.
+ StrikeRegister set(10 /* max size */, 1000 /* current time */,
+ 100 /* window secs */, kOrbit,
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED);
+ uint8 nonce[32];
+ SetNonce(nonce, 1000, kOrbit);
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce, 1000));
+ EXPECT_EQ(net::NONCE_NOT_UNIQUE_FAILURE, set.Insert(nonce, 1000));
+
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1000 /* current time */));
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1050 /* current time */));
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1100 /* current time */));
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1200 /* current time */));
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1300 /* current time */));
+}
+
+TEST(StrikeRegisterTest, WindowFuture) {
+ // The set must reject values outside the window.
+ StrikeRegister set(10 /* max size */, 1000 /* current time */,
+ 100 /* window secs */, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP);
+ uint8 nonce[32];
+ SetNonce(nonce, 1101, kOrbit);
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1000));
+ SetNonce(nonce, 999, kOrbit);
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1100));
+}
+
+TEST(StrikeRegisterTest, BadOrbit) {
+ // The set must reject values with the wrong orbit
+ StrikeRegister set(10 /* max size */, 1000 /* current time */,
+ 100 /* window secs */, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP);
+ uint8 nonce[32];
+ static const uint8 kBadOrbit[8] = { 0, 0, 0, 0, 1, 1, 1, 1 };
+ SetNonce(nonce, 1101, kBadOrbit);
+ EXPECT_EQ(net::NONCE_INVALID_ORBIT_FAILURE, set.Insert(nonce, 1100));
+}
+
+TEST(StrikeRegisterTest, OneValue) {
+ StrikeRegister set(10 /* max size */, 1000 /* current time */,
+ 100 /* window secs */, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP);
+ uint8 nonce[32];
+ SetNonce(nonce, 1101, kOrbit);
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce, 1101));
+}
+
+TEST(StrikeRegisterTest, RejectDuplicate) {
+ // The set must reject values with the wrong orbit
+ StrikeRegister set(10 /* max size */, 1000 /* current time */,
+ 100 /* window secs */, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP);
+ uint8 nonce[32];
+ SetNonce(nonce, 1101, kOrbit);
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce, 1101));
+ EXPECT_EQ(net::NONCE_NOT_UNIQUE_FAILURE, set.Insert(nonce, 1101));
+}
+
+TEST(StrikeRegisterTest, HorizonUpdating) {
+ StrikeRegister::StartupType startup_types[] = {
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP,
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED
+ };
+
+ for (size_t type_idx = 0; type_idx < arraysize(startup_types); ++type_idx) {
+ StrikeRegister set(5 /* max size */, 500 /* current time */,
+ 100 /* window secs */, kOrbit,
+ startup_types[type_idx]);
+ uint8 nonce[6][32];
+ for (unsigned i = 0; i < 5; i++) {
+ SetNonce(nonce[i], 1101 + i, kOrbit);
+ nonce[i][31] = i;
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[i], 1100));
+ }
+
+ // Valid window is still equal to |window_secs + 1|.
+ EXPECT_EQ(101u, set.GetCurrentValidWindowSecs(1100));
+
+ // This should push the oldest value out and force the horizon to
+ // be updated.
+ SetNonce(nonce[5], 1110, kOrbit);
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[5], 1110));
+ // Effective horizon is computed based on the timestamp of the
+ // value that was pushed out.
+ EXPECT_EQ(9u, set.GetCurrentValidWindowSecs(1110));
+
+ SetNonce(nonce[5], 1111, kOrbit);
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[5], 1110));
+ EXPECT_EQ(8u, set.GetCurrentValidWindowSecs(1110));
+
+ // This should be behind the horizon now:
+ SetNonce(nonce[5], 1101, kOrbit);
+ nonce[5][31] = 10;
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce[5], 1110));
+
+ // Insert beyond the valid range.
+ SetNonce(nonce[5], 1117, kOrbit);
+ nonce[5][31] = 2;
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce[5], 1110));
+
+ // Insert at the upper valid range.
+ SetNonce(nonce[5], 1116, kOrbit);
+ nonce[5][31] = 1;
+ EXPECT_EQ(net::NONCE_OK, set.Insert(nonce[5], 1110));
+
+ // This should be beyond the upper valid range now:
+ SetNonce(nonce[5], 1116, kOrbit);
+ nonce[5][31] = 2;
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce[5], 1110));
+ }
+}
+
+TEST(StrikeRegisterTest, InsertMany) {
+ StrikeRegister set(5000 /* max size */, 1000 /* current time */,
+ 500 /* window secs */, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP);
+
+ uint8 nonce[32];
+ SetNonce(nonce, 1101, kOrbit);
+ for (unsigned i = 0; i < 100000; i++) {
+ SetNonce(nonce, 1101 + i/500, kOrbit);
+ memcpy(nonce + 12, &i, sizeof(i));
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, set.Insert(nonce, 1100));
+ }
+}
+
+
+// For the following test we create a slow, but simple, version of a
+// StrikeRegister. The behaviour of this object is much easier to understand
+// than the fully fledged version. We then create a test to show, empirically,
+// that the two objects have identical behaviour.
+
+// A SlowStrikeRegister has the same public interface as a StrikeRegister, but
+// is much slower. Hopefully it is also more obviously correct and we can
+// empirically test that their behaviours are identical.
+class SlowStrikeRegister {
+ public:
+ SlowStrikeRegister(unsigned max_entries, uint32 current_time,
+ uint32 window_secs, const uint8 orbit[8])
+ : max_entries_(max_entries),
+ window_secs_(window_secs),
+ creation_time_(current_time),
+ horizon_(ExternalTimeToInternal(current_time + window_secs) + 1) {
+ memcpy(orbit_, orbit, sizeof(orbit_));
+ }
+
+ InsertStatus Insert(const uint8 nonce_bytes[32],
+ const uint32 nonce_time_external,
+ const uint32 current_time_external) {
+ if (nonces_.size() == max_entries_) {
+ DropOldestEntry();
+ }
+
+ const uint32 current_time = ExternalTimeToInternal(current_time_external);
+
+ // Check to see if the orbit is correct.
+ if (memcmp(nonce_bytes + 4, orbit_, sizeof(orbit_))) {
+ return net::NONCE_INVALID_ORBIT_FAILURE;
+ }
+ const uint32 nonce_time =
+ ExternalTimeToInternal(TimeFromBytes(nonce_bytes));
+ EXPECT_EQ(ExternalTimeToInternal(nonce_time_external), nonce_time);
+ // We have dropped one or more nonces with a time value of |horizon_ - 1|,
+ // so we have to reject anything with a timestamp less than or
+ // equal to that.
+ if (nonce_time < horizon_) {
+ return net::NONCE_INVALID_TIME_FAILURE;
+ }
+
+ // Check that the timestamp is in the current window.
+ if ((current_time > window_secs_ &&
+ nonce_time < (current_time - window_secs_)) ||
+ nonce_time > (current_time + window_secs_)) {
+ return net::NONCE_INVALID_TIME_FAILURE;
+ }
+
+ pair<uint32, string> nonce = make_pair(
+ nonce_time,
+ string(reinterpret_cast<const char*>(nonce_bytes), 32));
+
+ set<pair<uint32, string> >::const_iterator it = nonces_.find(nonce);
+ if (it != nonces_.end()) {
+ return net::NONCE_NOT_UNIQUE_FAILURE;
+ }
+
+ nonces_.insert(nonce);
+ return net::NONCE_OK;
+ }
+
+ uint32 GetCurrentValidWindowSecs(const uint32 current_time_external) const {
+ const uint32 current_time = ExternalTimeToInternal(current_time_external);
+ if (horizon_ > current_time) {
+ return 0;
+ }
+ return 1 + min(current_time - horizon_, window_secs_);
+ }
+
+ private:
+ // TimeFromBytes returns a big-endian uint32 from |d|.
+ static uint32 TimeFromBytes(const uint8 d[4]) {
+ return static_cast<uint32>(d[0]) << 24 |
+ static_cast<uint32>(d[1]) << 16 |
+ static_cast<uint32>(d[2]) << 8 |
+ static_cast<uint32>(d[3]);
+ }
+
+ uint32 ExternalTimeToInternal(uint32 external_time) const {
+ static const uint32 kCreationTimeFromInternalEpoch = 63115200.0;
+ uint32 internal_epoch = 0;
+ if (creation_time_ > kCreationTimeFromInternalEpoch) {
+ internal_epoch = creation_time_ - kCreationTimeFromInternalEpoch;
+ }
+
+ return external_time - internal_epoch;
+ }
+
+ void DropOldestEntry() {
+ set<pair<uint32, string> >::iterator oldest = nonces_.begin();
+ horizon_ = oldest->first + 1;
+ nonces_.erase(oldest);
+ }
+
+ const unsigned max_entries_;
+ const unsigned window_secs_;
+ const uint32 creation_time_;
+ uint8 orbit_[8];
+ uint32 horizon_;
+
+ set<pair<uint32, string> > nonces_;
+};
+
+class StrikeRegisterStressTest : public ::testing::Test {
+};
+
+TEST_F(StrikeRegisterStressTest, InOrderInsertion) {
+ // Fixed seed gives reproducibility for this test.
+ srand(42);
+
+ unsigned max_entries = 64;
+ uint32 current_time = 10000, window = 200;
+ scoped_ptr<StrikeRegister> s1(
+ new StrikeRegister(max_entries, current_time, window, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ scoped_ptr<SlowStrikeRegister> s2(
+ new SlowStrikeRegister(max_entries, current_time, window, kOrbit));
+
+ uint64 i;
+ const uint64 kMaxIterations = 10000;
+ for (i = 0; i < kMaxIterations; i++) {
+ const uint32 time = current_time + i;
+
+ uint8 nonce[32];
+ SetNonce(nonce, time, kOrbit);
+
+ // There are 2048 possible nonce values:
+ const uint32 v = rand() % 2048;
+ nonce[30] = v >> 8;
+ nonce[31] = v;
+
+ const InsertStatus nonce_error2 = s2->Insert(nonce, time, time);
+ const InsertStatus nonce_error1 = s1->Insert(nonce, time);
+ EXPECT_EQ(nonce_error1, nonce_error2);
+
+ // Inserts succeed after the startup period.
+ if (time > current_time + window) {
+ EXPECT_EQ(net::NONCE_OK, nonce_error1);
+ } else {
+ EXPECT_EQ(net::NONCE_INVALID_TIME_FAILURE, nonce_error1);
+ }
+ EXPECT_EQ(s1->GetCurrentValidWindowSecs(time),
+ s2->GetCurrentValidWindowSecs(time));
+
+ if (i % 10 == 0) {
+ s1->Validate();
+ }
+
+ if (HasFailure()) {
+ break;
+ }
+ }
+
+ if (i != kMaxIterations) {
+ FAIL() << "Failed after " << i << " iterations";
+ }
+}
+
+TEST_F(StrikeRegisterStressTest, Stress) {
+ // Fixed seed gives reproducibility for this test.
+ srand(42);
+ unsigned max_entries = 64;
+ uint32 current_time = 10000, window = 200;
+ scoped_ptr<StrikeRegister> s1(
+ new StrikeRegister(max_entries, current_time, window, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ scoped_ptr<SlowStrikeRegister> s2(
+ new SlowStrikeRegister(max_entries, current_time, window, kOrbit));
+ uint64 i;
+
+ // When making changes it's worth removing the limit on this test and running
+ // it for a while. For the initial development an opt binary was left running
+ // for 10 minutes.
+ const uint64 kMaxIterations = 10000;
+ for (i = 0; i < kMaxIterations; i++) {
+ if (rand() % 1000 == 0) {
+ // 0.1% chance of resetting the sets.
+ max_entries = rand() % 300 + 2;
+ current_time = rand() % 10000;
+ window = rand() % 500;
+ s1.reset(new StrikeRegister(max_entries, current_time, window, kOrbit,
+ StrikeRegister::DENY_REQUESTS_AT_STARTUP));
+ s2.reset(
+ new SlowStrikeRegister(max_entries, current_time, window, kOrbit));
+ }
+
+ int32 time_delta = rand() % (window * 4);
+ time_delta -= window * 2;
+ const uint32 time = current_time + time_delta;
+ if (time_delta < 0 && time > current_time) {
+ continue; // overflow
+ }
+
+ uint8 nonce[32];
+ SetNonce(nonce, time, kOrbit);
+
+ // There are 2048 possible nonce values:
+ const uint32 v = rand() % 2048;
+ nonce[30] = v >> 8;
+ nonce[31] = v;
+
+ const InsertStatus nonce_error2 = s2->Insert(nonce, time, time);
+ const InsertStatus nonce_error1 = s1->Insert(nonce, time);
+ EXPECT_EQ(nonce_error1, nonce_error2);
+ EXPECT_EQ(s1->GetCurrentValidWindowSecs(time),
+ s2->GetCurrentValidWindowSecs(time));
+
+ if (i % 10 == 0) {
+ s1->Validate();
+ }
+
+ if (HasFailure()) {
+ break;
+ }
+ }
+
+ if (i != kMaxIterations) {
+ FAIL() << "Failed after " << i << " iterations";
+ }
+}
+
+} // anonymous namespace
diff --git a/net/quic/iovector.cc b/net/quic/iovector.cc
new file mode 100644
index 0000000..a6d2c61
--- /dev/null
+++ b/net/quic/iovector.cc
@@ -0,0 +1,13 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/iovector.h"
+
+namespace net {
+
+IOVector::IOVector() {}
+
+IOVector::~IOVector() {}
+
+} // namespace net
diff --git a/net/quic/iovector.h b/net/quic/iovector.h
new file mode 100644
index 0000000..22d2cc9
--- /dev/null
+++ b/net/quic/iovector.h
@@ -0,0 +1,200 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_IOVECTOR_H_
+#define NET_QUIC_IOVECTOR_H_
+
+#include <stddef.h>
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/base/iovec.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Calculate the total number of bytes in an array of iovec structures.
+inline size_t TotalIovecLength(const struct iovec* iov, size_t iovcnt) {
+ size_t length = 0;
+ if (iov != NULL) {
+ for (size_t i = 0; i < iovcnt; ++i) {
+ length += iov[i].iov_len;
+ }
+ }
+ return length;
+}
+
+// IOVector is a helper class that makes it easier to work with POSIX vector I/O
+// struct. It is a thin wrapper by design and thus has no virtual functions and
+// all inlined methods. This class makes no assumptions about the ordering of
+// the pointer values of the blocks appended, it simply counts bytes when asked
+// to consume bytes.
+//
+// IOVector is a bookkeeping object that collects a description of buffers to
+// be read or written together and in order. It does not take ownership of the
+// blocks appended.
+//
+// Because it is used for scatter-gather operations, the order in which the
+// buffer blocks are added to the IOVector is important to the client. The
+// intended usage pattern is:
+//
+// iovector.Append(p0, len0);
+// ...
+// iovector.Append(pn, lenn);
+// int bytes_written = writev(fd, iovector.iovec(), iovector.Size());
+// if (bytes_written > 0)
+// iovector.Consume(bytes_written);
+//
+// The sequence is the same for readv, except that Consume() in this case is
+// used to change the IOVector to only keep track of description of blocks of
+// memory not yet written to.
+//
+// IOVector does not have any method to change the iovec entries that it
+// accumulates. This is due to the block merging nature of Append(): we'd like
+// to avoid accidentally change an entry that is assembled by two or more
+// Append()'s by simply an index access.
+//
+class NET_EXPORT_PRIVATE IOVector {
+ public:
+ // Provide a default constructor so it'll never be inhibited by adding other
+ // constructors.
+ IOVector();
+ ~IOVector();
+
+ // Provides a way to convert system call-like iovec representation to
+ // IOVector.
+ void AppendIovec(const struct iovec* iov, size_t iovcnt) {
+ for (size_t i = 0; i < iovcnt; ++i)
+ Append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ }
+
+ // Appends at most max_bytes from iovec to the IOVector.
+ size_t AppendIovecAtMostBytes(const struct iovec* iov,
+ size_t iovcnt,
+ size_t max_bytes) {
+ size_t bytes_appended = 0;
+ for (size_t i = 0; i < iovcnt && max_bytes > 0; ++i) {
+ const size_t length = std::min(max_bytes, iov[i].iov_len);
+ Append(static_cast<char*>(iov[i].iov_base), length);
+ max_bytes -= length;
+ bytes_appended += length;
+ }
+ return bytes_appended;
+ }
+
+ // Append another block to the IOVector. Since IOVector can be used for read
+ // and write, it always takes char*. Clients that writes will need to cast
+ // away the constant of the pointer before appending a block.
+ void Append(char* buffer, size_t length) {
+ if (buffer != NULL && length > 0) {
+ if (iovec_.size() > 0) {
+ struct iovec& last = iovec_.back();
+ // If the new block is contiguous with the last block, just extend.
+ if (static_cast<char*>(last.iov_base) + last.iov_len == buffer) {
+ last.iov_len += length;
+ return;
+ }
+ }
+ struct iovec tmp = {buffer, length};
+ iovec_.push_back(tmp);
+ }
+ }
+
+ // Same as Append, but doesn't do the tail merge optimization.
+ // Intended for testing.
+ void AppendNoCoalesce(char* buffer, size_t length) {
+ if (buffer != NULL && length > 0) {
+ struct iovec tmp = {buffer, length};
+ iovec_.push_back(tmp);
+ }
+ }
+
+ // Remove a number of bytes from the beginning of the IOVector. Since vector
+ // I/O operations always occur at the beginning of the block list, a method
+ // to remove bytes at the end is not provided.
+ // It returns the number of bytes actually consumed (it'll only be smaller
+ // than the requested number if the IOVector contains less data).
+ size_t Consume(size_t length) {
+ if (length == 0) return 0;
+
+ size_t bytes_to_consume = length;
+ std::vector<struct iovec>::iterator iter = iovec_.begin();
+ std::vector<struct iovec>::iterator end = iovec_.end();
+ for (; iter < end && bytes_to_consume >= iter->iov_len; ++iter) {
+ bytes_to_consume -= iter->iov_len;
+ }
+ iovec_.erase(iovec_.begin(), iter);
+ if (iovec_.size() > 0 && bytes_to_consume != 0) {
+ iovec_[0].iov_base =
+ static_cast<char*>(iovec_[0].iov_base) + bytes_to_consume;
+ iovec_[0].iov_len -= bytes_to_consume;
+ return length;
+ }
+ if (iovec_.size() == 0 && bytes_to_consume > 0) {
+ LOG(DFATAL) << "Attempting to consume " << bytes_to_consume
+ << " non-existent bytes.";
+ }
+ // At this point bytes_to_consume is the number of wanted bytes left over
+ // after walking through all the iovec entries.
+ return length - bytes_to_consume;
+ }
+
+ // TODO(joechan): If capacity is large, swap out for a blank one.
+ // Clears the IOVector object to contain no blocks.
+ void Clear() { iovec_.clear(); }
+
+ // Swap the guts of two IOVector.
+ void Swap(IOVector* other) { iovec_.swap(other->iovec_); }
+
+ // Returns the number of valid blocks in the IOVector (not the number of
+ // bytes).
+ size_t Size() const { return iovec_.size(); }
+
+ // Returns the total storage used by the IOVector in number of blocks (not
+ // the number of bytes).
+ size_t Capacity() const { return iovec_.capacity(); }
+
+ // Returns true if there are no blocks in the IOVector.
+ bool Empty() const { return iovec_.empty(); }
+
+ // Returns the pointer to the beginning of the iovec to be used for vector
+ // I/O operations. If the IOVector has no blocks appened, this function
+ // returns NULL.
+ struct iovec* iovec() { return !Empty() ? &iovec_[0] : NULL; }
+
+ // Const version.
+ const struct iovec* iovec() const { return !Empty() ? &iovec_[0] : NULL; }
+
+ // Returns a pointer to one past the last byte of the last block. If the
+ // IOVector is empty, NULL is returned.
+ const char* LastBlockEnd() const {
+ return iovec_.size() > 0 ?
+ static_cast<char *>(iovec_.back().iov_base) + iovec_.back().iov_len :
+ NULL;
+ }
+
+ // Returns the total number of bytes in the IOVector.
+ size_t TotalBufferSize() const { return TotalIovecLength(iovec(), Size()); }
+
+ void Resize(size_t count) {
+ iovec_.resize(count);
+ }
+
+ private:
+ std::vector<struct iovec> iovec_;
+
+ // IOVector has value-semantics; copy and assignment are allowed.
+ // This class does not explicitly define copy/move constructors or the
+ // assignment operator to preserve compiler-generated copy/move constructors
+ // and assignment operators. Note that since IOVector does not own the
+ // actual buffers that the struct iovecs point to, copies and assignments
+ // result in a shallow copy of the buffers; resulting IOVectors will point
+ // to the same copy of the underlying data.
+};
+
+} // namespace net
+
+#endif // NET_QUIC_IOVECTOR_H_
diff --git a/net/quic/iovector_test.cc b/net/quic/iovector_test.cc
new file mode 100644
index 0000000..e6cc058
--- /dev/null
+++ b/net/quic/iovector_test.cc
@@ -0,0 +1,283 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/iovector.h"
+
+#include <string.h>
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+const char* const test_data[] = {
+ "test string 1, a medium size one.",
+ "test string2",
+ "test string 3, a looooooooooooong loooooooooooooooong string"
+};
+
+TEST(IOVectorTest, CopyConstructor) {
+ IOVector iov1;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ iov1.Append(const_cast<char*>(test_data[i]), strlen(test_data[i]));
+ }
+ IOVector iov2 = iov1;
+ EXPECT_EQ(iov2.Size(), iov1.Size());
+ for (size_t i = 0; i < iov2.Size(); ++i) {
+ EXPECT_TRUE(iov2.iovec()[i].iov_base == iov1.iovec()[i].iov_base);
+ EXPECT_EQ(iov2.iovec()[i].iov_len, iov1.iovec()[i].iov_len);
+ }
+ EXPECT_EQ(iov2.TotalBufferSize(), iov1.TotalBufferSize());
+}
+
+TEST(IOVectorTest, AssignmentOperator) {
+ IOVector iov1;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ iov1.Append(const_cast<char*>(test_data[i]), strlen(test_data[i]));
+ }
+ IOVector iov2;
+ iov2.Append(const_cast<char*>("ephemeral string"), 16);
+ // The following assignment results in a shallow copy;
+ // both IOVectors point to the same underlying data.
+ iov2 = iov1;
+ EXPECT_EQ(iov2.Size(), iov1.Size());
+ for (size_t i = 0; i < iov2.Size(); ++i) {
+ EXPECT_TRUE(iov2.iovec()[i].iov_base == iov1.iovec()[i].iov_base);
+ EXPECT_EQ(iov2.iovec()[i].iov_len, iov1.iovec()[i].iov_len);
+ }
+ EXPECT_EQ(iov2.TotalBufferSize(), iov1.TotalBufferSize());
+}
+
+TEST(IOVectorTest, Append) {
+ IOVector iov;
+ int length = 0;
+ const struct iovec* iov2 = iov.iovec();
+
+ ASSERT_EQ(0u, iov.Size());
+ ASSERT_TRUE(iov2 == nullptr);
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ const int str_len = strlen(test_data[i]);
+ const int append_len = str_len / 2;
+ // This should append a new block
+ iov.Append(const_cast<char*>(test_data[i]), append_len);
+ length += append_len;
+ ASSERT_EQ(i + 1, static_cast<size_t>(iov.Size()));
+ ASSERT_TRUE(iov.LastBlockEnd() == test_data[i] + append_len);
+ // This should just lengthen the existing block.
+ iov.Append(const_cast<char*>(test_data[i] + append_len),
+ str_len - append_len);
+ length += (str_len - append_len);
+ ASSERT_EQ(i + 1, static_cast<size_t>(iov.Size()));
+ ASSERT_TRUE(iov.LastBlockEnd() == test_data[i] + str_len);
+ }
+
+ iov2 = iov.iovec();
+ ASSERT_TRUE(iov2 != nullptr);
+ for (size_t i = 0; i < iov.Size(); ++i) {
+ ASSERT_TRUE(test_data[i] == iov2[i].iov_base);
+ ASSERT_EQ(strlen(test_data[i]), iov2[i].iov_len);
+ }
+}
+
+TEST(IOVectorTest, AppendIovec) {
+ IOVector iov;
+ const struct iovec test_iov[] = {
+ {const_cast<char*>("foo"), 3},
+ {const_cast<char*>("bar"), 3},
+ {const_cast<char*>("buzzzz"), 6}
+ };
+ iov.AppendIovec(test_iov, ARRAYSIZE_UNSAFE(test_iov));
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_iov); ++i) {
+ EXPECT_EQ(test_iov[i].iov_base, iov.iovec()[i].iov_base);
+ EXPECT_EQ(test_iov[i].iov_len, iov.iovec()[i].iov_len);
+ }
+
+ // Test AppendIovecAtMostBytes.
+ iov.Clear();
+ // Stop in the middle of a block.
+ EXPECT_EQ(5u, iov.AppendIovecAtMostBytes(test_iov, ARRAYSIZE_UNSAFE(test_iov),
+ 5));
+ EXPECT_EQ(5u, iov.TotalBufferSize());
+ iov.Append(static_cast<char*>(test_iov[1].iov_base) + 2, 1);
+ // Make sure the boundary case, where max_bytes == size of block also works.
+ EXPECT_EQ(6u, iov.AppendIovecAtMostBytes(&test_iov[2], 1, 6));
+ ASSERT_LE(ARRAYSIZE_UNSAFE(test_iov), static_cast<size_t>(iov.Size()));
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_iov); ++i) {
+ EXPECT_EQ(test_iov[i].iov_base, iov.iovec()[i].iov_base);
+ EXPECT_EQ(test_iov[i].iov_len, iov.iovec()[i].iov_len);
+ }
+}
+
+TEST(IOVectorTest, ConsumeHalfBlocks) {
+ IOVector iov;
+ int length = 0;
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ const int str_len = strlen(test_data[i]);
+ iov.Append(const_cast<char*>(test_data[i]), str_len);
+ length += str_len;
+ }
+ const char* endp = iov.LastBlockEnd();
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ const struct iovec* iov2 = iov.iovec();
+ const size_t str_len = strlen(test_data[i]);
+ size_t tmp = str_len / 2;
+
+ ASSERT_TRUE(iov2 != nullptr);
+ ASSERT_TRUE(iov2[0].iov_base == test_data[i]);
+ ASSERT_EQ(str_len, iov2[0].iov_len);
+
+ // Consume half of the first block.
+ size_t consumed = iov.Consume(tmp);
+ ASSERT_EQ(tmp, consumed);
+ ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data) - i, static_cast<size_t>(iov.Size()));
+ iov2 = iov.iovec();
+ ASSERT_TRUE(iov2 != nullptr);
+ ASSERT_TRUE(iov2[0].iov_base == test_data[i] + tmp);
+ ASSERT_EQ(iov2[0].iov_len, str_len - tmp);
+
+ // Consume the rest of the first block
+ consumed = iov.Consume(str_len - tmp);
+ ASSERT_EQ(str_len - tmp, consumed);
+ ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data) - i - 1,
+ static_cast<size_t>(iov.Size()));
+ iov2 = iov.iovec();
+ if (iov.Size() > 0) {
+ ASSERT_TRUE(iov2 != nullptr);
+ ASSERT_TRUE(iov.LastBlockEnd() == endp);
+ } else {
+ ASSERT_TRUE(iov2 == nullptr);
+ ASSERT_TRUE(iov.LastBlockEnd() == nullptr);
+ }
+ }
+}
+
+TEST(IOVectorTest, ConsumeTwoAndHalfBlocks) {
+ IOVector iov;
+ int length = 0;
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ const int str_len = strlen(test_data[i]);
+ iov.Append(const_cast<char*>(test_data[i]), str_len);
+ length += str_len;
+ }
+ const size_t last_len = strlen(test_data[ARRAYSIZE_UNSAFE(test_data) - 1]);
+ const size_t half_len = last_len / 2;
+
+ const char* endp = iov.LastBlockEnd();
+ size_t consumed = iov.Consume(length - half_len);
+ ASSERT_EQ(length - half_len, consumed);
+ const struct iovec* iov2 = iov.iovec();
+ ASSERT_TRUE(iov2 != nullptr);
+ ASSERT_EQ(1u, iov.Size());
+ ASSERT_TRUE(iov2[0].iov_base ==
+ test_data[ARRAYSIZE_UNSAFE(test_data) - 1] + last_len - half_len);
+ ASSERT_EQ(half_len, iov2[0].iov_len);
+ ASSERT_TRUE(iov.LastBlockEnd() == endp);
+
+ consumed = iov.Consume(half_len);
+ ASSERT_EQ(half_len, consumed);
+ iov2 = iov.iovec();
+ ASSERT_EQ(0u, iov.Size());
+ ASSERT_TRUE(iov2 == nullptr);
+ ASSERT_TRUE(iov.LastBlockEnd() == nullptr);
+}
+
+TEST(IOVectorTest, ConsumeTooMuch) {
+ IOVector iov;
+ int length = 0;
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ const int str_len = strlen(test_data[i]);
+ iov.Append(const_cast<char*>(test_data[i]), str_len);
+ length += str_len;
+ }
+
+ int consumed = 0;
+ consumed = iov.Consume(length);
+ // TODO(rtenneti): enable when chromium supports EXPECT_DFATAL.
+ /*
+ EXPECT_DFATAL(
+ {consumed = iov.Consume(length + 1);},
+ "Attempting to consume 1 non-existent bytes.");
+ */
+ ASSERT_EQ(length, consumed);
+ const struct iovec* iov2 = iov.iovec();
+ ASSERT_EQ(0u, iov.Size());
+ ASSERT_TRUE(iov2 == nullptr);
+ ASSERT_TRUE(iov.LastBlockEnd() == nullptr);
+}
+
+TEST(IOVectorTest, Clear) {
+ IOVector iov;
+ int length = 0;
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_data); ++i) {
+ const int str_len = strlen(test_data[i]);
+ iov.Append(const_cast<char*>(test_data[i]), str_len);
+ length += str_len;
+ }
+ const struct iovec* iov2 = iov.iovec();
+ ASSERT_TRUE(iov2 != nullptr);
+ ASSERT_EQ(ARRAYSIZE_UNSAFE(test_data), static_cast<size_t>(iov.Size()));
+
+ iov.Clear();
+ iov2 = iov.iovec();
+ ASSERT_EQ(0u, iov.Size());
+ ASSERT_TRUE(iov2 == nullptr);
+}
+
+TEST(IOVectorTest, Capacity) {
+ IOVector iov;
+ // Note: IOVector merges adjacent Appends() into a single iov.
+ // Therefore, if we expect final size of iov to be 3, we must insure
+ // that the items we are appending are not adjacent. To achieve that
+ // we use use an array (a[1] provides a buffer between a[0] and b[0],
+ // and makes them non-adjacent).
+ char a[2], b[2], c[2];
+ iov.Append(&a[0], 1);
+ iov.Append(&b[0], 1);
+ iov.Append(&c[0], 1);
+ ASSERT_EQ(3u, iov.Size());
+ size_t capacity = iov.Capacity();
+ EXPECT_LE(iov.Size(), capacity);
+ iov.Consume(2);
+ // The capacity should not have changed.
+ EXPECT_EQ(capacity, iov.Capacity());
+}
+
+TEST(IOVectorTest, Swap) {
+ IOVector iov1, iov2;
+ // See IOVector merge comment above.
+ char a[2], b[2], c[2], d[2], e[2];
+ iov1.Append(&a[0], 1);
+ iov1.Append(&b[0], 1);
+
+ iov2.Append(&c[0], 1);
+ iov2.Append(&d[0], 1);
+ iov2.Append(&e[0], 1);
+ iov1.Swap(&iov2);
+
+ ASSERT_EQ(3u, iov1.Size());
+ EXPECT_EQ(&c[0], iov1.iovec()[0].iov_base);
+ EXPECT_EQ(1u, iov1.iovec()[0].iov_len);
+ EXPECT_EQ(&d[0], iov1.iovec()[1].iov_base);
+ EXPECT_EQ(1u, iov1.iovec()[1].iov_len);
+ EXPECT_EQ(&e[0], iov1.iovec()[2].iov_base);
+ EXPECT_EQ(1u, iov1.iovec()[2].iov_len);
+
+ ASSERT_EQ(2u, iov2.Size());
+ EXPECT_EQ(&a[0], iov2.iovec()[0].iov_base);
+ EXPECT_EQ(1u, iov2.iovec()[0].iov_len);
+ EXPECT_EQ(&b[0], iov2.iovec()[1].iov_base);
+ EXPECT_EQ(1u, iov2.iovec()[1].iov_len);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/port_suggester.cc b/net/quic/port_suggester.cc
new file mode 100644
index 0000000..6b7940e
--- /dev/null
+++ b/net/quic/port_suggester.cc
@@ -0,0 +1,49 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/port_suggester.h"
+
+#include "base/logging.h"
+#include "net/base/host_port_pair.h"
+
+namespace net {
+
+PortSuggester::PortSuggester(const HostPortPair& server, uint64 seed)
+ : call_count_(0),
+ previous_suggestion_(-1) {
+ unsigned char hash_bytes[base::kSHA1Length];
+ base::SHA1HashBytes(
+ reinterpret_cast<const unsigned char*>(server.host().data()),
+ server.host().length(), hash_bytes);
+ COMPILE_ASSERT(sizeof(seed_) < sizeof(hash_bytes), seed_larger_than_hash);
+ memcpy(&seed_, hash_bytes, sizeof(seed_));
+ seed_ ^= seed ^ server.port();
+}
+
+int PortSuggester::SuggestPort(int min, int max) {
+ // Sometimes our suggestion can't be used, so we ensure that if additional
+ // calls are made, then each call (probably) provides a new suggestion.
+ if (++call_count_ > 1) {
+ // Evolve the seed.
+ unsigned char hash_bytes[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<const unsigned char *>(&seed_),
+ sizeof(seed_), hash_bytes);
+ memcpy(&seed_, hash_bytes, sizeof(seed_));
+ }
+ DCHECK_LE(min, max);
+ DCHECK_GT(min, 0);
+ int range = max - min + 1;
+ // Ports (and hence the extent of the |range|) are generally under 2^16, so
+ // the tiny non-uniformity in the pseudo-random distribution is not
+ // significant.
+ previous_suggestion_ = static_cast<int>(seed_ % range) + min;
+ return previous_suggestion_;
+}
+
+int PortSuggester::previous_suggestion() const {
+ DCHECK_LT(0u, call_count_);
+ return previous_suggestion_;
+}
+
+} // namespace net
diff --git a/net/quic/port_suggester.h b/net/quic/port_suggester.h
new file mode 100644
index 0000000..0074f7c
--- /dev/null
+++ b/net/quic/port_suggester.h
@@ -0,0 +1,50 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_PORT_SUGGESTER_H_
+#define NET_QUIC_PORT_SUGGESTER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/sha1.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class HostPortPair;
+
+// We provide a pseudo-random number generator that is always seeded the same
+// way for a given destination host-port pair. The generator is used to
+// consistently suggest (for that host-port pair) an ephemeral source port,
+// and hence increase the likelihood that a server's load balancer will direct
+// a repeated connection to the same server (with QUIC, further increasing the
+// chance of connection establishment with 0-RTT).
+class NET_EXPORT_PRIVATE PortSuggester
+ : public base::RefCounted<PortSuggester> {
+ public:
+ PortSuggester(const HostPortPair& server, uint64 seed);
+
+ // Generate a pseudo-random int in the inclusive range from |min| to |max|.
+ // Will (probably) return different numbers when called repeatedly.
+ int SuggestPort(int min, int max);
+
+ uint32 call_count() const { return call_count_; }
+ int previous_suggestion() const;
+
+ private:
+ friend class base::RefCounted<PortSuggester>;
+
+ virtual ~PortSuggester() {}
+
+ // We maintain the first 8 bytes of a hash as our seed_ state.
+ uint64 seed_;
+ uint32 call_count_; // Number of suggestions made.
+ int previous_suggestion_;
+
+ DISALLOW_COPY_AND_ASSIGN(PortSuggester);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_PORT_SUGGESTER_H_
diff --git a/net/quic/port_suggester_unittest.cc b/net/quic/port_suggester_unittest.cc
new file mode 100644
index 0000000..add5258
--- /dev/null
+++ b/net/quic/port_suggester_unittest.cc
@@ -0,0 +1,112 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/port_suggester.h"
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "net/base/host_port_pair.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class PortSuggesterTest : public ::testing::Test {
+ protected:
+ PortSuggesterTest()
+ : entropy_(1345689),
+ min_ephemeral_port_(1025),
+ max_ephemeral_port_(65535) {
+ }
+
+ uint64 entropy_;
+ int min_ephemeral_port_;
+ int max_ephemeral_port_;
+};
+
+TEST_F(PortSuggesterTest, SmallRangeTest) {
+ // When the range is small (one wide), we always get that as our answer.
+ scoped_refptr<PortSuggester> port_suggester =
+ new PortSuggester(HostPortPair("www.example.com", 443), entropy_);
+ // Test this for a few different (small) ranges.
+ for (int port = 2000; port < 2010; ++port) {
+ // Use |port| for both |min| and |max| delimiting the suggestion range.
+ EXPECT_EQ(port, port_suggester->SuggestPort(port, port));
+ EXPECT_EQ(port, port_suggester->previous_suggestion());
+ }
+}
+
+TEST_F(PortSuggesterTest, SuggestAllPorts) {
+ // We should eventually fill out any range, but we'll just ensure that we
+ // fill out a small range of ports.
+ scoped_refptr<PortSuggester> port_suggester =
+ new PortSuggester(HostPortPair("www.example.com", 443), entropy_);
+ std::set<int> ports;
+ const uint32 port_range = 20;
+ const int insertion_limit = 200; // We should be done by then.
+ for (int i = 0; i < insertion_limit; ++i) {
+ ports.insert(port_suggester->SuggestPort(min_ephemeral_port_,
+ min_ephemeral_port_ + port_range - 1));
+ if (ports.size() == port_range) {
+ break;
+ }
+ }
+ EXPECT_EQ(port_range, ports.size());
+}
+
+TEST_F(PortSuggesterTest, AvoidDuplication) {
+ // When the range is large, duplicates are rare, but we'll ask for a few
+ // suggestions and make sure they are unique.
+ scoped_refptr<PortSuggester> port_suggester =
+ new PortSuggester(HostPortPair("www.example.com", 80), entropy_);
+ std::set<int> ports;
+ const size_t port_count = 200;
+ for (size_t i = 0; i < port_count; ++i) {
+ ports.insert(port_suggester->SuggestPort(min_ephemeral_port_,
+ max_ephemeral_port_));
+ }
+ EXPECT_EQ(port_suggester->call_count(), port_count);
+ EXPECT_EQ(port_count, ports.size());
+}
+
+TEST_F(PortSuggesterTest, ConsistentPorts) {
+ // For given hostname, port, and entropy, we should always get the same
+ // suggestions.
+ scoped_refptr<PortSuggester> port_suggester1 =
+ new PortSuggester(HostPortPair("www.example.com", 443), entropy_);
+ scoped_refptr<PortSuggester> port_suggester2 =
+ new PortSuggester(HostPortPair("www.example.com", 443), entropy_);
+ for (int test_count = 20; test_count > 0; --test_count) {
+ EXPECT_EQ(port_suggester1->SuggestPort(min_ephemeral_port_,
+ min_ephemeral_port_),
+ port_suggester2->SuggestPort(min_ephemeral_port_,
+ min_ephemeral_port_));
+ }
+}
+
+TEST_F(PortSuggesterTest, DifferentHostPortEntropy) {
+ // When we have different hosts, port, or entropy, we probably won't collide.
+ scoped_refptr<PortSuggester> port_suggester[] = {
+ new PortSuggester(HostPortPair("www.example.com", 80), entropy_),
+ new PortSuggester(HostPortPair("www.example.ORG", 80), entropy_),
+ new PortSuggester(HostPortPair("www.example.com", 443), entropy_),
+ new PortSuggester(HostPortPair("www.example.com", 80), entropy_ + 123456),
+ };
+
+ std::set<int> ports;
+ const int port_count = 40;
+ size_t insertion_count = 0;
+ for (size_t j = 0; j < arraysize(port_suggester); ++j) {
+ for (int i = 0; i < port_count; ++i) {
+ ports.insert(port_suggester[j]->SuggestPort(min_ephemeral_port_,
+ max_ephemeral_port_));
+ ++insertion_count;
+ }
+ }
+ EXPECT_EQ(insertion_count, ports.size());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_ack_notifier.cc b/net/quic/quic_ack_notifier.cc
new file mode 100644
index 0000000..3bd65e5
--- /dev/null
+++ b/net/quic/quic_ack_notifier.cc
@@ -0,0 +1,85 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_ack_notifier.h"
+
+#include <set>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+using base::hash_map;
+using std::make_pair;
+
+namespace net {
+
+QuicAckNotifier::PacketInfo::PacketInfo() : packet_payload_size(0) {
+}
+
+QuicAckNotifier::PacketInfo::PacketInfo(int payload_size)
+ : packet_payload_size(payload_size) {
+}
+
+QuicAckNotifier::DelegateInterface::DelegateInterface() {}
+
+QuicAckNotifier::DelegateInterface::~DelegateInterface() {}
+
+QuicAckNotifier::QuicAckNotifier(DelegateInterface* delegate)
+ : delegate_(delegate),
+ original_packet_count_(0),
+ original_byte_count_(0),
+ retransmitted_packet_count_(0),
+ retransmitted_byte_count_(0) {
+ DCHECK(delegate);
+}
+
+QuicAckNotifier::~QuicAckNotifier() {
+}
+
+void QuicAckNotifier::AddSequenceNumber(
+ const QuicPacketSequenceNumber& sequence_number,
+ int packet_payload_size) {
+ sequence_numbers_.insert(make_pair(sequence_number,
+ PacketInfo(packet_payload_size)));
+ ++original_packet_count_;
+ original_byte_count_ += packet_payload_size;
+}
+
+bool QuicAckNotifier::OnAck(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
+ DCHECK(ContainsKey(sequence_numbers_, sequence_number));
+ sequence_numbers_.erase(sequence_number);
+ if (IsEmpty()) {
+ // We have seen all the sequence numbers we were waiting for, trigger
+ // callback notification.
+ delegate_->OnAckNotification(
+ original_packet_count_, original_byte_count_,
+ retransmitted_packet_count_, retransmitted_byte_count_,
+ delta_largest_observed);
+ return true;
+ }
+ return false;
+}
+
+void QuicAckNotifier::UpdateSequenceNumber(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ DCHECK(!ContainsKey(sequence_numbers_, new_sequence_number));
+
+ PacketInfo packet_info;
+ hash_map<QuicPacketSequenceNumber, PacketInfo>::iterator it =
+ sequence_numbers_.find(old_sequence_number);
+ if (it != sequence_numbers_.end()) {
+ packet_info = it->second;
+ sequence_numbers_.erase(it);
+ } else {
+ DLOG(DFATAL) << "Old sequence number not found.";
+ }
+
+ ++retransmitted_packet_count_;
+ retransmitted_byte_count_ += packet_info.packet_payload_size;
+ sequence_numbers_.insert(make_pair(new_sequence_number, packet_info));
+}
+
+}; // namespace net
diff --git a/net/quic/quic_ack_notifier.h b/net/quic/quic_ack_notifier.h
new file mode 100644
index 0000000..f19d0f8
--- /dev/null
+++ b/net/quic/quic_ack_notifier.h
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_ACK_NOTIFIER_H_
+#define NET_QUIC_QUIC_ACK_NOTIFIER_H_
+
+#include "base/memory/ref_counted.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// Used to register with a QuicConnection for notification once a set of packets
+// have all been ACKed.
+// The connection informs this class of newly ACKed sequence numbers, and once
+// we have seen ACKs for all the sequence numbers we are interested in, we
+// trigger a call to a provided Closure.
+class NET_EXPORT_PRIVATE QuicAckNotifier {
+ public:
+ class NET_EXPORT_PRIVATE DelegateInterface
+ : public base::RefCounted<DelegateInterface> {
+ public:
+ DelegateInterface();
+ // Args:
+ // num_original_packets - Number of packets in the original transmission.
+ // num_original_bytes - Number of packets in the original transmission.
+ // num_retransmitted_packets - Number of packets that had to be
+ // retransmitted.
+ // num_retransmitted_bytes - Number of bytes that had to be retransmitted.
+ virtual void OnAckNotification(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) = 0;
+ protected:
+ friend class base::RefCounted<DelegateInterface>;
+
+ // Delegates are ref counted.
+ virtual ~DelegateInterface();
+ };
+
+ // QuicAckNotifier is expected to keep its own reference to the delegate.
+ explicit QuicAckNotifier(DelegateInterface* delegate);
+ virtual ~QuicAckNotifier();
+
+ // Register a sequence number that this AckNotifier should be interested in.
+ void AddSequenceNumber(const QuicPacketSequenceNumber& sequence_number,
+ int packet_payload_size);
+
+ // Called by the QuicConnection on receipt of new ACK frame, with the sequence
+ // number referenced by the ACK frame.
+ // Deletes the matching sequence number from the stored set of sequence
+ // numbers. If this set is now empty, call the stored delegate's
+ // OnAckNotification method.
+ //
+ // Returns true if the provided sequence_number caused the delegate to be
+ // called, false otherwise.
+ bool OnAck(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
+
+ bool IsEmpty() { return sequence_numbers_.empty(); }
+
+ // If a packet is retransmitted by the connection it will be sent with a
+ // different sequence number. Updates our internal set of sequence_numbers to
+ // track the latest number.
+ void UpdateSequenceNumber(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number);
+
+ private:
+ struct PacketInfo {
+ PacketInfo();
+ explicit PacketInfo(int payload_size);
+
+ int packet_payload_size;
+ };
+
+ // The delegate's OnAckNotification() method will be called once we have been
+ // notified of ACKs for all the sequence numbers we are tracking.
+ // This is not owned by OnAckNotifier and must outlive it.
+ scoped_refptr<DelegateInterface> delegate_;
+
+ // Sequence numbers this notifier is waiting to hear about. The
+ // delegate will not be called until this is empty.
+ base::hash_map<QuicPacketSequenceNumber, PacketInfo> sequence_numbers_;
+
+ // Transmission and retransmission stats.
+ // Number of packets in the original transmission.
+ int original_packet_count_;
+ // Number of packets in the original transmission.
+ int original_byte_count_;
+ // Number of packets that had to be retransmitted.
+ int retransmitted_packet_count_;
+ // Number of bytes that had to be retransmitted.
+ int retransmitted_byte_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicAckNotifier);
+};
+
+}; // namespace net
+
+#endif // NET_QUIC_QUIC_ACK_NOTIFIER_H_
diff --git a/net/quic/quic_ack_notifier_manager.cc b/net/quic/quic_ack_notifier_manager.cc
new file mode 100644
index 0000000..1d9ac9a
--- /dev/null
+++ b/net/quic/quic_ack_notifier_manager.cc
@@ -0,0 +1,107 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_ack_notifier_manager.h"
+
+#include <stddef.h>
+#include <list>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/stl_util.h"
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+AckNotifierManager::AckNotifierManager() {}
+
+AckNotifierManager::~AckNotifierManager() {
+ STLDeleteElements(&ack_notifiers_);
+}
+
+void AckNotifierManager::OnPacketAcked(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
+ // Inform all the registered AckNotifiers of the new ACK.
+ AckNotifierMap::iterator map_it = ack_notifier_map_.find(sequence_number);
+ if (map_it == ack_notifier_map_.end()) {
+ // No AckNotifier is interested in this sequence number.
+ return;
+ }
+
+ // One or more AckNotifiers are registered as interested in this sequence
+ // number. Iterate through them and call OnAck on each.
+ for (AckNotifierSet::iterator set_it = map_it->second.begin();
+ set_it != map_it->second.end(); ++set_it) {
+ QuicAckNotifier* ack_notifier = *set_it;
+ ack_notifier->OnAck(sequence_number, delta_largest_observed);
+
+ // If this has resulted in an empty AckNotifer, erase it.
+ if (ack_notifier->IsEmpty()) {
+ delete ack_notifier;
+ ack_notifiers_.erase(ack_notifier);
+ }
+ }
+
+ // Remove the sequence number from the map as we have notified all the
+ // registered AckNotifiers, and we won't see it again.
+ ack_notifier_map_.erase(map_it);
+}
+
+void AckNotifierManager::UpdateSequenceNumber(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ AckNotifierMap::iterator map_it = ack_notifier_map_.find(old_sequence_number);
+ if (map_it != ack_notifier_map_.end()) {
+ // We will add an entry to the map for the new sequence number, and move
+ // the
+ // list of AckNotifiers over.
+ AckNotifierSet new_set;
+ for (AckNotifierSet::iterator notifier_it = map_it->second.begin();
+ notifier_it != map_it->second.end(); ++notifier_it) {
+ (*notifier_it)
+ ->UpdateSequenceNumber(old_sequence_number, new_sequence_number);
+ new_set.insert(*notifier_it);
+ }
+ ack_notifier_map_[new_sequence_number] = new_set;
+ ack_notifier_map_.erase(map_it);
+ }
+}
+
+void AckNotifierManager::OnSerializedPacket(
+ const SerializedPacket& serialized_packet) {
+ // Run through all the frames and if any of them are stream frames and have
+ // an AckNotifier registered, then inform the AckNotifier that it should be
+ // interested in this packet's sequence number.
+
+ RetransmittableFrames* frames = serialized_packet.retransmittable_frames;
+
+ // AckNotifiers can only be attached to retransmittable frames.
+ if (!frames) {
+ return;
+ }
+
+ for (QuicFrames::const_iterator it = frames->frames().begin();
+ it != frames->frames().end(); ++it) {
+ if (it->type == STREAM_FRAME && it->stream_frame->notifier != nullptr) {
+ QuicAckNotifier* notifier = it->stream_frame->notifier;
+
+ // The AckNotifier needs to know it is tracking this packet's sequence
+ // number.
+ notifier->AddSequenceNumber(serialized_packet.sequence_number,
+ serialized_packet.packet->length());
+
+ // Update the mapping in the other direction, from sequence
+ // number to AckNotifier.
+ ack_notifier_map_[serialized_packet.sequence_number].insert(notifier);
+
+ // Take ownership of the AckNotifier.
+ ack_notifiers_.insert(notifier);
+ }
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_ack_notifier_manager.h b/net/quic/quic_ack_notifier_manager.h
new file mode 100644
index 0000000..bf5b345
--- /dev/null
+++ b/net/quic/quic_ack_notifier_manager.h
@@ -0,0 +1,80 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_ACK_NOTIFIER_MANAGER_H_
+#define NET_QUIC_QUIC_ACK_NOTIFIER_MANAGER_H_
+
+#include <map>
+
+#include "base/containers/hash_tables.h"
+#include "net/quic/quic_protocol.h"
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template<>
+struct hash<net::QuicAckNotifier*> {
+ std::size_t operator()(const net::QuicAckNotifier* ptr) const {
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+ }
+};
+}
+#endif
+
+namespace net {
+
+class QuicAckNotifier;
+
+// The AckNotifierManager is used by the QuicSentPacketManager to keep track of
+// all the AckNotifiers currently active. It owns the AckNotifiers which it gets
+// from the serialized packets passed into OnSerializedPacket. It maintains both
+// a set of AckNotifiers and a map from sequence number to AckNotifier the sake
+// of efficiency - we can quickly check the map to see if any AckNotifiers are
+// interested in a given sequence number.
+class NET_EXPORT_PRIVATE AckNotifierManager {
+ public:
+ AckNotifierManager();
+ virtual ~AckNotifierManager();
+
+ // Called when the connection receives a new AckFrame. If |sequence_number|
+ // exists in ack_notifier_map_ then the corresponding AckNotifiers will have
+ // their OnAck method called.
+ void OnPacketAcked(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
+
+ // If a packet has been retransmitted with a new sequence number, then this
+ // will be called. It updates the mapping in ack_notifier_map_, and also
+ // updates the internal set of sequence numbers in each matching AckNotifier.
+ void UpdateSequenceNumber(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number);
+
+ // This is called after a packet has been serialized, is ready to be sent, and
+ // contains retransmittable frames (which may have associated AckNotifiers).
+ // If any of the retransmittable frames included in |serialized_packet| have
+ // AckNotifiers registered, then add them to our internal map and additionally
+ // inform the AckNotifier of the sequence number which it should track.
+ void OnSerializedPacket(const SerializedPacket& serialized_packet);
+
+ private:
+ typedef base::hash_set<QuicAckNotifier*> AckNotifierSet;
+ typedef std::map<QuicPacketSequenceNumber, AckNotifierSet> AckNotifierMap;
+
+ // On every ACK frame received by the connection, all the ack_notifiers_ will
+ // be told which sequeunce numbers were ACKed.
+ // Once a given QuicAckNotifier has seen all the sequence numbers it is
+ // interested in, it will be deleted, and removed from this set.
+ // Owns the AckNotifiers in this set.
+ AckNotifierSet ack_notifiers_;
+
+ // Maps from sequence number to the AckNotifiers which are registered
+ // for that sequence number. On receipt of an ACK for a given sequence
+ // number, call OnAck for all mapped AckNotifiers.
+ // Does not own the AckNotifiers.
+ AckNotifierMap ack_notifier_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AckNotifierManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_ACK_NOTIFIER_MANAGER_H_
diff --git a/net/quic/quic_ack_notifier_test.cc b/net/quic/quic_ack_notifier_test.cc
new file mode 100644
index 0000000..63bc254
--- /dev/null
+++ b/net/quic/quic_ack_notifier_test.cc
@@ -0,0 +1,80 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_ack_notifier.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class QuicAckNotifierTest : public ::testing::Test {
+ protected:
+ QuicAckNotifierTest() : zero_(QuicTime::Delta::Zero()) {}
+
+ virtual void SetUp() {
+ delegate_ = new MockAckNotifierDelegate;
+ notifier_.reset(new QuicAckNotifier(delegate_));
+
+ notifier_->AddSequenceNumber(26, 100);
+ notifier_->AddSequenceNumber(99, 20);
+ notifier_->AddSequenceNumber(1234, 3);
+ }
+
+ MockAckNotifierDelegate* delegate_;
+ scoped_ptr<QuicAckNotifier> notifier_;
+ QuicTime::Delta zero_;
+};
+
+// Should trigger callback when we receive acks for all the registered seqnums.
+TEST_F(QuicAckNotifierTest, TriggerCallback) {
+ EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 0, 0, zero_)).Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_));
+ EXPECT_FALSE(notifier_->OnAck(99, zero_));
+ EXPECT_TRUE(notifier_->OnAck(1234, zero_));
+}
+
+// Should not trigger callback if we never provide all the seqnums.
+TEST_F(QuicAckNotifierTest, DoesNotTrigger) {
+ // Should not trigger callback as not all packets have been seen.
+ EXPECT_CALL(*delegate_, OnAckNotification(_, _, _, _, _)).Times(0);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_));
+ EXPECT_FALSE(notifier_->OnAck(99, zero_));
+}
+
+// Should trigger even after updating sequence numbers and receiving ACKs for
+// new sequeunce numbers.
+TEST_F(QuicAckNotifierTest, UpdateSeqNums) {
+ // Update a couple of the sequence numbers (i.e. retransmitted packets)
+ notifier_->UpdateSequenceNumber(99, 3000);
+ notifier_->UpdateSequenceNumber(1234, 3001);
+
+ EXPECT_CALL(*delegate_, OnAckNotification(3, 123, 2, 20 + 3, _)).Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, zero_)); // original
+ EXPECT_FALSE(notifier_->OnAck(3000, zero_)); // updated
+ EXPECT_TRUE(notifier_->OnAck(3001, zero_)); // updated
+}
+
+// Make sure the delegate is called with the delta time from the last ACK.
+TEST_F(QuicAckNotifierTest, DeltaTime) {
+ const QuicTime::Delta first_delta = QuicTime::Delta::FromSeconds(5);
+ const QuicTime::Delta second_delta = QuicTime::Delta::FromSeconds(33);
+ const QuicTime::Delta third_delta = QuicTime::Delta::FromSeconds(10);
+
+ EXPECT_CALL(*delegate_,
+ OnAckNotification(3, 123, 0, 0, third_delta))
+ .Times(1);
+ EXPECT_FALSE(notifier_->OnAck(26, first_delta));
+ EXPECT_FALSE(notifier_->OnAck(99, second_delta));
+ EXPECT_TRUE(notifier_->OnAck(1234, third_delta));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_address_mismatch.cc b/net/quic/quic_address_mismatch.cc
new file mode 100644
index 0000000..96aaef4
--- /dev/null
+++ b/net/quic/quic_address_mismatch.cc
@@ -0,0 +1,52 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_address_mismatch.h"
+
+#include "base/logging.h"
+#include "net/base/ip_endpoint.h"
+
+namespace net {
+
+int GetAddressMismatch(const IPEndPoint& first_address,
+ const IPEndPoint& second_address) {
+ if (first_address.address().empty() || second_address.address().empty()) {
+ return -1;
+ }
+ IPAddressNumber first_ip_address = first_address.address();
+ if (IsIPv4Mapped(first_ip_address)) {
+ first_ip_address = ConvertIPv4MappedToIPv4(first_ip_address);
+ }
+ IPAddressNumber second_ip_address = second_address.address();
+ if (IsIPv4Mapped(second_ip_address)) {
+ second_ip_address = ConvertIPv4MappedToIPv4(second_ip_address);
+ }
+
+ int sample;
+ if (first_ip_address != second_ip_address) {
+ sample = QUIC_ADDRESS_MISMATCH_BASE;
+ } else if (first_address.port() != second_address.port()) {
+ sample = QUIC_PORT_MISMATCH_BASE;
+ } else {
+ sample = QUIC_ADDRESS_AND_PORT_MATCH_BASE;
+ }
+
+ // Add an offset to |sample|:
+ // V4_V4: add 0
+ // V6_V6: add 1
+ // V4_V6: add 2
+ // V6_V4: add 3
+ bool first_ipv4 = (first_ip_address.size() == kIPv4AddressSize);
+ bool second_ipv4 = (second_ip_address.size() == kIPv4AddressSize);
+ if (first_ipv4 != second_ipv4) {
+ CHECK_EQ(sample, QUIC_ADDRESS_MISMATCH_BASE);
+ sample += 2;
+ }
+ if (!first_ipv4) {
+ sample += 1;
+ }
+ return sample;
+}
+
+} // namespace net
diff --git a/net/quic/quic_address_mismatch.h b/net/quic/quic_address_mismatch.h
new file mode 100644
index 0000000..3179c20
--- /dev/null
+++ b/net/quic/quic_address_mismatch.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
+#define NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+class IPEndPoint;
+
+enum QuicAddressMismatch {
+ // The addresses don't match.
+ QUIC_ADDRESS_MISMATCH_BASE = 0,
+ QUIC_ADDRESS_MISMATCH_V4_V4 = 0,
+ QUIC_ADDRESS_MISMATCH_V6_V6 = 1,
+ QUIC_ADDRESS_MISMATCH_V4_V6 = 2,
+ QUIC_ADDRESS_MISMATCH_V6_V4 = 3,
+
+ // The addresses match, but the ports don't match.
+ QUIC_PORT_MISMATCH_BASE = 4,
+ QUIC_PORT_MISMATCH_V4_V4 = 4,
+ QUIC_PORT_MISMATCH_V6_V6 = 5,
+
+ QUIC_ADDRESS_AND_PORT_MATCH_BASE = 6,
+ QUIC_ADDRESS_AND_PORT_MATCH_V4_V4 = 6,
+ QUIC_ADDRESS_AND_PORT_MATCH_V6_V6 = 7,
+
+ QUIC_ADDRESS_MISMATCH_MAX,
+};
+
+// Returns a value of the QuicAddressMismatch enum type that indicates how
+// |first_address| differs from |second_address|. Returns -1 if either address
+// is empty.
+//
+// Only used by the Net.QuicSession.PublicResetAddressMismatch histogram.
+NET_EXPORT_PRIVATE int GetAddressMismatch(const IPEndPoint& first_address,
+ const IPEndPoint& second_address);
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_ADDRESS_MISMATCH_H_
diff --git a/net/quic/quic_address_mismatch_test.cc b/net/quic/quic_address_mismatch_test.cc
new file mode 100644
index 0000000..b7c683b
--- /dev/null
+++ b/net/quic/quic_address_mismatch_test.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_address_mismatch.h"
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+// Test all cases of the GetAddressMismatch function.
+TEST(QuicAddressMismatchTest, GetAddressMismatch) {
+ IPAddressNumber ip4_1;
+ IPAddressNumber ip4_2;
+ IPAddressNumber ip6_1;
+ IPAddressNumber ip6_2;
+ IPAddressNumber ip4_mapped_1;
+ IPAddressNumber ip4_mapped_2;
+ ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &ip4_1));
+ ASSERT_TRUE(ParseIPLiteralToNumber("5.6.7.8", &ip4_2));
+ ASSERT_TRUE(ParseIPLiteralToNumber("1234::1", &ip6_1));
+ ASSERT_TRUE(ParseIPLiteralToNumber("1234::2", &ip6_2));
+ ip4_mapped_1 = ConvertIPv4NumberToIPv6Number(ip4_1);
+ ip4_mapped_2 = ConvertIPv4NumberToIPv6Number(ip4_2);
+ ASSERT_NE(ip4_1, ip4_2);
+ ASSERT_NE(ip6_1, ip6_2);
+ ASSERT_NE(ip4_mapped_1, ip4_mapped_2);
+
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint()));
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(), IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(-1, GetAddressMismatch(IPEndPoint(ip4_1, 443), IPEndPoint()));
+
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_AND_PORT_MATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip6_1, 443)));
+
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_PORT_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip6_1, 443)));
+
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V4,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip4_mapped_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip6_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V6,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip6_2, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_1, 443),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 443),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_1, 80),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V4_V6,
+ GetAddressMismatch(IPEndPoint(ip4_mapped_1, 80),
+ IPEndPoint(ip6_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 443),
+ IPEndPoint(ip4_mapped_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip4_1, 443)));
+ EXPECT_EQ(QUIC_ADDRESS_MISMATCH_V6_V4,
+ GetAddressMismatch(IPEndPoint(ip6_1, 80),
+ IPEndPoint(ip4_mapped_1, 443)));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_alarm.cc b/net/quic/quic_alarm.cc
new file mode 100644
index 0000000..b5aca8c
--- /dev/null
+++ b/net/quic/quic_alarm.cc
@@ -0,0 +1,61 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_alarm.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+QuicAlarm::QuicAlarm(Delegate* delegate)
+ : delegate_(delegate),
+ deadline_(QuicTime::Zero()) {
+}
+
+QuicAlarm::~QuicAlarm() {}
+
+void QuicAlarm::Set(QuicTime deadline) {
+ DCHECK(!IsSet());
+ DCHECK(deadline.IsInitialized());
+ deadline_ = deadline;
+ SetImpl();
+}
+
+void QuicAlarm::Cancel() {
+ deadline_ = QuicTime::Zero();
+ CancelImpl();
+}
+
+void QuicAlarm::Update(QuicTime deadline, QuicTime::Delta granularity) {
+ if (!deadline.IsInitialized()) {
+ Cancel();
+ return;
+ }
+ if (std::abs(deadline.Subtract(deadline_).ToMicroseconds()) <
+ granularity.ToMicroseconds()) {
+ return;
+ }
+ Cancel();
+ Set(deadline);
+}
+
+bool QuicAlarm::IsSet() const {
+ return deadline_.IsInitialized();
+}
+
+void QuicAlarm::Fire() {
+ if (!deadline_.IsInitialized()) {
+ return;
+ }
+
+ deadline_ = QuicTime::Zero();
+ QuicTime deadline = delegate_->OnAlarm();
+ // delegate_->OnAlarm() might call Set(), in which case deadline_ will
+ // already contain the new value, so don't overwrite it.
+ if (!deadline_.IsInitialized() && deadline.IsInitialized()) {
+ Set(deadline);
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_alarm.h b/net/quic/quic_alarm.h
new file mode 100644
index 0000000..5df4fe0
--- /dev/null
+++ b/net/quic/quic_alarm.h
@@ -0,0 +1,82 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_ALARM_H_
+#define NET_QUIC_QUIC_ALARM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+// Abstract class which represents an alarm which will go off at a
+// scheduled time, and execute the |OnAlarm| method of the delegate.
+// An alarm may be cancelled, in which case it may or may not be
+// removed from the underlying scheduling system, but in either case
+// the task will not be executed.
+class NET_EXPORT_PRIVATE QuicAlarm {
+ public:
+ class NET_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Invoked when the alarm fires. If the return value is not
+ // infinite, then the alarm will be rescheduled at the
+ // specified time.
+ virtual QuicTime OnAlarm() = 0;
+ };
+
+ explicit QuicAlarm(Delegate* delegate);
+ virtual ~QuicAlarm();
+
+ // Sets the alarm to fire at |deadline|. Must not be called while
+ // the alarm is set. To reschedule an alarm, call Cancel() first,
+ // then Set().
+ void Set(QuicTime deadline);
+
+ // Cancels the alarm. May be called repeatedly. Does not
+ // guarantee that the underlying scheduling system will remove
+ // the alarm's associated task, but guarantees that the
+ // delegates OnAlarm method will not be called.
+ void Cancel();
+
+ // Cancels and sets the alarm if the |deadline| is farther from the current
+ // deadline than |granularity|, and otherwise does nothing. If |deadline| is
+ // not initialized, the alarm is cancelled.
+ void Update(QuicTime deadline, QuicTime::Delta granularity);
+
+ bool IsSet() const;
+
+ QuicTime deadline() const { return deadline_; }
+
+ protected:
+ // Subclasses implement this method to perform the platform-specific
+ // scheduling of the alarm. Is called from Set() or Fire(), after the
+ // deadline has been updated.
+ virtual void SetImpl() = 0;
+
+ // Subclasses implement this method to perform the platform-specific
+ // cancelation of the alarm.
+ virtual void CancelImpl() = 0;
+
+ // Called by subclasses when the alarm fires. Invokes the
+ // delegates |OnAlarm| if a delegate is set, and if the deadline
+ // has been exceeded. Implementations which do not remove the
+ // alarm from the underlying scheduler on Cancel() may need to handle
+ // the situation where the task executes before the deadline has been
+ // reached, in which case they need to reschedule the task and must not
+ // call invoke this method.
+ void Fire();
+
+ private:
+ scoped_ptr<Delegate> delegate_;
+ QuicTime deadline_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicAlarm);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_ALARM_H_
diff --git a/net/quic/quic_alarm_test.cc b/net/quic/quic_alarm_test.cc
new file mode 100644
index 0000000..434bb38
--- /dev/null
+++ b/net/quic/quic_alarm_test.cc
@@ -0,0 +1,143 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_alarm.h"
+
+#include "base/logging.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Return;
+using testing::Invoke;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockDelegate : public QuicAlarm::Delegate {
+ public:
+ MOCK_METHOD0(OnAlarm, QuicTime());
+};
+
+class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicAlarm::Delegate* delegate) : QuicAlarm(delegate) {}
+
+ bool scheduled() const { return scheduled_; }
+
+ void FireAlarm() {
+ scheduled_ = false;
+ Fire();
+ }
+
+ protected:
+ virtual void SetImpl() OVERRIDE {
+ DCHECK(deadline().IsInitialized());
+ scheduled_ = true;
+ }
+
+ virtual void CancelImpl() OVERRIDE {
+ DCHECK(!deadline().IsInitialized());
+ scheduled_ = false;
+ }
+
+ private:
+ bool scheduled_;
+};
+
+class QuicAlarmTest : public ::testing::Test {
+ public:
+ QuicAlarmTest()
+ : delegate_(new MockDelegate()),
+ alarm_(delegate_),
+ deadline_(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(7))),
+ deadline2_(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(14))),
+ new_deadline_(QuicTime::Zero()) {
+ }
+
+ void ResetAlarm() {
+ alarm_.Set(new_deadline_);
+ }
+
+ MockDelegate* delegate_; // not owned
+ TestAlarm alarm_;
+ QuicTime deadline_;
+ QuicTime deadline2_;
+ QuicTime new_deadline_;
+};
+
+TEST_F(QuicAlarmTest, IsSet) {
+ EXPECT_FALSE(alarm_.IsSet());
+}
+
+TEST_F(QuicAlarmTest, Set) {
+ QuicTime deadline = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(7));
+ alarm_.Set(deadline);
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline, alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, Cancel) {
+ QuicTime deadline = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(7));
+ alarm_.Set(deadline);
+ alarm_.Cancel();
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, Update) {
+ QuicTime deadline = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(7));
+ alarm_.Set(deadline);
+ QuicTime new_deadline = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(8));
+ alarm_.Update(new_deadline, QuicTime::Delta::Zero());
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(new_deadline, alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, UpdateWithZero) {
+ QuicTime deadline = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(7));
+ alarm_.Set(deadline);
+ alarm_.Update(QuicTime::Zero(), QuicTime::Delta::Zero());
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, Fire) {
+ QuicTime deadline = QuicTime::Zero().Add(QuicTime::Delta::FromSeconds(7));
+ alarm_.Set(deadline);
+ EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Return(QuicTime::Zero()));
+ alarm_.FireAlarm();
+ EXPECT_FALSE(alarm_.IsSet());
+ EXPECT_FALSE(alarm_.scheduled());
+ EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, FireAndResetViaReturn) {
+ alarm_.Set(deadline_);
+ EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Return(deadline2_));
+ alarm_.FireAlarm();
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline2_, alarm_.deadline());
+}
+
+TEST_F(QuicAlarmTest, FireAndResetViaSet) {
+ alarm_.Set(deadline_);
+ new_deadline_ = deadline2_;
+ EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(DoAll(
+ Invoke(this, &QuicAlarmTest::ResetAlarm),
+ Return(QuicTime::Zero())));
+ alarm_.FireAlarm();
+ EXPECT_TRUE(alarm_.IsSet());
+ EXPECT_TRUE(alarm_.scheduled());
+ EXPECT_EQ(deadline2_, alarm_.deadline());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_bandwidth.cc b/net/quic/quic_bandwidth.cc
new file mode 100644
index 0000000..cdd2986
--- /dev/null
+++ b/net/quic/quic_bandwidth.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_bandwidth.h"
+
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace net {
+
+// Highest number that QuicBandwidth can hold.
+const int64 kQuicInfiniteBandwidth = GG_INT64_C(0x7fffffffffffffff);
+
+// static
+QuicBandwidth QuicBandwidth::Zero() {
+ return QuicBandwidth(0);
+}
+
+// static
+QuicBandwidth QuicBandwidth::FromBitsPerSecond(int64 bits_per_second) {
+ return QuicBandwidth(bits_per_second);
+}
+
+// static
+QuicBandwidth QuicBandwidth::FromKBitsPerSecond(int64 k_bits_per_second) {
+ DCHECK(k_bits_per_second < kQuicInfiniteBandwidth / 1000);
+ return QuicBandwidth(k_bits_per_second * 1000);
+}
+
+// static
+QuicBandwidth QuicBandwidth::FromBytesPerSecond(int64 bytes_per_second) {
+ DCHECK(bytes_per_second < kQuicInfiniteBandwidth / 8);
+ return QuicBandwidth(bytes_per_second * 8);
+}
+
+// static
+QuicBandwidth QuicBandwidth::FromKBytesPerSecond(int64 k_bytes_per_second) {
+ DCHECK(k_bytes_per_second < kQuicInfiniteBandwidth / 8000);
+ return QuicBandwidth(k_bytes_per_second * 8000);
+}
+
+// static
+QuicBandwidth QuicBandwidth::FromBytesAndTimeDelta(QuicByteCount bytes,
+ QuicTime::Delta delta) {
+ DCHECK_LT(bytes,
+ static_cast<uint64>(kQuicInfiniteBandwidth /
+ (8 * base::Time::kMicrosecondsPerSecond)));
+ int64 bytes_per_second = (bytes * base::Time::kMicrosecondsPerSecond) /
+ delta.ToMicroseconds();
+ return QuicBandwidth(bytes_per_second * 8);
+}
+
+QuicBandwidth::QuicBandwidth(int64 bits_per_second)
+ : bits_per_second_(bits_per_second) {
+ DCHECK_GE(bits_per_second, 0);
+}
+
+int64 QuicBandwidth::ToBitsPerSecond() const {
+ return bits_per_second_;
+}
+
+int64 QuicBandwidth::ToKBitsPerSecond() const {
+ return bits_per_second_ / 1000;
+}
+
+int64 QuicBandwidth::ToBytesPerSecond() const {
+ return bits_per_second_ / 8;
+}
+
+int64 QuicBandwidth::ToKBytesPerSecond() const {
+ return bits_per_second_ / 8000;
+}
+
+QuicByteCount QuicBandwidth::ToBytesPerPeriod(
+ QuicTime::Delta time_period) const {
+ return ToBytesPerSecond() * time_period.ToMicroseconds() /
+ base::Time::kMicrosecondsPerSecond;
+}
+
+int64 QuicBandwidth::ToKBytesPerPeriod(QuicTime::Delta time_period) const {
+ return ToKBytesPerSecond() * time_period.ToMicroseconds() /
+ base::Time::kMicrosecondsPerSecond;
+}
+
+bool QuicBandwidth::IsZero() const {
+ return (bits_per_second_ == 0);
+}
+
+QuicBandwidth QuicBandwidth::Add(const QuicBandwidth& delta) const {
+ return QuicBandwidth(bits_per_second_ + delta.bits_per_second_);
+}
+
+QuicBandwidth QuicBandwidth::Subtract(const QuicBandwidth& delta) const {
+ return QuicBandwidth(bits_per_second_ - delta.bits_per_second_);
+}
+
+QuicBandwidth QuicBandwidth::Scale(float scale_factor) const {
+ return QuicBandwidth(bits_per_second_ * scale_factor);
+}
+
+QuicTime::Delta QuicBandwidth::TransferTime(QuicByteCount bytes) const {
+ return QuicTime::Delta::FromMicroseconds(
+ bytes * 8 * base::Time::kMicrosecondsPerSecond / bits_per_second_);
+}
+
+} // namespace net
diff --git a/net/quic/quic_bandwidth.h b/net/quic/quic_bandwidth.h
new file mode 100644
index 0000000..4ae953d
--- /dev/null
+++ b/net/quic/quic_bandwidth.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// QuicBandwidth represents a bandwidth, stored in bits per second resolution.
+
+#ifndef NET_QUIC_QUIC_BANDWIDTH_H_
+#define NET_QUIC_QUIC_BANDWIDTH_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+typedef uint64 QuicByteCount;
+
+class NET_EXPORT_PRIVATE QuicBandwidth {
+ public:
+ // Creates a new QuicBandwidth with an internal value of 0.
+ static QuicBandwidth Zero();
+
+ // Create a new QuicBandwidth holding the bits per second.
+ static QuicBandwidth FromBitsPerSecond(int64 bits_per_second);
+
+ // Create a new QuicBandwidth holding the kilo bits per second.
+ static QuicBandwidth FromKBitsPerSecond(int64 k_bits_per_second);
+
+ // Create a new QuicBandwidth holding the bytes per second.
+ static QuicBandwidth FromBytesPerSecond(int64 bytes_per_second);
+
+ // Create a new QuicBandwidth holding the kilo bytes per second.
+ static QuicBandwidth FromKBytesPerSecond(int64 k_bytes_per_second);
+
+ // Create a new QuicBandwidth based on the bytes per the elapsed delta.
+ static QuicBandwidth FromBytesAndTimeDelta(QuicByteCount bytes,
+ QuicTime::Delta delta);
+
+ int64 ToBitsPerSecond() const;
+
+ int64 ToKBitsPerSecond() const;
+
+ int64 ToBytesPerSecond() const;
+
+ int64 ToKBytesPerSecond() const;
+
+ QuicByteCount ToBytesPerPeriod(QuicTime::Delta time_period) const;
+
+ int64 ToKBytesPerPeriod(QuicTime::Delta time_period) const;
+
+ bool IsZero() const;
+
+ QuicBandwidth Add(const QuicBandwidth& delta) const;
+
+ QuicBandwidth Subtract(const QuicBandwidth& delta) const;
+
+ QuicBandwidth Scale(float scale_factor) const;
+
+ QuicTime::Delta TransferTime(QuicByteCount bytes) const;
+
+ private:
+ explicit QuicBandwidth(int64 bits_per_second);
+ int64 bits_per_second_;
+};
+
+// Non-member relational operators for QuicBandwidth.
+inline bool operator==(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return lhs.ToBitsPerSecond() == rhs.ToBitsPerSecond();
+}
+inline bool operator!=(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return !(lhs == rhs);
+}
+inline bool operator<(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return lhs.ToBitsPerSecond() < rhs.ToBitsPerSecond();
+}
+inline bool operator>(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return rhs < lhs;
+}
+inline bool operator<=(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return !(rhs < lhs);
+}
+inline bool operator>=(QuicBandwidth lhs, QuicBandwidth rhs) {
+ return !(lhs < rhs);
+}
+
+} // namespace net
+#endif // NET_QUIC_QUIC_BANDWIDTH_H_
diff --git a/net/quic/quic_bandwidth_test.cc b/net/quic/quic_bandwidth_test.cc
new file mode 100644
index 0000000..e9913db
--- /dev/null
+++ b/net/quic/quic_bandwidth_test.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_bandwidth.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class QuicBandwidthTest : public ::testing::Test {
+};
+
+TEST_F(QuicBandwidthTest, FromTo) {
+ EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(1),
+ QuicBandwidth::FromBitsPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1),
+ QuicBandwidth::FromBytesPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromBitsPerSecond(8000),
+ QuicBandwidth::FromBytesPerSecond(1000));
+ EXPECT_EQ(QuicBandwidth::FromKBitsPerSecond(8),
+ QuicBandwidth::FromKBytesPerSecond(1));
+
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToBitsPerSecond());
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToKBitsPerSecond());
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToBytesPerSecond());
+ EXPECT_EQ(0, QuicBandwidth::Zero().ToKBytesPerSecond());
+
+ EXPECT_EQ(1, QuicBandwidth::FromBitsPerSecond(1000).ToKBitsPerSecond());
+ EXPECT_EQ(1000, QuicBandwidth::FromKBitsPerSecond(1).ToBitsPerSecond());
+ EXPECT_EQ(1, QuicBandwidth::FromBytesPerSecond(1000).ToKBytesPerSecond());
+ EXPECT_EQ(1000, QuicBandwidth::FromKBytesPerSecond(1).ToBytesPerSecond());
+}
+
+TEST_F(QuicBandwidthTest, Add) {
+ QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1);
+ QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1);
+
+ EXPECT_EQ(9000, bandwidht_1.Add(bandwidht_2).ToBitsPerSecond());
+ EXPECT_EQ(9000, bandwidht_2.Add(bandwidht_1).ToBitsPerSecond());
+}
+
+TEST_F(QuicBandwidthTest, Subtract) {
+ QuicBandwidth bandwidht_1 = QuicBandwidth::FromKBitsPerSecond(1);
+ QuicBandwidth bandwidht_2 = QuicBandwidth::FromKBytesPerSecond(1);
+
+ EXPECT_EQ(7000, bandwidht_2.Subtract(bandwidht_1).ToBitsPerSecond());
+}
+
+TEST_F(QuicBandwidthTest, TimeDelta) {
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1000),
+ QuicBandwidth::FromBytesAndTimeDelta(
+ 1000, QuicTime::Delta::FromMilliseconds(1)));
+
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(10),
+ QuicBandwidth::FromBytesAndTimeDelta(
+ 1000, QuicTime::Delta::FromMilliseconds(100)));
+}
+
+TEST_F(QuicBandwidthTest, Scale) {
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(500),
+ QuicBandwidth::FromKBytesPerSecond(1000).Scale(0.5f));
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(750),
+ QuicBandwidth::FromKBytesPerSecond(1000).Scale(0.75f));
+ EXPECT_EQ(QuicBandwidth::FromKBytesPerSecond(1250),
+ QuicBandwidth::FromKBytesPerSecond(1000).Scale(1.25f));
+}
+
+
+TEST_F(QuicBandwidthTest, BytesPerPeriod) {
+ EXPECT_EQ(2000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(1)));
+ EXPECT_EQ(2u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(1)));
+ EXPECT_EQ(200000u, QuicBandwidth::FromKBytesPerSecond(2000).ToBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(100)));
+ EXPECT_EQ(200u, QuicBandwidth::FromKBytesPerSecond(2000).ToKBytesPerPeriod(
+ QuicTime::Delta::FromMilliseconds(100)));
+}
+
+TEST_F(QuicBandwidthTest, TransferTime) {
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1),
+ QuicBandwidth::FromKBytesPerSecond(1).TransferTime(1000));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_blocked_writer_interface.h b/net/quic/quic_blocked_writer_interface.h
new file mode 100644
index 0000000..1c3a6fe
--- /dev/null
+++ b/net/quic/quic_blocked_writer_interface.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is an interface for all objects that want to be notified that
+// the underlying UDP socket is available for writing (not write blocked
+// anymore).
+
+#ifndef NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_
+#define NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicBlockedWriterInterface {
+ public:
+ virtual ~QuicBlockedWriterInterface() {}
+
+ // Called by the PacketWriter when the underlying socket becomes writable
+ // so that the BlockedWriter can go ahead and try writing.
+ virtual void OnCanWrite() = 0;
+};
+
+} // namespace net
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+// Hash pointers as if they were int's, but bring more entropy to the lower
+// bits.
+template <>
+struct hash<net::QuicBlockedWriterInterface*> {
+ std::size_t operator()(const net::QuicBlockedWriterInterface* ptr) const {
+ size_t k = reinterpret_cast<size_t>(ptr);
+ return k + (k >> 6);
+ }
+};
+}
+#endif
+
+#endif // NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
new file mode 100644
index 0000000..5454c10
--- /dev/null
+++ b/net/quic/quic_client_session.cc
@@ -0,0 +1,872 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_client_session.h"
+
+#include "base/callback_helpers.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/transport_security_state.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/crypto/quic_server_info.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_crypto_client_stream_factory.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/quic_stream_factory.h"
+#include "net/spdy/spdy_session.h"
+#include "net/ssl/channel_id_service.h"
+#include "net/ssl/ssl_connection_status_flags.h"
+#include "net/ssl/ssl_info.h"
+#include "net/udp/datagram_client_socket.h"
+
+namespace net {
+
+namespace {
+
+// The length of time to wait for a 0-RTT handshake to complete
+// before allowing the requests to possibly proceed over TCP.
+const int k0RttHandshakeTimeoutMs = 300;
+
+// Histograms for tracking down the crashes from http://crbug.com/354669
+// Note: these values must be kept in sync with the corresponding values in:
+// tools/metrics/histograms/histograms.xml
+enum Location {
+ DESTRUCTOR = 0,
+ ADD_OBSERVER = 1,
+ TRY_CREATE_STREAM = 2,
+ CREATE_OUTGOING_RELIABLE_STREAM = 3,
+ NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER = 4,
+ NOTIFY_FACTORY_OF_SESSION_CLOSED = 5,
+ NUM_LOCATIONS = 6,
+};
+
+void RecordUnexpectedOpenStreams(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedOpenStreams", location,
+ NUM_LOCATIONS);
+}
+
+void RecordUnexpectedObservers(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedObservers", location,
+ NUM_LOCATIONS);
+}
+
+void RecordUnexpectedNotGoingAway(Location location) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.UnexpectedNotGoingAway", location,
+ NUM_LOCATIONS);
+}
+
+// Histogram for recording the different reasons that a QUIC session is unable
+// to complete the handshake.
+enum HandshakeFailureReason {
+ HANDSHAKE_FAILURE_UNKNOWN = 0,
+ HANDSHAKE_FAILURE_BLACK_HOLE = 1,
+ HANDSHAKE_FAILURE_PUBLIC_RESET = 2,
+ NUM_HANDSHAKE_FAILURE_REASONS = 3,
+};
+
+void RecordHandshakeFailureReason(HandshakeFailureReason reason) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "Net.QuicSession.ConnectionClose.HandshakeNotConfirmed.Reason",
+ reason, NUM_HANDSHAKE_FAILURE_REASONS);
+}
+
+// Note: these values must be kept in sync with the corresponding values in:
+// tools/metrics/histograms/histograms.xml
+enum HandshakeState {
+ STATE_STARTED = 0,
+ STATE_ENCRYPTION_ESTABLISHED = 1,
+ STATE_HANDSHAKE_CONFIRMED = 2,
+ STATE_FAILED = 3,
+ NUM_HANDSHAKE_STATES = 4
+};
+
+void RecordHandshakeState(HandshakeState state) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state,
+ NUM_HANDSHAKE_STATES);
+}
+
+} // namespace
+
+QuicClientSession::StreamRequest::StreamRequest() : stream_(nullptr) {}
+
+QuicClientSession::StreamRequest::~StreamRequest() {
+ CancelRequest();
+}
+
+int QuicClientSession::StreamRequest::StartRequest(
+ const base::WeakPtr<QuicClientSession>& session,
+ QuicReliableClientStream** stream,
+ const CompletionCallback& callback) {
+ session_ = session;
+ stream_ = stream;
+ int rv = session_->TryCreateStream(this, stream_);
+ if (rv == ERR_IO_PENDING) {
+ callback_ = callback;
+ }
+
+ return rv;
+}
+
+void QuicClientSession::StreamRequest::CancelRequest() {
+ if (session_)
+ session_->CancelRequest(this);
+ session_.reset();
+ callback_.Reset();
+}
+
+void QuicClientSession::StreamRequest::OnRequestCompleteSuccess(
+ QuicReliableClientStream* stream) {
+ session_.reset();
+ *stream_ = stream;
+ ResetAndReturn(&callback_).Run(OK);
+}
+
+void QuicClientSession::StreamRequest::OnRequestCompleteFailure(int rv) {
+ session_.reset();
+ ResetAndReturn(&callback_).Run(rv);
+}
+
+QuicClientSession::QuicClientSession(
+ QuicConnection* connection,
+ scoped_ptr<DatagramClientSocket> socket,
+ QuicStreamFactory* stream_factory,
+ TransportSecurityState* transport_security_state,
+ scoped_ptr<QuicServerInfo> server_info,
+ const QuicConfig& config,
+ base::TaskRunner* task_runner,
+ NetLog* net_log)
+ : QuicClientSessionBase(connection, config),
+ require_confirmation_(false),
+ stream_factory_(stream_factory),
+ socket_(socket.Pass()),
+ read_buffer_(new IOBufferWithSize(kMaxPacketSize)),
+ transport_security_state_(transport_security_state),
+ server_info_(server_info.Pass()),
+ read_pending_(false),
+ num_total_streams_(0),
+ task_runner_(task_runner),
+ net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)),
+ logger_(new QuicConnectionLogger(net_log_)),
+ num_packets_read_(0),
+ going_away_(false),
+ weak_factory_(this) {
+ connection->set_debug_visitor(logger_);
+}
+
+void QuicClientSession::InitializeSession(
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* crypto_config,
+ QuicCryptoClientStreamFactory* crypto_client_stream_factory) {
+ server_host_port_ = server_id.host_port_pair();
+ crypto_stream_.reset(
+ crypto_client_stream_factory ?
+ crypto_client_stream_factory->CreateQuicCryptoClientStream(
+ server_id, this, crypto_config) :
+ new QuicCryptoClientStream(server_id, this,
+ new ProofVerifyContextChromium(net_log_),
+ crypto_config));
+ QuicClientSessionBase::InitializeSession();
+ // TODO(rch): pass in full host port proxy pair
+ net_log_.BeginEvent(
+ NetLog::TYPE_QUIC_SESSION,
+ NetLog::StringCallback("host", &server_id.host()));
+}
+
+QuicClientSession::~QuicClientSession() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(DESTRUCTOR);
+ if (!observers_.empty())
+ RecordUnexpectedObservers(DESTRUCTOR);
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(DESTRUCTOR);
+
+ while (!streams()->empty() ||
+ !observers_.empty() ||
+ !stream_requests_.empty()) {
+ // The session must be closed before it is destroyed.
+ DCHECK(streams()->empty());
+ CloseAllStreams(ERR_UNEXPECTED);
+ DCHECK(observers_.empty());
+ CloseAllObservers(ERR_UNEXPECTED);
+
+ connection()->set_debug_visitor(nullptr);
+ net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
+
+ while (!stream_requests_.empty()) {
+ StreamRequest* request = stream_requests_.front();
+ stream_requests_.pop_front();
+ request->OnRequestCompleteFailure(ERR_ABORTED);
+ }
+ }
+
+ if (connection()->connected()) {
+ // Ensure that the connection is closed by the time the session is
+ // destroyed.
+ connection()->CloseConnection(QUIC_INTERNAL_ERROR, false);
+ }
+
+ if (IsEncryptionEstablished())
+ RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED);
+ if (IsCryptoHandshakeConfirmed())
+ RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED);
+ else
+ RecordHandshakeState(STATE_FAILED);
+
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumTotalStreams", num_total_streams_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos",
+ crypto_stream_->num_sent_client_hellos());
+ if (!IsCryptoHandshakeConfirmed())
+ return;
+
+ // Sending one client_hello means we had zero handshake-round-trips.
+ int round_trip_handshakes = crypto_stream_->num_sent_client_hellos() - 1;
+
+ // Don't bother with these histogram during tests, which mock out
+ // num_sent_client_hellos().
+ if (round_trip_handshakes < 0 || !stream_factory_)
+ return;
+
+ bool port_selected = stream_factory_->enable_port_selection();
+ SSLInfo ssl_info;
+ if (!GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) {
+ if (port_selected) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ if (require_confirmation_) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTP",
+ round_trip_handshakes, 0, 3, 4);
+ }
+ }
+ } else {
+ if (port_selected) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectSelectPortForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.ConnectRandomPortForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ if (require_confirmation_) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.ConnectRandomPortRequiringConfirmationForHTTPS",
+ round_trip_handshakes, 0, 3, 4);
+ }
+ }
+ }
+ const QuicConnectionStats stats = connection()->GetStats();
+ if (stats.max_sequence_reordering == 0)
+ return;
+ const uint64 kMaxReordering = 100;
+ uint64 reordering = kMaxReordering;
+ if (stats.min_rtt_us > 0 ) {
+ reordering =
+ GG_UINT64_C(100) * stats.max_time_reordering_us / stats.min_rtt_us;
+ }
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTime",
+ reordering, 0, kMaxReordering, 50);
+ if (stats.min_rtt_us > 100 * 1000) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS("Net.QuicSession.MaxReorderingTimeLongRtt",
+ reordering, 0, kMaxReordering, 50);
+ }
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.MaxReordering",
+ stats.max_sequence_reordering);
+}
+
+void QuicClientSession::OnStreamFrames(
+ const std::vector<QuicStreamFrame>& frames) {
+ // Record total number of stream frames.
+ UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesInPacket", frames.size());
+
+ // Record number of frames per stream in packet.
+ typedef std::map<QuicStreamId, size_t> FrameCounter;
+ FrameCounter frames_per_stream;
+ for (size_t i = 0; i < frames.size(); ++i) {
+ frames_per_stream[frames[i].stream_id]++;
+ }
+ for (FrameCounter::const_iterator it = frames_per_stream.begin();
+ it != frames_per_stream.end(); ++it) {
+ UMA_HISTOGRAM_COUNTS("Net.QuicNumStreamFramesPerStreamInPacket",
+ it->second);
+ }
+
+ return QuicSession::OnStreamFrames(frames);
+}
+
+void QuicClientSession::AddObserver(Observer* observer) {
+ if (going_away_) {
+ RecordUnexpectedObservers(ADD_OBSERVER);
+ observer->OnSessionClosed(ERR_UNEXPECTED);
+ return;
+ }
+
+ DCHECK(!ContainsKey(observers_, observer));
+ observers_.insert(observer);
+}
+
+void QuicClientSession::RemoveObserver(Observer* observer) {
+ DCHECK(ContainsKey(observers_, observer));
+ observers_.erase(observer);
+}
+
+int QuicClientSession::TryCreateStream(StreamRequest* request,
+ QuicReliableClientStream** stream) {
+ if (!crypto_stream_->encryption_established()) {
+ DLOG(DFATAL) << "Encryption not established.";
+ return ERR_CONNECTION_CLOSED;
+ }
+
+ if (goaway_received()) {
+ DVLOG(1) << "Going away.";
+ return ERR_CONNECTION_CLOSED;
+ }
+
+ if (!connection()->connected()) {
+ DVLOG(1) << "Already closed.";
+ return ERR_CONNECTION_CLOSED;
+ }
+
+ if (going_away_) {
+ RecordUnexpectedOpenStreams(TRY_CREATE_STREAM);
+ return ERR_CONNECTION_CLOSED;
+ }
+
+ if (GetNumOpenStreams() < get_max_open_streams()) {
+ *stream = CreateOutgoingReliableStreamImpl();
+ return OK;
+ }
+
+ stream_requests_.push_back(request);
+ return ERR_IO_PENDING;
+}
+
+void QuicClientSession::CancelRequest(StreamRequest* request) {
+ // Remove |request| from the queue while preserving the order of the
+ // other elements.
+ StreamRequestQueue::iterator it =
+ std::find(stream_requests_.begin(), stream_requests_.end(), request);
+ if (it != stream_requests_.end()) {
+ it = stream_requests_.erase(it);
+ }
+}
+
+QuicReliableClientStream* QuicClientSession::CreateOutgoingDataStream() {
+ if (!crypto_stream_->encryption_established()) {
+ DVLOG(1) << "Encryption not active so no outgoing stream created.";
+ return nullptr;
+ }
+ if (GetNumOpenStreams() >= get_max_open_streams()) {
+ DVLOG(1) << "Failed to create a new outgoing stream. "
+ << "Already " << GetNumOpenStreams() << " open.";
+ return nullptr;
+ }
+ if (goaway_received()) {
+ DVLOG(1) << "Failed to create a new outgoing stream. "
+ << "Already received goaway.";
+ return nullptr;
+ }
+ if (going_away_) {
+ RecordUnexpectedOpenStreams(CREATE_OUTGOING_RELIABLE_STREAM);
+ return nullptr;
+ }
+ return CreateOutgoingReliableStreamImpl();
+}
+
+QuicReliableClientStream*
+QuicClientSession::CreateOutgoingReliableStreamImpl() {
+ DCHECK(connection()->connected());
+ QuicReliableClientStream* stream =
+ new QuicReliableClientStream(GetNextStreamId(), this, net_log_);
+ ActivateStream(stream);
+ ++num_total_streams_;
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.NumOpenStreams", GetNumOpenStreams());
+ return stream;
+}
+
+QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
+ return crypto_stream_.get();
+};
+
+// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways
+// we learn about SSL info (sync vs async vs cached).
+bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) const {
+ ssl_info->Reset();
+ if (!cert_verify_result_) {
+ return false;
+ }
+
+ ssl_info->cert_status = cert_verify_result_->cert_status;
+ ssl_info->cert = cert_verify_result_->verified_cert;
+
+ // TODO(wtc): Define QUIC "cipher suites".
+ // Report the TLS cipher suite that most closely resembles the crypto
+ // parameters of the QUIC connection.
+ QuicTag aead = crypto_stream_->crypto_negotiated_params().aead;
+ int cipher_suite;
+ int security_bits;
+ switch (aead) {
+ case kAESG:
+ cipher_suite = 0xc02f; // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ security_bits = 128;
+ break;
+ case kCC12:
+ cipher_suite = 0xcc13; // TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ security_bits = 256;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ int ssl_connection_status = 0;
+ ssl_connection_status |=
+ (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) <<
+ SSL_CONNECTION_CIPHERSUITE_SHIFT;
+ ssl_connection_status |=
+ (SSL_CONNECTION_VERSION_QUIC & SSL_CONNECTION_VERSION_MASK) <<
+ SSL_CONNECTION_VERSION_SHIFT;
+
+ ssl_info->public_key_hashes = cert_verify_result_->public_key_hashes;
+ ssl_info->is_issued_by_known_root =
+ cert_verify_result_->is_issued_by_known_root;
+
+ ssl_info->connection_status = ssl_connection_status;
+ ssl_info->client_cert_sent = false;
+ ssl_info->channel_id_sent = crypto_stream_->WasChannelIDSent();
+ ssl_info->security_bits = security_bits;
+ ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL;
+ ssl_info->pinning_failure_log = pinning_failure_log_;
+ return true;
+}
+
+int QuicClientSession::CryptoConnect(bool require_confirmation,
+ const CompletionCallback& callback) {
+ require_confirmation_ = require_confirmation;
+ handshake_start_ = base::TimeTicks::Now();
+ RecordHandshakeState(STATE_STARTED);
+ DCHECK(flow_controller());
+ if (!crypto_stream_->CryptoConnect()) {
+ // TODO(wtc): change crypto_stream_.CryptoConnect() to return a
+ // QuicErrorCode and map it to a net error code.
+ return ERR_CONNECTION_FAILED;
+ }
+
+ if (IsCryptoHandshakeConfirmed())
+ return OK;
+
+ // Unless we require handshake confirmation, activate the session if
+ // we have established initial encryption.
+ if (!require_confirmation_ && IsEncryptionEstablished()) {
+ // To mitigate the effects of hanging 0-RTT connections, set up a timer to
+ // cancel any requests, if the handshake takes too long.
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&QuicClientSession::OnConnectTimeout,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(k0RttHandshakeTimeoutMs));
+ return OK;
+
+ }
+
+ callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int QuicClientSession::ResumeCryptoConnect(const CompletionCallback& callback) {
+
+ if (IsCryptoHandshakeConfirmed())
+ return OK;
+
+ if (!connection()->connected())
+ return ERR_QUIC_HANDSHAKE_FAILED;
+
+ callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int QuicClientSession::GetNumSentClientHellos() const {
+ return crypto_stream_->num_sent_client_hellos();
+}
+
+bool QuicClientSession::CanPool(const std::string& hostname) const {
+ DCHECK(connection()->connected());
+ SSLInfo ssl_info;
+ if (!GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) {
+ // We can always pool with insecure QUIC sessions.
+ return true;
+ }
+
+ return SpdySession::CanPool(transport_security_state_, ssl_info,
+ server_host_port_.host(), hostname);
+}
+
+QuicDataStream* QuicClientSession::CreateIncomingDataStream(
+ QuicStreamId id) {
+ DLOG(ERROR) << "Server push not supported";
+ return nullptr;
+}
+
+void QuicClientSession::CloseStream(QuicStreamId stream_id) {
+ ReliableQuicStream* stream = GetStream(stream_id);
+ if (stream) {
+ logger_->UpdateReceivedFrameCounts(
+ stream_id, stream->num_frames_received(),
+ stream->num_duplicate_frames_received());
+ }
+ QuicSession::CloseStream(stream_id);
+ OnClosedStream();
+}
+
+void QuicClientSession::SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QuicSession::SendRstStream(id, error, bytes_written);
+ OnClosedStream();
+}
+
+void QuicClientSession::OnClosedStream() {
+ if (GetNumOpenStreams() < get_max_open_streams() &&
+ !stream_requests_.empty() &&
+ crypto_stream_->encryption_established() &&
+ !goaway_received() &&
+ !going_away_ &&
+ connection()->connected()) {
+ StreamRequest* request = stream_requests_.front();
+ stream_requests_.pop_front();
+ request->OnRequestCompleteSuccess(CreateOutgoingReliableStreamImpl());
+ }
+
+ if (GetNumOpenStreams() == 0) {
+ stream_factory_->OnIdleSession(this);
+ }
+}
+
+void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ if (!callback_.is_null() &&
+ (!require_confirmation_ || event == HANDSHAKE_CONFIRMED)) {
+ // TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_
+ // could be called because there are no error events in CryptoHandshakeEvent
+ // enum. If error events are added to CryptoHandshakeEvent, then the
+ // following code needs to changed.
+ base::ResetAndReturn(&callback_).Run(OK);
+ }
+ if (event == HANDSHAKE_CONFIRMED) {
+ UMA_HISTOGRAM_TIMES("Net.QuicSession.HandshakeConfirmedTime",
+ base::TimeTicks::Now() - handshake_start_);
+ ObserverSet::iterator it = observers_.begin();
+ while (it != observers_.end()) {
+ Observer* observer = *it;
+ ++it;
+ observer->OnCryptoHandshakeConfirmed();
+ }
+ }
+ QuicSession::OnCryptoHandshakeEvent(event);
+}
+
+void QuicClientSession::OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message) {
+ logger_->OnCryptoHandshakeMessageSent(message);
+}
+
+void QuicClientSession::OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message) {
+ logger_->OnCryptoHandshakeMessageReceived(message);
+}
+
+void QuicClientSession::OnConnectionClosed(QuicErrorCode error,
+ bool from_peer) {
+ DCHECK(!connection()->connected());
+ logger_->OnConnectionClosed(error, from_peer);
+ if (from_peer) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionCloseErrorCodeServer", error);
+ } else {
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionCloseErrorCodeClient", error);
+ }
+
+ if (error == QUIC_CONNECTION_TIMED_OUT) {
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.ConnectionClose.NumOpenStreams.TimedOut",
+ GetNumOpenStreams());
+ if (IsCryptoHandshakeConfirmed()) {
+ if (GetNumOpenStreams() > 0) {
+ UMA_HISTOGRAM_BOOLEAN(
+ "Net.QuicSession.TimedOutWithOpenStreams.HasUnackedPackets",
+ connection()->sent_packet_manager().HasUnackedPackets());
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveRTOCount",
+ connection()->sent_packet_manager().consecutive_rto_count());
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.TimedOutWithOpenStreams.ConsecutiveTLPCount",
+ connection()->sent_packet_manager().consecutive_tlp_count());
+ }
+ } else {
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.ConnectionClose.NumOpenStreams.HandshakeTimedOut",
+ GetNumOpenStreams());
+ UMA_HISTOGRAM_COUNTS(
+ "Net.QuicSession.ConnectionClose.NumTotalStreams.HandshakeTimedOut",
+ num_total_streams_);
+ }
+ }
+
+ if (!IsCryptoHandshakeConfirmed()) {
+ if (error == QUIC_PUBLIC_RESET) {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET);
+ } else if (connection()->GetStats().packets_received == 0) {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_BLACK_HOLE);
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionClose.HandshakeFailureBlackHole.QuicError",
+ error);
+ } else {
+ RecordHandshakeFailureReason(HANDSHAKE_FAILURE_UNKNOWN);
+ UMA_HISTOGRAM_SPARSE_SLOWLY(
+ "Net.QuicSession.ConnectionClose.HandshakeFailureUnknown.QuicError",
+ error);
+ }
+ }
+
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion",
+ connection()->version());
+ NotifyFactoryOfSessionGoingAway();
+ if (!callback_.is_null()) {
+ base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR);
+ }
+ socket_->Close();
+ QuicSession::OnConnectionClosed(error, from_peer);
+ DCHECK(streams()->empty());
+ CloseAllStreams(ERR_UNEXPECTED);
+ CloseAllObservers(ERR_UNEXPECTED);
+ NotifyFactoryOfSessionClosedLater();
+}
+
+void QuicClientSession::OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) {
+ logger_->OnSuccessfulVersionNegotiation(version);
+ QuicSession::OnSuccessfulVersionNegotiation(version);
+}
+
+void QuicClientSession::OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) {
+ DCHECK(cached.proof_valid());
+
+ if (!server_info_ || !server_info_->IsReadyToPersist()) {
+ return;
+ }
+
+ QuicServerInfo::State* state = server_info_->mutable_state();
+
+ state->server_config = cached.server_config();
+ state->source_address_token = cached.source_address_token();
+ state->server_config_sig = cached.signature();
+ state->certs = cached.certs();
+
+ server_info_->Persist();
+}
+
+void QuicClientSession::OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) {
+ const ProofVerifyDetailsChromium* verify_details_chromium =
+ reinterpret_cast<const ProofVerifyDetailsChromium*>(&verify_details);
+ CertVerifyResult* result_copy = new CertVerifyResult;
+ result_copy->CopyFrom(verify_details_chromium->cert_verify_result);
+ cert_verify_result_.reset(result_copy);
+ pinning_failure_log_ = verify_details_chromium->pinning_failure_log;
+ logger_->OnCertificateVerified(*cert_verify_result_);
+}
+
+void QuicClientSession::StartReading() {
+ if (read_pending_) {
+ return;
+ }
+ read_pending_ = true;
+ int rv = socket_->Read(read_buffer_.get(),
+ read_buffer_->size(),
+ base::Bind(&QuicClientSession::OnReadComplete,
+ weak_factory_.GetWeakPtr()));
+ if (rv == ERR_IO_PENDING) {
+ num_packets_read_ = 0;
+ return;
+ }
+
+ if (++num_packets_read_ > 32) {
+ num_packets_read_ = 0;
+ // Data was read, process it.
+ // Schedule the work through the message loop to 1) prevent infinite
+ // recursion and 2) avoid blocking the thread for too long.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuicClientSession::OnReadComplete,
+ weak_factory_.GetWeakPtr(), rv));
+ } else {
+ OnReadComplete(rv);
+ }
+}
+
+void QuicClientSession::CloseSessionOnError(int error) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error);
+ CloseSessionOnErrorInner(error, QUIC_INTERNAL_ERROR);
+ NotifyFactoryOfSessionClosed();
+}
+
+void QuicClientSession::CloseSessionOnErrorInner(int net_error,
+ QuicErrorCode quic_error) {
+ if (!callback_.is_null()) {
+ base::ResetAndReturn(&callback_).Run(net_error);
+ }
+ CloseAllStreams(net_error);
+ CloseAllObservers(net_error);
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR,
+ NetLog::IntegerCallback("net_error", net_error));
+
+ if (connection()->connected())
+ connection()->CloseConnection(quic_error, false);
+ DCHECK(!connection()->connected());
+}
+
+void QuicClientSession::CloseAllStreams(int net_error) {
+ while (!streams()->empty()) {
+ ReliableQuicStream* stream = streams()->begin()->second;
+ QuicStreamId id = stream->id();
+ static_cast<QuicReliableClientStream*>(stream)->OnError(net_error);
+ CloseStream(id);
+ }
+}
+
+void QuicClientSession::CloseAllObservers(int net_error) {
+ while (!observers_.empty()) {
+ Observer* observer = *observers_.begin();
+ observers_.erase(observer);
+ observer->OnSessionClosed(net_error);
+ }
+}
+
+base::Value* QuicClientSession::GetInfoAsValue(
+ const std::set<HostPortPair>& aliases) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("version", QuicVersionToString(connection()->version()));
+ dict->SetInteger("open_streams", GetNumOpenStreams());
+ base::ListValue* stream_list = new base::ListValue();
+ for (base::hash_map<QuicStreamId, QuicDataStream*>::const_iterator it
+ = streams()->begin();
+ it != streams()->end();
+ ++it) {
+ stream_list->Append(new base::StringValue(
+ base::Uint64ToString(it->second->id())));
+ }
+ dict->Set("active_streams", stream_list);
+
+ dict->SetInteger("total_streams", num_total_streams_);
+ dict->SetString("peer_address", peer_address().ToString());
+ dict->SetString("connection_id", base::Uint64ToString(connection_id()));
+ dict->SetBoolean("connected", connection()->connected());
+ const QuicConnectionStats& stats = connection()->GetStats();
+ dict->SetInteger("packets_sent", stats.packets_sent);
+ dict->SetInteger("packets_received", stats.packets_received);
+ dict->SetInteger("packets_lost", stats.packets_lost);
+ SSLInfo ssl_info;
+ dict->SetBoolean("secure", GetSSLInfo(&ssl_info) && ssl_info.cert.get());
+
+ base::ListValue* alias_list = new base::ListValue();
+ for (std::set<HostPortPair>::const_iterator it = aliases.begin();
+ it != aliases.end(); it++) {
+ alias_list->Append(new base::StringValue(it->ToString()));
+ }
+ dict->Set("aliases", alias_list);
+
+ return dict;
+}
+
+base::WeakPtr<QuicClientSession> QuicClientSession::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
+}
+
+void QuicClientSession::OnReadComplete(int result) {
+ read_pending_ = false;
+ if (result == 0)
+ result = ERR_CONNECTION_CLOSED;
+
+ if (result < 0) {
+ DVLOG(1) << "Closing session on read error: " << result;
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ReadError", -result);
+ NotifyFactoryOfSessionGoingAway();
+ CloseSessionOnErrorInner(result, QUIC_PACKET_READ_ERROR);
+ NotifyFactoryOfSessionClosedLater();
+ return;
+ }
+
+ QuicEncryptedPacket packet(read_buffer_->data(), result);
+ IPEndPoint local_address;
+ IPEndPoint peer_address;
+ socket_->GetLocalAddress(&local_address);
+ socket_->GetPeerAddress(&peer_address);
+ // ProcessUdpPacket might result in |this| being deleted, so we
+ // use a weak pointer to be safe.
+ connection()->ProcessUdpPacket(local_address, peer_address, packet);
+ if (!connection()->connected()) {
+ NotifyFactoryOfSessionClosedLater();
+ return;
+ }
+ StartReading();
+}
+
+void QuicClientSession::NotifyFactoryOfSessionGoingAway() {
+ going_away_ = true;
+ if (stream_factory_)
+ stream_factory_->OnSessionGoingAway(this);
+}
+
+void QuicClientSession::NotifyFactoryOfSessionClosedLater() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
+
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED_LATER);
+
+ going_away_ = true;
+ DCHECK_EQ(0u, GetNumOpenStreams());
+ DCHECK(!connection()->connected());
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuicClientSession::NotifyFactoryOfSessionClosed,
+ weak_factory_.GetWeakPtr()));
+}
+
+void QuicClientSession::NotifyFactoryOfSessionClosed() {
+ if (!streams()->empty())
+ RecordUnexpectedOpenStreams(NOTIFY_FACTORY_OF_SESSION_CLOSED);
+
+ if (!going_away_)
+ RecordUnexpectedNotGoingAway(NOTIFY_FACTORY_OF_SESSION_CLOSED);
+
+ going_away_ = true;
+ DCHECK_EQ(0u, GetNumOpenStreams());
+ // Will delete |this|.
+ if (stream_factory_)
+ stream_factory_->OnSessionClosed(this);
+}
+
+void QuicClientSession::OnConnectTimeout() {
+ DCHECK(callback_.is_null());
+ DCHECK(IsEncryptionEstablished());
+
+ if (IsCryptoHandshakeConfirmed())
+ return;
+
+ // TODO(rch): re-enable this code once beta is cut.
+ // if (stream_factory_)
+ // stream_factory_->OnSessionConnectTimeout(this);
+ // CloseAllStreams(ERR_QUIC_HANDSHAKE_FAILED);
+ // DCHECK_EQ(0u, GetNumOpenStreams());
+}
+
+} // namespace net
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
new file mode 100644
index 0000000..fa712c2
--- /dev/null
+++ b/net/quic/quic_client_session.h
@@ -0,0 +1,256 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A client specific QuicSession subclass. This class owns the underlying
+// QuicConnection and QuicConnectionHelper objects. The connection stores
+// a non-owning pointer to the helper so this session needs to ensure that
+// the helper outlives the connection.
+
+#ifndef NET_QUIC_QUIC_CLIENT_SESSION_H_
+#define NET_QUIC_QUIC_CLIENT_SESSION_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "net/base/completion_callback.h"
+#include "net/proxy/proxy_server.h"
+#include "net/quic/quic_client_session_base.h"
+#include "net/quic/quic_connection_logger.h"
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_reliable_client_stream.h"
+
+namespace net {
+
+class CertVerifyResult;
+class DatagramClientSocket;
+class QuicConnectionHelper;
+class QuicCryptoClientStreamFactory;
+class QuicServerId;
+class QuicServerInfo;
+class QuicStreamFactory;
+class SSLInfo;
+class TransportSecurityState;
+
+namespace test {
+class QuicClientSessionPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase {
+ public:
+ // An interface for observing events on a session.
+ class NET_EXPORT_PRIVATE Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnCryptoHandshakeConfirmed() = 0;
+ virtual void OnSessionClosed(int error) = 0;
+ };
+
+ // A helper class used to manage a request to create a stream.
+ class NET_EXPORT_PRIVATE StreamRequest {
+ public:
+ StreamRequest();
+ ~StreamRequest();
+
+ // Starts a request to create a stream. If OK is returned, then
+ // |stream| will be updated with the newly created stream. If
+ // ERR_IO_PENDING is returned, then when the request is eventuallly
+ // complete |callback| will be called.
+ int StartRequest(const base::WeakPtr<QuicClientSession>& session,
+ QuicReliableClientStream** stream,
+ const CompletionCallback& callback);
+
+ // Cancels any pending stream creation request. May be called
+ // repeatedly.
+ void CancelRequest();
+
+ private:
+ friend class QuicClientSession;
+
+ // Called by |session_| for an asynchronous request when the stream
+ // request has finished successfully.
+ void OnRequestCompleteSuccess(QuicReliableClientStream* stream);
+
+ // Called by |session_| for an asynchronous request when the stream
+ // request has finished with an error. Also called with ERR_ABORTED
+ // if |session_| is destroyed while the stream request is still pending.
+ void OnRequestCompleteFailure(int rv);
+
+ base::WeakPtr<QuicClientSession> session_;
+ CompletionCallback callback_;
+ QuicReliableClientStream** stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(StreamRequest);
+ };
+
+ // Constructs a new session which will own |connection|, but not
+ // |stream_factory|, which must outlive this session.
+ // TODO(rch): decouple the factory from the session via a Delegate interface.
+ QuicClientSession(QuicConnection* connection,
+ scoped_ptr<DatagramClientSocket> socket,
+ QuicStreamFactory* stream_factory,
+ TransportSecurityState* transport_security_state,
+ scoped_ptr<QuicServerInfo> server_info,
+ const QuicConfig& config,
+ base::TaskRunner* task_runner,
+ NetLog* net_log);
+ virtual ~QuicClientSession();
+
+ // Initialize session's connection to |server_id|.
+ void InitializeSession(
+ const QuicServerId& server_id,
+ QuicCryptoClientConfig* config,
+ QuicCryptoClientStreamFactory* crypto_client_stream_factory);
+
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ // Attempts to create a new stream. If the stream can be
+ // created immediately, returns OK. If the open stream limit
+ // has been reached, returns ERR_IO_PENDING, and |request|
+ // will be added to the stream requets queue and will
+ // be completed asynchronously.
+ // TODO(rch): remove |stream| from this and use setter on |request|
+ // and fix in spdy too.
+ int TryCreateStream(StreamRequest* request,
+ QuicReliableClientStream** stream);
+
+ // Cancels the pending stream creation request.
+ void CancelRequest(StreamRequest* request);
+
+ // QuicSession methods:
+ virtual void OnStreamFrames(
+ const std::vector<QuicStreamFrame>& frames) OVERRIDE;
+ virtual QuicReliableClientStream* CreateOutgoingDataStream() OVERRIDE;
+ virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE;
+ virtual void CloseStream(QuicStreamId stream_id) OVERRIDE;
+ virtual void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) OVERRIDE;
+ virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE;
+ virtual void OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message) OVERRIDE;
+ virtual void OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message) OVERRIDE;
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) const OVERRIDE;
+
+ // QuicClientSessionBase methods:
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) OVERRIDE;
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) OVERRIDE;
+
+ // QuicConnectionVisitorInterface methods:
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE;
+
+ // Performs a crypto handshake with the server.
+ int CryptoConnect(bool require_confirmation,
+ const CompletionCallback& callback);
+
+ // Resumes a crypto handshake with the server after a timeout.
+ int ResumeCryptoConnect(const CompletionCallback& callback);
+
+ // Causes the QuicConnectionHelper to start reading from the socket
+ // and passing the data along to the QuicConnection.
+ void StartReading();
+
+ // Close the session because of |error| and notifies the factory
+ // that this session has been closed, which will delete the session.
+ void CloseSessionOnError(int error);
+
+ base::Value* GetInfoAsValue(const std::set<HostPortPair>& aliases);
+
+ const BoundNetLog& net_log() const { return net_log_; }
+
+ base::WeakPtr<QuicClientSession> GetWeakPtr();
+
+ // Returns the number of client hello messages that have been sent on the
+ // crypto stream. If the handshake has completed then this is one greater
+ // than the number of round-trips needed for the handshake.
+ int GetNumSentClientHellos() const;
+
+ // Returns true if |hostname| may be pooled onto this session. If this
+ // is a secure QUIC session, then |hostname| must match the certificate
+ // presented during the handshake.
+ bool CanPool(const std::string& hostname) const;
+
+ protected:
+ // QuicSession methods:
+ virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE;
+
+ private:
+ friend class test::QuicClientSessionPeer;
+
+ typedef std::set<Observer*> ObserverSet;
+ typedef std::list<StreamRequest*> StreamRequestQueue;
+
+ QuicReliableClientStream* CreateOutgoingReliableStreamImpl();
+ // A completion callback invoked when a read completes.
+ void OnReadComplete(int result);
+
+ void OnClosedStream();
+
+ // A Session may be closed via any of three methods:
+ // OnConnectionClosed - called by the connection when the connection has been
+ // closed, perhaps due to a timeout or a protocol error.
+ // CloseSessionOnError - called from the owner of the session,
+ // the QuicStreamFactory, when there is an error.
+ // OnReadComplete - when there is a read error.
+ // This method closes all stream and performs any necessary cleanup.
+ void CloseSessionOnErrorInner(int net_error, QuicErrorCode quic_error);
+
+ void CloseAllStreams(int net_error);
+ void CloseAllObservers(int net_error);
+
+ // Notifies the factory that this session is going away and no more streams
+ // should be created from it. This needs to be called before closing any
+ // streams, because closing a stream may cause a new stream to be created.
+ void NotifyFactoryOfSessionGoingAway();
+
+ // Posts a task to notify the factory that this session has been closed.
+ void NotifyFactoryOfSessionClosedLater();
+
+ // Notifies the factory that this session has been closed which will
+ // delete |this|.
+ void NotifyFactoryOfSessionClosed();
+
+ void OnConnectTimeout();
+
+ HostPortPair server_host_port_;
+ bool require_confirmation_;
+ scoped_ptr<QuicCryptoClientStream> crypto_stream_;
+ QuicStreamFactory* stream_factory_;
+ scoped_ptr<DatagramClientSocket> socket_;
+ scoped_refptr<IOBufferWithSize> read_buffer_;
+ TransportSecurityState* transport_security_state_;
+ scoped_ptr<QuicServerInfo> server_info_;
+ scoped_ptr<CertVerifyResult> cert_verify_result_;
+ std::string pinning_failure_log_;
+ ObserverSet observers_;
+ StreamRequestQueue stream_requests_;
+ bool read_pending_;
+ CompletionCallback callback_;
+ size_t num_total_streams_;
+ base::TaskRunner* task_runner_;
+ BoundNetLog net_log_;
+ base::TimeTicks handshake_start_; // Time the handshake was started.
+ QuicConnectionLogger* logger_; // Owned by |connection_|.
+ // Number of packets read in the current read loop.
+ size_t num_packets_read_;
+ // True when the session is going away, and streams may no longer be created
+ // on this session. Existing stream will continue to be processed.
+ bool going_away_;
+ base::WeakPtrFactory<QuicClientSession> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSession);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CLIENT_SESSION_H_
diff --git a/net/quic/quic_client_session_base.cc b/net/quic/quic_client_session_base.cc
new file mode 100644
index 0000000..40d4b86
--- /dev/null
+++ b/net/quic/quic_client_session_base.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_client_session_base.h"
+
+#include "net/quic/quic_flags.h"
+
+namespace net {
+
+QuicClientSessionBase::QuicClientSessionBase(
+ QuicConnection* connection,
+ const QuicConfig& config)
+ : QuicSession(connection, config) {}
+
+QuicClientSessionBase::~QuicClientSessionBase() {}
+
+void QuicClientSessionBase::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ QuicSession::OnCryptoHandshakeEvent(event);
+ // Set FEC policy for streams immediately after sending CHLO and before any
+ // more data is sent.
+ if (!FLAGS_enable_quic_fec ||
+ event != ENCRYPTION_FIRST_ESTABLISHED ||
+ !config()->HasSendConnectionOptions() ||
+ !ContainsQuicTag(config()->SendConnectionOptions(), kFHDR)) {
+ return;
+ }
+ // kFHDR config maps to FEC protection always for headers stream.
+ // TODO(jri): Add crypto stream in addition to headers for kHDR.
+ headers_stream_->set_fec_policy(FEC_PROTECT_ALWAYS);
+}
+
+} // namespace net
diff --git a/net/quic/quic_client_session_base.h b/net/quic/quic_client_session_base.h
new file mode 100644
index 0000000..834d006
--- /dev/null
+++ b/net/quic/quic_client_session_base.h
@@ -0,0 +1,44 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
+#define NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
+
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+// Base class for all client-specific QuicSession subclasses.
+class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSession {
+ public:
+ QuicClientSessionBase(QuicConnection* connection,
+ const QuicConfig& config);
+
+ virtual ~QuicClientSessionBase();
+
+ // Called when the proof in |cached| is marked valid. If this is a secure
+ // QUIC session, then this will happen only after the proof verifier
+ // completes. If this is an insecure QUIC connection, this will happen
+ // as soon as a valid config is discovered (either from the cache or
+ // from the server).
+ virtual void OnProofValid(
+ const QuicCryptoClientConfig::CachedState& cached) = 0;
+
+ // Called when proof verification details become available, either because
+ // proof verification is complete, or when cached details are used. This
+ // will only be called for secure QUIC connections.
+ virtual void OnProofVerifyDetailsAvailable(
+ const ProofVerifyDetails& verify_details) = 0;
+
+ // Override base class to set FEC policy before any data is sent by client.
+ virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSessionBase);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CLIENT_SESSION_BASE_H_
diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc
new file mode 100644
index 0000000..9758ed4
--- /dev/null
+++ b/net/quic/quic_client_session_test.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_client_session.h"
+
+#include <vector>
+
+#include "base/base64.h"
+#include "base/files/file_path.h"
+#include "base/rand_util.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/http/transport_security_state.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_server_info.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_client_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "net/test/cert_test_util.h"
+#include "net/udp/datagram_client_socket.h"
+
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+const char kServerHostname[] = "www.example.org";
+const uint16 kServerPort = 80;
+
+class QuicClientSessionTest : public ::testing::TestWithParam<QuicVersion> {
+ protected:
+ QuicClientSessionTest()
+ : connection_(
+ new PacketSavingConnection(false, SupportedVersions(GetParam()))),
+ session_(connection_, GetSocket().Pass(), nullptr,
+ &transport_security_state_,
+ make_scoped_ptr((QuicServerInfo*)nullptr), DefaultQuicConfig(),
+ base::MessageLoop::current()->message_loop_proxy().get(),
+ &net_log_) {
+ session_.InitializeSession(QuicServerId(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED),
+ &crypto_config_, nullptr);
+ session_.config()->SetDefaults();
+ crypto_config_.SetDefaults();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ session_.CloseSessionOnError(ERR_ABORTED);
+ }
+
+ scoped_ptr<DatagramClientSocket> GetSocket() {
+ socket_factory_.AddSocketDataProvider(&socket_data_);
+ return socket_factory_.CreateDatagramClientSocket(
+ DatagramSocket::DEFAULT_BIND, base::Bind(&base::RandInt),
+ &net_log_, NetLog::Source());
+ }
+
+ void CompleteCryptoHandshake() {
+ ASSERT_EQ(ERR_IO_PENDING,
+ session_.CryptoConnect(false, callback_.callback()));
+ CryptoTestUtils::HandshakeWithFakeServer(
+ connection_, session_.GetCryptoStream());
+ ASSERT_EQ(OK, callback_.WaitForResult());
+ }
+
+ PacketSavingConnection* connection_;
+ CapturingNetLog net_log_;
+ MockClientSocketFactory socket_factory_;
+ StaticSocketDataProvider socket_data_;
+ TransportSecurityState transport_security_state_;
+ QuicClientSession session_;
+ MockClock clock_;
+ MockRandom random_;
+ QuicConnectionVisitorInterface* visitor_;
+ TestCompletionCallback callback_;
+ QuicCryptoClientConfig crypto_config_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicClientSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicClientSessionTest, CryptoConnect) {
+ CompleteCryptoHandshake();
+}
+
+TEST_P(QuicClientSessionTest, MaxNumStreams) {
+ CompleteCryptoHandshake();
+
+ std::vector<QuicReliableClientStream*> streams;
+ for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) {
+ QuicReliableClientStream* stream = session_.CreateOutgoingDataStream();
+ EXPECT_TRUE(stream);
+ streams.push_back(stream);
+ }
+ EXPECT_FALSE(session_.CreateOutgoingDataStream());
+
+ // Close a stream and ensure I can now open a new one.
+ session_.CloseStream(streams[0]->id());
+ EXPECT_TRUE(session_.CreateOutgoingDataStream());
+}
+
+TEST_P(QuicClientSessionTest, MaxNumStreamsViaRequest) {
+ CompleteCryptoHandshake();
+
+ std::vector<QuicReliableClientStream*> streams;
+ for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) {
+ QuicReliableClientStream* stream = session_.CreateOutgoingDataStream();
+ EXPECT_TRUE(stream);
+ streams.push_back(stream);
+ }
+
+ QuicReliableClientStream* stream;
+ QuicClientSession::StreamRequest stream_request;
+ TestCompletionCallback callback;
+ ASSERT_EQ(ERR_IO_PENDING,
+ stream_request.StartRequest(session_.GetWeakPtr(), &stream,
+ callback.callback()));
+
+ // Close a stream and ensure I can now open a new one.
+ session_.CloseStream(streams[0]->id());
+ ASSERT_TRUE(callback.have_result());
+ EXPECT_EQ(OK, callback.WaitForResult());
+ EXPECT_TRUE(stream != nullptr);
+}
+
+TEST_P(QuicClientSessionTest, GoAwayReceived) {
+ CompleteCryptoHandshake();
+
+ // After receiving a GoAway, I should no longer be able to create outgoing
+ // streams.
+ session_.OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
+ EXPECT_EQ(nullptr, session_.CreateOutgoingDataStream());
+}
+
+TEST_P(QuicClientSessionTest, CanPool) {
+ // Load a cert that is valid for:
+ // www.example.org
+ // mail.example.org
+ // www.example.com
+
+ ProofVerifyDetailsChromium details;
+ details.cert_verify_result.verified_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+ ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
+
+ session_.OnProofVerifyDetailsAvailable(details);
+ CompleteCryptoHandshake();
+
+
+ EXPECT_TRUE(session_.CanPool("www.example.org"));
+ EXPECT_TRUE(session_.CanPool("mail.example.org"));
+ EXPECT_TRUE(session_.CanPool("mail.example.com"));
+ EXPECT_FALSE(session_.CanPool("mail.google.com"));
+}
+
+TEST_P(QuicClientSessionTest, ConnectionPooledWithTlsChannelId) {
+ // Load a cert that is valid for:
+ // www.example.org
+ // mail.example.org
+ // www.example.com
+
+ ProofVerifyDetailsChromium details;
+ details.cert_verify_result.verified_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+ ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
+
+ session_.OnProofVerifyDetailsAvailable(details);
+ CompleteCryptoHandshake();
+ QuicClientSessionPeer::SetChannelIDSent(&session_, true);
+
+ EXPECT_TRUE(session_.CanPool("www.example.org"));
+ EXPECT_TRUE(session_.CanPool("mail.example.org"));
+ EXPECT_FALSE(session_.CanPool("mail.example.com"));
+ EXPECT_FALSE(session_.CanPool("mail.google.com"));
+}
+
+TEST_P(QuicClientSessionTest, ConnectionNotPooledWithDifferentPin) {
+ uint8 primary_pin = 1;
+ uint8 backup_pin = 2;
+ uint8 bad_pin = 3;
+ AddPin(&transport_security_state_, "mail.example.org", primary_pin,
+ backup_pin);
+
+ ProofVerifyDetailsChromium details;
+ details.cert_verify_result.verified_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+ details.cert_verify_result.is_issued_by_known_root = true;
+ details.cert_verify_result.public_key_hashes.push_back(
+ GetTestHashValue(bad_pin));
+
+ ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
+
+ session_.OnProofVerifyDetailsAvailable(details);
+ CompleteCryptoHandshake();
+ QuicClientSessionPeer::SetChannelIDSent(&session_, true);
+
+ EXPECT_FALSE(session_.CanPool("mail.example.org"));
+}
+
+TEST_P(QuicClientSessionTest, ConnectionPooledWithMatchingPin) {
+ uint8 primary_pin = 1;
+ uint8 backup_pin = 2;
+ AddPin(&transport_security_state_, "mail.example.org", primary_pin,
+ backup_pin);
+
+ ProofVerifyDetailsChromium details;
+ details.cert_verify_result.verified_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem");
+ details.cert_verify_result.is_issued_by_known_root = true;
+ details.cert_verify_result.public_key_hashes.push_back(
+ GetTestHashValue(primary_pin));
+
+ ASSERT_TRUE(details.cert_verify_result.verified_cert.get());
+
+ session_.OnProofVerifyDetailsAvailable(details);
+ CompleteCryptoHandshake();
+ QuicClientSessionPeer::SetChannelIDSent(&session_, true);
+
+ EXPECT_TRUE(session_.CanPool("mail.example.org"));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_clock.cc b/net/quic/quic_clock.cc
new file mode 100644
index 0000000..9d5ac6d
--- /dev/null
+++ b/net/quic/quic_clock.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_clock.h"
+
+#include "base/time/time.h"
+
+namespace net {
+
+QuicClock::QuicClock() {
+}
+
+QuicClock::~QuicClock() {}
+
+QuicTime QuicClock::ApproximateNow() const {
+ // At the moment, Chrome does not have a distinct notion of ApproximateNow().
+ // We should consider implementing this using MessageLoop::recent_time_.
+ return Now();
+}
+
+QuicTime QuicClock::Now() const {
+ return QuicTime(base::TimeTicks::Now());
+}
+
+QuicWallTime QuicClock::WallNow() const {
+ return QuicWallTime::FromUNIXSeconds(base::Time::Now().ToTimeT());
+}
+
+} // namespace net
diff --git a/net/quic/quic_clock.h b/net/quic/quic_clock.h
new file mode 100644
index 0000000..4e96d4c
--- /dev/null
+++ b/net/quic/quic_clock.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CLOCK_H_
+#define NET_QUIC_QUIC_CLOCK_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+typedef double WallTime;
+
+// Clock to efficiently retrieve an approximately accurate time from an
+// EpollServer.
+class NET_EXPORT_PRIVATE QuicClock {
+ public:
+ QuicClock();
+ virtual ~QuicClock();
+
+ // Returns the approximate current time as a QuicTime object.
+ virtual QuicTime ApproximateNow() const;
+
+ // Returns the current time as a QuicTime object.
+ // Note: this use significant resources please use only if needed.
+ virtual QuicTime Now() const;
+
+ // WallNow returns the current wall-time - a time that is consistent across
+ // different clocks.
+ virtual QuicWallTime WallNow() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClock);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CLOCK_H_
diff --git a/net/quic/quic_clock_test.cc b/net/quic/quic_clock_test.cc
new file mode 100644
index 0000000..6ee4540
--- /dev/null
+++ b/net/quic/quic_clock_test.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_clock.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+TEST(QuicClockTest, Now) {
+ QuicClock clock;
+
+ QuicTime start(base::TimeTicks::Now());
+ QuicTime now = clock.ApproximateNow();
+ QuicTime end(base::TimeTicks::Now());
+
+ EXPECT_LE(start, now);
+ EXPECT_LE(now, end);
+}
+
+TEST(QuicClockTest, WallNow) {
+ QuicClock clock;
+
+ base::Time start = base::Time::Now();
+ QuicWallTime now = clock.WallNow();
+ base::Time end = base::Time::Now();
+
+ // If end > start, then we can check now is between start and end.
+ if (end > start) {
+ EXPECT_LE(static_cast<uint64>(start.ToTimeT()), now.ToUNIXSeconds());
+ EXPECT_LE(now.ToUNIXSeconds(), static_cast<uint64>(end.ToTimeT()));
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc
new file mode 100644
index 0000000..5330b7c
--- /dev/null
+++ b/net/quic/quic_config.cc
@@ -0,0 +1,720 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_config.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
+
+using std::min;
+using std::string;
+
+namespace net {
+
+// Reads the value corresponding to |name_| from |msg| into |out|. If the
+// |name_| is absent in |msg| and |presence| is set to OPTIONAL |out| is set
+// to |default_value|.
+QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg,
+ QuicTag tag,
+ QuicConfigPresence presence,
+ uint32 default_value,
+ uint32* out,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = msg.GetUint32(tag, out);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicUtils::TagToString(tag);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out = default_value;
+ break;
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag);
+ break;
+ }
+ return error;
+}
+
+
+QuicConfigValue::QuicConfigValue(QuicTag tag,
+ QuicConfigPresence presence)
+ : tag_(tag),
+ presence_(presence) {
+}
+QuicConfigValue::~QuicConfigValue() {}
+
+QuicNegotiableValue::QuicNegotiableValue(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ negotiated_(false) {
+}
+QuicNegotiableValue::~QuicNegotiableValue() {}
+
+QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag,
+ QuicConfigPresence presence)
+ : QuicNegotiableValue(tag, presence),
+ max_value_(0),
+ default_value_(0) {
+}
+QuicNegotiableUint32::~QuicNegotiableUint32() {}
+
+void QuicNegotiableUint32::set(uint32 max, uint32 default_value) {
+ DCHECK_LE(default_value, max);
+ max_value_ = max;
+ default_value_ = default_value;
+}
+
+uint32 QuicNegotiableUint32::GetUint32() const {
+ if (negotiated_) {
+ return negotiated_value_;
+ }
+ return default_value_;
+}
+
+void QuicNegotiableUint32::ToHandshakeMessage(
+ CryptoHandshakeMessage* out) const {
+ if (negotiated_) {
+ out->SetValue(tag_, negotiated_value_);
+ } else {
+ out->SetValue(tag_, max_value_);
+ }
+}
+
+QuicErrorCode QuicNegotiableUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(!negotiated_);
+ DCHECK(error_details != nullptr);
+ uint32 value;
+ QuicErrorCode error = ReadUint32(peer_hello,
+ tag_,
+ presence_,
+ default_value_,
+ &value,
+ error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+ if (hello_type == SERVER && value > max_value_) {
+ *error_details =
+ "Invalid value received for " + QuicUtils::TagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
+ }
+
+ negotiated_ = true;
+ negotiated_value_ = min(value, max_value_);
+ return QUIC_NO_ERROR;
+}
+
+QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, QuicConfigPresence presence)
+ : QuicNegotiableValue(tag, presence),
+ negotiated_tag_(0),
+ default_value_(0) {
+}
+
+QuicNegotiableTag::~QuicNegotiableTag() {}
+
+void QuicNegotiableTag::set(const QuicTagVector& possible,
+ QuicTag default_value) {
+ DCHECK(ContainsQuicTag(possible, default_value));
+ possible_values_ = possible;
+ default_value_ = default_value;
+}
+
+QuicTag QuicNegotiableTag::GetTag() const {
+ if (negotiated_) {
+ return negotiated_tag_;
+ }
+ return default_value_;
+}
+
+void QuicNegotiableTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (negotiated_) {
+ // Because of the way we serialize and parse handshake messages we can
+ // serialize this as value and still parse it as a vector.
+ out->SetValue(tag_, negotiated_tag_);
+ } else {
+ out->SetVector(tag_, possible_values_);
+ }
+}
+
+QuicErrorCode QuicNegotiableTag::ReadVector(
+ const CryptoHandshakeMessage& msg,
+ const QuicTag** out,
+ size_t* out_length,
+ string* error_details) const {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = msg.GetTaglist(tag_, out, out_length);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_REQUIRED) {
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ error = QUIC_NO_ERROR;
+ *out_length = 1;
+ *out = &default_value_;
+
+ case QUIC_NO_ERROR:
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicErrorCode QuicNegotiableTag::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(!negotiated_);
+ DCHECK(error_details != nullptr);
+ const QuicTag* received_tags;
+ size_t received_tags_length;
+ QuicErrorCode error = ReadVector(peer_hello, &received_tags,
+ &received_tags_length, error_details);
+ if (error != QUIC_NO_ERROR) {
+ return error;
+ }
+
+ if (hello_type == SERVER) {
+ if (received_tags_length != 1 ||
+ !ContainsQuicTag(possible_values_, *received_tags)) {
+ *error_details = "Invalid " + QuicUtils::TagToString(tag_);
+ return QUIC_INVALID_NEGOTIATED_VALUE;
+ }
+ negotiated_tag_ = *received_tags;
+ } else {
+ QuicTag negotiated_tag;
+ if (!QuicUtils::FindMutualTag(possible_values_,
+ received_tags,
+ received_tags_length,
+ QuicUtils::LOCAL_PRIORITY,
+ &negotiated_tag,
+ nullptr)) {
+ *error_details = "Unsupported " + QuicUtils::TagToString(tag_);
+ return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP;
+ }
+ negotiated_tag_ = negotiated_tag;
+ }
+
+ negotiated_ = true;
+ return QUIC_NO_ERROR;
+}
+
+QuicFixedUint32::QuicFixedUint32(QuicTag tag, QuicConfigPresence presence)
+ : QuicConfigValue(tag, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {
+}
+QuicFixedUint32::~QuicFixedUint32() {}
+
+bool QuicFixedUint32::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32 QuicFixedUint32::GetSendValue() const {
+ LOG_IF(DFATAL, !has_send_value_)
+ << "No send value to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedUint32::SetSendValue(uint32 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedUint32::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32 QuicFixedUint32::GetReceivedValue() const {
+ LOG_IF(DFATAL, !has_receive_value_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedUint32::SetReceivedValue(uint32 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedUint32::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
+ }
+}
+
+QuicErrorCode QuicFixedUint32::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicFixedTag::QuicFixedTag(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_value_(false),
+ has_receive_value_(false) {
+}
+
+QuicFixedTag::~QuicFixedTag() {}
+
+bool QuicFixedTag::HasSendValue() const {
+ return has_send_value_;
+}
+
+uint32 QuicFixedTag::GetSendValue() const {
+ LOG_IF(DFATAL, !has_send_value_)
+ << "No send value to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_value_;
+}
+
+void QuicFixedTag::SetSendValue(uint32 value) {
+ has_send_value_ = true;
+ send_value_ = value;
+}
+
+bool QuicFixedTag::HasReceivedValue() const {
+ return has_receive_value_;
+}
+
+uint32 QuicFixedTag::GetReceivedValue() const {
+ LOG_IF(DFATAL, !has_receive_value_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_value_;
+}
+
+void QuicFixedTag::SetReceivedValue(uint32 value) {
+ has_receive_value_ = true;
+ receive_value_ = value;
+}
+
+void QuicFixedTag::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_value_) {
+ out->SetValue(tag_, send_value_);
+ }
+}
+
+QuicErrorCode QuicFixedTag::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+ QuicErrorCode error = peer_hello.GetUint32(tag_, &receive_value_);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ has_receive_value_ = true;
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicFixedTagVector::QuicFixedTagVector(QuicTag name,
+ QuicConfigPresence presence)
+ : QuicConfigValue(name, presence),
+ has_send_values_(false),
+ has_receive_values_(false) {
+}
+
+QuicFixedTagVector::~QuicFixedTagVector() {}
+
+bool QuicFixedTagVector::HasSendValues() const {
+ return has_send_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetSendValues() const {
+ LOG_IF(DFATAL, !has_send_values_)
+ << "No send values to get for tag:" << QuicUtils::TagToString(tag_);
+ return send_values_;
+}
+
+void QuicFixedTagVector::SetSendValues(const QuicTagVector& values) {
+ has_send_values_ = true;
+ send_values_ = values;
+}
+
+bool QuicFixedTagVector::HasReceivedValues() const {
+ return has_receive_values_;
+}
+
+QuicTagVector QuicFixedTagVector::GetReceivedValues() const {
+ LOG_IF(DFATAL, !has_receive_values_)
+ << "No receive value to get for tag:" << QuicUtils::TagToString(tag_);
+ return receive_values_;
+}
+
+void QuicFixedTagVector::SetReceivedValues(const QuicTagVector& values) {
+ has_receive_values_ = true;
+ receive_values_ = values;
+}
+
+void QuicFixedTagVector::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ if (has_send_values_) {
+ out->SetVector(tag_, send_values_);
+ }
+}
+
+QuicErrorCode QuicFixedTagVector::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+ const QuicTag* received_tags;
+ size_t received_tags_length;
+ QuicErrorCode error =
+ peer_hello.GetTaglist(tag_, &received_tags, &received_tags_length);
+ switch (error) {
+ case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND:
+ if (presence_ == PRESENCE_OPTIONAL) {
+ return QUIC_NO_ERROR;
+ }
+ *error_details = "Missing " + QuicUtils::TagToString(tag_);
+ break;
+ case QUIC_NO_ERROR:
+ DVLOG(1) << "Received Connection Option tags from receiver.";
+ has_receive_values_ = true;
+ for (size_t i = 0; i < received_tags_length; ++i) {
+ receive_values_.push_back(received_tags[i]);
+ }
+ break;
+ default:
+ *error_details = "Bad " + QuicUtils::TagToString(tag_);
+ break;
+ }
+ return error;
+}
+
+QuicConfig::QuicConfig()
+ : max_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
+ max_idle_time_before_crypto_handshake_(QuicTime::Delta::Zero()),
+ congestion_feedback_(kCGST, PRESENCE_REQUIRED),
+ connection_options_(kCOPT, PRESENCE_OPTIONAL),
+ idle_connection_state_lifetime_seconds_(kICSL, PRESENCE_REQUIRED),
+ keepalive_timeout_seconds_(kKATO, PRESENCE_OPTIONAL),
+ max_streams_per_connection_(kMSPC, PRESENCE_REQUIRED),
+ initial_congestion_window_(kSWND, PRESENCE_OPTIONAL),
+ initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Remove this when retiring QUIC_VERSION_19.
+ initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_19.
+ initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
+ // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+ // QUIC_VERSION_19.
+ initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL),
+ socket_receive_buffer_(kSRBF, PRESENCE_OPTIONAL) {
+}
+
+QuicConfig::~QuicConfig() {}
+
+void QuicConfig::SetCongestionFeedback(
+ const QuicTagVector& congestion_feedback,
+ QuicTag default_congestion_feedback) {
+ congestion_feedback_.set(congestion_feedback, default_congestion_feedback);
+}
+
+QuicTag QuicConfig::CongestionFeedback() const {
+ return congestion_feedback_.GetTag();
+}
+
+void QuicConfig::SetConnectionOptionsToSend(
+ const QuicTagVector& connection_options) {
+ connection_options_.SetSendValues(connection_options);
+}
+
+bool QuicConfig::HasReceivedConnectionOptions() const {
+ return connection_options_.HasReceivedValues();
+}
+
+QuicTagVector QuicConfig::ReceivedConnectionOptions() const {
+ return connection_options_.GetReceivedValues();
+}
+
+bool QuicConfig::HasSendConnectionOptions() const {
+ return connection_options_.HasSendValues();
+}
+
+QuicTagVector QuicConfig::SendConnectionOptions() const {
+ return connection_options_.GetSendValues();
+}
+
+void QuicConfig::SetIdleConnectionStateLifetime(
+ QuicTime::Delta max_idle_connection_state_lifetime,
+ QuicTime::Delta default_idle_conection_state_lifetime) {
+ idle_connection_state_lifetime_seconds_.set(
+ max_idle_connection_state_lifetime.ToSeconds(),
+ default_idle_conection_state_lifetime.ToSeconds());
+}
+
+QuicTime::Delta QuicConfig::IdleConnectionStateLifetime() const {
+ return QuicTime::Delta::FromSeconds(
+ idle_connection_state_lifetime_seconds_.GetUint32());
+}
+
+QuicTime::Delta QuicConfig::KeepaliveTimeout() const {
+ return QuicTime::Delta::FromSeconds(
+ keepalive_timeout_seconds_.GetUint32());
+}
+
+void QuicConfig::SetMaxStreamsPerConnection(size_t max_streams,
+ size_t default_streams) {
+ max_streams_per_connection_.set(max_streams, default_streams);
+}
+
+uint32 QuicConfig::MaxStreamsPerConnection() const {
+ return max_streams_per_connection_.GetUint32();
+}
+
+void QuicConfig::SetInitialCongestionWindowToSend(size_t initial_window) {
+ initial_congestion_window_.SetSendValue(initial_window);
+}
+
+bool QuicConfig::HasReceivedInitialCongestionWindow() const {
+ return initial_congestion_window_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialCongestionWindow() const {
+ return initial_congestion_window_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialRoundTripTimeUsToSend(size_t rtt) {
+ initial_round_trip_time_us_.SetSendValue(rtt);
+}
+
+bool QuicConfig::HasReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialRoundTripTimeUs() const {
+ return initial_round_trip_time_us_.GetReceivedValue();
+}
+
+bool QuicConfig::HasInitialRoundTripTimeUsToSend() const {
+ return initial_round_trip_time_us_.HasSendValue();
+}
+
+uint32 QuicConfig::GetInitialRoundTripTimeUsToSend() const {
+ return initial_round_trip_time_us_.GetSendValue();
+}
+
+void QuicConfig::SetInitialFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial flow control receive window (" << window_bytes
+ << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialFlowControlWindowToSend() const {
+ return initial_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialFlowControlWindowBytes() const {
+ return initial_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialFlowControlWindowBytes() const {
+ return initial_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialStreamFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial stream flow control receive window ("
+ << window_bytes << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialStreamFlowControlWindowToSend() const {
+ return initial_stream_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const {
+ return initial_stream_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialSessionFlowControlWindowToSend(uint32 window_bytes) {
+ if (window_bytes < kDefaultFlowControlSendWindow) {
+ LOG(DFATAL) << "Initial session flow control receive window ("
+ << window_bytes << ") cannot be set lower than default ("
+ << kDefaultFlowControlSendWindow << ").";
+ window_bytes = kDefaultFlowControlSendWindow;
+ }
+ initial_session_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialSessionFlowControlWindowToSend() const {
+ return initial_session_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const {
+ return initial_session_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetSocketReceiveBufferToSend(uint32 tcp_receive_window) {
+ socket_receive_buffer_.SetSendValue(tcp_receive_window);
+}
+
+uint32 QuicConfig::GetSocketReceiveBufferToSend() const {
+ return socket_receive_buffer_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedSocketReceiveBuffer() const {
+ return socket_receive_buffer_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedSocketReceiveBuffer() const {
+ return socket_receive_buffer_.GetReceivedValue();
+}
+
+bool QuicConfig::negotiated() const {
+ // TODO(ianswett): Add the negotiated parameters once and iterate over all
+ // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and
+ // ProcessServerHello.
+ return congestion_feedback_.negotiated() &&
+ idle_connection_state_lifetime_seconds_.negotiated() &&
+ keepalive_timeout_seconds_.negotiated() &&
+ max_streams_per_connection_.negotiated();
+}
+
+void QuicConfig::SetDefaults() {
+ QuicTagVector congestion_feedback;
+ congestion_feedback.push_back(kQBIC);
+ congestion_feedback_.set(congestion_feedback, kQBIC);
+ idle_connection_state_lifetime_seconds_.set(kMaximumIdleTimeoutSecs,
+ kDefaultIdleTimeoutSecs);
+ // kKATO is optional. Return 0 if not negotiated.
+ keepalive_timeout_seconds_.set(0, 0);
+ SetMaxStreamsPerConnection(kDefaultMaxStreamsPerConnection,
+ kDefaultMaxStreamsPerConnection);
+ max_time_before_crypto_handshake_ =
+ QuicTime::Delta::FromSeconds(kMaxTimeForCryptoHandshakeSecs);
+ max_idle_time_before_crypto_handshake_ =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs);
+
+ SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+ SetInitialStreamFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+ SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+}
+
+void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+ congestion_feedback_.ToHandshakeMessage(out);
+ idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out);
+ keepalive_timeout_seconds_.ToHandshakeMessage(out);
+ max_streams_per_connection_.ToHandshakeMessage(out);
+ initial_congestion_window_.ToHandshakeMessage(out);
+ initial_round_trip_time_us_.ToHandshakeMessage(out);
+ initial_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
+ initial_session_flow_control_window_bytes_.ToHandshakeMessage(out);
+ socket_receive_buffer_.ToHandshakeMessage(out);
+ connection_options_.ToHandshakeMessage(out);
+}
+
+QuicErrorCode QuicConfig::ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ string* error_details) {
+ DCHECK(error_details != nullptr);
+
+ QuicErrorCode error = QUIC_NO_ERROR;
+ if (error == QUIC_NO_ERROR) {
+ error = congestion_feedback_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = idle_connection_state_lifetime_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = keepalive_timeout_seconds_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = max_streams_per_connection_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_congestion_window_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_round_trip_time_us_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_stream_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = initial_session_flow_control_window_bytes_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = socket_receive_buffer_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ if (error == QUIC_NO_ERROR) {
+ error = connection_options_.ProcessPeerHello(
+ peer_hello, hello_type, error_details);
+ }
+ return error;
+}
+
+} // namespace net
diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h
new file mode 100644
index 0000000..203eb2e
--- /dev/null
+++ b/net/quic/quic_config.h
@@ -0,0 +1,417 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CONFIG_H_
+#define NET_QUIC_QUIC_CONFIG_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+namespace test {
+class QuicConfigPeer;
+} // namespace test
+
+class CryptoHandshakeMessage;
+
+// Describes whether or not a given QuicTag is required or optional in the
+// handshake message.
+enum QuicConfigPresence {
+ // This negotiable value can be absent from the handshake message. Default
+ // value is selected as the negotiated value in such a case.
+ PRESENCE_OPTIONAL,
+ // This negotiable value is required in the handshake message otherwise the
+ // Process*Hello function returns an error.
+ PRESENCE_REQUIRED,
+};
+
+// Whether the CryptoHandshakeMessage is from the client or server.
+enum HelloType {
+ CLIENT,
+ SERVER,
+};
+
+// An abstract base class that stores a value that can be sent in CHLO/SHLO
+// message. These values can be OPTIONAL or REQUIRED, depending on |presence_|.
+class NET_EXPORT_PRIVATE QuicConfigValue {
+ public:
+ QuicConfigValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicConfigValue();
+
+ // Serialises tag name and value(s) to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const = 0;
+
+ // Selects a mutually acceptable value from those offered in |peer_hello|
+ // and those defined in the subclass.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) = 0;
+
+ protected:
+ const QuicTag tag_;
+ const QuicConfigPresence presence_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableValue : public QuicConfigValue {
+ public:
+ QuicNegotiableValue(QuicTag tag, QuicConfigPresence presence);
+ virtual ~QuicNegotiableValue();
+
+ bool negotiated() const {
+ return negotiated_;
+ }
+
+ protected:
+ bool negotiated_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue {
+ public:
+ // Default and max values default to 0.
+ QuicNegotiableUint32(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicNegotiableUint32();
+
+ // Sets the maximum possible value that can be achieved after negotiation and
+ // also the default values to be assumed if PRESENCE_OPTIONAL and the *HLO msg
+ // doesn't contain a value corresponding to |name_|. |max| is serialised via
+ // ToHandshakeMessage call if |negotiated_| is false.
+ void set(uint32 max, uint32 default_value);
+
+ // Returns the value negotiated if |negotiated_| is true, otherwise returns
+ // default_value_ (used to set default values before negotiation finishes).
+ uint32 GetUint32() const;
+
+ // Serialises |name_| and value to |out|. If |negotiated_| is true then
+ // |negotiated_value_| is serialised, otherwise |max_value_| is serialised.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |negotiated_value_| to the minimum of |max_value_| and the
+ // corresponding value from |peer_hello|. If the corresponding value is
+ // missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to
+ // |default_value_|.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ uint32 max_value_;
+ uint32 default_value_;
+ uint32 negotiated_value_;
+};
+
+class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue {
+ public:
+ QuicNegotiableTag(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicNegotiableTag();
+
+ // Sets the possible values that |negotiated_tag_| can take after negotiation
+ // and the default value that |negotiated_tag_| takes if OPTIONAL and *HLO
+ // msg doesn't contain tag |name_|.
+ void set(const QuicTagVector& possible_values, QuicTag default_value);
+
+ // Returns the negotiated tag if |negotiated_| is true, otherwise returns
+ // |default_value_| (used to set default values before negotiation finishes).
+ QuicTag GetTag() const;
+
+ // Serialises |name_| and vector (either possible or negotiated) to |out|. If
+ // |negotiated_| is true then |negotiated_tag_| is serialised, otherwise
+ // |possible_values_| is serialised.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Selects the tag common to both tags in |client_hello| for |name_| and
+ // |possible_values_| with preference to tag in |possible_values_|. The
+ // selected tag is set as |negotiated_tag_|.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ // Reads the vector corresponding to |name_| from |msg| into |out|. If the
+ // |name_| is absent in |msg| and |presence_| is set to OPTIONAL |out| is set
+ // to |possible_values_|.
+ QuicErrorCode ReadVector(const CryptoHandshakeMessage& msg,
+ const QuicTag** out,
+ size_t* out_length,
+ std::string* error_details) const;
+
+ QuicTag negotiated_tag_;
+ QuicTagVector possible_values_;
+ QuicTag default_value_;
+};
+
+// Stores uint32 from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedUint32 : public QuicConfigValue {
+ public:
+ QuicFixedUint32(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedUint32();
+
+ bool HasSendValue() const;
+
+ uint32 GetSendValue() const;
+
+ void SetSendValue(uint32 value);
+
+ bool HasReceivedValue() const;
+
+ uint32 GetReceivedValue() const;
+
+ void SetReceivedValue(uint32 value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |value_| to the corresponding value from |peer_hello_| if it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ uint32 send_value_;
+ bool has_send_value_;
+ uint32 receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedTag : public QuicConfigValue {
+ public:
+ QuicFixedTag(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedTag();
+
+ bool HasSendValue() const;
+
+ QuicTag GetSendValue() const;
+
+ void SetSendValue(QuicTag value);
+
+ bool HasReceivedValue() const;
+
+ QuicTag GetReceivedValue() const;
+
+ void SetReceivedValue(QuicTag value);
+
+ // If has_send_value is true, serialises |tag_| and |send_value_| to |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |value_| to the corresponding value from |client_hello_| if it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ QuicTag send_value_;
+ bool has_send_value_;
+ QuicTag receive_value_;
+ bool has_receive_value_;
+};
+
+// Stores tag from CHLO or SHLO messages that are not negotiated.
+class NET_EXPORT_PRIVATE QuicFixedTagVector : public QuicConfigValue {
+ public:
+ QuicFixedTagVector(QuicTag name, QuicConfigPresence presence);
+ virtual ~QuicFixedTagVector();
+
+ bool HasSendValues() const;
+
+ QuicTagVector GetSendValues() const;
+
+ void SetSendValues(const QuicTagVector& values);
+
+ bool HasReceivedValues() const;
+
+ QuicTagVector GetReceivedValues() const;
+
+ void SetReceivedValues(const QuicTagVector& values);
+
+ // If has_send_value is true, serialises |tag_vector_| and |send_value_| to
+ // |out|.
+ virtual void ToHandshakeMessage(CryptoHandshakeMessage* out) const OVERRIDE;
+
+ // Sets |receive_values_| to the corresponding value from |client_hello_| if
+ // it exists.
+ virtual QuicErrorCode ProcessPeerHello(
+ const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details) OVERRIDE;
+
+ private:
+ QuicTagVector send_values_;
+ bool has_send_values_;
+ QuicTagVector receive_values_;
+ bool has_receive_values_;
+};
+
+// QuicConfig contains non-crypto configuration options that are negotiated in
+// the crypto handshake.
+class NET_EXPORT_PRIVATE QuicConfig {
+ public:
+ QuicConfig();
+ ~QuicConfig();
+
+ void SetCongestionFeedback(const QuicTagVector& congestion_feedback,
+ QuicTag default_congestion_feedback);
+
+ QuicTag CongestionFeedback() const;
+
+ void SetConnectionOptionsToSend(const QuicTagVector& connection_options);
+
+ bool HasReceivedConnectionOptions() const;
+
+ QuicTagVector ReceivedConnectionOptions() const;
+
+ bool HasSendConnectionOptions() const;
+
+ QuicTagVector SendConnectionOptions() const;
+
+ void SetIdleConnectionStateLifetime(
+ QuicTime::Delta max_idle_connection_state_lifetime,
+ QuicTime::Delta default_idle_conection_state_lifetime);
+
+ QuicTime::Delta IdleConnectionStateLifetime() const;
+
+ QuicTime::Delta KeepaliveTimeout() const;
+
+ void SetMaxStreamsPerConnection(size_t max_streams, size_t default_streams);
+
+ uint32 MaxStreamsPerConnection() const;
+
+ void set_max_time_before_crypto_handshake(
+ QuicTime::Delta max_time_before_crypto_handshake) {
+ max_time_before_crypto_handshake_ = max_time_before_crypto_handshake;
+ }
+
+ QuicTime::Delta max_time_before_crypto_handshake() const {
+ return max_time_before_crypto_handshake_;
+ }
+
+ void set_max_idle_time_before_crypto_handshake(
+ QuicTime::Delta max_idle_time_before_crypto_handshake) {
+ max_idle_time_before_crypto_handshake_ =
+ max_idle_time_before_crypto_handshake;
+ }
+
+ QuicTime::Delta max_idle_time_before_crypto_handshake() const {
+ return max_idle_time_before_crypto_handshake_;
+ }
+
+ // Sets the peer's default initial congestion window in packets.
+ void SetInitialCongestionWindowToSend(size_t initial_window);
+
+ bool HasReceivedInitialCongestionWindow() const;
+
+ uint32 ReceivedInitialCongestionWindow() const;
+
+ // Sets an estimated initial round trip time in us.
+ void SetInitialRoundTripTimeUsToSend(size_t rtt_us);
+
+ bool HasReceivedInitialRoundTripTimeUs() const;
+
+ uint32 ReceivedInitialRoundTripTimeUs() const;
+
+ bool HasInitialRoundTripTimeUsToSend() const;
+
+ uint32 GetInitialRoundTripTimeUsToSend() const;
+
+ // TODO(rjshade): Remove all InitialFlowControlWindow methods when removing
+ // QUIC_VERSION_19.
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialFlowControlWindowBytes() const;
+
+ // Sets an initial stream flow control window size to transmit to the peer.
+ void SetInitialStreamFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialStreamFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialStreamFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialStreamFlowControlWindowBytes() const;
+
+ // Sets an initial session flow control window size to transmit to the peer.
+ void SetInitialSessionFlowControlWindowToSend(uint32 window_bytes);
+
+ uint32 GetInitialSessionFlowControlWindowToSend() const;
+
+ bool HasReceivedInitialSessionFlowControlWindowBytes() const;
+
+ uint32 ReceivedInitialSessionFlowControlWindowBytes() const;
+
+ // Sets socket receive buffer to transmit to the peer.
+ void SetSocketReceiveBufferToSend(uint32 window_bytes);
+
+ uint32 GetSocketReceiveBufferToSend() const;
+
+ bool HasReceivedSocketReceiveBuffer() const;
+
+ uint32 ReceivedSocketReceiveBuffer() const;
+
+ bool negotiated() const;
+
+ // SetDefaults sets the members to sensible, default values.
+ void SetDefaults();
+
+ // ToHandshakeMessage serialises the settings in this object as a series of
+ // tags /value pairs and adds them to |out|.
+ void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+
+ // Calls ProcessPeerHello on each negotiable parameter. On failure returns
+ // the corresponding QuicErrorCode and sets detailed error in |error_details|.
+ QuicErrorCode ProcessPeerHello(const CryptoHandshakeMessage& peer_hello,
+ HelloType hello_type,
+ std::string* error_details);
+
+ private:
+ friend class test::QuicConfigPeer;
+
+ // Configurations options that are not negotiated.
+ // Maximum time the session can be alive before crypto handshake is finished.
+ QuicTime::Delta max_time_before_crypto_handshake_;
+ // Maximum idle time before the crypto handshake has completed.
+ QuicTime::Delta max_idle_time_before_crypto_handshake_;
+
+ // Congestion control feedback type.
+ QuicNegotiableTag congestion_feedback_;
+ // Connection options.
+ QuicFixedTagVector connection_options_;
+ // Idle connection state lifetime
+ QuicNegotiableUint32 idle_connection_state_lifetime_seconds_;
+ // Keepalive timeout, or 0 to turn off keepalive probes
+ QuicNegotiableUint32 keepalive_timeout_seconds_;
+ // Maximum number of streams that the connection can support.
+ QuicNegotiableUint32 max_streams_per_connection_;
+ // Initial congestion window in packets.
+ QuicFixedUint32 initial_congestion_window_;
+ // Initial round trip time estimate in microseconds.
+ QuicFixedUint32 initial_round_trip_time_us_;
+
+ // TODO(rjshade): Remove when removing QUIC_VERSION_19.
+ // Initial flow control receive window in bytes.
+ QuicFixedUint32 initial_flow_control_window_bytes_;
+
+ // Initial stream flow control receive window in bytes.
+ QuicFixedUint32 initial_stream_flow_control_window_bytes_;
+ // Initial session flow control receive window in bytes.
+ QuicFixedUint32 initial_session_flow_control_window_bytes_;
+
+ // Socket receive buffer in bytes.
+ QuicFixedUint32 socket_receive_buffer_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CONFIG_H_
diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc
new file mode 100644
index 0000000..2263e44
--- /dev/null
+++ b/net/quic/quic_config_test.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_config.h"
+
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+class QuicConfigTest : public ::testing::Test {
+ protected:
+ QuicConfigTest() {
+ config_.SetDefaults();
+ }
+
+ QuicConfig config_;
+};
+
+TEST_F(QuicConfigTest, ToHandshakeMessage) {
+ config_.SetDefaults();
+ config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config_.SetIdleConnectionStateLifetime(QuicTime::Delta::FromSeconds(5),
+ QuicTime::Delta::FromSeconds(2));
+ config_.SetMaxStreamsPerConnection(4, 2);
+ config_.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer);
+ CryptoHandshakeMessage msg;
+ config_.ToHandshakeMessage(&msg);
+
+ uint32 value;
+ QuicErrorCode error = msg.GetUint32(kICSL, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(5u, value);
+
+ error = msg.GetUint32(kMSPC, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(4u, value);
+
+ error = msg.GetUint32(kIFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kSFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kCFCW, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
+ error = msg.GetUint32(kSRBF, &value);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_EQ(kDefaultSocketReceiveBuffer, value);
+
+ const QuicTag* out;
+ size_t out_len;
+ error = msg.GetTaglist(kCGST, &out, &out_len);
+ EXPECT_EQ(1u, out_len);
+ EXPECT_EQ(kQBIC, *out);
+}
+
+TEST_F(QuicConfigTest, ProcessClientHello) {
+ QuicConfig client_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ client_config.SetCongestionFeedback(cgst, kQBIC);
+ client_config.SetIdleConnectionStateLifetime(
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs));
+ client_config.SetMaxStreamsPerConnection(
+ 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection);
+ client_config.SetInitialRoundTripTimeUsToSend(
+ 10 * base::Time::kMicrosecondsPerMillisecond);
+ client_config.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ client_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ client_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ client_config.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer);
+ QuicTagVector copt;
+ copt.push_back(kTBBR);
+ copt.push_back(kFHDR);
+ client_config.SetConnectionOptionsToSend(copt);
+ CryptoHandshakeMessage msg;
+ client_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+ EXPECT_EQ(kQBIC, config_.CongestionFeedback());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs),
+ config_.IdleConnectionStateLifetime());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ config_.MaxStreamsPerConnection());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.KeepaliveTimeout());
+ EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
+ config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_TRUE(config_.HasReceivedConnectionOptions());
+ EXPECT_EQ(2u, config_.ReceivedConnectionOptions().size());
+ EXPECT_EQ(config_.ReceivedConnectionOptions()[0], kTBBR);
+ EXPECT_EQ(config_.ReceivedConnectionOptions()[1], kFHDR);
+ EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedSocketReceiveBuffer(),
+ kDefaultSocketReceiveBuffer);
+}
+
+TEST_F(QuicConfigTest, ProcessServerHello) {
+ QuicConfig server_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ server_config.SetCongestionFeedback(cgst, kQBIC);
+ server_config.SetIdleConnectionStateLifetime(
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2),
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2));
+ server_config.SetMaxStreamsPerConnection(
+ kDefaultMaxStreamsPerConnection / 2,
+ kDefaultMaxStreamsPerConnection / 2);
+ server_config.SetInitialCongestionWindowToSend(kDefaultInitialWindow / 2);
+ server_config.SetInitialRoundTripTimeUsToSend(
+ 10 * base::Time::kMicrosecondsPerMillisecond);
+ server_config.SetInitialFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config.SetInitialStreamFlowControlWindowToSend(
+ 2 * kInitialStreamFlowControlWindowForTest);
+ server_config.SetInitialSessionFlowControlWindowToSend(
+ 2 * kInitialSessionFlowControlWindowForTest);
+ server_config.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer);
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ EXPECT_TRUE(config_.negotiated());
+ EXPECT_EQ(kQBIC, config_.CongestionFeedback());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs / 2),
+ config_.IdleConnectionStateLifetime());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2,
+ config_.MaxStreamsPerConnection());
+ EXPECT_EQ(kDefaultInitialWindow / 2,
+ config_.ReceivedInitialCongestionWindow());
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.KeepaliveTimeout());
+ EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
+ config_.ReceivedInitialRoundTripTimeUs());
+ EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+ 2 * kInitialStreamFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+ 2 * kInitialSessionFlowControlWindowForTest);
+ EXPECT_EQ(config_.ReceivedSocketReceiveBuffer(),
+ kDefaultSocketReceiveBuffer);
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ msg.SetValue(kMSPC, 1);
+
+ // No error, as rest are optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes());
+}
+
+TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) {
+ CryptoHandshakeMessage msg;
+
+ // Set all REQUIRED tags.
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ msg.SetValue(kMSPC, 1);
+
+ // No error, as rest are optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+
+ EXPECT_FALSE(config_.HasReceivedInitialFlowControlWindowBytes());
+}
+
+TEST_F(QuicConfigTest, MissingValueInCHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetVector(kCGST, QuicTagVector(1, kQBIC));
+ // Missing kMSPC. KATO is optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
+}
+
+TEST_F(QuicConfigTest, MissingValueInSHLO) {
+ CryptoHandshakeMessage msg;
+ msg.SetValue(kICSL, 1);
+ msg.SetValue(kMSPC, 3);
+ // Missing CGST. KATO is optional.
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
+}
+
+TEST_F(QuicConfigTest, OutOfBoundSHLO) {
+ QuicConfig server_config;
+ server_config.SetIdleConnectionStateLifetime(
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs));
+
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
+}
+
+TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) {
+ QuicConfig server_config;
+ QuicTagVector cgst;
+ cgst.push_back(kQBIC);
+ cgst.push_back(kTBBR);
+ server_config.SetCongestionFeedback(cgst, kQBIC);
+
+ CryptoHandshakeMessage msg;
+ server_config.ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, SERVER, &error_details);
+ EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
+}
+
+TEST_F(QuicConfigTest, NoOverLapInCGST) {
+ QuicConfig server_config;
+ server_config.SetDefaults();
+ QuicTagVector cgst;
+ cgst.push_back(kTBBR);
+ server_config.SetCongestionFeedback(cgst, kTBBR);
+
+ CryptoHandshakeMessage msg;
+ string error_details;
+ server_config.ToHandshakeMessage(&msg);
+ const QuicErrorCode error =
+ config_.ProcessPeerHello(msg, CLIENT, &error_details);
+ DVLOG(1) << QuicUtils::ErrorToString(error);
+ EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error);
+}
+
+TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
+ // QuicConfig should not accept an invalid flow control window to send to the
+ // peer: the receive window must be at least the default of 16 Kb.
+ QuicConfig config;
+ const uint64 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ EXPECT_DFATAL(config.SetInitialFlowControlWindowToSend(kInvalidWindow),
+ "Initial flow control receive window");
+
+ EXPECT_EQ(kDefaultFlowControlSendWindow,
+ config.GetInitialFlowControlWindowToSend());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
new file mode 100644
index 0000000..8d49010
--- /dev/null
+++ b/net/quic/quic_connection.cc
@@ -0,0 +1,2041 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/base/net_errors.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/iovector.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_fec_group.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using base::hash_map;
+using base::hash_set;
+using std::list;
+using std::make_pair;
+using std::max;
+using std::min;
+using std::numeric_limits;
+using std::set;
+using std::string;
+using std::vector;
+
+namespace net {
+
+class QuicDecrypter;
+class QuicEncrypter;
+
+namespace {
+
+// The largest gap in packets we'll accept without closing the connection.
+// This will likely have to be tuned.
+const QuicPacketSequenceNumber kMaxPacketGap = 5000;
+
+// Limit the number of FEC groups to two. If we get enough out of order packets
+// that this becomes limiting, we can revisit.
+const size_t kMaxFecGroups = 2;
+
+// Limit the number of undecryptable packets we buffer in
+// expectation of the CHLO/SHLO arriving.
+const size_t kMaxUndecryptablePackets = 10;
+
+// Maximum number of acks received before sending an ack in response.
+const size_t kMaxPacketsReceivedBeforeAckSend = 20;
+
+bool Near(QuicPacketSequenceNumber a, QuicPacketSequenceNumber b) {
+ QuicPacketSequenceNumber delta = (a > b) ? a - b : b - a;
+ return delta <= kMaxPacketGap;
+}
+
+// An alarm that is scheduled to send an ack if a timeout occurs.
+class AckAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit AckAlarm(QuicConnection* connection)
+ : connection_(connection) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->SendAck();
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(AckAlarm);
+};
+
+// This alarm will be scheduled any time a data-bearing packet is sent out.
+// When the alarm goes off, the connection checks to see if the oldest packets
+// have been acked, and retransmit them if they have not.
+class RetransmissionAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit RetransmissionAlarm(QuicConnection* connection)
+ : connection_(connection) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->OnRetransmissionTimeout();
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetransmissionAlarm);
+};
+
+// An alarm that is scheduled when the sent scheduler requires a
+// a delay before sending packets and fires when the packet may be sent.
+class SendAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit SendAlarm(QuicConnection* connection)
+ : connection_(connection) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->WriteIfNotBlocked();
+ // Never reschedule the alarm, since CanWrite does that.
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendAlarm);
+};
+
+class TimeoutAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit TimeoutAlarm(QuicConnection* connection)
+ : connection_(connection) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->CheckForTimeout();
+ // Never reschedule the alarm, since CheckForTimeout does that.
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimeoutAlarm);
+};
+
+class PingAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit PingAlarm(QuicConnection* connection)
+ : connection_(connection) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ connection_->SendPing();
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicConnection* connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(PingAlarm);
+};
+
+} // namespace
+
+QuicConnection::QueuedPacket::QueuedPacket(SerializedPacket packet,
+ EncryptionLevel level)
+ : serialized_packet(packet),
+ encryption_level(level),
+ transmission_type(NOT_RETRANSMISSION),
+ original_sequence_number(0) {
+}
+
+QuicConnection::QueuedPacket::QueuedPacket(
+ SerializedPacket packet,
+ EncryptionLevel level,
+ TransmissionType transmission_type,
+ QuicPacketSequenceNumber original_sequence_number)
+ : serialized_packet(packet),
+ encryption_level(level),
+ transmission_type(transmission_type),
+ original_sequence_number(original_sequence_number) {
+}
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicConnection::QuicConnection(QuicConnectionId connection_id,
+ IPEndPoint address,
+ QuicConnectionHelperInterface* helper,
+ const PacketWriterFactory& writer_factory,
+ bool owns_writer,
+ bool is_server,
+ const QuicVersionVector& supported_versions)
+ : framer_(supported_versions, helper->GetClock()->ApproximateNow(),
+ is_server),
+ helper_(helper),
+ writer_(writer_factory.Create(this)),
+ owns_writer_(owns_writer),
+ encryption_level_(ENCRYPTION_NONE),
+ clock_(helper->GetClock()),
+ random_generator_(helper->GetRandomGenerator()),
+ connection_id_(connection_id),
+ peer_address_(address),
+ migrating_peer_port_(0),
+ last_packet_revived_(false),
+ last_size_(0),
+ last_decrypted_packet_level_(ENCRYPTION_NONE),
+ largest_seen_packet_with_ack_(0),
+ largest_seen_packet_with_stop_waiting_(0),
+ pending_version_negotiation_packet_(false),
+ received_packet_manager_(&stats_),
+ ack_queued_(false),
+ num_packets_received_since_last_ack_sent_(0),
+ stop_waiting_count_(0),
+ ack_alarm_(helper->CreateAlarm(new AckAlarm(this))),
+ retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))),
+ send_alarm_(helper->CreateAlarm(new SendAlarm(this))),
+ resume_writes_alarm_(helper->CreateAlarm(new SendAlarm(this))),
+ timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))),
+ ping_alarm_(helper->CreateAlarm(new PingAlarm(this))),
+ packet_generator_(connection_id_, &framer_, random_generator_, this),
+ idle_network_timeout_(FLAGS_quic_unified_timeouts ?
+ QuicTime::Delta::Infinite() :
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs)),
+ overall_connection_timeout_(QuicTime::Delta::Infinite()),
+ time_of_last_received_packet_(clock_->ApproximateNow()),
+ time_of_last_sent_new_packet_(clock_->ApproximateNow()),
+ sequence_number_of_last_sent_packet_(0),
+ sent_packet_manager_(
+ is_server, clock_, &stats_,
+ FLAGS_quic_use_bbr_congestion_control ? kBBR : kCubic,
+ FLAGS_quic_use_time_loss_detection ? kTime : kNack),
+ version_negotiation_state_(START_NEGOTIATION),
+ is_server_(is_server),
+ connected_(true),
+ peer_ip_changed_(false),
+ peer_port_changed_(false),
+ self_ip_changed_(false),
+ self_port_changed_(false) {
+ DVLOG(1) << ENDPOINT << "Created connection with connection_id: "
+ << connection_id;
+ if (!FLAGS_quic_unified_timeouts) {
+ timeout_alarm_->Set(clock_->ApproximateNow().Add(idle_network_timeout_));
+ }
+ framer_.set_visitor(this);
+ framer_.set_received_entropy_calculator(&received_packet_manager_);
+ stats_.connection_creation_time = clock_->ApproximateNow();
+ sent_packet_manager_.set_network_change_visitor(this);
+}
+
+QuicConnection::~QuicConnection() {
+ if (owns_writer_) {
+ delete writer_;
+ }
+ STLDeleteElements(&undecryptable_packets_);
+ STLDeleteValues(&group_map_);
+ for (QueuedPacketList::iterator it = queued_packets_.begin();
+ it != queued_packets_.end(); ++it) {
+ delete it->serialized_packet.retransmittable_frames;
+ delete it->serialized_packet.packet;
+ }
+}
+
+void QuicConnection::SetFromConfig(const QuicConfig& config) {
+ if (FLAGS_quic_unified_timeouts) {
+ if (config.negotiated()) {
+ SetNetworkTimeouts(QuicTime::Delta::Infinite(),
+ config.IdleConnectionStateLifetime());
+ } else {
+ SetNetworkTimeouts(config.max_time_before_crypto_handshake(),
+ config.max_idle_time_before_crypto_handshake());
+ }
+ } else {
+ SetIdleNetworkTimeout(config.IdleConnectionStateLifetime());
+ }
+ sent_packet_manager_.SetFromConfig(config);
+}
+
+bool QuicConnection::SelectMutualVersion(
+ const QuicVersionVector& available_versions) {
+ // Try to find the highest mutual version by iterating over supported
+ // versions, starting with the highest, and breaking out of the loop once we
+ // find a matching version in the provided available_versions vector.
+ const QuicVersionVector& supported_versions = framer_.supported_versions();
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ const QuicVersion& version = supported_versions[i];
+ if (std::find(available_versions.begin(), available_versions.end(),
+ version) != available_versions.end()) {
+ framer_.set_version(version);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QuicConnection::OnError(QuicFramer* framer) {
+ // Packets that we cannot decrypt are dropped.
+ // TODO(rch): add stats to measure this.
+ if (!connected_ || framer->error() == QUIC_DECRYPTION_FAILURE) {
+ return;
+ }
+ SendConnectionCloseWithDetails(framer->error(), framer->detailed_error());
+}
+
+void QuicConnection::OnPacket() {
+ DCHECK(last_stream_frames_.empty() &&
+ last_ack_frames_.empty() &&
+ last_congestion_frames_.empty() &&
+ last_stop_waiting_frames_.empty() &&
+ last_rst_frames_.empty() &&
+ last_goaway_frames_.empty() &&
+ last_window_update_frames_.empty() &&
+ last_blocked_frames_.empty() &&
+ last_ping_frames_.empty() &&
+ last_close_frames_.empty());
+}
+
+void QuicConnection::OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) {
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnPublicResetPacket(packet);
+ }
+ CloseConnection(QUIC_PUBLIC_RESET, true);
+
+ DVLOG(1) << ENDPOINT << "Connection " << connection_id()
+ << " closed via QUIC_PUBLIC_RESET from peer.";
+}
+
+bool QuicConnection::OnProtocolVersionMismatch(QuicVersion received_version) {
+ DVLOG(1) << ENDPOINT << "Received packet with mismatched version "
+ << received_version;
+ // TODO(satyamshekhar): Implement no server state in this mode.
+ if (!is_server_) {
+ LOG(DFATAL) << ENDPOINT << "Framer called OnProtocolVersionMismatch. "
+ << "Closing connection.";
+ CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return false;
+ }
+ DCHECK_NE(version(), received_version);
+
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnProtocolVersionMismatch(received_version);
+ }
+
+ switch (version_negotiation_state_) {
+ case START_NEGOTIATION:
+ if (!framer_.IsSupportedVersion(received_version)) {
+ SendVersionNegotiationPacket();
+ version_negotiation_state_ = NEGOTIATION_IN_PROGRESS;
+ return false;
+ }
+ break;
+
+ case NEGOTIATION_IN_PROGRESS:
+ if (!framer_.IsSupportedVersion(received_version)) {
+ SendVersionNegotiationPacket();
+ return false;
+ }
+ break;
+
+ case NEGOTIATED_VERSION:
+ // Might be old packets that were sent by the client before the version
+ // was negotiated. Drop these.
+ return false;
+
+ default:
+ DCHECK(false);
+ }
+
+ version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(received_version);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnSuccessfulVersionNegotiation(received_version);
+ }
+ DVLOG(1) << ENDPOINT << "version negotiated " << received_version;
+
+ // Store the new version.
+ framer_.set_version(received_version);
+
+ // TODO(satyamshekhar): Store the sequence number of this packet and close the
+ // connection if we ever received a packet with incorrect version and whose
+ // sequence number is greater.
+ return true;
+}
+
+// Handles version negotiation for client connection.
+void QuicConnection::OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) {
+ if (is_server_) {
+ LOG(DFATAL) << ENDPOINT << "Framer parsed VersionNegotiationPacket."
+ << " Closing connection.";
+ CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return;
+ }
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnVersionNegotiationPacket(packet);
+ }
+
+ if (version_negotiation_state_ != START_NEGOTIATION) {
+ // Possibly a duplicate version negotiation packet.
+ return;
+ }
+
+ if (std::find(packet.versions.begin(),
+ packet.versions.end(), version()) !=
+ packet.versions.end()) {
+ DLOG(WARNING) << ENDPOINT << "The server already supports our version. "
+ << "It should have accepted our connection.";
+ // Just drop the connection.
+ CloseConnection(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, false);
+ return;
+ }
+
+ if (!SelectMutualVersion(packet.versions)) {
+ SendConnectionCloseWithDetails(QUIC_INVALID_VERSION,
+ "no common version found");
+ return;
+ }
+
+ DVLOG(1) << ENDPOINT
+ << "Negotiated version: " << QuicVersionToString(version());
+ server_supported_versions_ = packet.versions;
+ version_negotiation_state_ = NEGOTIATION_IN_PROGRESS;
+ RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+}
+
+void QuicConnection::OnRevivedPacket() {
+}
+
+bool QuicConnection::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ return true;
+}
+
+bool QuicConnection::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ return true;
+}
+
+void QuicConnection::OnDecryptedPacket(EncryptionLevel level) {
+ last_decrypted_packet_level_ = level;
+}
+
+bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) {
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnPacketHeader(header);
+ }
+
+ if (!ProcessValidatedPacket()) {
+ return false;
+ }
+
+ // Will be decrement below if we fall through to return true;
+ ++stats_.packets_dropped;
+
+ if (header.public_header.connection_id != connection_id_) {
+ DVLOG(1) << ENDPOINT << "Ignoring packet from unexpected ConnectionId: "
+ << header.public_header.connection_id << " instead of "
+ << connection_id_;
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnIncorrectConnectionId(
+ header.public_header.connection_id);
+ }
+ return false;
+ }
+
+ if (!Near(header.packet_sequence_number,
+ last_header_.packet_sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " out of bounds. Discarding";
+ SendConnectionCloseWithDetails(QUIC_INVALID_PACKET_HEADER,
+ "Packet sequence number out of bounds");
+ return false;
+ }
+
+ // If this packet has already been seen, or that the sender
+ // has told us will not be retransmitted, then stop processing the packet.
+ if (!received_packet_manager_.IsAwaitingPacket(
+ header.packet_sequence_number)) {
+ DVLOG(1) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " no longer being waited for. Discarding.";
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnDuplicatePacket(header.packet_sequence_number);
+ }
+ return false;
+ }
+
+ if (version_negotiation_state_ != NEGOTIATED_VERSION) {
+ if (is_server_) {
+ if (!header.public_header.version_flag) {
+ DLOG(WARNING) << ENDPOINT << "Packet " << header.packet_sequence_number
+ << " without version flag before version negotiated.";
+ // Packets should have the version flag till version negotiation is
+ // done.
+ CloseConnection(QUIC_INVALID_VERSION, false);
+ return false;
+ } else {
+ DCHECK_EQ(1u, header.public_header.versions.size());
+ DCHECK_EQ(header.public_header.versions[0], version());
+ version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(version());
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnSuccessfulVersionNegotiation(version());
+ }
+ }
+ } else {
+ DCHECK(!header.public_header.version_flag);
+ // If the client gets a packet without the version flag from the server
+ // it should stop sending version since the version negotiation is done.
+ packet_generator_.StopSendingVersion();
+ version_negotiation_state_ = NEGOTIATED_VERSION;
+ visitor_->OnSuccessfulVersionNegotiation(version());
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnSuccessfulVersionNegotiation(version());
+ }
+ }
+ }
+
+ DCHECK_EQ(NEGOTIATED_VERSION, version_negotiation_state_);
+
+ --stats_.packets_dropped;
+ DVLOG(1) << ENDPOINT << "Received packet header: " << header;
+ last_header_ = header;
+ DCHECK(connected_);
+ return true;
+}
+
+void QuicConnection::OnFecProtectedPayload(StringPiece payload) {
+ DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group);
+ DCHECK_NE(0u, last_header_.fec_group);
+ QuicFecGroup* group = GetFecGroup();
+ if (group != nullptr) {
+ group->Update(last_decrypted_packet_level_, last_header_, payload);
+ }
+}
+
+bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnStreamFrame(frame);
+ }
+ if (frame.stream_id != kCryptoStreamId &&
+ last_decrypted_packet_level_ == ENCRYPTION_NONE) {
+ DLOG(WARNING) << ENDPOINT
+ << "Received an unencrypted data frame: closing connection";
+ SendConnectionClose(QUIC_UNENCRYPTED_STREAM_DATA);
+ return false;
+ }
+ last_stream_frames_.push_back(frame);
+ return true;
+}
+
+bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnAckFrame(incoming_ack);
+ }
+ DVLOG(1) << ENDPOINT << "OnAckFrame: " << incoming_ack;
+
+ if (last_header_.packet_sequence_number <= largest_seen_packet_with_ack_) {
+ DVLOG(1) << ENDPOINT << "Received an old ack frame: ignoring";
+ return true;
+ }
+
+ if (!ValidateAckFrame(incoming_ack)) {
+ SendConnectionClose(QUIC_INVALID_ACK_DATA);
+ return false;
+ }
+
+ last_ack_frames_.push_back(incoming_ack);
+ return connected_;
+}
+
+void QuicConnection::ProcessAckFrame(const QuicAckFrame& incoming_ack) {
+ largest_seen_packet_with_ack_ = last_header_.packet_sequence_number;
+ sent_packet_manager_.OnIncomingAck(incoming_ack,
+ time_of_last_received_packet_);
+ sent_entropy_manager_.ClearEntropyBefore(
+ sent_packet_manager_.least_packet_awaited_by_peer() - 1);
+ if (sent_packet_manager_.HasPendingRetransmissions()) {
+ WriteIfNotBlocked();
+ }
+
+ // Always reset the retransmission alarm when an ack comes in, since we now
+ // have a better estimate of the current rtt than when it was set.
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ retransmission_alarm_->Update(retransmission_time,
+ QuicTime::Delta::FromMilliseconds(1));
+}
+
+void QuicConnection::ProcessStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ largest_seen_packet_with_stop_waiting_ = last_header_.packet_sequence_number;
+ received_packet_manager_.UpdatePacketInformationSentByPeer(stop_waiting);
+ // Possibly close any FecGroups which are now irrelevant.
+ CloseFecGroupsBefore(stop_waiting.least_unacked + 1);
+}
+
+bool QuicConnection::OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& feedback) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnCongestionFeedbackFrame(feedback);
+ }
+ last_congestion_frames_.push_back(feedback);
+ return connected_;
+}
+
+bool QuicConnection::OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {
+ DCHECK(connected_);
+
+ if (last_header_.packet_sequence_number <=
+ largest_seen_packet_with_stop_waiting_) {
+ DVLOG(1) << ENDPOINT << "Received an old stop waiting frame: ignoring";
+ return true;
+ }
+
+ if (!ValidateStopWaitingFrame(frame)) {
+ SendConnectionClose(QUIC_INVALID_STOP_WAITING_DATA);
+ return false;
+ }
+
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnStopWaitingFrame(frame);
+ }
+
+ last_stop_waiting_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnPingFrame(const QuicPingFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnPingFrame(frame);
+ }
+ last_ping_frames_.push_back(frame);
+ return true;
+}
+
+bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) {
+ if (incoming_ack.largest_observed > packet_generator_.sequence_number()) {
+ DLOG(ERROR) << ENDPOINT << "Peer's observed unsent packet:"
+ << incoming_ack.largest_observed << " vs "
+ << packet_generator_.sequence_number();
+ // We got an error for data we have not sent. Error out.
+ return false;
+ }
+
+ if (incoming_ack.largest_observed < sent_packet_manager_.largest_observed()) {
+ DLOG(ERROR) << ENDPOINT << "Peer's largest_observed packet decreased:"
+ << incoming_ack.largest_observed << " vs "
+ << sent_packet_manager_.largest_observed();
+ // A new ack has a diminished largest_observed value. Error out.
+ // If this was an old packet, we wouldn't even have checked.
+ return false;
+ }
+
+ if (!incoming_ack.missing_packets.empty() &&
+ *incoming_ack.missing_packets.rbegin() > incoming_ack.largest_observed) {
+ DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: "
+ << *incoming_ack.missing_packets.rbegin()
+ << " which is greater than largest observed: "
+ << incoming_ack.largest_observed;
+ return false;
+ }
+
+ if (!incoming_ack.missing_packets.empty() &&
+ *incoming_ack.missing_packets.begin() <
+ sent_packet_manager_.least_packet_awaited_by_peer()) {
+ DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: "
+ << *incoming_ack.missing_packets.begin()
+ << " which is smaller than least_packet_awaited_by_peer_: "
+ << sent_packet_manager_.least_packet_awaited_by_peer();
+ return false;
+ }
+
+ if (!sent_entropy_manager_.IsValidEntropy(
+ incoming_ack.largest_observed,
+ incoming_ack.missing_packets,
+ incoming_ack.entropy_hash)) {
+ DLOG(ERROR) << ENDPOINT << "Peer sent invalid entropy.";
+ return false;
+ }
+
+ for (SequenceNumberSet::const_iterator iter =
+ incoming_ack.revived_packets.begin();
+ iter != incoming_ack.revived_packets.end(); ++iter) {
+ if (!ContainsKey(incoming_ack.missing_packets, *iter)) {
+ DLOG(ERROR) << ENDPOINT
+ << "Peer specified revived packet which was not missing.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicConnection::ValidateStopWaitingFrame(
+ const QuicStopWaitingFrame& stop_waiting) {
+ if (stop_waiting.least_unacked <
+ received_packet_manager_.peer_least_packet_awaiting_ack()) {
+ DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: "
+ << stop_waiting.least_unacked << " vs "
+ << received_packet_manager_.peer_least_packet_awaiting_ack();
+ // We never process old ack frames, so this number should only increase.
+ return false;
+ }
+
+ if (stop_waiting.least_unacked >
+ last_header_.packet_sequence_number) {
+ DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:"
+ << stop_waiting.least_unacked
+ << " greater than the enclosing packet sequence number:"
+ << last_header_.packet_sequence_number;
+ return false;
+ }
+
+ return true;
+}
+
+void QuicConnection::OnFecData(const QuicFecData& fec) {
+ DCHECK_EQ(IN_FEC_GROUP, last_header_.is_in_fec_group);
+ DCHECK_NE(0u, last_header_.fec_group);
+ QuicFecGroup* group = GetFecGroup();
+ if (group != nullptr) {
+ group->UpdateFec(last_decrypted_packet_level_,
+ last_header_.packet_sequence_number, fec);
+ }
+}
+
+bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnRstStreamFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "Stream reset with error "
+ << QuicUtils::StreamErrorToString(frame.error_code);
+ last_rst_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnConnectionCloseFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "Connection " << connection_id()
+ << " closed with error "
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " " << frame.error_details;
+ last_close_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnGoAwayFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "Go away received with error "
+ << QuicUtils::ErrorToString(frame.error_code)
+ << " and reason:" << frame.reason_phrase;
+ last_goaway_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnWindowUpdateFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "WindowUpdate received for stream: "
+ << frame.stream_id << " with byte offset: " << frame.byte_offset;
+ last_window_update_frames_.push_back(frame);
+ return connected_;
+}
+
+bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ DCHECK(connected_);
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnBlockedFrame(frame);
+ }
+ DVLOG(1) << ENDPOINT << "Blocked frame received for stream: "
+ << frame.stream_id;
+ last_blocked_frames_.push_back(frame);
+ return connected_;
+}
+
+void QuicConnection::OnPacketComplete() {
+ // Don't do anything if this packet closed the connection.
+ if (!connected_) {
+ ClearLastFrames();
+ return;
+ }
+
+ DVLOG(1) << ENDPOINT << (last_packet_revived_ ? "Revived" : "Got")
+ << " packet " << last_header_.packet_sequence_number
+ << " with " << last_stream_frames_.size()<< " stream frames "
+ << last_ack_frames_.size() << " acks, "
+ << last_congestion_frames_.size() << " congestions, "
+ << last_stop_waiting_frames_.size() << " stop_waiting, "
+ << last_rst_frames_.size() << " rsts, "
+ << last_goaway_frames_.size() << " goaways, "
+ << last_window_update_frames_.size() << " window updates, "
+ << last_blocked_frames_.size() << " blocked, "
+ << last_ping_frames_.size() << " pings, "
+ << last_close_frames_.size() << " closes, "
+ << "for " << last_header_.public_header.connection_id;
+
+ ++num_packets_received_since_last_ack_sent_;
+
+ // Call MaybeQueueAck() before recording the received packet, since we want
+ // to trigger an ack if the newly received packet was previously missing.
+ MaybeQueueAck();
+
+ // Record received or revived packet to populate ack info correctly before
+ // processing stream frames, since the processing may result in a response
+ // packet with a bundled ack.
+ if (last_packet_revived_) {
+ received_packet_manager_.RecordPacketRevived(
+ last_header_.packet_sequence_number);
+ } else {
+ received_packet_manager_.RecordPacketReceived(
+ last_size_, last_header_, time_of_last_received_packet_);
+ }
+
+ if (!last_stream_frames_.empty()) {
+ visitor_->OnStreamFrames(last_stream_frames_);
+ }
+
+ for (size_t i = 0; i < last_stream_frames_.size(); ++i) {
+ stats_.stream_bytes_received +=
+ last_stream_frames_[i].data.TotalBufferSize();
+ }
+
+ // Process window updates, blocked, stream resets, acks, then congestion
+ // feedback.
+ if (!last_window_update_frames_.empty()) {
+ visitor_->OnWindowUpdateFrames(last_window_update_frames_);
+ }
+ if (!last_blocked_frames_.empty()) {
+ visitor_->OnBlockedFrames(last_blocked_frames_);
+ }
+ for (size_t i = 0; i < last_goaway_frames_.size(); ++i) {
+ visitor_->OnGoAway(last_goaway_frames_[i]);
+ }
+ for (size_t i = 0; i < last_rst_frames_.size(); ++i) {
+ visitor_->OnRstStream(last_rst_frames_[i]);
+ }
+ for (size_t i = 0; i < last_ack_frames_.size(); ++i) {
+ ProcessAckFrame(last_ack_frames_[i]);
+ }
+ for (size_t i = 0; i < last_congestion_frames_.size(); ++i) {
+ sent_packet_manager_.OnIncomingQuicCongestionFeedbackFrame(
+ last_congestion_frames_[i], time_of_last_received_packet_);
+ }
+ for (size_t i = 0; i < last_stop_waiting_frames_.size(); ++i) {
+ ProcessStopWaitingFrame(last_stop_waiting_frames_[i]);
+ }
+ if (!last_close_frames_.empty()) {
+ CloseConnection(last_close_frames_[0].error_code, true);
+ DCHECK(!connected_);
+ }
+
+ // If there are new missing packets to report, send an ack immediately.
+ if (received_packet_manager_.HasNewMissingPackets()) {
+ ack_queued_ = true;
+ ack_alarm_->Cancel();
+ }
+
+ UpdateStopWaitingCount();
+
+ ClearLastFrames();
+}
+
+void QuicConnection::MaybeQueueAck() {
+ // If the incoming packet was missing, send an ack immediately.
+ ack_queued_ = received_packet_manager_.IsMissing(
+ last_header_.packet_sequence_number);
+
+ if (!ack_queued_ && ShouldLastPacketInstigateAck()) {
+ if (ack_alarm_->IsSet()) {
+ ack_queued_ = true;
+ } else {
+ // Send an ack much more quickly for crypto handshake packets.
+ QuicTime::Delta delayed_ack_time = sent_packet_manager_.DelayedAckTime();
+ if (last_stream_frames_.size() == 1 &&
+ last_stream_frames_[0].stream_id == kCryptoStreamId) {
+ delayed_ack_time = QuicTime::Delta::Zero();
+ }
+ ack_alarm_->Set(clock_->ApproximateNow().Add(delayed_ack_time));
+ DVLOG(1) << "Ack timer set; next packet or timer will trigger ACK.";
+ }
+ }
+
+ if (ack_queued_) {
+ ack_alarm_->Cancel();
+ }
+}
+
+void QuicConnection::ClearLastFrames() {
+ last_stream_frames_.clear();
+ last_ack_frames_.clear();
+ last_congestion_frames_.clear();
+ last_stop_waiting_frames_.clear();
+ last_rst_frames_.clear();
+ last_goaway_frames_.clear();
+ last_window_update_frames_.clear();
+ last_blocked_frames_.clear();
+ last_ping_frames_.clear();
+ last_close_frames_.clear();
+}
+
+QuicAckFrame* QuicConnection::CreateAckFrame() {
+ QuicAckFrame* outgoing_ack = new QuicAckFrame();
+ received_packet_manager_.UpdateReceivedPacketInfo(
+ outgoing_ack, clock_->ApproximateNow());
+ DVLOG(1) << ENDPOINT << "Creating ack frame: " << *outgoing_ack;
+ return outgoing_ack;
+}
+
+QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() {
+ return new QuicCongestionFeedbackFrame(outgoing_congestion_feedback_);
+}
+
+QuicStopWaitingFrame* QuicConnection::CreateStopWaitingFrame() {
+ QuicStopWaitingFrame stop_waiting;
+ UpdateStopWaiting(&stop_waiting);
+ return new QuicStopWaitingFrame(stop_waiting);
+}
+
+bool QuicConnection::ShouldLastPacketInstigateAck() const {
+ if (!last_stream_frames_.empty() ||
+ !last_goaway_frames_.empty() ||
+ !last_rst_frames_.empty() ||
+ !last_window_update_frames_.empty() ||
+ !last_blocked_frames_.empty() ||
+ !last_ping_frames_.empty()) {
+ return true;
+ }
+
+ if (!last_ack_frames_.empty() && last_ack_frames_.back().is_truncated) {
+ return true;
+ }
+ // Always send an ack every 20 packets in order to allow the peer to discard
+ // information from the SentPacketManager and provide an RTT measurement.
+ if (num_packets_received_since_last_ack_sent_ >=
+ kMaxPacketsReceivedBeforeAckSend) {
+ return true;
+ }
+ return false;
+}
+
+void QuicConnection::UpdateStopWaitingCount() {
+ if (last_ack_frames_.empty()) {
+ return;
+ }
+
+ // If the peer is still waiting for a packet that we are no longer planning to
+ // send, send an ack to raise the high water mark.
+ if (!last_ack_frames_.back().missing_packets.empty() &&
+ GetLeastUnacked() > *last_ack_frames_.back().missing_packets.begin()) {
+ ++stop_waiting_count_;
+ } else {
+ stop_waiting_count_ = 0;
+ }
+}
+
+QuicPacketSequenceNumber QuicConnection::GetLeastUnacked() const {
+ return sent_packet_manager_.GetLeastUnacked();
+}
+
+void QuicConnection::MaybeSendInResponseToPacket() {
+ if (!connected_) {
+ return;
+ }
+ ScopedPacketBundler bundler(this, ack_queued_ ? SEND_ACK : NO_ACK);
+
+ // Now that we have received an ack, we might be able to send packets which
+ // are queued locally, or drain streams which are blocked.
+ if (CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ OnCanWrite();
+ }
+}
+
+void QuicConnection::SendVersionNegotiationPacket() {
+ // TODO(alyssar): implement zero server state negotiation.
+ pending_version_negotiation_packet_ = true;
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
+ return;
+ }
+ DVLOG(1) << ENDPOINT << "Sending version negotiation packet: {"
+ << QuicVersionVectorToString(framer_.supported_versions()) << "}";
+ scoped_ptr<QuicEncryptedPacket> version_packet(
+ packet_generator_.SerializeVersionNegotiationPacket(
+ framer_.supported_versions()));
+ WriteResult result = writer_->WritePacket(
+ version_packet->data(), version_packet->length(),
+ self_address().address(), peer_address());
+
+ if (result.status == WRITE_STATUS_ERROR) {
+ // We can't send an error as the socket is presumably borked.
+ CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
+ return;
+ }
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ visitor_->OnWriteBlocked();
+ if (writer_->IsWriteBlockedDataBuffered()) {
+ pending_version_negotiation_packet_ = false;
+ }
+ return;
+ }
+
+ pending_version_negotiation_packet_ = false;
+}
+
+QuicConsumedData QuicConnection::SendStreamData(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ if (!fin && data.Empty()) {
+ LOG(DFATAL) << "Attempt to send empty stream frame";
+ }
+
+ // This notifier will be owned by the AckNotifierManager (or deleted below if
+ // no data or FIN was consumed).
+ QuicAckNotifier* notifier = nullptr;
+ if (delegate) {
+ notifier = new QuicAckNotifier(delegate);
+ }
+
+ // Opportunistically bundle an ack with every outgoing packet.
+ // Particularly, we want to bundle with handshake packets since we don't know
+ // which decrypter will be used on an ack packet following a handshake
+ // packet (a handshake packet from client to server could result in a REJ or a
+ // SHLO from the server, leading to two different decrypters at the server.)
+ //
+ // TODO(jri): Note that ConsumeData may cause a response packet to be sent.
+ // We may end up sending stale ack information if there are undecryptable
+ // packets hanging around and/or there are revivable packets which may get
+ // handled after this packet is sent. Change ScopedPacketBundler to do the
+ // right thing: check ack_queued_, and then check undecryptable packets and
+ // also if there is possibility of revival. Only bundle an ack if there's no
+ // processing left that may cause received_info_ to change.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ QuicConsumedData consumed_data =
+ packet_generator_.ConsumeData(id, data, offset, fin, fec_protection,
+ notifier);
+
+ if (notifier &&
+ (consumed_data.bytes_consumed == 0 && !consumed_data.fin_consumed)) {
+ // No data was consumed, nor was a fin consumed, so delete the notifier.
+ delete notifier;
+ }
+
+ return consumed_data;
+}
+
+void QuicConnection::SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(QuicFrame(new QuicRstStreamFrame(
+ id, AdjustErrorForVersion(error, version()), bytes_written)));
+}
+
+void QuicConnection::SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(
+ QuicFrame(new QuicWindowUpdateFrame(id, byte_offset)));
+}
+
+void QuicConnection::SendBlocked(QuicStreamId id) {
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(QuicFrame(new QuicBlockedFrame(id)));
+}
+
+const QuicConnectionStats& QuicConnection::GetStats() {
+ // Update rtt and estimated bandwidth.
+ stats_.min_rtt_us =
+ sent_packet_manager_.GetRttStats()->min_rtt().ToMicroseconds();
+ stats_.srtt_us =
+ sent_packet_manager_.GetRttStats()->SmoothedRtt().ToMicroseconds();
+ stats_.estimated_bandwidth =
+ sent_packet_manager_.BandwidthEstimate().ToBytesPerSecond();
+ stats_.congestion_window = sent_packet_manager_.GetCongestionWindow();
+ stats_.slow_start_threshold = sent_packet_manager_.GetSlowStartThreshold();
+ stats_.max_packet_size = packet_generator_.max_packet_length();
+ return stats_;
+}
+
+void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet) {
+ if (!connected_) {
+ return;
+ }
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnPacketReceived(self_address, peer_address, packet);
+ }
+ last_packet_revived_ = false;
+ last_size_ = packet.length();
+
+ CheckForAddressMigration(self_address, peer_address);
+
+ stats_.bytes_received += packet.length();
+ ++stats_.packets_received;
+
+ if (!framer_.ProcessPacket(packet)) {
+ // If we are unable to decrypt this packet, it might be
+ // because the CHLO or SHLO packet was lost.
+ if (framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ if (encryption_level_ != ENCRYPTION_FORWARD_SECURE &&
+ undecryptable_packets_.size() < kMaxUndecryptablePackets) {
+ QueueUndecryptablePacket(packet);
+ } else if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnUndecryptablePacket();
+ }
+ }
+ DVLOG(1) << ENDPOINT << "Unable to process packet. Last packet processed: "
+ << last_header_.packet_sequence_number;
+ return;
+ }
+
+ ++stats_.packets_processed;
+ MaybeProcessUndecryptablePackets();
+ MaybeProcessRevivedPacket();
+ MaybeSendInResponseToPacket();
+ SetPingAlarm();
+}
+
+void QuicConnection::CheckForAddressMigration(
+ const IPEndPoint& self_address, const IPEndPoint& peer_address) {
+ peer_ip_changed_ = false;
+ peer_port_changed_ = false;
+ self_ip_changed_ = false;
+ self_port_changed_ = false;
+
+ if (peer_address_.address().empty()) {
+ peer_address_ = peer_address;
+ }
+ if (self_address_.address().empty()) {
+ self_address_ = self_address;
+ }
+
+ if (!peer_address.address().empty() && !peer_address_.address().empty()) {
+ peer_ip_changed_ = (peer_address.address() != peer_address_.address());
+ peer_port_changed_ = (peer_address.port() != peer_address_.port());
+
+ // Store in case we want to migrate connection in ProcessValidatedPacket.
+ migrating_peer_port_ = peer_address.port();
+ }
+
+ if (!self_address.address().empty() && !self_address_.address().empty()) {
+ self_ip_changed_ = (self_address.address() != self_address_.address());
+ self_port_changed_ = (self_address.port() != self_address_.port());
+ }
+}
+
+void QuicConnection::OnCanWrite() {
+ DCHECK(!writer_->IsWriteBlocked());
+
+ WriteQueuedPackets();
+ WritePendingRetransmissions();
+
+ // Sending queued packets may have caused the socket to become write blocked,
+ // or the congestion manager to prohibit sending. If we've sent everything
+ // we had queued and we're still not blocked, let the visitor know it can
+ // write more.
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ return;
+ }
+
+ { // Limit the scope of the bundler.
+ // Set |include_ack| to false in bundler; ack inclusion happens elsewhere.
+ ScopedPacketBundler bundler(this, NO_ACK);
+ visitor_->OnCanWrite();
+ }
+
+ // After the visitor writes, it may have caused the socket to become write
+ // blocked or the congestion manager to prohibit sending, so check again.
+ if (visitor_->WillingAndAbleToWrite() &&
+ !resume_writes_alarm_->IsSet() &&
+ CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ // We're not write blocked, but some stream didn't write out all of its
+ // bytes. Register for 'immediate' resumption so we'll keep writing after
+ // other connections and events have had a chance to use the thread.
+ resume_writes_alarm_->Set(clock_->ApproximateNow());
+ }
+}
+
+void QuicConnection::WriteIfNotBlocked() {
+ if (!writer_->IsWriteBlocked()) {
+ OnCanWrite();
+ }
+}
+
+bool QuicConnection::ProcessValidatedPacket() {
+ if (peer_ip_changed_ || self_ip_changed_ || self_port_changed_) {
+ SendConnectionCloseWithDetails(
+ QUIC_ERROR_MIGRATING_ADDRESS,
+ "Neither IP address migration, nor self port migration are supported.");
+ return false;
+ }
+
+ // Peer port migration is supported, do it now if port has changed.
+ if (peer_port_changed_) {
+ DVLOG(1) << ENDPOINT << "Peer's port changed from "
+ << peer_address_.port() << " to " << migrating_peer_port_
+ << ", migrating connection.";
+ peer_address_ = IPEndPoint(peer_address_.address(), migrating_peer_port_);
+ }
+
+ time_of_last_received_packet_ = clock_->Now();
+ DVLOG(1) << ENDPOINT << "time of last received packet: "
+ << time_of_last_received_packet_.ToDebuggingValue();
+
+ if (is_server_ && encryption_level_ == ENCRYPTION_NONE &&
+ last_size_ > packet_generator_.max_packet_length()) {
+ packet_generator_.set_max_packet_length(last_size_);
+ }
+ return true;
+}
+
+void QuicConnection::WriteQueuedPackets() {
+ DCHECK(!writer_->IsWriteBlocked());
+
+ if (pending_version_negotiation_packet_) {
+ SendVersionNegotiationPacket();
+ }
+
+ QueuedPacketList::iterator packet_iterator = queued_packets_.begin();
+ while (packet_iterator != queued_packets_.end() &&
+ WritePacket(&(*packet_iterator))) {
+ packet_iterator = queued_packets_.erase(packet_iterator);
+ }
+}
+
+void QuicConnection::WritePendingRetransmissions() {
+ // Keep writing as long as there's a pending retransmission which can be
+ // written.
+ while (sent_packet_manager_.HasPendingRetransmissions()) {
+ const QuicSentPacketManager::PendingRetransmission pending =
+ sent_packet_manager_.NextPendingRetransmission();
+ if (!CanWrite(HAS_RETRANSMITTABLE_DATA)) {
+ break;
+ }
+
+ // Re-packetize the frames with a new sequence number for retransmission.
+ // Retransmitted data packets do not use FEC, even when it's enabled.
+ // Retransmitted packets use the same sequence number length as the
+ // original.
+ // Flush the packet generator before making a new packet.
+ // TODO(ianswett): Implement ReserializeAllFrames as a separate path that
+ // does not require the creator to be flushed.
+ packet_generator_.FlushAllQueuedFrames();
+ SerializedPacket serialized_packet = packet_generator_.ReserializeAllFrames(
+ pending.retransmittable_frames.frames(),
+ pending.sequence_number_length);
+
+ DVLOG(1) << ENDPOINT << "Retransmitting " << pending.sequence_number
+ << " as " << serialized_packet.sequence_number;
+ SendOrQueuePacket(
+ QueuedPacket(serialized_packet,
+ pending.retransmittable_frames.encryption_level(),
+ pending.transmission_type,
+ pending.sequence_number));
+ }
+}
+
+void QuicConnection::RetransmitUnackedPackets(
+ TransmissionType retransmission_type) {
+ sent_packet_manager_.RetransmitUnackedPackets(retransmission_type);
+
+ WriteIfNotBlocked();
+}
+
+void QuicConnection::NeuterUnencryptedPackets() {
+ sent_packet_manager_.NeuterUnencryptedPackets();
+ // This may have changed the retransmission timer, so re-arm it.
+ QuicTime retransmission_time = sent_packet_manager_.GetRetransmissionTime();
+ retransmission_alarm_->Update(retransmission_time,
+ QuicTime::Delta::FromMilliseconds(1));
+}
+
+bool QuicConnection::ShouldGeneratePacket(
+ TransmissionType transmission_type,
+ HasRetransmittableData retransmittable,
+ IsHandshake handshake) {
+ // We should serialize handshake packets immediately to ensure that they
+ // end up sent at the right encryption level.
+ if (handshake == IS_HANDSHAKE) {
+ return true;
+ }
+
+ return CanWrite(retransmittable);
+}
+
+bool QuicConnection::CanWrite(HasRetransmittableData retransmittable) {
+ if (!connected_) {
+ return false;
+ }
+
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
+ return false;
+ }
+
+ QuicTime now = clock_->Now();
+ QuicTime::Delta delay = sent_packet_manager_.TimeUntilSend(
+ now, retransmittable);
+ if (delay.IsInfinite()) {
+ send_alarm_->Cancel();
+ return false;
+ }
+
+ // If the scheduler requires a delay, then we can not send this packet now.
+ if (!delay.IsZero()) {
+ send_alarm_->Update(now.Add(delay), QuicTime::Delta::FromMilliseconds(1));
+ DVLOG(1) << "Delaying sending.";
+ return false;
+ }
+ send_alarm_->Cancel();
+ return true;
+}
+
+bool QuicConnection::WritePacket(QueuedPacket* packet) {
+ if (!WritePacketInner(packet)) {
+ return false;
+ }
+ delete packet->serialized_packet.retransmittable_frames;
+ delete packet->serialized_packet.packet;
+ packet->serialized_packet.retransmittable_frames = nullptr;
+ packet->serialized_packet.packet = nullptr;
+ return true;
+}
+
+bool QuicConnection::WritePacketInner(QueuedPacket* packet) {
+ if (ShouldDiscardPacket(*packet)) {
+ ++stats_.packets_discarded;
+ return true;
+ }
+ // Connection close packets are encrypted and saved, so don't exit early.
+ if (writer_->IsWriteBlocked() && !IsConnectionClose(*packet)) {
+ return false;
+ }
+
+ QuicPacketSequenceNumber sequence_number =
+ packet->serialized_packet.sequence_number;
+ DCHECK_LE(sequence_number_of_last_sent_packet_, sequence_number);
+ sequence_number_of_last_sent_packet_ = sequence_number;
+
+ QuicEncryptedPacket* encrypted = framer_.EncryptPacket(
+ packet->encryption_level,
+ sequence_number,
+ *packet->serialized_packet.packet);
+ if (encrypted == nullptr) {
+ LOG(DFATAL) << ENDPOINT << "Failed to encrypt packet number "
+ << sequence_number;
+ // CloseConnection does not send close packet, so no infinite loop here.
+ CloseConnection(QUIC_ENCRYPTION_FAILURE, false);
+ return false;
+ }
+
+ // Connection close packets are eventually owned by TimeWaitListManager.
+ // Others are deleted at the end of this call.
+ scoped_ptr<QuicEncryptedPacket> encrypted_deleter;
+ if (IsConnectionClose(*packet)) {
+ DCHECK(connection_close_packet_.get() == nullptr);
+ connection_close_packet_.reset(encrypted);
+ // This assures we won't try to write *forced* packets when blocked.
+ // Return true to stop processing.
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked();
+ return true;
+ }
+ } else {
+ encrypted_deleter.reset(encrypted);
+ }
+
+ if (!FLAGS_quic_allow_oversized_packets_for_test) {
+ DCHECK_LE(encrypted->length(), kMaxPacketSize);
+ }
+ DCHECK_LE(encrypted->length(), packet_generator_.max_packet_length());
+ DVLOG(1) << ENDPOINT << "Sending packet " << sequence_number << " : "
+ << (packet->serialized_packet.packet->is_fec_packet() ? "FEC " :
+ (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA
+ ? "data bearing " : " ack only "))
+ << ", encryption level: "
+ << QuicUtils::EncryptionLevelToString(packet->encryption_level)
+ << ", length:"
+ << packet->serialized_packet.packet->length()
+ << ", encrypted length:"
+ << encrypted->length();
+ DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl
+ << QuicUtils::StringToHexASCIIDump(
+ packet->serialized_packet.packet->AsStringPiece());
+
+ WriteResult result = writer_->WritePacket(encrypted->data(),
+ encrypted->length(),
+ self_address().address(),
+ peer_address());
+ if (result.error_code == ERR_IO_PENDING) {
+ DCHECK_EQ(WRITE_STATUS_BLOCKED, result.status);
+ }
+ if (debug_visitor_.get() != nullptr) {
+ // Pass the write result to the visitor.
+ debug_visitor_->OnPacketSent(sequence_number,
+ packet->original_sequence_number,
+ packet->encryption_level,
+ packet->transmission_type,
+ *encrypted,
+ result);
+ }
+
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ visitor_->OnWriteBlocked();
+ // If the socket buffers the the data, then the packet should not
+ // be queued and sent again, which would result in an unnecessary
+ // duplicate packet being sent. The helper must call OnCanWrite
+ // when the write completes, and OnWriteError if an error occurs.
+ if (!writer_->IsWriteBlockedDataBuffered()) {
+ return false;
+ }
+ }
+ QuicTime now = clock_->Now();
+ if (packet->transmission_type == NOT_RETRANSMISSION) {
+ time_of_last_sent_new_packet_ = now;
+ }
+ SetPingAlarm();
+ DVLOG(1) << ENDPOINT << "time of last sent packet: "
+ << now.ToDebuggingValue();
+
+ // TODO(ianswett): Change the sequence number length and other packet creator
+ // options by a more explicit API than setting a struct value directly,
+ // perhaps via the NetworkChangeVisitor.
+ packet_generator_.UpdateSequenceNumberLength(
+ sent_packet_manager_.least_packet_awaited_by_peer(),
+ sent_packet_manager_.GetCongestionWindow());
+
+ bool reset_retransmission_alarm = sent_packet_manager_.OnPacketSent(
+ &packet->serialized_packet,
+ packet->original_sequence_number,
+ now,
+ encrypted->length(),
+ packet->transmission_type,
+ IsRetransmittable(*packet));
+
+ if (reset_retransmission_alarm || !retransmission_alarm_->IsSet()) {
+ retransmission_alarm_->Update(sent_packet_manager_.GetRetransmissionTime(),
+ QuicTime::Delta::FromMilliseconds(1));
+ }
+
+ stats_.bytes_sent += result.bytes_written;
+ ++stats_.packets_sent;
+ if (packet->transmission_type != NOT_RETRANSMISSION) {
+ stats_.bytes_retransmitted += result.bytes_written;
+ ++stats_.packets_retransmitted;
+ }
+
+ if (result.status == WRITE_STATUS_ERROR) {
+ OnWriteError(result.error_code);
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicConnection::ShouldDiscardPacket(const QueuedPacket& packet) {
+ if (!connected_) {
+ DVLOG(1) << ENDPOINT
+ << "Not sending packet as connection is disconnected.";
+ return true;
+ }
+
+ QuicPacketSequenceNumber sequence_number =
+ packet.serialized_packet.sequence_number;
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE &&
+ packet.encryption_level == ENCRYPTION_NONE) {
+ // Drop packets that are NULL encrypted since the peer won't accept them
+ // anymore.
+ DVLOG(1) << ENDPOINT << "Dropping NULL encrypted packet: "
+ << sequence_number << " since the connection is forward secure.";
+ return true;
+ }
+
+ // If a retransmission has been acked before sending, don't send it.
+ // This occurs if a packet gets serialized, queued, then discarded.
+ if (packet.transmission_type != NOT_RETRANSMISSION &&
+ (!sent_packet_manager_.IsUnacked(packet.original_sequence_number) ||
+ !sent_packet_manager_.HasRetransmittableFrames(
+ packet.original_sequence_number))) {
+ DVLOG(1) << ENDPOINT << "Dropping unacked packet: " << sequence_number
+ << " A previous transmission was acked while write blocked.";
+ return true;
+ }
+
+ return false;
+}
+
+void QuicConnection::OnWriteError(int error_code) {
+ DVLOG(1) << ENDPOINT << "Write failed with error: " << error_code
+ << " (" << ErrorToString(error_code) << ")";
+ // We can't send an error as the socket is presumably borked.
+ CloseConnection(QUIC_PACKET_WRITE_ERROR, false);
+}
+
+void QuicConnection::OnSerializedPacket(
+ const SerializedPacket& serialized_packet) {
+ if (serialized_packet.retransmittable_frames) {
+ serialized_packet.retransmittable_frames->
+ set_encryption_level(encryption_level_);
+ }
+ SendOrQueuePacket(QueuedPacket(serialized_packet, encryption_level_));
+}
+
+void QuicConnection::OnCongestionWindowChange(QuicByteCount congestion_window) {
+ packet_generator_.OnCongestionWindowChange(congestion_window);
+ visitor_->OnCongestionWindowChange(clock_->ApproximateNow());
+}
+
+void QuicConnection::OnHandshakeComplete() {
+ sent_packet_manager_.SetHandshakeConfirmed();
+}
+
+void QuicConnection::SendOrQueuePacket(QueuedPacket packet) {
+ // The caller of this function is responsible for checking CanWrite().
+ if (packet.serialized_packet.packet == nullptr) {
+ LOG(DFATAL)
+ << "packet.serialized_packet.packet == nullptr in to SendOrQueuePacket";
+ return;
+ }
+
+ sent_entropy_manager_.RecordPacketEntropyHash(
+ packet.serialized_packet.sequence_number,
+ packet.serialized_packet.entropy_hash);
+ LOG_IF(DFATAL, !queued_packets_.empty() && !writer_->IsWriteBlocked())
+ << "Packets should only be left queued if we're write blocked.";
+ if (!WritePacket(&packet)) {
+ queued_packets_.push_back(packet);
+ }
+}
+
+void QuicConnection::UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting) {
+ stop_waiting->least_unacked = GetLeastUnacked();
+ stop_waiting->entropy_hash = sent_entropy_manager_.GetCumulativeEntropy(
+ stop_waiting->least_unacked - 1);
+}
+
+void QuicConnection::SendPing() {
+ if (retransmission_alarm_->IsSet()) {
+ return;
+ }
+ packet_generator_.AddControlFrame(QuicFrame(new QuicPingFrame));
+}
+
+void QuicConnection::SendAck() {
+ ack_alarm_->Cancel();
+ stop_waiting_count_ = 0;
+ num_packets_received_since_last_ack_sent_ = 0;
+ bool send_feedback = false;
+
+ // Deprecating the Congestion Feedback Frame after QUIC_VERSION_22.
+ if (version() <= QUIC_VERSION_22) {
+ if (received_packet_manager_.GenerateCongestionFeedback(
+ &outgoing_congestion_feedback_)) {
+ DVLOG(1) << ENDPOINT << "Sending feedback: "
+ << outgoing_congestion_feedback_;
+ send_feedback = true;
+ }
+ }
+
+ packet_generator_.SetShouldSendAck(send_feedback, true);
+}
+
+void QuicConnection::OnRetransmissionTimeout() {
+ if (!sent_packet_manager_.HasUnackedPackets()) {
+ return;
+ }
+
+ sent_packet_manager_.OnRetransmissionTimeout();
+ WriteIfNotBlocked();
+
+ // A write failure can result in the connection being closed, don't attempt to
+ // write further packets, or to set alarms.
+ if (!connected_) {
+ return;
+ }
+
+ // In the TLP case, the SentPacketManager gives the connection the opportunity
+ // to send new data before retransmitting.
+ if (sent_packet_manager_.MaybeRetransmitTailLossProbe()) {
+ // Send the pending retransmission now that it's been queued.
+ WriteIfNotBlocked();
+ }
+
+ // Ensure the retransmission alarm is always set if there are unacked packets
+ // and nothing waiting to be sent.
+ if (!HasQueuedData() && !retransmission_alarm_->IsSet()) {
+ QuicTime rto_timeout = sent_packet_manager_.GetRetransmissionTime();
+ if (rto_timeout.IsInitialized()) {
+ retransmission_alarm_->Set(rto_timeout);
+ }
+ }
+}
+
+void QuicConnection::SetEncrypter(EncryptionLevel level,
+ QuicEncrypter* encrypter) {
+ framer_.SetEncrypter(level, encrypter);
+}
+
+const QuicEncrypter* QuicConnection::encrypter(EncryptionLevel level) const {
+ return framer_.encrypter(level);
+}
+
+void QuicConnection::SetDefaultEncryptionLevel(EncryptionLevel level) {
+ encryption_level_ = level;
+ packet_generator_.set_encryption_level(level);
+}
+
+void QuicConnection::SetDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level) {
+ framer_.SetDecrypter(decrypter, level);
+}
+
+void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
+ bool latch_once_used) {
+ framer_.SetAlternativeDecrypter(decrypter, level, latch_once_used);
+}
+
+const QuicDecrypter* QuicConnection::decrypter() const {
+ return framer_.decrypter();
+}
+
+const QuicDecrypter* QuicConnection::alternative_decrypter() const {
+ return framer_.alternative_decrypter();
+}
+
+void QuicConnection::QueueUndecryptablePacket(
+ const QuicEncryptedPacket& packet) {
+ DVLOG(1) << ENDPOINT << "Queueing undecryptable packet.";
+ undecryptable_packets_.push_back(packet.Clone());
+}
+
+void QuicConnection::MaybeProcessUndecryptablePackets() {
+ if (undecryptable_packets_.empty() || encryption_level_ == ENCRYPTION_NONE) {
+ return;
+ }
+
+ while (connected_ && !undecryptable_packets_.empty()) {
+ DVLOG(1) << ENDPOINT << "Attempting to process undecryptable packet";
+ QuicEncryptedPacket* packet = undecryptable_packets_.front();
+ if (!framer_.ProcessPacket(*packet) &&
+ framer_.error() == QUIC_DECRYPTION_FAILURE) {
+ DVLOG(1) << ENDPOINT << "Unable to process undecryptable packet...";
+ break;
+ }
+ DVLOG(1) << ENDPOINT << "Processed undecryptable packet!";
+ ++stats_.packets_processed;
+ delete packet;
+ undecryptable_packets_.pop_front();
+ }
+
+ // Once forward secure encryption is in use, there will be no
+ // new keys installed and hence any undecryptable packets will
+ // never be able to be decrypted.
+ if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) {
+ if (debug_visitor_.get() != nullptr) {
+ // TODO(rtenneti): perhaps more efficient to pass the number of
+ // undecryptable packets as the argument to OnUndecryptablePacket so that
+ // we just need to call OnUndecryptablePacket once?
+ for (size_t i = 0; i < undecryptable_packets_.size(); ++i) {
+ debug_visitor_->OnUndecryptablePacket();
+ }
+ }
+ STLDeleteElements(&undecryptable_packets_);
+ }
+}
+
+void QuicConnection::MaybeProcessRevivedPacket() {
+ QuicFecGroup* group = GetFecGroup();
+ if (!connected_ || group == nullptr || !group->CanRevive()) {
+ return;
+ }
+ QuicPacketHeader revived_header;
+ char revived_payload[kMaxPacketSize];
+ size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize);
+ revived_header.public_header.connection_id = connection_id_;
+ revived_header.public_header.connection_id_length =
+ last_header_.public_header.connection_id_length;
+ revived_header.public_header.version_flag = false;
+ revived_header.public_header.reset_flag = false;
+ revived_header.public_header.sequence_number_length =
+ last_header_.public_header.sequence_number_length;
+ revived_header.fec_flag = false;
+ revived_header.is_in_fec_group = NOT_IN_FEC_GROUP;
+ revived_header.fec_group = 0;
+ group_map_.erase(last_header_.fec_group);
+ last_decrypted_packet_level_ = group->effective_encryption_level();
+ DCHECK_LT(last_decrypted_packet_level_, NUM_ENCRYPTION_LEVELS);
+ delete group;
+
+ last_packet_revived_ = true;
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnRevivedPacket(revived_header,
+ StringPiece(revived_payload, len));
+ }
+
+ ++stats_.packets_revived;
+ framer_.ProcessRevivedPacket(&revived_header,
+ StringPiece(revived_payload, len));
+}
+
+QuicFecGroup* QuicConnection::GetFecGroup() {
+ QuicFecGroupNumber fec_group_num = last_header_.fec_group;
+ if (fec_group_num == 0) {
+ return nullptr;
+ }
+ if (group_map_.count(fec_group_num) == 0) {
+ if (group_map_.size() >= kMaxFecGroups) { // Too many groups
+ if (fec_group_num < group_map_.begin()->first) {
+ // The group being requested is a group we've seen before and deleted.
+ // Don't recreate it.
+ return nullptr;
+ }
+ // Clear the lowest group number.
+ delete group_map_.begin()->second;
+ group_map_.erase(group_map_.begin());
+ }
+ group_map_[fec_group_num] = new QuicFecGroup();
+ }
+ return group_map_[fec_group_num];
+}
+
+void QuicConnection::SendConnectionClose(QuicErrorCode error) {
+ SendConnectionCloseWithDetails(error, string());
+}
+
+void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error,
+ const string& details) {
+ // If we're write blocked, WritePacket() will not send, but will capture the
+ // serialized packet.
+ SendConnectionClosePacket(error, details);
+ if (connected_) {
+ // It's possible that while sending the connection close packet, we get a
+ // socket error and disconnect right then and there. Avoid a double
+ // disconnect in that case.
+ CloseConnection(error, false);
+ }
+}
+
+void QuicConnection::SendConnectionClosePacket(QuicErrorCode error,
+ const string& details) {
+ DVLOG(1) << ENDPOINT << "Force closing " << connection_id()
+ << " with error " << QuicUtils::ErrorToString(error)
+ << " (" << error << ") " << details;
+ ScopedPacketBundler ack_bundler(this, SEND_ACK);
+ QuicConnectionCloseFrame* frame = new QuicConnectionCloseFrame();
+ frame->error_code = error;
+ frame->error_details = details;
+ packet_generator_.AddControlFrame(QuicFrame(frame));
+ packet_generator_.FlushAllQueuedFrames();
+}
+
+void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
+ if (!connected_) {
+ DLOG(DFATAL) << "Error: attempt to close an already closed connection"
+ << base::debug::StackTrace().ToString();
+ return;
+ }
+ connected_ = false;
+ if (debug_visitor_.get() != nullptr) {
+ debug_visitor_->OnConnectionClosed(error, from_peer);
+ }
+ visitor_->OnConnectionClosed(error, from_peer);
+ // Cancel the alarms so they don't trigger any action now that the
+ // connection is closed.
+ ack_alarm_->Cancel();
+ ping_alarm_->Cancel();
+ resume_writes_alarm_->Cancel();
+ retransmission_alarm_->Cancel();
+ send_alarm_->Cancel();
+ timeout_alarm_->Cancel();
+}
+
+void QuicConnection::SendGoAway(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const string& reason) {
+ DVLOG(1) << ENDPOINT << "Going away with error "
+ << QuicUtils::ErrorToString(error)
+ << " (" << error << ")";
+
+ // Opportunistically bundle an ack with this outgoing packet.
+ ScopedPacketBundler ack_bundler(this, BUNDLE_PENDING_ACK);
+ packet_generator_.AddControlFrame(
+ QuicFrame(new QuicGoAwayFrame(error, last_good_stream_id, reason)));
+}
+
+void QuicConnection::CloseFecGroupsBefore(
+ QuicPacketSequenceNumber sequence_number) {
+ FecGroupMap::iterator it = group_map_.begin();
+ while (it != group_map_.end()) {
+ // If this is the current group or the group doesn't protect this packet
+ // we can ignore it.
+ if (last_header_.fec_group == it->first ||
+ !it->second->ProtectsPacketsBefore(sequence_number)) {
+ ++it;
+ continue;
+ }
+ QuicFecGroup* fec_group = it->second;
+ DCHECK(!fec_group->CanRevive());
+ FecGroupMap::iterator next = it;
+ ++next;
+ group_map_.erase(it);
+ delete fec_group;
+ it = next;
+ }
+}
+
+size_t QuicConnection::max_packet_length() const {
+ return packet_generator_.max_packet_length();
+}
+
+void QuicConnection::set_max_packet_length(size_t length) {
+ return packet_generator_.set_max_packet_length(length);
+}
+
+bool QuicConnection::HasQueuedData() const {
+ return pending_version_negotiation_packet_ ||
+ !queued_packets_.empty() || packet_generator_.HasQueuedFrames();
+}
+
+bool QuicConnection::CanWriteStreamData() {
+ // Don't write stream data if there are negotiation or queued data packets
+ // to send. Otherwise, continue and bundle as many frames as possible.
+ if (pending_version_negotiation_packet_ || !queued_packets_.empty()) {
+ return false;
+ }
+
+ IsHandshake pending_handshake = visitor_->HasPendingHandshake() ?
+ IS_HANDSHAKE : NOT_HANDSHAKE;
+ // Sending queued packets may have caused the socket to become write blocked,
+ // or the congestion manager to prohibit sending. If we've sent everything
+ // we had queued and we're still not blocked, let the visitor know it can
+ // write more.
+ return ShouldGeneratePacket(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA,
+ pending_handshake);
+}
+
+void QuicConnection::SetIdleNetworkTimeout(QuicTime::Delta timeout) {
+ // Adjust the idle timeout on client and server to prevent clients from
+ // sending requests to servers which have already closed the connection.
+ if (is_server_) {
+ timeout = timeout.Add(QuicTime::Delta::FromSeconds(1));
+ } else if (timeout > QuicTime::Delta::FromSeconds(1)) {
+ timeout = timeout.Subtract(QuicTime::Delta::FromSeconds(1));
+ }
+
+ if (timeout < idle_network_timeout_) {
+ idle_network_timeout_ = timeout;
+ if (FLAGS_quic_timeouts_only_from_alarms) {
+ SetTimeoutAlarm();
+ } else {
+ CheckForTimeout();
+ }
+ } else {
+ idle_network_timeout_ = timeout;
+ }
+}
+
+void QuicConnection::SetOverallConnectionTimeout(QuicTime::Delta timeout) {
+ if (timeout < overall_connection_timeout_) {
+ overall_connection_timeout_ = timeout;
+ if (FLAGS_quic_timeouts_only_from_alarms) {
+ SetTimeoutAlarm();
+ } else {
+ CheckForTimeout();
+ }
+ } else {
+ overall_connection_timeout_ = timeout;
+ }
+}
+
+void QuicConnection::SetNetworkTimeouts(QuicTime::Delta overall_timeout,
+ QuicTime::Delta idle_timeout) {
+ LOG_IF(DFATAL, idle_timeout > overall_timeout)
+ << "idle_timeout:" << idle_timeout.ToMilliseconds()
+ << " overall_timeout:" << overall_timeout.ToMilliseconds();
+ // Adjust the idle timeout on client and server to prevent clients from
+ // sending requests to servers which have already closed the connection.
+ if (is_server_) {
+ idle_timeout = idle_timeout.Add(QuicTime::Delta::FromSeconds(1));
+ } else if (idle_timeout > QuicTime::Delta::FromSeconds(1)) {
+ idle_timeout = idle_timeout.Subtract(QuicTime::Delta::FromSeconds(1));
+ }
+ overall_connection_timeout_ = overall_timeout;
+ idle_network_timeout_ = idle_timeout;
+
+ SetTimeoutAlarm();
+}
+
+void QuicConnection::CheckForTimeout() {
+ QuicTime now = clock_->ApproximateNow();
+ QuicTime time_of_last_packet = max(time_of_last_received_packet_,
+ time_of_last_sent_new_packet_);
+
+ // |delta| can be < 0 as |now| is approximate time but |time_of_last_packet|
+ // is accurate time. However, this should not change the behavior of
+ // timeout handling.
+ QuicTime::Delta idle_duration = now.Subtract(time_of_last_packet);
+ DVLOG(1) << ENDPOINT << "last packet "
+ << time_of_last_packet.ToDebuggingValue()
+ << " now:" << now.ToDebuggingValue()
+ << " idle_duration:" << idle_duration.ToMicroseconds()
+ << " idle_network_timeout: "
+ << idle_network_timeout_.ToMicroseconds();
+ if (idle_duration >= idle_network_timeout_) {
+ DVLOG(1) << ENDPOINT << "Connection timedout due to no network activity.";
+ SendConnectionClose(QUIC_CONNECTION_TIMED_OUT);
+ return;
+ }
+
+ if (!overall_connection_timeout_.IsInfinite()) {
+ QuicTime::Delta connected_duration =
+ now.Subtract(stats_.connection_creation_time);
+ DVLOG(1) << ENDPOINT << "connection time: "
+ << connected_duration.ToMicroseconds() << " overall timeout: "
+ << overall_connection_timeout_.ToMicroseconds();
+ if (connected_duration >= overall_connection_timeout_) {
+ DVLOG(1) << ENDPOINT <<
+ "Connection timedout due to overall connection timeout.";
+ SendConnectionClose(QUIC_CONNECTION_OVERALL_TIMED_OUT);
+ return;
+ }
+ }
+
+ SetTimeoutAlarm();
+}
+
+void QuicConnection::SetTimeoutAlarm() {
+ QuicTime time_of_last_packet = max(time_of_last_received_packet_,
+ time_of_last_sent_new_packet_);
+
+ QuicTime deadline = time_of_last_packet.Add(idle_network_timeout_);
+ if (!overall_connection_timeout_.IsInfinite()) {
+ deadline = min(deadline,
+ stats_.connection_creation_time.Add(
+ overall_connection_timeout_));
+ }
+
+ timeout_alarm_->Cancel();
+ timeout_alarm_->Set(deadline);
+}
+
+void QuicConnection::SetPingAlarm() {
+ if (is_server_) {
+ // Only clients send pings.
+ return;
+ }
+ if (!visitor_->HasOpenDataStreams()) {
+ ping_alarm_->Cancel();
+ // Don't send a ping unless there are open streams.
+ return;
+ }
+ QuicTime::Delta ping_timeout = QuicTime::Delta::FromSeconds(kPingTimeoutSecs);
+ ping_alarm_->Update(clock_->ApproximateNow().Add(ping_timeout),
+ QuicTime::Delta::FromSeconds(1));
+}
+
+QuicConnection::ScopedPacketBundler::ScopedPacketBundler(
+ QuicConnection* connection,
+ AckBundling send_ack)
+ : connection_(connection),
+ already_in_batch_mode_(connection != nullptr &&
+ connection->packet_generator_.InBatchMode()) {
+ if (connection_ == nullptr) {
+ return;
+ }
+ // Move generator into batch mode. If caller wants us to include an ack,
+ // check the delayed-ack timer to see if there's ack info to be sent.
+ if (!already_in_batch_mode_) {
+ DVLOG(1) << "Entering Batch Mode.";
+ connection_->packet_generator_.StartBatchOperations();
+ }
+ // Bundle an ack if the alarm is set or with every second packet if we need to
+ // raise the peer's least unacked.
+ bool ack_pending =
+ connection_->ack_alarm_->IsSet() || connection_->stop_waiting_count_ > 1;
+ if (send_ack == SEND_ACK || (send_ack == BUNDLE_PENDING_ACK && ack_pending)) {
+ DVLOG(1) << "Bundling ack with outgoing packet.";
+ connection_->SendAck();
+ }
+}
+
+QuicConnection::ScopedPacketBundler::~ScopedPacketBundler() {
+ if (connection_ == nullptr) {
+ return;
+ }
+ // If we changed the generator's batch state, restore original batch state.
+ if (!already_in_batch_mode_) {
+ DVLOG(1) << "Leaving Batch Mode.";
+ connection_->packet_generator_.FinishBatchOperations();
+ }
+ DCHECK_EQ(already_in_batch_mode_,
+ connection_->packet_generator_.InBatchMode());
+}
+
+HasRetransmittableData QuicConnection::IsRetransmittable(
+ const QueuedPacket& packet) {
+ // Retransmitted packets retransmittable frames are owned by the unacked
+ // packet map, but are not present in the serialized packet.
+ if (packet.transmission_type != NOT_RETRANSMISSION ||
+ packet.serialized_packet.retransmittable_frames != nullptr) {
+ return HAS_RETRANSMITTABLE_DATA;
+ } else {
+ return NO_RETRANSMITTABLE_DATA;
+ }
+}
+
+bool QuicConnection::IsConnectionClose(
+ QueuedPacket packet) {
+ RetransmittableFrames* retransmittable_frames =
+ packet.serialized_packet.retransmittable_frames;
+ if (!retransmittable_frames) {
+ return false;
+ }
+ for (size_t i = 0; i < retransmittable_frames->frames().size(); ++i) {
+ if (retransmittable_frames->frames()[i].type == CONNECTION_CLOSE_FRAME) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace net
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
new file mode 100644
index 0000000..0b017b0
--- /dev/null
+++ b/net/quic/quic_connection.h
@@ -0,0 +1,820 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The entity that handles framing writes for a Quic client or server.
+// Each QuicSession will have a connection associated with it.
+//
+// On the server side, the Dispatcher handles the raw reads, and hands off
+// packets via ProcessUdpPacket for framing and processing.
+//
+// On the client side, the Connection handles the raw reads, as well as the
+// processing.
+//
+// Note: this class is not thread-safe.
+
+#ifndef NET_QUIC_QUIC_CONNECTION_H_
+#define NET_QUIC_QUIC_CONNECTION_H_
+
+#include <stddef.h>
+#include <deque>
+#include <list>
+#include <map>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "net/base/iovec.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/iovector.h"
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_ack_notifier_manager.h"
+#include "net/quic/quic_alarm.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_packet_generator.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_received_packet_manager.h"
+#include "net/quic/quic_sent_entropy_manager.h"
+#include "net/quic/quic_sent_packet_manager.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/quic_types.h"
+
+namespace net {
+
+class QuicClock;
+class QuicConfig;
+class QuicConnection;
+class QuicDecrypter;
+class QuicEncrypter;
+class QuicFecGroup;
+class QuicRandom;
+
+namespace test {
+class PacketSavingConnection;
+class QuicConnectionPeer;
+} // namespace test
+
+// Class that receives callbacks from the connection when frames are received
+// and when other interesting events happen.
+class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface {
+ public:
+ virtual ~QuicConnectionVisitorInterface() {}
+
+ // A simple visitor interface for dealing with data frames.
+ virtual void OnStreamFrames(const std::vector<QuicStreamFrame>& frames) = 0;
+
+ // The session should process all WINDOW_UPDATE frames, adjusting both stream
+ // and connection level flow control windows.
+ virtual void OnWindowUpdateFrames(
+ const std::vector<QuicWindowUpdateFrame>& frames) = 0;
+
+ // BLOCKED frames tell us that the peer believes it is flow control blocked on
+ // a specified stream. If the session at this end disagrees, something has
+ // gone wrong with our flow control accounting.
+ virtual void OnBlockedFrames(const std::vector<QuicBlockedFrame>& frames) = 0;
+
+ // Called when the stream is reset by the peer.
+ virtual void OnRstStream(const QuicRstStreamFrame& frame) = 0;
+
+ // Called when the connection is going away according to the peer.
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) = 0;
+
+ // Called when the connection is closed either locally by the framer, or
+ // remotely by the peer.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) = 0;
+
+ // Called when the connection failed to write because the socket was blocked.
+ virtual void OnWriteBlocked() = 0;
+
+ // Called once a specific QUIC version is agreed by both endpoints.
+ virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) = 0;
+
+ // Called when a blocked socket becomes writable.
+ virtual void OnCanWrite() = 0;
+
+ // Called when the connection experiences a change in congestion window.
+ virtual void OnCongestionWindowChange(QuicTime now) = 0;
+
+ // Called to ask if the visitor wants to schedule write resumption as it both
+ // has pending data to write, and is able to write (e.g. based on flow control
+ // limits).
+ // Writes may be pending because they were write-blocked, congestion-throttled
+ // or yielded to other connections.
+ virtual bool WillingAndAbleToWrite() const = 0;
+
+ // Called to ask if any handshake messages are pending in this visitor.
+ virtual bool HasPendingHandshake() const = 0;
+
+ // Called to ask if any streams are open in this visitor, excluding the
+ // reserved crypto and headers stream.
+ virtual bool HasOpenDataStreams() const = 0;
+};
+
+// Interface which gets callbacks from the QuicConnection at interesting
+// points. Implementations must not mutate the state of the connection
+// as a result of these callbacks.
+class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor
+ : public QuicPacketGenerator::DebugDelegate,
+ public QuicSentPacketManager::DebugDelegate {
+ public:
+ virtual ~QuicConnectionDebugVisitor() {}
+
+ // Called when a packet has been sent.
+ virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber original_sequence_number,
+ EncryptionLevel level,
+ TransmissionType transmission_type,
+ const QuicEncryptedPacket& packet,
+ WriteResult result) {}
+
+ // Called when a packet has been received, but before it is
+ // validated or parsed.
+ virtual void OnPacketReceived(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet) {}
+
+ // Called when a packet is received with a connection id that does not
+ // match the ID of this connection.
+ virtual void OnIncorrectConnectionId(
+ QuicConnectionId connection_id) {}
+
+ // Called when an undecryptable packet has been received.
+ virtual void OnUndecryptablePacket() {}
+
+ // Called when a duplicate packet has been received.
+ virtual void OnDuplicatePacket(QuicPacketSequenceNumber sequence_number) {}
+
+ // Called when the protocol version on the received packet doensn't match
+ // current protocol version of the connection.
+ virtual void OnProtocolVersionMismatch(QuicVersion version) {}
+
+ // Called when the complete header of a packet has been parsed.
+ virtual void OnPacketHeader(const QuicPacketHeader& header) {}
+
+ // Called when a StreamFrame has been parsed.
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) {}
+
+ // Called when a AckFrame has been parsed.
+ virtual void OnAckFrame(const QuicAckFrame& frame) {}
+
+ // Called when a CongestionFeedbackFrame has been parsed.
+ virtual void OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) {}
+
+ // Called when a StopWaitingFrame has been parsed.
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) {}
+
+ // Called when a Ping has been parsed.
+ virtual void OnPingFrame(const QuicPingFrame& frame) {}
+
+ // Called when a GoAway has been parsed.
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) {}
+
+ // Called when a RstStreamFrame has been parsed.
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) {}
+
+ // Called when a ConnectionCloseFrame has been parsed.
+ virtual void OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {}
+
+ // Called when a WindowUpdate has been parsed.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {}
+
+ // Called when a BlockedFrame has been parsed.
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) {}
+
+ // Called when a public reset packet has been received.
+ virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) {}
+
+ // Called when a version negotiation packet has been received.
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) {}
+
+ // Called after a packet has been successfully parsed which results
+ // in the revival of a packet via FEC.
+ virtual void OnRevivedPacket(const QuicPacketHeader& revived_header,
+ base::StringPiece payload) {}
+
+ // Called when the connection is closed.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) {}
+
+ // Called when the version negotiation is successful.
+ virtual void OnSuccessfulVersionNegotiation(const QuicVersion& version) {}
+};
+
+class NET_EXPORT_PRIVATE QuicConnectionHelperInterface {
+ public:
+ virtual ~QuicConnectionHelperInterface() {}
+
+ // Returns a QuicClock to be used for all time related functions.
+ virtual const QuicClock* GetClock() const = 0;
+
+ // Returns a QuicRandom to be used for all random number related functions.
+ virtual QuicRandom* GetRandomGenerator() = 0;
+
+ // Creates a new platform-specific alarm which will be configured to
+ // notify |delegate| when the alarm fires. Caller takes ownership
+ // of the new alarm, which will not yet be "set" to fire.
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) = 0;
+};
+
+class NET_EXPORT_PRIVATE QuicConnection
+ : public QuicFramerVisitorInterface,
+ public QuicBlockedWriterInterface,
+ public QuicPacketGenerator::DelegateInterface,
+ public QuicSentPacketManager::NetworkChangeVisitor {
+ public:
+ enum AckBundling {
+ NO_ACK = 0,
+ SEND_ACK = 1,
+ BUNDLE_PENDING_ACK = 2,
+ };
+
+ class PacketWriterFactory {
+ public:
+ virtual ~PacketWriterFactory() {}
+
+ virtual QuicPacketWriter* Create(QuicConnection* connection) const = 0;
+ };
+
+ // Constructs a new QuicConnection for |connection_id| and |address|. Invokes
+ // writer_factory->Create() to get a writer; |owns_writer| specifies whether
+ // the connection takes ownership of the returned writer. |helper| must
+ // outlive this connection.
+ QuicConnection(QuicConnectionId connection_id,
+ IPEndPoint address,
+ QuicConnectionHelperInterface* helper,
+ const PacketWriterFactory& writer_factory,
+ bool owns_writer,
+ bool is_server,
+ const QuicVersionVector& supported_versions);
+ virtual ~QuicConnection();
+
+ // Sets connection parameters from the supplied |config|.
+ void SetFromConfig(const QuicConfig& config);
+
+ // Send the data in |data| to the peer in as few packets as possible.
+ // Returns a pair with the number of bytes consumed from data, and a boolean
+ // indicating if the fin bit was consumed. This does not indicate the data
+ // has been sent on the wire: it may have been turned into a packet and queued
+ // if the socket was unexpectedly blocked. |fec_protection| indicates if
+ // data is to be FEC protected. Note that data that is sent immediately
+ // following MUST_FEC_PROTECT data may get protected by falling within the
+ // same FEC group.
+ // If |delegate| is provided, then it will be informed once ACKs have been
+ // received for all the packets written in this call.
+ // The |delegate| is not owned by the QuicConnection and must outlive it.
+ QuicConsumedData SendStreamData(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* delegate);
+
+ // Send a RST_STREAM frame to the peer.
+ virtual void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Send a BLOCKED frame to the peer.
+ virtual void SendBlocked(QuicStreamId id);
+
+ // Send a WINDOW_UPDATE frame to the peer.
+ virtual void SendWindowUpdate(QuicStreamId id,
+ QuicStreamOffset byte_offset);
+
+ // Sends the connection close packet without affecting the state of the
+ // connection. This should only be called if the session is actively being
+ // destroyed: otherwise call SendConnectionCloseWithDetails instead.
+ virtual void SendConnectionClosePacket(QuicErrorCode error,
+ const std::string& details);
+
+ // Sends a connection close frame to the peer, and closes the connection by
+ // calling CloseConnection(notifying the visitor as it does so).
+ virtual void SendConnectionClose(QuicErrorCode error);
+ virtual void SendConnectionCloseWithDetails(QuicErrorCode error,
+ const std::string& details);
+ // Notifies the visitor of the close and marks the connection as disconnected.
+ virtual void CloseConnection(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void SendGoAway(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason);
+
+ // Returns statistics tracked for this connection.
+ const QuicConnectionStats& GetStats();
+
+ // Processes an incoming UDP packet (consisting of a QuicEncryptedPacket) from
+ // the peer. If processing this packet permits a packet to be revived from
+ // its FEC group that packet will be revived and processed.
+ virtual void ProcessUdpPacket(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet);
+
+ // QuicBlockedWriterInterface
+ // Called when the underlying connection becomes writable to allow queued
+ // writes to happen.
+ virtual void OnCanWrite() OVERRIDE;
+
+ // Called when an error occurs while attempting to write a packet to the
+ // network.
+ void OnWriteError(int error_code);
+
+ // If the socket is not blocked, writes queued packets.
+ void WriteIfNotBlocked();
+
+ // The version of the protocol this connection is using.
+ QuicVersion version() const { return framer_.version(); }
+
+ // The versions of the protocol that this connection supports.
+ const QuicVersionVector& supported_versions() const {
+ return framer_.supported_versions();
+ }
+
+ // From QuicFramerVisitorInterface
+ virtual void OnError(QuicFramer* framer) OVERRIDE;
+ virtual bool OnProtocolVersionMismatch(QuicVersion received_version) OVERRIDE;
+ virtual void OnPacket() OVERRIDE;
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE;
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) OVERRIDE;
+ virtual void OnRevivedPacket() OVERRIDE;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE;
+ virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE;
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE;
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
+ virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) OVERRIDE;
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
+ virtual void OnFecData(const QuicFecData& fec) OVERRIDE;
+ virtual void OnPacketComplete() OVERRIDE;
+
+ // QuicPacketGenerator::DelegateInterface
+ virtual bool ShouldGeneratePacket(TransmissionType transmission_type,
+ HasRetransmittableData retransmittable,
+ IsHandshake handshake) OVERRIDE;
+ virtual QuicAckFrame* CreateAckFrame() OVERRIDE;
+ virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE;
+ virtual QuicStopWaitingFrame* CreateStopWaitingFrame() OVERRIDE;
+ virtual void OnSerializedPacket(const SerializedPacket& packet) OVERRIDE;
+
+ // QuicSentPacketManager::NetworkChangeVisitor
+ virtual void OnCongestionWindowChange(
+ QuicByteCount congestion_window) OVERRIDE;
+
+ // Called by the crypto stream when the handshake completes. In the server's
+ // case this is when the SHLO has been ACKed. Clients call this on receipt of
+ // the SHLO.
+ void OnHandshakeComplete();
+
+ // Accessors
+ void set_visitor(QuicConnectionVisitorInterface* visitor) {
+ visitor_ = visitor;
+ }
+ // This method takes ownership of |debug_visitor|.
+ void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) {
+ debug_visitor_.reset(debug_visitor);
+ packet_generator_.set_debug_delegate(debug_visitor);
+ sent_packet_manager_.set_debug_delegate(debug_visitor);
+ }
+ const IPEndPoint& self_address() const { return self_address_; }
+ const IPEndPoint& peer_address() const { return peer_address_; }
+ QuicConnectionId connection_id() const { return connection_id_; }
+ const QuicClock* clock() const { return clock_; }
+ QuicRandom* random_generator() const { return random_generator_; }
+ size_t max_packet_length() const;
+ void set_max_packet_length(size_t length);
+
+ bool connected() const { return connected_; }
+
+ // Must only be called on client connections.
+ const QuicVersionVector& server_supported_versions() const {
+ DCHECK(!is_server_);
+ return server_supported_versions_;
+ }
+
+ size_t NumFecGroups() const { return group_map_.size(); }
+
+ // Testing only.
+ size_t NumQueuedPackets() const { return queued_packets_.size(); }
+
+ QuicEncryptedPacket* ReleaseConnectionClosePacket() {
+ return connection_close_packet_.release();
+ }
+
+ // Returns true if the underlying UDP socket is writable, there is
+ // no queued data and the connection is not congestion-control
+ // blocked.
+ bool CanWriteStreamData();
+
+ // Returns true if the connection has queued packets or frames.
+ bool HasQueuedData() const;
+
+ // TODO(ianswett): Remove when quic_unified_timeouts is removed.
+ // Sets (or resets) the idle state connection timeout. Also, checks and times
+ // out the connection if network timer has expired for |timeout|.
+ void SetIdleNetworkTimeout(QuicTime::Delta timeout);
+ // Sets (or resets) the total time delta the connection can be alive for.
+ // Also, checks and times out the connection if timer has expired for
+ // |timeout|. Used to limit the time a connection can be alive before crypto
+ // handshake finishes.
+ void SetOverallConnectionTimeout(QuicTime::Delta timeout);
+
+ // Sets the overall and idle state connection timeouts.
+ // Times out the connection if the timeout has been reached and
+ // the quic_timeouts_only_from_alarms flag is false.
+ void SetNetworkTimeouts(QuicTime::Delta overall_timeout,
+ QuicTime::Delta idle_timeout);
+
+ // If the connection has timed out, this will close the connection.
+ // Otherwise, it will reschedule the timeout alarm.
+ void CheckForTimeout();
+
+ // Sends a ping, and resets the ping alarm.
+ void SendPing();
+
+ // Sets up a packet with an QuicAckFrame and sends it out.
+ void SendAck();
+
+ // Called when an RTO fires. Resets the retransmission alarm if there are
+ // remaining unacked packets.
+ void OnRetransmissionTimeout();
+
+ // Retransmits all unacked packets with retransmittable frames if
+ // |retransmission_type| is ALL_UNACKED_PACKETS, otherwise retransmits only
+ // initially encrypted packets. Used when the negotiated protocol version is
+ // different from what was initially assumed and when the initial encryption
+ // changes.
+ void RetransmitUnackedPackets(TransmissionType retransmission_type);
+
+ // Calls |sent_packet_manager_|'s NeuterUnencryptedPackets. Used when the
+ // connection becomes forward secure and hasn't received acks for all packets.
+ void NeuterUnencryptedPackets();
+
+ // Changes the encrypter used for level |level| to |encrypter|. The function
+ // takes ownership of |encrypter|.
+ void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter);
+ const QuicEncrypter* encrypter(EncryptionLevel level) const;
+
+ // SetDefaultEncryptionLevel sets the encryption level that will be applied
+ // to new packets.
+ void SetDefaultEncryptionLevel(EncryptionLevel level);
+
+ // SetDecrypter sets the primary decrypter, replacing any that already exists,
+ // and takes ownership. If an alternative decrypter is in place then the
+ // function DCHECKs. This is intended for cases where one knows that future
+ // packets will be using the new decrypter and the previous decrypter is now
+ // obsolete. |level| indicates the encryption level of the new decrypter.
+ void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level);
+
+ // SetAlternativeDecrypter sets a decrypter that may be used to decrypt
+ // future packets and takes ownership of it. |level| indicates the encryption
+ // level of the decrypter. If |latch_once_used| is true, then the first time
+ // that the decrypter is successful it will replace the primary decrypter.
+ // Otherwise both decrypters will remain active and the primary decrypter
+ // will be the one last used.
+ void SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
+ bool latch_once_used);
+
+ const QuicDecrypter* decrypter() const;
+ const QuicDecrypter* alternative_decrypter() const;
+
+ bool is_server() const { return is_server_; }
+
+ // Returns the underlying sent packet manager.
+ const QuicSentPacketManager& sent_packet_manager() const {
+ return sent_packet_manager_;
+ }
+
+ bool CanWrite(HasRetransmittableData retransmittable);
+
+ // Stores current batch state for connection, puts the connection
+ // into batch mode, and destruction restores the stored batch state.
+ // While the bundler is in scope, any generated frames are bundled
+ // as densely as possible into packets. In addition, this bundler
+ // can be configured to ensure that an ACK frame is included in the
+ // first packet created, if there's new ack information to be sent.
+ class ScopedPacketBundler {
+ public:
+ // In addition to all outgoing frames being bundled when the
+ // bundler is in scope, setting |include_ack| to true ensures that
+ // an ACK frame is opportunistically bundled with the first
+ // outgoing packet.
+ ScopedPacketBundler(QuicConnection* connection, AckBundling send_ack);
+ ~ScopedPacketBundler();
+
+ private:
+ QuicConnection* connection_;
+ bool already_in_batch_mode_;
+ };
+
+ protected:
+ // Packets which have not been written to the wire.
+ // Owns the QuicPacket* packet.
+ struct QueuedPacket {
+ QueuedPacket(SerializedPacket packet,
+ EncryptionLevel level);
+ QueuedPacket(SerializedPacket packet,
+ EncryptionLevel level,
+ TransmissionType transmission_type,
+ QuicPacketSequenceNumber original_sequence_number);
+
+ SerializedPacket serialized_packet;
+ const EncryptionLevel encryption_level;
+ TransmissionType transmission_type;
+ // The packet's original sequence number if it is a retransmission.
+ // Otherwise it must be 0.
+ QuicPacketSequenceNumber original_sequence_number;
+ };
+
+ // Do any work which logically would be done in OnPacket but can not be
+ // safely done until the packet is validated. Returns true if the packet
+ // can be handled, false otherwise.
+ virtual bool ProcessValidatedPacket();
+
+ // Send a packet to the peer, and takes ownership of the packet if the packet
+ // cannot be written immediately.
+ virtual void SendOrQueuePacket(QueuedPacket packet);
+
+ QuicConnectionHelperInterface* helper() { return helper_; }
+
+ // Selects and updates the version of the protocol being used by selecting a
+ // version from |available_versions| which is also supported. Returns true if
+ // such a version exists, false otherwise.
+ bool SelectMutualVersion(const QuicVersionVector& available_versions);
+
+ QuicPacketWriter* writer() { return writer_; }
+
+ bool peer_port_changed() const { return peer_port_changed_; }
+
+ QuicPacketSequenceNumber sequence_number_of_last_sent_packet() const {
+ return sequence_number_of_last_sent_packet_;
+ }
+
+ private:
+ friend class test::QuicConnectionPeer;
+ friend class test::PacketSavingConnection;
+
+ typedef std::list<QueuedPacket> QueuedPacketList;
+ typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap;
+
+ // Writes the given packet to socket, encrypted with packet's
+ // encryption_level. Returns true on successful write, and false if the writer
+ // was blocked and the write needs to be tried again. Notifies the
+ // SentPacketManager when the write is successful and sets
+ // retransmittable frames to nullptr.
+ // Saves the connection close packet for later transmission, even if the
+ // writer is write blocked.
+ bool WritePacket(QueuedPacket* packet);
+
+ // Does the main work of WritePacket, but does not delete the packet or
+ // retransmittable frames upon success.
+ bool WritePacketInner(QueuedPacket* packet);
+
+ // Make sure an ack we got from our peer is sane.
+ bool ValidateAckFrame(const QuicAckFrame& incoming_ack);
+
+ // Make sure a stop waiting we got from our peer is sane.
+ bool ValidateStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting);
+
+ // Sends a version negotiation packet to the peer.
+ void SendVersionNegotiationPacket();
+
+ // Clears any accumulated frames from the last received packet.
+ void ClearLastFrames();
+
+ // Writes as many queued packets as possible. The connection must not be
+ // blocked when this is called.
+ void WriteQueuedPackets();
+
+ // Writes as many pending retransmissions as possible.
+ void WritePendingRetransmissions();
+
+ // Returns true if the packet should be discarded and not sent.
+ bool ShouldDiscardPacket(const QueuedPacket& packet);
+
+ // Queues |packet| in the hopes that it can be decrypted in the
+ // future, when a new key is installed.
+ void QueueUndecryptablePacket(const QuicEncryptedPacket& packet);
+
+ // Attempts to process any queued undecryptable packets.
+ void MaybeProcessUndecryptablePackets();
+
+ // If a packet can be revived from the current FEC group, then
+ // revive and process the packet.
+ void MaybeProcessRevivedPacket();
+
+ void ProcessAckFrame(const QuicAckFrame& incoming_ack);
+
+ void ProcessStopWaitingFrame(const QuicStopWaitingFrame& stop_waiting);
+
+ // Update |stop_waiting| for an outgoing ack.
+ void UpdateStopWaiting(QuicStopWaitingFrame* stop_waiting);
+
+ // Queues an ack or sets the ack alarm when an incoming packet arrives that
+ // should be acked.
+ void MaybeQueueAck();
+
+ // Checks if the last packet should instigate an ack.
+ bool ShouldLastPacketInstigateAck() const;
+
+ // Checks if the peer is waiting for packets that have been given up on, and
+ // therefore an ack frame should be sent with a larger least_unacked.
+ void UpdateStopWaitingCount();
+
+ // Sends any packets which are a response to the last packet, including both
+ // acks and pending writes if an ack opened the congestion window.
+ void MaybeSendInResponseToPacket();
+
+ // Gets the least unacked sequence number, which is the next sequence number
+ // to be sent if there are no outstanding packets.
+ QuicPacketSequenceNumber GetLeastUnacked() const;
+
+ // Get the FEC group associate with the last processed packet or nullptr, if
+ // the group has already been deleted.
+ QuicFecGroup* GetFecGroup();
+
+ // Closes any FEC groups protecting packets before |sequence_number|.
+ void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number);
+
+ // Sets the timeout alarm to the appropriate value, if any.
+ void SetTimeoutAlarm();
+
+ // Sets the ping alarm to the appropriate value, if any.
+ void SetPingAlarm();
+
+ // On arrival of a new packet, checks to see if the socket addresses have
+ // changed since the last packet we saw on this connection.
+ void CheckForAddressMigration(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address);
+
+ HasRetransmittableData IsRetransmittable(const QueuedPacket& packet);
+ bool IsConnectionClose(QueuedPacket packet);
+
+ QuicFramer framer_;
+ QuicConnectionHelperInterface* helper_; // Not owned.
+ QuicPacketWriter* writer_; // Owned or not depending on |owns_writer_|.
+ bool owns_writer_;
+ EncryptionLevel encryption_level_;
+ const QuicClock* clock_;
+ QuicRandom* random_generator_;
+
+ const QuicConnectionId connection_id_;
+ // Address on the last successfully processed packet received from the
+ // client.
+ IPEndPoint self_address_;
+ IPEndPoint peer_address_;
+ // Used to store latest peer port to possibly migrate to later.
+ int migrating_peer_port_;
+
+ bool last_packet_revived_; // True if the last packet was revived from FEC.
+ size_t last_size_; // Size of the last received packet.
+ EncryptionLevel last_decrypted_packet_level_;
+ QuicPacketHeader last_header_;
+ std::vector<QuicStreamFrame> last_stream_frames_;
+ std::vector<QuicAckFrame> last_ack_frames_;
+ std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_;
+ std::vector<QuicStopWaitingFrame> last_stop_waiting_frames_;
+ std::vector<QuicRstStreamFrame> last_rst_frames_;
+ std::vector<QuicGoAwayFrame> last_goaway_frames_;
+ std::vector<QuicWindowUpdateFrame> last_window_update_frames_;
+ std::vector<QuicBlockedFrame> last_blocked_frames_;
+ std::vector<QuicPingFrame> last_ping_frames_;
+ std::vector<QuicConnectionCloseFrame> last_close_frames_;
+
+ QuicCongestionFeedbackFrame outgoing_congestion_feedback_;
+
+ // Track some peer state so we can do less bookkeeping
+ // Largest sequence sent by the peer which had an ack frame (latest ack info).
+ QuicPacketSequenceNumber largest_seen_packet_with_ack_;
+
+ // Largest sequence number sent by the peer which had a stop waiting frame.
+ QuicPacketSequenceNumber largest_seen_packet_with_stop_waiting_;
+
+ // Collection of packets which were received before encryption was
+ // established, but which could not be decrypted. We buffer these on
+ // the assumption that they could not be processed because they were
+ // sent with the INITIAL encryption and the CHLO message was lost.
+ std::deque<QuicEncryptedPacket*> undecryptable_packets_;
+
+ // When the version negotiation packet could not be sent because the socket
+ // was not writable, this is set to true.
+ bool pending_version_negotiation_packet_;
+
+ // When packets could not be sent because the socket was not writable,
+ // they are added to this list. All corresponding frames are in
+ // unacked_packets_ if they are to be retransmitted.
+ QueuedPacketList queued_packets_;
+
+ // Contains the connection close packet if the connection has been closed.
+ scoped_ptr<QuicEncryptedPacket> connection_close_packet_;
+
+ FecGroupMap group_map_;
+
+ QuicReceivedPacketManager received_packet_manager_;
+ QuicSentEntropyManager sent_entropy_manager_;
+
+ // Indicates whether an ack should be sent the next time we try to write.
+ bool ack_queued_;
+ // Indicates how many consecutive packets have arrived without sending an ack.
+ uint32 num_packets_received_since_last_ack_sent_;
+ // Indicates how many consecutive times an ack has arrived which indicates
+ // the peer needs to stop waiting for some packets.
+ int stop_waiting_count_;
+
+ // An alarm that fires when an ACK should be sent to the peer.
+ scoped_ptr<QuicAlarm> ack_alarm_;
+ // An alarm that fires when a packet needs to be retransmitted.
+ scoped_ptr<QuicAlarm> retransmission_alarm_;
+ // An alarm that is scheduled when the sent scheduler requires a
+ // a delay before sending packets and fires when the packet may be sent.
+ scoped_ptr<QuicAlarm> send_alarm_;
+ // An alarm that is scheduled when the connection can still write and there
+ // may be more data to send.
+ scoped_ptr<QuicAlarm> resume_writes_alarm_;
+ // An alarm that fires when the connection may have timed out.
+ scoped_ptr<QuicAlarm> timeout_alarm_;
+ // An alarm that fires when a ping should be sent.
+ scoped_ptr<QuicAlarm> ping_alarm_;
+
+ QuicConnectionVisitorInterface* visitor_;
+ scoped_ptr<QuicConnectionDebugVisitor> debug_visitor_;
+ QuicPacketGenerator packet_generator_;
+
+ // Network idle time before we kill of this connection.
+ QuicTime::Delta idle_network_timeout_;
+ // Overall connection timeout.
+ QuicTime::Delta overall_connection_timeout_;
+
+ // Statistics for this session.
+ QuicConnectionStats stats_;
+
+ // The time that we got a packet for this connection.
+ // This is used for timeouts, and does not indicate the packet was processed.
+ QuicTime time_of_last_received_packet_;
+
+ // The last time a new (non-retransmitted) packet was sent for this
+ // connection.
+ QuicTime time_of_last_sent_new_packet_;
+
+ // Sequence number of the last sent packet. Packets are guaranteed to be sent
+ // in sequence number order.
+ QuicPacketSequenceNumber sequence_number_of_last_sent_packet_;
+
+ // Sent packet manager which tracks the status of packets sent by this
+ // connection and contains the send and receive algorithms to determine when
+ // to send packets.
+ QuicSentPacketManager sent_packet_manager_;
+
+ // The state of connection in version negotiation finite state machine.
+ QuicVersionNegotiationState version_negotiation_state_;
+
+ // Tracks if the connection was created by the server.
+ bool is_server_;
+
+ // True by default. False if we've received or sent an explicit connection
+ // close.
+ bool connected_;
+
+ // Set to true if the UDP packet headers have a new IP address for the peer.
+ // If true, do not perform connection migration.
+ bool peer_ip_changed_;
+
+ // Set to true if the UDP packet headers have a new port for the peer.
+ // If true, and the IP has not changed, then we can migrate the connection.
+ bool peer_port_changed_;
+
+ // Set to true if the UDP packet headers are addressed to a different IP.
+ // We do not support connection migration when the self IP changed.
+ bool self_ip_changed_;
+
+ // Set to true if the UDP packet headers are addressed to a different port.
+ // We do not support connection migration when the self port changed.
+ bool self_port_changed_;
+
+ // If non-empty this contains the set of versions received in a
+ // version negotiation packet.
+ QuicVersionVector server_supported_versions_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicConnection);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CONNECTION_H_
diff --git a/net/quic/quic_connection_helper.cc b/net/quic/quic_connection_helper.cc
new file mode 100644
index 0000000..fefd6fa
--- /dev/null
+++ b/net/quic/quic_connection_helper.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection_helper.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/task_runner.h"
+#include "base/time/time.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/quic/quic_utils.h"
+
+namespace net {
+
+namespace {
+
+class QuicChromeAlarm : public QuicAlarm {
+ public:
+ QuicChromeAlarm(const QuicClock* clock,
+ base::TaskRunner* task_runner,
+ QuicAlarm::Delegate* delegate)
+ : QuicAlarm(delegate),
+ clock_(clock),
+ task_runner_(task_runner),
+ task_deadline_(QuicTime::Zero()),
+ weak_factory_(this) {}
+
+ protected:
+ virtual void SetImpl() OVERRIDE {
+ DCHECK(deadline().IsInitialized());
+ if (task_deadline_.IsInitialized()) {
+ if (task_deadline_ <= deadline()) {
+ // Since tasks can not be un-posted, OnAlarm will be invoked which
+ // will notice that deadline has not yet been reached, and will set
+ // the alarm for the new deadline.
+ return;
+ }
+ // The scheduled task is after new deadline. Invalidate the weak ptrs
+ // so that task does not execute when we're not expecting it.
+ weak_factory_.InvalidateWeakPtrs();
+ }
+
+ int64 delay_us = deadline().Subtract(clock_->Now()).ToMicroseconds();
+ if (delay_us < 0) {
+ delay_us = 0;
+ }
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&QuicChromeAlarm::OnAlarm, weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMicroseconds(delay_us));
+ task_deadline_ = deadline();
+ }
+
+ virtual void CancelImpl() OVERRIDE {
+ DCHECK(!deadline().IsInitialized());
+ // Since tasks can not be un-posted, OnAlarm will be invoked which
+ // will notice that deadline is not Initialized and will do nothing.
+ }
+
+ private:
+ void OnAlarm() {
+ DCHECK(task_deadline_.IsInitialized());
+ task_deadline_ = QuicTime::Zero();
+ // The alarm may have been cancelled.
+ if (!deadline().IsInitialized()) {
+ return;
+ }
+
+ // The alarm may have been re-set to a later time.
+ if (clock_->Now() < deadline()) {
+ SetImpl();
+ return;
+ }
+
+ Fire();
+ }
+
+ const QuicClock* clock_;
+ base::TaskRunner* task_runner_;
+ // If a task has been posted to the message loop, this is the time it
+ // was scheduled to fire. Tracking this allows us to avoid posting a
+ // new tast if the new deadline is in the future, but permits us to
+ // post a new task when the new deadline now earlier than when
+ // previously posted.
+ QuicTime task_deadline_;
+ base::WeakPtrFactory<QuicChromeAlarm> weak_factory_;
+};
+
+} // namespace
+
+QuicConnectionHelper::QuicConnectionHelper(base::TaskRunner* task_runner,
+ const QuicClock* clock,
+ QuicRandom* random_generator)
+ : task_runner_(task_runner),
+ clock_(clock),
+ random_generator_(random_generator),
+ weak_factory_(this) {
+}
+
+QuicConnectionHelper::~QuicConnectionHelper() {
+}
+
+const QuicClock* QuicConnectionHelper::GetClock() const {
+ return clock_;
+}
+
+QuicRandom* QuicConnectionHelper::GetRandomGenerator() {
+ return random_generator_;
+}
+
+QuicAlarm* QuicConnectionHelper::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new QuicChromeAlarm(clock_, task_runner_, delegate);
+}
+
+} // namespace net
diff --git a/net/quic/quic_connection_helper.h b/net/quic/quic_connection_helper.h
new file mode 100644
index 0000000..28cff52
--- /dev/null
+++ b/net/quic/quic_connection_helper.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The Chrome-specific helper for QuicConnection which uses
+// a TaskRunner for alarms, and uses a DatagramClientSocket for writing data.
+
+#ifndef NET_QUIC_QUIC_CONNECTION_HELPER_H_
+#define NET_QUIC_QUIC_CONNECTION_HELPER_H_
+
+#include "net/quic/quic_connection.h"
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/udp/datagram_client_socket.h"
+
+namespace base {
+class TaskRunner;
+} // namespace base
+
+namespace net {
+
+class QuicClock;
+class QuicRandom;
+
+class NET_EXPORT_PRIVATE QuicConnectionHelper
+ : public QuicConnectionHelperInterface {
+ public:
+ QuicConnectionHelper(base::TaskRunner* task_runner,
+ const QuicClock* clock,
+ QuicRandom* random_generator);
+ virtual ~QuicConnectionHelper();
+
+ // QuicConnectionHelperInterface
+ virtual const QuicClock* GetClock() const OVERRIDE;
+ virtual QuicRandom* GetRandomGenerator() OVERRIDE;
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE;
+
+ private:
+ base::TaskRunner* task_runner_;
+ const QuicClock* clock_;
+ QuicRandom* random_generator_;
+ base::WeakPtrFactory<QuicConnectionHelper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicConnectionHelper);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CONNECTION_HELPER_H_
diff --git a/net/quic/quic_connection_helper_test.cc b/net/quic/quic_connection_helper_test.cc
new file mode 100644
index 0000000..638ca46
--- /dev/null
+++ b/net/quic/quic_connection_helper_test.cc
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection_helper.h"
+
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/test_task_runner.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+class TestDelegate : public QuicAlarm::Delegate {
+ public:
+ TestDelegate() : fired_(false) {}
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ fired_ = true;
+ return QuicTime::Zero();
+ }
+
+ bool fired() const { return fired_; }
+ void Clear() { fired_= false; }
+
+ private:
+ bool fired_;
+};
+
+class QuicConnectionHelperTest : public ::testing::Test {
+ protected:
+ QuicConnectionHelperTest()
+ : runner_(new TestTaskRunner(&clock_)),
+ helper_(runner_.get(), &clock_, &random_generator_) {
+ }
+
+ scoped_refptr<TestTaskRunner> runner_;
+ QuicConnectionHelper helper_;
+ MockClock clock_;
+ MockRandom random_generator_;
+};
+
+TEST_F(QuicConnectionHelperTest, GetClock) {
+ EXPECT_EQ(&clock_, helper_.GetClock());
+}
+
+TEST_F(QuicConnectionHelperTest, GetRandomGenerator) {
+ EXPECT_EQ(&random_generator_, helper_.GetRandomGenerator());
+}
+
+TEST_F(QuicConnectionHelperTest, CreateAlarm) {
+ TestDelegate* delegate = new TestDelegate();
+ scoped_ptr<QuicAlarm> alarm(helper_.CreateAlarm(delegate));
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock_.Now().Add(delta));
+
+ // Verify that the alarm task has been posted.
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+ EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()),
+ runner_->GetPostedTasks()[0].delay);
+
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now());
+ EXPECT_TRUE(delegate->fired());
+}
+
+TEST_F(QuicConnectionHelperTest, CreateAlarmAndCancel) {
+ TestDelegate* delegate = new TestDelegate();
+ scoped_ptr<QuicAlarm> alarm(helper_.CreateAlarm(delegate));
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock_.Now().Add(delta));
+ alarm->Cancel();
+
+ // The alarm task should still be posted.
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+ EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()),
+ runner_->GetPostedTasks()[0].delay);
+
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now());
+ EXPECT_FALSE(delegate->fired());
+}
+
+TEST_F(QuicConnectionHelperTest, CreateAlarmAndReset) {
+ TestDelegate* delegate = new TestDelegate();
+ scoped_ptr<QuicAlarm> alarm(helper_.CreateAlarm(delegate));
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock_.Now().Add(delta));
+ alarm->Cancel();
+ QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3);
+ alarm->Set(clock_.Now().Add(new_delta));
+
+ // The alarm task should still be posted.
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+ EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()),
+ runner_->GetPostedTasks()[0].delay);
+
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now());
+ EXPECT_FALSE(delegate->fired());
+
+ // The alarm task should be posted again.
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(new_delta), clock_.Now());
+ EXPECT_TRUE(delegate->fired());
+}
+
+TEST_F(QuicConnectionHelperTest, CreateAlarmAndResetEarlier) {
+ TestDelegate* delegate = new TestDelegate();
+ scoped_ptr<QuicAlarm> alarm(helper_.CreateAlarm(delegate));
+
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(3);
+ alarm->Set(clock_.Now().Add(delta));
+ alarm->Cancel();
+ QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock_.Now().Add(new_delta));
+
+ // Both alarm tasks will be posted.
+ ASSERT_EQ(2u, runner_->GetPostedTasks().size());
+
+ // The earlier task will execute and will fire the alarm->
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(new_delta), clock_.Now());
+ EXPECT_TRUE(delegate->fired());
+ delegate->Clear();
+
+ // The latter task is still posted.
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+
+ // When the latter task is executed, the weak ptr will be invalid and
+ // the alarm will not fire.
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now());
+ EXPECT_FALSE(delegate->fired());
+}
+
+TEST_F(QuicConnectionHelperTest, CreateAlarmAndUpdate) {
+ TestDelegate* delegate = new TestDelegate();
+ scoped_ptr<QuicAlarm> alarm(helper_.CreateAlarm(delegate));
+
+ const QuicClock* clock = helper_.GetClock();
+ QuicTime start = clock->Now();
+ QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
+ alarm->Set(clock->Now().Add(delta));
+ QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3);
+ alarm->Update(clock->Now().Add(new_delta),
+ QuicTime::Delta::FromMicroseconds(1));
+
+ // The alarm task should still be posted.
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+ EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()),
+ runner_->GetPostedTasks()[0].delay);
+
+ runner_->RunNextTask();
+ EXPECT_EQ(QuicTime::Zero().Add(delta), clock->Now());
+ EXPECT_FALSE(delegate->fired());
+
+ // Move the alarm forward 1us and ensure it doesn't move forward.
+ alarm->Update(clock->Now().Add(new_delta),
+ QuicTime::Delta::FromMicroseconds(2));
+
+ ASSERT_EQ(1u, runner_->GetPostedTasks().size());
+ EXPECT_EQ(
+ base::TimeDelta::FromMicroseconds(
+ new_delta.Subtract(delta).ToMicroseconds()),
+ runner_->GetPostedTasks()[0].delay);
+ runner_->RunNextTask();
+ EXPECT_EQ(start.Add(new_delta), clock->Now());
+ EXPECT_TRUE(delegate->fired());
+
+ // Set the alarm via an update call.
+ new_delta = QuicTime::Delta::FromMicroseconds(5);
+ alarm->Update(clock->Now().Add(new_delta),
+ QuicTime::Delta::FromMicroseconds(1));
+ EXPECT_TRUE(alarm->IsSet());
+
+ // Update it with an uninitialized time and ensure it's cancelled.
+ alarm->Update(QuicTime::Zero(), QuicTime::Delta::FromMicroseconds(1));
+ EXPECT_FALSE(alarm->IsSet());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
new file mode 100644
index 0000000..4e40417
--- /dev/null
+++ b/net/quic/quic_connection_logger.cc
@@ -0,0 +1,889 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection_logger.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "net/base/net_log.h"
+#include "net/base/net_util.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_address_mismatch.h"
+#include "net/quic/quic_socket_address_coder.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+namespace {
+
+// We have ranges-of-buckets in the cumulative histogram (covering 21 packet
+// sequences) of length 2, 3, 4, ... 22.
+// Hence the largest sample is bounded by the sum of those numbers.
+const int kBoundingSampleInCumulativeHistogram = ((2 + 22) * 21) / 2;
+
+base::Value* NetLogQuicPacketCallback(const IPEndPoint* self_address,
+ const IPEndPoint* peer_address,
+ size_t packet_size,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("self_address", self_address->ToString());
+ dict->SetString("peer_address", peer_address->ToString());
+ dict->SetInteger("size", packet_size);
+ return dict;
+}
+
+base::Value* NetLogQuicPacketSentCallback(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber original_sequence_number,
+ EncryptionLevel level,
+ TransmissionType transmission_type,
+ size_t packet_size,
+ WriteResult result,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("encryption_level", level);
+ dict->SetInteger("transmission_type", transmission_type);
+ dict->SetString("packet_sequence_number",
+ base::Uint64ToString(sequence_number));
+ dict->SetString("original_sequence_number",
+ base::Uint64ToString(original_sequence_number));
+ dict->SetInteger("size", packet_size);
+ if (result.status != WRITE_STATUS_OK) {
+ dict->SetInteger("net_error", result.error_code);
+ }
+ return dict;
+}
+
+base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("connection_id",
+ base::Uint64ToString(header->public_header.connection_id));
+ dict->SetInteger("reset_flag", header->public_header.reset_flag);
+ dict->SetInteger("version_flag", header->public_header.version_flag);
+ dict->SetString("packet_sequence_number",
+ base::Uint64ToString(header->packet_sequence_number));
+ dict->SetInteger("entropy_flag", header->entropy_flag);
+ dict->SetInteger("fec_flag", header->fec_flag);
+ dict->SetInteger("fec_group", header->fec_group);
+ return dict;
+}
+
+base::Value* NetLogQuicStreamFrameCallback(const QuicStreamFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ dict->SetBoolean("fin", frame->fin);
+ dict->SetString("offset", base::Uint64ToString(frame->offset));
+ dict->SetInteger("length", frame->data.TotalBufferSize());
+ return dict;
+}
+
+base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("largest_observed",
+ base::Uint64ToString(frame->largest_observed));
+ dict->SetInteger("delta_time_largest_observed_us",
+ frame->delta_time_largest_observed.ToMicroseconds());
+ dict->SetInteger("entropy_hash",
+ frame->entropy_hash);
+ dict->SetBoolean("truncated", frame->is_truncated);
+
+ base::ListValue* missing = new base::ListValue();
+ dict->Set("missing_packets", missing);
+ const SequenceNumberSet& missing_packets = frame->missing_packets;
+ for (SequenceNumberSet::const_iterator it = missing_packets.begin();
+ it != missing_packets.end(); ++it) {
+ missing->AppendString(base::Uint64ToString(*it));
+ }
+
+ base::ListValue* revived = new base::ListValue();
+ dict->Set("revived_packets", revived);
+ const SequenceNumberSet& revived_packets = frame->revived_packets;
+ for (SequenceNumberSet::const_iterator it = revived_packets.begin();
+ it != revived_packets.end(); ++it) {
+ revived->AppendString(base::Uint64ToString(*it));
+ }
+
+ base::ListValue* received = new base::ListValue();
+ dict->Set("received_packet_times", received);
+ const PacketTimeList& received_times = frame->received_packet_times;
+ for (PacketTimeList::const_iterator it = received_times.begin();
+ it != received_times.end(); ++it) {
+ base::DictionaryValue* info = new base::DictionaryValue();
+ info->SetInteger("sequence_number", it->first);
+ info->SetInteger("received", it->second.ToDebuggingValue());
+ received->Append(info);
+ }
+
+ return dict;
+}
+
+base::Value* NetLogQuicCongestionFeedbackFrameCallback(
+ const QuicCongestionFeedbackFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ switch (frame->type) {
+ case kTCP:
+ dict->SetString("type", "TCP");
+ dict->SetInteger("receive_window", frame->tcp.receive_window);
+ break;
+ }
+
+ return dict;
+}
+
+base::Value* NetLogQuicRstStreamFrameCallback(
+ const QuicRstStreamFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ dict->SetInteger("quic_rst_stream_error", frame->error_code);
+ dict->SetString("details", frame->error_details);
+ return dict;
+}
+
+base::Value* NetLogQuicConnectionCloseFrameCallback(
+ const QuicConnectionCloseFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("quic_error", frame->error_code);
+ dict->SetString("details", frame->error_details);
+ return dict;
+}
+
+base::Value* NetLogQuicWindowUpdateFrameCallback(
+ const QuicWindowUpdateFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ dict->SetString("byte_offset", base::Uint64ToString(frame->byte_offset));
+ return dict;
+}
+
+base::Value* NetLogQuicBlockedFrameCallback(
+ const QuicBlockedFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("stream_id", frame->stream_id);
+ return dict;
+}
+
+base::Value* NetLogQuicGoAwayFrameCallback(
+ const QuicGoAwayFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("quic_error", frame->error_code);
+ dict->SetInteger("last_good_stream_id", frame->last_good_stream_id);
+ dict->SetString("reason_phrase", frame->reason_phrase);
+ return dict;
+}
+
+base::Value* NetLogQuicStopWaitingFrameCallback(
+ const QuicStopWaitingFrame* frame,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ base::DictionaryValue* sent_info = new base::DictionaryValue();
+ dict->Set("sent_info", sent_info);
+ sent_info->SetString("least_unacked",
+ base::Uint64ToString(frame->least_unacked));
+ return dict;
+}
+
+base::Value* NetLogQuicVersionNegotiationPacketCallback(
+ const QuicVersionNegotiationPacket* packet,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ base::ListValue* versions = new base::ListValue();
+ dict->Set("versions", versions);
+ for (QuicVersionVector::const_iterator it = packet->versions.begin();
+ it != packet->versions.end(); ++it) {
+ versions->AppendString(QuicVersionToString(*it));
+ }
+ return dict;
+}
+
+base::Value* NetLogQuicCryptoHandshakeMessageCallback(
+ const CryptoHandshakeMessage* message,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetString("quic_crypto_handshake_message", message->DebugString());
+ return dict;
+}
+
+base::Value* NetLogQuicOnConnectionClosedCallback(
+ QuicErrorCode error,
+ bool from_peer,
+ NetLog::LogLevel /* log_level */) {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->SetInteger("quic_error", error);
+ dict->SetBoolean("from_peer", from_peer);
+ return dict;
+}
+
+base::Value* NetLogQuicCertificateVerifiedCallback(
+ scoped_refptr<X509Certificate> cert,
+ NetLog::LogLevel /* log_level */) {
+ // Only the subjects are logged so that we can investigate connection pooling.
+ // More fields could be logged in the future.
+ std::vector<std::string> dns_names;
+ cert->GetDNSNames(&dns_names);
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ base::ListValue* subjects = new base::ListValue();
+ for (std::vector<std::string>::const_iterator it = dns_names.begin();
+ it != dns_names.end(); it++) {
+ subjects->Append(new base::StringValue(*it));
+ }
+ dict->Set("subjects", subjects);
+ return dict;
+}
+
+void UpdatePacketGapSentHistogram(size_t num_consecutive_missing_packets) {
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.PacketGapSent",
+ num_consecutive_missing_packets);
+}
+
+void UpdatePublicResetAddressMismatchHistogram(
+ const IPEndPoint& server_hello_address,
+ const IPEndPoint& public_reset_address) {
+ int sample = GetAddressMismatch(server_hello_address, public_reset_address);
+ // We are seemingly talking to an older server that does not support the
+ // feature, so we can't report the results in the histogram.
+ if (sample < 0) {
+ return;
+ }
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PublicResetAddressMismatch2",
+ sample, QUIC_ADDRESS_MISMATCH_MAX);
+}
+
+const char* GetConnectionDescriptionString() {
+ NetworkChangeNotifier::ConnectionType type =
+ NetworkChangeNotifier::GetConnectionType();
+ const char* description = NetworkChangeNotifier::ConnectionTypeToString(type);
+ // Most platforms don't distingish Wifi vs Etherenet, and call everything
+ // CONNECTION_UNKNOWN :-(. We'll tease out some details when we are on WiFi,
+ // and hopefully leave only ethernet (with no WiFi available) in the
+ // CONNECTION_UNKNOWN category. This *might* err if there is both ethernet,
+ // as well as WiFi, where WiFi was not being used that much.
+ // This function only seems usefully defined on Windows currently.
+ if (type == NetworkChangeNotifier::CONNECTION_UNKNOWN ||
+ type == NetworkChangeNotifier::CONNECTION_WIFI) {
+ WifiPHYLayerProtocol wifi_type = GetWifiPHYLayerProtocol();
+ switch (wifi_type) {
+ case WIFI_PHY_LAYER_PROTOCOL_NONE:
+ // No wifi support or no associated AP.
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_ANCIENT:
+ // An obsolete modes introduced by the original 802.11, e.g. IR, FHSS.
+ description = "CONNECTION_WIFI_ANCIENT";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_A:
+ // 802.11a, OFDM-based rates.
+ description = "CONNECTION_WIFI_802.11a";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_B:
+ // 802.11b, DSSS or HR DSSS.
+ description = "CONNECTION_WIFI_802.11b";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_G:
+ // 802.11g, same rates as 802.11a but compatible with 802.11b.
+ description = "CONNECTION_WIFI_802.11g";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_N:
+ // 802.11n, HT rates.
+ description = "CONNECTION_WIFI_802.11n";
+ break;
+ case WIFI_PHY_LAYER_PROTOCOL_UNKNOWN:
+ // Unclassified mode or failure to identify.
+ break;
+ }
+ }
+ return description;
+}
+
+// If |address| is an IPv4-mapped IPv6 address, returns ADDRESS_FAMILY_IPV4
+// instead of ADDRESS_FAMILY_IPV6. Othewise, behaves like GetAddressFamily().
+AddressFamily GetRealAddressFamily(const IPAddressNumber& address) {
+ return IsIPv4Mapped(address) ? ADDRESS_FAMILY_IPV4 :
+ GetAddressFamily(address);
+}
+
+} // namespace
+
+QuicConnectionLogger::QuicConnectionLogger(const BoundNetLog& net_log)
+ : net_log_(net_log),
+ last_received_packet_sequence_number_(0),
+ last_received_packet_size_(0),
+ largest_received_packet_sequence_number_(0),
+ largest_received_missing_packet_sequence_number_(0),
+ num_out_of_order_received_packets_(0),
+ num_packets_received_(0),
+ num_truncated_acks_sent_(0),
+ num_truncated_acks_received_(0),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0),
+ num_incorrect_connection_ids_(0),
+ num_undecryptable_packets_(0),
+ num_duplicate_packets_(0),
+ connection_description_(GetConnectionDescriptionString()) {
+}
+
+QuicConnectionLogger::~QuicConnectionLogger() {
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderPacketsReceived",
+ num_out_of_order_received_packets_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksSent",
+ num_truncated_acks_sent_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.TruncatedAcksReceived",
+ num_truncated_acks_received_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.IncorrectConnectionIDsReceived",
+ num_incorrect_connection_ids_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.UndecryptablePacketsReceived",
+ num_undecryptable_packets_);
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.DuplicatePacketsReceived",
+ num_duplicate_packets_);
+
+ if (num_frames_received_ > 0) {
+ int duplicate_stream_frame_per_thousand =
+ num_duplicate_frames_received_ * 1000 / num_frames_received_;
+ if (num_packets_received_ < 100) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.StreamFrameDuplicatedShortConnection",
+ duplicate_stream_frame_per_thousand, 1, 1000, 75);
+ } else {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Net.QuicSession.StreamFrameDuplicatedLongConnection",
+ duplicate_stream_frame_per_thousand, 1, 1000, 75);
+
+ }
+ }
+
+ RecordLossHistograms();
+}
+
+void QuicConnectionLogger::OnFrameAddedToPacket(const QuicFrame& frame) {
+ switch (frame.type) {
+ case PADDING_FRAME:
+ break;
+ case STREAM_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STREAM_FRAME_SENT,
+ base::Bind(&NetLogQuicStreamFrameCallback, frame.stream_frame));
+ break;
+ case ACK_FRAME: {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_ACK_FRAME_SENT,
+ base::Bind(&NetLogQuicAckFrameCallback, frame.ack_frame));
+ const SequenceNumberSet& missing_packets =
+ frame.ack_frame->missing_packets;
+ const uint8 max_ranges = std::numeric_limits<uint8>::max();
+ // Compute an upper bound on the number of NACK ranges. If the bound
+ // is below the max, then it clearly isn't truncated.
+ if (missing_packets.size() < max_ranges ||
+ (*missing_packets.rbegin() - *missing_packets.begin() -
+ missing_packets.size() + 1) < max_ranges) {
+ break;
+ }
+ size_t num_ranges = 0;
+ QuicPacketSequenceNumber last_missing = 0;
+ for (SequenceNumberSet::const_iterator it = missing_packets.begin();
+ it != missing_packets.end(); ++it) {
+ if (*it != last_missing + 1 && ++num_ranges >= max_ranges) {
+ ++num_truncated_acks_sent_;
+ break;
+ }
+ last_missing = *it;
+ }
+ break;
+ }
+ case CONGESTION_FEEDBACK_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CONGESTION_FEEDBACK_FRAME_SENT,
+ base::Bind(&NetLogQuicCongestionFeedbackFrameCallback,
+ frame.congestion_feedback_frame));
+ break;
+ case RST_STREAM_FRAME:
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.RstStreamErrorCodeClient",
+ frame.rst_stream_frame->error_code);
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_RST_STREAM_FRAME_SENT,
+ base::Bind(&NetLogQuicRstStreamFrameCallback,
+ frame.rst_stream_frame));
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CONNECTION_CLOSE_FRAME_SENT,
+ base::Bind(&NetLogQuicConnectionCloseFrameCallback,
+ frame.connection_close_frame));
+ break;
+ case GOAWAY_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_SENT,
+ base::Bind(&NetLogQuicGoAwayFrameCallback,
+ frame.goaway_frame));
+ break;
+ case WINDOW_UPDATE_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_SENT,
+ base::Bind(&NetLogQuicWindowUpdateFrameCallback,
+ frame.window_update_frame));
+ break;
+ case BLOCKED_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_SENT,
+ base::Bind(&NetLogQuicBlockedFrameCallback,
+ frame.blocked_frame));
+ break;
+ case STOP_WAITING_FRAME:
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_SENT,
+ base::Bind(&NetLogQuicStopWaitingFrameCallback,
+ frame.stop_waiting_frame));
+ break;
+ case PING_FRAME:
+ // PingFrame has no contents to log, so just record that it was sent.
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_SENT);
+ break;
+ default:
+ DCHECK(false) << "Illegal frame type: " << frame.type;
+ }
+}
+
+void QuicConnectionLogger::OnPacketSent(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber original_sequence_number,
+ EncryptionLevel level,
+ TransmissionType transmission_type,
+ const QuicEncryptedPacket& packet,
+ WriteResult result) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_PACKET_SENT,
+ base::Bind(&NetLogQuicPacketSentCallback, sequence_number,
+ original_sequence_number, level, transmission_type,
+ packet.length(), result));
+}
+
+void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet) {
+ if (local_address_from_self_.GetFamily() == ADDRESS_FAMILY_UNSPECIFIED) {
+ local_address_from_self_ = self_address;
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromSelf",
+ GetRealAddressFamily(self_address.address()),
+ ADDRESS_FAMILY_LAST);
+ }
+
+ last_received_packet_size_ = packet.length();
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_PACKET_RECEIVED,
+ base::Bind(&NetLogQuicPacketCallback, &self_address, &peer_address,
+ packet.length()));
+}
+
+void QuicConnectionLogger::OnIncorrectConnectionId(
+ QuicConnectionId connection_id) {
+ ++num_incorrect_connection_ids_;
+}
+
+void QuicConnectionLogger::OnUndecryptablePacket() {
+ ++num_undecryptable_packets_;
+}
+
+void QuicConnectionLogger::OnDuplicatePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ ++num_duplicate_packets_;
+}
+
+void QuicConnectionLogger::OnProtocolVersionMismatch(
+ QuicVersion received_version) {
+ // TODO(rtenneti): Add logging.
+}
+
+void QuicConnectionLogger::OnPacketHeader(const QuicPacketHeader& header) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED,
+ base::Bind(&NetLogQuicPacketHeaderCallback, &header));
+ ++num_packets_received_;
+ if (largest_received_packet_sequence_number_ <
+ header.packet_sequence_number) {
+ QuicPacketSequenceNumber delta = header.packet_sequence_number -
+ largest_received_packet_sequence_number_;
+ if (delta > 1) {
+ // There is a gap between the largest packet previously received and
+ // the current packet. This indicates either loss, or out-of-order
+ // delivery.
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.PacketGapReceived", delta - 1);
+ }
+ largest_received_packet_sequence_number_ = header.packet_sequence_number;
+ }
+ if (header.packet_sequence_number < received_packets_.size())
+ received_packets_[header.packet_sequence_number] = true;
+ if (header.packet_sequence_number < last_received_packet_sequence_number_) {
+ ++num_out_of_order_received_packets_;
+ UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderGapReceived",
+ last_received_packet_sequence_number_ -
+ header.packet_sequence_number);
+ }
+ last_received_packet_sequence_number_ = header.packet_sequence_number;
+}
+
+void QuicConnectionLogger::OnStreamFrame(const QuicStreamFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STREAM_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicStreamFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnAckFrame(const QuicAckFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_ACK_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicAckFrameCallback, &frame));
+
+ const size_t kApproximateLargestSoloAckBytes = 100;
+ if (last_received_packet_sequence_number_ < received_acks_.size() &&
+ last_received_packet_size_ < kApproximateLargestSoloAckBytes)
+ received_acks_[last_received_packet_sequence_number_] = true;
+
+ if (frame.is_truncated)
+ ++num_truncated_acks_received_;
+
+ if (frame.missing_packets.empty())
+ return;
+
+ SequenceNumberSet missing_packets = frame.missing_packets;
+ SequenceNumberSet::const_iterator it = missing_packets.lower_bound(
+ largest_received_missing_packet_sequence_number_);
+ if (it == missing_packets.end())
+ return;
+
+ if (*it == largest_received_missing_packet_sequence_number_) {
+ ++it;
+ if (it == missing_packets.end())
+ return;
+ }
+ // Scan through the list and log consecutive ranges of missing packets.
+ size_t num_consecutive_missing_packets = 0;
+ QuicPacketSequenceNumber previous_missing_packet = *it - 1;
+ while (it != missing_packets.end()) {
+ if (previous_missing_packet == *it - 1) {
+ ++num_consecutive_missing_packets;
+ } else {
+ DCHECK_NE(0u, num_consecutive_missing_packets);
+ UpdatePacketGapSentHistogram(num_consecutive_missing_packets);
+ // Make sure this packet it included in the count.
+ num_consecutive_missing_packets = 1;
+ }
+ previous_missing_packet = *it;
+ ++it;
+ }
+ if (num_consecutive_missing_packets != 0) {
+ UpdatePacketGapSentHistogram(num_consecutive_missing_packets);
+ }
+ largest_received_missing_packet_sequence_number_ =
+ *missing_packets.rbegin();
+}
+
+void QuicConnectionLogger::OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CONGESTION_FEEDBACK_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicCongestionFeedbackFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_STOP_WAITING_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicStopWaitingFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnRstStreamFrame(const QuicRstStreamFrame& frame) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.RstStreamErrorCodeServer",
+ frame.error_code);
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_RST_STREAM_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicRstStreamFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CONNECTION_CLOSE_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicConnectionCloseFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_WINDOW_UPDATE_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicWindowUpdateFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_BLOCKED_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicBlockedFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_GOAWAY_FRAME_RECEIVED,
+ base::Bind(&NetLogQuicGoAwayFrameCallback, &frame));
+}
+
+void QuicConnectionLogger::OnPingFrame(const QuicPingFrame& frame) {
+ // PingFrame has no contents to log, so just record that it was received.
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PING_FRAME_RECEIVED);
+}
+
+void QuicConnectionLogger::OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) {
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED);
+ UpdatePublicResetAddressMismatchHistogram(local_address_from_shlo_,
+ packet.client_address);
+}
+
+void QuicConnectionLogger::OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_VERSION_NEGOTIATION_PACKET_RECEIVED,
+ base::Bind(&NetLogQuicVersionNegotiationPacketCallback, &packet));
+}
+
+void QuicConnectionLogger::OnRevivedPacket(
+ const QuicPacketHeader& revived_header,
+ base::StringPiece payload) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_REVIVED,
+ base::Bind(&NetLogQuicPacketHeaderCallback, &revived_header));
+}
+
+void QuicConnectionLogger::OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CRYPTO_HANDSHAKE_MESSAGE_RECEIVED,
+ base::Bind(&NetLogQuicCryptoHandshakeMessageCallback, &message));
+
+ if (message.tag() == kSHLO) {
+ StringPiece address;
+ QuicSocketAddressCoder decoder;
+ if (message.GetStringPiece(kCADR, &address) &&
+ decoder.Decode(address.data(), address.size())) {
+ local_address_from_shlo_ = IPEndPoint(decoder.ip(), decoder.port());
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.ConnectionTypeFromPeer",
+ GetRealAddressFamily(
+ local_address_from_shlo_.address()),
+ ADDRESS_FAMILY_LAST);
+ }
+ }
+}
+
+void QuicConnectionLogger::OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CRYPTO_HANDSHAKE_MESSAGE_SENT,
+ base::Bind(&NetLogQuicCryptoHandshakeMessageCallback, &message));
+}
+
+void QuicConnectionLogger::OnConnectionClosed(QuicErrorCode error,
+ bool from_peer) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CLOSED,
+ base::Bind(&NetLogQuicOnConnectionClosedCallback, error, from_peer));
+}
+
+void QuicConnectionLogger::OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) {
+ string quic_version = QuicVersionToString(version);
+ net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_VERSION_NEGOTIATED,
+ NetLog::StringCallback("version", &quic_version));
+}
+
+void QuicConnectionLogger::UpdateReceivedFrameCounts(
+ QuicStreamId stream_id,
+ int num_frames_received,
+ int num_duplicate_frames_received) {
+ if (stream_id != kCryptoStreamId) {
+ num_frames_received_ += num_frames_received;
+ num_duplicate_frames_received_ += num_duplicate_frames_received;
+ }
+}
+
+void QuicConnectionLogger::OnCertificateVerified(
+ const CertVerifyResult& result) {
+ net_log_.AddEvent(
+ NetLog::TYPE_QUIC_SESSION_CERTIFICATE_VERIFIED,
+ base::Bind(&NetLogQuicCertificateVerifiedCallback, result.verified_cert));
+}
+
+base::HistogramBase* QuicConnectionLogger::GetPacketSequenceNumberHistogram(
+ const char* statistic_name) const {
+ string prefix("Net.QuicSession.PacketReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + statistic_name + connection_description_,
+ 1, received_packets_.size(), received_packets_.size() + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+base::HistogramBase* QuicConnectionLogger::Get6PacketHistogram(
+ const char* which_6) const {
+ // This histogram takes a binary encoding of the 6 consecutive packets
+ // received. As a result, there are 64 possible sample-patterns.
+ string prefix("Net.QuicSession.6PacketsPatternsReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + which_6 + connection_description_, 1, 64, 65,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+base::HistogramBase* QuicConnectionLogger::Get21CumulativeHistogram(
+ const char* which_21) const {
+ // This histogram contains, for each sequence of 21 packets, the results from
+ // 21 distinct questions about that sequence. Conceptually the histogtram is
+ // broken into 21 distinct ranges, and one sample is added into each of those
+ // ranges whenever we process a set of 21 packets.
+ // There is a little rendundancy, as each "range" must have the same number
+ // of samples, all told, but the histogram is a tad easier to read this way.
+ // The questions are:
+ // Was the first packet present (bucket 0==>no; bucket 1==>yes)
+ // Of the first two packets, how many were present? (bucket 2==> none;
+ // bucket 3==> 1 of 2; bucket 4==> 2 of 2)
+ // Of the first three packets, how many were present? (bucket 5==>none;
+ // bucket 6==> 1 of 3; bucket 7==> 2 of 3; bucket 8==> 3 of 3).
+ // etc.
+ string prefix("Net.QuicSession.21CumulativePacketsReceived_");
+ return base::LinearHistogram::FactoryGet(
+ prefix + which_21 + connection_description_,
+ 1, kBoundingSampleInCumulativeHistogram,
+ kBoundingSampleInCumulativeHistogram + 1,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+// static
+void QuicConnectionLogger::AddTo21CumulativeHistogram(
+ base::HistogramBase* histogram,
+ int bit_mask_of_packets,
+ int valid_bits_in_mask) {
+ DCHECK_LE(valid_bits_in_mask, 21);
+ DCHECK_LT(bit_mask_of_packets, 1 << 21);
+ const int blank_bits_in_mask = 21 - valid_bits_in_mask;
+ DCHECK_EQ(bit_mask_of_packets & ((1 << blank_bits_in_mask) - 1), 0);
+ bit_mask_of_packets >>= blank_bits_in_mask;
+ int bits_so_far = 0;
+ int range_start = 0;
+ for (int i = 1; i <= valid_bits_in_mask; ++i) {
+ bits_so_far += bit_mask_of_packets & 1;
+ bit_mask_of_packets >>= 1;
+ DCHECK_LT(range_start + bits_so_far, kBoundingSampleInCumulativeHistogram);
+ histogram->Add(range_start + bits_so_far);
+ range_start += i + 1;
+ }
+}
+
+void QuicConnectionLogger::RecordAggregatePacketLossRate() const {
+ // For short connections under 22 packets in length, we'll rely on the
+ // Net.QuicSession.21CumulativePacketsReceived_* histogram to indicate packet
+ // loss rates. This way we avoid tremendously anomalous contributions to our
+ // histogram. (e.g., if we only got 5 packets, but lost 1, we'd otherwise
+ // record a 20% loss in this histogram!). We may still get some strange data
+ // (1 loss in 22 is still high :-/).
+ if (largest_received_packet_sequence_number_ <= 21)
+ return;
+
+ QuicPacketSequenceNumber divisor = largest_received_packet_sequence_number_;
+ QuicPacketSequenceNumber numerator = divisor - num_packets_received_;
+ if (divisor < 100000)
+ numerator *= 1000;
+ else
+ divisor /= 1000;
+ string prefix("Net.QuicSession.PacketLossRate_");
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ prefix + connection_description_, 1, 1000, 75,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->Add(numerator / divisor);
+}
+
+void QuicConnectionLogger::RecordLossHistograms() const {
+ if (largest_received_packet_sequence_number_ == 0)
+ return; // Connection was never used.
+ RecordAggregatePacketLossRate();
+
+ base::HistogramBase* is_not_ack_histogram =
+ GetPacketSequenceNumberHistogram("IsNotAck_");
+ base::HistogramBase* is_an_ack_histogram =
+ GetPacketSequenceNumberHistogram("IsAnAck_");
+ base::HistogramBase* packet_arrived_histogram =
+ GetPacketSequenceNumberHistogram("Ack_");
+ base::HistogramBase* packet_missing_histogram =
+ GetPacketSequenceNumberHistogram("Nack_");
+ base::HistogramBase* ongoing_cumulative_packet_histogram =
+ Get21CumulativeHistogram("Some21s_");
+ base::HistogramBase* first_cumulative_packet_histogram =
+ Get21CumulativeHistogram("First21_");
+ base::HistogramBase* six_packet_histogram = Get6PacketHistogram("Some6s_");
+
+ DCHECK_EQ(received_packets_.size(), received_acks_.size());
+ const QuicPacketSequenceNumber last_index =
+ std::min<QuicPacketSequenceNumber>(received_packets_.size() - 1,
+ largest_received_packet_sequence_number_);
+ const QuicPacketSequenceNumber index_of_first_21_contribution =
+ std::min<QuicPacketSequenceNumber>(21, last_index);
+ // Bit pattern of consecutively received packets that is maintained as we scan
+ // through the received_packets_ vector. Less significant bits correspond to
+ // less recent packets, and only the low order 21 bits are ever defined.
+ // Bit is 1 iff corresponding packet was received.
+ int packet_pattern_21 = 0;
+ // Zero is an invalid packet sequence number.
+ DCHECK(!received_packets_[0]);
+ for (size_t i = 1; i <= last_index; ++i) {
+ if (received_acks_[i])
+ is_an_ack_histogram->Add(i);
+ else
+ is_not_ack_histogram->Add(i);
+
+ packet_pattern_21 >>= 1;
+ if (received_packets_[i]) {
+ packet_arrived_histogram->Add(i);
+ packet_pattern_21 |= (1 << 20); // Turn on the 21st bit.
+ } else {
+ packet_missing_histogram->Add(i);
+ }
+
+ if (i == index_of_first_21_contribution) {
+ AddTo21CumulativeHistogram(first_cumulative_packet_histogram,
+ packet_pattern_21, i);
+ }
+ // We'll just record for non-overlapping ranges, to reduce histogramming
+ // cost for now. Each call does 21 separate histogram additions.
+ if (i > 21 || i % 21 == 0) {
+ AddTo21CumulativeHistogram(ongoing_cumulative_packet_histogram,
+ packet_pattern_21, 21);
+ }
+
+ if (i < 6)
+ continue; // Not enough packets to do any pattern recording.
+ int recent_6_mask = packet_pattern_21 >> 15;
+ DCHECK_LT(recent_6_mask, 64);
+ if (i == 6) {
+ Get6PacketHistogram("First6_")->Add(recent_6_mask);
+ continue;
+ }
+ // Record some overlapping patterns, to get a better picture, since this is
+ // not very expensive.
+ if (i % 3 == 0)
+ six_packet_histogram->Add(recent_6_mask);
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h
new file mode 100644
index 0000000..7693d79
--- /dev/null
+++ b/net/quic/quic_connection_logger.h
@@ -0,0 +1,174 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CONNECTION_LOGGER_H_
+#define NET_QUIC_QUIC_CONNECTION_LOGGER_H_
+
+#include <bitset>
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_log.h"
+#include "net/base/network_change_notifier.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace test {
+class QuicConnectionLoggerPeer;
+} // namespace test
+
+class CryptoHandshakeMessage;
+class CertVerifyResult;
+
+// This class is a debug visitor of a QuicConnection which logs
+// events to |net_log|.
+class NET_EXPORT_PRIVATE QuicConnectionLogger
+ : public QuicConnectionDebugVisitor {
+ public:
+ explicit QuicConnectionLogger(const BoundNetLog& net_log);
+
+ virtual ~QuicConnectionLogger();
+
+ // QuicPacketGenerator::DebugDelegateInterface
+ virtual void OnFrameAddedToPacket(const QuicFrame& frame) OVERRIDE;
+
+ // QuicConnectionDebugVisitorInterface
+ virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber original_sequence_number,
+ EncryptionLevel level,
+ TransmissionType transmission_type,
+ const QuicEncryptedPacket& packet,
+ WriteResult result) OVERRIDE;
+ virtual void OnPacketReceived(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet) OVERRIDE;
+ virtual void OnIncorrectConnectionId(
+ QuicConnectionId connection_id) OVERRIDE;
+ virtual void OnUndecryptablePacket() OVERRIDE;
+ virtual void OnDuplicatePacket(QuicPacketSequenceNumber sequence_number)
+ OVERRIDE;
+ virtual void OnProtocolVersionMismatch(QuicVersion version) OVERRIDE;
+ virtual void OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
+ virtual void OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
+ virtual void OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual void OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual void OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) OVERRIDE;
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual void OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
+ virtual void OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE;
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) OVERRIDE;
+ virtual void OnRevivedPacket(const QuicPacketHeader& revived_header,
+ base::StringPiece payload) OVERRIDE;
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE;
+
+ void OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message);
+ void OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message);
+ void UpdateReceivedFrameCounts(QuicStreamId stream_id,
+ int num_frames_received,
+ int num_duplicate_frames_received);
+ void OnCertificateVerified(const CertVerifyResult& result);
+
+ private:
+ friend class test::QuicConnectionLoggerPeer;
+
+ // Do a factory get for a histogram for recording data, about individual
+ // packet sequence numbers, that was gathered in the vectors
+ // received_packets_ and received_acks_. |statistic_name| identifies which
+ // element of data is recorded, and is used to form the histogram name.
+ base::HistogramBase* GetPacketSequenceNumberHistogram(
+ const char* statistic_name) const;
+ // Do a factory get for a histogram to record a 6-packet loss-sequence as a
+ // sample. The histogram will record the 64 distinct possible combinations.
+ // |which_6| is used to adjust the name of the histogram to distinguish the
+ // first 6 packets in a connection, vs. some later 6 packets.
+ base::HistogramBase* Get6PacketHistogram(const char* which_6) const;
+ // Do a factory get for a histogram to record cumulative stats across a 21
+ // packet sequence. |which_21| is used to adjust the name of the histogram
+ // to distinguish the first 21 packets' loss data, vs. some later 21 packet
+ // sequences' loss data.
+ base::HistogramBase* Get21CumulativeHistogram(const char* which_21) const;
+ // Add samples associated with a |bit_mask_of_packets| to the given histogram
+ // that was provided by Get21CumulativeHistogram(). The LSB of that mask
+ // corresponds to the oldest packet sequence number in the series of packets,
+ // and the bit in the 2^20 position corresponds to the most recently received
+ // packet. Of the maximum of 21 bits that are valid (correspond to packets),
+ // only the most significant |valid_bits_in_mask| are processed.
+ // A bit value of 0 indicates that a packet was never received, and a 1
+ // indicates the packet was received.
+ static void AddTo21CumulativeHistogram(base::HistogramBase* histogram,
+ int bit_mask_of_packets,
+ int valid_bits_in_mask);
+ // For connections longer than 21 received packets, this call will calculate
+ // the overall packet loss rate, and record it into a histogram.
+ void RecordAggregatePacketLossRate() const;
+ // At destruction time, this records results of |pacaket_received_| into
+ // histograms for specific connection types.
+ void RecordLossHistograms() const;
+
+ BoundNetLog net_log_;
+ // The last packet sequence number received.
+ QuicPacketSequenceNumber last_received_packet_sequence_number_;
+ // The size of the most recently received packet.
+ size_t last_received_packet_size_;
+ // The largest packet sequence number received. In the case where a packet is
+ // received late (out of order), this value will not be updated.
+ QuicPacketSequenceNumber largest_received_packet_sequence_number_;
+ // The largest packet sequence number which the peer has failed to
+ // receive, according to the missing packet set in their ack frames.
+ QuicPacketSequenceNumber largest_received_missing_packet_sequence_number_;
+ // Number of times that the current received packet sequence number is
+ // smaller than the last received packet sequence number.
+ size_t num_out_of_order_received_packets_;
+ // The number of times that OnPacketHeader was called.
+ // If the network replicates packets, then this number may be slightly
+ // different from the real number of distinct packets received.
+ QuicPacketSequenceNumber num_packets_received_;
+ // Number of times a truncated ACK frame was sent.
+ size_t num_truncated_acks_sent_;
+ // Number of times a truncated ACK frame was received.
+ size_t num_truncated_acks_received_;
+ // The kCADR value provided by the server in ServerHello.
+ IPEndPoint local_address_from_shlo_;
+ // The first local address from which a packet was received.
+ IPEndPoint local_address_from_self_;
+ // Count of the number of frames received.
+ int num_frames_received_;
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+ // Count of the number of packets received with incorrect connection IDs.
+ int num_incorrect_connection_ids_;
+ // Count of the number of undecryptable packets received.
+ int num_undecryptable_packets_;
+ // Count of the number of duplicate packets received.
+ int num_duplicate_packets_;
+ // Vector of inital packets status' indexed by packet sequence numbers, where
+ // false means never received. Zero is not a valid packet sequence number, so
+ // that offset is never used, and we'll track 150 packets.
+ std::bitset<151> received_packets_;
+ // Vector to indicate which of the initial 150 received packets turned out to
+ // contain solo ACK frames. An element is true iff an ACK frame was in the
+ // corresponding packet, and there was very little else.
+ std::bitset<151> received_acks_;
+ // The available type of connection (WiFi, 3G, etc.) when connection was first
+ // used.
+ const char* const connection_description_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicConnectionLogger);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CONNECTION_LOGGER_H_
diff --git a/net/quic/quic_connection_logger_unittest.cc b/net/quic/quic_connection_logger_unittest.cc
new file mode 100644
index 0000000..9f9cd9a
--- /dev/null
+++ b/net/quic/quic_connection_logger_unittest.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection_logger.h"
+
+#include "net/quic/quic_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+class QuicConnectionLoggerPeer {
+ public:
+ static size_t num_truncated_acks_sent(const QuicConnectionLogger& logger) {
+ return logger.num_truncated_acks_sent_;
+ }
+};
+
+class QuicConnectionLoggerTest : public ::testing::Test {
+ protected:
+ QuicConnectionLoggerTest() : logger_(net_log_) {}
+
+ BoundNetLog net_log_;
+ QuicConnectionLogger logger_;
+};
+
+TEST_F(QuicConnectionLoggerTest, TruncatedAcksSentNotChanged) {
+ QuicAckFrame frame;
+ logger_.OnFrameAddedToPacket(QuicFrame(&frame));
+ EXPECT_EQ(0u, QuicConnectionLoggerPeer::num_truncated_acks_sent(logger_));
+
+ for (QuicPacketSequenceNumber i = 0; i < 256; ++i) {
+ frame.missing_packets.insert(i);
+ }
+ logger_.OnFrameAddedToPacket(QuicFrame(&frame));
+ EXPECT_EQ(0u, QuicConnectionLoggerPeer::num_truncated_acks_sent(logger_));
+}
+
+TEST_F(QuicConnectionLoggerTest, TruncatedAcksSent) {
+ QuicAckFrame frame;
+ for (QuicPacketSequenceNumber i = 0; i < 512; i += 2) {
+ frame.missing_packets.insert(i);
+ }
+ logger_.OnFrameAddedToPacket(QuicFrame(&frame));
+ EXPECT_EQ(1u, QuicConnectionLoggerPeer::num_truncated_acks_sent(logger_));
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_connection_stats.cc b/net/quic/quic_connection_stats.cc
new file mode 100644
index 0000000..7081d0d
--- /dev/null
+++ b/net/quic/quic_connection_stats.cc
@@ -0,0 +1,90 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection_stats.h"
+
+using std::ostream;
+
+namespace net {
+
+QuicConnectionStats::QuicConnectionStats()
+ : bytes_sent(0),
+ packets_sent(0),
+ stream_bytes_sent(0),
+ packets_discarded(0),
+ bytes_received(0),
+ packets_received(0),
+ packets_processed(0),
+ stream_bytes_received(0),
+ bytes_retransmitted(0),
+ packets_retransmitted(0),
+ bytes_spuriously_retransmitted(0),
+ packets_spuriously_retransmitted(0),
+ packets_lost(0),
+ slowstart_packets_lost(0),
+ packets_revived(0),
+ packets_dropped(0),
+ crypto_retransmit_count(0),
+ loss_timeout_count(0),
+ tlp_count(0),
+ rto_count(0),
+ spurious_rto_count(0),
+ min_rtt_us(0),
+ srtt_us(0),
+ max_packet_size(0),
+ estimated_bandwidth(0),
+ congestion_window(0),
+ slow_start_threshold(0),
+ packets_reordered(0),
+ max_sequence_reordering(0),
+ max_time_reordering_us(0),
+ tcp_loss_events(0),
+ cwnd_increase_congestion_avoidance(0),
+ cwnd_increase_cubic_mode(0),
+ connection_creation_time(QuicTime::Zero()) {
+}
+
+QuicConnectionStats::~QuicConnectionStats() {}
+
+ostream& operator<<(ostream& os, const QuicConnectionStats& s) {
+ os << "{ bytes sent: " << s.bytes_sent
+ << ", packets sent:" << s.packets_sent
+ << ", stream bytes sent: " << s.stream_bytes_sent
+ << ", packets discarded: " << s.packets_discarded
+ << ", bytes received: " << s.bytes_received
+ << ", packets received: " << s.packets_received
+ << ", packets processed: " << s.packets_processed
+ << ", stream bytes received: " << s.stream_bytes_received
+ << ", bytes retransmitted: " << s.bytes_retransmitted
+ << ", packets retransmitted: " << s.packets_retransmitted
+ << ", bytes spuriously retransmitted: " << s.bytes_spuriously_retransmitted
+ << ", packets spuriously retransmitted: "
+ << s.packets_spuriously_retransmitted
+ << ", packets lost: " << s.packets_lost
+ << ", slowstart packets lost: " << s.slowstart_packets_lost
+ << ", packets revived: " << s.packets_revived
+ << ", packets dropped:" << s.packets_dropped
+ << ", crypto retransmit count: " << s.crypto_retransmit_count
+ << ", tlp count: " << s.tlp_count
+ << ", rto count: " << s.rto_count
+ << ", spurious_rto_count:" << s.spurious_rto_count
+ << ", min_rtt(us): " << s.min_rtt_us
+ << ", srtt(us): " << s.srtt_us
+ << ", max packet size: " << s.max_packet_size
+ << ", estimated bandwidth: " << s.estimated_bandwidth
+ << ", congestion window: " << s.congestion_window
+ << ", slow start threshold: " << s.slow_start_threshold
+ << ", tcp_loss_events: " << s.tcp_loss_events
+ << ", packets reordered: " << s.packets_reordered
+ << ", max sequence reordering: " << s.max_sequence_reordering
+ << ", max time reordering(us): " << s.max_time_reordering_us
+ << ", total amount of cwnd increase in TCPCubic, in congestion avoidance: "
+ << s.cwnd_increase_congestion_avoidance
+ << ", amount of cwnd increase in TCPCubic, in cubic mode: "
+ << s.cwnd_increase_cubic_mode
+ << "}\n";
+ return os;
+}
+
+} // namespace net
diff --git a/net/quic/quic_connection_stats.h b/net/quic/quic_connection_stats.h
new file mode 100644
index 0000000..1cb8ca7
--- /dev/null
+++ b/net/quic/quic_connection_stats.h
@@ -0,0 +1,84 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CONNECTION_STATS_H_
+#define NET_QUIC_QUIC_CONNECTION_STATS_H_
+
+#include <ostream>
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+// Structure to hold stats for a QuicConnection.
+struct NET_EXPORT_PRIVATE QuicConnectionStats {
+ QuicConnectionStats();
+ ~QuicConnectionStats();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConnectionStats& s);
+
+ uint64 bytes_sent; // Includes retransmissions, fec.
+ uint32 packets_sent;
+ uint64 stream_bytes_sent; // non-retransmitted bytes sent in a stream frame.
+ uint32 packets_discarded; // Packets serialized and discarded before sending.
+
+ // These include version negotiation and public reset packets, which do not
+ // have sequence numbers or frame data.
+ uint64 bytes_received; // Includes duplicate data for a stream, fec.
+ uint32 packets_received; // Includes packets which were not processable.
+ uint32 packets_processed; // Excludes packets which were not processable.
+ uint64 stream_bytes_received; // Bytes received in a stream frame.
+
+ uint64 bytes_retransmitted;
+ uint32 packets_retransmitted;
+
+ uint64 bytes_spuriously_retransmitted;
+ uint32 packets_spuriously_retransmitted;
+ // Number of packets abandoned as lost by the loss detection algorithm.
+ uint32 packets_lost;
+ uint32 slowstart_packets_lost; // Number of packets lost exiting slow start.
+
+ uint32 packets_revived;
+ uint32 packets_dropped; // Duplicate or less than least unacked.
+ uint32 crypto_retransmit_count;
+ // Count of times the loss detection alarm fired. At least one packet should
+ // be lost when the alarm fires.
+ uint32 loss_timeout_count;
+ uint32 tlp_count;
+ uint32 rto_count; // Count of times the rto timer fired.
+ uint32 spurious_rto_count;
+
+ uint32 min_rtt_us; // Minimum RTT in microseconds.
+ uint32 srtt_us; // Smoothed RTT in microseconds.
+ uint32 max_packet_size; // In bytes.
+ uint64 estimated_bandwidth; // In bytes per second.
+ uint32 congestion_window; // In bytes
+ uint32 slow_start_threshold; // In bytes
+
+ // Reordering stats for received packets.
+ // Number of packets received out of sequence number order.
+ uint32 packets_reordered;
+ // Maximum reordering observed in sequence space.
+ uint32 max_sequence_reordering;
+ // Maximum reordering observed in microseconds
+ uint32 max_time_reordering_us;
+
+ // The following stats are used only in TcpCubicSender.
+ // The number of loss events from TCP's perspective. Each loss event includes
+ // one or more lost packets.
+ uint32 tcp_loss_events;
+ // Total amount of cwnd increase by TCPCubic in congestion avoidance.
+ uint32 cwnd_increase_congestion_avoidance;
+ // Total amount of cwnd increase by TCPCubic in cubic mode.
+ uint32 cwnd_increase_cubic_mode;
+
+ // Creation time, as reported by the QuicClock.
+ QuicTime connection_creation_time;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CONNECTION_STATS_H_
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
new file mode 100644
index 0000000..2869513
--- /dev/null
+++ b/net/quic/quic_connection_test.cc
@@ -0,0 +1,4017 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_connection.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "net/base/net_errors.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_framer_peer.h"
+#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::map;
+using std::vector;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::ContainerEq;
+using testing::Contains;
+using testing::DoAll;
+using testing::InSequence;
+using testing::InvokeWithoutArgs;
+using testing::NiceMock;
+using testing::Ref;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+const char data1[] = "foo";
+const char data2[] = "bar";
+
+const bool kFin = true;
+const bool kEntropyFlag = true;
+
+const QuicPacketEntropyHash kTestEntropyHash = 76;
+
+const int kDefaultRetransmissionTimeMs = 500;
+
+class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
+ public:
+ explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback)
+ : feedback_(feedback) {
+ }
+
+ bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* congestion_feedback) {
+ if (feedback_ == nullptr) {
+ return false;
+ }
+ *congestion_feedback = *feedback_;
+ return true;
+ }
+
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
+
+ private:
+ QuicCongestionFeedbackFrame* feedback_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm);
+};
+
+// TaggingEncrypter appends kTagSize bytes of |tag| to the end of each message.
+class TaggingEncrypter : public QuicEncrypter {
+ public:
+ explicit TaggingEncrypter(uint8 tag)
+ : tag_(tag) {
+ }
+
+ virtual ~TaggingEncrypter() {}
+
+ // QuicEncrypter interface.
+ virtual bool SetKey(StringPiece key) OVERRIDE { return true; }
+ virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE {
+ return true;
+ }
+
+ virtual bool Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) OVERRIDE {
+ memcpy(output, plaintext.data(), plaintext.size());
+ output += plaintext.size();
+ memset(output, tag_, kTagSize);
+ return true;
+ }
+
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) OVERRIDE {
+ const size_t len = plaintext.size() + kTagSize;
+ uint8* buffer = new uint8[len];
+ Encrypt(StringPiece(), associated_data, plaintext, buffer);
+ return new QuicData(reinterpret_cast<char*>(buffer), len, true);
+ }
+
+ virtual size_t GetKeySize() const OVERRIDE { return 0; }
+ virtual size_t GetNoncePrefixSize() const OVERRIDE { return 0; }
+
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE {
+ return ciphertext_size - kTagSize;
+ }
+
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE {
+ return plaintext_size + kTagSize;
+ }
+
+ virtual StringPiece GetKey() const OVERRIDE {
+ return StringPiece();
+ }
+
+ virtual StringPiece GetNoncePrefix() const OVERRIDE {
+ return StringPiece();
+ }
+
+ private:
+ enum {
+ kTagSize = 12,
+ };
+
+ const uint8 tag_;
+
+ DISALLOW_COPY_AND_ASSIGN(TaggingEncrypter);
+};
+
+// TaggingDecrypter ensures that the final kTagSize bytes of the message all
+// have the same value and then removes them.
+class TaggingDecrypter : public QuicDecrypter {
+ public:
+ virtual ~TaggingDecrypter() {}
+
+ // QuicDecrypter interface
+ virtual bool SetKey(StringPiece key) OVERRIDE { return true; }
+ virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE {
+ return true;
+ }
+
+ virtual bool Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) OVERRIDE {
+ if (ciphertext.size() < kTagSize) {
+ return false;
+ }
+ if (!CheckTag(ciphertext, GetTag(ciphertext))) {
+ return false;
+ }
+ *output_length = ciphertext.size() - kTagSize;
+ memcpy(output, ciphertext.data(), *output_length);
+ return true;
+ }
+
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) OVERRIDE {
+ if (ciphertext.size() < kTagSize) {
+ return nullptr;
+ }
+ if (!CheckTag(ciphertext, GetTag(ciphertext))) {
+ return nullptr;
+ }
+ const size_t len = ciphertext.size() - kTagSize;
+ uint8* buf = new uint8[len];
+ memcpy(buf, ciphertext.data(), len);
+ return new QuicData(reinterpret_cast<char*>(buf), len,
+ true /* owns buffer */);
+ }
+
+ virtual StringPiece GetKey() const OVERRIDE { return StringPiece(); }
+ virtual StringPiece GetNoncePrefix() const OVERRIDE { return StringPiece(); }
+
+ protected:
+ virtual uint8 GetTag(StringPiece ciphertext) {
+ return ciphertext.data()[ciphertext.size()-1];
+ }
+
+ private:
+ enum {
+ kTagSize = 12,
+ };
+
+ bool CheckTag(StringPiece ciphertext, uint8 tag) {
+ for (size_t i = ciphertext.size() - kTagSize; i < ciphertext.size(); i++) {
+ if (ciphertext.data()[i] != tag) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+// StringTaggingDecrypter ensures that the final kTagSize bytes of the message
+// match the expected value.
+class StrictTaggingDecrypter : public TaggingDecrypter {
+ public:
+ explicit StrictTaggingDecrypter(uint8 tag) : tag_(tag) {}
+ virtual ~StrictTaggingDecrypter() {}
+
+ // TaggingQuicDecrypter
+ virtual uint8 GetTag(StringPiece ciphertext) OVERRIDE {
+ return tag_;
+ }
+
+ private:
+ const uint8 tag_;
+};
+
+class TestConnectionHelper : public QuicConnectionHelperInterface {
+ public:
+ class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicAlarm::Delegate* delegate)
+ : QuicAlarm(delegate) {
+ }
+
+ virtual void SetImpl() OVERRIDE {}
+ virtual void CancelImpl() OVERRIDE {}
+ using QuicAlarm::Fire;
+ };
+
+ TestConnectionHelper(MockClock* clock, MockRandom* random_generator)
+ : clock_(clock),
+ random_generator_(random_generator) {
+ clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ // QuicConnectionHelperInterface
+ virtual const QuicClock* GetClock() const OVERRIDE {
+ return clock_;
+ }
+
+ virtual QuicRandom* GetRandomGenerator() OVERRIDE {
+ return random_generator_;
+ }
+
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE {
+ return new TestAlarm(delegate);
+ }
+
+ private:
+ MockClock* clock_;
+ MockRandom* random_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestConnectionHelper);
+};
+
+class TestPacketWriter : public QuicPacketWriter {
+ public:
+ explicit TestPacketWriter(QuicVersion version)
+ : version_(version),
+ framer_(SupportedVersions(version_)),
+ last_packet_size_(0),
+ write_blocked_(false),
+ block_on_next_write_(false),
+ is_write_blocked_data_buffered_(false),
+ final_bytes_of_last_packet_(0),
+ final_bytes_of_previous_packet_(0),
+ use_tagging_decrypter_(false),
+ packets_write_attempts_(0) {
+ }
+
+ // QuicPacketWriter interface
+ virtual WriteResult WritePacket(
+ const char* buffer, size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE {
+ QuicEncryptedPacket packet(buffer, buf_len);
+ ++packets_write_attempts_;
+
+ if (packet.length() >= sizeof(final_bytes_of_last_packet_)) {
+ final_bytes_of_previous_packet_ = final_bytes_of_last_packet_;
+ memcpy(&final_bytes_of_last_packet_, packet.data() + packet.length() - 4,
+ sizeof(final_bytes_of_last_packet_));
+ }
+
+ if (use_tagging_decrypter_) {
+ framer_.framer()->SetDecrypter(new TaggingDecrypter, ENCRYPTION_NONE);
+ }
+ EXPECT_TRUE(framer_.ProcessPacket(packet));
+ if (block_on_next_write_) {
+ write_blocked_ = true;
+ block_on_next_write_ = false;
+ }
+ if (IsWriteBlocked()) {
+ return WriteResult(WRITE_STATUS_BLOCKED, -1);
+ }
+ last_packet_size_ = packet.length();
+ return WriteResult(WRITE_STATUS_OK, last_packet_size_);
+ }
+
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE {
+ return is_write_blocked_data_buffered_;
+ }
+
+ virtual bool IsWriteBlocked() const OVERRIDE { return write_blocked_; }
+
+ virtual void SetWritable() OVERRIDE { write_blocked_ = false; }
+
+ void BlockOnNextWrite() { block_on_next_write_ = true; }
+
+ const QuicPacketHeader& header() { return framer_.header(); }
+
+ size_t frame_count() const { return framer_.num_frames(); }
+
+ const vector<QuicAckFrame>& ack_frames() const {
+ return framer_.ack_frames();
+ }
+
+ const vector<QuicCongestionFeedbackFrame>& feedback_frames() const {
+ return framer_.feedback_frames();
+ }
+
+ const vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return framer_.stop_waiting_frames();
+ }
+
+ const vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return framer_.connection_close_frames();
+ }
+
+ const vector<QuicStreamFrame>& stream_frames() const {
+ return framer_.stream_frames();
+ }
+
+ const vector<QuicPingFrame>& ping_frames() const {
+ return framer_.ping_frames();
+ }
+
+ size_t last_packet_size() {
+ return last_packet_size_;
+ }
+
+ const QuicVersionNegotiationPacket* version_negotiation_packet() {
+ return framer_.version_negotiation_packet();
+ }
+
+ void set_is_write_blocked_data_buffered(bool buffered) {
+ is_write_blocked_data_buffered_ = buffered;
+ }
+
+ void set_is_server(bool is_server) {
+ // We invert is_server here, because the framer needs to parse packets
+ // we send.
+ QuicFramerPeer::SetIsServer(framer_.framer(), !is_server);
+ }
+
+ // final_bytes_of_last_packet_ returns the last four bytes of the previous
+ // packet as a little-endian, uint32. This is intended to be used with a
+ // TaggingEncrypter so that tests can determine which encrypter was used for
+ // a given packet.
+ uint32 final_bytes_of_last_packet() { return final_bytes_of_last_packet_; }
+
+ // Returns the final bytes of the second to last packet.
+ uint32 final_bytes_of_previous_packet() {
+ return final_bytes_of_previous_packet_;
+ }
+
+ void use_tagging_decrypter() {
+ use_tagging_decrypter_ = true;
+ }
+
+ uint32 packets_write_attempts() { return packets_write_attempts_; }
+
+ void Reset() { framer_.Reset(); }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
+ private:
+ QuicVersion version_;
+ SimpleQuicFramer framer_;
+ size_t last_packet_size_;
+ bool write_blocked_;
+ bool block_on_next_write_;
+ bool is_write_blocked_data_buffered_;
+ uint32 final_bytes_of_last_packet_;
+ uint32 final_bytes_of_previous_packet_;
+ bool use_tagging_decrypter_;
+ uint32 packets_write_attempts_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPacketWriter);
+};
+
+class TestConnection : public QuicConnection {
+ public:
+ TestConnection(QuicConnectionId connection_id,
+ IPEndPoint address,
+ TestConnectionHelper* helper,
+ const PacketWriterFactory& factory,
+ bool is_server,
+ QuicVersion version)
+ : QuicConnection(connection_id,
+ address,
+ helper,
+ factory,
+ /* owns_writer= */ false,
+ is_server,
+ SupportedVersions(version)) {
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(this), 0);
+ writer()->set_is_server(is_server);
+ }
+
+ void SendAck() {
+ QuicConnectionPeer::SendAck(this);
+ }
+
+ void SetReceiveAlgorithm(TestReceiveAlgorithm* receive_algorithm) {
+ QuicConnectionPeer::SetReceiveAlgorithm(this, receive_algorithm);
+ }
+
+ void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
+ QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
+ }
+
+ void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) {
+ QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicConnectionPeer::GetSentPacketManager(this), loss_algorithm);
+ }
+
+ void SendPacket(EncryptionLevel level,
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacket* packet,
+ QuicPacketEntropyHash entropy_hash,
+ HasRetransmittableData retransmittable) {
+ RetransmittableFrames* retransmittable_frames =
+ retransmittable == HAS_RETRANSMITTABLE_DATA
+ ? new RetransmittableFrames()
+ : nullptr;
+ OnSerializedPacket(
+ SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packet, entropy_hash, retransmittable_frames));
+ }
+
+ QuicConsumedData SendStreamDataWithString(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ return SendStreamDataWithStringHelper(id, data, offset, fin,
+ MAY_FEC_PROTECT, delegate);
+ }
+
+ QuicConsumedData SendStreamDataWithStringWithFec(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ return SendStreamDataWithStringHelper(id, data, offset, fin,
+ MUST_FEC_PROTECT, delegate);
+ }
+
+ QuicConsumedData SendStreamDataWithStringHelper(
+ QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ IOVector data_iov;
+ if (!data.empty()) {
+ data_iov.Append(const_cast<char*>(data.data()), data.size());
+ }
+ return QuicConnection::SendStreamData(id, data_iov, offset, fin,
+ fec_protection, delegate);
+ }
+
+ QuicConsumedData SendStreamData3() {
+ return SendStreamDataWithString(kClientDataStreamId1, "food", 0, !kFin,
+ nullptr);
+ }
+
+ QuicConsumedData SendStreamData3WithFec() {
+ return SendStreamDataWithStringWithFec(kClientDataStreamId1, "food", 0,
+ !kFin, nullptr);
+ }
+
+ QuicConsumedData SendStreamData5() {
+ return SendStreamDataWithString(kClientDataStreamId2, "food2", 0, !kFin,
+ nullptr);
+ }
+
+ QuicConsumedData SendStreamData5WithFec() {
+ return SendStreamDataWithStringWithFec(kClientDataStreamId2, "food2", 0,
+ !kFin, nullptr);
+ }
+ // Ensures the connection can write stream data before writing.
+ QuicConsumedData EnsureWritableAndSendStreamData5() {
+ EXPECT_TRUE(CanWriteStreamData());
+ return SendStreamData5();
+ }
+
+ // The crypto stream has special semantics so that it is not blocked by a
+ // congestion window limitation, and also so that it gets put into a separate
+ // packet (so that it is easier to reason about a crypto frame not being
+ // split needlessly across packet boundaries). As a result, we have separate
+ // tests for some cases for this stream.
+ QuicConsumedData SendCryptoStreamData() {
+ return SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, nullptr);
+ }
+
+ bool is_server() {
+ return QuicConnectionPeer::IsServer(this);
+ }
+
+ void set_version(QuicVersion version) {
+ QuicConnectionPeer::GetFramer(this)->set_version(version);
+ }
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions);
+ writer()->SetSupportedVersions(versions);
+ }
+
+ void set_is_server(bool is_server) {
+ writer()->set_is_server(is_server);
+ QuicConnectionPeer::SetIsServer(this, is_server);
+ }
+
+ TestConnectionHelper::TestAlarm* GetAckAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetAckAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetPingAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetPingAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetResumeWritesAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetResumeWritesAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetRetransmissionAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetRetransmissionAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetSendAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetSendAlarm(this));
+ }
+
+ TestConnectionHelper::TestAlarm* GetTimeoutAlarm() {
+ return reinterpret_cast<TestConnectionHelper::TestAlarm*>(
+ QuicConnectionPeer::GetTimeoutAlarm(this));
+ }
+
+ using QuicConnection::SelectMutualVersion;
+
+ private:
+ TestPacketWriter* writer() {
+ return static_cast<TestPacketWriter*>(QuicConnection::writer());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TestConnection);
+};
+
+// Used for testing packets revived from FEC packets.
+class FecQuicConnectionDebugVisitor
+ : public QuicConnectionDebugVisitor {
+ public:
+ virtual void OnRevivedPacket(const QuicPacketHeader& header,
+ StringPiece data) OVERRIDE {
+ revived_header_ = header;
+ }
+
+ // Public accessor method.
+ QuicPacketHeader revived_header() const {
+ return revived_header_;
+ }
+
+ private:
+ QuicPacketHeader revived_header_;
+};
+
+class MockPacketWriterFactory : public QuicConnection::PacketWriterFactory {
+ public:
+ MockPacketWriterFactory(QuicPacketWriter* writer) {
+ ON_CALL(*this, Create(_)).WillByDefault(Return(writer));
+ }
+ virtual ~MockPacketWriterFactory() {}
+
+ MOCK_CONST_METHOD1(Create, QuicPacketWriter*(QuicConnection* connection));
+};
+
+class QuicConnectionTest : public ::testing::TestWithParam<QuicVersion> {
+ protected:
+ QuicConnectionTest()
+ : connection_id_(42),
+ framer_(SupportedVersions(version()), QuicTime::Zero(), false),
+ peer_creator_(connection_id_, &framer_, &random_generator_),
+ send_algorithm_(new StrictMock<MockSendAlgorithm>),
+ loss_algorithm_(new MockLossAlgorithm()),
+ helper_(new TestConnectionHelper(&clock_, &random_generator_)),
+ writer_(new TestPacketWriter(version())),
+ factory_(writer_.get()),
+ connection_(connection_id_, IPEndPoint(), helper_.get(),
+ factory_, false, version()),
+ frame1_(1, false, 0, MakeIOVector(data1)),
+ frame2_(1, false, 3, MakeIOVector(data2)),
+ sequence_number_length_(PACKET_6BYTE_SEQUENCE_NUMBER),
+ connection_id_length_(PACKET_8BYTE_CONNECTION_ID) {
+ connection_.set_visitor(&visitor_);
+ connection_.SetSendAlgorithm(send_algorithm_);
+ connection_.SetLossAlgorithm(loss_algorithm_);
+ framer_.set_received_entropy_calculator(&entropy_calculator_);
+ // Simplify tests by not sending feedback unless specifically configured.
+ SetFeedback(nullptr);
+ EXPECT_CALL(
+ *send_algorithm_, TimeUntilSend(_, _, _)).WillRepeatedly(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*receive_algorithm_,
+ RecordIncomingPacket(_, _, _)).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize));
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
+ EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate())
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, HasPendingHandshake()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(AnyNumber());
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false));
+ EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber());
+
+ EXPECT_CALL(*loss_algorithm_, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
+ }
+
+ QuicVersion version() {
+ return GetParam();
+ }
+
+ QuicAckFrame* outgoing_ack() {
+ outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_));
+ return outgoing_ack_.get();
+ }
+
+ QuicStopWaitingFrame* stop_waiting() {
+ stop_waiting_.reset(
+ QuicConnectionPeer::CreateStopWaitingFrame(&connection_));
+ return stop_waiting_.get();
+ }
+
+ QuicPacketSequenceNumber least_unacked() {
+ if (writer_->stop_waiting_frames().empty()) {
+ return 0;
+ }
+ return writer_->stop_waiting_frames()[0].least_unacked;
+ }
+
+ void use_tagging_decrypter() {
+ writer_->use_tagging_decrypter();
+ }
+
+ void ProcessPacket(QuicPacketSequenceNumber number) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacket(number, 0, !kEntropyFlag);
+ }
+
+ QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) {
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_,
+ connection_.is_server());
+ SerializedPacket serialized_packet =
+ peer_creator_.SerializeAllFrames(frames);
+ scoped_ptr<QuicPacket> packet(serialized_packet.packet);
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE,
+ serialized_packet.sequence_number, *packet));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ return serialized_packet.entropy_hash;
+ }
+
+ size_t ProcessDataPacket(QuicPacketSequenceNumber number,
+ QuicFecGroupNumber fec_group,
+ bool entropy_flag) {
+ return ProcessDataPacketAtLevel(number, fec_group, entropy_flag,
+ ENCRYPTION_NONE);
+ }
+
+ size_t ProcessDataPacketAtLevel(QuicPacketSequenceNumber number,
+ QuicFecGroupNumber fec_group,
+ bool entropy_flag,
+ EncryptionLevel level) {
+ scoped_ptr<QuicPacket> packet(ConstructDataPacket(number, fec_group,
+ entropy_flag));
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
+ level, number, *packet));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ return encrypted->length();
+ }
+
+ void ProcessPingPacket(QuicPacketSequenceNumber number) {
+ scoped_ptr<QuicPacket> packet(ConstructPingPacket(number));
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
+ ENCRYPTION_NONE, number, *packet));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ }
+
+ void ProcessClosePacket(QuicPacketSequenceNumber number,
+ QuicFecGroupNumber fec_group) {
+ scoped_ptr<QuicPacket> packet(ConstructClosePacket(number, fec_group));
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
+ ENCRYPTION_NONE, number, *packet));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ }
+
+ size_t ProcessFecProtectedPacket(QuicPacketSequenceNumber number,
+ bool expect_revival, bool entropy_flag) {
+ if (expect_revival) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ }
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1).
+ RetiresOnSaturation();
+ return ProcessDataPacket(number, 1, entropy_flag);
+ }
+
+ // Processes an FEC packet that covers the packets that would have been
+ // received.
+ size_t ProcessFecPacket(QuicPacketSequenceNumber number,
+ QuicPacketSequenceNumber min_protected_packet,
+ bool expect_revival,
+ bool entropy_flag,
+ QuicPacket* packet) {
+ if (expect_revival) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ }
+
+ // Construct the decrypted data packet so we can compute the correct
+ // redundancy. If |packet| has been provided then use that, otherwise
+ // construct a default data packet.
+ scoped_ptr<QuicPacket> data_packet;
+ if (packet) {
+ data_packet.reset(packet);
+ } else {
+ data_packet.reset(ConstructDataPacket(number, 1, !kEntropyFlag));
+ }
+
+ header_.public_header.connection_id = connection_id_;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.public_header.sequence_number_length = sequence_number_length_;
+ header_.public_header.connection_id_length = connection_id_length_;
+ header_.packet_sequence_number = number;
+ header_.entropy_flag = entropy_flag;
+ header_.fec_flag = true;
+ header_.is_in_fec_group = IN_FEC_GROUP;
+ header_.fec_group = min_protected_packet;
+ QuicFecData fec_data;
+ fec_data.fec_group = header_.fec_group;
+
+ // Since all data packets in this test have the same payload, the
+ // redundancy is either equal to that payload or the xor of that payload
+ // with itself, depending on the number of packets.
+ if (((number - min_protected_packet) % 2) == 0) {
+ for (size_t i = GetStartOfFecProtectedData(
+ header_.public_header.connection_id_length,
+ header_.public_header.version_flag,
+ header_.public_header.sequence_number_length);
+ i < data_packet->length(); ++i) {
+ data_packet->mutable_data()[i] ^= data_packet->data()[i];
+ }
+ }
+ fec_data.redundancy = data_packet->FecProtectedData();
+
+ scoped_ptr<QuicPacket> fec_packet(
+ framer_.BuildFecPacket(header_, fec_data).packet);
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE, number, *fec_packet));
+
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ return encrypted->length();
+ }
+
+ QuicByteCount SendStreamDataToPeer(QuicStreamId id,
+ StringPiece data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicPacketSequenceNumber* last_packet) {
+ QuicByteCount packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<3>(&packet_size), Return(true)));
+ connection_.SendStreamDataWithString(id, data, offset, fin, nullptr);
+ if (last_packet != nullptr) {
+ *last_packet =
+ QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number();
+ }
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ return packet_size;
+ }
+
+ void SendAckPacketToPeer() {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendAck();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ }
+
+ QuicPacketEntropyHash ProcessAckPacket(QuicAckFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
+ }
+
+ QuicPacketEntropyHash ProcessStopWaitingPacket(QuicStopWaitingFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
+ }
+
+ QuicPacketEntropyHash ProcessGoAwayPacket(QuicGoAwayFrame* frame) {
+ return ProcessFramePacket(QuicFrame(frame));
+ }
+
+ bool IsMissing(QuicPacketSequenceNumber number) {
+ return IsAwaitingPacket(*outgoing_ack(), number);
+ }
+
+ QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number,
+ QuicFecGroupNumber fec_group,
+ bool entropy_flag) {
+ header_.public_header.connection_id = connection_id_;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.public_header.sequence_number_length = sequence_number_length_;
+ header_.public_header.connection_id_length = connection_id_length_;
+ header_.entropy_flag = entropy_flag;
+ header_.fec_flag = false;
+ header_.packet_sequence_number = number;
+ header_.is_in_fec_group = fec_group == 0u ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ header_.fec_group = fec_group;
+
+ QuicFrames frames;
+ QuicFrame frame(&frame1_);
+ frames.push_back(frame);
+ QuicPacket* packet =
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
+ EXPECT_TRUE(packet != nullptr);
+ return packet;
+ }
+
+ QuicPacket* ConstructPingPacket(QuicPacketSequenceNumber number) {
+ header_.public_header.connection_id = connection_id_;
+ header_.packet_sequence_number = number;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+ header_.is_in_fec_group = NOT_IN_FEC_GROUP;
+ header_.fec_group = 0;
+
+ QuicPingFrame ping;
+
+ QuicFrames frames;
+ QuicFrame frame(&ping);
+ frames.push_back(frame);
+ QuicPacket* packet =
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
+ EXPECT_TRUE(packet != nullptr);
+ return packet;
+ }
+
+ QuicPacket* ConstructClosePacket(QuicPacketSequenceNumber number,
+ QuicFecGroupNumber fec_group) {
+ header_.public_header.connection_id = connection_id_;
+ header_.packet_sequence_number = number;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+ header_.is_in_fec_group = fec_group == 0u ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ header_.fec_group = fec_group;
+
+ QuicConnectionCloseFrame qccf;
+ qccf.error_code = QUIC_PEER_GOING_AWAY;
+
+ QuicFrames frames;
+ QuicFrame frame(&qccf);
+ frames.push_back(frame);
+ QuicPacket* packet =
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet;
+ EXPECT_TRUE(packet != nullptr);
+ return packet;
+ }
+
+ void SetFeedback(QuicCongestionFeedbackFrame* feedback) {
+ receive_algorithm_ = new TestReceiveAlgorithm(feedback);
+ connection_.SetReceiveAlgorithm(receive_algorithm_);
+ }
+
+ QuicTime::Delta DefaultRetransmissionTime() {
+ return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ }
+
+ QuicTime::Delta DefaultDelayedAckTime() {
+ return QuicTime::Delta::FromMilliseconds(kMaxDelayedAckTimeMs);
+ }
+
+ // Initialize a frame acknowledging all packets up to largest_observed.
+ const QuicAckFrame InitAckFrame(QuicPacketSequenceNumber largest_observed) {
+ QuicAckFrame frame(MakeAckFrame(largest_observed));
+ if (largest_observed > 0) {
+ frame.entropy_hash =
+ QuicConnectionPeer::GetSentEntropyHash(&connection_,
+ largest_observed);
+ }
+ return frame;
+ }
+
+ const QuicStopWaitingFrame InitStopWaitingFrame(
+ QuicPacketSequenceNumber least_unacked) {
+ QuicStopWaitingFrame frame;
+ frame.least_unacked = least_unacked;
+ return frame;
+ }
+
+ // Explicitly nack a packet.
+ void NackPacket(QuicPacketSequenceNumber missing, QuicAckFrame* frame) {
+ frame->missing_packets.insert(missing);
+ frame->entropy_hash ^=
+ QuicConnectionPeer::PacketEntropy(&connection_, missing);
+ }
+
+ // Undo nacking a packet within the frame.
+ void AckPacket(QuicPacketSequenceNumber arrived, QuicAckFrame* frame) {
+ EXPECT_THAT(frame->missing_packets, Contains(arrived));
+ frame->missing_packets.erase(arrived);
+ frame->entropy_hash ^=
+ QuicConnectionPeer::PacketEntropy(&connection_, arrived);
+ }
+
+ void TriggerConnectionClose() {
+ // Send an erroneous packet to close the connection.
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
+ // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
+ // packet call to the visitor.
+ ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+ nullptr);
+ }
+
+ void BlockOnNextWrite() {
+ writer_->BlockOnNextWrite();
+ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1));
+ }
+
+ void CongestionBlockWrites() {
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::FromSeconds(1)));
+ }
+
+ void CongestionUnblockWrites() {
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ }
+
+ QuicConnectionId connection_id_;
+ QuicFramer framer_;
+ QuicPacketCreator peer_creator_;
+ MockEntropyCalculator entropy_calculator_;
+
+ MockSendAlgorithm* send_algorithm_;
+ MockLossAlgorithm* loss_algorithm_;
+ TestReceiveAlgorithm* receive_algorithm_;
+ MockClock clock_;
+ MockRandom random_generator_;
+ scoped_ptr<TestConnectionHelper> helper_;
+ scoped_ptr<TestPacketWriter> writer_;
+ NiceMock<MockPacketWriterFactory> factory_;
+ TestConnection connection_;
+ StrictMock<MockConnectionVisitor> visitor_;
+
+ QuicPacketHeader header_;
+ QuicStreamFrame frame1_;
+ QuicStreamFrame frame2_;
+ scoped_ptr<QuicAckFrame> outgoing_ack_;
+ scoped_ptr<QuicStopWaitingFrame> stop_waiting_;
+ QuicSequenceNumberLength sequence_number_length_;
+ QuicConnectionIdLength connection_id_length_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicConnectionTest);
+};
+
+// Run all end to end tests with all supported versions.
+INSTANTIATE_TEST_CASE_P(SupportedVersion,
+ QuicConnectionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicConnectionTest, PacketsInOrder) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(1);
+ EXPECT_EQ(1u, outgoing_ack()->largest_observed);
+ EXPECT_EQ(0u, outgoing_ack()->missing_packets.size());
+
+ ProcessPacket(2);
+ EXPECT_EQ(2u, outgoing_ack()->largest_observed);
+ EXPECT_EQ(0u, outgoing_ack()->missing_packets.size());
+
+ ProcessPacket(3);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_EQ(0u, outgoing_ack()->missing_packets.size());
+}
+
+TEST_P(QuicConnectionTest, PacketsOutOfOrder) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(2);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_FALSE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(1);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_FALSE(IsMissing(2));
+ EXPECT_FALSE(IsMissing(1));
+}
+
+TEST_P(QuicConnectionTest, DuplicatePacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ // Send packet 3 again, but do not set the expectation that
+ // the visitor OnStreamFrames() will be called.
+ ProcessDataPacket(3, 0, !kEntropyFlag);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+}
+
+TEST_P(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_TRUE(IsMissing(2));
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(2);
+ EXPECT_EQ(3u, outgoing_ack()->largest_observed);
+ EXPECT_TRUE(IsMissing(1));
+
+ ProcessPacket(5);
+ EXPECT_EQ(5u, outgoing_ack()->largest_observed);
+ EXPECT_TRUE(IsMissing(1));
+ EXPECT_TRUE(IsMissing(4));
+
+ // Pretend at this point the client has gotten acks for 2 and 3 and 1 is a
+ // packet the peer will not retransmit. It indicates this by sending 'least
+ // awaiting' is 4. The connection should then realize 1 will not be
+ // retransmitted, and will remove it from the missing list.
+ peer_creator_.set_sequence_number(5);
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Force an ack to be sent.
+ SendAckPacketToPeer();
+ EXPECT_TRUE(IsMissing(4));
+}
+
+TEST_P(QuicConnectionTest, RejectPacketTooFarOut) {
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
+ // Call ProcessDataPacket rather than ProcessPacket, as we should not get a
+ // packet call to the visitor.
+ ProcessDataPacket(6000, 0, !kEntropyFlag);
+ EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+ nullptr);
+}
+
+TEST_P(QuicConnectionTest, RejectUnencryptedStreamData) {
+ // Process an unencrypted packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_UNENCRYPTED_STREAM_DATA,
+ false));
+ ProcessDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) ==
+ nullptr);
+ const vector<QuicConnectionCloseFrame>& connection_close_frames =
+ writer_->connection_close_frames();
+ EXPECT_EQ(1u, connection_close_frames.size());
+ EXPECT_EQ(QUIC_UNENCRYPTED_STREAM_DATA,
+ connection_close_frames[0].error_code);
+}
+
+TEST_P(QuicConnectionTest, TruncatedAck) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketSequenceNumber num_packets = 256 * 2 + 1;
+ for (QuicPacketSequenceNumber i = 0; i < num_packets; ++i) {
+ SendStreamDataToPeer(3, "foo", i * 3, !kFin, nullptr);
+ }
+
+ QuicAckFrame frame = InitAckFrame(num_packets);
+ SequenceNumberSet lost_packets;
+ // Create an ack with 256 nacks, none adjacent to one another.
+ for (QuicPacketSequenceNumber i = 1; i <= 256; ++i) {
+ NackPacket(i * 2, &frame);
+ if (i < 256) { // Last packet is nacked, but not lost.
+ lost_packets.insert(i * 2);
+ }
+ }
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(511)).WillOnce(testing::Return(0));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+
+ const QuicSentPacketManager& sent_packet_manager =
+ connection_.sent_packet_manager();
+ // A truncated ack will not have the true largest observed.
+ EXPECT_GT(num_packets, sent_packet_manager.largest_observed());
+
+ AckPacket(192, &frame);
+
+ // Removing one missing packet allows us to ack 192 and one more range, but
+ // 192 has already been declared lost, so it doesn't register as an ack.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+ EXPECT_EQ(num_packets, sent_packet_manager.largest_observed());
+}
+
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(1);
+ // Delay sending, then queue up an ack.
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(1)));
+ QuicConnectionPeer::SendAck(&connection_);
+
+ // Process an ack with a least unacked of the received ack.
+ // This causes an ack to be sent when TimeUntilSend returns 0.
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ // Skip a packet and then record an ack.
+ peer_creator_.set_sequence_number(2);
+ QuicAckFrame frame = InitAckFrame(0);
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, OutOfOrderReceiptCausesAckSend) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessPacket(3);
+ // Should ack immediately since we have missing packets.
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+
+ ProcessPacket(2);
+ // Should ack immediately since we have missing packets.
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+
+ ProcessPacket(1);
+ // Should ack immediately, since this fills the last hole.
+ EXPECT_EQ(3u, writer_->packets_write_attempts());
+
+ ProcessPacket(4);
+ // Should not cause an ack.
+ EXPECT_EQ(3u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, AckReceiptCausesAckSend) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicPacketSequenceNumber original;
+ QuicByteCount packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&original), SaveArg<3>(&packet_size),
+ Return(true)));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+ QuicAckFrame frame = InitAckFrame(original);
+ NackPacket(original, &frame);
+ // First nack triggers early retransmit.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicPacketSequenceNumber retransmission;
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _))
+ .WillOnce(DoAll(SaveArg<2>(&retransmission), Return(true)));
+
+ ProcessAckPacket(&frame);
+
+ QuicAckFrame frame2 = InitAckFrame(retransmission);
+ NackPacket(original, &frame2);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ ProcessAckPacket(&frame2);
+
+ // Now if the peer sends an ack which still reports the retransmitted packet
+ // as missing, that will bundle an ack with data after two acks in a row
+ // indicate the high water mark needs to be raised.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _,
+ HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 3, !kFin, nullptr);
+ // No ack sent.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+
+ // No more packet loss for the rest of the test.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
+ ProcessAckPacket(&frame2);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _,
+ HAS_RETRANSMITTABLE_DATA));
+ connection_.SendStreamDataWithString(3, "foo", 3, !kFin, nullptr);
+ // Ack bundled.
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+
+ // But an ack with no missing packets will not send an ack.
+ AckPacket(original, &frame2);
+ ProcessAckPacket(&frame2);
+ ProcessAckPacket(&frame2);
+}
+
+TEST_P(QuicConnectionTest, 20AcksCausesAckSend) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr);
+
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ // But an ack with no missing packets will not send an ack.
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
+ for (int i = 0; i < 20; ++i) {
+ EXPECT_FALSE(ack_alarm->IsSet());
+ ProcessAckPacket(&frame);
+ }
+ EXPECT_TRUE(ack_alarm->IsSet());
+}
+
+TEST_P(QuicConnectionTest, LeastUnackedLower) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr);
+ SendStreamDataToPeer(1, "bar", 3, !kFin, nullptr);
+ SendStreamDataToPeer(1, "eep", 6, !kFin, nullptr);
+
+ // Start out saying the least unacked is 2.
+ peer_creator_.set_sequence_number(5);
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(2);
+ ProcessStopWaitingPacket(&frame);
+
+ // Change it to 1, but lower the sequence number to fake out-of-order packets.
+ // This should be fine.
+ peer_creator_.set_sequence_number(1);
+ // The scheduler will not process out of order acks, but all packet processing
+ // causes the connection to try to write.
+ EXPECT_CALL(visitor_, OnCanWrite());
+ QuicStopWaitingFrame frame2 = InitStopWaitingFrame(1);
+ ProcessStopWaitingPacket(&frame2);
+
+ // Now claim it's one, but set the ordering so it was sent "after" the first
+ // one. This should cause a connection error.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ peer_creator_.set_sequence_number(7);
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_STOP_WAITING_DATA, false));
+ QuicStopWaitingFrame frame3 = InitStopWaitingFrame(1);
+ ProcessStopWaitingPacket(&frame3);
+}
+
+TEST_P(QuicConnectionTest, LargestObservedLower) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr);
+ SendStreamDataToPeer(1, "bar", 3, !kFin, nullptr);
+ SendStreamDataToPeer(1, "eep", 6, !kFin, nullptr);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+
+ // Start out saying the largest observed is 2.
+ QuicAckFrame frame1 = InitAckFrame(1);
+ QuicAckFrame frame2 = InitAckFrame(2);
+ ProcessAckPacket(&frame2);
+
+ // Now change it to 1, and it should cause a connection error.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
+ ProcessAckPacket(&frame1);
+}
+
+TEST_P(QuicConnectionTest, AckUnsentData) {
+ // Ack a packet which has not been sent.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_INVALID_ACK_DATA, false));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ QuicAckFrame frame(MakeAckFrame(1));
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(0);
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, AckAll) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+
+ peer_creator_.set_sequence_number(1);
+ QuicAckFrame frame1 = InitAckFrame(0);
+ ProcessAckPacket(&frame1);
+}
+
+TEST_P(QuicConnectionTest, SendingDifferentSequenceNumberLengthsBandwidth) {
+ QuicPacketSequenceNumber last_packet;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet);
+ EXPECT_EQ(1u, last_packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256));
+
+ SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet);
+ EXPECT_EQ(2u, last_packet);
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ // The 1 packet lag is due to the sequence number length being recalculated in
+ // QuicConnection after a packet is sent.
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256));
+
+ SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet);
+ EXPECT_EQ(3u, last_packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256 * 256));
+
+ SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet);
+ EXPECT_EQ(4u, last_packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize * 256 * 256 * 256 * 256));
+
+ SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet);
+ EXPECT_EQ(5u, last_packet);
+ EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+}
+
+// TODO(ianswett): Re-enable this test by finding a good way to test different
+// sequence number lengths without sending packets with giant gaps.
+TEST_P(QuicConnectionTest,
+ DISABLED_SendingDifferentSequenceNumberLengthsUnackedDelta) {
+ QuicPacketSequenceNumber last_packet;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet);
+ EXPECT_EQ(1u, last_packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ creator->set_sequence_number(100);
+
+ SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet);
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ creator->set_sequence_number(100 * 256);
+
+ SendStreamDataToPeer(1, "foo", 6, !kFin, &last_packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ creator->set_sequence_number(100 * 256 * 256);
+
+ SendStreamDataToPeer(1, "bar", 9, !kFin, &last_packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+
+ creator->set_sequence_number(100 * 256 * 256 * 256);
+
+ SendStreamDataToPeer(1, "foo", 12, !kFin, &last_packet);
+ EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
+ creator->next_sequence_number_length());
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ writer_->header().public_header.sequence_number_length);
+}
+
+TEST_P(QuicConnectionTest, BasicSending) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketSequenceNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
+ EXPECT_EQ(1u, last_packet);
+ SendAckPacketToPeer(); // Packet 2
+
+ EXPECT_EQ(1u, least_unacked());
+
+ SendAckPacketToPeer(); // Packet 3
+ EXPECT_EQ(1u, least_unacked());
+
+ SendStreamDataToPeer(1, "bar", 3, !kFin, &last_packet); // Packet 4
+ EXPECT_EQ(4u, last_packet);
+ SendAckPacketToPeer(); // Packet 5
+ EXPECT_EQ(1u, least_unacked());
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+
+ // Peer acks up to packet 3.
+ QuicAckFrame frame = InitAckFrame(3);
+ ProcessAckPacket(&frame);
+ SendAckPacketToPeer(); // Packet 6
+
+ // As soon as we've acked one, we skip ack packets 2 and 3 and note lack of
+ // ack for 4.
+ EXPECT_EQ(4u, least_unacked());
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+
+ // Peer acks up to packet 4, the last packet.
+ QuicAckFrame frame2 = InitAckFrame(6);
+ ProcessAckPacket(&frame2); // Acks don't instigate acks.
+
+ // Verify that we did not send an ack.
+ EXPECT_EQ(6u, writer_->header().packet_sequence_number);
+
+ // So the last ack has not changed.
+ EXPECT_EQ(4u, least_unacked());
+
+ // If we force an ack, we shouldn't change our retransmit state.
+ SendAckPacketToPeer(); // Packet 7
+ EXPECT_EQ(7u, least_unacked());
+
+ // But if we send more data it should.
+ SendStreamDataToPeer(1, "eep", 6, !kFin, &last_packet); // Packet 8
+ EXPECT_EQ(8u, last_packet);
+ SendAckPacketToPeer(); // Packet 9
+ EXPECT_EQ(7u, least_unacked());
+}
+
+TEST_P(QuicConnectionTest, FECSending) {
+ // All packets carry version info till version is negotiated.
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ size_t payload_length;
+ // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
+ // packet length. The size of the offset field in a stream frame is 0 for
+ // offset 0, and 2 for non-zero offsets up through 64K. Increase
+ // max_packet_length by 2 so that subsequent packets containing subsequent
+ // stream frames with non-zero offets will fit within the packet length.
+ size_t length = 2 + GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ IN_FEC_GROUP, &payload_length);
+ creator->set_max_packet_length(length);
+
+ // Send 4 protected data packets, which should also trigger 1 FEC packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(5);
+ // The first stream frame will have 2 fewer overhead bytes than the other 3.
+ const string payload(payload_length * 4 + 2, 'a');
+ connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, nullptr);
+ // Expect the FEC group to be closed after SendStreamDataWithString.
+ EXPECT_FALSE(creator->IsFecGroupOpen());
+ EXPECT_FALSE(creator->IsFecProtected());
+}
+
+TEST_P(QuicConnectionTest, FECQueueing) {
+ // All packets carry version info till version is negotiated.
+ size_t payload_length;
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ size_t length = GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ IN_FEC_GROUP, &payload_length);
+ creator->set_max_packet_length(length);
+ EXPECT_TRUE(creator->IsFecEnabled());
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ BlockOnNextWrite();
+ const string payload(payload_length, 'a');
+ connection_.SendStreamDataWithStringWithFec(1, payload, 0, !kFin, nullptr);
+ EXPECT_FALSE(creator->IsFecGroupOpen());
+ EXPECT_FALSE(creator->IsFecProtected());
+ // Expect the first data packet and the fec packet to be queued.
+ EXPECT_EQ(2u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, AbandonFECFromCongestionWindow) {
+ EXPECT_TRUE(QuicConnectionPeer::GetPacketCreator(
+ &connection_)->IsFecEnabled());
+
+ // 1 Data and 1 FEC packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, nullptr);
+
+ const QuicTime::Delta retransmission_time =
+ QuicTime::Delta::FromMilliseconds(5000);
+ clock_.AdvanceTime(retransmission_time);
+
+ // Abandon FEC packet and data packet.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ EXPECT_CALL(visitor_, OnCanWrite());
+ connection_.OnRetransmissionTimeout();
+}
+
+TEST_P(QuicConnectionTest, DontAbandonAckedFEC) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_TRUE(QuicConnectionPeer::GetPacketCreator(
+ &connection_)->IsFecEnabled());
+
+ // 1 Data and 1 FEC packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, nullptr);
+ // Send some more data afterwards to ensure early retransmit doesn't trigger.
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, nullptr);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, nullptr);
+
+ QuicAckFrame ack_fec = InitAckFrame(2);
+ // Data packet missing.
+ // TODO(ianswett): Note that this is not a sensible ack, since if the FEC was
+ // received, it would cause the covered packet to be acked as well.
+ NackPacket(1, &ack_fec);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack_fec);
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+
+ // Don't abandon the acked FEC packet, but it will abandon 2 the subsequent
+ // FEC packets.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+ connection_.GetRetransmissionAlarm()->Fire();
+}
+
+TEST_P(QuicConnectionTest, AbandonAllFEC) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_TRUE(QuicConnectionPeer::GetPacketCreator(
+ &connection_)->IsFecEnabled());
+
+ // 1 Data and 1 FEC packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(6);
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 0, !kFin, nullptr);
+ // Send some more data afterwards to ensure early retransmit doesn't trigger.
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 3, !kFin, nullptr);
+ // Advance the time so not all the FEC packets are abandoned.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ connection_.SendStreamDataWithStringWithFec(3, "foo", 6, !kFin, nullptr);
+
+ QuicAckFrame ack_fec = InitAckFrame(5);
+ // Ack all data packets, but no fec packets.
+ NackPacket(2, &ack_fec);
+ NackPacket(4, &ack_fec);
+
+ // Lose the first FEC packet and ack the three data packets.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack_fec);
+
+ clock_.AdvanceTime(DefaultRetransmissionTime().Subtract(
+ QuicTime::Delta::FromMilliseconds(1)));
+
+ // Abandon all packets
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(false));
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Ensure the alarm is not set since all packets have been abandoned.
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, FramePacking) {
+ CongestionBlockWrites();
+
+ // Send an ack and two stream frames in 1 packet by queueing them.
+ connection_.SendAck();
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData3)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData5))));
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ CongestionUnblockWrites();
+ connection_.GetSendAlarm()->Fire();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's an ack and two stream frames from
+ // two different streams.
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingNonCryptoThenCrypto) {
+ CongestionBlockWrites();
+
+ // Send an ack and two stream frames (one non-crypto, then one crypto) in 2
+ // packets by queueing them.
+ connection_.SendAck();
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData3)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendCryptoStreamData))));
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ CongestionUnblockWrites();
+ connection_.GetSendAlarm()->Fire();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's the crypto stream frame.
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
+ CongestionBlockWrites();
+
+ // Send an ack and two stream frames (one crypto, then one non-crypto) in 2
+ // packets by queueing them.
+ connection_.SendAck();
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendCryptoStreamData)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData3))));
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
+ CongestionUnblockWrites();
+ connection_.GetSendAlarm()->Fire();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's the stream frame from stream 3.
+ EXPECT_EQ(1u, writer_->frame_count());
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingFEC) {
+ EXPECT_TRUE(QuicConnectionPeer::GetPacketCreator(
+ &connection_)->IsFecEnabled());
+
+ CongestionBlockWrites();
+
+ // Queue an ack and two stream frames. Ack gets flushed when FEC is turned on
+ // for sending protected data; two stream frames are packing in 1 packet.
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData3WithFec)),
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_, &TestConnection::SendStreamData5WithFec))));
+ connection_.SendAck();
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+ CongestionUnblockWrites();
+ connection_.GetSendAlarm()->Fire();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's in an fec group.
+ EXPECT_EQ(2u, writer_->header().fec_group);
+ EXPECT_EQ(0u, writer_->frame_count());
+}
+
+TEST_P(QuicConnectionTest, FramePackingAckResponse) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Process a data packet to queue up a pending ack.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacket(1, 1, kEntropyFlag);
+
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData3)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData5))));
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+
+ // Process an ack to cause the visitor's OnCanWrite to be invoked.
+ peer_creator_.set_sequence_number(2);
+ QuicAckFrame ack_one = InitAckFrame(0);
+ ProcessAckPacket(&ack_one);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's an ack and two stream frames from
+ // two different streams.
+ EXPECT_EQ(4u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ ASSERT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
+}
+
+TEST_P(QuicConnectionTest, FramePackingSendv) {
+ // Send data in 1 packet by writing multiple blocks in a single iovector
+ // using writev.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+
+ char data[] = "ABCD";
+ IOVector data_iov;
+ data_iov.AppendNoCoalesce(data, 2);
+ data_iov.AppendNoCoalesce(data + 2, 2);
+ connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, nullptr);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure multiple iovector blocks have
+ // been packed into a single stream frame from one stream.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ QuicStreamFrame frame = writer_->stream_frames()[0];
+ EXPECT_EQ(1u, frame.stream_id);
+ EXPECT_EQ("ABCD", string(static_cast<char*>
+ (frame.data.iovec()[0].iov_base),
+ (frame.data.iovec()[0].iov_len)));
+}
+
+TEST_P(QuicConnectionTest, FramePackingSendvQueued) {
+ // Try to send two stream frames in 1 packet by using writev.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+
+ BlockOnNextWrite();
+ char data[] = "ABCD";
+ IOVector data_iov;
+ data_iov.AppendNoCoalesce(data, 2);
+ data_iov.AppendNoCoalesce(data + 2, 2);
+ connection_.SendStreamData(1, data_iov, 0, !kFin, MAY_FEC_PROTECT, nullptr);
+
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ // Unblock the writes and actually send.
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+
+ // Parse the last packet and ensure it's one stream frame from one stream.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id);
+}
+
+TEST_P(QuicConnectionTest, SendingZeroBytes) {
+ // Send a zero byte write with a fin using writev.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ IOVector empty_iov;
+ connection_.SendStreamData(1, empty_iov, 0, kFin, MAY_FEC_PROTECT, nullptr);
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_FALSE(connection_.HasQueuedData());
+
+ // Parse the last packet and ensure it's one stream frame from one stream.
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(1u, writer_->stream_frames()[0].stream_id);
+ EXPECT_TRUE(writer_->stream_frames()[0].fin);
+}
+
+TEST_P(QuicConnectionTest, OnCanWrite) {
+ // Visitor's OnCanWrite will send data, but will have more pending writes.
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData3)),
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendStreamData5))));
+ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillOnce(Return(true));
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+
+ connection_.OnCanWrite();
+
+ // Parse the last packet and ensure it's the two stream frames from
+ // two different streams.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_EQ(2u, writer_->stream_frames().size());
+ EXPECT_EQ(kClientDataStreamId1, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(kClientDataStreamId2, writer_->stream_frames()[1].stream_id);
+}
+
+TEST_P(QuicConnectionTest, RetransmitOnNack) {
+ QuicPacketSequenceNumber last_packet;
+ QuicByteCount second_packet_size;
+ SendStreamDataToPeer(3, "foo", 0, !kFin, &last_packet); // Packet 1
+ second_packet_size =
+ SendStreamDataToPeer(3, "foos", 3, !kFin, &last_packet); // Packet 2
+ SendStreamDataToPeer(3, "fooos", 7, !kFin, &last_packet); // Packet 3
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Don't lose a packet on an ack, and nothing is retransmitted.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack_one = InitAckFrame(1);
+ ProcessAckPacket(&ack_one);
+
+ // Lose a packet and ensure it triggers retransmission.
+ QuicAckFrame nack_two = InitAckFrame(3);
+ NackPacket(2, &nack_two);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, second_packet_size - kQuicVersionSize, _)).
+ Times(1);
+ ProcessAckPacket(&nack_two);
+}
+
+TEST_P(QuicConnectionTest, DiscardRetransmit) {
+ QuicPacketSequenceNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
+ SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2
+ SendStreamDataToPeer(1, "fooos", 7, !kFin, &last_packet); // Packet 3
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Instigate a loss with an ack.
+ QuicAckFrame nack_two = InitAckFrame(3);
+ NackPacket(2, &nack_two);
+ // The first nack should trigger a fast retransmission, but we'll be
+ // write blocked, so the packet will be queued.
+ BlockOnNextWrite();
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&nack_two);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Now, ack the previous transmission.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ QuicAckFrame ack_all = InitAckFrame(3);
+ ProcessAckPacket(&ack_all);
+
+ // Unblock the socket and attempt to send the queued packets. However,
+ // since the previous transmission has been acked, we will not
+ // send the retransmission.
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, _, _)).Times(0);
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, RetransmitNackedLargestObserved) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketSequenceNumber largest_observed;
+ QuicByteCount packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&largest_observed), SaveArg<3>(&packet_size),
+ Return(true)));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+
+ QuicAckFrame frame = InitAckFrame(1);
+ NackPacket(largest_observed, &frame);
+ // The first nack should retransmit the largest observed packet.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, packet_size - kQuicVersionSize, _));
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, QueueAfterTwoRTOs) {
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(3, "foo", i * 3, !kFin, nullptr);
+ }
+
+ // Block the congestion window and ensure they're queued.
+ BlockOnNextWrite();
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ // Only one packet should be retransmitted.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ // Unblock the congestion window.
+ writer_->SetWritable();
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
+ 2 * DefaultRetransmissionTime().ToMicroseconds()));
+ // Retransmit already retransmitted packets event though the sequence number
+ // greater than the largest observed.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(10);
+ connection_.GetRetransmissionAlarm()->Fire();
+ connection_.OnCanWrite();
+}
+
+TEST_P(QuicConnectionTest, WriteBlockedThenSent) {
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, RetransmitWriteBlockedAckedOriginalThenSent) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ // Simulate the retransmission alarm firing.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(_));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Ack the sent packet before the callback returns, which happens in
+ // rare circumstances with write blocked sockets.
+ QuicAckFrame ack = InitAckFrame(1);
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
+ ProcessAckPacket(&ack);
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ // There is now a pending packet, but with no retransmittable frames.
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.sent_packet_manager().HasRetransmittableFrames(2));
+}
+
+TEST_P(QuicConnectionTest, AlarmsWhenWriteBlocked) {
+ // Block the connection.
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+
+ // Set the send and resumption alarms. Fire the alarms and ensure they don't
+ // attempt to write.
+ connection_.GetResumeWritesAlarm()->Set(clock_.ApproximateNow());
+ connection_.GetSendAlarm()->Set(clock_.ApproximateNow());
+ connection_.GetResumeWritesAlarm()->Fire();
+ connection_.GetSendAlarm()->Fire();
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, NoLimitPacketsPerNack) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ int offset = 0;
+ // Send packets 1 to 15.
+ for (int i = 0; i < 15; ++i) {
+ SendStreamDataToPeer(1, "foo", offset, !kFin, nullptr);
+ offset += 3;
+ }
+
+ // Ack 15, nack 1-14.
+ SequenceNumberSet lost_packets;
+ QuicAckFrame nack = InitAckFrame(15);
+ for (int i = 1; i < 15; ++i) {
+ NackPacket(i, &nack);
+ lost_packets.insert(i);
+ }
+
+ // 14 packets have been NACK'd and lost. In TCP cubic, PRR limits
+ // the retransmission rate in the case of burst losses.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(14);
+ ProcessAckPacket(&nack);
+}
+
+// Test sending multiple acks from the connection to the session.
+TEST_P(QuicConnectionTest, MultipleAcks) {
+ QuicPacketSequenceNumber last_packet;
+ SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1
+ EXPECT_EQ(1u, last_packet);
+ SendStreamDataToPeer(3, "foo", 0, !kFin, &last_packet); // Packet 2
+ EXPECT_EQ(2u, last_packet);
+ SendAckPacketToPeer(); // Packet 3
+ SendStreamDataToPeer(5, "foo", 0, !kFin, &last_packet); // Packet 4
+ EXPECT_EQ(4u, last_packet);
+ SendStreamDataToPeer(1, "foo", 3, !kFin, &last_packet); // Packet 5
+ EXPECT_EQ(5u, last_packet);
+ SendStreamDataToPeer(3, "foo", 3, !kFin, &last_packet); // Packet 6
+ EXPECT_EQ(6u, last_packet);
+
+ // Client will ack packets 1, 2, [!3], 4, 5.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame1 = InitAckFrame(5);
+ NackPacket(3, &frame1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessAckPacket(&frame1);
+
+ // Now the client implicitly acks 3, and explicitly acks 6.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame2 = InitAckFrame(6);
+ ProcessAckPacket(&frame2);
+}
+
+TEST_P(QuicConnectionTest, DontLatchUnackedPacket) {
+ SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr); // Packet 1;
+ // From now on, we send acks, so the send algorithm won't mark them pending.
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(false));
+ SendAckPacketToPeer(); // Packet 2
+
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1);
+ ProcessAckPacket(&frame);
+
+ // Verify that our internal state has least-unacked as 2, because we're still
+ // waiting for a potential ack for 2.
+
+ EXPECT_EQ(2u, stop_waiting()->least_unacked);
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(2);
+ ProcessAckPacket(&frame);
+ EXPECT_EQ(3u, stop_waiting()->least_unacked);
+
+ // When we send an ack, we make sure our least-unacked makes sense. In this
+ // case since we're not waiting on an ack for 2 and all packets are acked, we
+ // set it to 3.
+ SendAckPacketToPeer(); // Packet 3
+ // Least_unacked remains at 3 until another ack is received.
+ EXPECT_EQ(3u, stop_waiting()->least_unacked);
+ // Check that the outgoing ack had its sequence number as least_unacked.
+ EXPECT_EQ(3u, least_unacked());
+
+ // Ack the ack, which updates the rtt and raises the least unacked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(3);
+ ProcessAckPacket(&frame);
+
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
+ SendStreamDataToPeer(1, "bar", 3, false, nullptr); // Packet 4
+ EXPECT_EQ(4u, stop_waiting()->least_unacked);
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(false));
+ SendAckPacketToPeer(); // Packet 5
+ EXPECT_EQ(4u, least_unacked());
+
+ // Send two data packets at the end, and ensure if the last one is acked,
+ // the least unacked is raised above the ack packets.
+ ON_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillByDefault(Return(true));
+ SendStreamDataToPeer(1, "bar", 6, false, nullptr); // Packet 6
+ SendStreamDataToPeer(1, "bar", 9, false, nullptr); // Packet 7
+
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ frame = InitAckFrame(7);
+ NackPacket(5, &frame);
+ NackPacket(6, &frame);
+ ProcessAckPacket(&frame);
+
+ EXPECT_EQ(6u, stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Don't send missing packet 1.
+ ProcessFecPacket(2, 1, true, !kEntropyFlag, nullptr);
+ // Entropy flag should be false, so entropy should be 0.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingSeqNumLengths) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Set up a debug visitor to the connection.
+ FecQuicConnectionDebugVisitor* fec_visitor =
+ new FecQuicConnectionDebugVisitor();
+ connection_.set_debug_visitor(fec_visitor);
+
+ QuicPacketSequenceNumber fec_packet = 0;
+ QuicSequenceNumberLength lengths[] = {PACKET_6BYTE_SEQUENCE_NUMBER,
+ PACKET_4BYTE_SEQUENCE_NUMBER,
+ PACKET_2BYTE_SEQUENCE_NUMBER,
+ PACKET_1BYTE_SEQUENCE_NUMBER};
+ // For each sequence number length size, revive a packet and check sequence
+ // number length in the revived packet.
+ for (size_t i = 0; i < arraysize(lengths); ++i) {
+ // Set sequence_number_length_ (for data and FEC packets).
+ sequence_number_length_ = lengths[i];
+ fec_packet += 2;
+ // Don't send missing packet, but send fec packet right after it.
+ ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, nullptr);
+ // Sequence number length in the revived header should be the same as
+ // in the original data/fec packet headers.
+ EXPECT_EQ(sequence_number_length_, fec_visitor->revived_header().
+ public_header.sequence_number_length);
+ }
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketWithVaryingConnectionIdLengths) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Set up a debug visitor to the connection.
+ FecQuicConnectionDebugVisitor* fec_visitor =
+ new FecQuicConnectionDebugVisitor();
+ connection_.set_debug_visitor(fec_visitor);
+
+ QuicPacketSequenceNumber fec_packet = 0;
+ QuicConnectionIdLength lengths[] = {PACKET_8BYTE_CONNECTION_ID,
+ PACKET_4BYTE_CONNECTION_ID,
+ PACKET_1BYTE_CONNECTION_ID,
+ PACKET_0BYTE_CONNECTION_ID};
+ // For each connection id length size, revive a packet and check connection
+ // id length in the revived packet.
+ for (size_t i = 0; i < arraysize(lengths); ++i) {
+ // Set connection id length (for data and FEC packets).
+ connection_id_length_ = lengths[i];
+ fec_packet += 2;
+ // Don't send missing packet, but send fec packet right after it.
+ ProcessFecPacket(fec_packet, fec_packet - 1, true, !kEntropyFlag, nullptr);
+ // Connection id length in the revived header should be the same as
+ // in the original data/fec packet headers.
+ EXPECT_EQ(connection_id_length_,
+ fec_visitor->revived_header().public_header.connection_id_length);
+ }
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessFecProtectedPacket(1, false, kEntropyFlag);
+ // Don't send missing packet 2.
+ ProcessFecPacket(3, 1, true, !kEntropyFlag, nullptr);
+ // Entropy flag should be true, so entropy should not be 0.
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessFecProtectedPacket(1, false, !kEntropyFlag);
+ // Don't send missing packet 2.
+ ProcessFecProtectedPacket(3, false, !kEntropyFlag);
+ ProcessFecPacket(4, 1, true, kEntropyFlag, nullptr);
+ // Ensure QUIC no longer revives entropy for lost packets.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 4));
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Don't send missing packet 1.
+ ProcessFecPacket(3, 1, false, !kEntropyFlag, nullptr);
+ // Out of order.
+ ProcessFecProtectedPacket(2, true, !kEntropyFlag);
+ // Entropy flag should be false, so entropy should be 0.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+}
+
+TEST_P(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ ProcessFecProtectedPacket(1, false, !kEntropyFlag);
+ // Don't send missing packet 2.
+ ProcessFecPacket(6, 1, false, kEntropyFlag, nullptr);
+ ProcessFecProtectedPacket(3, false, kEntropyFlag);
+ ProcessFecProtectedPacket(4, false, kEntropyFlag);
+ ProcessFecProtectedPacket(5, true, !kEntropyFlag);
+ // Ensure entropy is not revived for the missing packet.
+ EXPECT_EQ(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 2));
+ EXPECT_NE(0u, QuicConnectionPeer::ReceivedEntropyHash(&connection_, 3));
+}
+
+TEST_P(QuicConnectionTest, TLP) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicConnectionPeer::GetSentPacketManager(&connection_), 1);
+
+ SendStreamDataToPeer(3, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(1u, stop_waiting()->least_unacked);
+ QuicTime retransmission_time =
+ connection_.GetRetransmissionAlarm()->deadline();
+ EXPECT_NE(QuicTime::Zero(), retransmission_time);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ // Simulate the retransmission alarm firing and sending a tlp,
+ // so send algorithm's OnRetransmissionTimeout is not called.
+ clock_.AdvanceTime(retransmission_time.Subtract(clock_.Now()));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, RTO) {
+ QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
+ DefaultRetransmissionTime());
+ SendStreamDataToPeer(3, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(1u, stop_waiting()->least_unacked);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ EXPECT_EQ(default_retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, stop_waiting()->least_unacked);
+}
+
+TEST_P(QuicConnectionTest, RTOWithSameEncryptionLevel) {
+ QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
+ DefaultRetransmissionTime());
+ use_tagging_decrypter();
+
+ // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
+ // the end of the packet. We can test this to check which encrypter was used.
+ connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
+ SendStreamDataToPeer(3, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+
+ connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ SendStreamDataToPeer(3, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+
+ EXPECT_EQ(default_retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+ {
+ InSequence s;
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 3, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 4, _, _));
+ }
+
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Packet should have been sent with ENCRYPTION_NONE.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet());
+
+ // Packet should have been sent with ENCRYPTION_INITIAL.
+ EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet());
+}
+
+TEST_P(QuicConnectionTest, SendHandshakeMessages) {
+ use_tagging_decrypter();
+ // A TaggingEncrypter puts kTagSize copies of the given byte (0x01 here) at
+ // the end of the packet. We can test this to check which encrypter was used.
+ connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
+
+ // Attempt to send a handshake message and have the socket block.
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillRepeatedly(
+ testing::Return(QuicTime::Delta::Zero()));
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ // The packet should be serialized, but not queued.
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Switch to the new encrypter.
+ connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+
+ // Now become writeable and flush the packets.
+ writer_->SetWritable();
+ EXPECT_CALL(visitor_, OnCanWrite());
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+
+ // Verify that the handshake packet went out at the null encryption.
+ EXPECT_EQ(0x01010101u, writer_->final_bytes_of_last_packet());
+}
+
+TEST_P(QuicConnectionTest,
+ DropRetransmitsForNullEncryptedPacketAfterForwardSecure) {
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
+ QuicPacketSequenceNumber sequence_number;
+ SendStreamDataToPeer(3, "foo", 0, !kFin, &sequence_number);
+
+ // Simulate the retransmission alarm firing and the socket blocking.
+ BlockOnNextWrite();
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Go forward secure.
+ connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE,
+ new TaggingEncrypter(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ connection_.NeuterUnencryptedPackets();
+
+ EXPECT_EQ(QuicTime::Zero(),
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Unblock the socket and ensure that no packets are sent.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+}
+
+TEST_P(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) {
+ use_tagging_decrypter();
+ connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE);
+
+ SendStreamDataToPeer(1, "foo", 0, !kFin, nullptr);
+
+ connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02));
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+
+ SendStreamDataToPeer(2, "bar", 0, !kFin, nullptr);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+
+ connection_.RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION);
+}
+
+TEST_P(QuicConnectionTest, BufferNonDecryptablePackets) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ use_tagging_decrypter();
+
+ const uint8 tag = 0x07;
+ framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
+
+ // Process an encrypted packet which can not yet be decrypted
+ // which should result in the packet being buffered.
+ ProcessDataPacketAtLevel(1, 0, kEntropyFlag, ENCRYPTION_INITIAL);
+
+ // Transition to the new encryption state and process another
+ // encrypted packet which should result in the original packet being
+ // processed.
+ connection_.SetDecrypter(new StrictTaggingDecrypter(tag),
+ ENCRYPTION_INITIAL);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL);
+ connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(2);
+ ProcessDataPacketAtLevel(2, 0, kEntropyFlag, ENCRYPTION_INITIAL);
+
+ // Finally, process a third packet and note that we do not
+ // reprocess the buffered packet.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacketAtLevel(3, 0, kEntropyFlag, ENCRYPTION_INITIAL);
+}
+
+TEST_P(QuicConnectionTest, TestRetransmitOrder) {
+ QuicByteCount first_packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
+ DoAll(SaveArg<3>(&first_packet_size), Return(true)));
+
+ connection_.SendStreamDataWithString(3, "first_packet", 0, !kFin, nullptr);
+ QuicByteCount second_packet_size;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).WillOnce(
+ DoAll(SaveArg<3>(&second_packet_size), Return(true)));
+ connection_.SendStreamDataWithString(3, "second_packet", 12, !kFin, nullptr);
+ EXPECT_NE(first_packet_size, second_packet_size);
+ // Advance the clock by huge time to make sure packets will be retransmitted.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ {
+ InSequence s;
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, first_packet_size, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, second_packet_size, _));
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Advance again and expect the packets to be sent again in the same order.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(20));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ {
+ InSequence s;
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, first_packet_size, _));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, second_packet_size, _));
+ }
+ connection_.GetRetransmissionAlarm()->Fire();
+}
+
+TEST_P(QuicConnectionTest, RetransmissionCountCalculation) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketSequenceNumber original_sequence_number;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&original_sequence_number), Return(true)));
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+
+ EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
+ &connection_, original_sequence_number));
+ EXPECT_FALSE(QuicConnectionPeer::IsRetransmission(
+ &connection_, original_sequence_number));
+ // Force retransmission due to RTO.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ QuicPacketSequenceNumber rto_sequence_number;
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&rto_sequence_number), Return(true)));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
+ &connection_, original_sequence_number));
+ ASSERT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
+ &connection_, rto_sequence_number));
+ EXPECT_TRUE(QuicConnectionPeer::IsRetransmission(
+ &connection_, rto_sequence_number));
+ // Once by explicit nack.
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(rto_sequence_number);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicPacketSequenceNumber nack_sequence_number = 0;
+ // Ack packets might generate some other packets, which are not
+ // retransmissions. (More ack packets).
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<2>(&nack_sequence_number), Return(true)));
+ QuicAckFrame ack = InitAckFrame(rto_sequence_number);
+ // Nack the retransmitted packet.
+ NackPacket(original_sequence_number, &ack);
+ NackPacket(rto_sequence_number, &ack);
+ ProcessAckPacket(&ack);
+
+ ASSERT_NE(0u, nack_sequence_number);
+ EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission(
+ &connection_, rto_sequence_number));
+ ASSERT_TRUE(QuicConnectionPeer::IsSavedForRetransmission(
+ &connection_, nack_sequence_number));
+ EXPECT_TRUE(QuicConnectionPeer::IsRetransmission(
+ &connection_, nack_sequence_number));
+}
+
+TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) {
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ // Make sure that RTO is not started when the packet is queued.
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ // Test that RTO is started once we write to the socket.
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, DelayRTOWithAckReceipt) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(2);
+ connection_.SendStreamDataWithString(2, "foo", 0, !kFin, nullptr);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, nullptr);
+ QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm();
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_EQ(clock_.Now().Add(DefaultRetransmissionTime()),
+ retransmission_alarm->deadline());
+
+ // Advance the time right before the RTO, then receive an ack for the first
+ // packet to delay the RTO.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack = InitAckFrame(1);
+ ProcessAckPacket(&ack);
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_GT(retransmission_alarm->deadline(), clock_.Now());
+
+ // Move forward past the original RTO and ensure the RTO is still pending.
+ clock_.AdvanceTime(DefaultRetransmissionTime().Multiply(2));
+
+ // Ensure the second packet gets retransmitted when it finally fires.
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ EXPECT_LT(retransmission_alarm->deadline(), clock_.ApproximateNow());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ // Manually cancel the alarm to simulate a real test.
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // The new retransmitted sequence number should set the RTO to a larger value
+ // than previously.
+ EXPECT_TRUE(retransmission_alarm->IsSet());
+ QuicTime next_rto_time = retransmission_alarm->deadline();
+ QuicTime expected_rto_time =
+ connection_.sent_packet_manager().GetRetransmissionTime();
+ EXPECT_EQ(next_rto_time, expected_rto_time);
+}
+
+TEST_P(QuicConnectionTest, TestQueued) {
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+
+ // Unblock the writes and actually send.
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, CloseFecGroup) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Don't send missing packet 1.
+ // Don't send missing packet 2.
+ ProcessFecProtectedPacket(3, false, !kEntropyFlag);
+ // Don't send missing FEC packet 3.
+ ASSERT_EQ(1u, connection_.NumFecGroups());
+
+ // Now send non-fec protected ack packet and close the group.
+ peer_creator_.set_sequence_number(4);
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(5);
+ ProcessStopWaitingPacket(&frame);
+ ASSERT_EQ(0u, connection_.NumFecGroups());
+}
+
+TEST_P(QuicConnectionTest, NoQuicCongestionFeedbackFrame) {
+ SendAckPacketToPeer();
+ EXPECT_TRUE(writer_->feedback_frames().empty());
+}
+
+TEST_P(QuicConnectionTest, WithQuicCongestionFeedbackFrame) {
+ QuicCongestionFeedbackFrame info;
+ info.type = kTCP;
+ info.tcp.receive_window = 0x4030;
+
+ // After QUIC_VERSION_22, do not send TCP Congestion Feedback Frames anymore.
+ if (version() > QUIC_VERSION_22) {
+ SendAckPacketToPeer();
+ ASSERT_TRUE(writer_->feedback_frames().empty());
+ } else {
+ // Only SetFeedback in this case because SetFeedback will create a receive
+ // algorithm which is how the received_packet_manager checks if it should be
+ // creating TCP Congestion Feedback Frames.
+ SetFeedback(&info);
+ SendAckPacketToPeer();
+ ASSERT_FALSE(writer_->feedback_frames().empty());
+ ASSERT_EQ(kTCP, writer_->feedback_frames()[0].type);
+ }
+}
+
+TEST_P(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) {
+ SendAckPacketToPeer();
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+}
+
+TEST_P(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ SendAckPacketToPeer();
+ // Process an FEC packet, and revive the missing data packet
+ // but only contact the receive_algorithm once.
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _));
+ ProcessFecPacket(2, 1, true, !kEntropyFlag, nullptr);
+}
+
+TEST_P(QuicConnectionTest, InitialTimeout) {
+ if (!FLAGS_quic_unified_timeouts) {
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false));
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+
+ QuicTime default_timeout = clock_.ApproximateNow().Add(
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // Simulate the timeout alarm firing.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
+ connection_.GetTimeoutAlarm()->Fire();
+
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+
+ // SetFromConfig sets the initial timeouts before negotiation.
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ config.SetDefaults();
+ connection_.SetFromConfig(config);
+ // Subtract a second from the idle timeout on the client side.
+ QuicTime default_timeout = clock_.ApproximateNow().Add(
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1));
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false));
+ // Simulate the timeout alarm firing.
+ clock_.AdvanceTime(
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1));
+ connection_.GetTimeoutAlarm()->Fire();
+
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, OverallTimeout) {
+ // Use a shorter overall connection timeout than idle timeout for this test.
+ const QuicTime::Delta timeout = QuicTime::Delta::FromSeconds(5);
+ connection_.SetNetworkTimeouts(timeout, timeout);
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AnyNumber());
+
+ QuicTime overall_timeout = clock_.ApproximateNow().Add(timeout).Subtract(
+ QuicTime::Delta::FromSeconds(1));
+ EXPECT_EQ(overall_timeout, connection_.GetTimeoutAlarm()->deadline());
+ EXPECT_TRUE(connection_.connected());
+
+ // Send and ack new data 3 seconds later to lengthen the idle timeout.
+ SendStreamDataToPeer(1, "GET /", 0, kFin, nullptr);
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(3));
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Fire early to verify it wouldn't timeout yet.
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+
+ clock_.AdvanceTime(timeout.Subtract(QuicTime::Delta::FromSeconds(2)));
+
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_CONNECTION_OVERALL_TIMED_OUT, false));
+ // Simulate the timeout alarm firing.
+ connection_.GetTimeoutAlarm()->Fire();
+
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetResumeWritesAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ EXPECT_FALSE(connection_.GetSendAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, PingAfterSend) {
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(true));
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+
+ // Advance to 5ms, and send a packet to the peer, which will set
+ // the ping alarm.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
+ SendStreamDataToPeer(1, "GET /", 0, kFin, nullptr);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15)),
+ connection_.GetPingAlarm()->deadline());
+
+ // Now recevie and ACK of the previous packet, which will move the
+ // ping alarm forward.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ QuicAckFrame frame = InitAckFrame(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+ EXPECT_TRUE(connection_.GetPingAlarm()->IsSet());
+ // The ping timer is set slightly less than 15 seconds in the future, because
+ // of the 1s ping timer alarm granularity.
+ EXPECT_EQ(clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(15))
+ .Subtract(QuicTime::Delta::FromMilliseconds(5)),
+ connection_.GetPingAlarm()->deadline());
+
+ writer_->Reset();
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(15));
+ connection_.GetPingAlarm()->Fire();
+ EXPECT_EQ(1u, writer_->frame_count());
+ if (version() >= QUIC_VERSION_18) {
+ ASSERT_EQ(1u, writer_->ping_frames().size());
+ } else {
+ ASSERT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_EQ(kCryptoStreamId, writer_->stream_frames()[0].stream_id);
+ EXPECT_EQ(0u, writer_->stream_frames()[0].offset);
+ }
+ writer_->Reset();
+
+ EXPECT_CALL(visitor_, HasOpenDataStreams()).WillRepeatedly(Return(false));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ SendAckPacketToPeer();
+
+ EXPECT_FALSE(connection_.GetPingAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, TimeoutAfterSend) {
+ if (!FLAGS_quic_unified_timeouts) {
+ EXPECT_TRUE(connection_.connected());
+
+ QuicTime default_timeout = clock_.ApproximateNow().Add(
+ QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
+
+ // When we send a packet, the timeout will change to 5000 +
+ // kDefaultInitialTimeoutSecs.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+
+ // Send an ack so we don't set the retransmission alarm.
+ SendAckPacketToPeer();
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5000. The alarm will reregister.
+ clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(
+ kDefaultIdleTimeoutSecs * 1000000 - 5000));
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)),
+ connection_.GetTimeoutAlarm()->deadline());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5));
+ EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)),
+ clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+ return;
+ }
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ QuicConfig config;
+ config.SetDefaults();
+ connection_.SetFromConfig(config);
+
+ const QuicTime::Delta initial_idle_timeout =
+ QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs - 1);
+ const QuicTime::Delta five_ms = QuicTime::Delta::FromMilliseconds(5);
+ QuicTime default_timeout = clock_.ApproximateNow().Add(initial_idle_timeout);
+
+ // When we send a packet, the timeout will change to 5ms +
+ // kInitialIdleTimeoutSecs.
+ clock_.AdvanceTime(five_ms);
+
+ // Send an ack so we don't set the retransmission alarm.
+ SendAckPacketToPeer();
+ EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline());
+
+ // The original alarm will fire. We should not time out because we had a
+ // network event at t=5ms. The alarm will reregister.
+ clock_.AdvanceTime(initial_idle_timeout.Subtract(five_ms));
+ EXPECT_EQ(default_timeout, clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_EQ(default_timeout.Add(five_ms),
+ connection_.GetTimeoutAlarm()->deadline());
+
+ // This time, we should time out.
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_CONNECTION_TIMED_OUT, false));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ clock_.AdvanceTime(five_ms);
+ EXPECT_EQ(default_timeout.Add(five_ms), clock_.ApproximateNow());
+ connection_.GetTimeoutAlarm()->Fire();
+ EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet());
+ EXPECT_FALSE(connection_.connected());
+}
+
+TEST_P(QuicConnectionTest, SendScheduler) {
+ // Test that if we send a packet without delay, it is not queued.
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendPacket(
+ ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, SendSchedulerEAGAIN) {
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ BlockOnNextWrite();
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+ connection_.SendPacket(
+ ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, TestQueueLimitsOnSendStreamData) {
+ // All packets carry version info till version is negotiated.
+ size_t payload_length;
+ size_t length = GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NOT_IN_FEC_GROUP, &payload_length);
+ QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length(
+ length);
+
+ // Queue the first packet.
+ EXPECT_CALL(*send_algorithm_,
+ TimeUntilSend(_, _, _)).WillOnce(
+ testing::Return(QuicTime::Delta::FromMicroseconds(10)));
+ const string payload(payload_length, 'a');
+ EXPECT_EQ(0u, connection_.SendStreamDataWithString(3, payload, 0, !kFin,
+ nullptr).bytes_consumed);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+}
+
+TEST_P(QuicConnectionTest, LoopThroughSendingPackets) {
+ // All packets carry version info till version is negotiated.
+ size_t payload_length;
+ // GetPacketLengthForOneStream() assumes a stream offset of 0 in determining
+ // packet length. The size of the offset field in a stream frame is 0 for
+ // offset 0, and 2 for non-zero offsets up through 16K. Increase
+ // max_packet_length by 2 so that subsequent packets containing subsequent
+ // stream frames with non-zero offets will fit within the packet length.
+ size_t length = 2 + GetPacketLengthForOneStream(
+ connection_.version(), kIncludeVersion, PACKET_1BYTE_SEQUENCE_NUMBER,
+ NOT_IN_FEC_GROUP, &payload_length);
+ QuicConnectionPeer::GetPacketCreator(&connection_)->set_max_packet_length(
+ length);
+
+ // Queue the first packet.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(7);
+ // The first stream frame will have 2 fewer overhead bytes than the other six.
+ const string payload(payload_length * 7 + 2, 'a');
+ EXPECT_EQ(payload.size(),
+ connection_.SendStreamDataWithString(1, payload, 0, !kFin, nullptr)
+ .bytes_consumed);
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAck) {
+ QuicTime ack_time = clock_.ApproximateNow().Add(DefaultDelayedAckTime());
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ const uint8 tag = 0x07;
+ connection_.SetDecrypter(new StrictTaggingDecrypter(tag),
+ ENCRYPTION_INITIAL);
+ framer_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag));
+ // Process a packet from the non-crypto stream.
+ frame1_.stream_id = 3;
+
+ // The same as ProcessPacket(1) except that ENCRYPTION_INITIAL is used
+ // instead of ENCRYPTION_NONE.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ ProcessDataPacketAtLevel(1, 0, !kEntropyFlag, ENCRYPTION_INITIAL);
+
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendEarlyDelayedAckForCrypto) {
+ QuicTime ack_time = clock_.ApproximateNow();
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ // Process a packet from the crypto stream, which is frame1_'s default.
+ ProcessPacket(1);
+ // Check if delayed ack timer is running for the expected interval.
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_EQ(ack_time, connection_.GetAckAlarm()->deadline());
+ // Simulate delayed ack alarm firing.
+ connection_.GetAckAlarm()->Fire();
+ // Check that ack is sent and that delayed ack alarm is reset.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnSecondPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ ProcessPacket(2);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ EXPECT_EQ(2u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckForPing) {
+ if (version() < QUIC_VERSION_18) {
+ return;
+ }
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ ProcessPingPacket(1);
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoAckOnOldNacks) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ // Drop one packet, triggering a sequence of acks.
+ ProcessPacket(2);
+ size_t frames_per_ack = 2;
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+ ProcessPacket(3);
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+ ProcessPacket(4);
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+ ProcessPacket(5);
+ EXPECT_EQ(frames_per_ack, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ writer_->Reset();
+ // Now only set the timer on the 6th packet, instead of sending another ack.
+ ProcessPacket(6);
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_TRUE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0, !kFin,
+ nullptr);
+ // Check that ack is bundled with outgoing data and that delayed ack
+ // alarm is reset.
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, SendDelayedAckOnOutgoingCryptoPacket) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ connection_.SendStreamDataWithString(kCryptoStreamId, "foo", 0, !kFin,
+ nullptr);
+ // Check that ack is bundled with outgoing crypto data.
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BlockAndBufferOnFirstCHLOPacketOfTwo) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ connection_.SendStreamDataWithString(kCryptoStreamId, "foo", 0, !kFin,
+ nullptr);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_FALSE(connection_.HasQueuedData());
+ connection_.SendStreamDataWithString(kCryptoStreamId, "bar", 3, !kFin,
+ nullptr);
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ EXPECT_TRUE(connection_.HasQueuedData());
+}
+
+TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+ IgnoreResult(InvokeWithoutArgs(&connection_,
+ &TestConnection::SendCryptoStreamData)));
+ // Process a packet from the crypto stream, which is frame1_'s default.
+ // Receiving the CHLO as packet 2 first will cause the connection to
+ // immediately send an ack, due to the packet gap.
+ ProcessPacket(2);
+ // Check that ack is sent and that delayed ack alarm is reset.
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0, !kFin,
+ nullptr);
+ connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 3, !kFin,
+ nullptr);
+ // Ack the second packet, which will retransmit the first packet.
+ QuicAckFrame ack = InitAckFrame(2);
+ NackPacket(1, &ack);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
+ EXPECT_EQ(1u, writer_->frame_count());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ writer_->Reset();
+
+ // Now ack the retransmission, which will both raise the high water mark
+ // and see if there is more data to send.
+ ack = InitAckFrame(3);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&ack);
+
+ // Check that no packet is sent and the ack alarm isn't set.
+ EXPECT_EQ(0u, writer_->frame_count());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+ writer_->Reset();
+
+ // Send the same ack, but send both data and an ack together.
+ ack = InitAckFrame(3);
+ NackPacket(1, &ack);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+ IgnoreResult(InvokeWithoutArgs(
+ &connection_,
+ &TestConnection::EnsureWritableAndSendStreamData5)));
+ ProcessAckPacket(&ack);
+
+ // Check that ack is bundled with outgoing data and the delayed ack
+ // alarm is reset.
+ EXPECT_EQ(3u, writer_->frame_count());
+ EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+ EXPECT_FALSE(writer_->ack_frames().empty());
+ EXPECT_EQ(1u, writer_->stream_frames().size());
+ EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
+TEST_P(QuicConnectionTest, NoAckSentForClose) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessPacket(1);
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+ ProcessClosePacket(2, 0);
+}
+
+TEST_P(QuicConnectionTest, SendWhenDisconnected) {
+ EXPECT_TRUE(connection_.connected());
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, false));
+ connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false);
+ EXPECT_FALSE(connection_.connected());
+ EXPECT_FALSE(connection_.CanWriteStreamData());
+ QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 1, _, _)).Times(0);
+ connection_.SendPacket(
+ ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA);
+}
+
+TEST_P(QuicConnectionTest, PublicReset) {
+ QuicPublicResetPacket header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = true;
+ header.public_header.version_flag = false;
+ header.rejected_sequence_number = 10101;
+ scoped_ptr<QuicEncryptedPacket> packet(
+ framer_.BuildPublicResetPacket(header));
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PUBLIC_RESET, true));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet);
+}
+
+TEST_P(QuicConnectionTest, GoAway) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicGoAwayFrame goaway;
+ goaway.last_good_stream_id = 1;
+ goaway.error_code = QUIC_PEER_GOING_AWAY;
+ goaway.reason_phrase = "Going away.";
+ EXPECT_CALL(visitor_, OnGoAway(_));
+ ProcessGoAwayPacket(&goaway);
+}
+
+TEST_P(QuicConnectionTest, WindowUpdate) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrames(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+}
+
+TEST_P(QuicConnectionTest, Blocked) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrames(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+}
+
+TEST_P(QuicConnectionTest, InvalidPacket) {
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_PACKET_HEADER, false));
+ QuicEncryptedPacket encrypted(nullptr, 0);
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), encrypted);
+ // The connection close packet should have error details.
+ ASSERT_FALSE(writer_->connection_close_frames().empty());
+ EXPECT_EQ("Unable to read public flags.",
+ writer_->connection_close_frames()[0].error_details);
+}
+
+TEST_P(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) {
+ // Set the sequence number of the ack packet to be least unacked (4).
+ peer_creator_.set_sequence_number(3);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(4);
+ ProcessStopWaitingPacket(&frame);
+ EXPECT_TRUE(outgoing_ack()->missing_packets.empty());
+}
+
+TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculation) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessDataPacket(4, 1, kEntropyFlag);
+ ProcessDataPacket(3, 1, !kEntropyFlag);
+ ProcessDataPacket(7, 1, kEntropyFlag);
+ EXPECT_EQ(146u, outgoing_ack()->entropy_hash);
+}
+
+TEST_P(QuicConnectionTest, ReceivedEntropyHashCalculationHalfFEC) {
+ // FEC packets should not change the entropy hash calculation.
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessFecPacket(4, 1, false, kEntropyFlag, nullptr);
+ ProcessDataPacket(3, 3, !kEntropyFlag);
+ ProcessFecPacket(7, 3, false, kEntropyFlag, nullptr);
+ EXPECT_EQ(146u, outgoing_ack()->entropy_hash);
+}
+
+TEST_P(QuicConnectionTest, UpdateEntropyForReceivedPackets) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessDataPacket(5, 1, kEntropyFlag);
+ ProcessDataPacket(4, 1, !kEntropyFlag);
+ EXPECT_EQ(34u, outgoing_ack()->entropy_hash);
+ // Make 4th packet my least unacked, and update entropy for 2, 3 packets.
+ peer_creator_.set_sequence_number(5);
+ QuicPacketEntropyHash six_packet_entropy_hash = 0;
+ QuicPacketEntropyHash kRandomEntropyHash = 129u;
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(4);
+ frame.entropy_hash = kRandomEntropyHash;
+ if (ProcessStopWaitingPacket(&frame)) {
+ six_packet_entropy_hash = 1 << 6;
+ }
+
+ EXPECT_EQ((kRandomEntropyHash + (1 << 5) + six_packet_entropy_hash),
+ outgoing_ack()->entropy_hash);
+}
+
+TEST_P(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ ProcessDataPacket(1, 1, kEntropyFlag);
+ ProcessDataPacket(5, 1, !kEntropyFlag);
+ ProcessDataPacket(22, 1, kEntropyFlag);
+ EXPECT_EQ(66u, outgoing_ack()->entropy_hash);
+ peer_creator_.set_sequence_number(22);
+ QuicPacketEntropyHash kRandomEntropyHash = 85u;
+ // Current packet is the least unacked packet.
+ QuicPacketEntropyHash ack_entropy_hash;
+ QuicStopWaitingFrame frame = InitStopWaitingFrame(23);
+ frame.entropy_hash = kRandomEntropyHash;
+ ack_entropy_hash = ProcessStopWaitingPacket(&frame);
+ EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash),
+ outgoing_ack()->entropy_hash);
+ ProcessDataPacket(25, 1, kEntropyFlag);
+ EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash + (1 << (25 % 8))),
+ outgoing_ack()->entropy_hash);
+}
+
+TEST_P(QuicConnectionTest, EntropyCalculationForTruncatedAck) {
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(AtLeast(1));
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ QuicPacketEntropyHash entropy[51];
+ entropy[0] = 0;
+ for (int i = 1; i < 51; ++i) {
+ bool should_send = i % 10 != 1;
+ bool entropy_flag = (i & (i - 1)) != 0;
+ if (!should_send) {
+ entropy[i] = entropy[i - 1];
+ continue;
+ }
+ if (entropy_flag) {
+ entropy[i] = entropy[i - 1] ^ (1 << (i % 8));
+ } else {
+ entropy[i] = entropy[i - 1];
+ }
+ ProcessDataPacket(i, 1, entropy_flag);
+ }
+ for (int i = 1; i < 50; ++i) {
+ EXPECT_EQ(entropy[i], QuicConnectionPeer::ReceivedEntropyHash(
+ &connection_, i));
+ }
+}
+
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacket) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
+ framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = true;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.packet_sequence_number = 12;
+ header.fec_group = 0;
+
+ QuicFrames frames;
+ QuicFrame frame(&frame1_);
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
+
+ framer_.set_version(version());
+ connection_.set_is_server(true);
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ EXPECT_TRUE(writer_->version_negotiation_packet() != nullptr);
+
+ size_t num_versions = arraysize(kSupportedQuicVersions);
+ ASSERT_EQ(num_versions,
+ writer_->version_negotiation_packet()->versions.size());
+
+ // We expect all versions in kSupportedQuicVersions to be
+ // included in the packet.
+ for (size_t i = 0; i < num_versions; ++i) {
+ EXPECT_EQ(kSupportedQuicVersions[i],
+ writer_->version_negotiation_packet()->versions[i]);
+ }
+}
+
+TEST_P(QuicConnectionTest, ServerSendsVersionNegotiationPacketSocketBlocked) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
+ framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = true;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.packet_sequence_number = 12;
+ header.fec_group = 0;
+
+ QuicFrames frames;
+ QuicFrame frame(&frame1_);
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
+
+ framer_.set_version(version());
+ connection_.set_is_server(true);
+ BlockOnNextWrite();
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ EXPECT_EQ(0u, writer_->last_packet_size());
+ EXPECT_TRUE(connection_.HasQueuedData());
+
+ writer_->SetWritable();
+ connection_.OnCanWrite();
+ EXPECT_TRUE(writer_->version_negotiation_packet() != nullptr);
+
+ size_t num_versions = arraysize(kSupportedQuicVersions);
+ ASSERT_EQ(num_versions,
+ writer_->version_negotiation_packet()->versions.size());
+
+ // We expect all versions in kSupportedQuicVersions to be
+ // included in the packet.
+ for (size_t i = 0; i < num_versions; ++i) {
+ EXPECT_EQ(kSupportedQuicVersions[i],
+ writer_->version_negotiation_packet()->versions[i]);
+ }
+}
+
+TEST_P(QuicConnectionTest,
+ ServerSendsVersionNegotiationPacketSocketBlockedDataBuffered) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
+ framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED);
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = true;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.packet_sequence_number = 12;
+ header.fec_group = 0;
+
+ QuicFrames frames;
+ QuicFrame frame(&frame1_);
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
+
+ framer_.set_version(version());
+ connection_.set_is_server(true);
+ BlockOnNextWrite();
+ writer_->set_is_write_blocked_data_buffered(true);
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+ EXPECT_EQ(0u, writer_->last_packet_size());
+ EXPECT_FALSE(connection_.HasQueuedData());
+}
+
+TEST_P(QuicConnectionTest, ClientHandlesVersionNegotiation) {
+ // Start out with some unsupported version.
+ QuicConnectionPeer::GetFramer(&connection_)->set_version_for_tests(
+ QUIC_VERSION_UNSUPPORTED);
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = true;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.packet_sequence_number = 12;
+ header.fec_group = 0;
+
+ QuicVersionVector supported_versions;
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ supported_versions.push_back(kSupportedQuicVersions[i]);
+ }
+
+ // Send a version negotiation packet.
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.BuildVersionNegotiationPacket(
+ header.public_header, supported_versions));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+
+ // Now force another packet. The connection should transition into
+ // NEGOTIATED_VERSION state and tell the packet creator to StopSendingVersion.
+ header.public_header.version_flag = false;
+ QuicFrames frames;
+ QuicFrame frame(&frame1_);
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer_, header, frames).packet);
+ encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+
+ ASSERT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket(
+ QuicConnectionPeer::GetPacketCreator(&connection_)));
+}
+
+TEST_P(QuicConnectionTest, BadVersionNegotiation) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = true;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.packet_sequence_number = 12;
+ header.fec_group = 0;
+
+ QuicVersionVector supported_versions;
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ supported_versions.push_back(kSupportedQuicVersions[i]);
+ }
+
+ // Send a version negotiation packet with the version the client started with.
+ // It should be rejected.
+ EXPECT_CALL(visitor_,
+ OnConnectionClosed(QUIC_INVALID_VERSION_NEGOTIATION_PACKET,
+ false));
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.BuildVersionNegotiationPacket(
+ header.public_header, supported_versions));
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+}
+
+TEST_P(QuicConnectionTest, CheckSendStats) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendStreamDataWithString(3, "first", 0, !kFin, nullptr);
+ size_t first_packet_size = writer_->last_packet_size();
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ connection_.SendStreamDataWithString(5, "second", 0, !kFin, nullptr);
+ size_t second_packet_size = writer_->last_packet_size();
+
+ // 2 retransmissions due to rto, 1 due to explicit nack.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+
+ // Retransmit due to RTO.
+ clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10));
+ connection_.GetRetransmissionAlarm()->Fire();
+
+ // Retransmit due to explicit nacks.
+ QuicAckFrame nack_three = InitAckFrame(4);
+ NackPacket(3, &nack_three);
+ NackPacket(1, &nack_three);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ lost_packets.insert(3);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(2);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
+ ProcessAckPacket(&nack_three);
+
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(
+ Return(QuicBandwidth::Zero()));
+
+ const uint32 kSlowStartThreshold = 23u;
+ EXPECT_CALL(*send_algorithm_, GetSlowStartThreshold()).WillOnce(
+ Return(kSlowStartThreshold));
+
+ const QuicConnectionStats& stats = connection_.GetStats();
+ EXPECT_EQ(3 * first_packet_size + 2 * second_packet_size - kQuicVersionSize,
+ stats.bytes_sent);
+ EXPECT_EQ(5u, stats.packets_sent);
+ EXPECT_EQ(2 * first_packet_size + second_packet_size - kQuicVersionSize,
+ stats.bytes_retransmitted);
+ EXPECT_EQ(3u, stats.packets_retransmitted);
+ EXPECT_EQ(1u, stats.rto_count);
+ EXPECT_EQ(kMaxPacketSize, stats.congestion_window);
+ EXPECT_EQ(kSlowStartThreshold, stats.slow_start_threshold);
+ EXPECT_EQ(kDefaultMaxPacketSize, stats.max_packet_size);
+}
+
+TEST_P(QuicConnectionTest, CheckReceiveStats) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ size_t received_bytes = 0;
+ received_bytes += ProcessFecProtectedPacket(1, false, !kEntropyFlag);
+ received_bytes += ProcessFecProtectedPacket(3, false, !kEntropyFlag);
+ // Should be counted against dropped packets.
+ received_bytes += ProcessDataPacket(3, 1, !kEntropyFlag);
+ received_bytes += ProcessFecPacket(4, 1, true, !kEntropyFlag, nullptr);
+
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce(
+ Return(QuicBandwidth::Zero()));
+ const uint32 kSlowStartThreshold = 23u;
+ EXPECT_CALL(*send_algorithm_, GetSlowStartThreshold()).WillOnce(
+ Return(kSlowStartThreshold));
+
+ const QuicConnectionStats& stats = connection_.GetStats();
+ EXPECT_EQ(received_bytes, stats.bytes_received);
+ EXPECT_EQ(4u, stats.packets_received);
+
+ EXPECT_EQ(1u, stats.packets_revived);
+ EXPECT_EQ(1u, stats.packets_dropped);
+
+ EXPECT_EQ(kSlowStartThreshold, stats.slow_start_threshold);
+}
+
+TEST_P(QuicConnectionTest, TestFecGroupLimits) {
+ // Create and return a group for 1.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != nullptr);
+
+ // Create and return a group for 2.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != nullptr);
+
+ // Create and return a group for 4. This should remove 1 but not 2.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != nullptr);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) == nullptr);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != nullptr);
+
+ // Create and return a group for 3. This will kill off 2.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) != nullptr);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) == nullptr);
+
+ // Verify that adding 5 kills off 3, despite 4 being created before 3.
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 5) != nullptr);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != nullptr);
+ ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == nullptr);
+}
+
+TEST_P(QuicConnectionTest, ProcessFramesIfPacketClosedConnection) {
+ // Construct a packet with stream frame and connection close frame.
+ header_.public_header.connection_id = connection_id_;
+ header_.packet_sequence_number = 1;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = false;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+ header_.fec_group = 0;
+
+ QuicConnectionCloseFrame qccf;
+ qccf.error_code = QUIC_PEER_GOING_AWAY;
+ QuicFrame close_frame(&qccf);
+ QuicFrame stream_frame(&frame1_);
+
+ QuicFrames frames;
+ frames.push_back(stream_frame);
+ frames.push_back(close_frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer_, header_, frames).packet);
+ EXPECT_TRUE(nullptr != packet.get());
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
+ ENCRYPTION_NONE, 1, *packet));
+
+ EXPECT_CALL(visitor_, OnConnectionClosed(QUIC_PEER_GOING_AWAY, true));
+ EXPECT_CALL(visitor_, OnStreamFrames(_)).Times(1);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted);
+}
+
+TEST_P(QuicConnectionTest, SelectMutualVersion) {
+ connection_.SetSupportedVersions(QuicSupportedVersions());
+ // Set the connection to speak the lowest quic version.
+ connection_.set_version(QuicVersionMin());
+ EXPECT_EQ(QuicVersionMin(), connection_.version());
+
+ // Pass in available versions which includes a higher mutually supported
+ // version. The higher mutually supported version should be selected.
+ QuicVersionVector supported_versions;
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ supported_versions.push_back(kSupportedQuicVersions[i]);
+ }
+ EXPECT_TRUE(connection_.SelectMutualVersion(supported_versions));
+ EXPECT_EQ(QuicVersionMax(), connection_.version());
+
+ // Expect that the lowest version is selected.
+ // Ensure the lowest supported version is less than the max, unless they're
+ // the same.
+ EXPECT_LE(QuicVersionMin(), QuicVersionMax());
+ QuicVersionVector lowest_version_vector;
+ lowest_version_vector.push_back(QuicVersionMin());
+ EXPECT_TRUE(connection_.SelectMutualVersion(lowest_version_vector));
+ EXPECT_EQ(QuicVersionMin(), connection_.version());
+
+ // Shouldn't be able to find a mutually supported version.
+ QuicVersionVector unsupported_version;
+ unsupported_version.push_back(QUIC_VERSION_UNSUPPORTED);
+ EXPECT_FALSE(connection_.SelectMutualVersion(unsupported_version));
+}
+
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWritable) {
+ EXPECT_FALSE(writer_->IsWriteBlocked());
+
+ // Send a packet.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(0u, connection_.NumQueuedPackets());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+
+ TriggerConnectionClose();
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, ConnectionCloseGettingWriteBlocked) {
+ BlockOnNextWrite();
+ TriggerConnectionClose();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+}
+
+TEST_P(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) {
+ BlockOnNextWrite();
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ EXPECT_EQ(1u, connection_.NumQueuedPackets());
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(writer_->IsWriteBlocked());
+ TriggerConnectionClose();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+}
+
+TEST_P(QuicConnectionTest, AckNotifierTriggerCallback) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate.get(), OnAckNotification(_, _, _, _, _)).Times(1);
+
+ // Send some data, which will register the delegate to be notified.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
+
+ // Process an ACK from the server which should trigger the callback.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(1);
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, AckNotifierFailToTriggerCallback) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Create a delegate which we don't expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate.get(), OnAckNotification(_, _, _, _, _)).Times(0);
+
+ // Send some data, which will register the delegate to be notified. This will
+ // not be ACKed and so the delegate should never be called.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
+
+ // Send some other data which we will ACK.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, nullptr);
+ connection_.SendStreamDataWithString(1, "bar", 0, !kFin, nullptr);
+
+ // Now we receive ACK for packets 2 and 3, but importantly missing packet 1
+ // which we registered to be notified about.
+ QuicAckFrame frame = InitAckFrame(3);
+ NackPacket(1, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(1);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, AckNotifierCallbackAfterRetransmission) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate.get(), OnAckNotification(_, _, _, _, _)).Times(1);
+
+ // Send four packets, and register to be notified on ACK of packet 2.
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(3, "baz", 0, !kFin, nullptr);
+ connection_.SendStreamDataWithString(3, "qux", 0, !kFin, nullptr);
+
+ // Now we receive ACK for packets 1, 3, and 4 and lose 2.
+ QuicAckFrame frame = InitAckFrame(4);
+ NackPacket(2, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Now we get an ACK for packet 5 (retransmitted packet 2), which should
+ // trigger the callback.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillRepeatedly(Return(SequenceNumberSet()));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame second_ack_frame = InitAckFrame(5);
+ ProcessAckPacket(&second_ack_frame);
+}
+
+// AckNotifierCallback is triggered by the ack of a packet that timed
+// out and was retransmitted, even though the retransmission has a
+// different sequence number.
+TEST_P(QuicConnectionTest, AckNotifierCallbackForAckAfterRTO) {
+ InSequence s;
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ QuicTime default_retransmission_time = clock_.ApproximateNow().Add(
+ DefaultRetransmissionTime());
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, delegate.get());
+ EXPECT_EQ(1u, stop_waiting()->least_unacked);
+
+ EXPECT_EQ(1u, writer_->header().packet_sequence_number);
+ EXPECT_EQ(default_retransmission_time,
+ connection_.GetRetransmissionAlarm()->deadline());
+ // Simulate the retransmission alarm firing.
+ clock_.AdvanceTime(DefaultRetransmissionTime());
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, 2u, _, _));
+ connection_.GetRetransmissionAlarm()->Fire();
+ EXPECT_EQ(2u, writer_->header().packet_sequence_number);
+ // We do not raise the high water mark yet.
+ EXPECT_EQ(1u, stop_waiting()->least_unacked);
+
+ // Ack the original packet, which will revert the RTO.
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*delegate.get(), OnAckNotification(1, _, 1, _, _));
+ EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame ack_frame = InitAckFrame(1);
+ ProcessAckPacket(&ack_frame);
+
+ // Delegate is not notified again when the retransmit is acked.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame second_ack_frame = InitAckFrame(2);
+ ProcessAckPacket(&second_ack_frame);
+}
+
+// AckNotifierCallback is triggered by the ack of a packet that was
+// previously nacked, even though the retransmission has a different
+// sequence number.
+TEST_P(QuicConnectionTest, AckNotifierCallbackForAckOfNackedPacket) {
+ InSequence s;
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ // Send four packets, and register to be notified on ACK of packet 2.
+ connection_.SendStreamDataWithString(3, "foo", 0, !kFin, nullptr);
+ connection_.SendStreamDataWithString(3, "bar", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(3, "baz", 0, !kFin, nullptr);
+ connection_.SendStreamDataWithString(3, "qux", 0, !kFin, nullptr);
+
+ // Now we receive ACK for packets 1, 3, and 4 and lose 2.
+ QuicAckFrame frame = InitAckFrame(4);
+ NackPacket(2, &frame);
+ SequenceNumberSet lost_packets;
+ lost_packets.insert(2);
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _));
+ ProcessAckPacket(&frame);
+
+ // Now we get an ACK for packet 2, which was previously nacked.
+ SequenceNumberSet no_lost_packets;
+ EXPECT_CALL(*delegate.get(), OnAckNotification(1, _, 1, _, _));
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(no_lost_packets));
+ QuicAckFrame second_ack_frame = InitAckFrame(4);
+ ProcessAckPacket(&second_ack_frame);
+
+ // Verify that the delegate is not notified again when the
+ // retransmit is acked.
+ EXPECT_CALL(*loss_algorithm_, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(no_lost_packets));
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame third_ack_frame = InitAckFrame(5);
+ ProcessAckPacket(&third_ack_frame);
+}
+
+TEST_P(QuicConnectionTest, AckNotifierFECTriggerCallback) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate.get(), OnAckNotification(_, _, _, _, _)).Times(1);
+
+ // Send some data, which will register the delegate to be notified.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
+ connection_.SendStreamDataWithString(2, "bar", 0, !kFin, nullptr);
+
+ // Process an ACK from the server with a revived packet, which should trigger
+ // the callback.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+ QuicAckFrame frame = InitAckFrame(2);
+ NackPacket(1, &frame);
+ frame.revived_packets.insert(1);
+ ProcessAckPacket(&frame);
+ // If the ack is processed again, the notifier should not be called again.
+ ProcessAckPacket(&frame);
+}
+
+TEST_P(QuicConnectionTest, AckNotifierCallbackAfterFECRecovery) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+ EXPECT_CALL(visitor_, OnCanWrite());
+
+ // Create a delegate which we expect to be called.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ EXPECT_CALL(*delegate.get(), OnAckNotification(_, _, _, _, _)).Times(1);
+
+ // Expect ACKs for 1 packet.
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _));
+
+ // Send one packet, and register to be notified on ACK.
+ connection_.SendStreamDataWithString(1, "foo", 0, !kFin, delegate.get());
+
+ // Ack packet gets dropped, but we receive an FEC packet that covers it.
+ // Should recover the Ack packet and trigger the notification callback.
+ QuicFrames frames;
+
+ QuicAckFrame ack_frame = InitAckFrame(1);
+ frames.push_back(QuicFrame(&ack_frame));
+
+ // Dummy stream frame to satisfy expectations set elsewhere.
+ frames.push_back(QuicFrame(&frame1_));
+
+ QuicPacketHeader ack_header;
+ ack_header.public_header.connection_id = connection_id_;
+ ack_header.public_header.reset_flag = false;
+ ack_header.public_header.version_flag = false;
+ ack_header.entropy_flag = !kEntropyFlag;
+ ack_header.fec_flag = true;
+ ack_header.packet_sequence_number = 1;
+ ack_header.is_in_fec_group = IN_FEC_GROUP;
+ ack_header.fec_group = 1;
+
+ QuicPacket* packet =
+ BuildUnsizedDataPacket(&framer_, ack_header, frames).packet;
+
+ // Take the packet which contains the ACK frame, and construct and deliver an
+ // FEC packet which allows the ACK packet to be recovered.
+ ProcessFecPacket(2, 1, true, !kEntropyFlag, packet);
+}
+
+TEST_P(QuicConnectionTest, NetworkChangeVisitorCallbacksChangeFecState) {
+ QuicPacketCreator* creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ size_t max_packets_per_fec_group = creator->max_packets_per_fec_group();
+
+ QuicSentPacketManager::NetworkChangeVisitor* visitor =
+ QuicSentPacketManagerPeer::GetNetworkChangeVisitor(
+ QuicConnectionPeer::GetSentPacketManager(&connection_));
+ EXPECT_TRUE(visitor);
+
+ // Increase FEC group size by increasing congestion window to a large number.
+ visitor->OnCongestionWindowChange(1000 * kDefaultTCPMSS);
+ EXPECT_LT(max_packets_per_fec_group, creator->max_packets_per_fec_group());
+}
+
+class MockQuicConnectionDebugVisitor
+ : public QuicConnectionDebugVisitor {
+ public:
+ MOCK_METHOD1(OnFrameAddedToPacket,
+ void(const QuicFrame&));
+
+ MOCK_METHOD6(OnPacketSent,
+ void(QuicPacketSequenceNumber,
+ QuicPacketSequenceNumber,
+ EncryptionLevel,
+ TransmissionType,
+ const QuicEncryptedPacket&,
+ WriteResult));
+
+ MOCK_METHOD3(OnPacketReceived,
+ void(const IPEndPoint&,
+ const IPEndPoint&,
+ const QuicEncryptedPacket&));
+
+ MOCK_METHOD1(OnProtocolVersionMismatch,
+ void(QuicVersion));
+
+ MOCK_METHOD1(OnPacketHeader,
+ void(const QuicPacketHeader& header));
+
+ MOCK_METHOD1(OnStreamFrame,
+ void(const QuicStreamFrame&));
+
+ MOCK_METHOD1(OnAckFrame,
+ void(const QuicAckFrame& frame));
+
+ MOCK_METHOD1(OnCongestionFeedbackFrame,
+ void(const QuicCongestionFeedbackFrame&));
+
+ MOCK_METHOD1(OnStopWaitingFrame,
+ void(const QuicStopWaitingFrame&));
+
+ MOCK_METHOD1(OnRstStreamFrame,
+ void(const QuicRstStreamFrame&));
+
+ MOCK_METHOD1(OnConnectionCloseFrame,
+ void(const QuicConnectionCloseFrame&));
+
+ MOCK_METHOD1(OnPublicResetPacket,
+ void(const QuicPublicResetPacket&));
+
+ MOCK_METHOD1(OnVersionNegotiationPacket,
+ void(const QuicVersionNegotiationPacket&));
+
+ MOCK_METHOD2(OnRevivedPacket,
+ void(const QuicPacketHeader&, StringPiece payload));
+};
+
+TEST_P(QuicConnectionTest, OnPacketHeaderDebugVisitor) {
+ QuicPacketHeader header;
+
+ MockQuicConnectionDebugVisitor* debug_visitor =
+ new MockQuicConnectionDebugVisitor();
+ connection_.set_debug_visitor(debug_visitor);
+ EXPECT_CALL(*debug_visitor, OnPacketHeader(Ref(header))).Times(1);
+ connection_.OnPacketHeader(header);
+}
+
+TEST_P(QuicConnectionTest, Pacing) {
+ TestConnection server(connection_id_, IPEndPoint(), helper_.get(),
+ factory_, /* is_server= */ true, version());
+ TestConnection client(connection_id_, IPEndPoint(), helper_.get(),
+ factory_, /* is_server= */ false, version());
+ EXPECT_FALSE(client.sent_packet_manager().using_pacing());
+ EXPECT_FALSE(server.sent_packet_manager().using_pacing());
+}
+
+TEST_P(QuicConnectionTest, ControlFramesInstigateAcks) {
+ EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+
+ // Send a WINDOW_UPDATE frame.
+ QuicWindowUpdateFrame window_update;
+ window_update.stream_id = 3;
+ window_update.byte_offset = 1234;
+ EXPECT_CALL(visitor_, OnWindowUpdateFrames(_));
+ ProcessFramePacket(QuicFrame(&window_update));
+
+ // Ensure that this has caused the ACK alarm to be set.
+ QuicAlarm* ack_alarm = QuicConnectionPeer::GetAckAlarm(&connection_);
+ EXPECT_TRUE(ack_alarm->IsSet());
+
+ // Cancel alarm, and try again with BLOCKED frame.
+ ack_alarm->Cancel();
+ QuicBlockedFrame blocked;
+ blocked.stream_id = 3;
+ EXPECT_CALL(visitor_, OnBlockedFrames(_));
+ ProcessFramePacket(QuicFrame(&blocked));
+ EXPECT_TRUE(ack_alarm->IsSet());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc
new file mode 100644
index 0000000..1c41e0a
--- /dev/null
+++ b/net/quic/quic_crypto_client_stream.cc
@@ -0,0 +1,601 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_crypto_client_stream.h"
+
+#include "base/metrics/histogram.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/quic_client_session_base.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+QuicCryptoClientStream::ChannelIDSourceCallbackImpl::
+ChannelIDSourceCallbackImpl(QuicCryptoClientStream* stream)
+ : stream_(stream) {}
+
+QuicCryptoClientStream::ChannelIDSourceCallbackImpl::
+~ChannelIDSourceCallbackImpl() {}
+
+void QuicCryptoClientStream::ChannelIDSourceCallbackImpl::Run(
+ scoped_ptr<ChannelIDKey>* channel_id_key) {
+ if (stream_ == nullptr) {
+ return;
+ }
+
+ stream_->channel_id_key_.reset(channel_id_key->release());
+ stream_->channel_id_source_callback_run_ = true;
+ stream_->channel_id_source_callback_ = nullptr;
+ stream_->DoHandshakeLoop(nullptr);
+
+ // The ChannelIDSource owns this object and will delete it when this method
+ // returns.
+}
+
+void QuicCryptoClientStream::ChannelIDSourceCallbackImpl::Cancel() {
+ stream_ = nullptr;
+}
+
+QuicCryptoClientStream::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
+ QuicCryptoClientStream* stream)
+ : stream_(stream) {}
+
+QuicCryptoClientStream::ProofVerifierCallbackImpl::
+~ProofVerifierCallbackImpl() {}
+
+void QuicCryptoClientStream::ProofVerifierCallbackImpl::Run(
+ bool ok,
+ const string& error_details,
+ scoped_ptr<ProofVerifyDetails>* details) {
+ if (stream_ == nullptr) {
+ return;
+ }
+
+ stream_->verify_ok_ = ok;
+ stream_->verify_error_details_ = error_details;
+ stream_->verify_details_.reset(details->release());
+ stream_->proof_verify_callback_ = nullptr;
+ stream_->DoHandshakeLoop(nullptr);
+
+ // The ProofVerifier owns this object and will delete it when this method
+ // returns.
+}
+
+void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() {
+ stream_ = nullptr;
+}
+
+QuicCryptoClientStream::QuicCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
+ QuicCryptoClientConfig* crypto_config)
+ : QuicCryptoStream(session),
+ next_state_(STATE_IDLE),
+ num_client_hellos_(0),
+ crypto_config_(crypto_config),
+ server_id_(server_id),
+ generation_counter_(0),
+ channel_id_sent_(false),
+ channel_id_source_callback_run_(false),
+ channel_id_source_callback_(nullptr),
+ verify_context_(verify_context),
+ proof_verify_callback_(nullptr) {}
+
+QuicCryptoClientStream::~QuicCryptoClientStream() {
+ if (channel_id_source_callback_) {
+ channel_id_source_callback_->Cancel();
+ }
+ if (proof_verify_callback_) {
+ proof_verify_callback_->Cancel();
+ }
+}
+
+void QuicCryptoClientStream::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ QuicCryptoStream::OnHandshakeMessage(message);
+
+ if (message.tag() == kSCUP) {
+ if (!handshake_confirmed()) {
+ CloseConnection(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE);
+ return;
+ }
+
+ // |message| is an update from the server, so we treat it differently from a
+ // handshake message.
+ HandleServerConfigUpdateMessage(message);
+ return;
+ }
+
+ // Do not process handshake messages after the handshake is confirmed.
+ if (handshake_confirmed()) {
+ CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
+ return;
+ }
+
+ DoHandshakeLoop(&message);
+}
+
+bool QuicCryptoClientStream::CryptoConnect() {
+ next_state_ = STATE_INITIALIZE;
+ DoHandshakeLoop(nullptr);
+ return true;
+}
+
+int QuicCryptoClientStream::num_sent_client_hellos() const {
+ return num_client_hellos_;
+}
+
+bool QuicCryptoClientStream::WasChannelIDSent() const {
+ return channel_id_sent_;
+}
+
+bool QuicCryptoClientStream::WasChannelIDSourceCallbackRun() const {
+ return channel_id_source_callback_run_;
+}
+
+void QuicCryptoClientStream::HandleServerConfigUpdateMessage(
+ const CryptoHandshakeMessage& server_config_update) {
+ DCHECK(server_config_update.tag() == kSCUP);
+ string error_details;
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_->LookupOrCreate(server_id_);
+ QuicErrorCode error = crypto_config_->ProcessServerConfigUpdate(
+ server_config_update,
+ session()->connection()->clock()->WallNow(),
+ cached,
+ &crypto_negotiated_params_,
+ &error_details);
+
+ if (error != QUIC_NO_ERROR) {
+ CloseConnectionWithDetails(
+ error, "Server config update invalid: " + error_details);
+ return;
+ }
+
+ DCHECK(handshake_confirmed());
+ if (proof_verify_callback_) {
+ proof_verify_callback_->Cancel();
+ }
+ next_state_ = STATE_INITIALIZE_SCUP;
+ DoHandshakeLoop(nullptr);
+}
+
+// kMaxClientHellos is the maximum number of times that we'll send a client
+// hello. The value 3 accounts for:
+// * One failure due to an incorrect or missing source-address token.
+// * One failure due the server's certificate chain being unavailible and the
+// server being unwilling to send it without a valid source-address token.
+static const int kMaxClientHellos = 3;
+
+void QuicCryptoClientStream::DoHandshakeLoop(
+ const CryptoHandshakeMessage* in) {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_->LookupOrCreate(server_id_);
+
+ QuicAsyncStatus rv = QUIC_SUCCESS;
+ do {
+ CHECK_NE(STATE_NONE, next_state_);
+ const State state = next_state_;
+ next_state_ = STATE_IDLE;
+ rv = QUIC_SUCCESS;
+ switch (state) {
+ case STATE_INITIALIZE:
+ DoInitialize(cached);
+ break;
+ case STATE_SEND_CHLO:
+ DoSendCHLO(in, cached);
+ return;
+ case STATE_RECV_REJ:
+ DoReceiveREJ(in, cached);
+ break;
+ case STATE_VERIFY_PROOF:
+ rv = DoVerifyProof(cached);
+ break;
+ case STATE_VERIFY_PROOF_COMPLETE:
+ DoVerifyProofComplete(cached);
+ break;
+ case STATE_GET_CHANNEL_ID:
+ rv = DoGetChannelID(cached);
+ break;
+ case STATE_GET_CHANNEL_ID_COMPLETE:
+ DoGetChannelIDComplete();
+ break;
+ case STATE_RECV_SHLO:
+ DoReceiveSHLO(in, cached);
+ break;
+ case STATE_IDLE:
+ // This means that the peer sent us a message that we weren't expecting.
+ CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
+ return;
+ case STATE_INITIALIZE_SCUP:
+ DoInitializeServerConfigUpdate(cached);
+ break;
+ case STATE_NONE:
+ NOTREACHED();
+ return; // We are done.
+ }
+ } while (rv != QUIC_PENDING && next_state_ != STATE_NONE);
+}
+
+void QuicCryptoClientStream::DoInitialize(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (!cached->IsEmpty() && !cached->signature().empty() &&
+ server_id_.is_https()) {
+ // Note that we verify the proof even if the cached proof is valid.
+ // This allows us to respond to CA trust changes or certificate
+ // expiration because it may have been a while since we last verified
+ // the proof.
+ DCHECK(crypto_config_->proof_verifier());
+ // If the cached state needs to be verified, do it now.
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ next_state_ = STATE_GET_CHANNEL_ID;
+ }
+}
+
+void QuicCryptoClientStream::DoSendCHLO(
+ const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached) {
+ // Send the client hello in plaintext.
+ session()->connection()->SetDefaultEncryptionLevel(ENCRYPTION_NONE);
+ if (num_client_hellos_ > kMaxClientHellos) {
+ CloseConnection(QUIC_CRYPTO_TOO_MANY_REJECTS);
+ return;
+ }
+ num_client_hellos_++;
+
+ CryptoHandshakeMessage out;
+ if (!cached->IsComplete(session()->connection()->clock()->WallNow())) {
+ crypto_config_->FillInchoateClientHello(
+ server_id_,
+ session()->connection()->supported_versions().front(),
+ cached, &crypto_negotiated_params_, &out);
+ // Pad the inchoate client hello to fill up a packet.
+ const size_t kFramingOverhead = 50; // A rough estimate.
+ const size_t max_packet_size =
+ session()->connection()->max_packet_length();
+ if (max_packet_size <= kFramingOverhead) {
+ DLOG(DFATAL) << "max_packet_length (" << max_packet_size
+ << ") has no room for framing overhead.";
+ CloseConnection(QUIC_INTERNAL_ERROR);
+ return;
+ }
+ if (kClientHelloMinimumSize > max_packet_size - kFramingOverhead) {
+ DLOG(DFATAL) << "Client hello won't fit in a single packet.";
+ CloseConnection(QUIC_INTERNAL_ERROR);
+ return;
+ }
+ out.set_minimum_size(max_packet_size - kFramingOverhead);
+ next_state_ = STATE_RECV_REJ;
+ SendHandshakeMessage(out);
+ return;
+ }
+
+ session()->config()->ToHandshakeMessage(&out);
+ string error_details;
+ QuicErrorCode error = crypto_config_->FillClientHello(
+ server_id_,
+ session()->connection()->connection_id(),
+ session()->connection()->supported_versions().front(),
+ cached,
+ session()->connection()->clock()->WallNow(),
+ session()->connection()->random_generator(),
+ channel_id_key_.get(),
+ &crypto_negotiated_params_,
+ &out,
+ &error_details);
+ if (error != QUIC_NO_ERROR) {
+ // Flush the cached config so that, if it's bad, the server has a
+ // chance to send us another in the future.
+ cached->InvalidateServerConfig();
+ CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+ channel_id_sent_ = (channel_id_key_.get() != nullptr);
+ if (cached->proof_verify_details()) {
+ client_session()->OnProofVerifyDetailsAvailable(
+ *cached->proof_verify_details());
+ }
+ next_state_ = STATE_RECV_SHLO;
+ SendHandshakeMessage(out);
+ // Be prepared to decrypt with the new server write key.
+ session()->connection()->SetAlternativeDecrypter(
+ crypto_negotiated_params_.initial_crypters.decrypter.release(),
+ ENCRYPTION_INITIAL,
+ true /* latch once used */);
+ // Send subsequent packets under encryption on the assumption that the
+ // server will accept the handshake.
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_INITIAL,
+ crypto_negotiated_params_.initial_crypters.encrypter.release());
+ session()->connection()->SetDefaultEncryptionLevel(
+ ENCRYPTION_INITIAL);
+ if (!encryption_established_) {
+ encryption_established_ = true;
+ session()->OnCryptoHandshakeEvent(
+ QuicSession::ENCRYPTION_FIRST_ESTABLISHED);
+ } else {
+ session()->OnCryptoHandshakeEvent(
+ QuicSession::ENCRYPTION_REESTABLISHED);
+ }
+}
+
+void QuicCryptoClientStream::DoReceiveREJ(
+ const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached) {
+ // We sent a dummy CHLO because we didn't have enough information to
+ // perform a handshake, or we sent a full hello that the server
+ // rejected. Here we hope to have a REJ that contains the information
+ // that we need.
+ if (in->tag() != kREJ) {
+ next_state_ = STATE_NONE;
+ CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Expected REJ");
+ return;
+ }
+ string error_details;
+ QuicErrorCode error = crypto_config_->ProcessRejection(
+ *in, session()->connection()->clock()->WallNow(), cached,
+ server_id_.is_https(), &crypto_negotiated_params_, &error_details);
+ if (error != QUIC_NO_ERROR) {
+ next_state_ = STATE_NONE;
+ CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+ if (!cached->proof_valid()) {
+ if (!server_id_.is_https()) {
+ // We don't check the certificates for insecure QUIC connections.
+ SetCachedProofValid(cached);
+ } else if (!cached->signature().empty()) {
+ // Note that we only verify the proof if the cached proof is not
+ // valid. If the cached proof is valid here, someone else must have
+ // just added the server config to the cache and verified the proof,
+ // so we can assume no CA trust changes or certificate expiration
+ // has happened since then.
+ next_state_ = STATE_VERIFY_PROOF;
+ return;
+ }
+ }
+ next_state_ = STATE_GET_CHANNEL_ID;
+}
+
+QuicAsyncStatus QuicCryptoClientStream::DoVerifyProof(
+ QuicCryptoClientConfig::CachedState* cached) {
+ ProofVerifier* verifier = crypto_config_->proof_verifier();
+ DCHECK(verifier);
+ next_state_ = STATE_VERIFY_PROOF_COMPLETE;
+ generation_counter_ = cached->generation_counter();
+
+ ProofVerifierCallbackImpl* proof_verify_callback =
+ new ProofVerifierCallbackImpl(this);
+
+ verify_ok_ = false;
+
+ QuicAsyncStatus status = verifier->VerifyProof(
+ server_id_.host(),
+ cached->server_config(),
+ cached->certs(),
+ cached->signature(),
+ verify_context_.get(),
+ &verify_error_details_,
+ &verify_details_,
+ proof_verify_callback);
+
+ switch (status) {
+ case QUIC_PENDING:
+ proof_verify_callback_ = proof_verify_callback;
+ DVLOG(1) << "Doing VerifyProof";
+ break;
+ case QUIC_FAILURE:
+ delete proof_verify_callback;
+ break;
+ case QUIC_SUCCESS:
+ delete proof_verify_callback;
+ verify_ok_ = true;
+ break;
+ }
+ return status;
+}
+
+void QuicCryptoClientStream::DoVerifyProofComplete(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (!verify_ok_) {
+ next_state_ = STATE_NONE;
+ client_session()->OnProofVerifyDetailsAvailable(*verify_details_);
+ UMA_HISTOGRAM_BOOLEAN("Net.QuicVerifyProofFailed.HandshakeConfirmed",
+ handshake_confirmed());
+ CloseConnectionWithDetails(
+ QUIC_PROOF_INVALID, "Proof invalid: " + verify_error_details_);
+ return;
+ }
+
+ // Check if generation_counter has changed between STATE_VERIFY_PROOF and
+ // STATE_VERIFY_PROOF_COMPLETE state changes.
+ if (generation_counter_ != cached->generation_counter()) {
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ SetCachedProofValid(cached);
+ cached->SetProofVerifyDetails(verify_details_.release());
+ if (!handshake_confirmed()) {
+ next_state_ = STATE_GET_CHANNEL_ID;
+ } else {
+ next_state_ = STATE_NONE;
+ }
+ }
+}
+
+QuicAsyncStatus QuicCryptoClientStream::DoGetChannelID(
+ QuicCryptoClientConfig::CachedState* cached) {
+ next_state_ = STATE_GET_CHANNEL_ID_COMPLETE;
+ channel_id_key_.reset();
+ if (!RequiresChannelID(cached)) {
+ next_state_ = STATE_SEND_CHLO;
+ return QUIC_SUCCESS;
+ }
+
+ ChannelIDSourceCallbackImpl* channel_id_source_callback =
+ new ChannelIDSourceCallbackImpl(this);
+ QuicAsyncStatus status =
+ crypto_config_->channel_id_source()->GetChannelIDKey(
+ server_id_.host(), &channel_id_key_,
+ channel_id_source_callback);
+
+ switch (status) {
+ case QUIC_PENDING:
+ channel_id_source_callback_ = channel_id_source_callback;
+ DVLOG(1) << "Looking up channel ID";
+ break;
+ case QUIC_FAILURE:
+ next_state_ = STATE_NONE;
+ delete channel_id_source_callback;
+ CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ "Channel ID lookup failed");
+ break;
+ case QUIC_SUCCESS:
+ delete channel_id_source_callback;
+ break;
+ }
+ return status;
+}
+
+void QuicCryptoClientStream::DoGetChannelIDComplete() {
+ if (!channel_id_key_.get()) {
+ next_state_ = STATE_NONE;
+ CloseConnectionWithDetails(QUIC_INVALID_CHANNEL_ID_SIGNATURE,
+ "Channel ID lookup failed");
+ return;
+ }
+ next_state_ = STATE_SEND_CHLO;
+}
+
+void QuicCryptoClientStream::DoReceiveSHLO(
+ const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached) {
+ next_state_ = STATE_NONE;
+ // We sent a CHLO that we expected to be accepted and now we're hoping
+ // for a SHLO from the server to confirm that.
+ if (in->tag() == kREJ) {
+ // alternative_decrypter will be nullptr if the original alternative
+ // decrypter latched and became the primary decrypter. That happens
+ // if we received a message encrypted with the INITIAL key.
+ if (session()->connection()->alternative_decrypter() == nullptr) {
+ // The rejection was sent encrypted!
+ CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
+ "encrypted REJ message");
+ return;
+ }
+ next_state_ = STATE_RECV_REJ;
+ return;
+ }
+
+ if (in->tag() != kSHLO) {
+ CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE,
+ "Expected SHLO or REJ");
+ return;
+ }
+
+ // alternative_decrypter will be nullptr if the original alternative
+ // decrypter latched and became the primary decrypter. That happens
+ // if we received a message encrypted with the INITIAL key.
+ if (session()->connection()->alternative_decrypter() != nullptr) {
+ // The server hello was sent without encryption.
+ CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT,
+ "unencrypted SHLO message");
+ return;
+ }
+
+ string error_details;
+ QuicErrorCode error = crypto_config_->ProcessServerHello(
+ *in, session()->connection()->connection_id(),
+ session()->connection()->server_supported_versions(),
+ cached, &crypto_negotiated_params_, &error_details);
+
+ if (error != QUIC_NO_ERROR) {
+ CloseConnectionWithDetails(error, "Server hello invalid: " + error_details);
+ return;
+ }
+ error = session()->config()->ProcessPeerHello(*in, SERVER, &error_details);
+ if (error != QUIC_NO_ERROR) {
+ CloseConnectionWithDetails(error, "Server hello invalid: " + error_details);
+ return;
+ }
+ session()->OnConfigNegotiated();
+
+ CrypterPair* crypters = &crypto_negotiated_params_.forward_secure_crypters;
+ // TODO(agl): we don't currently latch this decrypter because the idea
+ // has been floated that the server shouldn't send packets encrypted
+ // with the FORWARD_SECURE key until it receives a FORWARD_SECURE
+ // packet from the client.
+ session()->connection()->SetAlternativeDecrypter(
+ crypters->decrypter.release(), ENCRYPTION_FORWARD_SECURE,
+ false /* don't latch */);
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE, crypters->encrypter.release());
+ session()->connection()->SetDefaultEncryptionLevel(
+ ENCRYPTION_FORWARD_SECURE);
+
+ handshake_confirmed_ = true;
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ session()->connection()->OnHandshakeComplete();
+}
+
+void QuicCryptoClientStream::DoInitializeServerConfigUpdate(
+ QuicCryptoClientConfig::CachedState* cached) {
+ bool update_ignored = false;
+ if (!server_id_.is_https()) {
+ // We don't check the certificates for insecure QUIC connections.
+ SetCachedProofValid(cached);
+ next_state_ = STATE_NONE;
+ } else if (!cached->IsEmpty() && !cached->signature().empty()) {
+ // Note that we verify the proof even if the cached proof is valid.
+ DCHECK(crypto_config_->proof_verifier());
+ next_state_ = STATE_VERIFY_PROOF;
+ } else {
+ update_ignored = true;
+ next_state_ = STATE_NONE;
+ }
+ UMA_HISTOGRAM_COUNTS("Net.QuicNumServerConfig.UpdateMessagesIgnored",
+ update_ignored);
+}
+
+void QuicCryptoClientStream::SetCachedProofValid(
+ QuicCryptoClientConfig::CachedState* cached) {
+ cached->SetProofValid();
+ client_session()->OnProofValid(*cached);
+}
+
+bool QuicCryptoClientStream::RequiresChannelID(
+ QuicCryptoClientConfig::CachedState* cached) {
+ if (!server_id_.is_https() ||
+ server_id_.privacy_mode() == PRIVACY_MODE_ENABLED ||
+ !crypto_config_->channel_id_source()) {
+ return false;
+ }
+ const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
+ if (!scfg) { // scfg may be null when we send an inchoate CHLO.
+ return false;
+ }
+ const QuicTag* their_proof_demands;
+ size_t num_their_proof_demands;
+ if (scfg->GetTaglist(kPDMD, &their_proof_demands,
+ &num_their_proof_demands) != QUIC_NO_ERROR) {
+ return false;
+ }
+ for (size_t i = 0; i < num_their_proof_demands; i++) {
+ if (their_proof_demands[i] == kCHID) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicClientSessionBase* QuicCryptoClientStream::client_session() {
+ return reinterpret_cast<QuicClientSessionBase*>(session());
+}
+
+} // namespace net
diff --git a/net/quic/quic_crypto_client_stream.h b/net/quic/quic_crypto_client_stream.h
new file mode 100644
index 0000000..96d2556
--- /dev/null
+++ b/net/quic/quic_crypto_client_stream.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_H_
+#define NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_H_
+
+#include <string>
+
+#include "net/quic/crypto/channel_id.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/crypto/quic_crypto_client_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_server_id.h"
+
+namespace net {
+
+class QuicClientSessionBase;
+
+namespace test {
+class CryptoTestUtils;
+class QuicClientSessionPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream {
+ public:
+ QuicCryptoClientStream(const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
+ QuicCryptoClientConfig* crypto_config);
+ virtual ~QuicCryptoClientStream();
+
+ // CryptoFramerVisitorInterface implementation
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE;
+
+ // Performs a crypto handshake with the server. Returns true if the crypto
+ // handshake is started successfully.
+ // TODO(agl): this should probably return void.
+ virtual bool CryptoConnect();
+
+ // num_sent_client_hellos returns the number of client hello messages that
+ // have been sent. If the handshake has completed then this is one greater
+ // than the number of round-trips needed for the handshake.
+ int num_sent_client_hellos() const;
+
+ // Returns true if a channel ID was sent on this connection.
+ bool WasChannelIDSent() const;
+
+ // Returns true if our ChannelIDSourceCallback was run, which implies the
+ // ChannelIDSource operated asynchronously. Intended for testing.
+ bool WasChannelIDSourceCallbackRun() const;
+
+ private:
+ // ChannelIDSourceCallbackImpl is passed as the callback method to
+ // GetChannelIDKey. The ChannelIDSource calls this class with the result of
+ // channel ID lookup when lookup is performed asynchronously.
+ class ChannelIDSourceCallbackImpl : public ChannelIDSourceCallback {
+ public:
+ explicit ChannelIDSourceCallbackImpl(QuicCryptoClientStream* stream);
+ virtual ~ChannelIDSourceCallbackImpl();
+
+ // ChannelIDSourceCallback interface.
+ virtual void Run(scoped_ptr<ChannelIDKey>* channel_id_key) OVERRIDE;
+
+ // Cancel causes any future callbacks to be ignored. It must be called on
+ // the same thread as the callback will be made on.
+ void Cancel();
+
+ private:
+ QuicCryptoClientStream* stream_;
+ };
+
+ // ProofVerifierCallbackImpl is passed as the callback method to VerifyProof.
+ // The ProofVerifier calls this class with the result of proof verification
+ // when verification is performed asynchronously.
+ class ProofVerifierCallbackImpl : public ProofVerifierCallback {
+ public:
+ explicit ProofVerifierCallbackImpl(QuicCryptoClientStream* stream);
+ virtual ~ProofVerifierCallbackImpl();
+
+ // ProofVerifierCallback interface.
+ virtual void Run(bool ok,
+ const string& error_details,
+ scoped_ptr<ProofVerifyDetails>* details) OVERRIDE;
+
+ // Cancel causes any future callbacks to be ignored. It must be called on
+ // the same thread as the callback will be made on.
+ void Cancel();
+
+ private:
+ QuicCryptoClientStream* stream_;
+ };
+
+ friend class test::CryptoTestUtils;
+ friend class test::QuicClientSessionPeer;
+
+ enum State {
+ STATE_IDLE,
+ STATE_INITIALIZE,
+ STATE_SEND_CHLO,
+ STATE_RECV_REJ,
+ STATE_VERIFY_PROOF,
+ STATE_VERIFY_PROOF_COMPLETE,
+ STATE_GET_CHANNEL_ID,
+ STATE_GET_CHANNEL_ID_COMPLETE,
+ STATE_RECV_SHLO,
+ STATE_INITIALIZE_SCUP,
+ STATE_NONE,
+ };
+
+ // Handles new server config and optional source-address token provided by the
+ // server during a connection.
+ void HandleServerConfigUpdateMessage(
+ const CryptoHandshakeMessage& server_config_update);
+
+ // DoHandshakeLoop performs a step of the handshake state machine. Note that
+ // |in| may be nullptr if the call did not result from a received message.
+ void DoHandshakeLoop(const CryptoHandshakeMessage* in);
+
+ // Start the handshake process.
+ void DoInitialize(QuicCryptoClientConfig::CachedState* cached);
+
+ // Send either InchoateClientHello or ClientHello message to the server.
+ void DoSendCHLO(const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Process REJ message from the server.
+ void DoReceiveREJ(const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Start the proof verification process. Returns the QuicAsyncStatus returned
+ // by the ProofVerifier's VerifyProof.
+ QuicAsyncStatus DoVerifyProof(QuicCryptoClientConfig::CachedState* cached);
+
+ // If proof is valid then it sets the proof as valid (which persists the
+ // server config). If not, it closes the connection.
+ void DoVerifyProofComplete(QuicCryptoClientConfig::CachedState* cached);
+
+ // Start the look up of Channel ID process. Returns either QUIC_SUCCESS if
+ // RequiresChannelID returns false or QuicAsyncStatus returned by
+ // GetChannelIDKey.
+ QuicAsyncStatus DoGetChannelID(QuicCryptoClientConfig::CachedState* cached);
+
+ // If there is no channel ID, then close the connection otherwise transtion to
+ // STATE_SEND_CHLO state.
+ void DoGetChannelIDComplete();
+
+ // Process SHLO message from the server.
+ void DoReceiveSHLO(const CryptoHandshakeMessage* in,
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Start the proof verification if |server_id_| is https and |cached| has
+ // signature.
+ void DoInitializeServerConfigUpdate(
+ QuicCryptoClientConfig::CachedState* cached);
+
+ // Called to set the proof of |cached| valid. Also invokes the session's
+ // OnProofValid() method.
+ void SetCachedProofValid(QuicCryptoClientConfig::CachedState* cached);
+
+ // Returns true if the server crypto config in |cached| requires a ChannelID
+ // and the client config settings also allow sending a ChannelID.
+ bool RequiresChannelID(QuicCryptoClientConfig::CachedState* cached);
+
+ QuicClientSessionBase* client_session();
+
+ State next_state_;
+ // num_client_hellos_ contains the number of client hello messages that this
+ // connection has sent.
+ int num_client_hellos_;
+
+ QuicCryptoClientConfig* const crypto_config_;
+
+ // Client's connection nonce (4-byte timestamp + 28 random bytes)
+ std::string nonce_;
+ // Server's (hostname, port, is_https, privacy_mode) tuple.
+ const QuicServerId server_id_;
+
+ // Generation counter from QuicCryptoClientConfig's CachedState.
+ uint64 generation_counter_;
+
+ // True if a channel ID was sent.
+ bool channel_id_sent_;
+
+ // True if channel_id_source_callback_ was run.
+ bool channel_id_source_callback_run_;
+
+ // channel_id_source_callback_ contains the callback object that we passed
+ // to an asynchronous channel ID lookup. The ChannelIDSource owns this
+ // object.
+ ChannelIDSourceCallbackImpl* channel_id_source_callback_;
+
+ // These members are used to store the result of an asynchronous channel ID
+ // lookup. These members must not be used after
+ // STATE_GET_CHANNEL_ID_COMPLETE.
+ scoped_ptr<ChannelIDKey> channel_id_key_;
+
+ // verify_context_ contains the context object that we pass to asynchronous
+ // proof verifications.
+ scoped_ptr<ProofVerifyContext> verify_context_;
+
+ // proof_verify_callback_ contains the callback object that we passed to an
+ // asynchronous proof verification. The ProofVerifier owns this object.
+ ProofVerifierCallbackImpl* proof_verify_callback_;
+
+ // These members are used to store the result of an asynchronous proof
+ // verification. These members must not be used after
+ // STATE_VERIFY_PROOF_COMPLETE.
+ bool verify_ok_;
+ string verify_error_details_;
+ scoped_ptr<ProofVerifyDetails> verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_H_
diff --git a/net/quic/quic_crypto_client_stream_factory.h b/net/quic/quic_crypto_client_stream_factory.h
new file mode 100644
index 0000000..293fa82
--- /dev/null
+++ b/net/quic/quic_crypto_client_stream_factory.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_FACTORY_H_
+#define NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_FACTORY_H_
+
+#include <string>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+class QuicClientSession;
+class QuicCryptoClientStream;
+class QuicServerId;
+
+// An interface used to instantiate QuicCryptoClientStream objects. Used to
+// facilitate testing code with mock implementations.
+class NET_EXPORT QuicCryptoClientStreamFactory {
+ public:
+ virtual ~QuicCryptoClientStreamFactory() {}
+
+ virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicClientSession* session,
+ QuicCryptoClientConfig* crypto_config) = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_FACTORY_H_
diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc
new file mode 100644
index 0000000..5a8b08c
--- /dev/null
+++ b/net/quic/quic_crypto_client_stream_test.cc
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_crypto_client_stream.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+const char kServerHostname[] = "example.com";
+const uint16 kServerPort = 80;
+
+class QuicCryptoClientStreamTest : public ::testing::Test {
+ public:
+ QuicCryptoClientStreamTest()
+ : connection_(new PacketSavingConnection(false)),
+ session_(new TestClientSession(connection_, DefaultQuicConfig())),
+ server_id_(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED),
+ stream_(new QuicCryptoClientStream(server_id_, session_.get(), nullptr,
+ &crypto_config_)) {
+ session_->SetCryptoStream(stream_.get());
+ session_->config()->SetDefaults();
+ crypto_config_.SetDefaults();
+ }
+
+ void CompleteCryptoHandshake() {
+ EXPECT_TRUE(stream_->CryptoConnect());
+ CryptoTestUtils::HandshakeWithFakeServer(connection_, stream_.get());
+ }
+
+ void ConstructHandshakeMessage() {
+ CryptoFramer framer;
+ message_data_.reset(framer.ConstructHandshakeMessage(message_));
+ }
+
+ PacketSavingConnection* connection_;
+ scoped_ptr<TestClientSession> session_;
+ QuicServerId server_id_;
+ scoped_ptr<QuicCryptoClientStream> stream_;
+ CryptoHandshakeMessage message_;
+ scoped_ptr<QuicData> message_data_;
+ QuicCryptoClientConfig crypto_config_;
+};
+
+TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) {
+ EXPECT_FALSE(stream_->encryption_established());
+ EXPECT_FALSE(stream_->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) {
+ CompleteCryptoHandshake();
+ EXPECT_TRUE(stream_->encryption_established());
+ EXPECT_TRUE(stream_->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) {
+ CompleteCryptoHandshake();
+
+ EXPECT_CALL(*connection_, SendConnectionClose(
+ QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE));
+ message_.set_tag(kCHLO);
+ ConstructHandshakeMessage();
+ stream_->ProcessRawData(message_data_->data(), message_data_->length());
+}
+
+TEST_F(QuicCryptoClientStreamTest, BadMessageType) {
+ EXPECT_TRUE(stream_->CryptoConnect());
+
+ message_.set_tag(kCHLO);
+ ConstructHandshakeMessage();
+
+ EXPECT_CALL(*connection_, SendConnectionCloseWithDetails(
+ QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "Expected REJ"));
+ stream_->ProcessRawData(message_data_->data(), message_data_->length());
+}
+
+TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) {
+ CompleteCryptoHandshake();
+
+ const QuicConfig* config = session_->config();
+ EXPECT_EQ(kQBIC, config->CongestionFeedback());
+ EXPECT_EQ(kMaximumIdleTimeoutSecs,
+ config->IdleConnectionStateLifetime().ToSeconds());
+ EXPECT_EQ(kDefaultMaxStreamsPerConnection,
+ config->MaxStreamsPerConnection());
+ EXPECT_EQ(0, config->KeepaliveTimeout().ToSeconds());
+
+ const QuicCryptoNegotiatedParameters& crypto_params(
+ stream_->crypto_negotiated_params());
+ EXPECT_EQ(crypto_config_.aead[0], crypto_params.aead);
+ EXPECT_EQ(crypto_config_.kexs[0], crypto_params.key_exchange);
+}
+
+TEST_F(QuicCryptoClientStreamTest, InvalidHostname) {
+ QuicServerId server_id("invalid", 80, false, PRIVACY_MODE_DISABLED);
+ stream_.reset(new QuicCryptoClientStream(server_id, session_.get(), nullptr,
+ &crypto_config_));
+ session_->SetCryptoStream(stream_.get());
+
+ CompleteCryptoHandshake();
+ EXPECT_TRUE(stream_->encryption_established());
+ EXPECT_TRUE(stream_->handshake_confirmed());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ExpiredServerConfig) {
+ // Seed the config with a cached server config.
+ CompleteCryptoHandshake();
+
+ connection_ = new PacketSavingConnection(true);
+ session_.reset(new TestClientSession(connection_, DefaultQuicConfig()));
+ stream_.reset(new QuicCryptoClientStream(server_id_, session_.get(), nullptr,
+ &crypto_config_));
+
+ session_->SetCryptoStream(stream_.get());
+ session_->config()->SetDefaults();
+
+ // Advance time 5 years to ensure that we pass the expiry time of the cached
+ // server config.
+ reinterpret_cast<MockClock*>(const_cast<QuicClock*>(connection_->clock()))
+ ->AdvanceTime(QuicTime::Delta::FromSeconds(60 * 60 * 24 * 365 * 5));
+
+ // Check that a client hello was sent and that CryptoConnect doesn't fail
+ // with an error.
+ EXPECT_TRUE(stream_->CryptoConnect());
+ ASSERT_EQ(1u, connection_->packets_.size());
+}
+
+TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdate) {
+ // Test that the crypto client stream can receive server config updates after
+ // the connection has been established.
+ CompleteCryptoHandshake();
+
+ QuicCryptoClientConfig::CachedState* state =
+ crypto_config_.LookupOrCreate(server_id_);
+
+ // Ensure cached STK is different to what we send in the handshake.
+ EXPECT_NE("xstk", state->source_address_token());
+
+ // Initialize using {...} syntax to avoid trailing \0 if converting from
+ // string.
+ unsigned char stk[] = { 'x', 's', 't', 'k' };
+
+ // Minimum SCFG that passes config validation checks.
+ unsigned char scfg[] = {
+ // SCFG
+ 0x53, 0x43, 0x46, 0x47,
+ // num entries
+ 0x01, 0x00,
+ // padding
+ 0x00, 0x00,
+ // EXPY
+ 0x45, 0x58, 0x50, 0x59,
+ // EXPY end offset
+ 0x08, 0x00, 0x00, 0x00,
+ // Value
+ '1', '2', '3', '4',
+ '5', '6', '7', '8'
+ };
+
+ CryptoHandshakeMessage server_config_update;
+ server_config_update.set_tag(kSCUP);
+ server_config_update.SetValue(kSourceAddressTokenTag, stk);
+ server_config_update.SetValue(kSCFG, scfg);
+
+ scoped_ptr<QuicData> data(
+ CryptoFramer::ConstructHandshakeMessage(server_config_update));
+ stream_->ProcessRawData(data->data(), data->length());
+
+ // Make sure that the STK and SCFG are cached correctly.
+ EXPECT_EQ("xstk", state->source_address_token());
+
+ string cached_scfg = state->server_config();
+ test::CompareCharArraysWithHexError(
+ "scfg", cached_scfg.data(), cached_scfg.length(),
+ QuicUtils::AsChars(scfg), arraysize(scfg));
+}
+
+TEST_F(QuicCryptoClientStreamTest, ServerConfigUpdateBeforeHandshake) {
+ EXPECT_CALL(*connection_, SendConnectionClose(
+ QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE));
+ CryptoHandshakeMessage server_config_update;
+ server_config_update.set_tag(kSCUP);
+ scoped_ptr<QuicData> data(
+ CryptoFramer::ConstructHandshakeMessage(server_config_update));
+ stream_->ProcessRawData(data->data(), data->length());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc
new file mode 100644
index 0000000..eebab90
--- /dev/null
+++ b/net/quic/quic_crypto_server_stream.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_crypto_server_stream.h"
+
+#include "base/base64.h"
+#include "crypto/secure_hash.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/crypto/source_address_token.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+void ServerHelloNotifier::OnAckNotification(
+ int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) {
+ server_stream_->OnServerHelloAcked();
+}
+
+QuicCryptoServerStream::QuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config,
+ QuicSession* session)
+ : QuicCryptoStream(session),
+ crypto_config_(crypto_config),
+ validate_client_hello_cb_(nullptr),
+ num_handshake_messages_(0),
+ num_server_config_update_messages_sent_(0) {}
+
+QuicCryptoServerStream::~QuicCryptoServerStream() {
+ CancelOutstandingCallbacks();
+}
+
+void QuicCryptoServerStream::CancelOutstandingCallbacks() {
+ // Detach from the validation callback. Calling this multiple times is safe.
+ if (validate_client_hello_cb_ != nullptr) {
+ validate_client_hello_cb_->Cancel();
+ }
+}
+
+void QuicCryptoServerStream::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ QuicCryptoStream::OnHandshakeMessage(message);
+ ++num_handshake_messages_;
+
+ // Do not process handshake messages after the handshake is confirmed.
+ if (handshake_confirmed_) {
+ CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
+ return;
+ }
+
+ if (message.tag() != kCHLO) {
+ CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
+ return;
+ }
+
+ if (validate_client_hello_cb_ != nullptr) {
+ // Already processing some other handshake message. The protocol
+ // does not allow for clients to send multiple handshake messages
+ // before the server has a chance to respond.
+ CloseConnection(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO);
+ return;
+ }
+
+ validate_client_hello_cb_ = new ValidateCallback(this);
+ return crypto_config_.ValidateClientHello(
+ message,
+ session()->connection()->peer_address(),
+ session()->connection()->clock(),
+ validate_client_hello_cb_);
+}
+
+void QuicCryptoServerStream::FinishProcessingHandshakeMessage(
+ const CryptoHandshakeMessage& message,
+ const ValidateClientHelloResultCallback::Result& result) {
+ // Clear the callback that got us here.
+ DCHECK(validate_client_hello_cb_ != nullptr);
+ validate_client_hello_cb_ = nullptr;
+
+ string error_details;
+ CryptoHandshakeMessage reply;
+ QuicErrorCode error = ProcessClientHello(
+ message, result, &reply, &error_details);
+
+ if (error != QUIC_NO_ERROR) {
+ CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+
+ if (reply.tag() != kSHLO) {
+ SendHandshakeMessage(reply);
+ return;
+ }
+
+ // If we are returning a SHLO then we accepted the handshake.
+ QuicConfig* config = session()->config();
+ OverrideQuicConfigDefaults(config);
+ error = config->ProcessPeerHello(message, CLIENT, &error_details);
+ if (error != QUIC_NO_ERROR) {
+ CloseConnectionWithDetails(error, error_details);
+ return;
+ }
+ session()->OnConfigNegotiated();
+
+ config->ToHandshakeMessage(&reply);
+
+ // Receiving a full CHLO implies the client is prepared to decrypt with
+ // the new server write key. We can start to encrypt with the new server
+ // write key.
+ //
+ // NOTE: the SHLO will be encrypted with the new server write key.
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_INITIAL,
+ crypto_negotiated_params_.initial_crypters.encrypter.release());
+ session()->connection()->SetDefaultEncryptionLevel(
+ ENCRYPTION_INITIAL);
+ // Set the decrypter immediately so that we no longer accept unencrypted
+ // packets.
+ session()->connection()->SetDecrypter(
+ crypto_negotiated_params_.initial_crypters.decrypter.release(),
+ ENCRYPTION_INITIAL);
+
+ // We want to be notified when the SHLO is ACKed so that we can disable
+ // HANDSHAKE_MODE in the sent packet manager.
+ if (session()->connection()->version() <= QUIC_VERSION_21) {
+ SendHandshakeMessage(reply);
+ } else {
+ scoped_refptr<ServerHelloNotifier> server_hello_notifier(
+ new ServerHelloNotifier(this));
+ SendHandshakeMessage(reply, server_hello_notifier.get());
+ }
+
+ session()->connection()->SetEncrypter(
+ ENCRYPTION_FORWARD_SECURE,
+ crypto_negotiated_params_.forward_secure_crypters.encrypter.release());
+ session()->connection()->SetDefaultEncryptionLevel(
+ ENCRYPTION_FORWARD_SECURE);
+ session()->connection()->SetAlternativeDecrypter(
+ crypto_negotiated_params_.forward_secure_crypters.decrypter.release(),
+ ENCRYPTION_FORWARD_SECURE, false /* don't latch */);
+
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+
+ // Now that the handshake is complete, send an updated server config and
+ // source-address token to the client.
+ SendServerConfigUpdate(nullptr);
+}
+
+void QuicCryptoServerStream::SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params) {
+ if (session()->connection()->version() <= QUIC_VERSION_21 ||
+ !handshake_confirmed_) {
+ return;
+ }
+
+ CryptoHandshakeMessage server_config_update_message;
+ if (!crypto_config_.BuildServerConfigUpdateMessage(
+ session()->connection()->peer_address(),
+ session()->connection()->clock(),
+ session()->connection()->random_generator(),
+ crypto_negotiated_params_,
+ cached_network_params,
+ &server_config_update_message)) {
+ DVLOG(1) << "Server: Failed to build server config update (SCUP)!";
+ return;
+ }
+
+ DVLOG(1) << "Server: Sending server config update: "
+ << server_config_update_message.DebugString();
+ const QuicData& data = server_config_update_message.GetSerialized();
+ WriteOrBufferData(string(data.data(), data.length()), false, nullptr);
+
+ ++num_server_config_update_messages_sent_;
+}
+
+void QuicCryptoServerStream::OnServerHelloAcked() {
+ session()->connection()->OnHandshakeComplete();
+}
+
+bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID(
+ string* output) const {
+ if (!encryption_established_ ||
+ crypto_negotiated_params_.channel_id.empty()) {
+ return false;
+ }
+
+ const string& channel_id(crypto_negotiated_params_.channel_id);
+ scoped_ptr<crypto::SecureHash> hash(
+ crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+ hash->Update(channel_id.data(), channel_id.size());
+ uint8 digest[32];
+ hash->Finish(digest, sizeof(digest));
+
+ base::Base64Encode(string(
+ reinterpret_cast<const char*>(digest), sizeof(digest)), output);
+ // Remove padding.
+ size_t len = output->size();
+ if (len >= 2) {
+ if ((*output)[len - 1] == '=') {
+ len--;
+ if ((*output)[len - 1] == '=') {
+ len--;
+ }
+ output->resize(len);
+ }
+ }
+ return true;
+}
+
+QuicErrorCode QuicCryptoServerStream::ProcessClientHello(
+ const CryptoHandshakeMessage& message,
+ const ValidateClientHelloResultCallback::Result& result,
+ CryptoHandshakeMessage* reply,
+ string* error_details) {
+ return crypto_config_.ProcessClientHello(
+ result,
+ session()->connection()->connection_id(),
+ session()->connection()->peer_address(),
+ session()->connection()->version(),
+ session()->connection()->supported_versions(),
+ session()->connection()->clock(),
+ session()->connection()->random_generator(),
+ &crypto_negotiated_params_, reply, error_details);
+}
+
+void QuicCryptoServerStream::OverrideQuicConfigDefaults(QuicConfig* config) {
+}
+
+QuicCryptoServerStream::ValidateCallback::ValidateCallback(
+ QuicCryptoServerStream* parent) : parent_(parent) {
+}
+
+void QuicCryptoServerStream::ValidateCallback::Cancel() { parent_ = nullptr; }
+
+void QuicCryptoServerStream::ValidateCallback::RunImpl(
+ const CryptoHandshakeMessage& client_hello,
+ const Result& result) {
+ if (parent_ != nullptr) {
+ parent_->FinishProcessingHandshakeMessage(client_hello, result);
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h
new file mode 100644
index 0000000..46ac4f3
--- /dev/null
+++ b/net/quic/quic_crypto_server_stream.h
@@ -0,0 +1,141 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CRYPTO_SERVER_STREAM_H_
+#define NET_QUIC_QUIC_CRYPTO_SERVER_STREAM_H_
+
+#include <string>
+
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_crypto_stream.h"
+
+namespace net {
+
+class CachedNetworkParameters;
+class CryptoHandshakeMessage;
+class QuicCryptoServerConfig;
+class QuicCryptoServerStream;
+class QuicSession;
+
+namespace test {
+class CryptoTestUtils;
+} // namespace test
+
+// Receives a notification when the server hello (SHLO) has been ACKed by the
+// peer. At this point we disable HANDSHAKE_MODE in the sent packet manager.
+class NET_EXPORT_PRIVATE ServerHelloNotifier : public
+ QuicAckNotifier::DelegateInterface {
+ public:
+ explicit ServerHelloNotifier(QuicCryptoServerStream* stream)
+ : server_stream_(stream) {}
+
+ // QuicAckNotifier::DelegateInterface implementation
+ virtual void OnAckNotification(
+ int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed) OVERRIDE;
+
+ private:
+ virtual ~ServerHelloNotifier() {}
+
+ QuicCryptoServerStream* server_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerHelloNotifier);
+};
+
+class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream {
+ public:
+ QuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config,
+ QuicSession* session);
+ virtual ~QuicCryptoServerStream();
+
+ // Cancel any outstanding callbacks, such as asynchronous validation of client
+ // hello.
+ void CancelOutstandingCallbacks();
+
+ // CryptoFramerVisitorInterface implementation
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE;
+
+ // GetBase64SHA256ClientChannelID sets |*output| to the base64 encoded,
+ // SHA-256 hash of the client's ChannelID key and returns true, if the client
+ // presented a ChannelID. Otherwise it returns false.
+ bool GetBase64SHA256ClientChannelID(std::string* output) const;
+
+ uint8 num_handshake_messages() const { return num_handshake_messages_; }
+
+ int num_server_config_update_messages_sent() const {
+ return num_server_config_update_messages_sent_;
+ }
+
+ // Sends the latest server config and source-address token to the client.
+ virtual void SendServerConfigUpdate(
+ const CachedNetworkParameters* cached_network_params);
+
+ // Called by the ServerHello AckNotifier once the SHLO has been ACKed by the
+ // client.
+ void OnServerHelloAcked();
+
+ protected:
+ virtual QuicErrorCode ProcessClientHello(
+ const CryptoHandshakeMessage& message,
+ const ValidateClientHelloResultCallback::Result& result,
+ CryptoHandshakeMessage* reply,
+ std::string* error_details);
+
+ // Hook that allows the server to set QuicConfig defaults just
+ // before going through the parameter negotiation step.
+ virtual void OverrideQuicConfigDefaults(QuicConfig* config);
+
+ private:
+ friend class test::CryptoTestUtils;
+
+ class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+ explicit ValidateCallback(QuicCryptoServerStream* parent);
+ // To allow the parent to detach itself from the callback before deletion.
+ void Cancel();
+
+ // From ValidateClientHelloResultCallback
+ virtual void RunImpl(const CryptoHandshakeMessage& client_hello,
+ const Result& result) OVERRIDE;
+
+ private:
+ QuicCryptoServerStream* parent_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValidateCallback);
+ };
+
+ // Invoked by ValidateCallback::RunImpl once initial validation of
+ // the client hello is complete. Finishes processing of the client
+ // hello message and handles handshake success/failure.
+ void FinishProcessingHandshakeMessage(
+ const CryptoHandshakeMessage& message,
+ const ValidateClientHelloResultCallback::Result& result);
+
+ // crypto_config_ contains crypto parameters for the handshake.
+ const QuicCryptoServerConfig& crypto_config_;
+
+ // Pointer to the active callback that will receive the result of
+ // the client hello validation request and forward it to
+ // FinishProcessingHandshakeMessage for processing. nullptr if no
+ // handshake message is being validated.
+ ValidateCallback* validate_client_hello_cb_;
+
+ // Number of handshake messages received by this stream.
+ uint8 num_handshake_messages_;
+
+ // Number of server config update (SCUP) messages sent by this stream.
+ int num_server_config_update_messages_sent_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CRYPTO_SERVER_STREAM_H_
diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc
new file mode 100644
index 0000000..6ce059b
--- /dev/null
+++ b/net/quic/quic_crypto_server_stream_test.cc
@@ -0,0 +1,286 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_crypto_server_stream.h"
+
+#include <map>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/crypto/aes_128_gcm_12_encrypter.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/delayed_verify_strike_register_client.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+class QuicConnection;
+class ReliableQuicStream;
+} // namespace net
+
+using std::pair;
+using testing::_;
+
+namespace net {
+namespace test {
+
+class QuicCryptoServerConfigPeer {
+ public:
+ static string GetPrimaryOrbit(const QuicCryptoServerConfig& config) {
+ base::AutoLock lock(config.configs_lock_);
+ CHECK(config.primary_config_.get() != nullptr);
+ return string(reinterpret_cast<const char*>(config.primary_config_->orbit),
+ kOrbitSize);
+ }
+};
+
+namespace {
+
+const char kServerHostname[] = "test.example.com";
+const uint16 kServerPort = 80;
+
+class QuicCryptoServerStreamTest : public ::testing::TestWithParam<bool> {
+ public:
+ QuicCryptoServerStreamTest()
+ : connection_(new PacketSavingConnection(true)),
+ session_(connection_, DefaultQuicConfig()),
+ crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance()),
+ stream_(crypto_config_, &session_),
+ strike_register_client_(nullptr) {
+ config_.SetDefaults();
+ session_.config()->SetDefaults();
+ session_.SetCryptoStream(&stream_);
+ // We advance the clock initially because the default time is zero and the
+ // strike register worries that we've just overflowed a uint32 time.
+ connection_->AdvanceTime(QuicTime::Delta::FromSeconds(100000));
+ // TODO(wtc): replace this with ProofSourceForTesting() when Chromium has
+ // a working ProofSourceForTesting().
+ crypto_config_.SetProofSource(CryptoTestUtils::FakeProofSourceForTesting());
+ crypto_config_.set_strike_register_no_startup_period();
+
+ CryptoTestUtils::SetupCryptoServerConfigForTest(
+ connection_->clock(), connection_->random_generator(),
+ session_.config(), &crypto_config_);
+
+ if (AsyncStrikeRegisterVerification()) {
+ string orbit =
+ QuicCryptoServerConfigPeer::GetPrimaryOrbit(crypto_config_);
+ strike_register_client_ = new DelayedVerifyStrikeRegisterClient(
+ 10000, // strike_register_max_entries
+ static_cast<uint32>(connection_->clock()->WallNow().ToUNIXSeconds()),
+ 60, // strike_register_window_secs
+ reinterpret_cast<const uint8 *>(orbit.data()),
+ StrikeRegister::NO_STARTUP_PERIOD_NEEDED);
+ strike_register_client_->StartDelayingVerification();
+ crypto_config_.SetStrikeRegisterClient(strike_register_client_);
+ }
+ }
+
+ bool AsyncStrikeRegisterVerification() {
+ return GetParam();
+ }
+
+ void ConstructHandshakeMessage() {
+ CryptoFramer framer;
+ message_data_.reset(framer.ConstructHandshakeMessage(message_));
+ }
+
+ int CompleteCryptoHandshake() {
+ return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_,
+ client_options_);
+ }
+
+ protected:
+ PacketSavingConnection* connection_;
+ TestClientSession session_;
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ QuicCryptoServerStream stream_;
+ CryptoHandshakeMessage message_;
+ scoped_ptr<QuicData> message_data_;
+ CryptoTestUtils::FakeClientOptions client_options_;
+ DelayedVerifyStrikeRegisterClient* strike_register_client_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicCryptoServerStreamTest, testing::Bool());
+
+TEST_P(QuicCryptoServerStreamTest, NotInitiallyConected) {
+ EXPECT_FALSE(stream_.encryption_established());
+ EXPECT_FALSE(stream_.handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) {
+ // CompleteCryptoHandshake returns the number of client hellos sent. This
+ // test should send:
+ // * One to get a source-address token and certificates.
+ // * One to complete the handshake.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(stream_.encryption_established());
+ EXPECT_TRUE(stream_.handshake_confirmed());
+ EXPECT_EQ(1, stream_.num_server_config_update_messages_sent());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ZeroRTT) {
+ PacketSavingConnection* client_conn = new PacketSavingConnection(false);
+ PacketSavingConnection* server_conn = new PacketSavingConnection(false);
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000));
+ server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000));
+
+ QuicConfig client_config;
+ client_config.SetDefaults();
+ scoped_ptr<TestClientSession> client_session(
+ new TestClientSession(client_conn, client_config));
+ QuicCryptoClientConfig client_crypto_config;
+ client_crypto_config.SetDefaults();
+
+ QuicServerId server_id(kServerHostname, kServerPort, false,
+ PRIVACY_MODE_DISABLED);
+ scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream(
+ server_id, client_session.get(), nullptr, &client_crypto_config));
+ client_session->SetCryptoStream(client.get());
+
+ // Do a first handshake in order to prime the client config with the server's
+ // information.
+ CHECK(client->CryptoConnect());
+ CHECK_EQ(1u, client_conn->packets_.size());
+
+ scoped_ptr<TestSession> server_session(new TestSession(server_conn, config_));
+ scoped_ptr<QuicCryptoServerStream> server(
+ new QuicCryptoServerStream(crypto_config_, server_session.get()));
+ server_session->SetCryptoStream(server.get());
+
+ CryptoTestUtils::CommunicateHandshakeMessages(
+ client_conn, client.get(), server_conn, server.get());
+ EXPECT_EQ(2, client->num_sent_client_hellos());
+
+ // Now do another handshake, hopefully in 0-RTT.
+ LOG(INFO) << "Resetting for 0-RTT handshake attempt";
+
+ client_conn = new PacketSavingConnection(false);
+ server_conn = new PacketSavingConnection(false);
+ // We need to advance time past the strike-server window so that it's
+ // authoritative in this time span.
+ client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(102000));
+ server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(102000));
+
+ // This causes the client's nonce to be different and thus stops the
+ // strike-register from rejecting the repeated nonce.
+ reinterpret_cast<MockRandom*>(client_conn->random_generator())->ChangeValue();
+ client_session.reset(new TestClientSession(client_conn, client_config));
+ server_session.reset(new TestSession(server_conn, config_));
+ client.reset(new QuicCryptoClientStream(server_id, client_session.get(),
+ nullptr, &client_crypto_config));
+ client_session->SetCryptoStream(client.get());
+
+ server.reset(new QuicCryptoServerStream(crypto_config_,
+ server_session.get()));
+ server_session->SetCryptoStream(server.get());
+
+ CHECK(client->CryptoConnect());
+
+ if (AsyncStrikeRegisterVerification()) {
+ EXPECT_FALSE(client->handshake_confirmed());
+ EXPECT_FALSE(server->handshake_confirmed());
+
+ // Advance the handshake. Expect that the server will be stuck
+ // waiting for client nonce verification to complete.
+ pair<size_t, size_t> messages_moved = CryptoTestUtils::AdvanceHandshake(
+ client_conn, client.get(), 0, server_conn, server.get(), 0);
+ EXPECT_EQ(1u, messages_moved.first);
+ EXPECT_EQ(0u, messages_moved.second);
+ EXPECT_EQ(1, strike_register_client_->PendingVerifications());
+ EXPECT_FALSE(client->handshake_confirmed());
+ EXPECT_FALSE(server->handshake_confirmed());
+
+ // The server handshake completes once the nonce verification completes.
+ strike_register_client_->RunPendingVerifications();
+ EXPECT_FALSE(client->handshake_confirmed());
+ EXPECT_TRUE(server->handshake_confirmed());
+
+ messages_moved = CryptoTestUtils::AdvanceHandshake(
+ client_conn, client.get(), messages_moved.first,
+ server_conn, server.get(), messages_moved.second);
+ EXPECT_EQ(1u, messages_moved.first);
+ EXPECT_EQ(1u, messages_moved.second);
+ EXPECT_TRUE(client->handshake_confirmed());
+ EXPECT_TRUE(server->handshake_confirmed());
+ } else {
+ CryptoTestUtils::CommunicateHandshakeMessages(
+ client_conn, client.get(), server_conn, server.get());
+ }
+
+ EXPECT_EQ(1, client->num_sent_client_hellos());
+ EXPECT_EQ(1, server->num_server_config_update_messages_sent());
+}
+
+TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) {
+ CompleteCryptoHandshake();
+ EXPECT_CALL(*connection_, SendConnectionClose(
+ QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE));
+ message_.set_tag(kCHLO);
+ ConstructHandshakeMessage();
+ stream_.ProcessRawData(message_data_->data(), message_data_->length());
+}
+
+TEST_P(QuicCryptoServerStreamTest, BadMessageType) {
+ message_.set_tag(kSHLO);
+ ConstructHandshakeMessage();
+ EXPECT_CALL(*connection_, SendConnectionClose(
+ QUIC_INVALID_CRYPTO_MESSAGE_TYPE));
+ stream_.ProcessRawData(message_data_->data(), message_data_->length());
+}
+
+TEST_P(QuicCryptoServerStreamTest, WithoutCertificates) {
+ crypto_config_.SetProofSource(nullptr);
+ client_options_.dont_verify_certs = true;
+
+ // Only 2 client hellos need to be sent in the no-certs case: one to get the
+ // source-address token and the second to finish.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(stream_.encryption_established());
+ EXPECT_TRUE(stream_.handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ChannelID) {
+ client_options_.channel_id_enabled = true;
+ client_options_.channel_id_source_async = false;
+ // CompleteCryptoHandshake verifies
+ // stream_.crypto_negotiated_params().channel_id is correct.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(stream_.encryption_established());
+ EXPECT_TRUE(stream_.handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, ChannelIDAsync) {
+ client_options_.channel_id_enabled = true;
+ client_options_.channel_id_source_async = true;
+ // CompleteCryptoHandshake verifies
+ // stream_.crypto_negotiated_params().channel_id is correct.
+ EXPECT_EQ(2, CompleteCryptoHandshake());
+ EXPECT_TRUE(stream_.encryption_established());
+ EXPECT_TRUE(stream_.handshake_confirmed());
+}
+
+TEST_P(QuicCryptoServerStreamTest, OnlySendSCUPAfterHandshakeComplete) {
+ // An attempt to send a SCUP before completing handshake should fail.
+ stream_.SendServerConfigUpdate(nullptr);
+ EXPECT_EQ(0, stream_.num_server_config_update_messages_sent());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc
new file mode 100644
index 0000000..bef06aa
--- /dev/null
+++ b/net/quic/quic_crypto_stream.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_crypto_stream.h"
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/quic_utils.h"
+
+using std::string;
+using base::StringPiece;
+
+namespace net {
+
+#define ENDPOINT (session()->is_server() ? "Server: " : " Client: ")
+
+QuicCryptoStream::QuicCryptoStream(QuicSession* session)
+ : ReliableQuicStream(kCryptoStreamId, session),
+ encryption_established_(false),
+ handshake_confirmed_(false) {
+ crypto_framer_.set_visitor(this);
+ if (version() < QUIC_VERSION_21) {
+ // Prior to QUIC_VERSION_21 the crypto stream is not subject to any flow
+ // control.
+ DisableFlowControl();
+ }
+ // The crypto stream is exempt from connection level flow control.
+ DisableConnectionFlowControlForThisStream();
+}
+
+void QuicCryptoStream::OnError(CryptoFramer* framer) {
+ DLOG(WARNING) << "Error processing crypto data: "
+ << QuicUtils::ErrorToString(framer->error());
+}
+
+void QuicCryptoStream::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ DVLOG(1) << ENDPOINT << "Received " << message.DebugString();
+ session()->OnCryptoHandshakeMessageReceived(message);
+}
+
+uint32 QuicCryptoStream::ProcessRawData(const char* data,
+ uint32 data_len) {
+ if (!crypto_framer_.ProcessInput(StringPiece(data, data_len))) {
+ CloseConnection(crypto_framer_.error());
+ return 0;
+ }
+ return data_len;
+}
+
+QuicPriority QuicCryptoStream::EffectivePriority() const {
+ return QuicUtils::HighestPriority();
+}
+
+void QuicCryptoStream::SendHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ SendHandshakeMessage(message, nullptr);
+}
+
+void QuicCryptoStream::SendHandshakeMessage(
+ const CryptoHandshakeMessage& message,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ DVLOG(1) << ENDPOINT << "Sending " << message.DebugString();
+ session()->OnCryptoHandshakeMessageSent(message);
+ const QuicData& data = message.GetSerialized();
+ // TODO(wtc): check the return value.
+ WriteOrBufferData(string(data.data(), data.length()), false, delegate);
+}
+
+bool QuicCryptoStream::ExportKeyingMaterial(
+ StringPiece label,
+ StringPiece context,
+ size_t result_len,
+ string* result) const {
+ if (!handshake_confirmed()) {
+ DLOG(ERROR) << "ExportKeyingMaterial was called before forward-secure"
+ << "encryption was established.";
+ return false;
+ }
+ return CryptoUtils::ExportKeyingMaterial(
+ crypto_negotiated_params_.subkey_secret,
+ label,
+ context,
+ result_len,
+ result);
+}
+
+const QuicCryptoNegotiatedParameters&
+QuicCryptoStream::crypto_negotiated_params() const {
+ return crypto_negotiated_params_;
+}
+
+} // namespace net
diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h
new file mode 100644
index 0000000..dc8b227
--- /dev/null
+++ b/net/quic/quic_crypto_stream.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_CRYPTO_STREAM_H_
+#define NET_QUIC_QUIC_CRYPTO_STREAM_H_
+
+#include "base/basictypes.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+
+class CryptoHandshakeMessage;
+class QuicSession;
+
+// Crypto handshake messages in QUIC take place over a reserved
+// reliable stream with the id 1. Each endpoint (client and server)
+// will allocate an instance of a subclass of QuicCryptoStream
+// to send and receive handshake messages. (In the normal 1-RTT
+// handshake, the client will send a client hello, CHLO, message.
+// The server will receive this message and respond with a server
+// hello message, SHLO. At this point both sides will have established
+// a crypto context they can use to send encrypted messages.
+//
+// For more details: http://goto.google.com/quic-crypto
+class NET_EXPORT_PRIVATE QuicCryptoStream
+ : public ReliableQuicStream,
+ public CryptoFramerVisitorInterface {
+ public:
+ explicit QuicCryptoStream(QuicSession* session);
+
+ // CryptoFramerVisitorInterface implementation
+ virtual void OnError(CryptoFramer* framer) OVERRIDE;
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE;
+
+ // ReliableQuicStream implementation
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ // Sends |message| to the peer.
+ // TODO(wtc): return a success/failure status.
+ void SendHandshakeMessage(const CryptoHandshakeMessage& message);
+ // As above, but registers |delegate| for notification when |message| has been
+ // ACKed by the peer.
+ void SendHandshakeMessage(const CryptoHandshakeMessage& message,
+ QuicAckNotifier::DelegateInterface* delegate);
+
+ // Performs key extraction to derive a new secret of |result_len| bytes
+ // dependent on |label|, |context|, and the stream's negotiated subkey secret.
+ // Returns false if the handshake has not been confirmed or the parameters are
+ // invalid (e.g. |label| contains null bytes); returns true on success.
+ bool ExportKeyingMaterial(base::StringPiece label,
+ base::StringPiece context,
+ size_t result_len,
+ std::string* result) const;
+
+ bool encryption_established() const { return encryption_established_; }
+ bool handshake_confirmed() const { return handshake_confirmed_; }
+
+ const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const;
+
+ protected:
+ bool encryption_established_;
+ bool handshake_confirmed_;
+
+ QuicCryptoNegotiatedParameters crypto_negotiated_params_;
+
+ private:
+ CryptoFramer crypto_framer_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_CRYPTO_STREAM_H_
diff --git a/net/quic/quic_crypto_stream_test.cc b/net/quic/quic_crypto_stream_test.cc
new file mode 100644
index 0000000..4ea8f49
--- /dev/null
+++ b/net/quic/quic_crypto_stream_test.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_crypto_stream.h"
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockQuicCryptoStream : public QuicCryptoStream {
+ public:
+ explicit MockQuicCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session) {
+ }
+
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE {
+ messages_.push_back(message);
+ }
+
+ vector<CryptoHandshakeMessage>* messages() {
+ return &messages_;
+ }
+
+ private:
+ vector<CryptoHandshakeMessage> messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockQuicCryptoStream);
+};
+
+class QuicCryptoStreamTest : public ::testing::Test {
+ public:
+ QuicCryptoStreamTest()
+ : connection_(new MockConnection(false)),
+ session_(connection_),
+ stream_(&session_) {
+ message_.set_tag(kSHLO);
+ message_.SetStringPiece(1, "abc");
+ message_.SetStringPiece(2, "def");
+ ConstructHandshakeMessage();
+ }
+
+ void ConstructHandshakeMessage() {
+ CryptoFramer framer;
+ message_data_.reset(framer.ConstructHandshakeMessage(message_));
+ }
+
+ protected:
+ MockConnection* connection_;
+ MockSession session_;
+ MockQuicCryptoStream stream_;
+ CryptoHandshakeMessage message_;
+ scoped_ptr<QuicData> message_data_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicCryptoStreamTest);
+};
+
+TEST_F(QuicCryptoStreamTest, NotInitiallyConected) {
+ EXPECT_FALSE(stream_.encryption_established());
+ EXPECT_FALSE(stream_.handshake_confirmed());
+}
+
+TEST_F(QuicCryptoStreamTest, ProcessRawData) {
+ EXPECT_EQ(message_data_->length(),
+ stream_.ProcessRawData(message_data_->data(),
+ message_data_->length()));
+ ASSERT_EQ(1u, stream_.messages()->size());
+ const CryptoHandshakeMessage& message = (*stream_.messages())[0];
+ EXPECT_EQ(kSHLO, message.tag());
+ EXPECT_EQ(2u, message.tag_value_map().size());
+ EXPECT_EQ("abc", CryptoTestUtils::GetValueForTag(message, 1));
+ EXPECT_EQ("def", CryptoTestUtils::GetValueForTag(message, 2));
+}
+
+TEST_F(QuicCryptoStreamTest, ProcessBadData) {
+ string bad(message_data_->data(), message_data_->length());
+ const int kFirstTagIndex = sizeof(uint32) + // message tag
+ sizeof(uint16) + // number of tag-value pairs
+ sizeof(uint16); // padding
+ EXPECT_EQ(1, bad[kFirstTagIndex]);
+ bad[kFirstTagIndex] = 0x7F; // out of order tag
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_CRYPTO_TAGS_OUT_OF_ORDER));
+ EXPECT_EQ(0u, stream_.ProcessRawData(bad.data(), bad.length()));
+}
+
+TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) {
+ if (connection_->version() < QUIC_VERSION_21) {
+ EXPECT_FALSE(stream_.flow_controller()->IsEnabled());
+ } else {
+ EXPECT_TRUE(stream_.flow_controller()->IsEnabled());
+ }
+ EXPECT_FALSE(ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl(
+ &stream_));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_data_reader.cc b/net/quic/quic_data_reader.cc
new file mode 100644
index 0000000..b9a6acf
--- /dev/null
+++ b/net/quic/quic_data_reader.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_data_reader.h"
+
+#include "net/base/int128.h"
+#include "net/quic/quic_protocol.h"
+
+using base::StringPiece;
+
+namespace net {
+
+QuicDataReader::QuicDataReader(const char* data, const size_t len)
+ : data_(data),
+ len_(len),
+ pos_(0) {
+}
+
+bool QuicDataReader::ReadUInt16(uint16* result) {
+ return ReadBytes(result, sizeof(*result));
+}
+
+bool QuicDataReader::ReadUInt32(uint32* result) {
+ return ReadBytes(result, sizeof(*result));
+}
+
+bool QuicDataReader::ReadUInt48(uint64* result) {
+ uint32 lo;
+ if (!ReadUInt32(&lo)) {
+ return false;
+ }
+
+ uint16 hi;
+ if (!ReadUInt16(&hi)) {
+ return false;
+ }
+
+ *result = hi;
+ *result <<= 32;
+ *result += lo;
+
+ return true;
+}
+
+bool QuicDataReader::ReadUInt64(uint64* result) {
+ return ReadBytes(result, sizeof(*result));
+}
+
+bool QuicDataReader::ReadUInt128(uint128* result) {
+ uint64 high_hash;
+ uint64 low_hash;
+
+ if (!ReadUInt64(&low_hash)) {
+ return false;
+ }
+ if (!ReadUInt64(&high_hash)) {
+ return false;
+ }
+
+ *result = uint128(high_hash, low_hash);
+ return true;
+}
+
+bool QuicDataReader::ReadUFloat16(uint64* result) {
+ uint16 value;
+ if (!ReadUInt16(&value)) {
+ return false;
+ }
+
+ *result = value;
+ if (*result < (1 << kUFloat16MantissaEffectiveBits)) {
+ // Fast path: either the value is denormalized (no hidden bit), or
+ // normalized (hidden bit set, exponent offset by one) with exponent zero.
+ // Zero exponent offset by one sets the bit exactly where the hidden bit is.
+ // So in both cases the value encodes itself.
+ return true;
+ }
+
+ uint16 exponent = value >> kUFloat16MantissaBits; // No sign extend on uint!
+ // After the fast pass, the exponent is at least one (offset by one).
+ // Un-offset the exponent.
+ --exponent;
+ DCHECK_GE(exponent, 1);
+ DCHECK_LE(exponent, kUFloat16MaxExponent);
+ // Here we need to clear the exponent and set the hidden bit. We have already
+ // decremented the exponent, so when we subtract it, it leaves behind the
+ // hidden bit.
+ *result -= exponent << kUFloat16MantissaBits;
+ *result <<= exponent;
+ DCHECK_GE(value, 1 << kUFloat16MantissaEffectiveBits);
+ DCHECK_LE(value, kUFloat16MaxValue);
+ return true;
+}
+
+bool QuicDataReader::ReadStringPiece16(StringPiece* result) {
+ // Read resultant length.
+ uint16 result_len;
+ if (!ReadUInt16(&result_len)) {
+ // OnFailure() already called.
+ return false;
+ }
+
+ return ReadStringPiece(result, result_len);
+}
+
+bool QuicDataReader::ReadStringPiece(StringPiece* result, size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Set result.
+ result->set(data_ + pos_, size);
+
+ // Iterate.
+ pos_ += size;
+
+ return true;
+}
+
+StringPiece QuicDataReader::ReadRemainingPayload() {
+ StringPiece payload = PeekRemainingPayload();
+ pos_ = len_;
+ return payload;
+}
+
+StringPiece QuicDataReader::PeekRemainingPayload() {
+ return StringPiece(data_ + pos_, len_ - pos_);
+}
+
+bool QuicDataReader::ReadBytes(void* result, size_t size) {
+ // Make sure that we have enough data to read.
+ if (!CanRead(size)) {
+ OnFailure();
+ return false;
+ }
+
+ // Read into result.
+ memcpy(result, data_ + pos_, size);
+
+ // Iterate.
+ pos_ += size;
+
+ return true;
+}
+
+bool QuicDataReader::IsDoneReading() const {
+ return len_ == pos_;
+}
+
+size_t QuicDataReader::BytesRemaining() const {
+ return len_ - pos_;
+}
+
+bool QuicDataReader::CanRead(size_t bytes) const {
+ return bytes <= (len_ - pos_);
+}
+
+void QuicDataReader::OnFailure() {
+ // Set our iterator to the end of the buffer so that further reads fail
+ // immediately.
+ pos_ = len_;
+}
+
+} // namespace net
diff --git a/net/quic/quic_data_reader.h b/net/quic/quic_data_reader.h
new file mode 100644
index 0000000..1686de0
--- /dev/null
+++ b/net/quic/quic_data_reader.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_DATA_READER_H_
+#define NET_QUIC_QUIC_DATA_READER_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/int128.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Used for reading QUIC data. Though there isn't really anything terribly
+// QUIC-specific here, it's a helper class that's useful when doing QUIC
+// framing.
+//
+// To use, simply construct a QuicDataReader using the underlying buffer that
+// you'd like to read fields from, then call one of the Read*() methods to
+// actually do some reading.
+//
+// This class keeps an internal iterator to keep track of what's already been
+// read and each successive Read*() call automatically increments said iterator
+// on success. On failure, internal state of the QuicDataReader should not be
+// trusted and it is up to the caller to throw away the failed instance and
+// handle the error as appropriate. None of the Read*() methods should ever be
+// called after failure, as they will also fail immediately.
+class NET_EXPORT_PRIVATE QuicDataReader {
+ public:
+ // Caller must provide an underlying buffer to work on.
+ QuicDataReader(const char* data, const size_t len);
+
+ // Empty destructor.
+ ~QuicDataReader() {}
+
+ // Reads a 16-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt16(uint16* result);
+
+ // Reads a 32-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt32(uint32* result);
+
+ // Reads a 48-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt48(uint64* result);
+
+ // Reads a 64-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt64(uint64* result);
+
+ // Reads a 128-bit unsigned integer into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUInt128(uint128* result);
+
+ // Reads a 16-bit unsigned float into the given output parameter.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadUFloat16(uint64* result);
+
+ // Reads a string prefixed with 16-bit length into the given output parameter.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece16(base::StringPiece* result);
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadStringPiece(base::StringPiece* result, size_t len);
+
+ // Returns the remaining payload as a StringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // Forwards the internal iterator.
+ base::StringPiece ReadRemainingPayload();
+
+ // Returns the remaining payload as a StringPiece.
+ //
+ // NOTE: Does not copy but rather references strings in the underlying buffer.
+ // This should be kept in mind when handling memory management!
+ //
+ // DOES NOT forward the internal iterator.
+ base::StringPiece PeekRemainingPayload();
+
+ // Reads a given number of bytes into the given buffer. The buffer
+ // must be of adequate size.
+ // Forwards the internal iterator on success.
+ // Returns true on success, false otherwise.
+ bool ReadBytes(void* result, size_t size);
+
+ // Returns true if the entirety of the underlying buffer has been read via
+ // Read*() calls.
+ bool IsDoneReading() const;
+
+ // Returns the number of bytes remaining to be read.
+ size_t BytesRemaining() const;
+
+ private:
+ // Returns true if the underlying buffer has enough room to read the given
+ // amount of bytes.
+ bool CanRead(size_t bytes) const;
+
+ // To be called when a read fails for any reason.
+ void OnFailure();
+
+ // The data buffer that we're reading from.
+ const char* data_;
+
+ // The length of the data buffer that we're reading from.
+ const size_t len_;
+
+ // The location of the next read from our data buffer.
+ size_t pos_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataReader);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DATA_READER_H_
diff --git a/net/quic/quic_data_stream.cc b/net/quic/quic_data_stream.cc
new file mode 100644
index 0000000..aac58f9
--- /dev/null
+++ b/net/quic/quic_data_stream.cc
@@ -0,0 +1,187 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_data_stream.h"
+
+#include "base/logging.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
+
+using base::StringPiece;
+using std::min;
+
+namespace net {
+
+#define ENDPOINT (session()->is_server() ? "Server: " : " Client: ")
+
+namespace {
+
+// This is somewhat arbitrary. It's possible, but unlikely, we will either fail
+// to set a priority client-side, or cancel a stream before stripping the
+// priority from the wire server-side. In either case, start out with a
+// priority in the middle.
+QuicPriority kDefaultPriority = 3;
+
+} // namespace
+
+QuicDataStream::QuicDataStream(QuicStreamId id,
+ QuicSession* session)
+ : ReliableQuicStream(id, session),
+ visitor_(nullptr),
+ headers_decompressed_(false),
+ priority_(kDefaultPriority),
+ decompression_failed_(false),
+ priority_parsed_(false) {
+ DCHECK_NE(kCryptoStreamId, id);
+ // Don't receive any callbacks from the sequencer until headers
+ // are complete.
+ sequencer()->SetBlockedUntilFlush();
+}
+
+QuicDataStream::~QuicDataStream() {
+}
+
+size_t QuicDataStream::WriteHeaders(
+ const SpdyHeaderBlock& header_block,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ size_t bytes_written = session()->WriteHeaders(
+ id(), header_block, fin, ack_notifier_delegate);
+ if (fin) {
+ // TODO(rch): Add test to ensure fin_sent_ is set whenever a fin is sent.
+ set_fin_sent(true);
+ CloseWriteSide();
+ }
+ return bytes_written;
+}
+
+size_t QuicDataStream::Readv(const struct iovec* iov, size_t iov_len) {
+ if (FinishedReadingHeaders()) {
+ // If the headers have been read, simply delegate to the sequencer's
+ // Readv method.
+ return sequencer()->Readv(iov, iov_len);
+ }
+ // Otherwise, copy decompressed header data into |iov|.
+ size_t bytes_consumed = 0;
+ size_t iov_index = 0;
+ while (iov_index < iov_len &&
+ decompressed_headers_.length() > bytes_consumed) {
+ size_t bytes_to_read = min(iov[iov_index].iov_len,
+ decompressed_headers_.length() - bytes_consumed);
+ char* iov_ptr = static_cast<char*>(iov[iov_index].iov_base);
+ memcpy(iov_ptr,
+ decompressed_headers_.data() + bytes_consumed, bytes_to_read);
+ bytes_consumed += bytes_to_read;
+ ++iov_index;
+ }
+ decompressed_headers_.erase(0, bytes_consumed);
+ if (FinishedReadingHeaders()) {
+ sequencer()->FlushBufferedFrames();
+ }
+ return bytes_consumed;
+}
+
+int QuicDataStream::GetReadableRegions(iovec* iov, size_t iov_len) {
+ if (FinishedReadingHeaders()) {
+ return sequencer()->GetReadableRegions(iov, iov_len);
+ }
+ if (iov_len == 0) {
+ return 0;
+ }
+ iov[0].iov_base = static_cast<void*>(
+ const_cast<char*>(decompressed_headers_.data()));
+ iov[0].iov_len = decompressed_headers_.length();
+ return 1;
+}
+
+bool QuicDataStream::IsDoneReading() const {
+ if (!headers_decompressed_ || !decompressed_headers_.empty()) {
+ return false;
+ }
+ return sequencer()->IsClosed();
+}
+
+bool QuicDataStream::HasBytesToRead() const {
+ return !decompressed_headers_.empty() || sequencer()->HasBytesToRead();
+}
+
+void QuicDataStream::set_priority(QuicPriority priority) {
+ DCHECK_EQ(0u, stream_bytes_written());
+ priority_ = priority;
+}
+
+QuicPriority QuicDataStream::EffectivePriority() const {
+ return priority();
+}
+
+uint32 QuicDataStream::ProcessRawData(const char* data, uint32 data_len) {
+ if (!FinishedReadingHeaders()) {
+ LOG(DFATAL) << "ProcessRawData called before headers have been finished";
+ return 0;
+ }
+ return ProcessData(data, data_len);
+}
+
+const IPEndPoint& QuicDataStream::GetPeerAddress() {
+ return session()->peer_address();
+}
+
+bool QuicDataStream::GetSSLInfo(SSLInfo* ssl_info) {
+ return session()->GetSSLInfo(ssl_info);
+}
+
+uint32 QuicDataStream::ProcessHeaderData() {
+ if (decompressed_headers_.empty()) {
+ return 0;
+ }
+
+ size_t bytes_processed = ProcessData(decompressed_headers_.data(),
+ decompressed_headers_.length());
+ if (bytes_processed == decompressed_headers_.length()) {
+ decompressed_headers_.clear();
+ } else {
+ decompressed_headers_ = decompressed_headers_.erase(0, bytes_processed);
+ }
+ return bytes_processed;
+}
+
+void QuicDataStream::OnStreamHeaders(StringPiece headers_data) {
+ headers_data.AppendToString(&decompressed_headers_);
+ ProcessHeaderData();
+}
+
+void QuicDataStream::OnStreamHeadersPriority(QuicPriority priority) {
+ DCHECK(session()->connection()->is_server());
+ set_priority(priority);
+}
+
+void QuicDataStream::OnStreamHeadersComplete(bool fin, size_t frame_len) {
+ headers_decompressed_ = true;
+ if (fin) {
+ sequencer()->OnStreamFrame(QuicStreamFrame(id(), fin, 0, IOVector()));
+ }
+ ProcessHeaderData();
+ if (FinishedReadingHeaders()) {
+ sequencer()->FlushBufferedFrames();
+ }
+}
+
+void QuicDataStream::OnClose() {
+ ReliableQuicStream::OnClose();
+
+ if (visitor_) {
+ Visitor* visitor = visitor_;
+ // Calling Visitor::OnClose() may result the destruction of the visitor,
+ // so we need to ensure we don't call it again.
+ visitor_ = nullptr;
+ visitor->OnClose(this);
+ }
+}
+
+bool QuicDataStream::FinishedReadingHeaders() {
+ return headers_decompressed_ && decompressed_headers_.empty();
+}
+
+} // namespace net
diff --git a/net/quic/quic_data_stream.h b/net/quic/quic_data_stream.h
new file mode 100644
index 0000000..1af9004
--- /dev/null
+++ b/net/quic/quic_data_stream.h
@@ -0,0 +1,147 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The base class for streams which deliver data to/from an application.
+// In each direction, the data on such a stream first contains compressed
+// headers then body data.
+
+#ifndef NET_QUIC_QUIC_DATA_STREAM_H_
+#define NET_QUIC_QUIC_DATA_STREAM_H_
+
+#include <sys/types.h>
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+#include "net/base/iovec.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_stream_sequencer.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+namespace test {
+class QuicDataStreamPeer;
+class ReliableQuicStreamPeer;
+} // namespace test
+
+class IPEndPoint;
+class QuicSession;
+class SSLInfo;
+
+// All this does right now is send data to subclasses via the sequencer.
+class NET_EXPORT_PRIVATE QuicDataStream : public ReliableQuicStream {
+ public:
+ // Visitor receives callbacks from the stream.
+ class Visitor {
+ public:
+ Visitor() {}
+
+ // Called when the stream is closed.
+ virtual void OnClose(QuicDataStream* stream) = 0;
+
+ protected:
+ virtual ~Visitor() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Visitor);
+ };
+
+ QuicDataStream(QuicStreamId id, QuicSession* session);
+
+ virtual ~QuicDataStream();
+
+ // ReliableQuicStream implementation
+ virtual void OnClose() OVERRIDE;
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
+ // By default, this is the same as priority(), however it allows streams
+ // to temporarily alter effective priority. For example if a SPDY stream has
+ // compressed but not written headers it can write the headers with a higher
+ // priority.
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ // Overridden by subclasses to process data. The headers will be delivered
+ // via OnStreamHeaders, so only data will be delivered through this method.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) = 0;
+
+ // Called by the session when decompressed headers data is received
+ // for this stream.
+ // May be called multiple times, with each call providing additional headers
+ // data until OnStreamHeadersComplete is called.
+ virtual void OnStreamHeaders(base::StringPiece headers_data);
+
+ // Called by the session when headers with a priority have been received
+ // for this stream. This method will only be called for server streams.
+ virtual void OnStreamHeadersPriority(QuicPriority priority);
+
+ // Called by the session when decompressed headers have been completely
+ // delilvered to this stream. If |fin| is true, then this stream
+ // should be closed; no more data will be sent by the peer.
+ virtual void OnStreamHeadersComplete(bool fin, size_t frame_len);
+
+ // Writes the headers contained in |header_block| to the dedicated
+ // headers stream.
+ virtual size_t WriteHeaders(
+ const SpdyHeaderBlock& header_block,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // This block of functions wraps the sequencer's functions of the same
+ // name. These methods return uncompressed data until that has
+ // been fully processed. Then they simply delegate to the sequencer.
+ virtual size_t Readv(const struct iovec* iov, size_t iov_len);
+ virtual int GetReadableRegions(iovec* iov, size_t iov_len);
+ // Returns true when all data has been read from the peer, including the fin.
+ virtual bool IsDoneReading() const;
+ virtual bool HasBytesToRead() const;
+
+ void set_visitor(Visitor* visitor) { visitor_ = visitor; }
+
+ bool headers_decompressed() const { return headers_decompressed_; }
+
+ const IPEndPoint& GetPeerAddress();
+
+ // Gets the SSL connection information.
+ bool GetSSLInfo(SSLInfo* ssl_info);
+
+ protected:
+ // Sets priority_ to priority. This should only be called before bytes are
+ // written to the server.
+ void set_priority(QuicPriority priority);
+ // This is protected because external classes should use EffectivePriority
+ // instead.
+ QuicPriority priority() const { return priority_; }
+
+ private:
+ friend class test::QuicDataStreamPeer;
+ friend class test::ReliableQuicStreamPeer;
+ friend class QuicStreamUtils;
+
+ uint32 ProcessHeaderData();
+
+ bool FinishedReadingHeaders();
+
+ Visitor* visitor_;
+ // True if the headers have been completely decompresssed.
+ bool headers_decompressed_;
+ // The priority of the stream, once parsed.
+ QuicPriority priority_;
+ // Contains a copy of the decompressed headers until they are consumed
+ // via ProcessData or Readv.
+ string decompressed_headers_;
+ // True if an error was encountered during decompression.
+ bool decompression_failed_;
+ // True if the priority has been read, false otherwise.
+ bool priority_parsed_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DATA_STREAM_H_
diff --git a/net/quic/quic_data_stream_test.cc b/net/quic/quic_data_stream_test.cc
new file mode 100644
index 0000000..279dcd3
--- /dev/null
+++ b/net/quic/quic_data_stream_test.cc
@@ -0,0 +1,543 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_data_stream.h"
+
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::StringPiece;
+using std::min;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+const bool kIsServer = true;
+const bool kShouldProcessData = true;
+
+class TestStream : public QuicDataStream {
+ public:
+ TestStream(QuicStreamId id,
+ QuicSession* session,
+ bool should_process_data)
+ : QuicDataStream(id, session),
+ should_process_data_(should_process_data) {}
+
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE {
+ EXPECT_NE(0u, data_len);
+ DVLOG(1) << "ProcessData data_len: " << data_len;
+ data_ += string(data, data_len);
+ return should_process_data_ ? data_len : 0;
+ }
+
+ using ReliableQuicStream::WriteOrBufferData;
+ using ReliableQuicStream::CloseReadSide;
+ using ReliableQuicStream::CloseWriteSide;
+
+ const string& data() const { return data_; }
+
+ private:
+ bool should_process_data_;
+ string data_;
+};
+
+class QuicDataStreamTest : public ::testing::TestWithParam<QuicVersion> {
+ public:
+ QuicDataStreamTest() {
+ headers_[":host"] = "www.google.com";
+ headers_[":path"] = "/index.hml";
+ headers_[":scheme"] = "https";
+ headers_["cookie"] =
+ "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+ "__utmc=160408618; "
+ "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+ "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+ "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+ "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+ "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+ "1zFMi5vzcns38-8_Sns; "
+ "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+ "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+ "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+ "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+ "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+ "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+ "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+ "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+ "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+ "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+ "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+ "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+ "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+ "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+ "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
+ }
+
+ void Initialize(bool stream_should_process_data) {
+ connection_ = new testing::StrictMock<MockConnection>(
+ kIsServer, SupportedVersions(GetParam()));
+ session_.reset(new testing::StrictMock<MockSession>(connection_));
+ stream_.reset(new TestStream(kClientDataStreamId1, session_.get(),
+ stream_should_process_data));
+ stream2_.reset(new TestStream(kClientDataStreamId2, session_.get(),
+ stream_should_process_data));
+ write_blocked_list_ =
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
+ }
+
+ protected:
+ MockConnection* connection_;
+ scoped_ptr<MockSession> session_;
+ scoped_ptr<TestStream> stream_;
+ scoped_ptr<TestStream> stream2_;
+ SpdyHeaderBlock headers_;
+ QuicWriteBlockedList* write_blocked_list_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicDataStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicDataStreamTest, ProcessHeaders) {
+ Initialize(kShouldProcessData);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeadersPriority(QuicUtils::HighestPriority());
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ EXPECT_EQ(QuicUtils::HighestPriority(), stream_->EffectivePriority());
+ EXPECT_EQ(headers, stream_->data());
+ EXPECT_FALSE(stream_->IsDoneReading());
+}
+
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBody) {
+ Initialize(kShouldProcessData);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame);
+
+ EXPECT_EQ(headers + body, stream_->data());
+}
+
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragments) {
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+
+ for (size_t fragment_size = 1; fragment_size < body.size();
+ ++fragment_size) {
+ Initialize(kShouldProcessData);
+ for (size_t offset = 0; offset < headers.size();
+ offset += fragment_size) {
+ size_t remaining_data = headers.size() - offset;
+ StringPiece fragment(headers.data() + offset,
+ min(fragment_size, remaining_data));
+ stream_->OnStreamHeaders(fragment);
+ }
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ for (size_t offset = 0; offset < body.size(); offset += fragment_size) {
+ size_t remaining_data = body.size() - offset;
+ StringPiece fragment(body.data() + offset,
+ min(fragment_size, remaining_data));
+ QuicStreamFrame frame(kClientDataStreamId1, false, offset,
+ MakeIOVector(fragment));
+ stream_->OnStreamFrame(frame);
+ }
+ ASSERT_EQ(headers + body,
+ stream_->data()) << "fragment_size: " << fragment_size;
+ }
+}
+
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyFragmentsSplit) {
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+
+ for (size_t split_point = 1; split_point < body.size() - 1; ++split_point) {
+ Initialize(kShouldProcessData);
+ StringPiece headers1(headers.data(), split_point);
+ stream_->OnStreamHeaders(headers1);
+
+ StringPiece headers2(headers.data() + split_point,
+ headers.size() - split_point);
+ stream_->OnStreamHeaders(headers2);
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ StringPiece fragment1(body.data(), split_point);
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0,
+ MakeIOVector(fragment1));
+ stream_->OnStreamFrame(frame1);
+
+ StringPiece fragment2(body.data() + split_point,
+ body.size() - split_point);
+ QuicStreamFrame frame2(kClientDataStreamId1, false, split_point,
+ MakeIOVector(fragment2));
+ stream_->OnStreamFrame(frame2);
+
+ ASSERT_EQ(headers + body,
+ stream_->data()) << "split_point: " << split_point;
+ }
+}
+
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyReadv) {
+ Initialize(!kShouldProcessData);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame);
+
+ char buffer[2048];
+ ASSERT_LT(headers.length() + body.length(), arraysize(buffer));
+ struct iovec vec;
+ vec.iov_base = buffer;
+ vec.iov_len = arraysize(buffer);
+
+ size_t bytes_read = stream_->Readv(&vec, 1);
+ EXPECT_EQ(headers.length(), bytes_read);
+ EXPECT_EQ(headers, string(buffer, bytes_read));
+
+ bytes_read = stream_->Readv(&vec, 1);
+ EXPECT_EQ(body.length(), bytes_read);
+ EXPECT_EQ(body, string(buffer, bytes_read));
+}
+
+TEST_P(QuicDataStreamTest, ProcessHeadersAndBodyIncrementalReadv) {
+ Initialize(!kShouldProcessData);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame);
+
+ char buffer[1];
+ struct iovec vec;
+ vec.iov_base = buffer;
+ vec.iov_len = arraysize(buffer);
+
+ string data = headers + body;
+ for (size_t i = 0; i < data.length(); ++i) {
+ size_t bytes_read = stream_->Readv(&vec, 1);
+ ASSERT_EQ(1u, bytes_read);
+ EXPECT_EQ(data.data()[i], buffer[0]);
+ }
+}
+
+TEST_P(QuicDataStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) {
+ Initialize(!kShouldProcessData);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body = "this is the body";
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame);
+
+ char buffer1[1];
+ char buffer2[1];
+ struct iovec vec[2];
+ vec[0].iov_base = buffer1;
+ vec[0].iov_len = arraysize(buffer1);
+ vec[1].iov_base = buffer2;
+ vec[1].iov_len = arraysize(buffer2);
+ string data = headers + body;
+ for (size_t i = 0; i < data.length(); i += 2) {
+ size_t bytes_read = stream_->Readv(vec, 2);
+ ASSERT_EQ(2u, bytes_read) << i;
+ ASSERT_EQ(data.data()[i], buffer1[0]) << i;
+ ASSERT_EQ(data.data()[i + 1], buffer2[0]) << i;
+ }
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlBlocked) {
+ // Tests that we send a BLOCKED frame to the peer when we attempt to write,
+ // but are flow control blocked.
+ Initialize(kShouldProcessData);
+
+ // Set a small flow control limit.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetSendWindowOffset(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::SendWindowOffset(
+ stream_->flow_controller()));
+
+ // Try to send more data than the flow control limit allows.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ const uint64 kOverflow = 15;
+ GenerateBody(&body, kWindow + kOverflow);
+
+ EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1));
+ EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(kWindow, true)));
+ stream_->WriteOrBufferData(body, false, nullptr);
+
+ // Should have sent as much as possible, resulting in no send window left.
+ EXPECT_EQ(0u,
+ QuicFlowControllerPeer::SendWindowSize(stream_->flow_controller()));
+
+ // And we should have queued the overflowed data.
+ EXPECT_EQ(kOverflow,
+ ReliableQuicStreamPeer::SizeOfQueuedData(stream_.get()));
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlNoWindowUpdateIfNotConsumed) {
+ // The flow control receive window decreases whenever we add new bytes to the
+ // sequencer, whether they are consumed immediately or buffered. However we
+ // only send WINDOW_UPDATE frames based on increasing number of bytes
+ // consumed.
+
+ // Don't process data - it will be buffered instead.
+ Initialize(!kShouldProcessData);
+
+ // Expect no WINDOW_UPDATE frames to be sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(_, _)).Times(0);
+
+ // Set a small flow control receive window.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ GenerateBody(&body, kWindow / 3);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame1);
+ EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This should all be buffered, decreasing the receive window but
+ // not sending WINDOW_UPDATE.
+ QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3,
+ MakeIOVector(body));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(
+ kWindow - (2 * kWindow / 3),
+ QuicFlowControllerPeer::ReceiveWindowSize(stream_->flow_controller()));
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the stream updates its receive window offset
+ // appropriately, and sends WINDOW_UPDATE frames when its receive window drops
+ // too low.
+ Initialize(kShouldProcessData);
+
+ // Set a small flow control limit.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream receives enough data to fill a fraction of the receive window.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ string body;
+ GenerateBody(&body, kWindow / 3);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame1);
+ EXPECT_EQ(kWindow - (kWindow / 3), QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+
+ // Now receive another frame which results in the receive window being over
+ // half full. This will trigger the stream to increase its receive window
+ // offset and send a WINDOW_UPDATE. The result will be again an available
+ // window of kWindow bytes.
+ QuicStreamFrame frame2(kClientDataStreamId1, false, kWindow / 3,
+ MakeIOVector(body));
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(kClientDataStreamId1,
+ QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()) +
+ 2 * kWindow / 3));
+ stream_->OnStreamFrame(frame2);
+ EXPECT_EQ(kWindow, QuicFlowControllerPeer::ReceiveWindowSize(
+ stream_->flow_controller()));
+}
+
+TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) {
+ // Tests that on receipt of data, the connection updates its receive window
+ // offset appropriately, and sends WINDOW_UPDATE frames when its receive
+ // window drops too low.
+ if (GetParam() < QUIC_VERSION_19) {
+ return;
+ }
+ Initialize(kShouldProcessData);
+
+ // Set a small flow control limit for streams and connection.
+ const uint64 kWindow = 36;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(stream2_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kWindow);
+ QuicFlowControllerPeer::SetMaxReceiveWindow(session_->flow_controller(),
+ kWindow);
+
+ // Supply headers to both streams so that they are happy to receive data.
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ stream_->OnStreamHeadersComplete(false, headers.size());
+ stream2_->OnStreamHeaders(headers);
+ stream2_->OnStreamHeadersComplete(false, headers.size());
+
+ // Each stream gets a quarter window of data. This should not trigger a
+ // WINDOW_UPDATE for either stream, nor for the connection.
+ string body;
+ GenerateBody(&body, kWindow / 4);
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ stream_->OnStreamFrame(frame1);
+ QuicStreamFrame frame2(kClientDataStreamId2, false, 0, MakeIOVector(body));
+ stream2_->OnStreamFrame(frame2);
+
+ // Now receive a further single byte on one stream - again this does not
+ // trigger a stream WINDOW_UPDATE, but now the connection flow control window
+ // is over half full and thus a connection WINDOW_UPDATE is sent.
+ EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId1, _)).Times(0);
+ EXPECT_CALL(*connection_, SendWindowUpdate(kClientDataStreamId2, _)).Times(0);
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, QuicFlowControllerPeer::ReceiveWindowOffset(
+ session_->flow_controller()) +
+ 1 + kWindow / 2));
+ QuicStreamFrame frame3(kClientDataStreamId1, false, (kWindow / 4),
+ MakeIOVector("a"));
+ stream_->OnStreamFrame(frame3);
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), then we terminate the connection.
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
+ Initialize(!kShouldProcessData);
+
+ // Set a small flow control limit.
+ const uint64 kWindow = 50;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kWindow);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ // Receive data to overflow the window, violating flow control.
+ string body;
+ GenerateBody(&body, kWindow + 1);
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) {
+ // Tests that on if the peer sends too much data (i.e. violates the flow
+ // control protocol), at the connection level (rather than the stream level)
+ // then we terminate the connection.
+ if (GetParam() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Stream should not process data, so that data gets buffered in the
+ // sequencer, triggering flow control limits.
+ Initialize(!kShouldProcessData);
+
+ // Set a small flow control window on streams, and connection.
+ const uint64 kStreamWindow = 50;
+ const uint64 kConnectionWindow = 10;
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(),
+ kStreamWindow);
+ QuicFlowControllerPeer::SetReceiveWindowOffset(session_->flow_controller(),
+ kConnectionWindow);
+
+ string headers = SpdyUtils::SerializeUncompressedHeaders(headers_);
+ stream_->OnStreamHeaders(headers);
+ EXPECT_EQ(headers, stream_->data());
+ stream_->OnStreamHeadersComplete(false, headers.size());
+
+ // Send enough data to overflow the connection level flow control window.
+ string body;
+ GenerateBody(&body, kConnectionWindow + 1);
+ EXPECT_LT(body.size(), kStreamWindow);
+ QuicStreamFrame frame(kClientDataStreamId1, false, 0, MakeIOVector(body));
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_P(QuicDataStreamTest, StreamFlowControlFinNotBlocked) {
+ // An attempt to write a FIN with no data should not be flow control blocked,
+ // even if the send window is 0.
+
+ Initialize(kShouldProcessData);
+
+ // Set a flow control limit of zero.
+ QuicFlowControllerPeer::SetReceiveWindowOffset(stream_->flow_controller(), 0);
+ EXPECT_EQ(0u, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Send a frame with a FIN but no data. This should not be blocked.
+ string body = "";
+ bool fin = true;
+
+ EXPECT_CALL(*connection_, SendBlocked(kClientDataStreamId1)).Times(0);
+ EXPECT_CALL(*session_, WritevData(kClientDataStreamId1, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, fin)));
+
+ stream_->WriteOrBufferData(body, fin, nullptr);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_data_writer.cc b/net/quic/quic_data_writer.cc
new file mode 100644
index 0000000..11b6b55
--- /dev/null
+++ b/net/quic/quic_data_writer.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_data_writer.h"
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::StringPiece;
+using std::numeric_limits;
+
+namespace net {
+
+QuicDataWriter::QuicDataWriter(size_t size)
+ : buffer_(new char[size]),
+ capacity_(size),
+ length_(0) {
+}
+
+QuicDataWriter::~QuicDataWriter() {
+ delete[] buffer_;
+}
+
+char* QuicDataWriter::take() {
+ char* rv = buffer_;
+ buffer_ = nullptr;
+ capacity_ = 0;
+ length_ = 0;
+ return rv;
+}
+
+bool QuicDataWriter::WriteUInt8(uint8 value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUInt16(uint16 value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUInt32(uint32 value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUInt48(uint64 value) {
+ uint32 hi = value >> 32;
+ uint32 lo = value & GG_UINT64_C(0x00000000FFFFFFFF);
+ return WriteUInt32(lo) && WriteUInt16(hi);
+}
+
+bool QuicDataWriter::WriteUInt64(uint64 value) {
+ return WriteBytes(&value, sizeof(value));
+}
+
+bool QuicDataWriter::WriteUFloat16(uint64 value) {
+ uint16 result;
+ if (value < (GG_UINT64_C(1) << kUFloat16MantissaEffectiveBits)) {
+ // Fast path: either the value is denormalized, or has exponent zero.
+ // Both cases are represented by the value itself.
+ result = value;
+ } else if (value >= kUFloat16MaxValue) {
+ // Value is out of range; clamp it to the maximum representable.
+ result = numeric_limits<uint16>::max();
+ } else {
+ // The highest bit is between position 13 and 42 (zero-based), which
+ // corresponds to exponent 1-30. In the output, mantissa is from 0 to 10,
+ // hidden bit is 11 and exponent is 11 to 15. Shift the highest bit to 11
+ // and count the shifts.
+ uint16 exponent = 0;
+ for (uint16 offset = 16; offset > 0; offset /= 2) {
+ // Right-shift the value until the highest bit is in position 11.
+ // For offset of 16, 8, 4, 2 and 1 (binary search over 1-30),
+ // shift if the bit is at or above 11 + offset.
+ if (value >= (GG_UINT64_C(1) << (kUFloat16MantissaBits + offset))) {
+ exponent += offset;
+ value >>= offset;
+ }
+ }
+
+ DCHECK_GE(exponent, 1);
+ DCHECK_LE(exponent, kUFloat16MaxExponent);
+ DCHECK_GE(value, GG_UINT64_C(1) << kUFloat16MantissaBits);
+ DCHECK_LT(value, GG_UINT64_C(1) << kUFloat16MantissaEffectiveBits);
+
+ // Hidden bit (position 11) is set. We should remove it and increment the
+ // exponent. Equivalently, we just add it to the exponent.
+ // This hides the bit.
+ result = value + (exponent << kUFloat16MantissaBits);
+ }
+
+ return WriteBytes(&result, sizeof(result));
+}
+
+bool QuicDataWriter::WriteStringPiece16(StringPiece val) {
+ if (val.length() > numeric_limits<uint16>::max()) {
+ return false;
+ }
+ if (!WriteUInt16(val.size())) {
+ return false;
+ }
+ return WriteBytes(val.data(), val.size());
+}
+
+bool QuicDataWriter::WriteIOVector(const IOVector& data) {
+ char *dest = BeginWrite(data.TotalBufferSize());
+ if (!dest) {
+ return false;
+ }
+ for (size_t i = 0; i < data.Size(); ++i) {
+ WriteBytes(data.iovec()[i].iov_base, data.iovec()[i].iov_len);
+ }
+
+ return true;
+}
+
+char* QuicDataWriter::BeginWrite(size_t length) {
+ if (length_ > capacity_) {
+ return nullptr;
+ }
+
+ if (capacity_ - length_ < length) {
+ return nullptr;
+ }
+
+#ifdef ARCH_CPU_64_BITS
+ DCHECK_LE(length, numeric_limits<uint32>::max());
+#endif
+
+ return buffer_ + length_;
+}
+
+bool QuicDataWriter::WriteBytes(const void* data, size_t data_len) {
+ char* dest = BeginWrite(data_len);
+ if (!dest) {
+ return false;
+ }
+
+ memcpy(dest, data, data_len);
+
+ length_ += data_len;
+ return true;
+}
+
+bool QuicDataWriter::WriteRepeatedByte(uint8 byte, size_t count) {
+ char* dest = BeginWrite(count);
+ if (!dest) {
+ return false;
+ }
+
+ memset(dest, byte, count);
+
+ length_ += count;
+ return true;
+}
+
+void QuicDataWriter::WritePadding() {
+ DCHECK_LE(length_, capacity_);
+ if (length_ > capacity_) {
+ return;
+ }
+ memset(buffer_ + length_, 0x00, capacity_ - length_);
+ length_ = capacity_;
+}
+
+bool QuicDataWriter::WriteUInt8ToOffset(uint8 value, size_t offset) {
+ if (offset >= capacity_) {
+ LOG(DFATAL) << "offset: " << offset << " >= capacity: " << capacity_;
+ return false;
+ }
+ size_t latched_length = length_;
+ length_ = offset;
+ bool success = WriteUInt8(value);
+ DCHECK_LE(length_, latched_length);
+ length_ = latched_length;
+ return success;
+}
+
+bool QuicDataWriter::WriteUInt32ToOffset(uint32 value, size_t offset) {
+ DCHECK_LT(offset, capacity_);
+ size_t latched_length = length_;
+ length_ = offset;
+ bool success = WriteUInt32(value);
+ DCHECK_LE(length_, latched_length);
+ length_ = latched_length;
+ return success;
+}
+
+bool QuicDataWriter::WriteUInt48ToOffset(uint64 value, size_t offset) {
+ DCHECK_LT(offset, capacity_);
+ size_t latched_length = length_;
+ length_ = offset;
+ bool success = WriteUInt48(value);
+ DCHECK_LE(length_, latched_length);
+ length_ = latched_length;
+ return success;
+}
+
+} // namespace net
diff --git a/net/quic/quic_data_writer.h b/net/quic/quic_data_writer.h
new file mode 100644
index 0000000..8204c3a
--- /dev/null
+++ b/net/quic/quic_data_writer.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_DATA_WRITER_H_
+#define NET_QUIC_QUIC_DATA_WRITER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/port.h"
+#include "base/strings/string_piece.h"
+#include "net/base/int128.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// This class provides facilities for packing QUIC data.
+//
+// The QuicDataWriter supports appending primitive values (int, string, etc)
+// to a frame instance. The QuicDataWriter grows its internal memory buffer
+// dynamically to hold the sequence of primitive values. The internal memory
+// buffer is exposed as the "data" of the QuicDataWriter.
+class NET_EXPORT_PRIVATE QuicDataWriter {
+ public:
+ explicit QuicDataWriter(size_t length);
+
+ ~QuicDataWriter();
+
+ // Returns the size of the QuicDataWriter's data.
+ size_t length() const { return length_; }
+
+ // Takes the buffer from the QuicDataWriter.
+ char* take();
+
+ // Methods for adding to the payload. These values are appended to the end
+ // of the QuicDataWriter payload. Note - binary integers are written in
+ // host byte order (little endian) not network byte order (big endian).
+ bool WriteUInt8(uint8 value);
+ bool WriteUInt16(uint16 value);
+ bool WriteUInt32(uint32 value);
+ bool WriteUInt48(uint64 value);
+ bool WriteUInt64(uint64 value);
+ // Write unsigned floating point corresponding to the value. Large values are
+ // clamped to the maximum representable (kUFloat16MaxValue). Values that can
+ // not be represented directly are rounded down.
+ bool WriteUFloat16(uint64 value);
+ bool WriteStringPiece16(base::StringPiece val);
+ bool WriteIOVector(const IOVector& data);
+ bool WriteBytes(const void* data, size_t data_len);
+ bool WriteRepeatedByte(uint8 byte, size_t count);
+ // Fills the remaining buffer with null characters.
+ void WritePadding();
+
+ // Methods for editing the payload at a specific offset, where the
+ // offset must be within the writer's capacity.
+ // Return true if there is enough space at that offset, false otherwise.
+ bool WriteUInt8ToOffset(uint8 value, size_t offset);
+ bool WriteUInt32ToOffset(uint32 value, size_t offset);
+ bool WriteUInt48ToOffset(uint64 value, size_t offset);
+
+ size_t capacity() const {
+ return capacity_;
+ }
+
+ private:
+ // Returns the location that the data should be written at, or nullptr if
+ // there is not enough room. Call EndWrite with the returned offset and the
+ // given length to pad out for the next write.
+ char* BeginWrite(size_t length);
+
+ char* buffer_;
+ size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
+ size_t length_; // Current length of the buffer.
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDataWriter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DATA_WRITER_H_
diff --git a/net/quic/quic_data_writer_test.cc b/net/quic/quic_data_writer_test.cc
new file mode 100644
index 0000000..07346c7
--- /dev/null
+++ b/net/quic/quic_data_writer_test.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_data_writer.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/test/gtest_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicDataWriterTest, WriteUInt8ToOffset) {
+ QuicDataWriter writer(4);
+
+ writer.WriteUInt32(0xfefdfcfb);
+ EXPECT_TRUE(writer.WriteUInt8ToOffset(1, 0));
+ EXPECT_TRUE(writer.WriteUInt8ToOffset(2, 1));
+ EXPECT_TRUE(writer.WriteUInt8ToOffset(3, 2));
+ EXPECT_TRUE(writer.WriteUInt8ToOffset(4, 3));
+
+ scoped_ptr<char[]> data(writer.take());
+
+ EXPECT_EQ(1, data[0]);
+ EXPECT_EQ(2, data[1]);
+ EXPECT_EQ(3, data[2]);
+ EXPECT_EQ(4, data[3]);
+}
+
+TEST(QuicDataWriterDeathTest, WriteUInt8ToOffset) {
+ QuicDataWriter writer(4);
+
+ EXPECT_DFATAL(EXPECT_FALSE(writer.WriteUInt8ToOffset(5, 4)),
+ "offset: 4 >= capacity: 4");
+}
+
+TEST(QuicDataWriterTest, SanityCheckUFloat16Consts) {
+ // Check the arithmetic on the constants - otherwise the values below make
+ // no sense.
+ EXPECT_EQ(30, kUFloat16MaxExponent);
+ EXPECT_EQ(11, kUFloat16MantissaBits);
+ EXPECT_EQ(12, kUFloat16MantissaEffectiveBits);
+ EXPECT_EQ(GG_UINT64_C(0x3FFC0000000), kUFloat16MaxValue);
+}
+
+TEST(QuicDataWriterTest, WriteUFloat16) {
+ struct TestCase {
+ uint64 decoded;
+ uint16 encoded;
+ };
+ TestCase test_cases[] = {
+ // Small numbers represent themselves.
+ { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 },
+ { 7, 7 }, { 15, 15 }, { 31, 31 }, { 42, 42 }, { 123, 123 }, { 1234, 1234 },
+ // Check transition through 2^11.
+ { 2046, 2046 }, { 2047, 2047 }, { 2048, 2048 }, { 2049, 2049 },
+ // Running out of mantissa at 2^12.
+ { 4094, 4094 }, { 4095, 4095 }, { 4096, 4096 }, { 4097, 4096 },
+ { 4098, 4097 }, { 4099, 4097 }, { 4100, 4098 }, { 4101, 4098 },
+ // Check transition through 2^13.
+ { 8190, 6143 }, { 8191, 6143 }, { 8192, 6144 }, { 8193, 6144 },
+ { 8194, 6144 }, { 8195, 6144 }, { 8196, 6145 }, { 8197, 6145 },
+ // Half-way through the exponents.
+ { 0x7FF8000, 0x87FF }, { 0x7FFFFFF, 0x87FF }, { 0x8000000, 0x8800 },
+ { 0xFFF0000, 0x8FFF }, { 0xFFFFFFF, 0x8FFF }, { 0x10000000, 0x9000 },
+ // Transition into the largest exponent.
+ { 0x1FFFFFFFFFE, 0xF7FF}, { 0x1FFFFFFFFFF, 0xF7FF},
+ { 0x20000000000, 0xF800}, { 0x20000000001, 0xF800},
+ { 0x2003FFFFFFE, 0xF800}, { 0x2003FFFFFFF, 0xF800},
+ { 0x20040000000, 0xF801}, { 0x20040000001, 0xF801},
+ // Transition into the max value and clamping.
+ { 0x3FF80000000, 0xFFFE}, { 0x3FFBFFFFFFF, 0xFFFE},
+ { 0x3FFC0000000, 0xFFFF}, { 0x3FFC0000001, 0xFFFF},
+ { 0x3FFFFFFFFFF, 0xFFFF}, { 0x40000000000, 0xFFFF},
+ { 0xFFFFFFFFFFFFFFFF, 0xFFFF},
+ };
+ int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
+
+ for (int i = 0; i < num_test_cases; ++i) {
+ QuicDataWriter writer(2);
+ EXPECT_TRUE(writer.WriteUFloat16(test_cases[i].decoded));
+ scoped_ptr<char[]> data(writer.take());
+ EXPECT_EQ(test_cases[i].encoded, *reinterpret_cast<uint16*>(data.get()));
+ }
+}
+
+TEST(QuicDataWriterTest, ReadUFloat16) {
+ struct TestCase {
+ uint64 decoded;
+ uint16 encoded;
+ };
+ TestCase test_cases[] = {
+ // There are fewer decoding test cases because encoding truncates, and
+ // decoding returns the smallest expansion.
+ // Small numbers represent themselves.
+ { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 },
+ { 7, 7 }, { 15, 15 }, { 31, 31 }, { 42, 42 }, { 123, 123 }, { 1234, 1234 },
+ // Check transition through 2^11.
+ { 2046, 2046 }, { 2047, 2047 }, { 2048, 2048 }, { 2049, 2049 },
+ // Running out of mantissa at 2^12.
+ { 4094, 4094 }, { 4095, 4095 }, { 4096, 4096 },
+ { 4098, 4097 }, { 4100, 4098 },
+ // Check transition through 2^13.
+ { 8190, 6143 }, { 8192, 6144 }, { 8196, 6145 },
+ // Half-way through the exponents.
+ { 0x7FF8000, 0x87FF }, { 0x8000000, 0x8800 },
+ { 0xFFF0000, 0x8FFF }, { 0x10000000, 0x9000 },
+ // Transition into the largest exponent.
+ { 0x1FFE0000000, 0xF7FF}, { 0x20000000000, 0xF800},
+ { 0x20040000000, 0xF801},
+ // Transition into the max value.
+ { 0x3FF80000000, 0xFFFE}, { 0x3FFC0000000, 0xFFFF},
+ };
+ int num_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
+
+ for (int i = 0; i < num_test_cases; ++i) {
+ QuicDataReader reader(reinterpret_cast<char*>(&test_cases[i].encoded), 2);
+ uint64 value;
+ EXPECT_TRUE(reader.ReadUFloat16(&value));
+ EXPECT_EQ(test_cases[i].decoded, value);
+ }
+}
+
+TEST(QuicDataWriterTest, RoundTripUFloat16) {
+ // Just test all 16-bit encoded values. 0 and max already tested above.
+ uint64 previous_value = 0;
+ for (uint16 i = 1; i < 0xFFFF; ++i) {
+ // Read the two bytes.
+ QuicDataReader reader(reinterpret_cast<char*>(&i), 2);
+ uint64 value;
+ // All values must be decodable.
+ EXPECT_TRUE(reader.ReadUFloat16(&value));
+ // Check that small numbers represent themselves
+ if (i < 4097)
+ EXPECT_EQ(i, value);
+ // Check there's monotonic growth.
+ EXPECT_LT(previous_value, value);
+ // Check that precision is within 0.5% away from the denormals.
+ if (i > 2000)
+ EXPECT_GT(previous_value * 1005, value * 1000);
+ // Check we're always within the promised range.
+ EXPECT_LT(value, GG_UINT64_C(0x3FFC0000000));
+ previous_value = value;
+ QuicDataWriter writer(6);
+ EXPECT_TRUE(writer.WriteUFloat16(value - 1));
+ EXPECT_TRUE(writer.WriteUFloat16(value));
+ EXPECT_TRUE(writer.WriteUFloat16(value + 1));
+ scoped_ptr<char[]> data(writer.take());
+ // Check minimal decoding (previous decoding has previous encoding).
+ EXPECT_EQ(i-1, *reinterpret_cast<uint16*>(data.get()));
+ // Check roundtrip.
+ EXPECT_EQ(i, *reinterpret_cast<uint16*>(data.get() + 2));
+ // Check next decoding.
+ EXPECT_EQ(i < 4096? i+1 : i, *reinterpret_cast<uint16*>(data.get() + 4));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_default_packet_writer.cc b/net/quic/quic_default_packet_writer.cc
new file mode 100644
index 0000000..d851dfb
--- /dev/null
+++ b/net/quic/quic_default_packet_writer.cc
@@ -0,0 +1,74 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_default_packet_writer.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+
+namespace net {
+
+QuicDefaultPacketWriter::QuicDefaultPacketWriter() : weak_factory_(this) {
+}
+
+QuicDefaultPacketWriter::QuicDefaultPacketWriter(DatagramClientSocket* socket)
+ : socket_(socket),
+ write_blocked_(false),
+ weak_factory_(this) {
+}
+
+QuicDefaultPacketWriter::~QuicDefaultPacketWriter() {}
+
+WriteResult QuicDefaultPacketWriter::WritePacket(
+ const char* buffer, size_t buf_len,
+ const net::IPAddressNumber& self_address,
+ const net::IPEndPoint& peer_address) {
+ scoped_refptr<StringIOBuffer> buf(
+ new StringIOBuffer(std::string(buffer, buf_len)));
+ DCHECK(!IsWriteBlocked());
+ int rv = socket_->Write(buf.get(),
+ buf_len,
+ base::Bind(&QuicDefaultPacketWriter::OnWriteComplete,
+ weak_factory_.GetWeakPtr()));
+ WriteStatus status = WRITE_STATUS_OK;
+ if (rv < 0) {
+ if (rv != ERR_IO_PENDING) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.WriteError", -rv);
+ status = WRITE_STATUS_ERROR;
+ } else {
+ status = WRITE_STATUS_BLOCKED;
+ write_blocked_ = true;
+ }
+ }
+
+ return WriteResult(status, rv);
+}
+
+bool QuicDefaultPacketWriter::IsWriteBlockedDataBuffered() const {
+ // Chrome sockets' Write() methods buffer the data until the Write is
+ // permitted.
+ return true;
+}
+
+bool QuicDefaultPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicDefaultPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
+void QuicDefaultPacketWriter::OnWriteComplete(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ write_blocked_ = false;
+ if (rv < 0) {
+ connection_->OnWriteError(rv);
+ }
+ connection_->OnCanWrite();
+}
+
+} // namespace net
diff --git a/net/quic/quic_default_packet_writer.h b/net/quic/quic_default_packet_writer.h
new file mode 100644
index 0000000..15eb0dd
--- /dev/null
+++ b/net/quic/quic_default_packet_writer.h
@@ -0,0 +1,61 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_DEFAULT_PACKET_WRITER_H_
+#define NET_QUIC_QUIC_DEFAULT_PACKET_WRITER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/udp/datagram_client_socket.h"
+
+namespace net {
+
+struct WriteResult;
+
+// Chrome specific packet writer which uses a DatagramClientSocket for writing
+// data.
+class NET_EXPORT_PRIVATE QuicDefaultPacketWriter : public QuicPacketWriter {
+ public:
+ QuicDefaultPacketWriter();
+ explicit QuicDefaultPacketWriter(DatagramClientSocket* socket);
+ virtual ~QuicDefaultPacketWriter();
+
+ // QuicPacketWriter
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ void OnWriteComplete(int rv);
+ void SetConnection(QuicConnection* connection) {
+ connection_ = connection;
+ }
+
+ protected:
+ void set_write_blocked(bool is_blocked) {
+ write_blocked_ = is_blocked;
+ }
+
+ private:
+ DatagramClientSocket* socket_;
+ QuicConnection* connection_;
+
+ // Whether a write is currently in flight.
+ bool write_blocked_;
+
+ base::WeakPtrFactory<QuicDefaultPacketWriter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDefaultPacketWriter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DEFAULT_PACKET_WRITER_H_
diff --git a/net/quic/quic_dispatcher.cc b/net/quic/quic_dispatcher.cc
new file mode 100644
index 0000000..a72faa6
--- /dev/null
+++ b/net/quic/quic_dispatcher.cc
@@ -0,0 +1,408 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_dispatcher.h"
+
+#include <errno.h>
+
+#include "base/debug/stack_trace.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_per_connection_packet_writer.h"
+#include "net/quic/quic_time_wait_list_manager.h"
+#include "net/quic/quic_utils.h"
+
+namespace net {
+
+using base::StringPiece;
+using std::make_pair;
+using std::find;
+
+class DeleteSessionsAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit DeleteSessionsAlarm(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {
+ }
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ dispatcher_->DeleteSessions();
+ return QuicTime::Zero();
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+};
+
+class QuicDispatcher::QuicFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ explicit QuicFramerVisitor(QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher),
+ connection_id_(0) {}
+
+ // QuicFramerVisitorInterface implementation
+ virtual void OnPacket() OVERRIDE {}
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ connection_id_ = header.connection_id;
+ return dispatcher_->OnUnauthenticatedPublicHeader(header);
+ }
+ virtual bool OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) OVERRIDE {
+ dispatcher_->OnUnauthenticatedHeader(header);
+ return false;
+ }
+ virtual void OnError(QuicFramer* framer) OVERRIDE {
+ DVLOG(1) << QuicUtils::ErrorToString(framer->error());
+ }
+
+ virtual bool OnProtocolVersionMismatch(
+ QuicVersion /*received_version*/) OVERRIDE {
+ if (dispatcher_->time_wait_list_manager()->IsConnectionIdInTimeWait(
+ connection_id_)) {
+ // Keep processing after protocol mismatch - this will be dealt with by
+ // the TimeWaitListManager.
+ return true;
+ } else {
+ DLOG(DFATAL) << "Version mismatch, connection ID (" << connection_id_
+ << ") not in time wait list.";
+ return false;
+ }
+ }
+
+ // The following methods should never get called because we always return
+ // false from OnUnauthenticatedHeader(). As a result, we never process the
+ // payload of the packet.
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& /*packet*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnPacketHeader(const QuicPacketHeader& /*header*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnRevivedPacket() OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnFecProtectedPayload(StringPiece /*payload*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual bool OnStreamFrame(const QuicStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnAckFrame(const QuicAckFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnPingFrame(const QuicPingFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame & /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& /*frame*/) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& /*frame*/)
+ OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ DCHECK(false);
+ return false;
+ }
+ virtual void OnFecData(const QuicFecData& /*fec*/) OVERRIDE {
+ DCHECK(false);
+ }
+ virtual void OnPacketComplete() OVERRIDE {
+ DCHECK(false);
+ }
+
+ private:
+ QuicDispatcher* dispatcher_;
+
+ // Latched in OnUnauthenticatedPublicHeader for use later.
+ QuicConnectionId connection_id_;
+};
+
+QuicPacketWriter* QuicDispatcher::DefaultPacketWriterFactory::Create(
+ QuicServerPacketWriter* writer,
+ QuicConnection* connection) {
+ return new QuicPerConnectionPacketWriter(writer, connection);
+}
+
+QuicDispatcher::PacketWriterFactoryAdapter::PacketWriterFactoryAdapter(
+ QuicDispatcher* dispatcher)
+ : dispatcher_(dispatcher) {}
+
+QuicDispatcher::PacketWriterFactoryAdapter::~PacketWriterFactoryAdapter() {}
+
+QuicPacketWriter* QuicDispatcher::PacketWriterFactoryAdapter::Create(
+ QuicConnection* connection) const {
+ return dispatcher_->packet_writer_factory_->Create(
+ dispatcher_->writer_.get(),
+ connection);
+}
+
+QuicDispatcher::QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ const QuicVersionVector& supported_versions,
+ PacketWriterFactory* packet_writer_factory,
+ QuicConnectionHelperInterface* helper)
+ : config_(config),
+ crypto_config_(crypto_config),
+ helper_(helper),
+ delete_sessions_alarm_(
+ helper_->CreateAlarm(new DeleteSessionsAlarm(this))),
+ packet_writer_factory_(packet_writer_factory),
+ connection_writer_factory_(this),
+ supported_versions_(supported_versions),
+ current_packet_(nullptr),
+ framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
+ framer_visitor_(new QuicFramerVisitor(this)) {
+ framer_.set_visitor(framer_visitor_.get());
+}
+
+QuicDispatcher::~QuicDispatcher() {
+ STLDeleteValues(&session_map_);
+ STLDeleteElements(&closed_session_list_);
+}
+
+void QuicDispatcher::Initialize(QuicServerPacketWriter* writer) {
+ DCHECK(writer_ == nullptr);
+ writer_.reset(writer);
+ time_wait_list_manager_.reset(CreateQuicTimeWaitListManager());
+}
+
+void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet) {
+ current_server_address_ = server_address;
+ current_client_address_ = client_address;
+ current_packet_ = &packet;
+ // ProcessPacket will cause the packet to be dispatched in
+ // OnUnauthenticatedPublicHeader, or sent to the time wait list manager
+ // in OnAuthenticatedHeader.
+ framer_.ProcessPacket(packet);
+ // TODO(rjshade): Return a status describing if/why a packet was dropped,
+ // and log somehow. Maybe expose as a varz.
+}
+
+bool QuicDispatcher::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ QuicSession* session = nullptr;
+
+ QuicConnectionId connection_id = header.connection_id;
+ SessionMap::iterator it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ if (header.reset_flag) {
+ return false;
+ }
+ if (time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)) {
+ return HandlePacketForTimeWait(header);
+ }
+
+ // Ensure the packet has a version negotiation bit set before creating a new
+ // session for it. All initial packets for a new connection are required to
+ // have the flag set. Otherwise it may be a stray packet.
+ if (header.version_flag) {
+ session = CreateQuicSession(connection_id, current_server_address_,
+ current_client_address_);
+ }
+
+ if (session == nullptr) {
+ DVLOG(1) << "Failed to create session for " << connection_id;
+ // Add this connection_id fo the time-wait state, to safely reject future
+ // packets.
+
+ if (header.version_flag &&
+ !framer_.IsSupportedVersion(header.versions.front())) {
+ // TODO(ianswett): Produce a no-version version negotiation packet.
+ return false;
+ }
+
+ // Use the version in the packet if possible, otherwise assume the latest.
+ QuicVersion version = header.version_flag ? header.versions.front() :
+ supported_versions_.front();
+ time_wait_list_manager_->AddConnectionIdToTimeWait(connection_id, version,
+ nullptr);
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
+ return HandlePacketForTimeWait(header);
+ }
+ DVLOG(1) << "Created new session for " << connection_id;
+ session_map_.insert(make_pair(connection_id, session));
+ } else {
+ session = it->second;
+ }
+
+ session->connection()->ProcessUdpPacket(
+ current_server_address_, current_client_address_, *current_packet_);
+
+ // Do not parse the packet further. The session will process it completely.
+ return false;
+}
+
+void QuicDispatcher::OnUnauthenticatedHeader(const QuicPacketHeader& header) {
+ DCHECK(time_wait_list_manager_->IsConnectionIdInTimeWait(
+ header.public_header.connection_id));
+ time_wait_list_manager_->ProcessPacket(current_server_address_,
+ current_client_address_,
+ header.public_header.connection_id,
+ header.packet_sequence_number,
+ *current_packet_);
+}
+
+void QuicDispatcher::CleanUpSession(SessionMap::iterator it) {
+ QuicConnection* connection = it->second->connection();
+ QuicEncryptedPacket* connection_close_packet =
+ connection->ReleaseConnectionClosePacket();
+ write_blocked_list_.erase(connection);
+ time_wait_list_manager_->AddConnectionIdToTimeWait(it->first,
+ connection->version(),
+ connection_close_packet);
+ session_map_.erase(it);
+}
+
+void QuicDispatcher::DeleteSessions() {
+ STLDeleteElements(&closed_session_list_);
+}
+
+void QuicDispatcher::OnCanWrite() {
+ // We finished a write: the socket should not be blocked.
+ writer_->SetWritable();
+
+ // Give all the blocked writers one chance to write, until we're blocked again
+ // or there's no work left.
+ while (!write_blocked_list_.empty() && !writer_->IsWriteBlocked()) {
+ QuicBlockedWriterInterface* blocked_writer =
+ write_blocked_list_.begin()->first;
+ write_blocked_list_.erase(write_blocked_list_.begin());
+ blocked_writer->OnCanWrite();
+ }
+}
+
+bool QuicDispatcher::HasPendingWrites() const {
+ return !write_blocked_list_.empty();
+}
+
+void QuicDispatcher::Shutdown() {
+ while (!session_map_.empty()) {
+ QuicSession* session = session_map_.begin()->second;
+ session->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
+ // Validate that the session removes itself from the session map on close.
+ DCHECK(session_map_.empty() || session_map_.begin()->second != session);
+ }
+ DeleteSessions();
+}
+
+void QuicDispatcher::OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) {
+ SessionMap::iterator it = session_map_.find(connection_id);
+ if (it == session_map_.end()) {
+ LOG(DFATAL) << "ConnectionId " << connection_id
+ << " does not exist in the session map. "
+ << "Error: " << QuicUtils::ErrorToString(error);
+ LOG(DFATAL) << base::debug::StackTrace().ToString();
+ return;
+ }
+ DVLOG_IF(1, error != QUIC_NO_ERROR) << "Closing connection ("
+ << connection_id
+ << ") due to error: "
+ << QuicUtils::ErrorToString(error);
+ if (closed_session_list_.empty()) {
+ delete_sessions_alarm_->Set(helper_->GetClock()->ApproximateNow());
+ }
+ closed_session_list_.push_back(it->second);
+ CleanUpSession(it);
+}
+
+void QuicDispatcher::OnWriteBlocked(
+ QuicBlockedWriterInterface* blocked_writer) {
+ if (!writer_->IsWriteBlocked()) {
+ LOG(DFATAL) <<
+ "QuicDispatcher::OnWriteBlocked called when the writer is not blocked.";
+ // Return without adding the connection to the blocked list, to avoid
+ // infinite loops in OnCanWrite.
+ return;
+ }
+ write_blocked_list_.insert(make_pair(blocked_writer, true));
+}
+
+QuicSession* QuicDispatcher::CreateQuicSession(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ QuicServerSession* session = new QuicServerSession(
+ config_,
+ CreateQuicConnection(connection_id, server_address, client_address),
+ this);
+ session->InitializeSession(crypto_config_);
+ return session;
+}
+
+QuicConnection* QuicDispatcher::CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address) {
+ return new QuicConnection(connection_id,
+ client_address,
+ helper_,
+ connection_writer_factory_,
+ /* owns_writer= */ true,
+ /* is_server= */ true,
+ supported_versions_);
+}
+
+QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() {
+ return new QuicTimeWaitListManager(
+ writer_.get(), this, helper_, supported_versions());
+}
+
+bool QuicDispatcher::HandlePacketForTimeWait(
+ const QuicPacketPublicHeader& header) {
+ if (header.reset_flag) {
+ // Public reset packets do not have sequence numbers, so ignore the packet.
+ return false;
+ }
+
+ // Switch the framer to the correct version, so that the sequence number can
+ // be parsed correctly.
+ framer_.set_version(time_wait_list_manager_->GetQuicVersionFromConnectionId(
+ header.connection_id));
+
+ // Continue parsing the packet to extract the sequence number. Then
+ // send it to the time wait manager in OnUnathenticatedHeader.
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/quic_dispatcher.h b/net/quic/quic_dispatcher.h
new file mode 100644
index 0000000..d07c46c
--- /dev/null
+++ b/net/quic/quic_dispatcher.h
@@ -0,0 +1,253 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A server side dispatcher which dispatches a given client's data to their
+// stream.
+
+#ifndef NET_QUIC_QUIC_DISPATCHER_H_
+#define NET_QUIC_QUIC_DISPATCHER_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_packet_writer.h"
+#include "net/quic/quic_server_session.h"
+#include "net/quic/quic_time_wait_list_manager.h"
+
+namespace net {
+namespace test {
+class QuicDispatcherPeer;
+} // namespace test
+
+class DeleteSessionsAlarm;
+class QuicConfig;
+class QuicCryptoServerConfig;
+class QuicSession;
+
+class ProcessPacketInterface {
+ public:
+ virtual ~ProcessPacketInterface() {}
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet) = 0;
+};
+
+class QuicDispatcher : public QuicBlockedWriterInterface,
+ public QuicServerSessionVisitor,
+ public ProcessPacketInterface {
+ public:
+ // Creates per-connection packet writers out of the QuicDispatcher's shared
+ // QuicPacketWriter. The per-connection writers' IsWriteBlocked() state must
+ // always be the same as the shared writer's IsWriteBlocked(), or else the
+ // QuicDispatcher::OnCanWrite logic will not work. (This will hopefully be
+ // cleaned up for bug 16950226.)
+ class PacketWriterFactory {
+ public:
+ virtual ~PacketWriterFactory() {}
+
+ virtual QuicPacketWriter* Create(QuicServerPacketWriter* writer,
+ QuicConnection* connection) = 0;
+ };
+
+ // Creates ordinary QuicPerConnectionPacketWriter instances.
+ class DefaultPacketWriterFactory : public PacketWriterFactory {
+ public:
+ virtual ~DefaultPacketWriterFactory() {}
+
+ virtual QuicPacketWriter* Create(
+ QuicServerPacketWriter* writer,
+ QuicConnection* connection) OVERRIDE;
+ };
+
+ // Ideally we'd have a linked_hash_set: the boolean is unused.
+ typedef linked_hash_map<QuicBlockedWriterInterface*, bool> WriteBlockedList;
+
+ // Due to the way delete_sessions_closure_ is registered, the Dispatcher must
+ // live until epoll_server Shutdown. |supported_versions| specifies the list
+ // of supported QUIC versions. Takes ownership of |packet_writer_factory|,
+ // which is used to create per-connection writers.
+ QuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ const QuicVersionVector& supported_versions,
+ PacketWriterFactory* packet_writer_factory,
+ QuicConnectionHelperInterface* helper);
+
+ virtual ~QuicDispatcher();
+
+ // Takes ownership of the packet writer.
+ virtual void Initialize(QuicServerPacketWriter* writer);
+
+ // Process the incoming packet by creating a new session, passing it to
+ // an existing session, or passing it to the TimeWaitListManager.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet) OVERRIDE;
+
+ // Returns true if there's anything in the blocked writer list.
+ virtual bool HasPendingWrites() const;
+
+ // Sends ConnectionClose frames to all connected clients.
+ void Shutdown();
+
+ // QuicBlockedWriterInterface implementation:
+ // Called when the socket becomes writable to allow queued writes to happen.
+ virtual void OnCanWrite() OVERRIDE;
+
+ // QuicServerSessionVisitor interface implementation:
+ // Ensure that the closed connection is cleaned up asynchronously.
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) OVERRIDE;
+
+ // Queues the blocked writer for later resumption.
+ virtual void OnWriteBlocked(
+ QuicBlockedWriterInterface* blocked_writer) OVERRIDE;
+
+ typedef base::hash_map<QuicConnectionId, QuicSession*> SessionMap;
+
+ // Deletes all sessions on the closed session list and clears the list.
+ void DeleteSessions();
+
+ const SessionMap& session_map() const { return session_map_; }
+
+ protected:
+ virtual QuicSession* CreateQuicSession(QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ virtual QuicConnection* CreateQuicConnection(
+ QuicConnectionId connection_id,
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address);
+
+ // Called by |framer_visitor_| when the public header has been parsed.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header);
+
+ // Create and return the time wait list manager for this dispatcher, which
+ // will be owned by the dispatcher as time_wait_list_manager_
+ virtual QuicTimeWaitListManager* CreateQuicTimeWaitListManager();
+
+ // Replaces the packet writer with |writer|. Takes ownership of |writer|.
+ void set_writer(QuicServerPacketWriter* writer) {
+ writer_.reset(writer);
+ }
+
+ QuicTimeWaitListManager* time_wait_list_manager() {
+ return time_wait_list_manager_.get();
+ }
+
+ const QuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ const IPEndPoint& current_server_address() {
+ return current_server_address_;
+ }
+ const IPEndPoint& current_client_address() {
+ return current_client_address_;
+ }
+ const QuicEncryptedPacket& current_packet() {
+ return *current_packet_;
+ }
+
+ const QuicConfig& config() const { return config_; }
+
+ const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
+
+ QuicFramer* framer() { return &framer_; }
+
+ QuicConnectionHelperInterface* helper() { return helper_; }
+
+ QuicServerPacketWriter* writer() { return writer_.get(); }
+
+ const QuicConnection::PacketWriterFactory& connection_writer_factory() {
+ return connection_writer_factory_;
+ }
+
+ private:
+ class QuicFramerVisitor;
+ friend class net::test::QuicDispatcherPeer;
+
+ // An adapter that creates packet writers using the dispatcher's
+ // PacketWriterFactory and shared writer. Essentially, it just curries the
+ // writer argument away from QuicDispatcher::PacketWriterFactory.
+ class PacketWriterFactoryAdapter :
+ public QuicConnection::PacketWriterFactory {
+ public:
+ PacketWriterFactoryAdapter(QuicDispatcher* dispatcher);
+ virtual ~PacketWriterFactoryAdapter ();
+
+ virtual QuicPacketWriter* Create(QuicConnection* connection) const OVERRIDE;
+
+ private:
+ QuicDispatcher* dispatcher_;
+ };
+
+ // Called by |framer_visitor_| when the private header has been parsed
+ // of a data packet that is destined for the time wait manager.
+ void OnUnauthenticatedHeader(const QuicPacketHeader& header);
+
+ // Removes the session from the session map and write blocked list, and
+ // adds the ConnectionId to the time-wait list.
+ void CleanUpSession(SessionMap::iterator it);
+
+ bool HandlePacketForTimeWait(const QuicPacketPublicHeader& header);
+
+ const QuicConfig& config_;
+
+ const QuicCryptoServerConfig& crypto_config_;
+
+ // The list of connections waiting to write.
+ WriteBlockedList write_blocked_list_;
+
+ SessionMap session_map_;
+
+ // Entity that manages connection_ids in time wait state.
+ scoped_ptr<QuicTimeWaitListManager> time_wait_list_manager_;
+
+ // The helper used for all connections. Owned by the server.
+ QuicConnectionHelperInterface* helper_;
+
+ // An alarm which deletes closed sessions.
+ scoped_ptr<QuicAlarm> delete_sessions_alarm_;
+
+ // The list of closed but not-yet-deleted sessions.
+ std::list<QuicSession*> closed_session_list_;
+
+ // The writer to write to the socket with.
+ scoped_ptr<QuicServerPacketWriter> writer_;
+
+ // Used to create per-connection packet writers, not |writer_| itself.
+ scoped_ptr<PacketWriterFactory> packet_writer_factory_;
+
+ // Passed in to QuicConnection for it to create the per-connection writers
+ PacketWriterFactoryAdapter connection_writer_factory_;
+
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary).
+ const QuicVersionVector supported_versions_;
+
+ // Information about the packet currently being handled.
+ IPEndPoint current_client_address_;
+ IPEndPoint current_server_address_;
+ const QuicEncryptedPacket* current_packet_;
+
+ QuicFramer framer_;
+ scoped_ptr<QuicFramerVisitor> framer_visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_DISPATCHER_H_
diff --git a/net/quic/quic_end_to_end_unittest.cc b/net/quic/quic_end_to_end_unittest.cc
new file mode 100644
index 0000000..a37bd19
--- /dev/null
+++ b/net/quic/quic_end_to_end_unittest.cc
@@ -0,0 +1,313 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/upload_bytes_element_reader.h"
+#include "net/base/upload_data_stream.h"
+#include "net/cert/mock_cert_verifier.h"
+#include "net/dns/mapped_host_resolver.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_network_transaction.h"
+#include "net/http/http_server_properties_impl.h"
+#include "net/http/http_transaction_test_util.h"
+#include "net/http/transport_security_state.h"
+#include "net/proxy/proxy_service.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/ssl/ssl_config_service_defaults.h"
+#include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
+#include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
+#include "net/tools/quic/test_tools/server_thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+using base::StringPiece;
+using net::tools::QuicInMemoryCache;
+using net::tools::QuicServer;
+using net::tools::test::QuicInMemoryCachePeer;
+using net::tools::test::ServerThread;
+
+namespace net {
+namespace test {
+
+namespace {
+
+const char kResponseBody[] = "some arbitrary response body";
+
+// Factory for creating HttpTransactions, used by TestTransactionConsumer.
+class TestTransactionFactory : public HttpTransactionFactory {
+ public:
+ TestTransactionFactory(const HttpNetworkSession::Params& params)
+ : session_(new HttpNetworkSession(params)) {}
+
+ virtual ~TestTransactionFactory() {
+ }
+
+ // HttpTransactionFactory methods
+ virtual int CreateTransaction(RequestPriority priority,
+ scoped_ptr<HttpTransaction>* trans) OVERRIDE {
+ trans->reset(new HttpNetworkTransaction(priority, session_.get()));
+ return OK;
+ }
+
+ virtual HttpCache* GetCache() OVERRIDE {
+ return nullptr;
+ }
+
+ virtual HttpNetworkSession* GetSession() OVERRIDE { return session_.get(); };
+
+ private:
+ scoped_refptr<HttpNetworkSession> session_;
+};
+
+} // namespace
+
+class QuicEndToEndTest : public PlatformTest {
+ protected:
+ QuicEndToEndTest()
+ : host_resolver_impl_(CreateResolverImpl()),
+ host_resolver_(host_resolver_impl_.PassAs<HostResolver>()),
+ ssl_config_service_(new SSLConfigServiceDefaults),
+ proxy_service_(ProxyService::CreateDirect()),
+ auth_handler_factory_(
+ HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+ strike_register_no_startup_period_(false) {
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+ request_.load_flags = 0;
+
+ params_.enable_quic = true;
+ params_.quic_clock = nullptr;
+ params_.quic_random = nullptr;
+ params_.host_resolver = &host_resolver_;
+ params_.cert_verifier = &cert_verifier_;
+ params_.transport_security_state = &transport_security_state_;
+ params_.proxy_service = proxy_service_.get();
+ params_.ssl_config_service = ssl_config_service_.get();
+ params_.http_auth_handler_factory = auth_handler_factory_.get();
+ params_.http_server_properties = http_server_properties.GetWeakPtr();
+ }
+
+ // Creates a mock host resolver in which www.google.com
+ // resolves to localhost.
+ static MockHostResolver* CreateResolverImpl() {
+ MockHostResolver* resolver = new MockHostResolver();
+ resolver->rules()->AddRule("www.google.com", "127.0.0.1");
+ return resolver;
+ }
+
+ virtual void SetUp() {
+ QuicInMemoryCachePeer::ResetForTests();
+ StartServer();
+
+ // Use a mapped host resolver so that request for www.google.com (port 80)
+ // reach the server running on localhost.
+ std::string map_rule = "MAP www.google.com www.google.com:" +
+ base::IntToString(server_thread_->GetPort());
+ EXPECT_TRUE(host_resolver_.AddRuleFromString(map_rule));
+
+ // To simplify the test, and avoid the race with the HTTP request, we force
+ // QUIC for these requests.
+ params_.origin_to_force_quic_on =
+ HostPortPair::FromString("www.google.com:80");
+
+ transaction_factory_.reset(new TestTransactionFactory(params_));
+ }
+
+ virtual void TearDown() {
+ StopServer();
+ QuicInMemoryCachePeer::ResetForTests();
+ }
+
+ // Starts the QUIC server listening on a random port.
+ void StartServer() {
+ net::IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip));
+ server_address_ = IPEndPoint(ip, 0);
+ server_config_.SetDefaults();
+ server_config_.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ server_config_.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ server_config_.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ server_thread_.reset(new ServerThread(
+ new QuicServer(server_config_, QuicSupportedVersions()),
+ server_address_,
+ strike_register_no_startup_period_));
+ server_thread_->Initialize();
+ server_address_ = IPEndPoint(server_address_.address(),
+ server_thread_->GetPort());
+ server_thread_->Start();
+ server_started_ = true;
+ }
+
+ // Stops the QUIC server.
+ void StopServer() {
+ if (!server_started_) {
+ return;
+ }
+ if (server_thread_.get()) {
+ server_thread_->Quit();
+ server_thread_->Join();
+ }
+ }
+
+ // Adds an entry to the cache used by the QUIC server to serve
+ // responses.
+ void AddToCache(const StringPiece& method,
+ const StringPiece& path,
+ const StringPiece& version,
+ const StringPiece& response_code,
+ const StringPiece& response_detail,
+ const StringPiece& body) {
+ QuicInMemoryCache::GetInstance()->AddSimpleResponse(
+ method, path, version, response_code, response_detail, body);
+ }
+
+ // Populates |request_body_| with |length_| ASCII bytes.
+ void GenerateBody(size_t length) {
+ request_body_.clear();
+ request_body_.reserve(length);
+ for (size_t i = 0; i < length; ++i) {
+ request_body_.append(1, static_cast<char>(32 + i % (126 - 32)));
+ }
+ }
+
+ // Initializes |request_| for a post of |length| bytes.
+ void InitializePostRequest(size_t length) {
+ GenerateBody(length);
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(
+ new UploadBytesElementReader(request_body_.data(),
+ request_body_.length()));
+ upload_data_stream_.reset(new UploadDataStream(element_readers.Pass(), 0));
+ request_.method = "POST";
+ request_.url = GURL("http://www.google.com/");
+ request_.upload_data_stream = upload_data_stream_.get();
+ ASSERT_EQ(OK, request_.upload_data_stream->Init(CompletionCallback()));
+ }
+
+ // Checks that |consumer| completed and received |status_line| and |body|.
+ void CheckResponse(const TestTransactionConsumer& consumer,
+ const std::string& status_line,
+ const std::string& body) {
+ ASSERT_TRUE(consumer.is_done());
+ EXPECT_EQ(OK, consumer.error());
+ EXPECT_EQ(status_line,
+ consumer.response_info()->headers->GetStatusLine());
+ EXPECT_EQ(body, consumer.content());
+ }
+
+ scoped_ptr<MockHostResolver> host_resolver_impl_;
+ MappedHostResolver host_resolver_;
+ MockCertVerifier cert_verifier_;
+ TransportSecurityState transport_security_state_;
+ scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
+ scoped_ptr<ProxyService> proxy_service_;
+ scoped_ptr<HttpAuthHandlerFactory> auth_handler_factory_;
+ HttpServerPropertiesImpl http_server_properties;
+ HttpNetworkSession::Params params_;
+ scoped_ptr<TestTransactionFactory> transaction_factory_;
+ HttpRequestInfo request_;
+ std::string request_body_;
+ scoped_ptr<UploadDataStream> upload_data_stream_;
+ scoped_ptr<ServerThread> server_thread_;
+ IPEndPoint server_address_;
+ std::string server_hostname_;
+ QuicConfig server_config_;
+ bool server_started_;
+ bool strike_register_no_startup_period_;
+};
+
+TEST_F(QuicEndToEndTest, LargeGetWithNoPacketLoss) {
+ std::string response(10 * 1024, 'x');
+
+ AddToCache("GET", request_.url.spec(),
+ "HTTP/1.1", "200", "OK",
+ response);
+
+ TestTransactionConsumer consumer(DEFAULT_PRIORITY,
+ transaction_factory_.get());
+ consumer.Start(&request_, BoundNetLog());
+
+ // Will terminate when the last consumer completes.
+ base::MessageLoop::current()->Run();
+
+ CheckResponse(consumer, "HTTP/1.1 200 OK", response);
+}
+
+// http://crbug.com/307284
+TEST_F(QuicEndToEndTest, DISABLED_LargePostWithNoPacketLoss) {
+ InitializePostRequest(10 * 1024 * 1024);
+
+ AddToCache("POST", request_.url.spec(),
+ "HTTP/1.1", "200", "OK",
+ kResponseBody);
+
+ TestTransactionConsumer consumer(DEFAULT_PRIORITY,
+ transaction_factory_.get());
+ consumer.Start(&request_, BoundNetLog());
+
+ // Will terminate when the last consumer completes.
+ base::MessageLoop::current()->Run();
+
+ CheckResponse(consumer, "HTTP/1.1 200 OK", kResponseBody);
+}
+
+TEST_F(QuicEndToEndTest, LargePostWithPacketLoss) {
+ // FLAGS_fake_packet_loss_percentage = 30;
+ InitializePostRequest(1024 * 1024);
+
+ const char kResponseBody[] = "some really big response body";
+ AddToCache("POST", request_.url.spec(),
+ "HTTP/1.1", "200", "OK",
+ kResponseBody);
+
+ TestTransactionConsumer consumer(DEFAULT_PRIORITY,
+ transaction_factory_.get());
+ consumer.Start(&request_, BoundNetLog());
+
+ // Will terminate when the last consumer completes.
+ base::MessageLoop::current()->Run();
+
+ CheckResponse(consumer, "HTTP/1.1 200 OK", kResponseBody);
+}
+
+TEST_F(QuicEndToEndTest, UberTest) {
+ // FLAGS_fake_packet_loss_percentage = 30;
+
+ const char kResponseBody[] = "some really big response body";
+ AddToCache("GET", request_.url.spec(),
+ "HTTP/1.1", "200", "OK",
+ kResponseBody);
+
+ std::vector<TestTransactionConsumer*> consumers;
+ size_t num_requests = 100;
+ for (size_t i = 0; i < num_requests; ++i) {
+ TestTransactionConsumer* consumer =
+ new TestTransactionConsumer(DEFAULT_PRIORITY,
+ transaction_factory_.get());
+ consumers.push_back(consumer);
+ consumer->Start(&request_, BoundNetLog());
+ }
+
+ // Will terminate when the last consumer completes.
+ base::MessageLoop::current()->Run();
+
+ for (size_t i = 0; i < num_requests; ++i) {
+ CheckResponse(*consumers[i], "HTTP/1.1 200 OK", kResponseBody);
+ }
+ STLDeleteElements(&consumers);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_fec_group.cc b/net/quic/quic_fec_group.cc
new file mode 100644
index 0000000..344f216
--- /dev/null
+++ b/net/quic/quic_fec_group.cc
@@ -0,0 +1,170 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_fec_group.h"
+
+#include <limits>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+using base::StringPiece;
+using std::numeric_limits;
+using std::set;
+
+namespace net {
+
+namespace {
+const QuicPacketSequenceNumber kNoSequenceNumber = kuint64max;
+} // namespace
+
+QuicFecGroup::QuicFecGroup()
+ : min_protected_packet_(kNoSequenceNumber),
+ max_protected_packet_(kNoSequenceNumber),
+ payload_parity_len_(0),
+ effective_encryption_level_(NUM_ENCRYPTION_LEVELS) {
+}
+
+QuicFecGroup::~QuicFecGroup() {}
+
+bool QuicFecGroup::Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
+ StringPiece decrypted_payload) {
+ if (received_packets_.count(header.packet_sequence_number) != 0) {
+ return false;
+ }
+ if (min_protected_packet_ != kNoSequenceNumber &&
+ max_protected_packet_ != kNoSequenceNumber &&
+ (header.packet_sequence_number < min_protected_packet_ ||
+ header.packet_sequence_number > max_protected_packet_)) {
+ DLOG(ERROR) << "FEC group does not cover received packet: "
+ << header.packet_sequence_number;
+ return false;
+ }
+ if (!UpdateParity(decrypted_payload)) {
+ return false;
+ }
+ received_packets_.insert(header.packet_sequence_number);
+ if (encryption_level < effective_encryption_level_) {
+ effective_encryption_level_ = encryption_level;
+ }
+ return true;
+}
+
+bool QuicFecGroup::UpdateFec(
+ EncryptionLevel encryption_level,
+ QuicPacketSequenceNumber fec_packet_sequence_number,
+ const QuicFecData& fec) {
+ if (min_protected_packet_ != kNoSequenceNumber) {
+ return false;
+ }
+ SequenceNumberSet::const_iterator it = received_packets_.begin();
+ while (it != received_packets_.end()) {
+ if ((*it < fec.fec_group) || (*it >= fec_packet_sequence_number)) {
+ DLOG(ERROR) << "FEC group does not cover received packet: " << *it;
+ return false;
+ }
+ ++it;
+ }
+ if (!UpdateParity(fec.redundancy)) {
+ return false;
+ }
+ min_protected_packet_ = fec.fec_group;
+ max_protected_packet_ = fec_packet_sequence_number - 1;
+ if (encryption_level < effective_encryption_level_) {
+ effective_encryption_level_ = encryption_level;
+ }
+ return true;
+}
+
+bool QuicFecGroup::CanRevive() const {
+ // We can revive if we're missing exactly 1 packet.
+ return NumMissingPackets() == 1;
+}
+
+bool QuicFecGroup::IsFinished() const {
+ // We are finished if we are not missing any packets.
+ return NumMissingPackets() == 0;
+}
+
+size_t QuicFecGroup::Revive(QuicPacketHeader* header,
+ char* decrypted_payload,
+ size_t decrypted_payload_len) {
+ if (!CanRevive()) {
+ return 0;
+ }
+
+ // Identify the packet sequence number to be resurrected.
+ QuicPacketSequenceNumber missing = kNoSequenceNumber;
+ for (QuicPacketSequenceNumber i = min_protected_packet_;
+ i <= max_protected_packet_; ++i) {
+ // Is this packet missing?
+ if (received_packets_.count(i) == 0) {
+ missing = i;
+ break;
+ }
+ }
+ DCHECK_NE(kNoSequenceNumber, missing);
+
+ DCHECK_LE(payload_parity_len_, decrypted_payload_len);
+ if (payload_parity_len_ > decrypted_payload_len) {
+ return 0;
+ }
+ for (size_t i = 0; i < payload_parity_len_; ++i) {
+ decrypted_payload[i] = payload_parity_[i];
+ }
+
+ header->packet_sequence_number = missing;
+ header->entropy_flag = false; // Unknown entropy.
+
+ received_packets_.insert(missing);
+ return payload_parity_len_;
+}
+
+bool QuicFecGroup::ProtectsPacketsBefore(QuicPacketSequenceNumber num) const {
+ if (max_protected_packet_ != kNoSequenceNumber) {
+ return max_protected_packet_ < num;
+ }
+ // Since we might not yet have received the FEC packet, we must check
+ // the packets we have received.
+ return *received_packets_.begin() < num;
+}
+
+bool QuicFecGroup::UpdateParity(StringPiece payload) {
+ DCHECK_LE(payload.size(), kMaxPacketSize);
+ if (payload.size() > kMaxPacketSize) {
+ DLOG(ERROR) << "Illegal payload size: " << payload.size();
+ return false;
+ }
+ if (payload_parity_len_ < payload.size()) {
+ payload_parity_len_ = payload.size();
+ }
+ DCHECK_LE(payload.size(), kMaxPacketSize);
+ if (received_packets_.empty() &&
+ min_protected_packet_ == kNoSequenceNumber) {
+ // Initialize the parity to the value of this payload
+ memcpy(payload_parity_, payload.data(), payload.size());
+ if (payload.size() < kMaxPacketSize) {
+ // TODO(rch): expand as needed.
+ memset(payload_parity_ + payload.size(), 0,
+ kMaxPacketSize - payload.size());
+ }
+ return true;
+ }
+ // Update the parity by XORing in the data (padding with 0s if necessary).
+ for (size_t i = 0; i < kMaxPacketSize; ++i) {
+ uint8 byte = i < payload.size() ? payload[i] : 0x00;
+ payload_parity_[i] ^= byte;
+ }
+ return true;
+}
+
+size_t QuicFecGroup::NumMissingPackets() const {
+ if (min_protected_packet_ == kNoSequenceNumber)
+ return numeric_limits<size_t>::max();
+ return (max_protected_packet_ - min_protected_packet_ + 1) -
+ received_packets_.size();
+}
+
+} // namespace net
diff --git a/net/quic/quic_fec_group.h b/net/quic/quic_fec_group.h
new file mode 100644
index 0000000..718d09f
--- /dev/null
+++ b/net/quic/quic_fec_group.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Tracks information about an FEC group, including the packets
+// that have been seen, and the running parity. Provided the ability
+// to revive a dropped packet.
+
+#ifndef NET_QUIC_QUIC_FEC_GROUP_H_
+#define NET_QUIC_QUIC_FEC_GROUP_H_
+
+#include "base/strings/string_piece.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicFecGroup {
+ public:
+ QuicFecGroup();
+ ~QuicFecGroup();
+
+ // Updates the FEC group based on the delivery of a data packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen,
+ // true otherwise.
+ bool Update(EncryptionLevel encryption_level,
+ const QuicPacketHeader& header,
+ base::StringPiece decrypted_payload);
+
+ // Updates the FEC group based on the delivery of an FEC packet decrypted at
+ // |encryption_level|. Returns false if this packet has already been seen or
+ // if it does not claim to protect all the packets previously seen in this
+ // group.
+ bool UpdateFec(EncryptionLevel encryption_level,
+ QuicPacketSequenceNumber fec_packet_sequence_number,
+ const QuicFecData& fec);
+
+ // Returns true if a packet can be revived from this FEC group.
+ bool CanRevive() const;
+
+ // Returns true if all packets (FEC and data) from this FEC group have been
+ // seen or revived
+ bool IsFinished() const;
+
+ // Revives the missing packet from this FEC group. This may return a packet
+ // that is null padded to a greater length than the original packet, but
+ // the framer will handle it correctly. Returns the length of the data
+ // written to |decrypted_payload|, or 0 if the packet could not be revived.
+ size_t Revive(QuicPacketHeader* header,
+ char* decrypted_payload,
+ size_t decrypted_payload_len);
+
+ // Returns true of this FEC group protects any packets with sequence
+ // numbers less than |num|.
+ bool ProtectsPacketsBefore(QuicPacketSequenceNumber num) const;
+
+ const base::StringPiece payload_parity() const {
+ return base::StringPiece(payload_parity_, payload_parity_len_);
+ }
+
+ QuicPacketSequenceNumber min_protected_packet() const {
+ return min_protected_packet_;
+ }
+
+ size_t NumReceivedPackets() const {
+ return received_packets_.size();
+ }
+
+ // Returns the effective encryption level of the FEC group.
+ EncryptionLevel effective_encryption_level() const {
+ return effective_encryption_level_;
+ }
+
+ private:
+ bool UpdateParity(base::StringPiece payload);
+ // Returns the number of missing packets, or size_t max if the number
+ // of missing packets is not known.
+ size_t NumMissingPackets() const;
+
+ // Set of packets that we have recevied.
+ SequenceNumberSet received_packets_;
+ // Sequence number of the first protected packet in this group (the one
+ // with the lowest packet sequence number). Will only be set once the FEC
+ // packet has been seen.
+ QuicPacketSequenceNumber min_protected_packet_;
+ // Sequence number of the last protected packet in this group (the one
+ // with the highest packet sequence number). Will only be set once the FEC
+ // packet has been seen.
+ QuicPacketSequenceNumber max_protected_packet_;
+ // The cumulative parity calculation of all received packets.
+ char payload_parity_[kMaxPacketSize];
+ size_t payload_parity_len_;
+ // The effective encryption level, which is the lowest encryption level of
+ // the data and FEC in the group.
+ EncryptionLevel effective_encryption_level_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicFecGroup);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_FEC_GROUP_H_
diff --git a/net/quic/quic_fec_group_test.cc b/net/quic/quic_fec_group_test.cc
new file mode 100644
index 0000000..b9420dd
--- /dev/null
+++ b/net/quic/quic_fec_group_test.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_fec_group.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::_;
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const char* kData[] = {
+ "abc12345678",
+ "987defg",
+ "ghi12345",
+ "987jlkmno",
+ "mno4567890",
+ "789pqrstuvw",
+};
+
+const bool kEntropyFlag[] = {
+ false,
+ true,
+ true,
+ false,
+ true,
+ true,
+};
+
+} // namespace
+
+class QuicFecGroupTest : public ::testing::Test {
+ protected:
+ void RunTest(size_t num_packets, size_t lost_packet, bool out_of_order) {
+ size_t max_len = strlen(kData[0]);
+ scoped_ptr<char[]> redundancy(new char[max_len]);
+ for (size_t packet = 0; packet < num_packets; ++packet) {
+ for (size_t i = 0; i < max_len; i++) {
+ if (packet == 0) {
+ // Initialize to the first packet.
+ redundancy[i] = kData[0][i];
+ continue;
+ }
+ // XOR in the remaining packets.
+ uint8 byte = i > strlen(kData[packet]) ? 0x00 : kData[packet][i];
+ redundancy[i] = redundancy[i] ^ byte;
+ }
+ }
+
+ QuicFecGroup group;
+
+ // If we're out of order, send the FEC packet in the position of the
+ // lost packet. Otherwise send all (non-missing) packets, then FEC.
+ if (out_of_order) {
+ // Update the FEC state for each non-lost packet.
+ for (size_t packet = 0; packet < num_packets; packet++) {
+ if (packet == lost_packet) {
+ ASSERT_FALSE(group.IsFinished());
+ QuicFecData fec;
+ fec.fec_group = 0;
+ fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets,
+ fec));
+ } else {
+ QuicPacketHeader header;
+ header.packet_sequence_number = packet;
+ header.entropy_flag = kEntropyFlag[packet];
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header,
+ kData[packet]));
+ }
+ ASSERT_TRUE(group.CanRevive() == (packet == num_packets - 1));
+ }
+ } else {
+ // Update the FEC state for each non-lost packet.
+ for (size_t packet = 0; packet < num_packets; packet++) {
+ if (packet == lost_packet) {
+ continue;
+ }
+
+ QuicPacketHeader header;
+ header.packet_sequence_number = packet;
+ header.entropy_flag = kEntropyFlag[packet];
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header,
+ kData[packet]));
+ ASSERT_FALSE(group.CanRevive());
+ }
+
+ ASSERT_FALSE(group.IsFinished());
+ // Attempt to revive the missing packet.
+ QuicFecData fec;
+ fec.fec_group = 0;
+ fec.redundancy = StringPiece(redundancy.get(), strlen(kData[0]));
+
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, num_packets,
+ fec));
+ }
+ QuicPacketHeader header;
+ char recovered[kMaxPacketSize];
+ ASSERT_TRUE(group.CanRevive());
+ size_t len = group.Revive(&header, recovered, arraysize(recovered));
+ ASSERT_NE(0u, len)
+ << "Failed to revive packet " << lost_packet << " out of "
+ << num_packets;
+ EXPECT_EQ(lost_packet, header.packet_sequence_number)
+ << "Failed to revive packet " << lost_packet << " out of "
+ << num_packets;
+ // Revived packets have an unknown entropy.
+ EXPECT_FALSE(header.entropy_flag);
+ ASSERT_GE(len, strlen(kData[lost_packet])) << "Incorrect length";
+ for (size_t i = 0; i < strlen(kData[lost_packet]); i++) {
+ EXPECT_EQ(kData[lost_packet][i], recovered[i]);
+ }
+ ASSERT_TRUE(group.IsFinished());
+ }
+};
+
+TEST_F(QuicFecGroupTest, UpdateAndRevive) {
+ RunTest(2, 0, false);
+ RunTest(2, 1, false);
+
+ RunTest(3, 0, false);
+ RunTest(3, 1, false);
+ RunTest(3, 2, false);
+}
+
+TEST_F(QuicFecGroupTest, UpdateAndReviveOutOfOrder) {
+ RunTest(2, 0, true);
+ RunTest(2, 1, true);
+
+ RunTest(3, 0, true);
+ RunTest(3, 1, true);
+ RunTest(3, 2, true);
+}
+
+TEST_F(QuicFecGroupTest, UpdateFecIfReceivedPacketIsNotCovered) {
+ char data1[] = "abc123";
+ char redundancy[arraysize(data1)];
+ for (size_t i = 0; i < arraysize(data1); i++) {
+ redundancy[i] = data1[i];
+ }
+
+ QuicFecGroup group;
+
+ QuicPacketHeader header;
+ header.packet_sequence_number = 3;
+ group.Update(ENCRYPTION_FORWARD_SECURE, header, data1);
+
+ QuicFecData fec;
+ fec.fec_group = 1;
+ fec.redundancy = redundancy;
+
+ header.packet_sequence_number = 2;
+ ASSERT_FALSE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 2, fec));
+}
+
+TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) {
+ QuicPacketHeader header;
+ header.packet_sequence_number = 3;
+
+ QuicFecGroup group;
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
+
+ EXPECT_FALSE(group.ProtectsPacketsBefore(1));
+ EXPECT_FALSE(group.ProtectsPacketsBefore(2));
+ EXPECT_FALSE(group.ProtectsPacketsBefore(3));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(4));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(5));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(50));
+}
+
+TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithSeveralPackets) {
+ QuicPacketHeader header;
+ header.packet_sequence_number = 3;
+
+ QuicFecGroup group;
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
+
+ header.packet_sequence_number = 7;
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
+
+ header.packet_sequence_number = 5;
+ ASSERT_TRUE(group.Update(ENCRYPTION_FORWARD_SECURE, header, kData[0]));
+
+ EXPECT_FALSE(group.ProtectsPacketsBefore(1));
+ EXPECT_FALSE(group.ProtectsPacketsBefore(2));
+ EXPECT_FALSE(group.ProtectsPacketsBefore(3));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(4));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(5));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(6));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(7));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(8));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(9));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(50));
+}
+
+TEST_F(QuicFecGroupTest, ProtectsPacketsBeforeWithFecData) {
+ QuicFecData fec;
+ fec.fec_group = 2;
+ fec.redundancy = kData[0];
+
+ QuicFecGroup group;
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 3, fec));
+
+ EXPECT_FALSE(group.ProtectsPacketsBefore(1));
+ EXPECT_FALSE(group.ProtectsPacketsBefore(2));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(3));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(4));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(5));
+ EXPECT_TRUE(group.ProtectsPacketsBefore(50));
+}
+
+TEST_F(QuicFecGroupTest, EffectiveEncryptionLevel) {
+ QuicFecGroup group;
+ EXPECT_EQ(NUM_ENCRYPTION_LEVELS, group.effective_encryption_level());
+
+ QuicPacketHeader header;
+ header.packet_sequence_number = 5;
+ ASSERT_TRUE(group.Update(ENCRYPTION_INITIAL, header, kData[0]));
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+
+ QuicFecData fec;
+ fec.fec_group = 0;
+ fec.redundancy = kData[0];
+ ASSERT_TRUE(group.UpdateFec(ENCRYPTION_FORWARD_SECURE, 7, fec));
+ EXPECT_EQ(ENCRYPTION_INITIAL, group.effective_encryption_level());
+
+ header.packet_sequence_number = 3;
+ ASSERT_TRUE(group.Update(ENCRYPTION_NONE, header, kData[0]));
+ EXPECT_EQ(ENCRYPTION_NONE, group.effective_encryption_level());
+}
+
+} // namespace net
diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc
new file mode 100644
index 0000000..864e12b
--- /dev/null
+++ b/net/quic/quic_flags.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_flags.h"
+
+// TODO(rtenneti): Remove this.
+// Do not flip this flag until the flakiness of the
+// net/tools/quic/end_to_end_test is fixed.
+// If true, then QUIC connections will track the retransmission history of a
+// packet so that an ack of a previous transmission will ack the data of all
+// other transmissions.
+bool FLAGS_track_retransmission_history = false;
+
+bool FLAGS_quic_allow_oversized_packets_for_test = false;
+
+// When true, the use time based loss detection instead of nack.
+bool FLAGS_quic_use_time_loss_detection = false;
+
+// If true, it will return as soon as an error is detected while validating
+// CHLO.
+bool FLAGS_use_early_return_when_verifying_chlo = true;
+
+// If true, QUIC crypto reject message will include the reasons for rejection.
+bool FLAGS_send_quic_crypto_reject_reason = false;
+
+// If true, QUIC connections will support FEC protection of data while sending
+// packets, to reduce latency of data delivery to the application. The client
+// must also request FEC protection for the server to use FEC.
+bool FLAGS_enable_quic_fec = false;
+
+// If true, a QUIC connection with too many unfinished streams will be closed.
+bool FLAGS_close_quic_connection_unfinished_streams_2 = false;
+
+// When true, defaults to BBR congestion control instead of Cubic.
+bool FLAGS_quic_use_bbr_congestion_control = false;
+
+// If true, the server will accept slightly more streams than the negotiated
+// limit.
+bool FLAGS_quic_allow_more_open_streams = false;
+
+// If true, then QUIC connections will only timeout when an alarm fires, never
+// when setting a timeout.
+bool FLAGS_quic_timeouts_only_from_alarms = true;
+
+// If true, then QUIC connections will set both idle and overall timeouts in a
+// single method.
+bool FLAGS_quic_unified_timeouts = false;
diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h
new file mode 100644
index 0000000..f1fea1e
--- /dev/null
+++ b/net/quic/quic_flags.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_FLAGS_H_
+#define NET_QUIC_QUIC_FLAGS_H_
+
+#include "net/base/net_export.h"
+
+NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_oversized_packets_for_test;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection;
+NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo;
+NET_EXPORT_PRIVATE extern bool FLAGS_send_quic_crypto_reject_reason;
+NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_fec;
+NET_EXPORT_PRIVATE extern bool FLAGS_close_quic_connection_unfinished_streams_2;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_bbr_congestion_control;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_more_open_streams;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_timeouts_only_from_alarms;
+NET_EXPORT_PRIVATE extern bool FLAGS_quic_unified_timeouts;
+
+#endif // NET_QUIC_QUIC_FLAGS_H_
diff --git a/net/quic/quic_flow_controller.cc b/net/quic/quic_flow_controller.cc
new file mode 100644
index 0000000..b145280
--- /dev/null
+++ b/net/quic/quic_flow_controller.cc
@@ -0,0 +1,191 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_flow_controller.h"
+
+#include "base/basictypes.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicFlowController::QuicFlowController(QuicConnection* connection,
+ QuicStreamId id,
+ bool is_server,
+ uint64 send_window_offset,
+ uint64 receive_window_offset,
+ uint64 max_receive_window)
+ : connection_(connection),
+ id_(id),
+ is_enabled_(true),
+ is_server_(is_server),
+ bytes_consumed_(0),
+ highest_received_byte_offset_(0),
+ bytes_sent_(0),
+ send_window_offset_(send_window_offset),
+ receive_window_offset_(receive_window_offset),
+ max_receive_window_(max_receive_window),
+ last_blocked_send_window_offset_(0) {
+ DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_
+ << ", setting initial receive window offset to: "
+ << receive_window_offset_
+ << ", max receive window to: "
+ << max_receive_window_
+ << ", setting send window offset to: " << send_window_offset_;
+}
+
+void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ bytes_consumed_ += bytes_consumed;
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_;
+
+ MaybeSendWindowUpdate();
+}
+
+bool QuicFlowController::UpdateHighestReceivedOffset(uint64 new_offset) {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ // Only update if offset has increased.
+ if (new_offset <= highest_received_byte_offset_) {
+ return false;
+ }
+
+ DVLOG(1) << ENDPOINT << "Stream " << id_
+ << " highest byte offset increased from: "
+ << highest_received_byte_offset_ << " to " << new_offset;
+ highest_received_byte_offset_ = new_offset;
+ return true;
+}
+
+void QuicFlowController::AddBytesSent(uint64 bytes_sent) {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ if (bytes_sent_ + bytes_sent > send_window_offset_) {
+ LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra "
+ << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_
+ << ", and send_window_offset_ = " << send_window_offset_;
+ bytes_sent_ = send_window_offset_;
+
+ // This is an error on our side, close the connection as soon as possible.
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ return;
+ }
+
+ bytes_sent_ += bytes_sent;
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_;
+}
+
+bool QuicFlowController::FlowControlViolation() {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ if (highest_received_byte_offset_ > receive_window_offset_) {
+ LOG(ERROR) << ENDPOINT << "Flow control violation on stream "
+ << id_ << ", receive window offset: "
+ << receive_window_offset_
+ << ", highest received byte offset: "
+ << highest_received_byte_offset_;
+ return true;
+ }
+ return false;
+}
+
+void QuicFlowController::MaybeSendWindowUpdate() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ // Send WindowUpdate to increase receive window if
+ // (receive window offset - consumed bytes) < (max window / 2).
+ // This is behaviour copied from SPDY.
+ DCHECK_LT(bytes_consumed_, receive_window_offset_);
+ size_t consumed_window = receive_window_offset_ - bytes_consumed_;
+ size_t threshold = (max_receive_window_ / 2);
+
+ if (consumed_window < threshold) {
+ // Update our receive window.
+ receive_window_offset_ += (max_receive_window_ - consumed_window);
+
+ DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_
+ << ", consumed bytes: " << bytes_consumed_
+ << ", consumed window: " << consumed_window
+ << ", and threshold: " << threshold
+ << ", and max recvw: " << max_receive_window_
+ << ". New receive window offset is: " << receive_window_offset_;
+
+ // Inform the peer of our new receive window.
+ connection_->SendWindowUpdate(id_, receive_window_offset_);
+ }
+}
+
+void QuicFlowController::MaybeSendBlocked() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ if (SendWindowSize() == 0 &&
+ last_blocked_send_window_offset_ < send_window_offset_) {
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. "
+ << "Send window: " << SendWindowSize()
+ << ", bytes sent: " << bytes_sent_
+ << ", send limit: " << send_window_offset_;
+ // The entire send_window has been consumed, we are now flow control
+ // blocked.
+ connection_->SendBlocked(id_);
+
+ // Keep track of when we last sent a BLOCKED frame so that we only send one
+ // at a given send offset.
+ last_blocked_send_window_offset_ = send_window_offset_;
+ }
+}
+
+bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) {
+ if (!IsEnabled()) {
+ return false;
+ }
+
+ // Only update if send window has increased.
+ if (new_send_window_offset <= send_window_offset_) {
+ return false;
+ }
+
+ DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_
+ << " with new offset " << new_send_window_offset
+ << " , current offset: " << send_window_offset_;
+
+ send_window_offset_ = new_send_window_offset;
+ return true;
+}
+
+void QuicFlowController::Disable() {
+ is_enabled_ = false;
+}
+
+bool QuicFlowController::IsEnabled() const {
+ return is_enabled_;
+}
+
+bool QuicFlowController::IsBlocked() const {
+ return IsEnabled() && SendWindowSize() == 0;
+}
+
+uint64 QuicFlowController::SendWindowSize() const {
+ if (bytes_sent_ > send_window_offset_) {
+ return 0;
+ }
+ return send_window_offset_ - bytes_sent_;
+}
+
+} // namespace net
diff --git a/net/quic/quic_flow_controller.h b/net/quic/quic_flow_controller.h
new file mode 100644
index 0000000..e5f5494
--- /dev/null
+++ b/net/quic/quic_flow_controller.h
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_FLOW_CONTROLLER_H_
+#define NET_QUIC_QUIC_FLOW_CONTROLLER_H_
+
+#include "base/basictypes.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace test {
+class QuicFlowControllerPeer;
+} // namespace test
+
+class QuicConnection;
+
+const QuicStreamId kConnectionLevelId = 0;
+
+// QuicFlowController allows a QUIC stream or connection to perform flow
+// control. The stream/connection owns a QuicFlowController which keeps track of
+// bytes sent/received, can tell the owner if it is flow control blocked, and
+// can send WINDOW_UPDATE or BLOCKED frames when needed.
+class NET_EXPORT_PRIVATE QuicFlowController {
+ public:
+ QuicFlowController(QuicConnection* connection,
+ QuicStreamId id,
+ bool is_server,
+ uint64 send_window_offset,
+ uint64 receive_window_offset,
+ uint64 max_receive_window);
+ ~QuicFlowController() {}
+
+ // Called when we see a new highest received byte offset from the peer, either
+ // via a data frame or a RST.
+ // Returns true if this call changes highest_received_byte_offset_, and false
+ // in the case where |new_offset| is <= highest_received_byte_offset_.
+ bool UpdateHighestReceivedOffset(uint64 new_offset);
+
+ // Called when bytes received from the peer are consumed locally. This may
+ // trigger the sending of a WINDOW_UPDATE frame using |connection|.
+ void AddBytesConsumed(uint64 bytes_consumed);
+
+ // Called when bytes are sent to the peer.
+ void AddBytesSent(uint64 bytes_sent);
+
+ // Set a new send window offset.
+ // Returns true if this changes send_window_offset_, and false in the case
+ // where |new_send_window| is <= send_window_offset_.
+ bool UpdateSendWindowOffset(uint64 new_send_window_offset);
+
+ // Returns the current available send window.
+ uint64 SendWindowSize() const;
+
+ // Send a BLOCKED frame if appropriate.
+ void MaybeSendBlocked();
+
+ // Disable flow control.
+ void Disable();
+
+ // Returns true if flow control is enabled.
+ bool IsEnabled() const;
+
+ // Returns true if flow control send limits have been reached.
+ bool IsBlocked() const;
+
+ // Returns true if flow control receive limits have been violated by the peer.
+ bool FlowControlViolation();
+
+ uint64 bytes_consumed() const { return bytes_consumed_; }
+
+ uint64 highest_received_byte_offset() const {
+ return highest_received_byte_offset_;
+ }
+
+ private:
+ friend class test::QuicFlowControllerPeer;
+
+ // Send a WINDOW_UPDATE frame if appropriate.
+ void MaybeSendWindowUpdate();
+
+ // The parent connection, used to send connection close on flow control
+ // violation, and WINDOW_UPDATE and BLOCKED frames when appropriate.
+ // Not owned.
+ QuicConnection* connection_;
+
+ // ID of stream this flow controller belongs to. This can be 0 if this is a
+ // connection level flow controller.
+ QuicStreamId id_;
+
+ // True if flow control is enabled.
+ bool is_enabled_;
+
+ // True if this is owned by a server.
+ bool is_server_;
+
+ // Track number of bytes received from the peer, which have been consumed
+ // locally.
+ uint64 bytes_consumed_;
+
+ // The highest byte offset we have seen from the peer. This could be the
+ // highest offset in a data frame, or a final value in a RST.
+ uint64 highest_received_byte_offset_;
+
+ // Tracks number of bytes sent to the peer.
+ uint64 bytes_sent_;
+
+ // The absolute offset in the outgoing byte stream. If this offset is reached
+ // then we become flow control blocked until we receive a WINDOW_UPDATE.
+ uint64 send_window_offset_;
+
+ // The absolute offset in the incoming byte stream. The peer should never send
+ // us bytes which are beyond this offset.
+ uint64 receive_window_offset_;
+
+ // Largest size the receive window can grow to.
+ uint64 max_receive_window_;
+
+ // Keep track of the last time we sent a BLOCKED frame. We should only send
+ // another when the number of bytes we have sent has changed.
+ uint64 last_blocked_send_window_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicFlowController);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_FLOW_CONTROLLER_H_
diff --git a/net/quic/quic_flow_controller_test.cc b/net/quic/quic_flow_controller_test.cc
new file mode 100644
index 0000000..f960169
--- /dev/null
+++ b/net/quic/quic_flow_controller_test.cc
@@ -0,0 +1,158 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_flow_controller.h"
+
+#include "base/strings/stringprintf.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::StringPrintf;
+
+namespace net {
+namespace test {
+
+using ::testing::_;
+
+class QuicFlowControllerTest : public ::testing::Test {
+ public:
+ QuicFlowControllerTest()
+ : stream_id_(1234),
+ send_window_(kInitialSessionFlowControlWindowForTest),
+ receive_window_(kInitialSessionFlowControlWindowForTest),
+ max_receive_window_(kInitialSessionFlowControlWindowForTest),
+ connection_(false) {
+ }
+
+ void Initialize() {
+ flow_controller_.reset(new QuicFlowController(
+ &connection_, stream_id_, false, send_window_,
+ receive_window_, max_receive_window_));
+ }
+
+ protected:
+ QuicStreamId stream_id_;
+ uint64 send_window_;
+ uint64 receive_window_;
+ uint64 max_receive_window_;
+ scoped_ptr<QuicFlowController> flow_controller_;
+ MockConnection connection_;
+};
+
+TEST_F(QuicFlowControllerTest, SendingBytes) {
+ Initialize();
+
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send some bytes, but not enough to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_ / 2, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_ / 2);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent.
+ EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(1);
+ flow_controller_->MaybeSendBlocked();
+
+ // Update the send window, and verify this has unblocked.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Updating with a smaller offset doesn't change anything.
+ EXPECT_FALSE(flow_controller_->UpdateSendWindowOffset(send_window_ / 10));
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Try to send more bytes, violating flow control.
+ EXPECT_CALL(connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA));
+ EXPECT_DFATAL(
+ flow_controller_->AddBytesSent(send_window_ * 10),
+ StringPrintf("Trying to send an extra %d bytes",
+ static_cast<int>(send_window_ * 10)));
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+}
+
+TEST_F(QuicFlowControllerTest, ReceivingBytes) {
+ Initialize();
+
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Receive some bytes, updating highest received offset, but not enough to
+ // fill flow control receive window.
+ EXPECT_TRUE(
+ flow_controller_->UpdateHighestReceivedOffset(1 + receive_window_ / 2));
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ((receive_window_ / 2) - 1,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+
+ // Consume enough bytes to send a WINDOW_UPDATE frame.
+ EXPECT_CALL(connection_, SendWindowUpdate(stream_id_, _)).Times(1);
+
+ flow_controller_->AddBytesConsumed(1 + receive_window_ / 2);
+
+ // Result is that once again we have a fully open receive window.
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
+ QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
+}
+
+TEST_F(QuicFlowControllerTest, OnlySendBlockedFrameOncePerOffset) {
+ Initialize();
+
+ // Test that we don't send duplicate BLOCKED frames. We should only send one
+ // BLOCKED frame at a given send window offset.
+ EXPECT_TRUE(flow_controller_->IsEnabled());
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_FALSE(flow_controller_->FlowControlViolation());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+
+ // Send enough bytes to block.
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // Expect that 2 BLOCKED frames should get sent in total.
+ EXPECT_CALL(connection_, SendBlocked(stream_id_)).Times(2);
+
+ // BLOCKED frame should get sent.
+ flow_controller_->MaybeSendBlocked();
+
+ // BLOCKED frame should not get sent again until our send offset changes.
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+ flow_controller_->MaybeSendBlocked();
+
+ // Update the send window, then send enough bytes to block again.
+ EXPECT_TRUE(flow_controller_->UpdateSendWindowOffset(2 * send_window_));
+ EXPECT_FALSE(flow_controller_->IsBlocked());
+ EXPECT_EQ(send_window_, flow_controller_->SendWindowSize());
+ flow_controller_->AddBytesSent(send_window_);
+ EXPECT_TRUE(flow_controller_->IsBlocked());
+ EXPECT_EQ(0u, flow_controller_->SendWindowSize());
+
+ // BLOCKED frame should get sent as send offset has changed.
+ flow_controller_->MaybeSendBlocked();
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
new file mode 100644
index 0000000..83c44c0
--- /dev/null
+++ b/net/quic/quic_framer.cc
@@ -0,0 +1,2325 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_framer.h"
+
+#include "base/containers/hash_tables.h"
+#include "base/stl_util.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_handshake_message.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/quic/quic_data_writer.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_socket_address_coder.h"
+
+using base::StringPiece;
+using std::make_pair;
+using std::map;
+using std::max;
+using std::min;
+using std::numeric_limits;
+using std::string;
+
+namespace net {
+
+namespace {
+
+// Mask to select the lowest 48 bits of a sequence number.
+const QuicPacketSequenceNumber k6ByteSequenceNumberMask =
+ GG_UINT64_C(0x0000FFFFFFFFFFFF);
+const QuicPacketSequenceNumber k4ByteSequenceNumberMask =
+ GG_UINT64_C(0x00000000FFFFFFFF);
+const QuicPacketSequenceNumber k2ByteSequenceNumberMask =
+ GG_UINT64_C(0x000000000000FFFF);
+const QuicPacketSequenceNumber k1ByteSequenceNumberMask =
+ GG_UINT64_C(0x00000000000000FF);
+
+const QuicConnectionId k1ByteConnectionIdMask = GG_UINT64_C(0x00000000000000FF);
+const QuicConnectionId k4ByteConnectionIdMask = GG_UINT64_C(0x00000000FFFFFFFF);
+
+// Number of bits the sequence number length bits are shifted from the right
+// edge of the public header.
+const uint8 kPublicHeaderSequenceNumberShift = 4;
+
+// New Frame Types, QUIC v. >= 10:
+// There are two interpretations for the Frame Type byte in the QUIC protocol,
+// resulting in two Frame Types: Special Frame Types and Regular Frame Types.
+//
+// Regular Frame Types use the Frame Type byte simply. Currently defined
+// Regular Frame Types are:
+// Padding : 0b 00000000 (0x00)
+// ResetStream : 0b 00000001 (0x01)
+// ConnectionClose : 0b 00000010 (0x02)
+// GoAway : 0b 00000011 (0x03)
+// WindowUpdate : 0b 00000100 (0x04)
+// Blocked : 0b 00000101 (0x05)
+//
+// Special Frame Types encode both a Frame Type and corresponding flags
+// all in the Frame Type byte. Currently defined Special Frame Types are:
+// Stream : 0b 1xxxxxxx
+// Ack : 0b 01xxxxxx
+// CongestionFeedback : 0b 001xxxxx
+//
+// Semantics of the flag bits above (the x bits) depends on the frame type.
+
+// Masks to determine if the frame type is a special use
+// and for specific special frame types.
+const uint8 kQuicFrameTypeSpecialMask = 0xE0; // 0b 11100000
+const uint8 kQuicFrameTypeStreamMask = 0x80;
+const uint8 kQuicFrameTypeAckMask = 0x40;
+const uint8 kQuicFrameTypeCongestionFeedbackMask = 0x20;
+
+// Stream frame relative shifts and masks for interpreting the stream flags.
+// StreamID may be 1, 2, 3, or 4 bytes.
+const uint8 kQuicStreamIdShift = 2;
+const uint8 kQuicStreamIDLengthMask = 0x03;
+
+// Offset may be 0, 2, 3, 4, 5, 6, 7, 8 bytes.
+const uint8 kQuicStreamOffsetShift = 3;
+const uint8 kQuicStreamOffsetMask = 0x07;
+
+// Data length may be 0 or 2 bytes.
+const uint8 kQuicStreamDataLengthShift = 1;
+const uint8 kQuicStreamDataLengthMask = 0x01;
+
+// Fin bit may be set or not.
+const uint8 kQuicStreamFinShift = 1;
+const uint8 kQuicStreamFinMask = 0x01;
+
+// Sequence number size shift used in AckFrames.
+const uint8 kQuicSequenceNumberLengthShift = 2;
+
+// Acks may be truncated.
+const uint8 kQuicAckTruncatedShift = 1;
+const uint8 kQuicAckTruncatedMask = 0x01;
+
+// Acks may not have any nacks.
+const uint8 kQuicHasNacksMask = 0x01;
+
+// Returns the absolute value of the difference between |a| and |b|.
+QuicPacketSequenceNumber Delta(QuicPacketSequenceNumber a,
+ QuicPacketSequenceNumber b) {
+ // Since these are unsigned numbers, we can't just return abs(a - b)
+ if (a < b) {
+ return b - a;
+ }
+ return a - b;
+}
+
+QuicPacketSequenceNumber ClosestTo(QuicPacketSequenceNumber target,
+ QuicPacketSequenceNumber a,
+ QuicPacketSequenceNumber b) {
+ return (Delta(target, a) < Delta(target, b)) ? a : b;
+}
+
+QuicSequenceNumberLength ReadSequenceNumberLength(uint8 flags) {
+ switch (flags & PACKET_FLAGS_6BYTE_SEQUENCE) {
+ case PACKET_FLAGS_6BYTE_SEQUENCE:
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_4BYTE_SEQUENCE:
+ return PACKET_4BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_2BYTE_SEQUENCE:
+ return PACKET_2BYTE_SEQUENCE_NUMBER;
+ case PACKET_FLAGS_1BYTE_SEQUENCE:
+ return PACKET_1BYTE_SEQUENCE_NUMBER;
+ default:
+ LOG(DFATAL) << "Unreachable case statement.";
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ }
+}
+
+} // namespace
+
+bool QuicFramerVisitorInterface::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool QuicFramerVisitorInterface::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
+QuicFramer::QuicFramer(const QuicVersionVector& supported_versions,
+ QuicTime creation_time,
+ bool is_server)
+ : visitor_(nullptr),
+ fec_builder_(nullptr),
+ entropy_calculator_(nullptr),
+ error_(QUIC_NO_ERROR),
+ last_sequence_number_(0),
+ last_serialized_connection_id_(0),
+ supported_versions_(supported_versions),
+ decrypter_level_(ENCRYPTION_NONE),
+ alternative_decrypter_level_(ENCRYPTION_NONE),
+ alternative_decrypter_latch_(false),
+ is_server_(is_server),
+ validate_flags_(true),
+ creation_time_(creation_time),
+ last_timestamp_(QuicTime::Delta::Zero()) {
+ DCHECK(!supported_versions.empty());
+ quic_version_ = supported_versions_[0];
+ decrypter_.reset(QuicDecrypter::Create(kNULL));
+ encrypter_[ENCRYPTION_NONE].reset(QuicEncrypter::Create(kNULL));
+}
+
+QuicFramer::~QuicFramer() {}
+
+// static
+size_t QuicFramer::GetMinStreamFrameSize(QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group) {
+ bool no_stream_frame_length = last_frame_in_packet &&
+ is_in_fec_group == NOT_IN_FEC_GROUP;
+ return kQuicFrameTypeSize + GetStreamIdSize(stream_id) +
+ GetStreamOffsetSize(offset) +
+ (no_stream_frame_length ? 0 : kQuicStreamPayloadLengthSize);
+}
+
+// static
+size_t QuicFramer::GetMinAckFrameSize(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicSequenceNumberLength largest_observed_length) {
+ return kQuicFrameTypeSize + kQuicEntropyHashSize +
+ largest_observed_length + kQuicDeltaTimeLargestObservedSize;
+}
+
+// static
+size_t QuicFramer::GetStopWaitingFrameSize(
+ QuicSequenceNumberLength sequence_number_length) {
+ return kQuicFrameTypeSize + kQuicEntropyHashSize +
+ sequence_number_length;
+}
+
+// static
+size_t QuicFramer::GetMinRstStreamFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ kQuicMaxStreamOffsetSize + kQuicErrorCodeSize +
+ kQuicErrorDetailsLengthSize;
+}
+
+// static
+size_t QuicFramer::GetMinConnectionCloseFrameSize() {
+ return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize;
+}
+
+// static
+size_t QuicFramer::GetMinGoAwayFrameSize() {
+ return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize +
+ kQuicMaxStreamIdSize;
+}
+
+// static
+size_t QuicFramer::GetWindowUpdateFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+}
+
+// static
+size_t QuicFramer::GetBlockedFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize;
+}
+
+// static
+size_t QuicFramer::GetStreamIdSize(QuicStreamId stream_id) {
+ // Sizes are 1 through 4 bytes.
+ for (int i = 1; i <= 4; ++i) {
+ stream_id >>= 8;
+ if (stream_id == 0) {
+ return i;
+ }
+ }
+ LOG(DFATAL) << "Failed to determine StreamIDSize.";
+ return 4;
+}
+
+// static
+size_t QuicFramer::GetStreamOffsetSize(QuicStreamOffset offset) {
+ // 0 is a special case.
+ if (offset == 0) {
+ return 0;
+ }
+ // 2 through 8 are the remaining sizes.
+ offset >>= 8;
+ for (int i = 2; i <= 8; ++i) {
+ offset >>= 8;
+ if (offset == 0) {
+ return i;
+ }
+ }
+ LOG(DFATAL) << "Failed to determine StreamOffsetSize.";
+ return 8;
+}
+
+// static
+size_t QuicFramer::GetVersionNegotiationPacketSize(size_t number_versions) {
+ return kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID +
+ number_versions * kQuicVersionSize;
+}
+
+bool QuicFramer::IsSupportedVersion(const QuicVersion version) const {
+ for (size_t i = 0; i < supported_versions_.size(); ++i) {
+ if (version == supported_versions_[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t QuicFramer::GetSerializedFrameLength(
+ const QuicFrame& frame,
+ size_t free_bytes,
+ bool first_frame,
+ bool last_frame,
+ InFecGroup is_in_fec_group,
+ QuicSequenceNumberLength sequence_number_length) {
+ if (frame.type == PADDING_FRAME) {
+ // PADDING implies end of packet.
+ return free_bytes;
+ }
+ size_t frame_len =
+ ComputeFrameLength(frame, last_frame, is_in_fec_group,
+ sequence_number_length);
+ if (frame_len <= free_bytes) {
+ // Frame fits within packet. Note that acks may be truncated.
+ return frame_len;
+ }
+ // Only truncate the first frame in a packet, so if subsequent ones go
+ // over, stop including more frames.
+ if (!first_frame) {
+ return 0;
+ }
+ bool can_truncate = frame.type == ACK_FRAME &&
+ free_bytes >= GetMinAckFrameSize(PACKET_6BYTE_SEQUENCE_NUMBER,
+ PACKET_6BYTE_SEQUENCE_NUMBER);
+ if (can_truncate) {
+ // Truncate the frame so the packet will not exceed kMaxPacketSize.
+ // Note that we may not use every byte of the writer in this case.
+ DVLOG(1) << "Truncating large frame, free bytes: " << free_bytes;
+ return free_bytes;
+ }
+ if (!FLAGS_quic_allow_oversized_packets_for_test) {
+ return 0;
+ }
+ LOG(DFATAL) << "Packet size too small to fit frame.";
+ return frame_len;
+}
+
+QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) {}
+
+QuicFramer::AckFrameInfo::~AckFrameInfo() {}
+
+QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash(
+ const QuicPacketHeader& header) const {
+ return header.entropy_flag << (header.packet_sequence_number % 8);
+}
+
+SerializedPacket QuicFramer::BuildDataPacket(
+ const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size) {
+ QuicDataWriter writer(packet_size);
+ const SerializedPacket kNoPacket(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0,
+ nullptr);
+ if (!AppendPacketHeader(header, &writer)) {
+ LOG(DFATAL) << "AppendPacketHeader failed";
+ return kNoPacket;
+ }
+
+ for (size_t i = 0; i < frames.size(); ++i) {
+ const QuicFrame& frame = frames[i];
+
+ // Determine if we should write stream frame length in header.
+ const bool no_stream_frame_length =
+ (header.is_in_fec_group == NOT_IN_FEC_GROUP) &&
+ (i == frames.size() - 1);
+ if (!AppendTypeByte(frame, no_stream_frame_length, &writer)) {
+ LOG(DFATAL) << "AppendTypeByte failed";
+ return kNoPacket;
+ }
+
+ switch (frame.type) {
+ case PADDING_FRAME:
+ writer.WritePadding();
+ break;
+ case STREAM_FRAME:
+ if (!AppendStreamFrame(
+ *frame.stream_frame, no_stream_frame_length, &writer)) {
+ LOG(DFATAL) << "AppendStreamFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case ACK_FRAME:
+ if (!AppendAckFrameAndTypeByte(
+ header, *frame.ack_frame, &writer)) {
+ LOG(DFATAL) << "AppendAckFrameAndTypeByte failed";
+ return kNoPacket;
+ }
+ break;
+ case CONGESTION_FEEDBACK_FRAME:
+ if (!AppendCongestionFeedbackFrame(
+ *frame.congestion_feedback_frame, &writer)) {
+ LOG(DFATAL) << "AppendCongestionFeedbackFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case STOP_WAITING_FRAME:
+ if (!AppendStopWaitingFrame(
+ header, *frame.stop_waiting_frame, &writer)) {
+ LOG(DFATAL) << "AppendStopWaitingFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case PING_FRAME:
+ // Ping has no payload.
+ break;
+ case RST_STREAM_FRAME:
+ if (!AppendRstStreamFrame(*frame.rst_stream_frame, &writer)) {
+ LOG(DFATAL) << "AppendRstStreamFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ if (!AppendConnectionCloseFrame(
+ *frame.connection_close_frame, &writer)) {
+ LOG(DFATAL) << "AppendConnectionCloseFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case GOAWAY_FRAME:
+ if (!AppendGoAwayFrame(*frame.goaway_frame, &writer)) {
+ LOG(DFATAL) << "AppendGoAwayFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case WINDOW_UPDATE_FRAME:
+ if (!AppendWindowUpdateFrame(*frame.window_update_frame, &writer)) {
+ LOG(DFATAL) << "AppendWindowUpdateFrame failed";
+ return kNoPacket;
+ }
+ break;
+ case BLOCKED_FRAME:
+ if (!AppendBlockedFrame(*frame.blocked_frame, &writer)) {
+ LOG(DFATAL) << "AppendBlockedFrame failed";
+ return kNoPacket;
+ }
+ break;
+ default:
+ RaiseError(QUIC_INVALID_FRAME_DATA);
+ LOG(DFATAL) << "QUIC_INVALID_FRAME_DATA";
+ return kNoPacket;
+ }
+ }
+
+ // Save the length before writing, because take clears it.
+ const size_t len = writer.length();
+ // Less than or equal because truncated acks end up with max_plaintex_size
+ // length, even though they're typically slightly shorter.
+ DCHECK_LE(len, packet_size);
+ QuicPacket* packet = QuicPacket::NewDataPacket(
+ writer.take(), len, true, header.public_header.connection_id_length,
+ header.public_header.version_flag,
+ header.public_header.sequence_number_length);
+
+ if (fec_builder_) {
+ fec_builder_->OnBuiltFecProtectedPayload(header,
+ packet->FecProtectedData());
+ }
+
+ return SerializedPacket(header.packet_sequence_number,
+ header.public_header.sequence_number_length, packet,
+ GetPacketEntropyHash(header), nullptr);
+}
+
+SerializedPacket QuicFramer::BuildFecPacket(const QuicPacketHeader& header,
+ const QuicFecData& fec) {
+ DCHECK_EQ(IN_FEC_GROUP, header.is_in_fec_group);
+ DCHECK_NE(0u, header.fec_group);
+ size_t len = GetPacketHeaderSize(header);
+ len += fec.redundancy.length();
+
+ QuicDataWriter writer(len);
+ const SerializedPacket kNoPacket(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0,
+ nullptr);
+ if (!AppendPacketHeader(header, &writer)) {
+ LOG(DFATAL) << "AppendPacketHeader failed";
+ return kNoPacket;
+ }
+
+ if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) {
+ LOG(DFATAL) << "Failed to add FEC";
+ return kNoPacket;
+ }
+
+ return SerializedPacket(
+ header.packet_sequence_number,
+ header.public_header.sequence_number_length,
+ QuicPacket::NewFecPacket(writer.take(), len, true,
+ header.public_header.connection_id_length,
+ header.public_header.version_flag,
+ header.public_header.sequence_number_length),
+ GetPacketEntropyHash(header), nullptr);
+}
+
+// static
+QuicEncryptedPacket* QuicFramer::BuildPublicResetPacket(
+ const QuicPublicResetPacket& packet) {
+ DCHECK(packet.public_header.reset_flag);
+
+ CryptoHandshakeMessage reset;
+ reset.set_tag(kPRST);
+ reset.SetValue(kRNON, packet.nonce_proof);
+ reset.SetValue(kRSEQ, packet.rejected_sequence_number);
+ if (!packet.client_address.address().empty()) {
+ // packet.client_address is non-empty.
+ QuicSocketAddressCoder address_coder(packet.client_address);
+ string serialized_address = address_coder.Encode();
+ if (serialized_address.empty()) {
+ return nullptr;
+ }
+ reset.SetStringPiece(kCADR, serialized_address);
+ }
+ const QuicData& reset_serialized = reset.GetSerialized();
+
+ size_t len =
+ kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID + reset_serialized.length();
+ QuicDataWriter writer(len);
+
+ uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST |
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
+ if (!writer.WriteUInt8(flags)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteUInt64(packet.public_header.connection_id)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteBytes(reset_serialized.data(), reset_serialized.length())) {
+ return nullptr;
+ }
+
+ return new QuicEncryptedPacket(writer.take(), len, true);
+}
+
+QuicEncryptedPacket* QuicFramer::BuildVersionNegotiationPacket(
+ const QuicPacketPublicHeader& header,
+ const QuicVersionVector& supported_versions) {
+ DCHECK(header.version_flag);
+ size_t len = GetVersionNegotiationPacketSize(supported_versions.size());
+ QuicDataWriter writer(len);
+
+ uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_VERSION |
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID);
+ if (!writer.WriteUInt8(flags)) {
+ return nullptr;
+ }
+
+ if (!writer.WriteUInt64(header.connection_id)) {
+ return nullptr;
+ }
+
+ for (size_t i = 0; i < supported_versions.size(); ++i) {
+ if (!writer.WriteUInt32(QuicVersionToQuicTag(supported_versions[i]))) {
+ return nullptr;
+ }
+ }
+
+ return new QuicEncryptedPacket(writer.take(), len, true);
+}
+
+bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ DCHECK(!reader_.get());
+ reader_.reset(new QuicDataReader(packet.data(), packet.length()));
+
+ visitor_->OnPacket();
+
+ // First parse the public header.
+ QuicPacketPublicHeader public_header;
+ if (!ProcessPublicHeader(&public_header)) {
+ DLOG(WARNING) << "Unable to process public header.";
+ DCHECK_NE("", detailed_error_);
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (!visitor_->OnUnauthenticatedPublicHeader(public_header)) {
+ // The visitor suppresses further processing of the packet.
+ reader_.reset(nullptr);
+ return true;
+ }
+
+ if (is_server_ && public_header.version_flag &&
+ public_header.versions[0] != quic_version_) {
+ if (!visitor_->OnProtocolVersionMismatch(public_header.versions[0])) {
+ reader_.reset(nullptr);
+ return true;
+ }
+ }
+
+ bool rv;
+ if (!is_server_ && public_header.version_flag) {
+ rv = ProcessVersionNegotiationPacket(&public_header);
+ } else if (public_header.reset_flag) {
+ rv = ProcessPublicResetPacket(public_header);
+ } else {
+ rv = ProcessDataPacket(public_header, packet);
+ }
+
+ reader_.reset(nullptr);
+ return rv;
+}
+
+bool QuicFramer::ProcessVersionNegotiationPacket(
+ QuicPacketPublicHeader* public_header) {
+ DCHECK(!is_server_);
+ // Try reading at least once to raise error if the packet is invalid.
+ do {
+ QuicTag version;
+ if (!reader_->ReadBytes(&version, kQuicVersionSize)) {
+ set_detailed_error("Unable to read supported version in negotiation.");
+ return RaiseError(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+ }
+ public_header->versions.push_back(QuicTagToQuicVersion(version));
+ } while (!reader_->IsDoneReading());
+
+ visitor_->OnVersionNegotiationPacket(*public_header);
+ return true;
+}
+
+bool QuicFramer::ProcessDataPacket(
+ const QuicPacketPublicHeader& public_header,
+ const QuicEncryptedPacket& packet) {
+ QuicPacketHeader header(public_header);
+ if (!ProcessPacketHeader(&header, packet)) {
+ DLOG(WARNING) << "Unable to process data packet header.";
+ return false;
+ }
+
+ if (!visitor_->OnPacketHeader(header)) {
+ // The visitor suppresses further processing of the packet.
+ return true;
+ }
+
+ if (packet.length() > kMaxPacketSize) {
+ DLOG(WARNING) << "Packet too large: " << packet.length();
+ return RaiseError(QUIC_PACKET_TOO_LARGE);
+ }
+
+ // Handle the payload.
+ if (!header.fec_flag) {
+ if (header.is_in_fec_group == IN_FEC_GROUP) {
+ StringPiece payload = reader_->PeekRemainingPayload();
+ visitor_->OnFecProtectedPayload(payload);
+ }
+ if (!ProcessFrameData(header)) {
+ DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error.
+ DLOG(WARNING) << "Unable to process frame data.";
+ return false;
+ }
+ } else {
+ QuicFecData fec_data;
+ fec_data.fec_group = header.fec_group;
+ fec_data.redundancy = reader_->ReadRemainingPayload();
+ visitor_->OnFecData(fec_data);
+ }
+
+ visitor_->OnPacketComplete();
+ return true;
+}
+
+bool QuicFramer::ProcessPublicResetPacket(
+ const QuicPacketPublicHeader& public_header) {
+ QuicPublicResetPacket packet(public_header);
+
+ scoped_ptr<CryptoHandshakeMessage> reset(
+ CryptoFramer::ParseMessage(reader_->ReadRemainingPayload()));
+ if (!reset.get()) {
+ set_detailed_error("Unable to read reset message.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ if (reset->tag() != kPRST) {
+ set_detailed_error("Incorrect message tag.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+
+ if (reset->GetUint64(kRNON, &packet.nonce_proof) != QUIC_NO_ERROR) {
+ set_detailed_error("Unable to read nonce proof.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ // TODO(satyamshekhar): validate nonce to protect against DoS.
+
+ if (reset->GetUint64(kRSEQ, &packet.rejected_sequence_number) !=
+ QUIC_NO_ERROR) {
+ set_detailed_error("Unable to read rejected sequence number.");
+ return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+
+ StringPiece address;
+ if (reset->GetStringPiece(kCADR, &address)) {
+ QuicSocketAddressCoder address_coder;
+ if (address_coder.Decode(address.data(), address.length())) {
+ packet.client_address = IPEndPoint(address_coder.ip(),
+ address_coder.port());
+ }
+ }
+
+ visitor_->OnPublicResetPacket(packet);
+ return true;
+}
+
+bool QuicFramer::ProcessRevivedPacket(QuicPacketHeader* header,
+ StringPiece payload) {
+ DCHECK(!reader_.get());
+
+ visitor_->OnRevivedPacket();
+
+ header->entropy_hash = GetPacketEntropyHash(*header);
+
+ if (!visitor_->OnPacketHeader(*header)) {
+ return true;
+ }
+
+ if (payload.length() > kMaxPacketSize) {
+ set_detailed_error("Revived packet too large.");
+ return RaiseError(QUIC_PACKET_TOO_LARGE);
+ }
+
+ reader_.reset(new QuicDataReader(payload.data(), payload.length()));
+ if (!ProcessFrameData(*header)) {
+ DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error.
+ DLOG(WARNING) << "Unable to process frame data.";
+ return false;
+ }
+
+ visitor_->OnPacketComplete();
+ reader_.reset(nullptr);
+ return true;
+}
+
+bool QuicFramer::AppendPacketHeader(const QuicPacketHeader& header,
+ QuicDataWriter* writer) {
+ DVLOG(1) << "Appending header: " << header;
+ DCHECK(header.fec_group > 0 || header.is_in_fec_group == NOT_IN_FEC_GROUP);
+ uint8 public_flags = 0;
+ if (header.public_header.reset_flag) {
+ public_flags |= PACKET_PUBLIC_FLAGS_RST;
+ }
+ if (header.public_header.version_flag) {
+ public_flags |= PACKET_PUBLIC_FLAGS_VERSION;
+ }
+
+ public_flags |=
+ GetSequenceNumberFlags(header.public_header.sequence_number_length)
+ << kPublicHeaderSequenceNumberShift;
+
+ switch (header.public_header.connection_id_length) {
+ case PACKET_0BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID)) {
+ return false;
+ }
+ break;
+ case PACKET_1BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID)) {
+ return false;
+ }
+ if (!writer->WriteUInt8(
+ header.public_header.connection_id & k1ByteConnectionIdMask)) {
+ return false;
+ }
+ break;
+ case PACKET_4BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID)) {
+ return false;
+ }
+ if (!writer->WriteUInt32(
+ header.public_header.connection_id & k4ByteConnectionIdMask)) {
+ return false;
+ }
+ break;
+ case PACKET_8BYTE_CONNECTION_ID:
+ if (!writer->WriteUInt8(
+ public_flags | PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID)) {
+ return false;
+ }
+ if (!writer->WriteUInt64(header.public_header.connection_id)) {
+ return false;
+ }
+ break;
+ }
+ last_serialized_connection_id_ = header.public_header.connection_id;
+
+ if (header.public_header.version_flag) {
+ DCHECK(!is_server_);
+ writer->WriteUInt32(QuicVersionToQuicTag(quic_version_));
+ }
+
+ if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length,
+ header.packet_sequence_number, writer)) {
+ return false;
+ }
+
+ uint8 private_flags = 0;
+ if (header.entropy_flag) {
+ private_flags |= PACKET_PRIVATE_FLAGS_ENTROPY;
+ }
+ if (header.is_in_fec_group == IN_FEC_GROUP) {
+ private_flags |= PACKET_PRIVATE_FLAGS_FEC_GROUP;
+ }
+ if (header.fec_flag) {
+ private_flags |= PACKET_PRIVATE_FLAGS_FEC;
+ }
+ if (!writer->WriteUInt8(private_flags)) {
+ return false;
+ }
+
+ // The FEC group number is the sequence number of the first fec
+ // protected packet, or 0 if this packet is not protected.
+ if (header.is_in_fec_group == IN_FEC_GROUP) {
+ DCHECK_GE(header.packet_sequence_number, header.fec_group);
+ DCHECK_GT(255u, header.packet_sequence_number - header.fec_group);
+ // Offset from the current packet sequence number to the first fec
+ // protected packet.
+ uint8 first_fec_protected_packet_offset =
+ header.packet_sequence_number - header.fec_group;
+ if (!writer->WriteBytes(&first_fec_protected_packet_offset, 1)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+const QuicTime::Delta QuicFramer::CalculateTimestampFromWire(
+ uint32 time_delta_us) {
+ // The new time_delta might have wrapped to the next epoch, or it
+ // might have reverse wrapped to the previous epoch, or it might
+ // remain in the same epoch. Select the time closest to the previous
+ // time.
+ //
+ // epoch_delta is the delta between epochs. A delta is 4 bytes of
+ // microseconds.
+ const uint64 epoch_delta = GG_UINT64_C(1) << 32;
+ uint64 epoch = last_timestamp_.ToMicroseconds() & ~(epoch_delta - 1);
+ // Wrapping is safe here because a wrapped value will not be ClosestTo below.
+ uint64 prev_epoch = epoch - epoch_delta;
+ uint64 next_epoch = epoch + epoch_delta;
+
+ uint64 time = ClosestTo(last_timestamp_.ToMicroseconds(),
+ epoch + time_delta_us,
+ ClosestTo(last_timestamp_.ToMicroseconds(),
+ prev_epoch + time_delta_us,
+ next_epoch + time_delta_us));
+
+ return QuicTime::Delta::FromMicroseconds(time);
+}
+
+QuicPacketSequenceNumber QuicFramer::CalculatePacketSequenceNumberFromWire(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber packet_sequence_number) const {
+ // The new sequence number might have wrapped to the next epoch, or
+ // it might have reverse wrapped to the previous epoch, or it might
+ // remain in the same epoch. Select the sequence number closest to the
+ // next expected sequence number, the previous sequence number plus 1.
+
+ // epoch_delta is the delta between epochs the sequence number was serialized
+ // with, so the correct value is likely the same epoch as the last sequence
+ // number or an adjacent epoch.
+ const QuicPacketSequenceNumber epoch_delta =
+ GG_UINT64_C(1) << (8 * sequence_number_length);
+ QuicPacketSequenceNumber next_sequence_number = last_sequence_number_ + 1;
+ QuicPacketSequenceNumber epoch = last_sequence_number_ & ~(epoch_delta - 1);
+ QuicPacketSequenceNumber prev_epoch = epoch - epoch_delta;
+ QuicPacketSequenceNumber next_epoch = epoch + epoch_delta;
+
+ return ClosestTo(next_sequence_number,
+ epoch + packet_sequence_number,
+ ClosestTo(next_sequence_number,
+ prev_epoch + packet_sequence_number,
+ next_epoch + packet_sequence_number));
+}
+
+bool QuicFramer::ProcessPublicHeader(
+ QuicPacketPublicHeader* public_header) {
+ uint8 public_flags;
+ if (!reader_->ReadBytes(&public_flags, 1)) {
+ set_detailed_error("Unable to read public flags.");
+ return false;
+ }
+
+ public_header->reset_flag = (public_flags & PACKET_PUBLIC_FLAGS_RST) != 0;
+ public_header->version_flag =
+ (public_flags & PACKET_PUBLIC_FLAGS_VERSION) != 0;
+
+ if (validate_flags_ &&
+ !public_header->version_flag && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
+ set_detailed_error("Illegal public flags value.");
+ return false;
+ }
+
+ if (public_header->reset_flag && public_header->version_flag) {
+ set_detailed_error("Got version flag in reset packet");
+ return false;
+ }
+
+ switch (public_flags & PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID) {
+ case PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID:
+ if (!reader_->ReadUInt64(&public_header->connection_id)) {
+ set_detailed_error("Unable to read ConnectionId.");
+ return false;
+ }
+ public_header->connection_id_length = PACKET_8BYTE_CONNECTION_ID;
+ break;
+ case PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID:
+ // If the connection_id is truncated, expect to read the last serialized
+ // connection_id.
+ if (!reader_->ReadBytes(&public_header->connection_id,
+ PACKET_4BYTE_CONNECTION_ID)) {
+ set_detailed_error("Unable to read ConnectionId.");
+ return false;
+ }
+ if ((public_header->connection_id & k4ByteConnectionIdMask) !=
+ (last_serialized_connection_id_ & k4ByteConnectionIdMask)) {
+ set_detailed_error("Truncated 4 byte ConnectionId does not match "
+ "previous connection_id.");
+ return false;
+ }
+ public_header->connection_id_length = PACKET_4BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
+ break;
+ case PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID:
+ if (!reader_->ReadBytes(&public_header->connection_id,
+ PACKET_1BYTE_CONNECTION_ID)) {
+ set_detailed_error("Unable to read ConnectionId.");
+ return false;
+ }
+ if ((public_header->connection_id & k1ByteConnectionIdMask) !=
+ (last_serialized_connection_id_ & k1ByteConnectionIdMask)) {
+ set_detailed_error("Truncated 1 byte ConnectionId does not match "
+ "previous connection_id.");
+ return false;
+ }
+ public_header->connection_id_length = PACKET_1BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
+ break;
+ case PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID:
+ public_header->connection_id_length = PACKET_0BYTE_CONNECTION_ID;
+ public_header->connection_id = last_serialized_connection_id_;
+ break;
+ }
+
+ public_header->sequence_number_length =
+ ReadSequenceNumberLength(
+ public_flags >> kPublicHeaderSequenceNumberShift);
+
+ // Read the version only if the packet is from the client.
+ // version flag from the server means version negotiation packet.
+ if (public_header->version_flag && is_server_) {
+ QuicTag version_tag;
+ if (!reader_->ReadUInt32(&version_tag)) {
+ set_detailed_error("Unable to read protocol version.");
+ return false;
+ }
+
+ // If the version from the new packet is the same as the version of this
+ // framer, then the public flags should be set to something we understand.
+ // If not, this raises an error.
+ QuicVersion version = QuicTagToQuicVersion(version_tag);
+ if (version == quic_version_ && public_flags > PACKET_PUBLIC_FLAGS_MAX) {
+ set_detailed_error("Illegal public flags value.");
+ return false;
+ }
+ public_header->versions.push_back(version);
+ }
+ return true;
+}
+
+// static
+QuicSequenceNumberLength QuicFramer::GetMinSequenceNumberLength(
+ QuicPacketSequenceNumber sequence_number) {
+ if (sequence_number < 1 << (PACKET_1BYTE_SEQUENCE_NUMBER * 8)) {
+ return PACKET_1BYTE_SEQUENCE_NUMBER;
+ } else if (sequence_number < 1 << (PACKET_2BYTE_SEQUENCE_NUMBER * 8)) {
+ return PACKET_2BYTE_SEQUENCE_NUMBER;
+ } else if (sequence_number <
+ GG_UINT64_C(1) << (PACKET_4BYTE_SEQUENCE_NUMBER * 8)) {
+ return PACKET_4BYTE_SEQUENCE_NUMBER;
+ } else {
+ return PACKET_6BYTE_SEQUENCE_NUMBER;
+ }
+}
+
+// static
+uint8 QuicFramer::GetSequenceNumberFlags(
+ QuicSequenceNumberLength sequence_number_length) {
+ switch (sequence_number_length) {
+ case PACKET_1BYTE_SEQUENCE_NUMBER:
+ return PACKET_FLAGS_1BYTE_SEQUENCE;
+ case PACKET_2BYTE_SEQUENCE_NUMBER:
+ return PACKET_FLAGS_2BYTE_SEQUENCE;
+ case PACKET_4BYTE_SEQUENCE_NUMBER:
+ return PACKET_FLAGS_4BYTE_SEQUENCE;
+ case PACKET_6BYTE_SEQUENCE_NUMBER:
+ return PACKET_FLAGS_6BYTE_SEQUENCE;
+ default:
+ LOG(DFATAL) << "Unreachable case statement.";
+ return PACKET_FLAGS_6BYTE_SEQUENCE;
+ }
+}
+
+// static
+QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo(
+ const QuicAckFrame& frame) {
+ AckFrameInfo ack_info;
+ if (!frame.missing_packets.empty()) {
+ DCHECK_GE(frame.largest_observed, *frame.missing_packets.rbegin());
+ size_t cur_range_length = 0;
+ SequenceNumberSet::const_iterator iter = frame.missing_packets.begin();
+ QuicPacketSequenceNumber last_missing = *iter;
+ ++iter;
+ for (; iter != frame.missing_packets.end(); ++iter) {
+ if (cur_range_length != numeric_limits<uint8>::max() &&
+ *iter == (last_missing + 1)) {
+ ++cur_range_length;
+ } else {
+ ack_info.nack_ranges[last_missing - cur_range_length] =
+ cur_range_length;
+ cur_range_length = 0;
+ }
+ ack_info.max_delta = max(ack_info.max_delta, *iter - last_missing);
+ last_missing = *iter;
+ }
+ // Include the last nack range.
+ ack_info.nack_ranges[last_missing - cur_range_length] = cur_range_length;
+ // Include the range to the largest observed.
+ ack_info.max_delta = max(ack_info.max_delta,
+ frame.largest_observed - last_missing);
+ }
+ return ack_info;
+}
+
+bool QuicFramer::ProcessPacketHeader(
+ QuicPacketHeader* header,
+ const QuicEncryptedPacket& packet) {
+ if (!ProcessPacketSequenceNumber(header->public_header.sequence_number_length,
+ &header->packet_sequence_number)) {
+ set_detailed_error("Unable to read sequence number.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (header->packet_sequence_number == 0u) {
+ set_detailed_error("Packet sequence numbers cannot be 0.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (!visitor_->OnUnauthenticatedHeader(*header)) {
+ return false;
+ }
+
+ if (!DecryptPayload(*header, packet)) {
+ set_detailed_error("Unable to decrypt payload.");
+ return RaiseError(QUIC_DECRYPTION_FAILURE);
+ }
+
+ uint8 private_flags;
+ if (!reader_->ReadBytes(&private_flags, 1)) {
+ set_detailed_error("Unable to read private flags.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ if (private_flags > PACKET_PRIVATE_FLAGS_MAX) {
+ set_detailed_error("Illegal private flags value.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+
+ header->entropy_flag = (private_flags & PACKET_PRIVATE_FLAGS_ENTROPY) != 0;
+ header->fec_flag = (private_flags & PACKET_PRIVATE_FLAGS_FEC) != 0;
+
+ if ((private_flags & PACKET_PRIVATE_FLAGS_FEC_GROUP) != 0) {
+ header->is_in_fec_group = IN_FEC_GROUP;
+ uint8 first_fec_protected_packet_offset;
+ if (!reader_->ReadBytes(&first_fec_protected_packet_offset, 1)) {
+ set_detailed_error("Unable to read first fec protected packet offset.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ if (first_fec_protected_packet_offset >= header->packet_sequence_number) {
+ set_detailed_error("First fec protected packet offset must be less "
+ "than the sequence number.");
+ return RaiseError(QUIC_INVALID_PACKET_HEADER);
+ }
+ header->fec_group =
+ header->packet_sequence_number - first_fec_protected_packet_offset;
+ }
+
+ header->entropy_hash = GetPacketEntropyHash(*header);
+ // Set the last sequence number after we have decrypted the packet
+ // so we are confident is not attacker controlled.
+ last_sequence_number_ = header->packet_sequence_number;
+ return true;
+}
+
+bool QuicFramer::ProcessPacketSequenceNumber(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber* sequence_number) {
+ QuicPacketSequenceNumber wire_sequence_number = 0u;
+ if (!reader_->ReadBytes(&wire_sequence_number, sequence_number_length)) {
+ return false;
+ }
+
+ // TODO(ianswett): Explore the usefulness of trying multiple sequence numbers
+ // in case the first guess is incorrect.
+ *sequence_number =
+ CalculatePacketSequenceNumberFromWire(sequence_number_length,
+ wire_sequence_number);
+ return true;
+}
+
+bool QuicFramer::ProcessFrameData(const QuicPacketHeader& header) {
+ if (reader_->IsDoneReading()) {
+ set_detailed_error("Packet has no frames.");
+ return RaiseError(QUIC_MISSING_PAYLOAD);
+ }
+ while (!reader_->IsDoneReading()) {
+ uint8 frame_type;
+ if (!reader_->ReadBytes(&frame_type, 1)) {
+ set_detailed_error("Unable to read frame type.");
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+
+ if (frame_type & kQuicFrameTypeSpecialMask) {
+ // Stream Frame
+ if (frame_type & kQuicFrameTypeStreamMask) {
+ QuicStreamFrame frame;
+ if (!ProcessStreamFrame(frame_type, &frame)) {
+ return RaiseError(QUIC_INVALID_STREAM_DATA);
+ }
+ if (!visitor_->OnStreamFrame(frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ // Ack Frame
+ if (frame_type & kQuicFrameTypeAckMask) {
+ QuicAckFrame frame;
+ if (!ProcessAckFrame(frame_type, &frame)) {
+ return RaiseError(QUIC_INVALID_ACK_DATA);
+ }
+ if (!visitor_->OnAckFrame(frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ // Congestion Feedback Frame
+ if (frame_type & kQuicFrameTypeCongestionFeedbackMask) {
+ if (quic_version_ > QUIC_VERSION_22) {
+ set_detailed_error("Congestion Feedback Frame has been deprecated.");
+ DLOG(WARNING) << "Congestion Feedback Frame has been deprecated.";
+ }
+ QuicCongestionFeedbackFrame frame;
+ if (!ProcessCongestionFeedbackFrame(&frame)) {
+ return RaiseError(QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
+ }
+ if (!visitor_->OnCongestionFeedbackFrame(frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ // This was a special frame type that did not match any
+ // of the known ones. Error.
+ set_detailed_error("Illegal frame type.");
+ DLOG(WARNING) << "Illegal frame type: "
+ << static_cast<int>(frame_type);
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+
+ switch (frame_type) {
+ case PADDING_FRAME:
+ // We're done with the packet.
+ return true;
+
+ case RST_STREAM_FRAME: {
+ QuicRstStreamFrame frame;
+ if (!ProcessRstStreamFrame(&frame)) {
+ return RaiseError(QUIC_INVALID_RST_STREAM_DATA);
+ }
+ if (!visitor_->OnRstStreamFrame(frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case CONNECTION_CLOSE_FRAME: {
+ QuicConnectionCloseFrame frame;
+ if (!ProcessConnectionCloseFrame(&frame)) {
+ return RaiseError(QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ }
+
+ if (!visitor_->OnConnectionCloseFrame(frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case GOAWAY_FRAME: {
+ QuicGoAwayFrame goaway_frame;
+ if (!ProcessGoAwayFrame(&goaway_frame)) {
+ return RaiseError(QUIC_INVALID_GOAWAY_DATA);
+ }
+ if (!visitor_->OnGoAwayFrame(goaway_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case WINDOW_UPDATE_FRAME: {
+ QuicWindowUpdateFrame window_update_frame;
+ if (!ProcessWindowUpdateFrame(&window_update_frame)) {
+ return RaiseError(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+ if (!visitor_->OnWindowUpdateFrame(window_update_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case BLOCKED_FRAME: {
+ QuicBlockedFrame blocked_frame;
+ if (!ProcessBlockedFrame(&blocked_frame)) {
+ return RaiseError(QUIC_INVALID_BLOCKED_DATA);
+ }
+ if (!visitor_->OnBlockedFrame(blocked_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ case STOP_WAITING_FRAME: {
+ QuicStopWaitingFrame stop_waiting_frame;
+ if (!ProcessStopWaitingFrame(header, &stop_waiting_frame)) {
+ return RaiseError(QUIC_INVALID_STOP_WAITING_DATA);
+ }
+ if (!visitor_->OnStopWaitingFrame(stop_waiting_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+ case PING_FRAME: {
+ // Ping has no payload.
+ QuicPingFrame ping_frame;
+ if (!visitor_->OnPingFrame(ping_frame)) {
+ DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ continue;
+ }
+
+ default:
+ set_detailed_error("Illegal frame type.");
+ DLOG(WARNING) << "Illegal frame type: "
+ << static_cast<int>(frame_type);
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessStreamFrame(uint8 frame_type,
+ QuicStreamFrame* frame) {
+ uint8 stream_flags = frame_type;
+
+ stream_flags &= ~kQuicFrameTypeStreamMask;
+
+ // Read from right to left: StreamID, Offset, Data Length, Fin.
+ const uint8 stream_id_length = (stream_flags & kQuicStreamIDLengthMask) + 1;
+ stream_flags >>= kQuicStreamIdShift;
+
+ uint8 offset_length = (stream_flags & kQuicStreamOffsetMask);
+ // There is no encoding for 1 byte, only 0 and 2 through 8.
+ if (offset_length > 0) {
+ offset_length += 1;
+ }
+ stream_flags >>= kQuicStreamOffsetShift;
+
+ bool has_data_length =
+ (stream_flags & kQuicStreamDataLengthMask) == kQuicStreamDataLengthMask;
+ stream_flags >>= kQuicStreamDataLengthShift;
+
+ frame->fin = (stream_flags & kQuicStreamFinMask) == kQuicStreamFinShift;
+
+ frame->stream_id = 0;
+ if (!reader_->ReadBytes(&frame->stream_id, stream_id_length)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ frame->offset = 0;
+ if (!reader_->ReadBytes(&frame->offset, offset_length)) {
+ set_detailed_error("Unable to read offset.");
+ return false;
+ }
+
+ StringPiece frame_data;
+ if (has_data_length) {
+ if (!reader_->ReadStringPiece16(&frame_data)) {
+ set_detailed_error("Unable to read frame data.");
+ return false;
+ }
+ } else {
+ if (!reader_->ReadStringPiece(&frame_data, reader_->BytesRemaining())) {
+ set_detailed_error("Unable to read frame data.");
+ return false;
+ }
+ }
+ // Point frame to the right data.
+ frame->data.Clear();
+ if (!frame_data.empty()) {
+ frame->data.Append(const_cast<char*>(frame_data.data()), frame_data.size());
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessAckFrame(uint8 frame_type, QuicAckFrame* ack_frame) {
+ // Determine the three lengths from the frame type: largest observed length,
+ // missing sequence number length, and missing range length.
+ const QuicSequenceNumberLength missing_sequence_number_length =
+ ReadSequenceNumberLength(frame_type);
+ frame_type >>= kQuicSequenceNumberLengthShift;
+ const QuicSequenceNumberLength largest_observed_sequence_number_length =
+ ReadSequenceNumberLength(frame_type);
+ frame_type >>= kQuicSequenceNumberLengthShift;
+ ack_frame->is_truncated = frame_type & kQuicAckTruncatedMask;
+ frame_type >>= kQuicAckTruncatedShift;
+ bool has_nacks = frame_type & kQuicHasNacksMask;
+
+ if (!reader_->ReadBytes(&ack_frame->entropy_hash, 1)) {
+ set_detailed_error("Unable to read entropy hash for received packets.");
+ return false;
+ }
+
+ if (!reader_->ReadBytes(&ack_frame->largest_observed,
+ largest_observed_sequence_number_length)) {
+ set_detailed_error("Unable to read largest observed.");
+ return false;
+ }
+
+ uint64 delta_time_largest_observed_us;
+ if (!reader_->ReadUFloat16(&delta_time_largest_observed_us)) {
+ set_detailed_error("Unable to read delta time largest observed.");
+ return false;
+ }
+
+ if (delta_time_largest_observed_us == kUFloat16MaxValue) {
+ ack_frame->delta_time_largest_observed = QuicTime::Delta::Infinite();
+ } else {
+ ack_frame->delta_time_largest_observed =
+ QuicTime::Delta::FromMicroseconds(delta_time_largest_observed_us);
+ }
+
+ if (!ProcessTimestampsInAckFrame(ack_frame)) {
+ return false;
+ }
+
+ if (!has_nacks) {
+ return true;
+ }
+
+ uint8 num_missing_ranges;
+ if (!reader_->ReadBytes(&num_missing_ranges, 1)) {
+ set_detailed_error("Unable to read num missing packet ranges.");
+ return false;
+ }
+
+ QuicPacketSequenceNumber last_sequence_number = ack_frame->largest_observed;
+ for (size_t i = 0; i < num_missing_ranges; ++i) {
+ QuicPacketSequenceNumber missing_delta = 0;
+ if (!reader_->ReadBytes(&missing_delta, missing_sequence_number_length)) {
+ set_detailed_error("Unable to read missing sequence number delta.");
+ return false;
+ }
+ last_sequence_number -= missing_delta;
+ QuicPacketSequenceNumber range_length = 0;
+ if (!reader_->ReadBytes(&range_length, PACKET_1BYTE_SEQUENCE_NUMBER)) {
+ set_detailed_error("Unable to read missing sequence number range.");
+ return false;
+ }
+ for (size_t i = 0; i <= range_length; ++i) {
+ ack_frame->missing_packets.insert(last_sequence_number - i);
+ }
+ // Subtract an extra 1 to ensure ranges are represented efficiently and
+ // can't overlap by 1 sequence number. This allows a missing_delta of 0
+ // to represent an adjacent nack range.
+ last_sequence_number -= (range_length + 1);
+ }
+
+ // Parse the revived packets list.
+ uint8 num_revived_packets;
+ if (!reader_->ReadBytes(&num_revived_packets, 1)) {
+ set_detailed_error("Unable to read num revived packets.");
+ return false;
+ }
+
+ for (size_t i = 0; i < num_revived_packets; ++i) {
+ QuicPacketSequenceNumber revived_packet = 0;
+ if (!reader_->ReadBytes(&revived_packet,
+ largest_observed_sequence_number_length)) {
+ set_detailed_error("Unable to read revived packet.");
+ return false;
+ }
+
+ ack_frame->revived_packets.insert(revived_packet);
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessTimestampsInAckFrame(QuicAckFrame* ack_frame) {
+ if (version() > QUIC_VERSION_22 && !ack_frame->is_truncated) {
+ uint8 num_received_packets;
+ if (!reader_->ReadBytes(&num_received_packets, 1)) {
+ set_detailed_error("Unable to read num received packets.");
+ return false;
+ }
+
+ if (num_received_packets > 0) {
+ uint8 delta_from_largest_observed;
+ if (!reader_->ReadBytes(&delta_from_largest_observed,
+ PACKET_1BYTE_SEQUENCE_NUMBER)) {
+ set_detailed_error(
+ "Unable to read sequence delta in received packets.");
+ return false;
+ }
+ QuicPacketSequenceNumber seq_num = ack_frame->largest_observed -
+ delta_from_largest_observed;
+
+ // Time delta from the framer creation.
+ uint32 time_delta_us;
+ if (!reader_->ReadBytes(&time_delta_us, sizeof(time_delta_us))) {
+ set_detailed_error("Unable to read time delta in received packets.");
+ return false;
+ }
+
+ last_timestamp_ = CalculateTimestampFromWire(time_delta_us);
+
+ ack_frame->received_packet_times.push_back(
+ make_pair(seq_num, creation_time_.Add(last_timestamp_)));
+
+ for (uint8 i = 1; i < num_received_packets; ++i) {
+ if (!reader_->ReadBytes(&delta_from_largest_observed,
+ PACKET_1BYTE_SEQUENCE_NUMBER)) {
+ set_detailed_error(
+ "Unable to read sequence delta in received packets.");
+ return false;
+ }
+ seq_num = ack_frame->largest_observed - delta_from_largest_observed;
+
+ // Time delta from the previous timestamp.
+ uint64 incremental_time_delta_us;
+ if (!reader_->ReadUFloat16(&incremental_time_delta_us)) {
+ set_detailed_error(
+ "Unable to read incremental time delta in received packets.");
+ return false;
+ }
+
+ last_timestamp_ = last_timestamp_.Add(
+ QuicTime::Delta::FromMicroseconds(incremental_time_delta_us));
+ ack_frame->received_packet_times.push_back(
+ make_pair(seq_num, creation_time_.Add(last_timestamp_)));
+ }
+ }
+ }
+ return true;
+}
+
+bool QuicFramer::ProcessStopWaitingFrame(const QuicPacketHeader& header,
+ QuicStopWaitingFrame* stop_waiting) {
+ if (!reader_->ReadBytes(&stop_waiting->entropy_hash, 1)) {
+ set_detailed_error("Unable to read entropy hash for sent packets.");
+ return false;
+ }
+
+ QuicPacketSequenceNumber least_unacked_delta = 0;
+ if (!reader_->ReadBytes(&least_unacked_delta,
+ header.public_header.sequence_number_length)) {
+ set_detailed_error("Unable to read least unacked delta.");
+ return false;
+ }
+ DCHECK_GE(header.packet_sequence_number, least_unacked_delta);
+ stop_waiting->least_unacked =
+ header.packet_sequence_number - least_unacked_delta;
+
+ return true;
+}
+
+bool QuicFramer::ProcessCongestionFeedbackFrame(
+ QuicCongestionFeedbackFrame* frame) {
+ uint8 feedback_type;
+ if (!reader_->ReadBytes(&feedback_type, 1)) {
+ set_detailed_error("Unable to read congestion feedback type.");
+ return false;
+ }
+ frame->type =
+ static_cast<CongestionFeedbackType>(feedback_type);
+
+ switch (frame->type) {
+ case kTCP: {
+ CongestionFeedbackMessageTCP* tcp = &frame->tcp;
+ uint16 receive_window = 0;
+ if (!reader_->ReadUInt16(&receive_window)) {
+ set_detailed_error("Unable to read receive window.");
+ return false;
+ }
+ // Simple bit packing, don't send the 4 least significant bits.
+ tcp->receive_window = static_cast<QuicByteCount>(receive_window) << 4;
+ break;
+ }
+ default:
+ set_detailed_error("Illegal congestion feedback type.");
+ DLOG(WARNING) << "Illegal congestion feedback type: "
+ << frame->type;
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessRstStreamFrame(QuicRstStreamFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ if (!reader_->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read rst stream sent byte offset.");
+ return false;
+ }
+
+ uint32 error_code;
+ if (!reader_->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read rst stream error code.");
+ return false;
+ }
+
+ if (error_code >= QUIC_STREAM_LAST_ERROR ||
+ error_code < QUIC_STREAM_NO_ERROR) {
+ set_detailed_error("Invalid rst stream error code.");
+ return false;
+ }
+
+ frame->error_code = static_cast<QuicRstStreamErrorCode>(error_code);
+
+ StringPiece error_details;
+ if (!reader_->ReadStringPiece16(&error_details)) {
+ set_detailed_error("Unable to read rst stream error details.");
+ return false;
+ }
+ frame->error_details = error_details.as_string();
+
+ return true;
+}
+
+bool QuicFramer::ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame) {
+ uint32 error_code;
+ if (!reader_->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read connection close error code.");
+ return false;
+ }
+
+ if (error_code >= QUIC_LAST_ERROR ||
+ error_code < QUIC_NO_ERROR) {
+ set_detailed_error("Invalid error code.");
+ return false;
+ }
+
+ frame->error_code = static_cast<QuicErrorCode>(error_code);
+
+ StringPiece error_details;
+ if (!reader_->ReadStringPiece16(&error_details)) {
+ set_detailed_error("Unable to read connection close error details.");
+ return false;
+ }
+ frame->error_details = error_details.as_string();
+
+ return true;
+}
+
+bool QuicFramer::ProcessGoAwayFrame(QuicGoAwayFrame* frame) {
+ uint32 error_code;
+ if (!reader_->ReadUInt32(&error_code)) {
+ set_detailed_error("Unable to read go away error code.");
+ return false;
+ }
+ frame->error_code = static_cast<QuicErrorCode>(error_code);
+
+ if (error_code >= QUIC_LAST_ERROR ||
+ error_code < QUIC_NO_ERROR) {
+ set_detailed_error("Invalid error code.");
+ return false;
+ }
+
+ uint32 stream_id;
+ if (!reader_->ReadUInt32(&stream_id)) {
+ set_detailed_error("Unable to read last good stream id.");
+ return false;
+ }
+ frame->last_good_stream_id = static_cast<QuicStreamId>(stream_id);
+
+ StringPiece reason_phrase;
+ if (!reader_->ReadStringPiece16(&reason_phrase)) {
+ set_detailed_error("Unable to read goaway reason.");
+ return false;
+ }
+ frame->reason_phrase = reason_phrase.as_string();
+
+ return true;
+}
+
+bool QuicFramer::ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ if (!reader_->ReadUInt64(&frame->byte_offset)) {
+ set_detailed_error("Unable to read window byte_offset.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::ProcessBlockedFrame(QuicBlockedFrame* frame) {
+ if (!reader_->ReadUInt32(&frame->stream_id)) {
+ set_detailed_error("Unable to read stream_id.");
+ return false;
+ }
+
+ return true;
+}
+
+// static
+StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket(
+ const QuicEncryptedPacket& encrypted,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ return StringPiece(
+ encrypted.data() + kStartOfHashData, GetStartOfEncryptedData(
+ connection_id_length, includes_version, sequence_number_length)
+ - kStartOfHashData);
+}
+
+void QuicFramer::SetDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level) {
+ DCHECK(alternative_decrypter_.get() == nullptr);
+ DCHECK_GE(level, decrypter_level_);
+ decrypter_.reset(decrypter);
+ decrypter_level_ = level;
+}
+
+void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
+ bool latch_once_used) {
+ alternative_decrypter_.reset(decrypter);
+ alternative_decrypter_level_ = level;
+ alternative_decrypter_latch_ = latch_once_used;
+}
+
+const QuicDecrypter* QuicFramer::decrypter() const {
+ return decrypter_.get();
+}
+
+const QuicDecrypter* QuicFramer::alternative_decrypter() const {
+ return alternative_decrypter_.get();
+}
+
+void QuicFramer::SetEncrypter(EncryptionLevel level,
+ QuicEncrypter* encrypter) {
+ DCHECK_GE(level, 0);
+ DCHECK_LT(level, NUM_ENCRYPTION_LEVELS);
+ encrypter_[level].reset(encrypter);
+}
+
+const QuicEncrypter* QuicFramer::encrypter(EncryptionLevel level) const {
+ DCHECK_GE(level, 0);
+ DCHECK_LT(level, NUM_ENCRYPTION_LEVELS);
+ DCHECK(encrypter_[level].get() != nullptr);
+ return encrypter_[level].get();
+}
+
+QuicEncryptedPacket* QuicFramer::EncryptPacket(
+ EncryptionLevel level,
+ QuicPacketSequenceNumber packet_sequence_number,
+ const QuicPacket& packet) {
+ DCHECK(encrypter_[level].get() != nullptr);
+
+ scoped_ptr<QuicData> out(encrypter_[level]->EncryptPacket(
+ packet_sequence_number, packet.AssociatedData(), packet.Plaintext()));
+ if (out.get() == nullptr) {
+ RaiseError(QUIC_ENCRYPTION_FAILURE);
+ return nullptr;
+ }
+ StringPiece header_data = packet.BeforePlaintext();
+ size_t len = header_data.length() + out->length();
+ char* buffer = new char[len];
+ // TODO(rch): eliminate this buffer copy by passing in a buffer to Encrypt().
+ memcpy(buffer, header_data.data(), header_data.length());
+ memcpy(buffer + header_data.length(), out->data(), out->length());
+ return new QuicEncryptedPacket(buffer, len, true);
+}
+
+size_t QuicFramer::GetMaxPlaintextSize(size_t ciphertext_size) {
+ // In order to keep the code simple, we don't have the current encryption
+ // level to hand. Both the NullEncrypter and AES-GCM have a tag length of 12.
+ size_t min_plaintext_size = ciphertext_size;
+
+ for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
+ if (encrypter_[i].get() != nullptr) {
+ size_t size = encrypter_[i]->GetMaxPlaintextSize(ciphertext_size);
+ if (size < min_plaintext_size) {
+ min_plaintext_size = size;
+ }
+ }
+ }
+
+ return min_plaintext_size;
+}
+
+bool QuicFramer::DecryptPayload(const QuicPacketHeader& header,
+ const QuicEncryptedPacket& packet) {
+ StringPiece encrypted;
+ if (!reader_->ReadStringPiece(&encrypted, reader_->BytesRemaining())) {
+ return false;
+ }
+ DCHECK(decrypter_.get() != nullptr);
+ decrypted_.reset(decrypter_->DecryptPacket(
+ header.packet_sequence_number,
+ GetAssociatedDataFromEncryptedPacket(
+ packet,
+ header.public_header.connection_id_length,
+ header.public_header.version_flag,
+ header.public_header.sequence_number_length),
+ encrypted));
+ if (decrypted_.get() != nullptr) {
+ visitor_->OnDecryptedPacket(decrypter_level_);
+ } else if (alternative_decrypter_.get() != nullptr) {
+ decrypted_.reset(alternative_decrypter_->DecryptPacket(
+ header.packet_sequence_number,
+ GetAssociatedDataFromEncryptedPacket(
+ packet,
+ header.public_header.connection_id_length,
+ header.public_header.version_flag,
+ header.public_header.sequence_number_length),
+ encrypted));
+ if (decrypted_.get() != nullptr) {
+ visitor_->OnDecryptedPacket(alternative_decrypter_level_);
+ if (alternative_decrypter_latch_) {
+ // Switch to the alternative decrypter and latch so that we cannot
+ // switch back.
+ decrypter_.reset(alternative_decrypter_.release());
+ decrypter_level_ = alternative_decrypter_level_;
+ alternative_decrypter_level_ = ENCRYPTION_NONE;
+ } else {
+ // Switch the alternative decrypter so that we use it first next time.
+ decrypter_.swap(alternative_decrypter_);
+ EncryptionLevel level = alternative_decrypter_level_;
+ alternative_decrypter_level_ = decrypter_level_;
+ decrypter_level_ = level;
+ }
+ }
+ }
+
+ if (decrypted_.get() == nullptr) {
+ DLOG(WARNING) << "DecryptPacket failed for sequence_number:"
+ << header.packet_sequence_number;
+ return false;
+ }
+
+ reader_.reset(new QuicDataReader(decrypted_->data(), decrypted_->length()));
+ return true;
+}
+
+size_t QuicFramer::GetAckFrameSize(
+ const QuicAckFrame& ack,
+ QuicSequenceNumberLength sequence_number_length) {
+ AckFrameInfo ack_info = GetAckFrameInfo(ack);
+ QuicSequenceNumberLength largest_observed_length =
+ GetMinSequenceNumberLength(ack.largest_observed);
+ QuicSequenceNumberLength missing_sequence_number_length =
+ GetMinSequenceNumberLength(ack_info.max_delta);
+
+ size_t ack_size = GetMinAckFrameSize(sequence_number_length,
+ largest_observed_length);
+ if (!ack_info.nack_ranges.empty()) {
+ ack_size += kNumberOfNackRangesSize + kNumberOfRevivedPacketsSize;
+ ack_size += min(ack_info.nack_ranges.size(), kMaxNackRanges) *
+ (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
+ ack_size += min(ack.revived_packets.size(),
+ kMaxRevivedPackets) * largest_observed_length;
+ }
+
+ // In version 23, if the ack will be truncated due to too many nack ranges,
+ // then do not include the number of timestamps (1 byte).
+ if (version() > QUIC_VERSION_22 &&
+ ack_info.nack_ranges.size() <= kMaxNackRanges) {
+ // 1 byte for the number of timestamps.
+ ack_size += 1;
+ if (ack.received_packet_times.size() > 0) {
+ // 1 byte for sequence number, 4 bytes for timestamp for the first
+ // packet.
+ ack_size += 5;
+
+ // 1 byte for sequence number, 2 bytes for timestamp for the other
+ // packets.
+ ack_size += 3 * (ack.received_packet_times.size() - 1);
+ }
+ }
+
+ return ack_size;
+}
+
+size_t QuicFramer::ComputeFrameLength(
+ const QuicFrame& frame,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
+ QuicSequenceNumberLength sequence_number_length) {
+ switch (frame.type) {
+ case STREAM_FRAME:
+ return GetMinStreamFrameSize(frame.stream_frame->stream_id,
+ frame.stream_frame->offset,
+ last_frame_in_packet,
+ is_in_fec_group) +
+ frame.stream_frame->data.TotalBufferSize();
+ case ACK_FRAME: {
+ return GetAckFrameSize(*frame.ack_frame, sequence_number_length);
+ }
+ case CONGESTION_FEEDBACK_FRAME: {
+ size_t len = kQuicFrameTypeSize;
+ const QuicCongestionFeedbackFrame& congestion_feedback =
+ *frame.congestion_feedback_frame;
+ len += 1; // Congestion feedback type.
+
+ switch (congestion_feedback.type) {
+ case kTCP:
+ len += 2; // Receive window.
+ break;
+ default:
+ set_detailed_error("Illegal feedback type.");
+ DVLOG(1) << "Illegal feedback type: " << congestion_feedback.type;
+ break;
+ }
+ return len;
+ }
+ case STOP_WAITING_FRAME:
+ return GetStopWaitingFrameSize(sequence_number_length);
+ case PING_FRAME:
+ // Ping has no payload.
+ return kQuicFrameTypeSize;
+ case RST_STREAM_FRAME:
+ return GetMinRstStreamFrameSize() +
+ frame.rst_stream_frame->error_details.size();
+ case CONNECTION_CLOSE_FRAME:
+ return GetMinConnectionCloseFrameSize() +
+ frame.connection_close_frame->error_details.size();
+ case GOAWAY_FRAME:
+ return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size();
+ case WINDOW_UPDATE_FRAME:
+ return GetWindowUpdateFrameSize();
+ case BLOCKED_FRAME:
+ return GetBlockedFrameSize();
+ case PADDING_FRAME:
+ DCHECK(false);
+ return 0;
+ case NUM_FRAME_TYPES:
+ DCHECK(false);
+ return 0;
+ }
+
+ // Not reachable, but some Chrome compilers can't figure that out. *sigh*
+ DCHECK(false);
+ return 0;
+}
+
+bool QuicFramer::AppendTypeByte(const QuicFrame& frame,
+ bool no_stream_frame_length,
+ QuicDataWriter* writer) {
+ uint8 type_byte = 0;
+ switch (frame.type) {
+ case STREAM_FRAME: {
+ if (frame.stream_frame == nullptr) {
+ LOG(DFATAL) << "Failed to append STREAM frame with no stream_frame.";
+ }
+ // Fin bit.
+ type_byte |= frame.stream_frame->fin ? kQuicStreamFinMask : 0;
+
+ // Data Length bit.
+ type_byte <<= kQuicStreamDataLengthShift;
+ type_byte |= no_stream_frame_length ? 0: kQuicStreamDataLengthMask;
+
+ // Offset 3 bits.
+ type_byte <<= kQuicStreamOffsetShift;
+ const size_t offset_len = GetStreamOffsetSize(frame.stream_frame->offset);
+ if (offset_len > 0) {
+ type_byte |= offset_len - 1;
+ }
+
+ // stream id 2 bits.
+ type_byte <<= kQuicStreamIdShift;
+ type_byte |= GetStreamIdSize(frame.stream_frame->stream_id) - 1;
+ type_byte |= kQuicFrameTypeStreamMask; // Set Stream Frame Type to 1.
+ break;
+ }
+ case ACK_FRAME:
+ return true;
+ case CONGESTION_FEEDBACK_FRAME: {
+ // TODO(ianswett): Use extra 5 bits in the congestion feedback framing.
+ type_byte = kQuicFrameTypeCongestionFeedbackMask;
+ break;
+ }
+ default:
+ type_byte = frame.type;
+ break;
+ }
+
+ return writer->WriteUInt8(type_byte);
+}
+
+// static
+bool QuicFramer::AppendPacketSequenceNumber(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber packet_sequence_number,
+ QuicDataWriter* writer) {
+ // Ensure the entire sequence number can be written.
+ if (writer->capacity() - writer->length() <
+ static_cast<size_t>(sequence_number_length)) {
+ return false;
+ }
+ switch (sequence_number_length) {
+ case PACKET_1BYTE_SEQUENCE_NUMBER:
+ return writer->WriteUInt8(
+ packet_sequence_number & k1ByteSequenceNumberMask);
+ break;
+ case PACKET_2BYTE_SEQUENCE_NUMBER:
+ return writer->WriteUInt16(
+ packet_sequence_number & k2ByteSequenceNumberMask);
+ break;
+ case PACKET_4BYTE_SEQUENCE_NUMBER:
+ return writer->WriteUInt32(
+ packet_sequence_number & k4ByteSequenceNumberMask);
+ break;
+ case PACKET_6BYTE_SEQUENCE_NUMBER:
+ return writer->WriteUInt48(
+ packet_sequence_number & k6ByteSequenceNumberMask);
+ break;
+ default:
+ DCHECK(false) << "sequence_number_length: " << sequence_number_length;
+ return false;
+ }
+}
+
+bool QuicFramer::AppendStreamFrame(
+ const QuicStreamFrame& frame,
+ bool no_stream_frame_length,
+ QuicDataWriter* writer) {
+ if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) {
+ LOG(DFATAL) << "Writing stream id size failed.";
+ return false;
+ }
+ if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) {
+ LOG(DFATAL) << "Writing offset size failed.";
+ return false;
+ }
+ if (!no_stream_frame_length) {
+ if (!writer->WriteUInt16(frame.data.TotalBufferSize())) {
+ LOG(DFATAL) << "Writing stream frame length failed";
+ return false;
+ }
+ }
+
+ if (!writer->WriteIOVector(frame.data)) {
+ LOG(DFATAL) << "Writing frame data failed.";
+ return false;
+ }
+ return true;
+}
+
+// static
+void QuicFramer::set_version(const QuicVersion version) {
+ DCHECK(IsSupportedVersion(version)) << QuicVersionToString(version);
+ quic_version_ = version;
+}
+
+bool QuicFramer::AppendAckFrameAndTypeByte(
+ const QuicPacketHeader& header,
+ const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ AckFrameInfo ack_info = GetAckFrameInfo(frame);
+ QuicPacketSequenceNumber ack_largest_observed = frame.largest_observed;
+ QuicSequenceNumberLength largest_observed_length =
+ GetMinSequenceNumberLength(ack_largest_observed);
+ QuicSequenceNumberLength missing_sequence_number_length =
+ GetMinSequenceNumberLength(ack_info.max_delta);
+ // Determine whether we need to truncate ranges.
+ size_t available_range_bytes = writer->capacity() - writer->length() -
+ kNumberOfRevivedPacketsSize - kNumberOfNackRangesSize -
+ GetMinAckFrameSize(header.public_header.sequence_number_length,
+ largest_observed_length);
+ size_t max_num_ranges = available_range_bytes /
+ (missing_sequence_number_length + PACKET_1BYTE_SEQUENCE_NUMBER);
+ max_num_ranges = min(kMaxNackRanges, max_num_ranges);
+ bool truncated = ack_info.nack_ranges.size() > max_num_ranges;
+ DVLOG_IF(1, truncated) << "Truncating ack from "
+ << ack_info.nack_ranges.size() << " ranges to "
+ << max_num_ranges;
+ // Write out the type byte by setting the low order bits and doing shifts
+ // to make room for the next bit flags to be set.
+ // Whether there are any nacks.
+ uint8 type_byte = ack_info.nack_ranges.empty() ? 0 : kQuicHasNacksMask;
+
+ // truncating bit.
+ type_byte <<= kQuicAckTruncatedShift;
+ type_byte |= truncated ? kQuicAckTruncatedMask : 0;
+
+ // Largest observed sequence number length.
+ type_byte <<= kQuicSequenceNumberLengthShift;
+ type_byte |= GetSequenceNumberFlags(largest_observed_length);
+
+ // Missing sequence number length.
+ type_byte <<= kQuicSequenceNumberLengthShift;
+ type_byte |= GetSequenceNumberFlags(missing_sequence_number_length);
+
+ type_byte |= kQuicFrameTypeAckMask;
+
+ if (!writer->WriteUInt8(type_byte)) {
+ return false;
+ }
+
+ QuicPacketEntropyHash ack_entropy_hash = frame.entropy_hash;
+ NackRangeMap::reverse_iterator ack_iter = ack_info.nack_ranges.rbegin();
+ if (truncated) {
+ // Skip the nack ranges which the truncated ack won't include and set
+ // a correct largest observed for the truncated ack.
+ for (size_t i = 1; i < (ack_info.nack_ranges.size() - max_num_ranges);
+ ++i) {
+ ++ack_iter;
+ }
+ // If the last range is followed by acks, include them.
+ // If the last range is followed by another range, specify the end of the
+ // range as the largest_observed.
+ ack_largest_observed = ack_iter->first - 1;
+ // Also update the entropy so it matches the largest observed.
+ ack_entropy_hash = entropy_calculator_->EntropyHash(ack_largest_observed);
+ ++ack_iter;
+ }
+
+ if (!writer->WriteUInt8(ack_entropy_hash)) {
+ return false;
+ }
+
+ if (!AppendPacketSequenceNumber(largest_observed_length,
+ ack_largest_observed, writer)) {
+ return false;
+ }
+
+ uint64 delta_time_largest_observed_us = kUFloat16MaxValue;
+ if (!frame.delta_time_largest_observed.IsInfinite()) {
+ DCHECK_LE(0u, frame.delta_time_largest_observed.ToMicroseconds());
+ delta_time_largest_observed_us =
+ frame.delta_time_largest_observed.ToMicroseconds();
+ }
+
+ if (!writer->WriteUFloat16(delta_time_largest_observed_us)) {
+ return false;
+ }
+
+ // Timestamp goes at the end of the required fields.
+ if (version() > QUIC_VERSION_22 && !truncated) {
+ if (!AppendTimestampToAckFrame(frame, writer)) {
+ return false;
+ }
+ }
+
+ if (ack_info.nack_ranges.empty()) {
+ return true;
+ }
+
+ const uint8 num_missing_ranges =
+ min(ack_info.nack_ranges.size(), max_num_ranges);
+ if (!writer->WriteBytes(&num_missing_ranges, 1)) {
+ return false;
+ }
+
+ int num_ranges_written = 0;
+ QuicPacketSequenceNumber last_sequence_written = ack_largest_observed;
+ for (; ack_iter != ack_info.nack_ranges.rend(); ++ack_iter) {
+ // Calculate the delta to the last number in the range.
+ QuicPacketSequenceNumber missing_delta =
+ last_sequence_written - (ack_iter->first + ack_iter->second);
+ if (!AppendPacketSequenceNumber(missing_sequence_number_length,
+ missing_delta, writer)) {
+ return false;
+ }
+ if (!AppendPacketSequenceNumber(PACKET_1BYTE_SEQUENCE_NUMBER,
+ ack_iter->second, writer)) {
+ return false;
+ }
+ // Subtract 1 so a missing_delta of 0 means an adjacent range.
+ last_sequence_written = ack_iter->first - 1;
+ ++num_ranges_written;
+ }
+ DCHECK_EQ(num_missing_ranges, num_ranges_written);
+
+ // Append revived packets.
+ // If not all the revived packets fit, only mention the ones that do.
+ uint8 num_revived_packets = min(frame.revived_packets.size(),
+ kMaxRevivedPackets);
+ num_revived_packets = min(
+ static_cast<size_t>(num_revived_packets),
+ (writer->capacity() - writer->length()) / largest_observed_length);
+ if (!writer->WriteBytes(&num_revived_packets, 1)) {
+ return false;
+ }
+
+ SequenceNumberSet::const_iterator iter = frame.revived_packets.begin();
+ for (int i = 0; i < num_revived_packets; ++i, ++iter) {
+ LOG_IF(DFATAL, !ContainsKey(frame.missing_packets, *iter));
+ if (!AppendPacketSequenceNumber(largest_observed_length,
+ *iter, writer)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteBytes(&frame.type, 1)) {
+ return false;
+ }
+
+ switch (frame.type) {
+ case kTCP: {
+ const CongestionFeedbackMessageTCP& tcp = frame.tcp;
+ DCHECK_LE(tcp.receive_window, 1u << 20);
+ // Simple bit packing, don't send the 4 least significant bits.
+ uint16 receive_window = static_cast<uint16>(tcp.receive_window >> 4);
+ if (!writer->WriteUInt16(receive_window)) {
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendTimestampToAckFrame(const QuicAckFrame& frame,
+ QuicDataWriter* writer) {
+ DCHECK_GE(version(), QUIC_VERSION_23);
+ DCHECK_GE(numeric_limits<uint8>::max(), frame.received_packet_times.size());
+ // num_received_packets is only 1 byte.
+ if (frame.received_packet_times.size() > numeric_limits<uint8>::max()) {
+ return false;
+ }
+
+ uint8 num_received_packets = frame.received_packet_times.size();
+
+ if (!writer->WriteBytes(&num_received_packets, 1)) {
+ return false;
+ }
+ if (num_received_packets == 0) {
+ return true;
+ }
+
+ PacketTimeList::const_iterator it = frame.received_packet_times.begin();
+ QuicPacketSequenceNumber sequence_number = it->first;
+ QuicPacketSequenceNumber delta_from_largest_observed =
+ frame.largest_observed - sequence_number;
+
+ DCHECK_GE(numeric_limits<uint8>::max(), delta_from_largest_observed);
+ if (delta_from_largest_observed > numeric_limits<uint8>::max()) {
+ return false;
+ }
+
+ if (!writer->WriteUInt8(
+ delta_from_largest_observed & k1ByteSequenceNumberMask)) {
+ return false;
+ }
+
+ // Use the lowest 4 bytes of the time delta from the creation_time_.
+ const uint64 time_epoch_delta_us = GG_UINT64_C(1) << 32;
+ uint32 time_delta_us =
+ static_cast<uint32>(it->second.Subtract(creation_time_).ToMicroseconds()
+ & (time_epoch_delta_us - 1));
+ if (!writer->WriteBytes(&time_delta_us, sizeof(time_delta_us))) {
+ return false;
+ }
+
+ QuicTime prev_time = it->second;
+
+ for (++it; it != frame.received_packet_times.end(); ++it) {
+ sequence_number = it->first;
+ delta_from_largest_observed = frame.largest_observed - sequence_number;
+
+ if (delta_from_largest_observed > numeric_limits<uint8>::max()) {
+ return false;
+ }
+
+ if (!writer->WriteUInt8(
+ delta_from_largest_observed & k1ByteSequenceNumberMask)) {
+ return false;
+ }
+
+ uint64 time_delta_us = it->second.Subtract(prev_time).ToMicroseconds();
+ prev_time = it->second;
+ if (!writer->WriteUFloat16(time_delta_us)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QuicFramer::AppendStopWaitingFrame(
+ const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* writer) {
+ DCHECK_GE(header.packet_sequence_number, frame.least_unacked);
+ const QuicPacketSequenceNumber least_unacked_delta =
+ header.packet_sequence_number - frame.least_unacked;
+ const QuicPacketSequenceNumber length_shift =
+ header.public_header.sequence_number_length * 8;
+ if (!writer->WriteUInt8(frame.entropy_hash)) {
+ LOG(DFATAL) << " hash failed";
+ return false;
+ }
+
+ if (least_unacked_delta >> length_shift > 0) {
+ LOG(DFATAL) << "sequence_number_length "
+ << header.public_header.sequence_number_length
+ << " is too small for least_unacked_delta: "
+ << least_unacked_delta;
+ return false;
+ }
+ if (!AppendPacketSequenceNumber(header.public_header.sequence_number_length,
+ least_unacked_delta, writer)) {
+ LOG(DFATAL) << " seq failed: "
+ << header.public_header.sequence_number_length;
+ return false;
+ }
+
+ return true;
+}
+
+bool QuicFramer::AppendRstStreamFrame(
+ const QuicRstStreamFrame& frame,
+ QuicDataWriter* writer) {
+ if (!writer->WriteUInt32(frame.stream_id)) {
+ return false;
+ }
+
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+
+ uint32 error_code = static_cast<uint32>(frame.error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+
+ if (!writer->WriteStringPiece16(frame.error_details)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 error_code = static_cast<uint32>(frame.error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+ if (!writer->WriteStringPiece16(frame.error_details)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendGoAwayFrame(const QuicGoAwayFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 error_code = static_cast<uint32>(frame.error_code);
+ if (!writer->WriteUInt32(error_code)) {
+ return false;
+ }
+ uint32 stream_id = static_cast<uint32>(frame.last_good_stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteStringPiece16(frame.reason_phrase)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 stream_id = static_cast<uint32>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ if (!writer->WriteUInt64(frame.byte_offset)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer) {
+ uint32 stream_id = static_cast<uint32>(frame.stream_id);
+ if (!writer->WriteUInt32(stream_id)) {
+ return false;
+ }
+ return true;
+}
+
+bool QuicFramer::RaiseError(QuicErrorCode error) {
+ DVLOG(1) << "Error detail: " << detailed_error_;
+ set_error(error);
+ visitor_->OnError(this);
+ reader_.reset(nullptr);
+ return false;
+}
+
+} // namespace net
diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h
new file mode 100644
index 0000000..4cc51f1
--- /dev/null
+++ b/net/quic/quic_framer.h
@@ -0,0 +1,548 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_FRAMER_H_
+#define NET_QUIC_QUIC_FRAMER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace test {
+class QuicFramerPeer;
+} // namespace test
+
+class QuicDataReader;
+class QuicDataWriter;
+class QuicDecrypter;
+class QuicEncrypter;
+class QuicFramer;
+
+// Number of bytes reserved for the frame type preceding each frame.
+const size_t kQuicFrameTypeSize = 1;
+// Number of bytes reserved for error code.
+const size_t kQuicErrorCodeSize = 4;
+// Number of bytes reserved to denote the length of error details field.
+const size_t kQuicErrorDetailsLengthSize = 2;
+
+// Maximum number of bytes reserved for stream id.
+const size_t kQuicMaxStreamIdSize = 4;
+// Maximum number of bytes reserved for byte offset in stream frame.
+const size_t kQuicMaxStreamOffsetSize = 8;
+// Number of bytes reserved to store payload length in stream frame.
+const size_t kQuicStreamPayloadLengthSize = 2;
+
+// Size in bytes of the entropy hash sent in ack frames.
+const size_t kQuicEntropyHashSize = 1;
+// Size in bytes reserved for the delta time of the largest observed
+// sequence number in ack frames.
+const size_t kQuicDeltaTimeLargestObservedSize = 2;
+// Size in bytes reserved for the number of received packets with timestamps.
+const size_t kQuicNumTimestampsSize = 1;
+// Size in bytes reserved for the number of missing packets in ack frames.
+const size_t kNumberOfNackRangesSize = 1;
+// Maximum number of missing packet ranges that can fit within an ack frame.
+const size_t kMaxNackRanges =
+ (1 << (kNumberOfNackRangesSize * 8)) - 1;
+// Size in bytes reserved for the number of revived packets in ack frames.
+const size_t kNumberOfRevivedPacketsSize = 1;
+// Maximum number of revived packets that can fit within an ack frame.
+const size_t kMaxRevivedPackets =
+ (1 << (kNumberOfRevivedPacketsSize * 8)) - 1;
+
+// This class receives callbacks from the framer when packets
+// are processed.
+class NET_EXPORT_PRIVATE QuicFramerVisitorInterface {
+ public:
+ virtual ~QuicFramerVisitorInterface() {}
+
+ // Called if an error is detected in the QUIC protocol.
+ virtual void OnError(QuicFramer* framer) = 0;
+
+ // Called only when |is_server_| is true and the the framer gets a packet with
+ // version flag true and the version on the packet doesn't match
+ // |quic_version_|. The visitor should return true after it updates the
+ // version of the |framer_| to |received_version| or false to stop processing
+ // this packet.
+ virtual bool OnProtocolVersionMismatch(QuicVersion received_version) = 0;
+
+ // Called when a new packet has been received, before it
+ // has been validated or processed.
+ virtual void OnPacket() = 0;
+
+ // Called when a public reset packet has been parsed but has not yet
+ // been validated.
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) = 0;
+
+ // Called only when |is_server_| is false and a version negotiation packet has
+ // been parsed.
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) = 0;
+
+ // Called when a lost packet has been recovered via FEC,
+ // before it has been processed.
+ virtual void OnRevivedPacket() = 0;
+
+ // Called when the public header has been parsed, but has not been
+ // authenticated. If it returns false, framing for this packet will cease.
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) = 0;
+
+ // Called when the unauthenticated portion of the header has been parsed.
+ // If OnUnauthenticatedHeader returns false, framing for this packet will
+ // cease.
+ virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) = 0;
+
+ // Called when a packet has been decrypted. |level| is the encryption level
+ // of the packet.
+ virtual void OnDecryptedPacket(EncryptionLevel level) = 0;
+
+ // Called when the complete header of a packet had been parsed.
+ // If OnPacketHeader returns false, framing for this packet will cease.
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) = 0;
+
+ // Called when a data packet is parsed that is part of an FEC group.
+ // |payload| is the non-encrypted FEC protected payload of the packet.
+ virtual void OnFecProtectedPayload(base::StringPiece payload) = 0;
+
+ // Called when a StreamFrame has been parsed.
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0;
+
+ // Called when a AckFrame has been parsed. If OnAckFrame returns false,
+ // the framer will stop parsing the current packet.
+ virtual bool OnAckFrame(const QuicAckFrame& frame) = 0;
+
+ // Called when a CongestionFeedbackFrame has been parsed.
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) = 0;
+
+ // Called when a StopWaitingFrame has been parsed.
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0;
+
+ // Called when a PingFrame has been parsed.
+ virtual bool OnPingFrame(const QuicPingFrame& frame) = 0;
+
+ // Called when a RstStreamFrame has been parsed.
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0;
+
+ // Called when a ConnectionCloseFrame has been parsed.
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) = 0;
+
+ // Called when a GoAwayFrame has been parsed.
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) = 0;
+
+ // Called when a WindowUpdateFrame has been parsed.
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) = 0;
+
+ // Called when a BlockedFrame has been parsed.
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) = 0;
+
+ // Called when FEC data has been parsed.
+ virtual void OnFecData(const QuicFecData& fec) = 0;
+
+ // Called when a packet has been completely processed.
+ virtual void OnPacketComplete() = 0;
+};
+
+class NET_EXPORT_PRIVATE QuicFecBuilderInterface {
+ public:
+ virtual ~QuicFecBuilderInterface() {}
+
+ // Called when a data packet is constructed that is part of an FEC group.
+ // |payload| is the non-encrypted FEC protected payload of the packet.
+ virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header,
+ base::StringPiece payload) = 0;
+};
+
+// This class calculates the received entropy of the ack packet being
+// framed, should it get truncated.
+class NET_EXPORT_PRIVATE QuicReceivedEntropyHashCalculatorInterface {
+ public:
+ virtual ~QuicReceivedEntropyHashCalculatorInterface() {}
+
+ // When an ack frame gets truncated while being framed the received
+ // entropy of the ack frame needs to be calculated since the some of the
+ // missing packets are not added and the largest observed might be lowered.
+ // This should return the received entropy hash of the packets received up to
+ // and including |sequence_number|.
+ virtual QuicPacketEntropyHash EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const = 0;
+};
+
+// Class for parsing and constructing QUIC packets. It has a
+// QuicFramerVisitorInterface that is called when packets are parsed.
+// It also has a QuicFecBuilder that is called when packets are constructed
+// in order to generate FEC data for subsequently building FEC packets.
+class NET_EXPORT_PRIVATE QuicFramer {
+ public:
+ // Constructs a new framer that installs a kNULL QuicEncrypter and
+ // QuicDecrypter for level ENCRYPTION_NONE. |supported_versions| specifies the
+ // list of supported QUIC versions. |quic_version_| is set to the maximum
+ // version in |supported_versions|.
+ QuicFramer(const QuicVersionVector& supported_versions,
+ QuicTime creation_time,
+ bool is_server);
+
+ virtual ~QuicFramer();
+
+ // Returns true if |version| is a supported protocol version.
+ bool IsSupportedVersion(const QuicVersion version) const;
+
+ // Set callbacks to be called from the framer. A visitor must be set, or
+ // else the framer will likely crash. It is acceptable for the visitor
+ // to do nothing. If this is called multiple times, only the last visitor
+ // will be used.
+ void set_visitor(QuicFramerVisitorInterface* visitor) {
+ visitor_ = visitor;
+ }
+
+ // Set a builder to be called from the framer when building FEC protected
+ // packets. If this is called multiple times, only the last builder
+ // will be used. The builder need not be set.
+ void set_fec_builder(QuicFecBuilderInterface* builder) {
+ fec_builder_ = builder;
+ }
+
+ const QuicVersionVector& supported_versions() const {
+ return supported_versions_;
+ }
+
+ QuicVersion version() const {
+ return quic_version_;
+ }
+
+ void set_version(const QuicVersion version);
+
+ // Does not DCHECK for supported version. Used by tests to set unsupported
+ // version to trigger version negotiation.
+ void set_version_for_tests(const QuicVersion version) {
+ quic_version_ = version;
+ }
+
+ // Set entropy calculator to be called from the framer when it needs the
+ // entropy of a truncated ack frame. An entropy calculator must be set or else
+ // the framer will likely crash. If this is called multiple times, only the
+ // last calculator will be used.
+ void set_received_entropy_calculator(
+ QuicReceivedEntropyHashCalculatorInterface* entropy_calculator) {
+ entropy_calculator_ = entropy_calculator;
+ }
+
+ QuicErrorCode error() const {
+ return error_;
+ }
+
+ // Pass a UDP packet into the framer for parsing.
+ // Return true if the packet was processed succesfully. |packet| must be a
+ // single, complete UDP packet (not a frame of a packet). This packet
+ // might be null padded past the end of the payload, which will be correctly
+ // ignored.
+ bool ProcessPacket(const QuicEncryptedPacket& packet);
+
+ // Pass a data packet that was revived from FEC data into the framer
+ // for parsing.
+ // Return true if the packet was processed succesfully. |payload| must be
+ // the complete DECRYPTED payload of the revived packet.
+ bool ProcessRevivedPacket(QuicPacketHeader* header,
+ base::StringPiece payload);
+
+ // Largest size in bytes of all stream frame fields without the payload.
+ static size_t GetMinStreamFrameSize(QuicStreamId stream_id,
+ QuicStreamOffset offset,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group);
+ // Size in bytes of all ack frame fields without the missing packets.
+ static size_t GetMinAckFrameSize(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicSequenceNumberLength largest_observed_length);
+ // Size in bytes of a stop waiting frame.
+ static size_t GetStopWaitingFrameSize(
+ QuicSequenceNumberLength sequence_number_length);
+ // Size in bytes of all reset stream frame without the error details.
+ static size_t GetMinRstStreamFrameSize();
+ // Size in bytes of all connection close frame fields without the error
+ // details and the missing packets from the enclosed ack frame.
+ static size_t GetMinConnectionCloseFrameSize();
+ // Size in bytes of all GoAway frame fields without the reason phrase.
+ static size_t GetMinGoAwayFrameSize();
+ // Size in bytes of all WindowUpdate frame fields.
+ static size_t GetWindowUpdateFrameSize();
+ // Size in bytes of all Blocked frame fields.
+ static size_t GetBlockedFrameSize();
+ // Size in bytes required to serialize the stream id.
+ static size_t GetStreamIdSize(QuicStreamId stream_id);
+ // Size in bytes required to serialize the stream offset.
+ static size_t GetStreamOffsetSize(QuicStreamOffset offset);
+ // Size in bytes required for a serialized version negotiation packet
+ static size_t GetVersionNegotiationPacketSize(size_t number_versions);
+
+ // Returns the number of bytes added to the packet for the specified frame,
+ // and 0 if the frame doesn't fit. Includes the header size for the first
+ // frame.
+ size_t GetSerializedFrameLength(
+ const QuicFrame& frame,
+ size_t free_bytes,
+ bool first_frame_in_packet,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
+ QuicSequenceNumberLength sequence_number_length);
+
+ // Returns the associated data from the encrypted packet |encrypted| as a
+ // stringpiece.
+ static base::StringPiece GetAssociatedDataFromEncryptedPacket(
+ const QuicEncryptedPacket& encrypted,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length);
+
+ // Returns a SerializedPacket whose |packet| member is owned by the caller,
+ // is created from the first |num_frames| frames, or is nullptr if the packet
+ // could not be created. The packet must be of size |packet_size|.
+ SerializedPacket BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames,
+ size_t packet_size);
+
+ // Returns a SerializedPacket whose |packet| member is owned by the caller,
+ // and is populated with the fields in |header| and |fec|, or is nullptr if
+ // the packet could not be created.
+ SerializedPacket BuildFecPacket(const QuicPacketHeader& header,
+ const QuicFecData& fec);
+
+ // Returns a new public reset packet, owned by the caller.
+ static QuicEncryptedPacket* BuildPublicResetPacket(
+ const QuicPublicResetPacket& packet);
+
+ QuicEncryptedPacket* BuildVersionNegotiationPacket(
+ const QuicPacketPublicHeader& header,
+ const QuicVersionVector& supported_versions);
+
+ // SetDecrypter sets the primary decrypter, replacing any that already exists,
+ // and takes ownership. If an alternative decrypter is in place then the
+ // function DCHECKs. This is intended for cases where one knows that future
+ // packets will be using the new decrypter and the previous decrypter is now
+ // obsolete. |level| indicates the encryption level of the new decrypter.
+ void SetDecrypter(QuicDecrypter* decrypter, EncryptionLevel level);
+
+ // SetAlternativeDecrypter sets a decrypter that may be used to decrypt
+ // future packets and takes ownership of it. |level| indicates the encryption
+ // level of the decrypter. If |latch_once_used| is true, then the first time
+ // that the decrypter is successful it will replace the primary decrypter.
+ // Otherwise both decrypters will remain active and the primary decrypter
+ // will be the one last used.
+ void SetAlternativeDecrypter(QuicDecrypter* decrypter,
+ EncryptionLevel level,
+ bool latch_once_used);
+
+ const QuicDecrypter* decrypter() const;
+ const QuicDecrypter* alternative_decrypter() const;
+
+ // Changes the encrypter used for level |level| to |encrypter|. The function
+ // takes ownership of |encrypter|.
+ void SetEncrypter(EncryptionLevel level, QuicEncrypter* encrypter);
+ const QuicEncrypter* encrypter(EncryptionLevel level) const;
+
+ // Returns a new encrypted packet, owned by the caller.
+ QuicEncryptedPacket* EncryptPacket(EncryptionLevel level,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicPacket& packet);
+
+ // Returns the maximum length of plaintext that can be encrypted
+ // to ciphertext no larger than |ciphertext_size|.
+ size_t GetMaxPlaintextSize(size_t ciphertext_size);
+
+ const std::string& detailed_error() { return detailed_error_; }
+
+ // The minimum sequence number length required to represent |sequence_number|.
+ static QuicSequenceNumberLength GetMinSequenceNumberLength(
+ QuicPacketSequenceNumber sequence_number);
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ quic_version_ = versions[0];
+ }
+
+ void set_validate_flags(bool value) { validate_flags_ = value; }
+
+ bool is_server() const { return is_server_; }
+
+ private:
+ friend class test::QuicFramerPeer;
+
+ typedef std::map<QuicPacketSequenceNumber, uint8> NackRangeMap;
+
+ struct AckFrameInfo {
+ AckFrameInfo();
+ ~AckFrameInfo();
+
+ // The maximum delta between ranges.
+ QuicPacketSequenceNumber max_delta;
+ // Nack ranges starting with start sequence numbers and lengths.
+ NackRangeMap nack_ranges;
+ };
+
+ QuicPacketEntropyHash GetPacketEntropyHash(
+ const QuicPacketHeader& header) const;
+
+ bool ProcessDataPacket(const QuicPacketPublicHeader& public_header,
+ const QuicEncryptedPacket& packet);
+
+ bool ProcessPublicResetPacket(const QuicPacketPublicHeader& public_header);
+
+ bool ProcessVersionNegotiationPacket(QuicPacketPublicHeader* public_header);
+
+ bool ProcessPublicHeader(QuicPacketPublicHeader* header);
+
+ bool ProcessPacketHeader(QuicPacketHeader* header,
+ const QuicEncryptedPacket& packet);
+
+ bool ProcessPacketSequenceNumber(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber* sequence_number);
+ bool ProcessFrameData(const QuicPacketHeader& header);
+ bool ProcessStreamFrame(uint8 frame_type, QuicStreamFrame* frame);
+ bool ProcessAckFrame(uint8 frame_type, QuicAckFrame* frame);
+ bool ProcessTimestampsInAckFrame(QuicAckFrame* frame);
+ bool ProcessStopWaitingFrame(const QuicPacketHeader& public_header,
+ QuicStopWaitingFrame* stop_waiting);
+ bool ProcessCongestionFeedbackFrame(
+ QuicCongestionFeedbackFrame* congestion_feedback);
+ bool ProcessRstStreamFrame(QuicRstStreamFrame* frame);
+ bool ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame);
+ bool ProcessGoAwayFrame(QuicGoAwayFrame* frame);
+ bool ProcessWindowUpdateFrame(QuicWindowUpdateFrame* frame);
+ bool ProcessBlockedFrame(QuicBlockedFrame* frame);
+
+ bool DecryptPayload(const QuicPacketHeader& header,
+ const QuicEncryptedPacket& packet);
+
+ // Returns the full packet sequence number from the truncated
+ // wire format version and the last seen packet sequence number.
+ QuicPacketSequenceNumber CalculatePacketSequenceNumberFromWire(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber packet_sequence_number) const;
+
+ // Returns the QuicTime::Delta corresponding to the time from when the framer
+ // was created.
+ const QuicTime::Delta CalculateTimestampFromWire(uint32 time_delta_us);
+
+ // Computes the wire size in bytes of the |ack| frame, assuming no truncation.
+ size_t GetAckFrameSize(const QuicAckFrame& ack,
+ QuicSequenceNumberLength sequence_number_length);
+
+ // Computes the wire size in bytes of the payload of |frame|.
+ size_t ComputeFrameLength(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ InFecGroup is_in_fec_group,
+ QuicSequenceNumberLength sequence_number_length);
+
+ static bool AppendPacketSequenceNumber(
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber packet_sequence_number,
+ QuicDataWriter* writer);
+
+ static uint8 GetSequenceNumberFlags(
+ QuicSequenceNumberLength sequence_number_length);
+
+ static AckFrameInfo GetAckFrameInfo(const QuicAckFrame& frame);
+
+ // The Append* methods attempt to write the provided header or frame using the
+ // |writer|, and return true if successful.
+ bool AppendPacketHeader(const QuicPacketHeader& header,
+ QuicDataWriter* writer);
+ bool AppendTypeByte(const QuicFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* writer);
+ bool AppendStreamFrame(const QuicStreamFrame& frame,
+ bool last_frame_in_packet,
+ QuicDataWriter* builder);
+ bool AppendAckFrameAndTypeByte(const QuicPacketHeader& header,
+ const QuicAckFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendCongestionFeedbackFrame(const QuicCongestionFeedbackFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendTimestampToAckFrame(const QuicAckFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendStopWaitingFrame(const QuicPacketHeader& header,
+ const QuicStopWaitingFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendRstStreamFrame(const QuicRstStreamFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendConnectionCloseFrame(const QuicConnectionCloseFrame& frame,
+ QuicDataWriter* builder);
+ bool AppendGoAwayFrame(const QuicGoAwayFrame& frame, QuicDataWriter* writer);
+ bool AppendWindowUpdateFrame(const QuicWindowUpdateFrame& frame,
+ QuicDataWriter* writer);
+ bool AppendBlockedFrame(const QuicBlockedFrame& frame,
+ QuicDataWriter* writer);
+
+ bool RaiseError(QuicErrorCode error);
+
+ void set_error(QuicErrorCode error) {
+ error_ = error;
+ }
+
+ void set_detailed_error(const char* error) {
+ detailed_error_ = error;
+ }
+
+ std::string detailed_error_;
+ scoped_ptr<QuicDataReader> reader_;
+ QuicFramerVisitorInterface* visitor_;
+ QuicFecBuilderInterface* fec_builder_;
+ QuicReceivedEntropyHashCalculatorInterface* entropy_calculator_;
+ QuicErrorCode error_;
+ // Updated by ProcessPacketHeader when it succeeds.
+ QuicPacketSequenceNumber last_sequence_number_;
+ // Updated by WritePacketHeader.
+ QuicConnectionId last_serialized_connection_id_;
+ // Buffer containing decrypted payload data during parsing.
+ scoped_ptr<QuicData> decrypted_;
+ // Version of the protocol being used.
+ QuicVersion quic_version_;
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary).
+ QuicVersionVector supported_versions_;
+ // Primary decrypter used to decrypt packets during parsing.
+ scoped_ptr<QuicDecrypter> decrypter_;
+ // Alternative decrypter that can also be used to decrypt packets.
+ scoped_ptr<QuicDecrypter> alternative_decrypter_;
+ // The encryption level of |decrypter_|.
+ EncryptionLevel decrypter_level_;
+ // The encryption level of |alternative_decrypter_|.
+ EncryptionLevel alternative_decrypter_level_;
+ // |alternative_decrypter_latch_| is true if, when |alternative_decrypter_|
+ // successfully decrypts a packet, we should install it as the only
+ // decrypter.
+ bool alternative_decrypter_latch_;
+ // Encrypters used to encrypt packets via EncryptPacket().
+ scoped_ptr<QuicEncrypter> encrypter_[NUM_ENCRYPTION_LEVELS];
+ // Tracks if the framer is being used by the entity that received the
+ // connection or the entity that initiated it.
+ bool is_server_;
+ // If false, skip validation that the public flags are set to legal values.
+ bool validate_flags_;
+ // The time this framer was created. Time written to the wire will be
+ // written as a delta from this value.
+ QuicTime creation_time_;
+ // The time delta computed for the last timestamp frame. This is relative to
+ // the creation_time.
+ QuicTime::Delta last_timestamp_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicFramer);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_FRAMER_H_
diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc
new file mode 100644
index 0000000..535d31a
--- /dev/null
+++ b/net/quic/quic_framer_test.cc
@@ -0,0 +1,5074 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_framer.h"
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/port.h"
+#include "base/stl_util.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_framer_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+
+using base::hash_set;
+using base::StringPiece;
+using std::make_pair;
+using std::map;
+using std::numeric_limits;
+using std::pair;
+using std::string;
+using std::vector;
+using testing::Return;
+using testing::_;
+
+namespace net {
+namespace test {
+
+const QuicPacketSequenceNumber kEpoch = GG_UINT64_C(1) << 48;
+const QuicPacketSequenceNumber kMask = kEpoch - 1;
+
+// Index into the connection_id offset in the header.
+const size_t kConnectionIdOffset = kPublicFlagsSize;
+// Index into the version string in the header. (if present).
+const size_t kVersionOffset = kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
+
+// Size in bytes of the stream frame fields for an arbitrary StreamID and
+// offset and the last frame in a packet.
+size_t GetMinStreamFrameSize() {
+ return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize;
+}
+
+// Index into the sequence number offset in the header.
+size_t GetSequenceNumberOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return kConnectionIdOffset + connection_id_length +
+ (include_version ? kQuicVersionSize : 0);
+}
+
+size_t GetSequenceNumberOffset(bool include_version) {
+ return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version);
+}
+
+// Index into the private flags offset in the data packet header.
+size_t GetPrivateFlagsOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return GetSequenceNumberOffset(connection_id_length, include_version) +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+}
+
+size_t GetPrivateFlagsOffset(bool include_version) {
+ return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version);
+}
+
+size_t GetPrivateFlagsOffset(bool include_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ return GetSequenceNumberOffset(PACKET_8BYTE_CONNECTION_ID, include_version) +
+ sequence_number_length;
+}
+
+// Index into the fec group offset in the header.
+size_t GetFecGroupOffset(QuicConnectionIdLength connection_id_length,
+ bool include_version) {
+ return GetPrivateFlagsOffset(connection_id_length, include_version) +
+ kPrivateFlagsSize;
+}
+
+size_t GetFecGroupOffset(bool include_version) {
+ return GetPrivateFlagsOffset(PACKET_8BYTE_CONNECTION_ID, include_version) +
+ kPrivateFlagsSize;
+}
+
+size_t GetFecGroupOffset(bool include_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ return GetPrivateFlagsOffset(include_version, sequence_number_length) +
+ kPrivateFlagsSize;
+}
+
+// Index into the message tag of the public reset packet.
+// Public resets always have full connection_ids.
+const size_t kPublicResetPacketMessageTagOffset =
+ kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID;
+
+class TestEncrypter : public QuicEncrypter {
+ public:
+ virtual ~TestEncrypter() {}
+ virtual bool SetKey(StringPiece key) OVERRIDE {
+ return true;
+ }
+ virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE {
+ return true;
+ }
+ virtual bool Encrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece plaintext,
+ unsigned char* output) OVERRIDE {
+ CHECK(false) << "Not implemented";
+ return false;
+ }
+ virtual QuicData* EncryptPacket(QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece plaintext) OVERRIDE {
+ sequence_number_ = sequence_number;
+ associated_data_ = associated_data.as_string();
+ plaintext_ = plaintext.as_string();
+ return new QuicData(plaintext.data(), plaintext.length());
+ }
+ virtual size_t GetKeySize() const OVERRIDE {
+ return 0;
+ }
+ virtual size_t GetNoncePrefixSize() const OVERRIDE {
+ return 0;
+ }
+ virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) const OVERRIDE {
+ return ciphertext_size;
+ }
+ virtual size_t GetCiphertextSize(size_t plaintext_size) const OVERRIDE {
+ return plaintext_size;
+ }
+ virtual StringPiece GetKey() const OVERRIDE {
+ return StringPiece();
+ }
+ virtual StringPiece GetNoncePrefix() const OVERRIDE {
+ return StringPiece();
+ }
+ QuicPacketSequenceNumber sequence_number_;
+ string associated_data_;
+ string plaintext_;
+};
+
+class TestDecrypter : public QuicDecrypter {
+ public:
+ virtual ~TestDecrypter() {}
+ virtual bool SetKey(StringPiece key) OVERRIDE {
+ return true;
+ }
+ virtual bool SetNoncePrefix(StringPiece nonce_prefix) OVERRIDE {
+ return true;
+ }
+ virtual bool Decrypt(StringPiece nonce,
+ StringPiece associated_data,
+ StringPiece ciphertext,
+ unsigned char* output,
+ size_t* output_length) OVERRIDE {
+ CHECK(false) << "Not implemented";
+ return false;
+ }
+ virtual QuicData* DecryptPacket(QuicPacketSequenceNumber sequence_number,
+ StringPiece associated_data,
+ StringPiece ciphertext) OVERRIDE {
+ sequence_number_ = sequence_number;
+ associated_data_ = associated_data.as_string();
+ ciphertext_ = ciphertext.as_string();
+ return new QuicData(ciphertext.data(), ciphertext.length());
+ }
+ virtual StringPiece GetKey() const OVERRIDE {
+ return StringPiece();
+ }
+ virtual StringPiece GetNoncePrefix() const OVERRIDE {
+ return StringPiece();
+ }
+ QuicPacketSequenceNumber sequence_number_;
+ string associated_data_;
+ string ciphertext_;
+};
+
+class TestQuicVisitor : public ::net::QuicFramerVisitorInterface {
+ public:
+ TestQuicVisitor()
+ : error_count_(0),
+ version_mismatch_(0),
+ packet_count_(0),
+ frame_count_(0),
+ fec_count_(0),
+ complete_packets_(0),
+ revived_packets_(0),
+ accept_packet_(true),
+ accept_public_header_(true) {
+ }
+
+ virtual ~TestQuicVisitor() {
+ STLDeleteElements(&stream_frames_);
+ STLDeleteElements(&ack_frames_);
+ STLDeleteElements(&congestion_feedback_frames_);
+ STLDeleteElements(&stop_waiting_frames_);
+ STLDeleteElements(&ping_frames_);
+ STLDeleteElements(&fec_data_);
+ }
+
+ virtual void OnError(QuicFramer* f) OVERRIDE {
+ DVLOG(1) << "QuicFramer Error: " << QuicUtils::ErrorToString(f->error())
+ << " (" << f->error() << ")";
+ ++error_count_;
+ }
+
+ virtual void OnPacket() OVERRIDE {}
+
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE {
+ public_reset_packet_.reset(new QuicPublicResetPacket(packet));
+ }
+
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) OVERRIDE {
+ version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet));
+ }
+
+ virtual void OnRevivedPacket() OVERRIDE {
+ ++revived_packets_;
+ }
+
+ virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE {
+ DVLOG(1) << "QuicFramer Version Mismatch, version: " << version;
+ ++version_mismatch_;
+ return true;
+ }
+
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ public_header_.reset(new QuicPacketPublicHeader(header));
+ return accept_public_header_;
+ }
+
+ virtual bool OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) OVERRIDE {
+ return true;
+ }
+
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
+ ++packet_count_;
+ header_.reset(new QuicPacketHeader(header));
+ return accept_packet_;
+ }
+
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE {
+ ++frame_count_;
+ stream_frames_.push_back(new QuicStreamFrame(frame));
+ return true;
+ }
+
+ virtual void OnFecProtectedPayload(StringPiece payload) OVERRIDE {
+ fec_protected_payload_ = payload.as_string();
+ }
+
+ virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE {
+ ++frame_count_;
+ ack_frames_.push_back(new QuicAckFrame(frame));
+ return true;
+ }
+
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) OVERRIDE {
+ ++frame_count_;
+ congestion_feedback_frames_.push_back(
+ new QuicCongestionFeedbackFrame(frame));
+ return true;
+ }
+
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE {
+ ++frame_count_;
+ stop_waiting_frames_.push_back(new QuicStopWaitingFrame(frame));
+ return true;
+ }
+
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE {
+ ++frame_count_;
+ ping_frames_.push_back(new QuicPingFrame(frame));
+ return true;
+ }
+
+ virtual void OnFecData(const QuicFecData& fec) OVERRIDE {
+ ++fec_count_;
+ fec_data_.push_back(new QuicFecData(fec));
+ }
+
+ virtual void OnPacketComplete() OVERRIDE {
+ ++complete_packets_;
+ }
+
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE {
+ rst_stream_frame_ = frame;
+ return true;
+ }
+
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) OVERRIDE {
+ connection_close_frame_ = frame;
+ return true;
+ }
+
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE {
+ goaway_frame_ = frame;
+ return true;
+ }
+
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame)
+ OVERRIDE {
+ window_update_frame_ = frame;
+ return true;
+ }
+
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ blocked_frame_ = frame;
+ return true;
+ }
+
+ // Counters from the visitor_ callbacks.
+ int error_count_;
+ int version_mismatch_;
+ int packet_count_;
+ int frame_count_;
+ int fec_count_;
+ int complete_packets_;
+ int revived_packets_;
+ bool accept_packet_;
+ bool accept_public_header_;
+
+ scoped_ptr<QuicPacketHeader> header_;
+ scoped_ptr<QuicPacketPublicHeader> public_header_;
+ scoped_ptr<QuicPublicResetPacket> public_reset_packet_;
+ scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ vector<QuicStreamFrame*> stream_frames_;
+ vector<QuicAckFrame*> ack_frames_;
+ vector<QuicCongestionFeedbackFrame*> congestion_feedback_frames_;
+ vector<QuicStopWaitingFrame*> stop_waiting_frames_;
+ vector<QuicPingFrame*> ping_frames_;
+ vector<QuicFecData*> fec_data_;
+ string fec_protected_payload_;
+ QuicRstStreamFrame rst_stream_frame_;
+ QuicConnectionCloseFrame connection_close_frame_;
+ QuicGoAwayFrame goaway_frame_;
+ QuicWindowUpdateFrame window_update_frame_;
+ QuicBlockedFrame blocked_frame_;
+};
+
+class QuicFramerTest : public ::testing::TestWithParam<QuicVersion> {
+ public:
+ QuicFramerTest()
+ : encrypter_(new test::TestEncrypter()),
+ decrypter_(new test::TestDecrypter()),
+ start_(QuicTime::Zero().Add(QuicTime::Delta::FromMicroseconds(0x10))),
+ framer_(QuicSupportedVersions(), start_, true) {
+ version_ = GetParam();
+ framer_.set_version(version_);
+ framer_.SetDecrypter(decrypter_, ENCRYPTION_NONE);
+ framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_);
+ framer_.set_visitor(&visitor_);
+ framer_.set_received_entropy_calculator(&entropy_calculator_);
+ }
+
+ // Helper function to get unsigned char representation of digit in the
+ // units place of the current QUIC version number.
+ unsigned char GetQuicVersionDigitOnes() {
+ return static_cast<unsigned char> ('0' + version_%10);
+ }
+
+ // Helper function to get unsigned char representation of digit in the
+ // tens place of the current QUIC version number.
+ unsigned char GetQuicVersionDigitTens() {
+ return static_cast<unsigned char> ('0' + (version_/10)%10);
+ }
+
+ bool CheckEncryption(QuicPacketSequenceNumber sequence_number,
+ QuicPacket* packet) {
+ if (sequence_number != encrypter_->sequence_number_) {
+ LOG(ERROR) << "Encrypted incorrect packet sequence number. expected "
+ << sequence_number << " actual: "
+ << encrypter_->sequence_number_;
+ return false;
+ }
+ if (packet->AssociatedData() != encrypter_->associated_data_) {
+ LOG(ERROR) << "Encrypted incorrect associated data. expected "
+ << packet->AssociatedData() << " actual: "
+ << encrypter_->associated_data_;
+ return false;
+ }
+ if (packet->Plaintext() != encrypter_->plaintext_) {
+ LOG(ERROR) << "Encrypted incorrect plaintext data. expected "
+ << packet->Plaintext() << " actual: "
+ << encrypter_->plaintext_;
+ return false;
+ }
+ return true;
+ }
+
+ bool CheckDecryption(const QuicEncryptedPacket& encrypted,
+ bool includes_version) {
+ if (visitor_.header_->packet_sequence_number !=
+ decrypter_->sequence_number_) {
+ LOG(ERROR) << "Decrypted incorrect packet sequence number. expected "
+ << visitor_.header_->packet_sequence_number << " actual: "
+ << decrypter_->sequence_number_;
+ return false;
+ }
+ if (QuicFramer::GetAssociatedDataFromEncryptedPacket(
+ encrypted, PACKET_8BYTE_CONNECTION_ID,
+ includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) !=
+ decrypter_->associated_data_) {
+ LOG(ERROR) << "Decrypted incorrect associated data. expected "
+ << QuicFramer::GetAssociatedDataFromEncryptedPacket(
+ encrypted, PACKET_8BYTE_CONNECTION_ID,
+ includes_version, PACKET_6BYTE_SEQUENCE_NUMBER)
+ << " actual: " << decrypter_->associated_data_;
+ return false;
+ }
+ StringPiece ciphertext(encrypted.AsStringPiece().substr(
+ GetStartOfEncryptedData(PACKET_8BYTE_CONNECTION_ID, includes_version,
+ PACKET_6BYTE_SEQUENCE_NUMBER)));
+ if (ciphertext != decrypter_->ciphertext_) {
+ LOG(ERROR) << "Decrypted incorrect ciphertext data. expected "
+ << ciphertext << " actual: "
+ << decrypter_->ciphertext_;
+ return false;
+ }
+ return true;
+ }
+
+ char* AsChars(unsigned char* data) {
+ return reinterpret_cast<char*>(data);
+ }
+
+ void CheckProcessingFails(unsigned char* packet,
+ size_t len,
+ string expected_error,
+ QuicErrorCode error_code) {
+ QuicEncryptedPacket encrypted(AsChars(packet), len, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted)) << "len: " << len;
+ EXPECT_EQ(expected_error, framer_.detailed_error()) << "len: " << len;
+ EXPECT_EQ(error_code, framer_.error()) << "len: " << len;
+ }
+
+ // Checks if the supplied string matches data in the supplied StreamFrame.
+ void CheckStreamFrameData(string str, QuicStreamFrame* frame) {
+ scoped_ptr<string> frame_data(frame->GetDataAsString());
+ EXPECT_EQ(str, *frame_data);
+ }
+
+ void CheckStreamFrameBoundaries(unsigned char* packet,
+ size_t stream_id_size,
+ bool include_version) {
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize; i < GetMinStreamFrameSize(); ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + stream_id_size) {
+ expected_error = "Unable to read stream_id.";
+ } else if (i < kQuicFrameTypeSize + stream_id_size +
+ kQuicMaxStreamOffsetSize) {
+ expected_error = "Unable to read offset.";
+ } else {
+ expected_error = "Unable to read frame data.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version,
+ PACKET_6BYTE_SEQUENCE_NUMBER,
+ NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_STREAM_DATA);
+ }
+ }
+
+ void CheckCalculatePacketSequenceNumber(
+ QuicPacketSequenceNumber expected_sequence_number,
+ QuicPacketSequenceNumber last_sequence_number) {
+ QuicPacketSequenceNumber wire_sequence_number =
+ expected_sequence_number & kMask;
+ QuicFramerPeer::SetLastSequenceNumber(&framer_, last_sequence_number);
+ EXPECT_EQ(expected_sequence_number,
+ QuicFramerPeer::CalculatePacketSequenceNumberFromWire(
+ &framer_, PACKET_6BYTE_SEQUENCE_NUMBER, wire_sequence_number))
+ << "last_sequence_number: " << last_sequence_number
+ << " wire_sequence_number: " << wire_sequence_number;
+ }
+
+ QuicPacket* BuildDataPacket(const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ return BuildUnsizedDataPacket(&framer_, header, frames).packet;
+ }
+
+ test::TestEncrypter* encrypter_;
+ test::TestDecrypter* decrypter_;
+ QuicVersion version_;
+ QuicTime start_;
+ QuicFramer framer_;
+ test::TestQuicVisitor visitor_;
+ test::TestEntropyCalculator entropy_calculator_;
+};
+
+// Run all framer tests with all supported versions of QUIC.
+INSTANTIATE_TEST_CASE_P(QuicFramerTests,
+ QuicFramerTest,
+ ::testing::ValuesIn(kSupportedQuicVersions));
+
+TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearEpochStart) {
+ // A few quick manual sanity checks
+ CheckCalculatePacketSequenceNumber(GG_UINT64_C(1), GG_UINT64_C(0));
+ CheckCalculatePacketSequenceNumber(kEpoch + 1, kMask);
+ CheckCalculatePacketSequenceNumber(kEpoch, kMask);
+
+ // Cases where the last number was close to the start of the range
+ for (uint64 last = 0; last < 10; last++) {
+ // Small numbers should not wrap (even if they're out of order).
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(j, last);
+ }
+
+ // Large numbers should not wrap either (because we're near 0 already).
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(kEpoch - 1 - j, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearEpochEnd) {
+ // Cases where the last number was close to the end of the range
+ for (uint64 i = 0; i < 10; i++) {
+ QuicPacketSequenceNumber last = kEpoch - i;
+
+ // Small numbers should wrap.
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(kEpoch + j, last);
+ }
+
+ // Large numbers should not (even if they're out of order).
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(kEpoch - 1 - j, last);
+ }
+ }
+}
+
+// Next check where we're in a non-zero epoch to verify we handle
+// reverse wrapping, too.
+TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearPrevEpoch) {
+ const uint64 prev_epoch = 1 * kEpoch;
+ const uint64 cur_epoch = 2 * kEpoch;
+ // Cases where the last number was close to the start of the range
+ for (uint64 i = 0; i < 10; i++) {
+ uint64 last = cur_epoch + i;
+ // Small number should not wrap (even if they're out of order).
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(cur_epoch + j, last);
+ }
+
+ // But large numbers should reverse wrap.
+ for (uint64 j = 0; j < 10; j++) {
+ uint64 num = kEpoch - 1 - j;
+ CheckCalculatePacketSequenceNumber(prev_epoch + num, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearNextEpoch) {
+ const uint64 cur_epoch = 2 * kEpoch;
+ const uint64 next_epoch = 3 * kEpoch;
+ // Cases where the last number was close to the end of the range
+ for (uint64 i = 0; i < 10; i++) {
+ QuicPacketSequenceNumber last = next_epoch - 1 - i;
+
+ // Small numbers should wrap.
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(next_epoch + j, last);
+ }
+
+ // but large numbers should not (even if they're out of order).
+ for (uint64 j = 0; j < 10; j++) {
+ uint64 num = kEpoch - 1 - j;
+ CheckCalculatePacketSequenceNumber(cur_epoch + num, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, CalculatePacketSequenceNumberFromWireNearNextMax) {
+ const uint64 max_number = numeric_limits<uint64>::max();
+ const uint64 max_epoch = max_number & ~kMask;
+
+ // Cases where the last number was close to the end of the range
+ for (uint64 i = 0; i < 10; i++) {
+ // Subtract 1, because the expected next sequence number is 1 more than the
+ // last sequence number.
+ QuicPacketSequenceNumber last = max_number - i - 1;
+
+ // Small numbers should not wrap, because they have nowhere to go.
+ for (uint64 j = 0; j < 10; j++) {
+ CheckCalculatePacketSequenceNumber(max_epoch + j, last);
+ }
+
+ // Large numbers should not wrap either.
+ for (uint64 j = 0; j < 10; j++) {
+ uint64 num = kEpoch - 1 - j;
+ CheckCalculatePacketSequenceNumber(max_epoch + num, last);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, EmptyPacket) {
+ char packet[] = { 0x00 };
+ QuicEncryptedPacket encrypted(packet, 0, false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error());
+}
+
+TEST_P(QuicFramerTest, LargePacket) {
+ unsigned char packet[kMaxPacketSize + 1] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+
+ memset(packet + GetPacketHeaderSize(
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), 0,
+ kMaxPacketSize - GetPacketHeaderSize(
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP) + 1);
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+
+ ASSERT_TRUE(visitor_.header_.get());
+ // Make sure we've parsed the packet header, so we can send an error.
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ // Make sure the correct error is propagated.
+ EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error());
+}
+
+TEST_P(QuicFramerTest, PacketHeader) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(!kIncludeVersion)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(!kIncludeVersion)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith4ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
+
+ unsigned char packet[] = {
+ // public flags (4 byte connection_id)
+ 0x38,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_4BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(PACKET_4BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeader1ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
+
+ unsigned char packet[] = {
+ // public flags (1 byte connection_id)
+ 0x34,
+ // connection_id
+ 0x10,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_1BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(PACKET_1BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith0ByteConnectionId) {
+ QuicFramerPeer::SetLastSerializedConnectionId(
+ &framer_, GG_UINT64_C(0xFEDCBA9876543210));
+
+ unsigned char packet[] = {
+ // public flags (0 byte connection_id)
+ 0x30,
+ // connection_id
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_0BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(PACKET_0BYTE_CONNECTION_ID,
+ !kIncludeVersion)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWithVersionFlag) {
+ unsigned char packet[] = {
+ // public flags (version)
+ 0x3D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_TRUE(visitor_.header_->public_header.version_flag);
+ EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < kVersionOffset) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetSequenceNumberOffset(kIncludeVersion)) {
+ expected_error = "Unable to read protocol version.";
+ } else if (i < GetPrivateFlagsOffset(kIncludeVersion)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(kIncludeVersion)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith4ByteSequenceNumber) {
+ QuicFramerPeer::SetLastSequenceNumber(&framer_,
+ GG_UINT64_C(0x123456789ABA));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id and 4 byte sequence number)
+ 0x2C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
+ PACKET_4BYTE_SEQUENCE_NUMBER)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(!kIncludeVersion,
+ PACKET_4BYTE_SEQUENCE_NUMBER)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith2ByteSequenceNumber) {
+ QuicFramerPeer::SetLastSequenceNumber(&framer_,
+ GG_UINT64_C(0x123456789ABA));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id and 2 byte sequence number)
+ 0x1C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
+ PACKET_2BYTE_SEQUENCE_NUMBER)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(!kIncludeVersion,
+ PACKET_2BYTE_SEQUENCE_NUMBER)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, PacketHeaderWith1ByteSequenceNumber) {
+ QuicFramerPeer::SetLastSequenceNumber(&framer_,
+ GG_UINT64_C(0x123456789ABA));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id and 1 byte sequence number)
+ 0x0C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC,
+ // private flags
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_MISSING_PAYLOAD, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+ EXPECT_FALSE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(0, visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ // Now test framing boundaries.
+ for (size_t i = 0;
+ i < GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ ++i) {
+ string expected_error;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) {
+ expected_error = "Unable to read ConnectionId.";
+ } else if (i < GetPrivateFlagsOffset(!kIncludeVersion,
+ PACKET_1BYTE_SEQUENCE_NUMBER)) {
+ expected_error = "Unable to read sequence number.";
+ } else if (i < GetFecGroupOffset(!kIncludeVersion,
+ PACKET_1BYTE_SEQUENCE_NUMBER)) {
+ expected_error = "Unable to read private flags.";
+ } else {
+ expected_error = "Unable to read first fec protected packet offset.";
+ }
+ CheckProcessingFails(packet, i, expected_error, QUIC_INVALID_PACKET_HEADER);
+ }
+}
+
+TEST_P(QuicFramerTest, InvalidPublicFlag) {
+ unsigned char packet[] = {
+ // public flags: all flags set but the public reset flag and version flag.
+ 0xFC,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ CheckProcessingFails(packet,
+ arraysize(packet),
+ "Illegal public flags value.",
+ QUIC_INVALID_PACKET_HEADER);
+
+ // Now turn off validation.
+ framer_.set_validate_flags(false);
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+};
+
+TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id and version flag and an unknown flag)
+ 0x4D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ CheckProcessingFails(packet,
+ arraysize(packet),
+ "Illegal public flags value.",
+ QUIC_INVALID_PACKET_HEADER);
+};
+
+TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id, version flag and an unknown flag)
+ 0x7D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', '0', '0',
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(0, visitor_.frame_count_);
+ EXPECT_EQ(1, visitor_.version_mismatch_);
+};
+
+TEST_P(QuicFramerTest, InvalidPrivateFlag) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x10,
+
+ // frame type (padding)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ CheckProcessingFails(packet,
+ arraysize(packet),
+ "Illegal private flags value.",
+ QUIC_INVALID_PACKET_HEADER);
+};
+
+TEST_P(QuicFramerTest, InvalidFECGroupOffset) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ // private flags (fec group)
+ 0x02,
+ // first fec protected packet offset
+ 0x10
+ };
+ CheckProcessingFails(packet,
+ arraysize(packet),
+ "First fec protected packet offset must be less "
+ "than the sequence number.",
+ QUIC_INVALID_PACKET_HEADER);
+};
+
+TEST_P(QuicFramerTest, PaddingFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (padding frame)
+ 0x00,
+ // Ignored data (which in this case is a stream frame)
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ ASSERT_EQ(0u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ // A packet with no frames is not acceptable.
+ CheckProcessingFails(
+ packet,
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ "Packet has no frames.", QUIC_MISSING_PAYLOAD);
+}
+
+TEST_P(QuicFramerTest, StreamFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(static_cast<uint64>(0x01020304),
+ visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+
+ // Now test framing boundaries.
+ CheckStreamFrameBoundaries(packet, kQuicMaxStreamIdSize, !kIncludeVersion);
+}
+
+TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFE,
+ // stream id
+ 0x04, 0x03, 0x02,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(GG_UINT64_C(0x00020304),
+ visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+
+ // Now test framing boundaries.
+ const size_t stream_id_size = 3;
+ CheckStreamFrameBoundaries(packet, stream_id_size, !kIncludeVersion);
+}
+
+TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFD,
+ // stream id
+ 0x04, 0x03,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(static_cast<uint64>(0x00000304),
+ visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+
+ // Now test framing boundaries.
+ const size_t stream_id_size = 2;
+ CheckStreamFrameBoundaries(packet, stream_id_size, !kIncludeVersion);
+}
+
+TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFC,
+ // stream id
+ 0x04,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(static_cast<uint64>(0x00000004),
+ visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+
+ // Now test framing boundaries.
+ const size_t stream_id_size = 1;
+ CheckStreamFrameBoundaries(packet, stream_id_size, !kIncludeVersion);
+}
+
+TEST_P(QuicFramerTest, StreamFrameWithVersion) {
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x3D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(visitor_.header_->public_header.version_flag);
+ EXPECT_EQ(GetParam(), visitor_.header_->public_header.versions[0]);
+ EXPECT_TRUE(CheckDecryption(encrypted, kIncludeVersion));
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(static_cast<uint64>(0x01020304),
+ visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+
+ // Now test framing boundaries.
+ CheckStreamFrameBoundaries(packet, kQuicMaxStreamIdSize, kIncludeVersion);
+}
+
+TEST_P(QuicFramerTest, RejectPacket) {
+ visitor_.accept_packet_ = false;
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ ASSERT_EQ(0u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+}
+
+TEST_P(QuicFramerTest, RejectPublicHeader) {
+ visitor_.accept_public_header_ = false;
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_header_.get());
+ ASSERT_FALSE(visitor_.header_.get());
+}
+
+TEST_P(QuicFramerTest, RevivedStreamFrame) {
+ unsigned char payload[] = {
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = true;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ // Do not encrypt the payload because the revived payload is post-encryption.
+ EXPECT_TRUE(framer_.ProcessRevivedPacket(&header,
+ StringPiece(AsChars(payload),
+ arraysize(payload))));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_EQ(1, visitor_.revived_packets_);
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.header_->public_header.connection_id);
+ EXPECT_FALSE(visitor_.header_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.header_->public_header.version_flag);
+ EXPECT_TRUE(visitor_.header_->fec_flag);
+ EXPECT_TRUE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(1 << (header.packet_sequence_number % 8),
+ visitor_.header_->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.header_->packet_sequence_number);
+ EXPECT_EQ(NOT_IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(0x00u, visitor_.header_->fec_group);
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+}
+
+TEST_P(QuicFramerTest, StreamFrameInFecGroup) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x12, 0x34,
+ // private flags (fec group)
+ 0x02,
+ // first fec protected packet offset
+ 0x02,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+ EXPECT_EQ(IN_FEC_GROUP, visitor_.header_->is_in_fec_group);
+ EXPECT_EQ(GG_UINT64_C(0x341256789ABA),
+ visitor_.header_->fec_group);
+ const size_t fec_offset =
+ GetStartOfFecProtectedData(PACKET_8BYTE_CONNECTION_ID,
+ !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER);
+ EXPECT_EQ(
+ string(AsChars(packet) + fec_offset, arraysize(packet) - fec_offset),
+ visitor_.fec_protected_payload_);
+
+ ASSERT_EQ(1u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.stream_frames_[0]->stream_id);
+ EXPECT_TRUE(visitor_.stream_frames_[0]->fin);
+ EXPECT_EQ(GG_UINT64_C(0xBA98FEDC32107654),
+ visitor_.stream_frames_[0]->offset);
+ CheckStreamFrameData("hello world!", visitor_.stream_frames_[0]);
+}
+
+TEST_P(QuicFramerTest, AckFramev22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed);
+ ASSERT_EQ(1u, frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries.
+ const size_t ack_frame_size = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+
+TEST_P(QuicFramerTest, AckFrameTwoTimestamp) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // Number of timestamps.
+ 0x02,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ // Delta from largest observed.
+ 0x02,
+ // Delta time.
+ 0x10, 0x32,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed);
+ ASSERT_EQ(1u, frame.missing_packets.size());
+ ASSERT_EQ(2u, frame.received_packet_times.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumTimestampsOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kTimestampDeltaLargestObserved1 = kNumTimestampsOffset +
+ kQuicNumTimestampsSize;
+ const size_t kTimestampTimeDeltaLargestObserved1 =
+ kTimestampDeltaLargestObserved1 + 1;
+ const size_t kTimestampDeltaLargestObserved2 =
+ kTimestampTimeDeltaLargestObserved1 + 4;
+ const size_t kTimestampTimeDeltaLargestObserved2 =
+ kTimestampDeltaLargestObserved2 + 1;
+ const size_t kNumMissingPacketOffset =
+ kTimestampTimeDeltaLargestObserved2 + 2;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries.
+ const size_t ack_frame_size = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumTimestampsOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kTimestampDeltaLargestObserved1) {
+ expected_error = "Unable to read num received packets.";
+ } else if (i < kTimestampTimeDeltaLargestObserved1) {
+ expected_error = "Unable to read sequence delta in received packets.";
+ } else if (i < kTimestampDeltaLargestObserved2) {
+ expected_error = "Unable to read time delta in received packets.";
+ } else if (i < kTimestampTimeDeltaLargestObserved2) {
+ expected_error = "Unable to read sequence delta in received packets.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error =
+ "Unable to read incremental time delta in received packets.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+
+TEST_P(QuicFramerTest, AckFrameOneTimestamp) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // Number of timestamps.
+ 0x01,
+ // Delta from largest observed.
+ 0x01,
+ // Delta time.
+ 0x10, 0x32, 0x54, 0x76,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed);
+ ASSERT_EQ(1u, frame.missing_packets.size());
+ ASSERT_EQ(1u, frame.received_packet_times.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumTimestampsOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kTimestampDeltaLargestObserved = kNumTimestampsOffset +
+ kQuicNumTimestampsSize;
+ const size_t kTimestampTimeDeltaLargestObserved =
+ kTimestampDeltaLargestObserved + 1;
+ const size_t kNumMissingPacketOffset = kTimestampTimeDeltaLargestObserved + 4;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries.
+ const size_t ack_frame_size = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumTimestampsOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kTimestampDeltaLargestObserved) {
+ expected_error = "Unable to read num received packets.";
+ } else if (i < kTimestampTimeDeltaLargestObserved) {
+ expected_error = "Unable to read sequence delta in received packets.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read time delta in received packets.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+
+TEST_P(QuicFramerTest, AckFrame) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // Number of timestamps.
+ 0x00,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed);
+ ASSERT_EQ(1u, frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumTimestampsOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kNumMissingPacketOffset = kNumTimestampsOffset +
+ kQuicNumTimestampsSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries.
+ const size_t ack_frame_size = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumTimestampsOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read num received packets.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else {
+ expected_error = "Unable to read num revived packets.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPackets) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num received packets.
+ 0x00,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x01,
+ // Revived packet sequence number.
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed);
+ ASSERT_EQ(1u, frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumTimestampsOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kNumMissingPacketOffset = kNumTimestampsOffset +
+ kQuicNumTimestampsSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries.
+ const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kReceivedEntropyOffset) {
+ expected_error = "Unable to read least unacked delta.";
+ } else if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumTimestampsOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read num received packets.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else if (i < kRevivedPacketSequenceNumberLength) {
+ expected_error = "Unable to read num revived packets.";
+ } else {
+ expected_error = "Unable to read revived packet.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameRevivedPacketsv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packets
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // Number of revived packets.
+ 0x01,
+ // Revived packet sequence number.
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Number of revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ const QuicAckFrame& frame = *visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.largest_observed);
+ ASSERT_EQ(1u, frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ frame.missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter);
+
+ const size_t kReceivedEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLargestObservedOffset = kReceivedEntropyOffset +
+ kQuicEntropyHashSize;
+ const size_t kMissingDeltaTimeOffset = kLargestObservedOffset +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ const size_t kNumMissingPacketOffset = kMissingDeltaTimeOffset +
+ kQuicDeltaTimeLargestObservedSize;
+ const size_t kMissingPacketsOffset = kNumMissingPacketOffset +
+ kNumberOfNackRangesSize;
+ const size_t kMissingPacketsRange = kMissingPacketsOffset +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketsLength = kMissingPacketsRange +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ const size_t kRevivedPacketSequenceNumberLength = kRevivedPacketsLength +
+ PACKET_1BYTE_SEQUENCE_NUMBER;
+ // Now test framing boundaries.
+ const size_t ack_frame_size = kRevivedPacketSequenceNumberLength +
+ PACKET_6BYTE_SEQUENCE_NUMBER;
+ for (size_t i = kQuicFrameTypeSize; i < ack_frame_size; ++i) {
+ string expected_error;
+ if (i < kReceivedEntropyOffset) {
+ expected_error = "Unable to read least unacked delta.";
+ } else if (i < kLargestObservedOffset) {
+ expected_error = "Unable to read entropy hash for received packets.";
+ } else if (i < kMissingDeltaTimeOffset) {
+ expected_error = "Unable to read largest observed.";
+ } else if (i < kNumMissingPacketOffset) {
+ expected_error = "Unable to read delta time largest observed.";
+ } else if (i < kMissingPacketsOffset) {
+ expected_error = "Unable to read num missing packet ranges.";
+ } else if (i < kMissingPacketsRange) {
+ expected_error = "Unable to read missing sequence number delta.";
+ } else if (i < kRevivedPacketsLength) {
+ expected_error = "Unable to read missing sequence number range.";
+ } else if (i < kRevivedPacketSequenceNumberLength) {
+ expected_error = "Unable to read num revived packets.";
+ } else {
+ expected_error = "Unable to read revived packet.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_ACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, AckFrameNoNacksv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (no nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x4C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->largest_observed);
+ ASSERT_EQ(0u, frame->missing_packets.size());
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrameNoNacks) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (no nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x4C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // Number of received packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->largest_observed);
+ ASSERT_EQ(0u, frame->missing_packets.size());
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrame500Nacksv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges
+ 0x02,
+ // missing packet delta
+ 0x01,
+ // 243 more missing packets in range.
+ // The ranges are listed in this order so the re-constructed packet matches.
+ 0xF3,
+ // No gap between ranges
+ 0x00,
+ // 255 more missing packets in range.
+ 0xFF,
+ // No revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->largest_observed);
+ EXPECT_EQ(0u, frame->revived_packets.size());
+ ASSERT_EQ(500u, frame->missing_packets.size());
+ SequenceNumberSet::const_iterator first_missing_iter =
+ frame->missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ frame->missing_packets.rbegin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter);
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, AckFrame500Nacks) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0xBA,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // No received packets.
+ 0x00,
+ // num missing packet ranges
+ 0x02,
+ // missing packet delta
+ 0x01,
+ // 243 more missing packets in range.
+ // The ranges are listed in this order so the re-constructed packet matches.
+ 0xF3,
+ // No gap between ranges
+ 0x00,
+ // 255 more missing packets in range.
+ 0xFF,
+ // No revived packets.
+ 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame* frame = visitor_.ack_frames_[0];
+ EXPECT_EQ(0xBA, frame->entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame->largest_observed);
+ EXPECT_EQ(0u, frame->revived_packets.size());
+ ASSERT_EQ(500u, frame->missing_packets.size());
+ SequenceNumberSet::const_iterator first_missing_iter =
+ frame->missing_packets.begin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE) - 499, *first_missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ frame->missing_packets.rbegin();
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *last_missing_iter);
+
+ // Verify that the packet re-serializes identically.
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame));
+ scoped_ptr<QuicPacket> data(BuildDataPacket(*visitor_.header_, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (congestion feedback frame)
+ 0x20,
+ // congestion feedback type (tcp)
+ 0x00,
+ // ack_frame.feedback.tcp.receive_window
+ 0x03, 0x04,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size());
+ const QuicCongestionFeedbackFrame& frame =
+ *visitor_.congestion_feedback_frames_[0];
+ ASSERT_EQ(kTCP, frame.type);
+ EXPECT_EQ(0x4030u, frame.tcp.receive_window);
+
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize; i < 4; ++i) {
+ string expected_error;
+ if (i < 2) {
+ expected_error = "Unable to read congestion feedback type.";
+ } else if (i < 4) {
+ expected_error = "Unable to read receive window.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (congestion feedback frame)
+ 0x20,
+ // congestion feedback type (invalid)
+ 0x03,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+ EXPECT_EQ(QUIC_INVALID_CONGESTION_FEEDBACK_DATA, framer_.error());
+}
+
+TEST_P(QuicFramerTest, StopWaitingFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x06,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0xAB,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ ASSERT_EQ(1u, visitor_.stop_waiting_frames_.size());
+ const QuicStopWaitingFrame& frame = *visitor_.stop_waiting_frames_[0];
+ EXPECT_EQ(0xAB, frame.entropy_hash);
+ EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.least_unacked);
+
+ const size_t kSentEntropyOffset = kQuicFrameTypeSize;
+ const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize;
+ const size_t frame_size = 7;
+ for (size_t i = kQuicFrameTypeSize; i < frame_size; ++i) {
+ string expected_error;
+ if (i < kLeastUnackedOffset) {
+ expected_error = "Unable to read entropy hash for sent packets.";
+ } else {
+ expected_error = "Unable to read least unacked delta.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_STOP_WAITING_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, RstStreamFrameQuic) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (rst stream frame)
+ 0x01,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+
+ // sent byte offset
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
+
+ // error code
+ 0x01, 0x00, 0x00, 0x00,
+
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304), visitor_.rst_stream_frame_.stream_id);
+ EXPECT_EQ(0x01, visitor_.rst_stream_frame_.error_code);
+ EXPECT_EQ("because I can", visitor_.rst_stream_frame_.error_details);
+ EXPECT_EQ(GG_UINT64_C(0x0807060504030201),
+ visitor_.rst_stream_frame_.byte_offset);
+
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetMinRstStreamFrameSize(); ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
+ expected_error = "Unable to read stream_id.";
+ } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ + kQuicMaxStreamOffsetSize) {
+ expected_error = "Unable to read rst stream sent byte offset.";
+ } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize +
+ + kQuicMaxStreamOffsetSize + kQuicErrorCodeSize) {
+ expected_error = "Unable to read rst stream error code.";
+ } else {
+ expected_error = "Unable to read rst stream error details.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_RST_STREAM_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, ConnectionCloseFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x11, 0x00, 0x00, 0x00,
+
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+
+ EXPECT_EQ(0x11, visitor_.connection_close_frame_.error_code);
+ EXPECT_EQ("because I can", visitor_.connection_close_frame_.error_details);
+
+ ASSERT_EQ(0u, visitor_.ack_frames_.size());
+
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetMinConnectionCloseFrameSize(); ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) {
+ expected_error = "Unable to read connection close error code.";
+ } else {
+ expected_error = "Unable to read connection close error details.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, GoAwayFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x09, 0x00, 0x00, 0x00,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.goaway_frame_.last_good_stream_id);
+ EXPECT_EQ(0x9, visitor_.goaway_frame_.error_code);
+ EXPECT_EQ("because I can", visitor_.goaway_frame_.reason_phrase);
+
+ const size_t reason_size = arraysize("because I can") - 1;
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetMinGoAwayFrameSize() + reason_size; ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + kQuicErrorCodeSize) {
+ expected_error = "Unable to read go away error code.";
+ } else if (i < kQuicFrameTypeSize + kQuicErrorCodeSize +
+ kQuicMaxStreamIdSize) {
+ expected_error = "Unable to read last good stream id.";
+ } else {
+ expected_error = "Unable to read goaway reason.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_GOAWAY_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, WindowUpdateFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // byte offset
+ 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.window_update_frame_.stream_id);
+ EXPECT_EQ(GG_UINT64_C(0x0c0b0a0908070605),
+ visitor_.window_update_frame_.byte_offset);
+
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize;
+ i < QuicFramer::GetWindowUpdateFrameSize(); ++i) {
+ string expected_error;
+ if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) {
+ expected_error = "Unable to read stream_id.";
+ } else {
+ expected_error = "Unable to read window byte_offset.";
+ }
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_WINDOW_UPDATE_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, BlockedFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(GG_UINT64_C(0x01020304),
+ visitor_.blocked_frame_.stream_id);
+
+ // Now test framing boundaries.
+ for (size_t i = kQuicFrameTypeSize; i < QuicFramer::GetBlockedFrameSize();
+ ++i) {
+ string expected_error = "Unable to read stream_id.";
+ CheckProcessingFails(
+ packet,
+ i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ expected_error, QUIC_INVALID_BLOCKED_DATA);
+ }
+}
+
+TEST_P(QuicFramerTest, PingFrame) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(1u, visitor_.ping_frames_.size());
+
+ // No need to check the PING frame boundaries because it has no payload.
+}
+
+TEST_P(QuicFramerTest, PublicResetPacket) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.public_reset_packet_->public_header.connection_id);
+ EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
+ EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
+ visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.public_reset_packet_->rejected_sequence_number);
+ EXPECT_TRUE(
+ visitor_.public_reset_packet_->client_address.address().empty());
+
+ // Now test framing boundaries.
+ for (size_t i = 0; i < arraysize(packet); ++i) {
+ string expected_error;
+ DVLOG(1) << "iteration: " << i;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
+ } else if (i < kPublicResetPacketMessageTagOffset) {
+ expected_error = "Unable to read ConnectionId.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
+ } else {
+ expected_error = "Unable to read reset message.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithTrailingJunk) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // trailing junk
+ 'j', 'u', 'n', 'k',
+ };
+
+ string expected_error = "Unable to read reset message.";
+ CheckProcessingFails(packet, arraysize(packet), expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
+}
+
+TEST_P(QuicFramerTest, PublicResetPacketWithClientAddress) {
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte connection_id)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (3) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address: 4.31.198.44:443
+ 0x02, 0x00,
+ 0x04, 0x1F, 0xC6, 0x2C,
+ 0xBB, 0x01,
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.public_reset_packet_.get());
+ EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210),
+ visitor_.public_reset_packet_->public_header.connection_id);
+ EXPECT_TRUE(visitor_.public_reset_packet_->public_header.reset_flag);
+ EXPECT_FALSE(visitor_.public_reset_packet_->public_header.version_flag);
+ EXPECT_EQ(GG_UINT64_C(0xABCDEF0123456789),
+ visitor_.public_reset_packet_->nonce_proof);
+ EXPECT_EQ(GG_UINT64_C(0x123456789ABC),
+ visitor_.public_reset_packet_->rejected_sequence_number);
+ EXPECT_EQ("4.31.198.44",
+ IPAddressToString(visitor_.public_reset_packet_->
+ client_address.address()));
+ EXPECT_EQ(443, visitor_.public_reset_packet_->client_address.port());
+
+ // Now test framing boundaries.
+ for (size_t i = 0; i < arraysize(packet); ++i) {
+ string expected_error;
+ DVLOG(1) << "iteration: " << i;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
+ } else if (i < kPublicResetPacketMessageTagOffset) {
+ expected_error = "Unable to read ConnectionId.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PACKET_HEADER);
+ } else {
+ expected_error = "Unable to read reset message.";
+ CheckProcessingFails(packet, i, expected_error,
+ QUIC_INVALID_PUBLIC_RST_PACKET);
+ }
+ }
+}
+
+TEST_P(QuicFramerTest, VersionNegotiationPacket) {
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x3D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
+ 'Q', '2', '.', '0',
+ };
+
+ QuicFramerPeer::SetIsServer(&framer_, false);
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ ASSERT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.version_negotiation_packet_.get());
+ EXPECT_EQ(2u, visitor_.version_negotiation_packet_->versions.size());
+ EXPECT_EQ(GetParam(), visitor_.version_negotiation_packet_->versions[0]);
+
+ for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_CONNECTION_ID; ++i) {
+ string expected_error;
+ QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER;
+ if (i < kConnectionIdOffset) {
+ expected_error = "Unable to read public flags.";
+ } else if (i < kVersionOffset) {
+ expected_error = "Unable to read ConnectionId.";
+ } else {
+ expected_error = "Unable to read supported version in negotiation.";
+ error_code = QUIC_INVALID_VERSION_NEGOTIATION_PACKET;
+ }
+ CheckProcessingFails(packet, i, expected_error, error_code);
+ }
+}
+
+TEST_P(QuicFramerTest, FecPacket) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (fec group & FEC)
+ 0x06,
+ // first fec protected packet offset
+ 0x01,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion));
+
+ EXPECT_EQ(0u, visitor_.stream_frames_.size());
+ EXPECT_EQ(0u, visitor_.ack_frames_.size());
+ ASSERT_EQ(1, visitor_.fec_count_);
+ const QuicFecData& fec_data = *visitor_.fec_data_[0];
+ EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), fec_data.fec_group);
+ EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy);
+}
+
+TEST_P(QuicFramerTest, BuildPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPaddingFrame padding_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&padding_frame));
+
+ unsigned char packet[kMaxPacketSize] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ uint64 header_size =
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.public_header.sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPaddingFrame padding_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&padding_frame));
+
+ unsigned char packet[kMaxPacketSize] = {
+ // public flags (8 byte connection_id and 4 byte sequence number)
+ 0x2C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ // private flags
+ 0x00,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ uint64 header_size =
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.public_header.sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPaddingFrame padding_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&padding_frame));
+
+ unsigned char packet[kMaxPacketSize] = {
+ // public flags (8 byte connection_id and 2 byte sequence number)
+ 0x1C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A,
+ // private flags
+ 0x00,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ uint64 header_size =
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPaddingFrame padding_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&padding_frame));
+
+ unsigned char packet[kMaxPacketSize] = {
+ // public flags (8 byte connection_id and 1 byte sequence number)
+ 0x0C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC,
+ // private flags
+ 0x00,
+
+ // frame type (padding frame)
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
+ uint64 header_size =
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP);
+ memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1);
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC);
+ header.fec_group = 0;
+
+ QuicStreamFrame stream_frame;
+ stream_frame.stream_id = 0x01020304;
+ stream_frame.fin = true;
+ stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654);
+ stream_frame.data = MakeIOVector("hello world!");
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stream_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacketInFecGroup) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC);
+ header.is_in_fec_group = IN_FEC_GROUP;
+ header.fec_group = GG_UINT64_C(0x77123456789ABC);
+
+ QuicStreamFrame stream_frame;
+ stream_frame.stream_id = 0x01020304;
+ stream_frame.fin = true;
+ stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654);
+ stream_frame.data = MakeIOVector("hello world!");
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stream_frame));
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy, is_in_fec_group)
+ 0x03,
+ // FEC group
+ 0x00,
+ // frame type (stream frame with fin and data length field)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length (since packet is in an FEC group)
+ 0x0C, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = true;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x77123456789ABC);
+ header.fec_group = 0;
+
+ QuicStreamFrame stream_frame;
+ stream_frame.stream_id = 0x01020304;
+ stream_frame.fin = true;
+ stream_frame.offset = GG_UINT64_C(0xBA98FEDC32107654);
+ stream_frame.data = MakeIOVector("hello world!");
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stream_frame));
+
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x3D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicFramerPeer::SetIsServer(&framer_, false);
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) {
+ QuicPacketPublicHeader header;
+ header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.reset_flag = false;
+ header.version_flag = true;
+
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x0D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '0', GetQuicVersionDigitTens(), GetQuicVersionDigitOnes(),
+ };
+
+ QuicVersionVector versions;
+ versions.push_back(GetParam());
+ scoped_ptr<QuicEncryptedPacket> data(
+ framer_.BuildVersionNegotiationPacket(header, versions));
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacketv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ ack_frame.entropy_hash = 0x43;
+ ack_frame.largest_observed = GG_UINT64_C(0x770123456789ABF);
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ ack_frame.missing_packets.insert(
+ GG_UINT64_C(0x770123456789ABE));
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0x43,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildAckFramePacket) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ ack_frame.entropy_hash = 0x43;
+ ack_frame.largest_observed = GG_UINT64_C(0x770123456789ABF);
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ ack_frame.missing_packets.insert(
+ GG_UINT64_C(0x770123456789ABE));
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, not truncated, 6 byte largest observed, 1 byte delta)
+ 0x6C,
+ // entropy hash of all received packets.
+ 0x43,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num received packets.
+ 0x00,
+ // num missing packet ranges
+ 0x01,
+ // missing packet delta
+ 0x01,
+ // 0 more missing packets in range.
+ 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+// TODO(jri): Add test for tuncated packets in which the original ack frame had
+// revived packets. (In both the large and small packet cases below).
+
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacketv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.entropy_hash = 0x43;
+ ack_frame.largest_observed = 2 * 300;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 510 (0x1FE), since only 255 nack ranges can fit.
+ 0xFE, 0x01,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 255 by size of this field).
+ 0xFF,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges x 42 + 3 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameLargePacket) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.entropy_hash = 0x43;
+ ack_frame.largest_observed = 2 * 300;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 510 (0x1FE), since only 255 nack ranges can fit.
+ 0xFE, 0x01,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 255 by size of this field).
+ 0xFF,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges x 42 + 3 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacketv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.entropy_hash = 0x43;
+ ack_frame.largest_observed = 2 * 300;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 12 (0x0C), since only 6 nack ranges can fit.
+ 0x0C, 0x00,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 6 by packet size of 37).
+ 0x06,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildDataPacket(header, frames, 37u).packet);
+ ASSERT_TRUE(data != nullptr);
+ // Expect 1 byte unused since at least 2 bytes are needed to fit more nacks.
+ EXPECT_EQ(36u, data->length());
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildTruncatedAckFrameSmallPacket) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ // This entropy hash is different from what shows up in the packet below,
+ // since entropy is recomputed by the framer on ack truncation (by
+ // TestEntropyCalculator for this test.)
+ ack_frame.entropy_hash = 0x43;
+ ack_frame.largest_observed = 2 * 300;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ for (size_t i = 1; i < 2 * 300; i += 2) {
+ ack_frame.missing_packets.insert(i);
+ }
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (ack frame)
+ // (has nacks, is truncated, 2 byte largest observed, 1 byte delta)
+ 0x74,
+ // entropy hash of all received packets, set to 1 by TestEntropyCalculator
+ // since ack is truncated.
+ 0x01,
+ // 2-byte largest observed packet sequence number.
+ // Expected to be 12 (0x0C), since only 6 nack ranges can fit.
+ 0x0C, 0x00,
+ // Zero delta time.
+ 0x0, 0x0,
+ // num missing packet ranges (limited to 6 by packet size of 37).
+ 0x06,
+ // {missing packet delta, further missing packets in range}
+ // 6 nack ranges
+ 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ // 0 revived packets.
+ 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildDataPacket(header, frames, 37u).packet);
+ ASSERT_TRUE(data != nullptr);
+ // Expect 1 byte unused since at least 2 bytes are needed to fit more nacks.
+ EXPECT_EQ(36u, data->length());
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicCongestionFeedbackFrame congestion_feedback_frame;
+ congestion_feedback_frame.type = kTCP;
+ congestion_feedback_frame.tcp.receive_window = 0x4030;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&congestion_feedback_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (congestion feedback frame)
+ 0x20,
+ // congestion feedback type (TCP)
+ 0x00,
+ // TCP receive window
+ 0x03, 0x04,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildStopWaitingPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x770123456789AA8);
+ header.fec_group = 0;
+
+ QuicStopWaitingFrame stop_waiting_frame;
+ stop_waiting_frame.entropy_hash = 0x14;
+ stop_waiting_frame.least_unacked = GG_UINT64_C(0x770123456789AA0);
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&stop_waiting_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xA8, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (stop waiting frame)
+ 0x06,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0x14,
+ // least packet sequence number awaiting an ack, delta from sequence number.
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicCongestionFeedbackFrame congestion_feedback_frame;
+ congestion_feedback_frame.type =
+ static_cast<CongestionFeedbackType>(kTCP + 1);
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&congestion_feedback_frame));
+
+ scoped_ptr<QuicPacket> data;
+ EXPECT_DFATAL(
+ data.reset(BuildDataPacket(header, frames)),
+ "AppendCongestionFeedbackFrame failed");
+ ASSERT_TRUE(data == nullptr);
+}
+
+TEST_P(QuicFramerTest, BuildRstFramePacketQuic) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicRstStreamFrame rst_frame;
+ rst_frame.stream_id = 0x01020304;
+ rst_frame.error_code = static_cast<QuicRstStreamErrorCode>(0x05060708);
+ rst_frame.error_details = "because I can";
+ rst_frame.byte_offset = 0x0807060504030201;
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00,
+
+ // frame type (rst stream frame)
+ 0x01,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // sent byte offset
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08,
+ // error code
+ 0x08, 0x07, 0x06, 0x05,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&rst_frame));
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildCloseFramePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicConnectionCloseFrame close_frame;
+ close_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+ close_frame.error_details = "because I can";
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&close_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy)
+ 0x01,
+
+ // frame type (connection close frame)
+ 0x02,
+ // error code
+ 0x08, 0x07, 0x06, 0x05,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildGoAwayPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicGoAwayFrame goaway_frame;
+ goaway_frame.error_code = static_cast<QuicErrorCode>(0x05060708);
+ goaway_frame.last_good_stream_id = 0x01020304;
+ goaway_frame.reason_phrase = "because I can";
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&goaway_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (go away frame)
+ 0x03,
+ // error code
+ 0x08, 0x07, 0x06, 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // error details length
+ 0x0d, 0x00,
+ // error details
+ 'b', 'e', 'c', 'a',
+ 'u', 's', 'e', ' ',
+ 'I', ' ', 'c', 'a',
+ 'n',
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildWindowUpdatePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicWindowUpdateFrame window_update_frame;
+ window_update_frame.stream_id = 0x01020304;
+ window_update_frame.byte_offset = 0x1122334455667788;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&window_update_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (window update frame)
+ 0x04,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // byte offset
+ 0x88, 0x77, 0x66, 0x55,
+ 0x44, 0x33, 0x22, 0x11,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildBlockedPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicBlockedFrame blocked_frame;
+ blocked_frame.stream_id = 0x01020304;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&blocked_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (blocked frame)
+ 0x05,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ };
+
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPingPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicPingFrame ping_frame;
+
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ping_frame));
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags(entropy)
+ 0x01,
+
+ // frame type (ping frame)
+ 0x07,
+ };
+
+ if (version_ >= QUIC_VERSION_18) {
+ scoped_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet", data->data(),
+ data->length(), AsChars(packet),
+ arraysize(packet));
+ } else {
+ string expected_error =
+ "Attempt to add a PingFrame in " + QuicVersionToString(version_);
+ EXPECT_DFATAL(BuildDataPacket(header, frames),
+ expected_error);
+ return;
+ }
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacket) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ reset_packet.public_header.reset_flag = true;
+ reset_packet.public_header.version_flag = false;
+ reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
+ reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
+
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (2) + padding
+ 0x02, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ };
+
+ scoped_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildPublicResetPacketWithClientAddress) {
+ QuicPublicResetPacket reset_packet;
+ reset_packet.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ reset_packet.public_header.reset_flag = true;
+ reset_packet.public_header.version_flag = false;
+ reset_packet.rejected_sequence_number = GG_UINT64_C(0x123456789ABC);
+ reset_packet.nonce_proof = GG_UINT64_C(0xABCDEF0123456789);
+ reset_packet.client_address = IPEndPoint(Loopback4(), 0x1234);
+
+ unsigned char packet[] = {
+ // public flags (public reset, 8 byte ConnectionId)
+ 0x0E,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // message tag (kPRST)
+ 'P', 'R', 'S', 'T',
+ // num_entries (3) + padding
+ 0x03, 0x00, 0x00, 0x00,
+ // tag kRNON
+ 'R', 'N', 'O', 'N',
+ // end offset 8
+ 0x08, 0x00, 0x00, 0x00,
+ // tag kRSEQ
+ 'R', 'S', 'E', 'Q',
+ // end offset 16
+ 0x10, 0x00, 0x00, 0x00,
+ // tag kCADR
+ 'C', 'A', 'D', 'R',
+ // end offset 24
+ 0x18, 0x00, 0x00, 0x00,
+ // nonce proof
+ 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0xEF, 0xCD, 0xAB,
+ // rejected sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12, 0x00, 0x00,
+ // client address
+ 0x02, 0x00,
+ 0x7F, 0x00, 0x00, 0x01,
+ 0x34, 0x12,
+ };
+
+ scoped_ptr<QuicEncryptedPacket> data(
+ framer_.BuildPublicResetPacket(reset_packet));
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, BuildFecPacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = true;
+ header.entropy_flag = true;
+ header.packet_sequence_number = (GG_UINT64_C(0x123456789ABC));
+ header.is_in_fec_group = IN_FEC_GROUP;
+ header.fec_group = GG_UINT64_C(0x123456789ABB);;
+
+ QuicFecData fec_data;
+ fec_data.fec_group = 1;
+ fec_data.redundancy = "abcdefghijklmnop";
+
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (entropy & fec group & fec packet)
+ 0x07,
+ // first fec protected packet offset
+ 0x01,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ scoped_ptr<QuicPacket> data(
+ framer_.BuildFecPacket(header, fec_data).packet);
+ ASSERT_TRUE(data != nullptr);
+
+ test::CompareCharArraysWithHexError("constructed packet",
+ data->data(), data->length(),
+ AsChars(packet), arraysize(packet));
+}
+
+TEST_P(QuicFramerTest, EncryptPacket) {
+ QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (fec group & fec packet)
+ 0x06,
+ // first fec protected packet offset
+ 0x01,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ scoped_ptr<QuicPacket> raw(
+ QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false,
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER));
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw));
+
+ ASSERT_TRUE(encrypted.get() != nullptr);
+ EXPECT_TRUE(CheckEncryption(sequence_number, raw.get()));
+}
+
+TEST_P(QuicFramerTest, EncryptPacketWithVersionFlag) {
+ QuicPacketSequenceNumber sequence_number = GG_UINT64_C(0x123456789ABC);
+ unsigned char packet[] = {
+ // public flags (version, 8 byte connection_id)
+ 0x3D,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // version tag
+ 'Q', '.', '1', '0',
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (fec group & fec flags)
+ 0x06,
+ // first fec protected packet offset
+ 0x01,
+
+ // redundancy
+ 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p',
+ };
+
+ scoped_ptr<QuicPacket> raw(
+ QuicPacket::NewDataPacket(AsChars(packet), arraysize(packet), false,
+ PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER));
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw));
+
+ ASSERT_TRUE(encrypted.get() != nullptr);
+ EXPECT_TRUE(CheckEncryption(sequence_number, raw.get()));
+}
+
+TEST_P(QuicFramerTest, AckTruncationLargePacket) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, kMaxPacketSize).packet);
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.is_truncated);
+ EXPECT_EQ(510u, processed_ack_frame.largest_observed);
+ ASSERT_EQ(255u, processed_ack_frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.missing_packets.rbegin();
+ EXPECT_EQ(509u, *last_missing_iter);
+}
+
+TEST_P(QuicFramerTest, AckTruncationSmallPacketv22) {
+ if (version_ > QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, 500).packet);
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.is_truncated);
+ EXPECT_EQ(476u, processed_ack_frame.largest_observed);
+ ASSERT_EQ(238u, processed_ack_frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.missing_packets.rbegin();
+ EXPECT_EQ(475u, *last_missing_iter);
+}
+
+
+TEST_P(QuicFramerTest, AckTruncationSmallPacket) {
+ if (version_ <= QUIC_VERSION_22) {
+ return;
+ }
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = false;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ // Create a packet with just the ack.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(300, 0u);
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
+
+ // Build an ack packet with truncation due to limit in number of nack ranges.
+ scoped_ptr<QuicPacket> raw_ack_packet(
+ framer_.BuildDataPacket(header, frames, 500).packet);
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+ ASSERT_EQ(1u, visitor_.ack_frames_.size());
+ QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0];
+ EXPECT_TRUE(processed_ack_frame.is_truncated);
+ EXPECT_EQ(476u, processed_ack_frame.largest_observed);
+ ASSERT_EQ(238u, processed_ack_frame.missing_packets.size());
+ SequenceNumberSet::const_iterator missing_iter =
+ processed_ack_frame.missing_packets.begin();
+ EXPECT_EQ(1u, *missing_iter);
+ SequenceNumberSet::const_reverse_iterator last_missing_iter =
+ processed_ack_frame.missing_packets.rbegin();
+ EXPECT_EQ(475u, *last_missing_iter);
+}
+
+TEST_P(QuicFramerTest, CleanTruncation) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210);
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.fec_flag = false;
+ header.entropy_flag = true;
+ header.packet_sequence_number = GG_UINT64_C(0x123456789ABC);
+ header.fec_group = 0;
+
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 201;
+ for (uint64 i = 1; i < ack_frame.largest_observed; ++i) {
+ ack_frame.missing_packets.insert(i);
+ }
+
+ // Create a packet with just the ack.
+ QuicFrame frame;
+ frame.type = ACK_FRAME;
+ frame.ack_frame = &ack_frame;
+ QuicFrames frames;
+ frames.push_back(frame);
+
+ scoped_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames));
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+
+ scoped_ptr<QuicEncryptedPacket> ack_packet(
+ framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number,
+ *raw_ack_packet));
+
+ // Now make sure we can turn our ack packet back into an ack frame.
+ ASSERT_TRUE(framer_.ProcessPacket(*ack_packet));
+
+ // Test for clean truncation of the ack by comparing the length of the
+ // original packets to the re-serialized packets.
+ frames.clear();
+ frame.type = ACK_FRAME;
+ frame.ack_frame = visitor_.ack_frames_[0];
+ frames.push_back(frame);
+
+ size_t original_raw_length = raw_ack_packet->length();
+ raw_ack_packet.reset(BuildDataPacket(header, frames));
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+ EXPECT_EQ(original_raw_length, raw_ack_packet->length());
+ ASSERT_TRUE(raw_ack_packet != nullptr);
+}
+
+TEST_P(QuicFramerTest, EntropyFlagTest) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (Entropy)
+ 0x01,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash);
+ EXPECT_FALSE(visitor_.header_->fec_flag);
+};
+
+TEST_P(QuicFramerTest, FecEntropyTest) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags (Entropy & fec group & FEC)
+ 0x07,
+ // first fec protected packet offset
+ 0xFF,
+
+ // frame type (stream frame with fin and no length)
+ 0xDF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+ };
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(visitor_.header_->fec_flag);
+ EXPECT_TRUE(visitor_.header_->entropy_flag);
+ EXPECT_EQ(1 << 4, visitor_.header_->entropy_hash);
+};
+
+TEST_P(QuicFramerTest, StopPacketProcessing) {
+ unsigned char packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // Entropy
+ 0x01,
+
+ // frame type (stream frame with fin)
+ 0xFF,
+ // stream id
+ 0x04, 0x03, 0x02, 0x01,
+ // offset
+ 0x54, 0x76, 0x10, 0x32,
+ 0xDC, 0xFE, 0x98, 0xBA,
+ // data length
+ 0x0c, 0x00,
+ // data
+ 'h', 'e', 'l', 'l',
+ 'o', ' ', 'w', 'o',
+ 'r', 'l', 'd', '!',
+
+ // frame type (ack frame)
+ 0x40,
+ // entropy hash of sent packets till least awaiting - 1.
+ 0x14,
+ // least packet sequence number awaiting an ack
+ 0xA0, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // entropy hash of all received packets.
+ 0x43,
+ // largest observed packet sequence number
+ 0xBF, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // num missing packets
+ 0x01,
+ // missing packet
+ 0xBE, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ };
+
+ MockFramerVisitor visitor;
+ framer_.set_visitor(&visitor);
+ EXPECT_CALL(visitor, OnPacket());
+ EXPECT_CALL(visitor, OnPacketHeader(_));
+ EXPECT_CALL(visitor, OnStreamFrame(_)).WillOnce(Return(false));
+ EXPECT_CALL(visitor, OnAckFrame(_)).Times(0);
+ EXPECT_CALL(visitor, OnPacketComplete());
+ EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)).WillOnce(Return(true));
+
+ QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(QUIC_NO_ERROR, framer_.error());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc
new file mode 100644
index 0000000..35dac3f
--- /dev/null
+++ b/net/quic/quic_headers_stream.cc
@@ -0,0 +1,276 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_headers_stream.h"
+
+#include "net/quic/quic_session.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+const QuicStreamId kInvalidStreamId = 0;
+
+} // namespace
+
+// A SpdyFramer visitor which passed SYN_STREAM and SYN_REPLY frames to
+// the QuicDataStream, and closes the connection if any unexpected frames
+// are received.
+class QuicHeadersStream::SpdyFramerVisitor
+ : public SpdyFramerVisitorInterface,
+ public SpdyFramerDebugVisitorInterface {
+ public:
+ explicit SpdyFramerVisitor(QuicHeadersStream* stream) : stream_(stream) {}
+
+ // SpdyFramerVisitorInterface implementation
+ virtual void OnSynStream(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return;
+ }
+
+ if (associated_stream_id != 0) {
+ CloseConnection("associated_stream_id != 0");
+ return;
+ }
+
+ if (unidirectional != 0) {
+ CloseConnection("unidirectional != 0");
+ return;
+ }
+
+ stream_->OnSynStream(stream_id, priority, fin);
+ }
+
+ virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return;
+ }
+
+ stream_->OnSynReply(stream_id, fin);
+ }
+
+ virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) OVERRIDE {
+ if (!stream_->IsConnected()) {
+ return false;
+ }
+ stream_->OnControlFrameHeaderData(stream_id, header_data, len);
+ return true;
+ }
+
+ virtual void OnStreamFrameData(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin) OVERRIDE {
+ if (fin && len == 0) {
+ // The framer invokes OnStreamFrameData with zero-length data and
+ // fin = true after processing a SYN_STREAM or SYN_REPLY frame
+ // that had the fin bit set.
+ return;
+ }
+ CloseConnection("SPDY DATA frame received.");
+ }
+
+ virtual void OnError(SpdyFramer* framer) OVERRIDE {
+ CloseConnection("SPDY framing error.");
+ }
+
+ virtual void OnDataFrameHeader(SpdyStreamId stream_id,
+ size_t length,
+ bool fin) OVERRIDE {
+ CloseConnection("SPDY DATA frame received.");
+ }
+
+ virtual void OnRstStream(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status) OVERRIDE {
+ CloseConnection("SPDY RST_STREAM frame received.");
+ }
+
+ virtual void OnSetting(SpdySettingsIds id,
+ uint8 flags,
+ uint32 value) OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnSettingsAck() OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnSettingsEnd() OVERRIDE {
+ CloseConnection("SPDY SETTINGS frame received.");
+ }
+
+ virtual void OnPing(SpdyPingId unique_id, bool is_ack) OVERRIDE {
+ CloseConnection("SPDY PING frame received.");
+ }
+
+ virtual void OnGoAway(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status) OVERRIDE {
+ CloseConnection("SPDY GOAWAY frame received.");
+ }
+
+ virtual void OnHeaders(SpdyStreamId stream_id, bool fin, bool end) OVERRIDE {
+ CloseConnection("SPDY HEADERS frame received.");
+ }
+
+ virtual void OnWindowUpdate(SpdyStreamId stream_id,
+ uint32 delta_window_size) OVERRIDE {
+ CloseConnection("SPDY WINDOW_UPDATE frame received.");
+ }
+
+ virtual void OnPushPromise(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end) OVERRIDE {
+ LOG(DFATAL) << "PUSH_PROMISE frame received from a SPDY/3 framer";
+ CloseConnection("SPDY PUSH_PROMISE frame received.");
+ }
+
+ virtual void OnContinuation(SpdyStreamId stream_id, bool end) OVERRIDE {
+ CloseConnection("SPDY CONTINUATION frame received.");
+ }
+
+ virtual bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) OVERRIDE {
+ CloseConnection("SPDY unknown frame received.");
+ return false;
+ }
+
+ // SpdyFramerDebugVisitorInterface implementation
+ virtual void OnSendCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t payload_len,
+ size_t frame_len) OVERRIDE {}
+
+ virtual void OnReceiveCompressedFrame(SpdyStreamId stream_id,
+ SpdyFrameType type,
+ size_t frame_len) OVERRIDE {
+ if (stream_->IsConnected()) {
+ stream_->OnCompressedFrameSize(frame_len);
+ }
+ }
+
+ private:
+ void CloseConnection(const string& details) {
+ if (stream_->IsConnected()) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA, details);
+ }
+ }
+
+ private:
+ QuicHeadersStream* stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(SpdyFramerVisitor);
+};
+
+QuicHeadersStream::QuicHeadersStream(QuicSession* session)
+ : ReliableQuicStream(kHeadersStreamId, session),
+ stream_id_(kInvalidStreamId),
+ fin_(false),
+ frame_len_(0),
+ spdy_framer_(SPDY3),
+ spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
+ spdy_framer_.set_visitor(spdy_framer_visitor_.get());
+ spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+ if (version() < QUIC_VERSION_21) {
+ // Prior to QUIC_VERSION_21 the headers stream is not subject to any flow
+ // control.
+ DisableFlowControl();
+ }
+ // The headers stream is exempt from connection level flow control.
+ DisableConnectionFlowControlForThisStream();
+}
+
+QuicHeadersStream::~QuicHeadersStream() {}
+
+size_t QuicHeadersStream::WriteHeaders(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ scoped_ptr<SpdySerializedFrame> frame;
+ if (session()->is_server()) {
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers);
+ syn_reply.set_fin(fin);
+ frame.reset(spdy_framer_.SerializeFrame(syn_reply));
+ } else {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers);
+ syn_stream.set_fin(fin);
+ frame.reset(spdy_framer_.SerializeFrame(syn_stream));
+ }
+ WriteOrBufferData(StringPiece(frame->data(), frame->size()), false,
+ ack_notifier_delegate);
+ return frame->size();
+}
+
+uint32 QuicHeadersStream::ProcessRawData(const char* data,
+ uint32 data_len) {
+ return spdy_framer_.ProcessInput(data, data_len);
+}
+
+QuicPriority QuicHeadersStream::EffectivePriority() const { return 0; }
+
+void QuicHeadersStream::OnSynStream(SpdyStreamId stream_id,
+ SpdyPriority priority,
+ bool fin) {
+ if (!session()->is_server()) {
+ CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SYN_STREAM frame received at the client");
+ return;
+ }
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+ session()->OnStreamHeadersPriority(stream_id, priority);
+}
+
+void QuicHeadersStream::OnSynReply(SpdyStreamId stream_id, bool fin) {
+ if (session()->is_server()) {
+ CloseConnectionWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SYN_REPLY frame received at the server");
+ return;
+ }
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ stream_id_ = stream_id;
+ fin_ = fin;
+}
+
+void QuicHeadersStream::OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len) {
+ DCHECK_EQ(stream_id_, stream_id);
+ if (len == 0) {
+ DCHECK_NE(0u, stream_id_);
+ DCHECK_NE(0u, frame_len_);
+ session()->OnStreamHeadersComplete(stream_id_, fin_, frame_len_);
+ // Reset state for the next frame.
+ stream_id_ = kInvalidStreamId;
+ fin_ = false;
+ frame_len_ = 0;
+ } else {
+ session()->OnStreamHeaders(stream_id_, StringPiece(header_data, len));
+ }
+}
+
+void QuicHeadersStream::OnCompressedFrameSize(size_t frame_len) {
+ DCHECK_EQ(kInvalidStreamId, stream_id_);
+ DCHECK_EQ(0u, frame_len_);
+ frame_len_ = frame_len;
+}
+
+bool QuicHeadersStream::IsConnected() {
+ return session()->connection()->connected();
+}
+
+} // namespace net
diff --git a/net/quic/quic_headers_stream.h b/net/quic/quic_headers_stream.h
new file mode 100644
index 0000000..c3ccbda
--- /dev/null
+++ b/net/quic/quic_headers_stream.h
@@ -0,0 +1,82 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_HEADERS_STREAM_H_
+#define NET_QUIC_QUIC_HEADERS_STREAM_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+// Headers in QUIC are sent as SPDY SYN_STREAM or SYN_REPLY frames
+// over a reserved reliable stream with the id 2. Each endpoint (client
+// and server) will allocate an instance of QuicHeadersStream to send
+// and receive headers.
+class NET_EXPORT_PRIVATE QuicHeadersStream : public ReliableQuicStream {
+ public:
+ explicit QuicHeadersStream(QuicSession* session);
+ virtual ~QuicHeadersStream();
+
+ // Writes |headers| for |stream_id| in a SYN_STREAM or SYN_REPLY
+ // frame to the peer. If |fin| is true, the fin flag will be set on
+ // the SPDY frame. Returns the size, in bytes, of the resulting
+ // SPDY frame.
+ size_t WriteHeaders(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // ReliableQuicStream implementation
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE;
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ private:
+ class SpdyFramerVisitor;
+
+ // The following methods are called by the SimpleVisitor.
+
+ // Called when a SYN_STREAM frame has been received.
+ void OnSynStream(SpdyStreamId stream_id,
+ SpdyPriority priority,
+ bool fin);
+
+ // Called when a SYN_REPLY frame been received.
+ void OnSynReply(SpdyStreamId stream_id, bool fin);
+
+ // Called when a chunk of header data is available. This is called
+ // after OnSynStream, or OnSynReply.
+ // |stream_id| The stream receiving the header data.
+ // |header_data| A buffer containing the header data chunk received.
+ // |len| The length of the header data buffer. A length of zero indicates
+ // that the header data block has been completely sent.
+ void OnControlFrameHeaderData(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len);
+
+ // Called when the size of the compressed frame payload is available.
+ void OnCompressedFrameSize(size_t frame_len);
+
+ // Returns true if the session is still connected.
+ bool IsConnected();
+
+ // Data about the stream whose headers are being processed.
+ QuicStreamId stream_id_;
+ bool fin_;
+ size_t frame_len_;
+
+ SpdyFramer spdy_framer_;
+ scoped_ptr<SpdyFramerVisitor> spdy_framer_visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicHeadersStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_HEADERS_STREAM_H_
diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc
new file mode 100644
index 0000000..32d22fe
--- /dev/null
+++ b/net/quic/quic_headers_stream_test.cc
@@ -0,0 +1,340 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_headers_stream.h"
+
+#include "net/quic/quic_utils.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using testing::Invoke;
+using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockVisitor : public SpdyFramerVisitorInterface {
+ public:
+ MOCK_METHOD1(OnError, void(SpdyFramer* framer));
+ MOCK_METHOD3(OnDataFrameHeader, void(SpdyStreamId stream_id,
+ size_t length,
+ bool fin));
+ MOCK_METHOD4(OnStreamFrameData, void(SpdyStreamId stream_id,
+ const char* data,
+ size_t len,
+ bool fin));
+ MOCK_METHOD3(OnControlFrameHeaderData, bool(SpdyStreamId stream_id,
+ const char* header_data,
+ size_t len));
+ MOCK_METHOD5(OnSynStream, void(SpdyStreamId stream_id,
+ SpdyStreamId associated_stream_id,
+ SpdyPriority priority,
+ bool fin,
+ bool unidirectional));
+ MOCK_METHOD2(OnSynReply, void(SpdyStreamId stream_id, bool fin));
+ MOCK_METHOD2(OnRstStream, void(SpdyStreamId stream_id,
+ SpdyRstStreamStatus status));
+ MOCK_METHOD1(OnSettings, void(bool clear_persisted));
+ MOCK_METHOD3(OnSetting, void(SpdySettingsIds id, uint8 flags, uint32 value));
+ MOCK_METHOD0(OnSettingsAck, void());
+ MOCK_METHOD0(OnSettingsEnd, void());
+ MOCK_METHOD2(OnPing, void(SpdyPingId unique_id, bool is_ack));
+ MOCK_METHOD2(OnGoAway, void(SpdyStreamId last_accepted_stream_id,
+ SpdyGoAwayStatus status));
+ MOCK_METHOD3(OnHeaders, void(SpdyStreamId stream_id, bool fin, bool end));
+ MOCK_METHOD2(OnWindowUpdate, void(SpdyStreamId stream_id,
+ uint32 delta_window_size));
+ MOCK_METHOD2(OnCredentialFrameData, bool(const char* credential_data,
+ size_t len));
+ MOCK_METHOD1(OnBlocked, void(SpdyStreamId stream_id));
+ MOCK_METHOD3(OnPushPromise, void(SpdyStreamId stream_id,
+ SpdyStreamId promised_stream_id,
+ bool end));
+ MOCK_METHOD2(OnContinuation, void(SpdyStreamId stream_id, bool end));
+ MOCK_METHOD6(OnAltSvc, void(SpdyStreamId stream_id,
+ uint32 max_age,
+ uint16 port,
+ StringPiece protocol_id,
+ StringPiece host,
+ StringPiece origin));
+ MOCK_METHOD2(OnUnknownFrame, bool(SpdyStreamId stream_id, int frame_type));
+};
+
+class QuicHeadersStreamTest : public ::testing::TestWithParam<bool> {
+ public:
+ static QuicVersionVector GetVersions() {
+ QuicVersionVector versions;
+ versions.push_back(QuicVersionMax());
+ return versions;
+ }
+
+ QuicHeadersStreamTest()
+ : connection_(new StrictMock<MockConnection>(is_server(), GetVersions())),
+ session_(connection_),
+ headers_stream_(QuicSessionPeer::GetHeadersStream(&session_)),
+ body_("hello world"),
+ framer_(SPDY3) {
+ headers_[":version"] = "HTTP/1.1";
+ headers_[":status"] = "200 Ok";
+ headers_["content-length"] = "11";
+ framer_.set_visitor(&visitor_);
+ EXPECT_EQ(QuicVersionMax(), session_.connection()->version());
+ EXPECT_TRUE(headers_stream_ != nullptr);
+ }
+
+ QuicConsumedData SaveIov(const IOVector& data) {
+ const iovec* iov = data.iovec();
+ int count = data.Capacity();
+ for (int i = 0 ; i < count; ++i) {
+ saved_data_.append(static_cast<char*>(iov[i].iov_base), iov[i].iov_len);
+ }
+ return QuicConsumedData(saved_data_.length(), false);
+ }
+
+ bool SaveHeaderData(const char* data, int len) {
+ saved_header_data_.append(data, len);
+ return true;
+ }
+
+ void SaveHeaderDataStringPiece(StringPiece data) {
+ saved_header_data_.append(data.data(), data.length());
+ }
+
+ void WriteHeadersAndExpectSynStream(QuicStreamId stream_id,
+ bool fin,
+ QuicPriority priority) {
+ WriteHeadersAndCheckData(stream_id, fin, priority, SYN_STREAM);
+ }
+
+ void WriteHeadersAndExpectSynReply(QuicStreamId stream_id,
+ bool fin) {
+ WriteHeadersAndCheckData(stream_id, fin, 0, SYN_REPLY);
+ }
+
+ void WriteHeadersAndCheckData(QuicStreamId stream_id,
+ bool fin,
+ QuicPriority priority,
+ SpdyFrameType type) {
+ // Write the headers and capture the outgoing data
+ EXPECT_CALL(session_, WritevData(kHeadersStreamId, _, _, false, _, nullptr))
+ .WillOnce(WithArgs<1>(Invoke(this, &QuicHeadersStreamTest::SaveIov)));
+ headers_stream_->WriteHeaders(stream_id, headers_, fin, nullptr);
+
+ // Parse the outgoing data and check that it matches was was written.
+ if (type == SYN_STREAM) {
+ EXPECT_CALL(visitor_, OnSynStream(stream_id, kNoAssociatedStream, 0,
+ // priority,
+ fin, kNotUnidirectional));
+ } else {
+ EXPECT_CALL(visitor_, OnSynReply(stream_id, fin));
+ }
+ EXPECT_CALL(visitor_, OnControlFrameHeaderData(stream_id, _, _))
+ .WillRepeatedly(WithArgs<1, 2>(
+ Invoke(this, &QuicHeadersStreamTest::SaveHeaderData)));
+ if (fin) {
+ EXPECT_CALL(visitor_, OnStreamFrameData(stream_id, nullptr, 0, true));
+ }
+ framer_.ProcessInput(saved_data_.data(), saved_data_.length());
+ EXPECT_FALSE(framer_.HasError()) << framer_.error_code();
+
+ CheckHeaders();
+ saved_data_.clear();
+ }
+
+ void CheckHeaders() {
+ SpdyHeaderBlock headers;
+ EXPECT_EQ(saved_header_data_.length(),
+ framer_.ParseHeaderBlockInBuffer(saved_header_data_.data(),
+ saved_header_data_.length(),
+ &headers));
+ EXPECT_EQ(headers_, headers);
+ saved_header_data_.clear();
+ }
+
+ bool is_server() {
+ return GetParam();
+ }
+
+ void CloseConnection() {
+ QuicConnectionPeer::CloseConnection(connection_);
+ }
+
+ static const bool kNotUnidirectional = false;
+ static const bool kNoAssociatedStream = false;
+
+ StrictMock<MockConnection>* connection_;
+ StrictMock<MockSession> session_;
+ QuicHeadersStream* headers_stream_;
+ SpdyHeaderBlock headers_;
+ string body_;
+ string saved_data_;
+ string saved_header_data_;
+ SpdyFramer framer_;
+ StrictMock<MockVisitor> visitor_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicHeadersStreamTest, testing::Bool());
+
+TEST_P(QuicHeadersStreamTest, StreamId) {
+ EXPECT_EQ(3u, headers_stream_->id());
+}
+
+TEST_P(QuicHeadersStreamTest, EffectivePriority) {
+ EXPECT_EQ(0u, headers_stream_->EffectivePriority());
+}
+
+TEST_P(QuicHeadersStreamTest, WriteHeaders) {
+ for (QuicStreamId stream_id = kClientDataStreamId1;
+ stream_id < kClientDataStreamId3; stream_id += 2) {
+ for (int count = 0; count < 2; ++count) {
+ bool fin = (count == 0);
+ if (is_server()) {
+ WriteHeadersAndExpectSynReply(stream_id, fin);
+ } else {
+ for (QuicPriority priority = 0; priority < 7; ++priority) {
+ WriteHeadersAndExpectSynStream(stream_id, fin, priority);
+ }
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessRawData) {
+ for (QuicStreamId stream_id = kClientDataStreamId1;
+ stream_id < kClientDataStreamId3; stream_id += 2) {
+ for (int count = 0; count < 2; ++count) {
+ bool fin = (count == 0);
+ for (QuicPriority priority = 0; priority < 7; ++priority) {
+ // Replace with "WriteHeadersAndSaveData"
+ scoped_ptr<SpdySerializedFrame> frame;
+ if (is_server()) {
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers_);
+ syn_stream.set_fin(fin);
+ frame.reset(framer_.SerializeSynStream(syn_stream));
+ EXPECT_CALL(session_, OnStreamHeadersPriority(stream_id, 0));
+ } else {
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers_);
+ syn_reply.set_fin(fin);
+ frame.reset(framer_.SerializeSynReply(syn_reply));
+ }
+ EXPECT_CALL(session_, OnStreamHeaders(stream_id, _))
+ .WillRepeatedly(WithArgs<1>(
+ Invoke(this,
+ &QuicHeadersStreamTest::SaveHeaderDataStringPiece)));
+ EXPECT_CALL(session_,
+ OnStreamHeadersComplete(stream_id, fin, frame->size()));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+
+ CheckHeaders();
+ }
+ }
+ }
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyDataFrame) {
+ SpdyDataIR data(2, "");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY DATA frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyRstStreamFrame) {
+ SpdyRstStreamIR data(2, RST_STREAM_PROTOCOL_ERROR, "");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY RST_STREAM frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdySettingsFrame) {
+ SpdySettingsIR data;
+ data.AddSetting(SETTINGS_UPLOAD_BANDWIDTH, true, true, 0);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY SETTINGS frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyPingFrame) {
+ SpdyPingIR data(1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY PING frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyGoAwayFrame) {
+ SpdyGoAwayIR data(1, GOAWAY_PROTOCOL_ERROR, "go away");
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY GOAWAY frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyHeadersFrame) {
+ SpdyHeadersIR data(1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY HEADERS frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) {
+ SpdyWindowUpdateIR data(1, 1);
+ scoped_ptr<SpdySerializedFrame> frame(framer_.SerializeFrame(data));
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(
+ QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY WINDOW_UPDATE frame received."))
+ .WillOnce(InvokeWithoutArgs(this,
+ &QuicHeadersStreamTest::CloseConnection));
+ headers_stream_->ProcessRawData(frame->data(), frame->size());
+}
+
+TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) {
+ if (connection_->version() < QUIC_VERSION_21) {
+ EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled());
+ } else {
+ EXPECT_TRUE(headers_stream_->flow_controller()->IsEnabled());
+ }
+ EXPECT_FALSE(ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl(
+ headers_stream_));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
new file mode 100644
index 0000000..4f8e4e4
--- /dev/null
+++ b/net/quic/quic_http_stream.cc
@@ -0,0 +1,565 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_stream.h"
+
+#include "base/callback_helpers.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/stringprintf.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_util.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_reliable_client_stream.h"
+#include "net/quic/quic_utils.h"
+#include "net/socket/next_proto.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_http_utils.h"
+#include "net/ssl/ssl_info.h"
+
+namespace net {
+
+static const size_t kHeaderBufInitialSize = 4096;
+
+QuicHttpStream::QuicHttpStream(const base::WeakPtr<QuicClientSession>& session)
+ : next_state_(STATE_NONE),
+ session_(session),
+ session_error_(OK),
+ was_handshake_confirmed_(session->IsCryptoHandshakeConfirmed()),
+ stream_(nullptr),
+ request_info_(nullptr),
+ request_body_stream_(nullptr),
+ priority_(MINIMUM_PRIORITY),
+ response_info_(nullptr),
+ response_status_(OK),
+ response_headers_received_(false),
+ read_buf_(new GrowableIOBuffer()),
+ closed_stream_received_bytes_(0),
+ user_buffer_len_(0),
+ weak_factory_(this) {
+ DCHECK(session_);
+ session_->AddObserver(this);
+}
+
+QuicHttpStream::~QuicHttpStream() {
+ Close(false);
+ if (session_)
+ session_->RemoveObserver(this);
+}
+
+int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info,
+ RequestPriority priority,
+ const BoundNetLog& stream_net_log,
+ const CompletionCallback& callback) {
+ DCHECK(!stream_);
+ if (!session_)
+ return was_handshake_confirmed_ ? ERR_CONNECTION_CLOSED :
+ ERR_QUIC_HANDSHAKE_FAILED;
+
+ if (request_info->url.SchemeIsSecure()) {
+ SSLInfo ssl_info;
+ bool secure_session =
+ session_->GetSSLInfo(&ssl_info) && ssl_info.cert.get();
+ UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.SecureResourceSecureSession",
+ secure_session);
+ if (!secure_session)
+ return ERR_REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC;
+ }
+
+ stream_net_log_ = stream_net_log;
+ request_info_ = request_info;
+ request_time_ = base::Time::Now();
+ priority_ = priority;
+
+ int rv = stream_request_.StartRequest(
+ session_, &stream_, base::Bind(&QuicHttpStream::OnStreamReady,
+ weak_factory_.GetWeakPtr()));
+ if (rv == ERR_IO_PENDING) {
+ callback_ = callback;
+ } else if (rv == OK) {
+ stream_->SetDelegate(this);
+ } else if (!was_handshake_confirmed_) {
+ rv = ERR_QUIC_HANDSHAKE_FAILED;
+ }
+
+ return rv;
+}
+
+void QuicHttpStream::OnStreamReady(int rv) {
+ DCHECK(rv == OK || !stream_);
+ if (rv == OK) {
+ stream_->SetDelegate(this);
+ } else if (!was_handshake_confirmed_) {
+ rv = ERR_QUIC_HANDSHAKE_FAILED;
+ }
+
+ ResetAndReturn(&callback_).Run(rv);
+}
+
+int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers,
+ HttpResponseInfo* response,
+ const CompletionCallback& callback) {
+ CHECK(!request_body_stream_);
+ CHECK(!response_info_);
+ CHECK(!callback.is_null());
+ CHECK(response);
+
+ if (!stream_) {
+ return ERR_CONNECTION_CLOSED;
+ }
+
+ QuicPriority priority = ConvertRequestPriorityToQuicPriority(priority_);
+ stream_->set_priority(priority);
+ // Store the serialized request headers.
+ CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers,
+ SPDY3, /*direct=*/true, &request_headers_);
+
+ // Store the request body.
+ request_body_stream_ = request_info_->upload_data_stream;
+ if (request_body_stream_) {
+ // TODO(rch): Can we be more precise about when to allocate
+ // raw_request_body_buf_. Removed the following check. DoReadRequestBody()
+ // was being called even if we didn't yet allocate raw_request_body_buf_.
+ // && (request_body_stream_->size() ||
+ // request_body_stream_->is_chunked()))
+ // Use 10 packets as the body buffer size to give enough space to
+ // help ensure we don't often send out partial packets.
+ raw_request_body_buf_ = new IOBufferWithSize(10 * kMaxPacketSize);
+ // The request body buffer is empty at first.
+ request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), 0);
+ }
+
+ // Store the response info.
+ response_info_ = response;
+
+ next_state_ = STATE_SEND_HEADERS;
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ callback_ = callback;
+
+ return rv > 0 ? OK : rv;
+}
+
+UploadProgress QuicHttpStream::GetUploadProgress() const {
+ if (!request_body_stream_)
+ return UploadProgress();
+
+ return UploadProgress(request_body_stream_->position(),
+ request_body_stream_->size());
+}
+
+int QuicHttpStream::ReadResponseHeaders(const CompletionCallback& callback) {
+ CHECK(!callback.is_null());
+
+ if (stream_ == nullptr)
+ return response_status_;
+
+ // Check if we already have the response headers. If so, return synchronously.
+ if (response_headers_received_)
+ return OK;
+
+ // Still waiting for the response, return IO_PENDING.
+ CHECK(callback_.is_null());
+ callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+int QuicHttpStream::ReadResponseBody(
+ IOBuffer* buf, int buf_len, const CompletionCallback& callback) {
+ CHECK(buf);
+ CHECK(buf_len);
+ CHECK(!callback.is_null());
+
+ // If we have data buffered, complete the IO immediately.
+ if (!response_body_.empty()) {
+ int bytes_read = 0;
+ while (!response_body_.empty() && buf_len > 0) {
+ scoped_refptr<IOBufferWithSize> data = response_body_.front();
+ const int bytes_to_copy = std::min(buf_len, data->size());
+ memcpy(&(buf->data()[bytes_read]), data->data(), bytes_to_copy);
+ buf_len -= bytes_to_copy;
+ if (bytes_to_copy == data->size()) {
+ response_body_.pop_front();
+ } else {
+ const int bytes_remaining = data->size() - bytes_to_copy;
+ IOBufferWithSize* new_buffer = new IOBufferWithSize(bytes_remaining);
+ memcpy(new_buffer->data(), &(data->data()[bytes_to_copy]),
+ bytes_remaining);
+ response_body_.pop_front();
+ response_body_.push_front(make_scoped_refptr(new_buffer));
+ }
+ bytes_read += bytes_to_copy;
+ }
+ return bytes_read;
+ }
+
+ if (!stream_) {
+ // If the stream is already closed, there is no body to read.
+ return response_status_;
+ }
+
+ CHECK(callback_.is_null());
+ CHECK(!user_buffer_.get());
+ CHECK_EQ(0, user_buffer_len_);
+
+ callback_ = callback;
+ user_buffer_ = buf;
+ user_buffer_len_ = buf_len;
+ return ERR_IO_PENDING;
+}
+
+void QuicHttpStream::Close(bool not_reusable) {
+ // Note: the not_reusable flag has no meaning for SPDY streams.
+ if (stream_) {
+ closed_stream_received_bytes_ = stream_->stream_bytes_read();
+ stream_->SetDelegate(nullptr);
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+ stream_ = nullptr;
+ response_status_ = was_handshake_confirmed_ ?
+ ERR_CONNECTION_CLOSED : ERR_QUIC_HANDSHAKE_FAILED;
+ }
+}
+
+HttpStream* QuicHttpStream::RenewStreamForAuth() {
+ return nullptr;
+}
+
+bool QuicHttpStream::IsResponseBodyComplete() const {
+ return next_state_ == STATE_OPEN && !stream_;
+}
+
+bool QuicHttpStream::CanFindEndOfResponse() const {
+ return true;
+}
+
+bool QuicHttpStream::IsConnectionReused() const {
+ // TODO(rch): do something smarter here.
+ return stream_ && stream_->id() > 1;
+}
+
+void QuicHttpStream::SetConnectionReused() {
+ // QUIC doesn't need an indicator here.
+}
+
+bool QuicHttpStream::IsConnectionReusable() const {
+ // QUIC streams aren't considered reusable.
+ return false;
+}
+
+int64 QuicHttpStream::GetTotalReceivedBytes() const {
+ if (stream_) {
+ return stream_->stream_bytes_read();
+ }
+
+ return closed_stream_received_bytes_;
+}
+
+bool QuicHttpStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const {
+ // TODO(mmenke): Figure out what to do here.
+ return true;
+}
+
+void QuicHttpStream::GetSSLInfo(SSLInfo* ssl_info) {
+ DCHECK(stream_);
+ stream_->GetSSLInfo(ssl_info);
+}
+
+void QuicHttpStream::GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) {
+ DCHECK(stream_);
+ NOTIMPLEMENTED();
+}
+
+bool QuicHttpStream::IsSpdyHttpStream() const {
+ return false;
+}
+
+void QuicHttpStream::Drain(HttpNetworkSession* session) {
+ Close(false);
+ delete this;
+}
+
+void QuicHttpStream::SetPriority(RequestPriority priority) {
+ priority_ = priority;
+}
+
+int QuicHttpStream::OnDataReceived(const char* data, int length) {
+ DCHECK_NE(0, length);
+ // Are we still reading the response headers.
+ if (!response_headers_received_) {
+ // Grow the read buffer if necessary.
+ if (read_buf_->RemainingCapacity() < length) {
+ size_t additional_capacity = length - read_buf_->RemainingCapacity();
+ if (additional_capacity < kHeaderBufInitialSize)
+ additional_capacity = kHeaderBufInitialSize;
+ read_buf_->SetCapacity(read_buf_->capacity() + additional_capacity);
+ }
+ memcpy(read_buf_->data(), data, length);
+ read_buf_->set_offset(read_buf_->offset() + length);
+ int rv = ParseResponseHeaders();
+ if (rv != ERR_IO_PENDING && !callback_.is_null()) {
+ DoCallback(rv);
+ }
+ return OK;
+ }
+
+ if (callback_.is_null()) {
+ BufferResponseBody(data, length);
+ return OK;
+ }
+
+ if (length <= user_buffer_len_) {
+ memcpy(user_buffer_->data(), data, length);
+ } else {
+ memcpy(user_buffer_->data(), data, user_buffer_len_);
+ int delta = length - user_buffer_len_;
+ BufferResponseBody(data + user_buffer_len_, delta);
+ length = user_buffer_len_;
+ }
+
+ user_buffer_ = nullptr;
+ user_buffer_len_ = 0;
+ DoCallback(length);
+ return OK;
+}
+
+void QuicHttpStream::OnClose(QuicErrorCode error) {
+ if (error != QUIC_NO_ERROR) {
+ response_status_ = was_handshake_confirmed_ ?
+ ERR_QUIC_PROTOCOL_ERROR : ERR_QUIC_HANDSHAKE_FAILED;
+ } else if (!response_headers_received_) {
+ response_status_ = ERR_ABORTED;
+ }
+
+ closed_stream_received_bytes_ = stream_->stream_bytes_read();
+ stream_ = nullptr;
+ if (!callback_.is_null())
+ DoCallback(response_status_);
+}
+
+void QuicHttpStream::OnError(int error) {
+ stream_ = nullptr;
+ response_status_ = was_handshake_confirmed_ ?
+ error : ERR_QUIC_HANDSHAKE_FAILED;
+ if (!callback_.is_null())
+ DoCallback(response_status_);
+}
+
+bool QuicHttpStream::HasSendHeadersComplete() {
+ return next_state_ > STATE_SEND_HEADERS_COMPLETE;
+}
+
+void QuicHttpStream::OnCryptoHandshakeConfirmed() {
+ was_handshake_confirmed_ = true;
+}
+
+void QuicHttpStream::OnSessionClosed(int error) {
+ Close(false);
+ session_error_ = error;
+ session_.reset();
+}
+
+void QuicHttpStream::OnIOComplete(int rv) {
+ rv = DoLoop(rv);
+
+ if (rv != ERR_IO_PENDING && !callback_.is_null()) {
+ DoCallback(rv);
+ }
+}
+
+void QuicHttpStream::DoCallback(int rv) {
+ CHECK_NE(rv, ERR_IO_PENDING);
+ CHECK(!callback_.is_null());
+
+ // The client callback can do anything, including destroying this class,
+ // so any pending callback must be issued after everything else is done.
+ base::ResetAndReturn(&callback_).Run(rv);
+}
+
+int QuicHttpStream::DoLoop(int rv) {
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_SEND_HEADERS:
+ CHECK_EQ(OK, rv);
+ rv = DoSendHeaders();
+ break;
+ case STATE_SEND_HEADERS_COMPLETE:
+ rv = DoSendHeadersComplete(rv);
+ break;
+ case STATE_READ_REQUEST_BODY:
+ CHECK_EQ(OK, rv);
+ rv = DoReadRequestBody();
+ break;
+ case STATE_READ_REQUEST_BODY_COMPLETE:
+ rv = DoReadRequestBodyComplete(rv);
+ break;
+ case STATE_SEND_BODY:
+ CHECK_EQ(OK, rv);
+ rv = DoSendBody();
+ break;
+ case STATE_SEND_BODY_COMPLETE:
+ rv = DoSendBodyComplete(rv);
+ break;
+ case STATE_OPEN:
+ CHECK_EQ(OK, rv);
+ break;
+ default:
+ NOTREACHED() << "next_state_: " << next_state_;
+ break;
+ }
+ } while (next_state_ != STATE_NONE && next_state_ != STATE_OPEN &&
+ rv != ERR_IO_PENDING);
+
+ return rv;
+}
+
+int QuicHttpStream::DoSendHeaders() {
+ if (!stream_)
+ return ERR_UNEXPECTED;
+
+ // Log the actual request with the URL Request's net log.
+ stream_net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_QUIC_SEND_REQUEST_HEADERS,
+ base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_,
+ priority_));
+ // Also log to the QuicSession's net log.
+ stream_->net_log().AddEvent(
+ NetLog::TYPE_QUIC_HTTP_STREAM_SEND_REQUEST_HEADERS,
+ base::Bind(&QuicRequestNetLogCallback, stream_->id(), &request_headers_,
+ priority_));
+
+ bool has_upload_data = request_body_stream_ != nullptr;
+
+ next_state_ = STATE_SEND_HEADERS_COMPLETE;
+ int rv = stream_->WriteHeaders(request_headers_, !has_upload_data, nullptr);
+ request_headers_.clear();
+ return rv;
+}
+
+int QuicHttpStream::DoSendHeadersComplete(int rv) {
+ if (rv < 0)
+ return rv;
+
+ next_state_ = request_body_stream_ ?
+ STATE_READ_REQUEST_BODY : STATE_OPEN;
+
+ return OK;
+}
+
+int QuicHttpStream::DoReadRequestBody() {
+ next_state_ = STATE_READ_REQUEST_BODY_COMPLETE;
+ return request_body_stream_->Read(
+ raw_request_body_buf_.get(),
+ raw_request_body_buf_->size(),
+ base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr()));
+}
+
+int QuicHttpStream::DoReadRequestBodyComplete(int rv) {
+ // |rv| is the result of read from the request body from the last call to
+ // DoSendBody().
+ if (rv < 0)
+ return rv;
+
+ request_body_buf_ = new DrainableIOBuffer(raw_request_body_buf_.get(), rv);
+ if (rv == 0) { // Reached the end.
+ DCHECK(request_body_stream_->IsEOF());
+ }
+
+ next_state_ = STATE_SEND_BODY;
+ return OK;
+}
+
+int QuicHttpStream::DoSendBody() {
+ if (!stream_)
+ return ERR_UNEXPECTED;
+
+ CHECK(request_body_stream_);
+ CHECK(request_body_buf_.get());
+ const bool eof = request_body_stream_->IsEOF();
+ int len = request_body_buf_->BytesRemaining();
+ if (len > 0 || eof) {
+ next_state_ = STATE_SEND_BODY_COMPLETE;
+ base::StringPiece data(request_body_buf_->data(), len);
+ return stream_->WriteStreamData(
+ data, eof,
+ base::Bind(&QuicHttpStream::OnIOComplete, weak_factory_.GetWeakPtr()));
+ }
+
+ next_state_ = STATE_OPEN;
+ return OK;
+}
+
+int QuicHttpStream::DoSendBodyComplete(int rv) {
+ if (rv < 0)
+ return rv;
+
+ request_body_buf_->DidConsume(request_body_buf_->BytesRemaining());
+
+ if (!request_body_stream_->IsEOF()) {
+ next_state_ = STATE_READ_REQUEST_BODY;
+ return OK;
+ }
+
+ next_state_ = STATE_OPEN;
+ return OK;
+}
+
+int QuicHttpStream::ParseResponseHeaders() {
+ size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
+ SpdyFramer framer(SPDY3);
+ SpdyHeaderBlock headers;
+ char* data = read_buf_->StartOfBuffer();
+ size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_->offset(),
+ &headers);
+
+ if (len == 0) {
+ return ERR_IO_PENDING;
+ }
+
+ // Save the remaining received data.
+ size_t delta = read_buf_len - len;
+ if (delta > 0) {
+ BufferResponseBody(data + len, delta);
+ }
+
+ // The URLRequest logs these headers, so only log to the QuicSession's
+ // net log.
+ stream_->net_log().AddEvent(
+ NetLog::TYPE_QUIC_HTTP_STREAM_READ_RESPONSE_HEADERS,
+ base::Bind(&SpdyHeaderBlockNetLogCallback, &headers));
+
+ if (!SpdyHeadersToHttpResponse(headers, SPDY3, response_info_)) {
+ DLOG(WARNING) << "Invalid headers";
+ return ERR_QUIC_PROTOCOL_ERROR;
+ }
+ // Put the peer's IP address and port into the response.
+ IPEndPoint address = stream_->GetPeerAddress();
+ response_info_->socket_address = HostPortPair::FromIPEndPoint(address);
+ response_info_->connection_info =
+ HttpResponseInfo::CONNECTION_INFO_QUIC1_SPDY3;
+ response_info_->vary_data
+ .Init(*request_info_, *response_info_->headers.get());
+ response_info_->was_npn_negotiated = true;
+ response_info_->npn_negotiated_protocol = "quic/1+spdy/3";
+ response_info_->response_time = base::Time::Now();
+ response_info_->request_time = request_time_;
+ response_headers_received_ = true;
+
+ return OK;
+}
+
+void QuicHttpStream::BufferResponseBody(const char* data, int length) {
+ if (length == 0)
+ return;
+ IOBufferWithSize* io_buffer = new IOBufferWithSize(length);
+ memcpy(io_buffer->data(), data, length);
+ response_body_.push_back(make_scoped_refptr(io_buffer));
+}
+
+} // namespace net
diff --git a/net/quic/quic_http_stream.h b/net/quic/quic_http_stream.h
new file mode 100644
index 0000000..5c60822
--- /dev/null
+++ b/net/quic/quic_http_stream.h
@@ -0,0 +1,173 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_HTTP_STREAM_H_
+#define NET_QUIC_QUIC_HTTP_STREAM_H_
+
+#include <list>
+
+#include "base/memory/weak_ptr.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_stream.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_reliable_client_stream.h"
+
+namespace net {
+
+namespace test {
+class QuicHttpStreamPeer;
+} // namespace test
+
+// The QuicHttpStream is a QUIC-specific HttpStream subclass. It holds a
+// non-owning pointer to a QuicReliableClientStream which it uses to
+// send and receive data.
+class NET_EXPORT_PRIVATE QuicHttpStream :
+ public QuicClientSession::Observer,
+ public QuicReliableClientStream::Delegate,
+ public HttpStream {
+ public:
+ explicit QuicHttpStream(const base::WeakPtr<QuicClientSession>& session);
+
+ virtual ~QuicHttpStream();
+
+ // HttpStream implementation.
+ virtual int InitializeStream(const HttpRequestInfo* request_info,
+ RequestPriority priority,
+ const BoundNetLog& net_log,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual int SendRequest(const HttpRequestHeaders& request_headers,
+ HttpResponseInfo* response,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual UploadProgress GetUploadProgress() const OVERRIDE;
+ virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE;
+ virtual int ReadResponseBody(IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) OVERRIDE;
+ virtual void Close(bool not_reusable) OVERRIDE;
+ virtual HttpStream* RenewStreamForAuth() OVERRIDE;
+ virtual bool IsResponseBodyComplete() const OVERRIDE;
+ virtual bool CanFindEndOfResponse() const OVERRIDE;
+ virtual bool IsConnectionReused() const OVERRIDE;
+ virtual void SetConnectionReused() OVERRIDE;
+ virtual bool IsConnectionReusable() const OVERRIDE;
+ virtual int64 GetTotalReceivedBytes() const OVERRIDE;
+ virtual bool GetLoadTimingInfo(
+ LoadTimingInfo* load_timing_info) const OVERRIDE;
+ virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
+ virtual void GetSSLCertRequestInfo(
+ SSLCertRequestInfo* cert_request_info) OVERRIDE;
+ virtual bool IsSpdyHttpStream() const OVERRIDE;
+ virtual void Drain(HttpNetworkSession* session) OVERRIDE;
+ virtual void SetPriority(RequestPriority priority) OVERRIDE;
+
+ // QuicReliableClientStream::Delegate implementation
+ virtual int OnDataReceived(const char* data, int length) OVERRIDE;
+ virtual void OnClose(QuicErrorCode error) OVERRIDE;
+ virtual void OnError(int error) OVERRIDE;
+ virtual bool HasSendHeadersComplete() OVERRIDE;
+
+ // QuicClientSession::Observer implementation
+ virtual void OnCryptoHandshakeConfirmed() OVERRIDE;
+ virtual void OnSessionClosed(int error) OVERRIDE;
+
+ private:
+ friend class test::QuicHttpStreamPeer;
+
+ enum State {
+ STATE_NONE,
+ STATE_SEND_HEADERS,
+ STATE_SEND_HEADERS_COMPLETE,
+ STATE_READ_REQUEST_BODY,
+ STATE_READ_REQUEST_BODY_COMPLETE,
+ STATE_SEND_BODY,
+ STATE_SEND_BODY_COMPLETE,
+ STATE_OPEN,
+ };
+
+ void OnStreamReady(int rv);
+ void OnIOComplete(int rv);
+ void DoCallback(int rv);
+
+ int DoLoop(int);
+ int DoSendHeaders();
+ int DoSendHeadersComplete(int rv);
+ int DoReadRequestBody();
+ int DoReadRequestBodyComplete(int rv);
+ int DoSendBody();
+ int DoSendBodyComplete(int rv);
+ int DoReadResponseHeaders();
+ int DoReadResponseHeadersComplete(int rv);
+
+ int ParseResponseHeaders();
+
+ void BufferResponseBody(const char* data, int length);
+
+ State next_state_;
+
+ base::WeakPtr<QuicClientSession> session_;
+ int session_error_; // Error code from the connection shutdown.
+ bool was_handshake_confirmed_; // True if the crypto handshake succeeded.
+ QuicClientSession::StreamRequest stream_request_;
+ QuicReliableClientStream* stream_; // Non-owning.
+
+ // The following three fields are all owned by the caller and must
+ // outlive this object, according to the HttpStream contract.
+
+ // The request to send.
+ const HttpRequestInfo* request_info_;
+ // The request body to send, if any, owned by the caller.
+ UploadDataStream* request_body_stream_;
+ // Time the request was issued.
+ base::Time request_time_;
+ // The priority of the request.
+ RequestPriority priority_;
+ // |response_info_| is the HTTP response data object which is filled in
+ // when a the response headers are read. It is not owned by this stream.
+ HttpResponseInfo* response_info_;
+ // Because response data is buffered, also buffer the response status if the
+ // stream is explicitly closed via OnError or OnClose with an error.
+ // Once all buffered data has been returned, this will be used as the final
+ // response.
+ int response_status_;
+
+ // Serialized request headers.
+ SpdyHeaderBlock request_headers_;
+
+ bool response_headers_received_;
+
+ // Serialized HTTP request.
+ std::string request_;
+
+ // Buffer into which response header data is read.
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+
+ // We buffer the response body as it arrives asynchronously from the stream.
+ // TODO(rch): This is infinite buffering, which is bad.
+ std::list<scoped_refptr<IOBufferWithSize> > response_body_;
+
+ // Number of bytes received when the stream was closed.
+ int64 closed_stream_received_bytes_;
+
+ // The caller's callback to be used for asynchronous operations.
+ CompletionCallback callback_;
+
+ // Caller provided buffer for the ReadResponseBody() response.
+ scoped_refptr<IOBuffer> user_buffer_;
+ int user_buffer_len_;
+
+ // Temporary buffer used to read the request body from UploadDataStream.
+ scoped_refptr<IOBufferWithSize> raw_request_body_buf_;
+ // Wraps raw_request_body_buf_ to read the remaining data progressively.
+ scoped_refptr<DrainableIOBuffer> request_body_buf_;
+
+ BoundNetLog stream_net_log_;
+
+ base::WeakPtrFactory<QuicHttpStream> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicHttpStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_HTTP_STREAM_H_
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
new file mode 100644
index 0000000..313e7f5
--- /dev/null
+++ b/net/quic/quic_http_stream_test.cc
@@ -0,0 +1,778 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_stream.h"
+
+#include <vector>
+
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/base/upload_bytes_element_reader.h"
+#include "net/base/upload_data_stream.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/transport_security_state.h"
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_server_info.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_default_packet_writer.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_reliable_client_stream.h"
+#include "net/quic/quic_write_blocked_list.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/test_task_runner.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_http_utils.h"
+#include "net/spdy/spdy_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::Return;
+
+namespace net {
+namespace test {
+namespace {
+
+const char kUploadData[] = "Really nifty data!";
+const char kServerHostname[] = "www.google.com";
+const uint16 kServerPort = 80;
+
+class TestQuicConnection : public QuicConnection {
+ public:
+ TestQuicConnection(const QuicVersionVector& versions,
+ QuicConnectionId connection_id,
+ IPEndPoint address,
+ QuicConnectionHelper* helper,
+ const QuicConnection::PacketWriterFactory& writer_factory)
+ : QuicConnection(connection_id,
+ address,
+ helper,
+ writer_factory,
+ true /* owns_writer */,
+ false /* is_server */,
+ versions) {
+ }
+
+ void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) {
+ QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm);
+ }
+
+ void SetReceiveAlgorithm(ReceiveAlgorithmInterface* receive_algorithm) {
+ QuicConnectionPeer::SetReceiveAlgorithm(this, receive_algorithm);
+ }
+};
+
+class TestReceiveAlgorithm : public ReceiveAlgorithmInterface {
+ public:
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* /*congestion_feedback*/) {
+ return false;
+ }
+
+ MOCK_METHOD3(RecordIncomingPacket,
+ void(QuicByteCount, QuicPacketSequenceNumber, QuicTime));
+};
+
+// Subclass of QuicHttpStream that closes itself when the first piece of data
+// is received.
+class AutoClosingStream : public QuicHttpStream {
+ public:
+ explicit AutoClosingStream(const base::WeakPtr<QuicClientSession>& session)
+ : QuicHttpStream(session) {
+ }
+
+ virtual int OnDataReceived(const char* data, int length) OVERRIDE {
+ Close(false);
+ return OK;
+ }
+};
+
+class TestPacketWriterFactory : public QuicConnection::PacketWriterFactory {
+ public:
+ explicit TestPacketWriterFactory(DatagramClientSocket* socket)
+ : socket_(socket) {}
+ virtual ~TestPacketWriterFactory() {}
+
+ virtual QuicPacketWriter* Create(QuicConnection* connection) const OVERRIDE {
+ return new QuicDefaultPacketWriter(socket_);
+ }
+
+ private:
+ DatagramClientSocket* socket_;
+};
+
+} // namespace
+
+class QuicHttpStreamPeer {
+ public:
+ static QuicReliableClientStream* GetQuicReliableClientStream(
+ QuicHttpStream* stream) {
+ return stream->stream_;
+ }
+};
+
+class QuicHttpStreamTest : public ::testing::TestWithParam<QuicVersion> {
+ protected:
+ static const bool kFin = true;
+ static const bool kIncludeVersion = true;
+ static const bool kIncludeCongestionFeedback = true;
+
+ // Holds a packet to be written to the wire, and the IO mode that should
+ // be used by the mock socket when performing the write.
+ struct PacketToWrite {
+ PacketToWrite(IoMode mode, QuicEncryptedPacket* packet)
+ : mode(mode),
+ packet(packet) {
+ }
+ IoMode mode;
+ QuicEncryptedPacket* packet;
+ };
+
+ QuicHttpStreamTest()
+ : net_log_(BoundNetLog()),
+ use_closing_stream_(false),
+ read_buffer_(new IOBufferWithSize(4096)),
+ connection_id_(2),
+ stream_id_(kClientDataStreamId1),
+ maker_(GetParam(), connection_id_, &clock_),
+ random_generator_(0) {
+ IPAddressNumber ip;
+ CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
+ peer_addr_ = IPEndPoint(ip, 443);
+ self_addr_ = IPEndPoint(ip, 8435);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+ }
+
+ ~QuicHttpStreamTest() {
+ session_->CloseSessionOnError(ERR_ABORTED);
+ for (size_t i = 0; i < writes_.size(); i++) {
+ delete writes_[i].packet;
+ }
+ }
+
+ // Adds a packet to the list of expected writes.
+ void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
+ writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release()));
+ }
+
+ // Returns the packet to be written at position |pos|.
+ QuicEncryptedPacket* GetWrite(size_t pos) {
+ return writes_[pos].packet;
+ }
+
+ bool AtEof() {
+ return socket_data_->at_read_eof() && socket_data_->at_write_eof();
+ }
+
+ void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) {
+ connection_->ProcessUdpPacket(self_addr_, peer_addr_, *packet);
+ }
+
+ // Configures the test fixture to use the list of expected writes.
+ void Initialize() {
+ mock_writes_.reset(new MockWrite[writes_.size()]);
+ for (size_t i = 0; i < writes_.size(); i++) {
+ mock_writes_[i] = MockWrite(writes_[i].mode,
+ writes_[i].packet->data(),
+ writes_[i].packet->length());
+ };
+
+ socket_data_.reset(new StaticSocketDataProvider(
+ nullptr, 0, mock_writes_.get(), writes_.size()));
+
+ MockUDPClientSocket* socket = new MockUDPClientSocket(socket_data_.get(),
+ net_log_.net_log());
+ socket->Connect(peer_addr_);
+ runner_ = new TestTaskRunner(&clock_);
+ send_algorithm_ = new MockSendAlgorithm();
+ receive_algorithm_ = new TestReceiveAlgorithm();
+ EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _)).
+ Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, _, _, _)).WillRepeatedly(Return(true));
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow()).WillRepeatedly(
+ Return(kMaxPacketSize));
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).
+ WillRepeatedly(Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillRepeatedly(
+ Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)).Times(AnyNumber());
+ helper_.reset(new QuicConnectionHelper(runner_.get(), &clock_,
+ &random_generator_));
+ TestPacketWriterFactory writer_factory(socket);
+ connection_ = new TestQuicConnection(SupportedVersions(GetParam()),
+ connection_id_, peer_addr_,
+ helper_.get(), writer_factory);
+ connection_->set_visitor(&visitor_);
+ connection_->SetSendAlgorithm(send_algorithm_);
+ connection_->SetReceiveAlgorithm(receive_algorithm_);
+ crypto_config_.SetDefaults();
+ session_.reset(
+ new QuicClientSession(connection_,
+ scoped_ptr<DatagramClientSocket>(socket),
+ nullptr,
+ &transport_security_state_,
+ make_scoped_ptr((QuicServerInfo*)nullptr),
+ DefaultQuicConfig(),
+ base::MessageLoop::current()->
+ message_loop_proxy().get(),
+ nullptr));
+ session_->InitializeSession(QuicServerId(kServerHostname, kServerPort,
+ false, PRIVACY_MODE_DISABLED),
+ &crypto_config_,
+ &crypto_client_stream_factory_);
+ session_->GetCryptoStream()->CryptoConnect();
+ EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed());
+ stream_.reset(use_closing_stream_ ?
+ new AutoClosingStream(session_->GetWeakPtr()) :
+ new QuicHttpStream(session_->GetWeakPtr()));
+ }
+
+ void SetRequest(const std::string& method,
+ const std::string& path,
+ RequestPriority priority) {
+ request_headers_ = maker_.GetRequestHeaders(method, "http", path);
+ }
+
+ void SetResponse(const std::string& status, const std::string& body) {
+ response_headers_ = maker_.GetResponseHeaders(status);
+ response_data_ = body;
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data) {
+ return maker_.MakeDataPacket(
+ sequence_number, stream_id_, should_include_version, fin, offset, data);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool fin) {
+ return maker_.MakeRequestHeadersPacket(
+ sequence_number, stream_id_, kIncludeVersion, fin, request_headers_);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ bool fin) {
+ return maker_.MakeResponseHeadersPacket(
+ sequence_number, stream_id_, !kIncludeVersion, fin, response_headers_);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return maker_.MakeRstPacket(
+ sequence_number, true, stream_id_,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam()));
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return maker_.MakeAckAndRstPacket(
+ sequence_number, !kIncludeVersion, stream_id_, QUIC_STREAM_CANCELLED,
+ 2, 1, !kIncludeCongestionFeedback);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked) {
+ return maker_.MakeAckPacket(sequence_number, largest_received,
+ least_unacked, !kIncludeCongestionFeedback);
+ }
+
+ BoundNetLog net_log_;
+ bool use_closing_stream_;
+ MockSendAlgorithm* send_algorithm_;
+ TestReceiveAlgorithm* receive_algorithm_;
+ scoped_refptr<TestTaskRunner> runner_;
+ scoped_ptr<MockWrite[]> mock_writes_;
+ MockClock clock_;
+ TestQuicConnection* connection_;
+ scoped_ptr<QuicConnectionHelper> helper_;
+ testing::StrictMock<MockConnectionVisitor> visitor_;
+ scoped_ptr<QuicHttpStream> stream_;
+ TransportSecurityState transport_security_state_;
+ scoped_ptr<QuicClientSession> session_;
+ QuicCryptoClientConfig crypto_config_;
+ TestCompletionCallback callback_;
+ HttpRequestInfo request_;
+ HttpRequestHeaders headers_;
+ HttpResponseInfo response_;
+ scoped_refptr<IOBufferWithSize> read_buffer_;
+ SpdyHeaderBlock request_headers_;
+ SpdyHeaderBlock response_headers_;
+ std::string request_data_;
+ std::string response_data_;
+
+ private:
+ const QuicConnectionId connection_id_;
+ const QuicStreamId stream_id_;
+ QuicTestPacketMaker maker_;
+ IPEndPoint self_addr_;
+ IPEndPoint peer_addr_;
+ MockRandom random_generator_;
+ MockCryptoClientStreamFactory crypto_client_stream_factory_;
+ scoped_ptr<StaticSocketDataProvider> socket_data_;
+ std::vector<PacketToWrite> writes_;
+};
+
+INSTANTIATE_TEST_CASE_P(Version, QuicHttpStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicHttpStreamTest, RenewStreamForAuth) {
+ Initialize();
+ EXPECT_EQ(nullptr, stream_->RenewStreamForAuth());
+}
+
+TEST_P(QuicHttpStreamTest, CanFindEndOfResponse) {
+ Initialize();
+ EXPECT_TRUE(stream_->CanFindEndOfResponse());
+}
+
+TEST_P(QuicHttpStreamTest, IsConnectionReusable) {
+ Initialize();
+ EXPECT_FALSE(stream_->IsConnectionReusable());
+}
+
+TEST_P(QuicHttpStreamTest, GetRequest) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ // Ack the request.
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->ReadResponseHeaders(callback_.callback()));
+
+ SetResponse("404 Not Found", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
+
+ // Now that the headers have been processed, the callback will return.
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ ASSERT_TRUE(response_.headers.get());
+ EXPECT_EQ(404, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+ EXPECT_FALSE(response_.response_time.is_null());
+ EXPECT_FALSE(response_.request_time.is_null());
+
+ // There is no body, so this should return immediately.
+ EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(),
+ read_buffer_->size(),
+ callback_.callback()));
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
+// Regression test for http://crbug.com/288128
+TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ // Ack the request.
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->ReadResponseHeaders(callback_.callback()));
+
+ SpdyHeaderBlock headers;
+ headers[":status"] = "200 OK";
+ headers[":version"] = "HTTP/1.1";
+ headers["content-type"] = "text/plain";
+ headers["big6"] = std::string(10000, 'x'); // Lots of x's.
+
+ std::string response = SpdyUtils::SerializeUncompressedHeaders(headers);
+ EXPECT_LT(4096u, response.length());
+ stream_->OnDataReceived(response.data(), response.length());
+ stream_->OnClose(QUIC_NO_ERROR);
+
+ // Now that the headers have been processed, the callback will return.
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ ASSERT_TRUE(response_.headers.get());
+ EXPECT_EQ(200, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // There is no body, so this should return immediately.
+ EXPECT_EQ(0, stream_->ReadResponseBody(read_buffer_.get(),
+ read_buffer_->size(),
+ callback_.callback()));
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
+// Regression test for http://crbug.com/409101
+TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendRequest) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+
+ session_->connection()->CloseConnection(QUIC_NO_ERROR, true);
+
+ EXPECT_EQ(ERR_CONNECTION_CLOSED,
+ stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+}
+
+// Regression test for http://crbug.com/409871
+TEST_P(QuicHttpStreamTest, SessionClosedBeforeReadResponseHeaders) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ session_->connection()->CloseConnection(QUIC_NO_ERROR, true);
+
+ EXPECT_NE(OK, stream_->ReadResponseHeaders(callback_.callback()));
+}
+
+TEST_P(QuicHttpStreamTest, SendPostRequest) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData));
+ AddWrite(ConstructAckPacket(3, 3, 1));
+
+ Initialize();
+
+ ScopedVector<UploadElementReader> element_readers;
+ element_readers.push_back(
+ new UploadBytesElementReader(kUploadData, strlen(kUploadData)));
+ UploadDataStream upload_data_stream(element_readers.Pass(), 0);
+ request_.method = "POST";
+ request_.url = GURL("http://www.google.com/");
+ request_.upload_data_stream = &upload_data_stream;
+ ASSERT_EQ(OK, request_.upload_data_stream->Init(CompletionCallback()));
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ // Ack both packets in the request.
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+
+ // Send the response headers (but not the body).
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
+
+ // Since the headers have already arrived, this should return immediately.
+ EXPECT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
+ ASSERT_TRUE(response_.headers.get());
+ EXPECT_EQ(200, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // Send the response body.
+ const char kResponseBody[] = "Hello world!";
+ ProcessPacket(ConstructDataPacket(3, false, kFin, 0, kResponseBody));
+ // Since the body has already arrived, this should return immediately.
+ EXPECT_EQ(static_cast<int>(strlen(kResponseBody)),
+ stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
+ callback_.callback()));
+
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
+TEST_P(QuicHttpStreamTest, SendChunkedPostRequest) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
+ size_t chunk_size = strlen(kUploadData);
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
+ AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size,
+ kUploadData));
+ AddWrite(ConstructAckPacket(4, 3, 1));
+ Initialize();
+
+ UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
+ upload_data_stream.AppendChunk(kUploadData, chunk_size, false);
+
+ request_.method = "POST";
+ request_.url = GURL("http://www.google.com/");
+ request_.upload_data_stream = &upload_data_stream;
+ ASSERT_EQ(OK, request_.upload_data_stream->Init(CompletionCallback()));
+
+ ASSERT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ upload_data_stream.AppendChunk(kUploadData, chunk_size, true);
+
+ // Ack both packets in the request.
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+
+ // Send the response headers (but not the body).
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
+
+ // Since the headers have already arrived, this should return immediately.
+ ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
+ ASSERT_TRUE(response_.headers.get());
+ EXPECT_EQ(200, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // Send the response body.
+ const char kResponseBody[] = "Hello world!";
+ ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(),
+ kResponseBody));
+
+ // Since the body has already arrived, this should return immediately.
+ ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
+ stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
+ callback_.callback()));
+
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
+TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithFinalEmptyDataPacket) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
+ size_t chunk_size = strlen(kUploadData);
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, !kFin, 0, kUploadData));
+ AddWrite(ConstructDataPacket(3, kIncludeVersion, kFin, chunk_size, ""));
+ AddWrite(ConstructAckPacket(4, 3, 1));
+ Initialize();
+
+ UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
+ upload_data_stream.AppendChunk(kUploadData, chunk_size, false);
+
+ request_.method = "POST";
+ request_.url = GURL("http://www.google.com/");
+ request_.upload_data_stream = &upload_data_stream;
+ ASSERT_EQ(OK, request_.upload_data_stream->Init(CompletionCallback()));
+
+ ASSERT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ upload_data_stream.AppendChunk(nullptr, 0, true);
+
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+
+ // Send the response headers (but not the body).
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
+
+ // Since the headers have already arrived, this should return immediately.
+ ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
+ ASSERT_TRUE(response_.headers.get());
+ EXPECT_EQ(200, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // Send the response body.
+ const char kResponseBody[] = "Hello world!";
+ ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(),
+ kResponseBody));
+
+ // Since the body has already arrived, this should return immediately.
+ ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
+ stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
+ callback_.callback()));
+
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
+TEST_P(QuicHttpStreamTest, SendChunkedPostRequestWithOneEmptyDataPacket) {
+ SetRequest("POST", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, !kFin));
+ AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, ""));
+ AddWrite(ConstructAckPacket(3, 3, 1));
+ Initialize();
+
+ UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0);
+
+ request_.method = "POST";
+ request_.url = GURL("http://www.google.com/");
+ request_.upload_data_stream = &upload_data_stream;
+ ASSERT_EQ(OK, request_.upload_data_stream->Init(CompletionCallback()));
+
+ ASSERT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ ASSERT_EQ(ERR_IO_PENDING, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ upload_data_stream.AppendChunk(nullptr, 0, true);
+
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+
+ // Send the response headers (but not the body).
+ SetResponse("200 OK", std::string());
+ ProcessPacket(ConstructResponseHeadersPacket(2, !kFin));
+
+ // Since the headers have already arrived, this should return immediately.
+ ASSERT_EQ(OK, stream_->ReadResponseHeaders(callback_.callback()));
+ ASSERT_TRUE(response_.headers.get());
+ EXPECT_EQ(200, response_.headers->response_code());
+ EXPECT_TRUE(response_.headers->HasHeaderValue("Content-Type", "text/plain"));
+
+ // Send the response body.
+ const char kResponseBody[] = "Hello world!";
+ ProcessPacket(ConstructDataPacket(3, false, kFin, response_data_.length(),
+ kResponseBody));
+
+ // Since the body has already arrived, this should return immediately.
+ ASSERT_EQ(static_cast<int>(strlen(kResponseBody)),
+ stream_->ReadResponseBody(read_buffer_.get(), read_buffer_->size(),
+ callback_.callback()));
+
+ EXPECT_TRUE(stream_->IsResponseBodyComplete());
+ EXPECT_TRUE(AtEof());
+}
+
+TEST_P(QuicHttpStreamTest, DestroyedEarly) {
+ SetRequest("GET", "/", DEFAULT_PRIORITY);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ AddWrite(ConstructAckAndRstStreamPacket(2));
+ use_closing_stream_ = true;
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY,
+ net_log_, callback_.callback()));
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ // Ack the request.
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->ReadResponseHeaders(callback_.callback()));
+
+ // Send the response with a body.
+ SetResponse("404 OK", "hello world!");
+ // In the course of processing this packet, the QuicHttpStream close itself.
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
+
+ EXPECT_TRUE(AtEof());
+}
+
+TEST_P(QuicHttpStreamTest, Priority) {
+ SetRequest("GET", "/", MEDIUM);
+ AddWrite(ConstructRequestHeadersPacket(1, kFin));
+ AddWrite(ConstructAckAndRstStreamPacket(2));
+ use_closing_stream_ = true;
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM,
+ net_log_, callback_.callback()));
+
+ // Check that priority is highest.
+ QuicReliableClientStream* reliable_stream =
+ QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
+ DCHECK(reliable_stream);
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
+ reliable_stream->EffectivePriority());
+
+ EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_,
+ callback_.callback()));
+
+ // Check that priority has now dropped back to MEDIUM.
+ DCHECK_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority(
+ reliable_stream->EffectivePriority()));
+
+ // Ack the request.
+ ProcessPacket(ConstructAckPacket(1, 0, 0));
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->ReadResponseHeaders(callback_.callback()));
+
+ // Send the response with a body.
+ SetResponse("404 OK", "hello world!");
+ // In the course of processing this packet, the QuicHttpStream close itself.
+ ProcessPacket(ConstructResponseHeadersPacket(2, kFin));
+
+ EXPECT_TRUE(AtEof());
+}
+
+// Regression test for http://crbug.com/294870
+TEST_P(QuicHttpStreamTest, CheckPriorityWithNoDelegate) {
+ SetRequest("GET", "/", MEDIUM);
+ use_closing_stream_ = true;
+
+ AddWrite(ConstructRstStreamPacket(1));
+
+ Initialize();
+
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+
+ EXPECT_EQ(OK, stream_->InitializeStream(&request_, MEDIUM,
+ net_log_, callback_.callback()));
+
+ // Check that priority is highest.
+ QuicReliableClientStream* reliable_stream =
+ QuicHttpStreamPeer::GetQuicReliableClientStream(stream_.get());
+ DCHECK(reliable_stream);
+ QuicReliableClientStream::Delegate* delegate = reliable_stream->GetDelegate();
+ DCHECK(delegate);
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
+ reliable_stream->EffectivePriority());
+
+ // Set Delegate to nullptr and make sure EffectivePriority returns highest
+ // priority.
+ reliable_stream->SetDelegate(nullptr);
+ DCHECK_EQ(QuicWriteBlockedList::kHighestPriority,
+ reliable_stream->EffectivePriority());
+ reliable_stream->SetDelegate(delegate);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_http_utils.cc b/net/quic/quic_http_utils.cc
new file mode 100644
index 0000000..2bad8a6
--- /dev/null
+++ b/net/quic/quic_http_utils.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_utils.h"
+
+namespace net {
+
+QuicPriority ConvertRequestPriorityToQuicPriority(
+ const RequestPriority priority) {
+ DCHECK_GE(priority, MINIMUM_PRIORITY);
+ DCHECK_LE(priority, MAXIMUM_PRIORITY);
+ return static_cast<QuicPriority>(HIGHEST - priority);
+}
+
+NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
+ QuicPriority priority) {
+ // Handle invalid values gracefully.
+ return (priority >= 5) ?
+ IDLE : static_cast<RequestPriority>(HIGHEST - priority);
+}
+
+base::Value* QuicRequestNetLogCallback(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock* headers,
+ QuicPriority priority,
+ NetLog::LogLevel log_level) {
+ base::DictionaryValue* dict = static_cast<base::DictionaryValue*>(
+ SpdyHeaderBlockNetLogCallback(headers, log_level));
+ dict->SetInteger("quic_priority", static_cast<int>(priority));
+ dict->SetInteger("quic_stream_id", static_cast<int>(stream_id));
+ return dict;
+}
+
+} // namespace net
diff --git a/net/quic/quic_http_utils.h b/net/quic/quic_http_utils.h
new file mode 100644
index 0000000..862b7c6
--- /dev/null
+++ b/net/quic/quic_http_utils.h
@@ -0,0 +1,32 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_HTTP_UTILS_H_
+#define NET_QUIC_QUIC_HTTP_UTILS_H_
+
+#include "base/values.h"
+#include "net/base/net_export.h"
+#include "net/base/request_priority.h"
+#include "net/quic/quic_protocol.h"
+#include "net/spdy/spdy_header_block.h"
+
+namespace net {
+
+NET_EXPORT_PRIVATE QuicPriority ConvertRequestPriorityToQuicPriority(
+ RequestPriority priority);
+
+NET_EXPORT_PRIVATE RequestPriority ConvertQuicPriorityToRequestPriority(
+ QuicPriority priority);
+
+// Converts a SpdyHeaderBlock and priority into NetLog event parameters. Caller
+// takes ownership of returned value.
+NET_EXPORT base::Value* QuicRequestNetLogCallback(
+ QuicStreamId stream_id,
+ const SpdyHeaderBlock* headers,
+ QuicPriority priority,
+ NetLog::LogLevel log_level);
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_HTTP_UTILS_H_
diff --git a/net/quic/quic_http_utils_test.cc b/net/quic/quic_http_utils_test.cc
new file mode 100644
index 0000000..93b62e2
--- /dev/null
+++ b/net/quic/quic_http_utils_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_http_utils.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+TEST(QuicHttpUtilsTest, ConvertRequestPriorityToQuicPriority) {
+ EXPECT_EQ(0u, ConvertRequestPriorityToQuicPriority(HIGHEST));
+ EXPECT_EQ(1u, ConvertRequestPriorityToQuicPriority(MEDIUM));
+ EXPECT_EQ(2u, ConvertRequestPriorityToQuicPriority(LOW));
+ EXPECT_EQ(3u, ConvertRequestPriorityToQuicPriority(LOWEST));
+ EXPECT_EQ(4u, ConvertRequestPriorityToQuicPriority(IDLE));
+}
+
+TEST(QuicHttpUtilsTest, ConvertQuicPriorityToRequestPriority) {
+ EXPECT_EQ(HIGHEST, ConvertQuicPriorityToRequestPriority(0));
+ EXPECT_EQ(MEDIUM, ConvertQuicPriorityToRequestPriority(1));
+ EXPECT_EQ(LOW, ConvertQuicPriorityToRequestPriority(2));
+ EXPECT_EQ(LOWEST, ConvertQuicPriorityToRequestPriority(3));
+ EXPECT_EQ(IDLE, ConvertQuicPriorityToRequestPriority(4));
+ // These are invalid values, but we should still handle them
+ // gracefully. TODO(rtenneti): should we test for all possible values of
+ // uint32?
+ for (int i = 5; i < kuint8max; ++i) {
+ EXPECT_EQ(IDLE, ConvertQuicPriorityToRequestPriority(i));
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_in_memory_cache.cc b/net/quic/quic_in_memory_cache.cc
new file mode 100644
index 0000000..31e346f
--- /dev/null
+++ b/net/quic/quic_in_memory_cache.cc
@@ -0,0 +1,183 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_in_memory_cache.h"
+
+#include "base/files/file_enumerator.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "net/http/http_util.h"
+#include "url/gurl.h"
+
+using base::FilePath;
+using base::StringPiece;
+using std::string;
+
+// Specifies the directory used during QuicInMemoryCache
+// construction to seed the cache. Cache directory can be
+// generated using `wget -p --save-headers <url>
+
+namespace net {
+
+FilePath::StringType g_quic_in_memory_cache_dir = FILE_PATH_LITERAL("");
+
+QuicInMemoryCache::Response::Response() : response_type_(REGULAR_RESPONSE) {
+}
+
+QuicInMemoryCache::Response::~Response() {
+}
+
+// static
+QuicInMemoryCache* QuicInMemoryCache::GetInstance() {
+ return Singleton<QuicInMemoryCache>::get();
+}
+
+const QuicInMemoryCache::Response* QuicInMemoryCache::GetResponse(
+ const GURL& url) const {
+ ResponseMap::const_iterator it = responses_.find(GetKey(url));
+ if (it == responses_.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
+void QuicInMemoryCache::AddSimpleResponse(StringPiece path,
+ StringPiece version,
+ StringPiece response_code,
+ StringPiece response_detail,
+ StringPiece body) {
+ GURL url("http://" + path.as_string());
+
+ string status_line = version.as_string() + " " +
+ response_code.as_string() + " " +
+ response_detail.as_string();
+
+ string header = "content-length: " +
+ base::Uint64ToString(static_cast<uint64>(body.length()));
+
+ scoped_refptr<HttpResponseHeaders> response_headers =
+ new HttpResponseHeaders(status_line + '\0' + header + '\0' + '\0');
+
+ AddResponse(url, response_headers, body);
+}
+
+void QuicInMemoryCache::AddResponse(
+ const GURL& url,
+ scoped_refptr<HttpResponseHeaders> response_headers,
+ StringPiece response_body) {
+ string key = GetKey(url);
+ VLOG(1) << "Adding response for: " << key;
+ if (ContainsKey(responses_, key)) {
+ LOG(DFATAL) << "Response for given request already exists!";
+ return;
+ }
+ Response* new_response = new Response();
+ new_response->set_headers(response_headers);
+ new_response->set_body(response_body);
+ responses_[key] = new_response;
+}
+
+void QuicInMemoryCache::AddSpecialResponse(StringPiece path,
+ SpecialResponseType response_type) {
+ GURL url("http://" + path.as_string());
+
+ AddResponse(url, nullptr, string());
+ responses_[GetKey(url)]->response_type_ = response_type;
+}
+
+QuicInMemoryCache::QuicInMemoryCache() {
+ Initialize();
+}
+
+void QuicInMemoryCache::ResetForTests() {
+ STLDeleteValues(&responses_);
+ Initialize();
+}
+
+void QuicInMemoryCache::Initialize() {
+ // If there's no defined cache dir, we have no initialization to do.
+ if (g_quic_in_memory_cache_dir.size() == 0) {
+ VLOG(1) << "No cache directory found. Skipping initialization.";
+ return;
+ }
+ VLOG(1) << "Attempting to initialize QuicInMemoryCache from directory: "
+ << g_quic_in_memory_cache_dir;
+
+ FilePath directory(g_quic_in_memory_cache_dir);
+ base::FileEnumerator file_list(directory,
+ true,
+ base::FileEnumerator::FILES);
+
+ FilePath file = file_list.Next();
+ for (; !file.empty(); file = file_list.Next()) {
+ // Need to skip files in .svn directories
+ if (file.value().find(FILE_PATH_LITERAL("/.svn/")) != string::npos) {
+ continue;
+ }
+
+ string file_contents;
+ base::ReadFileToString(file, &file_contents);
+
+ if (file_contents.length() > INT_MAX) {
+ LOG(WARNING) << "File '" << file.value() << "' too large: "
+ << file_contents.length();
+ continue;
+ }
+ int file_len = static_cast<int>(file_contents.length());
+
+ int headers_end = HttpUtil::LocateEndOfHeaders(file_contents.data(),
+ file_len);
+ if (headers_end < 1) {
+ LOG(DFATAL) << "Headers invalid or empty, ignoring: " << file.value();
+ continue;
+ }
+
+ string raw_headers =
+ HttpUtil::AssembleRawHeaders(file_contents.data(), headers_end);
+
+ scoped_refptr<HttpResponseHeaders> response_headers =
+ new HttpResponseHeaders(raw_headers);
+
+ string base;
+ if (response_headers->GetNormalizedHeader("X-Original-Url", &base)) {
+ response_headers->RemoveHeader("X-Original-Url");
+ // Remove the protocol so we can add it below.
+ if (StartsWithASCII(base, "https://", false)) {
+ base = base.substr(8);
+ } else if (StartsWithASCII(base, "http://", false)) {
+ base = base.substr(7);
+ }
+ } else {
+ base = file.AsUTF8Unsafe();
+ }
+ if (base.length() == 0 || base[0] == '/') {
+ LOG(DFATAL) << "Invalid path, ignoring: " << base;
+ continue;
+ }
+ if (base[base.length() - 1] == ',') {
+ base = base.substr(0, base.length() - 1);
+ }
+
+ GURL url("http://" + base);
+
+ VLOG(1) << "Inserting '" << GetKey(url) << "' into QuicInMemoryCache.";
+
+ StringPiece body(file_contents.data() + headers_end,
+ file_contents.size() - headers_end);
+
+ AddResponse(url, response_headers, body);
+ }
+}
+
+QuicInMemoryCache::~QuicInMemoryCache() {
+ STLDeleteValues(&responses_);
+}
+
+string QuicInMemoryCache::GetKey(const GURL& url) const {
+ // Take everything but the scheme portion of the URL.
+ return url.host() + url.PathForRequest();
+}
+
+} // namespace net
diff --git a/net/quic/quic_in_memory_cache.h b/net/quic/quic_in_memory_cache.h
new file mode 100644
index 0000000..ee3a9de
--- /dev/null
+++ b/net/quic/quic_in_memory_cache.h
@@ -0,0 +1,114 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
+#define NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
+
+#include <string>
+
+#include "base/containers/hash_tables.h"
+#include "base/files/file_util.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_piece.h"
+#include "net/http/http_response_headers.h"
+
+template <typename T> struct DefaultSingletonTraits;
+class GURL;
+
+namespace net {
+
+extern base::FilePath::StringType g_quic_in_memory_cache_dir;
+
+namespace test {
+class QuicInMemoryCachePeer;
+} // namespace
+
+class QuicServer;
+
+// In-memory cache for HTTP responses.
+// Reads from disk cache generated by:
+// `wget -p --save_headers <url>`
+class QuicInMemoryCache {
+ public:
+ enum SpecialResponseType {
+ REGULAR_RESPONSE, // Send the headers and body like a server should.
+ CLOSE_CONNECTION, // Close the connection (sending the close packet).
+ IGNORE_REQUEST, // Do nothing, expect the client to time out.
+ };
+
+ // Container for response header/body pairs.
+ class Response {
+ public:
+ Response();
+ ~Response();
+
+ SpecialResponseType response_type() const { return response_type_; }
+ const HttpResponseHeaders& headers() const { return *headers_.get(); }
+ const base::StringPiece body() const { return base::StringPiece(body_); }
+
+ private:
+ friend class QuicInMemoryCache;
+
+ void set_headers(scoped_refptr<HttpResponseHeaders> headers) {
+ headers_ = headers;
+ }
+ void set_body(base::StringPiece body) {
+ body.CopyToString(&body_);
+ }
+
+ SpecialResponseType response_type_;
+ scoped_refptr<HttpResponseHeaders> headers_;
+ std::string body_;
+
+ DISALLOW_COPY_AND_ASSIGN(Response);
+ };
+
+ // Returns the singleton instance of the cache.
+ static QuicInMemoryCache* GetInstance();
+
+ // Retrieve a response from this cache for a given request.
+ // If no appropriate response exists, nullptr is returned.
+ // Currently, responses are selected based on request URI only.
+ const Response* GetResponse(const GURL& url) const;
+
+ // Adds a simple response to the cache. The response headers will
+ // only contain the "content-length" header with the length of |body|.
+ void AddSimpleResponse(base::StringPiece path,
+ base::StringPiece version,
+ base::StringPiece response_code,
+ base::StringPiece response_detail,
+ base::StringPiece body);
+
+ // Add a response to the cache.
+ void AddResponse(const GURL& url,
+ scoped_refptr<HttpResponseHeaders> response_headers,
+ base::StringPiece response_body);
+
+ // Simulate a special behavior at a particular path.
+ void AddSpecialResponse(base::StringPiece path,
+ SpecialResponseType response_type);
+
+ private:
+ typedef base::hash_map<std::string, Response*> ResponseMap;
+ friend struct DefaultSingletonTraits<QuicInMemoryCache>;
+ friend class test::QuicInMemoryCachePeer;
+
+ QuicInMemoryCache();
+ ~QuicInMemoryCache();
+
+ void ResetForTests();
+
+ void Initialize();
+
+ std::string GetKey(const GURL& url) const;
+
+ // Cached responses.
+ ResponseMap responses_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicInMemoryCache);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_IN_MEMORY_CACHE_H_
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
new file mode 100644
index 0000000..9d12a01
--- /dev/null
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -0,0 +1,1041 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "net/base/capturing_net_log.h"
+#include "net/base/net_log_unittest.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/mock_cert_verifier.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_auth_handler_factory.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_network_transaction.h"
+#include "net/http/http_server_properties_impl.h"
+#include "net/http/http_stream.h"
+#include "net/http/http_stream_factory.h"
+#include "net/http/http_transaction_test_util.h"
+#include "net/http/transport_security_state.h"
+#include "net/proxy/proxy_config_service_fixed.h"
+#include "net/proxy/proxy_resolver.h"
+#include "net/proxy/proxy_service.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/test_tools/crypto_test_utils.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/mock_client_socket_pool_manager.h"
+#include "net/socket/socket_test_util.h"
+#include "net/socket/ssl_client_socket.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/ssl/ssl_config_service_defaults.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+// This is the expected return from a current server advertising QUIC.
+static const char kQuicAlternateProtocolHttpHeader[] =
+ "Alternate-Protocol: 80:quic\r\n\r\n";
+static const char kQuicAlternateProtocol50pctHttpHeader[] =
+ "Alternate-Protocol: 80:quic,p=.5\r\n\r\n";
+static const char kQuicAlternateProtocolHttpsHeader[] =
+ "Alternate-Protocol: 443:quic\r\n\r\n";
+
+} // namespace
+
+namespace net {
+namespace test {
+
+// Helper class to encapsulate MockReads and MockWrites for QUIC.
+// Simplify ownership issues and the interaction with the MockSocketFactory.
+class MockQuicData {
+ public:
+ ~MockQuicData() {
+ STLDeleteElements(&packets_);
+ }
+
+ void AddRead(scoped_ptr<QuicEncryptedPacket> packet) {
+ reads_.push_back(MockRead(SYNCHRONOUS, packet->data(), packet->length(),
+ sequence_number_++));
+ packets_.push_back(packet.release());
+ }
+
+ void AddRead(IoMode mode, int rv) {
+ reads_.push_back(MockRead(mode, rv));
+ }
+
+ void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
+ writes_.push_back(MockWrite(SYNCHRONOUS, packet->data(), packet->length(),
+ sequence_number_++));
+ packets_.push_back(packet.release());
+ }
+
+ void AddDelayedSocketDataToFactory(MockClientSocketFactory* factory,
+ size_t delay) {
+ MockRead* reads = reads_.empty() ? nullptr : &reads_[0];
+ MockWrite* writes = writes_.empty() ? nullptr : &writes_[0];
+ socket_data_.reset(new DelayedSocketData(
+ delay, reads, reads_.size(), writes, writes_.size()));
+ factory->AddSocketDataProvider(socket_data_.get());
+ }
+
+ private:
+ std::vector<QuicEncryptedPacket*> packets_;
+ std::vector<MockWrite> writes_;
+ std::vector<MockRead> reads_;
+ size_t sequence_number_;
+ scoped_ptr<SocketDataProvider> socket_data_;
+};
+
+class QuicNetworkTransactionTest
+ : public PlatformTest,
+ public ::testing::WithParamInterface<QuicVersion> {
+ protected:
+ QuicNetworkTransactionTest()
+ : clock_(new MockClock),
+ maker_(GetParam(), 0, clock_),
+ ssl_config_service_(new SSLConfigServiceDefaults),
+ proxy_service_(ProxyService::CreateDirect()),
+ auth_handler_factory_(
+ HttpAuthHandlerFactory::CreateDefault(&host_resolver_)),
+ random_generator_(0),
+ hanging_data_(nullptr, 0, nullptr, 0) {
+ request_.method = "GET";
+ request_.url = GURL("http://www.google.com/");
+ request_.load_flags = 0;
+ clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+ }
+
+ virtual void SetUp() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ virtual void TearDown() {
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ // Empty the current queue.
+ base::MessageLoop::current()->RunUntilIdle();
+ PlatformTest::TearDown();
+ NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+ base::MessageLoop::current()->RunUntilIdle();
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructConnectionClosePacket(
+ QuicPacketSequenceNumber num) {
+ return maker_.MakeConnectionClosePacket(num);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked) {
+ return maker_.MakeAckPacket(2, largest_received, least_unacked, true);
+ }
+
+ SpdyHeaderBlock GetRequestHeaders(const std::string& method,
+ const std::string& scheme,
+ const std::string& path) {
+ return maker_.GetRequestHeaders(method, scheme, path);
+ }
+
+ SpdyHeaderBlock GetResponseHeaders(const std::string& status) {
+ return maker_.GetResponseHeaders(status);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data) {
+ return maker_.MakeDataPacket(
+ sequence_number, stream_id, should_include_version, fin, offset, data);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ return maker_.MakeRequestHeadersPacket(
+ sequence_number, stream_id, should_include_version, fin, headers);
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ return maker_.MakeResponseHeadersPacket(
+ sequence_number, stream_id, should_include_version, fin, headers);
+ }
+
+ void CreateSession() {
+ CreateSessionWithFactory(&socket_factory_, false);
+ }
+
+ void CreateSessionWithNextProtos() {
+ CreateSessionWithFactory(&socket_factory_, true);
+ }
+
+ // If |use_next_protos| is true, enables SPDY and QUIC.
+ void CreateSessionWithFactory(ClientSocketFactory* socket_factory,
+ bool use_next_protos) {
+ params_.enable_quic = true;
+ params_.quic_clock = clock_;
+ params_.quic_random = &random_generator_;
+ params_.client_socket_factory = socket_factory;
+ params_.quic_crypto_client_stream_factory = &crypto_client_stream_factory_;
+ params_.host_resolver = &host_resolver_;
+ params_.cert_verifier = &cert_verifier_;
+ params_.transport_security_state = &transport_security_state_;
+ params_.proxy_service = proxy_service_.get();
+ params_.ssl_config_service = ssl_config_service_.get();
+ params_.http_auth_handler_factory = auth_handler_factory_.get();
+ params_.http_server_properties = http_server_properties.GetWeakPtr();
+ params_.quic_supported_versions = SupportedVersions(GetParam());
+
+ if (use_next_protos) {
+ params_.use_alternate_protocols = true;
+ params_.next_protos = NextProtosSpdy3();
+ }
+
+ session_ = new HttpNetworkSession(params_);
+ session_->quic_stream_factory()->set_require_confirmation(false);
+ }
+
+ void CheckWasQuicResponse(const scoped_ptr<HttpNetworkTransaction>& trans) {
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != nullptr);
+ ASSERT_TRUE(response->headers.get() != nullptr);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ EXPECT_TRUE(response->was_fetched_via_spdy);
+ EXPECT_TRUE(response->was_npn_negotiated);
+ EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_QUIC1_SPDY3,
+ response->connection_info);
+ }
+
+ void CheckWasHttpResponse(const scoped_ptr<HttpNetworkTransaction>& trans) {
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response != nullptr);
+ ASSERT_TRUE(response->headers.get() != nullptr);
+ EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine());
+ EXPECT_FALSE(response->was_fetched_via_spdy);
+ EXPECT_FALSE(response->was_npn_negotiated);
+ EXPECT_EQ(HttpResponseInfo::CONNECTION_INFO_HTTP1,
+ response->connection_info);
+ }
+
+ void CheckResponseData(HttpNetworkTransaction* trans,
+ const std::string& expected) {
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans, &response_data));
+ EXPECT_EQ(expected, response_data);
+ }
+
+ void RunTransaction(HttpNetworkTransaction* trans) {
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback.WaitForResult());
+ }
+
+ void SendRequestAndExpectHttpResponse(const std::string& expected) {
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ RunTransaction(trans.get());
+ CheckWasHttpResponse(trans);
+ CheckResponseData(trans.get(), expected);
+ }
+
+ void SendRequestAndExpectQuicResponse(const std::string& expected) {
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ RunTransaction(trans.get());
+ CheckWasQuicResponse(trans);
+ CheckResponseData(trans.get(), expected);
+ }
+
+ void AddQuicAlternateProtocolMapping(
+ MockCryptoClientStream::HandshakeMode handshake_mode) {
+ crypto_client_stream_factory_.set_handshake_mode(handshake_mode);
+ session_->http_server_properties()->SetAlternateProtocol(
+ HostPortPair::FromURL(request_.url), 80, QUIC, 1);
+ }
+
+ void ExpectBrokenAlternateProtocolMapping() {
+ ASSERT_TRUE(session_->http_server_properties()->HasAlternateProtocol(
+ HostPortPair::FromURL(request_.url)));
+ const AlternateProtocolInfo alternate =
+ session_->http_server_properties()->GetAlternateProtocol(
+ HostPortPair::FromURL(request_.url));
+ EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
+ }
+
+ void ExpectQuicAlternateProtocolMapping() {
+ ASSERT_TRUE(session_->http_server_properties()->HasAlternateProtocol(
+ HostPortPair::FromURL(request_.url)));
+ const AlternateProtocolInfo alternate =
+ session_->http_server_properties()->GetAlternateProtocol(
+ HostPortPair::FromURL(request_.url));
+ EXPECT_EQ(QUIC, alternate.protocol);
+ }
+
+ void AddHangingNonAlternateProtocolSocketData() {
+ MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING);
+ hanging_data_.set_connect_data(hanging_connect);
+ socket_factory_.AddSocketDataProvider(&hanging_data_);
+ }
+
+ MockClock* clock_; // Owned by QuicStreamFactory after CreateSession.
+ QuicTestPacketMaker maker_;
+ scoped_refptr<HttpNetworkSession> session_;
+ MockClientSocketFactory socket_factory_;
+ MockCryptoClientStreamFactory crypto_client_stream_factory_;
+ MockHostResolver host_resolver_;
+ MockCertVerifier cert_verifier_;
+ TransportSecurityState transport_security_state_;
+ scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_;
+ scoped_ptr<ProxyService> proxy_service_;
+ scoped_ptr<HttpAuthHandlerFactory> auth_handler_factory_;
+ MockRandom random_generator_;
+ HttpServerPropertiesImpl http_server_properties;
+ HttpNetworkSession::Params params_;
+ HttpRequestInfo request_;
+ CapturingBoundNetLog net_log_;
+ StaticSocketDataProvider hanging_data_;
+};
+
+INSTANTIATE_TEST_CASE_P(Version, QuicNetworkTransactionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicNetworkTransactionTest, ForceQuic) {
+ params_.origin_to_force_quic_on =
+ HostPortPair::FromString("www.google.com:80");
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ CreateSession();
+
+ SendRequestAndExpectQuicResponse("hello!");
+
+ // Check that the NetLog was filled reasonably.
+ net::CapturingNetLog::CapturedEntryList entries;
+ net_log_.GetEntries(&entries);
+ EXPECT_LT(0u, entries.size());
+
+ // Check that we logged a QUIC_SESSION_PACKET_RECEIVED.
+ int pos = net::ExpectLogContainsSomewhere(
+ entries, 0,
+ net::NetLog::TYPE_QUIC_SESSION_PACKET_RECEIVED,
+ net::NetLog::PHASE_NONE);
+ EXPECT_LT(0, pos);
+
+ // ... and also a TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED.
+ pos = net::ExpectLogContainsSomewhere(
+ entries, 0,
+ net::NetLog::TYPE_QUIC_SESSION_PACKET_HEADER_RECEIVED,
+ net::NetLog::PHASE_NONE);
+ EXPECT_LT(0, pos);
+
+ std::string packet_sequence_number;
+ ASSERT_TRUE(entries[pos].GetStringValue(
+ "packet_sequence_number", &packet_sequence_number));
+ EXPECT_EQ("1", packet_sequence_number);
+
+ // ... and also a QUIC_SESSION_STREAM_FRAME_RECEIVED.
+ pos = net::ExpectLogContainsSomewhere(
+ entries, 0,
+ net::NetLog::TYPE_QUIC_SESSION_STREAM_FRAME_RECEIVED,
+ net::NetLog::PHASE_NONE);
+ EXPECT_LT(0, pos);
+
+ int log_stream_id;
+ ASSERT_TRUE(entries[pos].GetIntegerValue("stream_id", &log_stream_id));
+ EXPECT_EQ(3, log_stream_id);
+}
+
+TEST_P(QuicNetworkTransactionTest, QuicProxy) {
+ proxy_service_.reset(
+ ProxyService::CreateFixedFromPacResult("QUIC myproxy:70"));
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // There is no need to set up an alternate protocol job, because
+ // no attempt will be made to speak to the proxy over TCP.
+
+ CreateSession();
+
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) {
+ params_.origin_to_force_quic_on =
+ HostPortPair::FromString("www.google.com:80");
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddRead(ASYNC, ERR_SOCKET_NOT_CONNECTED);
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0);
+
+ CreateSession();
+
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+}
+
+TEST_P(QuicNetworkTransactionTest, DoNotForceQuicForHttps) {
+ // Attempt to "force" quic on 443, which will not be honored.
+ params_.origin_to_force_quic_on =
+ HostPortPair::FromString("www.google.com:443");
+
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider data(http_reads, arraysize(http_reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&data);
+ SSLSocketDataProvider ssl(ASYNC, OK);
+ socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+ CreateSession();
+
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) {
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocolHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ CreateSessionWithNextProtos();
+
+ SendRequestAndExpectHttpResponse("hello world");
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolProbabilityForQuic) {
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocol50pctHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ params_.alternate_protocol_probability_threshold = .25;
+ CreateSessionWithNextProtos();
+
+ SendRequestAndExpectHttpResponse("hello world");
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, DontUseAlternateProtocolProbabilityForQuic) {
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocol50pctHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ params_.alternate_protocol_probability_threshold = .75;
+ CreateSessionWithNextProtos();
+
+ SendRequestAndExpectHttpResponse("hello world");
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest,
+ DontUseAlternateProtocolWithBadProbabilityForQuic) {
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead("Alternate-Protocol: 443:quic,p=2\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ params_.alternate_protocol_probability_threshold = .75;
+ CreateSessionWithNextProtos();
+
+ SendRequestAndExpectHttpResponse("hello world");
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) {
+ params_.origin_to_force_quic_on =
+ HostPortPair::FromString("www.google.com:443");
+
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocolHttpsHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ CreateSessionWithNextProtos();
+
+ // TODO(rtenneti): Test QUIC over HTTPS, GetSSLInfo().
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest, HungAlternateProtocol) {
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ MockWrite http_writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "Host: www.google.com\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "Connection: keep-alive\r\n\r\n")
+ };
+
+ MockRead http_reads[] = {
+ MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 4, kQuicAlternateProtocolHttpHeader),
+ MockRead(SYNCHRONOUS, 5, "hello world"),
+ MockRead(SYNCHRONOUS, OK, 6)
+ };
+
+ DeterministicMockClientSocketFactory socket_factory;
+
+ DeterministicSocketData http_data(http_reads, arraysize(http_reads),
+ http_writes, arraysize(http_writes));
+ socket_factory.AddSocketDataProvider(&http_data);
+
+ // The QUIC transaction will not be allowed to complete.
+ MockWrite quic_writes[] = {
+ MockWrite(ASYNC, ERR_IO_PENDING, 0)
+ };
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_IO_PENDING, 1),
+ };
+ DeterministicSocketData quic_data(quic_reads, arraysize(quic_reads),
+ quic_writes, arraysize(quic_writes));
+ socket_factory.AddSocketDataProvider(&quic_data);
+
+ // The HTTP transaction will complete.
+ DeterministicSocketData http_data2(http_reads, arraysize(http_reads),
+ http_writes, arraysize(http_writes));
+ socket_factory.AddSocketDataProvider(&http_data2);
+
+ CreateSessionWithFactory(&socket_factory, true);
+
+ // Run the first request.
+ http_data.StopAfter(arraysize(http_reads) + arraysize(http_writes));
+ SendRequestAndExpectHttpResponse("hello world");
+ ASSERT_TRUE(http_data.at_read_eof());
+ ASSERT_TRUE(http_data.at_write_eof());
+
+ // Now run the second request in which the QUIC socket hangs,
+ // and verify the the transaction continues over HTTP.
+ http_data2.StopAfter(arraysize(http_reads) + arraysize(http_writes));
+ SendRequestAndExpectHttpResponse("hello world");
+
+ ASSERT_TRUE(http_data2.at_read_eof());
+ ASSERT_TRUE(http_data2.at_write_eof());
+ ASSERT_TRUE(!quic_data.at_read_eof());
+ ASSERT_TRUE(!quic_data.at_write_eof());
+}
+
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ nullptr,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectQuicResponse("hello!");
+}
+
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithProxy) {
+ proxy_service_.reset(
+ ProxyService::CreateFixedFromPacResult("PROXY myproxy:70"));
+
+ // Since we are using a proxy, the QUIC job will not succeed.
+ MockWrite http_writes[] = {
+ MockWrite(SYNCHRONOUS, 0, "GET http://www.google.com/ HTTP/1.1\r\n"),
+ MockWrite(SYNCHRONOUS, 1, "Host: www.google.com\r\n"),
+ MockWrite(SYNCHRONOUS, 2, "Proxy-Connection: keep-alive\r\n\r\n")
+ };
+
+ MockRead http_reads[] = {
+ MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
+ MockRead(SYNCHRONOUS, 4, kQuicAlternateProtocolHttpHeader),
+ MockRead(SYNCHRONOUS, 5, "hello world"),
+ MockRead(SYNCHRONOUS, OK, 6)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ http_writes, arraysize(http_writes));
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ nullptr,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddRead(
+ ConstructResponseHeadersPacket(1, kClientDataStreamId1, false, false,
+ GetResponseHeaders("200 OK")));
+ mock_quic_data.AddRead(
+ ConstructDataPacket(2, kClientDataStreamId1, false, true, 0, "hello!"));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddRead(SYNCHRONOUS, 0); // EOF
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 1);
+
+ // The non-alternate protocol job needs to hang in order to guarantee that
+ // the alternate-protocol job will "win".
+ AddHangingNonAlternateProtocolSocketData();
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously. Of course, even though QUIC *could* perform a 0-RTT
+ // connection to the the server, in this test we require confirmation
+ // before encrypting so the HTTP job will still start.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address,
+ CompletionCallback(), nullptr, net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ session_->quic_stream_factory()->set_require_confirmation(true);
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+ QuicSession::HANDSHAKE_CONFIRMED);
+ EXPECT_EQ(OK, callback.WaitForResult());
+}
+
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocol) {
+ // Alternate-protocol job
+ scoped_ptr<QuicEncryptedPacket> close(ConstructConnectionClosePacket(1));
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, close->data(), close->length()),
+ MockRead(ASYNC, OK), // EOF
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job which will succeed even though the alternate job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ SendRequestAndExpectHttpResponse("hello from http");
+ ExpectBrokenAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) {
+ // Alternate-protocol job
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job which will succeed even though the alternate job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ SendRequestAndExpectHttpResponse("hello from http");
+ ExpectBrokenAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, NoBrokenAlternateProtocolIfTcpFails) {
+ // Alternate-protocol job will fail when the session attempts to read.
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job will also fail.
+ MockRead http_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ http_data.set_connect_data(MockConnect(ASYNC, ERR_SOCKET_NOT_CONNECTED));
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ scoped_ptr<HttpNetworkTransaction> trans(
+ new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get()));
+ TestCompletionCallback callback;
+ int rv = trans->Start(&request_, callback.callback(), net_log_.bound());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, callback.WaitForResult());
+ ExpectQuicAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) {
+ // Alternate-protocol job
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ AddHangingNonAlternateProtocolSocketData();
+
+ // Second Alternate-protocol job which will race with the TCP job.
+ StaticSocketDataProvider quic_data2(quic_reads, arraysize(quic_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data2);
+
+ // Final job that will proceed when the QUIC job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+
+ SendRequestAndExpectHttpResponse("hello from http");
+
+ ExpectBrokenAlternateProtocolMapping();
+
+ EXPECT_TRUE(quic_data.at_read_eof());
+ EXPECT_TRUE(quic_data.at_write_eof());
+}
+
+TEST_P(QuicNetworkTransactionTest, DISABLED_HangingZeroRttFallback) {
+ // Alternate-protocol job
+ MockRead quic_reads[] = {
+ MockRead(ASYNC, ERR_IO_PENDING),
+ };
+ StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job that will proceed when the QUIC job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+
+ SendRequestAndExpectHttpResponse("hello from http");
+}
+
+TEST_P(QuicNetworkTransactionTest, BrokenAlternateProtocolOnConnectFailure) {
+ // Alternate-protocol job will fail before creating a QUIC session.
+ StaticSocketDataProvider quic_data(nullptr, 0, nullptr, 0);
+ quic_data.set_connect_data(MockConnect(SYNCHRONOUS,
+ ERR_INTERNET_DISCONNECTED));
+ socket_factory_.AddSocketDataProvider(&quic_data);
+
+ // Main job which will succeed even though the alternate job fails.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n\r\n"),
+ MockRead("hello from http"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START);
+ SendRequestAndExpectHttpResponse("hello from http");
+
+ ExpectBrokenAlternateProtocolMapping();
+}
+
+TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) {
+ MockQuicData mock_quic_data;
+ mock_quic_data.AddRead(ConstructConnectionClosePacket(1));
+ mock_quic_data.AddWrite(
+ ConstructRequestHeadersPacket(1, kClientDataStreamId1, true, true,
+ GetRequestHeaders("GET", "http", "/")));
+ mock_quic_data.AddWrite(ConstructAckPacket(2, 1));
+ mock_quic_data.AddDelayedSocketDataToFactory(&socket_factory_, 0);
+
+ // When the QUIC connection fails, we will try the request again over HTTP.
+ MockRead http_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead(kQuicAlternateProtocolHttpHeader),
+ MockRead("hello world"),
+ MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
+ MockRead(ASYNC, OK)
+ };
+
+ StaticSocketDataProvider http_data(http_reads, arraysize(http_reads),
+ nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&http_data);
+
+ // In order for a new QUIC session to be established via alternate-protocol
+ // without racing an HTTP connection, we need the host resolution to happen
+ // synchronously.
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule("www.google.com", "192.168.0.1", "");
+ HostResolver::RequestInfo info(HostPortPair("www.google.com", 80));
+ AddressList address;
+ host_resolver_.Resolve(info,
+ DEFAULT_PRIORITY,
+ &address,
+ CompletionCallback(),
+ nullptr,
+ net_log_.bound());
+
+ CreateSessionWithNextProtos();
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT);
+ SendRequestAndExpectHttpResponse("hello world");
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
new file mode 100644
index 0000000..17a4a10
--- /dev/null
+++ b/net/quic/quic_packet_creator.cc
@@ -0,0 +1,522 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_creator.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_fec_group.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::make_pair;
+using std::max;
+using std::min;
+using std::pair;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+// Default max packets in an FEC group.
+static const size_t kDefaultMaxPacketsPerFecGroup = 10;
+// Lowest max packets in an FEC group.
+static const size_t kLowestMaxPacketsPerFecGroup = 2;
+
+} // namespace
+
+// A QuicRandom wrapper that gets a bucket of entropy and distributes it
+// bit-by-bit. Replenishes the bucket as needed. Not thread-safe. Expose this
+// class if single bit randomness is needed elsewhere.
+class QuicRandomBoolSource {
+ public:
+ // random: Source of entropy. Not owned.
+ explicit QuicRandomBoolSource(QuicRandom* random)
+ : random_(random),
+ bit_bucket_(0),
+ bit_mask_(0) {}
+
+ ~QuicRandomBoolSource() {}
+
+ // Returns the next random bit from the bucket.
+ bool RandBool() {
+ if (bit_mask_ == 0) {
+ bit_bucket_ = random_->RandUint64();
+ bit_mask_ = 1;
+ }
+ bool result = ((bit_bucket_ & bit_mask_) != 0);
+ bit_mask_ <<= 1;
+ return result;
+ }
+
+ private:
+ // Source of entropy.
+ QuicRandom* random_;
+ // Stored random bits.
+ uint64 bit_bucket_;
+ // The next available bit has "1" in the mask. Zero means empty bucket.
+ uint64 bit_mask_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicRandomBoolSource);
+};
+
+QuicPacketCreator::QuicPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator)
+ : connection_id_(connection_id),
+ encryption_level_(ENCRYPTION_NONE),
+ framer_(framer),
+ random_bool_source_(new QuicRandomBoolSource(random_generator)),
+ sequence_number_(0),
+ should_fec_protect_(false),
+ fec_group_number_(0),
+ send_version_in_packet_(!framer->is_server()),
+ max_packet_length_(kDefaultMaxPacketSize),
+ max_packets_per_fec_group_(kDefaultMaxPacketsPerFecGroup),
+ connection_id_length_(PACKET_8BYTE_CONNECTION_ID),
+ next_sequence_number_length_(PACKET_1BYTE_SEQUENCE_NUMBER),
+ sequence_number_length_(next_sequence_number_length_),
+ packet_size_(0) {
+ framer_->set_fec_builder(this);
+}
+
+QuicPacketCreator::~QuicPacketCreator() {
+}
+
+void QuicPacketCreator::OnBuiltFecProtectedPayload(
+ const QuicPacketHeader& header, StringPiece payload) {
+ if (fec_group_.get()) {
+ DCHECK_NE(0u, header.fec_group);
+ fec_group_->Update(encryption_level_, header, payload);
+ }
+}
+
+void QuicPacketCreator::set_max_packets_per_fec_group(
+ size_t max_packets_per_fec_group) {
+ max_packets_per_fec_group_ = max(kLowestMaxPacketsPerFecGroup,
+ max_packets_per_fec_group);
+ DCHECK_LT(0u, max_packets_per_fec_group_);
+}
+
+bool QuicPacketCreator::ShouldSendFec(bool force_close) const {
+ DCHECK(!HasPendingFrames());
+ return fec_group_.get() != nullptr && fec_group_->NumReceivedPackets() > 0 &&
+ (force_close ||
+ fec_group_->NumReceivedPackets() >= max_packets_per_fec_group_);
+}
+
+bool QuicPacketCreator::IsFecGroupOpen() const {
+ return fec_group_.get() != nullptr;
+}
+
+void QuicPacketCreator::StartFecProtectingPackets() {
+ if (!IsFecEnabled()) {
+ LOG(DFATAL) << "Cannot start FEC protection when FEC is not enabled.";
+ return;
+ }
+ // TODO(jri): This currently requires that the generator flush out any
+ // pending frames when FEC protection is turned on. If current packet can be
+ // converted to an FEC protected packet, do it. This will require the
+ // generator to check if the resulting expansion still allows the incoming
+ // frame to be added to the packet.
+ if (HasPendingFrames()) {
+ LOG(DFATAL) << "Cannot start FEC protection with pending frames.";
+ return;
+ }
+ DCHECK(!should_fec_protect_);
+ should_fec_protect_ = true;
+}
+
+void QuicPacketCreator::StopFecProtectingPackets() {
+ if (fec_group_.get() != nullptr) {
+ LOG(DFATAL) << "Cannot stop FEC protection with open FEC group.";
+ return;
+ }
+ DCHECK(should_fec_protect_);
+ should_fec_protect_ = false;
+ fec_group_number_ = 0;
+}
+
+bool QuicPacketCreator::IsFecProtected() const {
+ return should_fec_protect_;
+}
+
+bool QuicPacketCreator::IsFecEnabled() const {
+ return max_packets_per_fec_group_ > 0;
+}
+
+InFecGroup QuicPacketCreator::MaybeUpdateLengthsAndStartFec() {
+ if (fec_group_.get() != nullptr) {
+ // Don't update any lengths when an FEC group is open, to ensure same
+ // packet header size in all packets within a group.
+ return IN_FEC_GROUP;
+ }
+ if (!queued_frames_.empty()) {
+ // Don't change creator state if there are frames queued.
+ return fec_group_.get() == nullptr ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ }
+
+ // Update sequence number length only on packet and FEC group boundaries.
+ sequence_number_length_ = next_sequence_number_length_;
+
+ if (!should_fec_protect_) {
+ return NOT_IN_FEC_GROUP;
+ }
+ // Start a new FEC group since protection is on. Set the fec group number to
+ // the sequence number of the next packet.
+ fec_group_number_ = sequence_number() + 1;
+ fec_group_.reset(new QuicFecGroup());
+ return IN_FEC_GROUP;
+}
+
+// Stops serializing version of the protocol in packets sent after this call.
+// A packet that is already open might send kQuicVersionSize bytes less than the
+// maximum packet size if we stop sending version before it is serialized.
+void QuicPacketCreator::StopSendingVersion() {
+ DCHECK(send_version_in_packet_);
+ send_version_in_packet_ = false;
+ if (packet_size_ > 0) {
+ DCHECK_LT(kQuicVersionSize, packet_size_);
+ packet_size_ -= kQuicVersionSize;
+ }
+}
+
+void QuicPacketCreator::UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window) {
+ DCHECK_LE(least_packet_awaited_by_peer, sequence_number_ + 1);
+ // Since the packet creator will not change sequence number length mid FEC
+ // group, include the size of an FEC group to be safe.
+ const QuicPacketSequenceNumber current_delta =
+ max_packets_per_fec_group_ + sequence_number_ + 1
+ - least_packet_awaited_by_peer;
+ const uint64 congestion_window_packets =
+ congestion_window / max_packet_length_;
+ const uint64 delta = max(current_delta, congestion_window_packets);
+ next_sequence_number_length_ =
+ QuicFramer::GetMinSequenceNumberLength(delta * 4);
+}
+
+bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id,
+ QuicStreamOffset offset) const {
+ // TODO(jri): This is a simple safe decision for now, but make
+ // is_in_fec_group a parameter. Same as with all public methods in
+ // QuicPacketCreator.
+ return BytesFree() >
+ QuicFramer::GetMinStreamFrameSize(id, offset, true,
+ should_fec_protect_ ? IN_FEC_GROUP :
+ NOT_IN_FEC_GROUP);
+}
+
+// static
+size_t QuicPacketCreator::StreamFramePacketOverhead(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length,
+ QuicStreamOffset offset,
+ InFecGroup is_in_fec_group) {
+ return GetPacketHeaderSize(connection_id_length, include_version,
+ sequence_number_length, is_in_fec_group) +
+ // Assumes this is a stream with a single lone packet.
+ QuicFramer::GetMinStreamFrameSize(1u, offset, true, is_in_fec_group);
+}
+
+size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame) {
+ DCHECK_GT(max_packet_length_, StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, offset, IN_FEC_GROUP));
+
+ InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec();
+
+ LOG_IF(DFATAL, !HasRoomForStreamFrame(id, offset))
+ << "No room for Stream frame, BytesFree: " << BytesFree()
+ << " MinStreamFrameSize: "
+ << QuicFramer::GetMinStreamFrameSize(id, offset, true, is_in_fec_group);
+
+ if (data.Empty()) {
+ LOG_IF(DFATAL, !fin)
+ << "Creating a stream frame with no data or fin.";
+ // Create a new packet for the fin, if necessary.
+ *frame = QuicFrame(new QuicStreamFrame(id, true, offset, data));
+ return 0;
+ }
+
+ const size_t data_size = data.TotalBufferSize();
+ size_t min_frame_size = QuicFramer::GetMinStreamFrameSize(
+ id, offset, /* last_frame_in_packet= */ true, is_in_fec_group);
+ size_t bytes_consumed = min<size_t>(BytesFree() - min_frame_size, data_size);
+
+ bool set_fin = fin && bytes_consumed == data_size; // Last frame.
+ IOVector frame_data;
+ frame_data.AppendIovecAtMostBytes(data.iovec(), data.Size(),
+ bytes_consumed);
+ DCHECK_EQ(frame_data.TotalBufferSize(), bytes_consumed);
+ *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, frame_data));
+ return bytes_consumed;
+}
+
+size_t QuicPacketCreator::CreateStreamFrameWithNotifier(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier* notifier,
+ QuicFrame* frame) {
+ size_t bytes_consumed = CreateStreamFrame(id, data, offset, fin, frame);
+
+ // The frame keeps track of the QuicAckNotifier until it is serialized into
+ // a packet. At that point the notifier is informed of the sequence number
+ // of the packet that this frame was eventually sent in.
+ frame->stream_frame->notifier = notifier;
+
+ return bytes_consumed;
+}
+
+SerializedPacket QuicPacketCreator::ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length) {
+ DCHECK(fec_group_.get() == nullptr);
+ const QuicSequenceNumberLength saved_length = sequence_number_length_;
+ const QuicSequenceNumberLength saved_next_length =
+ next_sequence_number_length_;
+ const bool saved_should_fec_protect = should_fec_protect_;
+
+ // Temporarily set the sequence number length and stop FEC protection.
+ sequence_number_length_ = original_length;
+ next_sequence_number_length_ = original_length;
+ should_fec_protect_ = false;
+
+ // Serialize the packet and restore the FEC and sequence number length state.
+ SerializedPacket serialized_packet = SerializeAllFrames(frames);
+ sequence_number_length_ = saved_length;
+ next_sequence_number_length_ = saved_next_length;
+ should_fec_protect_ = saved_should_fec_protect;
+
+ return serialized_packet;
+}
+
+SerializedPacket QuicPacketCreator::SerializeAllFrames(
+ const QuicFrames& frames) {
+ // TODO(satyamshekhar): Verify that this DCHECK won't fail. What about queued
+ // frames from SendStreamData()[send_stream_should_flush_ == false &&
+ // data.empty() == true] and retransmit due to RTO.
+ DCHECK_EQ(0u, queued_frames_.size());
+ LOG_IF(DFATAL, frames.empty())
+ << "Attempt to serialize empty packet";
+ for (size_t i = 0; i < frames.size(); ++i) {
+ bool success = AddFrame(frames[i], false);
+ DCHECK(success);
+ }
+ SerializedPacket packet = SerializePacket();
+ DCHECK(packet.retransmittable_frames == nullptr);
+ return packet;
+}
+
+bool QuicPacketCreator::HasPendingFrames() const {
+ return !queued_frames_.empty();
+}
+
+bool QuicPacketCreator::HasPendingRetransmittableFrames() const {
+ return queued_retransmittable_frames_.get() != nullptr &&
+ !queued_retransmittable_frames_->frames().empty();
+}
+
+size_t QuicPacketCreator::ExpansionOnNewFrame() const {
+ // If packet is FEC protected, there's no expansion.
+ if (should_fec_protect_) {
+ return 0;
+ }
+ // If the last frame in the packet is a stream frame, then it will expand to
+ // include the stream_length field when a new frame is added.
+ bool has_trailing_stream_frame =
+ !queued_frames_.empty() && queued_frames_.back().type == STREAM_FRAME;
+ return has_trailing_stream_frame ? kQuicStreamPayloadLengthSize : 0;
+}
+
+size_t QuicPacketCreator::BytesFree() const {
+ const size_t max_plaintext_size =
+ framer_->GetMaxPlaintextSize(max_packet_length_);
+ DCHECK_GE(max_plaintext_size, PacketSize());
+ return max_plaintext_size - min(max_plaintext_size, PacketSize()
+ + ExpansionOnNewFrame());
+}
+
+size_t QuicPacketCreator::PacketSize() const {
+ if (!queued_frames_.empty()) {
+ return packet_size_;
+ }
+ if (fec_group_.get() == nullptr) {
+ // Update sequence number length on packet and FEC boundary.
+ sequence_number_length_ = next_sequence_number_length_;
+ }
+ packet_size_ = GetPacketHeaderSize(
+ connection_id_length_, send_version_in_packet_, sequence_number_length_,
+ should_fec_protect_ ? IN_FEC_GROUP : NOT_IN_FEC_GROUP);
+ return packet_size_;
+}
+
+bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) {
+ return AddFrame(frame, true);
+}
+
+SerializedPacket QuicPacketCreator::SerializePacket() {
+ LOG_IF(DFATAL, queued_frames_.empty())
+ << "Attempt to serialize empty packet";
+ DCHECK_GE(sequence_number_ + 1, fec_group_number_);
+ QuicPacketHeader header;
+ FillPacketHeader(should_fec_protect_ ? fec_group_number_ : 0, false, &header);
+
+ MaybeAddPadding();
+
+ size_t max_plaintext_size =
+ framer_->GetMaxPlaintextSize(max_packet_length_);
+ DCHECK_GE(max_plaintext_size, packet_size_);
+ // ACK Frames will be truncated due to length only if they're the only frame
+ // in the packet, and if packet_size_ was set to max_plaintext_size. If
+ // truncation due to length occurred, then GetSerializedFrameLength will have
+ // returned all bytes free.
+ bool possibly_truncated_by_length = packet_size_ == max_plaintext_size &&
+ queued_frames_.size() == 1 &&
+ queued_frames_.back().type == ACK_FRAME;
+ SerializedPacket serialized =
+ framer_->BuildDataPacket(header, queued_frames_, packet_size_);
+ LOG_IF(DFATAL, !serialized.packet)
+ << "Failed to serialize " << queued_frames_.size() << " frames.";
+ // Because of possible truncation, we can't be confident that our
+ // packet size calculation worked correctly.
+ if (!possibly_truncated_by_length) {
+ DCHECK_EQ(packet_size_, serialized.packet->length());
+ }
+ packet_size_ = 0;
+ queued_frames_.clear();
+ serialized.retransmittable_frames = queued_retransmittable_frames_.release();
+ return serialized;
+}
+
+SerializedPacket QuicPacketCreator::SerializeFec() {
+ if (fec_group_.get() == nullptr || fec_group_->NumReceivedPackets() <= 0) {
+ LOG(DFATAL) << "SerializeFEC called but no group or zero packets in group.";
+ // TODO(jri): Make this a public method of framer?
+ SerializedPacket kNoPacket(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0,
+ nullptr);
+ return kNoPacket;
+ }
+ DCHECK_EQ(0u, queued_frames_.size());
+ QuicPacketHeader header;
+ FillPacketHeader(fec_group_number_, true, &header);
+ QuicFecData fec_data;
+ fec_data.fec_group = fec_group_->min_protected_packet();
+ fec_data.redundancy = fec_group_->payload_parity();
+ SerializedPacket serialized = framer_->BuildFecPacket(header, fec_data);
+ fec_group_.reset(nullptr);
+ packet_size_ = 0;
+ LOG_IF(DFATAL, !serialized.packet)
+ << "Failed to serialize fec packet for group:" << fec_data.fec_group;
+ DCHECK_GE(max_packet_length_, serialized.packet->length());
+ return serialized;
+}
+
+SerializedPacket QuicPacketCreator::SerializeConnectionClose(
+ QuicConnectionCloseFrame* close_frame) {
+ QuicFrames frames;
+ frames.push_back(QuicFrame(close_frame));
+ return SerializeAllFrames(frames);
+}
+
+QuicEncryptedPacket* QuicPacketCreator::SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions) {
+ DCHECK(framer_->is_server());
+ QuicPacketPublicHeader header;
+ header.connection_id = connection_id_;
+ header.reset_flag = false;
+ header.version_flag = true;
+ header.versions = supported_versions;
+ QuicEncryptedPacket* encrypted =
+ framer_->BuildVersionNegotiationPacket(header, supported_versions);
+ DCHECK(encrypted);
+ DCHECK_GE(max_packet_length_, encrypted->length());
+ return encrypted;
+}
+
+void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group,
+ bool fec_flag,
+ QuicPacketHeader* header) {
+ header->public_header.connection_id = connection_id_;
+ header->public_header.reset_flag = false;
+ header->public_header.version_flag = send_version_in_packet_;
+ header->fec_flag = fec_flag;
+ header->packet_sequence_number = ++sequence_number_;
+ header->public_header.sequence_number_length = sequence_number_length_;
+ header->entropy_flag = random_bool_source_->RandBool();
+ header->is_in_fec_group = fec_group == 0 ? NOT_IN_FEC_GROUP : IN_FEC_GROUP;
+ header->fec_group = fec_group;
+}
+
+bool QuicPacketCreator::ShouldRetransmit(const QuicFrame& frame) {
+ switch (frame.type) {
+ case ACK_FRAME:
+ case CONGESTION_FEEDBACK_FRAME:
+ case PADDING_FRAME:
+ case STOP_WAITING_FRAME:
+ return false;
+ default:
+ return true;
+ }
+}
+
+bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
+ bool save_retransmittable_frames) {
+ DVLOG(1) << "Adding frame: " << frame;
+ InFecGroup is_in_fec_group = MaybeUpdateLengthsAndStartFec();
+
+ size_t frame_len = framer_->GetSerializedFrameLength(
+ frame, BytesFree(), queued_frames_.empty(), true, is_in_fec_group,
+ sequence_number_length_);
+ if (frame_len == 0) {
+ return false;
+ }
+ DCHECK_LT(0u, packet_size_);
+ packet_size_ += ExpansionOnNewFrame() + frame_len;
+
+ if (save_retransmittable_frames && ShouldRetransmit(frame)) {
+ if (queued_retransmittable_frames_.get() == nullptr) {
+ queued_retransmittable_frames_.reset(new RetransmittableFrames());
+ }
+ if (frame.type == STREAM_FRAME) {
+ queued_frames_.push_back(
+ queued_retransmittable_frames_->AddStreamFrame(frame.stream_frame));
+ } else {
+ queued_frames_.push_back(
+ queued_retransmittable_frames_->AddNonStreamFrame(frame));
+ }
+ } else {
+ queued_frames_.push_back(frame);
+ }
+ return true;
+}
+
+void QuicPacketCreator::MaybeAddPadding() {
+ if (queued_retransmittable_frames_.get() == nullptr) {
+ return;
+ }
+ if (!queued_retransmittable_frames_->HasCryptoHandshake()) {
+ return;
+ }
+ if (BytesFree() == 0) {
+ // Don't pad full packets.
+ return;
+ }
+ QuicPaddingFrame padding;
+ bool success = AddFrame(QuicFrame(&padding), false);
+ DCHECK(success);
+}
+
+} // namespace net
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h
new file mode 100644
index 0000000..313632e
--- /dev/null
+++ b/net/quic/quic_packet_creator.h
@@ -0,0 +1,296 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Accumulates frames for the next packet until more frames no longer fit or
+// it's time to create a packet from them. Also provides packet creation of
+// FEC packets based on previously created packets.
+
+#ifndef NET_QUIC_QUIC_PACKET_CREATOR_H_
+#define NET_QUIC_QUIC_PACKET_CREATOR_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "net/quic/quic_fec_group.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace test {
+class QuicPacketCreatorPeer;
+}
+
+class QuicAckNotifier;
+class QuicRandom;
+class QuicRandomBoolSource;
+
+class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface {
+ public:
+ // QuicRandom* required for packet entropy.
+ QuicPacketCreator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator);
+
+ virtual ~QuicPacketCreator();
+
+ // QuicFecBuilderInterface
+ virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header,
+ base::StringPiece payload) OVERRIDE;
+
+ // Turn on FEC protection for subsequently created packets. FEC should be
+ // enabled first (max_packets_per_fec_group should be non-zero) for FEC
+ // protection to start.
+ void StartFecProtectingPackets();
+
+ // Turn off FEC protection for subsequently created packets. If the creator
+ // has any open FEC group, call will fail. It is the caller's responsibility
+ // to flush out FEC packets in generation, and to verify with ShouldSendFec()
+ // that there is no open FEC group.
+ void StopFecProtectingPackets();
+
+ // Checks if it's time to send an FEC packet. |force_close| forces this to
+ // return true if an FEC group is open.
+ bool ShouldSendFec(bool force_close) const;
+
+ // Returns true if an FEC packet is under construction.
+ bool IsFecGroupOpen() const;
+
+ // Makes the framer not serialize the protocol version in sent packets.
+ void StopSendingVersion();
+
+ // Update the sequence number length to use in future packets as soon as it
+ // can be safely changed.
+ void UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window);
+
+ // The overhead the framing will add for a packet with one frame.
+ static size_t StreamFramePacketOverhead(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length,
+ QuicStreamOffset offset,
+ InFecGroup is_in_fec_group);
+
+ bool HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset) const;
+
+ // Converts a raw payload to a frame which fits into the currently open
+ // packet if there is one. Returns the number of bytes consumed from data.
+ // If data is empty and fin is true, the expected behavior is to consume the
+ // fin but return 0.
+ size_t CreateStreamFrame(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicFrame* frame);
+
+ // As above, but keeps track of an QuicAckNotifier that should be called when
+ // the packet that contains this stream frame is ACKed.
+ // The |notifier| is not owned by the QuicPacketGenerator and must outlive the
+ // generated packet.
+ size_t CreateStreamFrameWithNotifier(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ QuicAckNotifier* notifier,
+ QuicFrame* frame);
+
+ // Serializes all frames into a single packet. All frames must fit into a
+ // single packet. Also, sets the entropy hash of the serialized packet to a
+ // random bool and returns that value as a member of SerializedPacket.
+ // Never returns a RetransmittableFrames in SerializedPacket.
+ SerializedPacket SerializeAllFrames(const QuicFrames& frames);
+
+ // Re-serializes frames with the original packet's sequence number length.
+ // Used for retransmitting packets to ensure they aren't too long.
+ // Caller must ensure that any open FEC group is closed before calling this
+ // method.
+ SerializedPacket ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length);
+
+ // Returns true if there are frames pending to be serialized.
+ bool HasPendingFrames() const;
+
+ // Returns true if there are retransmittable frames pending to be serialized.
+ bool HasPendingRetransmittableFrames() const;
+
+ // Returns whether FEC protection is currently enabled. Note: Enabled does not
+ // mean that an FEC group is currently active; i.e., IsFecProtected() may
+ // still return false.
+ bool IsFecEnabled() const;
+
+ // Returns true if subsequent packets will be FEC protected. Note: True does
+ // not mean that an FEC packet is currently under construction; i.e.,
+ // fec_group_.get() may still be nullptr, until MaybeStartFec() is called.
+ bool IsFecProtected() const;
+
+ // Returns the number of bytes which are available to be used by additional
+ // frames in the packet. Since stream frames are slightly smaller when they
+ // are the last frame in a packet, this method will return a different
+ // value than max_packet_size - PacketSize(), in this case.
+ size_t BytesFree() const;
+
+ // Returns the number of bytes that the packet will expand by if a new frame
+ // is added to the packet. If the last frame was a stream frame, it will
+ // expand slightly when a new frame is added, and this method returns the
+ // amount of expected expansion. If the packet is in an FEC group, no
+ // expansion happens and this method always returns zero.
+ size_t ExpansionOnNewFrame() const;
+
+ // Returns the number of bytes in the current packet, including the header,
+ // if serialized with the current frames. Adding a frame to the packet
+ // may change the serialized length of existing frames, as per the comment
+ // in BytesFree.
+ size_t PacketSize() const;
+
+ // TODO(jri): AddSavedFrame calls AddFrame, which only saves the frame
+ // if it is a stream frame, not other types of frames. Fix this API;
+ // add a AddNonSavedFrame method.
+ // Adds |frame| to the packet creator's list of frames to be serialized.
+ // Returns false if the frame doesn't fit into the current packet.
+ bool AddSavedFrame(const QuicFrame& frame);
+
+ // Serializes all frames which have been added and adds any which should be
+ // retransmitted to |retransmittable_frames| if it's not nullptr. All frames
+ // must fit into a single packet. Sets the entropy hash of the serialized
+ // packet to a random bool and returns that value as a member of
+ // SerializedPacket. Also, sets |serialized_frames| in the SerializedPacket to
+ // the corresponding RetransmittableFrames if any frames are to be
+ // retransmitted.
+ SerializedPacket SerializePacket();
+
+ // Packetize FEC data. All frames must fit into a single packet. Also, sets
+ // the entropy hash of the serialized packet to a random bool and returns
+ // that value as a member of SerializedPacket.
+ SerializedPacket SerializeFec();
+
+ // Creates a packet with connection close frame. Caller owns the created
+ // packet. Also, sets the entropy hash of the serialized packet to a random
+ // bool and returns that value as a member of SerializedPacket.
+ SerializedPacket SerializeConnectionClose(
+ QuicConnectionCloseFrame* close_frame);
+
+ // Creates a version negotiation packet which supports |supported_versions|.
+ // Caller owns the created packet. Also, sets the entropy hash of the
+ // serialized packet to a random bool and returns that value as a member of
+ // SerializedPacket.
+ QuicEncryptedPacket* SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions);
+
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level) {
+ encryption_level_ = level;
+ }
+
+ // Sequence number of the last created packet, or 0 if no packets have been
+ // created.
+ QuicPacketSequenceNumber sequence_number() const {
+ return sequence_number_;
+ }
+
+ void set_sequence_number(QuicPacketSequenceNumber s) {
+ sequence_number_ = s;
+ }
+
+ QuicConnectionIdLength connection_id_length() const {
+ return connection_id_length_;
+ }
+
+ QuicSequenceNumberLength next_sequence_number_length() const {
+ return next_sequence_number_length_;
+ }
+
+ void set_next_sequence_number_length(QuicSequenceNumberLength length) {
+ next_sequence_number_length_ = length;
+ }
+
+ size_t max_packet_length() const {
+ return max_packet_length_;
+ }
+
+ void set_max_packet_length(size_t length) {
+ // |max_packet_length_| should not be changed mid-packet or mid-FEC group.
+ DCHECK(fec_group_.get() == nullptr && queued_frames_.empty());
+ max_packet_length_ = length;
+ }
+
+ // Returns current max number of packets covered by an FEC group.
+ size_t max_packets_per_fec_group() const {
+ return max_packets_per_fec_group_;
+ }
+
+ // Sets creator's max number of packets covered by an FEC group.
+ // Note: While there are no constraints on |max_packets_per_fec_group|,
+ // this setter enforces a min value of kLowestMaxPacketsPerFecGroup.
+ // To turn off FEC protection, use StopFecProtectingPackets().
+ void set_max_packets_per_fec_group(size_t max_packets_per_fec_group);
+
+ private:
+ friend class test::QuicPacketCreatorPeer;
+
+ static bool ShouldRetransmit(const QuicFrame& frame);
+
+ // Updates sequence number and max packet lengths on a packet or FEC group
+ // boundary.
+ void MaybeUpdateLengths();
+
+ // Updates lengths and also starts an FEC group if FEC protection is on and
+ // there is not already an FEC group open.
+ InFecGroup MaybeUpdateLengthsAndStartFec();
+
+ void FillPacketHeader(QuicFecGroupNumber fec_group,
+ bool fec_flag,
+ QuicPacketHeader* header);
+
+ // Allows a frame to be added without creating retransmittable frames.
+ // Particularly useful for retransmits using SerializeAllFrames().
+ bool AddFrame(const QuicFrame& frame, bool save_retransmittable_frames);
+
+ // Adds a padding frame to the current packet only if the current packet
+ // contains a handshake message, and there is sufficient room to fit a
+ // padding frame.
+ void MaybeAddPadding();
+
+ QuicConnectionId connection_id_;
+ EncryptionLevel encryption_level_;
+ QuicFramer* framer_;
+ scoped_ptr<QuicRandomBoolSource> random_bool_source_;
+ QuicPacketSequenceNumber sequence_number_;
+ // If true, any created packets will be FEC protected.
+ bool should_fec_protect_;
+ QuicFecGroupNumber fec_group_number_;
+ scoped_ptr<QuicFecGroup> fec_group_;
+ // Controls whether protocol version should be included while serializing the
+ // packet.
+ bool send_version_in_packet_;
+ // Maximum length including headers and encryption (UDP payload length.)
+ size_t max_packet_length_;
+ // 0 indicates FEC is disabled.
+ size_t max_packets_per_fec_group_;
+ // Length of connection_id to send over the wire.
+ QuicConnectionIdLength connection_id_length_;
+ // Staging variable to hold next packet sequence number length. When sequence
+ // number length is to be changed, this variable holds the new length until
+ // a packet or FEC group boundary, when the creator's sequence_number_length_
+ // can be changed to this new value.
+ QuicSequenceNumberLength next_sequence_number_length_;
+ // Sequence number length for the current packet and for the current FEC group
+ // when FEC is enabled. Mutable so PacketSize() can adjust it when the packet
+ // is empty.
+ mutable QuicSequenceNumberLength sequence_number_length_;
+ // packet_size_ is mutable because it's just a cache of the current size.
+ // packet_size should never be read directly, use PacketSize() instead.
+ mutable size_t packet_size_;
+ QuicFrames queued_frames_;
+ scoped_ptr<RetransmittableFrames> queued_retransmittable_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketCreator);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PACKET_CREATOR_H_
diff --git a/net/quic/quic_packet_creator_test.cc b/net/quic/quic_packet_creator_test.cc
new file mode 100644
index 0000000..c8d5686
--- /dev/null
+++ b/net/quic/quic_packet_creator_test.cc
@@ -0,0 +1,1021 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_creator.h"
+
+#include "base/stl_util.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_framer_peer.h"
+#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::StringPiece;
+using std::ostream;
+using std::string;
+using std::vector;
+using testing::DoAll;
+using testing::InSequence;
+using testing::Return;
+using testing::SaveArg;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+// Run tests with combinations of {QuicVersion, ToggleVersionSerialization}.
+struct TestParams {
+ TestParams(QuicVersion version,
+ bool version_serialization)
+ : version(version),
+ version_serialization(version_serialization) {
+ }
+
+ friend ostream& operator<<(ostream& os, const TestParams& p) {
+ os << "{ client_version: " << QuicVersionToString(p.version)
+ << " include version: " << p.version_serialization << " }";
+ return os;
+ }
+
+ QuicVersion version;
+ bool version_serialization;
+};
+
+// Constructs various test permutations.
+vector<TestParams> GetTestParams() {
+ vector<TestParams> params;
+ QuicVersionVector all_supported_versions = QuicSupportedVersions();
+ for (size_t i = 0; i < all_supported_versions.size(); ++i) {
+ params.push_back(TestParams(all_supported_versions[i], true));
+ params.push_back(TestParams(all_supported_versions[i], false));
+ }
+ return params;
+}
+
+class QuicPacketCreatorTest : public ::testing::TestWithParam<TestParams> {
+ protected:
+ QuicPacketCreatorTest()
+ : server_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(),
+ true),
+ client_framer_(SupportedVersions(GetParam().version), QuicTime::Zero(),
+ false),
+ sequence_number_(0),
+ connection_id_(2),
+ data_("foo"),
+ creator_(connection_id_, &client_framer_, &mock_random_) {
+ client_framer_.set_visitor(&framer_visitor_);
+ client_framer_.set_received_entropy_calculator(&entropy_calculator_);
+ server_framer_.set_visitor(&framer_visitor_);
+ }
+
+ virtual ~QuicPacketCreatorTest() OVERRIDE {
+ }
+
+ void ProcessPacket(QuicPacket* packet) {
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ server_framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number_,
+ *packet));
+ server_framer_.ProcessPacket(*encrypted);
+ }
+
+ void CheckStreamFrame(const QuicFrame& frame,
+ QuicStreamId stream_id,
+ const string& data,
+ QuicStreamOffset offset,
+ bool fin) {
+ EXPECT_EQ(STREAM_FRAME, frame.type);
+ ASSERT_TRUE(frame.stream_frame);
+ EXPECT_EQ(stream_id, frame.stream_frame->stream_id);
+ scoped_ptr<string> frame_data(frame.stream_frame->GetDataAsString());
+ EXPECT_EQ(data, *frame_data);
+ EXPECT_EQ(offset, frame.stream_frame->offset);
+ EXPECT_EQ(fin, frame.stream_frame->fin);
+ }
+
+ // Returns the number of bytes consumed by the header of packet, including
+ // the version.
+ size_t GetPacketHeaderOverhead(InFecGroup is_in_fec_group) {
+ return GetPacketHeaderSize(creator_.connection_id_length(),
+ kIncludeVersion,
+ creator_.next_sequence_number_length(),
+ is_in_fec_group);
+ }
+
+ // Returns the number of bytes of overhead that will be added to a packet
+ // of maximum length.
+ size_t GetEncryptionOverhead() {
+ return creator_.max_packet_length() - client_framer_.GetMaxPlaintextSize(
+ creator_.max_packet_length());
+ }
+
+ // Returns the number of bytes consumed by the non-data fields of a stream
+ // frame, assuming it is the last frame in the packet
+ size_t GetStreamFrameOverhead(InFecGroup is_in_fec_group) {
+ return QuicFramer::GetMinStreamFrameSize(kClientDataStreamId1, kOffset,
+ true, is_in_fec_group);
+ }
+
+ // Enables and turns on FEC protection. Returns true if FEC protection is on.
+ bool SwitchFecProtectionOn(size_t max_packets_per_fec_group) {
+ creator_.set_max_packets_per_fec_group(max_packets_per_fec_group);
+ creator_.StartFecProtectingPackets();
+ return creator_.IsFecProtected();
+ }
+
+ static const QuicStreamOffset kOffset = 1u;
+
+ QuicFrames frames_;
+ QuicFramer server_framer_;
+ QuicFramer client_framer_;
+ testing::StrictMock<MockFramerVisitor> framer_visitor_;
+ QuicPacketSequenceNumber sequence_number_;
+ QuicConnectionId connection_id_;
+ string data_;
+ MockRandom mock_random_;
+ QuicPacketCreator creator_;
+ MockEntropyCalculator entropy_calculator_;
+};
+
+// Run all packet creator tests with all supported versions of QUIC, and with
+// and without version in the packet header.
+INSTANTIATE_TEST_CASE_P(QuicPacketCreatorTests,
+ QuicPacketCreatorTest,
+ ::testing::ValuesIn(GetTestParams()));
+
+TEST_P(QuicPacketCreatorTest, SerializeFrames) {
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u))));
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, true, 0u, IOVector())));
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].ack_frame;
+ delete frames_[1].stream_frame;
+ delete frames_[2].stream_frame;
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeWithFEC) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ serialized = creator_.SerializeFec();
+ ASSERT_EQ(2u, serialized.sequence_number);
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecData(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeChangingSequenceNumberLength) {
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u))));
+ creator_.AddSavedFrame(frames_[0]);
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
+ SerializedPacket serialized = creator_.SerializePacket();
+ // The sequence number length will not change mid-packet.
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ creator_.AddSavedFrame(frames_[0]);
+ serialized = creator_.SerializePacket();
+ // Now the actual sequence number length should have changed.
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+ delete frames_[0].ack_frame;
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, ChangeSequenceNumberLengthMidPacket) {
+ // Changing the sequence number length with queued frames in the creator
+ // should hold the change until after any currently queued frames are
+ // serialized.
+
+ // Packet 1.
+ // Queue a frame in the creator.
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ QuicFrame ack_frame = QuicFrame(new QuicAckFrame(MakeAckFrame(0u)));
+ creator_.AddSavedFrame(ack_frame);
+
+ // Now change sequence number length.
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
+
+ // Add a STOP_WAITING frame since it contains a packet sequence number,
+ // whose length should be 1.
+ QuicStopWaitingFrame stop_waiting_frame;
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created.
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ // Verify that header in transmitted packet has 1 byte sequence length.
+ QuicPacketHeader header;
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ header.public_header.sequence_number_length);
+ delete serialized.packet;
+
+ // Packet 2.
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ // Generate Packet 2 with one frame -- sequence number length should now
+ // change to 4 bytes.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&stop_waiting_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created.
+ serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ // Verify that header in transmitted packet has 4 byte sequence length.
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnStopWaitingFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ header.public_header.sequence_number_length);
+
+ delete serialized.packet;
+ delete ack_frame.ack_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeWithFECChangingSequenceNumberLength) {
+ // Test goal is to test the following sequence (P1 => generate Packet 1):
+ // P1 <change seq num length> P2 FEC,
+ // and we expect that sequence number length should not change until the end
+ // of the open FEC group.
+
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ frames_.push_back(QuicFrame(new QuicAckFrame(MakeAckFrame(0u))));
+
+ // Generate Packet 1.
+ creator_.AddSavedFrame(frames_[0]);
+ // Change the sequence number length mid-FEC group and it should not change.
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
+ SerializedPacket serialized = creator_.SerializePacket();
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ // Generate Packet 2.
+ creator_.AddSavedFrame(frames_[0]);
+ serialized = creator_.SerializePacket();
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecProtectedPayload(_));
+ EXPECT_CALL(framer_visitor_, OnAckFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ // Should return false since we do not have enough packets in the FEC group to
+ // trigger an FEC packet.
+ ASSERT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ ASSERT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ // Force generation of FEC packet.
+ serialized = creator_.SerializeFec();
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+ ASSERT_EQ(3u, serialized.sequence_number);
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnFecData(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+
+ // Ensure the next FEC group starts using the new sequence number length.
+ serialized = creator_.SerializeAllFrames(frames_);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+ delete frames_[0].ack_frame;
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, ReserializeFramesWithSequenceNumberLength) {
+ // If the original packet sequence number length, the current sequence number
+ // length, and the configured send sequence number length are different, the
+ // retransmit must sent with the original length and the others do not change.
+ creator_.set_next_sequence_number_length(PACKET_4BYTE_SEQUENCE_NUMBER);
+ QuicPacketCreatorPeer::SetSequenceNumberLength(&creator_,
+ PACKET_2BYTE_SEQUENCE_NUMBER);
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ SerializedPacket serialized =
+ creator_.ReserializeAllFrames(frames_, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ QuicPacketCreatorPeer::GetSequenceNumberLength(&creator_));
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER, serialized.sequence_number_length);
+ delete frames_[0].stream_frame;
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeConnectionClose) {
+ QuicConnectionCloseFrame frame;
+ frame.error_code = QUIC_NO_ERROR;
+ frame.error_details = "error";
+
+ SerializedPacket serialized = creator_.SerializeConnectionClose(&frame);
+ ASSERT_EQ(1u, serialized.sequence_number);
+ ASSERT_EQ(1u, creator_.sequence_number());
+
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_));
+ EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+
+ ProcessPacket(serialized.packet);
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithNoGroup) {
+ // Enable FEC protection.
+ creator_.set_max_packets_per_fec_group(6);
+ EXPECT_TRUE(creator_.IsFecEnabled());
+ EXPECT_FALSE(creator_.IsFecProtected());
+
+ // Turn on FEC protection.
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // We have no packets in the FEC group, so no FEC packet can be created.
+ EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/true));
+ // Since no packets are in FEC group yet, we should be able to turn FEC
+ // off with no trouble.
+ creator_.StopFecProtectingPackets();
+ EXPECT_FALSE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnOffWithGroupInProgress) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
+ delete serialized.packet;
+
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // We do not have enough packets in the FEC group to trigger an FEC packet.
+ EXPECT_FALSE(creator_.ShouldSendFec(/*force_close=*/false));
+ // Should return true since there are packets in the FEC group.
+ EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ // Switching FEC off should not change creator state, since there is an
+ // FEC packet under construction.
+ EXPECT_DFATAL(creator_.StopFecProtectingPackets(),
+ "Cannot stop FEC protection with open FEC group.");
+ EXPECT_TRUE(creator_.IsFecProtected());
+ // Confirm that FEC packet is still under construction.
+ EXPECT_TRUE(creator_.ShouldSendFec(/*force_close=*/true));
+
+ serialized = creator_.SerializeFec();
+ delete serialized.packet;
+
+ // Switching FEC on/off should work now.
+ creator_.StopFecProtectingPackets();
+ EXPECT_FALSE(creator_.IsFecProtected());
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, SwitchFecOnWithStreamFrameQueued) {
+ // Add a stream frame to the creator.
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 1u, MakeIOVector("test"), 0u, false, &frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ creator_.set_max_packets_per_fec_group(6);
+ EXPECT_TRUE(creator_.IsFecEnabled());
+ EXPECT_DFATAL(creator_.StartFecProtectingPackets(),
+ "Cannot start FEC protection with pending frames.");
+ EXPECT_FALSE(creator_.IsFecProtected());
+
+ // Serialize packet for transmission.
+ SerializedPacket serialized = creator_.SerializePacket();
+ delete serialized.packet;
+ delete serialized.retransmittable_frames;
+ EXPECT_FALSE(creator_.HasPendingFrames());
+
+ // Since all pending frames have been serialized, turning FEC on should work.
+ creator_.StartFecProtectingPackets();
+ EXPECT_TRUE(creator_.IsFecProtected());
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrame) {
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 0u,
+ false, &frame);
+ EXPECT_EQ(4u, consumed);
+ CheckStreamFrame(frame, 1u, "test", 0u, false);
+ delete frame.stream_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameFin) {
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(1u, MakeIOVector("test"), 10u,
+ true, &frame);
+ EXPECT_EQ(4u, consumed);
+ CheckStreamFrame(frame, 1u, "test", 10u, true);
+ delete frame.stream_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameFinOnly) {
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(1u, IOVector(), 0u, true,
+ &frame);
+ EXPECT_EQ(0u, consumed);
+ CheckStreamFrame(frame, 1u, string(), 0u, true);
+ delete frame.stream_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) {
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead();
+ for (size_t i = overhead; i < overhead + 100; ++i) {
+ creator_.set_max_packet_length(i);
+ const bool should_have_room = i > overhead + GetStreamFrameOverhead(
+ NOT_IN_FEC_GROUP);
+ ASSERT_EQ(should_have_room, creator_.HasRoomForStreamFrame(
+ kClientDataStreamId1, kOffset));
+ if (should_have_room) {
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kClientDataStreamId1, MakeIOVector("testdata"), kOffset, false,
+ &frame);
+ EXPECT_LT(0u, bytes_consumed);
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumption) {
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
+ EXPECT_EQ(capacity - bytes_free, bytes_consumed);
+
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ // BytesFree() returns bytes available for the next frame, which will
+ // be two bytes smaller since the stream frame would need to be grown.
+ EXPECT_EQ(2u, creator_.ExpansionOnNewFrame());
+ size_t expected_bytes_free = bytes_free < 3 ? 0 : bytes_free - 2;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, StreamFrameConsumptionWithFec) {
+ // Enable FEC protection, and send FEC packet every 6 packets.
+ EXPECT_TRUE(SwitchFecProtectionOn(6));
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead(IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(IN_FEC_GROUP);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
+ EXPECT_EQ(capacity - bytes_free, bytes_consumed);
+
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ // BytesFree() returns bytes available for the next frame. Since stream
+ // frame does not grow for FEC protected packets, this should be the same
+ // as bytes_free (bound by 0).
+ EXPECT_EQ(0u, creator_.ExpansionOnNewFrame());
+ size_t expected_bytes_free = bytes_free > 0 ? bytes_free : 0;
+ EXPECT_EQ(expected_bytes_free, creator_.BytesFree()) << "delta: " << delta;
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, CryptoStreamFramePacketPadding) {
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
+ ASSERT_GT(kMaxPacketSize, overhead);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kCryptoStreamId, MakeIOVector(data), kOffset, false, &frame);
+ EXPECT_LT(0u, bytes_consumed);
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ // If there is not enough space in the packet to fit a padding frame
+ // (1 byte) and to expand the stream frame (another 2 bytes) the packet
+ // will not be padded.
+ if (bytes_free < 3) {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize)
+ - bytes_free, serialized_packet.packet->length());
+ } else {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize),
+ serialized_packet.packet->length());
+ }
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, NonCryptoStreamFramePacketNonPadding) {
+ // Compute the total overhead for a single frame in packet.
+ const size_t overhead = GetPacketHeaderOverhead(NOT_IN_FEC_GROUP)
+ + GetEncryptionOverhead() + GetStreamFrameOverhead(NOT_IN_FEC_GROUP);
+ ASSERT_GT(kDefaultMaxPacketSize, overhead);
+ size_t capacity = kDefaultMaxPacketSize - overhead;
+ // Now, test various sizes around this size.
+ for (int delta = -5; delta <= 5; ++delta) {
+ string data(capacity + delta, 'A');
+ size_t bytes_free = delta > 0 ? 0 : 0 - delta;
+
+ QuicFrame frame;
+ size_t bytes_consumed = creator_.CreateStreamFrame(
+ kClientDataStreamId1, MakeIOVector(data), kOffset, false, &frame);
+ EXPECT_LT(0u, bytes_consumed);
+ ASSERT_TRUE(creator_.AddSavedFrame(frame));
+ SerializedPacket serialized_packet = creator_.SerializePacket();
+ ASSERT_TRUE(serialized_packet.packet);
+ if (bytes_free > 0) {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize)
+ - bytes_free, serialized_packet.packet->length());
+ } else {
+ EXPECT_EQ(client_framer_.GetMaxPlaintextSize(kDefaultMaxPacketSize),
+ serialized_packet.packet->length());
+ }
+ delete serialized_packet.packet;
+ delete serialized_packet.retransmittable_frames;
+ }
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) {
+ QuicFramerPeer::SetIsServer(&client_framer_, true);
+ QuicVersionVector versions;
+ versions.push_back(test::QuicVersionMax());
+ scoped_ptr<QuicEncryptedPacket> encrypted(
+ creator_.SerializeVersionNegotiationPacket(versions));
+
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_));
+ }
+ QuicFramerPeer::SetIsServer(&client_framer_, false);
+ client_framer_.ProcessPacket(*encrypted);
+}
+
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthLeastAwaiting) {
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ size_t max_packets_per_fec_group = 10;
+ creator_.set_max_packets_per_fec_group(max_packets_per_fec_group);
+ creator_.set_sequence_number(64 - max_packets_per_fec_group);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.set_sequence_number(64 * 256 - max_packets_per_fec_group);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.set_sequence_number(64 * 256 * 256 - max_packets_per_fec_group);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.set_sequence_number(
+ GG_UINT64_C(64) * 256 * 256 * 256 * 256 - max_packets_per_fec_group);
+ creator_.UpdateSequenceNumberLength(2, 10000);
+ EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+}
+
+TEST_P(QuicPacketCreatorTest, UpdatePacketSequenceNumberLengthBandwidth) {
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.UpdateSequenceNumberLength(1, 10000);
+ EXPECT_EQ(PACKET_1BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.UpdateSequenceNumberLength(1, 10000 * 256);
+ EXPECT_EQ(PACKET_2BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.UpdateSequenceNumberLength(1, 10000 * 256 * 256);
+ EXPECT_EQ(PACKET_4BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+
+ creator_.UpdateSequenceNumberLength(
+ 1, GG_UINT64_C(1000) * 256 * 256 * 256 * 256);
+ EXPECT_EQ(PACKET_6BYTE_SEQUENCE_NUMBER,
+ creator_.next_sequence_number_length());
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameWithNotifier) {
+ // Ensure that if CreateStreamFrame does not consume any data (e.g. a FIN only
+ // frame) then any QuicAckNotifier that is passed in still gets attached to
+ // the frame.
+ scoped_refptr<MockAckNotifierDelegate> delegate(new MockAckNotifierDelegate);
+ QuicAckNotifier notifier(delegate.get());
+ QuicFrame frame;
+ IOVector empty_iovector;
+ bool fin = true;
+ size_t consumed_bytes = creator_.CreateStreamFrameWithNotifier(
+ 1u, empty_iovector, 0u, fin, ¬ifier, &frame);
+ EXPECT_EQ(0u, consumed_bytes);
+ EXPECT_EQ(¬ifier, frame.stream_frame->notifier);
+ delete frame.stream_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeFrame) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ delete frames_[0].stream_frame;
+
+ QuicPacketHeader header;
+ {
+ InSequence s;
+ EXPECT_CALL(framer_visitor_, OnPacket());
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedPublicHeader(_));
+ EXPECT_CALL(framer_visitor_, OnUnauthenticatedHeader(_));
+ EXPECT_CALL(framer_visitor_, OnDecryptedPacket(_));
+ EXPECT_CALL(framer_visitor_, OnPacketHeader(_)).WillOnce(
+ DoAll(SaveArg<0>(&header), Return(true)));
+ EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
+ EXPECT_CALL(framer_visitor_, OnPacketComplete());
+ }
+ ProcessPacket(serialized.packet);
+ EXPECT_EQ(GetParam().version_serialization,
+ header.public_header.version_flag);
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ // A string larger than fits into a frame.
+ size_t payload_length;
+ creator_.set_max_packet_length(GetPacketLengthForOneStream(
+ client_framer_.version(),
+ QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
+ PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP, &payload_length));
+ QuicFrame frame;
+ const string too_long_payload(payload_length * 2, 'a');
+ size_t consumed = creator_.CreateStreamFrame(
+ 1u, MakeIOVector(too_long_payload), 0u, true, &frame);
+ EXPECT_EQ(payload_length, consumed);
+ const string payload(payload_length, 'a');
+ CheckStreamFrame(frame, 1u, payload, 0u, false);
+ delete frame.stream_frame;
+}
+
+TEST_P(QuicPacketCreatorTest, AddFrameAndSerialize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ EXPECT_EQ(max_plaintext_size -
+ GetPacketHeaderSize(
+ creator_.connection_id_length(),
+ QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
+ PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP),
+ creator_.BytesFree());
+
+ // Add a variety of frame types and then a padding frame.
+ QuicAckFrame ack_frame(MakeAckFrame(0u));
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ QuicCongestionFeedbackFrame congestion_feedback;
+ congestion_feedback.type = kTCP;
+ congestion_feedback.tcp.receive_window = 0x4030;
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&congestion_feedback)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ QuicFrame frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 1u, MakeIOVector("test"), 0u, false, &frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ QuicPaddingFrame padding_frame;
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&padding_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_EQ(0u, creator_.BytesFree());
+
+ EXPECT_FALSE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+
+ // Ensure the packet is successfully created.
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ delete serialized.packet;
+ ASSERT_TRUE(serialized.retransmittable_frames);
+ RetransmittableFrames* retransmittable = serialized.retransmittable_frames;
+ ASSERT_EQ(1u, retransmittable->frames().size());
+ EXPECT_EQ(STREAM_FRAME, retransmittable->frames()[0].type);
+ ASSERT_TRUE(retransmittable->frames()[0].stream_frame);
+ delete serialized.retransmittable_frames;
+
+ EXPECT_FALSE(creator_.HasPendingFrames());
+ EXPECT_EQ(max_plaintext_size -
+ GetPacketHeaderSize(
+ creator_.connection_id_length(),
+ QuicPacketCreatorPeer::SendVersionInPacket(&creator_),
+ PACKET_1BYTE_SEQUENCE_NUMBER,
+ NOT_IN_FEC_GROUP),
+ creator_.BytesFree());
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithLargePacketSize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_max_packet_length(kMaxPacketSize);
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+
+ // Serialized length of ack frame with 2000 nack ranges should be limited by
+ // the number of nack ranges that can be fit in an ack frame.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u);
+ size_t frame_len = client_framer_.GetSerializedFrameLength(
+ QuicFrame(&ack_frame), creator_.BytesFree(), true, true,
+ NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_GT(creator_.BytesFree(), frame_len);
+ EXPECT_GT(max_plaintext_size, creator_.PacketSize());
+
+ // Add ack frame to creator.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_GT(max_plaintext_size, creator_.PacketSize());
+ EXPECT_LT(0u, creator_.BytesFree());
+
+ // Make sure that an additional stream frame can be added to the packet.
+ QuicFrame stream_frame;
+ size_t consumed = creator_.CreateStreamFrame(
+ 2u, MakeIOVector("test"), 0u, false, &stream_frame);
+ EXPECT_EQ(4u, consumed);
+ ASSERT_TRUE(stream_frame.stream_frame);
+ EXPECT_TRUE(creator_.AddSavedFrame(stream_frame));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+
+ // Ensure the packet is successfully created, and the packet size estimate
+ // matches the serialized packet length.
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(_)).WillOnce(testing::Return(0));
+ size_t est_packet_size = creator_.PacketSize();
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_EQ(est_packet_size, serialized.packet->length());
+ delete serialized.retransmittable_frames;
+ delete serialized.packet;
+}
+
+TEST_P(QuicPacketCreatorTest, SerializeTruncatedAckFrameWithSmallPacketSize) {
+ if (!GetParam().version_serialization) {
+ creator_.StopSendingVersion();
+ }
+ creator_.set_max_packet_length(500u);
+
+ const size_t max_plaintext_size =
+ client_framer_.GetMaxPlaintextSize(creator_.max_packet_length());
+ EXPECT_EQ(max_plaintext_size - creator_.PacketSize(), creator_.BytesFree());
+
+ // Serialized length of ack frame with 2000 nack ranges should be limited by
+ // the packet size.
+ QuicAckFrame ack_frame = MakeAckFrameWithNackRanges(2000u, 0u);
+ size_t frame_len = client_framer_.GetSerializedFrameLength(
+ QuicFrame(&ack_frame), creator_.BytesFree(), true, true,
+ NOT_IN_FEC_GROUP, PACKET_1BYTE_SEQUENCE_NUMBER);
+ EXPECT_EQ(creator_.BytesFree(), frame_len);
+
+ // Add ack frame to creator.
+ EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame)));
+ EXPECT_TRUE(creator_.HasPendingFrames());
+ EXPECT_EQ(max_plaintext_size, creator_.PacketSize());
+ EXPECT_EQ(0u, creator_.BytesFree());
+
+ // Ensure the packet is successfully created, and the packet size estimate
+ // may not match the serialized packet length.
+ EXPECT_CALL(entropy_calculator_,
+ EntropyHash(_)).WillOnce(Return(0));
+ size_t est_packet_size = creator_.PacketSize();
+ SerializedPacket serialized = creator_.SerializePacket();
+ ASSERT_TRUE(serialized.packet);
+ EXPECT_GE(est_packet_size, serialized.packet->length());
+ delete serialized.packet;
+}
+
+
+TEST_P(QuicPacketCreatorTest, EntropyFlag) {
+ frames_.push_back(QuicFrame(new QuicStreamFrame(0u, false, 0u, IOVector())));
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 64; ++j) {
+ SerializedPacket serialized = creator_.SerializeAllFrames(frames_);
+ // Verify both BoolSource and hash algorithm.
+ bool expected_rand_bool =
+ (mock_random_.RandUint64() & (GG_UINT64_C(1) << j)) != 0;
+ bool observed_rand_bool =
+ (serialized.entropy_hash & (1 << ((j+1) % 8))) != 0;
+ uint8 rest_of_hash = serialized.entropy_hash & ~(1 << ((j+1) % 8));
+ EXPECT_EQ(expected_rand_bool, observed_rand_bool);
+ EXPECT_EQ(0, rest_of_hash);
+ delete serialized.packet;
+ }
+ // After 64 calls, BoolSource will refresh the bucket - make sure it does.
+ mock_random_.ChangeValue();
+ }
+
+ delete frames_[0].stream_frame;
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc
new file mode 100644
index 0000000..11a45cc
--- /dev/null
+++ b/net/quic/quic_packet_generator.cc
@@ -0,0 +1,391 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_generator.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "net/quic/quic_fec_group.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+
+namespace net {
+
+namespace {
+
+// We want to put some space between a protected packet and the FEC packet to
+// avoid losing them both within the same loss episode. On the other hand,
+// we expect to be able to recover from any loss in about an RTT.
+// We resolve this tradeoff by sending an FEC packet atmost half an RTT,
+// or equivalently, half a cwnd, after the first protected packet. Since we
+// don't want to delay an FEC packet past half an RTT, we set the max FEC
+// group size to be half the current congestion window.
+const float kCongestionWindowMultiplierForFecGroupSize = 0.5;
+
+} // namespace
+
+class QuicAckNotifier;
+
+QuicPacketGenerator::QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate)
+ : delegate_(delegate),
+ debug_delegate_(nullptr),
+ packet_creator_(connection_id, framer, random_generator),
+ batch_mode_(false),
+ should_fec_protect_(false),
+ should_send_ack_(false),
+ should_send_feedback_(false),
+ should_send_stop_waiting_(false) {}
+
+QuicPacketGenerator::~QuicPacketGenerator() {
+ for (QuicFrames::iterator it = queued_control_frames_.begin();
+ it != queued_control_frames_.end(); ++it) {
+ switch (it->type) {
+ case PADDING_FRAME:
+ delete it->padding_frame;
+ break;
+ case STREAM_FRAME:
+ delete it->stream_frame;
+ break;
+ case ACK_FRAME:
+ delete it->ack_frame;
+ break;
+ case CONGESTION_FEEDBACK_FRAME:
+ delete it->congestion_feedback_frame;
+ break;
+ case RST_STREAM_FRAME:
+ delete it->rst_stream_frame;
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ delete it->connection_close_frame;
+ break;
+ case GOAWAY_FRAME:
+ delete it->goaway_frame;
+ break;
+ case WINDOW_UPDATE_FRAME:
+ delete it->window_update_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete it->blocked_frame;
+ break;
+ case STOP_WAITING_FRAME:
+ delete it->stop_waiting_frame;
+ break;
+ case PING_FRAME:
+ delete it->ping_frame;
+ break;
+ case NUM_FRAME_TYPES:
+ DCHECK(false) << "Cannot delete type: " << it->type;
+ }
+ }
+}
+
+// NetworkChangeVisitor method.
+void QuicPacketGenerator::OnCongestionWindowChange(
+ QuicByteCount congestion_window) {
+ packet_creator_.set_max_packets_per_fec_group(
+ static_cast<size_t>(kCongestionWindowMultiplierForFecGroupSize *
+ congestion_window / kDefaultTCPMSS));
+}
+
+void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback,
+ bool also_send_stop_waiting) {
+ should_send_ack_ = true;
+ should_send_feedback_ = also_send_feedback;
+ should_send_stop_waiting_ = also_send_stop_waiting;
+ SendQueuedFrames(false);
+}
+
+void QuicPacketGenerator::SetShouldSendStopWaiting() {
+ should_send_stop_waiting_ = true;
+ SendQueuedFrames(false);
+}
+
+void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) {
+ queued_control_frames_.push_back(frame);
+ SendQueuedFrames(false);
+}
+
+QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id,
+ const IOVector& data_to_write,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier* notifier) {
+ IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ const bool flush = handshake == IS_HANDSHAKE &&
+ packet_creator_.HasPendingRetransmittableFrames();
+ SendQueuedFrames(flush);
+
+ size_t total_bytes_consumed = 0;
+ bool fin_consumed = false;
+
+ if (!packet_creator_.HasRoomForStreamFrame(id, offset)) {
+ SerializeAndSendPacket();
+ }
+
+ if (fec_protection == MUST_FEC_PROTECT) {
+ MaybeStartFecProtection();
+ }
+
+ IOVector data = data_to_write;
+ size_t data_size = data.TotalBufferSize();
+ while (delegate_->ShouldGeneratePacket(NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA, handshake)) {
+ QuicFrame frame;
+ size_t bytes_consumed;
+ if (notifier != nullptr) {
+ // We want to track which packet this stream frame ends up in.
+ bytes_consumed = packet_creator_.CreateStreamFrameWithNotifier(
+ id, data, offset + total_bytes_consumed, fin, notifier, &frame);
+ } else {
+ bytes_consumed = packet_creator_.CreateStreamFrame(
+ id, data, offset + total_bytes_consumed, fin, &frame);
+ }
+ if (!AddFrame(frame)) {
+ LOG(DFATAL) << "Failed to add stream frame.";
+ // Inability to add a STREAM frame creates an unrecoverable hole in a
+ // the stream, so it's best to close the connection.
+ delegate_->CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return QuicConsumedData(0, false);
+ }
+
+ total_bytes_consumed += bytes_consumed;
+ fin_consumed = fin && total_bytes_consumed == data_size;
+ data.Consume(bytes_consumed);
+ DCHECK(data.Empty() || packet_creator_.BytesFree() == 0u);
+
+ // TODO(ianswett): Restore packet reordering.
+ if (!InBatchMode() || !packet_creator_.HasRoomForStreamFrame(id, offset)) {
+ SerializeAndSendPacket();
+ }
+
+ if (data.Empty()) {
+ // We're done writing the data. Exit the loop.
+ // We don't make this a precondition because we could have 0 bytes of data
+ // if we're simply writing a fin.
+ if (fec_protection == MUST_FEC_PROTECT) {
+ // Turn off FEC protection when we're done writing protected data.
+ DVLOG(1) << "Turning FEC protection OFF";
+ should_fec_protect_ = false;
+ }
+ break;
+ }
+ }
+
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ if (handshake == IS_HANDSHAKE) {
+ SendQueuedFrames(true);
+ }
+
+ // Try to close FEC group since we've either run out of data to send or we're
+ // blocked. If not in batch mode, force close the group.
+ // TODO(jri): This method should be called with flush=false here
+ // once the timer-based FEC sending is done, to separate FEC sending from
+ // the end of batch operations.
+ MaybeSendFecPacketAndCloseGroup(!InBatchMode());
+
+ DCHECK(InBatchMode() || !packet_creator_.HasPendingFrames());
+ return QuicConsumedData(total_bytes_consumed, fin_consumed);
+}
+
+bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const {
+ DCHECK(HasPendingFrames());
+ HasRetransmittableData retransmittable =
+ (should_send_ack_ || should_send_feedback_ || should_send_stop_waiting_)
+ ? NO_RETRANSMITTABLE_DATA : HAS_RETRANSMITTABLE_DATA;
+ if (retransmittable == HAS_RETRANSMITTABLE_DATA) {
+ DCHECK(!queued_control_frames_.empty()); // These are retransmittable.
+ }
+ return delegate_->ShouldGeneratePacket(NOT_RETRANSMISSION, retransmittable,
+ NOT_HANDSHAKE);
+}
+
+void QuicPacketGenerator::SendQueuedFrames(bool flush) {
+ // Only add pending frames if we are SURE we can then send the whole packet.
+ while (HasPendingFrames() &&
+ (flush || CanSendWithNextPendingFrameAddition())) {
+ if (!AddNextPendingFrame()) {
+ // Packet was full, so serialize and send it.
+ SerializeAndSendPacket();
+ }
+ }
+
+ if (!InBatchMode() || flush) {
+ if (packet_creator_.HasPendingFrames()) {
+ SerializeAndSendPacket();
+ }
+ // Ensure the FEC group is closed at the end of this method unless other
+ // writes are pending.
+ MaybeSendFecPacketAndCloseGroup(true);
+ }
+}
+
+void QuicPacketGenerator::MaybeStartFecProtection() {
+ if (!packet_creator_.IsFecEnabled()) {
+ return;
+ }
+ DVLOG(1) << "Turning FEC protection ON";
+ should_fec_protect_ = true;
+ if (packet_creator_.IsFecProtected()) {
+ // Only start creator's FEC protection if not already on.
+ return;
+ }
+ if (HasQueuedFrames()) {
+ // TODO(jri): This currently requires that the generator flush out any
+ // pending frames when FEC protection is turned on. If current packet can be
+ // converted to an FEC protected packet, do it. This will require the
+ // generator to check if the resulting expansion still allows the incoming
+ // frame to be added to the packet.
+ SendQueuedFrames(true);
+ }
+ packet_creator_.StartFecProtectingPackets();
+ DCHECK(packet_creator_.IsFecProtected());
+}
+
+void QuicPacketGenerator::MaybeSendFecPacketAndCloseGroup(bool force) {
+ if (!packet_creator_.IsFecProtected() ||
+ packet_creator_.HasPendingFrames() ||
+ !packet_creator_.ShouldSendFec(force)) {
+ return;
+ }
+ // TODO(jri): SerializeFec can return a NULL packet, and this should
+ // cause an early return, with a call to delegate_->OnPacketGenerationError.
+ SerializedPacket serialized_fec = packet_creator_.SerializeFec();
+ DCHECK(serialized_fec.packet);
+ delegate_->OnSerializedPacket(serialized_fec);
+ // Turn FEC protection off if creator's protection is on and the creator
+ // does not have an open FEC group.
+ // Note: We only wait until the frames queued in the creator are flushed;
+ // pending frames in the generator will not keep us from turning FEC off.
+ if (!should_fec_protect_ && !packet_creator_.IsFecGroupOpen()) {
+ packet_creator_.StopFecProtectingPackets();
+ DCHECK(!packet_creator_.IsFecProtected());
+ }
+}
+
+bool QuicPacketGenerator::InBatchMode() {
+ return batch_mode_;
+}
+
+void QuicPacketGenerator::StartBatchOperations() {
+ batch_mode_ = true;
+}
+
+void QuicPacketGenerator::FinishBatchOperations() {
+ batch_mode_ = false;
+ SendQueuedFrames(false);
+}
+
+void QuicPacketGenerator::FlushAllQueuedFrames() {
+ SendQueuedFrames(true);
+}
+
+bool QuicPacketGenerator::HasQueuedFrames() const {
+ return packet_creator_.HasPendingFrames() || HasPendingFrames();
+}
+
+bool QuicPacketGenerator::HasPendingFrames() const {
+ return should_send_ack_ || should_send_feedback_ ||
+ should_send_stop_waiting_ || !queued_control_frames_.empty();
+}
+
+bool QuicPacketGenerator::AddNextPendingFrame() {
+ if (should_send_ack_) {
+ pending_ack_frame_.reset(delegate_->CreateAckFrame());
+ // If we can't this add the frame now, then we still need to do so later.
+ should_send_ack_ = !AddFrame(QuicFrame(pending_ack_frame_.get()));
+ // Return success if we have cleared out this flag (i.e., added the frame).
+ // If we still need to send, then the frame is full, and we have failed.
+ return !should_send_ack_;
+ }
+
+ if (should_send_feedback_) {
+ pending_feedback_frame_.reset(delegate_->CreateFeedbackFrame());
+ // If we can't this add the frame now, then we still need to do so later.
+ should_send_feedback_ = !AddFrame(QuicFrame(pending_feedback_frame_.get()));
+ // Return success if we have cleared out this flag (i.e., added the frame).
+ // If we still need to send, then the frame is full, and we have failed.
+ return !should_send_feedback_;
+ }
+
+ if (should_send_stop_waiting_) {
+ pending_stop_waiting_frame_.reset(delegate_->CreateStopWaitingFrame());
+ // If we can't this add the frame now, then we still need to do so later.
+ should_send_stop_waiting_ =
+ !AddFrame(QuicFrame(pending_stop_waiting_frame_.get()));
+ // Return success if we have cleared out this flag (i.e., added the frame).
+ // If we still need to send, then the frame is full, and we have failed.
+ return !should_send_stop_waiting_;
+ }
+
+ LOG_IF(DFATAL, queued_control_frames_.empty())
+ << "AddNextPendingFrame called with no queued control frames.";
+ if (!AddFrame(queued_control_frames_.back())) {
+ // Packet was full.
+ return false;
+ }
+ queued_control_frames_.pop_back();
+ return true;
+}
+
+bool QuicPacketGenerator::AddFrame(const QuicFrame& frame) {
+ bool success = packet_creator_.AddSavedFrame(frame);
+ if (success && debug_delegate_) {
+ debug_delegate_->OnFrameAddedToPacket(frame);
+ }
+ return success;
+}
+
+void QuicPacketGenerator::SerializeAndSendPacket() {
+ SerializedPacket serialized_packet = packet_creator_.SerializePacket();
+ DCHECK(serialized_packet.packet);
+ delegate_->OnSerializedPacket(serialized_packet);
+ MaybeSendFecPacketAndCloseGroup(false);
+}
+
+void QuicPacketGenerator::StopSendingVersion() {
+ packet_creator_.StopSendingVersion();
+}
+
+QuicPacketSequenceNumber QuicPacketGenerator::sequence_number() const {
+ return packet_creator_.sequence_number();
+}
+
+size_t QuicPacketGenerator::max_packet_length() const {
+ return packet_creator_.max_packet_length();
+}
+
+void QuicPacketGenerator::set_max_packet_length(size_t length) {
+ packet_creator_.set_max_packet_length(length);
+}
+
+QuicEncryptedPacket* QuicPacketGenerator::SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions) {
+ return packet_creator_.SerializeVersionNegotiationPacket(supported_versions);
+}
+
+SerializedPacket QuicPacketGenerator::ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length) {
+ return packet_creator_.ReserializeAllFrames(frames, original_length);
+}
+
+void QuicPacketGenerator::UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window) {
+ return packet_creator_.UpdateSequenceNumberLength(
+ least_packet_awaited_by_peer, congestion_window);
+}
+
+void QuicPacketGenerator::set_encryption_level(EncryptionLevel level) {
+ packet_creator_.set_encryption_level(level);
+}
+
+} // namespace net
diff --git a/net/quic/quic_packet_generator.h b/net/quic/quic_packet_generator.h
new file mode 100644
index 0000000..44a6ddf
--- /dev/null
+++ b/net/quic/quic_packet_generator.h
@@ -0,0 +1,247 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Responsible for generating packets on behalf of a QuicConnection.
+// Packets are serialized just-in-time. Control frames are queued.
+// Ack and Feedback frames will be requested from the Connection
+// just-in-time. When a packet needs to be sent, the Generator
+// will serialize a packet and pass it to QuicConnection::SendOrQueuePacket()
+//
+// The Generator's mode of operation is controlled by two conditions:
+//
+// 1) Is the Delegate writable?
+//
+// If the Delegate is not writable, then no operations will cause
+// a packet to be serialized. In particular:
+// * SetShouldSendAck will simply record that an ack is to be sent.
+// * AddControlFrame will enqueue the control frame.
+// * ConsumeData will do nothing.
+//
+// If the Delegate is writable, then the behavior depends on the second
+// condition:
+//
+// 2) Is the Generator in batch mode?
+//
+// If the Generator is NOT in batch mode, then each call to a write
+// operation will serialize one or more packets. The contents will
+// include any previous queued frames. If an ack should be sent
+// but has not been sent, then the Delegate will be asked to create
+// an Ack frame which will then be included in the packet. When
+// the write call completes, the current packet will be serialized
+// and sent to the Delegate, even if it is not full.
+//
+// If the Generator is in batch mode, then each write operation will
+// add data to the "current" packet. When the current packet becomes
+// full, it will be serialized and sent to the packet. When batch
+// mode is ended via |FinishBatchOperations|, the current packet
+// will be serialzied, even if it is not full.
+//
+// FEC behavior also depends on batch mode. In batch mode, FEC packets
+// will be sent after |max_packets_per_group| have been sent, as well
+// as after batch operations are complete. When not in batch mode,
+// an FEC packet will be sent after each write call completes.
+//
+// TODO(rch): This behavior should probably be tuned. When not in batch
+// mode, we should probably set a timer so that several independent
+// operations can be grouped into the same FEC group.
+//
+// When an FEC packet is generated, it will be send to the Delegate,
+// even if the Delegate has become unwritable after handling the
+// data packet immediately proceeding the FEC packet.
+
+#ifndef NET_QUIC_QUIC_PACKET_GENERATOR_H_
+#define NET_QUIC_QUIC_PACKET_GENERATOR_H_
+
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_sent_packet_manager.h"
+#include "net/quic/quic_types.h"
+
+namespace net {
+
+namespace test {
+class QuicPacketGeneratorPeer;
+} // namespace test
+
+class QuicAckNotifier;
+
+class NET_EXPORT_PRIVATE QuicPacketGenerator {
+ public:
+ class NET_EXPORT_PRIVATE DelegateInterface {
+ public:
+ virtual ~DelegateInterface() {}
+ virtual bool ShouldGeneratePacket(TransmissionType transmission_type,
+ HasRetransmittableData retransmittable,
+ IsHandshake handshake) = 0;
+ virtual QuicAckFrame* CreateAckFrame() = 0;
+ virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0;
+ virtual QuicStopWaitingFrame* CreateStopWaitingFrame() = 0;
+ // Takes ownership of |packet.packet| and |packet.retransmittable_frames|.
+ virtual void OnSerializedPacket(const SerializedPacket& packet) = 0;
+ virtual void CloseConnection(QuicErrorCode error, bool from_peer) = 0;
+ };
+
+ // Interface which gets callbacks from the QuicPacketGenerator at interesting
+ // points. Implementations must not mutate the state of the generator
+ // as a result of these callbacks.
+ class NET_EXPORT_PRIVATE DebugDelegate {
+ public:
+ virtual ~DebugDelegate() {}
+
+ // Called when a frame has been added to the current packet.
+ virtual void OnFrameAddedToPacket(const QuicFrame& frame) {}
+ };
+
+ QuicPacketGenerator(QuicConnectionId connection_id,
+ QuicFramer* framer,
+ QuicRandom* random_generator,
+ DelegateInterface* delegate);
+
+ virtual ~QuicPacketGenerator();
+
+ // Called by the connection in the event of the congestion window changing.
+ void OnCongestionWindowChange(QuicByteCount congestion_window);
+
+ // Indicates that an ACK frame should be sent. If |also_send_feedback| is
+ // true, then it also indicates a CONGESTION_FEEDBACK frame should be sent.
+ // If |also_send_stop_waiting| is true, then it also indicates that a
+ // STOP_WAITING frame should be sent as well.
+ // The contents of the frame(s) will be generated via a call to the delegates
+ // CreateAckFrame() and CreateFeedbackFrame() when the packet is serialized.
+ void SetShouldSendAck(bool also_send_feedback,
+ bool also_send_stop_waiting);
+
+ // Indicates that a STOP_WAITING frame should be sent.
+ void SetShouldSendStopWaiting();
+
+ void AddControlFrame(const QuicFrame& frame);
+
+ // Given some data, may consume part or all of it and pass it to the
+ // packet creator to be serialized into packets. If not in batch
+ // mode, these packets will also be sent during this call. Also
+ // attaches a QuicAckNotifier to any created stream frames, which
+ // will be called once the frame is ACKed by the peer. The
+ // QuicAckNotifier is owned by the QuicConnection. |notifier| may
+ // be nullptr.
+ QuicConsumedData ConsumeData(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier* notifier);
+
+ // Indicates whether batch mode is currently enabled.
+ bool InBatchMode();
+ // Disables flushing.
+ void StartBatchOperations();
+ // Enables flushing and flushes queued data which can be sent.
+ void FinishBatchOperations();
+
+ // Flushes all queued frames, even frames which are not sendable.
+ void FlushAllQueuedFrames();
+
+ bool HasQueuedFrames() const;
+
+ // Makes the framer not serialize the protocol version in sent packets.
+ void StopSendingVersion();
+
+ // Creates a version negotiation packet which supports |supported_versions|.
+ // Caller owns the created packet. Also, sets the entropy hash of the
+ // serialized packet to a random bool and returns that value as a member of
+ // SerializedPacket.
+ QuicEncryptedPacket* SerializeVersionNegotiationPacket(
+ const QuicVersionVector& supported_versions);
+
+
+ // Re-serializes frames with the original packet's sequence number length.
+ // Used for retransmitting packets to ensure they aren't too long.
+ // Caller must ensure that any open FEC group is closed before calling this
+ // method.
+ SerializedPacket ReserializeAllFrames(
+ const QuicFrames& frames,
+ QuicSequenceNumberLength original_length);
+
+ // Update the sequence number length to use in future packets as soon as it
+ // can be safely changed.
+ void UpdateSequenceNumberLength(
+ QuicPacketSequenceNumber least_packet_awaited_by_peer,
+ QuicByteCount congestion_window);
+
+ // Sets the encryption level that will be applied to new packets.
+ void set_encryption_level(EncryptionLevel level);
+
+ // Sequence number of the last created packet, or 0 if no packets have been
+ // created.
+ QuicPacketSequenceNumber sequence_number() const;
+
+ size_t max_packet_length() const;
+
+ void set_max_packet_length(size_t length);
+
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
+ debug_delegate_ = debug_delegate;
+ }
+
+ private:
+ friend class test::QuicPacketGeneratorPeer;
+
+ // Turn on FEC protection for subsequent packets in the generator.
+ // If no FEC group is currently open in the creator, this method flushes any
+ // queued frames in the generator and in the creator, and it then turns FEC on
+ // in the creator. This method may be called with an open FEC group in the
+ // creator, in which case, only the generator's state is altered.
+ void MaybeStartFecProtection();
+
+ // Serializes and calls the delegate on an FEC packet if one was under
+ // construction in the creator. When |force| is false, it relies on the
+ // creator being ready to send an FEC packet, otherwise FEC packet is sent
+ // as long as one is under construction in the creator. Also tries to turn
+ // off FEC protection in the creator if it's off in the generator.
+ void MaybeSendFecPacketAndCloseGroup(bool force);
+
+ void SendQueuedFrames(bool flush);
+
+ // Test to see if we have pending ack, feedback, or control frames.
+ bool HasPendingFrames() const;
+ // Test to see if the addition of a pending frame (which might be
+ // retransmittable) would still allow the resulting packet to be sent now.
+ bool CanSendWithNextPendingFrameAddition() const;
+ // Add exactly one pending frame, preferring ack over feedback over control
+ // frames.
+ bool AddNextPendingFrame();
+
+ bool AddFrame(const QuicFrame& frame);
+
+ void SerializeAndSendPacket();
+
+ DelegateInterface* delegate_;
+ DebugDelegate* debug_delegate_;
+
+ QuicPacketCreator packet_creator_;
+ QuicFrames queued_control_frames_;
+
+ // True if batch mode is currently enabled.
+ bool batch_mode_;
+
+ // True if FEC protection is on. The creator may have an open FEC group even
+ // if this variable is false.
+ bool should_fec_protect_;
+
+ // Flags to indicate the need for just-in-time construction of a frame.
+ bool should_send_ack_;
+ bool should_send_feedback_;
+ bool should_send_stop_waiting_;
+ // If we put a non-retransmittable frame (namley ack or feedback frame) in
+ // this packet, then we have to hold a reference to it until we flush (and
+ // serialize it). Retransmittable frames are referenced elsewhere so that they
+ // can later be (optionally) retransmitted.
+ scoped_ptr<QuicAckFrame> pending_ack_frame_;
+ scoped_ptr<QuicCongestionFeedbackFrame> pending_feedback_frame_;
+ scoped_ptr<QuicStopWaitingFrame> pending_stop_waiting_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketGenerator);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PACKET_GENERATOR_H_
diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc
new file mode 100644
index 0000000..703fd64
--- /dev/null
+++ b/net/quic/quic_packet_generator_test.cc
@@ -0,0 +1,957 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_packet_generator.h"
+
+#include <string>
+
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_packet_creator_peer.h"
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using testing::InSequence;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+class MockDelegate : public QuicPacketGenerator::DelegateInterface {
+ public:
+ MockDelegate() {}
+ virtual ~MockDelegate() OVERRIDE {}
+
+ MOCK_METHOD3(ShouldGeneratePacket,
+ bool(TransmissionType transmission_type,
+ HasRetransmittableData retransmittable,
+ IsHandshake handshake));
+ MOCK_METHOD0(CreateAckFrame, QuicAckFrame*());
+ MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*());
+ MOCK_METHOD0(CreateStopWaitingFrame, QuicStopWaitingFrame*());
+ MOCK_METHOD1(OnSerializedPacket, void(const SerializedPacket& packet));
+ MOCK_METHOD2(CloseConnection, void(QuicErrorCode, bool));
+
+ void SetCanWriteAnything() {
+ EXPECT_CALL(*this, ShouldGeneratePacket(NOT_RETRANSMISSION, _, _))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*this, ShouldGeneratePacket(NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA, _))
+ .WillRepeatedly(Return(true));
+ }
+
+ void SetCanNotWrite() {
+ EXPECT_CALL(*this, ShouldGeneratePacket(NOT_RETRANSMISSION, _, _))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*this, ShouldGeneratePacket(NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA, _))
+ .WillRepeatedly(Return(false));
+ }
+
+ // Use this when only ack and feedback frames should be allowed to be written.
+ void SetCanWriteOnlyNonRetransmittable() {
+ EXPECT_CALL(*this, ShouldGeneratePacket(NOT_RETRANSMISSION, _, _))
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*this, ShouldGeneratePacket(NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA, _))
+ .WillRepeatedly(Return(true));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+// Simple struct for describing the contents of a packet.
+// Useful in conjunction with a SimpleQuicFrame for validating
+// that a packet contains the expected frames.
+struct PacketContents {
+ PacketContents()
+ : num_ack_frames(0),
+ num_connection_close_frames(0),
+ num_feedback_frames(0),
+ num_goaway_frames(0),
+ num_rst_stream_frames(0),
+ num_stop_waiting_frames(0),
+ num_stream_frames(0),
+ fec_group(0) {
+ }
+
+ size_t num_ack_frames;
+ size_t num_connection_close_frames;
+ size_t num_feedback_frames;
+ size_t num_goaway_frames;
+ size_t num_rst_stream_frames;
+ size_t num_stop_waiting_frames;
+ size_t num_stream_frames;
+
+ QuicFecGroupNumber fec_group;
+};
+
+} // namespace
+
+class QuicPacketGeneratorTest : public ::testing::Test {
+ protected:
+ QuicPacketGeneratorTest()
+ : framer_(QuicSupportedVersions(), QuicTime::Zero(), false),
+ generator_(42, &framer_, &random_, &delegate_),
+ creator_(QuicPacketGeneratorPeer::GetPacketCreator(&generator_)),
+ packet_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr),
+ packet2_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr),
+ packet3_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr),
+ packet4_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr),
+ packet5_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr),
+ packet6_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr),
+ packet7_(0, PACKET_1BYTE_SEQUENCE_NUMBER, nullptr, 0, nullptr) {}
+
+ virtual ~QuicPacketGeneratorTest() OVERRIDE {
+ delete packet_.packet;
+ delete packet_.retransmittable_frames;
+ delete packet2_.packet;
+ delete packet2_.retransmittable_frames;
+ delete packet3_.packet;
+ delete packet3_.retransmittable_frames;
+ delete packet4_.packet;
+ delete packet4_.retransmittable_frames;
+ delete packet5_.packet;
+ delete packet5_.retransmittable_frames;
+ delete packet6_.packet;
+ delete packet6_.retransmittable_frames;
+ delete packet7_.packet;
+ delete packet7_.retransmittable_frames;
+ }
+
+ QuicAckFrame* CreateAckFrame() {
+ // TODO(rch): Initialize this so it can be verified later.
+ return new QuicAckFrame(MakeAckFrame(0));
+ }
+
+ QuicCongestionFeedbackFrame* CreateFeedbackFrame() {
+ QuicCongestionFeedbackFrame* frame = new QuicCongestionFeedbackFrame;
+ frame->type = kTCP;
+ frame->tcp.receive_window = 0x4030;
+ return frame;
+ }
+
+ QuicStopWaitingFrame* CreateStopWaitingFrame() {
+ QuicStopWaitingFrame* frame = new QuicStopWaitingFrame();
+ frame->entropy_hash = 0;
+ frame->least_unacked = 0;
+ return frame;
+ }
+
+ QuicRstStreamFrame* CreateRstStreamFrame() {
+ return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR, 0);
+ }
+
+ QuicGoAwayFrame* CreateGoAwayFrame() {
+ return new QuicGoAwayFrame(QUIC_NO_ERROR, 1, string());
+ }
+
+ void CheckPacketContains(const PacketContents& contents,
+ const SerializedPacket& packet) {
+ size_t num_retransmittable_frames = contents.num_connection_close_frames +
+ contents.num_goaway_frames + contents.num_rst_stream_frames +
+ contents.num_stream_frames;
+ size_t num_frames = contents.num_feedback_frames + contents.num_ack_frames +
+ contents.num_stop_waiting_frames + num_retransmittable_frames;
+
+ if (num_retransmittable_frames == 0) {
+ ASSERT_TRUE(packet.retransmittable_frames == nullptr);
+ } else {
+ ASSERT_TRUE(packet.retransmittable_frames != nullptr);
+ EXPECT_EQ(num_retransmittable_frames,
+ packet.retransmittable_frames->frames().size());
+ }
+
+ ASSERT_TRUE(packet.packet != nullptr);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet));
+ EXPECT_EQ(num_frames, simple_framer_.num_frames());
+ EXPECT_EQ(contents.num_ack_frames, simple_framer_.ack_frames().size());
+ EXPECT_EQ(contents.num_connection_close_frames,
+ simple_framer_.connection_close_frames().size());
+ EXPECT_EQ(contents.num_feedback_frames,
+ simple_framer_.feedback_frames().size());
+ EXPECT_EQ(contents.num_goaway_frames,
+ simple_framer_.goaway_frames().size());
+ EXPECT_EQ(contents.num_rst_stream_frames,
+ simple_framer_.rst_stream_frames().size());
+ EXPECT_EQ(contents.num_stream_frames,
+ simple_framer_.stream_frames().size());
+ EXPECT_EQ(contents.num_stop_waiting_frames,
+ simple_framer_.stop_waiting_frames().size());
+ EXPECT_EQ(contents.fec_group, simple_framer_.header().fec_group);
+ }
+
+ void CheckPacketHasSingleStreamFrame(const SerializedPacket& packet) {
+ ASSERT_TRUE(packet.retransmittable_frames != nullptr);
+ EXPECT_EQ(1u, packet.retransmittable_frames->frames().size());
+ ASSERT_TRUE(packet.packet != nullptr);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet));
+ EXPECT_EQ(1u, simple_framer_.num_frames());
+ EXPECT_EQ(1u, simple_framer_.stream_frames().size());
+ }
+
+ void CheckPacketIsFec(const SerializedPacket& packet,
+ QuicPacketSequenceNumber fec_group) {
+ ASSERT_TRUE(packet.retransmittable_frames == nullptr);
+ ASSERT_TRUE(packet.packet != nullptr);
+ ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet));
+ EXPECT_TRUE(simple_framer_.header().fec_flag);
+ EXPECT_EQ(fec_group, simple_framer_.fec_data().fec_group);
+ }
+
+ IOVector CreateData(size_t len) {
+ data_array_.reset(new char[len]);
+ memset(data_array_.get(), '?', len);
+ IOVector data;
+ data.Append(data_array_.get(), len);
+ return data;
+ }
+
+ QuicFramer framer_;
+ MockRandom random_;
+ StrictMock<MockDelegate> delegate_;
+ QuicPacketGenerator generator_;
+ QuicPacketCreator* creator_;
+ SimpleQuicFramer simple_framer_;
+ SerializedPacket packet_;
+ SerializedPacket packet2_;
+ SerializedPacket packet3_;
+ SerializedPacket packet4_;
+ SerializedPacket packet5_;
+ SerializedPacket packet6_;
+ SerializedPacket packet7_;
+
+ private:
+ scoped_ptr<char[]> data_array_;
+};
+
+class MockDebugDelegate : public QuicPacketGenerator::DebugDelegate {
+ public:
+ MOCK_METHOD1(OnFrameAddedToPacket,
+ void(const QuicFrame&));
+};
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) {
+ delegate_.SetCanNotWrite();
+
+ generator_.SetShouldSendAck(false, false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) {
+ StrictMock<MockDebugDelegate> debug_delegate;
+
+ generator_.set_debug_delegate(&debug_delegate);
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+ generator_.StartBatchOperations();
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(debug_delegate, OnFrameAddedToPacket(_)).Times(1);
+
+ generator_.SetShouldSendAck(false, false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) {
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+
+ generator_.SetShouldSendAck(false, false);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ShouldSendAckWithFeedback_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+ generator_.StartBatchOperations();
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ generator_.SetShouldSendAck(true, false);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ShouldSendAckWithFeedback_WritableAndShouldFlush) {
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+ EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce(
+ Return(CreateStopWaitingFrame()));
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+
+ generator_.SetShouldSendAck(true, true);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_stop_waiting_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritable) {
+ delegate_.SetCanNotWrite();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_OnlyAckWritable) {
+ delegate_.SetCanWriteOnlyNonRetransmittable();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_NotWritableBatchThenFlush) {
+ delegate_.SetCanNotWrite();
+ generator_.StartBatchOperations();
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ generator_.FinishBatchOperations();
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+ generator_.FlushAllQueuedFrames();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_rst_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, AddControlFrame_WritableAndShouldFlush) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_rst_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_NotWritable) {
+ delegate_.SetCanNotWrite();
+
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(0u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_WritableAndShouldFlush) {
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+ QuicConsumedData consumed = generator_.ConsumeData(
+ kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest,
+ ConsumeDataMultipleTimes_WritableAndShouldNotFlush) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+ MAY_FEC_PROTECT, nullptr);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(4u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_BatchOperations) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+ MAY_FEC_PROTECT, nullptr);
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(4u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+
+ // Now both frames will be flushed out.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+ generator_.FinishBatchOperations();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 2;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataFEC) {
+ delegate_.SetCanWriteAnything();
+
+ // Send FEC every two packets.
+ creator_->set_max_packets_per_fec_group(2);
+
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet3_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet4_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet5_));
+ }
+
+ // Send enough data to create 3 packets: two full and one partial. Send
+ // with MUST_FEC_PROTECT flag.
+ size_t data_len = 2 * kDefaultMaxPacketSize + 100;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, CreateData(data_len), 0, true, MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ CheckPacketHasSingleStreamFrame(packet_);
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketIsFec(packet3_, 1);
+
+ CheckPacketHasSingleStreamFrame(packet4_);
+ CheckPacketIsFec(packet5_, 4);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeDataSendsFecAtEnd) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(6);
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet3_));
+ }
+
+ // Send enough data to create 2 packets: one full and one partial. Send
+ // with MUST_FEC_PROTECT flag.
+ size_t data_len = 1 * kDefaultMaxPacketSize + 100;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, CreateData(data_len), 0, true, MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ CheckPacketHasSingleStreamFrame(packet_);
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketIsFec(packet3_, 1);
+}
+
+TEST_F(QuicPacketGeneratorTest, ConsumeData_FramesPreviouslyQueued) {
+ // Set the packet size be enough for two stream frames with 0 stream offset,
+ // but not enough for a stream frame of 0 offset and one with non-zero offset.
+ size_t length =
+ NullEncrypter().GetCiphertextSize(0) +
+ GetPacketHeaderSize(creator_->connection_id_length(),
+ true,
+ creator_->next_sequence_number_length(),
+ NOT_IN_FEC_GROUP) +
+ // Add an extra 3 bytes for the payload and 1 byte so BytesFree is larger
+ // than the GetMinStreamFrameSize.
+ QuicFramer::GetMinStreamFrameSize(1, 0, false, NOT_IN_FEC_GROUP) + 3 +
+ QuicFramer::GetMinStreamFrameSize(1, 0, true, NOT_IN_FEC_GROUP) + 1;
+ creator_->set_max_packet_length(length);
+ delegate_.SetCanWriteAnything();
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ }
+ generator_.StartBatchOperations();
+ // Queue enough data to prevent a stream frame with a non-zero offset from
+ // fitting.
+ QuicConsumedData consumed =
+ generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 0, false,
+ MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_FALSE(consumed.fin_consumed);
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+
+ // This frame will not fit with the existing frame, causing the queued frame
+ // to be serialized, and it will not fit with another frame like it, so it is
+ // serialized by itself.
+ consumed = generator_.ConsumeData(kHeadersStreamId, MakeIOVector("bar"), 3,
+ true, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(3u, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+ CheckPacketContains(contents, packet2_);
+}
+
+TEST_F(QuicPacketGeneratorTest, FecGroupSizeOnCongestionWindowChange) {
+ delegate_.SetCanWriteAnything();
+ creator_->set_max_packets_per_fec_group(50);
+ EXPECT_EQ(50u, creator_->max_packets_per_fec_group());
+ EXPECT_FALSE(creator_->IsFecGroupOpen());
+
+ // On reduced cwnd.
+ generator_.OnCongestionWindowChange(7 * kDefaultTCPMSS);
+ EXPECT_EQ(3u, creator_->max_packets_per_fec_group());
+
+ // On increased cwnd.
+ generator_.OnCongestionWindowChange(100 * kDefaultTCPMSS);
+ EXPECT_EQ(50u, creator_->max_packets_per_fec_group());
+
+ // On collapsed cwnd.
+ generator_.OnCongestionWindowChange(1 * kDefaultTCPMSS);
+ EXPECT_EQ(2u, creator_->max_packets_per_fec_group());
+}
+
+TEST_F(QuicPacketGeneratorTest, FecGroupSizeChangeWithOpenGroup) {
+ delegate_.SetCanWriteAnything();
+ // TODO(jri): This starting of batch mode should not be required when
+ // FEC sending is separated from batching operations.
+ generator_.StartBatchOperations();
+ creator_->set_max_packets_per_fec_group(50);
+ EXPECT_EQ(50u, creator_->max_packets_per_fec_group());
+ EXPECT_FALSE(creator_->IsFecGroupOpen());
+
+ // Send enough data to create 4 packets with MUST_FEC_PROTECT flag.
+ // 3 packets are sent, one is queued in the creator.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet3_));
+ }
+ size_t data_len = 3 * kDefaultMaxPacketSize + 1;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 7, CreateData(data_len), 0, true, MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(creator_->IsFecGroupOpen());
+
+ // Change FEC groupsize.
+ generator_.OnCongestionWindowChange(2 * kDefaultTCPMSS);
+ EXPECT_EQ(2u, creator_->max_packets_per_fec_group());
+
+ // Send enough data to trigger one unprotected data packet,
+ // causing the FEC packet to also be sent.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet4_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet5_));
+ }
+ consumed = generator_.ConsumeData(7, CreateData(kDefaultMaxPacketSize), 0,
+ true, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(kDefaultMaxPacketSize, consumed.bytes_consumed);
+ // Verify that one FEC packet was sent.
+ CheckPacketIsFec(packet5_, /*fec_group=*/1u);
+ EXPECT_FALSE(creator_->IsFecGroupOpen());
+ EXPECT_FALSE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOff) {
+ delegate_.SetCanWriteAnything();
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Send one unprotected data packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+ // Verify that one data packet was sent.
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet3_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet4_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet5_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet6_));
+ }
+ // Send enough data to create 3 packets with MUST_FEC_PROTECT flag.
+ size_t data_len = 2 * kDefaultMaxPacketSize + 100;
+ consumed = generator_.ConsumeData(7, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ // Verify that two FEC packets were sent.
+ CheckPacketHasSingleStreamFrame(packet2_);
+ CheckPacketHasSingleStreamFrame(packet3_);
+ CheckPacketIsFec(packet4_, /*fec_group=*/2u);
+ CheckPacketHasSingleStreamFrame(packet5_);
+ CheckPacketIsFec(packet6_, /*fec_group=*/5u); // Sent at the end of stream.
+
+ // Send one unprotected data packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet7_));
+ consumed = generator_.ConsumeData(7, CreateData(1u), 0, true, MAY_FEC_PROTECT,
+ nullptr);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+ // Verify that one unprotected data packet was sent.
+ CheckPacketContains(contents, packet7_);
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFrameInCreator) {
+ delegate_.SetCanWriteAnything();
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+
+ generator_.StartBatchOperations();
+ // Queue enough data to prevent a stream frame with a non-zero offset from
+ // fitting.
+ QuicConsumedData consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ EXPECT_TRUE(creator_->HasPendingFrames());
+
+ // Queue protected data for sending. Should cause queued frames to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ EXPECT_FALSE(creator_->IsFecProtected());
+ consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 1;
+ // Transmitted packet was not FEC protected.
+ CheckPacketContains(contents, packet_);
+ EXPECT_TRUE(creator_->IsFecProtected());
+ EXPECT_TRUE(creator_->HasPendingFrames());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnWithPendingFramesInGenerator) {
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+
+ // Queue control frames in generator.
+ delegate_.SetCanNotWrite();
+ generator_.SetShouldSendAck(true, true);
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ // Set up frames to write into the creator when control frames are written.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+ EXPECT_CALL(delegate_, CreateStopWaitingFrame()).WillOnce(
+ Return(CreateStopWaitingFrame()));
+
+ // Generator should have queued control frames, and creator should be empty.
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+ EXPECT_FALSE(creator_->HasPendingFrames());
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue protected data for sending. Should cause queued frames to be flushed.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ QuicConsumedData consumed = generator_.ConsumeData(7, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_stop_waiting_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ // FEC protection should be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentFramesProtected) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue stream frame to be protected in creator.
+ generator_.StartBatchOperations();
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(1u, consumed.bytes_consumed);
+ // Creator has a pending protected frame.
+ EXPECT_TRUE(creator_->HasPendingFrames());
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Add enough unprotected data to exceed size of current packet, so that
+ // current packet is sent. Both frames will be sent out in a single packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+ size_t data_len = kDefaultMaxPacketSize;
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 2u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+ // FEC protection should still be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffWithSubsequentPacketsProtected) {
+ delegate_.SetCanWriteAnything();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ generator_.StartBatchOperations();
+ // Send first packet, FEC protected.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+ // Write enough data to cause a packet to be emitted.
+ size_t data_len = kDefaultMaxPacketSize;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 5, CreateData(data_len), 0, true, MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 1u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+
+ // FEC should still be on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Send enough unprotected data to cause second packet to be sent, which gets
+ // protected because it happens to fall within an open FEC group. Data packet
+ // will be followed by FEC packet.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet3_));
+ }
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ contents.num_stream_frames = 2u;
+ CheckPacketContains(contents, packet2_);
+ CheckPacketIsFec(packet3_, /*fec_group=*/1u);
+
+ // FEC protection should be off in creator.
+ EXPECT_FALSE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, SwitchFecOnOffThenOnWithCreatorProtectionOn) {
+ delegate_.SetCanWriteAnything();
+ generator_.StartBatchOperations();
+
+ // Enable FEC.
+ creator_->set_max_packets_per_fec_group(2);
+ EXPECT_FALSE(creator_->IsFecProtected());
+
+ // Queue one byte of FEC protected data.
+ QuicConsumedData consumed = generator_.ConsumeData(5, CreateData(1u), 0, true,
+ MUST_FEC_PROTECT, nullptr);
+ EXPECT_TRUE(creator_->HasPendingFrames());
+
+ // Add more unprotected data causing first packet to be sent, FEC protected.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ size_t data_len = kDefaultMaxPacketSize;
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ PacketContents contents;
+ contents.num_stream_frames = 2u;
+ contents.fec_group = 1u;
+ CheckPacketContains(contents, packet_);
+
+ // FEC group is still open in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+
+ // Add data that should be protected, large enough to cause second packet to
+ // be sent. Data packet should be followed by FEC packet.
+ {
+ InSequence dummy;
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet3_));
+ }
+ consumed = generator_.ConsumeData(5, CreateData(data_len), 0, true,
+ MUST_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ CheckPacketContains(contents, packet2_);
+ CheckPacketIsFec(packet3_, /*fec_group=*/1u);
+
+ // FEC protection should remain on in creator.
+ EXPECT_TRUE(creator_->IsFecProtected());
+}
+
+TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations) {
+ delegate_.SetCanNotWrite();
+
+ generator_.SetShouldSendAck(true, false);
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+
+ delegate_.SetCanWriteAnything();
+
+ generator_.StartBatchOperations();
+
+ // When the first write operation is invoked, the ack and feedback
+ // frames will be returned.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ // Send some data and a control frame
+ generator_.ConsumeData(3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT,
+ nullptr);
+ generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
+
+ // All five frames will be flushed out in a single packet.
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(SaveArg<0>(&packet_));
+ generator_.FinishBatchOperations();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_goaway_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_rst_stream_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+}
+
+TEST_F(QuicPacketGeneratorTest, NotWritableThenBatchOperations2) {
+ delegate_.SetCanNotWrite();
+
+ generator_.SetShouldSendAck(true, false);
+ generator_.AddControlFrame(QuicFrame(CreateRstStreamFrame()));
+ EXPECT_TRUE(generator_.HasQueuedFrames());
+
+ delegate_.SetCanWriteAnything();
+
+ generator_.StartBatchOperations();
+
+ // When the first write operation is invoked, the ack and feedback
+ // frames will be returned.
+ EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame()));
+ EXPECT_CALL(delegate_, CreateFeedbackFrame()).WillOnce(
+ Return(CreateFeedbackFrame()));
+
+ {
+ InSequence dummy;
+ // All five frames will be flushed out in a single packet
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet_));
+ EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
+ SaveArg<0>(&packet2_));
+ }
+
+ // Send enough data to exceed one packet
+ size_t data_len = kDefaultMaxPacketSize + 100;
+ QuicConsumedData consumed = generator_.ConsumeData(
+ 3, CreateData(data_len), 0, true, MAY_FEC_PROTECT, nullptr);
+ EXPECT_EQ(data_len, consumed.bytes_consumed);
+ EXPECT_TRUE(consumed.fin_consumed);
+ generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame()));
+
+ generator_.FinishBatchOperations();
+ EXPECT_FALSE(generator_.HasQueuedFrames());
+
+ // The first packet should have the queued data and part of the stream data.
+ PacketContents contents;
+ contents.num_ack_frames = 1;
+ contents.num_feedback_frames = 1;
+ contents.num_rst_stream_frames = 1;
+ contents.num_stream_frames = 1;
+ CheckPacketContains(contents, packet_);
+
+ // The second should have the remainder of the stream data.
+ PacketContents contents2;
+ contents2.num_goaway_frames = 1;
+ contents2.num_stream_frames = 1;
+ CheckPacketContains(contents2, packet2_);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_packet_writer.h b/net/quic/quic_packet_writer.h
new file mode 100644
index 0000000..16b7ade
--- /dev/null
+++ b/net/quic/quic_packet_writer.h
@@ -0,0 +1,46 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_PACKET_WRITER_H_
+#define NET_QUIC_QUIC_PACKET_WRITER_H_
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+struct WriteResult;
+
+// An interface between writers and the entity managing the
+// socket (in our case the QuicDispatcher). This allows the Dispatcher to
+// control writes, and manage any writers who end up write blocked.
+class NET_EXPORT_PRIVATE QuicPacketWriter {
+ public:
+ virtual ~QuicPacketWriter() {}
+
+ // Sends the packet out to the peer. If the write succeeded, the result's
+ // status is WRITE_STATUS_OK and bytes_written is populated. If the write
+ // failed, the result's status is WRITE_STATUS_BLOCKED or WRITE_STATUS_ERROR
+ // and error_code is populated.
+ virtual WriteResult WritePacket(
+ const char* buffer, size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) = 0;
+
+ // Returns true if the writer buffers and subsequently rewrites data
+ // when an attempt to write results in the underlying socket becoming
+ // write blocked.
+ virtual bool IsWriteBlockedDataBuffered() const = 0;
+
+ // Returns true if the network socket is not writable.
+ virtual bool IsWriteBlocked() const = 0;
+
+ // Records that the socket has become writable, for example when an EPOLLOUT
+ // is received or an asynchronous write completes.
+ virtual void SetWritable() = 0;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PACKET_WRITER_H_
diff --git a/net/quic/quic_per_connection_packet_writer.cc b/net/quic/quic_per_connection_packet_writer.cc
new file mode 100644
index 0000000..3882da8
--- /dev/null
+++ b/net/quic/quic_per_connection_packet_writer.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_per_connection_packet_writer.h"
+
+#include "net/quic/quic_server_packet_writer.h"
+
+namespace net {
+
+QuicPerConnectionPacketWriter::QuicPerConnectionPacketWriter(
+ QuicServerPacketWriter* shared_writer,
+ QuicConnection* connection)
+ : shared_writer_(shared_writer),
+ connection_(connection),
+ weak_factory_(this){
+}
+
+QuicPerConnectionPacketWriter::~QuicPerConnectionPacketWriter() {
+}
+
+QuicPacketWriter* QuicPerConnectionPacketWriter::shared_writer() const {
+ return shared_writer_;
+}
+
+WriteResult QuicPerConnectionPacketWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) {
+ return shared_writer_->WritePacketWithCallback(
+ buffer,
+ buf_len,
+ self_address,
+ peer_address,
+ base::Bind(&QuicPerConnectionPacketWriter::OnWriteComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+bool QuicPerConnectionPacketWriter::IsWriteBlockedDataBuffered() const {
+ return shared_writer_->IsWriteBlockedDataBuffered();
+}
+
+bool QuicPerConnectionPacketWriter::IsWriteBlocked() const {
+ return shared_writer_->IsWriteBlocked();
+}
+
+void QuicPerConnectionPacketWriter::SetWritable() {
+ shared_writer_->SetWritable();
+}
+
+void QuicPerConnectionPacketWriter::OnWriteComplete(WriteResult result) {
+ if (result.status == WRITE_STATUS_ERROR) {
+ connection_->OnWriteError(result.error_code);
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_per_connection_packet_writer.h b/net/quic/quic_per_connection_packet_writer.h
new file mode 100644
index 0000000..9b9b912
--- /dev/null
+++ b/net/quic/quic_per_connection_packet_writer.h
@@ -0,0 +1,53 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_PER_CONNECTION_PACKET_WRITER_H_
+#define NET_QUIC_QUIC_PER_CONNECTION_PACKET_WRITER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_packet_writer.h"
+
+namespace net {
+
+class QuicServerPacketWriter;
+
+// A connection-specific packet writer that notifies its connection when its
+// writes to the shared QuicServerPacketWriter complete.
+// This class is necessary because multiple connections can share the same
+// QuicServerPacketWriter, so it has no way to know which connection to notify.
+class QuicPerConnectionPacketWriter : public QuicPacketWriter {
+ public:
+ // Does not take ownership of |shared_writer| or |connection|.
+ QuicPerConnectionPacketWriter(QuicServerPacketWriter* shared_writer,
+ QuicConnection* connection);
+ virtual ~QuicPerConnectionPacketWriter();
+
+ QuicPacketWriter* shared_writer() const;
+ QuicConnection* connection() const { return connection_; }
+
+ // Default implementation of the QuicPacketWriter interface: Passes everything
+ // to |shared_writer_|.
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ private:
+ void OnWriteComplete(WriteResult result);
+
+ QuicServerPacketWriter* shared_writer_; // Not owned.
+ QuicConnection* connection_; // Not owned.
+
+ base::WeakPtrFactory<QuicPerConnectionPacketWriter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicPerConnectionPacketWriter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PER_CONNECTION_PACKET_WRITER_H_
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
new file mode 100644
index 0000000..96a4557
--- /dev/null
+++ b/net/quic/quic_protocol.cc
@@ -0,0 +1,749 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_protocol.h"
+
+#include "base/stl_util.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::map;
+using std::numeric_limits;
+using std::ostream;
+using std::string;
+
+namespace net {
+
+size_t GetPacketHeaderSize(const QuicPacketHeader& header) {
+ return GetPacketHeaderSize(header.public_header.connection_id_length,
+ header.public_header.version_flag,
+ header.public_header.sequence_number_length,
+ header.is_in_fec_group);
+}
+
+size_t GetPacketHeaderSize(QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length,
+ InFecGroup is_in_fec_group) {
+ return kPublicFlagsSize + connection_id_length +
+ (include_version ? kQuicVersionSize : 0) + sequence_number_length +
+ kPrivateFlagsSize + (is_in_fec_group == IN_FEC_GROUP ? kFecGroupSize : 0);
+}
+
+size_t GetStartOfFecProtectedData(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ return GetPacketHeaderSize(connection_id_length,
+ include_version,
+ sequence_number_length,
+ IN_FEC_GROUP);
+}
+
+size_t GetStartOfEncryptedData(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ // Don't include the fec size, since encryption starts before private flags.
+ return GetPacketHeaderSize(connection_id_length,
+ include_version,
+ sequence_number_length,
+ NOT_IN_FEC_GROUP) - kPrivateFlagsSize;
+}
+
+QuicPacketPublicHeader::QuicPacketPublicHeader()
+ : connection_id(0),
+ connection_id_length(PACKET_8BYTE_CONNECTION_ID),
+ reset_flag(false),
+ version_flag(false),
+ sequence_number_length(PACKET_6BYTE_SEQUENCE_NUMBER) {
+}
+
+QuicPacketPublicHeader::QuicPacketPublicHeader(
+ const QuicPacketPublicHeader& other)
+ : connection_id(other.connection_id),
+ connection_id_length(other.connection_id_length),
+ reset_flag(other.reset_flag),
+ version_flag(other.version_flag),
+ sequence_number_length(other.sequence_number_length),
+ versions(other.versions) {
+}
+
+QuicPacketPublicHeader::~QuicPacketPublicHeader() {}
+
+QuicPacketHeader::QuicPacketHeader()
+ : fec_flag(false),
+ entropy_flag(false),
+ entropy_hash(0),
+ packet_sequence_number(0),
+ is_in_fec_group(NOT_IN_FEC_GROUP),
+ fec_group(0) {
+}
+
+QuicPacketHeader::QuicPacketHeader(const QuicPacketPublicHeader& header)
+ : public_header(header),
+ fec_flag(false),
+ entropy_flag(false),
+ entropy_hash(0),
+ packet_sequence_number(0),
+ is_in_fec_group(NOT_IN_FEC_GROUP),
+ fec_group(0) {
+}
+
+QuicPublicResetPacket::QuicPublicResetPacket()
+ : nonce_proof(0),
+ rejected_sequence_number(0) {}
+
+QuicPublicResetPacket::QuicPublicResetPacket(
+ const QuicPacketPublicHeader& header)
+ : public_header(header),
+ nonce_proof(0),
+ rejected_sequence_number(0) {}
+
+QuicStreamFrame::QuicStreamFrame()
+ : stream_id(0),
+ fin(false),
+ offset(0),
+ notifier(nullptr) {}
+
+QuicStreamFrame::QuicStreamFrame(const QuicStreamFrame& frame)
+ : stream_id(frame.stream_id),
+ fin(frame.fin),
+ offset(frame.offset),
+ data(frame.data),
+ notifier(frame.notifier) {
+}
+
+QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ IOVector data)
+ : stream_id(stream_id),
+ fin(fin),
+ offset(offset),
+ data(data),
+ notifier(nullptr) {}
+
+string* QuicStreamFrame::GetDataAsString() const {
+ string* data_string = new string();
+ data_string->reserve(data.TotalBufferSize());
+ for (size_t i = 0; i < data.Size(); ++i) {
+ data_string->append(static_cast<char*>(data.iovec()[i].iov_base),
+ data.iovec()[i].iov_len);
+ }
+ DCHECK_EQ(data_string->size(), data.TotalBufferSize());
+ return data_string;
+}
+
+uint32 MakeQuicTag(char a, char b, char c, char d) {
+ return static_cast<uint32>(a) |
+ static_cast<uint32>(b) << 8 |
+ static_cast<uint32>(c) << 16 |
+ static_cast<uint32>(d) << 24;
+}
+
+bool ContainsQuicTag(const QuicTagVector& tag_vector, QuicTag tag) {
+ return std::find(tag_vector.begin(), tag_vector.end(), tag)
+ != tag_vector.end();
+}
+
+QuicVersionVector QuicSupportedVersions() {
+ QuicVersionVector supported_versions;
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ supported_versions.push_back(kSupportedQuicVersions[i]);
+ }
+ return supported_versions;
+}
+
+QuicTag QuicVersionToQuicTag(const QuicVersion version) {
+ switch (version) {
+ case QUIC_VERSION_18:
+ return MakeQuicTag('Q', '0', '1', '8');
+ case QUIC_VERSION_19:
+ return MakeQuicTag('Q', '0', '1', '9');
+ case QUIC_VERSION_21:
+ return MakeQuicTag('Q', '0', '2', '1');
+ case QUIC_VERSION_22:
+ return MakeQuicTag('Q', '0', '2', '2');
+ case QUIC_VERSION_23:
+ return MakeQuicTag('Q', '0', '2', '3');
+ default:
+ // This shold be an ERROR because we should never attempt to convert an
+ // invalid QuicVersion to be written to the wire.
+ LOG(ERROR) << "Unsupported QuicVersion: " << version;
+ return 0;
+ }
+}
+
+QuicVersion QuicTagToQuicVersion(const QuicTag version_tag) {
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ if (version_tag == QuicVersionToQuicTag(kSupportedQuicVersions[i])) {
+ return kSupportedQuicVersions[i];
+ }
+ }
+ // Reading from the client so this should not be considered an ERROR.
+ DVLOG(1) << "Unsupported QuicTag version: "
+ << QuicUtils::TagToString(version_tag);
+ return QUIC_VERSION_UNSUPPORTED;
+}
+
+#define RETURN_STRING_LITERAL(x) \
+case x: \
+return #x
+
+string QuicVersionToString(const QuicVersion version) {
+ switch (version) {
+ RETURN_STRING_LITERAL(QUIC_VERSION_18);
+ RETURN_STRING_LITERAL(QUIC_VERSION_19);
+ RETURN_STRING_LITERAL(QUIC_VERSION_21);
+ RETURN_STRING_LITERAL(QUIC_VERSION_22);
+ RETURN_STRING_LITERAL(QUIC_VERSION_23);
+ default:
+ return "QUIC_VERSION_UNSUPPORTED";
+ }
+}
+
+string QuicVersionVectorToString(const QuicVersionVector& versions) {
+ string result = "";
+ for (size_t i = 0; i < versions.size(); ++i) {
+ if (i != 0) {
+ result.append(",");
+ }
+ result.append(QuicVersionToString(versions[i]));
+ }
+ return result;
+}
+
+ostream& operator<<(ostream& os, const QuicPacketHeader& header) {
+ os << "{ connection_id: " << header.public_header.connection_id
+ << ", connection_id_length:" << header.public_header.connection_id_length
+ << ", sequence_number_length:"
+ << header.public_header.sequence_number_length
+ << ", reset_flag: " << header.public_header.reset_flag
+ << ", version_flag: " << header.public_header.version_flag;
+ if (header.public_header.version_flag) {
+ os << " version: ";
+ for (size_t i = 0; i < header.public_header.versions.size(); ++i) {
+ os << header.public_header.versions[0] << " ";
+ }
+ }
+ os << ", fec_flag: " << header.fec_flag
+ << ", entropy_flag: " << header.entropy_flag
+ << ", entropy hash: " << static_cast<int>(header.entropy_hash)
+ << ", sequence_number: " << header.packet_sequence_number
+ << ", is_in_fec_group:" << header.is_in_fec_group
+ << ", fec_group: " << header.fec_group<< "}\n";
+ return os;
+}
+
+bool IsAwaitingPacket(const QuicAckFrame& ack_frame,
+ QuicPacketSequenceNumber sequence_number) {
+ return sequence_number > ack_frame.largest_observed ||
+ ContainsKey(ack_frame.missing_packets, sequence_number);
+}
+
+void InsertMissingPacketsBetween(QuicAckFrame* ack_frame,
+ QuicPacketSequenceNumber lower,
+ QuicPacketSequenceNumber higher) {
+ for (QuicPacketSequenceNumber i = lower; i < higher; ++i) {
+ ack_frame->missing_packets.insert(i);
+ }
+}
+
+QuicStopWaitingFrame::QuicStopWaitingFrame()
+ : entropy_hash(0),
+ least_unacked(0) {
+}
+
+QuicStopWaitingFrame::~QuicStopWaitingFrame() {}
+
+QuicAckFrame::QuicAckFrame()
+ : entropy_hash(0),
+ largest_observed(0),
+ delta_time_largest_observed(QuicTime::Delta::Infinite()),
+ is_truncated(false) {}
+
+QuicAckFrame::~QuicAckFrame() {}
+
+CongestionFeedbackMessageTCP::CongestionFeedbackMessageTCP()
+ : receive_window(0) {
+}
+
+QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() : type(kTCP) {}
+
+QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() {}
+
+QuicRstStreamErrorCode AdjustErrorForVersion(
+ QuicRstStreamErrorCode error_code,
+ QuicVersion version) {
+ switch (error_code) {
+ case QUIC_RST_FLOW_CONTROL_ACCOUNTING:
+ if (version < QUIC_VERSION_18) {
+ return QUIC_STREAM_NO_ERROR;
+ }
+ break;
+ default:
+ return error_code;
+ }
+ return error_code;
+}
+
+QuicRstStreamFrame::QuicRstStreamFrame()
+ : stream_id(0),
+ error_code(QUIC_STREAM_NO_ERROR) {
+}
+
+QuicRstStreamFrame::QuicRstStreamFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written)
+ : stream_id(stream_id),
+ error_code(error_code),
+ byte_offset(bytes_written) {
+ DCHECK_LE(error_code, numeric_limits<uint8>::max());
+}
+
+QuicConnectionCloseFrame::QuicConnectionCloseFrame()
+ : error_code(QUIC_NO_ERROR) {
+}
+
+QuicFrame::QuicFrame() {}
+
+QuicFrame::QuicFrame(QuicPaddingFrame* padding_frame)
+ : type(PADDING_FRAME),
+ padding_frame(padding_frame) {
+}
+
+QuicFrame::QuicFrame(QuicStreamFrame* stream_frame)
+ : type(STREAM_FRAME),
+ stream_frame(stream_frame) {
+}
+
+QuicFrame::QuicFrame(QuicAckFrame* frame)
+ : type(ACK_FRAME),
+ ack_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicCongestionFeedbackFrame* frame)
+ : type(CONGESTION_FEEDBACK_FRAME),
+ congestion_feedback_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicStopWaitingFrame* frame)
+ : type(STOP_WAITING_FRAME),
+ stop_waiting_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicPingFrame* frame)
+ : type(PING_FRAME),
+ ping_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicRstStreamFrame* frame)
+ : type(RST_STREAM_FRAME),
+ rst_stream_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicConnectionCloseFrame* frame)
+ : type(CONNECTION_CLOSE_FRAME),
+ connection_close_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicGoAwayFrame* frame)
+ : type(GOAWAY_FRAME),
+ goaway_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicWindowUpdateFrame* frame)
+ : type(WINDOW_UPDATE_FRAME),
+ window_update_frame(frame) {
+}
+
+QuicFrame::QuicFrame(QuicBlockedFrame* frame)
+ : type(BLOCKED_FRAME),
+ blocked_frame(frame) {
+}
+
+QuicFecData::QuicFecData() : fec_group(0) {}
+
+ostream& operator<<(ostream& os, const QuicStopWaitingFrame& sent_info) {
+ os << "entropy_hash: " << static_cast<int>(sent_info.entropy_hash)
+ << " least_unacked: " << sent_info.least_unacked;
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) {
+ os << "entropy_hash: " << static_cast<int>(ack_frame.entropy_hash)
+ << " largest_observed: " << ack_frame.largest_observed
+ << " delta_time_largest_observed: "
+ << ack_frame.delta_time_largest_observed.ToMicroseconds()
+ << " missing_packets: [ ";
+ for (SequenceNumberSet::const_iterator it = ack_frame.missing_packets.begin();
+ it != ack_frame.missing_packets.end(); ++it) {
+ os << *it << " ";
+ }
+ os << " ] is_truncated: " << ack_frame.is_truncated;
+ os << " revived_packets: [ ";
+ for (SequenceNumberSet::const_iterator it = ack_frame.revived_packets.begin();
+ it != ack_frame.revived_packets.end(); ++it) {
+ os << *it << " ";
+ }
+ os << " ] received_packets: [ ";
+ for (PacketTimeList::const_iterator it =
+ ack_frame.received_packet_times.begin();
+ it != ack_frame.received_packet_times.end(); ++it) {
+ os << it->first << " at " << it->second.ToDebuggingValue() << " ";
+ }
+ os << " ]";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicFrame& frame) {
+ switch (frame.type) {
+ case PADDING_FRAME: {
+ os << "type { PADDING_FRAME } ";
+ break;
+ }
+ case RST_STREAM_FRAME: {
+ os << "type { " << RST_STREAM_FRAME << " } " << *(frame.rst_stream_frame);
+ break;
+ }
+ case CONNECTION_CLOSE_FRAME: {
+ os << "type { CONNECTION_CLOSE_FRAME } "
+ << *(frame.connection_close_frame);
+ break;
+ }
+ case GOAWAY_FRAME: {
+ os << "type { GOAWAY_FRAME } " << *(frame.goaway_frame);
+ break;
+ }
+ case WINDOW_UPDATE_FRAME: {
+ os << "type { WINDOW_UPDATE_FRAME } " << *(frame.window_update_frame);
+ break;
+ }
+ case BLOCKED_FRAME: {
+ os << "type { BLOCKED_FRAME } " << *(frame.blocked_frame);
+ break;
+ }
+ case STREAM_FRAME: {
+ os << "type { STREAM_FRAME } " << *(frame.stream_frame);
+ break;
+ }
+ case ACK_FRAME: {
+ os << "type { ACK_FRAME } " << *(frame.ack_frame);
+ break;
+ }
+ case CONGESTION_FEEDBACK_FRAME: {
+ os << "type { CONGESTION_FEEDBACK_FRAME } "
+ << *(frame.congestion_feedback_frame);
+ break;
+ }
+ case STOP_WAITING_FRAME: {
+ os << "type { STOP_WAITING_FRAME } " << *(frame.stop_waiting_frame);
+ break;
+ }
+ case PING_FRAME: {
+ os << "type { PING_FRAME } ";
+ break;
+ }
+ default: {
+ LOG(ERROR) << "Unknown frame type: " << frame.type;
+ break;
+ }
+ }
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicRstStreamFrame& rst_frame) {
+ os << "stream_id { " << rst_frame.stream_id << " } "
+ << "error_code { " << rst_frame.error_code << " } "
+ << "error_details { " << rst_frame.error_details << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os,
+ const QuicConnectionCloseFrame& connection_close_frame) {
+ os << "error_code { " << connection_close_frame.error_code << " } "
+ << "error_details { " << connection_close_frame.error_details << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicGoAwayFrame& goaway_frame) {
+ os << "error_code { " << goaway_frame.error_code << " } "
+ << "last_good_stream_id { " << goaway_frame.last_good_stream_id << " } "
+ << "reason_phrase { " << goaway_frame.reason_phrase << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os,
+ const QuicWindowUpdateFrame& window_update_frame) {
+ os << "stream_id { " << window_update_frame.stream_id << " } "
+ << "byte_offset { " << window_update_frame.byte_offset << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicBlockedFrame& blocked_frame) {
+ os << "stream_id { " << blocked_frame.stream_id << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os, const QuicStreamFrame& stream_frame) {
+ os << "stream_id { " << stream_frame.stream_id << " } "
+ << "fin { " << stream_frame.fin << " } "
+ << "offset { " << stream_frame.offset << " } "
+ << "data { "
+ << QuicUtils::StringToHexASCIIDump(*(stream_frame.GetDataAsString()))
+ << " }\n";
+ return os;
+}
+
+ostream& operator<<(ostream& os,
+ const QuicCongestionFeedbackFrame& congestion_frame) {
+ os << "type: " << congestion_frame.type;
+ switch (congestion_frame.type) {
+ case kTCP: {
+ const CongestionFeedbackMessageTCP& tcp = congestion_frame.tcp;
+ os << " receive_window: " << tcp.receive_window;
+ break;
+ }
+ }
+ return os;
+}
+
+QuicGoAwayFrame::QuicGoAwayFrame()
+ : error_code(QUIC_NO_ERROR),
+ last_good_stream_id(0) {
+}
+
+QuicGoAwayFrame::QuicGoAwayFrame(QuicErrorCode error_code,
+ QuicStreamId last_good_stream_id,
+ const string& reason)
+ : error_code(error_code),
+ last_good_stream_id(last_good_stream_id),
+ reason_phrase(reason) {
+ DCHECK_LE(error_code, numeric_limits<uint8>::max());
+}
+
+QuicData::QuicData(const char* buffer,
+ size_t length)
+ : buffer_(buffer),
+ length_(length),
+ owns_buffer_(false) {
+}
+
+QuicData::QuicData(char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : buffer_(buffer),
+ length_(length),
+ owns_buffer_(owns_buffer) {
+}
+
+QuicData::~QuicData() {
+ if (owns_buffer_) {
+ delete [] const_cast<char*>(buffer_);
+ }
+}
+
+QuicWindowUpdateFrame::QuicWindowUpdateFrame(QuicStreamId stream_id,
+ QuicStreamOffset byte_offset)
+ : stream_id(stream_id),
+ byte_offset(byte_offset) {}
+
+QuicBlockedFrame::QuicBlockedFrame(QuicStreamId stream_id)
+ : stream_id(stream_id) {}
+
+QuicPacket::QuicPacket(char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length,
+ bool is_fec_packet)
+ : QuicData(buffer, length, owns_buffer),
+ buffer_(buffer),
+ is_fec_packet_(is_fec_packet),
+ connection_id_length_(connection_id_length),
+ includes_version_(includes_version),
+ sequence_number_length_(sequence_number_length) {
+}
+
+QuicEncryptedPacket::QuicEncryptedPacket(const char* buffer,
+ size_t length)
+ : QuicData(buffer, length) {
+}
+
+QuicEncryptedPacket::QuicEncryptedPacket(char* buffer,
+ size_t length,
+ bool owns_buffer)
+ : QuicData(buffer, length, owns_buffer) {
+}
+
+StringPiece QuicPacket::FecProtectedData() const {
+ const size_t start_of_fec = GetStartOfFecProtectedData(
+ connection_id_length_, includes_version_, sequence_number_length_);
+ return StringPiece(data() + start_of_fec, length() - start_of_fec);
+}
+
+StringPiece QuicPacket::AssociatedData() const {
+ return StringPiece(
+ data() + kStartOfHashData,
+ GetStartOfEncryptedData(
+ connection_id_length_, includes_version_, sequence_number_length_) -
+ kStartOfHashData);
+}
+
+StringPiece QuicPacket::BeforePlaintext() const {
+ return StringPiece(data(), GetStartOfEncryptedData(connection_id_length_,
+ includes_version_,
+ sequence_number_length_));
+}
+
+StringPiece QuicPacket::Plaintext() const {
+ const size_t start_of_encrypted_data =
+ GetStartOfEncryptedData(
+ connection_id_length_, includes_version_, sequence_number_length_);
+ return StringPiece(data() + start_of_encrypted_data,
+ length() - start_of_encrypted_data);
+}
+
+RetransmittableFrames::RetransmittableFrames()
+ : encryption_level_(NUM_ENCRYPTION_LEVELS),
+ has_crypto_handshake_(NOT_HANDSHAKE) {
+}
+
+RetransmittableFrames::~RetransmittableFrames() {
+ for (QuicFrames::iterator it = frames_.begin(); it != frames_.end(); ++it) {
+ switch (it->type) {
+ case PADDING_FRAME:
+ delete it->padding_frame;
+ break;
+ case STREAM_FRAME:
+ delete it->stream_frame;
+ break;
+ case ACK_FRAME:
+ delete it->ack_frame;
+ break;
+ case CONGESTION_FEEDBACK_FRAME:
+ delete it->congestion_feedback_frame;
+ break;
+ case STOP_WAITING_FRAME:
+ delete it->stop_waiting_frame;
+ break;
+ case PING_FRAME:
+ delete it->ping_frame;
+ break;
+ case RST_STREAM_FRAME:
+ delete it->rst_stream_frame;
+ break;
+ case CONNECTION_CLOSE_FRAME:
+ delete it->connection_close_frame;
+ break;
+ case GOAWAY_FRAME:
+ delete it->goaway_frame;
+ break;
+ case WINDOW_UPDATE_FRAME:
+ delete it->window_update_frame;
+ break;
+ case BLOCKED_FRAME:
+ delete it->blocked_frame;
+ break;
+ case NUM_FRAME_TYPES:
+ DCHECK(false) << "Cannot delete type: " << it->type;
+ }
+ }
+ STLDeleteElements(&stream_data_);
+}
+
+const QuicFrame& RetransmittableFrames::AddStreamFrame(
+ QuicStreamFrame* stream_frame) {
+ // Make an owned copy of the stream frame's data.
+ stream_data_.push_back(stream_frame->GetDataAsString());
+ // Ensure the stream frame's IOVector points to the owned copy of the data.
+ stream_frame->data.Clear();
+ stream_frame->data.Append(const_cast<char*>(stream_data_.back()->data()),
+ stream_data_.back()->size());
+ frames_.push_back(QuicFrame(stream_frame));
+ if (stream_frame->stream_id == kCryptoStreamId) {
+ has_crypto_handshake_ = IS_HANDSHAKE;
+ }
+ return frames_.back();
+}
+
+const QuicFrame& RetransmittableFrames::AddNonStreamFrame(
+ const QuicFrame& frame) {
+ DCHECK_NE(frame.type, STREAM_FRAME);
+ frames_.push_back(frame);
+ return frames_.back();
+}
+
+void RetransmittableFrames::set_encryption_level(EncryptionLevel level) {
+ encryption_level_ = level;
+}
+
+SerializedPacket::SerializedPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacket* packet,
+ QuicPacketEntropyHash entropy_hash,
+ RetransmittableFrames* retransmittable_frames)
+ : sequence_number(sequence_number),
+ sequence_number_length(sequence_number_length),
+ packet(packet),
+ entropy_hash(entropy_hash),
+ retransmittable_frames(retransmittable_frames) {
+}
+
+SerializedPacket::~SerializedPacket() {}
+
+QuicEncryptedPacket* QuicEncryptedPacket::Clone() const {
+ char* buffer = new char[this->length()];
+ memcpy(buffer, this->data(), this->length());
+ return new QuicEncryptedPacket(buffer, this->length(), true);
+}
+
+ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) {
+ os << s.length() << "-byte data";
+ return os;
+}
+
+TransmissionInfo::TransmissionInfo()
+ : retransmittable_frames(nullptr),
+ sequence_number_length(PACKET_1BYTE_SEQUENCE_NUMBER),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(NOT_RETRANSMISSION),
+ all_transmissions(nullptr),
+ in_flight(false),
+ is_unackable(false) {}
+
+TransmissionInfo::TransmissionInfo(
+ RetransmittableFrames* retransmittable_frames,
+ QuicSequenceNumberLength sequence_number_length)
+ : retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(NOT_RETRANSMISSION),
+ all_transmissions(nullptr),
+ in_flight(false),
+ is_unackable(false) {}
+
+TransmissionInfo::TransmissionInfo(
+ RetransmittableFrames* retransmittable_frames,
+ QuicSequenceNumberLength sequence_number_length,
+ TransmissionType transmission_type,
+ SequenceNumberList* all_transmissions)
+ : retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length),
+ sent_time(QuicTime::Zero()),
+ bytes_sent(0),
+ nack_count(0),
+ transmission_type(transmission_type),
+ all_transmissions(all_transmissions),
+ in_flight(false),
+ is_unackable(false) {}
+
+} // namespace net
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
new file mode 100644
index 0000000..6a1bec5
--- /dev/null
+++ b/net/quic/quic_protocol.h
@@ -0,0 +1,1093 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_PROTOCOL_H_
+#define NET_QUIC_QUIC_PROTOCOL_H_
+
+#include <stddef.h>
+#include <limits>
+#include <list>
+#include <map>
+#include <ostream>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/base/int128.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+#include "net/quic/iovector.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+class QuicAckNotifier;
+class QuicPacket;
+struct QuicPacketHeader;
+
+typedef uint64 QuicConnectionId;
+typedef uint32 QuicStreamId;
+typedef uint64 QuicStreamOffset;
+typedef uint64 QuicPacketSequenceNumber;
+typedef QuicPacketSequenceNumber QuicFecGroupNumber;
+typedef uint64 QuicPublicResetNonceProof;
+typedef uint8 QuicPacketEntropyHash;
+typedef uint32 QuicHeaderId;
+// QuicTag is the type of a tag in the wire protocol.
+typedef uint32 QuicTag;
+typedef std::vector<QuicTag> QuicTagVector;
+typedef std::map<QuicTag, std::string> QuicTagValueMap;
+// TODO(rtenneti): Didn't use SpdyPriority because SpdyPriority is uint8 and
+// QuicPriority is uint32. Use SpdyPriority when we change the QUIC_VERSION.
+typedef uint32 QuicPriority;
+
+// TODO(rch): Consider Quic specific names for these constants.
+// Default and initial maximum size in bytes of a QUIC packet.
+const QuicByteCount kDefaultMaxPacketSize = 1350;
+// The maximum packet size of any QUIC packet, based on ethernet's max size,
+// minus the IP and UDP headers. IPv6 has a 40 byte header, UPD adds an
+// additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's
+// max packet size is 1500 bytes, 1500 - 48 = 1452.
+const QuicByteCount kMaxPacketSize = 1452;
+// Default maximum packet size used in Linux TCP implementations.
+const QuicByteCount kDefaultTCPMSS = 1460;
+
+// Maximum size of the initial congestion window in packets.
+const size_t kDefaultInitialWindow = 10;
+const uint32 kMaxInitialWindow = 100;
+
+// Default size of initial flow control window, for both stream and session.
+const uint32 kDefaultFlowControlSendWindow = 16 * 1024; // 16 KB
+
+// Maximum size of the congestion window, in packets, for TCP congestion control
+// algorithms.
+const size_t kMaxTcpCongestionWindow = 200;
+
+// Size of the socket receive buffer in bytes.
+const QuicByteCount kDefaultSocketReceiveBuffer = 256 * 1024;
+
+// Don't allow a client to suggest an RTT longer than 15 seconds.
+const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond;
+
+// Maximum number of open streams per connection.
+const size_t kDefaultMaxStreamsPerConnection = 100;
+
+// Number of bytes reserved for public flags in the packet header.
+const size_t kPublicFlagsSize = 1;
+// Number of bytes reserved for version number in the packet header.
+const size_t kQuicVersionSize = 4;
+// Number of bytes reserved for private flags in the packet header.
+const size_t kPrivateFlagsSize = 1;
+// Number of bytes reserved for FEC group in the packet header.
+const size_t kFecGroupSize = 1;
+
+// Signifies that the QuicPacket will contain version of the protocol.
+const bool kIncludeVersion = true;
+
+// Index of the first byte in a QUIC packet which is used in hash calculation.
+const size_t kStartOfHashData = 0;
+
+// Limit on the delta between stream IDs.
+const QuicStreamId kMaxStreamIdDelta = 200;
+// Limit on the delta between header IDs.
+const QuicHeaderId kMaxHeaderIdDelta = 200;
+
+// Reserved ID for the crypto stream.
+const QuicStreamId kCryptoStreamId = 1;
+
+// Reserved ID for the headers stream.
+const QuicStreamId kHeadersStreamId = 3;
+
+// Maximum delayed ack time, in ms.
+const int kMaxDelayedAckTimeMs = 25;
+
+// The timeout before the handshake succeeds.
+const int64 kInitialIdleTimeoutSecs = 5;
+// The default idle timeout.
+const int64 kDefaultIdleTimeoutSecs = 30;
+// The maximum idle timeout that can be negotiated.
+const int64 kMaximumIdleTimeoutSecs = 60 * 10; // 10 minutes.
+// The default timeout for a connection until the crypto handshake succeeds.
+const int64 kMaxTimeForCryptoHandshakeSecs = 10; // 10 secs.
+
+// Default ping timeout.
+const int64 kPingTimeoutSecs = 15; // 15 secs.
+
+// Minimum number of RTTs between Server Config Updates (SCUP) sent to client.
+const int kMinIntervalBetweenServerConfigUpdatesRTTs = 10;
+
+// Minimum time between Server Config Updates (SCUP) sent to client.
+const int kMinIntervalBetweenServerConfigUpdatesMs = 1000;
+
+// The number of open streams that a server will accept is set to be slightly
+// larger than the negotiated limit. Immediately closing the connection if the
+// client opens slightly too many streams is not ideal: the client may have sent
+// a FIN that was lost, and simultaneously opened a new stream. The number of
+// streams a server accepts is a fixed increment over the negotiated limit, or a
+// percentage increase, whichever is larger.
+const float kMaxStreamsMultiplier = 1.1f;
+const int kMaxStreamsMinimumIncrement = 10;
+
+// We define an unsigned 16-bit floating point value, inspired by IEEE floats
+// (http://en.wikipedia.org/wiki/Half_precision_floating-point_format),
+// with 5-bit exponent (bias 1), 11-bit mantissa (effective 12 with hidden
+// bit) and denormals, but without signs, transfinites or fractions. Wire format
+// 16 bits (little-endian byte order) are split into exponent (high 5) and
+// mantissa (low 11) and decoded as:
+// uint64 value;
+// if (exponent == 0) value = mantissa;
+// else value = (mantissa | 1 << 11) << (exponent - 1)
+const int kUFloat16ExponentBits = 5;
+const int kUFloat16MaxExponent = (1 << kUFloat16ExponentBits) - 2; // 30
+const int kUFloat16MantissaBits = 16 - kUFloat16ExponentBits; // 11
+const int kUFloat16MantissaEffectiveBits = kUFloat16MantissaBits + 1; // 12
+const uint64 kUFloat16MaxValue = // 0x3FFC0000000
+ ((GG_UINT64_C(1) << kUFloat16MantissaEffectiveBits) - 1) <<
+ kUFloat16MaxExponent;
+
+enum TransmissionType {
+ NOT_RETRANSMISSION,
+ FIRST_TRANSMISSION_TYPE = NOT_RETRANSMISSION,
+ HANDSHAKE_RETRANSMISSION, // Retransmits due to handshake timeouts.
+ ALL_UNACKED_RETRANSMISSION, // Retransmits all unacked packets.
+ ALL_INITIAL_RETRANSMISSION, // Retransmits all initially encrypted packets.
+ LOSS_RETRANSMISSION, // Retransmits due to loss detection.
+ RTO_RETRANSMISSION, // Retransmits due to retransmit time out.
+ TLP_RETRANSMISSION, // Tail loss probes.
+ LAST_TRANSMISSION_TYPE = TLP_RETRANSMISSION,
+};
+
+enum HasRetransmittableData {
+ NO_RETRANSMITTABLE_DATA,
+ HAS_RETRANSMITTABLE_DATA,
+};
+
+enum IsHandshake {
+ NOT_HANDSHAKE,
+ IS_HANDSHAKE
+};
+
+// Indicates FEC protection level for data being written.
+enum FecProtection {
+ MUST_FEC_PROTECT, // Callee must FEC protect this data.
+ MAY_FEC_PROTECT // Callee does not have to but may FEC protect this data.
+};
+
+// Indicates FEC policy.
+enum FecPolicy {
+ FEC_PROTECT_ALWAYS, // All data in the stream should be FEC protected.
+ FEC_PROTECT_OPTIONAL // Data in the stream does not need FEC protection.
+};
+
+enum QuicFrameType {
+ // Regular frame types. The values set here cannot change without the
+ // introduction of a new QUIC version.
+ PADDING_FRAME = 0,
+ RST_STREAM_FRAME = 1,
+ CONNECTION_CLOSE_FRAME = 2,
+ GOAWAY_FRAME = 3,
+ WINDOW_UPDATE_FRAME = 4,
+ BLOCKED_FRAME = 5,
+ STOP_WAITING_FRAME = 6,
+ PING_FRAME = 7,
+
+ // STREAM, ACK, and CONGESTION_FEEDBACK frames are special frames. They are
+ // encoded differently on the wire and their values do not need to be stable.
+ STREAM_FRAME,
+ ACK_FRAME,
+ CONGESTION_FEEDBACK_FRAME,
+ NUM_FRAME_TYPES
+};
+
+enum QuicConnectionIdLength {
+ PACKET_0BYTE_CONNECTION_ID = 0,
+ PACKET_1BYTE_CONNECTION_ID = 1,
+ PACKET_4BYTE_CONNECTION_ID = 4,
+ PACKET_8BYTE_CONNECTION_ID = 8
+};
+
+enum InFecGroup {
+ NOT_IN_FEC_GROUP,
+ IN_FEC_GROUP,
+};
+
+enum QuicSequenceNumberLength {
+ PACKET_1BYTE_SEQUENCE_NUMBER = 1,
+ PACKET_2BYTE_SEQUENCE_NUMBER = 2,
+ PACKET_4BYTE_SEQUENCE_NUMBER = 4,
+ PACKET_6BYTE_SEQUENCE_NUMBER = 6
+};
+
+// Used to indicate a QuicSequenceNumberLength using two flag bits.
+enum QuicSequenceNumberLengthFlags {
+ PACKET_FLAGS_1BYTE_SEQUENCE = 0, // 00
+ PACKET_FLAGS_2BYTE_SEQUENCE = 1, // 01
+ PACKET_FLAGS_4BYTE_SEQUENCE = 1 << 1, // 10
+ PACKET_FLAGS_6BYTE_SEQUENCE = 1 << 1 | 1, // 11
+};
+
+// The public flags are specified in one byte.
+enum QuicPacketPublicFlags {
+ PACKET_PUBLIC_FLAGS_NONE = 0,
+
+ // Bit 0: Does the packet header contains version info?
+ PACKET_PUBLIC_FLAGS_VERSION = 1 << 0,
+
+ // Bit 1: Is this packet a public reset packet?
+ PACKET_PUBLIC_FLAGS_RST = 1 << 1,
+
+ // Bits 2 and 3 specify the length of the ConnectionId as follows:
+ // ----00--: 0 bytes
+ // ----01--: 1 byte
+ // ----10--: 4 bytes
+ // ----11--: 8 bytes
+ PACKET_PUBLIC_FLAGS_0BYTE_CONNECTION_ID = 0,
+ PACKET_PUBLIC_FLAGS_1BYTE_CONNECTION_ID = 1 << 2,
+ PACKET_PUBLIC_FLAGS_4BYTE_CONNECTION_ID = 1 << 3,
+ PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID = 1 << 3 | 1 << 2,
+
+ // Bits 4 and 5 describe the packet sequence number length as follows:
+ // --00----: 1 byte
+ // --01----: 2 bytes
+ // --10----: 4 bytes
+ // --11----: 6 bytes
+ PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE = PACKET_FLAGS_1BYTE_SEQUENCE << 4,
+ PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE = PACKET_FLAGS_2BYTE_SEQUENCE << 4,
+ PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE = PACKET_FLAGS_4BYTE_SEQUENCE << 4,
+ PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE = PACKET_FLAGS_6BYTE_SEQUENCE << 4,
+
+ // All bits set (bits 6 and 7 are not currently used): 00111111
+ PACKET_PUBLIC_FLAGS_MAX = (1 << 6) - 1
+};
+
+// The private flags are specified in one byte.
+enum QuicPacketPrivateFlags {
+ PACKET_PRIVATE_FLAGS_NONE = 0,
+
+ // Bit 0: Does this packet contain an entropy bit?
+ PACKET_PRIVATE_FLAGS_ENTROPY = 1 << 0,
+
+ // Bit 1: Payload is part of an FEC group?
+ PACKET_PRIVATE_FLAGS_FEC_GROUP = 1 << 1,
+
+ // Bit 2: Payload is FEC as opposed to frames?
+ PACKET_PRIVATE_FLAGS_FEC = 1 << 2,
+
+ // All bits set (bits 3-7 are not currently used): 00000111
+ PACKET_PRIVATE_FLAGS_MAX = (1 << 3) - 1
+};
+
+// The available versions of QUIC. Guaranteed that the integer value of the enum
+// will match the version number.
+// When adding a new version to this enum you should add it to
+// kSupportedQuicVersions (if appropriate), and also add a new case to the
+// helper methods QuicVersionToQuicTag, QuicTagToQuicVersion, and
+// QuicVersionToString.
+enum QuicVersion {
+ // Special case to indicate unknown/unsupported QUIC version.
+ QUIC_VERSION_UNSUPPORTED = 0,
+
+ QUIC_VERSION_18 = 18, // PING frame.
+ QUIC_VERSION_19 = 19, // Connection level flow control.
+ QUIC_VERSION_21 = 21, // Headers/crypto streams are flow controlled.
+ QUIC_VERSION_22 = 22, // Send Server Config Update messages on crypto stream.
+ QUIC_VERSION_23 = 23, // Timestamp in the ack frame.
+};
+
+// This vector contains QUIC versions which we currently support.
+// This should be ordered such that the highest supported version is the first
+// element, with subsequent elements in descending order (versions can be
+// skipped as necessary).
+//
+// IMPORTANT: if you are adding to this list, follow the instructions at
+// http://sites/quic/adding-and-removing-versions
+static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_23,
+ QUIC_VERSION_22,
+ QUIC_VERSION_21,
+ QUIC_VERSION_19,
+ QUIC_VERSION_18};
+
+typedef std::vector<QuicVersion> QuicVersionVector;
+
+// Returns a vector of QUIC versions in kSupportedQuicVersions.
+NET_EXPORT_PRIVATE QuicVersionVector QuicSupportedVersions();
+
+// QuicTag is written to and read from the wire, but we prefer to use
+// the more readable QuicVersion at other levels.
+// Helper function which translates from a QuicVersion to a QuicTag. Returns 0
+// if QuicVersion is unsupported.
+NET_EXPORT_PRIVATE QuicTag QuicVersionToQuicTag(const QuicVersion version);
+
+// Returns appropriate QuicVersion from a QuicTag.
+// Returns QUIC_VERSION_UNSUPPORTED if version_tag cannot be understood.
+NET_EXPORT_PRIVATE QuicVersion QuicTagToQuicVersion(const QuicTag version_tag);
+
+// Helper function which translates from a QuicVersion to a string.
+// Returns strings corresponding to enum names (e.g. QUIC_VERSION_6).
+NET_EXPORT_PRIVATE std::string QuicVersionToString(const QuicVersion version);
+
+// Returns comma separated list of string representations of QuicVersion enum
+// values in the supplied |versions| vector.
+NET_EXPORT_PRIVATE std::string QuicVersionVectorToString(
+ const QuicVersionVector& versions);
+
+// Version and Crypto tags are written to the wire with a big-endian
+// representation of the name of the tag. For example
+// the client hello tag (CHLO) will be written as the
+// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is
+// stored in memory as a little endian uint32, we need
+// to reverse the order of the bytes.
+
+// MakeQuicTag returns a value given the four bytes. For example:
+// MakeQuicTag('C', 'H', 'L', 'O');
+NET_EXPORT_PRIVATE QuicTag MakeQuicTag(char a, char b, char c, char d);
+
+// Returns true if the tag vector contains the specified tag.
+NET_EXPORT_PRIVATE bool ContainsQuicTag(const QuicTagVector& tag_vector,
+ QuicTag tag);
+
+// Size in bytes of the data or fec packet header.
+NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(const QuicPacketHeader& header);
+
+NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length,
+ InFecGroup is_in_fec_group);
+
+// Index of the first byte in a QUIC packet of FEC protected data.
+NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length);
+// Index of the first byte in a QUIC packet of encrypted data.
+NET_EXPORT_PRIVATE size_t GetStartOfEncryptedData(
+ QuicConnectionIdLength connection_id_length,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length);
+
+enum QuicRstStreamErrorCode {
+ QUIC_STREAM_NO_ERROR = 0,
+
+ // There was some error which halted stream processing.
+ QUIC_ERROR_PROCESSING_STREAM,
+ // We got two fin or reset offsets which did not match.
+ QUIC_MULTIPLE_TERMINATION_OFFSETS,
+ // We got bad payload and can not respond to it at the protocol level.
+ QUIC_BAD_APPLICATION_PAYLOAD,
+ // Stream closed due to connection error. No reset frame is sent when this
+ // happens.
+ QUIC_STREAM_CONNECTION_ERROR,
+ // GoAway frame sent. No more stream can be created.
+ QUIC_STREAM_PEER_GOING_AWAY,
+ // The stream has been cancelled.
+ QUIC_STREAM_CANCELLED,
+ // Sending a RST to allow for proper flow control accounting.
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+
+ // No error. Used as bound while iterating.
+ QUIC_STREAM_LAST_ERROR,
+};
+
+// Because receiving an unknown QuicRstStreamErrorCode results in connection
+// teardown, we use this to make sure any errors predating a given version are
+// downgraded to the most appropriate existing error.
+NET_EXPORT_PRIVATE QuicRstStreamErrorCode AdjustErrorForVersion(
+ QuicRstStreamErrorCode error_code,
+ QuicVersion version);
+
+// These values must remain stable as they are uploaded to UMA histograms.
+// To add a new error code, use the current value of QUIC_LAST_ERROR and
+// increment QUIC_LAST_ERROR.
+enum QuicErrorCode {
+ QUIC_NO_ERROR = 0,
+
+ // Connection has reached an invalid state.
+ QUIC_INTERNAL_ERROR = 1,
+ // There were data frames after the a fin or reset.
+ QUIC_STREAM_DATA_AFTER_TERMINATION = 2,
+ // Control frame is malformed.
+ QUIC_INVALID_PACKET_HEADER = 3,
+ // Frame data is malformed.
+ QUIC_INVALID_FRAME_DATA = 4,
+ // The packet contained no payload.
+ QUIC_MISSING_PAYLOAD = 48,
+ // FEC data is malformed.
+ QUIC_INVALID_FEC_DATA = 5,
+ // STREAM frame data is malformed.
+ QUIC_INVALID_STREAM_DATA = 46,
+ // STREAM frame data is not encrypted.
+ QUIC_UNENCRYPTED_STREAM_DATA = 61,
+ // RST_STREAM frame data is malformed.
+ QUIC_INVALID_RST_STREAM_DATA = 6,
+ // CONNECTION_CLOSE frame data is malformed.
+ QUIC_INVALID_CONNECTION_CLOSE_DATA = 7,
+ // GOAWAY frame data is malformed.
+ QUIC_INVALID_GOAWAY_DATA = 8,
+ // WINDOW_UPDATE frame data is malformed.
+ QUIC_INVALID_WINDOW_UPDATE_DATA = 57,
+ // BLOCKED frame data is malformed.
+ QUIC_INVALID_BLOCKED_DATA = 58,
+ // STOP_WAITING frame data is malformed.
+ QUIC_INVALID_STOP_WAITING_DATA = 60,
+ // ACK frame data is malformed.
+ QUIC_INVALID_ACK_DATA = 9,
+ // CONGESTION_FEEDBACK frame data is malformed.
+ QUIC_INVALID_CONGESTION_FEEDBACK_DATA = 47,
+ // Version negotiation packet is malformed.
+ QUIC_INVALID_VERSION_NEGOTIATION_PACKET = 10,
+ // Public RST packet is malformed.
+ QUIC_INVALID_PUBLIC_RST_PACKET = 11,
+ // There was an error decrypting.
+ QUIC_DECRYPTION_FAILURE = 12,
+ // There was an error encrypting.
+ QUIC_ENCRYPTION_FAILURE = 13,
+ // The packet exceeded kMaxPacketSize.
+ QUIC_PACKET_TOO_LARGE = 14,
+ // Data was sent for a stream which did not exist.
+ QUIC_PACKET_FOR_NONEXISTENT_STREAM = 15,
+ // The peer is going away. May be a client or server.
+ QUIC_PEER_GOING_AWAY = 16,
+ // A stream ID was invalid.
+ QUIC_INVALID_STREAM_ID = 17,
+ // A priority was invalid.
+ QUIC_INVALID_PRIORITY = 49,
+ // Too many streams already open.
+ QUIC_TOO_MANY_OPEN_STREAMS = 18,
+ // The peer must send a FIN/RST for each stream, and has not been doing so.
+ QUIC_TOO_MANY_UNFINISHED_STREAMS = 66,
+ // Received public reset for this connection.
+ QUIC_PUBLIC_RESET = 19,
+ // Invalid protocol version.
+ QUIC_INVALID_VERSION = 20,
+
+ // deprecated: QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED = 21
+
+ // The Header ID for a stream was too far from the previous.
+ QUIC_INVALID_HEADER_ID = 22,
+ // Negotiable parameter received during handshake had invalid value.
+ QUIC_INVALID_NEGOTIATED_VALUE = 23,
+ // There was an error decompressing data.
+ QUIC_DECOMPRESSION_FAILURE = 24,
+ // We hit our prenegotiated (or default) timeout
+ QUIC_CONNECTION_TIMED_OUT = 25,
+ // We hit our overall connection timeout
+ QUIC_CONNECTION_OVERALL_TIMED_OUT = 67,
+ // There was an error encountered migrating addresses
+ QUIC_ERROR_MIGRATING_ADDRESS = 26,
+ // There was an error while writing to the socket.
+ QUIC_PACKET_WRITE_ERROR = 27,
+ // There was an error while reading from the socket.
+ QUIC_PACKET_READ_ERROR = 51,
+ // We received a STREAM_FRAME with no data and no fin flag set.
+ QUIC_INVALID_STREAM_FRAME = 50,
+ // We received invalid data on the headers stream.
+ QUIC_INVALID_HEADERS_STREAM_DATA = 56,
+ // The peer received too much data, violating flow control.
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA = 59,
+ // The peer sent too much data, violating flow control.
+ QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA = 63,
+ // The peer received an invalid flow control window.
+ QUIC_FLOW_CONTROL_INVALID_WINDOW = 64,
+ // The connection has been IP pooled into an existing connection.
+ QUIC_CONNECTION_IP_POOLED = 62,
+
+ // Crypto errors.
+
+ // Hanshake failed.
+ QUIC_HANDSHAKE_FAILED = 28,
+ // Handshake message contained out of order tags.
+ QUIC_CRYPTO_TAGS_OUT_OF_ORDER = 29,
+ // Handshake message contained too many entries.
+ QUIC_CRYPTO_TOO_MANY_ENTRIES = 30,
+ // Handshake message contained an invalid value length.
+ QUIC_CRYPTO_INVALID_VALUE_LENGTH = 31,
+ // A crypto message was received after the handshake was complete.
+ QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE = 32,
+ // A crypto message was received with an illegal message tag.
+ QUIC_INVALID_CRYPTO_MESSAGE_TYPE = 33,
+ // A crypto message was received with an illegal parameter.
+ QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER = 34,
+ // An invalid channel id signature was supplied.
+ QUIC_INVALID_CHANNEL_ID_SIGNATURE = 52,
+ // A crypto message was received with a mandatory parameter missing.
+ QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND = 35,
+ // A crypto message was received with a parameter that has no overlap
+ // with the local parameter.
+ QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP = 36,
+ // A crypto message was received that contained a parameter with too few
+ // values.
+ QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND = 37,
+ // An internal error occured in crypto processing.
+ QUIC_CRYPTO_INTERNAL_ERROR = 38,
+ // A crypto handshake message specified an unsupported version.
+ QUIC_CRYPTO_VERSION_NOT_SUPPORTED = 39,
+ // There was no intersection between the crypto primitives supported by the
+ // peer and ourselves.
+ QUIC_CRYPTO_NO_SUPPORT = 40,
+ // The server rejected our client hello messages too many times.
+ QUIC_CRYPTO_TOO_MANY_REJECTS = 41,
+ // The client rejected the server's certificate chain or signature.
+ QUIC_PROOF_INVALID = 42,
+ // A crypto message was received with a duplicate tag.
+ QUIC_CRYPTO_DUPLICATE_TAG = 43,
+ // A crypto message was received with the wrong encryption level (i.e. it
+ // should have been encrypted but was not.)
+ QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT = 44,
+ // The server config for a server has expired.
+ QUIC_CRYPTO_SERVER_CONFIG_EXPIRED = 45,
+ // We failed to setup the symmetric keys for a connection.
+ QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED = 53,
+ // A handshake message arrived, but we are still validating the
+ // previous handshake message.
+ QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO = 54,
+ // A server config update arrived before the handshake is complete.
+ QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE = 65,
+ // This connection involved a version negotiation which appears to have been
+ // tampered with.
+ QUIC_VERSION_NEGOTIATION_MISMATCH = 55,
+
+ // No error. Used as bound while iterating.
+ QUIC_LAST_ERROR = 68,
+};
+
+struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
+ QuicPacketPublicHeader();
+ explicit QuicPacketPublicHeader(const QuicPacketPublicHeader& other);
+ ~QuicPacketPublicHeader();
+
+ // Universal header. All QuicPacket headers will have a connection_id and
+ // public flags.
+ QuicConnectionId connection_id;
+ QuicConnectionIdLength connection_id_length;
+ bool reset_flag;
+ bool version_flag;
+ QuicSequenceNumberLength sequence_number_length;
+ QuicVersionVector versions;
+};
+
+// Header for Data or FEC packets.
+struct NET_EXPORT_PRIVATE QuicPacketHeader {
+ QuicPacketHeader();
+ explicit QuicPacketHeader(const QuicPacketPublicHeader& header);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicPacketHeader& s);
+
+ QuicPacketPublicHeader public_header;
+ bool fec_flag;
+ bool entropy_flag;
+ QuicPacketEntropyHash entropy_hash;
+ QuicPacketSequenceNumber packet_sequence_number;
+ InFecGroup is_in_fec_group;
+ QuicFecGroupNumber fec_group;
+};
+
+struct NET_EXPORT_PRIVATE QuicPublicResetPacket {
+ QuicPublicResetPacket();
+ explicit QuicPublicResetPacket(const QuicPacketPublicHeader& header);
+
+ QuicPacketPublicHeader public_header;
+ QuicPublicResetNonceProof nonce_proof;
+ QuicPacketSequenceNumber rejected_sequence_number;
+ IPEndPoint client_address;
+};
+
+enum QuicVersionNegotiationState {
+ START_NEGOTIATION = 0,
+ // Server-side this implies we've sent a version negotiation packet and are
+ // waiting on the client to select a compatible version. Client-side this
+ // implies we've gotten a version negotiation packet, are retransmitting the
+ // initial packets with a supported version and are waiting for our first
+ // packet from the server.
+ NEGOTIATION_IN_PROGRESS,
+ // This indicates this endpoint has received a packet from the peer with a
+ // version this endpoint supports. Version negotiation is complete, and the
+ // version number will no longer be sent with future packets.
+ NEGOTIATED_VERSION
+};
+
+typedef QuicPacketPublicHeader QuicVersionNegotiationPacket;
+
+// A padding frame contains no payload.
+struct NET_EXPORT_PRIVATE QuicPaddingFrame {
+};
+
+// A ping frame contains no payload, though it is retransmittable,
+// and ACK'd just like other normal frames.
+struct NET_EXPORT_PRIVATE QuicPingFrame {
+};
+
+struct NET_EXPORT_PRIVATE QuicStreamFrame {
+ QuicStreamFrame();
+ QuicStreamFrame(const QuicStreamFrame& frame);
+ QuicStreamFrame(QuicStreamId stream_id,
+ bool fin,
+ QuicStreamOffset offset,
+ IOVector data);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicStreamFrame& s);
+
+ // Returns a copy of the IOVector |data| as a heap-allocated string.
+ // Caller must take ownership of the returned string.
+ std::string* GetDataAsString() const;
+
+ QuicStreamId stream_id;
+ bool fin;
+ QuicStreamOffset offset; // Location of this data in the stream.
+ IOVector data;
+
+ // If this is set, then when this packet is ACKed the AckNotifier will be
+ // informed.
+ QuicAckNotifier* notifier;
+};
+
+// TODO(ianswett): Re-evaluate the trade-offs of hash_set vs set when framing
+// is finalized.
+typedef std::set<QuicPacketSequenceNumber> SequenceNumberSet;
+typedef std::list<QuicPacketSequenceNumber> SequenceNumberList;
+
+typedef std::list<
+ std::pair<QuicPacketSequenceNumber, QuicTime> > PacketTimeList;
+
+struct NET_EXPORT_PRIVATE QuicStopWaitingFrame {
+ QuicStopWaitingFrame();
+ ~QuicStopWaitingFrame();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicStopWaitingFrame& s);
+ // Entropy hash of all packets up to, but not including, the least unacked
+ // packet.
+ QuicPacketEntropyHash entropy_hash;
+ // The lowest packet we've sent which is unacked, and we expect an ack for.
+ QuicPacketSequenceNumber least_unacked;
+};
+
+struct NET_EXPORT_PRIVATE QuicAckFrame {
+ QuicAckFrame();
+ ~QuicAckFrame();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicAckFrame& s);
+
+ // Entropy hash of all packets up to largest observed not including missing
+ // packets.
+ QuicPacketEntropyHash entropy_hash;
+
+ // The highest packet sequence number we've observed from the peer.
+ //
+ // In general, this should be the largest packet number we've received. In
+ // the case of truncated acks, we may have to advertise a lower "upper bound"
+ // than largest received, to avoid implicitly acking missing packets that
+ // don't fit in the missing packet list due to size limitations. In this
+ // case, largest_observed may be a packet which is also in the missing packets
+ // list.
+ QuicPacketSequenceNumber largest_observed;
+
+ // Time elapsed since largest_observed was received until this Ack frame was
+ // sent.
+ QuicTime::Delta delta_time_largest_observed;
+
+ // TODO(satyamshekhar): Can be optimized using an interval set like data
+ // structure.
+ // The set of packets which we're expecting and have not received.
+ SequenceNumberSet missing_packets;
+
+ // Whether the ack had to be truncated when sent.
+ bool is_truncated;
+
+ // Packets which have been revived via FEC.
+ // All of these must also be in missing_packets.
+ SequenceNumberSet revived_packets;
+
+ // List of <sequence_number, time> for when packets arrived.
+ PacketTimeList received_packet_times;
+};
+
+// True if the sequence number is greater than largest_observed or is listed
+// as missing.
+// Always returns false for sequence numbers less than least_unacked.
+bool NET_EXPORT_PRIVATE IsAwaitingPacket(
+ const QuicAckFrame& ack_frame,
+ QuicPacketSequenceNumber sequence_number);
+
+// Inserts missing packets between [lower, higher).
+void NET_EXPORT_PRIVATE InsertMissingPacketsBetween(
+ QuicAckFrame* ack_frame,
+ QuicPacketSequenceNumber lower,
+ QuicPacketSequenceNumber higher);
+
+// Defines for all types of congestion feedback that will be negotiated in QUIC,
+// kTCP MUST be supported by all QUIC implementations to guarantee 100%
+// compatibility.
+// TODO(cyr): Remove this when removing QUIC_VERSION_22.
+enum CongestionFeedbackType {
+ kTCP, // Used to mimic TCP.
+};
+
+// Defines for all types of congestion control algorithms that can be used in
+// QUIC. Note that this is separate from the congestion feedback type -
+// some congestion control algorithms may use the same feedback type
+// (Reno and Cubic are the classic example for that).
+enum CongestionControlType {
+ kCubic,
+ kReno,
+ kBBR,
+};
+
+enum LossDetectionType {
+ kNack, // Used to mimic TCP's loss detection.
+ kTime, // Time based loss detection.
+};
+
+// TODO(cyr): Remove this when removing QUIC_VERSION_22.
+struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP {
+ CongestionFeedbackMessageTCP();
+
+ QuicByteCount receive_window;
+};
+
+// TODO(cyr): Remove this when removing QUIC_VERSION_22.
+struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame {
+ QuicCongestionFeedbackFrame();
+ ~QuicCongestionFeedbackFrame();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicCongestionFeedbackFrame& c);
+
+ CongestionFeedbackType type;
+ // This should really be a union, but since the timestamp struct
+ // is non-trivial, C++ prohibits it.
+ CongestionFeedbackMessageTCP tcp;
+};
+
+struct NET_EXPORT_PRIVATE QuicRstStreamFrame {
+ QuicRstStreamFrame();
+ QuicRstStreamFrame(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicStreamOffset bytes_written);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicRstStreamFrame& r);
+
+ QuicStreamId stream_id;
+ QuicRstStreamErrorCode error_code;
+ std::string error_details;
+
+ // Used to update flow control windows. On termination of a stream, both
+ // endpoints must inform the peer of the number of bytes they have sent on
+ // that stream. This can be done through normal termination (data packet with
+ // FIN) or through a RST.
+ QuicStreamOffset byte_offset;
+};
+
+struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame {
+ QuicConnectionCloseFrame();
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConnectionCloseFrame& c);
+
+ QuicErrorCode error_code;
+ std::string error_details;
+};
+
+struct NET_EXPORT_PRIVATE QuicGoAwayFrame {
+ QuicGoAwayFrame();
+ QuicGoAwayFrame(QuicErrorCode error_code,
+ QuicStreamId last_good_stream_id,
+ const std::string& reason);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicGoAwayFrame& g);
+
+ QuicErrorCode error_code;
+ QuicStreamId last_good_stream_id;
+ std::string reason_phrase;
+};
+
+// Flow control updates per-stream and at the connection levoel.
+// Based on SPDY's WINDOW_UPDATE frame, but uses an absolute byte offset rather
+// than a window delta.
+// TODO(rjshade): A possible future optimization is to make stream_id and
+// byte_offset variable length, similar to stream frames.
+struct NET_EXPORT_PRIVATE QuicWindowUpdateFrame {
+ QuicWindowUpdateFrame() {}
+ QuicWindowUpdateFrame(QuicStreamId stream_id, QuicStreamOffset byte_offset);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicWindowUpdateFrame& w);
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+
+ // Byte offset in the stream or connection. The receiver of this frame must
+ // not send data which would result in this offset being exceeded.
+ QuicStreamOffset byte_offset;
+};
+
+// The BLOCKED frame is used to indicate to the remote endpoint that this
+// endpoint believes itself to be flow-control blocked but otherwise ready to
+// send data. The BLOCKED frame is purely advisory and optional.
+// Based on SPDY's BLOCKED frame (undocumented as of 2014-01-28).
+struct NET_EXPORT_PRIVATE QuicBlockedFrame {
+ QuicBlockedFrame() {}
+ explicit QuicBlockedFrame(QuicStreamId stream_id);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicBlockedFrame& b);
+
+ // The stream this frame applies to. 0 is a special case meaning the overall
+ // connection rather than a specific stream.
+ QuicStreamId stream_id;
+};
+
+// EncryptionLevel enumerates the stages of encryption that a QUIC connection
+// progresses through. When retransmitting a packet, the encryption level needs
+// to be specified so that it is retransmitted at a level which the peer can
+// understand.
+enum EncryptionLevel {
+ ENCRYPTION_NONE = 0,
+ ENCRYPTION_INITIAL = 1,
+ ENCRYPTION_FORWARD_SECURE = 2,
+
+ NUM_ENCRYPTION_LEVELS,
+};
+
+struct NET_EXPORT_PRIVATE QuicFrame {
+ QuicFrame();
+ explicit QuicFrame(QuicPaddingFrame* padding_frame);
+ explicit QuicFrame(QuicStreamFrame* stream_frame);
+ explicit QuicFrame(QuicAckFrame* frame);
+
+ // TODO(cyr): Remove this when removing QUIC_VERSION_22.
+ explicit QuicFrame(QuicCongestionFeedbackFrame* frame);
+
+ explicit QuicFrame(QuicRstStreamFrame* frame);
+ explicit QuicFrame(QuicConnectionCloseFrame* frame);
+ explicit QuicFrame(QuicStopWaitingFrame* frame);
+ explicit QuicFrame(QuicPingFrame* frame);
+ explicit QuicFrame(QuicGoAwayFrame* frame);
+ explicit QuicFrame(QuicWindowUpdateFrame* frame);
+ explicit QuicFrame(QuicBlockedFrame* frame);
+
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicFrame& frame);
+
+ QuicFrameType type;
+ union {
+ QuicPaddingFrame* padding_frame;
+ QuicStreamFrame* stream_frame;
+ QuicAckFrame* ack_frame;
+
+ // TODO(cyr): Remove this when removing QUIC_VERSION_22.
+ QuicCongestionFeedbackFrame* congestion_feedback_frame;
+ QuicStopWaitingFrame* stop_waiting_frame;
+
+ QuicPingFrame* ping_frame;
+ QuicRstStreamFrame* rst_stream_frame;
+ QuicConnectionCloseFrame* connection_close_frame;
+ QuicGoAwayFrame* goaway_frame;
+ QuicWindowUpdateFrame* window_update_frame;
+ QuicBlockedFrame* blocked_frame;
+ };
+};
+
+typedef std::vector<QuicFrame> QuicFrames;
+
+struct NET_EXPORT_PRIVATE QuicFecData {
+ QuicFecData();
+
+ // The FEC group number is also the sequence number of the first
+ // FEC protected packet. The last protected packet's sequence number will
+ // be one less than the sequence number of the FEC packet.
+ QuicFecGroupNumber fec_group;
+ base::StringPiece redundancy;
+};
+
+class NET_EXPORT_PRIVATE QuicData {
+ public:
+ QuicData(const char* buffer, size_t length);
+ QuicData(char* buffer, size_t length, bool owns_buffer);
+ virtual ~QuicData();
+
+ base::StringPiece AsStringPiece() const {
+ return base::StringPiece(data(), length());
+ }
+
+ const char* data() const { return buffer_; }
+ size_t length() const { return length_; }
+
+ private:
+ const char* buffer_;
+ size_t length_;
+ bool owns_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicData);
+};
+
+class NET_EXPORT_PRIVATE QuicPacket : public QuicData {
+ public:
+ static QuicPacket* NewDataPacket(
+ char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ return new QuicPacket(buffer, length, owns_buffer, connection_id_length,
+ includes_version, sequence_number_length, false);
+ }
+
+ static QuicPacket* NewFecPacket(
+ char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length) {
+ return new QuicPacket(buffer, length, owns_buffer, connection_id_length,
+ includes_version, sequence_number_length, true);
+ }
+
+ base::StringPiece FecProtectedData() const;
+ base::StringPiece AssociatedData() const;
+ base::StringPiece BeforePlaintext() const;
+ base::StringPiece Plaintext() const;
+
+ bool is_fec_packet() const { return is_fec_packet_; }
+
+ char* mutable_data() { return buffer_; }
+
+ private:
+ QuicPacket(char* buffer,
+ size_t length,
+ bool owns_buffer,
+ QuicConnectionIdLength connection_id_length,
+ bool includes_version,
+ QuicSequenceNumberLength sequence_number_length,
+ bool is_fec_packet);
+
+ char* buffer_;
+ const bool is_fec_packet_;
+ const QuicConnectionIdLength connection_id_length_;
+ const bool includes_version_;
+ const QuicSequenceNumberLength sequence_number_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicPacket);
+};
+
+class NET_EXPORT_PRIVATE QuicEncryptedPacket : public QuicData {
+ public:
+ QuicEncryptedPacket(const char* buffer, size_t length);
+ QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer);
+
+ // Clones the packet into a new packet which owns the buffer.
+ QuicEncryptedPacket* Clone() const;
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member (in the base class QuicData) causes this object to have padding
+ // bytes, which causes the default gtest object printer to read
+ // uninitialize memory. So we need to teach gtest how to print this object.
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicEncryptedPacket& s);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicEncryptedPacket);
+};
+
+class NET_EXPORT_PRIVATE RetransmittableFrames {
+ public:
+ RetransmittableFrames();
+ ~RetransmittableFrames();
+
+ // Allocates a local copy of the referenced StringPiece has QuicStreamFrame
+ // use it.
+ // Takes ownership of |stream_frame|.
+ const QuicFrame& AddStreamFrame(QuicStreamFrame* stream_frame);
+ // Takes ownership of the frame inside |frame|.
+ const QuicFrame& AddNonStreamFrame(const QuicFrame& frame);
+ const QuicFrames& frames() const { return frames_; }
+
+ IsHandshake HasCryptoHandshake() const {
+ return has_crypto_handshake_;
+ }
+
+ void set_encryption_level(EncryptionLevel level);
+ EncryptionLevel encryption_level() const {
+ return encryption_level_;
+ }
+
+ private:
+ QuicFrames frames_;
+ EncryptionLevel encryption_level_;
+ IsHandshake has_crypto_handshake_;
+ // Data referenced by the StringPiece of a QuicStreamFrame.
+ std::vector<std::string*> stream_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetransmittableFrames);
+};
+
+struct NET_EXPORT_PRIVATE SerializedPacket {
+ SerializedPacket(QuicPacketSequenceNumber sequence_number,
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacket* packet,
+ QuicPacketEntropyHash entropy_hash,
+ RetransmittableFrames* retransmittable_frames);
+ ~SerializedPacket();
+
+ QuicPacketSequenceNumber sequence_number;
+ QuicSequenceNumberLength sequence_number_length;
+ QuicPacket* packet;
+ QuicPacketEntropyHash entropy_hash;
+ RetransmittableFrames* retransmittable_frames;
+
+ // If set, these will be called when this packet is ACKed by the peer.
+ std::set<QuicAckNotifier*> notifiers;
+};
+
+struct NET_EXPORT_PRIVATE TransmissionInfo {
+ // Used by STL when assigning into a map.
+ TransmissionInfo();
+
+ // Constructs a Transmission with a new all_tranmissions set
+ // containing |sequence_number|.
+ TransmissionInfo(RetransmittableFrames* retransmittable_frames,
+ QuicSequenceNumberLength sequence_number_length);
+
+ // Constructs a Transmission with the specified |all_tranmissions| set
+ // and inserts |sequence_number| into it.
+ TransmissionInfo(RetransmittableFrames* retransmittable_frames,
+ QuicSequenceNumberLength sequence_number_length,
+ TransmissionType transmission_type,
+ SequenceNumberList* all_transmissions);
+
+ RetransmittableFrames* retransmittable_frames;
+ QuicSequenceNumberLength sequence_number_length;
+ // Zero when the packet is serialized, non-zero once it's sent.
+ QuicTime sent_time;
+ // Zero when the packet is serialized, non-zero once it's sent.
+ QuicByteCount bytes_sent;
+ size_t nack_count;
+ // Reason why this packet was transmitted.
+ TransmissionType transmission_type;
+ // Stores the sequence numbers of all transmissions of this packet.
+ // Must always be nullptr or have multiple elements.
+ SequenceNumberList* all_transmissions;
+ // In flight packets have not been abandoned or lost.
+ bool in_flight;
+ // True if the packet can never be acked, so it can be removed.
+ bool is_unackable;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_PROTOCOL_H_
diff --git a/net/quic/quic_protocol_test.cc b/net/quic/quic_protocol_test.cc
new file mode 100644
index 0000000..4f23490
--- /dev/null
+++ b/net/quic/quic_protocol_test.cc
@@ -0,0 +1,168 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_protocol.h"
+
+#include "base/stl_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicProtocolTest, AdjustErrorForVersion) {
+ ASSERT_EQ(8, QUIC_STREAM_LAST_ERROR)
+ << "Any additions to QuicRstStreamErrorCode require an addition to "
+ << "AdjustErrorForVersion and this associated test.";
+
+ EXPECT_EQ(QUIC_RST_FLOW_CONTROL_ACCOUNTING, AdjustErrorForVersion(
+ QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ QUIC_VERSION_18));
+}
+
+TEST(QuicProtocolTest, MakeQuicTag) {
+ QuicTag tag = MakeQuicTag('A', 'B', 'C', 'D');
+ char bytes[4];
+ memcpy(bytes, &tag, 4);
+ EXPECT_EQ('A', bytes[0]);
+ EXPECT_EQ('B', bytes[1]);
+ EXPECT_EQ('C', bytes[2]);
+ EXPECT_EQ('D', bytes[3]);
+}
+
+TEST(QuicProtocolTest, IsAawaitingPacket) {
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 10u;
+ EXPECT_TRUE(IsAwaitingPacket(ack_frame, 11u));
+ EXPECT_FALSE(IsAwaitingPacket(ack_frame, 1u));
+
+ ack_frame.missing_packets.insert(10);
+ EXPECT_TRUE(IsAwaitingPacket(ack_frame, 10u));
+}
+
+TEST(QuicProtocolTest, InsertMissingPacketsBetween) {
+ QuicAckFrame ack_frame;
+ InsertMissingPacketsBetween(&ack_frame, 4u, 10u);
+ EXPECT_EQ(6u, ack_frame.missing_packets.size());
+
+ QuicPacketSequenceNumber i = 4;
+ for (SequenceNumberSet::iterator it = ack_frame.missing_packets.begin();
+ it != ack_frame.missing_packets.end(); ++it, ++i) {
+ EXPECT_EQ(i, *it);
+ }
+}
+
+TEST(QuicProtocolTest, QuicVersionToQuicTag) {
+ // If you add a new version to the QuicVersion enum you will need to add a new
+ // case to QuicVersionToQuicTag, otherwise this test will fail.
+
+ // TODO(rtenneti): Enable checking of Log(ERROR) messages.
+#if 0
+ // Any logs would indicate an unsupported version which we don't expect.
+ ScopedMockLog log(kDoNotCaptureLogsYet);
+ EXPECT_CALL(log, Log(_, _, _)).Times(0);
+ log.StartCapturingLogs();
+#endif
+
+ // Explicitly test a specific version.
+ EXPECT_EQ(MakeQuicTag('Q', '0', '1', '9'),
+ QuicVersionToQuicTag(QUIC_VERSION_19));
+
+ // Loop over all supported versions and make sure that we never hit the
+ // default case (i.e. all supported versions should be successfully converted
+ // to valid QuicTags).
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ QuicVersion version = kSupportedQuicVersions[i];
+ EXPECT_LT(0u, QuicVersionToQuicTag(version));
+ }
+}
+
+TEST(QuicProtocolTest, QuicVersionToQuicTagUnsupported) {
+ // TODO(rtenneti): Enable checking of Log(ERROR) messages.
+#if 0
+ // TODO(rjshade): Change to DFATAL once we actually support multiple versions,
+ // and QuicConnectionTest::SendVersionNegotiationPacket can be changed to use
+ // mis-matched versions rather than relying on QUIC_VERSION_UNSUPPORTED.
+ ScopedMockLog log(kDoNotCaptureLogsYet);
+ EXPECT_CALL(log, Log(ERROR, _, "Unsupported QuicVersion: 0")).Times(1);
+ log.StartCapturingLogs();
+#endif
+
+ EXPECT_EQ(0u, QuicVersionToQuicTag(QUIC_VERSION_UNSUPPORTED));
+}
+
+TEST(QuicProtocolTest, QuicTagToQuicVersion) {
+ // If you add a new version to the QuicVersion enum you will need to add a new
+ // case to QuicTagToQuicVersion, otherwise this test will fail.
+
+ // TODO(rtenneti): Enable checking of Log(ERROR) messages.
+#if 0
+ // Any logs would indicate an unsupported version which we don't expect.
+ ScopedMockLog log(kDoNotCaptureLogsYet);
+ EXPECT_CALL(log, Log(_, _, _)).Times(0);
+ log.StartCapturingLogs();
+#endif
+
+ // Explicitly test specific versions.
+ EXPECT_EQ(QUIC_VERSION_19,
+ QuicTagToQuicVersion(MakeQuicTag('Q', '0', '1', '9')));
+
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ QuicVersion version = kSupportedQuicVersions[i];
+
+ // Get the tag from the version (we can loop over QuicVersions easily).
+ QuicTag tag = QuicVersionToQuicTag(version);
+ EXPECT_LT(0u, tag);
+
+ // Now try converting back.
+ QuicVersion tag_to_quic_version = QuicTagToQuicVersion(tag);
+ EXPECT_EQ(version, tag_to_quic_version);
+ EXPECT_NE(QUIC_VERSION_UNSUPPORTED, tag_to_quic_version);
+ }
+}
+
+TEST(QuicProtocolTest, QuicTagToQuicVersionUnsupported) {
+ // TODO(rtenneti): Enable checking of Log(ERROR) messages.
+#if 0
+ ScopedMockLog log(kDoNotCaptureLogsYet);
+#ifndef NDEBUG
+ EXPECT_CALL(log, Log(INFO, _, "Unsupported QuicTag version: FAKE")).Times(1);
+#endif
+ log.StartCapturingLogs();
+#endif
+
+ EXPECT_EQ(QUIC_VERSION_UNSUPPORTED,
+ QuicTagToQuicVersion(MakeQuicTag('F', 'A', 'K', 'E')));
+}
+
+TEST(QuicProtocolTest, QuicVersionToString) {
+ EXPECT_EQ("QUIC_VERSION_19", QuicVersionToString(QUIC_VERSION_19));
+ EXPECT_EQ("QUIC_VERSION_UNSUPPORTED",
+ QuicVersionToString(QUIC_VERSION_UNSUPPORTED));
+
+ QuicVersion single_version[] = {QUIC_VERSION_19};
+ QuicVersionVector versions_vector;
+ for (size_t i = 0; i < arraysize(single_version); ++i) {
+ versions_vector.push_back(single_version[i]);
+ }
+ EXPECT_EQ("QUIC_VERSION_19", QuicVersionVectorToString(versions_vector));
+
+ QuicVersion multiple_versions[] = {QUIC_VERSION_UNSUPPORTED, QUIC_VERSION_19};
+ versions_vector.clear();
+ for (size_t i = 0; i < arraysize(multiple_versions); ++i) {
+ versions_vector.push_back(multiple_versions[i]);
+ }
+ EXPECT_EQ("QUIC_VERSION_UNSUPPORTED,QUIC_VERSION_19",
+ QuicVersionVectorToString(versions_vector));
+
+ // Make sure that all supported versions are present in QuicVersionToString.
+ for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) {
+ QuicVersion version = kSupportedQuicVersions[i];
+ EXPECT_NE("QUIC_VERSION_UNSUPPORTED", QuicVersionToString(version));
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_received_packet_manager.cc b/net/quic/quic_received_packet_manager.cc
new file mode 100644
index 0000000..255ce4d
--- /dev/null
+++ b/net/quic/quic_received_packet_manager.cc
@@ -0,0 +1,296 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_received_packet_manager.h"
+
+#include <limits>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_connection_stats.h"
+
+using std::make_pair;
+using std::max;
+using std::min;
+using std::numeric_limits;
+
+namespace net {
+
+namespace {
+
+// The maximum number of packets to ack immediately after a missing packet for
+// fast retransmission to kick in at the sender. This limit is created to
+// reduce the number of acks sent that have no benefit for fast retransmission.
+// Set to the number of nacks needed for fast retransmit plus one for protection
+// against an ack loss
+const size_t kMaxPacketsAfterNewMissing = 4;
+
+}
+
+QuicReceivedPacketManager::EntropyTracker::EntropyTracker()
+ : packets_entropy_hash_(0),
+ first_gap_(1),
+ largest_observed_(0) {
+}
+
+QuicReceivedPacketManager::EntropyTracker::~EntropyTracker() {}
+
+QuicPacketEntropyHash QuicReceivedPacketManager::EntropyTracker::EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK_LE(sequence_number, largest_observed_);
+ if (sequence_number == largest_observed_) {
+ return packets_entropy_hash_;
+ }
+
+ DCHECK_GE(sequence_number, first_gap_);
+ DCHECK_EQ(first_gap_ + packets_entropy_.size() - 1, largest_observed_);
+ QuicPacketEntropyHash hash = packets_entropy_hash_;
+ ReceivedEntropyHashes::const_reverse_iterator it = packets_entropy_.rbegin();
+ for (QuicPacketSequenceNumber i = 0;
+ i < (largest_observed_ - sequence_number); ++i, ++it) {
+ hash ^= it->first;
+ }
+ return hash;
+}
+
+void QuicReceivedPacketManager::EntropyTracker::RecordPacketEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ if (sequence_number < first_gap_) {
+ DVLOG(1) << "Ignoring received packet entropy for sequence_number:"
+ << sequence_number << " less than largest_peer_sequence_number:"
+ << first_gap_;
+ return;
+ }
+ // RecordPacketEntropyHash is only intended to be called once per packet.
+ DCHECK(sequence_number > largest_observed_ ||
+ !packets_entropy_[sequence_number - first_gap_].second);
+
+ packets_entropy_hash_ ^= entropy_hash;
+
+ // Optimize the typical case of no gaps.
+ if (sequence_number == largest_observed_ + 1 && packets_entropy_.empty()) {
+ ++first_gap_;
+ largest_observed_ = sequence_number;
+ return;
+ }
+ if (sequence_number > largest_observed_) {
+ for (QuicPacketSequenceNumber i = 0;
+ i < (sequence_number - largest_observed_ - 1); ++i) {
+ packets_entropy_.push_back(make_pair(0, false));
+ }
+ packets_entropy_.push_back(make_pair(entropy_hash, true));
+ largest_observed_ = sequence_number;
+ } else {
+ packets_entropy_[sequence_number - first_gap_] =
+ make_pair(entropy_hash, true);
+ AdvanceFirstGapAndGarbageCollectEntropyMap();
+ }
+
+ DVLOG(2) << "setting cumulative received entropy hash to: "
+ << static_cast<int>(packets_entropy_hash_)
+ << " updated with sequence number " << sequence_number
+ << " entropy hash: " << static_cast<int>(entropy_hash);
+}
+
+void QuicReceivedPacketManager::EntropyTracker::SetCumulativeEntropyUpTo(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ DCHECK_LE(sequence_number, largest_observed_);
+ if (sequence_number < first_gap_) {
+ DVLOG(1) << "Ignoring set entropy at:" << sequence_number
+ << " less than first_gap_:" << first_gap_;
+ return;
+ }
+ while (first_gap_ < sequence_number) {
+ ++first_gap_;
+ if (!packets_entropy_.empty()) {
+ packets_entropy_.pop_front();
+ }
+ }
+ // Compute the current entropy by XORing in all entropies received including
+ // and since sequence_number.
+ packets_entropy_hash_ = entropy_hash;
+ for (ReceivedEntropyHashes::const_iterator it = packets_entropy_.begin();
+ it != packets_entropy_.end(); ++it) {
+ packets_entropy_hash_ ^= it->first;
+ }
+
+ // Garbage collect entries from the beginning of the map.
+ AdvanceFirstGapAndGarbageCollectEntropyMap();
+}
+
+void QuicReceivedPacketManager::EntropyTracker::
+AdvanceFirstGapAndGarbageCollectEntropyMap() {
+ while (!packets_entropy_.empty() && packets_entropy_.front().second) {
+ ++first_gap_;
+ packets_entropy_.pop_front();
+ }
+}
+
+QuicReceivedPacketManager::QuicReceivedPacketManager(
+ QuicConnectionStats* stats)
+ : peer_least_packet_awaiting_ack_(0),
+ time_largest_observed_(QuicTime::Zero()),
+ receive_algorithm_(ReceiveAlgorithmInterface::Create(kTCP)),
+ stats_(stats) {
+ ack_frame_.largest_observed = 0;
+ ack_frame_.entropy_hash = 0;
+}
+
+QuicReceivedPacketManager::~QuicReceivedPacketManager() {}
+
+void QuicReceivedPacketManager::RecordPacketReceived(
+ QuicByteCount bytes,
+ const QuicPacketHeader& header,
+ QuicTime receipt_time) {
+ QuicPacketSequenceNumber sequence_number = header.packet_sequence_number;
+ DCHECK(IsAwaitingPacket(sequence_number));
+
+ InsertMissingPacketsBetween(
+ &ack_frame_,
+ max(ack_frame_.largest_observed + 1, peer_least_packet_awaiting_ack_),
+ sequence_number);
+
+ if (ack_frame_.largest_observed > sequence_number) {
+ // We've gotten one of the out of order packets - remove it from our
+ // "missing packets" list.
+ DVLOG(1) << "Removing " << sequence_number << " from missing list";
+ ack_frame_.missing_packets.erase(sequence_number);
+
+ // Record how out of order stats.
+ ++stats_->packets_reordered;
+ uint32 sequence_gap = ack_frame_.largest_observed - sequence_number;
+ stats_->max_sequence_reordering =
+ max(stats_->max_sequence_reordering, sequence_gap);
+ uint32 reordering_time_us =
+ receipt_time.Subtract(time_largest_observed_).ToMicroseconds();
+ stats_->max_time_reordering_us = max(stats_->max_time_reordering_us,
+ reordering_time_us);
+ }
+ if (sequence_number > ack_frame_.largest_observed) {
+ ack_frame_.largest_observed = sequence_number;
+ time_largest_observed_ = receipt_time;
+ }
+ entropy_tracker_.RecordPacketEntropyHash(sequence_number,
+ header.entropy_hash);
+
+ receive_algorithm_->RecordIncomingPacket(
+ bytes, sequence_number, receipt_time);
+
+ received_packet_times_.push_back(
+ std::make_pair(sequence_number, receipt_time));
+
+ ack_frame_.revived_packets.erase(sequence_number);
+}
+
+void QuicReceivedPacketManager::RecordPacketRevived(
+ QuicPacketSequenceNumber sequence_number) {
+ LOG_IF(DFATAL, !IsAwaitingPacket(sequence_number));
+ ack_frame_.revived_packets.insert(sequence_number);
+}
+
+bool QuicReceivedPacketManager::IsMissing(
+ QuicPacketSequenceNumber sequence_number) {
+ return ContainsKey(ack_frame_.missing_packets, sequence_number);
+}
+
+bool QuicReceivedPacketManager::IsAwaitingPacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return ::net::IsAwaitingPacket(ack_frame_, sequence_number);
+}
+
+namespace {
+struct isTooLarge {
+ explicit isTooLarge(QuicPacketSequenceNumber n) : largest_observed_(n) {}
+ QuicPacketSequenceNumber largest_observed_;
+
+ // Return true if the packet in p is too different from largest_observed_
+ // to express.
+ bool operator() (
+ const std::pair<QuicPacketSequenceNumber, QuicTime>& p) const {
+ return largest_observed_ - p.first >= numeric_limits<uint8>::max();
+ }
+};
+} // namespace
+
+void QuicReceivedPacketManager::UpdateReceivedPacketInfo(
+ QuicAckFrame* ack_frame, QuicTime approximate_now) {
+ *ack_frame = ack_frame_;
+ ack_frame->entropy_hash = EntropyHash(ack_frame_.largest_observed);
+
+ if (time_largest_observed_ == QuicTime::Zero()) {
+ // We have received no packets.
+ ack_frame->delta_time_largest_observed = QuicTime::Delta::Infinite();
+ return;
+ }
+
+ if (approximate_now < time_largest_observed_) {
+ // Approximate now may well be "in the past".
+ ack_frame->delta_time_largest_observed = QuicTime::Delta::Zero();
+ return;
+ }
+
+ ack_frame->delta_time_largest_observed =
+ approximate_now.Subtract(time_largest_observed_);
+
+ // Remove all packets that are too far from largest_observed to express.
+ received_packet_times_.remove_if(isTooLarge(ack_frame_.largest_observed));
+
+ ack_frame->received_packet_times = received_packet_times_;
+ received_packet_times_.clear();
+}
+
+bool QuicReceivedPacketManager::GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback) {
+ return receive_algorithm_->GenerateCongestionFeedback(feedback);
+}
+
+QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ return entropy_tracker_.EntropyHash(sequence_number);
+}
+
+bool QuicReceivedPacketManager::DontWaitForPacketsBefore(
+ QuicPacketSequenceNumber least_unacked) {
+ ack_frame_.revived_packets.erase(
+ ack_frame_.revived_packets.begin(),
+ ack_frame_.revived_packets.lower_bound(least_unacked));
+ size_t missing_packets_count = ack_frame_.missing_packets.size();
+ ack_frame_.missing_packets.erase(
+ ack_frame_.missing_packets.begin(),
+ ack_frame_.missing_packets.lower_bound(least_unacked));
+ return missing_packets_count != ack_frame_.missing_packets.size();
+}
+
+void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer(
+ const QuicStopWaitingFrame& stop_waiting) {
+ // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks.
+ DCHECK_LE(peer_least_packet_awaiting_ack_, stop_waiting.least_unacked);
+ if (stop_waiting.least_unacked > peer_least_packet_awaiting_ack_) {
+ bool missed_packets = DontWaitForPacketsBefore(stop_waiting.least_unacked);
+ if (missed_packets) {
+ DVLOG(1) << "Updating entropy hashed since we missed packets";
+ // There were some missing packets that we won't ever get now. Recalculate
+ // the received entropy hash.
+ entropy_tracker_.SetCumulativeEntropyUpTo(stop_waiting.least_unacked,
+ stop_waiting.entropy_hash);
+ }
+ peer_least_packet_awaiting_ack_ = stop_waiting.least_unacked;
+ }
+ DCHECK(ack_frame_.missing_packets.empty() ||
+ *ack_frame_.missing_packets.begin() >=
+ peer_least_packet_awaiting_ack_);
+}
+
+bool QuicReceivedPacketManager::HasNewMissingPackets() {
+ return !ack_frame_.missing_packets.empty() &&
+ (ack_frame_.largest_observed -
+ *ack_frame_.missing_packets.rbegin()) <= kMaxPacketsAfterNewMissing;
+}
+
+} // namespace net
diff --git a/net/quic/quic_received_packet_manager.h b/net/quic/quic_received_packet_manager.h
new file mode 100644
index 0000000..1231891
--- /dev/null
+++ b/net/quic/quic_received_packet_manager.h
@@ -0,0 +1,182 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Manages the packet entropy calculation for both sent and received packets
+// for a connection.
+
+#ifndef NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_
+#define NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_
+
+#include <deque>
+
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace test {
+class EntropyTrackerPeer;
+class QuicConnectionPeer;
+class QuicReceivedPacketManagerPeer;
+} // namespace test
+
+struct QuicConnectionStats;
+
+// Records all received packets by a connection and tracks their entropy.
+// Also calculates the correct entropy for the framer when it truncates an ack
+// frame being serialized.
+class NET_EXPORT_PRIVATE QuicReceivedPacketManager :
+ public QuicReceivedEntropyHashCalculatorInterface {
+ public:
+ class NET_EXPORT_PRIVATE EntropyTracker {
+ public:
+ EntropyTracker();
+ ~EntropyTracker();
+
+ // Compute the XOR of the entropy of all received packets up to
+ // and including sequence_number.
+ // Requires that either:
+ // sequence_number == largest_observed_
+ // or:
+ // sequence_number > first_gap_ &&
+ // sequence_number < largest_observed_ &&
+ // sequence_number in packets_entropy_
+ QuicPacketEntropyHash EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Record the received entropy hash against |sequence_number|.
+ // Performs garbage collection to advance first_gap_ if
+ // sequence_number == first_gap_.
+ void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Sets the entropy hash up to but not including a sequence number based
+ // on the hash provided by a StopWaiting frame. Clears older packet
+ // entropy entries and performs garbage collection up to the first gap.
+ void SetCumulativeEntropyUpTo(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ private:
+ friend class test::EntropyTrackerPeer;
+
+ // A deque indexed by sequence number storing the packet's hash and whether
+ // a hash was recorded for that sequence number.
+ typedef std::deque<std::pair<QuicPacketEntropyHash, bool> >
+ ReceivedEntropyHashes;
+
+ // Recomputes first_gap_ and removes packets_entropy_ entries that are no
+ // longer needed to compute EntropyHash.
+ void AdvanceFirstGapAndGarbageCollectEntropyMap();
+
+ // Map of received sequence numbers to their corresponding entropy.
+ // Stores an entry for every received packet whose sequence_number is larger
+ // than first_gap_. Packets without the entropy bit set have an entropy
+ // value of 0.
+ ReceivedEntropyHashes packets_entropy_;
+
+ // Cumulative hash of entropy of all received packets.
+ QuicPacketEntropyHash packets_entropy_hash_;
+
+ // Sequence number of the first packet that we do not know the entropy of.
+ // If there are no gaps in the received packet sequence,
+ // packets_entropy_ will be empty and first_gap_ will be equal to
+ // 'largest_observed_ + 1' since that's the first packet for which
+ // entropy is unknown. If there are gaps, packets_entropy_ will
+ // contain entries for all received packets with sequence_number >
+ // first_gap_.
+ QuicPacketSequenceNumber first_gap_;
+
+ // Sequence number of the largest observed packet.
+ QuicPacketSequenceNumber largest_observed_;
+
+ DISALLOW_COPY_AND_ASSIGN(EntropyTracker);
+ };
+
+ explicit QuicReceivedPacketManager(QuicConnectionStats* stats);
+ virtual ~QuicReceivedPacketManager();
+
+ // Updates the internal state concerning which packets have been received.
+ // bytes: the packet size in bytes including Quic Headers.
+ // header: the packet header.
+ // timestamp: the arrival time of the packet.
+ void RecordPacketReceived(QuicByteCount bytes,
+ const QuicPacketHeader& header,
+ QuicTime receipt_time);
+
+ void RecordPacketRevived(QuicPacketSequenceNumber sequence_number);
+
+ // Checks whether |sequence_number| is missing and less than largest observed.
+ bool IsMissing(QuicPacketSequenceNumber sequence_number);
+
+ // Checks if we're still waiting for the packet with |sequence_number|.
+ bool IsAwaitingPacket(QuicPacketSequenceNumber sequence_number);
+
+ // Update the |ack_frame| for an outgoing ack.
+ void UpdateReceivedPacketInfo(QuicAckFrame* ack_frame,
+ QuicTime approximate_now);
+
+ // Should be called before sending an ACK packet, to decide if we need
+ // to attach a QuicCongestionFeedbackFrame block.
+ // Returns false if no QuicCongestionFeedbackFrame block is needed.
+ // Otherwise fills in feedback and returns true.
+ virtual bool GenerateCongestionFeedback(
+ QuicCongestionFeedbackFrame* feedback);
+
+ // QuicReceivedEntropyHashCalculatorInterface
+ // Called by QuicFramer, when the outgoing ack gets truncated, to recalculate
+ // the received entropy hash for the truncated ack frame.
+ virtual QuicPacketEntropyHash EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const OVERRIDE;
+
+ // Updates internal state based on |stop_waiting|.
+ void UpdatePacketInformationSentByPeer(
+ const QuicStopWaitingFrame& stop_waiting);
+
+ // Returns true when there are new missing packets to be reported within 3
+ // packets of the largest observed.
+ bool HasNewMissingPackets();
+
+ QuicPacketSequenceNumber peer_least_packet_awaiting_ack() {
+ return peer_least_packet_awaiting_ack_;
+ }
+
+ private:
+ friend class test::QuicConnectionPeer;
+ friend class test::QuicReceivedPacketManagerPeer;
+
+ // Deletes all missing packets before least unacked. The connection won't
+ // process any packets with sequence number before |least_unacked| that it
+ // received after this call. Returns true if there were missing packets before
+ // |least_unacked| unacked, false otherwise.
+ bool DontWaitForPacketsBefore(QuicPacketSequenceNumber least_unacked);
+
+ // Tracks entropy hashes of received packets.
+ EntropyTracker entropy_tracker_;
+
+ // Least sequence number of the the packet sent by the peer for which it
+ // hasn't received an ack.
+ QuicPacketSequenceNumber peer_least_packet_awaiting_ack_;
+
+ // Received packet information used to produce acks.
+ QuicAckFrame ack_frame_;
+
+ // The time we received the largest_observed sequence number, or zero if
+ // no sequence numbers have been received since UpdateReceivedPacketInfo.
+ // Needed for calculating delta_time_largest_observed.
+ QuicTime time_largest_observed_;
+
+ scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_;
+
+ QuicConnectionStats* stats_;
+
+ PacketTimeList received_packet_times_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicReceivedPacketManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_
diff --git a/net/quic/quic_received_packet_manager_test.cc b/net/quic/quic_received_packet_manager_test.cc
new file mode 100644
index 0000000..9e8c128
--- /dev/null
+++ b/net/quic/quic_received_packet_manager_test.cc
@@ -0,0 +1,365 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_received_packet_manager.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/test_tools/quic_received_packet_manager_peer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::make_pair;
+using std::pair;
+using std::vector;
+
+namespace net {
+namespace test {
+
+class EntropyTrackerPeer {
+ public:
+ static QuicPacketSequenceNumber first_gap(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.first_gap_;
+ }
+ static QuicPacketSequenceNumber largest_observed(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.largest_observed_;
+ }
+ static int packets_entropy_size(
+ const QuicReceivedPacketManager::EntropyTracker& tracker) {
+ return tracker.packets_entropy_.size();
+ }
+ static bool IsTrackingPacket(
+ const QuicReceivedPacketManager::EntropyTracker& tracker,
+ QuicPacketSequenceNumber sequence_number) {
+ return sequence_number >= tracker.first_gap_ &&
+ sequence_number <
+ (tracker.first_gap_ + tracker.packets_entropy_.size()) &&
+ tracker.packets_entropy_[sequence_number - tracker.first_gap_].second;
+ }
+};
+
+namespace {
+
+// Entropy of individual packets is not tracked if there are no gaps.
+TEST(EntropyTrackerTest, NoGaps) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(1, 23);
+ tracker.RecordPacketEntropyHash(2, 42);
+
+ EXPECT_EQ(23 ^ 42, tracker.EntropyHash(2));
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+
+ EXPECT_EQ(2u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+}
+
+// Entropy of individual packets is tracked as long as there are gaps.
+// Filling the first gap results in entropy getting garbage collected.
+TEST(EntropyTrackerTest, FillGaps) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(2, 5);
+ tracker.RecordPacketEntropyHash(5, 17);
+ tracker.RecordPacketEntropyHash(6, 23);
+ tracker.RecordPacketEntropyHash(9, 42);
+
+ EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(9, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(5, tracker.EntropyHash(2));
+ EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 1.
+ tracker.RecordPacketEntropyHash(1, 2);
+
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(7, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 1));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 2));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 4.
+ tracker.RecordPacketEntropyHash(4, 2);
+
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(7, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(5, tracker.EntropyHash(4));
+ EXPECT_EQ(5 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(5 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill the gap at 3. Entropy for packets 3 to 6 are forgotten.
+ tracker.RecordPacketEntropyHash(3, 2);
+
+ EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 3));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 4));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 5));
+ EXPECT_FALSE(EntropyTrackerPeer::IsTrackingPacket(tracker, 6));
+ EXPECT_TRUE(EntropyTrackerPeer::IsTrackingPacket(tracker, 9));
+
+ // Fill in the rest.
+ tracker.RecordPacketEntropyHash(7, 2);
+ tracker.RecordPacketEntropyHash(8, 2);
+
+ EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(2 ^ 5 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+}
+
+TEST(EntropyTrackerTest, SetCumulativeEntropyUpTo) {
+ QuicReceivedPacketManager::EntropyTracker tracker;
+
+ tracker.RecordPacketEntropyHash(2, 5);
+ tracker.RecordPacketEntropyHash(5, 17);
+ tracker.RecordPacketEntropyHash(6, 23);
+ tracker.RecordPacketEntropyHash(9, 42);
+
+ EXPECT_EQ(1u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(9, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ // Inform the tracker about value of the hash at a gap.
+ tracker.SetCumulativeEntropyUpTo(3, 7);
+ EXPECT_EQ(3u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(7, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(7 ^ 17, tracker.EntropyHash(5));
+ EXPECT_EQ(7 ^ 17 ^ 23, tracker.EntropyHash(6));
+ EXPECT_EQ(7 ^ 17 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ // Inform the tracker about value of the hash at a known location.
+ tracker.SetCumulativeEntropyUpTo(6, 1);
+ EXPECT_EQ(7u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(3, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(1 ^ 23 ^ 42, tracker.EntropyHash(9));
+
+ // Inform the tracker about value of the hash at the last location.
+ tracker.SetCumulativeEntropyUpTo(9, 21);
+ EXPECT_EQ(10u, EntropyTrackerPeer::first_gap(tracker));
+ EXPECT_EQ(9u, EntropyTrackerPeer::largest_observed(tracker));
+ EXPECT_EQ(0, EntropyTrackerPeer::packets_entropy_size(tracker));
+
+ EXPECT_EQ(42 ^ 21, tracker.EntropyHash(9));
+}
+
+class QuicReceivedPacketManagerTest : public ::testing::Test {
+ protected:
+ QuicReceivedPacketManagerTest() : received_manager_(&stats_) {}
+
+ void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ RecordPacketReceipt(sequence_number, entropy_hash, QuicTime::Zero());
+ }
+
+ void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash,
+ QuicTime receipt_time) {
+ QuicPacketHeader header;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_hash = entropy_hash;
+ received_manager_.RecordPacketReceived(0u, header, receipt_time);
+ }
+
+ void RecordPacketRevived(QuicPacketSequenceNumber sequence_number) {
+ received_manager_.RecordPacketRevived(sequence_number);
+ }
+
+ QuicConnectionStats stats_;
+ QuicReceivedPacketManager received_manager_;
+};
+
+TEST_F(QuicReceivedPacketManagerTest, ReceivedPacketEntropyHash) {
+ vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
+ entropies.push_back(make_pair(1, 12));
+ entropies.push_back(make_pair(7, 1));
+ entropies.push_back(make_pair(2, 33));
+ entropies.push_back(make_pair(5, 3));
+ entropies.push_back(make_pair(8, 34));
+
+ for (size_t i = 0; i < entropies.size(); ++i) {
+ RecordPacketReceipt(entropies[i].first, entropies[i].second);
+ }
+
+ sort(entropies.begin(), entropies.end());
+
+ QuicPacketEntropyHash hash = 0;
+ size_t index = 0;
+ for (size_t i = 1; i <= (*entropies.rbegin()).first; ++i) {
+ if (entropies[index].first == i) {
+ hash ^= entropies[index].second;
+ ++index;
+ }
+ if (i < 3) continue;
+ EXPECT_EQ(hash, received_manager_.EntropyHash(i));
+ }
+ // Reorder by 5 when 2 is received after 7.
+ EXPECT_EQ(5u, stats_.max_sequence_reordering);
+ EXPECT_EQ(0u, stats_.max_time_reordering_us);
+ EXPECT_EQ(2u, stats_.packets_reordered);
+}
+
+TEST_F(QuicReceivedPacketManagerTest, EntropyHashBelowLeastObserved) {
+ EXPECT_EQ(0, received_manager_.EntropyHash(0));
+ RecordPacketReceipt(4, 5);
+ EXPECT_EQ(0, received_manager_.EntropyHash(3));
+}
+
+TEST_F(QuicReceivedPacketManagerTest, EntropyHashAboveLargestObserved) {
+ EXPECT_EQ(0, received_manager_.EntropyHash(0));
+ RecordPacketReceipt(4, 5);
+ EXPECT_EQ(0, received_manager_.EntropyHash(3));
+}
+
+TEST_F(QuicReceivedPacketManagerTest, SetCumulativeEntropyUpTo) {
+ vector<pair<QuicPacketSequenceNumber, QuicPacketEntropyHash> > entropies;
+ entropies.push_back(make_pair(1, 12));
+ entropies.push_back(make_pair(2, 1));
+ entropies.push_back(make_pair(3, 33));
+ entropies.push_back(make_pair(4, 3));
+ entropies.push_back(make_pair(6, 34));
+ entropies.push_back(make_pair(7, 29));
+
+ QuicPacketEntropyHash entropy_hash = 0;
+ for (size_t i = 0; i < entropies.size(); ++i) {
+ RecordPacketReceipt(entropies[i].first, entropies[i].second);
+ entropy_hash ^= entropies[i].second;
+ }
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
+
+ // Now set the entropy hash up to 5 to be 100.
+ entropy_hash ^= 100;
+ for (size_t i = 0; i < 4; ++i) {
+ entropy_hash ^= entropies[i].second;
+ }
+ QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
+ &received_manager_, 5, 100);
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
+
+ QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
+ &received_manager_, 1, 50);
+ EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(7));
+
+ // No reordering.
+ EXPECT_EQ(0u, stats_.max_sequence_reordering);
+ EXPECT_EQ(0u, stats_.max_time_reordering_us);
+ EXPECT_EQ(0u, stats_.packets_reordered);
+}
+
+TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) {
+ QuicPacketHeader header;
+ header.packet_sequence_number = 2u;
+ received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero());
+ header.packet_sequence_number = 7u;
+ received_manager_.RecordPacketReceived(0u, header, QuicTime::Zero());
+ EXPECT_TRUE(received_manager_.IsAwaitingPacket(3u));
+ EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u));
+ EXPECT_TRUE(QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore(
+ &received_manager_, 4));
+ EXPECT_FALSE(received_manager_.IsAwaitingPacket(3u));
+ EXPECT_TRUE(received_manager_.IsAwaitingPacket(6u));
+}
+
+TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedPacketInfo) {
+ QuicPacketHeader header;
+ header.packet_sequence_number = 2u;
+ QuicTime two_ms = QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(2));
+ received_manager_.RecordPacketReceived(0u, header, two_ms);
+
+ QuicAckFrame ack;
+ received_manager_.UpdateReceivedPacketInfo(&ack, QuicTime::Zero());
+ // When UpdateReceivedPacketInfo with a time earlier than the time of the
+ // largest observed packet, make sure that the delta is 0, not negative.
+ EXPECT_EQ(QuicTime::Delta::Zero(), ack.delta_time_largest_observed);
+
+ QuicTime four_ms = QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(4));
+ received_manager_.UpdateReceivedPacketInfo(&ack, four_ms);
+ // When UpdateReceivedPacketInfo after not having received a new packet,
+ // the delta should still be accurate.
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2),
+ ack.delta_time_largest_observed);
+}
+
+TEST_F(QuicReceivedPacketManagerTest, UpdateReceivedConnectionStats) {
+ RecordPacketReceipt(1, 0);
+ RecordPacketReceipt(6, 0);
+ RecordPacketReceipt(
+ 2, 0, QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1)));
+
+ EXPECT_EQ(4u, stats_.max_sequence_reordering);
+ EXPECT_EQ(1000u, stats_.max_time_reordering_us);
+ EXPECT_EQ(1u, stats_.packets_reordered);
+}
+
+TEST_F(QuicReceivedPacketManagerTest, RevivedPacket) {
+ RecordPacketReceipt(1, 0);
+ RecordPacketReceipt(3, 0);
+ RecordPacketRevived(2);
+
+ QuicAckFrame ack;
+ received_manager_.UpdateReceivedPacketInfo(&ack, QuicTime::Zero());
+ EXPECT_EQ(1u, ack.missing_packets.size());
+ EXPECT_EQ(2u, *ack.missing_packets.begin());
+ EXPECT_EQ(1u, ack.revived_packets.size());
+ EXPECT_EQ(2u, *ack.missing_packets.begin());
+}
+
+TEST_F(QuicReceivedPacketManagerTest, PacketRevivedThenReceived) {
+ RecordPacketReceipt(1, 0);
+ RecordPacketReceipt(3, 0);
+ RecordPacketRevived(2);
+ RecordPacketReceipt(2, 0);
+
+ QuicAckFrame ack;
+ received_manager_.UpdateReceivedPacketInfo(&ack, QuicTime::Zero());
+ EXPECT_TRUE(ack.missing_packets.empty());
+ EXPECT_TRUE(ack.revived_packets.empty());
+}
+
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_reliable_client_stream.cc b/net/quic/quic_reliable_client_stream.cc
new file mode 100644
index 0000000..b1a82d1
--- /dev/null
+++ b/net/quic/quic_reliable_client_stream.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_reliable_client_stream.h"
+
+#include "base/callback_helpers.h"
+#include "net/base/net_errors.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/quic_write_blocked_list.h"
+
+namespace net {
+
+QuicReliableClientStream::QuicReliableClientStream(QuicStreamId id,
+ QuicSession* session,
+ const BoundNetLog& net_log)
+ : QuicDataStream(id, session),
+ net_log_(net_log),
+ delegate_(nullptr) {
+}
+
+QuicReliableClientStream::~QuicReliableClientStream() {
+ if (delegate_)
+ delegate_->OnClose(connection_error());
+}
+
+uint32 QuicReliableClientStream::ProcessData(const char* data,
+ uint32 data_len) {
+ // TODO(rch): buffer data if we don't have a delegate.
+ if (!delegate_) {
+ DLOG(ERROR) << "Missing delegate";
+ Reset(QUIC_STREAM_CANCELLED);
+ return 0;
+ }
+
+ int rv = delegate_->OnDataReceived(data, data_len);
+ if (rv != OK) {
+ DLOG(ERROR) << "Delegate refused data, rv: " << rv;
+ Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ return 0;
+ }
+ return data_len;
+}
+
+void QuicReliableClientStream::OnFinRead() {
+ if (delegate_) {
+ delegate_->OnClose(connection_error());
+ delegate_ = nullptr;
+ }
+ ReliableQuicStream::OnFinRead();
+}
+
+void QuicReliableClientStream::OnCanWrite() {
+ ReliableQuicStream::OnCanWrite();
+
+ if (!HasBufferedData() && !callback_.is_null()) {
+ base::ResetAndReturn(&callback_).Run(OK);
+ }
+}
+
+QuicPriority QuicReliableClientStream::EffectivePriority() const {
+ if (delegate_ && delegate_->HasSendHeadersComplete()) {
+ return QuicDataStream::EffectivePriority();
+ }
+ return QuicWriteBlockedList::kHighestPriority;
+}
+
+int QuicReliableClientStream::WriteStreamData(
+ base::StringPiece data,
+ bool fin,
+ const CompletionCallback& callback) {
+ // We should not have data buffered.
+ DCHECK(!HasBufferedData());
+ // Writes the data, or buffers it.
+ WriteOrBufferData(data, fin, nullptr);
+ if (!HasBufferedData()) {
+ return OK;
+ }
+
+ callback_ = callback;
+ return ERR_IO_PENDING;
+}
+
+void QuicReliableClientStream::SetDelegate(
+ QuicReliableClientStream::Delegate* delegate) {
+ DCHECK((!delegate_ && delegate) || (delegate_ && !delegate));
+ delegate_ = delegate;
+}
+
+void QuicReliableClientStream::OnError(int error) {
+ if (delegate_) {
+ QuicReliableClientStream::Delegate* delegate = delegate_;
+ delegate_ = nullptr;
+ delegate->OnError(error);
+ }
+}
+
+bool QuicReliableClientStream::CanWrite(const CompletionCallback& callback) {
+ bool can_write = session()->connection()->CanWrite(HAS_RETRANSMITTABLE_DATA);
+ if (!can_write) {
+ session()->MarkWriteBlocked(id(), EffectivePriority());
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ }
+ return can_write;
+}
+
+} // namespace net
diff --git a/net/quic/quic_reliable_client_stream.h b/net/quic/quic_reliable_client_stream.h
new file mode 100644
index 0000000..2d5b818
--- /dev/null
+++ b/net/quic/quic_reliable_client_stream.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// NOTE: This code is not shared between Google and Chrome.
+
+#ifndef NET_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_
+#define NET_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/upload_data_stream.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/http/http_stream.h"
+#include "net/quic/quic_data_stream.h"
+
+namespace net {
+
+class QuicClientSession;
+
+// A client-initiated ReliableQuicStream. Instances of this class
+// are owned by the QuicClientSession which created them.
+class NET_EXPORT_PRIVATE QuicReliableClientStream : public QuicDataStream {
+ public:
+ // Delegate handles protocol specific behavior of a quic stream.
+ class NET_EXPORT_PRIVATE Delegate {
+ public:
+ Delegate() {}
+
+ // Called when data is received.
+ // Returns network error code. OK when it successfully receives data.
+ virtual int OnDataReceived(const char* data, int length) = 0;
+
+ // Called when the stream is closed by the peer.
+ virtual void OnClose(QuicErrorCode error) = 0;
+
+ // Called when the stream is closed because of an error.
+ virtual void OnError(int error) = 0;
+
+ // Returns true if sending of headers has completed.
+ virtual bool HasSendHeadersComplete() = 0;
+
+ protected:
+ virtual ~Delegate() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+ };
+
+ QuicReliableClientStream(QuicStreamId id,
+ QuicSession* session,
+ const BoundNetLog& net_log);
+
+ virtual ~QuicReliableClientStream();
+
+ // QuicDataStream
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
+ virtual void OnFinRead() OVERRIDE;
+ virtual void OnCanWrite() OVERRIDE;
+ virtual QuicPriority EffectivePriority() const OVERRIDE;
+
+ // While the server's set_priority shouldn't be called externally, the creator
+ // of client-side streams should be able to set the priority.
+ using QuicDataStream::set_priority;
+
+ int WriteStreamData(base::StringPiece data,
+ bool fin,
+ const CompletionCallback& callback);
+ // Set new |delegate|. |delegate| must not be NULL.
+ // If this stream has already received data, OnDataReceived() will be
+ // called on the delegate.
+ void SetDelegate(Delegate* delegate);
+ Delegate* GetDelegate() { return delegate_; }
+ void OnError(int error);
+
+ // Returns true if the stream can possible write data. (The socket may
+ // turn out to be write blocked, of course). If the stream can not write,
+ // this method returns false, and |callback| will be invoked when
+ // it becomes writable.
+ bool CanWrite(const CompletionCallback& callback);
+
+ const BoundNetLog& net_log() const { return net_log_; }
+
+ using QuicDataStream::HasBufferedData;
+
+ private:
+ BoundNetLog net_log_;
+ Delegate* delegate_;
+
+ CompletionCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicReliableClientStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_
diff --git a/net/quic/quic_reliable_client_stream_test.cc b/net/quic/quic_reliable_client_stream_test.cc
new file mode 100644
index 0000000..caa697a
--- /dev/null
+++ b/net/quic/quic_reliable_client_stream_test.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_reliable_client_stream.h"
+
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::AnyNumber;
+using testing::Return;
+using testing::StrEq;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+const QuicConnectionId kStreamId = 3;
+
+class MockDelegate : public QuicReliableClientStream::Delegate {
+ public:
+ MockDelegate() {}
+
+ MOCK_METHOD0(OnSendData, int());
+ MOCK_METHOD2(OnSendDataComplete, int(int, bool*));
+ MOCK_METHOD2(OnDataReceived, int(const char*, int));
+ MOCK_METHOD1(OnClose, void(QuicErrorCode));
+ MOCK_METHOD1(OnError, void(int));
+ MOCK_METHOD0(HasSendHeadersComplete, bool());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockDelegate);
+};
+
+class QuicReliableClientStreamTest
+ : public ::testing::TestWithParam<QuicVersion> {
+ public:
+ QuicReliableClientStreamTest()
+ : session_(new MockConnection(false, SupportedVersions(GetParam()))) {
+ stream_ = new QuicReliableClientStream(kStreamId, &session_, BoundNetLog());
+ session_.ActivateStream(stream_);
+ stream_->SetDelegate(&delegate_);
+ }
+
+ void InitializeHeaders() {
+ headers_[":host"] = "www.google.com";
+ headers_[":path"] = "/index.hml";
+ headers_[":scheme"] = "https";
+ headers_["cookie"] =
+ "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+ "__utmc=160408618; "
+ "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+ "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+ "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+ "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+ "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+ "1zFMi5vzcns38-8_Sns; "
+ "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+ "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+ "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+ "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+ "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+ "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+ "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+ "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+ "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+ "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+ "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+ "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+ "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+ "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+ "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
+ }
+
+ testing::StrictMock<MockDelegate> delegate_;
+ MockSession session_;
+ QuicReliableClientStream* stream_;
+ QuicCryptoClientConfig crypto_config_;
+ SpdyHeaderBlock headers_;
+};
+
+INSTANTIATE_TEST_CASE_P(Version, QuicReliableClientStreamTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicReliableClientStreamTest, OnFinRead) {
+ InitializeHeaders();
+ string uncompressed_headers =
+ SpdyUtils::SerializeUncompressedHeaders(headers_);
+ EXPECT_CALL(delegate_, OnDataReceived(StrEq(uncompressed_headers.data()),
+ uncompressed_headers.size()));
+ QuicStreamOffset offset = 0;
+ stream_->OnStreamHeaders(uncompressed_headers);
+ stream_->OnStreamHeadersComplete(false, uncompressed_headers.length());
+
+ IOVector iov;
+ QuicStreamFrame frame2(kStreamId, true, offset, iov);
+ EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
+ stream_->OnStreamFrame(frame2);
+}
+
+TEST_P(QuicReliableClientStreamTest, ProcessData) {
+ const char data[] = "hello world!";
+ EXPECT_CALL(delegate_, OnDataReceived(StrEq(data), arraysize(data)));
+ EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
+
+ EXPECT_EQ(arraysize(data), stream_->ProcessData(data, arraysize(data)));
+}
+
+TEST_P(QuicReliableClientStreamTest, ProcessDataWithError) {
+ const char data[] = "hello world!";
+ EXPECT_CALL(delegate_,
+ OnDataReceived(StrEq(data),
+ arraysize(data))).WillOnce(Return(ERR_UNEXPECTED));
+ EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
+
+
+ EXPECT_EQ(0u, stream_->ProcessData(data, arraysize(data)));
+}
+
+TEST_P(QuicReliableClientStreamTest, OnError) {
+ EXPECT_CALL(delegate_, OnError(ERR_INTERNET_DISCONNECTED));
+
+ stream_->OnError(ERR_INTERNET_DISCONNECTED);
+ EXPECT_FALSE(stream_->GetDelegate());
+}
+
+TEST_P(QuicReliableClientStreamTest, WriteStreamData) {
+ EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
+
+ const char kData1[] = "hello world";
+ const size_t kDataLen = arraysize(kData1);
+
+ // All data written.
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen, true)));
+ TestCompletionCallback callback;
+ EXPECT_EQ(OK, stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
+ true, callback.callback()));
+}
+
+TEST_P(QuicReliableClientStreamTest, WriteStreamDataAsync) {
+ EXPECT_CALL(delegate_, HasSendHeadersComplete()).Times(AnyNumber());
+ EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR));
+
+ const char kData1[] = "hello world";
+ const size_t kDataLen = arraysize(kData1);
+
+ // No data written.
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
+ Return(QuicConsumedData(0, false)));
+ TestCompletionCallback callback;
+ EXPECT_EQ(ERR_IO_PENDING,
+ stream_->WriteStreamData(base::StringPiece(kData1, kDataLen),
+ true, callback.callback()));
+ ASSERT_FALSE(callback.have_result());
+
+ // All data written.
+ EXPECT_CALL(session_, WritevData(stream_->id(), _, _, _, _, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen, true)));
+ stream_->OnCanWrite();
+ ASSERT_TRUE(callback.have_result());
+ EXPECT_EQ(OK, callback.WaitForResult());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_sent_entropy_manager.cc b/net/quic/quic_sent_entropy_manager.cc
new file mode 100644
index 0000000..206a9ff
--- /dev/null
+++ b/net/quic/quic_sent_entropy_manager.cc
@@ -0,0 +1,112 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sent_entropy_manager.h"
+
+#include "base/logging.h"
+#include "net/base/linked_hash_map.h"
+
+using std::make_pair;
+using std::max;
+using std::min;
+
+namespace net {
+
+QuicSentEntropyManager::QuicSentEntropyManager() : map_offset_(1) {}
+
+QuicSentEntropyManager::~QuicSentEntropyManager() {}
+
+QuicPacketEntropyHash QuicSentEntropyManager::GetPacketEntropy(
+ QuicPacketSequenceNumber sequence_number) const {
+ return packets_entropy_[sequence_number - map_offset_];
+}
+
+QuicPacketSequenceNumber
+QuicSentEntropyManager::GetLargestPacketWithEntropy() const {
+ return map_offset_ + packets_entropy_.size() - 1;
+}
+
+QuicPacketSequenceNumber
+QuicSentEntropyManager::GetSmallestPacketWithEntropy() const {
+ return map_offset_;
+}
+
+void QuicSentEntropyManager::UpdateCumulativeEntropy(
+ QuicPacketSequenceNumber sequence_number,
+ CumulativeEntropy* cumulative) const {
+ while (cumulative->sequence_number < sequence_number) {
+ ++cumulative->sequence_number;
+ cumulative->entropy ^= GetPacketEntropy(cumulative->sequence_number);
+ }
+}
+
+void QuicSentEntropyManager::RecordPacketEntropyHash(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash) {
+ if (!packets_entropy_.empty()) {
+ // Ensure packets always are recorded in order.
+ // Every packet's entropy is recorded, even if it's not sent, so there
+ // are not sequence number gaps.
+ DCHECK_EQ(GetLargestPacketWithEntropy() + 1, sequence_number);
+ }
+ packets_entropy_.push_back(entropy_hash);
+ DVLOG(2) << "Recorded sequence number " << sequence_number
+ << " with entropy hash: " << static_cast<int>(entropy_hash);
+}
+
+QuicPacketEntropyHash QuicSentEntropyManager::GetCumulativeEntropy(
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK_LE(last_cumulative_entropy_.sequence_number, sequence_number);
+ DCHECK_GE(GetLargestPacketWithEntropy(), sequence_number);
+ // First the entropy for largest_observed sequence number should be updated.
+ UpdateCumulativeEntropy(sequence_number, &last_cumulative_entropy_);
+ return last_cumulative_entropy_.entropy;
+}
+
+bool QuicSentEntropyManager::IsValidEntropy(
+ QuicPacketSequenceNumber largest_observed,
+ const SequenceNumberSet& missing_packets,
+ QuicPacketEntropyHash entropy_hash) {
+ DCHECK_GE(largest_observed, last_valid_entropy_.sequence_number);
+ // Ensure the largest and smallest sequence numbers are in range.
+ if (largest_observed > GetLargestPacketWithEntropy()) {
+ return false;
+ }
+ if (!missing_packets.empty() &&
+ *missing_packets.begin() < GetSmallestPacketWithEntropy()) {
+ return false;
+ }
+ // First the entropy for largest_observed sequence number should be updated.
+ UpdateCumulativeEntropy(largest_observed, &last_valid_entropy_);
+
+ // Now XOR out all the missing entropies.
+ QuicPacketEntropyHash expected_entropy_hash = last_valid_entropy_.entropy;
+ for (SequenceNumberSet::const_iterator it = missing_packets.begin();
+ it != missing_packets.end(); ++it) {
+ expected_entropy_hash ^= GetPacketEntropy(*it);
+ }
+ DLOG_IF(WARNING, entropy_hash != expected_entropy_hash)
+ << "Invalid entropy hash: " << static_cast<int>(entropy_hash)
+ << " expected entropy hash: " << static_cast<int>(expected_entropy_hash);
+ return entropy_hash == expected_entropy_hash;
+}
+
+void QuicSentEntropyManager::ClearEntropyBefore(
+ QuicPacketSequenceNumber sequence_number) {
+ // Don't discard entropy before updating the cumulative entropy used to
+ // calculate EntropyHash and IsValidEntropy.
+ if (last_cumulative_entropy_.sequence_number < sequence_number) {
+ UpdateCumulativeEntropy(sequence_number, &last_cumulative_entropy_);
+ }
+ if (last_valid_entropy_.sequence_number < sequence_number) {
+ UpdateCumulativeEntropy(sequence_number, &last_valid_entropy_);
+ }
+ while (map_offset_ < sequence_number) {
+ packets_entropy_.pop_front();
+ ++map_offset_;
+ }
+ DVLOG(2) << "Cleared entropy before: " << sequence_number;
+}
+
+} // namespace net
diff --git a/net/quic/quic_sent_entropy_manager.h b/net/quic/quic_sent_entropy_manager.h
new file mode 100644
index 0000000..9173d95
--- /dev/null
+++ b/net/quic/quic_sent_entropy_manager.h
@@ -0,0 +1,89 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Manages the packet entropy calculation for both sent and received packets
+// for a connection.
+
+#ifndef NET_QUIC_QUIC_SENT_ENTROPY_MANAGER_H_
+#define NET_QUIC_QUIC_SENT_ENTROPY_MANAGER_H_
+
+#include <deque>
+
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+namespace test {
+class QuicConnectionPeer;
+} // namespace test
+
+// Records all sent packets by a connection to track the cumulative entropy of
+// sent packets. It is used by the connection to validate an ack
+// frame sent by the peer as a preventive measure against the optimistic ack
+// attack.
+class NET_EXPORT_PRIVATE QuicSentEntropyManager {
+ public:
+ QuicSentEntropyManager();
+ virtual ~QuicSentEntropyManager();
+
+ // Record |entropy_hash| for sent packet corresponding to |sequence_number|.
+ void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Retrieves the cumulative entropy up to |sequence_number|.
+ // Must always be called with a monotonically increasing |sequence_number|.
+ QuicPacketEntropyHash GetCumulativeEntropy(
+ QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if |entropy_hash| matches the expected sent entropy hash
+ // up to |largest_observed| removing sequence numbers from |missing_packets|.
+ // Must always be called with a monotonically increasing |largest_observed|.
+ bool IsValidEntropy(QuicPacketSequenceNumber largest_observed,
+ const SequenceNumberSet& missing_packets,
+ QuicPacketEntropyHash entropy_hash);
+
+ // Removes unnecessary entries before |sequence_number|.
+ void ClearEntropyBefore(QuicPacketSequenceNumber sequence_number);
+
+ private:
+ friend class test::QuicConnectionPeer;
+
+ typedef std::deque<QuicPacketEntropyHash> SentEntropyMap;
+
+ struct CumulativeEntropy {
+ CumulativeEntropy() : sequence_number(0), entropy(0) {}
+
+ QuicPacketSequenceNumber sequence_number;
+ QuicPacketEntropyHash entropy;
+ };
+
+ // Convenience methods to get the largest and smallest packets with entropies.
+ QuicPacketSequenceNumber GetLargestPacketWithEntropy() const;
+ QuicPacketSequenceNumber GetSmallestPacketWithEntropy() const;
+ // Convenience method to get the entropy hash for |sequence_number|.
+ QuicPacketEntropyHash GetPacketEntropy(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Update the cumulative entropy to |sequence_number|.
+ void UpdateCumulativeEntropy(QuicPacketSequenceNumber sequence_number,
+ CumulativeEntropy* cumulative) const;
+
+ // Maps sequence numbers to the sent entropy hash for the sequence number.
+ SentEntropyMap packets_entropy_;
+ QuicPacketSequenceNumber map_offset_;
+
+ // Cache the cumulative entropy for IsValidEntropy.
+ CumulativeEntropy last_valid_entropy_;
+
+ // Cache the cumulative entropy for the sequence number used by EntropyHash.
+ CumulativeEntropy last_cumulative_entropy_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSentEntropyManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SENT_ENTROPY_MANAGER_H_
diff --git a/net/quic/quic_sent_entropy_manager_test.cc b/net/quic/quic_sent_entropy_manager_test.cc
new file mode 100644
index 0000000..78bdcce
--- /dev/null
+++ b/net/quic/quic_sent_entropy_manager_test.cc
@@ -0,0 +1,99 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sent_entropy_manager.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::make_pair;
+using std::pair;
+using std::vector;
+
+namespace net {
+namespace test {
+namespace {
+
+class QuicSentEntropyManagerTest : public ::testing::Test {
+ protected:
+ QuicSentEntropyManager entropy_manager_;
+};
+
+TEST_F(QuicSentEntropyManagerTest, SentEntropyHash) {
+ EXPECT_EQ(0, entropy_manager_.GetCumulativeEntropy(0));
+
+ QuicPacketEntropyHash entropies[4] = {12, 1, 33, 3};
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ entropy_manager_.RecordPacketEntropyHash(i + 1, entropies[i]);
+ }
+
+ QuicPacketEntropyHash hash = 0;
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ hash ^= entropies[i];
+ EXPECT_EQ(hash, entropy_manager_.GetCumulativeEntropy(i + 1));
+ }
+}
+
+TEST_F(QuicSentEntropyManagerTest, IsValidEntropy) {
+ QuicPacketEntropyHash entropies[10] =
+ {12, 1, 33, 3, 32, 100, 28, 42, 22, 255};
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ entropy_manager_.RecordPacketEntropyHash(i + 1, entropies[i]);
+ }
+
+ SequenceNumberSet missing_packets;
+ missing_packets.insert(1);
+ missing_packets.insert(4);
+ missing_packets.insert(7);
+ missing_packets.insert(8);
+
+ QuicPacketEntropyHash entropy_hash = 0;
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ if (missing_packets.find(i + 1) == missing_packets.end()) {
+ entropy_hash ^= entropies[i];
+ }
+ }
+
+ EXPECT_TRUE(entropy_manager_.IsValidEntropy(10, missing_packets,
+ entropy_hash));
+}
+
+TEST_F(QuicSentEntropyManagerTest, ClearEntropiesBefore) {
+ QuicPacketEntropyHash entropies[10] =
+ {12, 1, 33, 3, 32, 100, 28, 42, 22, 255};
+
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ entropy_manager_.RecordPacketEntropyHash(i + 1, entropies[i]);
+ }
+
+ // Discard the first 5 entropies and ensure IsValidEntropy and EntropyHash
+ // still return correct results.
+ entropy_manager_.ClearEntropyBefore(5);
+
+ SequenceNumberSet missing_packets;
+ missing_packets.insert(7);
+ missing_packets.insert(8);
+
+ QuicPacketEntropyHash entropy_hash = 0;
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ if (missing_packets.find(i + 1) == missing_packets.end()) {
+ entropy_hash ^= entropies[i];
+ }
+ }
+ EXPECT_TRUE(entropy_manager_.IsValidEntropy(10, missing_packets,
+ entropy_hash));
+
+ entropy_hash = 0;
+ for (size_t i = 0; i < arraysize(entropies); ++i) {
+ entropy_hash ^= entropies[i];
+ }
+ EXPECT_EQ(entropy_hash, entropy_manager_.GetCumulativeEntropy(10));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc
new file mode 100644
index 0000000..a062827
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager.cc
@@ -0,0 +1,889 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sent_packet_manager.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/pacing_sender.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_ack_notifier_manager.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_utils_chromium.h"
+
+using std::make_pair;
+using std::max;
+using std::min;
+
+namespace net {
+
+// The length of the recent min rtt window in seconds. Windowing is disabled for
+// values less than or equal to 0.
+int32 FLAGS_quic_recent_min_rtt_window_s = 60;
+
+namespace {
+static const int kDefaultRetransmissionTimeMs = 500;
+// TCP RFC calls for 1 second RTO however Linux differs from this default and
+// define the minimum RTO to 200ms, we will use the same until we have data to
+// support a higher or lower value.
+static const int kMinRetransmissionTimeMs = 200;
+static const int kMaxRetransmissionTimeMs = 60000;
+static const size_t kMaxRetransmissions = 10;
+
+// Only exponentially back off the handshake timer 5 times due to a timeout.
+static const size_t kMaxHandshakeRetransmissionBackoffs = 5;
+static const size_t kMinHandshakeTimeoutMs = 10;
+
+// Sends up to two tail loss probes before firing an RTO,
+// per draft RFC draft-dukkipati-tcpm-tcp-loss-probe.
+static const size_t kDefaultMaxTailLossProbes = 2;
+static const int64 kMinTailLossProbeTimeoutMs = 10;
+
+// Number of samples before we force a new recent min rtt to be captured.
+static const size_t kNumMinRttSamplesAfterQuiescence = 2;
+
+// Number of unpaced packets to send after quiescence.
+static const size_t kInitialUnpacedBurst = 10;
+
+bool HasCryptoHandshake(const TransmissionInfo& transmission_info) {
+ if (transmission_info.retransmittable_frames == nullptr) {
+ return false;
+ }
+ return transmission_info.retransmittable_frames->HasCryptoHandshake() ==
+ IS_HANDSHAKE;
+}
+
+} // namespace
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+QuicSentPacketManager::QuicSentPacketManager(
+ bool is_server,
+ const QuicClock* clock,
+ QuicConnectionStats* stats,
+ CongestionControlType congestion_control_type,
+ LossDetectionType loss_type)
+ : unacked_packets_(),
+ is_server_(is_server),
+ clock_(clock),
+ stats_(stats),
+ debug_delegate_(nullptr),
+ network_change_visitor_(nullptr),
+ send_algorithm_(SendAlgorithmInterface::Create(clock,
+ &rtt_stats_,
+ congestion_control_type,
+ stats)),
+ loss_algorithm_(LossDetectionInterface::Create(loss_type)),
+ least_packet_awaited_by_peer_(1),
+ first_rto_transmission_(0),
+ consecutive_rto_count_(0),
+ consecutive_tlp_count_(0),
+ consecutive_crypto_retransmission_count_(0),
+ pending_timer_transmission_count_(0),
+ max_tail_loss_probes_(kDefaultMaxTailLossProbes),
+ using_pacing_(false),
+ handshake_confirmed_(false) {
+}
+
+QuicSentPacketManager::~QuicSentPacketManager() {
+}
+
+void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) {
+ if (config.HasReceivedInitialRoundTripTimeUs() &&
+ config.ReceivedInitialRoundTripTimeUs() > 0) {
+ rtt_stats_.set_initial_rtt_us(min(kMaxInitialRoundTripTimeUs,
+ config.ReceivedInitialRoundTripTimeUs()));
+ } else if (config.HasInitialRoundTripTimeUsToSend()) {
+ rtt_stats_.set_initial_rtt_us(
+ min(kMaxInitialRoundTripTimeUs,
+ config.GetInitialRoundTripTimeUsToSend()));
+ }
+ // TODO(ianswett): BBR is currently a server only feature.
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kTBBR)) {
+ if (FLAGS_quic_recent_min_rtt_window_s > 0) {
+ rtt_stats_.set_recent_min_rtt_window(
+ QuicTime::Delta::FromSeconds(FLAGS_quic_recent_min_rtt_window_s));
+ }
+ send_algorithm_.reset(
+ SendAlgorithmInterface::Create(clock_, &rtt_stats_, kBBR, stats_));
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kRENO)) {
+ send_algorithm_.reset(
+ SendAlgorithmInterface::Create(clock_, &rtt_stats_, kReno, stats_));
+ }
+ if (HasClientSentConnectionOption(config, kPACE)) {
+ EnablePacing();
+ }
+ if (HasClientSentConnectionOption(config, k1CON)) {
+ send_algorithm_->SetNumEmulatedConnections(1);
+ }
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), kTIME)) {
+ loss_algorithm_.reset(LossDetectionInterface::Create(kTime));
+ }
+ send_algorithm_->SetFromConfig(config, is_server_);
+
+ if (network_change_visitor_ != nullptr) {
+ network_change_visitor_->OnCongestionWindowChange(GetCongestionWindow());
+ }
+}
+
+bool QuicSentPacketManager::HasClientSentConnectionOption(
+ const QuicConfig& config, QuicTag tag) const {
+ if (is_server_) {
+ if (config.HasReceivedConnectionOptions() &&
+ ContainsQuicTag(config.ReceivedConnectionOptions(), tag)) {
+ return true;
+ }
+ } else if (config.HasSendConnectionOptions() &&
+ ContainsQuicTag(config.SendConnectionOptions(), tag)) {
+ return true;
+ }
+ return false;
+}
+
+void QuicSentPacketManager::OnRetransmittedPacket(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ TransmissionType transmission_type;
+ PendingRetransmissionMap::iterator it =
+ pending_retransmissions_.find(old_sequence_number);
+ if (it != pending_retransmissions_.end()) {
+ transmission_type = it->second;
+ pending_retransmissions_.erase(it);
+ } else {
+ DLOG(DFATAL) << "Expected sequence number to be in "
+ "pending_retransmissions_. sequence_number: " << old_sequence_number;
+ transmission_type = NOT_RETRANSMISSION;
+ }
+
+ // A notifier may be waiting to hear about ACKs for the original sequence
+ // number. Inform them that the sequence number has changed.
+ ack_notifier_manager_.UpdateSequenceNumber(old_sequence_number,
+ new_sequence_number);
+
+ unacked_packets_.OnRetransmittedPacket(old_sequence_number,
+ new_sequence_number,
+ transmission_type);
+}
+
+void QuicSentPacketManager::OnIncomingAck(const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time) {
+ QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
+
+ UpdatePacketInformationReceivedByPeer(ack_frame);
+ // We rely on delta_time_largest_observed to compute an RTT estimate, so
+ // we only update rtt when the largest observed gets acked.
+ bool largest_observed_acked = MaybeUpdateRTT(ack_frame, ack_receive_time);
+ DCHECK_GE(ack_frame.largest_observed, unacked_packets_.largest_observed());
+ unacked_packets_.IncreaseLargestObserved(ack_frame.largest_observed);
+
+ HandleAckForSentPackets(ack_frame);
+ InvokeLossDetection(ack_receive_time);
+ MaybeInvokeCongestionEvent(largest_observed_acked, bytes_in_flight);
+ unacked_packets_.RemoveObsoletePackets();
+
+ sustained_bandwidth_recorder_.RecordEstimate(
+ send_algorithm_->InRecovery(),
+ send_algorithm_->InSlowStart(),
+ send_algorithm_->BandwidthEstimate(),
+ ack_receive_time,
+ clock_->WallNow(),
+ rtt_stats_.SmoothedRtt());
+
+ // If we have received a truncated ack, then we need to clear out some
+ // previous transmissions to allow the peer to actually ACK new packets.
+ if (ack_frame.is_truncated) {
+ unacked_packets_.ClearAllPreviousRetransmissions();
+ }
+
+ // Anytime we are making forward progress and have a new RTT estimate, reset
+ // the backoff counters.
+ if (largest_observed_acked) {
+ // Reset all retransmit counters any time a new packet is acked.
+ consecutive_rto_count_ = 0;
+ consecutive_tlp_count_ = 0;
+ consecutive_crypto_retransmission_count_ = 0;
+ }
+
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnIncomingAck(ack_frame,
+ ack_receive_time,
+ unacked_packets_.largest_observed(),
+ largest_observed_acked,
+ GetLeastUnacked());
+ }
+}
+
+void QuicSentPacketManager::UpdatePacketInformationReceivedByPeer(
+ const QuicAckFrame& ack_frame) {
+ if (ack_frame.missing_packets.empty()) {
+ least_packet_awaited_by_peer_ = ack_frame.largest_observed + 1;
+ } else {
+ least_packet_awaited_by_peer_ = *(ack_frame.missing_packets.begin());
+ }
+}
+
+void QuicSentPacketManager::MaybeInvokeCongestionEvent(
+ bool rtt_updated, QuicByteCount bytes_in_flight) {
+ if (!rtt_updated && packets_acked_.empty() && packets_lost_.empty()) {
+ return;
+ }
+ send_algorithm_->OnCongestionEvent(rtt_updated, bytes_in_flight,
+ packets_acked_, packets_lost_);
+ packets_acked_.clear();
+ packets_lost_.clear();
+ if (network_change_visitor_ != nullptr) {
+ network_change_visitor_->OnCongestionWindowChange(GetCongestionWindow());
+ }
+}
+
+void QuicSentPacketManager::HandleAckForSentPackets(
+ const QuicAckFrame& ack_frame) {
+ // Go through the packets we have not received an ack for and see if this
+ // incoming_ack shows they've been seen by the peer.
+ QuicTime::Delta delta_largest_observed =
+ ack_frame.delta_time_largest_observed;
+ QuicPacketSequenceNumber sequence_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ if (sequence_number > ack_frame.largest_observed) {
+ // These packets are still in flight.
+ break;
+ }
+
+ if (ContainsKey(ack_frame.missing_packets, sequence_number)) {
+ // Don't continue to increase the nack count for packets not in flight.
+ if (!it->in_flight) {
+ continue;
+ }
+ // Consider it multiple nacks when there is a gap between the missing
+ // packet and the largest observed, since the purpose of a nack
+ // threshold is to tolerate re-ordering. This handles both StretchAcks
+ // and Forward Acks.
+ // The nack count only increases when the largest observed increases.
+ size_t min_nacks = ack_frame.largest_observed - sequence_number;
+ // Truncated acks can nack the largest observed, so use a min of 1.
+ if (min_nacks == 0) {
+ min_nacks = 1;
+ }
+ unacked_packets_.NackPacket(sequence_number, min_nacks);
+ continue;
+ }
+ // Packet was acked, so remove it from our unacked packet list.
+ DVLOG(1) << ENDPOINT << "Got an ack for packet " << sequence_number;
+ // If data is associated with the most recent transmission of this
+ // packet, then inform the caller.
+ if (it->in_flight) {
+ packets_acked_.push_back(make_pair(sequence_number, *it));
+ }
+ MarkPacketHandled(sequence_number, *it, delta_largest_observed);
+ }
+
+ // Discard any retransmittable frames associated with revived packets.
+ for (SequenceNumberSet::const_iterator revived_it =
+ ack_frame.revived_packets.begin();
+ revived_it != ack_frame.revived_packets.end(); ++revived_it) {
+ MarkPacketRevived(*revived_it, delta_largest_observed);
+ }
+}
+
+bool QuicSentPacketManager::HasRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const {
+ return unacked_packets_.HasRetransmittableFrames(sequence_number);
+}
+
+void QuicSentPacketManager::RetransmitUnackedPackets(
+ TransmissionType retransmission_type) {
+ DCHECK(retransmission_type == ALL_UNACKED_RETRANSMISSION ||
+ retransmission_type == ALL_INITIAL_RETRANSMISSION);
+ QuicPacketSequenceNumber sequence_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ const RetransmittableFrames* frames = it->retransmittable_frames;
+ if (frames != nullptr &&
+ (retransmission_type == ALL_UNACKED_RETRANSMISSION ||
+ frames->encryption_level() == ENCRYPTION_INITIAL)) {
+ MarkForRetransmission(sequence_number, retransmission_type);
+ }
+ }
+}
+
+void QuicSentPacketManager::NeuterUnencryptedPackets() {
+ QuicPacketSequenceNumber sequence_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ const RetransmittableFrames* frames = it->retransmittable_frames;
+ if (frames != nullptr && frames->encryption_level() == ENCRYPTION_NONE) {
+ // Once you're forward secure, no unencrypted packets will be sent, crypto
+ // or otherwise. Unencrypted packets are neutered and abandoned, to ensure
+ // they are not retransmitted or considered lost from a congestion control
+ // perspective.
+ pending_retransmissions_.erase(sequence_number);
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ unacked_packets_.RemoveRetransmittability(sequence_number);
+ }
+ }
+}
+
+void QuicSentPacketManager::MarkForRetransmission(
+ QuicPacketSequenceNumber sequence_number,
+ TransmissionType transmission_type) {
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ LOG_IF(DFATAL, transmission_info.retransmittable_frames == nullptr);
+ if (transmission_type != TLP_RETRANSMISSION) {
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
+ // TODO(ianswett): Currently the RTO can fire while there are pending NACK
+ // retransmissions for the same data, which is not ideal.
+ if (ContainsKey(pending_retransmissions_, sequence_number)) {
+ return;
+ }
+
+ pending_retransmissions_[sequence_number] = transmission_type;
+}
+
+void QuicSentPacketManager::RecordSpuriousRetransmissions(
+ const SequenceNumberList& all_transmissions,
+ QuicPacketSequenceNumber acked_sequence_number) {
+ if (acked_sequence_number < first_rto_transmission_) {
+ // Cancel all pending RTO transmissions and restore their in flight status.
+ // Replace SRTT with latest_rtt and increase the variance to prevent
+ // a spurious RTO from happening again.
+ rtt_stats_.ExpireSmoothedMetrics();
+ for (PendingRetransmissionMap::const_iterator it =
+ pending_retransmissions_.begin();
+ it != pending_retransmissions_.end(); ++it) {
+ DCHECK_EQ(it->second, RTO_RETRANSMISSION);
+ unacked_packets_.RestoreInFlight(it->first);
+ }
+ pending_retransmissions_.clear();
+ send_algorithm_->RevertRetransmissionTimeout();
+ first_rto_transmission_ = 0;
+ ++stats_->spurious_rto_count;
+ }
+ for (SequenceNumberList::const_reverse_iterator it =
+ all_transmissions.rbegin();
+ it != all_transmissions.rend() && *it > acked_sequence_number; ++it) {
+ const TransmissionInfo& retransmit_info =
+ unacked_packets_.GetTransmissionInfo(*it);
+
+ stats_->bytes_spuriously_retransmitted += retransmit_info.bytes_sent;
+ ++stats_->packets_spuriously_retransmitted;
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnSpuriousPacketRetransmition(
+ retransmit_info.transmission_type,
+ retransmit_info.bytes_sent);
+ }
+ }
+}
+
+bool QuicSentPacketManager::HasPendingRetransmissions() const {
+ return !pending_retransmissions_.empty();
+}
+
+QuicSentPacketManager::PendingRetransmission
+ QuicSentPacketManager::NextPendingRetransmission() {
+ DCHECK(!pending_retransmissions_.empty());
+ QuicPacketSequenceNumber sequence_number =
+ pending_retransmissions_.begin()->first;
+ TransmissionType transmission_type = pending_retransmissions_.begin()->second;
+ if (unacked_packets_.HasPendingCryptoPackets()) {
+ // Ensure crypto packets are retransmitted before other packets.
+ PendingRetransmissionMap::const_iterator it =
+ pending_retransmissions_.begin();
+ do {
+ if (HasCryptoHandshake(unacked_packets_.GetTransmissionInfo(it->first))) {
+ sequence_number = it->first;
+ transmission_type = it->second;
+ break;
+ }
+ ++it;
+ } while (it != pending_retransmissions_.end());
+ }
+ DCHECK(unacked_packets_.IsUnacked(sequence_number)) << sequence_number;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ DCHECK(transmission_info.retransmittable_frames);
+
+ return PendingRetransmission(sequence_number,
+ transmission_type,
+ *transmission_info.retransmittable_frames,
+ transmission_info.sequence_number_length);
+}
+
+void QuicSentPacketManager::MarkPacketRevived(
+ QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed) {
+ if (!unacked_packets_.IsUnacked(sequence_number)) {
+ return;
+ }
+
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ QuicPacketSequenceNumber newest_transmission =
+ transmission_info.all_transmissions == nullptr
+ ? sequence_number
+ : *transmission_info.all_transmissions->rbegin();
+ // This packet has been revived at the receiver. If we were going to
+ // retransmit it, do not retransmit it anymore.
+ pending_retransmissions_.erase(newest_transmission);
+
+ // The AckNotifierManager needs to be notified for revived packets,
+ // since it indicates the packet arrived from the appliction's perspective.
+ if (transmission_info.retransmittable_frames) {
+ ack_notifier_manager_.OnPacketAcked(
+ newest_transmission, delta_largest_observed);
+ }
+
+ unacked_packets_.RemoveRetransmittability(sequence_number);
+}
+
+void QuicSentPacketManager::MarkPacketHandled(
+ QuicPacketSequenceNumber sequence_number,
+ const TransmissionInfo& info,
+ QuicTime::Delta delta_largest_observed) {
+ QuicPacketSequenceNumber newest_transmission =
+ info.all_transmissions == nullptr ? sequence_number
+ : *info.all_transmissions->rbegin();
+ // Remove the most recent packet, if it is pending retransmission.
+ pending_retransmissions_.erase(newest_transmission);
+
+ // The AckNotifierManager needs to be notified about the most recent
+ // transmission, since that's the one only one it tracks.
+ ack_notifier_manager_.OnPacketAcked(newest_transmission,
+ delta_largest_observed);
+ if (newest_transmission != sequence_number) {
+ RecordSpuriousRetransmissions(*info.all_transmissions, sequence_number);
+ // Remove the most recent packet from flight if it's a crypto handshake
+ // packet, since they won't be acked now that one has been processed.
+ // Other crypto handshake packets won't be in flight, only the newest
+ // transmission of a crypto packet is in flight at once.
+ // TODO(ianswett): Instead of handling all crypto packets special,
+ // only handle nullptr encrypted packets in a special way.
+ if (HasCryptoHandshake(
+ unacked_packets_.GetTransmissionInfo(newest_transmission))) {
+ unacked_packets_.RemoveFromInFlight(newest_transmission);
+ }
+ }
+
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ unacked_packets_.RemoveRetransmittability(sequence_number);
+}
+
+bool QuicSentPacketManager::IsUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ return unacked_packets_.IsUnacked(sequence_number);
+}
+
+bool QuicSentPacketManager::HasUnackedPackets() const {
+ return unacked_packets_.HasUnackedPackets();
+}
+
+QuicPacketSequenceNumber
+QuicSentPacketManager::GetLeastUnacked() const {
+ return unacked_packets_.GetLeastUnacked();
+}
+
+bool QuicSentPacketManager::OnPacketSent(
+ SerializedPacket* serialized_packet,
+ QuicPacketSequenceNumber original_sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes,
+ TransmissionType transmission_type,
+ HasRetransmittableData has_retransmittable_data) {
+ QuicPacketSequenceNumber sequence_number = serialized_packet->sequence_number;
+ DCHECK_LT(0u, sequence_number);
+ DCHECK(!unacked_packets_.IsUnacked(sequence_number));
+ LOG_IF(DFATAL, bytes == 0) << "Cannot send empty packets.";
+ if (debug_delegate_ != nullptr) {
+ debug_delegate_->OnSentPacket(*serialized_packet,
+ original_sequence_number,
+ sent_time,
+ bytes,
+ transmission_type);
+ }
+
+ if (original_sequence_number == 0) {
+ if (serialized_packet->retransmittable_frames) {
+ ack_notifier_manager_.OnSerializedPacket(*serialized_packet);
+ }
+ unacked_packets_.AddPacket(*serialized_packet);
+ serialized_packet->retransmittable_frames = nullptr;
+ } else {
+ OnRetransmittedPacket(original_sequence_number, sequence_number);
+ }
+
+ if (pending_timer_transmission_count_ > 0) {
+ --pending_timer_transmission_count_;
+ }
+
+ if (unacked_packets_.bytes_in_flight() == 0) {
+ // TODO(ianswett): Consider being less aggressive to force a new
+ // recent_min_rtt, likely by not discarding a relatively new sample.
+ DVLOG(1) << "Sampling a new recent min rtt within 2 samples. currently:"
+ << rtt_stats_.recent_min_rtt().ToMilliseconds() << "ms";
+ rtt_stats_.SampleNewRecentMinRtt(kNumMinRttSamplesAfterQuiescence);
+ }
+
+ // Only track packets as in flight that the send algorithm wants us to track.
+ const bool in_flight =
+ send_algorithm_->OnPacketSent(sent_time,
+ unacked_packets_.bytes_in_flight(),
+ sequence_number,
+ bytes,
+ has_retransmittable_data);
+ unacked_packets_.SetSent(sequence_number, sent_time, bytes, in_flight);
+
+ // Reset the retransmission timer anytime a pending packet is sent.
+ return in_flight;
+}
+
+void QuicSentPacketManager::OnRetransmissionTimeout() {
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ DCHECK_EQ(0u, pending_timer_transmission_count_);
+ // Handshake retransmission, timer based loss detection, TLP, and RTO are
+ // implemented with a single alarm. The handshake alarm is set when the
+ // handshake has not completed, the loss alarm is set when the loss detection
+ // algorithm says to, and the TLP and RTO alarms are set after that.
+ // The TLP alarm is always set to run for under an RTO.
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ ++stats_->crypto_retransmit_count;
+ RetransmitCryptoPackets();
+ return;
+ case LOSS_MODE: {
+ ++stats_->loss_timeout_count;
+ QuicByteCount bytes_in_flight = unacked_packets_.bytes_in_flight();
+ InvokeLossDetection(clock_->Now());
+ MaybeInvokeCongestionEvent(false, bytes_in_flight);
+ return;
+ }
+ case TLP_MODE:
+ // If no tail loss probe can be sent, because there are no retransmittable
+ // packets, execute a conventional RTO to abandon old packets.
+ ++stats_->tlp_count;
+ ++consecutive_tlp_count_;
+ pending_timer_transmission_count_ = 1;
+ // TLPs prefer sending new data instead of retransmitting data, so
+ // give the connection a chance to write before completing the TLP.
+ return;
+ case RTO_MODE:
+ ++stats_->rto_count;
+ RetransmitAllPackets();
+ return;
+ }
+}
+
+void QuicSentPacketManager::RetransmitCryptoPackets() {
+ DCHECK_EQ(HANDSHAKE_MODE, GetRetransmissionMode());
+ // TODO(ianswett): Typical TCP implementations only retransmit 5 times.
+ consecutive_crypto_retransmission_count_ =
+ min(kMaxHandshakeRetransmissionBackoffs,
+ consecutive_crypto_retransmission_count_ + 1);
+ bool packet_retransmitted = false;
+ QuicPacketSequenceNumber sequence_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->in_flight || it->retransmittable_frames == nullptr ||
+ it->retransmittable_frames->HasCryptoHandshake() != IS_HANDSHAKE) {
+ continue;
+ }
+ packet_retransmitted = true;
+ MarkForRetransmission(sequence_number, HANDSHAKE_RETRANSMISSION);
+ ++pending_timer_transmission_count_;
+ }
+ DCHECK(packet_retransmitted) << "No crypto packets found to retransmit.";
+}
+
+bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() {
+ if (pending_timer_transmission_count_ == 0) {
+ return false;
+ }
+ QuicPacketSequenceNumber sequence_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ // Only retransmit frames which are in flight, and therefore have been sent.
+ if (!it->in_flight || it->retransmittable_frames == nullptr) {
+ continue;
+ }
+ if (!handshake_confirmed_) {
+ DCHECK_NE(IS_HANDSHAKE, it->retransmittable_frames->HasCryptoHandshake());
+ }
+ MarkForRetransmission(sequence_number, TLP_RETRANSMISSION);
+ return true;
+ }
+ DLOG(FATAL)
+ << "No retransmittable packets, so RetransmitOldestPacket failed.";
+ return false;
+}
+
+void QuicSentPacketManager::RetransmitAllPackets() {
+ DVLOG(1) << "RetransmitAllPackets() called with "
+ << unacked_packets_.GetNumUnackedPacketsDebugOnly()
+ << " unacked packets.";
+ // Request retransmission of all retransmittable packets when the RTO
+ // fires, and let the congestion manager decide how many to send
+ // immediately and the remaining packets will be queued.
+ // Abandon any non-retransmittable packets that are sufficiently old.
+ bool packets_retransmitted = false;
+ QuicPacketSequenceNumber sequence_number = unacked_packets_.GetLeastUnacked();
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ if (it->retransmittable_frames != nullptr) {
+ packets_retransmitted = true;
+ MarkForRetransmission(sequence_number, RTO_RETRANSMISSION);
+ } else {
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
+ }
+
+ send_algorithm_->OnRetransmissionTimeout(packets_retransmitted);
+ if (packets_retransmitted) {
+ if (consecutive_rto_count_ == 0) {
+ first_rto_transmission_ = unacked_packets_.largest_sent_packet() + 1;
+ }
+ ++consecutive_rto_count_;
+ }
+
+ if (network_change_visitor_ != nullptr) {
+ network_change_visitor_->OnCongestionWindowChange(GetCongestionWindow());
+ }
+}
+
+QuicSentPacketManager::RetransmissionTimeoutMode
+ QuicSentPacketManager::GetRetransmissionMode() const {
+ DCHECK(unacked_packets_.HasInFlightPackets());
+ if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) {
+ return HANDSHAKE_MODE;
+ }
+ if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) {
+ return LOSS_MODE;
+ }
+ if (consecutive_tlp_count_ < max_tail_loss_probes_) {
+ if (unacked_packets_.HasUnackedRetransmittableFrames()) {
+ return TLP_MODE;
+ }
+ }
+ return RTO_MODE;
+}
+
+void QuicSentPacketManager::OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame,
+ const QuicTime& feedback_receive_time) {
+ send_algorithm_->OnIncomingQuicCongestionFeedbackFrame(
+ frame, feedback_receive_time);
+}
+
+void QuicSentPacketManager::InvokeLossDetection(QuicTime time) {
+ SequenceNumberSet lost_packets =
+ loss_algorithm_->DetectLostPackets(unacked_packets_,
+ time,
+ unacked_packets_.largest_observed(),
+ rtt_stats_);
+ for (SequenceNumberSet::const_iterator it = lost_packets.begin();
+ it != lost_packets.end(); ++it) {
+ QuicPacketSequenceNumber sequence_number = *it;
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(sequence_number);
+ // TODO(ianswett): If it's expected the FEC packet may repair the loss, it
+ // should be recorded as a loss to the send algorithm, but not retransmitted
+ // until it's known whether the FEC packet arrived.
+ ++stats_->packets_lost;
+ packets_lost_.push_back(make_pair(sequence_number, transmission_info));
+ DVLOG(1) << ENDPOINT << "Lost packet " << sequence_number;
+
+ if (transmission_info.retransmittable_frames != nullptr) {
+ MarkForRetransmission(sequence_number, LOSS_RETRANSMISSION);
+ } else {
+ // Since we will not retransmit this, we need to remove it from
+ // unacked_packets_. This is either the current transmission of
+ // a packet whose previous transmission has been acked, a packet that has
+ // been TLP retransmitted, or an FEC packet.
+ unacked_packets_.RemoveFromInFlight(sequence_number);
+ }
+ }
+}
+
+bool QuicSentPacketManager::MaybeUpdateRTT(
+ const QuicAckFrame& ack_frame,
+ const QuicTime& ack_receive_time) {
+ if (!unacked_packets_.IsUnacked(ack_frame.largest_observed)) {
+ return false;
+ }
+ // We calculate the RTT based on the highest ACKed sequence number, the lower
+ // sequence numbers will include the ACK aggregation delay.
+ const TransmissionInfo& transmission_info =
+ unacked_packets_.GetTransmissionInfo(ack_frame.largest_observed);
+ // Ensure the packet has a valid sent time.
+ if (transmission_info.sent_time == QuicTime::Zero()) {
+ LOG(DFATAL) << "Acked packet has zero sent time, largest_observed:"
+ << ack_frame.largest_observed;
+ return false;
+ }
+
+ QuicTime::Delta send_delta =
+ ack_receive_time.Subtract(transmission_info.sent_time);
+ rtt_stats_.UpdateRtt(
+ send_delta, ack_frame.delta_time_largest_observed, ack_receive_time);
+ return true;
+}
+
+QuicTime::Delta QuicSentPacketManager::TimeUntilSend(
+ QuicTime now,
+ HasRetransmittableData retransmittable) {
+ // The TLP logic is entirely contained within QuicSentPacketManager, so the
+ // send algorithm does not need to be consulted.
+ if (pending_timer_transmission_count_ > 0) {
+ return QuicTime::Delta::Zero();
+ }
+ return send_algorithm_->TimeUntilSend(
+ now, unacked_packets_.bytes_in_flight(), retransmittable);
+}
+
+// Uses a 25ms delayed ack timer. Also helps with better signaling
+// in low-bandwidth (< ~384 kbps), where an ack is sent per packet.
+// Ensures that the Delayed Ack timer is always set to a value lesser
+// than the retransmission timer's minimum value (MinRTO). We want the
+// delayed ack to get back to the QUIC peer before the sender's
+// retransmission timer triggers. Since we do not know the
+// reverse-path one-way delay, we assume equal delays for forward and
+// reverse paths, and ensure that the timer is set to less than half
+// of the MinRTO.
+// There may be a value in making this delay adaptive with the help of
+// the sender and a signaling mechanism -- if the sender uses a
+// different MinRTO, we may get spurious retransmissions. May not have
+// any benefits, but if the delayed ack becomes a significant source
+// of (likely, tail) latency, then consider such a mechanism.
+const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() const {
+ return QuicTime::Delta::FromMilliseconds(min(kMaxDelayedAckTimeMs,
+ kMinRetransmissionTimeMs/2));
+}
+
+const QuicTime QuicSentPacketManager::GetRetransmissionTime() const {
+ // Don't set the timer if there are no packets in flight or we've already
+ // queued a tlp transmission and it hasn't been sent yet.
+ if (!unacked_packets_.HasInFlightPackets() ||
+ pending_timer_transmission_count_ > 0) {
+ return QuicTime::Zero();
+ }
+ switch (GetRetransmissionMode()) {
+ case HANDSHAKE_MODE:
+ return clock_->ApproximateNow().Add(GetCryptoRetransmissionDelay());
+ case LOSS_MODE:
+ return loss_algorithm_->GetLossTimeout();
+ case TLP_MODE: {
+ // TODO(ianswett): When CWND is available, it would be preferable to
+ // set the timer based on the earliest retransmittable packet.
+ // Base the updated timer on the send time of the last packet.
+ const QuicTime sent_time = unacked_packets_.GetLastPacketSentTime();
+ const QuicTime tlp_time = sent_time.Add(GetTailLossProbeDelay());
+ // Ensure the TLP timer never gets set to a time in the past.
+ return QuicTime::Max(clock_->ApproximateNow(), tlp_time);
+ }
+ case RTO_MODE: {
+ // The RTO is based on the first outstanding packet.
+ const QuicTime sent_time =
+ unacked_packets_.GetFirstInFlightPacketSentTime();
+ QuicTime rto_time = sent_time.Add(GetRetransmissionDelay());
+ // Wait for TLP packets to be acked before an RTO fires.
+ QuicTime tlp_time =
+ unacked_packets_.GetLastPacketSentTime().Add(GetTailLossProbeDelay());
+ return QuicTime::Max(tlp_time, rto_time);
+ }
+ }
+ DCHECK(false);
+ return QuicTime::Zero();
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay()
+ const {
+ // This is equivalent to the TailLossProbeDelay, but slightly more aggressive
+ // because crypto handshake messages don't incur a delayed ack time.
+ int64 delay_ms = max<int64>(kMinHandshakeTimeoutMs,
+ 1.5 * rtt_stats_.SmoothedRtt().ToMilliseconds());
+ return QuicTime::Delta::FromMilliseconds(
+ delay_ms << consecutive_crypto_retransmission_count_);
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay() const {
+ QuicTime::Delta srtt = rtt_stats_.SmoothedRtt();
+ if (!unacked_packets_.HasMultipleInFlightPackets()) {
+ return QuicTime::Delta::Max(
+ srtt.Multiply(2), srtt.Multiply(1.5)
+ .Add(QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2)));
+ }
+ return QuicTime::Delta::FromMilliseconds(
+ max(kMinTailLossProbeTimeoutMs,
+ static_cast<int64>(2 * srtt.ToMilliseconds())));
+}
+
+const QuicTime::Delta QuicSentPacketManager::GetRetransmissionDelay() const {
+ QuicTime::Delta retransmission_delay = send_algorithm_->RetransmissionDelay();
+ // TODO(rch): This code should move to |send_algorithm_|.
+ if (retransmission_delay.IsZero()) {
+ // We are in the initial state, use default timeout values.
+ retransmission_delay =
+ QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs);
+ } else if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) {
+ retransmission_delay =
+ QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs);
+ }
+
+ // Calculate exponential back off.
+ retransmission_delay = retransmission_delay.Multiply(
+ 1 << min<size_t>(consecutive_rto_count_, kMaxRetransmissions));
+
+ if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) {
+ return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs);
+ }
+ return retransmission_delay;
+}
+
+const RttStats* QuicSentPacketManager::GetRttStats() const {
+ return &rtt_stats_;
+}
+
+QuicBandwidth QuicSentPacketManager::BandwidthEstimate() const {
+ return send_algorithm_->BandwidthEstimate();
+}
+
+bool QuicSentPacketManager::HasReliableBandwidthEstimate() const {
+ return send_algorithm_->HasReliableBandwidthEstimate();
+}
+
+const QuicSustainedBandwidthRecorder&
+QuicSentPacketManager::SustainedBandwidthRecorder() const {
+ return sustained_bandwidth_recorder_;
+}
+
+QuicByteCount QuicSentPacketManager::GetCongestionWindow() const {
+ return send_algorithm_->GetCongestionWindow();
+}
+
+QuicByteCount QuicSentPacketManager::GetSlowStartThreshold() const {
+ return send_algorithm_->GetSlowStartThreshold();
+}
+
+void QuicSentPacketManager::EnablePacing() {
+ if (using_pacing_) {
+ return;
+ }
+
+ // Set up a pacing sender with a 5 millisecond alarm granularity.
+ using_pacing_ = true;
+ send_algorithm_.reset(
+ new PacingSender(send_algorithm_.release(),
+ QuicTime::Delta::FromMilliseconds(5),
+ kInitialUnpacedBurst));
+}
+
+} // namespace net
diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h
new file mode 100644
index 0000000..2b5f738
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager.h
@@ -0,0 +1,393 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
+#define NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
+
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/congestion_control/rtt_stats.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_ack_notifier_manager.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_sustained_bandwidth_recorder.h"
+#include "net/quic/quic_unacked_packet_map.h"
+
+namespace net {
+
+namespace test {
+class QuicConnectionPeer;
+class QuicSentPacketManagerPeer;
+} // namespace test
+
+class QuicClock;
+class QuicConfig;
+struct QuicConnectionStats;
+
+// Class which tracks the set of packets sent on a QUIC connection and contains
+// a send algorithm to decide when to send new packets. It keeps track of any
+// retransmittable data associated with each packet. If a packet is
+// retransmitted, it will keep track of each version of a packet so that if a
+// previous transmission is acked, the data will not be retransmitted.
+class NET_EXPORT_PRIVATE QuicSentPacketManager {
+ public:
+ // Interface which gets callbacks from the QuicSentPacketManager at
+ // interesting points. Implementations must not mutate the state of
+ // the packet manager or connection as a result of these callbacks.
+ class NET_EXPORT_PRIVATE DebugDelegate {
+ public:
+ virtual ~DebugDelegate() {}
+
+ // Called when a spurious retransmission is detected.
+ virtual void OnSpuriousPacketRetransmition(
+ TransmissionType transmission_type,
+ QuicByteCount byte_size) {}
+
+ virtual void OnSentPacket(
+ const SerializedPacket& packet,
+ QuicPacketSequenceNumber original_sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes,
+ TransmissionType transmission_type) {}
+
+ virtual void OnIncomingAck(
+ const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time,
+ QuicPacketSequenceNumber largest_observed,
+ bool largest_observed_acked,
+ QuicPacketSequenceNumber least_unacked_sent_packet) {}
+ };
+
+ // Interface which gets callbacks from the QuicSentPacketManager when
+ // network-related state changes. Implementations must not mutate the
+ // state of the packet manager as a result of these callbacks.
+ class NET_EXPORT_PRIVATE NetworkChangeVisitor {
+ public:
+ virtual ~NetworkChangeVisitor() {}
+
+ // Called when congestion window may have changed.
+ virtual void OnCongestionWindowChange(QuicByteCount congestion_window) = 0;
+ // TODO(jri): Add OnRttStatsChange() to this class as well.
+ };
+
+ // Struct to store the pending retransmission information.
+ struct PendingRetransmission {
+ PendingRetransmission(QuicPacketSequenceNumber sequence_number,
+ TransmissionType transmission_type,
+ const RetransmittableFrames& retransmittable_frames,
+ QuicSequenceNumberLength sequence_number_length)
+ : sequence_number(sequence_number),
+ transmission_type(transmission_type),
+ retransmittable_frames(retransmittable_frames),
+ sequence_number_length(sequence_number_length) {
+ }
+
+ QuicPacketSequenceNumber sequence_number;
+ TransmissionType transmission_type;
+ const RetransmittableFrames& retransmittable_frames;
+ QuicSequenceNumberLength sequence_number_length;
+ };
+
+ QuicSentPacketManager(bool is_server,
+ const QuicClock* clock,
+ QuicConnectionStats* stats,
+ CongestionControlType congestion_control_type,
+ LossDetectionType loss_type);
+ virtual ~QuicSentPacketManager();
+
+ virtual void SetFromConfig(const QuicConfig& config);
+
+ void SetHandshakeConfirmed() { handshake_confirmed_ = true; }
+
+ // Processes the incoming ack.
+ void OnIncomingAck(const QuicAckFrame& ack_frame,
+ QuicTime ack_receive_time);
+
+ // Returns true if the non-FEC packet |sequence_number| is unacked.
+ bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Requests retransmission of all unacked packets of |retransmission_type|.
+ void RetransmitUnackedPackets(TransmissionType retransmission_type);
+
+ // Retransmits the oldest pending packet there is still a tail loss probe
+ // pending. Invoked after OnRetransmissionTimeout.
+ bool MaybeRetransmitTailLossProbe();
+
+ // Removes the retransmittable frames from all unencrypted packets to ensure
+ // they don't get retransmitted.
+ void NeuterUnencryptedPackets();
+
+ // Returns true if the unacked packet |sequence_number| has retransmittable
+ // frames. This will only return false if the packet has been acked, if a
+ // previous transmission of this packet was ACK'd, or if this packet has been
+ // retransmitted as with different sequence number.
+ bool HasRetransmittableFrames(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if there are pending retransmissions.
+ bool HasPendingRetransmissions() const;
+
+ // Retrieves the next pending retransmission.
+ PendingRetransmission NextPendingRetransmission();
+
+ bool HasUnackedPackets() const;
+
+ // Returns the smallest sequence number of a serialized packet which has not
+ // been acked by the peer.
+ QuicPacketSequenceNumber GetLeastUnacked() const;
+
+ // Called when a congestion feedback frame is received from peer.
+ virtual void OnIncomingQuicCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame,
+ const QuicTime& feedback_receive_time);
+
+ // Called when we have sent bytes to the peer. This informs the manager both
+ // the number of bytes sent and if they were retransmitted. Returns true if
+ // the sender should reset the retransmission timer.
+ virtual bool OnPacketSent(SerializedPacket* serialized_packet,
+ QuicPacketSequenceNumber original_sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes,
+ TransmissionType transmission_type,
+ HasRetransmittableData has_retransmittable_data);
+
+ // Called when the retransmission timer expires.
+ virtual void OnRetransmissionTimeout();
+
+ // Calculate the time until we can send the next packet to the wire.
+ // Note 1: When kUnknownWaitTime is returned, there is no need to poll
+ // TimeUntilSend again until we receive an OnIncomingAckFrame event.
+ // Note 2: Send algorithms may or may not use |retransmit| in their
+ // calculations.
+ virtual QuicTime::Delta TimeUntilSend(QuicTime now,
+ HasRetransmittableData retransmittable);
+
+ // Returns amount of time for delayed ack timer.
+ const QuicTime::Delta DelayedAckTime() const;
+
+ // Returns the current delay for the retransmission timer, which may send
+ // either a tail loss probe or do a full RTO. Returns QuicTime::Zero() if
+ // there are no retransmittable packets.
+ const QuicTime GetRetransmissionTime() const;
+
+ const RttStats* GetRttStats() const;
+
+ // Returns the estimated bandwidth calculated by the congestion algorithm.
+ QuicBandwidth BandwidthEstimate() const;
+
+ // Returns true if the current instantaneous bandwidth estimate is reliable.
+ bool HasReliableBandwidthEstimate() const;
+
+ const QuicSustainedBandwidthRecorder& SustainedBandwidthRecorder() const;
+
+ // Returns the size of the current congestion window in bytes. Note, this is
+ // not the *available* window. Some send algorithms may not use a congestion
+ // window and will return 0.
+ QuicByteCount GetCongestionWindow() const;
+
+ // Returns the size of the slow start congestion window in bytes,
+ // aka ssthresh. Some send algorithms do not define a slow start
+ // threshold and will return 0.
+ QuicByteCount GetSlowStartThreshold() const;
+
+ // Enables pacing if it has not already been enabled.
+ void EnablePacing();
+
+ bool using_pacing() const { return using_pacing_; }
+
+ void set_debug_delegate(DebugDelegate* debug_delegate) {
+ debug_delegate_ = debug_delegate;
+ }
+
+ QuicPacketSequenceNumber largest_observed() const {
+ return unacked_packets_.largest_observed();
+ }
+
+ QuicPacketSequenceNumber least_packet_awaited_by_peer() {
+ return least_packet_awaited_by_peer_;
+ }
+
+ void set_network_change_visitor(NetworkChangeVisitor* visitor) {
+ DCHECK(!network_change_visitor_);
+ DCHECK(visitor);
+ network_change_visitor_ = visitor;
+ }
+
+ size_t consecutive_rto_count() const {
+ return consecutive_rto_count_;
+ }
+
+ size_t consecutive_tlp_count() const {
+ return consecutive_tlp_count_;
+ }
+
+ private:
+ friend class test::QuicConnectionPeer;
+ friend class test::QuicSentPacketManagerPeer;
+
+ // The retransmission timer is a single timer which switches modes depending
+ // upon connection state.
+ enum RetransmissionTimeoutMode {
+ // A conventional TCP style RTO.
+ RTO_MODE,
+ // A tail loss probe. By default, QUIC sends up to two before RTOing.
+ TLP_MODE,
+ // Retransmission of handshake packets prior to handshake completion.
+ HANDSHAKE_MODE,
+ // Re-invoke the loss detection when a packet is not acked before the
+ // loss detection algorithm expects.
+ LOSS_MODE,
+ };
+
+ typedef linked_hash_map<QuicPacketSequenceNumber,
+ TransmissionType> PendingRetransmissionMap;
+
+ // Called when a packet is retransmitted with a new sequence number.
+ // Replaces the old entry in the unacked packet map with the new
+ // sequence number.
+ void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number);
+
+ // Updates the least_packet_awaited_by_peer.
+ void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& ack_frame);
+
+ // Process the incoming ack looking for newly ack'd data packets.
+ void HandleAckForSentPackets(const QuicAckFrame& ack_frame);
+
+ // Returns the current retransmission mode.
+ RetransmissionTimeoutMode GetRetransmissionMode() const;
+
+ // Retransmits all crypto stream packets.
+ void RetransmitCryptoPackets();
+
+ // Retransmits all the packets and abandons by invoking a full RTO.
+ void RetransmitAllPackets();
+
+ // Returns the timer for retransmitting crypto handshake packets.
+ const QuicTime::Delta GetCryptoRetransmissionDelay() const;
+
+ // Returns the timer for a new tail loss probe.
+ const QuicTime::Delta GetTailLossProbeDelay() const;
+
+ // Returns the retransmission timeout, after which a full RTO occurs.
+ const QuicTime::Delta GetRetransmissionDelay() const;
+
+ // Update the RTT if the ack is for the largest acked sequence number.
+ // Returns true if the rtt was updated.
+ bool MaybeUpdateRTT(const QuicAckFrame& ack_frame,
+ const QuicTime& ack_receive_time);
+
+ // Invokes the loss detection algorithm and loses and retransmits packets if
+ // necessary.
+ void InvokeLossDetection(QuicTime time);
+
+ // Invokes OnCongestionEvent if |rtt_updated| is true, there are pending acks,
+ // or pending losses. Clears pending acks and pending losses afterwards.
+ // |bytes_in_flight| is the number of bytes in flight before the losses or
+ // acks.
+ void MaybeInvokeCongestionEvent(bool rtt_updated,
+ QuicByteCount bytes_in_flight);
+
+ // Marks |sequence_number| as having been revived by the peer, but not
+ // received, so the packet remains pending if it is and the congestion control
+ // does not consider the packet acked.
+ void MarkPacketRevived(QuicPacketSequenceNumber sequence_number,
+ QuicTime::Delta delta_largest_observed);
+
+ // Removes the retransmittability and pending properties from the packet at
+ // |it| due to receipt by the peer. Returns an iterator to the next remaining
+ // unacked packet.
+ void MarkPacketHandled(QuicPacketSequenceNumber sequence_number,
+ const TransmissionInfo& info,
+ QuicTime::Delta delta_largest_observed);
+
+ // Request that |sequence_number| be retransmitted after the other pending
+ // retransmissions. Does not add it to the retransmissions if it's already
+ // a pending retransmission.
+ void MarkForRetransmission(QuicPacketSequenceNumber sequence_number,
+ TransmissionType transmission_type);
+
+ // Notify observers about spurious retransmits.
+ void RecordSpuriousRetransmissions(
+ const SequenceNumberList& all_transmissions,
+ QuicPacketSequenceNumber acked_sequence_number);
+
+ // Returns true if the client is sending or the server has received a
+ // connection option.
+ bool HasClientSentConnectionOption(const QuicConfig& config,
+ QuicTag tag) const;
+
+ // Newly serialized retransmittable and fec packets are added to this map,
+ // which contains owning pointers to any contained frames. If a packet is
+ // retransmitted, this map will contain entries for both the old and the new
+ // packet. The old packet's retransmittable frames entry will be nullptr,
+ // while the new packet's entry will contain the frames to retransmit.
+ // If the old packet is acked before the new packet, then the old entry will
+ // be removed from the map and the new entry's retransmittable frames will be
+ // set to nullptr.
+ QuicUnackedPacketMap unacked_packets_;
+
+ // Pending retransmissions which have not been packetized and sent yet.
+ PendingRetransmissionMap pending_retransmissions_;
+
+ // Tracks if the connection was created by the server.
+ bool is_server_;
+
+ // An AckNotifier can register to be informed when ACKs have been received for
+ // all packets that a given block of data was sent in. The AckNotifierManager
+ // maintains the currently active notifiers.
+ AckNotifierManager ack_notifier_manager_;
+
+ const QuicClock* clock_;
+ QuicConnectionStats* stats_;
+ DebugDelegate* debug_delegate_;
+ NetworkChangeVisitor* network_change_visitor_;
+ RttStats rtt_stats_;
+ scoped_ptr<SendAlgorithmInterface> send_algorithm_;
+ scoped_ptr<LossDetectionInterface> loss_algorithm_;
+
+ // Least sequence number which the peer is still waiting for.
+ QuicPacketSequenceNumber least_packet_awaited_by_peer_;
+
+ // Tracks the first RTO packet. If any packet before that packet gets acked,
+ // it indicates the RTO was spurious and should be reversed(F-RTO).
+ QuicPacketSequenceNumber first_rto_transmission_;
+ // Number of times the RTO timer has fired in a row without receiving an ack.
+ size_t consecutive_rto_count_;
+ // Number of times the tail loss probe has been sent.
+ size_t consecutive_tlp_count_;
+ // Number of times the crypto handshake has been retransmitted.
+ size_t consecutive_crypto_retransmission_count_;
+ // Number of pending transmissions of TLP or crypto packets.
+ size_t pending_timer_transmission_count_;
+ // Maximum number of tail loss probes to send before firing an RTO.
+ size_t max_tail_loss_probes_;
+ bool using_pacing_;
+
+ // Vectors packets acked and lost as a result of the last congestion event.
+ SendAlgorithmInterface::CongestionVector packets_acked_;
+ SendAlgorithmInterface::CongestionVector packets_lost_;
+
+ // Set to true after the crypto handshake has successfully completed. After
+ // this is true we no longer use HANDSHAKE_MODE, and further frames sent on
+ // the crypto stream (i.e. SCUP messages) are treated like normal
+ // retransmittable frames.
+ bool handshake_confirmed_;
+
+ // Records bandwidth from server to client in normal operation, over periods
+ // of time with no loss events.
+ QuicSustainedBandwidthRecorder sustained_bandwidth_recorder_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SENT_PACKET_MANAGER_H_
diff --git a/net/quic/quic_sent_packet_manager_test.cc b/net/quic/quic_sent_packet_manager_test.cc
new file mode 100644
index 0000000..42c9a6f
--- /dev/null
+++ b/net/quic/quic_sent_packet_manager_test.cc
@@ -0,0 +1,1390 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sent_packet_manager.h"
+
+#include "base/stl_util.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::vector;
+using testing::AnyNumber;
+using testing::ElementsAre;
+using testing::Pair;
+using testing::Pointwise;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32 kDefaultLength = 1000;
+
+// Matcher to check the key of the key-value pair it receives as first argument
+// equals its second argument.
+MATCHER(KeyEq, "") {
+ return std::tr1::get<0>(arg).first == std::tr1::get<1>(arg);
+}
+
+class MockDebugDelegate : public QuicSentPacketManager::DebugDelegate {
+ public:
+ MOCK_METHOD2(OnSpuriousPacketRetransmition,
+ void(TransmissionType transmission_type,
+ QuicByteCount byte_size));
+};
+
+class QuicSentPacketManagerTest : public ::testing::TestWithParam<bool> {
+ protected:
+ QuicSentPacketManagerTest()
+ : manager_(true, &clock_, &stats_, kCubic, kNack),
+ send_algorithm_(new StrictMock<MockSendAlgorithm>),
+ network_change_visitor_(new StrictMock<MockNetworkChangeVisitor>) {
+ QuicSentPacketManagerPeer::SetSendAlgorithm(&manager_, send_algorithm_);
+ // Disable tail loss probes for most tests.
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 0);
+ // Advance the time 1s so the send times are never QuicTime::Zero.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+ manager_.set_network_change_visitor(network_change_visitor_.get());
+
+ EXPECT_CALL(*send_algorithm_, HasReliableBandwidthEstimate())
+ .Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, BandwidthEstimate())
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(QuicBandwidth::Zero()));
+ EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber());
+ EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber());
+ }
+
+ virtual ~QuicSentPacketManagerTest() OVERRIDE {
+ STLDeleteElements(&packets_);
+ }
+
+ QuicByteCount BytesInFlight() {
+ return QuicSentPacketManagerPeer::GetBytesInFlight(&manager_);
+ }
+ void VerifyUnackedPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ if (num_packets == 0) {
+ EXPECT_FALSE(manager_.HasUnackedPackets());
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
+ return;
+ }
+
+ EXPECT_TRUE(manager_.HasUnackedPackets());
+ EXPECT_EQ(packets[0], manager_.GetLeastUnacked());
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(manager_.IsUnacked(packets[i])) << packets[i];
+ }
+ }
+
+ void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ EXPECT_EQ(num_packets,
+ QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ &manager_));
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(manager_.HasRetransmittableFrames(packets[i]))
+ << " packets[" << i << "]:" << packets[i];
+ }
+ }
+
+ void ExpectAck(QuicPacketSequenceNumber largest_observed) {
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(
+ true, _, ElementsAre(Pair(largest_observed, _)), _));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ }
+
+ void ExpectUpdatedRtt(QuicPacketSequenceNumber largest_observed) {
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(true, _, _, _));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ }
+
+ void ExpectAckAndLoss(bool rtt_updated,
+ QuicPacketSequenceNumber largest_observed,
+ QuicPacketSequenceNumber lost_packet) {
+ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(
+ rtt_updated, _, ElementsAre(Pair(largest_observed, _)),
+ ElementsAre(Pair(lost_packet, _))));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ }
+
+ // |packets_acked| and |packets_lost| should be in sequence number order.
+ void ExpectAcksAndLosses(bool rtt_updated,
+ QuicPacketSequenceNumber* packets_acked,
+ size_t num_packets_acked,
+ QuicPacketSequenceNumber* packets_lost,
+ size_t num_packets_lost) {
+ vector<QuicPacketSequenceNumber> ack_vector;
+ for (size_t i = 0; i < num_packets_acked; ++i) {
+ ack_vector.push_back(packets_acked[i]);
+ }
+ vector<QuicPacketSequenceNumber> lost_vector;
+ for (size_t i = 0; i < num_packets_lost; ++i) {
+ lost_vector.push_back(packets_lost[i]);
+ }
+ EXPECT_CALL(*send_algorithm_,
+ OnCongestionEvent(rtt_updated, _,
+ Pointwise(KeyEq(), ack_vector),
+ Pointwise(KeyEq(), lost_vector)));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillRepeatedly(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_)).
+ Times(AnyNumber());
+ }
+
+ void RetransmitAndSendPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number) {
+ QuicSentPacketManagerPeer::MarkForRetransmission(
+ &manager_, old_sequence_number, TLP_RETRANSMISSION);
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ QuicSentPacketManager::PendingRetransmission next_retransmission =
+ manager_.NextPendingRetransmission();
+ EXPECT_EQ(old_sequence_number, next_retransmission.sequence_number);
+ EXPECT_EQ(TLP_RETRANSMISSION,
+ next_retransmission.transmission_type);
+
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), new_sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
+ .WillOnce(Return(true));
+ SerializedPacket packet(CreatePacket(new_sequence_number, false));
+ manager_.OnPacketSent(&packet,
+ old_sequence_number,
+ clock_.Now(),
+ kDefaultLength,
+ LOSS_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(
+ &manager_, new_sequence_number));
+ }
+
+ SerializedPacket CreateDataPacket(QuicPacketSequenceNumber sequence_number) {
+ return CreatePacket(sequence_number, true);
+ }
+
+ SerializedPacket CreatePacket(QuicPacketSequenceNumber sequence_number,
+ bool retransmittable) {
+ packets_.push_back(QuicPacket::NewDataPacket(
+ nullptr, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false,
+ PACKET_6BYTE_SEQUENCE_NUMBER));
+ return SerializedPacket(
+ sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER, packets_.back(), 0u,
+ retransmittable ? new RetransmittableFrames() : nullptr);
+ }
+
+ SerializedPacket CreateFecPacket(QuicPacketSequenceNumber sequence_number) {
+ packets_.push_back(QuicPacket::NewFecPacket(
+ nullptr, kDefaultLength, false, PACKET_8BYTE_CONNECTION_ID, false,
+ PACKET_6BYTE_SEQUENCE_NUMBER));
+ return SerializedPacket(sequence_number, PACKET_6BYTE_SEQUENCE_NUMBER,
+ packets_.back(), 0u, nullptr);
+ }
+
+ void SendDataPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number, _, _))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateDataPacket(sequence_number));
+ manager_.OnPacketSent(&packet, 0, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void SendCryptoPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateDataPacket(sequence_number));
+ packet.retransmittable_frames->AddStreamFrame(
+ new QuicStreamFrame(1, false, 0, IOVector()));
+ packet.retransmittable_frames->set_encryption_level(ENCRYPTION_NONE);
+ manager_.OnPacketSent(&packet, 0, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ void SendFecPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, NO_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(true));
+ SerializedPacket packet(CreateFecPacket(sequence_number));
+ manager_.OnPacketSent(&packet, 0, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
+ void SendAckPacket(QuicPacketSequenceNumber sequence_number) {
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, BytesInFlight(), sequence_number,
+ kDefaultLength, NO_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(false));
+ SerializedPacket packet(CreatePacket(sequence_number, false));
+ manager_.OnPacketSent(&packet, 0, clock_.Now(),
+ packet.packet->length(), NOT_RETRANSMISSION,
+ NO_RETRANSMITTABLE_DATA);
+ }
+
+ // Based on QuicConnection's WritePendingRetransmissions.
+ void RetransmitNextPacket(
+ QuicPacketSequenceNumber retransmission_sequence_number) {
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_CALL(*send_algorithm_,
+ OnPacketSent(_, _, retransmission_sequence_number,
+ kDefaultLength, HAS_RETRANSMITTABLE_DATA))
+ .Times(1).WillOnce(Return(true));
+ const QuicSentPacketManager::PendingRetransmission pending =
+ manager_.NextPendingRetransmission();
+ SerializedPacket packet(
+ CreatePacket(retransmission_sequence_number, false));
+ manager_.OnPacketSent(&packet, pending.sequence_number, clock_.Now(),
+ kDefaultLength, pending.transmission_type,
+ HAS_RETRANSMITTABLE_DATA);
+ }
+
+ QuicSentPacketManager manager_;
+ vector<QuicPacket*> packets_;
+ MockClock clock_;
+ QuicConnectionStats stats_;
+ MockSendAlgorithm* send_algorithm_;
+ scoped_ptr<MockNetworkChangeVisitor> network_change_visitor_;
+};
+
+TEST_F(QuicSentPacketManagerTest, IsUnacked) {
+ VerifyUnackedPackets(nullptr, 0);
+ SendDataPacket(1);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 1 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+}
+
+TEST_F(QuicSentPacketManagerTest, IsUnAckedRetransmit) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ EXPECT_TRUE(QuicSentPacketManagerPeer::IsRetransmission(&manager_, 2));
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 2 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitThenAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Ack 2 but not 1.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.missing_packets.insert(1);
+ ExpectAck(2);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ // Packet 1 is unacked, pending, but not retransmittable.
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitThenAckBeforeSend) {
+ SendDataPacket(1);
+ QuicSentPacketManagerPeer::MarkForRetransmission(
+ &manager_, 1, TLP_RETRANSMISSION);
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+
+ // Ack 1.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 1;
+ ExpectAck(1);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ // There should no longer be a pending retransmission.
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // No unacked packets remain.
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+ EXPECT_EQ(0u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPrevious) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // Ack 1 but not 2.
+ ExpectAck(1);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 1;
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ // 2 remains unacked, but no packets have retransmittable data.
+ QuicPacketSequenceNumber unacked[] = { 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ EXPECT_EQ(1u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitThenAckPreviousThenNackRetransmit) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // First, ACK packet 1 which makes packet 2 non-retransmittable.
+ ExpectAck(1);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 1;
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendDataPacket(5);
+ clock_.AdvanceTime(rtt);
+
+ // Next, NACK packet 2 three times.
+ ack_frame.largest_observed = 3;
+ ack_frame.missing_packets.insert(2);
+ ExpectAck(3);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ ack_frame.largest_observed = 4;
+ ExpectAck(4);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ ack_frame.largest_observed = 5;
+ ExpectAckAndLoss(true, 5, 2);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ // No packets remain unacked.
+ VerifyUnackedPackets(nullptr, 0);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckPreviousBeforeSend) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+
+ // Fire the RTO, which will mark 2 for retransmission (but will not send it).
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(2 * kDefaultTCPMSS));
+ manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+
+ // Ack 1 but not 2, before 2 is able to be sent.
+ // Since 1 has been retransmitted, it has already been lost, and so the
+ // send algorithm is not informed that it has been ACK'd.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 1;
+ ExpectUpdatedRtt(1);
+ EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ // Since 2 was marked for retransmit, when 1 is acked, 2 is kept for RTT.
+ QuicPacketSequenceNumber unacked[] = { 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Verify that the retransmission alarm would not fire,
+ // since there is no retransmittable data outstanding.
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmitTwiceThenAckFirst) {
+ StrictMock<MockDebugDelegate> debug_delegate;
+ EXPECT_CALL(debug_delegate, OnSpuriousPacketRetransmition(
+ TLP_RETRANSMISSION, kDefaultLength)).Times(2);
+ manager_.set_debug_delegate(&debug_delegate);
+
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(15);
+ clock_.AdvanceTime(rtt);
+
+ // Ack 1 but not 2 or 3.
+ ExpectAck(1);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 1;
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ // 2 and 3 remain unacked, but no packets have retransmittable data.
+ QuicPacketSequenceNumber unacked[] = { 2, 3 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Ensure packet 2 is lost when 4 is sent and 3 and 4 are acked.
+ SendDataPacket(4);
+ ack_frame.largest_observed = 4;
+ ack_frame.missing_packets.insert(2);
+ QuicPacketSequenceNumber acked[] = { 3, 4 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), nullptr, 0);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ QuicPacketSequenceNumber unacked2[] = { 2 };
+ VerifyUnackedPackets(unacked2, arraysize(unacked2));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ SendDataPacket(5);
+ ack_frame.largest_observed = 5;
+ ExpectAckAndLoss(true, 5, 2);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ VerifyUnackedPackets(nullptr, 0);
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.packets_spuriously_retransmitted);
+}
+
+TEST_F(QuicSentPacketManagerTest, LoseButDontRetransmitRevivedPacket) {
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendFecPacket(3);
+ SendDataPacket(4);
+
+ // Ack 2 and 3, and mark 1 as revived.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 3;
+ ack_frame.missing_packets.insert(1);
+ ack_frame.revived_packets.insert(1);
+ QuicPacketSequenceNumber acked[] = { 2, 3 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), nullptr, 0);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ QuicPacketSequenceNumber unacked[] = { 1, 4 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ QuicPacketSequenceNumber retransmittable[] = { 4 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ // Ack the 4th packet and expect the 1st to be considered lost.
+ ack_frame.largest_observed = 4;
+ ExpectAckAndLoss(true, 4, 1);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicSentPacketManagerTest, MarkLostThenReviveAndDontRetransmitPacket) {
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendDataPacket(3);
+ SendDataPacket(4);
+ SendFecPacket(5);
+
+ // Ack 2, 3, and 4, and expect the 1st to be considered lost.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 4;
+ ack_frame.missing_packets.insert(1);
+ QuicPacketSequenceNumber acked[] = { 2, 3, 4 };
+ QuicPacketSequenceNumber lost[] = { 1 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), lost, arraysize(lost));
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ QuicPacketSequenceNumber unacked[] = { 1, 5 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 1 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ // Ack 5th packet (FEC) and revive 1st packet. 1st packet should now be
+ // removed from pending retransmissions map.
+ ack_frame.largest_observed = 5;
+ ack_frame.revived_packets.insert(1);
+ ExpectAck(5);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicSentPacketManagerTest, TruncatedAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ RetransmitAndSendPacket(3, 4);
+ RetransmitAndSendPacket(4, 5);
+
+ // Truncated ack with 4 NACKs, so the first packet is lost.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 4;
+ ack_frame.missing_packets.insert(1);
+ ack_frame.missing_packets.insert(2);
+ ack_frame.missing_packets.insert(3);
+ ack_frame.missing_packets.insert(4);
+ ack_frame.is_truncated = true;
+
+ QuicPacketSequenceNumber lost[] = { 1 };
+ ExpectAcksAndLosses(true, nullptr, 0, lost, arraysize(lost));
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ // High water mark will be raised.
+ QuicPacketSequenceNumber unacked[] = { 2, 3, 4, 5 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 5 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+}
+
+TEST_F(QuicSentPacketManagerTest, AckPreviousTransmissionThenTruncatedAck) {
+ SendDataPacket(1);
+ RetransmitAndSendPacket(1, 2);
+ RetransmitAndSendPacket(2, 3);
+ RetransmitAndSendPacket(3, 4);
+ SendDataPacket(5);
+ SendDataPacket(6);
+ SendDataPacket(7);
+ SendDataPacket(8);
+ SendDataPacket(9);
+
+ // Ack previous transmission
+ {
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.missing_packets.insert(1);
+ ExpectAck(2);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+ EXPECT_TRUE(manager_.IsUnacked(4));
+ }
+
+ // Truncated ack with 4 NACKs
+ {
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 6;
+ ack_frame.missing_packets.insert(3);
+ ack_frame.missing_packets.insert(4);
+ ack_frame.missing_packets.insert(5);
+ ack_frame.missing_packets.insert(6);
+ ack_frame.is_truncated = true;
+ ExpectAckAndLoss(true, 1, 3);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+ }
+
+ // High water mark will be raised.
+ QuicPacketSequenceNumber unacked[] = { 4, 5, 6, 7, 8, 9 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 5, 6, 7, 8, 9 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnacked) {
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedUnacked) {
+ SendDataPacket(1);
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedUnackedFec) {
+ SendFecPacket(1);
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLeastUnackedAndDiscard) {
+ VerifyUnackedPackets(nullptr, 0);
+
+ SendFecPacket(1);
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+
+ SendFecPacket(2);
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+
+ SendFecPacket(3);
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2, 3 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ // Ack 2, so there's an rtt update.
+ ExpectAck(2);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.missing_packets.insert(1);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ EXPECT_EQ(1u, manager_.GetLeastUnacked());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetSentTime) {
+ VerifyUnackedPackets(nullptr, 0);
+
+ QuicTime sent_time = clock_.Now();
+ SendFecPacket(1);
+ QuicTime sent_time2 = clock_.Now();
+ SendFecPacket(2);
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ EXPECT_TRUE(manager_.HasUnackedPackets());
+ EXPECT_EQ(sent_time, QuicSentPacketManagerPeer::GetSentTime(&manager_, 1));
+ EXPECT_EQ(sent_time2, QuicSentPacketManagerPeer::GetSentTime(&manager_, 2));
+}
+
+TEST_F(QuicSentPacketManagerTest, AckAckAndUpdateRtt) {
+ SendDataPacket(1);
+ SendAckPacket(2);
+
+ // Now ack the ack and expect an RTT update.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.delta_time_largest_observed =
+ QuicTime::Delta::FromMilliseconds(5);
+
+ ExpectAck(1);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ SendAckPacket(3);
+
+ // Now ack the ack and expect only an RTT update.
+ ack_frame.largest_observed = 3;
+ ExpectUpdatedRtt(3);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+}
+
+TEST_F(QuicSentPacketManagerTest, Rtt) {
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
+
+ ExpectAck(sequence_number);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = sequence_number;
+ ack_frame.delta_time_largest_observed =
+ QuicTime::Delta::FromMilliseconds(5);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttWithInvalidDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // delta_time_largest_observed is larger than the local time elapsed
+ // and is hence invalid.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(sequence_number);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = sequence_number;
+ ack_frame.delta_time_largest_observed =
+ QuicTime::Delta::FromMilliseconds(11);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttWithInfiniteDelta) {
+ // Expect that the RTT is equal to the local time elapsed, since the
+ // delta_time_largest_observed is infinite, and is hence invalid.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(sequence_number);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = sequence_number;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Infinite();
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RttZeroDelta) {
+ // Expect that the RTT is the time between send and receive since the
+ // delta_time_largest_observed is zero.
+ QuicPacketSequenceNumber sequence_number = 1;
+ QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10);
+ SendDataPacket(sequence_number);
+ clock_.AdvanceTime(expected_rtt);
+
+ ExpectAck(sequence_number);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = sequence_number;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+ EXPECT_EQ(expected_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->latest_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, TailLossProbeTimeout) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+ // Send 1 packet.
+ QuicPacketSequenceNumber sequence_number = 1;
+ SendDataPacket(sequence_number);
+
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(2);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // Ack the third and ensure the first two are still pending.
+ ExpectAck(3);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 3;
+ ack_frame.missing_packets.insert(1);
+ ack_frame.missing_packets.insert(2);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Acking two more packets will lose both of them due to nacks.
+ ack_frame.largest_observed = 5;
+ QuicPacketSequenceNumber lost[] = { 1, 2 };
+ ExpectAcksAndLosses(false, nullptr, 0, lost, arraysize(lost));
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(0u, stats_.rto_count);
+}
+
+TEST_F(QuicSentPacketManagerTest, TailLossProbeThenRTO) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+
+ // Send 100 packets.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ // The first tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ manager_.MaybeRetransmitTailLossProbe();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(101);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ // The second tail loss probe retransmits 1 packet.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(102);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+
+ // Advance the time enough to ensure all packets are RTO'd.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1000));
+
+ // The final RTO abandons all of them.
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(2 * kDefaultTCPMSS));
+ manager_.OnRetransmissionTimeout();
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(2u, stats_.tlp_count);
+ EXPECT_EQ(1u, stats_.rto_count);
+}
+
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeout) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmits 2 packets.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The second retransmits 2 packets.
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ RetransmitNextPacket(8);
+ RetransmitNextPacket(9);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now ack the two crypto packets and the speculatively encrypted request,
+ // and ensure the first four crypto packets get abandoned, but not lost.
+ QuicPacketSequenceNumber acked[] = { 3, 4, 5, 8, 9 };
+ ExpectAcksAndLosses(true, acked, arraysize(acked), nullptr, 0);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 9;
+ ack_frame.missing_packets.insert(1);
+ ack_frame.missing_packets.insert(2);
+ ack_frame.missing_packets.insert(6);
+ ack_frame.missing_packets.insert(7);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
+
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutVersionNegotiation) {
+ // Send 2 crypto packets and 3 data packets.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ const size_t kNumSentDataPackets = 3;
+ for (size_t i = 1; i <= kNumSentDataPackets; ++i) {
+ SendDataPacket(kNumSentCryptoPackets + i);
+ }
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // The first retransmission timeout retransmits 2 crypto packets.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(6);
+ RetransmitNextPacket(7);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Now act like a version negotiation packet arrived, which would cause all
+ // unacked packets to be retransmitted.
+ manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+
+ // Ensure the first two pending packets are the crypto retransmits.
+ ASSERT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(6u, manager_.NextPendingRetransmission().sequence_number);
+ RetransmitNextPacket(8);
+ EXPECT_EQ(7u, manager_.NextPendingRetransmission().sequence_number);
+ RetransmitNextPacket(9);
+
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ // Send 3 more data packets and ensure the least unacked is raised.
+ RetransmitNextPacket(10);
+ RetransmitNextPacket(11);
+ RetransmitNextPacket(12);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ EXPECT_EQ(8u, manager_.GetLeastUnacked());
+}
+
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeSpuriousRetransmission) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
+
+ // Retransmit the crypto packet as 3.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+
+ // Now ack the second crypto packet, and ensure the first gets removed, but
+ // the third does not.
+ ExpectUpdatedRtt(2);
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.missing_packets.insert(1);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ QuicPacketSequenceNumber unacked[] = { 3 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+}
+
+TEST_F(QuicSentPacketManagerTest, CryptoHandshakeTimeoutUnsentDataPacket) {
+ // Send 2 crypto packets and 1 data packet.
+ const size_t kNumSentCryptoPackets = 2;
+ for (size_t i = 1; i <= kNumSentCryptoPackets; ++i) {
+ SendCryptoPacket(i);
+ }
+ SendDataPacket(3);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit 2 crypto packets, but not the serialized packet.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(4);
+ RetransmitNextPacket(5);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+}
+
+TEST_F(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenRetransmitAll) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
+
+ // Now retransmit all the unacked packets, which occurs when there is a
+ // version negotiation.
+ manager_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION);
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+}
+
+TEST_F(QuicSentPacketManagerTest,
+ CryptoHandshakeRetransmissionThenNeuterAndAck) {
+ // Send 1 crypto packet.
+ SendCryptoPacket(1);
+ EXPECT_TRUE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+
+ // Retransmit the crypto packet as 2.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
+
+ // Retransmit the crypto packet as 3.
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(3);
+
+ // Now neuter all unacked unencrypted packets, which occurs when the
+ // connection goes forward secure.
+ manager_.NeuterUnencryptedPackets();
+ QuicPacketSequenceNumber unacked[] = { 1, 2, 3};
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasUnackedCryptoPackets(&manager_));
+ EXPECT_FALSE(QuicSentPacketManagerPeer::HasPendingPackets(&manager_));
+
+ // Ensure both packets get discarded when packet 2 is acked.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 3;
+ ack_frame.missing_packets.insert(1);
+ ack_frame.missing_packets.insert(2);
+ ExpectUpdatedRtt(3);
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicSentPacketManagerTest, ResetRecentMinRTTWithEmptyWindow) {
+ QuicTime::Delta min_rtt = QuicTime::Delta::FromMilliseconds(50);
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt(
+ min_rtt, QuicTime::Delta::Zero(), QuicTime::Zero());
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt());
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+
+ // Send two packets with no prior bytes in flight.
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+ // Ack two packets with 100ms RTT observations.
+ QuicAckFrame ack_frame;
+ ack_frame.delta_time_largest_observed = QuicTime::Delta::Zero();
+ ack_frame.largest_observed = 1;
+ ExpectAck(1);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ // First ack does not change recent min rtt.
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+
+ ack_frame.largest_observed = 2;
+ ExpectAck(2);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ EXPECT_EQ(min_rtt,
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->min_rtt());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(100),
+ QuicSentPacketManagerPeer::GetRttStats(
+ &manager_)->recent_min_rtt());
+}
+
+TEST_F(QuicSentPacketManagerTest, RetransmissionTimeout) {
+ // Send 100 packets and then ensure all are abandoned when the RTO fires.
+ const size_t kNumSentPackets = 100;
+ for (size_t i = 1; i <= kNumSentPackets; ++i) {
+ SendDataPacket(i);
+ }
+
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(2 * kDefaultTCPMSS));
+ EXPECT_FALSE(manager_.MaybeRetransmitTailLossProbe());
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTime) {
+ EXPECT_EQ(QuicTime::Zero(), manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeCryptoHandshake) {
+ SendCryptoPacket(1);
+
+ // Check the min.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 1 * base::Time::kMicrosecondsPerMillisecond);
+ EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 100 * base::Time::kMicrosecondsPerMillisecond);
+
+ QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt();
+ QuicTime expected_time = clock_.Now().Add(srtt.Multiply(1.5));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(srtt.Multiply(1.5));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(2);
+
+ // The retransmission time should now be twice as far in the future.
+ expected_time = clock_.Now().Add(srtt.Multiply(2).Multiply(1.5));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeTailLossProbe) {
+ QuicSentPacketManagerPeer::SetMaxTailLossProbes(&manager_, 2);
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ // Check the min.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 1 * base::Time::kMicrosecondsPerMillisecond);
+ EXPECT_EQ(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)),
+ manager_.GetRetransmissionTime());
+
+ // Test with a standard smoothed RTT.
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->set_initial_rtt_us(
+ 100 * base::Time::kMicrosecondsPerMillisecond);
+ QuicTime::Delta srtt = manager_.GetRttStats()->SmoothedRtt();
+ QuicTime::Delta expected_tlp_delay = srtt.Multiply(2);
+ QuicTime expected_time = clock_.Now().Add(expected_tlp_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ clock_.AdvanceTime(expected_tlp_delay);
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(QuicTime::Delta::Zero(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_TRUE(manager_.MaybeRetransmitTailLossProbe());
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+ RetransmitNextPacket(3);
+ EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ EXPECT_EQ(QuicTime::Delta::Infinite(),
+ manager_.TimeUntilSend(clock_.Now(), HAS_RETRANSMITTABLE_DATA));
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+
+ expected_time = clock_.Now().Add(expected_tlp_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionTimeRTO) {
+ QuicSentPacketManagerPeer::GetRttStats(&manager_)->UpdateRtt(
+ QuicTime::Delta::FromMilliseconds(100),
+ QuicTime::Delta::Zero(),
+ QuicTime::Zero());
+
+ SendDataPacket(1);
+ SendDataPacket(2);
+ SendDataPacket(3);
+ SendDataPacket(4);
+
+ QuicTime::Delta expected_rto_delay = QuicTime::Delta::FromMilliseconds(500);
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
+ .WillRepeatedly(Return(expected_rto_delay));
+ QuicTime expected_time = clock_.Now().Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Retransmit the packet by invoking the retransmission timeout.
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(2 * kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ clock_.AdvanceTime(expected_rto_delay);
+ manager_.OnRetransmissionTimeout();
+ EXPECT_EQ(0u, QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ RetransmitNextPacket(5);
+ RetransmitNextPacket(6);
+ EXPECT_EQ(2 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+ EXPECT_TRUE(manager_.HasPendingRetransmissions());
+
+ // The delay should double the second time.
+ expected_time = clock_.Now().Add(expected_rto_delay).Add(expected_rto_delay);
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+
+ // Ack a packet and ensure the RTO goes back to the original value.
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.missing_packets.insert(1);
+ ExpectUpdatedRtt(2);
+ EXPECT_CALL(*send_algorithm_, RevertRetransmissionTimeout());
+ manager_.OnIncomingAck(ack_frame, clock_.ApproximateNow());
+ EXPECT_FALSE(manager_.HasPendingRetransmissions());
+ EXPECT_EQ(4 * kDefaultLength,
+ QuicSentPacketManagerPeer::GetBytesInFlight(&manager_));
+
+ // Wait 2RTTs from now for the RTO, since it's the max of the RTO time
+ // and the TLP time. In production, there would always be two TLP's first.
+ expected_time = clock_.Now().Add(QuicTime::Delta::FromMilliseconds(200));
+ EXPECT_EQ(expected_time, manager_.GetRetransmissionTime());
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMin) {
+ SendDataPacket(1);
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
+ .WillRepeatedly(Return(QuicTime::Delta::FromMilliseconds(1)));
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(200);
+
+ // If the delay is smaller than the min, ensure it exponentially backs off
+ // from the min.
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ delay = delay.Add(delay);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(2 * kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(i + 2);
+ }
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionDelayMax) {
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
+ .WillOnce(Return(QuicTime::Delta::FromSeconds(500)));
+
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(60),
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+}
+
+TEST_F(QuicSentPacketManagerTest, GetTransmissionDelay) {
+ SendDataPacket(1);
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(500);
+ EXPECT_CALL(*send_algorithm_, RetransmissionDelay())
+ .WillRepeatedly(Return(delay));
+
+ // Delay should back off exponentially.
+ for (int i = 0; i < 5; ++i) {
+ EXPECT_EQ(delay,
+ QuicSentPacketManagerPeer::GetRetransmissionDelay(&manager_));
+ delay = delay.Add(delay);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(2 * kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, OnRetransmissionTimeout(true));
+ manager_.OnRetransmissionTimeout();
+ RetransmitNextPacket(i + 2);
+ }
+}
+
+TEST_F(QuicSentPacketManagerTest, GetLossDelay) {
+ MockLossAlgorithm* loss_algorithm = new MockLossAlgorithm();
+ QuicSentPacketManagerPeer::SetLossAlgorithm(&manager_, loss_algorithm);
+
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(QuicTime::Zero()));
+ SendDataPacket(1);
+ SendDataPacket(2);
+
+ // Handle an ack which causes the loss algorithm to be evaluated and
+ // set the loss timeout.
+ ExpectAck(2);
+ EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ QuicAckFrame ack_frame;
+ ack_frame.largest_observed = 2;
+ ack_frame.missing_packets.insert(1);
+ manager_.OnIncomingAck(ack_frame, clock_.Now());
+
+ QuicTime timeout(clock_.Now().Add(QuicTime::Delta::FromMilliseconds(10)));
+ EXPECT_CALL(*loss_algorithm, GetLossTimeout())
+ .WillRepeatedly(Return(timeout));
+ EXPECT_EQ(timeout, manager_.GetRetransmissionTime());
+
+ // Fire the retransmission timeout and ensure the loss detection algorithm
+ // is invoked.
+ EXPECT_CALL(*loss_algorithm, DetectLostPackets(_, _, _, _))
+ .WillOnce(Return(SequenceNumberSet()));
+ manager_.OnRetransmissionTimeout();
+}
+
+TEST_F(QuicSentPacketManagerTest, NegotiateTimeLossDetectionFromOptions) {
+ EXPECT_EQ(kNack,
+ QuicSentPacketManagerPeer::GetLossAlgorithm(
+ &manager_)->GetLossDetectionType());
+
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kTIME);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ manager_.SetFromConfig(config);
+
+ EXPECT_EQ(kTime,
+ QuicSentPacketManagerPeer::GetLossAlgorithm(
+ &manager_)->GetLossDetectionType());
+}
+
+TEST_F(QuicSentPacketManagerTest, NegotiateCongestionControlFromOptions) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(kRENO);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kReno, QuicSentPacketManagerPeer::GetCongestionControlAlgorithm(
+ manager_)->GetCongestionControlType());
+
+ // TODO(rtenneti): Enable the following code after BBR code is checked in.
+#if 0
+ options.clear();
+ options.push_back(kTBBR);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ manager_.SetFromConfig(config);
+ EXPECT_EQ(kBBR, QuicSentPacketManagerPeer::GetCongestionControlAlgorithm(
+ manager_)->GetCongestionControlType());
+#endif
+}
+
+TEST_F(QuicSentPacketManagerTest, NegotiateNumConnectionsFromOptions) {
+ QuicConfig config;
+ QuicTagVector options;
+
+ options.push_back(k1CON);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, SetNumEmulatedConnections(1));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+
+ QuicSentPacketManagerPeer::SetIsServer(&manager_, false);
+ QuicConfig client_config;
+ client_config.SetConnectionOptionsToSend(options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, SetNumEmulatedConnections(1));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(client_config);
+}
+
+TEST_F(QuicSentPacketManagerTest, NegotiatePacingFromOptions) {
+ EXPECT_FALSE(manager_.using_pacing());
+
+ QuicConfig config;
+ QuicTagVector options;
+ options.push_back(kPACE);
+ QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ manager_.SetFromConfig(config);
+
+ EXPECT_TRUE(manager_.using_pacing());
+}
+
+TEST_F(QuicSentPacketManagerTest, UseInitialRoundTripTimeToSend) {
+ uint32 initial_rtt_us = 325000;
+ EXPECT_NE(initial_rtt_us,
+ manager_.GetRttStats()->SmoothedRtt().ToMicroseconds());
+
+ QuicConfig config;
+ config.SetInitialRoundTripTimeUsToSend(initial_rtt_us);
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ EXPECT_CALL(*network_change_visitor_, OnCongestionWindowChange(_));
+ EXPECT_CALL(*send_algorithm_, GetCongestionWindow())
+ .WillOnce(Return(100 * kDefaultTCPMSS));
+ manager_.SetFromConfig(config);
+
+ EXPECT_EQ(initial_rtt_us,
+ manager_.GetRttStats()->SmoothedRtt().ToMicroseconds());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_server.cc b/net/quic/quic_server.cc
new file mode 100644
index 0000000..b8013c4
--- /dev/null
+++ b/net/quic/quic_server.cc
@@ -0,0 +1,176 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server.h"
+
+#include <string.h>
+
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_errors.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_data_reader.h"
+#include "net/quic/quic_dispatcher.h"
+#include "net/quic/quic_in_memory_cache.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_packet_writer.h"
+#include "net/udp/udp_server_socket.h"
+
+namespace net {
+
+namespace {
+
+const char kSourceAddressTokenSecret[] = "secret";
+
+// Allocate some extra space so we can send an error if the client goes over
+// the limit.
+const int kReadBufferSize = 2 * kMaxPacketSize;
+
+const uint32 kServerInitialFlowControlWindow = 100 * kMaxPacketSize;
+
+} // namespace
+
+QuicServer::QuicServer(const QuicConfig& config,
+ const QuicVersionVector& supported_versions)
+ : helper_(base::MessageLoop::current()->message_loop_proxy().get(),
+ &clock_,
+ QuicRandom::GetInstance()),
+ config_(config),
+ crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance()),
+ supported_versions_(supported_versions),
+ read_pending_(false),
+ synchronous_read_count_(0),
+ read_buffer_(new IOBufferWithSize(kReadBufferSize)),
+ weak_factory_(this) {
+ Initialize();
+}
+
+void QuicServer::Initialize() {
+ // Initialize the in memory cache now.
+ QuicInMemoryCache::GetInstance();
+
+ scoped_ptr<CryptoHandshakeMessage> scfg(
+ crypto_config_.AddDefaultConfig(helper_.GetRandomGenerator(),
+ helper_.GetClock(),
+ QuicCryptoServerConfig::ConfigOptions()));
+
+ config_.SetInitialCongestionWindowToSend(kServerInitialFlowControlWindow);
+}
+
+QuicServer::~QuicServer() {
+}
+
+int QuicServer::Listen(const IPEndPoint& address) {
+ scoped_ptr<UDPServerSocket> socket(
+ new UDPServerSocket(&net_log_, NetLog::Source()));
+
+ socket->AllowAddressReuse();
+
+ int rc = socket->Listen(address);
+ if (rc < 0) {
+ LOG(ERROR) << "Listen() failed: " << ErrorToString(rc);
+ return rc;
+ }
+
+ // These send and receive buffer sizes are sized for a single connection,
+ // because the default usage of QuicServer is as a test server with one or
+ // two clients. Adjust higher for use with many clients.
+ rc = socket->SetReceiveBufferSize(TcpReceiver::kReceiveWindowTCP);
+ if (rc < 0) {
+ LOG(ERROR) << "SetReceiveBufferSize() failed: " << ErrorToString(rc);
+ return rc;
+ }
+
+ rc = socket->SetSendBufferSize(20 * kMaxPacketSize);
+ if (rc < 0) {
+ LOG(ERROR) << "SetSendBufferSize() failed: " << ErrorToString(rc);
+ return rc;
+ }
+
+ rc = socket->GetLocalAddress(&server_address_);
+ if (rc < 0) {
+ LOG(ERROR) << "GetLocalAddress() failed: " << ErrorToString(rc);
+ return rc;
+ }
+
+ DVLOG(1) << "Listening on " << server_address_.ToString();
+
+ socket_.swap(socket);
+
+ dispatcher_.reset(
+ new QuicDispatcher(config_,
+ crypto_config_,
+ supported_versions_,
+ new QuicDispatcher::DefaultPacketWriterFactory(),
+ &helper_));
+ QuicServerPacketWriter* writer = new QuicServerPacketWriter(
+ socket_.get(),
+ dispatcher_.get());
+ dispatcher_->Initialize(writer);
+
+ StartReading();
+
+ return OK;
+}
+
+void QuicServer::Shutdown() {
+ // Before we shut down the epoll server, give all active sessions a chance to
+ // notify clients that they're closing.
+ dispatcher_->Shutdown();
+
+ socket_->Close();
+ socket_.reset();
+}
+
+void QuicServer::StartReading() {
+ if (read_pending_) {
+ return;
+ }
+ read_pending_ = true;
+
+ int result = socket_->RecvFrom(
+ read_buffer_.get(),
+ read_buffer_->size(),
+ &client_address_,
+ base::Bind(&QuicServer::OnReadComplete, base::Unretained(this)));
+
+ if (result == ERR_IO_PENDING) {
+ synchronous_read_count_ = 0;
+ return;
+ }
+
+ if (++synchronous_read_count_ > 32) {
+ synchronous_read_count_ = 0;
+ // Schedule the processing through the message loop to 1) prevent infinite
+ // recursion and 2) avoid blocking the thread for too long.
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&QuicServer::OnReadComplete,
+ weak_factory_.GetWeakPtr(),
+ result));
+ } else {
+ OnReadComplete(result);
+ }
+}
+
+void QuicServer::OnReadComplete(int result) {
+ read_pending_ = false;
+ if (result == 0)
+ result = ERR_CONNECTION_CLOSED;
+
+ if (result < 0) {
+ LOG(ERROR) << "QuicServer read failed: " << ErrorToString(result);
+ Shutdown();
+ return;
+ }
+
+ QuicEncryptedPacket packet(read_buffer_->data(), result, false);
+ dispatcher_->ProcessPacket(server_address_, client_address_, packet);
+
+ StartReading();
+}
+
+} // namespace net
diff --git a/net/quic/quic_server.h b/net/quic/quic_server.h
new file mode 100644
index 0000000..d53963e
--- /dev/null
+++ b/net/quic/quic_server.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A toy server, which listens on a specified address for QUIC traffic and
+// handles incoming responses.
+
+#ifndef NET_QUIC_QUIC_SERVER_H_
+#define NET_QUIC_QUIC_SERVER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/io_buffer.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_log.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_connection_helper.h"
+
+namespace net {
+
+
+namespace test {
+class QuicServerPeer;
+} // namespace test
+
+class QuicDispatcher;
+class UDPServerSocket;
+
+class QuicServer {
+ public:
+ QuicServer(const QuicConfig& config,
+ const QuicVersionVector& supported_versions);
+
+ virtual ~QuicServer();
+
+ // Start listening on the specified address. Returns an error code.
+ int Listen(const IPEndPoint& address);
+
+ // Server deletion is imminent. Start cleaning up.
+ void Shutdown();
+
+ // Start reading on the socket. On asynchronous reads, this registers
+ // OnReadComplete as the callback, which will then call StartReading again.
+ void StartReading();
+
+ // Called on reads that complete asynchronously. Dispatches the packet and
+ // continues the read loop.
+ void OnReadComplete(int result);
+
+ void SetStrikeRegisterNoStartupPeriod() {
+ crypto_config_.set_strike_register_no_startup_period();
+ }
+
+ // SetProofSource sets the ProofSource that will be used to verify the
+ // server's certificate, and takes ownership of |source|.
+ void SetProofSource(ProofSource* source) {
+ crypto_config_.SetProofSource(source);
+ }
+
+ private:
+ friend class net::test::QuicServerPeer;
+
+ // Initialize the internal state of the server.
+ void Initialize();
+
+ // Accepts data from the framer and demuxes clients to sessions.
+ scoped_ptr<QuicDispatcher> dispatcher_;
+
+ // Used by the helper_ to time alarms.
+ QuicClock clock_;
+
+ // Used to manage the message loop.
+ QuicConnectionHelper helper_;
+
+ // Listening socket. Also used for outbound client communication.
+ scoped_ptr<UDPServerSocket> socket_;
+
+ // config_ contains non-crypto parameters that are negotiated in the crypto
+ // handshake.
+ QuicConfig config_;
+ // crypto_config_ contains crypto parameters for the handshake.
+ QuicCryptoServerConfig crypto_config_;
+
+ // This vector contains QUIC versions which we currently support.
+ // This should be ordered such that the highest supported version is the first
+ // element, with subsequent elements in descending order (versions can be
+ // skipped as necessary).
+ QuicVersionVector supported_versions_;
+
+ // The address that the server listens on.
+ IPEndPoint server_address_;
+
+ // Keeps track of whether a read is currently in flight, after which
+ // OnReadComplete will be called.
+ bool read_pending_;
+
+ // The number of iterations of the read loop that have completed synchronously
+ // and without posting a new task to the message loop.
+ int synchronous_read_count_;
+
+ // The target buffer of the current read.
+ scoped_refptr<IOBufferWithSize> read_buffer_;
+
+ // The source address of the current read.
+ IPEndPoint client_address_;
+
+ // The log to use for the socket.
+ NetLog net_log_;
+
+ base::WeakPtrFactory<QuicServer> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServer);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_H_
diff --git a/net/quic/quic_server_bin.cc b/net/quic/quic_server_bin.cc
new file mode 100644
index 0000000..63b85a7
--- /dev/null
+++ b/net/quic/quic_server_bin.cc
@@ -0,0 +1,77 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A binary wrapper for QuicServer. It listens forever on --port
+// (default 6121) until it's killed or ctrl-cd to death.
+
+#include <iostream>
+
+#include "base/at_exit.h"
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_in_memory_cache.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server.h"
+
+// The port the quic server will listen on.
+int32 FLAGS_port = 6121;
+
+int main(int argc, char *argv[]) {
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine* line = base::CommandLine::ForCurrentProcess();
+
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ CHECK(logging::InitLogging(settings));
+
+ if (line->HasSwitch("h") || line->HasSwitch("help")) {
+ const char* help_str =
+ "Usage: quic_server [options]\n"
+ "\n"
+ "Options:\n"
+ "-h, --help show this help message and exit\n"
+ "--port=<port> specify the port to listen on\n"
+ "--quic_in_memory_cache_dir directory containing response data\n"
+ " to load\n";
+ std::cout << help_str;
+ exit(0);
+ }
+
+ if (line->HasSwitch("quic_in_memory_cache_dir")) {
+ net::g_quic_in_memory_cache_dir =
+ line->GetSwitchValueNative("quic_in_memory_cache_dir");
+ }
+
+ if (line->HasSwitch("port")) {
+ int port;
+ if (base::StringToInt(line->GetSwitchValueASCII("port"), &port)) {
+ FLAGS_port = port;
+ }
+ }
+
+ base::AtExitManager exit_manager;
+
+ base::MessageLoopForIO message_loop;
+
+ net::IPAddressNumber ip;
+ CHECK(net::ParseIPLiteralToNumber("::", &ip));
+
+ net::QuicConfig config;
+ config.SetDefaults();
+
+ net::QuicServer server(config, net::QuicSupportedVersions());
+
+ int rc = server.Listen(net::IPEndPoint(ip, FLAGS_port));
+ if (rc < 0) {
+ return 1;
+ }
+
+ base::RunLoop().Run();
+
+ return 0;
+}
diff --git a/net/quic/quic_server_id.cc b/net/quic/quic_server_id.cc
new file mode 100644
index 0000000..ff87099
--- /dev/null
+++ b/net/quic/quic_server_id.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_id.h"
+
+using std::string;
+
+namespace net {
+
+QuicServerId::QuicServerId() {}
+
+QuicServerId::QuicServerId(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode)
+ : host_port_pair_(host_port_pair),
+ is_https_(is_https),
+ privacy_mode_(privacy_mode) {}
+
+QuicServerId::QuicServerId(const string& host,
+ uint16 port,
+ bool is_https)
+ : host_port_pair_(host, port),
+ is_https_(is_https),
+ privacy_mode_(PRIVACY_MODE_DISABLED) {}
+
+QuicServerId::QuicServerId(const string& host,
+ uint16 port,
+ bool is_https,
+ PrivacyMode privacy_mode)
+ : host_port_pair_(host, port),
+ is_https_(is_https),
+ privacy_mode_(privacy_mode) {}
+
+QuicServerId::~QuicServerId() {}
+
+bool QuicServerId::operator<(const QuicServerId& other) const {
+ if (!host_port_pair_.Equals(other.host_port_pair_)) {
+ return host_port_pair_ < other.host_port_pair_;
+ }
+ if (is_https_ != other.is_https_) {
+ return is_https_ < other.is_https_;
+ }
+ return privacy_mode_ < other.privacy_mode_;
+}
+
+bool QuicServerId::operator==(const QuicServerId& other) const {
+ return is_https_ == other.is_https_ &&
+ privacy_mode_ == other.privacy_mode_ &&
+ host_port_pair_.Equals(other.host_port_pair_);
+}
+
+string QuicServerId::ToString() const {
+ return (is_https_ ? "https://" : "http://") + host_port_pair_.ToString() +
+ (privacy_mode_ == PRIVACY_MODE_ENABLED ? "/private" : "");
+}
+
+} // namespace net
diff --git a/net/quic/quic_server_id.h b/net/quic/quic_server_id.h
new file mode 100644
index 0000000..6d01670
--- /dev/null
+++ b/net/quic/quic_server_id.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SERVER_ID_H_
+#define NET_QUIC_QUIC_SERVER_ID_H_
+
+#include <string>
+
+#include "net/base/host_port_pair.h"
+#include "net/base/net_export.h"
+#include "net/base/privacy_mode.h"
+
+namespace net {
+
+// The id used to identify sessions. Includes the hostname, port, scheme and
+// privacy_mode.
+class NET_EXPORT_PRIVATE QuicServerId {
+ public:
+ QuicServerId();
+ QuicServerId(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode);
+ QuicServerId(const std::string& host,
+ uint16 port,
+ bool is_https);
+ QuicServerId(const std::string& host,
+ uint16 port,
+ bool is_https,
+ PrivacyMode privacy_mode);
+ ~QuicServerId();
+
+ // Needed to be an element of std::set.
+ bool operator<(const QuicServerId& other) const;
+ bool operator==(const QuicServerId& other) const;
+
+ // ToString() will convert the QuicServerId to "scheme:hostname:port" or
+ // "scheme:hostname:port/private". "scheme" would either be "http" or "https"
+ // based on |is_https|.
+ std::string ToString() const;
+
+ const HostPortPair& host_port_pair() const { return host_port_pair_; }
+
+ const std::string& host() const { return host_port_pair_.host(); }
+
+ uint16 port() const { return host_port_pair_.port(); }
+
+ bool is_https() const { return is_https_; }
+
+ PrivacyMode privacy_mode() const { return privacy_mode_; }
+
+ private:
+ HostPortPair host_port_pair_;
+ bool is_https_;
+ PrivacyMode privacy_mode_;
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_ID_H_
diff --git a/net/quic/quic_server_id_test.cc b/net/quic/quic_server_id_test.cc
new file mode 100644
index 0000000..1e08b30
--- /dev/null
+++ b/net/quic/quic_server_id_test.cc
@@ -0,0 +1,287 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_id.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+TEST(QuicServerIdTest, ToString) {
+ HostPortPair google_host_port_pair("google.com", 10);
+
+ QuicServerId google_http_server_id(google_host_port_pair, false,
+ PRIVACY_MODE_DISABLED);
+ string google_http_server_id_str = google_http_server_id.ToString();
+ EXPECT_EQ("http://google.com:10", google_http_server_id_str);
+
+ QuicServerId google_https_server_id(google_host_port_pair, true,
+ PRIVACY_MODE_DISABLED);
+ string google_https_server_id_str = google_https_server_id.ToString();
+ EXPECT_EQ("https://google.com:10", google_https_server_id_str);
+
+ QuicServerId private_http_server_id(google_host_port_pair, false,
+ PRIVACY_MODE_ENABLED);
+ string private_http_server_id_str = private_http_server_id.ToString();
+ EXPECT_EQ("http://google.com:10/private", private_http_server_id_str);
+
+ QuicServerId private_https_server_id(google_host_port_pair, true,
+ PRIVACY_MODE_ENABLED);
+ string private_https_server_id_str = private_https_server_id.ToString();
+ EXPECT_EQ("https://google.com:10/private", private_https_server_id_str);
+}
+
+TEST(QuicServerIdTest, LessThan) {
+ QuicServerId a_10_http(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_10_https(HostPortPair("a.com", 10), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_11_http(HostPortPair("a.com", 11), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId a_11_https(HostPortPair("a.com", 11), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_10_http(HostPortPair("b.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_10_https(HostPortPair("b.com", 10), true,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_11_http(HostPortPair("b.com", 11), false,
+ PRIVACY_MODE_DISABLED);
+ QuicServerId b_11_https(HostPortPair("b.com", 11), true,
+ PRIVACY_MODE_DISABLED);
+
+ QuicServerId a_10_http_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_10_https_private(HostPortPair("a.com", 10), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_11_http_private(HostPortPair("a.com", 11), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId a_11_https_private(HostPortPair("a.com", 11), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_10_http_private(HostPortPair("b.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_10_https_private(HostPortPair("b.com", 10), true,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_11_http_private(HostPortPair("b.com", 11), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId b_11_https_private(HostPortPair("b.com", 11), true,
+ PRIVACY_MODE_ENABLED);
+
+ // Test combinations of host, port, https and privacy being same on left and
+ // right side of less than.
+ EXPECT_FALSE(a_10_http < a_10_http);
+ EXPECT_TRUE(a_10_http < a_10_https);
+ EXPECT_FALSE(a_10_https < a_10_http);
+ EXPECT_FALSE(a_10_https < a_10_https);
+
+ EXPECT_TRUE(a_10_http < a_10_http_private);
+ EXPECT_TRUE(a_10_http < a_10_https_private);
+ EXPECT_FALSE(a_10_https < a_10_http_private);
+ EXPECT_TRUE(a_10_https < a_10_https_private);
+
+ EXPECT_FALSE(a_10_http_private < a_10_http);
+ EXPECT_TRUE(a_10_http_private < a_10_https);
+ EXPECT_FALSE(a_10_https_private < a_10_http);
+ EXPECT_FALSE(a_10_https_private < a_10_https);
+
+ EXPECT_FALSE(a_10_http_private < a_10_http_private);
+ EXPECT_TRUE(a_10_http_private < a_10_https_private);
+ EXPECT_FALSE(a_10_https_private < a_10_http_private);
+ EXPECT_FALSE(a_10_https_private < a_10_https_private);
+
+ // Test with either host, port or https being different on left and right side
+ // of less than.
+ PrivacyMode left_privacy;
+ PrivacyMode right_privacy;
+ for (int i = 0; i < 4; i++) {
+ left_privacy = static_cast<PrivacyMode>(i / 2);
+ right_privacy = static_cast<PrivacyMode>(i % 2);
+ QuicServerId a_10_http_left_private(HostPortPair("a.com", 10), false,
+ left_privacy);
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_left_private(HostPortPair("a.com", 10), true,
+ left_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_left_private(HostPortPair("a.com", 11), false,
+ left_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_left_private(HostPortPair("a.com", 11), true,
+ left_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+
+ QuicServerId b_10_http_left_private(HostPortPair("b.com", 10), false,
+ left_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_left_private(HostPortPair("b.com", 10), true,
+ left_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_left_private(HostPortPair("b.com", 11), false,
+ left_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_left_private(HostPortPair("b.com", 11), true,
+ left_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ EXPECT_TRUE(a_10_http_left_private < a_11_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < a_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < a_11_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < a_11_https_right_private);
+
+ EXPECT_TRUE(a_10_http_left_private < b_10_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < b_10_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_10_https_right_private);
+
+ EXPECT_TRUE(a_10_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_10_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_10_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(a_11_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(a_11_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(a_11_https_left_private < a_10_https_right_private);
+
+ EXPECT_FALSE(a_11_http_left_private < b_10_http_right_private);
+ EXPECT_FALSE(a_11_http_left_private < b_10_https_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_http_right_private);
+ EXPECT_FALSE(a_11_https_left_private < b_10_https_right_private);
+
+ EXPECT_TRUE(a_11_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_11_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(a_11_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(b_10_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_10_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_10_https_left_private < a_10_https_right_private);
+
+ EXPECT_TRUE(b_10_http_left_private < a_11_http_right_private);
+ EXPECT_TRUE(b_10_http_left_private < a_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_http_right_private);
+ EXPECT_TRUE(b_10_https_left_private < a_11_https_right_private);
+
+ EXPECT_TRUE(b_10_http_left_private < b_11_http_right_private);
+ EXPECT_TRUE(b_10_http_left_private < b_11_https_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_http_right_private);
+ EXPECT_TRUE(b_10_https_left_private < b_11_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < a_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_10_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < a_11_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < a_11_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < a_11_https_right_private);
+
+ EXPECT_FALSE(b_11_http_left_private < b_10_http_right_private);
+ EXPECT_FALSE(b_11_http_left_private < b_10_https_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_http_right_private);
+ EXPECT_FALSE(b_11_https_left_private < b_10_https_right_private);
+ }
+}
+
+TEST(QuicServerIdTest, Equals) {
+ PrivacyMode left_privacy;
+ PrivacyMode right_privacy;
+ for (int i = 0; i < 2; i++) {
+ left_privacy = right_privacy = static_cast<PrivacyMode>(i);
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false,
+ left_privacy);
+ QuicServerId new_a_10_https_left_private(HostPortPair("a.com", 10), true,
+ left_privacy);
+ QuicServerId new_a_11_http_left_private(HostPortPair("a.com", 11), false,
+ left_privacy);
+ QuicServerId new_a_11_https_left_private(HostPortPair("a.com", 11), true,
+ left_privacy);
+ QuicServerId new_b_10_http_left_private(HostPortPair("b.com", 10), false,
+ left_privacy);
+ QuicServerId new_b_10_https_left_private(HostPortPair("b.com", 10), true,
+ left_privacy);
+ QuicServerId new_b_11_http_left_private(HostPortPair("b.com", 11), false,
+ left_privacy);
+ QuicServerId new_b_11_https_left_private(HostPortPair("b.com", 11), true,
+ left_privacy);
+
+ EXPECT_EQ(new_a_10_http_left_private, a_10_http_right_private);
+ EXPECT_EQ(new_a_10_https_left_private, a_10_https_right_private);
+ EXPECT_EQ(new_a_11_http_left_private, a_11_http_right_private);
+ EXPECT_EQ(new_a_11_https_left_private, a_11_https_right_private);
+ EXPECT_EQ(new_b_10_http_left_private, b_10_http_right_private);
+ EXPECT_EQ(new_b_10_https_left_private, b_10_https_right_private);
+ EXPECT_EQ(new_b_11_http_left_private, b_11_http_right_private);
+ EXPECT_EQ(new_b_11_https_left_private, b_11_https_right_private);
+ }
+
+ for (int i = 0; i < 2; i++) {
+ right_privacy = static_cast<PrivacyMode>(i);
+ QuicServerId a_10_http_right_private(HostPortPair("a.com", 10), false,
+ right_privacy);
+ QuicServerId a_10_https_right_private(HostPortPair("a.com", 10), true,
+ right_privacy);
+ QuicServerId a_11_http_right_private(HostPortPair("a.com", 11), false,
+ right_privacy);
+ QuicServerId a_11_https_right_private(HostPortPair("a.com", 11), true,
+ right_privacy);
+ QuicServerId b_10_http_right_private(HostPortPair("b.com", 10), false,
+ right_privacy);
+ QuicServerId b_10_https_right_private(HostPortPair("b.com", 10), true,
+ right_privacy);
+ QuicServerId b_11_http_right_private(HostPortPair("b.com", 11), false,
+ right_privacy);
+ QuicServerId b_11_https_right_private(HostPortPair("b.com", 11), true,
+ right_privacy);
+
+ QuicServerId new_a_10_http_left_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+
+ EXPECT_FALSE(new_a_10_http_left_private == a_10_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == a_11_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_10_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == a_11_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_10_https_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_11_http_right_private);
+ EXPECT_FALSE(new_a_10_http_left_private == b_11_https_right_private);
+ }
+ QuicServerId a_10_http_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_ENABLED);
+ QuicServerId new_a_10_http_no_private(HostPortPair("a.com", 10), false,
+ PRIVACY_MODE_DISABLED);
+ EXPECT_FALSE(new_a_10_http_no_private == a_10_http_private);
+}
+
+} // namespace
+
+} // namespace net
diff --git a/net/quic/quic_server_packet_writer.cc b/net/quic/quic_server_packet_writer.cc
new file mode 100644
index 0000000..38d93d9
--- /dev/null
+++ b/net/quic/quic_server_packet_writer.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_packet_writer.h"
+
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/udp/udp_server_socket.h"
+
+namespace net {
+
+QuicServerPacketWriter::QuicServerPacketWriter(
+ UDPServerSocket* socket,
+ QuicBlockedWriterInterface* blocked_writer)
+ : socket_(socket),
+ blocked_writer_(blocked_writer),
+ write_blocked_(false),
+ weak_factory_(this) {
+}
+
+QuicServerPacketWriter::~QuicServerPacketWriter() {
+}
+
+WriteResult QuicServerPacketWriter::WritePacketWithCallback(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ WriteCallback callback) {
+ DCHECK(callback_.is_null());
+ callback_ = callback;
+ WriteResult result = WritePacket(buffer, buf_len, self_address, peer_address);
+ if (result.status != WRITE_STATUS_BLOCKED) {
+ callback_.Reset();
+ }
+ return result;
+}
+
+void QuicServerPacketWriter::OnWriteComplete(int rv) {
+ DCHECK_NE(rv, ERR_IO_PENDING);
+ write_blocked_ = false;
+ WriteResult result(rv < 0 ? WRITE_STATUS_ERROR : WRITE_STATUS_OK, rv);
+ base::ResetAndReturn(&callback_).Run(result);
+ blocked_writer_->OnCanWrite();
+}
+
+bool QuicServerPacketWriter::IsWriteBlockedDataBuffered() const {
+ // UDPServerSocket::SendTo buffers the data until the Write is permitted.
+ return true;
+}
+
+bool QuicServerPacketWriter::IsWriteBlocked() const {
+ return write_blocked_;
+}
+
+void QuicServerPacketWriter::SetWritable() {
+ write_blocked_ = false;
+}
+
+WriteResult QuicServerPacketWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) {
+ scoped_refptr<StringIOBuffer> buf(
+ new StringIOBuffer(std::string(buffer, buf_len)));
+ DCHECK(!IsWriteBlocked());
+ DCHECK(!callback_.is_null());
+ int rv;
+ if (buf_len <= static_cast<size_t>(std::numeric_limits<int>::max())) {
+ rv = socket_->SendTo(buf.get(),
+ static_cast<int>(buf_len),
+ peer_address,
+ base::Bind(&QuicServerPacketWriter::OnWriteComplete,
+ weak_factory_.GetWeakPtr()));
+ } else {
+ rv = ERR_MSG_TOO_BIG;
+ }
+ WriteStatus status = WRITE_STATUS_OK;
+ if (rv < 0) {
+ if (rv != ERR_IO_PENDING) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.WriteError", -rv);
+ status = WRITE_STATUS_ERROR;
+ } else {
+ status = WRITE_STATUS_BLOCKED;
+ write_blocked_ = true;
+ }
+ }
+ return WriteResult(status, rv);
+}
+
+} // namespace net
diff --git a/net/quic/quic_server_packet_writer.h b/net/quic/quic_server_packet_writer.h
new file mode 100644
index 0000000..417ea8d
--- /dev/null
+++ b/net/quic/quic_server_packet_writer.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+#define NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicBlockedWriterInterface;
+class UDPServerSocket;
+struct WriteResult;
+
+// Chrome specific packet writer which uses a UDPServerSocket for writing
+// data.
+class QuicServerPacketWriter : public QuicPacketWriter {
+ public:
+ typedef base::Callback<void(WriteResult)> WriteCallback;
+
+ QuicServerPacketWriter(UDPServerSocket* socket,
+ QuicBlockedWriterInterface* blocked_writer);
+ virtual ~QuicServerPacketWriter();
+
+ // Use this method to write packets rather than WritePacket:
+ // QuicServerPacketWriter requires a callback to exist for every write, which
+ // will be called once the write completes.
+ virtual WriteResult WritePacketWithCallback(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address,
+ WriteCallback callback);
+
+ void OnWriteComplete(int rv);
+
+ // QuicPacketWriter implementation:
+ virtual bool IsWriteBlockedDataBuffered() const OVERRIDE;
+ virtual bool IsWriteBlocked() const OVERRIDE;
+ virtual void SetWritable() OVERRIDE;
+
+ protected:
+ // Do not call WritePacket on its own -- use WritePacketWithCallback
+ virtual WriteResult WritePacket(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
+ private:
+ UDPServerSocket* socket_;
+
+ // To be notified after every successful asynchronous write.
+ QuicBlockedWriterInterface* blocked_writer_;
+
+ // To call once the write completes.
+ WriteCallback callback_;
+
+ // Whether a write is currently in flight.
+ bool write_blocked_;
+
+ base::WeakPtrFactory<QuicServerPacketWriter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerPacketWriter);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_PACKET_WRITER_H_
diff --git a/net/quic/quic_server_session.cc b/net/quic/quic_server_session.cc
new file mode 100644
index 0000000..bacd59e
--- /dev/null
+++ b/net/quic/quic_server_session.cc
@@ -0,0 +1,175 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server_session.h"
+
+#include "base/logging.h"
+#include "net/quic/crypto/source_address_token.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_spdy_server_stream.h"
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+
+QuicServerSession::QuicServerSession(
+ const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor)
+ : QuicSession(connection, config),
+ visitor_(visitor),
+ bandwidth_estimate_sent_to_client_(QuicBandwidth::Zero()),
+ last_server_config_update_time_(QuicTime::Zero()) {}
+
+QuicServerSession::~QuicServerSession() {}
+
+void QuicServerSession::InitializeSession(
+ const QuicCryptoServerConfig& crypto_config) {
+ QuicSession::InitializeSession();
+ crypto_stream_.reset(CreateQuicCryptoServerStream(crypto_config));
+}
+
+QuicCryptoServerStream* QuicServerSession::CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config) {
+ return new QuicCryptoServerStream(crypto_config, this);
+}
+
+void QuicServerSession::OnConfigNegotiated() {
+ QuicSession::OnConfigNegotiated();
+ if (!FLAGS_enable_quic_fec ||
+ !config()->HasReceivedConnectionOptions() ||
+ !ContainsQuicTag(config()->ReceivedConnectionOptions(), kFHDR)) {
+ return;
+ }
+ // kFHDR config maps to FEC protection always for headers stream.
+ // TODO(jri): Add crypto stream in addition to headers for kHDR.
+ headers_stream_->set_fec_policy(FEC_PROTECT_ALWAYS);
+}
+
+void QuicServerSession::OnConnectionClosed(QuicErrorCode error,
+ bool from_peer) {
+ QuicSession::OnConnectionClosed(error, from_peer);
+ // In the unlikely event we get a connection close while doing an asynchronous
+ // crypto event, make sure we cancel the callback.
+ if (crypto_stream_.get() != nullptr) {
+ crypto_stream_->CancelOutstandingCallbacks();
+ }
+ visitor_->OnConnectionClosed(connection()->connection_id(), error);
+}
+
+void QuicServerSession::OnWriteBlocked() {
+ QuicSession::OnWriteBlocked();
+ visitor_->OnWriteBlocked(connection());
+}
+
+void QuicServerSession::OnCongestionWindowChange(QuicTime now) {
+ if (connection()->version() <= QUIC_VERSION_21) {
+ return;
+ }
+
+ // If not enough time has passed since the last time we sent an update to the
+ // client, then return early.
+ const QuicSentPacketManager& sent_packet_manager =
+ connection()->sent_packet_manager();
+ int64 srtt_ms =
+ sent_packet_manager.GetRttStats()->SmoothedRtt().ToMilliseconds();
+ int64 now_ms = now.Subtract(last_server_config_update_time_).ToMilliseconds();
+ if (now_ms < (kMinIntervalBetweenServerConfigUpdatesRTTs * srtt_ms) ||
+ now_ms < kMinIntervalBetweenServerConfigUpdatesMs) {
+ return;
+ }
+
+ // If the bandwidth recorder does not have a valid estimate, return early.
+ const QuicSustainedBandwidthRecorder& bandwidth_recorder =
+ sent_packet_manager.SustainedBandwidthRecorder();
+ if (!bandwidth_recorder.HasEstimate()) {
+ return;
+ }
+
+ // The bandwidth recorder has recorded at least one sustained bandwidth
+ // estimate. Check that it's substantially different from the last one that
+ // we sent to the client, and if so, send the new one.
+ QuicBandwidth new_bandwidth_estimate = bandwidth_recorder.BandwidthEstimate();
+
+ int64 bandwidth_delta =
+ std::abs(new_bandwidth_estimate.ToBitsPerSecond() -
+ bandwidth_estimate_sent_to_client_.ToBitsPerSecond());
+
+ // Define "substantial" difference as a 50% increase or decrease from the
+ // last estimate.
+ bool substantial_difference =
+ bandwidth_delta >
+ 0.5 * bandwidth_estimate_sent_to_client_.ToBitsPerSecond();
+ if (!substantial_difference) {
+ return;
+ }
+
+ bandwidth_estimate_sent_to_client_ = new_bandwidth_estimate;
+ DVLOG(1) << "Server: sending new bandwidth estimate (KBytes/s): "
+ << bandwidth_estimate_sent_to_client_.ToKBytesPerSecond();
+
+ // Include max bandwidth in the update.
+ QuicBandwidth max_bandwidth_estimate =
+ bandwidth_recorder.MaxBandwidthEstimate();
+ int32 max_bandwidth_timestamp = bandwidth_recorder.MaxBandwidthTimestamp();
+
+ // Fill the proto before passing it to the crypto stream to send.
+ CachedNetworkParameters cached_network_params;
+ cached_network_params.set_bandwidth_estimate_bytes_per_second(
+ bandwidth_estimate_sent_to_client_.ToBytesPerSecond());
+ cached_network_params.set_max_bandwidth_estimate_bytes_per_second(
+ max_bandwidth_estimate.ToBytesPerSecond());
+ cached_network_params.set_max_bandwidth_timestamp_seconds(
+ max_bandwidth_timestamp);
+ cached_network_params.set_min_rtt_ms(
+ sent_packet_manager.GetRttStats()->min_rtt().ToMilliseconds());
+ cached_network_params.set_previous_connection_state(
+ bandwidth_recorder.EstimateRecordedDuringSlowStart()
+ ? CachedNetworkParameters::SLOW_START
+ : CachedNetworkParameters::CONGESTION_AVOIDANCE);
+ cached_network_params.set_timestamp(
+ connection()->clock()->WallNow().ToUNIXSeconds());
+ if (!serving_region_.empty()) {
+ cached_network_params.set_serving_region(serving_region_);
+ }
+
+ crypto_stream_->SendServerConfigUpdate(&cached_network_params);
+ last_server_config_update_time_ = now;
+}
+
+bool QuicServerSession::ShouldCreateIncomingDataStream(QuicStreamId id) {
+ if (id % 2 == 0) {
+ DVLOG(1) << "Invalid incoming even stream_id:" << id;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ return false;
+ }
+ if (GetNumOpenStreams() >= get_max_open_streams()) {
+ DVLOG(1) << "Failed to create a new incoming stream with id:" << id
+ << " Already " << GetNumOpenStreams() << " streams open (max "
+ << get_max_open_streams() << ").";
+ connection()->SendConnectionClose(QUIC_TOO_MANY_OPEN_STREAMS);
+ return false;
+ }
+ return true;
+}
+
+QuicDataStream* QuicServerSession::CreateIncomingDataStream(
+ QuicStreamId id) {
+ if (!ShouldCreateIncomingDataStream(id)) {
+ return nullptr;
+ }
+
+ return new QuicSpdyServerStream(id, this);
+}
+
+QuicDataStream* QuicServerSession::CreateOutgoingDataStream() {
+ DLOG(ERROR) << "Server push not yet supported";
+ return nullptr;
+}
+
+QuicCryptoServerStream* QuicServerSession::GetCryptoStream() {
+ return crypto_stream_.get();
+}
+
+} // namespace net
diff --git a/net/quic/quic_server_session.h b/net/quic/quic_server_session.h
new file mode 100644
index 0000000..a2e4feb
--- /dev/null
+++ b/net/quic/quic_server_session.h
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A server specific QuicSession subclass.
+
+#ifndef NET_QUIC_QUIC_SERVER_SESSION_H_
+#define NET_QUIC_QUIC_SERVER_SESSION_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_crypto_server_stream.h"
+#include "net/quic/quic_per_connection_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+class QuicBlockedWriterInterface;
+class QuicConfig;
+class QuicConnection;
+class QuicCryptoServerConfig;
+class ReliableQuicStream;
+
+namespace test {
+class QuicServerSessionPeer;
+} // namespace test
+
+// An interface from the session to the entity owning the session.
+// This lets the session notify its owner (the Dispatcher) when the connection
+// is closed or blocked.
+class QuicServerSessionVisitor {
+ public:
+ virtual ~QuicServerSessionVisitor() {}
+
+ virtual void OnConnectionClosed(QuicConnectionId connection_id,
+ QuicErrorCode error) = 0;
+ virtual void OnWriteBlocked(QuicBlockedWriterInterface* blocked_writer) = 0;
+};
+
+class QuicServerSession : public QuicSession {
+ public:
+ QuicServerSession(const QuicConfig& config,
+ QuicConnection* connection,
+ QuicServerSessionVisitor* visitor);
+
+ // Override the base class to notify the owner of the connection close.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE;
+
+ // Sends a server config update to the client, containing new bandwidth
+ // estimate.
+ virtual void OnCongestionWindowChange(QuicTime now) OVERRIDE;
+
+ virtual ~QuicServerSession();
+
+ virtual void InitializeSession(const QuicCryptoServerConfig& crypto_config);
+
+ const QuicCryptoServerStream* crypto_stream() const {
+ return crypto_stream_.get();
+ }
+
+ // Override base class to process FEC config received from client.
+ virtual void OnConfigNegotiated() OVERRIDE;
+
+ void set_serving_region(string serving_region) {
+ serving_region_ = serving_region;
+ }
+
+ protected:
+ // QuicSession methods:
+ virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE;
+ virtual QuicDataStream* CreateOutgoingDataStream() OVERRIDE;
+ virtual QuicCryptoServerStream* GetCryptoStream() OVERRIDE;
+
+ // If we should create an incoming stream, returns true. Otherwise
+ // does error handling, including communicating the error to the client and
+ // possibly closing the connection, and returns false.
+ virtual bool ShouldCreateIncomingDataStream(QuicStreamId id);
+
+ virtual QuicCryptoServerStream* CreateQuicCryptoServerStream(
+ const QuicCryptoServerConfig& crypto_config);
+
+ private:
+ friend class test::QuicServerSessionPeer;
+
+ scoped_ptr<QuicCryptoServerStream> crypto_stream_;
+ QuicServerSessionVisitor* visitor_;
+
+ // The most recent bandwidth estimate sent to the client.
+ QuicBandwidth bandwidth_estimate_sent_to_client_;
+
+ // Text describing server location. Sent to the client as part of the bandwith
+ // estimate in the source-address token. Optional, can be left empty.
+ string serving_region_;
+
+ // Time at which we send the last SCUP to the client.
+ QuicTime last_server_config_update_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicServerSession);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SERVER_SESSION_H_
diff --git a/net/quic/quic_server_test.cc b/net/quic/quic_server_test.cc
new file mode 100644
index 0000000..9e4e894
--- /dev/null
+++ b/net/quic/quic_server_test.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_server.h"
+
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/mock_quic_dispatcher.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+
+namespace net {
+namespace test {
+
+namespace {
+
+// TODO(dmz) Remove "Chrome" part of name once net/tools/quic is deleted.
+class QuicChromeServerDispatchPacketTest : public ::testing::Test {
+ public:
+ QuicChromeServerDispatchPacketTest()
+ : crypto_config_("blah", QuicRandom::GetInstance()),
+ dispatcher_(config_,
+ crypto_config_,
+ new QuicDispatcher::DefaultPacketWriterFactory(),
+ &helper_) {
+ dispatcher_.Initialize(nullptr);
+ }
+
+ void DispatchPacket(const QuicEncryptedPacket& packet) {
+ IPEndPoint client_addr, server_addr;
+ dispatcher_.ProcessPacket(server_addr, client_addr, packet);
+ }
+
+ protected:
+ QuicConfig config_;
+ QuicCryptoServerConfig crypto_config_;
+ MockHelper helper_;
+ MockQuicDispatcher dispatcher_;
+};
+
+TEST_F(QuicChromeServerDispatchPacketTest, DispatchPacket) {
+ unsigned char valid_packet[] = {
+ // public flags (8 byte connection_id)
+ 0x3C,
+ // connection_id
+ 0x10, 0x32, 0x54, 0x76,
+ 0x98, 0xBA, 0xDC, 0xFE,
+ // packet sequence number
+ 0xBC, 0x9A, 0x78, 0x56,
+ 0x34, 0x12,
+ // private flags
+ 0x00 };
+ QuicEncryptedPacket encrypted_valid_packet(QuicUtils::AsChars(valid_packet),
+ arraysize(valid_packet), false);
+
+ EXPECT_CALL(dispatcher_, ProcessPacket(_, _, _)).Times(1);
+ DispatchPacket(encrypted_valid_packet);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
new file mode 100644
index 0000000..cef5656
--- /dev/null
+++ b/net/quic/quic_session.cc
@@ -0,0 +1,785 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_session.h"
+
+#include "base/stl_util.h"
+#include "net/quic/crypto/proof_verifier.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_headers_stream.h"
+#include "net/ssl/ssl_info.h"
+
+using base::StringPiece;
+using base::hash_map;
+using base::hash_set;
+using std::make_pair;
+using std::max;
+using std::vector;
+
+namespace net {
+
+#define ENDPOINT (is_server() ? "Server: " : " Client: ")
+
+// We want to make sure we delete any closed streams in a safe manner.
+// To avoid deleting a stream in mid-operation, we have a simple shim between
+// us and the stream, so we can delete any streams when we return from
+// processing.
+//
+// We could just override the base methods, but this makes it easier to make
+// sure we don't miss any.
+class VisitorShim : public QuicConnectionVisitorInterface {
+ public:
+ explicit VisitorShim(QuicSession* session) : session_(session) {}
+
+ virtual void OnStreamFrames(const vector<QuicStreamFrame>& frames) OVERRIDE {
+ session_->OnStreamFrames(frames);
+ session_->PostProcessAfterData();
+ }
+ virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE {
+ session_->OnRstStream(frame);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE {
+ session_->OnGoAway(frame);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnWindowUpdateFrames(const vector<QuicWindowUpdateFrame>& frames)
+ OVERRIDE {
+ session_->OnWindowUpdateFrames(frames);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnBlockedFrames(const vector<QuicBlockedFrame>& frames)
+ OVERRIDE {
+ session_->OnBlockedFrames(frames);
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnCanWrite() OVERRIDE {
+ session_->OnCanWrite();
+ session_->PostProcessAfterData();
+ }
+
+ virtual void OnCongestionWindowChange(QuicTime now) OVERRIDE {
+ session_->OnCongestionWindowChange(now);
+ }
+
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE {
+ session_->OnSuccessfulVersionNegotiation(version);
+ }
+
+ virtual void OnConnectionClosed(
+ QuicErrorCode error, bool from_peer) OVERRIDE {
+ session_->OnConnectionClosed(error, from_peer);
+ // The session will go away, so don't bother with cleanup.
+ }
+
+ virtual void OnWriteBlocked() OVERRIDE {
+ session_->OnWriteBlocked();
+ }
+
+ virtual bool WillingAndAbleToWrite() const OVERRIDE {
+ return session_->WillingAndAbleToWrite();
+ }
+
+ virtual bool HasPendingHandshake() const OVERRIDE {
+ return session_->HasPendingHandshake();
+ }
+
+ virtual bool HasOpenDataStreams() const OVERRIDE {
+ return session_->HasOpenDataStreams();
+ }
+
+ private:
+ QuicSession* session_;
+};
+
+QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config)
+ : connection_(connection),
+ visitor_shim_(new VisitorShim(this)),
+ config_(config),
+ max_open_streams_(config_.MaxStreamsPerConnection()),
+ next_stream_id_(is_server() ? 2 : 5),
+ largest_peer_created_stream_id_(0),
+ error_(QUIC_NO_ERROR),
+ goaway_received_(false),
+ goaway_sent_(false),
+ has_pending_handshake_(false) {
+ if (connection_->version() <= QUIC_VERSION_19) {
+ flow_controller_.reset(new QuicFlowController(
+ connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+ config_.GetInitialFlowControlWindowToSend(),
+ config_.GetInitialFlowControlWindowToSend()));
+ } else {
+ flow_controller_.reset(new QuicFlowController(
+ connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+ config_.GetInitialSessionFlowControlWindowToSend(),
+ config_.GetInitialSessionFlowControlWindowToSend()));
+ }
+}
+
+void QuicSession::InitializeSession() {
+ connection_->set_visitor(visitor_shim_.get());
+ connection_->SetFromConfig(config_);
+ if (!FLAGS_quic_unified_timeouts && connection_->connected()) {
+ connection_->SetOverallConnectionTimeout(
+ config_.max_time_before_crypto_handshake());
+ }
+ headers_stream_.reset(new QuicHeadersStream(this));
+}
+
+QuicSession::~QuicSession() {
+ STLDeleteElements(&closed_streams_);
+ STLDeleteValues(&stream_map_);
+
+ DLOG_IF(WARNING,
+ locally_closed_streams_highest_offset_.size() > max_open_streams_)
+ << "Surprisingly high number of locally closed streams still waiting for "
+ "final byte offset: " << locally_closed_streams_highest_offset_.size();
+}
+
+void QuicSession::OnStreamFrames(const vector<QuicStreamFrame>& frames) {
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // TODO(rch) deal with the error case of stream id 0.
+ const QuicStreamFrame& frame = frames[i];
+ QuicStreamId stream_id = frame.stream_id;
+ ReliableQuicStream* stream = GetStream(stream_id);
+ if (!stream) {
+ // The stream no longer exists, but we may still be interested in the
+ // final stream byte offset sent by the peer. A frame with a FIN can give
+ // us this offset.
+ if (frame.fin) {
+ QuicStreamOffset final_byte_offset =
+ frame.offset + frame.data.TotalBufferSize();
+ UpdateFlowControlOnFinalReceivedByteOffset(stream_id,
+ final_byte_offset);
+ }
+
+ continue;
+ }
+ stream->OnStreamFrame(frames[i]);
+ }
+}
+
+void QuicSession::OnStreamHeaders(QuicStreamId stream_id,
+ StringPiece headers_data) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeaders(headers_data);
+}
+
+void QuicSession::OnStreamHeadersPriority(QuicStreamId stream_id,
+ QuicPriority priority) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeadersPriority(priority);
+}
+
+void QuicSession::OnStreamHeadersComplete(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len) {
+ QuicDataStream* stream = GetDataStream(stream_id);
+ if (!stream) {
+ // It's quite possible to receive headers after a stream has been reset.
+ return;
+ }
+ stream->OnStreamHeadersComplete(fin, frame_len);
+}
+
+void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) {
+ if (frame.stream_id == kCryptoStreamId) {
+ connection()->SendConnectionCloseWithDetails(
+ QUIC_INVALID_STREAM_ID,
+ "Attempt to reset the crypto stream");
+ return;
+ }
+ if (frame.stream_id == kHeadersStreamId) {
+ connection()->SendConnectionCloseWithDetails(
+ QUIC_INVALID_STREAM_ID,
+ "Attempt to reset the headers stream");
+ return;
+ }
+
+ QuicDataStream* stream = GetDataStream(frame.stream_id);
+ if (!stream) {
+ // The RST frame contains the final byte offset for the stream: we can now
+ // update the connection level flow controller if needed.
+ UpdateFlowControlOnFinalReceivedByteOffset(frame.stream_id,
+ frame.byte_offset);
+ return; // Errors are handled by GetStream.
+ }
+
+ stream->OnStreamReset(frame);
+}
+
+void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) {
+ DCHECK(frame.last_good_stream_id < next_stream_id_);
+ goaway_received_ = true;
+}
+
+void QuicSession::OnConnectionClosed(QuicErrorCode error, bool from_peer) {
+ DCHECK(!connection_->connected());
+ if (error_ == QUIC_NO_ERROR) {
+ error_ = error;
+ }
+
+ while (!stream_map_.empty()) {
+ DataStreamMap::iterator it = stream_map_.begin();
+ QuicStreamId id = it->first;
+ it->second->OnConnectionClosed(error, from_peer);
+ // The stream should call CloseStream as part of OnConnectionClosed.
+ if (stream_map_.find(id) != stream_map_.end()) {
+ LOG(DFATAL) << ENDPOINT
+ << "Stream failed to close under OnConnectionClosed";
+ CloseStream(id);
+ }
+ }
+}
+
+void QuicSession::OnWindowUpdateFrames(
+ const vector<QuicWindowUpdateFrame>& frames) {
+ bool connection_window_updated = false;
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // Stream may be closed by the time we receive a WINDOW_UPDATE, so we can't
+ // assume that it still exists.
+ QuicStreamId stream_id = frames[i].stream_id;
+ if (stream_id == kConnectionLevelId) {
+ // This is a window update that applies to the connection, rather than an
+ // individual stream.
+ DVLOG(1) << ENDPOINT
+ << "Received connection level flow control window update with "
+ "byte offset: " << frames[i].byte_offset;
+ if (flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) {
+ connection_window_updated = true;
+ }
+ continue;
+ }
+
+ if (connection_->version() < QUIC_VERSION_21 &&
+ (stream_id == kCryptoStreamId || stream_id == kHeadersStreamId)) {
+ DLOG(DFATAL) << "WindowUpdate for stream " << stream_id << " in version "
+ << QuicVersionToString(connection_->version());
+ return;
+ }
+
+ ReliableQuicStream* stream = GetStream(stream_id);
+ if (stream) {
+ stream->OnWindowUpdateFrame(frames[i]);
+ }
+ }
+
+ // Connection level flow control window has increased, so blocked streams can
+ // write again.
+ if (connection_window_updated) {
+ OnCanWrite();
+ }
+}
+
+void QuicSession::OnBlockedFrames(const vector<QuicBlockedFrame>& frames) {
+ for (size_t i = 0; i < frames.size(); ++i) {
+ // TODO(rjshade): Compare our flow control receive windows for specified
+ // streams: if we have a large window then maybe something
+ // had gone wrong with the flow control accounting.
+ DVLOG(1) << ENDPOINT << "Received BLOCKED frame with stream id: "
+ << frames[i].stream_id;
+ }
+}
+
+void QuicSession::OnCanWrite() {
+ // We limit the number of writes to the number of pending streams. If more
+ // streams become pending, WillingAndAbleToWrite will be true, which will
+ // cause the connection to request resumption before yielding to other
+ // connections.
+ size_t num_writes = write_blocked_streams_.NumBlockedStreams();
+ if (flow_controller_->IsBlocked()) {
+ // If we are connection level flow control blocked, then only allow the
+ // crypto and headers streams to try writing as all other streams will be
+ // blocked.
+ num_writes = 0;
+ if (write_blocked_streams_.crypto_stream_blocked()) {
+ num_writes += 1;
+ }
+ if (write_blocked_streams_.headers_stream_blocked()) {
+ num_writes += 1;
+ }
+ }
+ if (num_writes == 0) {
+ return;
+ }
+
+ QuicConnection::ScopedPacketBundler ack_bundler(
+ connection_.get(), QuicConnection::NO_ACK);
+ for (size_t i = 0; i < num_writes; ++i) {
+ if (!(write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams())) {
+ // Writing one stream removed another!? Something's broken.
+ LOG(DFATAL) << "WriteBlockedStream is missing";
+ connection_->CloseConnection(QUIC_INTERNAL_ERROR, false);
+ return;
+ }
+ if (!connection_->CanWriteStreamData()) {
+ return;
+ }
+ QuicStreamId stream_id = write_blocked_streams_.PopFront();
+ if (stream_id == kCryptoStreamId) {
+ has_pending_handshake_ = false; // We just popped it.
+ }
+ ReliableQuicStream* stream = GetStream(stream_id);
+ if (stream != nullptr && !stream->flow_controller()->IsBlocked()) {
+ // If the stream can't write all bytes, it'll re-add itself to the blocked
+ // list.
+ stream->OnCanWrite();
+ }
+ }
+}
+
+bool QuicSession::WillingAndAbleToWrite() const {
+ // If the crypto or headers streams are blocked, we want to schedule a write -
+ // they don't get blocked by connection level flow control. Otherwise only
+ // schedule a write if we are not flow control blocked at the connection
+ // level.
+ return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ (!flow_controller_->IsBlocked() &&
+ write_blocked_streams_.HasWriteBlockedDataStreams());
+}
+
+bool QuicSession::HasPendingHandshake() const {
+ return has_pending_handshake_;
+}
+
+bool QuicSession::HasOpenDataStreams() const {
+ return GetNumOpenStreams() > 0;
+}
+
+QuicConsumedData QuicSession::WritevData(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ return connection_->SendStreamData(id, data, offset, fin, fec_protection,
+ ack_notifier_delegate);
+}
+
+size_t QuicSession::WriteHeaders(
+ QuicStreamId id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ return headers_stream_->WriteHeaders(id, headers, fin, ack_notifier_delegate);
+}
+
+void QuicSession::SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ if (connection()->connected()) {
+ // Only send a RST_STREAM frame if still connected.
+ connection_->SendRstStream(id, error, bytes_written);
+ }
+ CloseStreamInner(id, true);
+}
+
+void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) {
+ if (goaway_sent_) {
+ return;
+ }
+ goaway_sent_ = true;
+ connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason);
+}
+
+void QuicSession::CloseStream(QuicStreamId stream_id) {
+ CloseStreamInner(stream_id, false);
+}
+
+void QuicSession::CloseStreamInner(QuicStreamId stream_id,
+ bool locally_reset) {
+ DVLOG(1) << ENDPOINT << "Closing stream " << stream_id;
+
+ DataStreamMap::iterator it = stream_map_.find(stream_id);
+ if (it == stream_map_.end()) {
+ DVLOG(1) << ENDPOINT << "Stream is already closed: " << stream_id;
+ return;
+ }
+ QuicDataStream* stream = it->second;
+
+ // Tell the stream that a RST has been sent.
+ if (locally_reset) {
+ stream->set_rst_sent(true);
+ }
+
+ closed_streams_.push_back(it->second);
+
+ // If we haven't received a FIN or RST for this stream, we need to keep track
+ // of the how many bytes the stream's flow controller believes it has
+ // received, for accurate connection level flow control accounting.
+ if (!stream->HasFinalReceivedByteOffset() &&
+ stream->flow_controller()->IsEnabled()) {
+ locally_closed_streams_highest_offset_[stream_id] =
+ stream->flow_controller()->highest_received_byte_offset();
+ }
+
+ stream_map_.erase(it);
+ stream->OnClose();
+}
+
+void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset(
+ QuicStreamId stream_id, QuicStreamOffset final_byte_offset) {
+ map<QuicStreamId, QuicStreamOffset>::iterator it =
+ locally_closed_streams_highest_offset_.find(stream_id);
+ if (it == locally_closed_streams_highest_offset_.end()) {
+ return;
+ }
+
+ DVLOG(1) << ENDPOINT << "Received final byte offset " << final_byte_offset
+ << " for stream " << stream_id;
+ uint64 offset_diff = final_byte_offset - it->second;
+ if (flow_controller_->UpdateHighestReceivedOffset(
+ flow_controller_->highest_received_byte_offset() + offset_diff)) {
+ // If the final offset violates flow control, close the connection now.
+ if (flow_controller_->FlowControlViolation()) {
+ connection_->SendConnectionClose(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ return;
+ }
+ }
+
+ flow_controller_->AddBytesConsumed(offset_diff);
+ locally_closed_streams_highest_offset_.erase(it);
+}
+
+bool QuicSession::IsEncryptionEstablished() {
+ return GetCryptoStream()->encryption_established();
+}
+
+bool QuicSession::IsCryptoHandshakeConfirmed() {
+ return GetCryptoStream()->handshake_confirmed();
+}
+
+void QuicSession::OnConfigNegotiated() {
+ connection_->SetFromConfig(config_);
+ QuicVersion version = connection()->version();
+
+ if (FLAGS_quic_allow_more_open_streams) {
+ uint32 max_streams = config_.MaxStreamsPerConnection();
+ if (is_server()) {
+ // A server should accept a small number of additional streams beyond the
+ // limit sent to the client. This helps avoid early connection termination
+ // when FIN/RSTs for old streams are lost or arrive out of order.
+ // Use a minimum number of additional streams, or a percentage increase,
+ // whichever is larger.
+ max_streams =
+ max(max_streams + kMaxStreamsMinimumIncrement,
+ static_cast<uint32>(max_streams * kMaxStreamsMultiplier));
+ }
+ set_max_open_streams(max_streams);
+ }
+
+ if (version <= QUIC_VERSION_19) {
+ // QUIC_VERSION_17,18,19 don't support independent stream/session flow
+ // control windows.
+ if (config_.HasReceivedInitialFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes();
+ OnNewStreamFlowControlWindow(new_window);
+ OnNewSessionFlowControlWindow(new_window);
+ }
+
+ return;
+ }
+
+ // QUIC_VERSION_21 and higher can have independent stream and session flow
+ // control windows.
+ if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
+ // Streams which were created before the SHLO was received (0-RTT
+ // requests) are now informed of the peer's initial flow control window.
+ OnNewStreamFlowControlWindow(
+ config_.ReceivedInitialStreamFlowControlWindowBytes());
+ }
+ if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) {
+ OnNewSessionFlowControlWindow(
+ config_.ReceivedInitialSessionFlowControlWindowBytes());
+ }
+}
+
+void QuicSession::OnNewStreamFlowControlWindow(uint32 new_window) {
+ if (new_window < kDefaultFlowControlSendWindow) {
+ LOG(ERROR)
+ << "Peer sent us an invalid stream flow control send window: "
+ << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ }
+ return;
+ }
+
+ // Inform all existing streams about the new window.
+ if (connection_->version() >= QUIC_VERSION_21) {
+ GetCryptoStream()->flow_controller()->UpdateSendWindowOffset(new_window);
+ headers_stream_->flow_controller()->UpdateSendWindowOffset(new_window);
+ }
+ for (DataStreamMap::iterator it = stream_map_.begin();
+ it != stream_map_.end(); ++it) {
+ it->second->flow_controller()->UpdateSendWindowOffset(new_window);
+ }
+}
+
+void QuicSession::OnNewSessionFlowControlWindow(uint32 new_window) {
+ if (new_window < kDefaultFlowControlSendWindow) {
+ LOG(ERROR)
+ << "Peer sent us an invalid session flow control send window: "
+ << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+ if (connection_->connected()) {
+ connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ }
+ return;
+ }
+
+ flow_controller_->UpdateSendWindowOffset(new_window);
+}
+
+void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
+ switch (event) {
+ // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter
+ // to QuicSession since it is the glue.
+ case ENCRYPTION_FIRST_ESTABLISHED:
+ break;
+
+ case ENCRYPTION_REESTABLISHED:
+ // Retransmit originally packets that were sent, since they can't be
+ // decrypted by the peer.
+ connection_->RetransmitUnackedPackets(ALL_INITIAL_RETRANSMISSION);
+ break;
+
+ case HANDSHAKE_CONFIRMED:
+ LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT
+ << "Handshake confirmed without parameter negotiation.";
+ // Discard originally encrypted packets, since they can't be decrypted by
+ // the peer.
+ connection_->NeuterUnencryptedPackets();
+ if (!FLAGS_quic_unified_timeouts) {
+ connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite());
+ }
+ if (!FLAGS_quic_allow_more_open_streams) {
+ max_open_streams_ = config_.MaxStreamsPerConnection();
+ }
+ break;
+
+ default:
+ LOG(ERROR) << ENDPOINT << "Got unknown handshake event: " << event;
+ }
+}
+
+void QuicSession::OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message) {
+}
+
+void QuicSession::OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message) {
+}
+
+QuicConfig* QuicSession::config() {
+ return &config_;
+}
+
+void QuicSession::ActivateStream(QuicDataStream* stream) {
+ DVLOG(1) << ENDPOINT << "num_streams: " << stream_map_.size()
+ << ". activating " << stream->id();
+ DCHECK_EQ(stream_map_.count(stream->id()), 0u);
+ stream_map_[stream->id()] = stream;
+}
+
+QuicStreamId QuicSession::GetNextStreamId() {
+ QuicStreamId id = next_stream_id_;
+ next_stream_id_ += 2;
+ return id;
+}
+
+ReliableQuicStream* QuicSession::GetStream(const QuicStreamId stream_id) {
+ if (stream_id == kCryptoStreamId) {
+ return GetCryptoStream();
+ }
+ if (stream_id == kHeadersStreamId) {
+ return headers_stream_.get();
+ }
+ return GetDataStream(stream_id);
+}
+
+QuicDataStream* QuicSession::GetDataStream(const QuicStreamId stream_id) {
+ if (stream_id == kCryptoStreamId) {
+ DLOG(FATAL) << "Attempt to call GetDataStream with the crypto stream id";
+ return nullptr;
+ }
+ if (stream_id == kHeadersStreamId) {
+ DLOG(FATAL) << "Attempt to call GetDataStream with the headers stream id";
+ return nullptr;
+ }
+
+ DataStreamMap::iterator it = stream_map_.find(stream_id);
+ if (it != stream_map_.end()) {
+ return it->second;
+ }
+
+ if (IsClosedStream(stream_id)) {
+ return nullptr;
+ }
+
+ if (stream_id % 2 == next_stream_id_ % 2) {
+ // We've received a frame for a locally-created stream that is not
+ // currently active. This is an error.
+ if (connection()->connected()) {
+ connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
+ }
+ return nullptr;
+ }
+
+ return GetIncomingDataStream(stream_id);
+}
+
+QuicDataStream* QuicSession::GetIncomingDataStream(QuicStreamId stream_id) {
+ if (IsClosedStream(stream_id)) {
+ return nullptr;
+ }
+
+ implicitly_created_streams_.erase(stream_id);
+ if (stream_id > largest_peer_created_stream_id_) {
+ if (stream_id - largest_peer_created_stream_id_ > kMaxStreamIdDelta) {
+ // We may already have sent a connection close due to multiple reset
+ // streams in the same packet.
+ if (connection()->connected()) {
+ LOG(ERROR) << "Trying to get stream: " << stream_id
+ << ", largest peer created stream: "
+ << largest_peer_created_stream_id_
+ << ", max delta: " << kMaxStreamIdDelta;
+ connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID);
+ }
+ return nullptr;
+ }
+ if (largest_peer_created_stream_id_ == 0) {
+ if (is_server()) {
+ largest_peer_created_stream_id_= 3;
+ } else {
+ largest_peer_created_stream_id_= 1;
+ }
+ }
+ for (QuicStreamId id = largest_peer_created_stream_id_ + 2;
+ id < stream_id;
+ id += 2) {
+ implicitly_created_streams_.insert(id);
+ }
+ largest_peer_created_stream_id_ = stream_id;
+ }
+ QuicDataStream* stream = CreateIncomingDataStream(stream_id);
+ if (stream == nullptr) {
+ return nullptr;
+ }
+ ActivateStream(stream);
+ return stream;
+}
+
+void QuicSession::set_max_open_streams(size_t max_open_streams) {
+ DVLOG(1) << "Setting max_open_streams_ to " << max_open_streams;
+ max_open_streams_ = max_open_streams;
+}
+
+bool QuicSession::IsClosedStream(QuicStreamId id) {
+ DCHECK_NE(0u, id);
+ if (id == kCryptoStreamId) {
+ return false;
+ }
+ if (id == kHeadersStreamId) {
+ return false;
+ }
+ if (ContainsKey(stream_map_, id)) {
+ // Stream is active
+ return false;
+ }
+ if (id % 2 == next_stream_id_ % 2) {
+ // Locally created streams are strictly in-order. If the id is in the
+ // range of created streams and it's not active, it must have been closed.
+ return id < next_stream_id_;
+ }
+ // For peer created streams, we also need to consider implicitly created
+ // streams.
+ return id <= largest_peer_created_stream_id_ &&
+ implicitly_created_streams_.count(id) == 0;
+}
+
+size_t QuicSession::GetNumOpenStreams() const {
+ return stream_map_.size() + implicitly_created_streams_.size();
+}
+
+void QuicSession::MarkWriteBlocked(QuicStreamId id, QuicPriority priority) {
+#ifndef NDEBUG
+ ReliableQuicStream* stream = GetStream(id);
+ if (stream != nullptr) {
+ LOG_IF(DFATAL, priority != stream->EffectivePriority())
+ << ENDPOINT << "Stream " << id
+ << "Priorities do not match. Got: " << priority
+ << " Expected: " << stream->EffectivePriority();
+ } else {
+ LOG(DFATAL) << "Marking unknown stream " << id << " blocked.";
+ }
+#endif
+
+ if (id == kCryptoStreamId) {
+ DCHECK(!has_pending_handshake_);
+ has_pending_handshake_ = true;
+ // TODO(jar): Be sure to use the highest priority for the crypto stream,
+ // perhaps by adding a "special" priority for it that is higher than
+ // kHighestPriority.
+ priority = kHighestPriority;
+ }
+ write_blocked_streams_.PushBack(id, priority);
+}
+
+bool QuicSession::HasDataToWrite() const {
+ return write_blocked_streams_.HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_streams_.HasWriteBlockedDataStreams() ||
+ connection_->HasQueuedData();
+}
+
+bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) const {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void QuicSession::PostProcessAfterData() {
+ STLDeleteElements(&closed_streams_);
+ closed_streams_.clear();
+
+ if (FLAGS_close_quic_connection_unfinished_streams_2 &&
+ connection()->connected() &&
+ locally_closed_streams_highest_offset_.size() > max_open_streams_) {
+ // A buggy client may fail to send FIN/RSTs. Don't tolerate this.
+ connection_->SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS);
+ }
+}
+
+void QuicSession::OnSuccessfulVersionNegotiation(const QuicVersion& version) {
+ if (version < QUIC_VERSION_19) {
+ flow_controller_->Disable();
+ }
+
+ // Disable stream level flow control based on negotiated version. Streams may
+ // have been created with a different version.
+ if (version < QUIC_VERSION_21) {
+ GetCryptoStream()->flow_controller()->Disable();
+ headers_stream_->flow_controller()->Disable();
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
new file mode 100644
index 0000000..7a52fba
--- /dev/null
+++ b/net/quic/quic_session.h
@@ -0,0 +1,331 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// A QuicSession, which demuxes a single connection to individual streams.
+
+#ifndef NET_QUIC_QUIC_SESSION_H_
+#define NET_QUIC_QUIC_SESSION_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_headers_stream.h"
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_write_blocked_list.h"
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+
+class QuicCryptoStream;
+class QuicFlowController;
+class ReliableQuicStream;
+class SSLInfo;
+class VisitorShim;
+
+namespace test {
+class QuicSessionPeer;
+} // namespace test
+
+class NET_EXPORT_PRIVATE QuicSession : public QuicConnectionVisitorInterface {
+ public:
+ // CryptoHandshakeEvent enumerates the events generated by a QuicCryptoStream.
+ enum CryptoHandshakeEvent {
+ // ENCRYPTION_FIRST_ESTABLISHED indicates that a full client hello has been
+ // sent by a client and that subsequent packets will be encrypted. (Client
+ // only.)
+ ENCRYPTION_FIRST_ESTABLISHED,
+ // ENCRYPTION_REESTABLISHED indicates that a client hello was rejected by
+ // the server and thus the encryption key has been updated. Therefore the
+ // connection should resend any packets that were sent under
+ // ENCRYPTION_INITIAL. (Client only.)
+ ENCRYPTION_REESTABLISHED,
+ // HANDSHAKE_CONFIRMED, in a client, indicates the the server has accepted
+ // our handshake. In a server it indicates that a full, valid client hello
+ // has been received. (Client and server.)
+ HANDSHAKE_CONFIRMED,
+ };
+
+ QuicSession(QuicConnection* connection, const QuicConfig& config);
+ void InitializeSession();
+
+ virtual ~QuicSession();
+
+ // QuicConnectionVisitorInterface methods:
+ virtual void OnStreamFrames(
+ const std::vector<QuicStreamFrame>& frames) OVERRIDE;
+ virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual void OnWindowUpdateFrames(
+ const std::vector<QuicWindowUpdateFrame>& frames) OVERRIDE;
+ virtual void OnBlockedFrames(
+ const std::vector<QuicBlockedFrame>& frames) OVERRIDE;
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE;
+ virtual void OnWriteBlocked() OVERRIDE {}
+ virtual void OnSuccessfulVersionNegotiation(
+ const QuicVersion& version) OVERRIDE;
+ virtual void OnCanWrite() OVERRIDE;
+ virtual void OnCongestionWindowChange(QuicTime now) OVERRIDE {}
+ virtual bool WillingAndAbleToWrite() const OVERRIDE;
+ virtual bool HasPendingHandshake() const OVERRIDE;
+ virtual bool HasOpenDataStreams() const OVERRIDE;
+
+ // Called by the headers stream when headers have been received for a stream.
+ virtual void OnStreamHeaders(QuicStreamId stream_id,
+ base::StringPiece headers_data);
+ // Called by the headers stream when headers with a priority have been
+ // received for this stream. This method will only be called for server
+ // streams.
+ virtual void OnStreamHeadersPriority(QuicStreamId stream_id,
+ QuicPriority priority);
+ // Called by the headers stream when headers have been completely received
+ // for a stream. |fin| will be true if the fin flag was set in the headers
+ // frame.
+ virtual void OnStreamHeadersComplete(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len);
+
+ // Called by streams when they want to write data to the peer.
+ // Returns a pair with the number of bytes consumed from data, and a boolean
+ // indicating if the fin bit was consumed. This does not indicate the data
+ // has been sent on the wire: it may have been turned into a packet and queued
+ // if the socket was unexpectedly blocked. |fec_protection| indicates if
+ // data is to be FEC protected. Note that data that is sent immediately
+ // following MUST_FEC_PROTECT data may get protected by falling within the
+ // same FEC group.
+ // If provided, |ack_notifier_delegate| will be registered to be notified when
+ // we have seen ACKs for all packets resulting from this call.
+ virtual QuicConsumedData WritevData(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // Writes |headers| for the stream |id| to the dedicated headers stream.
+ // If |fin| is true, then no more data will be sent for the stream |id|.
+ // If provided, |ack_notifier_delegate| will be registered to be notified when
+ // we have seen ACKs for all packets resulting from this call.
+ size_t WriteHeaders(
+ QuicStreamId id,
+ const SpdyHeaderBlock& headers,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // Called by streams when they want to close the stream in both directions.
+ virtual void SendRstStream(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written);
+
+ // Called when the session wants to go away and not accept any new streams.
+ void SendGoAway(QuicErrorCode error_code, const std::string& reason);
+
+ // Removes the stream associated with 'stream_id' from the active stream map.
+ virtual void CloseStream(QuicStreamId stream_id);
+
+ // Returns true if outgoing packets will be encrypted, even if the server
+ // hasn't confirmed the handshake yet.
+ virtual bool IsEncryptionEstablished();
+
+ // For a client, returns true if the server has confirmed our handshake. For
+ // a server, returns true if a full, valid client hello has been received.
+ virtual bool IsCryptoHandshakeConfirmed();
+
+ // Called by the QuicCryptoStream when a new QuicConfig has been negotiated.
+ virtual void OnConfigNegotiated();
+
+ // Called by the QuicCryptoStream when the handshake enters a new state.
+ //
+ // Clients will call this function in the order:
+ // ENCRYPTION_FIRST_ESTABLISHED
+ // zero or more ENCRYPTION_REESTABLISHED
+ // HANDSHAKE_CONFIRMED
+ //
+ // Servers will simply call it once with HANDSHAKE_CONFIRMED.
+ virtual void OnCryptoHandshakeEvent(CryptoHandshakeEvent event);
+
+ // Called by the QuicCryptoStream when a handshake message is sent.
+ virtual void OnCryptoHandshakeMessageSent(
+ const CryptoHandshakeMessage& message);
+
+ // Called by the QuicCryptoStream when a handshake message is received.
+ virtual void OnCryptoHandshakeMessageReceived(
+ const CryptoHandshakeMessage& message);
+
+ // Returns mutable config for this session. Returned config is owned
+ // by QuicSession.
+ QuicConfig* config();
+
+ // Returns true if the stream existed previously and has been closed.
+ // Returns false if the stream is still active or if the stream has
+ // not yet been created.
+ bool IsClosedStream(QuicStreamId id);
+
+ QuicConnection* connection() { return connection_.get(); }
+ const QuicConnection* connection() const { return connection_.get(); }
+ size_t num_active_requests() const { return stream_map_.size(); }
+ const IPEndPoint& peer_address() const {
+ return connection_->peer_address();
+ }
+ QuicConnectionId connection_id() const {
+ return connection_->connection_id();
+ }
+
+ // Returns the number of currently open streams, including those which have
+ // been implicitly created, but excluding the reserved headers and crypto
+ // streams.
+ virtual size_t GetNumOpenStreams() const;
+
+ void MarkWriteBlocked(QuicStreamId id, QuicPriority priority);
+
+ // Returns true if the session has data to be sent, either queued in the
+ // connection, or in a write-blocked stream.
+ bool HasDataToWrite() const;
+
+ bool goaway_received() const {
+ return goaway_received_;
+ }
+
+ bool goaway_sent() const {
+ return goaway_sent_;
+ }
+
+ // Gets the SSL connection information.
+ virtual bool GetSSLInfo(SSLInfo* ssl_info) const;
+
+ QuicErrorCode error() const { return error_; }
+
+ bool is_server() const { return connection_->is_server(); }
+
+ QuicFlowController* flow_controller() { return flow_controller_.get(); }
+
+ size_t get_max_open_streams() const { return max_open_streams_; }
+
+ protected:
+ typedef base::hash_map<QuicStreamId, QuicDataStream*> DataStreamMap;
+
+ // Creates a new stream, owned by the caller, to handle a peer-initiated
+ // stream. Returns nullptr and does error handling if the stream can not be
+ // created.
+ virtual QuicDataStream* CreateIncomingDataStream(QuicStreamId id) = 0;
+
+ // Create a new stream, owned by the caller, to handle a locally-initiated
+ // stream. Returns nullptr if max streams have already been opened.
+ virtual QuicDataStream* CreateOutgoingDataStream() = 0;
+
+ // Return the reserved crypto stream.
+ virtual QuicCryptoStream* GetCryptoStream() = 0;
+
+ // Adds 'stream' to the active stream map.
+ virtual void ActivateStream(QuicDataStream* stream);
+
+ // Returns the stream id for a new stream.
+ QuicStreamId GetNextStreamId();
+
+ QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id);
+
+ QuicDataStream* GetDataStream(const QuicStreamId stream_id);
+
+ ReliableQuicStream* GetStream(const QuicStreamId stream_id);
+
+ // This is called after every call other than OnConnectionClose from the
+ // QuicConnectionVisitor to allow post-processing once the work has been done.
+ // In this case, it deletes streams given that it's safe to do so (no other
+ // operations are being done on the streams at this time)
+ virtual void PostProcessAfterData();
+
+ base::hash_map<QuicStreamId, QuicDataStream*>* streams() {
+ return &stream_map_;
+ }
+
+ const base::hash_map<QuicStreamId, QuicDataStream*>* streams() const {
+ return &stream_map_;
+ }
+
+ std::vector<QuicDataStream*>* closed_streams() { return &closed_streams_; }
+
+ void set_max_open_streams(size_t max_open_streams);
+
+ scoped_ptr<QuicHeadersStream> headers_stream_;
+
+ private:
+ friend class test::QuicSessionPeer;
+ friend class VisitorShim;
+
+ // Performs the work required to close |stream_id|. If |locally_reset|
+ // then the stream has been reset by this endpoint, not by the peer.
+ void CloseStreamInner(QuicStreamId stream_id, bool locally_reset);
+
+ // When a stream is closed locally, it may not yet know how many bytes the
+ // peer sent on that stream.
+ // When this data arrives (via stream frame w. FIN, or RST) this method
+ // is called, and correctly updates the connection level flow controller.
+ void UpdateFlowControlOnFinalReceivedByteOffset(
+ QuicStreamId id, QuicStreamOffset final_byte_offset);
+
+ // Called in OnConfigNegotiated when we receive a new stream level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewStreamFlowControlWindow(uint32 new_window);
+
+ // Called in OnConfigNegotiated when we receive a new session level flow
+ // control window in a negotiated config. Closes the connection if invalid.
+ void OnNewSessionFlowControlWindow(uint32 new_window);
+
+ // Keep track of highest received byte offset of locally closed streams, while
+ // waiting for a definitive final highest offset from the peer.
+ std::map<QuicStreamId, QuicStreamOffset>
+ locally_closed_streams_highest_offset_;
+
+ scoped_ptr<QuicConnection> connection_;
+
+ // A shim to stand between the connection and the session, to handle stream
+ // deletions.
+ scoped_ptr<VisitorShim> visitor_shim_;
+
+ std::vector<QuicDataStream*> closed_streams_;
+
+ QuicConfig config_;
+
+ // Returns the maximum number of streams this connection can open.
+ size_t max_open_streams_;
+
+ // Map from StreamId to pointers to streams that are owned by the caller.
+ DataStreamMap stream_map_;
+ QuicStreamId next_stream_id_;
+
+ // Set of stream ids that have been "implicitly created" by receipt
+ // of a stream id larger than the next expected stream id.
+ base::hash_set<QuicStreamId> implicitly_created_streams_;
+
+ // A list of streams which need to write more data.
+ QuicWriteBlockedList write_blocked_streams_;
+
+ QuicStreamId largest_peer_created_stream_id_;
+
+ // The latched error with which the connection was closed.
+ QuicErrorCode error_;
+
+ // Whether a GoAway has been received.
+ bool goaway_received_;
+ // Whether a GoAway has been sent.
+ bool goaway_sent_;
+
+ // Indicate if there is pending data for the crypto stream.
+ bool has_pending_handshake_;
+
+ // Used for session level flow control.
+ scoped_ptr<QuicFlowController> flow_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSession);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SESSION_H_
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
new file mode 100644
index 0000000..a36b77c
--- /dev/null
+++ b/net/quic/quic_session_test.cc
@@ -0,0 +1,954 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_session.h"
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_flags.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_data_stream_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::hash_map;
+using std::set;
+using std::vector;
+using testing::CreateFunctor;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+const QuicPriority kHighestPriority = 0;
+const QuicPriority kSomeMiddlePriority = 3;
+
+class TestCryptoStream : public QuicCryptoStream {
+ public:
+ explicit TestCryptoStream(QuicSession* session)
+ : QuicCryptoStream(session) {
+ }
+
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE {
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+ CryptoHandshakeMessage msg;
+ string error_details;
+ session()->config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session()->config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session()->config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session()->config()->ToHandshakeMessage(&msg);
+ const QuicErrorCode error = session()->config()->ProcessPeerHello(
+ msg, CLIENT, &error_details);
+ EXPECT_EQ(QUIC_NO_ERROR, error);
+ session()->OnConfigNegotiated();
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+};
+
+class TestHeadersStream : public QuicHeadersStream {
+ public:
+ explicit TestHeadersStream(QuicSession* session)
+ : QuicHeadersStream(session) {
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+};
+
+class TestStream : public QuicDataStream {
+ public:
+ TestStream(QuicStreamId id, QuicSession* session)
+ : QuicDataStream(id, session) {
+ }
+
+ using ReliableQuicStream::CloseWriteSide;
+
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE {
+ return data_len;
+ }
+
+ void SendBody(const string& data, bool fin) {
+ WriteOrBufferData(data, fin, nullptr);
+ }
+
+ MOCK_METHOD0(OnCanWrite, void());
+};
+
+// Poor man's functor for use as callback in a mock.
+class StreamBlocker {
+ public:
+ StreamBlocker(QuicSession* session, QuicStreamId stream_id)
+ : session_(session),
+ stream_id_(stream_id) {
+ }
+
+ void MarkWriteBlocked() {
+ session_->MarkWriteBlocked(stream_id_, kSomeMiddlePriority);
+ }
+
+ private:
+ QuicSession* const session_;
+ const QuicStreamId stream_id_;
+};
+
+class TestSession : public QuicSession {
+ public:
+ explicit TestSession(QuicConnection* connection)
+ : QuicSession(connection,
+ DefaultQuicConfig()),
+ crypto_stream_(this),
+ writev_consumes_all_data_(false) {
+ InitializeSession();
+ }
+
+ virtual TestCryptoStream* GetCryptoStream() OVERRIDE {
+ return &crypto_stream_;
+ }
+
+ virtual TestStream* CreateOutgoingDataStream() OVERRIDE {
+ TestStream* stream = new TestStream(GetNextStreamId(), this);
+ ActivateStream(stream);
+ return stream;
+ }
+
+ virtual TestStream* CreateIncomingDataStream(QuicStreamId id) OVERRIDE {
+ return new TestStream(id, this);
+ }
+
+ bool IsClosedStream(QuicStreamId id) {
+ return QuicSession::IsClosedStream(id);
+ }
+
+ QuicDataStream* GetIncomingDataStream(QuicStreamId stream_id) {
+ return QuicSession::GetIncomingDataStream(stream_id);
+ }
+
+ virtual QuicConsumedData WritevData(
+ QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) OVERRIDE {
+ // Always consumes everything.
+ if (writev_consumes_all_data_) {
+ return QuicConsumedData(data.TotalBufferSize(), fin);
+ } else {
+ return QuicSession::WritevData(id, data, offset, fin, fec_protection,
+ ack_notifier_delegate);
+ }
+ }
+
+ void set_writev_consumes_all_data(bool val) {
+ writev_consumes_all_data_ = val;
+ }
+
+ QuicConsumedData SendStreamData(QuicStreamId id) {
+ return WritevData(id, IOVector(), 0, true, MAY_FEC_PROTECT, nullptr);
+ }
+
+ using QuicSession::PostProcessAfterData;
+
+ private:
+ StrictMock<TestCryptoStream> crypto_stream_;
+
+ bool writev_consumes_all_data_;
+};
+
+class QuicSessionTest : public ::testing::TestWithParam<QuicVersion> {
+ protected:
+ QuicSessionTest()
+ : connection_(new MockConnection(true, SupportedVersions(GetParam()))),
+ session_(connection_) {
+ session_.config()->SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ session_.config()->SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ session_.config()->SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ headers_[":host"] = "www.google.com";
+ headers_[":path"] = "/index.hml";
+ headers_[":scheme"] = "http";
+ headers_["cookie"] =
+ "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+ "__utmc=160408618; "
+ "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+ "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+ "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+ "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+ "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+ "1zFMi5vzcns38-8_Sns; "
+ "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+ "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+ "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+ "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+ "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+ "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+ "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+ "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+ "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+ "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+ "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+ "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+ "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+ "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+ "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
+ }
+
+ void CheckClosedStreams() {
+ for (int i = kCryptoStreamId; i < 100; i++) {
+ if (closed_streams_.count(i) == 0) {
+ EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i;
+ } else {
+ EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i;
+ }
+ }
+ }
+
+ void CloseStream(QuicStreamId id) {
+ session_.CloseStream(id);
+ closed_streams_.insert(id);
+ }
+
+ QuicVersion version() const { return connection_->version(); }
+
+ MockConnection* connection_;
+ TestSession session_;
+ set<QuicStreamId> closed_streams_;
+ SpdyHeaderBlock headers_;
+};
+
+INSTANTIATE_TEST_CASE_P(Tests, QuicSessionTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicSessionTest, PeerAddress) {
+ EXPECT_EQ(IPEndPoint(Loopback4(), kTestPort), session_.peer_address());
+}
+
+TEST_P(QuicSessionTest, IsCryptoHandshakeConfirmed) {
+ EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed());
+ CryptoHandshakeMessage message;
+ session_.GetCryptoStream()->OnHandshakeMessage(message);
+ EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed());
+}
+
+TEST_P(QuicSessionTest, IsClosedStreamDefault) {
+ // Ensure that no streams are initially closed.
+ for (int i = kCryptoStreamId; i < 100; i++) {
+ EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i;
+ }
+}
+
+TEST_P(QuicSessionTest, ImplicitlyCreatedStreams) {
+ ASSERT_TRUE(session_.GetIncomingDataStream(7) != nullptr);
+ // Both 3 and 5 should be implicitly created.
+ EXPECT_FALSE(session_.IsClosedStream(3));
+ EXPECT_FALSE(session_.IsClosedStream(5));
+ ASSERT_TRUE(session_.GetIncomingDataStream(5) != nullptr);
+ ASSERT_TRUE(session_.GetIncomingDataStream(3) != nullptr);
+}
+
+TEST_P(QuicSessionTest, IsClosedStreamLocallyCreated) {
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ EXPECT_EQ(2u, stream2->id());
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ EXPECT_EQ(4u, stream4->id());
+
+ CheckClosedStreams();
+ CloseStream(4);
+ CheckClosedStreams();
+ CloseStream(2);
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSessionTest, IsClosedStreamPeerCreated) {
+ QuicStreamId stream_id1 = kClientDataStreamId1;
+ QuicStreamId stream_id2 = kClientDataStreamId2;
+ QuicDataStream* stream1 = session_.GetIncomingDataStream(stream_id1);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream1, true);
+ QuicDataStream* stream2 = session_.GetIncomingDataStream(stream_id2);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream2, true);
+
+ CheckClosedStreams();
+ CloseStream(stream_id1);
+ CheckClosedStreams();
+ CloseStream(stream_id2);
+ // Create a stream explicitly, and another implicitly.
+ QuicDataStream* stream3 = session_.GetIncomingDataStream(stream_id2 + 4);
+ QuicDataStreamPeer::SetHeadersDecompressed(stream3, true);
+ CheckClosedStreams();
+ // Close one, but make sure the other is still not closed
+ CloseStream(stream3->id());
+ CheckClosedStreams();
+}
+
+TEST_P(QuicSessionTest, StreamIdTooLarge) {
+ QuicStreamId stream_id = kClientDataStreamId1;
+ session_.GetIncomingDataStream(stream_id);
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID));
+ session_.GetIncomingDataStream(stream_id + kMaxStreamIdDelta + 2);
+}
+
+TEST_P(QuicSessionTest, DecompressionError) {
+ QuicHeadersStream* stream = QuicSessionPeer::GetHeadersStream(&session_);
+ const unsigned char data[] = {
+ 0x80, 0x03, 0x00, 0x01, // SPDY/3 SYN_STREAM frame
+ 0x00, 0x00, 0x00, 0x25, // flags/length
+ 0x00, 0x00, 0x00, 0x05, // stream id
+ 0x00, 0x00, 0x00, 0x00, // associated stream id
+ 0x00, 0x00,
+ 'a', 'b', 'c', 'd' // invalid compressed data
+ };
+ EXPECT_CALL(*connection_,
+ SendConnectionCloseWithDetails(QUIC_INVALID_HEADERS_STREAM_DATA,
+ "SPDY framing error."));
+ stream->ProcessRawData(reinterpret_cast<const char*>(data),
+ arraysize(data));
+}
+
+TEST_P(QuicSessionTest, DebugDFatalIfMarkingClosedStreamWriteBlocked) {
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ // Close the stream.
+ stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD);
+ // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL.
+ /*
+ QuicStreamId kClosedStreamId = stream2->id();
+ EXPECT_DEBUG_DFATAL(
+ session_.MarkWriteBlocked(kClosedStreamId, kSomeMiddlePriority),
+ "Marking unknown stream 2 blocked.");
+ */
+}
+
+TEST_P(QuicSessionTest, DebugDFatalIfMarkWriteBlockedCalledWithWrongPriority) {
+ const QuicPriority kDifferentPriority = 0;
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ EXPECT_NE(kDifferentPriority, stream2->EffectivePriority());
+ // TODO(rtenneti): enable when chromium supports EXPECT_DEBUG_DFATAL.
+ /*
+ EXPECT_DEBUG_DFATAL(
+ session_.MarkWriteBlocked(stream2->id(), kDifferentPriority),
+ "Priorities do not match. Got: 0 Expected: 3");
+ */
+}
+
+TEST_P(QuicSessionTest, OnCanWrite) {
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ InSequence s;
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ // Reregister, to test the loop limit.
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, OnCanWriteBundlesStreams) {
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillRepeatedly(
+ Return(QuicTime::Delta::Zero()));
+ EXPECT_CALL(*send_algorithm, GetCongestionWindow())
+ .WillOnce(Return(kMaxPacketSize * 10));
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream2->id()))));
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream4->id()))));
+ EXPECT_CALL(*stream6, OnCanWrite())
+ .WillOnce(IgnoreResult(Invoke(CreateFunctor(
+ &session_, &TestSession::SendStreamData, stream6->id()))));
+
+ // Expect that we only send one packet, the writes from different streams
+ // should be bundled together.
+ MockPacketWriter* writer =
+ static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _)).WillOnce(
+ Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)).Times(1);
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, OnCanWriteCongestionControlBlocks) {
+ InSequence s;
+
+ // Drive congestion control manually.
+ MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>;
+ QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm);
+
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream6, OnCanWrite());
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ // stream4->OnCanWrite is not called.
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // Still congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Infinite()));
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+
+ // stream4->OnCanWrite is called once the connection stops being
+ // congestion-control blocked.
+ EXPECT_CALL(*send_algorithm, TimeUntilSend(_, _, _)).WillOnce(Return(
+ QuicTime::Delta::Zero()));
+ EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, BufferedHandshake) {
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Default value.
+
+ // Test that blocking other streams does not change our status.
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ StreamBlocker stream2_blocker(&session_, stream2->id());
+ stream2_blocker.MarkWriteBlocked();
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ TestStream* stream3 = session_.CreateOutgoingDataStream();
+ StreamBlocker stream3_blocker(&session_, stream3->id());
+ stream3_blocker.MarkWriteBlocked();
+ EXPECT_FALSE(session_.HasPendingHandshake());
+
+ // Blocking (due to buffering of) the Crypto stream is detected.
+ session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority);
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ StreamBlocker stream4_blocker(&session_, stream4->id());
+ stream4_blocker.MarkWriteBlocked();
+ EXPECT_TRUE(session_.HasPendingHandshake());
+
+ InSequence s;
+ // Force most streams to re-register, which is common scenario when we block
+ // the Crypto stream, and only the crypto stream can "really" write.
+
+ // Due to prioritization, we *should* be asked to write the crypto stream
+ // first.
+ // Don't re-register the crypto stream (which signals complete writing).
+ TestCryptoStream* crypto_stream = session_.GetCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite());
+
+ // Re-register all other streams, to show they weren't able to proceed.
+ EXPECT_CALL(*stream2, OnCanWrite())
+ .WillOnce(Invoke(&stream2_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream3, OnCanWrite())
+ .WillOnce(Invoke(&stream3_blocker, &StreamBlocker::MarkWriteBlocked));
+ EXPECT_CALL(*stream4, OnCanWrite())
+ .WillOnce(Invoke(&stream4_blocker, &StreamBlocker::MarkWriteBlocked));
+
+ session_.OnCanWrite();
+ EXPECT_TRUE(session_.WillingAndAbleToWrite());
+ EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote.
+}
+
+TEST_P(QuicSessionTest, OnCanWriteWithClosedStream) {
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ TestStream* stream4 = session_.CreateOutgoingDataStream();
+ TestStream* stream6 = session_.CreateOutgoingDataStream();
+
+ session_.MarkWriteBlocked(stream2->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream6->id(), kSomeMiddlePriority);
+ session_.MarkWriteBlocked(stream4->id(), kSomeMiddlePriority);
+ CloseStream(stream6->id());
+
+ InSequence s;
+ EXPECT_CALL(*stream2, OnCanWrite());
+ EXPECT_CALL(*stream4, OnCanWrite());
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Ensure connection level flow control blockage.
+ QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
+ EXPECT_TRUE(session_.flow_controller()->IsBlocked());
+
+ // Mark the crypto and headers streams as write blocked, we expect them to be
+ // allowed to write later.
+ session_.MarkWriteBlocked(kCryptoStreamId, kHighestPriority);
+ session_.MarkWriteBlocked(kHeadersStreamId, kHighestPriority);
+
+ // Create a data stream, and although it is write blocked we never expect it
+ // to be allowed to write as we are connection level flow control blocked.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ session_.MarkWriteBlocked(stream->id(), kSomeMiddlePriority);
+ EXPECT_CALL(*stream, OnCanWrite()).Times(0);
+
+ // The crypto and headers streams should be called even though we are
+ // connection flow control blocked.
+ TestCryptoStream* crypto_stream = session_.GetCryptoStream();
+ EXPECT_CALL(*crypto_stream, OnCanWrite()).Times(1);
+ TestHeadersStream* headers_stream = new TestHeadersStream(&session_);
+ QuicSessionPeer::SetHeadersStream(&session_, headers_stream);
+ EXPECT_CALL(*headers_stream, OnCanWrite()).Times(1);
+
+ session_.OnCanWrite();
+ EXPECT_FALSE(session_.WillingAndAbleToWrite());
+}
+
+TEST_P(QuicSessionTest, SendGoAway) {
+ EXPECT_CALL(*connection_,
+ SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away."));
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+
+ EXPECT_CALL(*connection_,
+ SendRstStream(3u, QUIC_STREAM_PEER_GOING_AWAY, 0)).Times(0);
+ EXPECT_TRUE(session_.GetIncomingDataStream(3u));
+}
+
+TEST_P(QuicSessionTest, DoNotSendGoAwayTwice) {
+ EXPECT_CALL(*connection_,
+ SendGoAway(QUIC_PEER_GOING_AWAY, 0u, "Going Away.")).Times(1);
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+ EXPECT_TRUE(session_.goaway_sent());
+ session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away.");
+}
+
+TEST_P(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) {
+ EXPECT_EQ((FLAGS_quic_unified_timeouts ?
+ kInitialIdleTimeoutSecs : kDefaultIdleTimeoutSecs) + 1,
+ QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+ CryptoHandshakeMessage msg;
+ session_.GetCryptoStream()->OnHandshakeMessage(msg);
+ EXPECT_EQ(kMaximumIdleTimeoutSecs + 1,
+ QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds());
+}
+
+TEST_P(QuicSessionTest, RstStreamBeforeHeadersDecompressed) {
+ // Send two bytes of payload.
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
+
+ QuicRstStreamFrame rst1(kClientDataStreamId1, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst1);
+ EXPECT_EQ(0u, session_.GetNumOpenStreams());
+ // Connection should remain alive.
+ EXPECT_TRUE(connection_->connected());
+}
+
+TEST_P(QuicSessionTest, MultipleRstStreamsCauseSingleConnectionClose) {
+ // If multiple invalid reset stream frames arrive in a single packet, this
+ // should trigger a connection close. However there is no need to send
+ // multiple connection close frames.
+
+ // Create valid stream.
+ QuicStreamFrame data1(kClientDataStreamId1, false, 0, MakeIOVector("HT"));
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
+
+ // Process first invalid stream reset, resulting in the connection being
+ // closed.
+ EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID))
+ .Times(1);
+ QuicStreamId kLargeInvalidStreamId = 99999999;
+ QuicRstStreamFrame rst1(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst1);
+ QuicConnectionPeer::CloseConnection(connection_);
+
+ // Processing of second invalid stream reset should not result in the
+ // connection being closed for a second time.
+ QuicRstStreamFrame rst2(kLargeInvalidStreamId, QUIC_STREAM_NO_ERROR, 0);
+ session_.OnRstStream(rst2);
+}
+
+TEST_P(QuicSessionTest, HandshakeUnblocksFlowControlBlockedStream) {
+ // Test that if a stream is flow control blocked, then on receipt of the SHLO
+ // containing a suitable send window offset, the stream becomes unblocked.
+
+ // Ensure that Writev consumes all the data it is given (simulate no socket
+ // blocking).
+ session_.set_writev_consumes_all_data(true);
+
+ // Create a stream, and send enough data to make it flow control blocked.
+ TestStream* stream2 = session_.CreateOutgoingDataStream();
+ string body(kDefaultFlowControlSendWindow, '.');
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+ stream2->SendBody(body, false);
+ EXPECT_TRUE(stream2->flow_controller()->IsBlocked());
+
+ // Now complete the crypto handshake, resulting in an increased flow control
+ // send window.
+ CryptoHandshakeMessage msg;
+ session_.GetCryptoStream()->OnHandshakeMessage(msg);
+
+ // Stream is now unblocked.
+ EXPECT_FALSE(stream2->flow_controller()->IsBlocked());
+}
+
+TEST_P(QuicSessionTest, InvalidFlowControlWindowInHandshake) {
+ // TODO(rjshade): Remove this test when removing QUIC_VERSION_19.
+ // Test that receipt of an invalid (< default) flow control window from
+ // the peer results in the connection being torn down.
+ if (version() > QUIC_VERSION_19) {
+ return;
+ }
+
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)).Times(2);
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) stream flow control window from
+ // the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_19) {
+ return;
+ }
+
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) {
+ // Test that receipt of an invalid (< default) session flow control window
+ // from the peer results in the connection being torn down.
+ if (version() <= QUIC_VERSION_19) {
+ return;
+ }
+
+ uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+ QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(),
+ kInvalidWindow);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+ session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Test that when we receive an out of order stream RST we correctly adjust
+ // our connection level flow control receive window.
+ // On close, the stream should mark as consumed all bytes between the highest
+ // byte consumed so far and the final byte offset from the RST frame.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+ kByteOffset)).Times(1);
+
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kByteOffset);
+ session_.OnRstStream(rst_frame);
+ session_.PostProcessAfterData();
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Test the situation where we receive a FIN on a stream, and before we fully
+ // consume all the data from the sequencer buffer we locally RST the stream.
+ // The bytes between highest consumed byte, and the final byte offset that we
+ // determined when the FIN arrived, should be marked as consumed at the
+ // connection level flow controller when the stream is reset.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+
+ const QuicStreamOffset kByteOffset =
+ 1 + kInitialSessionFlowControlWindowForTest / 2;
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector());
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+ session_.PostProcessAfterData();
+
+ EXPECT_EQ(0u, stream->flow_controller()->bytes_consumed());
+ EXPECT_EQ(kByteOffset,
+ stream->flow_controller()->highest_received_byte_offset());
+
+ // We only expect to see a connection WINDOW_UPDATE when talking
+ // QUIC_VERSION_19, as in this case both stream and session flow control
+ // windows are the same size. In later versions we will not see a connection
+ // level WINDOW_UPDATE when exhausting a stream, as the stream flow control
+ // limit is much lower than the connection flow control limit.
+ if (version() == QUIC_VERSION_19) {
+ // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+ EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+ // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+ EXPECT_CALL(*connection_,
+ SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+ kByteOffset)).Times(1);
+ }
+
+ // Reset stream locally.
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a FIN from the peer, we correctly adjust our connection level flow
+ // control receive window.
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64 kInitialConnectionBytesConsumed = 567;
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a response from the peer with a FIN. We should handle this by
+ // adjusting the connection level flow control receive window to take into
+ // account the total number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ string body = "hello";
+ IOVector data = MakeIOVector(body);
+ QuicStreamFrame frame(stream->id(), true, kByteOffset, data);
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+
+ QuicStreamOffset total_stream_bytes_sent_by_peer =
+ kByteOffset + body.length();
+ EXPECT_EQ(kInitialConnectionBytesConsumed + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(
+ kInitialConnectionHighestReceivedOffset + total_stream_bytes_sent_by_peer,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) {
+ // Test that when we RST the stream (and tear down stream state), and then
+ // receive a RST from the peer, we correctly adjust our connection level flow
+ // control receive window.
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Connection starts with some non-zero highest received byte offset,
+ // due to other active streams.
+ const uint64 kInitialConnectionBytesConsumed = 567;
+ const uint64 kInitialConnectionHighestReceivedOffset = 1234;
+ EXPECT_LT(kInitialConnectionBytesConsumed,
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->UpdateHighestReceivedOffset(
+ kInitialConnectionHighestReceivedOffset);
+ session_.flow_controller()->AddBytesConsumed(kInitialConnectionBytesConsumed);
+
+ // Reset our stream: this results in the stream being closed locally.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+
+ // Now receive a RST from the peer. We should handle this by adjusting the
+ // connection level flow control receive window to take into account the total
+ // number of bytes sent by the peer.
+ const QuicStreamOffset kByteOffset = 5678;
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kByteOffset);
+ session_.OnRstStream(rst_frame);
+
+ EXPECT_EQ(kInitialConnectionBytesConsumed + kByteOffset,
+ session_.flow_controller()->bytes_consumed());
+ EXPECT_EQ(kInitialConnectionHighestReceivedOffset + kByteOffset,
+ session_.flow_controller()->highest_received_byte_offset());
+}
+
+TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) {
+ // Test that if we receive a stream RST with a highest byte offset that
+ // violates flow control, that we close the connection.
+ const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA))
+ .Times(2);
+
+ // Check that stream frame + FIN results in connection close.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ stream->Reset(QUIC_STREAM_CANCELLED);
+ QuicStreamFrame frame(stream->id(), true, kLargeOffset, IOVector());
+ vector<QuicStreamFrame> frames;
+ frames.push_back(frame);
+ session_.OnStreamFrames(frames);
+
+ // Check that RST results in connection close.
+ QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
+ kLargeOffset);
+ session_.OnRstStream(rst_frame);
+}
+
+TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) {
+ if (version() < QUIC_VERSION_19) {
+ return;
+ }
+
+ // Test that after successful version negotiation, flow control is disabled
+ // appropriately at both the connection and stream level.
+
+ // Initially both stream and connection flow control are enabled.
+ TestStream* stream = session_.CreateOutgoingDataStream();
+ EXPECT_TRUE(stream->flow_controller()->IsEnabled());
+ EXPECT_TRUE(session_.flow_controller()->IsEnabled());
+
+ // Version 18 implies that stream flow control is enabled, but connection
+ // level is disabled.
+ session_.OnSuccessfulVersionNegotiation(QUIC_VERSION_18);
+ EXPECT_FALSE(session_.flow_controller()->IsEnabled());
+ EXPECT_TRUE(stream->flow_controller()->IsEnabled());
+}
+
+TEST_P(QuicSessionTest, WindowUpdateUnblocksHeadersStream) {
+ // Test that a flow control blocked headers stream gets unblocked on recipt of
+ // a WINDOW_UPDATE frame. Regression test for b/17413860.
+ if (version() < QUIC_VERSION_21) {
+ return;
+ }
+
+ // Set the headers stream to be flow control blocked.
+ QuicHeadersStream* headers_stream =
+ QuicSessionPeer::GetHeadersStream(&session_);
+ QuicFlowControllerPeer::SetSendWindowOffset(headers_stream->flow_controller(),
+ 0);
+ EXPECT_TRUE(headers_stream->flow_controller()->IsBlocked());
+
+ // Unblock the headers stream by supplying a WINDOW_UPDATE.
+ QuicWindowUpdateFrame window_update_frame(headers_stream->id(),
+ 2 * kDefaultFlowControlSendWindow);
+ vector<QuicWindowUpdateFrame> frames;
+ frames.push_back(window_update_frame);
+ session_.OnWindowUpdateFrames(frames);
+ EXPECT_FALSE(headers_stream->flow_controller()->IsBlocked());
+}
+
+TEST_P(QuicSessionTest, TooManyUnfinishedStreamsCauseConnectionClose) {
+ if (version() < QUIC_VERSION_18) {
+ return;
+ }
+ // If a buggy/malicious peer creates too many streams that are not ended with
+ // a FIN or RST then we send a connection close.
+ ValueRestore<bool> old_flag(&FLAGS_close_quic_connection_unfinished_streams_2,
+ true);
+
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS)).Times(1);
+
+ const int kMaxStreams = 5;
+ QuicSessionPeer::SetMaxOpenStreams(&session_, kMaxStreams);
+
+ // Create kMaxStreams + 1 data streams, and close them all without receiving a
+ // FIN or a RST from the client.
+ const int kFirstStreamId = kClientDataStreamId1;
+ const int kFinalStreamId = kClientDataStreamId1 + 2 * kMaxStreams + 1;
+ for (int i = kFirstStreamId; i < kFinalStreamId; i += 2) {
+ QuicStreamFrame data1(i, false, 0, MakeIOVector("HT"));
+ vector<QuicStreamFrame> frames;
+ frames.push_back(data1);
+ session_.OnStreamFrames(frames);
+ EXPECT_EQ(1u, session_.GetNumOpenStreams());
+ session_.CloseStream(i);
+ }
+
+ // Called after any new data is received by the session, and triggers the call
+ // to close the connection.
+ session_.PostProcessAfterData();
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_socket_address_coder.cc b/net/quic/quic_socket_address_coder.cc
new file mode 100644
index 0000000..77e595f
--- /dev/null
+++ b/net/quic/quic_socket_address_coder.cc
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_socket_address_coder.h"
+
+using std::string;
+
+namespace net {
+
+namespace {
+
+// For convenience, the values of these constants match the values of AF_INET
+// and AF_INET6 on Linux.
+const uint16 kIPv4 = 2;
+const uint16 kIPv6 = 10;
+
+} // namespace
+
+QuicSocketAddressCoder::QuicSocketAddressCoder() {
+}
+
+QuicSocketAddressCoder::QuicSocketAddressCoder(const IPEndPoint& address)
+ : address_(address) {
+}
+
+QuicSocketAddressCoder::~QuicSocketAddressCoder() {
+}
+
+string QuicSocketAddressCoder::Encode() const {
+ string serialized;
+ uint16 address_family;
+ switch (address_.GetSockAddrFamily()) {
+ case AF_INET:
+ address_family = kIPv4;
+ break;
+ case AF_INET6:
+ address_family = kIPv6;
+ break;
+ default:
+ return serialized;
+ }
+ serialized.append(reinterpret_cast<const char*>(&address_family),
+ sizeof(address_family));
+ serialized.append(IPAddressToPackedString(address_.address()));
+ uint16 port = address_.port();
+ serialized.append(reinterpret_cast<const char*>(&port), sizeof(port));
+ return serialized;
+}
+
+bool QuicSocketAddressCoder::Decode(const char* data, size_t length) {
+ uint16 address_family;
+ if (length < sizeof(address_family)) {
+ return false;
+ }
+ memcpy(&address_family, data, sizeof(address_family));
+ data += sizeof(address_family);
+ length -= sizeof(address_family);
+
+ size_t ip_length;
+ switch (address_family) {
+ case kIPv4:
+ ip_length = kIPv4AddressSize;
+ break;
+ case kIPv6:
+ ip_length = kIPv6AddressSize;
+ break;
+ default:
+ return false;
+ }
+ if (length < ip_length) {
+ return false;
+ }
+ IPAddressNumber ip(ip_length);
+ memcpy(&ip[0], data, ip_length);
+ data += ip_length;
+ length -= ip_length;
+
+ uint16 port;
+ if (length != sizeof(port)) {
+ return false;
+ }
+ memcpy(&port, data, length);
+
+ address_ = IPEndPoint(ip, port);
+ return true;
+}
+
+} // namespace net
diff --git a/net/quic/quic_socket_address_coder.h b/net/quic/quic_socket_address_coder.h
new file mode 100644
index 0000000..36ad1d0
--- /dev/null
+++ b/net/quic/quic_socket_address_coder.h
@@ -0,0 +1,45 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
+#define NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Serializes and parses a socket address (IP address and port), to be used in
+// the kCADR tag in the ServerHello handshake message and the Public Reset
+// packet.
+class NET_EXPORT_PRIVATE QuicSocketAddressCoder {
+ public:
+ QuicSocketAddressCoder();
+ explicit QuicSocketAddressCoder(const IPEndPoint& address);
+ ~QuicSocketAddressCoder();
+
+ std::string Encode() const;
+
+ bool Decode(const char* data, size_t length);
+
+ IPAddressNumber ip() const {
+ return address_.address();
+ }
+
+ uint16 port() const {
+ return address_.port();
+ }
+
+ private:
+ IPEndPoint address_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSocketAddressCoder);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SOCKET_ADDRESS_CODER_H_
diff --git a/net/quic/quic_socket_address_coder_test.cc b/net/quic/quic_socket_address_coder_test.cc
new file mode 100644
index 0000000..56bed58
--- /dev/null
+++ b/net/quic/quic_socket_address_coder_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_socket_address_coder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace net {
+namespace test {
+
+TEST(QuicSocketAddressCoderTest, EncodeIPv4) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber("4.31.198.44", &ip));
+ QuicSocketAddressCoder coder(IPEndPoint(ip, 0x1234));
+ string serialized = coder.Encode();
+ string expected("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST(QuicSocketAddressCoderTest, EncodeIPv6) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber("2001:700:300:1800::f", &ip));
+ QuicSocketAddressCoder coder(IPEndPoint(ip, 0x5678));
+ string serialized = coder.Encode();
+ string expected("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ EXPECT_EQ(expected, serialized);
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeIPv4) {
+ string serialized("\x02\x00\x04\x1f\xc6\x2c\x34\x12", 8);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(AF_INET, ConvertAddressFamily(GetAddressFamily(coder.ip())));
+ string expected_addr("\x04\x1f\xc6\x2c", 4);
+ EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip()));
+ EXPECT_EQ(0x1234, coder.port());
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeIPv6) {
+ string serialized("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ QuicSocketAddressCoder coder;
+ ASSERT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(AF_INET6, ConvertAddressFamily(GetAddressFamily(coder.ip())));
+ string expected_addr("\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f", 16);
+ EXPECT_EQ(expected_addr, IPAddressToPackedString(coder.ip()));
+ EXPECT_EQ(0x5678, coder.port());
+}
+
+TEST(QuicSocketAddressCoderTest, DecodeBad) {
+ string serialized("\x0a\x00"
+ "\x20\x01\x07\x00\x03\x00\x18\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x0f"
+ "\x78\x56", 20);
+ QuicSocketAddressCoder coder;
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+ // Append junk.
+ serialized.push_back('\0');
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized.resize(20);
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Set an unknown address family.
+ serialized[0] = '\x03';
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ // Undo.
+ serialized[0] = '\x0a';
+ EXPECT_TRUE(coder.Decode(serialized.data(), serialized.length()));
+
+ // Truncate.
+ size_t len = serialized.length();
+ for (size_t i = 0; i < len; i++) {
+ ASSERT_FALSE(serialized.empty());
+ serialized.erase(serialized.length() - 1);
+ EXPECT_FALSE(coder.Decode(serialized.data(), serialized.length()));
+ }
+ EXPECT_TRUE(serialized.empty());
+}
+
+TEST(QuicSocketAddressCoderTest, EncodeAndDecode) {
+ struct {
+ const char* ip_literal;
+ uint16 port;
+ } test_case[] = {
+ { "93.184.216.119", 0x1234 },
+ { "199.204.44.194", 80 },
+ { "149.20.4.69", 443 },
+ { "127.0.0.1", 8080 },
+ { "2001:700:300:1800::", 0x5678 },
+ { "::1", 65534 },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_case); i++) {
+ IPAddressNumber ip;
+ ASSERT_TRUE(ParseIPLiteralToNumber(test_case[i].ip_literal, &ip));
+ QuicSocketAddressCoder encoder(IPEndPoint(ip, test_case[i].port));
+ string serialized = encoder.Encode();
+
+ QuicSocketAddressCoder decoder;
+ ASSERT_TRUE(decoder.Decode(serialized.data(), serialized.length()));
+ EXPECT_EQ(encoder.ip(), decoder.ip());
+ EXPECT_EQ(encoder.port(), decoder.port());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_spdy_server_stream.cc b/net/quic/quic_spdy_server_stream.cc
new file mode 100644
index 0000000..b0f8abb
--- /dev/null
+++ b/net/quic/quic_spdy_server_stream.cc
@@ -0,0 +1,163 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_spdy_server_stream.h"
+
+#include "base/memory/singleton.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/quic_in_memory_cache.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/spdy_utils.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_header_block.h"
+#include "net/spdy/spdy_http_utils.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+static const size_t kHeaderBufInitialSize = 4096;
+
+QuicSpdyServerStream::QuicSpdyServerStream(QuicStreamId id,
+ QuicSession* session)
+ : QuicDataStream(id, session),
+ read_buf_(new GrowableIOBuffer()),
+ request_headers_received_(false) {
+ read_buf_->SetCapacity(kHeaderBufInitialSize);
+}
+
+QuicSpdyServerStream::~QuicSpdyServerStream() {
+}
+
+uint32 QuicSpdyServerStream::ProcessData(const char* data, uint32 data_len) {
+ if (data_len > INT_MAX) {
+ LOG(DFATAL) << "Data length too long: " << data_len;
+ return 0;
+ }
+ // Are we still reading the request headers.
+ if (!request_headers_received_) {
+ // Grow the read buffer if necessary.
+ while (read_buf_->RemainingCapacity() < static_cast<int>(data_len)) {
+ read_buf_->SetCapacity(read_buf_->capacity() * 2);
+ }
+ memcpy(read_buf_->data(), data, data_len);
+ read_buf_->set_offset(read_buf_->offset() + data_len);
+ // Try parsing the request headers. This will set request_headers_received_
+ // if successful; if not, it will be tried again with more data.
+ ParseRequestHeaders();
+ } else {
+ body_.append(data, data_len);
+ }
+ return data_len;
+}
+
+void QuicSpdyServerStream::OnFinRead() {
+ ReliableQuicStream::OnFinRead();
+ if (write_side_closed() || fin_buffered()) {
+ return;
+ }
+
+ if (!request_headers_received_) {
+ SendErrorResponse(); // We're not done reading headers.
+ return;
+ }
+
+ SpdyHeaderBlock::const_iterator it = headers_.find("content-length");
+ size_t content_length;
+ if (it != headers_.end() &&
+ (!base::StringToSizeT(it->second, &content_length) ||
+ body_.size() != content_length)) {
+ SendErrorResponse(); // Invalid content length
+ return;
+ }
+
+ SendResponse();
+}
+
+// Try parsing the request headers. If successful, sets
+// request_headers_received_. If not successful, it can just be tried again once
+// there's more data.
+void QuicSpdyServerStream::ParseRequestHeaders() {
+ SpdyFramer framer((kDefaultSpdyMajorVersion));
+ const char* data = read_buf_->StartOfBuffer();
+ size_t read_buf_len = static_cast<size_t>(read_buf_->offset());
+ size_t len = framer.ParseHeaderBlockInBuffer(data, read_buf_len, &headers_);
+ if (len == 0) {
+ // Not enough data yet, presumably. (If we still don't succeed by the end of
+ // the stream, then we'll error in OnFinRead().)
+ return;
+ }
+
+ // Headers received and parsed: extract the request URL.
+ request_url_ = GetUrlFromHeaderBlock(headers_,
+ kDefaultSpdyMajorVersion,
+ false);
+ if (!request_url_.is_valid()) {
+ SendErrorResponse();
+ return;
+ }
+
+ // Add any data past the headers to the request body.
+ size_t delta = read_buf_len - len;
+ if (delta > 0) {
+ body_.append(data + len, delta);
+ }
+
+ request_headers_received_ = true;
+}
+
+void QuicSpdyServerStream::SendResponse() {
+ // Find response in cache. If not found, send error response.
+ const QuicInMemoryCache::Response* response =
+ QuicInMemoryCache::GetInstance()->GetResponse(request_url_);
+ if (response == nullptr) {
+ SendErrorResponse();
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
+ DVLOG(1) << "Special response: closing connection.";
+ CloseConnection(QUIC_NO_ERROR);
+ return;
+ }
+
+ if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
+ DVLOG(1) << "Special response: ignoring request.";
+ return;
+ }
+
+ DVLOG(1) << "Sending response for stream " << id();
+ SendHeadersAndBody(response->headers(), response->body());
+}
+
+void QuicSpdyServerStream::SendErrorResponse() {
+ DVLOG(1) << "Sending error response for stream " << id();
+ scoped_refptr<HttpResponseHeaders> headers
+ = new HttpResponseHeaders(string("HTTP/1.1 500 Server Error") + '\0' +
+ "content-length: 3" + '\0' + '\0');
+ SendHeadersAndBody(*headers.get(), "bad");
+}
+
+void QuicSpdyServerStream::SendHeadersAndBody(
+ const HttpResponseHeaders& response_headers,
+ StringPiece body) {
+ // We only support SPDY and HTTP, and neither handles bidirectional streaming.
+ if (!read_side_closed()) {
+ CloseReadSide();
+ }
+
+ SpdyHeaderBlock header_block;
+ CreateSpdyHeadersFromHttpResponse(response_headers,
+ kDefaultSpdyMajorVersion,
+ &header_block);
+
+ WriteHeaders(header_block, body.empty(), nullptr);
+
+ if (!body.empty()) {
+ WriteOrBufferData(body, true, nullptr);
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_spdy_server_stream.h b/net/quic/quic_spdy_server_stream.h
new file mode 100644
index 0000000..3cecb6b
--- /dev/null
+++ b/net/quic/quic_spdy_server_stream.h
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+#define NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "net/base/io_buffer.h"
+#include "net/quic/quic_data_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "url/gurl.h"
+
+namespace net {
+
+class HttpResponseHeaders;
+class QuicSession;
+
+namespace test {
+class QuicSpdyServerStreamPeer;
+} // namespace test
+
+// All this does right now is aggregate data, and on fin, send an HTTP
+// response.
+class QuicSpdyServerStream : public QuicDataStream {
+ public:
+ QuicSpdyServerStream(QuicStreamId id, QuicSession* session);
+ virtual ~QuicSpdyServerStream();
+
+ // ReliableQuicStream implementation called by the session when there's
+ // data for us.
+ virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE;
+ virtual void OnFinRead() OVERRIDE;
+
+ void ParseRequestHeaders();
+
+ private:
+ friend class test::QuicSpdyServerStreamPeer;
+
+ // Sends a basic 200 response using SendHeaders for the headers and WriteData
+ // for the body.
+ void SendResponse();
+
+ // Sends a basic 500 response using SendHeaders for the headers and WriteData
+ // for the body
+ void SendErrorResponse();
+
+ void SendHeadersAndBody(const HttpResponseHeaders& response_headers,
+ base::StringPiece body);
+
+ SpdyHeaderBlock headers_;
+ string body_;
+ GURL request_url_;
+
+ // Buffer into which response header data is read.
+ scoped_refptr<GrowableIOBuffer> read_buf_;
+ bool request_headers_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSpdyServerStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SPDY_SERVER_STREAM_H_
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
new file mode 100644
index 0000000..ccbca64
--- /dev/null
+++ b/net/quic/quic_stream_factory.cc
@@ -0,0 +1,1034 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_stream_factory.h"
+
+#include <set>
+
+#include "base/cpu.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/metrics/histogram.h"
+#include "base/rand_util.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_verifier.h"
+#include "net/dns/host_resolver.h"
+#include "net/dns/single_request_host_resolver.h"
+#include "net/http/http_server_properties.h"
+#include "net/quic/congestion_control/tcp_receiver.h"
+#include "net/quic/crypto/channel_id_chromium.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/quic_server_info.h"
+#include "net/quic/port_suggester.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_crypto_client_stream_factory.h"
+#include "net/quic/quic_default_packet_writer.h"
+#include "net/quic/quic_http_stream.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
+#include "net/socket/client_socket_factory.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
+using std::string;
+using std::vector;
+
+namespace net {
+
+namespace {
+
+enum CreateSessionFailure {
+ CREATION_ERROR_CONNECTING_SOCKET,
+ CREATION_ERROR_SETTING_RECEIVE_BUFFER,
+ CREATION_ERROR_SETTING_SEND_BUFFER,
+ CREATION_ERROR_MAX
+};
+
+// When a connection is idle for 30 seconds it will be closed.
+const int kIdleConnectionTimeoutSeconds = 30;
+
+// The initial receive window size for both streams and sessions.
+const int32 kInitialReceiveWindowSize = 10 * 1024 * 1024; // 10MB
+
+// The suggested initial congestion windows for a server to use.
+// TODO: This should be tested and optimized, and even better, suggest a window
+// that corresponds to historical bandwidth and min-RTT.
+// Larger initial congestion windows can, if we don't overshoot, reduce latency
+// by avoiding the RTT needed for slow start to double (and re-double) from a
+// default of 10.
+// We match SPDY's use of 32 when secure (since we'd compete with SPDY).
+const int32 kServerSecureInitialCongestionWindow = 32;
+// Be conservative, and just use double a typical TCP ICWND for HTTP.
+const int32 kServerInecureInitialCongestionWindow = 20;
+
+const char kDummyHostname[] = "quic.global.props";
+const uint16 kDummyPort = 0;
+
+void HistogramCreateSessionFailure(enum CreateSessionFailure error) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.CreationError", error,
+ CREATION_ERROR_MAX);
+}
+
+bool IsEcdsaSupported() {
+#if defined(OS_WIN)
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+#endif
+
+ return true;
+}
+
+QuicConfig InitializeQuicConfig(const QuicTagVector& connection_options) {
+ QuicConfig config;
+ config.SetDefaults();
+ config.SetIdleConnectionStateLifetime(
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds),
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds));
+ config.SetConnectionOptionsToSend(connection_options);
+ return config;
+}
+
+class DefaultPacketWriterFactory : public QuicConnection::PacketWriterFactory {
+ public:
+ explicit DefaultPacketWriterFactory(DatagramClientSocket* socket)
+ : socket_(socket) {}
+ virtual ~DefaultPacketWriterFactory() {}
+
+ virtual QuicPacketWriter* Create(QuicConnection* connection) const OVERRIDE;
+
+ private:
+ DatagramClientSocket* socket_;
+};
+
+QuicPacketWriter* DefaultPacketWriterFactory::Create(
+ QuicConnection* connection) const {
+ scoped_ptr<QuicDefaultPacketWriter> writer(
+ new QuicDefaultPacketWriter(socket_));
+ writer->SetConnection(connection);
+ return writer.release();
+}
+
+} // namespace
+
+QuicStreamFactory::IpAliasKey::IpAliasKey() {}
+
+QuicStreamFactory::IpAliasKey::IpAliasKey(IPEndPoint ip_endpoint,
+ bool is_https)
+ : ip_endpoint(ip_endpoint),
+ is_https(is_https) {}
+
+QuicStreamFactory::IpAliasKey::~IpAliasKey() {}
+
+bool QuicStreamFactory::IpAliasKey::operator<(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ if (!(ip_endpoint == other.ip_endpoint)) {
+ return ip_endpoint < other.ip_endpoint;
+ }
+ return is_https < other.is_https;
+}
+
+bool QuicStreamFactory::IpAliasKey::operator==(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ return is_https == other.is_https &&
+ ip_endpoint == other.ip_endpoint;
+};
+
+// Responsible for creating a new QUIC session to the specified server, and
+// for notifying any associated requests when complete.
+class QuicStreamFactory::Job {
+ public:
+ Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
+ const BoundNetLog& net_log);
+
+ // Creates a new job to handle the resumption of for connecting an
+ // existing session.
+ Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id);
+
+ ~Job();
+
+ int Run(const CompletionCallback& callback);
+
+ int DoLoop(int rv);
+ int DoResolveHost();
+ int DoResolveHostComplete(int rv);
+ int DoLoadServerInfo();
+ int DoLoadServerInfoComplete(int rv);
+ int DoConnect();
+ int DoResumeConnect();
+ int DoConnectComplete(int rv);
+
+ void OnIOComplete(int rv);
+
+ CompletionCallback callback() {
+ return callback_;
+ }
+
+ const QuicServerId server_id() const {
+ return server_id_;
+ }
+
+ private:
+ enum IoState {
+ STATE_NONE,
+ STATE_RESOLVE_HOST,
+ STATE_RESOLVE_HOST_COMPLETE,
+ STATE_LOAD_SERVER_INFO,
+ STATE_LOAD_SERVER_INFO_COMPLETE,
+ STATE_CONNECT,
+ STATE_RESUME_CONNECT,
+ STATE_CONNECT_COMPLETE,
+ };
+ IoState io_state_;
+
+ QuicStreamFactory* factory_;
+ SingleRequestHostResolver host_resolver_;
+ QuicServerId server_id_;
+ bool is_post_;
+ bool was_alternate_protocol_recently_broken_;
+ scoped_ptr<QuicServerInfo> server_info_;
+ const BoundNetLog net_log_;
+ QuicClientSession* session_;
+ CompletionCallback callback_;
+ AddressList address_list_;
+ base::TimeTicks disk_cache_load_start_time_;
+ base::TimeTicks dns_resolution_start_time_;
+ base::WeakPtrFactory<Job> weak_factory_;
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
+ const BoundNetLog& net_log)
+ : io_state_(STATE_RESOLVE_HOST),
+ factory_(factory),
+ host_resolver_(host_resolver),
+ server_id_(host_port_pair, is_https, privacy_mode),
+ is_post_(method == "POST"),
+ was_alternate_protocol_recently_broken_(
+ was_alternate_protocol_recently_broken),
+ server_info_(server_info),
+ net_log_(net_log),
+ session_(nullptr),
+ weak_factory_(this) {}
+
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id)
+ : io_state_(STATE_RESUME_CONNECT),
+ factory_(factory),
+ host_resolver_(host_resolver), // unused
+ server_id_(server_id),
+ is_post_(false), // unused
+ was_alternate_protocol_recently_broken_(false), // unused
+ net_log_(session->net_log()), // unused
+ session_(session),
+ weak_factory_(this) {}
+
+QuicStreamFactory::Job::~Job() {
+}
+
+int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
+ int rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ callback_ = callback;
+
+ return rv > 0 ? OK : rv;
+}
+
+int QuicStreamFactory::Job::DoLoop(int rv) {
+ do {
+ IoState state = io_state_;
+ io_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_RESOLVE_HOST:
+ CHECK_EQ(OK, rv);
+ rv = DoResolveHost();
+ break;
+ case STATE_RESOLVE_HOST_COMPLETE:
+ rv = DoResolveHostComplete(rv);
+ break;
+ case STATE_LOAD_SERVER_INFO:
+ CHECK_EQ(OK, rv);
+ rv = DoLoadServerInfo();
+ break;
+ case STATE_LOAD_SERVER_INFO_COMPLETE:
+ rv = DoLoadServerInfoComplete(rv);
+ break;
+ case STATE_CONNECT:
+ CHECK_EQ(OK, rv);
+ rv = DoConnect();
+ break;
+ case STATE_RESUME_CONNECT:
+ CHECK_EQ(OK, rv);
+ rv = DoResumeConnect();
+ break;
+ case STATE_CONNECT_COMPLETE:
+ rv = DoConnectComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "io_state_: " << io_state_;
+ break;
+ }
+ } while (io_state_ != STATE_NONE && rv != ERR_IO_PENDING);
+ return rv;
+}
+
+void QuicStreamFactory::Job::OnIOComplete(int rv) {
+ rv = DoLoop(rv);
+
+ if (rv != ERR_IO_PENDING && !callback_.is_null()) {
+ callback_.Run(rv);
+ }
+}
+
+int QuicStreamFactory::Job::DoResolveHost() {
+ // Start loading the data now, and wait for it after we resolve the host.
+ if (server_info_) {
+ disk_cache_load_start_time_ = base::TimeTicks::Now();
+ server_info_->Start();
+ }
+
+ io_state_ = STATE_RESOLVE_HOST_COMPLETE;
+ dns_resolution_start_time_ = base::TimeTicks::Now();
+ return host_resolver_.Resolve(
+ HostResolver::RequestInfo(server_id_.host_port_pair()),
+ DEFAULT_PRIORITY,
+ &address_list_,
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()),
+ net_log_);
+}
+
+int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
+ UMA_HISTOGRAM_TIMES("Net.QuicSession.HostResolutionTime",
+ base::TimeTicks::Now() - dns_resolution_start_time_);
+ if (rv != OK)
+ return rv;
+
+ DCHECK(!factory_->HasActiveSession(server_id_));
+
+ // Inform the factory of this resolution, which will set up
+ // a session alias, if possible.
+ if (factory_->OnResolution(server_id_, address_list_)) {
+ return OK;
+ }
+
+ io_state_ = STATE_LOAD_SERVER_INFO;
+ return OK;
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfo() {
+ io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE;
+
+ if (!server_info_)
+ return OK;
+
+ return server_info_->WaitForDataReady(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) {
+ if (server_info_) {
+ UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheReadTime",
+ base::TimeTicks::Now() - disk_cache_load_start_time_);
+ }
+
+ if (rv != OK) {
+ server_info_.reset();
+ }
+
+ io_state_ = STATE_CONNECT;
+ return OK;
+}
+
+int QuicStreamFactory::Job::DoConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = factory_->CreateSession(server_id_, server_info_.Pass(),
+ address_list_, net_log_, &session_);
+ if (rv != OK) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(!session_);
+ return rv;
+ }
+
+ if (!session_->connection()->connected()) {
+ return ERR_CONNECTION_CLOSED;
+ }
+
+ session_->StartReading();
+ if (!session_->connection()->connected()) {
+ return ERR_QUIC_PROTOCOL_ERROR;
+ }
+ bool require_confirmation =
+ factory_->require_confirmation() || is_post_ ||
+ was_alternate_protocol_recently_broken_;
+ rv = session_->CryptoConnect(
+ require_confirmation,
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoResumeConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = session_->ResumeCryptoConnect(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoConnectComplete(int rv) {
+ if (rv != OK)
+ return rv;
+
+ DCHECK(!factory_->HasActiveSession(server_id_));
+ // There may well now be an active session for this IP. If so, use the
+ // existing session instead.
+ AddressList address(session_->connection()->peer_address());
+ if (factory_->OnResolution(server_id_, address)) {
+ session_->connection()->SendConnectionClose(QUIC_CONNECTION_IP_POOLED);
+ session_ = nullptr;
+ return OK;
+ }
+
+ factory_->ActivateSession(server_id_, session_);
+
+ return OK;
+}
+
+QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory)
+ : factory_(factory) {}
+
+QuicStreamRequest::~QuicStreamRequest() {
+ if (factory_ && !callback_.is_null())
+ factory_->CancelRequest(this);
+}
+
+int QuicStreamRequest::Request(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ const CompletionCallback& callback) {
+ DCHECK(!stream_);
+ DCHECK(callback_.is_null());
+ DCHECK(factory_);
+ int rv = factory_->Create(host_port_pair, is_https, privacy_mode, method,
+ net_log, this);
+ if (rv == ERR_IO_PENDING) {
+ host_port_pair_ = host_port_pair;
+ is_https_ = is_https;
+ net_log_ = net_log;
+ callback_ = callback;
+ } else {
+ factory_ = nullptr;
+ }
+ if (rv == OK)
+ DCHECK(stream_);
+ return rv;
+}
+
+void QuicStreamRequest::set_stream(scoped_ptr<QuicHttpStream> stream) {
+ DCHECK(stream);
+ stream_ = stream.Pass();
+}
+
+void QuicStreamRequest::OnRequestComplete(int rv) {
+ factory_ = nullptr;
+ callback_.Run(rv);
+}
+
+scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() {
+ DCHECK(stream_);
+ return stream_.Pass();
+}
+
+QuicStreamFactory::QuicStreamFactory(
+ HostResolver* host_resolver,
+ ClientSocketFactory* client_socket_factory,
+ base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
+ ChannelIDService* channel_id_service,
+ TransportSecurityState* transport_security_state,
+ QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
+ QuicRandom* random_generator,
+ QuicClock* clock,
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool always_require_handshake_confirmation,
+ bool disable_connection_pooling,
+ const QuicTagVector& connection_options)
+ : require_confirmation_(true),
+ host_resolver_(host_resolver),
+ client_socket_factory_(client_socket_factory),
+ http_server_properties_(http_server_properties),
+ transport_security_state_(transport_security_state),
+ quic_server_info_factory_(nullptr),
+ quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory),
+ random_generator_(random_generator),
+ clock_(clock),
+ max_packet_length_(max_packet_length),
+ config_(InitializeQuicConfig(connection_options)),
+ supported_versions_(supported_versions),
+ enable_port_selection_(enable_port_selection),
+ always_require_handshake_confirmation_(
+ always_require_handshake_confirmation),
+ disable_connection_pooling_(disable_connection_pooling),
+ port_seed_(random_generator_->RandUint64()),
+ check_persisted_supports_quic_(true),
+ weak_factory_(this) {
+ DCHECK(transport_security_state_);
+ crypto_config_.SetDefaults();
+ crypto_config_.set_user_agent_id(user_agent_id);
+ crypto_config_.AddCanonicalSuffix(".c.youtube.com");
+ crypto_config_.AddCanonicalSuffix(".googlevideo.com");
+ crypto_config_.SetProofVerifier(
+ new ProofVerifierChromium(cert_verifier, transport_security_state));
+ crypto_config_.SetChannelIDSource(
+ new ChannelIDSourceChromium(channel_id_service));
+ base::CPU cpu;
+ if (cpu.has_aesni() && cpu.has_avx())
+ crypto_config_.PreferAesGcm();
+ if (!IsEcdsaSupported())
+ crypto_config_.DisableEcdsa();
+}
+
+QuicStreamFactory::~QuicStreamFactory() {
+ CloseAllSessions(ERR_ABORTED);
+ while (!all_sessions_.empty()) {
+ delete all_sessions_.begin()->first;
+ all_sessions_.erase(all_sessions_.begin());
+ }
+ STLDeleteValues(&active_jobs_);
+}
+
+void QuicStreamFactory::set_require_confirmation(bool require_confirmation) {
+ require_confirmation_ = require_confirmation;
+ if (http_server_properties_ && (!(local_address_ == IPEndPoint()))) {
+ // TODO(rtenneti): Delete host_port_pair and persist data in globals.
+ HostPortPair host_port_pair(kDummyHostname, kDummyPort);
+ http_server_properties_->SetSupportsQuic(
+ host_port_pair, !require_confirmation,
+ local_address_.ToStringWithoutPort());
+ }
+}
+
+int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ QuicStreamRequest* request) {
+ QuicServerId server_id(host_port_pair, is_https, privacy_mode);
+ if (HasActiveSession(server_id)) {
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
+ return OK;
+ }
+
+ if (HasActiveJob(server_id)) {
+ Job* job = active_jobs_[server_id];
+ active_requests_[request] = job;
+ job_requests_map_[job].insert(request);
+ return ERR_IO_PENDING;
+ }
+
+ QuicServerInfo* quic_server_info = nullptr;
+ if (quic_server_info_factory_) {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ DCHECK(cached);
+ if (cached->IsEmpty()) {
+ quic_server_info = quic_server_info_factory_->GetForServer(server_id);
+ }
+ }
+ bool was_alternate_protocol_recently_broken =
+ http_server_properties_ &&
+ http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server_id.host_port_pair());
+ scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https,
+ was_alternate_protocol_recently_broken,
+ privacy_mode, method, quic_server_info, net_log));
+ int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
+ base::Unretained(this), job.get()));
+
+ if (rv == ERR_IO_PENDING) {
+ active_requests_[request] = job.get();
+ job_requests_map_[job.get()].insert(request);
+ active_jobs_[server_id] = job.release();
+ }
+ if (rv == OK) {
+ DCHECK(HasActiveSession(server_id));
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
+ }
+ return rv;
+}
+
+bool QuicStreamFactory::OnResolution(
+ const QuicServerId& server_id,
+ const AddressList& address_list) {
+ DCHECK(!HasActiveSession(server_id));
+ if (disable_connection_pooling_) {
+ return false;
+ }
+ for (size_t i = 0; i < address_list.size(); ++i) {
+ const IPEndPoint& address = address_list[i];
+ const IpAliasKey ip_alias_key(address, server_id.is_https());
+ if (!ContainsKey(ip_aliases_, ip_alias_key))
+ continue;
+
+ const SessionSet& sessions = ip_aliases_[ip_alias_key];
+ for (SessionSet::const_iterator i = sessions.begin();
+ i != sessions.end(); ++i) {
+ QuicClientSession* session = *i;
+ if (!session->CanPool(server_id.host()))
+ continue;
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ return true;
+ }
+ }
+ return false;
+}
+
+void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
+ if (rv == OK) {
+ if (!always_require_handshake_confirmation_)
+ set_require_confirmation(false);
+
+ // Create all the streams, but do not notify them yet.
+ for (RequestSet::iterator it = job_requests_map_[job].begin();
+ it != job_requests_map_[job].end() ; ++it) {
+ DCHECK(HasActiveSession(job->server_id()));
+ (*it)->set_stream(CreateIfSessionExists(job->server_id(),
+ (*it)->net_log()));
+ }
+ }
+ while (!job_requests_map_[job].empty()) {
+ RequestSet::iterator it = job_requests_map_[job].begin();
+ QuicStreamRequest* request = *it;
+ job_requests_map_[job].erase(it);
+ active_requests_.erase(request);
+ // Even though we're invoking callbacks here, we don't need to worry
+ // about |this| being deleted, because the factory is owned by the
+ // profile which can not be deleted via callbacks.
+ request->OnRequestComplete(rv);
+ }
+ active_jobs_.erase(job->server_id());
+ job_requests_map_.erase(job);
+ delete job;
+ return;
+}
+
+// Returns a newly created QuicHttpStream owned by the caller, if a
+// matching session already exists. Returns nullptr otherwise.
+scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists(
+ const QuicServerId& server_id,
+ const BoundNetLog& net_log) {
+ if (!HasActiveSession(server_id)) {
+ DVLOG(1) << "No active session";
+ return scoped_ptr<QuicHttpStream>();
+ }
+
+ QuicClientSession* session = active_sessions_[server_id];
+ DCHECK(session);
+ return scoped_ptr<QuicHttpStream>(
+ new QuicHttpStream(session->GetWeakPtr()));
+}
+
+void QuicStreamFactory::OnIdleSession(QuicClientSession* session) {
+}
+
+void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
+ const AliasSet& aliases = session_aliases_[session];
+ for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
+ ++it) {
+ DCHECK(active_sessions_.count(*it));
+ DCHECK_EQ(session, active_sessions_[*it]);
+ // Track sessions which have recently gone away so that we can disable
+ // port suggestions.
+ if (session->goaway_received()) {
+ gone_away_aliases_.insert(*it);
+ }
+
+ active_sessions_.erase(*it);
+ ProcessGoingAwaySession(session, *it, true);
+ }
+ ProcessGoingAwaySession(session, all_sessions_[session], false);
+ if (!aliases.empty()) {
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
+ }
+ }
+ session_aliases_.erase(session);
+}
+
+void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) {
+ DCHECK_EQ(0u, session->GetNumOpenStreams());
+ OnSessionGoingAway(session);
+ delete session;
+ all_sessions_.erase(session);
+}
+
+void QuicStreamFactory::OnSessionConnectTimeout(
+ QuicClientSession* session) {
+ const AliasSet& aliases = session_aliases_[session];
+ for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
+ ++it) {
+ DCHECK(active_sessions_.count(*it));
+ DCHECK_EQ(session, active_sessions_[*it]);
+ active_sessions_.erase(*it);
+ }
+
+ if (aliases.empty()) {
+ return;
+ }
+
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
+ }
+ QuicServerId server_id = *aliases.begin();
+ session_aliases_.erase(session);
+ Job* job = new Job(this, host_resolver_, session, server_id);
+ active_jobs_[server_id] = job;
+ int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
+ base::Unretained(this), job));
+ DCHECK_EQ(ERR_IO_PENDING, rv);
+}
+
+void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
+ DCHECK(ContainsKey(active_requests_, request));
+ Job* job = active_requests_[request];
+ job_requests_map_[job].erase(request);
+ active_requests_.erase(request);
+}
+
+void QuicStreamFactory::CloseAllSessions(int error) {
+ while (!active_sessions_.empty()) {
+ size_t initial_size = active_sessions_.size();
+ active_sessions_.begin()->second->CloseSessionOnError(error);
+ DCHECK_NE(initial_size, active_sessions_.size());
+ }
+ while (!all_sessions_.empty()) {
+ size_t initial_size = all_sessions_.size();
+ all_sessions_.begin()->first->CloseSessionOnError(error);
+ DCHECK_NE(initial_size, all_sessions_.size());
+ }
+ DCHECK(all_sessions_.empty());
+}
+
+base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
+ base::ListValue* list = new base::ListValue();
+
+ for (SessionMap::const_iterator it = active_sessions_.begin();
+ it != active_sessions_.end(); ++it) {
+ const QuicServerId& server_id = it->first;
+ QuicClientSession* session = it->second;
+ const AliasSet& aliases = session_aliases_.find(session)->second;
+ // Only add a session to the list once.
+ if (server_id == *aliases.begin()) {
+ std::set<HostPortPair> hosts;
+ for (AliasSet::const_iterator alias_it = aliases.begin();
+ alias_it != aliases.end(); ++alias_it) {
+ hosts.insert(alias_it->host_port_pair());
+ }
+ list->Append(session->GetInfoAsValue(hosts));
+ }
+ }
+ return list;
+}
+
+void QuicStreamFactory::ClearCachedStatesInCryptoConfig() {
+ crypto_config_.ClearCachedStates();
+}
+
+void QuicStreamFactory::OnIPAddressChanged() {
+ CloseAllSessions(ERR_NETWORK_CHANGED);
+ set_require_confirmation(true);
+}
+
+void QuicStreamFactory::OnCertAdded(const X509Certificate* cert) {
+ CloseAllSessions(ERR_CERT_DATABASE_CHANGED);
+}
+
+void QuicStreamFactory::OnCACertChanged(const X509Certificate* cert) {
+ // We should flush the sessions if we removed trust from a
+ // cert, because a previously trusted server may have become
+ // untrusted.
+ //
+ // We should not flush the sessions if we added trust to a cert.
+ //
+ // Since the OnCACertChanged method doesn't tell us what
+ // kind of change it is, we have to flush the socket
+ // pools to be safe.
+ CloseAllSessions(ERR_CERT_DATABASE_CHANGED);
+}
+
+bool QuicStreamFactory::HasActiveSession(
+ const QuicServerId& server_id) const {
+ return ContainsKey(active_sessions_, server_id);
+}
+
+int QuicStreamFactory::CreateSession(
+ const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> server_info,
+ const AddressList& address_list,
+ const BoundNetLog& net_log,
+ QuicClientSession** session) {
+ bool enable_port_selection = enable_port_selection_;
+ if (enable_port_selection &&
+ ContainsKey(gone_away_aliases_, server_id)) {
+ // Disable port selection when the server is going away.
+ // There is no point in trying to return to the same server, if
+ // that server is no longer handling requests.
+ enable_port_selection = false;
+ gone_away_aliases_.erase(server_id);
+ }
+
+ QuicConnectionId connection_id = random_generator_->RandUint64();
+ IPEndPoint addr = *address_list.begin();
+ scoped_refptr<PortSuggester> port_suggester =
+ new PortSuggester(server_id.host_port_pair(), port_seed_);
+ DatagramSocket::BindType bind_type = enable_port_selection ?
+ DatagramSocket::RANDOM_BIND : // Use our callback.
+ DatagramSocket::DEFAULT_BIND; // Use OS to randomize.
+ scoped_ptr<DatagramClientSocket> socket(
+ client_socket_factory_->CreateDatagramClientSocket(
+ bind_type,
+ base::Bind(&PortSuggester::SuggestPort, port_suggester),
+ net_log.net_log(), net_log.source()));
+ int rv = socket->Connect(addr);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_CONNECTING_SOCKET);
+ return rv;
+ }
+ UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
+ port_suggester->call_count());
+ if (enable_port_selection) {
+ DCHECK_LE(1u, port_suggester->call_count());
+ } else {
+ DCHECK_EQ(0u, port_suggester->call_count());
+ }
+
+ // We should adaptively set this buffer size, but for now, we'll use a size
+ // that is more than large enough for a full receive window, and yet
+ // does not consume "too much" memory. If we see bursty packet loss, we may
+ // revisit this setting and test for its impact.
+ const int32 kSocketBufferSize(TcpReceiver::kReceiveWindowTCP);
+ rv = socket->SetReceiveBufferSize(kSocketBufferSize);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_RECEIVE_BUFFER);
+ return rv;
+ }
+ // Set a buffer large enough to contain the initial CWND's worth of packet
+ // to work around the problem with CHLO packets being sent out with the
+ // wrong encryption level, when the send buffer is full.
+ rv = socket->SetSendBufferSize(kMaxPacketSize * 20);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_SEND_BUFFER);
+ return rv;
+ }
+
+ socket->GetLocalAddress(&local_address_);
+ if (check_persisted_supports_quic_ && http_server_properties_) {
+ check_persisted_supports_quic_ = false;
+ // TODO(rtenneti): Delete host_port_pair and persist data in globals.
+ HostPortPair host_port_pair(kDummyHostname, kDummyPort);
+ SupportsQuic supports_quic(true, local_address_.ToStringWithoutPort());
+ if (http_server_properties_->GetSupportsQuic(
+ host_port_pair).Equals(supports_quic)) {
+ require_confirmation_ = false;
+ }
+ }
+
+ DefaultPacketWriterFactory packet_writer_factory(socket.get());
+
+ if (!helper_.get()) {
+ helper_.reset(new QuicConnectionHelper(
+ base::MessageLoop::current()->message_loop_proxy().get(),
+ clock_.get(), random_generator_));
+ }
+
+ QuicConnection* connection = new QuicConnection(connection_id,
+ addr,
+ helper_.get(),
+ packet_writer_factory,
+ true /* owns_writer */,
+ false /* is_server */,
+ supported_versions_);
+ connection->set_max_packet_length(max_packet_length_);
+
+ InitializeCachedStateInCryptoConfig(server_id, server_info);
+
+ QuicConfig config = config_;
+ config.SetInitialCongestionWindowToSend(
+ server_id.is_https() ? kServerSecureInitialCongestionWindow
+ : kServerInecureInitialCongestionWindow);
+ config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize);
+ if (http_server_properties_) {
+ const HttpServerProperties::NetworkStats* stats =
+ http_server_properties_->GetServerNetworkStats(
+ server_id.host_port_pair());
+ if (stats != nullptr) {
+ config.SetInitialRoundTripTimeUsToSend(stats->srtt.InMicroseconds());
+ }
+ }
+
+ *session = new QuicClientSession(
+ connection, socket.Pass(), this, transport_security_state_,
+ server_info.Pass(), config,
+ base::MessageLoop::current()->message_loop_proxy().get(),
+ net_log.net_log());
+ all_sessions_[*session] = server_id; // owning pointer
+ (*session)->InitializeSession(server_id, &crypto_config_,
+ quic_crypto_client_stream_factory_);
+ bool closed_during_initialize =
+ !ContainsKey(all_sessions_, *session) ||
+ !(*session)->connection()->connected();
+ UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.ClosedDuringInitializeSession",
+ closed_during_initialize);
+ if (closed_during_initialize) {
+ DLOG(DFATAL) << "Session closed during initialize";
+ *session = nullptr;
+ return ERR_CONNECTION_CLOSED;
+ }
+ return OK;
+}
+
+bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
+ return ContainsKey(active_jobs_, key);
+}
+
+void QuicStreamFactory::ActivateSession(
+ const QuicServerId& server_id,
+ QuicClientSession* session) {
+ DCHECK(!HasActiveSession(server_id));
+ UMA_HISTOGRAM_COUNTS("Net.QuicActiveSessions", active_sessions_.size());
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ server_id.is_https());
+ DCHECK(!ContainsKey(ip_aliases_[ip_alias_key], session));
+ ip_aliases_[ip_alias_key].insert(session);
+}
+
+void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info) {
+ if (!server_info)
+ return;
+
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ if (!cached->IsEmpty())
+ return;
+
+ if (!cached->Initialize(server_info->state().server_config,
+ server_info->state().source_address_token,
+ server_info->state().certs,
+ server_info->state().server_config_sig,
+ clock_->WallNow()))
+ return;
+
+ if (!server_id.is_https()) {
+ // Don't check the certificates for insecure QUIC.
+ cached->SetProofValid();
+ }
+}
+
+void QuicStreamFactory::ProcessGoingAwaySession(
+ QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool session_was_active) {
+ if (!http_server_properties_)
+ return;
+
+ const QuicConnectionStats& stats = session->connection()->GetStats();
+ if (session->IsCryptoHandshakeConfirmed()) {
+ HttpServerProperties::NetworkStats network_stats;
+ network_stats.srtt = base::TimeDelta::FromMicroseconds(stats.srtt_us);
+ network_stats.bandwidth_estimate = stats.estimated_bandwidth;
+ http_server_properties_->SetServerNetworkStats(server_id.host_port_pair(),
+ network_stats);
+ return;
+ }
+
+ UMA_HISTOGRAM_COUNTS("Net.QuicHandshakeNotConfirmedNumPacketsReceived",
+ stats.packets_received);
+
+ if (!session_was_active)
+ return;
+
+ const HostPortPair& server = server_id.host_port_pair();
+ // Don't try to change the alternate-protocol state, if the
+ // alternate-protocol state is unknown.
+ if (!http_server_properties_->HasAlternateProtocol(server))
+ return;
+
+ // TODO(rch): In the special case where the session has received no
+ // packets from the peer, we should consider blacklisting this
+ // differently so that we still race TCP but we don't consider the
+ // session connected until the handshake has been confirmed.
+ HistogramBrokenAlternateProtocolLocation(
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY);
+ AlternateProtocolInfo alternate =
+ http_server_properties_->GetAlternateProtocol(server);
+ DCHECK_EQ(QUIC, alternate.protocol);
+
+ // Since the session was active, there's no longer an
+ // HttpStreamFactoryImpl::Job running which can mark it broken, unless the
+ // TCP job also fails. So to avoid not using QUIC when we otherwise could,
+ // we mark it as broken, and then immediately re-enable it. This leaves
+ // QUIC as "recently broken" which means that 0-RTT will be disabled but
+ // we'll still race.
+ http_server_properties_->SetBrokenAlternateProtocol(server);
+ http_server_properties_->ClearAlternateProtocol(server);
+ http_server_properties_->SetAlternateProtocol(
+ server, alternate.port, alternate.protocol, 1);
+ DCHECK_EQ(QUIC,
+ http_server_properties_->GetAlternateProtocol(server).protocol);
+ DCHECK(http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server));
+}
+
+} // namespace net
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
new file mode 100644
index 0000000..f4aa77e
--- /dev/null
+++ b/net/quic/quic_stream_factory.h
@@ -0,0 +1,304 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_STREAM_FACTORY_H_
+#define NET_QUIC_QUIC_STREAM_FACTORY_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/weak_ptr.h"
+#include "net/base/address_list.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_log.h"
+#include "net/base/network_change_notifier.h"
+#include "net/cert/cert_database.h"
+#include "net/proxy/proxy_server.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_http_stream.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class CertVerifier;
+class ChannelIDService;
+class ClientSocketFactory;
+class HostResolver;
+class HttpServerProperties;
+class QuicClock;
+class QuicClientSession;
+class QuicConnectionHelper;
+class QuicCryptoClientStreamFactory;
+class QuicRandom;
+class QuicServerInfoFactory;
+class QuicServerId;
+class QuicStreamFactory;
+class TransportSecurityState;
+
+namespace test {
+class QuicStreamFactoryPeer;
+} // namespace test
+
+// Encapsulates a pending request for a QuicHttpStream.
+// If the request is still pending when it is destroyed, it will
+// cancel the request with the factory.
+class NET_EXPORT_PRIVATE QuicStreamRequest {
+ public:
+ explicit QuicStreamRequest(QuicStreamFactory* factory);
+ ~QuicStreamRequest();
+
+ // For http, |is_https| is false.
+ int Request(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ const CompletionCallback& callback);
+
+ void OnRequestComplete(int rv);
+
+ scoped_ptr<QuicHttpStream> ReleaseStream();
+
+ void set_stream(scoped_ptr<QuicHttpStream> stream);
+
+ const BoundNetLog& net_log() const{
+ return net_log_;
+ }
+
+ private:
+ QuicStreamFactory* factory_;
+ HostPortPair host_port_pair_;
+ bool is_https_;
+ BoundNetLog net_log_;
+ CompletionCallback callback_;
+ scoped_ptr<QuicHttpStream> stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamRequest);
+};
+
+// A factory for creating new QuicHttpStreams on top of a pool of
+// QuicClientSessions.
+class NET_EXPORT_PRIVATE QuicStreamFactory
+ : public NetworkChangeNotifier::IPAddressObserver,
+ public CertDatabase::Observer {
+ public:
+ QuicStreamFactory(
+ HostResolver* host_resolver,
+ ClientSocketFactory* client_socket_factory,
+ base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
+ ChannelIDService* channel_id_service,
+ TransportSecurityState* transport_security_state,
+ QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
+ QuicRandom* random_generator,
+ QuicClock* clock,
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool always_require_handshake_confirmation,
+ bool disable_connection_pooling,
+ const QuicTagVector& connection_options);
+ virtual ~QuicStreamFactory();
+
+ // Creates a new QuicHttpStream to |host_port_pair| which will be
+ // owned by |request|. |is_https| specifies if the protocol is https or not.
+ // If a matching session already exists, this method will return OK. If no
+ // matching session exists, this will return ERR_IO_PENDING and will invoke
+ // OnRequestComplete asynchronously.
+ int Create(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ QuicStreamRequest* request);
+
+ // Called by a session when it becomes idle.
+ void OnIdleSession(QuicClientSession* session);
+
+ // Called by a session when it is going away and no more streams should be
+ // created on it.
+ void OnSessionGoingAway(QuicClientSession* session);
+
+ // Called by a session after it shuts down.
+ void OnSessionClosed(QuicClientSession* session);
+
+ // Called by a session whose connection has timed out.
+ void OnSessionConnectTimeout(QuicClientSession* session);
+
+ // Cancels a pending request.
+ void CancelRequest(QuicStreamRequest* request);
+
+ // Closes all current sessions.
+ void CloseAllSessions(int error);
+
+ base::Value* QuicStreamFactoryInfoToValue() const;
+
+ // Delete all cached state objects in |crypto_config_|.
+ void ClearCachedStatesInCryptoConfig();
+
+ // NetworkChangeNotifier::IPAddressObserver methods:
+
+ // Until the servers support roaming, close all connections when the local
+ // IP address changes.
+ virtual void OnIPAddressChanged() OVERRIDE;
+
+ // CertDatabase::Observer methods:
+
+ // We close all sessions when certificate database is changed.
+ virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE;
+ virtual void OnCACertChanged(const X509Certificate* cert) OVERRIDE;
+
+ bool require_confirmation() const {
+ return require_confirmation_;
+ }
+
+ void set_require_confirmation(bool require_confirmation);
+
+ QuicConnectionHelper* helper() { return helper_.get(); }
+
+ bool enable_port_selection() const { return enable_port_selection_; }
+
+ bool has_quic_server_info_factory() {
+ return quic_server_info_factory_ != NULL;
+ }
+
+ void set_quic_server_info_factory(
+ QuicServerInfoFactory* quic_server_info_factory) {
+ DCHECK(!quic_server_info_factory_);
+ quic_server_info_factory_ = quic_server_info_factory;
+ }
+
+ private:
+ class Job;
+ friend class test::QuicStreamFactoryPeer;
+
+ // The key used to find session by ip. Includes
+ // the ip address, port, and scheme.
+ struct NET_EXPORT_PRIVATE IpAliasKey {
+ IpAliasKey();
+ IpAliasKey(IPEndPoint ip_endpoint, bool is_https);
+ ~IpAliasKey();
+
+ IPEndPoint ip_endpoint;
+ bool is_https;
+
+ // Needed to be an element of std::set.
+ bool operator<(const IpAliasKey &other) const;
+ bool operator==(const IpAliasKey &other) const;
+ };
+
+ typedef std::map<QuicServerId, QuicClientSession*> SessionMap;
+ typedef std::map<QuicClientSession*, QuicServerId> SessionIdMap;
+ typedef std::set<QuicServerId> AliasSet;
+ typedef std::map<QuicClientSession*, AliasSet> SessionAliasMap;
+ typedef std::set<QuicClientSession*> SessionSet;
+ typedef std::map<IpAliasKey, SessionSet> IPAliasMap;
+ typedef std::map<QuicServerId, QuicCryptoClientConfig*> CryptoConfigMap;
+ typedef std::map<QuicServerId, Job*> JobMap;
+ typedef std::map<QuicStreamRequest*, Job*> RequestMap;
+ typedef std::set<QuicStreamRequest*> RequestSet;
+ typedef std::map<Job*, RequestSet> JobRequestsMap;
+
+ // Returns a newly created QuicHttpStream owned by the caller, if a
+ // matching session already exists. Returns NULL otherwise.
+ scoped_ptr<QuicHttpStream> CreateIfSessionExists(const QuicServerId& key,
+ const BoundNetLog& net_log);
+
+ bool OnResolution(const QuicServerId& server_id,
+ const AddressList& address_list);
+ void OnJobComplete(Job* job, int rv);
+ bool HasActiveSession(const QuicServerId& server_id) const;
+ bool HasActiveJob(const QuicServerId& server_id) const;
+ int CreateSession(const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> quic_server_info,
+ const AddressList& address_list,
+ const BoundNetLog& net_log,
+ QuicClientSession** session);
+ void ActivateSession(const QuicServerId& key,
+ QuicClientSession* session);
+
+ // Initializes the cached state associated with |server_id| in
+ // |crypto_config_| with the information in |server_info|.
+ void InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info);
+
+ void ProcessGoingAwaySession(QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool was_session_active);
+
+ bool require_confirmation_;
+ HostResolver* host_resolver_;
+ ClientSocketFactory* client_socket_factory_;
+ base::WeakPtr<HttpServerProperties> http_server_properties_;
+ TransportSecurityState* transport_security_state_;
+ QuicServerInfoFactory* quic_server_info_factory_;
+ QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_;
+ QuicRandom* random_generator_;
+ scoped_ptr<QuicClock> clock_;
+ const size_t max_packet_length_;
+
+ // The helper used for all connections.
+ scoped_ptr<QuicConnectionHelper> helper_;
+
+ // Contains owning pointers to all sessions that currently exist.
+ SessionIdMap all_sessions_;
+ // Contains non-owning pointers to currently active session
+ // (not going away session, once they're implemented).
+ SessionMap active_sessions_;
+ // Map from session to set of aliases that this session is known by.
+ SessionAliasMap session_aliases_;
+ // Map from IP address to sessions which are connected to this address.
+ IPAliasMap ip_aliases_;
+
+ // Origins which have gone away recently.
+ AliasSet gone_away_aliases_;
+
+ const QuicConfig config_;
+ QuicCryptoClientConfig crypto_config_;
+
+ JobMap active_jobs_;
+ JobRequestsMap job_requests_map_;
+ RequestMap active_requests_;
+
+ QuicVersionVector supported_versions_;
+
+ // Determine if we should consistently select a client UDP port. If false,
+ // then we will just let the OS select a random client port for each new
+ // connection.
+ bool enable_port_selection_;
+
+ // Set if we always require handshake confirmation. If true, this will
+ // introduce at least one RTT for the handshake before the client sends data.
+ bool always_require_handshake_confirmation_;
+
+ // Set if we do not want connection pooling.
+ bool disable_connection_pooling_;
+
+ // Each profile will (probably) have a unique port_seed_ value. This value is
+ // used to help seed a pseudo-random number generator (PortSuggester) so that
+ // we consistently (within this profile) suggest the same ephemeral port when
+ // we re-connect to any given server/port. The differences between profiles
+ // (probablistically) prevent two profiles from colliding in their ephemeral
+ // port requests.
+ uint64 port_seed_;
+
+ // Local address of socket that was created in CreateSession.
+ IPEndPoint local_address_;
+ bool check_persisted_supports_quic_;
+
+ base::WeakPtrFactory<QuicStreamFactory> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamFactory);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_STREAM_FACTORY_H_
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
new file mode 100644
index 0000000..ece3e64
--- /dev/null
+++ b/net/quic/quic_stream_factory_test.cc
@@ -0,0 +1,1513 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_stream_factory.h"
+
+#include "base/run_loop.h"
+#include "base/strings/string_util.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_verifier.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_response_headers.h"
+#include "net/http/http_response_info.h"
+#include "net/http/http_util.h"
+#include "net/http/transport_security_state.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_http_stream.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/quic/test_tools/quic_test_packet_maker.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_test_utils.h"
+#include "net/ssl/channel_id_service.h"
+#include "net/ssl/default_channel_id_store.h"
+#include "net/test/cert_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+namespace {
+const char kDefaultServerHostName[] = "www.google.com";
+const int kDefaultServerPort = 443;
+} // namespace anonymous
+
+class QuicStreamFactoryPeer {
+ public:
+ static QuicCryptoClientConfig* GetCryptoConfig(QuicStreamFactory* factory) {
+ return &factory->crypto_config_;
+ }
+
+ static bool HasActiveSession(QuicStreamFactory* factory,
+ const HostPortPair& host_port_pair,
+ bool is_https) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ return factory->HasActiveSession(server_id);
+ }
+
+ static QuicClientSession* GetActiveSession(
+ QuicStreamFactory* factory,
+ const HostPortPair& host_port_pair,
+ bool is_https) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ DCHECK(factory->HasActiveSession(server_id));
+ return factory->active_sessions_[server_id];
+ }
+
+ static scoped_ptr<QuicHttpStream> CreateIfSessionExists(
+ QuicStreamFactory* factory,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ const BoundNetLog& net_log) {
+ QuicServerId server_id(host_port_pair, is_https, PRIVACY_MODE_DISABLED);
+ return factory->CreateIfSessionExists(server_id, net_log);
+ }
+
+ static bool IsLiveSession(QuicStreamFactory* factory,
+ QuicClientSession* session) {
+ for (QuicStreamFactory::SessionIdMap::iterator it =
+ factory->all_sessions_.begin();
+ it != factory->all_sessions_.end(); ++it) {
+ if (it->first == session)
+ return true;
+ }
+ return false;
+ }
+
+ static void DisableConnectionPooling(QuicStreamFactory* factory) {
+ factory->disable_connection_pooling_ = true;
+ }
+};
+
+class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
+ protected:
+ QuicStreamFactoryTest()
+ : random_generator_(0),
+ clock_(new MockClock()),
+ maker_(GetParam(), 0, clock_),
+ cert_verifier_(CertVerifier::CreateDefault()),
+ channel_id_service_(
+ new ChannelIDService(new DefaultChannelIDStore(nullptr),
+ base::MessageLoopProxy::current())),
+ factory_(&host_resolver_,
+ &socket_factory_,
+ base::WeakPtr<HttpServerProperties>(),
+ cert_verifier_.get(),
+ channel_id_service_.get(),
+ &transport_security_state_,
+ &crypto_client_stream_factory_,
+ &random_generator_,
+ clock_,
+ kDefaultMaxPacketSize,
+ std::string(),
+ SupportedVersions(GetParam()),
+ /*enable_port_selection=*/true,
+ /*always_require_handshake_confirmation=*/false,
+ /*disable_connection_pooling=*/false,
+ QuicTagVector()),
+ host_port_pair_(kDefaultServerHostName, kDefaultServerPort),
+ is_https_(false),
+ privacy_mode_(PRIVACY_MODE_DISABLED) {
+ factory_.set_require_confirmation(false);
+ clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
+ }
+
+ scoped_ptr<QuicHttpStream> CreateIfSessionExists(
+ const HostPortPair& host_port_pair,
+ const BoundNetLog& net_log) {
+ return QuicStreamFactoryPeer::CreateIfSessionExists(
+ &factory_, host_port_pair, false, net_log_);
+ }
+
+ int GetSourcePortForNewSession(const HostPortPair& destination) {
+ return GetSourcePortForNewSessionInner(destination, false);
+ }
+
+ int GetSourcePortForNewSessionAndGoAway(
+ const HostPortPair& destination) {
+ return GetSourcePortForNewSessionInner(destination, true);
+ }
+
+ int GetSourcePortForNewSessionInner(const HostPortPair& destination,
+ bool goaway_received) {
+ // Should only be called if there is no active session for this destination.
+ EXPECT_EQ(nullptr, CreateIfSessionExists(destination, net_log_).get());
+ size_t socket_count = socket_factory_.udp_client_sockets().size();
+
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(destination,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, destination, is_https_);
+
+ if (socket_count + 1 != socket_factory_.udp_client_sockets().size()) {
+ EXPECT_TRUE(false);
+ return 0;
+ }
+
+ IPEndPoint endpoint;
+ socket_factory_.
+ udp_client_sockets()[socket_count]->GetLocalAddress(&endpoint);
+ int port = endpoint.port();
+ if (goaway_received) {
+ QuicGoAwayFrame goaway(QUIC_NO_ERROR, 1, "");
+ session->OnGoAway(goaway);
+ }
+
+ factory_.OnSessionClosed(session);
+ EXPECT_EQ(nullptr, CreateIfSessionExists(destination, net_log_).get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ return port;
+ }
+
+ scoped_ptr<QuicEncryptedPacket> ConstructRstPacket() {
+ QuicStreamId stream_id = kClientDataStreamId1;
+ return maker_.MakeRstPacket(
+ 1, true, stream_id,
+ AdjustErrorForVersion(QUIC_RST_FLOW_CONTROL_ACCOUNTING, GetParam()));
+ }
+
+ MockHostResolver host_resolver_;
+ DeterministicMockClientSocketFactory socket_factory_;
+ MockCryptoClientStreamFactory crypto_client_stream_factory_;
+ MockRandom random_generator_;
+ MockClock* clock_; // Owned by factory_.
+ QuicTestPacketMaker maker_;
+ scoped_ptr<CertVerifier> cert_verifier_;
+ scoped_ptr<ChannelIDService> channel_id_service_;
+ TransportSecurityState transport_security_state_;
+ QuicStreamFactory factory_;
+ HostPortPair host_port_pair_;
+ bool is_https_;
+ PrivacyMode privacy_mode_;
+ BoundNetLog net_log_;
+ TestCompletionCallback callback_;
+};
+
+INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest,
+ ::testing::ValuesIn(QuicSupportedVersions()));
+
+TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) {
+ EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get());
+}
+
+TEST_P(QuicStreamFactoryTest, Create) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ // Will reset stream 3.
+ stream = CreateIfSessionExists(host_port_pair_, net_log_);
+ EXPECT_TRUE(stream.get());
+
+ // TODO(rtenneti): We should probably have a tests that HTTP and HTTPS result
+ // in streams on different sessions.
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ stream = request2.ReleaseStream(); // Will reset stream 5.
+ stream.reset(); // Will reset stream 7.
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CreateZeroRtt) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::ZERO_RTT);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+ "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CreateZeroRttPost) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::ZERO_RTT);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
+ "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ // Posts require handshake confirmation, so this will return asynchronously.
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "POST",
+ net_log_,
+ callback_.callback()));
+
+ // Confirm the handshake and verify that the stream is created.
+ crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent(
+ QuicSession::HANDSHAKE_CONFIRMED);
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ !is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, !is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, Pooling) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoPoolingIfDisabled) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ // Disable connection pooling.
+ QuicStreamFactoryPeer::DisableConnectionPooling(&factory_);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(&factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server2("mail.google.com", kDefaultServerPort);
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(
+ kDefaultServerHostName, "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(
+ "mail.google.com", "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ factory_.OnSessionGoingAway(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, server2, is_https_));
+
+ TestCompletionCallback callback3;
+ QuicStreamRequest request3(&factory_);
+ EXPECT_EQ(OK,
+ request3.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback3.callback()));
+ scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream();
+ EXPECT_TRUE(stream3.get());
+
+ EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, HttpsPooling) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ // www.example.com
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ verify_details.cert_verify_result.is_issued_by_known_root = true;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoHttpsPoolingIfDisabled) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ // www.example.com
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ verify_details.cert_verify_result.is_issued_by_known_root = true;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ // Disable connection pooling.
+ QuicStreamFactoryPeer::DisableConnectionPooling(&factory_);
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.google.com", 443);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org
+ // www.example.com
+ // But is not valid for mail.google.com (server2).
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+ uint8 primary_pin = 1;
+ uint8 backup_pin = 2;
+ test::AddPin(&transport_security_state_, "mail.example.org", primary_pin,
+ backup_pin);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ verify_details.cert_verify_result.is_issued_by_known_root = true;
+ verify_details.cert_verify_result.public_key_hashes.push_back(
+ test::GetTestHashValue(primary_pin));
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithMatchingPinsIfDisabled) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+ uint8 primary_pin = 1;
+ uint8 backup_pin = 2;
+ test::AddPin(&transport_security_state_, "mail.example.org", primary_pin,
+ backup_pin);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ verify_details.cert_verify_result.is_issued_by_known_root = true;
+ verify_details.cert_verify_result.public_key_hashes.push_back(
+ test::GetTestHashValue(primary_pin));
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ // Disable connection pooling.
+ QuicStreamFactoryPeer::DisableConnectionPooling(&factory_);
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data1(reads, arraysize(reads), nullptr, 0);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data1.StopAfter(1);
+ socket_data2.StopAfter(1);
+
+ HostPortPair server1("www.example.org", 443);
+ HostPortPair server2("mail.example.org", 443);
+ uint8 primary_pin = 1;
+ uint8 backup_pin = 2;
+ uint8 bad_pin = 3;
+ test::AddPin(&transport_security_state_, "mail.example.org", primary_pin,
+ backup_pin);
+
+ // Load a cert that is valid for:
+ // www.example.org (server1)
+ // mail.example.org (server2)
+ base::FilePath certs_dir = GetTestCertsDirectory();
+ scoped_refptr<X509Certificate> test_cert(
+ ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
+ ASSERT_NE(static_cast<X509Certificate*>(nullptr), test_cert.get());
+ ProofVerifyDetailsChromium verify_details;
+ verify_details.cert_verify_result.verified_cert = test_cert;
+ verify_details.cert_verify_result.is_issued_by_known_root = true;
+ verify_details.cert_verify_result.public_key_hashes.push_back(
+ test::GetTestHashValue(bad_pin));
+ crypto_client_stream_factory_.set_proof_verify_details(&verify_details);
+
+
+ host_resolver_.set_synchronous_mode(true);
+ host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", "");
+ host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", "");
+
+ QuicStreamRequest request(&factory_);
+ is_https_ = true;
+ EXPECT_EQ(OK,
+ request.Request(server1,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ TestCompletionCallback callback;
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(OK,
+ request2.Request(server2,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server1, is_https_),
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, server2, is_https_));
+
+ EXPECT_TRUE(socket_data1.at_read_eof());
+ EXPECT_TRUE(socket_data1.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, Goaway) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_data.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ DeterministicSocketData socket_data2(reads, arraysize(reads), nullptr, 0);
+ socket_data2.StopAfter(1);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream.get());
+
+ // Mark the session as going away. Ensure that while it is still alive
+ // that it is no longer active.
+ QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_);
+ factory_.OnSessionGoingAway(session);
+ EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
+ EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get());
+
+ // Create a new request for the same destination and verify that a
+ // new session is created.
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
+ EXPECT_TRUE(stream2.get());
+
+ EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
+ host_port_pair_,
+ is_https_));
+ EXPECT_NE(session,
+ QuicStreamFactoryPeer::GetActiveSession(
+ &factory_, host_port_pair_, is_https_));
+ EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
+
+ stream2.reset();
+ stream.reset();
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, MaxOpenStream) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ QuicStreamId stream_id = kClientDataStreamId1;
+ scoped_ptr<QuicEncryptedPacket> rst(
+ maker_.MakeRstPacket(1, true, stream_id, QUIC_STREAM_CANCELLED));
+ MockWrite writes[] = {
+ MockWrite(ASYNC, rst->data(), rst->length(), 1),
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes, arraysize(writes));
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ HttpRequestInfo request_info;
+ std::vector<QuicHttpStream*> streams;
+ // The MockCryptoClientStream sets max_open_streams to be
+ // 2 * kDefaultMaxStreamsPerConnection.
+ for (size_t i = 0; i < 2 * kDefaultMaxStreamsPerConnection; i++) {
+ QuicStreamRequest request(&factory_);
+ int rv = request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback());
+ if (i == 0) {
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ } else {
+ EXPECT_EQ(OK, rv);
+ }
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream);
+ EXPECT_EQ(OK, stream->InitializeStream(
+ &request_info, DEFAULT_PRIORITY, net_log_, CompletionCallback()));
+ streams.push_back(stream.release());
+ }
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(OK,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ CompletionCallback()));
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ EXPECT_TRUE(stream);
+ EXPECT_EQ(ERR_IO_PENDING, stream->InitializeStream(
+ &request_info, DEFAULT_PRIORITY, net_log_, callback_.callback()));
+
+ // Close the first stream.
+ streams.front()->Close(false);
+
+ ASSERT_TRUE(callback_.have_result());
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ STLDeleteElements(&streams);
+}
+
+TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) {
+ DeterministicSocketData socket_data(nullptr, 0, nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+
+ host_resolver_.rules()->AddSimulatedFailure(kDefaultServerHostName);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) {
+ MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
+ DeterministicSocketData socket_data(nullptr, 0, nullptr, 0);
+ socket_data.set_connect_data(connect);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult());
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CancelCreate) {
+ MockRead reads[] = {
+ MockRead(ASYNC, OK, 0) // EOF
+ };
+ DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ {
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+ }
+
+ socket_data.StopAfter(1);
+ base::RunLoop run_loop;
+ run_loop.RunUntilIdle();
+
+ scoped_ptr<QuicHttpStream> stream(
+ CreateIfSessionExists(host_port_pair_, net_log_));
+ EXPECT_TRUE(stream.get());
+ stream.reset();
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, CreateConsistentEphemeralPort) {
+ // Sequentially connect to the default host, then another host, and then the
+ // default host. Verify that the default host gets a consistent ephemeral
+ // port, that is different from the other host's connection.
+
+ std::string other_server_name = "other.google.com";
+ EXPECT_NE(kDefaultServerHostName, other_server_name);
+ HostPortPair host_port_pair2(other_server_name, kDefaultServerPort);
+
+ int original_port = GetSourcePortForNewSession(host_port_pair_);
+ EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair2));
+ EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_));
+}
+
+TEST_P(QuicStreamFactoryTest, GoAwayDisablesConsistentEphemeralPort) {
+ // Get a session to the host using the port suggester.
+ int original_port =
+ GetSourcePortForNewSessionAndGoAway(host_port_pair_);
+ // Verify that the port is different after the goaway.
+ EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_pair_));
+ // Since the previous session did not goaway we should see the original port.
+ EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_pair_));
+}
+
+TEST_P(QuicStreamFactoryTest, CloseAllSessions) {
+ MockRead reads[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? nullptr : &writes[0],
+ writes.size());
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ MockRead reads2[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ DeterministicSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ HttpRequestInfo request_info;
+ EXPECT_EQ(OK, stream->InitializeStream(&request_info,
+ DEFAULT_PRIORITY,
+ net_log_, CompletionCallback()));
+
+ // Close the session and verify that stream saw the error.
+ factory_.CloseAllSessions(ERR_INTERNET_DISCONNECTED);
+ EXPECT_EQ(ERR_INTERNET_DISCONNECTED,
+ stream->ReadResponseHeaders(callback_.callback()));
+
+ // Now attempting to request a stream to the same origin should create
+ // a new session.
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ stream.reset(); // Will reset stream 3.
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) {
+ MockRead reads[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? nullptr : &writes[0],
+ writes.size());
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ MockRead reads2[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ DeterministicSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ HttpRequestInfo request_info;
+ EXPECT_EQ(OK, stream->InitializeStream(&request_info,
+ DEFAULT_PRIORITY,
+ net_log_, CompletionCallback()));
+
+ // Change the IP address and verify that stream saw the error.
+ factory_.OnIPAddressChanged();
+ EXPECT_EQ(ERR_NETWORK_CHANGED,
+ stream->ReadResponseHeaders(callback_.callback()));
+ EXPECT_TRUE(factory_.require_confirmation());
+
+ // Now attempting to request a stream to the same origin should create
+ // a new session.
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ stream.reset(); // Will reset stream 3.
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, OnCertAdded) {
+ MockRead reads[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? nullptr : &writes[0],
+ writes.size());
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ MockRead reads2[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ DeterministicSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ HttpRequestInfo request_info;
+ EXPECT_EQ(OK, stream->InitializeStream(&request_info,
+ DEFAULT_PRIORITY,
+ net_log_, CompletionCallback()));
+
+ // Add a cert and verify that stream saw the event.
+ factory_.OnCertAdded(nullptr);
+ EXPECT_EQ(ERR_CERT_DATABASE_CHANGED,
+ stream->ReadResponseHeaders(callback_.callback()));
+ EXPECT_FALSE(factory_.require_confirmation());
+
+ // Now attempting to request a stream to the same origin should create
+ // a new session.
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ stream.reset(); // Will reset stream 3.
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, OnCACertChanged) {
+ MockRead reads[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ scoped_ptr<QuicEncryptedPacket> rst(ConstructRstPacket());
+ std::vector<MockWrite> writes;
+ writes.push_back(MockWrite(ASYNC, rst->data(), rst->length(), 1));
+ DeterministicSocketData socket_data(reads, arraysize(reads),
+ writes.empty() ? nullptr : &writes[0],
+ writes.size());
+ socket_factory_.AddSocketDataProvider(&socket_data);
+ socket_data.StopAfter(1);
+
+ MockRead reads2[] = {
+ MockRead(ASYNC, 0, 0) // EOF
+ };
+ DeterministicSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0);
+ socket_factory_.AddSocketDataProvider(&socket_data2);
+ socket_data2.StopAfter(1);
+
+ QuicStreamRequest request(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
+ HttpRequestInfo request_info;
+ EXPECT_EQ(OK, stream->InitializeStream(&request_info,
+ DEFAULT_PRIORITY,
+ net_log_, CompletionCallback()));
+
+ // Change the CA cert and verify that stream saw the event.
+ factory_.OnCACertChanged(nullptr);
+ EXPECT_EQ(ERR_CERT_DATABASE_CHANGED,
+ stream->ReadResponseHeaders(callback_.callback()));
+ EXPECT_FALSE(factory_.require_confirmation());
+
+ // Now attempting to request a stream to the same origin should create
+ // a new session.
+
+ QuicStreamRequest request2(&factory_);
+ EXPECT_EQ(ERR_IO_PENDING,
+ request2.Request(host_port_pair_,
+ is_https_,
+ privacy_mode_,
+ "GET",
+ net_log_,
+ callback_.callback()));
+
+ EXPECT_EQ(OK, callback_.WaitForResult());
+ stream = request2.ReleaseStream();
+ stream.reset(); // Will reset stream 3.
+
+ EXPECT_TRUE(socket_data.at_read_eof());
+ EXPECT_TRUE(socket_data.at_write_eof());
+ EXPECT_TRUE(socket_data2.at_read_eof());
+ EXPECT_TRUE(socket_data2.at_write_eof());
+}
+
+TEST_P(QuicStreamFactoryTest, SharedCryptoConfig) {
+ vector<string> cannoncial_suffixes;
+ cannoncial_suffixes.push_back(string(".c.youtube.com"));
+ cannoncial_suffixes.push_back(string(".googlevideo.com"));
+
+ for (unsigned i = 0; i < cannoncial_suffixes.size(); ++i) {
+ string r1_host_name("r1");
+ string r2_host_name("r2");
+ r1_host_name.append(cannoncial_suffixes[i]);
+ r2_host_name.append(cannoncial_suffixes[i]);
+
+ HostPortPair host_port_pair1(r1_host_name, 80);
+ QuicCryptoClientConfig* crypto_config =
+ QuicStreamFactoryPeer::GetCryptoConfig(&factory_);
+ QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_);
+ QuicCryptoClientConfig::CachedState* cached1 =
+ crypto_config->LookupOrCreate(server_id1);
+ EXPECT_FALSE(cached1->proof_valid());
+ EXPECT_TRUE(cached1->source_address_token().empty());
+
+ // Mutate the cached1 to have different data.
+ // TODO(rtenneti): mutate other members of CachedState.
+ cached1->set_source_address_token(r1_host_name);
+ cached1->SetProofValid();
+
+ HostPortPair host_port_pair2(r2_host_name, 80);
+ QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_);
+ QuicCryptoClientConfig::CachedState* cached2 =
+ crypto_config->LookupOrCreate(server_id2);
+ EXPECT_EQ(cached1->source_address_token(), cached2->source_address_token());
+ EXPECT_TRUE(cached2->proof_valid());
+ }
+}
+
+TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
+ vector<string> cannoncial_suffixes;
+ cannoncial_suffixes.push_back(string(".c.youtube.com"));
+ cannoncial_suffixes.push_back(string(".googlevideo.com"));
+
+ for (unsigned i = 0; i < cannoncial_suffixes.size(); ++i) {
+ string r3_host_name("r3");
+ string r4_host_name("r4");
+ r3_host_name.append(cannoncial_suffixes[i]);
+ r4_host_name.append(cannoncial_suffixes[i]);
+
+ HostPortPair host_port_pair1(r3_host_name, 80);
+ QuicCryptoClientConfig* crypto_config =
+ QuicStreamFactoryPeer::GetCryptoConfig(&factory_);
+ QuicServerId server_id1(host_port_pair1, is_https_, privacy_mode_);
+ QuicCryptoClientConfig::CachedState* cached1 =
+ crypto_config->LookupOrCreate(server_id1);
+ EXPECT_FALSE(cached1->proof_valid());
+ EXPECT_TRUE(cached1->source_address_token().empty());
+
+ // Mutate the cached1 to have different data.
+ // TODO(rtenneti): mutate other members of CachedState.
+ cached1->set_source_address_token(r3_host_name);
+ cached1->SetProofInvalid();
+
+ HostPortPair host_port_pair2(r4_host_name, 80);
+ QuicServerId server_id2(host_port_pair2, is_https_, privacy_mode_);
+ QuicCryptoClientConfig::CachedState* cached2 =
+ crypto_config->LookupOrCreate(server_id2);
+ EXPECT_NE(cached1->source_address_token(), cached2->source_address_token());
+ EXPECT_TRUE(cached2->source_address_token().empty());
+ EXPECT_FALSE(cached2->proof_valid());
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_stream_sequencer.cc b/net/quic/quic_stream_sequencer.cc
new file mode 100644
index 0000000..5df81e5
--- /dev/null
+++ b/net/quic/quic_stream_sequencer.cc
@@ -0,0 +1,294 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_stream_sequencer.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "net/quic/reliable_quic_stream.h"
+
+using std::make_pair;
+using std::min;
+using std::numeric_limits;
+
+namespace net {
+
+QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream)
+ : stream_(quic_stream),
+ num_bytes_consumed_(0),
+ close_offset_(numeric_limits<QuicStreamOffset>::max()),
+ blocked_(false),
+ num_bytes_buffered_(0),
+ num_frames_received_(0),
+ num_duplicate_frames_received_(0) {
+}
+
+QuicStreamSequencer::~QuicStreamSequencer() {
+}
+
+void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) {
+ ++num_frames_received_;
+ if (IsDuplicate(frame)) {
+ ++num_duplicate_frames_received_;
+ // Silently ignore duplicates.
+ return;
+ }
+
+ if (FrameOverlapsBufferedData(frame)) {
+ stream_->CloseConnectionWithDetails(
+ QUIC_INVALID_STREAM_FRAME, "Stream frame overlaps with buffered data.");
+ return;
+ }
+
+ QuicStreamOffset byte_offset = frame.offset;
+ size_t data_len = frame.data.TotalBufferSize();
+ if (data_len == 0 && !frame.fin) {
+ // Stream frames must have data or a fin flag.
+ stream_->CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME,
+ "Empty stream frame without FIN set.");
+ return;
+ }
+
+ if (frame.fin) {
+ CloseStreamAtOffset(frame.offset + data_len);
+ if (data_len == 0) {
+ return;
+ }
+ }
+
+ IOVector data;
+ data.AppendIovec(frame.data.iovec(), frame.data.Size());
+
+ // If the frame has arrived in-order then we can process it immediately, only
+ // buffering if the stream is unable to process it.
+ if (!blocked_ && byte_offset == num_bytes_consumed_) {
+ DVLOG(1) << "Processing byte offset " << byte_offset;
+ size_t bytes_consumed = 0;
+ for (size_t i = 0; i < data.Size(); ++i) {
+ bytes_consumed += stream_->ProcessRawData(
+ static_cast<char*>(data.iovec()[i].iov_base),
+ data.iovec()[i].iov_len);
+ }
+ num_bytes_consumed_ += bytes_consumed;
+ stream_->AddBytesConsumed(bytes_consumed);
+
+ if (MaybeCloseStream()) {
+ return;
+ }
+ if (bytes_consumed > data_len) {
+ stream_->Reset(QUIC_ERROR_PROCESSING_STREAM);
+ return;
+ } else if (bytes_consumed == data_len) {
+ FlushBufferedFrames();
+ return; // it's safe to ack this frame.
+ } else {
+ // Set ourselves up to buffer what's left.
+ data_len -= bytes_consumed;
+ data.Consume(bytes_consumed);
+ byte_offset += bytes_consumed;
+ }
+ }
+
+ // Buffer any remaining data to be consumed by the stream when ready.
+ for (size_t i = 0; i < data.Size(); ++i) {
+ DVLOG(1) << "Buffering stream data at offset " << byte_offset;
+ const iovec& iov = data.iovec()[i];
+ buffered_frames_.insert(make_pair(
+ byte_offset, string(static_cast<char*>(iov.iov_base), iov.iov_len)));
+ byte_offset += iov.iov_len;
+ num_bytes_buffered_ += iov.iov_len;
+ }
+ return;
+}
+
+void QuicStreamSequencer::CloseStreamAtOffset(QuicStreamOffset offset) {
+ const QuicStreamOffset kMaxOffset = numeric_limits<QuicStreamOffset>::max();
+
+ // If we have a scheduled termination or close, any new offset should match
+ // it.
+ if (close_offset_ != kMaxOffset && offset != close_offset_) {
+ stream_->Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS);
+ return;
+ }
+
+ close_offset_ = offset;
+
+ MaybeCloseStream();
+}
+
+bool QuicStreamSequencer::MaybeCloseStream() {
+ if (!blocked_ && IsClosed()) {
+ DVLOG(1) << "Passing up termination, as we've processed "
+ << num_bytes_consumed_ << " of " << close_offset_
+ << " bytes.";
+ // Technically it's an error if num_bytes_consumed isn't exactly
+ // equal, but error handling seems silly at this point.
+ stream_->OnFinRead();
+ buffered_frames_.clear();
+ num_bytes_buffered_ = 0;
+ return true;
+ }
+ return false;
+}
+
+int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) {
+ DCHECK(!blocked_);
+ FrameMap::iterator it = buffered_frames_.begin();
+ size_t index = 0;
+ QuicStreamOffset offset = num_bytes_consumed_;
+ while (it != buffered_frames_.end() && index < iov_len) {
+ if (it->first != offset) return index;
+
+ iov[index].iov_base = static_cast<void*>(
+ const_cast<char*>(it->second.data()));
+ iov[index].iov_len = it->second.size();
+ offset += it->second.size();
+
+ ++index;
+ ++it;
+ }
+ return index;
+}
+
+int QuicStreamSequencer::Readv(const struct iovec* iov, size_t iov_len) {
+ DCHECK(!blocked_);
+ FrameMap::iterator it = buffered_frames_.begin();
+ size_t iov_index = 0;
+ size_t iov_offset = 0;
+ size_t frame_offset = 0;
+ size_t initial_bytes_consumed = num_bytes_consumed_;
+
+ while (iov_index < iov_len &&
+ it != buffered_frames_.end() &&
+ it->first == num_bytes_consumed_) {
+ int bytes_to_read = min(iov[iov_index].iov_len - iov_offset,
+ it->second.size() - frame_offset);
+
+ char* iov_ptr = static_cast<char*>(iov[iov_index].iov_base) + iov_offset;
+ memcpy(iov_ptr,
+ it->second.data() + frame_offset, bytes_to_read);
+ frame_offset += bytes_to_read;
+ iov_offset += bytes_to_read;
+
+ if (iov[iov_index].iov_len == iov_offset) {
+ // We've filled this buffer.
+ iov_offset = 0;
+ ++iov_index;
+ }
+ if (it->second.size() == frame_offset) {
+ // We've copied this whole frame
+ RecordBytesConsumed(it->second.size());
+ buffered_frames_.erase(it);
+ it = buffered_frames_.begin();
+ frame_offset = 0;
+ }
+ }
+ // We've finished copying. If we have a partial frame, update it.
+ if (frame_offset != 0) {
+ buffered_frames_.insert(
+ make_pair(it->first + frame_offset, it->second.substr(frame_offset)));
+ buffered_frames_.erase(buffered_frames_.begin());
+ RecordBytesConsumed(frame_offset);
+ }
+ return num_bytes_consumed_ - initial_bytes_consumed;
+}
+
+bool QuicStreamSequencer::HasBytesToRead() const {
+ FrameMap::const_iterator it = buffered_frames_.begin();
+
+ return it != buffered_frames_.end() && it->first == num_bytes_consumed_;
+}
+
+bool QuicStreamSequencer::IsClosed() const {
+ return num_bytes_consumed_ >= close_offset_;
+}
+
+bool QuicStreamSequencer::FrameOverlapsBufferedData(
+ const QuicStreamFrame& frame) const {
+ if (buffered_frames_.empty()) {
+ return false;
+ }
+
+ FrameMap::const_iterator next_frame =
+ buffered_frames_.lower_bound(frame.offset);
+ // Duplicate frames should have been dropped in IsDuplicate.
+ DCHECK(next_frame == buffered_frames_.end() ||
+ next_frame->first != frame.offset);
+
+ // If there is a buffered frame with a higher starting offset, then we check
+ // to see if the new frame runs into the higher frame.
+ if (next_frame != buffered_frames_.end() &&
+ (frame.offset + frame.data.TotalBufferSize()) > next_frame->first) {
+ DVLOG(1) << "New frame overlaps next frame: " << frame.offset << " + "
+ << frame.data.TotalBufferSize() << " > " << next_frame->first;
+ return true;
+ }
+
+ // If there is a buffered frame with a lower starting offset, then we check
+ // to see if the buffered frame runs into the new frame.
+ if (next_frame != buffered_frames_.begin()) {
+ FrameMap::const_iterator preceeding_frame = --next_frame;
+ QuicStreamOffset offset = preceeding_frame->first;
+ uint64 data_length = preceeding_frame->second.length();
+ if ((offset + data_length) > frame.offset) {
+ DVLOG(1) << "Preceeding frame overlaps new frame: " << offset << " + "
+ << data_length << " > " << frame.offset;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicStreamSequencer::IsDuplicate(const QuicStreamFrame& frame) const {
+ // A frame is duplicate if the frame offset is smaller than our bytes consumed
+ // or we have stored the frame in our map.
+ // TODO(pwestin): Is it possible that a new frame contain more data even if
+ // the offset is the same?
+ return frame.offset < num_bytes_consumed_ ||
+ buffered_frames_.find(frame.offset) != buffered_frames_.end();
+}
+
+void QuicStreamSequencer::SetBlockedUntilFlush() {
+ blocked_ = true;
+}
+
+void QuicStreamSequencer::FlushBufferedFrames() {
+ blocked_ = false;
+ FrameMap::iterator it = buffered_frames_.find(num_bytes_consumed_);
+ while (it != buffered_frames_.end()) {
+ DVLOG(1) << "Flushing buffered packet at offset " << it->first;
+ string* data = &it->second;
+ size_t bytes_consumed = stream_->ProcessRawData(data->c_str(),
+ data->size());
+ RecordBytesConsumed(bytes_consumed);
+ if (MaybeCloseStream()) {
+ return;
+ }
+ if (bytes_consumed > data->size()) {
+ stream_->Reset(QUIC_ERROR_PROCESSING_STREAM); // Programming error
+ return;
+ } else if (bytes_consumed == data->size()) {
+ buffered_frames_.erase(it);
+ it = buffered_frames_.find(num_bytes_consumed_);
+ } else {
+ string new_data = it->second.substr(bytes_consumed);
+ buffered_frames_.erase(it);
+ buffered_frames_.insert(make_pair(num_bytes_consumed_, new_data));
+ return;
+ }
+ }
+ MaybeCloseStream();
+}
+
+void QuicStreamSequencer::RecordBytesConsumed(size_t bytes_consumed) {
+ num_bytes_consumed_ += bytes_consumed;
+ num_bytes_buffered_ -= bytes_consumed;
+
+ stream_->AddBytesConsumed(bytes_consumed);
+}
+
+} // namespace net
diff --git a/net/quic/quic_stream_sequencer.h b/net/quic/quic_stream_sequencer.h
new file mode 100644
index 0000000..5da2c0e
--- /dev/null
+++ b/net/quic/quic_stream_sequencer.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_STREAM_SEQUENCER_H_
+#define NET_QUIC_QUIC_STREAM_SEQUENCER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "net/base/iovec.h"
+#include "net/quic/quic_protocol.h"
+
+using std::map;
+using std::string;
+
+namespace net {
+
+namespace test {
+class QuicStreamSequencerPeer;
+} // namespace test
+
+class QuicSession;
+class ReliableQuicStream;
+
+// Buffers frames until we have something which can be passed
+// up to the next layer.
+// TOOD(alyssar) add some checks for overflow attempts [1, 256,] [2, 256]
+class NET_EXPORT_PRIVATE QuicStreamSequencer {
+ public:
+ explicit QuicStreamSequencer(ReliableQuicStream* quic_stream);
+ virtual ~QuicStreamSequencer();
+
+ // If the frame is the next one we need in order to process in-order data,
+ // ProcessData will be immediately called on the stream until all buffered
+ // data is processed or the stream fails to consume data. Any unconsumed
+ // data will be buffered. If the frame is not the next in line, it will be
+ // buffered.
+ void OnStreamFrame(const QuicStreamFrame& frame);
+
+ // Once data is buffered, it's up to the stream to read it when the stream
+ // can handle more data. The following three functions make that possible.
+
+ // Fills in up to iov_len iovecs with the next readable regions. Returns the
+ // number of iovs used. Non-destructive of the underlying data.
+ int GetReadableRegions(iovec* iov, size_t iov_len);
+
+ // Copies the data into the iov_len buffers provided. Returns the number of
+ // bytes read. Any buffered data no longer in use will be released.
+ int Readv(const struct iovec* iov, size_t iov_len);
+
+ // Returns true if the sequncer has bytes available for reading.
+ bool HasBytesToRead() const;
+
+ // Returns true if the sequencer has delivered the fin.
+ bool IsClosed() const;
+
+ // Returns true if the sequencer has received this frame before.
+ bool IsDuplicate(const QuicStreamFrame& frame) const;
+
+ // Returns true if |frame| contains data which overlaps buffered data
+ // (indicating an invalid stream frame has been received).
+ bool FrameOverlapsBufferedData(const QuicStreamFrame& frame) const;
+
+ // Calls |ProcessRawData| on |stream_| for each buffered frame that may
+ // be processed.
+ void FlushBufferedFrames();
+
+ // Blocks processing of frames until |FlushBufferedFrames| is called.
+ void SetBlockedUntilFlush();
+
+ size_t num_bytes_buffered() const { return num_bytes_buffered_; }
+ QuicStreamOffset num_bytes_consumed() const { return num_bytes_consumed_; }
+
+ int num_frames_received() const { return num_frames_received_; }
+
+ int num_duplicate_frames_received() const {
+ return num_duplicate_frames_received_;
+ }
+
+ private:
+ friend class test::QuicStreamSequencerPeer;
+
+ // Wait until we've seen 'offset' bytes, and then terminate the stream.
+ void CloseStreamAtOffset(QuicStreamOffset offset);
+
+ // If we've received a FIN and have processed all remaining data, then inform
+ // the stream of FIN, and clear buffers.
+ bool MaybeCloseStream();
+
+ // Called whenever bytes are consumed by the stream. Updates
+ // num_bytes_consumed_ and num_bytes_buffered_.
+ void RecordBytesConsumed(size_t bytes_consumed);
+
+ // The stream which owns this sequencer.
+ ReliableQuicStream* stream_;
+
+ // The last data consumed by the stream.
+ QuicStreamOffset num_bytes_consumed_;
+
+ // TODO(alyssar) use something better than strings.
+ // TODO(rjshade): In future we may support retransmission of partial stream
+ // frames, in which case we will have to allow receipt of overlapping frames.
+ // Maybe write new frames into a ring buffer, and keep track of consumed
+ // bytes, and gaps.
+ typedef map<QuicStreamOffset, string> FrameMap;
+
+ // Stores buffered frames (maps from sequence number -> frame data as string).
+ FrameMap buffered_frames_;
+
+ // The offset, if any, we got a stream termination for. When this many bytes
+ // have been processed, the sequencer will be closed.
+ QuicStreamOffset close_offset_;
+
+ // If true, the sequencer is blocked from passing data to the stream and will
+ // buffer all new incoming data until FlushBufferedFrames is called.
+ bool blocked_;
+
+ // Tracks how many bytes the sequencer has buffered.
+ size_t num_bytes_buffered_;
+
+ // Count of the number of frames received.
+ int num_frames_received_;
+
+ // Count of the number of duplicate frames received.
+ int num_duplicate_frames_received_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencer);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_STREAM_SEQUENCER_H_
diff --git a/net/quic/quic_stream_sequencer_test.cc b/net/quic/quic_stream_sequencer_test.cc
new file mode 100644
index 0000000..c553fe5
--- /dev/null
+++ b/net/quic/quic_stream_sequencer_test.cc
@@ -0,0 +1,445 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_stream_sequencer.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_stream_sequencer_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::map;
+using std::min;
+using std::pair;
+using std::vector;
+using testing::_;
+using testing::AnyNumber;
+using testing::InSequence;
+using testing::Return;
+using testing::StrEq;
+
+namespace net {
+namespace test {
+
+class MockStream : public ReliableQuicStream {
+ public:
+ MockStream(QuicSession* session, QuicStreamId id)
+ : ReliableQuicStream(id, session) {
+ }
+
+ MOCK_METHOD0(OnFinRead, void());
+ MOCK_METHOD2(ProcessRawData, uint32(const char* data, uint32 data_len));
+ MOCK_METHOD2(CloseConnectionWithDetails, void(QuicErrorCode error,
+ const string& details));
+ MOCK_METHOD1(Reset, void(QuicRstStreamErrorCode error));
+ MOCK_METHOD0(OnCanWrite, void());
+ virtual QuicPriority EffectivePriority() const OVERRIDE {
+ return QuicUtils::HighestPriority();
+ }
+ virtual bool IsFlowControlEnabled() const {
+ return true;
+ }
+};
+
+namespace {
+
+static const char kPayload[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+class QuicStreamSequencerTest : public ::testing::Test {
+ protected:
+ QuicStreamSequencerTest()
+ : connection_(new MockConnection(false)),
+ session_(connection_),
+ stream_(&session_, 1),
+ sequencer_(new QuicStreamSequencer(&stream_)),
+ buffered_frames_(
+ QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get())) {
+ }
+
+ bool VerifyReadableRegions(const char** expected, size_t num_expected) {
+ iovec iovecs[5];
+ size_t num_iovecs = sequencer_->GetReadableRegions(iovecs,
+ arraysize(iovecs));
+ return VerifyIovecs(iovecs, num_iovecs, expected, num_expected);
+ }
+
+ bool VerifyIovecs(iovec* iovecs,
+ size_t num_iovecs,
+ const char** expected,
+ size_t num_expected) {
+ if (num_expected != num_iovecs) {
+ LOG(ERROR) << "Incorrect number of iovecs. Expected: "
+ << num_expected << " Actual: " << num_iovecs;
+ return false;
+ }
+ for (size_t i = 0; i < num_expected; ++i) {
+ if (!VerifyIovec(iovecs[i], expected[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool VerifyIovec(const iovec& iovec, StringPiece expected) {
+ if (iovec.iov_len != expected.length()) {
+ LOG(ERROR) << "Invalid length: " << iovec.iov_len
+ << " vs " << expected.length();
+ return false;
+ }
+ if (memcmp(iovec.iov_base, expected.data(), expected.length()) != 0) {
+ LOG(ERROR) << "Invalid data: " << static_cast<char*>(iovec.iov_base)
+ << " vs " << expected.data();
+ return false;
+ }
+ return true;
+ }
+
+ void OnFinFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data.Append(const_cast<char*>(data), strlen(data));
+ frame.fin = true;
+ sequencer_->OnStreamFrame(frame);
+ }
+
+ void OnFrame(QuicStreamOffset byte_offset, const char* data) {
+ QuicStreamFrame frame;
+ frame.stream_id = 1;
+ frame.offset = byte_offset;
+ frame.data.Append(const_cast<char*>(data), strlen(data));
+ frame.fin = false;
+ sequencer_->OnStreamFrame(frame);
+ }
+
+ MockConnection* connection_;
+ MockSession session_;
+ testing::StrictMock<MockStream> stream_;
+ scoped_ptr<QuicStreamSequencer> sequencer_;
+ map<QuicStreamOffset, string>* buffered_frames_;
+};
+
+TEST_F(QuicStreamSequencerTest, RejectOldFrame) {
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+ // Ignore this - it matches a past sequence number and we should not see it
+ // again.
+ OnFrame(0, "def");
+ EXPECT_EQ(0u, buffered_frames_->size());
+}
+
+TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) {
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ // Ignore this - it matches a buffered frame.
+ // Right now there's no checking that the payload is consistent.
+ OnFrame(0, "def");
+ EXPECT_EQ(1u, buffered_frames_->size());
+}
+
+TEST_F(QuicStreamSequencerTest, FullFrameConsumed) {
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ sequencer_->FlushBufferedFrames();
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ OnFinFrame(3, "def");
+}
+
+TEST_F(QuicStreamSequencerTest, BlockedThenFullFrameAndFinConsumed) {
+ sequencer_->SetBlockedUntilFlush();
+
+ OnFinFrame(0, "abc");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ sequencer_->FlushBufferedFrames();
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(3u, sequencer_->num_bytes_consumed());
+}
+
+TEST_F(QuicStreamSequencerTest, EmptyFrame) {
+ EXPECT_CALL(stream_,
+ CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _));
+ OnFrame(0, "");
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+}
+
+TEST_F(QuicStreamSequencerTest, EmptyFinFrame) {
+ EXPECT_CALL(stream_, OnFinRead());
+ OnFinFrame(0, "");
+ EXPECT_EQ(0u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+}
+
+TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) {
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(2));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(2u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ("c", buffered_frames_->find(2)->second);
+}
+
+TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) {
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
+
+ OnFrame(0, "abc");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ("abc", buffered_frames_->find(0)->second);
+}
+
+TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) {
+ OnFrame(3, "abc");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ("abc", buffered_frames_->find(3)->second);
+}
+
+TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) {
+ // Buffer the first
+ OnFrame(6, "ghi");
+ EXPECT_EQ(1u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(3u, sequencer_->num_bytes_buffered());
+ // Buffer the second
+ OnFrame(3, "def");
+ EXPECT_EQ(2u, buffered_frames_->size());
+ EXPECT_EQ(0u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(6u, sequencer_->num_bytes_buffered());
+
+ InSequence s;
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("ghi"), 3)).WillOnce(Return(3));
+
+ // Ack right away
+ OnFrame(0, "abc");
+ EXPECT_EQ(9u, sequencer_->num_bytes_consumed());
+ EXPECT_EQ(0u, sequencer_->num_bytes_buffered());
+
+ EXPECT_EQ(0u, buffered_frames_->size());
+}
+
+TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) {
+ InSequence s;
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+ OnFinFrame(0, "abc");
+
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+}
+
+TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) {
+ OnFinFrame(6, "");
+ EXPECT_EQ(6u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+ InSequence s;
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("def"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+
+ OnFrame(3, "def");
+ OnFrame(0, "abc");
+}
+
+TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) {
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+ InSequence s;
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(3));
+ EXPECT_CALL(stream_, OnFinRead());
+
+ OnFrame(0, "abc");
+}
+
+TEST_F(QuicStreamSequencerTest, TerminateWithReadv) {
+ char buffer[3];
+
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_FALSE(sequencer_->IsClosed());
+
+ EXPECT_CALL(stream_, ProcessRawData(StrEq("abc"), 3)).WillOnce(Return(0));
+ OnFrame(0, "abc");
+
+ iovec iov = {&buffer[0], 3};
+ int bytes_read = sequencer_->Readv(&iov, 1);
+ EXPECT_EQ(3, bytes_read);
+ EXPECT_TRUE(sequencer_->IsClosed());
+}
+
+TEST_F(QuicStreamSequencerTest, MutipleOffsets) {
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
+ OnFinFrame(5, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ EXPECT_CALL(stream_, Reset(QUIC_MULTIPLE_TERMINATION_OFFSETS));
+ OnFinFrame(1, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+
+ OnFinFrame(3, "");
+ EXPECT_EQ(3u, QuicStreamSequencerPeer::GetCloseOffset(sequencer_.get()));
+}
+
+class QuicSequencerRandomTest : public QuicStreamSequencerTest {
+ public:
+ typedef pair<int, string> Frame;
+ typedef vector<Frame> FrameList;
+
+ void CreateFrames() {
+ int payload_size = arraysize(kPayload) - 1;
+ int remaining_payload = payload_size;
+ while (remaining_payload != 0) {
+ int size = min(OneToN(6), remaining_payload);
+ int index = payload_size - remaining_payload;
+ list_.push_back(make_pair(index, string(kPayload + index, size)));
+ remaining_payload -= size;
+ }
+ }
+
+ QuicSequencerRandomTest() {
+ CreateFrames();
+ }
+
+ int OneToN(int n) {
+ return base::RandInt(1, n);
+ }
+
+ int MaybeProcessMaybeBuffer(const char* data, uint32 len) {
+ int to_process = len;
+ if (base::RandUint64() % 2 != 0) {
+ to_process = base::RandInt(0, len);
+ }
+ output_.append(data, to_process);
+ return to_process;
+ }
+
+ string output_;
+ FrameList list_;
+};
+
+// All frames are processed as soon as we have sequential data.
+// Infinite buffering, so all frames are acked right away.
+TEST_F(QuicSequencerRandomTest, RandomFramesNoDroppingNoBackup) {
+ InSequence s;
+ for (size_t i = 0; i < list_.size(); ++i) {
+ string* data = &list_[i].second;
+ EXPECT_CALL(stream_, ProcessRawData(StrEq(*data), data->size()))
+ .WillOnce(Return(data->size()));
+ }
+
+ while (!list_.empty()) {
+ int index = OneToN(list_.size()) - 1;
+ LOG(ERROR) << "Sending index " << index << " " << list_[index].second;
+ OnFrame(list_[index].first, list_[index].second.data());
+
+ list_.erase(list_.begin() + index);
+ }
+}
+
+TEST_F(QuicStreamSequencerTest, FrameOverlapsBufferedData) {
+ // Ensure that FrameOverlapsBufferedData returns appropriate responses when
+ // there is existing data buffered.
+
+ map<QuicStreamOffset, string>* buffered_frames =
+ QuicStreamSequencerPeer::GetBufferedFrames(sequencer_.get());
+
+ const int kBufferedOffset = 10;
+ const int kBufferedDataLength = 3;
+ const int kNewDataLength = 3;
+ IOVector data = MakeIOVector(string(kNewDataLength, '.'));
+
+ // No overlap if no buffered frames.
+ EXPECT_TRUE(buffered_frames_->empty());
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
+
+ // Add a buffered frame.
+ buffered_frames->insert(
+ make_pair(kBufferedOffset, string(kBufferedDataLength, '.')));
+
+ // New byte range partially overlaps with buffered frame, start offset
+ // preceeding buffered frame.
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - 1, data)));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength + 1, data)));
+
+ // New byte range partially overlaps with buffered frame, start offset
+ // inside existing buffered frame.
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset + 1, data)));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+ 1, false, kBufferedOffset + kBufferedDataLength - 1, data)));
+
+ // New byte range entirely outside of buffered frames, start offset preceeding
+ // buffered frame.
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(
+ QuicStreamFrame(1, false, kBufferedOffset - kNewDataLength, data)));
+
+ // New byte range entirely outside of buffered frames, start offset later than
+ // buffered frame.
+ EXPECT_FALSE(sequencer_->FrameOverlapsBufferedData(QuicStreamFrame(
+ 1, false, kBufferedOffset + kBufferedDataLength, data)));
+}
+
+TEST_F(QuicStreamSequencerTest, DontAcceptOverlappingFrames) {
+ // The peer should never send us non-identical stream frames which contain
+ // overlapping byte ranges - if they do, we close the connection.
+
+ QuicStreamFrame frame1(kClientDataStreamId1, false, 1, MakeIOVector("hello"));
+ sequencer_->OnStreamFrame(frame1);
+
+ QuicStreamFrame frame2(kClientDataStreamId1, false, 2, MakeIOVector("hello"));
+ EXPECT_TRUE(sequencer_->FrameOverlapsBufferedData(frame2));
+ EXPECT_CALL(stream_, CloseConnectionWithDetails(QUIC_INVALID_STREAM_FRAME, _))
+ .Times(1);
+ sequencer_->OnStreamFrame(frame2);
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_sustained_bandwidth_recorder.cc b/net/quic/quic_sustained_bandwidth_recorder.cc
new file mode 100644
index 0000000..41aa51e
--- /dev/null
+++ b/net/quic/quic_sustained_bandwidth_recorder.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sustained_bandwidth_recorder.h"
+
+#include "base/logging.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+QuicSustainedBandwidthRecorder::QuicSustainedBandwidthRecorder()
+ : has_estimate_(false),
+ is_recording_(false),
+ bandwidth_estimate_recorded_during_slow_start_(false),
+ bandwidth_estimate_(QuicBandwidth::Zero()),
+ max_bandwidth_estimate_(QuicBandwidth::Zero()),
+ max_bandwidth_timestamp_(0),
+ start_time_(QuicTime::Zero()) {}
+
+void QuicSustainedBandwidthRecorder::RecordEstimate(bool in_recovery,
+ bool in_slow_start,
+ QuicBandwidth bandwidth,
+ QuicTime estimate_time,
+ QuicWallTime wall_time,
+ QuicTime::Delta srtt) {
+ if (in_recovery) {
+ is_recording_ = false;
+ DVLOG(1) << "Stopped recording at: " << estimate_time.ToDebuggingValue();
+ return;
+ }
+
+ if (!is_recording_) {
+ // This is the first estimate of a new recording period.
+ start_time_ = estimate_time;
+ is_recording_ = true;
+ DVLOG(1) << "Started recording at: " << start_time_.ToDebuggingValue();
+ return;
+ }
+
+ // If we have been recording for at least 3 * srtt, then record the latest
+ // bandwidth estimate as a valid sustained bandwidth estimate.
+ if (estimate_time.Subtract(start_time_) >= srtt.Multiply(3)) {
+ has_estimate_ = true;
+ bandwidth_estimate_recorded_during_slow_start_ = in_slow_start;
+ bandwidth_estimate_ = bandwidth;
+ DVLOG(1) << "New sustained bandwidth estimate (KBytes/s): "
+ << bandwidth_estimate_.ToKBytesPerSecond();
+ }
+
+ // Check for an increase in max bandwidth.
+ if (bandwidth > max_bandwidth_estimate_) {
+ max_bandwidth_estimate_ = bandwidth;
+ max_bandwidth_timestamp_ = wall_time.ToUNIXSeconds();
+ DVLOG(1) << "New max bandwidth estimate (KBytes/s): "
+ << max_bandwidth_estimate_.ToKBytesPerSecond();
+ }
+}
+
+} // namespace net
diff --git a/net/quic/quic_sustained_bandwidth_recorder.h b/net/quic/quic_sustained_bandwidth_recorder.h
new file mode 100644
index 0000000..11685d2
--- /dev/null
+++ b/net/quic/quic_sustained_bandwidth_recorder.h
@@ -0,0 +1,92 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_
+#define NET_QUIC_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_
+
+#include "base/logging.h"
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_time.h"
+
+namespace net {
+
+namespace test {
+class QuicSustainedBandwidthRecorderPeer;
+} // namespace test
+
+// This class keeps track of a sustained bandwidth estimate to ultimately send
+// to the client in a server config update message. A sustained bandwidth
+// estimate is only marked as valid if the QuicSustainedBandwidthRecorder has
+// been given uninterrupted reliable estimates over a certain period of time.
+class NET_EXPORT_PRIVATE QuicSustainedBandwidthRecorder {
+ public:
+ QuicSustainedBandwidthRecorder();
+
+ // As long as |in_recovery| is consistently false, multiple calls to this
+ // method over a 3 * srtt period results in storage of a valid sustained
+ // bandwidth estimate.
+ // |time_now| is used as a max bandwidth timestamp if needed.
+ void RecordEstimate(bool in_recovery,
+ bool in_slow_start,
+ QuicBandwidth bandwidth,
+ QuicTime estimate_time,
+ QuicWallTime wall_time,
+ QuicTime::Delta srtt);
+
+ bool HasEstimate() const {
+ return has_estimate_;
+ }
+
+ QuicBandwidth BandwidthEstimate() const {
+ DCHECK(has_estimate_);
+ return bandwidth_estimate_;
+ }
+
+ QuicBandwidth MaxBandwidthEstimate() const {
+ DCHECK(has_estimate_);
+ return max_bandwidth_estimate_;
+ }
+
+ int64 MaxBandwidthTimestamp() const {
+ DCHECK(has_estimate_);
+ return max_bandwidth_timestamp_;
+ }
+
+ bool EstimateRecordedDuringSlowStart() const {
+ DCHECK(has_estimate_);
+ return bandwidth_estimate_recorded_during_slow_start_;
+ }
+
+ private:
+ friend class test::QuicSustainedBandwidthRecorderPeer;
+
+ // True if we have been able to calculate sustained bandwidth, over at least
+ // one recording period (3 * rtt).
+ bool has_estimate_;
+
+ // True if the last call to RecordEstimate had a reliable estimate.
+ bool is_recording_;
+
+ // True if the current sustained bandwidth estimate was generated while in
+ // slow start.
+ bool bandwidth_estimate_recorded_during_slow_start_;
+
+ // The latest sustained bandwidth estimate.
+ QuicBandwidth bandwidth_estimate_;
+
+ // The maximum sustained bandwidth seen over the lifetime of the connection.
+ QuicBandwidth max_bandwidth_estimate_;
+
+ // Timestamp indicating when the max_bandwidth_estimate_ was seen.
+ int64 max_bandwidth_timestamp_;
+
+ // Timestamp marking the beginning of the latest recording period.
+ QuicTime start_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicSustainedBandwidthRecorder);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_SUSTAINED_BANDWIDTH_RECORDER_H_
diff --git a/net/quic/quic_sustained_bandwidth_recorder_test.cc b/net/quic/quic_sustained_bandwidth_recorder_test.cc
new file mode 100644
index 0000000..6a5fbb2
--- /dev/null
+++ b/net/quic/quic_sustained_bandwidth_recorder_test.cc
@@ -0,0 +1,131 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_sustained_bandwidth_recorder.h"
+
+#include "net/quic/quic_bandwidth.h"
+#include "net/quic/quic_time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicSustainedBandwidthRecorderTest, BandwidthEstimates) {
+ QuicSustainedBandwidthRecorder recorder;
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ QuicTime estimate_time = QuicTime::Zero();
+ QuicWallTime wall_time = QuicWallTime::Zero();
+ QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150);
+ const int kBandwidthBitsPerSecond = 12345678;
+ QuicBandwidth bandwidth =
+ QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond);
+
+ bool in_recovery = false;
+ bool in_slow_start = false;
+
+ // This triggers recording, but should not yield a valid estimate yet.
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ // Send a second reading, again this should not result in a valid estimate,
+ // as not enough time has passed.
+ estimate_time = estimate_time.Add(srtt);
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate.
+ estimate_time = estimate_time.Add(srtt);
+ estimate_time = estimate_time.Add(srtt);
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_TRUE(recorder.HasEstimate());
+ EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth);
+ EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate());
+
+ // Resetting, and sending a different estimate will only change output after
+ // a further 3 * kSRTT has passed.
+ QuicBandwidth second_bandwidth =
+ QuicBandwidth::FromBitsPerSecond(2 * kBandwidthBitsPerSecond);
+ // Reset the recorder by passing in a measurement while in recovery.
+ in_recovery = true;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ in_recovery = false;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), bandwidth);
+
+ estimate_time = estimate_time.Add(srtt.Multiply(3));
+ const int64 kSeconds = 556677;
+ QuicWallTime second_bandwidth_wall_time =
+ QuicWallTime::FromUNIXSeconds(kSeconds);
+ recorder.RecordEstimate(in_recovery, in_slow_start, second_bandwidth,
+ estimate_time, second_bandwidth_wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), second_bandwidth);
+ EXPECT_EQ(recorder.BandwidthEstimate(), recorder.MaxBandwidthEstimate());
+ EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds);
+
+ // Reset again, this time recording a lower bandwidth than before.
+ QuicBandwidth third_bandwidth =
+ QuicBandwidth::FromBitsPerSecond(0.5 * kBandwidthBitsPerSecond);
+ // Reset the recorder by passing in an unreliable measurement.
+ recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
+ estimate_time, wall_time, srtt);
+ recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
+ estimate_time, wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth);
+
+ estimate_time = estimate_time.Add(srtt.Multiply(3));
+ recorder.RecordEstimate(in_recovery, in_slow_start, third_bandwidth,
+ estimate_time, wall_time, srtt);
+ EXPECT_EQ(recorder.BandwidthEstimate(), third_bandwidth);
+
+ // Max bandwidth should not have changed.
+ EXPECT_LT(third_bandwidth, second_bandwidth);
+ EXPECT_EQ(recorder.MaxBandwidthEstimate(), second_bandwidth);
+ EXPECT_EQ(recorder.MaxBandwidthTimestamp(), kSeconds);
+}
+
+TEST(QuicSustainedBandwidthRecorderTest, SlowStart) {
+ // Verify that slow start status is correctly recorded.
+ QuicSustainedBandwidthRecorder recorder;
+ EXPECT_FALSE(recorder.HasEstimate());
+
+ QuicTime estimate_time = QuicTime::Zero();
+ QuicWallTime wall_time = QuicWallTime::Zero();
+ QuicTime::Delta srtt = QuicTime::Delta::FromMilliseconds(150);
+ const int kBandwidthBitsPerSecond = 12345678;
+ QuicBandwidth bandwidth =
+ QuicBandwidth::FromBitsPerSecond(kBandwidthBitsPerSecond);
+
+ bool in_recovery = false;
+ bool in_slow_start = true;
+
+ // This triggers recording, but should not yield a valid estimate yet.
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+
+ // Now 3 * kSRTT has elapsed since first recording, expect a valid estimate.
+ estimate_time = estimate_time.Add(srtt.Multiply(3));
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_TRUE(recorder.HasEstimate());
+ EXPECT_TRUE(recorder.EstimateRecordedDuringSlowStart());
+
+ // Now send another estimate, this time not in slow start.
+ estimate_time = estimate_time.Add(srtt.Multiply(3));
+ in_slow_start = false;
+ recorder.RecordEstimate(in_recovery, in_slow_start, bandwidth, estimate_time,
+ wall_time, srtt);
+ EXPECT_TRUE(recorder.HasEstimate());
+ EXPECT_FALSE(recorder.EstimateRecordedDuringSlowStart());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_time.cc b/net/quic/quic_time.cc
new file mode 100644
index 0000000..e20a338
--- /dev/null
+++ b/net/quic/quic_time.cc
@@ -0,0 +1,187 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_time.h"
+
+#include "base/logging.h"
+
+namespace net {
+
+// Highest number of microseconds that DateTimeOffset can hold.
+const int64 kQuicInfiniteTimeUs = GG_INT64_C(0x7fffffffffffffff) / 10;
+
+QuicTime::Delta::Delta(base::TimeDelta delta)
+ : delta_(delta) {
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::Zero() {
+ return QuicTime::Delta::FromMicroseconds(0);
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::Infinite() {
+ return QuicTime::Delta::FromMicroseconds(kQuicInfiniteTimeUs);
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::FromSeconds(int64 seconds) {
+ return QuicTime::Delta(base::TimeDelta::FromSeconds(seconds));
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::FromMilliseconds(int64 ms) {
+ return QuicTime::Delta(base::TimeDelta::FromMilliseconds(ms));
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::FromMicroseconds(int64 us) {
+ return QuicTime::Delta(base::TimeDelta::FromMicroseconds(us));
+}
+
+int64 QuicTime::Delta::ToSeconds() const {
+ return delta_.InSeconds();
+}
+
+int64 QuicTime::Delta::ToMilliseconds() const {
+ return delta_.InMilliseconds();
+}
+
+int64 QuicTime::Delta::ToMicroseconds() const {
+ return delta_.InMicroseconds();
+}
+
+QuicTime::Delta QuicTime::Delta::Add(const Delta& delta) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() +
+ delta.ToMicroseconds());
+}
+
+QuicTime::Delta QuicTime::Delta::Subtract(const Delta& delta) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() -
+ delta.ToMicroseconds());
+}
+
+QuicTime::Delta QuicTime::Delta::Multiply(int i) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * i);
+}
+
+QuicTime::Delta QuicTime::Delta::Multiply(double d) const {
+ return QuicTime::Delta::FromMicroseconds(ToMicroseconds() * d);
+}
+
+// static
+QuicTime::Delta QuicTime::Delta::Max(QuicTime::Delta delta1,
+ QuicTime::Delta delta2) {
+ return delta1 < delta2 ? delta2 : delta1;
+}
+
+bool QuicTime::Delta::IsZero() const {
+ return delta_.InMicroseconds() == 0;
+}
+
+bool QuicTime::Delta::IsInfinite() const {
+ return delta_.InMicroseconds() == kQuicInfiniteTimeUs;
+}
+
+// static
+QuicTime QuicTime::Zero() {
+ return QuicTime(base::TimeTicks());
+}
+
+// static
+QuicTime QuicTime::Infinite() {
+ return QuicTime(base::TimeTicks::FromInternalValue(kQuicInfiniteTimeUs));
+}
+
+// static
+QuicTime QuicTime::Max(QuicTime time1, QuicTime time2) {
+ return time1 > time2 ? time1 : time2;
+}
+
+QuicTime::QuicTime(base::TimeTicks ticks)
+ : ticks_(ticks) {
+}
+
+int64 QuicTime::ToDebuggingValue() const {
+ return (ticks_ - base::TimeTicks()).InMicroseconds();
+}
+
+bool QuicTime::IsInitialized() const {
+ return ticks_ != base::TimeTicks();
+}
+
+QuicTime QuicTime::Add(const Delta& delta) const {
+ return QuicTime(ticks_ + delta.delta_);
+}
+
+QuicTime QuicTime::Subtract(const Delta& delta) const {
+ return QuicTime(ticks_ - delta.delta_);
+}
+
+QuicTime::Delta QuicTime::Subtract(const QuicTime& other) const {
+ return QuicTime::Delta(ticks_ - other.ticks_);
+}
+
+// static
+QuicWallTime QuicWallTime::FromUNIXSeconds(uint64 seconds) {
+ return QuicWallTime(seconds);
+}
+
+// static
+QuicWallTime QuicWallTime::Zero() {
+ return QuicWallTime(0);
+}
+
+uint64 QuicWallTime::ToUNIXSeconds() const {
+ return seconds_;
+}
+
+bool QuicWallTime::IsAfter(QuicWallTime other) const {
+ return seconds_ > other.seconds_;
+}
+
+bool QuicWallTime::IsBefore(QuicWallTime other) const {
+ return seconds_ < other.seconds_;
+}
+
+bool QuicWallTime::IsZero() const {
+ return seconds_ == 0;
+}
+
+QuicTime::Delta QuicWallTime::AbsoluteDifference(QuicWallTime other) const {
+ uint64 d;
+
+ if (seconds_ > other.seconds_) {
+ d = seconds_ - other.seconds_;
+ } else {
+ d = other.seconds_ - seconds_;
+ }
+
+ if (d > static_cast<uint64>(kint64max)) {
+ d = kint64max;
+ }
+ return QuicTime::Delta::FromSeconds(d);
+}
+
+QuicWallTime QuicWallTime::Add(QuicTime::Delta delta) const {
+ uint64 seconds = seconds_ + delta.ToSeconds();
+ if (seconds < seconds_) {
+ seconds = kuint64max;
+ }
+ return QuicWallTime(seconds);
+}
+
+QuicWallTime QuicWallTime::Subtract(QuicTime::Delta delta) const {
+ uint64 seconds = seconds_ - delta.ToSeconds();
+ if (seconds > seconds_) {
+ seconds = 0;
+ }
+ return QuicWallTime(seconds);
+}
+
+QuicWallTime::QuicWallTime(uint64 seconds)
+ : seconds_(seconds) {
+}
+
+} // namespace net
diff --git a/net/quic/quic_time.h b/net/quic/quic_time.h
new file mode 100644
index 0000000..5fbe30c
--- /dev/null
+++ b/net/quic/quic_time.h
@@ -0,0 +1,196 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// QuicTime represents one point in time, stored in microsecond resolution.
+// QuicTime is monotonically increasing, even across system clock adjustments.
+// The epoch (time 0) of QuicTime is unspecified.
+//
+// This implementation wraps the classes base::TimeTicks and base::TimeDelta.
+
+#ifndef NET_QUIC_QUIC_TIME_H_
+#define NET_QUIC_QUIC_TIME_H_
+
+#include "base/basictypes.h"
+#include "base/time/time.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+static const uint64 kNumMicrosPerSecond = base::Time::kMicrosecondsPerSecond;
+
+// A QuicTime is a purely relative time. QuicTime values from different clocks
+// cannot be compared to each other. If you need an absolute time, see
+// QuicWallTime, below.
+class NET_EXPORT_PRIVATE QuicTime {
+ public:
+ // A QuicTime::Delta represents the signed difference between two points in
+ // time, stored in microsecond resolution.
+ class NET_EXPORT_PRIVATE Delta {
+ public:
+ explicit Delta(base::TimeDelta delta);
+
+ // Create a object with an offset of 0.
+ static Delta Zero();
+
+ // Create a object with infinite offset time.
+ static Delta Infinite();
+
+ // Converts a number of seconds to a time offset.
+ static Delta FromSeconds(int64 secs);
+
+ // Converts a number of milliseconds to a time offset.
+ static Delta FromMilliseconds(int64 ms);
+
+ // Converts a number of microseconds to a time offset.
+ static Delta FromMicroseconds(int64 us);
+
+ // Converts the time offset to a rounded number of seconds.
+ int64 ToSeconds() const;
+
+ // Converts the time offset to a rounded number of milliseconds.
+ int64 ToMilliseconds() const;
+
+ // Converts the time offset to a rounded number of microseconds.
+ int64 ToMicroseconds() const;
+
+ Delta Add(const Delta& delta) const;
+
+ Delta Subtract(const Delta& delta) const;
+
+ Delta Multiply(int i) const;
+ Delta Multiply(double d) const;
+
+ // Returns the later delta of time1 and time2.
+ static Delta Max(Delta delta1, Delta delta2);
+
+ bool IsZero() const;
+
+ bool IsInfinite() const;
+
+ private:
+ base::TimeDelta delta_;
+
+ friend class QuicTime;
+ friend class QuicClock;
+ };
+
+ explicit QuicTime(base::TimeTicks ticks);
+
+ // Creates a new QuicTime with an internal value of 0. IsInitialized()
+ // will return false for these times.
+ static QuicTime Zero();
+
+ // Creates a new QuicTime with an infinite time.
+ static QuicTime Infinite();
+
+ // Returns the later time of time1 and time2.
+ static QuicTime Max(QuicTime time1, QuicTime time2);
+
+ // Produce the internal value to be used when logging. This value
+ // represents the number of microseconds since some epoch. It may
+ // be the UNIX epoch on some platforms. On others, it may
+ // be a CPU ticks based value.
+ int64 ToDebuggingValue() const;
+
+ bool IsInitialized() const;
+
+ QuicTime Add(const Delta& delta) const;
+
+ QuicTime Subtract(const Delta& delta) const;
+
+ Delta Subtract(const QuicTime& other) const;
+
+ private:
+ friend bool operator==(QuicTime lhs, QuicTime rhs);
+ friend bool operator<(QuicTime lhs, QuicTime rhs);
+
+ friend class QuicClock;
+ friend class QuicClockTest;
+
+ base::TimeTicks ticks_;
+};
+
+// A QuicWallTime represents an absolute time that is globally consistent. It
+// provides, at most, one second granularity and, in practice, clock-skew means
+// that you shouldn't even depend on that.
+class NET_EXPORT_PRIVATE QuicWallTime {
+ public:
+ // FromUNIXSeconds constructs a QuicWallTime from a count of the seconds
+ // since the UNIX epoch.
+ static QuicWallTime FromUNIXSeconds(uint64 seconds);
+
+ // Zero returns a QuicWallTime set to zero. IsZero will return true for this
+ // value.
+ static QuicWallTime Zero();
+
+ // ToUNIXSeconds converts a QuicWallTime into a count of seconds since the
+ // UNIX epoch.
+ uint64 ToUNIXSeconds() const;
+
+ bool IsAfter(QuicWallTime other) const;
+ bool IsBefore(QuicWallTime other) const;
+
+ // IsZero returns true if this object is the result of calling |Zero|.
+ bool IsZero() const;
+
+ // AbsoluteDifference returns the absolute value of the time difference
+ // between |this| and |other|.
+ QuicTime::Delta AbsoluteDifference(QuicWallTime other) const;
+
+ // Add returns a new QuicWallTime that represents the time of |this| plus
+ // |delta|.
+ QuicWallTime Add(QuicTime::Delta delta) const;
+
+ // Subtract returns a new QuicWallTime that represents the time of |this|
+ // minus |delta|.
+ QuicWallTime Subtract(QuicTime::Delta delta) const;
+
+ private:
+ explicit QuicWallTime(uint64 seconds);
+
+ uint64 seconds_;
+};
+
+// Non-member relational operators for QuicTime::Delta.
+inline bool operator==(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return lhs.ToMicroseconds() == rhs.ToMicroseconds();
+}
+inline bool operator!=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return !(lhs == rhs);
+}
+inline bool operator<(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return lhs.ToMicroseconds() < rhs.ToMicroseconds();
+}
+inline bool operator>(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return rhs < lhs;
+}
+inline bool operator<=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return !(rhs < lhs);
+}
+inline bool operator>=(QuicTime::Delta lhs, QuicTime::Delta rhs) {
+ return !(lhs < rhs);
+}
+// Non-member relational operators for QuicTime.
+inline bool operator==(QuicTime lhs, QuicTime rhs) {
+ return lhs.ticks_ == rhs.ticks_;
+}
+inline bool operator!=(QuicTime lhs, QuicTime rhs) {
+ return !(lhs == rhs);
+}
+inline bool operator<(QuicTime lhs, QuicTime rhs) {
+ return lhs.ticks_ < rhs.ticks_;
+}
+inline bool operator>(QuicTime lhs, QuicTime rhs) {
+ return rhs < lhs;
+}
+inline bool operator<=(QuicTime lhs, QuicTime rhs) {
+ return !(rhs < lhs);
+}
+inline bool operator>=(QuicTime lhs, QuicTime rhs) {
+ return !(lhs < rhs);
+}
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TIME_H_
diff --git a/net/quic/quic_time_test.cc b/net/quic/quic_time_test.cc
new file mode 100644
index 0000000..a2e3550
--- /dev/null
+++ b/net/quic/quic_time_test.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_time.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+TEST(QuicTimeDeltaTest, Zero) {
+ EXPECT_TRUE(QuicTime::Delta::Zero().IsZero());
+ EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite());
+ EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsZero());
+}
+
+TEST(QuicTimeDeltaTest, Infinite) {
+ EXPECT_TRUE(QuicTime::Delta::Infinite().IsInfinite());
+ EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite());
+ EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsInfinite());
+}
+
+TEST(QuicTimeDeltaTest, FromTo) {
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1),
+ QuicTime::Delta::FromMicroseconds(1000));
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1),
+ QuicTime::Delta::FromMilliseconds(1000));
+ EXPECT_EQ(QuicTime::Delta::FromSeconds(1),
+ QuicTime::Delta::FromMicroseconds(1000000));
+
+ EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds());
+ EXPECT_EQ(2, QuicTime::Delta::FromMilliseconds(2000).ToSeconds());
+ EXPECT_EQ(1000, QuicTime::Delta::FromMilliseconds(1).ToMicroseconds());
+ EXPECT_EQ(1, QuicTime::Delta::FromMicroseconds(1000).ToMilliseconds());
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(2000).ToMicroseconds(),
+ QuicTime::Delta::FromSeconds(2).ToMicroseconds());
+}
+
+TEST(QuicTimeDeltaTest, Add) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000),
+ QuicTime::Delta::Zero().Add(QuicTime::Delta::FromMilliseconds(2)));
+}
+
+TEST(QuicTimeDeltaTest, Subtract) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMilliseconds(2).Subtract(
+ QuicTime::Delta::FromMilliseconds(1)));
+}
+
+TEST(QuicTimeDeltaTest, Multiply) {
+ int i = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2).Multiply(i));
+ double d = 2;
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(4000),
+ QuicTime::Delta::FromMilliseconds(2).Multiply(d));
+}
+
+TEST(QuicTimeDeltaTest, Max) {
+ EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000),
+ QuicTime::Delta::Max(QuicTime::Delta::FromMicroseconds(1000),
+ QuicTime::Delta::FromMicroseconds(2000)));
+}
+
+TEST(QuicTimeDeltaTest, NotEqual) {
+ EXPECT_TRUE(QuicTime::Delta::FromSeconds(0) !=
+ QuicTime::Delta::FromSeconds(1));
+ EXPECT_FALSE(QuicTime::Delta::FromSeconds(0) !=
+ QuicTime::Delta::FromSeconds(0));
+}
+
+class QuicTimeTest : public ::testing::Test {
+ protected:
+ MockClock clock_;
+};
+
+TEST_F(QuicTimeTest, Initialized) {
+ EXPECT_FALSE(QuicTime::Zero().IsInitialized());
+ EXPECT_TRUE(QuicTime::Zero().Add(
+ QuicTime::Delta::FromMicroseconds(1)).IsInitialized());
+}
+
+TEST_F(QuicTimeTest, Add) {
+ QuicTime time_1 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(1));
+ QuicTime time_2 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(2));
+
+ QuicTime::Delta diff = time_2.Subtract(time_1);
+
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), diff);
+ EXPECT_EQ(1000, diff.ToMicroseconds());
+ EXPECT_EQ(1, diff.ToMilliseconds());
+}
+
+TEST_F(QuicTimeTest, Subtract) {
+ QuicTime time_1 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(1));
+ QuicTime time_2 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(2));
+
+ EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1), time_2.Subtract(time_1));
+}
+
+TEST_F(QuicTimeTest, SubtractDelta) {
+ QuicTime time = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(2));
+ EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1)),
+ time.Subtract(QuicTime::Delta::FromMilliseconds(1)));
+}
+
+TEST_F(QuicTimeTest, Max) {
+ QuicTime time_1 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(1));
+ QuicTime time_2 = QuicTime::Zero().Add(
+ QuicTime::Delta::FromMilliseconds(2));
+
+ EXPECT_EQ(time_2, QuicTime::Max(time_1, time_2));
+}
+
+TEST_F(QuicTimeTest, MockClock) {
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+
+ QuicTime now = clock_.ApproximateNow();
+ QuicTime time = QuicTime::Zero().Add(QuicTime::Delta::FromMicroseconds(1000));
+
+ EXPECT_EQ(now, time);
+
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
+ now = clock_.ApproximateNow();
+
+ EXPECT_NE(now, time);
+
+ time = time.Add(QuicTime::Delta::FromMilliseconds(1));
+ EXPECT_EQ(now, time);
+}
+
+TEST_F(QuicTimeTest, LE) {
+ const QuicTime zero = QuicTime::Zero();
+ const QuicTime one = zero.Add(QuicTime::Delta::FromSeconds(1));
+ EXPECT_TRUE(zero <= zero);
+ EXPECT_TRUE(zero <= one);
+ EXPECT_TRUE(one <= one);
+ EXPECT_FALSE(one <= zero);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_time_wait_list_manager.cc b/net/quic/quic_time_wait_list_manager.cc
new file mode 100644
index 0000000..d1f3419
--- /dev/null
+++ b/net/quic/quic_time_wait_list_manager.cc
@@ -0,0 +1,283 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_time_wait_list_manager.h"
+
+#include <errno.h>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_session.h"
+#include "net/quic/quic_utils.h"
+
+using base::StringPiece;
+using std::make_pair;
+
+namespace net {
+
+namespace {
+
+// Time period for which the connection_id should live in time wait state..
+const int kTimeWaitSeconds = 5;
+
+} // namespace
+
+// A very simple alarm that just informs the QuicTimeWaitListManager to clean
+// up old connection_ids. This alarm should be unregistered and deleted before
+// the QuicTimeWaitListManager is deleted.
+class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
+ public:
+ explicit ConnectionIdCleanUpAlarm(
+ QuicTimeWaitListManager* time_wait_list_manager)
+ : time_wait_list_manager_(time_wait_list_manager) {}
+
+ virtual QuicTime OnAlarm() OVERRIDE {
+ time_wait_list_manager_->CleanUpOldConnectionIds();
+ // Let the time wait manager register the alarm at appropriate time.
+ return QuicTime::Zero();
+ }
+
+ private:
+ // Not owned.
+ QuicTimeWaitListManager* time_wait_list_manager_;
+};
+
+// This class stores pending public reset packets to be sent to clients.
+// server_address - server address on which a packet what was received for
+// a connection_id in time wait state.
+// client_address - address of the client that sent that packet. Needed to send
+// the public reset packet back to the client.
+// packet - the pending public reset packet that is to be sent to the client.
+// created instance takes the ownership of this packet.
+class QuicTimeWaitListManager::QueuedPacket {
+ public:
+ QueuedPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicEncryptedPacket* packet)
+ : server_address_(server_address),
+ client_address_(client_address),
+ packet_(packet) {}
+
+ const IPEndPoint& server_address() const { return server_address_; }
+ const IPEndPoint& client_address() const { return client_address_; }
+ QuicEncryptedPacket* packet() { return packet_.get(); }
+
+ private:
+ const IPEndPoint server_address_;
+ const IPEndPoint client_address_;
+ scoped_ptr<QuicEncryptedPacket> packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
+};
+
+QuicTimeWaitListManager::QuicTimeWaitListManager(
+ QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
+ QuicConnectionHelperInterface* helper,
+ const QuicVersionVector& supported_versions)
+ : helper_(helper),
+ kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
+ connection_id_clean_up_alarm_(
+ helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
+ writer_(writer),
+ visitor_(visitor) {
+ SetConnectionIdCleanUpAlarm();
+}
+
+QuicTimeWaitListManager::~QuicTimeWaitListManager() {
+ connection_id_clean_up_alarm_->Cancel();
+ STLDeleteElements(&pending_packets_queue_);
+ for (ConnectionIdMap::iterator it = connection_id_map_.begin();
+ it != connection_id_map_.end();
+ ++it) {
+ delete it->second.close_packet;
+ }
+}
+
+void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
+ QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet) {
+ DVLOG(1) << "Adding " << connection_id << " to the time wait list.";
+ int num_packets = 0;
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ if (it != connection_id_map_.end()) { // Replace record if it is reinserted.
+ num_packets = it->second.num_packets;
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ ConnectionIdData data(num_packets,
+ version,
+ helper_->GetClock()->ApproximateNow(),
+ close_packet);
+ connection_id_map_.insert(make_pair(connection_id, data));
+}
+
+bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
+ QuicConnectionId connection_id) const {
+ return ContainsKey(connection_id_map_, connection_id);
+}
+
+QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
+ QuicConnectionId connection_id) {
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ return (it->second).version;
+}
+
+void QuicTimeWaitListManager::OnCanWrite() {
+ while (!pending_packets_queue_.empty()) {
+ QueuedPacket* queued_packet = pending_packets_queue_.front();
+ if (!WriteToWire(queued_packet)) {
+ return;
+ }
+ pending_packets_queue_.pop_front();
+ delete queued_packet;
+ }
+}
+
+void QuicTimeWaitListManager::ProcessPacket(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& /*packet*/) {
+ DCHECK(IsConnectionIdInTimeWait(connection_id));
+ DVLOG(1) << "Processing " << connection_id << " in time wait state.";
+ // TODO(satyamshekhar): Think about handling packets from different client
+ // addresses.
+ ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
+ DCHECK(it != connection_id_map_.end());
+ // Increment the received packet count.
+ ++((it->second).num_packets);
+ if (!ShouldSendResponse((it->second).num_packets)) {
+ return;
+ }
+ if (it->second.close_packet) {
+ QueuedPacket* queued_packet =
+ new QueuedPacket(server_address,
+ client_address,
+ it->second.close_packet->Clone());
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+ } else {
+ SendPublicReset(server_address,
+ client_address,
+ connection_id,
+ sequence_number);
+ }
+}
+
+// Returns true if the number of packets received for this connection_id is a
+// power of 2 to throttle the number of public reset packets we send to a
+// client.
+bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
+ return (received_packet_count & (received_packet_count - 1)) == 0;
+}
+
+void QuicTimeWaitListManager::SendPublicReset(
+ const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber rejected_sequence_number) {
+ QuicPublicResetPacket packet;
+ packet.public_header.connection_id = connection_id;
+ packet.public_header.reset_flag = true;
+ packet.public_header.version_flag = false;
+ packet.rejected_sequence_number = rejected_sequence_number;
+ // TODO(satyamshekhar): generate a valid nonce for this connection_id.
+ packet.nonce_proof = 1010101;
+ packet.client_address = client_address;
+ QueuedPacket* queued_packet = new QueuedPacket(
+ server_address,
+ client_address,
+ BuildPublicReset(packet));
+ // Takes ownership of the packet.
+ SendOrQueuePacket(queued_packet);
+}
+
+QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
+ const QuicPublicResetPacket& packet) {
+ return QuicFramer::BuildPublicResetPacket(packet);
+}
+
+// Either sends the packet and deletes it or makes pending queue the
+// owner of the packet.
+void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
+ if (WriteToWire(packet)) {
+ delete packet;
+ } else {
+ // pending_packets_queue takes the ownership of the queued packet.
+ pending_packets_queue_.push_back(packet);
+ }
+}
+
+bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
+ if (writer_->IsWriteBlocked()) {
+ visitor_->OnWriteBlocked(this);
+ return false;
+ }
+ WriteResult result = writer_->WritePacket(
+ queued_packet->packet()->data(),
+ queued_packet->packet()->length(),
+ queued_packet->server_address().address(),
+ queued_packet->client_address());
+ if (result.status == WRITE_STATUS_BLOCKED) {
+ // If blocked and unbuffered, return false to retry sending.
+ DCHECK(writer_->IsWriteBlocked());
+ visitor_->OnWriteBlocked(this);
+ return writer_->IsWriteBlockedDataBuffered();
+ } else if (result.status == WRITE_STATUS_ERROR) {
+ LOG(WARNING) << "Received unknown error while sending reset packet to "
+ << queued_packet->client_address().ToString() << ": "
+ << strerror(result.error_code);
+ }
+ return true;
+}
+
+void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
+ connection_id_clean_up_alarm_->Cancel();
+ QuicTime now = helper_->GetClock()->ApproximateNow();
+ QuicTime next_alarm_time = now;
+ if (!connection_id_map_.empty()) {
+ QuicTime oldest_connection_id =
+ connection_id_map_.begin()->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
+ } else {
+ LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
+ }
+ } else {
+ // No connection_ids added so none will expire before kTimeWaitPeriod_.
+ next_alarm_time = now.Add(kTimeWaitPeriod_);
+ }
+
+ connection_id_clean_up_alarm_->Set(next_alarm_time);
+}
+
+void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
+ QuicTime now = helper_->GetClock()->ApproximateNow();
+ while (!connection_id_map_.empty()) {
+ ConnectionIdMap::iterator it = connection_id_map_.begin();
+ QuicTime oldest_connection_id = it->second.time_added;
+ if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
+ break;
+ }
+ // This connection_id has lived its age, retire it now.
+ delete it->second.close_packet;
+ connection_id_map_.erase(it);
+ }
+ SetConnectionIdCleanUpAlarm();
+}
+
+} // namespace net
diff --git a/net/quic/quic_time_wait_list_manager.h b/net/quic/quic_time_wait_list_manager.h
new file mode 100644
index 0000000..4d7b0c5
--- /dev/null
+++ b/net/quic/quic_time_wait_list_manager.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Handles packets for connection_ids in time wait state by discarding the
+// packet and sending the clients a public reset packet with exponential
+// backoff.
+
+#ifndef NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+#define NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
+
+#include <deque>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/strings/string_piece.h"
+#include "net/base/linked_hash_map.h"
+#include "net/quic/quic_blocked_writer_interface.h"
+#include "net/quic/quic_connection_helper.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class ConnectionIdCleanUpAlarm;
+class QuicServerSessionVisitor;
+
+namespace test {
+class QuicTimeWaitListManagerPeer;
+} // namespace test
+
+// Maintains a list of all connection_ids that have been recently closed. A
+// connection_id lives in this state for kTimeWaitPeriod. All packets received
+// for connection_ids in this state are handed over to the
+// QuicTimeWaitListManager by the QuicDispatcher. Decides whether to send a
+// public reset packet, a copy of the previously sent connection close packet,
+// or nothing to the client which sent a packet with the connection_id in time
+// wait state. After the connection_id expires its time wait period, a new
+// connection/session will be created if a packet is received for this
+// connection_id.
+class QuicTimeWaitListManager : public QuicBlockedWriterInterface {
+ public:
+ // writer - the entity that writes to the socket. (Owned by the dispatcher)
+ // visitor - the entity that manages blocked writers. (The dispatcher)
+ // helper - used to run clean up alarms. (Owned by the owner of the server)
+ QuicTimeWaitListManager(QuicPacketWriter* writer,
+ QuicServerSessionVisitor* visitor,
+ QuicConnectionHelperInterface* helper,
+ const QuicVersionVector& supported_versions);
+ virtual ~QuicTimeWaitListManager();
+
+ // Adds the given connection_id to time wait state for kTimeWaitPeriod.
+ // Henceforth, any packet bearing this connection_id should not be processed
+ // while the connection_id remains in this list. If a non-nullptr
+ // |close_packet| is provided, it is sent again when packets are received for
+ // added connection_ids. If nullptr, a public reset packet is sent with the
+ // specified |version|. DCHECKs that connection_id is not already on the list.
+ void AddConnectionIdToTimeWait(QuicConnectionId connection_id,
+ QuicVersion version,
+ QuicEncryptedPacket* close_packet); // Owned.
+
+ // Returns true if the connection_id is in time wait state, false otherwise.
+ // Packets received for this connection_id should not lead to creation of new
+ // QuicSessions.
+ bool IsConnectionIdInTimeWait(QuicConnectionId connection_id) const;
+
+ // Called when a packet is received for a connection_id that is in time wait
+ // state. Sends a public reset packet to the client which sent this
+ // connection_id. Sending of the public reset packet is throttled by using
+ // exponential back off. DCHECKs for the connection_id to be in time wait
+ // state. virtual to override in tests.
+ virtual void ProcessPacket(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber sequence_number,
+ const QuicEncryptedPacket& packet);
+
+ // Called by the dispatcher when the underlying socket becomes writable again,
+ // since we might need to send pending public reset packets which we didn't
+ // send because the underlying socket was write blocked.
+ virtual void OnCanWrite() OVERRIDE;
+
+ // Used to delete connection_id entries that have outlived their time wait
+ // period.
+ void CleanUpOldConnectionIds();
+
+ // Given a ConnectionId that exists in the time wait list, returns the
+ // QuicVersion associated with it.
+ QuicVersion GetQuicVersionFromConnectionId(QuicConnectionId connection_id);
+
+ protected:
+ virtual QuicEncryptedPacket* BuildPublicReset(
+ const QuicPublicResetPacket& packet);
+
+ private:
+ friend class test::QuicTimeWaitListManagerPeer;
+
+ // Internal structure to store pending public reset packets.
+ class QueuedPacket;
+
+ // Decides if a packet should be sent for this connection_id based on the
+ // number of received packets.
+ bool ShouldSendResponse(int received_packet_count);
+
+ // Creates a public reset packet and sends it or queues it to be sent later.
+ void SendPublicReset(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ QuicConnectionId connection_id,
+ QuicPacketSequenceNumber rejected_sequence_number);
+
+ // Either sends the packet and deletes it or makes pending_packets_queue_ the
+ // owner of the packet.
+ void SendOrQueuePacket(QueuedPacket* packet);
+
+ // Sends the packet out. Returns true if the packet was successfully consumed.
+ // If the writer got blocked and did not buffer the packet, we'll need to keep
+ // the packet and retry sending. In case of all other errors we drop the
+ // packet.
+ bool WriteToWire(QueuedPacket* packet);
+
+ // Register the alarm to wake up at appropriate time.
+ void SetConnectionIdCleanUpAlarm();
+
+ // A map from a recently closed connection_id to the number of packets
+ // received after the termination of the connection bound to the
+ // connection_id.
+ struct ConnectionIdData {
+ ConnectionIdData(int num_packets_,
+ QuicVersion version_,
+ QuicTime time_added_,
+ QuicEncryptedPacket* close_packet)
+ : num_packets(num_packets_),
+ version(version_),
+ time_added(time_added_),
+ close_packet(close_packet) {}
+ int num_packets;
+ QuicVersion version;
+ QuicTime time_added;
+ QuicEncryptedPacket* close_packet;
+ };
+
+ // linked_hash_map allows lookup by ConnectionId and traversal in add order.
+ typedef linked_hash_map<QuicConnectionId, ConnectionIdData> ConnectionIdMap;
+ ConnectionIdMap connection_id_map_;
+
+ // Pending public reset packets that need to be sent out to the client
+ // when we are given a chance to write by the dispatcher.
+ std::deque<QueuedPacket*> pending_packets_queue_;
+
+ // Used to schedule alarms to delete old connection_ids which have been in the
+ // list for too long.
+ QuicConnectionHelperInterface* helper_;
+
+ // Time period for which connection_ids should remain in time wait state.
+ const QuicTime::Delta kTimeWaitPeriod_;
+
+ // Alarm registered with the connection helper to clean up connection_ids that
+ // have
+ // out lived their duration in time wait state.
+ scoped_ptr<QuicAlarm> connection_id_clean_up_alarm_;
+
+ // Interface that writes given buffer to the socket.
+ QuicPacketWriter* writer_;
+
+ // Interface that manages blocked writers.
+ QuicServerSessionVisitor* visitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTimeWaitListManager);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TIME_WAIT_LIST_MANAGER_H_
diff --git a/net/quic/quic_types.cc b/net/quic/quic_types.cc
new file mode 100644
index 0000000..cdfb36d
--- /dev/null
+++ b/net/quic/quic_types.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_types.h"
+
+using std::ostream;
+
+namespace net {
+
+QuicConsumedData::QuicConsumedData(size_t bytes_consumed,
+ bool fin_consumed)
+ : bytes_consumed(bytes_consumed),
+ fin_consumed(fin_consumed) {
+}
+
+ostream& operator<<(ostream& os, const QuicConsumedData& s) {
+ os << "bytes_consumed: " << s.bytes_consumed
+ << " fin_consumed: " << s.fin_consumed;
+ return os;
+}
+
+WriteResult::WriteResult()
+ : status(WRITE_STATUS_ERROR),
+ bytes_written(0) {
+}
+
+WriteResult::WriteResult(WriteStatus status,
+ int bytes_written_or_error_code)
+ : status(status),
+ bytes_written(bytes_written_or_error_code) {
+}
+
+} // namespace net
diff --git a/net/quic/quic_types.h b/net/quic/quic_types.h
new file mode 100644
index 0000000..01415bc
--- /dev/null
+++ b/net/quic/quic_types.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_TYPES_H_
+#define NET_QUIC_QUIC_TYPES_H_
+
+// This header defines some basic types that don't depend on quic_protocol.h,
+// so that classes not directly related to the protocol wire format can avoid
+// including quic_protocol.h.
+
+#include <stddef.h>
+#include <ostream>
+
+#include "net/base/net_export.h"
+
+namespace net {
+
+// A struct for functions which consume data payloads and fins.
+struct NET_EXPORT_PRIVATE QuicConsumedData {
+ QuicConsumedData(size_t bytes_consumed, bool fin_consumed);
+
+ // By default, gtest prints the raw bytes of an object. The bool data
+ // member causes this object to have padding bytes, which causes the
+ // default gtest object printer to read uninitialize memory. So we need
+ // to teach gtest how to print this object.
+ NET_EXPORT_PRIVATE friend std::ostream& operator<<(
+ std::ostream& os, const QuicConsumedData& s);
+
+ // How many bytes were consumed.
+ size_t bytes_consumed;
+
+ // True if an incoming fin was consumed.
+ bool fin_consumed;
+};
+
+// QuicAsyncStatus enumerates the possible results of an asynchronous
+// operation.
+enum QuicAsyncStatus {
+ QUIC_SUCCESS = 0,
+ QUIC_FAILURE = 1,
+ // QUIC_PENDING results from an operation that will occur asynchonously. When
+ // the operation is complete, a callback's |Run| method will be called.
+ QUIC_PENDING = 2,
+};
+
+// TODO(wtc): see if WriteStatus can be replaced by QuicAsyncStatus.
+enum WriteStatus {
+ WRITE_STATUS_OK,
+ WRITE_STATUS_BLOCKED,
+ WRITE_STATUS_ERROR,
+};
+
+// A struct used to return the result of write calls including either the number
+// of bytes written or the error code, depending upon the status.
+struct NET_EXPORT_PRIVATE WriteResult {
+ WriteResult(WriteStatus status, int bytes_written_or_error_code);
+ WriteResult();
+
+ WriteStatus status;
+ union {
+ int bytes_written; // only valid when status is WRITE_STATUS_OK
+ int error_code; // only valid when status is WRITE_STATUS_ERROR
+ };
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_TYPES_H_
diff --git a/net/quic/quic_unacked_packet_map.cc b/net/quic/quic_unacked_packet_map.cc
new file mode 100644
index 0000000..ab46dfb
--- /dev/null
+++ b/net/quic/quic_unacked_packet_map.cc
@@ -0,0 +1,391 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_unacked_packet_map.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_utils_chromium.h"
+
+using std::max;
+
+namespace net {
+
+QuicUnackedPacketMap::QuicUnackedPacketMap()
+ : largest_sent_packet_(0),
+ largest_observed_(0),
+ least_unacked_(1),
+ bytes_in_flight_(0),
+ pending_crypto_packet_count_(0) {
+}
+
+QuicUnackedPacketMap::~QuicUnackedPacketMap() {
+ QuicPacketSequenceNumber index = least_unacked_;
+ for (UnackedPacketMap::iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++index) {
+ delete it->retransmittable_frames;
+ // Only delete all_transmissions once, for the newest packet.
+ if (it->all_transmissions != nullptr &&
+ index == *it->all_transmissions->rbegin()) {
+ delete it->all_transmissions;
+ }
+ }
+}
+
+// TODO(ianswett): Combine this method with OnPacketSent once packets are always
+// sent in order and the connection tracks RetransmittableFrames for longer.
+void QuicUnackedPacketMap::AddPacket(
+ const SerializedPacket& serialized_packet) {
+ DCHECK_GE(serialized_packet.sequence_number,
+ least_unacked_ + unacked_packets_.size());
+ while (least_unacked_ + unacked_packets_.size() <
+ serialized_packet.sequence_number) {
+ unacked_packets_.push_back(TransmissionInfo());
+ unacked_packets_.back().is_unackable = true;
+ }
+ unacked_packets_.push_back(
+ TransmissionInfo(serialized_packet.retransmittable_frames,
+ serialized_packet.sequence_number_length));
+ if (serialized_packet.retransmittable_frames != nullptr &&
+ serialized_packet.retransmittable_frames->HasCryptoHandshake() ==
+ IS_HANDSHAKE) {
+ ++pending_crypto_packet_count_;
+ }
+}
+
+void QuicUnackedPacketMap::RemoveObsoletePackets() {
+ while (!unacked_packets_.empty()) {
+ if (!IsPacketRemovable(least_unacked_, unacked_packets_.front())) {
+ break;
+ }
+ unacked_packets_.pop_front();
+ ++least_unacked_;
+ }
+}
+
+void QuicUnackedPacketMap::OnRetransmittedPacket(
+ QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ TransmissionType transmission_type) {
+ DCHECK_GE(old_sequence_number, least_unacked_);
+ DCHECK_LT(old_sequence_number, least_unacked_ + unacked_packets_.size());
+ DCHECK_GE(new_sequence_number, least_unacked_ + unacked_packets_.size());
+ while (least_unacked_ + unacked_packets_.size() < new_sequence_number) {
+ unacked_packets_.push_back(TransmissionInfo());
+ unacked_packets_.back().is_unackable = true;
+ }
+
+ // TODO(ianswett): Discard and lose the packet lazily instead of immediately.
+ TransmissionInfo* transmission_info =
+ &unacked_packets_.at(old_sequence_number - least_unacked_);
+ RetransmittableFrames* frames = transmission_info->retransmittable_frames;
+ LOG_IF(DFATAL, frames == nullptr)
+ << "Attempt to retransmit packet with no "
+ << "retransmittable frames: " << old_sequence_number;
+
+ // We keep the old packet in the unacked packet list until it, or one of
+ // the retransmissions of it are acked.
+ transmission_info->retransmittable_frames = nullptr;
+ // Only keep one transmission older than largest observed, because only the
+ // most recent is expected to possibly be a spurious retransmission.
+ while (transmission_info->all_transmissions != nullptr &&
+ transmission_info->all_transmissions->size() > 1 &&
+ *(++transmission_info->all_transmissions->begin()) <
+ largest_observed_) {
+ QuicPacketSequenceNumber old_transmission =
+ *transmission_info->all_transmissions->begin();
+ TransmissionInfo* old_info =
+ &unacked_packets_[old_transmission - least_unacked_];
+ // Don't remove old packets if they're still in flight.
+ if (old_info->in_flight) {
+ break;
+ }
+ old_info->all_transmissions->pop_front();
+ // This will cause the packet be removed in RemoveObsoletePackets.
+ old_info->all_transmissions = nullptr;
+ }
+ // Don't link old transmissions to new ones when version or
+ // encryption changes.
+ if (transmission_type == ALL_INITIAL_RETRANSMISSION ||
+ transmission_type == ALL_UNACKED_RETRANSMISSION) {
+ RemoveAckability(transmission_info);
+ } else {
+ if (transmission_info->all_transmissions == nullptr) {
+ transmission_info->all_transmissions = new SequenceNumberList();
+ transmission_info->all_transmissions->push_back(old_sequence_number);
+ }
+ transmission_info->all_transmissions->push_back(new_sequence_number);
+ }
+ unacked_packets_.push_back(
+ TransmissionInfo(frames,
+ transmission_info->sequence_number_length,
+ transmission_type,
+ transmission_info->all_transmissions));
+ RemoveObsoletePackets();
+}
+
+void QuicUnackedPacketMap::ClearAllPreviousRetransmissions() {
+ while (!unacked_packets_.empty() && least_unacked_ < largest_observed_) {
+ // If this packet is in flight, or has retransmittable data, then there is
+ // no point in clearing out any further packets, because they would not
+ // affect the high water mark.
+ TransmissionInfo* info = &unacked_packets_.front();
+ if (info->in_flight || info->retransmittable_frames != nullptr) {
+ break;
+ }
+
+ if (info->all_transmissions != nullptr) {
+ if (info->all_transmissions->size() < 2) {
+ LOG(DFATAL) << "all_transmissions must be nullptr or have multiple "
+ << "elements. size:" << info->all_transmissions->size();
+ delete info->all_transmissions;
+ } else {
+ info->all_transmissions->pop_front();
+ if (info->all_transmissions->size() == 1) {
+ // Set the newer transmission's 'all_transmissions' entry to nullptr.
+ QuicPacketSequenceNumber new_transmission =
+ info->all_transmissions->front();
+ TransmissionInfo* new_info =
+ &unacked_packets_.at(new_transmission - least_unacked_);
+ delete new_info->all_transmissions;
+ new_info->all_transmissions = nullptr;
+ }
+ }
+ }
+ unacked_packets_.pop_front();
+ ++least_unacked_;
+ }
+}
+
+bool QuicUnackedPacketMap::HasRetransmittableFrames(
+ QuicPacketSequenceNumber sequence_number) const {
+ DCHECK_GE(sequence_number, least_unacked_);
+ DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
+ return unacked_packets_[sequence_number - least_unacked_]
+ .retransmittable_frames != nullptr;
+}
+
+void QuicUnackedPacketMap::NackPacket(QuicPacketSequenceNumber sequence_number,
+ size_t min_nacks) {
+ DCHECK_GE(sequence_number, least_unacked_);
+ DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
+ unacked_packets_[sequence_number - least_unacked_].nack_count =
+ max(min_nacks,
+ unacked_packets_[sequence_number - least_unacked_].nack_count);
+}
+
+void QuicUnackedPacketMap::RemoveRetransmittability(
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK_GE(sequence_number, least_unacked_);
+ DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
+ TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
+ SequenceNumberList* all_transmissions = info->all_transmissions;
+ if (all_transmissions == nullptr) {
+ MaybeRemoveRetransmittableFrames(info);
+ return;
+ }
+ // TODO(ianswett): Consider adding a check to ensure there are retransmittable
+ // frames associated with this packet.
+ for (SequenceNumberList::const_iterator it = all_transmissions->begin();
+ it != all_transmissions->end(); ++it) {
+ TransmissionInfo* transmission_info =
+ &unacked_packets_[*it - least_unacked_];
+ MaybeRemoveRetransmittableFrames(transmission_info);
+ transmission_info->all_transmissions = nullptr;
+ }
+ delete all_transmissions;
+}
+
+void QuicUnackedPacketMap::RemoveAckability(TransmissionInfo* info) {
+ DCHECK(info->retransmittable_frames == nullptr);
+ info->is_unackable = true;
+ SequenceNumberList* all_transmissions = info->all_transmissions;
+ if (all_transmissions == nullptr) {
+ return;
+ }
+ for (SequenceNumberList::const_iterator it = all_transmissions->begin();
+ it != all_transmissions->end(); ++it) {
+ TransmissionInfo* transmission_info =
+ &unacked_packets_[*it - least_unacked_];
+ transmission_info->all_transmissions = nullptr;
+ transmission_info->is_unackable = true;
+ }
+ delete all_transmissions;
+}
+
+void QuicUnackedPacketMap::MaybeRemoveRetransmittableFrames(
+ TransmissionInfo* transmission_info) {
+ if (transmission_info->retransmittable_frames != nullptr) {
+ if (transmission_info->retransmittable_frames->HasCryptoHandshake()
+ == IS_HANDSHAKE) {
+ --pending_crypto_packet_count_;
+ }
+ delete transmission_info->retransmittable_frames;
+ transmission_info->retransmittable_frames = nullptr;
+ }
+}
+
+void QuicUnackedPacketMap::IncreaseLargestObserved(
+ QuicPacketSequenceNumber largest_observed) {
+ DCHECK_LE(largest_observed_, largest_observed);
+ largest_observed_ = largest_observed;
+}
+
+bool QuicUnackedPacketMap::IsPacketUseless(
+ QuicPacketSequenceNumber sequence_number,
+ const TransmissionInfo& info) const {
+ return (info.is_unackable || sequence_number <= largest_observed_) &&
+ !info.in_flight && info.retransmittable_frames == nullptr &&
+ info.all_transmissions == nullptr;
+}
+
+bool QuicUnackedPacketMap::IsPacketRemovable(
+ QuicPacketSequenceNumber sequence_number,
+ const TransmissionInfo& info) const {
+ return (info.is_unackable || sequence_number <= largest_observed_ ||
+ unacked_packets_.size() > kMaxTcpCongestionWindow) &&
+ !info.in_flight && info.retransmittable_frames == nullptr &&
+ info.all_transmissions == nullptr;
+}
+
+bool QuicUnackedPacketMap::IsUnacked(
+ QuicPacketSequenceNumber sequence_number) const {
+ if (sequence_number < least_unacked_ ||
+ sequence_number >= least_unacked_ + unacked_packets_.size()) {
+ return false;
+ }
+ return !IsPacketUseless(sequence_number,
+ unacked_packets_[sequence_number - least_unacked_]);
+}
+
+void QuicUnackedPacketMap::RemoveFromInFlight(
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK_GE(sequence_number, least_unacked_);
+ DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
+ TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
+ if (info->in_flight) {
+ LOG_IF(DFATAL, bytes_in_flight_ < info->bytes_sent);
+ bytes_in_flight_ -= info->bytes_sent;
+ info->in_flight = false;
+ }
+}
+
+bool QuicUnackedPacketMap::HasUnackedPackets() const {
+ return !unacked_packets_.empty();
+}
+
+bool QuicUnackedPacketMap::HasInFlightPackets() const {
+ return bytes_in_flight_ > 0;
+}
+
+const TransmissionInfo& QuicUnackedPacketMap::GetTransmissionInfo(
+ QuicPacketSequenceNumber sequence_number) const {
+ return unacked_packets_[sequence_number - least_unacked_];
+}
+
+QuicTime QuicUnackedPacketMap::GetLastPacketSentTime() const {
+ UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
+ while (it != unacked_packets_.rend()) {
+ if (it->in_flight) {
+ LOG_IF(DFATAL, it->sent_time == QuicTime::Zero())
+ << "Sent time can never be zero for a packet in flight.";
+ return it->sent_time;
+ }
+ ++it;
+ }
+ LOG(DFATAL) << "GetLastPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+}
+
+QuicTime QuicUnackedPacketMap::GetFirstInFlightPacketSentTime() const {
+ UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ while (it != unacked_packets_.end() && !it->in_flight) {
+ ++it;
+ }
+ if (it == unacked_packets_.end()) {
+ LOG(DFATAL) << "GetFirstInFlightPacketSentTime requires in flight packets.";
+ return QuicTime::Zero();
+ }
+ return it->sent_time;
+}
+
+size_t QuicUnackedPacketMap::GetNumUnackedPacketsDebugOnly() const {
+ size_t unacked_packet_count = 0;
+ QuicPacketSequenceNumber sequence_number = least_unacked_;
+ for (UnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it, ++sequence_number) {
+ if (!IsPacketUseless(sequence_number, *it)) {
+ ++unacked_packet_count;
+ }
+ }
+ return unacked_packet_count;
+}
+
+bool QuicUnackedPacketMap::HasMultipleInFlightPackets() const {
+ size_t num_in_flight = 0;
+ for (UnackedPacketMap::const_reverse_iterator it = unacked_packets_.rbegin();
+ it != unacked_packets_.rend(); ++it) {
+ if (it->in_flight) {
+ ++num_in_flight;
+ }
+ if (num_in_flight > 1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QuicUnackedPacketMap::HasPendingCryptoPackets() const {
+ return pending_crypto_packet_count_ > 0;
+}
+
+bool QuicUnackedPacketMap::HasUnackedRetransmittableFrames() const {
+ for (UnackedPacketMap::const_reverse_iterator it =
+ unacked_packets_.rbegin(); it != unacked_packets_.rend(); ++it) {
+ if (it->in_flight && it->retransmittable_frames) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QuicPacketSequenceNumber
+QuicUnackedPacketMap::GetLeastUnacked() const {
+ return least_unacked_;
+}
+
+void QuicUnackedPacketMap::SetSent(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes_sent,
+ bool set_in_flight) {
+ DCHECK_GE(sequence_number, least_unacked_);
+ DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
+ TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
+ DCHECK(!info->in_flight);
+
+ DCHECK_LT(largest_sent_packet_, sequence_number);
+ largest_sent_packet_ = max(sequence_number, largest_sent_packet_);
+ info->sent_time = sent_time;
+ if (set_in_flight) {
+ bytes_in_flight_ += bytes_sent;
+ info->bytes_sent = bytes_sent;
+ info->in_flight = true;
+ }
+}
+
+void QuicUnackedPacketMap::RestoreInFlight(
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK_GE(sequence_number, least_unacked_);
+ DCHECK_LT(sequence_number, least_unacked_ + unacked_packets_.size());
+ TransmissionInfo* info = &unacked_packets_[sequence_number - least_unacked_];
+ DCHECK(!info->in_flight);
+ DCHECK_NE(0u, info->bytes_sent);
+ DCHECK(info->sent_time.IsInitialized());
+
+ bytes_in_flight_ += info->bytes_sent;
+ info->in_flight = true;
+}
+
+} // namespace net
diff --git a/net/quic/quic_unacked_packet_map.h b/net/quic/quic_unacked_packet_map.h
new file mode 100644
index 0000000..9806ffc
--- /dev/null
+++ b/net/quic/quic_unacked_packet_map.h
@@ -0,0 +1,174 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
+#define NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
+
+#include <deque>
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+// Class which tracks unacked packets for three purposes:
+// 1) Track retransmittable data, including multiple transmissions of frames.
+// 2) Track packets and bytes in flight for congestion control.
+// 3) Track sent time of packets to provide RTT measurements from acks.
+class NET_EXPORT_PRIVATE QuicUnackedPacketMap {
+ public:
+ QuicUnackedPacketMap();
+ ~QuicUnackedPacketMap();
+
+ // Adds |serialized_packet| to the map. Does not mark it in flight.
+ void AddPacket(const SerializedPacket& serialized_packet);
+
+ // Called when a packet is retransmitted with a new sequence number.
+ // |old_sequence_number| will remain unacked, but will have no
+ // retransmittable data associated with it. |new_sequence_number| will
+ // be both unacked and associated with retransmittable data.
+ void OnRetransmittedPacket(QuicPacketSequenceNumber old_sequence_number,
+ QuicPacketSequenceNumber new_sequence_number,
+ TransmissionType transmission_type);
+
+ // Returns true if the packet |sequence_number| is unacked.
+ bool IsUnacked(QuicPacketSequenceNumber sequence_number) const;
+
+ // Sets the nack count to the max of the current nack count and |min_nacks|.
+ void NackPacket(QuicPacketSequenceNumber sequence_number,
+ size_t min_nacks);
+
+ // Marks |sequence_number| as no longer in flight.
+ void RemoveFromInFlight(QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if the unacked packet |sequence_number| has retransmittable
+ // frames. This will return false if the packet has been acked, if a
+ // previous transmission of this packet was ACK'd, or if this packet has been
+ // retransmitted as with different sequence number, or if the packet never
+ // had any retransmittable packets in the first place.
+ bool HasRetransmittableFrames(QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns true if there are any unacked packets.
+ bool HasUnackedPackets() const;
+
+ // Returns true if there are any unacked packets which have retransmittable
+ // frames.
+ bool HasUnackedRetransmittableFrames() const;
+
+ // Returns the largest sequence number that has been sent.
+ QuicPacketSequenceNumber largest_sent_packet() const {
+ return largest_sent_packet_;
+ }
+
+ // Returns the largest sequence number that has been acked.
+ QuicPacketSequenceNumber largest_observed() const {
+ return largest_observed_;
+ }
+
+ // Returns the sum of bytes from all packets in flight.
+ QuicByteCount bytes_in_flight() const {
+ return bytes_in_flight_;
+ }
+
+ // Returns the smallest sequence number of a serialized packet which has not
+ // been acked by the peer. If there are no unacked packets, returns 0.
+ QuicPacketSequenceNumber GetLeastUnacked() const;
+
+ // Sets a packet as sent with the sent time |sent_time|. Marks the packet
+ // as in flight if |set_in_flight| is true.
+ // Packets marked as in flight are expected to be marked as missing when they
+ // don't arrive, indicating the need for retransmission.
+ void SetSent(QuicPacketSequenceNumber sequence_number,
+ QuicTime sent_time,
+ QuicByteCount bytes_sent,
+ bool set_in_flight);
+
+ // Restores the in flight status for a packet that was previously sent.
+ void RestoreInFlight(QuicPacketSequenceNumber sequence_number);
+
+ // Clears all previous transmissions in order to make room in the ack frame
+ // for newly acked packets.
+ void ClearAllPreviousRetransmissions();
+
+ typedef std::deque<TransmissionInfo> UnackedPacketMap;
+
+ typedef UnackedPacketMap::const_iterator const_iterator;
+
+ const_iterator begin() const { return unacked_packets_.begin(); }
+ const_iterator end() const { return unacked_packets_.end(); }
+
+ // Returns true if there are unacked packets that are in flight.
+ bool HasInFlightPackets() const;
+
+ // Returns the TransmissionInfo associated with |sequence_number|, which
+ // must be unacked.
+ const TransmissionInfo& GetTransmissionInfo(
+ QuicPacketSequenceNumber sequence_number) const;
+
+ // Returns the time that the last unacked packet was sent.
+ QuicTime GetLastPacketSentTime() const;
+
+ // Returns the time that the first in flight packet was sent.
+ QuicTime GetFirstInFlightPacketSentTime() const;
+
+ // Returns the number of unacked packets.
+ size_t GetNumUnackedPacketsDebugOnly() const;
+
+ // Returns true if there are multiple packets in flight.
+ bool HasMultipleInFlightPackets() const;
+
+ // Returns true if there are any pending crypto packets.
+ bool HasPendingCryptoPackets() const;
+
+ // Removes any retransmittable frames from this transmission or an associated
+ // transmission. It removes now useless transmissions, and disconnects any
+ // other packets from other transmissions.
+ void RemoveRetransmittability(QuicPacketSequenceNumber sequence_number);
+
+ // Removes any other retransmissions and marks all transmissions unackable.
+ void RemoveAckability(TransmissionInfo* info);
+
+ // Increases the largest observed. Any packets less or equal to
+ // |largest_acked_packet| are discarded if they are only for the RTT purposes.
+ void IncreaseLargestObserved(QuicPacketSequenceNumber largest_observed);
+
+ // Remove any packets no longer needed for retransmission, congestion, or
+ // RTT measurement purposes.
+ void RemoveObsoletePackets();
+
+ private:
+ void MaybeRemoveRetransmittableFrames(TransmissionInfo* transmission_info);
+
+ // Returns true if the packet no longer has a purpose in the map.
+ bool IsPacketUseless(QuicPacketSequenceNumber sequence_number,
+ const TransmissionInfo& info) const;
+ // Returns true if the packet is useless or it's only purpose is RTT
+ // measurement, and it's old enough that is unlikely to ever happen.
+ bool IsPacketRemovable(QuicPacketSequenceNumber sequence_number,
+ const TransmissionInfo& info) const;
+
+ QuicPacketSequenceNumber largest_sent_packet_;
+ QuicPacketSequenceNumber largest_observed_;
+
+ // Newly serialized retransmittable and fec packets are added to this map,
+ // which contains owning pointers to any contained frames. If a packet is
+ // retransmitted, this map will contain entries for both the old and the new
+ // packet. The old packet's retransmittable frames entry will be nullptr,
+ // while the new packet's entry will contain the frames to retransmit.
+ // If the old packet is acked before the new packet, then the old entry will
+ // be removed from the map and the new entry's retransmittable frames will be
+ // set to nullptr.
+ UnackedPacketMap unacked_packets_;
+ // The packet at the 0th index of unacked_packets_.
+ QuicPacketSequenceNumber least_unacked_;
+
+ size_t bytes_in_flight_;
+ // Number of retransmittable crypto handshake packets.
+ size_t pending_crypto_packet_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicUnackedPacketMap);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UNACKED_PACKET_MAP_H_
diff --git a/net/quic/quic_unacked_packet_map_test.cc b/net/quic/quic_unacked_packet_map_test.cc
new file mode 100644
index 0000000..4f5d6a5
--- /dev/null
+++ b/net/quic/quic_unacked_packet_map_test.cc
@@ -0,0 +1,371 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_unacked_packet_map.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::min;
+
+namespace net {
+namespace test {
+namespace {
+
+// Default packet length.
+const uint32 kDefaultAckLength = 50;
+const uint32 kDefaultLength = 1000;
+
+class QuicUnackedPacketMapTest : public ::testing::Test {
+ protected:
+ QuicUnackedPacketMapTest()
+ : now_(QuicTime::Zero().Add(QuicTime::Delta::FromMilliseconds(1000))) {
+ }
+
+ SerializedPacket CreateRetransmittablePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return SerializedPacket(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ nullptr, 0, new RetransmittableFrames());
+ }
+
+ SerializedPacket CreateNonRetransmittablePacket(
+ QuicPacketSequenceNumber sequence_number) {
+ return SerializedPacket(sequence_number, PACKET_1BYTE_SEQUENCE_NUMBER,
+ nullptr, 0, nullptr);
+ }
+
+ void VerifyInFlightPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ unacked_packets_.RemoveObsoletePackets();
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ return;
+ }
+ if (num_packets == 1) {
+ EXPECT_TRUE(unacked_packets_.HasInFlightPackets());
+ EXPECT_FALSE(unacked_packets_.HasMultipleInFlightPackets());
+ ASSERT_TRUE(unacked_packets_.IsUnacked(packets[0]));
+ EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[0]).in_flight);
+ }
+ for (size_t i = 0; i < num_packets; ++i) {
+ ASSERT_TRUE(unacked_packets_.IsUnacked(packets[i]));
+ EXPECT_TRUE(unacked_packets_.GetTransmissionInfo(packets[i]).in_flight);
+ }
+ size_t in_flight_count = 0;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ if (it->in_flight) {
+ ++in_flight_count;
+ }
+ }
+ EXPECT_EQ(num_packets, in_flight_count);
+ }
+
+ void VerifyUnackedPackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ unacked_packets_.RemoveObsoletePackets();
+ if (num_packets == 0) {
+ EXPECT_FALSE(unacked_packets_.HasUnackedPackets());
+ EXPECT_FALSE(unacked_packets_.HasUnackedRetransmittableFrames());
+ return;
+ }
+ EXPECT_TRUE(unacked_packets_.HasUnackedPackets());
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.IsUnacked(packets[i])) << packets[i];
+ }
+ EXPECT_EQ(num_packets, unacked_packets_.GetNumUnackedPacketsDebugOnly());
+ }
+
+ void VerifyRetransmittablePackets(QuicPacketSequenceNumber* packets,
+ size_t num_packets) {
+ unacked_packets_.RemoveObsoletePackets();
+ size_t num_retransmittable_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it = unacked_packets_.begin();
+ it != unacked_packets_.end(); ++it) {
+ if (it->retransmittable_frames != nullptr) {
+ ++num_retransmittable_packets;
+ }
+ }
+ EXPECT_EQ(num_packets, num_retransmittable_packets);
+ for (size_t i = 0; i < num_packets; ++i) {
+ EXPECT_TRUE(unacked_packets_.HasRetransmittableFrames(packets[i]))
+ << " packets[" << i << "]:" << packets[i];
+ }
+ }
+
+ QuicUnackedPacketMap unacked_packets_;
+ QuicTime now_;
+};
+
+TEST_F(QuicUnackedPacketMapTest, RttOnly) {
+ // Acks are only tracked for RTT measurement purposes.
+ unacked_packets_.AddPacket(CreateNonRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultAckLength, false);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.IncreaseLargestObserved(1);
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, DiscardOldRttOnly) {
+ // Acks are only tracked for RTT measurement purposes, and are discarded
+ // when more than 200 accumulate.
+ const size_t kNumUnackedPackets = 200;
+ for (size_t i = 1; i < 400; ++i) {
+ unacked_packets_.AddPacket(CreateNonRetransmittablePacket(i));
+ unacked_packets_.SetSent(i, now_, kDefaultAckLength, false);
+ unacked_packets_.RemoveObsoletePackets();
+ EXPECT_EQ(min(i, kNumUnackedPackets),
+ unacked_packets_.GetNumUnackedPacketsDebugOnly());
+ }
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmittableInflightAndRtt) {
+ // Simulate a retransmittable packet being sent and acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(unacked, arraysize(unacked));
+
+ unacked_packets_.RemoveRetransmittability(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.IncreaseLargestObserved(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmittedPacket) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.OnRetransmittedPacket(1, 2, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(2, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 2 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ unacked_packets_.RemoveRetransmittability(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.IncreaseLargestObserved(2);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.RemoveFromInFlight(2);
+ QuicPacketSequenceNumber unacked2[] = { 1 };
+ VerifyUnackedPackets(unacked, arraysize(unacked2));
+ VerifyInFlightPackets(unacked, arraysize(unacked2));
+ VerifyRetransmittablePackets(nullptr, 0);
+
+ unacked_packets_.RemoveFromInFlight(1);
+ VerifyUnackedPackets(nullptr, 0);
+ VerifyInFlightPackets(nullptr, 0);
+ VerifyRetransmittablePackets(nullptr, 0);
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmitThreeTimes) {
+ // Simulate a retransmittable packet being sent and retransmitted twice.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(2));
+ unacked_packets_.SetSent(2, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 1, 2 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ // Early retransmit 1 as 3 and send new data as 4.
+ unacked_packets_.IncreaseLargestObserved(2);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.RemoveRetransmittability(2);
+ unacked_packets_.RemoveFromInFlight(1);
+ unacked_packets_.OnRetransmittedPacket(1, 3, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(3, now_, kDefaultLength, true);
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(4));
+ unacked_packets_.SetSent(4, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked2[] = { 1, 3, 4 };
+ VerifyUnackedPackets(unacked2, arraysize(unacked2));
+ QuicPacketSequenceNumber pending2[] = { 3, 4, };
+ VerifyInFlightPackets(pending2, arraysize(pending2));
+ QuicPacketSequenceNumber retransmittable2[] = { 3, 4 };
+ VerifyRetransmittablePackets(retransmittable2, arraysize(retransmittable2));
+
+ // Early retransmit 3 (formerly 1) as 5, and remove 1 from unacked.
+ unacked_packets_.IncreaseLargestObserved(4);
+ unacked_packets_.RemoveFromInFlight(4);
+ unacked_packets_.RemoveRetransmittability(4);
+ unacked_packets_.OnRetransmittedPacket(3, 5, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(5, now_, kDefaultLength, true);
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(6));
+ unacked_packets_.SetSent(6, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked3[] = { 3, 5, 6 };
+ VerifyUnackedPackets(unacked3, arraysize(unacked3));
+ QuicPacketSequenceNumber pending3[] = { 3, 5, 6 };
+ VerifyInFlightPackets(pending3, arraysize(pending3));
+ QuicPacketSequenceNumber retransmittable3[] = { 5, 6 };
+ VerifyRetransmittablePackets(retransmittable3, arraysize(retransmittable3));
+
+ // Early retransmit 5 as 7 and ensure in flight packet 3 is not removed.
+ unacked_packets_.IncreaseLargestObserved(6);
+ unacked_packets_.RemoveFromInFlight(6);
+ unacked_packets_.RemoveRetransmittability(6);
+ unacked_packets_.OnRetransmittedPacket(5, 7, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(7, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked4[] = { 3, 5, 7 };
+ VerifyUnackedPackets(unacked4, arraysize(unacked4));
+ QuicPacketSequenceNumber pending4[] = { 3, 5, 7 };
+ VerifyInFlightPackets(pending4, arraysize(pending4));
+ QuicPacketSequenceNumber retransmittable4[] = { 7 };
+ VerifyRetransmittablePackets(retransmittable4, arraysize(retransmittable4));
+
+ // Remove the older two transmissions from in flight.
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.RemoveFromInFlight(5);
+ QuicPacketSequenceNumber pending5[] = { 7 };
+ VerifyInFlightPackets(pending5, arraysize(pending5));
+
+ // Now test ClearAllPreviousTransmissions, leaving one packet.
+ unacked_packets_.ClearAllPreviousRetransmissions();
+ QuicPacketSequenceNumber unacked5[] = { 7 };
+ VerifyUnackedPackets(unacked5, arraysize(unacked5));
+ QuicPacketSequenceNumber retransmittable5[] = { 7 };
+ VerifyRetransmittablePackets(retransmittable5, arraysize(retransmittable5));
+}
+
+TEST_F(QuicUnackedPacketMapTest, RetransmitFourTimes) {
+ // Simulate a retransmittable packet being sent and retransmitted twice.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(2));
+ unacked_packets_.SetSent(2, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 1, 2 };
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+
+ // Early retransmit 1 as 3.
+ unacked_packets_.IncreaseLargestObserved(2);
+ unacked_packets_.RemoveFromInFlight(2);
+ unacked_packets_.RemoveRetransmittability(2);
+ unacked_packets_.RemoveFromInFlight(1);
+ unacked_packets_.OnRetransmittedPacket(1, 3, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(3, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked2[] = { 1, 3 };
+ VerifyUnackedPackets(unacked2, arraysize(unacked2));
+ QuicPacketSequenceNumber pending2[] = { 3 };
+ VerifyInFlightPackets(pending2, arraysize(pending2));
+ QuicPacketSequenceNumber retransmittable2[] = { 3 };
+ VerifyRetransmittablePackets(retransmittable2, arraysize(retransmittable2));
+
+ // TLP 3 (formerly 1) as 4, and don't remove 1 from unacked.
+ unacked_packets_.OnRetransmittedPacket(3, 4, TLP_RETRANSMISSION);
+ unacked_packets_.SetSent(4, now_, kDefaultLength, true);
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(5));
+ unacked_packets_.SetSent(5, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked3[] = { 1, 3, 4, 5 };
+ VerifyUnackedPackets(unacked3, arraysize(unacked3));
+ QuicPacketSequenceNumber pending3[] = { 3, 4, 5 };
+ VerifyInFlightPackets(pending3, arraysize(pending3));
+ QuicPacketSequenceNumber retransmittable3[] = { 4, 5 };
+ VerifyRetransmittablePackets(retransmittable3, arraysize(retransmittable3));
+
+ // Early retransmit 4 as 6 and ensure in flight packet 3 is removed.
+ unacked_packets_.IncreaseLargestObserved(5);
+ unacked_packets_.RemoveFromInFlight(5);
+ unacked_packets_.RemoveRetransmittability(5);
+ unacked_packets_.RemoveFromInFlight(3);
+ unacked_packets_.RemoveFromInFlight(4);
+ unacked_packets_.OnRetransmittedPacket(4, 6, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(6, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked4[] = { 4, 6 };
+ VerifyUnackedPackets(unacked4, arraysize(unacked4));
+ QuicPacketSequenceNumber pending4[] = { 6 };
+ VerifyInFlightPackets(pending4, arraysize(pending4));
+ QuicPacketSequenceNumber retransmittable4[] = { 6 };
+ VerifyRetransmittablePackets(retransmittable4, arraysize(retransmittable4));
+}
+
+TEST_F(QuicUnackedPacketMapTest, RestoreInflight) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.OnRetransmittedPacket(1, 2, RTO_RETRANSMISSION);
+ unacked_packets_.RemoveFromInFlight(1);
+ unacked_packets_.SetSent(2, now_, kDefaultLength, true);
+
+ QuicPacketSequenceNumber unacked[] = { 1, 2 };
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ QuicPacketSequenceNumber retransmittable[] = { 2 };
+ VerifyInFlightPackets(retransmittable, arraysize(retransmittable));
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+ EXPECT_EQ(kDefaultLength, unacked_packets_.bytes_in_flight());
+
+ // Simulate an F-RTO, and restore 1 to flight.
+ unacked_packets_.RestoreInFlight(1);
+ VerifyUnackedPackets(unacked, arraysize(unacked));
+ VerifyInFlightPackets(unacked, arraysize(unacked));
+ VerifyRetransmittablePackets(retransmittable, arraysize(retransmittable));
+ EXPECT_EQ(2 * kDefaultLength, unacked_packets_.bytes_in_flight());
+}
+
+TEST_F(QuicUnackedPacketMapTest, SendWithGap) {
+ // Simulate a retransmittable packet being sent, retransmitted, and the first
+ // transmission being acked.
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(1));
+ unacked_packets_.SetSent(1, now_, kDefaultLength, true);
+ unacked_packets_.AddPacket(CreateRetransmittablePacket(3));
+ unacked_packets_.SetSent(3, now_, kDefaultLength, true);
+ unacked_packets_.OnRetransmittedPacket(1, 5, LOSS_RETRANSMISSION);
+ unacked_packets_.SetSent(5, now_, kDefaultLength, true);
+
+ EXPECT_EQ(1u, unacked_packets_.GetLeastUnacked());
+ EXPECT_TRUE(unacked_packets_.IsUnacked(1));
+ EXPECT_FALSE(unacked_packets_.IsUnacked(2));
+ EXPECT_TRUE(unacked_packets_.IsUnacked(3));
+ EXPECT_FALSE(unacked_packets_.IsUnacked(4));
+ EXPECT_TRUE(unacked_packets_.IsUnacked(5));
+ EXPECT_EQ(5u, unacked_packets_.largest_sent_packet());
+}
+
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc
new file mode 100644
index 0000000..f8cf47a
--- /dev/null
+++ b/net/quic/quic_utils.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_utils.h"
+
+#include <ctype.h>
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/port.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/quic_write_blocked_list.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+// static
+uint64 QuicUtils::FNV1a_64_Hash(const char* data, int len) {
+ static const uint64 kOffset = GG_UINT64_C(14695981039346656037);
+ static const uint64 kPrime = GG_UINT64_C(1099511628211);
+
+ const uint8* octets = reinterpret_cast<const uint8*>(data);
+
+ uint64 hash = kOffset;
+
+ for (int i = 0; i < len; ++i) {
+ hash = hash ^ octets[i];
+ hash = hash * kPrime;
+ }
+
+ return hash;
+}
+
+// static
+uint128 QuicUtils::FNV1a_128_Hash(const char* data, int len) {
+ // The following two constants are defined as part of the hash algorithm.
+ // see http://www.isthe.com/chongo/tech/comp/fnv/
+ // 309485009821345068724781371
+ const uint128 kPrime(16777216, 315);
+ // 144066263297769815596495629667062367629
+ const uint128 kOffset(GG_UINT64_C(7809847782465536322),
+ GG_UINT64_C(7113472399480571277));
+
+ const uint8* octets = reinterpret_cast<const uint8*>(data);
+
+ uint128 hash = kOffset;
+
+ for (int i = 0; i < len; ++i) {
+ hash = hash ^ uint128(0, octets[i]);
+ hash = hash * kPrime;
+ }
+
+ return hash;
+}
+
+// static
+bool QuicUtils::FindMutualTag(const QuicTagVector& our_tags_vector,
+ const QuicTag* their_tags,
+ size_t num_their_tags,
+ Priority priority,
+ QuicTag* out_result,
+ size_t* out_index) {
+ if (our_tags_vector.empty()) {
+ return false;
+ }
+ const size_t num_our_tags = our_tags_vector.size();
+ const QuicTag* our_tags = &our_tags_vector[0];
+
+ size_t num_priority_tags, num_inferior_tags;
+ const QuicTag* priority_tags;
+ const QuicTag* inferior_tags;
+ if (priority == LOCAL_PRIORITY) {
+ num_priority_tags = num_our_tags;
+ priority_tags = our_tags;
+ num_inferior_tags = num_their_tags;
+ inferior_tags = their_tags;
+ } else {
+ num_priority_tags = num_their_tags;
+ priority_tags = their_tags;
+ num_inferior_tags = num_our_tags;
+ inferior_tags = our_tags;
+ }
+
+ for (size_t i = 0; i < num_priority_tags; i++) {
+ for (size_t j = 0; j < num_inferior_tags; j++) {
+ if (priority_tags[i] == inferior_tags[j]) {
+ *out_result = priority_tags[i];
+ if (out_index) {
+ if (priority == LOCAL_PRIORITY) {
+ *out_index = j;
+ } else {
+ *out_index = i;
+ }
+ }
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// static
+void QuicUtils::SerializeUint128(uint128 v, uint8* out) {
+ const uint64 lo = Uint128Low64(v);
+ const uint64 hi = Uint128High64(v);
+ // This assumes that the system is little-endian.
+ memcpy(out, &lo, sizeof(lo));
+ memcpy(out + sizeof(lo), &hi, sizeof(hi));
+}
+
+// static
+void QuicUtils::SerializeUint128Short(uint128 v, uint8* out) {
+ const uint64 lo = Uint128Low64(v);
+ const uint64 hi = Uint128High64(v);
+ // This assumes that the system is little-endian.
+ memcpy(out, &lo, sizeof(lo));
+ memcpy(out + sizeof(lo), &hi, sizeof(hi) / 2);
+}
+
+#define RETURN_STRING_LITERAL(x) \
+case x: \
+return #x;
+
+// static
+const char* QuicUtils::StreamErrorToString(QuicRstStreamErrorCode error) {
+ switch (error) {
+ RETURN_STRING_LITERAL(QUIC_STREAM_NO_ERROR);
+ RETURN_STRING_LITERAL(QUIC_STREAM_CONNECTION_ERROR);
+ RETURN_STRING_LITERAL(QUIC_ERROR_PROCESSING_STREAM);
+ RETURN_STRING_LITERAL(QUIC_MULTIPLE_TERMINATION_OFFSETS);
+ RETURN_STRING_LITERAL(QUIC_BAD_APPLICATION_PAYLOAD);
+ RETURN_STRING_LITERAL(QUIC_STREAM_PEER_GOING_AWAY);
+ RETURN_STRING_LITERAL(QUIC_STREAM_CANCELLED);
+ RETURN_STRING_LITERAL(QUIC_RST_FLOW_CONTROL_ACCOUNTING);
+ RETURN_STRING_LITERAL(QUIC_STREAM_LAST_ERROR);
+ }
+ // Return a default value so that we return this when |error| doesn't match
+ // any of the QuicRstStreamErrorCodes. This can happen when the RstStream
+ // frame sent by the peer (attacker) has invalid error code.
+ return "INVALID_RST_STREAM_ERROR_CODE";
+}
+
+// static
+const char* QuicUtils::ErrorToString(QuicErrorCode error) {
+ switch (error) {
+ RETURN_STRING_LITERAL(QUIC_NO_ERROR);
+ RETURN_STRING_LITERAL(QUIC_INTERNAL_ERROR);
+ RETURN_STRING_LITERAL(QUIC_STREAM_DATA_AFTER_TERMINATION);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PACKET_HEADER);
+ RETURN_STRING_LITERAL(QUIC_INVALID_FRAME_DATA);
+ RETURN_STRING_LITERAL(QUIC_MISSING_PAYLOAD);
+ RETURN_STRING_LITERAL(QUIC_INVALID_FEC_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_UNENCRYPTED_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_RST_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CONNECTION_CLOSE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_GOAWAY_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_WINDOW_UPDATE_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_BLOCKED_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STOP_WAITING_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_ACK_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CONGESTION_FEEDBACK_DATA);
+ RETURN_STRING_LITERAL(QUIC_INVALID_VERSION_NEGOTIATION_PACKET);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PUBLIC_RST_PACKET);
+ RETURN_STRING_LITERAL(QUIC_DECRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_ENCRYPTION_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_PACKET_TOO_LARGE);
+ RETURN_STRING_LITERAL(QUIC_PACKET_FOR_NONEXISTENT_STREAM);
+ RETURN_STRING_LITERAL(QUIC_PEER_GOING_AWAY);
+ RETURN_STRING_LITERAL(QUIC_HANDSHAKE_FAILED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_TAGS_OUT_OF_ORDER);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_ENTRIES);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_TOO_MANY_REJECTS);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_INVALID_VALUE_LENGTH)
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_INTERNAL_ERROR);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_VERSION_NOT_SUPPORTED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_NO_SUPPORT);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_TYPE);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID);
+ RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS);
+ RETURN_STRING_LITERAL(QUIC_TOO_MANY_UNFINISHED_STREAMS);
+ RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET);
+ RETURN_STRING_LITERAL(QUIC_INVALID_VERSION);
+ RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID);
+ RETURN_STRING_LITERAL(QUIC_INVALID_NEGOTIATED_VALUE);
+ RETURN_STRING_LITERAL(QUIC_DECOMPRESSION_FAILURE);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_TIMED_OUT);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_OVERALL_TIMED_OUT);
+ RETURN_STRING_LITERAL(QUIC_ERROR_MIGRATING_ADDRESS);
+ RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR);
+ RETURN_STRING_LITERAL(QUIC_PACKET_READ_ERROR);
+ RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_FRAME);
+ RETURN_STRING_LITERAL(QUIC_INVALID_HEADERS_STREAM_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
+ RETURN_STRING_LITERAL(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+ RETURN_STRING_LITERAL(QUIC_CONNECTION_IP_POOLED);
+ RETURN_STRING_LITERAL(QUIC_PROOF_INVALID);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_DUPLICATE_TAG);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_SERVER_CONFIG_EXPIRED);
+ RETURN_STRING_LITERAL(QUIC_INVALID_CHANNEL_ID_SIGNATURE);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_SYMMETRIC_KEY_SETUP_FAILED);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_MESSAGE_WHILE_VALIDATING_CLIENT_HELLO);
+ RETURN_STRING_LITERAL(QUIC_CRYPTO_UPDATE_BEFORE_HANDSHAKE_COMPLETE);
+ RETURN_STRING_LITERAL(QUIC_VERSION_NEGOTIATION_MISMATCH);
+ RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
+ // Intentionally have no default case, so we'll break the build
+ // if we add errors and don't put them here.
+ }
+ // Return a default value so that we return this when |error| doesn't match
+ // any of the QuicErrorCodes. This can happen when the ConnectionClose
+ // frame sent by the peer (attacker) has invalid error code.
+ return "INVALID_ERROR_CODE";
+}
+
+// static
+const char* QuicUtils::EncryptionLevelToString(EncryptionLevel level) {
+ switch (level) {
+ RETURN_STRING_LITERAL(ENCRYPTION_NONE);
+ RETURN_STRING_LITERAL(ENCRYPTION_INITIAL);
+ RETURN_STRING_LITERAL(ENCRYPTION_FORWARD_SECURE);
+ RETURN_STRING_LITERAL(NUM_ENCRYPTION_LEVELS);
+ }
+ return "INVALID_ENCRYPTION_LEVEL";
+}
+
+// static
+const char* QuicUtils::TransmissionTypeToString(TransmissionType type) {
+ switch (type) {
+ RETURN_STRING_LITERAL(NOT_RETRANSMISSION);
+ RETURN_STRING_LITERAL(HANDSHAKE_RETRANSMISSION);
+ RETURN_STRING_LITERAL(LOSS_RETRANSMISSION);
+ RETURN_STRING_LITERAL(ALL_UNACKED_RETRANSMISSION);
+ RETURN_STRING_LITERAL(ALL_INITIAL_RETRANSMISSION);
+ RETURN_STRING_LITERAL(RTO_RETRANSMISSION);
+ RETURN_STRING_LITERAL(TLP_RETRANSMISSION);
+ }
+ return "INVALID_TRANSMISSION_TYPE";
+}
+
+// static
+string QuicUtils::TagToString(QuicTag tag) {
+ char chars[4];
+ bool ascii = true;
+ const QuicTag orig_tag = tag;
+
+ for (size_t i = 0; i < sizeof(chars); i++) {
+ chars[i] = tag;
+ if ((chars[i] == 0 || chars[i] == '\xff') && i == 3) {
+ chars[i] = ' ';
+ }
+ if (!isprint(static_cast<unsigned char>(chars[i]))) {
+ ascii = false;
+ break;
+ }
+ tag >>= 8;
+ }
+
+ if (ascii) {
+ return string(chars, sizeof(chars));
+ }
+
+ return base::UintToString(orig_tag);
+}
+
+// static
+string QuicUtils::StringToHexASCIIDump(StringPiece in_buffer) {
+ int offset = 0;
+ const int kBytesPerLine = 16; // Max bytes dumped per line
+ const char* buf = in_buffer.data();
+ int bytes_remaining = in_buffer.size();
+ string s; // our output
+ const char* p = buf;
+ while (bytes_remaining > 0) {
+ const int line_bytes = std::min(bytes_remaining, kBytesPerLine);
+ base::StringAppendF(&s, "0x%04x: ", offset); // Do the line header
+ for (int i = 0; i < kBytesPerLine; ++i) {
+ if (i < line_bytes) {
+ base::StringAppendF(&s, "%02x", static_cast<unsigned char>(p[i]));
+ } else {
+ s += " "; // two-space filler instead of two-space hex digits
+ }
+ if (i % 2) s += ' ';
+ }
+ s += ' ';
+ for (int i = 0; i < line_bytes; ++i) { // Do the ASCII dump
+ s+= (p[i] > 32 && p[i] < 127) ? p[i] : '.';
+ }
+
+ bytes_remaining -= line_bytes;
+ offset += line_bytes;
+ p += line_bytes;
+ s += '\n';
+ }
+ return s;
+}
+
+// static
+QuicPriority QuicUtils::LowestPriority() {
+ return QuicWriteBlockedList::kLowestPriority;
+}
+
+// static
+QuicPriority QuicUtils::HighestPriority() {
+ return QuicWriteBlockedList::kHighestPriority;
+}
+
+} // namespace net
diff --git a/net/quic/quic_utils.h b/net/quic/quic_utils.h
new file mode 100644
index 0000000..1ecb37d
--- /dev/null
+++ b/net/quic/quic_utils.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Some helpers for quic.
+
+#ifndef NET_QUIC_QUIC_UTILS_H_
+#define NET_QUIC_QUIC_UTILS_H_
+
+#include "net/base/int128.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class NET_EXPORT_PRIVATE QuicUtils {
+ public:
+ enum Priority {
+ LOCAL_PRIORITY,
+ PEER_PRIORITY,
+ };
+
+ // Returns the 64 bit FNV1a hash of the data. See
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
+ static uint64 FNV1a_64_Hash(const char* data, int len);
+
+ // returns the 128 bit FNV1a hash of the data. See
+ // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
+ static uint128 FNV1a_128_Hash(const char* data, int len);
+
+ // FindMutualTag sets |out_result| to the first tag in the priority list that
+ // is also in the other list and returns true. If there is no intersection it
+ // returns false.
+ //
+ // Which list has priority is determined by |priority|.
+ //
+ // If |out_index| is non-nullptr and a match is found then the index of that
+ // match in |their_tags| is written to |out_index|.
+ static bool FindMutualTag(const QuicTagVector& our_tags,
+ const QuicTag* their_tags,
+ size_t num_their_tags,
+ Priority priority,
+ QuicTag* out_result,
+ size_t* out_index);
+
+ // SerializeUint128 writes |v| in little-endian form to |out|.
+ static void SerializeUint128(uint128 v, uint8* out);
+
+ // SerializeUint128 writes the first 96 bits of |v| in little-endian form
+ // to |out|.
+ static void SerializeUint128Short(uint128 v, uint8* out);
+
+ // Returns the name of the QuicRstStreamErrorCode as a char*
+ static const char* StreamErrorToString(QuicRstStreamErrorCode error);
+
+ // Returns the name of the QuicErrorCode as a char*
+ static const char* ErrorToString(QuicErrorCode error);
+
+ // Returns the level of encryption as a char*
+ static const char* EncryptionLevelToString(EncryptionLevel level);
+
+ // Returns TransmissionType as a char*
+ static const char* TransmissionTypeToString(TransmissionType type);
+
+ // TagToString is a utility function for pretty-printing handshake messages
+ // that converts a tag to a string. It will try to maintain the human friendly
+ // name if possible (i.e. kABCD -> "ABCD"), or will just treat it as a number
+ // if not.
+ static std::string TagToString(QuicTag tag);
+
+ // Given a binary buffer, return a hex+ASCII dump in the style of
+ // tcpdump's -X and -XX options:
+ // "0x0000: 0090 69bd 5400 000d 610f 0189 0800 4500 ..i.T...a.....E.\n"
+ // "0x0010: 001c fb98 4000 4001 7e18 d8ef 2301 455d ....@.@.~...#.E]\n"
+ // "0x0020: 7fe2 0800 6bcb 0bc6 806e ....k....n\n"
+ static std::string StringToHexASCIIDump(base::StringPiece in_buffer);
+
+ static char* AsChars(unsigned char* data) {
+ return reinterpret_cast<char*>(data);
+ }
+
+ static QuicPriority LowestPriority();
+
+ static QuicPriority HighestPriority();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicUtils);
+};
+
+// Utility function that returns an IOVector object wrapped around |str|.
+inline IOVector MakeIOVector(base::StringPiece str) {
+ IOVector iov;
+ iov.Append(const_cast<char*>(str.data()), str.size());
+ return iov;
+}
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UTILS_H_
diff --git a/net/quic/quic_utils_chromium.h b/net/quic/quic_utils_chromium.h
new file mode 100644
index 0000000..0229d1d
--- /dev/null
+++ b/net/quic/quic_utils_chromium.h
@@ -0,0 +1,80 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Some helpers for quic that are for chromium codebase.
+
+#ifndef NET_QUIC_QUIC_UTILS_CHROMIUM_H_
+#define NET_QUIC_QUIC_UTILS_CHROMIUM_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace net {
+
+//
+// Find*()
+//
+
+// Returns a const reference to the value associated with the given key if it
+// exists. Crashes otherwise.
+//
+// This is intended as a replacement for operator[] as an rvalue (for reading)
+// when the key is guaranteed to exist.
+//
+// operator[] for lookup is discouraged for several reasons:
+// * It has a side-effect of inserting missing keys
+// * It is not thread-safe (even when it is not inserting, it can still
+// choose to resize the underlying storage)
+// * It invalidates iterators (when it chooses to resize)
+// * It default constructs a value object even if it doesn't need to
+//
+// This version assumes the key is printable, and includes it in the fatal log
+// message.
+template <class Collection>
+const typename Collection::value_type::second_type&
+FindOrDie(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ CHECK(it != collection.end()) << "Map key not found: " << key;
+ return it->second;
+}
+
+// Same as above, but returns a non-const reference.
+template <class Collection>
+typename Collection::value_type::second_type&
+FindOrDie(Collection& collection, // NOLINT
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::iterator it = collection.find(key);
+ CHECK(it != collection.end()) << "Map key not found: " << key;
+ return it->second;
+}
+
+// Returns a pointer to the const value associated with the given key if it
+// exists, or NULL otherwise.
+template <class Collection>
+const typename Collection::value_type::second_type*
+FindOrNull(const Collection& collection,
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::const_iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+// Same as above but returns a pointer to the non-const value.
+template <class Collection>
+typename Collection::value_type::second_type*
+FindOrNull(Collection& collection, // NOLINT
+ const typename Collection::value_type::first_type& key) {
+ typename Collection::iterator it = collection.find(key);
+ if (it == collection.end()) {
+ return 0;
+ }
+ return &it->second;
+}
+
+} // namespace net
+
+#endif // NET_QUIC_QUIC_UTILS_CHROMIUM_H_
diff --git a/net/quic/quic_utils_chromium_test.cc b/net/quic/quic_utils_chromium_test.cc
new file mode 100644
index 0000000..b9620cf
--- /dev/null
+++ b/net/quic/quic_utils_chromium_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_utils_chromium.h"
+
+#include <map>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::map;
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicUtilsChromiumTest, FindOrNullTest) {
+ map<int, int> m;
+ m[0] = 2;
+
+ // Check FindOrNull
+ int* p1 = FindOrNull(m, 0);
+ CHECK_EQ(*p1, 2);
+ ++(*p1);
+ const map<int, int>& const_m = m;
+ const int* p2 = FindOrNull(const_m, 0);
+ CHECK_EQ(*p2, 3);
+ CHECK(FindOrNull(m, 1) == nullptr);
+}
+
+TEST(QuicUtilsChromiumTest, FindOrDieTest) {
+ std::map<int, int> m;
+ m[10] = 15;
+ EXPECT_EQ(15, FindOrDie(m, 10));
+ // TODO(rtenneti): Use the latest DEATH macros after merging with latest rch's
+ // changes.
+ // ASSERT_DEATH(FindOrDie(m, 8), "Map key not found: 8");
+
+ // Make sure the non-const reference returning version works.
+ FindOrDie(m, 10) = 20;
+ EXPECT_EQ(20, FindOrDie(m, 10));
+
+ // Make sure we can lookup values in a const map.
+ const map<int, int>& const_m = m;
+ EXPECT_EQ(20, FindOrDie(const_m, 10));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_utils_test.cc b/net/quic/quic_utils_test.cc
new file mode 100644
index 0000000..0a0355f
--- /dev/null
+++ b/net/quic/quic_utils_test.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_utils.h"
+
+#include "net/quic/crypto/crypto_protocol.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+namespace test {
+namespace {
+
+// A test string and a hex+ASCII dump of the same string.
+const unsigned char kString[] = {
+ 0x00, 0x90, 0x69, 0xbd, 0x54, 0x00, 0x00, 0x0d,
+ 0x61, 0x0f, 0x01, 0x89, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1c, 0xfb, 0x98, 0x40, 0x00, 0x40, 0x01,
+ 0x7e, 0x18, 0xd8, 0xef, 0x23, 0x01, 0x45, 0x5d,
+ 0x7f, 0xe2, 0x08, 0x00, 0x6b, 0xcb, 0x0b, 0xc6,
+ 0x80, 0x6e
+};
+
+const unsigned char kHexDump[] =
+"0x0000: 0090 69bd 5400 000d 610f 0189 0800 4500 ..i.T...a.....E.\n"
+"0x0010: 001c fb98 4000 4001 7e18 d8ef 2301 455d ....@.@.~...#.E]\n"
+"0x0020: 7fe2 0800 6bcb 0bc6 806e ....k....n\n";
+
+TEST(QuicUtilsTest, StreamErrorToString) {
+ EXPECT_STREQ("QUIC_BAD_APPLICATION_PAYLOAD",
+ QuicUtils::StreamErrorToString(QUIC_BAD_APPLICATION_PAYLOAD));
+}
+
+TEST(QuicUtilsTest, ErrorToString) {
+ EXPECT_STREQ("QUIC_NO_ERROR",
+ QuicUtils::ErrorToString(QUIC_NO_ERROR));
+}
+
+TEST(QuicUtilsTest, StringToHexASCIIDumpArgTypes) {
+ // Verify that char*, string and StringPiece are all valid argument types.
+ struct {
+ const string input;
+ const string expected;
+ } tests[] = {
+ { "", "", },
+ { "A", "0x0000: 41 A\n", },
+ { "AB", "0x0000: 4142 AB\n", },
+ { "ABC", "0x0000: 4142 43 ABC\n", },
+ { "original",
+ "0x0000: 6f72 6967 696e 616c original\n", },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ EXPECT_EQ(tests[i].expected,
+ QuicUtils::StringToHexASCIIDump(tests[i].input.c_str()));
+ EXPECT_EQ(tests[i].expected,
+ QuicUtils::StringToHexASCIIDump(tests[i].input));
+ EXPECT_EQ(tests[i].expected,
+ QuicUtils::StringToHexASCIIDump(StringPiece(tests[i].input)));
+ }
+}
+
+TEST(QuicUtilsTest, StringToHexASCIIDumpSuccess) {
+ EXPECT_EQ(string(reinterpret_cast<const char*>(kHexDump)),
+ QuicUtils::StringToHexASCIIDump(
+ string(reinterpret_cast<const char*>(kString), sizeof(kString))));
+}
+
+TEST(QuicUtilsTest, TagToString) {
+ EXPECT_EQ("SCFG",
+ QuicUtils::TagToString(kSCFG));
+ EXPECT_EQ("SNO ",
+ QuicUtils::TagToString(kServerNonceTag));
+ EXPECT_EQ("CRT ",
+ QuicUtils::TagToString(kCertificateTag));
+ EXPECT_EQ("CHLO",
+ QuicUtils::TagToString(MakeQuicTag('C', 'H', 'L', 'O')));
+ // A tag that contains a non-printing character will be printed as a decimal
+ // number.
+ EXPECT_EQ("525092931",
+ QuicUtils::TagToString(MakeQuicTag('C', 'H', 'L', '\x1f')));
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/quic_write_blocked_list.cc b/net/quic/quic_write_blocked_list.cc
new file mode 100644
index 0000000..10f9c34
--- /dev/null
+++ b/net/quic/quic_write_blocked_list.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/quic_write_blocked_list.h"
+
+namespace net {
+
+const QuicPriority QuicWriteBlockedList::kHighestPriority =
+ static_cast<QuicPriority>(net::kHighestPriority);
+const QuicPriority QuicWriteBlockedList::kLowestPriority =
+ static_cast<QuicPriority>(net::kLowestPriority);
+
+QuicWriteBlockedList::QuicWriteBlockedList()
+ : crypto_stream_blocked_(false),
+ headers_stream_blocked_(false) {}
+
+QuicWriteBlockedList::~QuicWriteBlockedList() {}
+
+} // namespace net
diff --git a/net/quic/quic_write_blocked_list.h b/net/quic/quic_write_blocked_list.h
new file mode 100644
index 0000000..6727fb4
--- /dev/null
+++ b/net/quic/quic_write_blocked_list.h
@@ -0,0 +1,113 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#ifndef NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
+#define NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
+
+#include <set>
+
+#include "net/base/net_export.h"
+#include "net/quic/quic_protocol.h"
+#include "net/spdy/write_blocked_list.h"
+
+namespace net {
+
+// Keeps tracks of the QUIC streams that have data to write, sorted by
+// priority. QUIC stream priority order is:
+// Crypto stream > Headers stream > Data streams by requested priority.
+class NET_EXPORT_PRIVATE QuicWriteBlockedList {
+ private:
+ typedef WriteBlockedList<QuicStreamId> QuicWriteBlockedListBase;
+
+ public:
+ static const QuicPriority kHighestPriority;
+ static const QuicPriority kLowestPriority;
+
+ QuicWriteBlockedList();
+ ~QuicWriteBlockedList();
+
+ bool HasWriteBlockedDataStreams() const {
+ return base_write_blocked_list_.HasWriteBlockedStreams();
+ }
+
+ bool HasWriteBlockedCryptoOrHeadersStream() const {
+ return crypto_stream_blocked_ || headers_stream_blocked_;
+ }
+
+ size_t NumBlockedStreams() const {
+ size_t num_blocked = base_write_blocked_list_.NumBlockedStreams();
+ if (crypto_stream_blocked_) {
+ ++num_blocked;
+ }
+ if (headers_stream_blocked_) {
+ ++num_blocked;
+ }
+
+ return num_blocked;
+ }
+
+ QuicStreamId PopFront() {
+ if (crypto_stream_blocked_) {
+ crypto_stream_blocked_ = false;
+ return kCryptoStreamId;
+ }
+
+ if (headers_stream_blocked_) {
+ headers_stream_blocked_ = false;
+ return kHeadersStreamId;
+ }
+
+ SpdyPriority priority =
+ base_write_blocked_list_.GetHighestPriorityWriteBlockedList();
+ QuicStreamId id = base_write_blocked_list_.PopFront(priority);
+ blocked_streams_.erase(id);
+ return id;
+ }
+
+ void PushBack(QuicStreamId stream_id, QuicPriority priority) {
+ if (stream_id == kCryptoStreamId) {
+ DCHECK_EQ(kHighestPriority, priority);
+ // TODO(avd) Add DCHECK(!crypto_stream_blocked_)
+ crypto_stream_blocked_ = true;
+ return;
+ }
+
+ if (stream_id == kHeadersStreamId) {
+ DCHECK_EQ(kHighestPriority, priority);
+ // TODO(avd) Add DCHECK(!headers_stream_blocked_);
+ headers_stream_blocked_ = true;
+ return;
+ }
+
+ if (blocked_streams_.find(stream_id) != blocked_streams_.end()) {
+ DVLOG(1) << "Stream " << stream_id << " already in write blocked list.";
+ return;
+ }
+
+ base_write_blocked_list_.PushBack(
+ stream_id, static_cast<SpdyPriority>(priority));
+ blocked_streams_.insert(stream_id);
+ return;
+ }
+
+ bool crypto_stream_blocked() const { return crypto_stream_blocked_; }
+ bool headers_stream_blocked() const { return headers_stream_blocked_; }
+
+ private:
+ QuicWriteBlockedListBase base_write_blocked_list_;
+ bool crypto_stream_blocked_;
+ bool headers_stream_blocked_;
+
+ // Keep track of write blocked streams in a set for faster membership checking
+ // than iterating over the base_write_blocked_list_. The contents of this set
+ // should mirror the contents of base_write_blocked_list_.
+ std::set<QuicStreamId> blocked_streams_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicWriteBlockedList);
+};
+
+} // namespace net
+
+
+#endif // NET_QUIC_QUIC_WRITE_BLOCKED_LIST_H_
diff --git a/net/quic/quic_write_blocked_list_test.cc b/net/quic/quic_write_blocked_list_test.cc
new file mode 100644
index 0000000..0633f63
--- /dev/null
+++ b/net/quic/quic_write_blocked_list_test.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#include "net/quic/quic_write_blocked_list.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+namespace {
+
+TEST(QuicWriteBlockedListTest, PriorityOrder) {
+ QuicWriteBlockedList write_blocked_list;
+
+ // Mark streams blocked in roughly reverse priority order, and
+ // verify that streams are sorted.
+ write_blocked_list.PushBack(40,
+ QuicWriteBlockedList::kLowestPriority);
+ write_blocked_list.PushBack(23,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(17,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kCryptoStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(5u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // The Crypto stream is highest priority.
+ EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront());
+ // Followed by the Headers stream.
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ // Streams with same priority are popped in the order they were inserted.
+ EXPECT_EQ(23u, write_blocked_list.PopFront());
+ EXPECT_EQ(17u, write_blocked_list.PopFront());
+ // Low priority stream appears last.
+ EXPECT_EQ(40u, write_blocked_list.PopFront());
+
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST(QuicWriteBlockedListTest, CryptoStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(kCryptoStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_EQ(kCryptoStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+}
+
+TEST(QuicWriteBlockedListTest, HeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+}
+
+TEST(QuicWriteBlockedListTest, VerifyHeadersStream) {
+ QuicWriteBlockedList write_blocked_list;
+ write_blocked_list.PushBack(5,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kHeadersStreamId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ EXPECT_EQ(2u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+ // In newer QUIC versions, there is a headers stream which is
+ // higher priority than data streams.
+ EXPECT_EQ(kHeadersStreamId, write_blocked_list.PopFront());
+ EXPECT_EQ(5u, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedCryptoOrHeadersStream());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+TEST(QuicWriteBlockedListTest, NoDuplicateEntries) {
+ // Test that QuicWriteBlockedList doesn't allow duplicate entries.
+ QuicWriteBlockedList write_blocked_list;
+
+ // Try to add a stream to the write blocked list multiple times at the same
+ // priority.
+ const QuicStreamId kBlockedId = kClientDataStreamId1;
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+ write_blocked_list.PushBack(kBlockedId,
+ QuicWriteBlockedList::kHighestPriority);
+
+ // This should only result in one blocked stream being added.
+ EXPECT_EQ(1u, write_blocked_list.NumBlockedStreams());
+ EXPECT_TRUE(write_blocked_list.HasWriteBlockedDataStreams());
+
+ // There should only be one stream to pop off the front.
+ EXPECT_EQ(kBlockedId, write_blocked_list.PopFront());
+ EXPECT_EQ(0u, write_blocked_list.NumBlockedStreams());
+ EXPECT_FALSE(write_blocked_list.HasWriteBlockedDataStreams());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
new file mode 100644
index 0000000..a10b0ab
--- /dev/null
+++ b/net/quic/reliable_quic_stream.cc
@@ -0,0 +1,544 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/reliable_quic_stream.h"
+
+#include "base/logging.h"
+#include "net/quic/iovector.h"
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/quic_write_blocked_list.h"
+
+using base::StringPiece;
+using std::min;
+
+namespace net {
+
+#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
+
+namespace {
+
+struct iovec MakeIovec(StringPiece data) {
+ struct iovec iov = {const_cast<char*>(data.data()),
+ static_cast<size_t>(data.size())};
+ return iov;
+}
+
+size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) {
+ QuicVersion version = session->connection()->version();
+ if (version <= QUIC_VERSION_19) {
+ return session->config()->GetInitialFlowControlWindowToSend();
+ }
+
+ return session->config()->GetInitialStreamFlowControlWindowToSend();
+}
+
+size_t GetReceivedFlowControlWindow(QuicSession* session) {
+ QuicVersion version = session->connection()->version();
+ if (version <= QUIC_VERSION_19) {
+ if (session->config()->HasReceivedInitialFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialFlowControlWindowBytes();
+ }
+
+ return kDefaultFlowControlSendWindow;
+ }
+
+ // Version must be >= QUIC_VERSION_21, so we check for stream specific flow
+ // control window.
+ if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) {
+ return session->config()->ReceivedInitialStreamFlowControlWindowBytes();
+ }
+
+ return kDefaultFlowControlSendWindow;
+}
+
+} // namespace
+
+// Wrapper that aggregates OnAckNotifications for packets sent using
+// WriteOrBufferData and delivers them to the original
+// QuicAckNotifier::DelegateInterface after all bytes written using
+// WriteOrBufferData are acked. This level of indirection is
+// necessary because the delegate interface provides no mechanism that
+// WriteOrBufferData can use to inform it that the write required
+// multiple WritevData calls or that only part of the data has been
+// sent out by the time ACKs start arriving.
+class ReliableQuicStream::ProxyAckNotifierDelegate
+ : public QuicAckNotifier::DelegateInterface {
+ public:
+ explicit ProxyAckNotifierDelegate(DelegateInterface* delegate)
+ : delegate_(delegate),
+ pending_acks_(0),
+ wrote_last_data_(false),
+ num_original_packets_(0),
+ num_original_bytes_(0),
+ num_retransmitted_packets_(0),
+ num_retransmitted_bytes_(0) {
+ }
+
+ virtual void OnAckNotification(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed)
+ OVERRIDE {
+ DCHECK_LT(0, pending_acks_);
+ --pending_acks_;
+ num_original_packets_ += num_original_packets;
+ num_original_bytes_ += num_original_bytes;
+ num_retransmitted_packets_ += num_retransmitted_packets;
+ num_retransmitted_bytes_ += num_retransmitted_bytes;
+
+ if (wrote_last_data_ && pending_acks_ == 0) {
+ delegate_->OnAckNotification(num_original_packets_,
+ num_original_bytes_,
+ num_retransmitted_packets_,
+ num_retransmitted_bytes_,
+ delta_largest_observed);
+ }
+ }
+
+ void WroteData(bool last_data) {
+ DCHECK(!wrote_last_data_);
+ ++pending_acks_;
+ wrote_last_data_ = last_data;
+ }
+
+ protected:
+ // Delegates are ref counted.
+ virtual ~ProxyAckNotifierDelegate() OVERRIDE {
+ }
+
+ private:
+ // Original delegate. delegate_->OnAckNotification will be called when:
+ // wrote_last_data_ == true and pending_acks_ == 0
+ scoped_refptr<DelegateInterface> delegate_;
+
+ // Number of outstanding acks.
+ int pending_acks_;
+
+ // True if no pending writes remain.
+ bool wrote_last_data_;
+
+ // Accumulators.
+ int num_original_packets_;
+ int num_original_bytes_;
+ int num_retransmitted_packets_;
+ int num_retransmitted_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProxyAckNotifierDelegate);
+};
+
+ReliableQuicStream::PendingData::PendingData(
+ string data_in, scoped_refptr<ProxyAckNotifierDelegate> delegate_in)
+ : data(data_in), delegate(delegate_in) {
+}
+
+ReliableQuicStream::PendingData::~PendingData() {
+}
+
+ReliableQuicStream::ReliableQuicStream(QuicStreamId id, QuicSession* session)
+ : sequencer_(this),
+ id_(id),
+ session_(session),
+ stream_bytes_read_(0),
+ stream_bytes_written_(0),
+ stream_error_(QUIC_STREAM_NO_ERROR),
+ connection_error_(QUIC_NO_ERROR),
+ read_side_closed_(false),
+ write_side_closed_(false),
+ fin_buffered_(false),
+ fin_sent_(false),
+ fin_received_(false),
+ rst_sent_(false),
+ rst_received_(false),
+ fec_policy_(FEC_PROTECT_OPTIONAL),
+ is_server_(session_->is_server()),
+ flow_controller_(
+ session_->connection(), id_, is_server_,
+ GetReceivedFlowControlWindow(session),
+ GetInitialStreamFlowControlWindowToSend(session),
+ GetInitialStreamFlowControlWindowToSend(session)),
+ connection_flow_controller_(session_->flow_controller()),
+ stream_contributes_to_connection_flow_control_(true) {
+}
+
+ReliableQuicStream::~ReliableQuicStream() {
+}
+
+void ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
+ if (read_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Ignoring frame " << frame.stream_id;
+ // We don't want to be reading: blackhole the data.
+ return;
+ }
+
+ if (frame.stream_id != id_) {
+ session_->connection()->SendConnectionClose(QUIC_INTERNAL_ERROR);
+ return;
+ }
+
+ if (frame.fin) {
+ fin_received_ = true;
+ }
+
+ // This count include duplicate data received.
+ size_t frame_payload_size = frame.data.TotalBufferSize();
+ stream_bytes_read_ += frame_payload_size;
+
+ // Flow control is interested in tracking highest received offset.
+ if (MaybeIncreaseHighestReceivedOffset(frame.offset + frame_payload_size)) {
+ // As the highest received offset has changed, we should check to see if
+ // this is a violation of flow control.
+ if (flow_controller_.FlowControlViolation() ||
+ connection_flow_controller_->FlowControlViolation()) {
+ session_->connection()->SendConnectionClose(
+ QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA);
+ return;
+ }
+ }
+
+ sequencer_.OnStreamFrame(frame);
+}
+
+int ReliableQuicStream::num_frames_received() const {
+ return sequencer_.num_frames_received();
+}
+
+int ReliableQuicStream::num_duplicate_frames_received() const {
+ return sequencer_.num_duplicate_frames_received();
+}
+
+void ReliableQuicStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+ rst_received_ = true;
+ MaybeIncreaseHighestReceivedOffset(frame.byte_offset);
+
+ stream_error_ = frame.error_code;
+ CloseWriteSide();
+ CloseReadSide();
+}
+
+void ReliableQuicStream::OnConnectionClosed(QuicErrorCode error,
+ bool from_peer) {
+ if (read_side_closed_ && write_side_closed_) {
+ return;
+ }
+ if (error != QUIC_NO_ERROR) {
+ stream_error_ = QUIC_STREAM_CONNECTION_ERROR;
+ connection_error_ = error;
+ }
+
+ CloseWriteSide();
+ CloseReadSide();
+}
+
+void ReliableQuicStream::OnFinRead() {
+ DCHECK(sequencer_.IsClosed());
+ fin_received_ = true;
+ CloseReadSide();
+}
+
+void ReliableQuicStream::Reset(QuicRstStreamErrorCode error) {
+ DCHECK_NE(QUIC_STREAM_NO_ERROR, error);
+ stream_error_ = error;
+ // Sending a RstStream results in calling CloseStream.
+ session()->SendRstStream(id(), error, stream_bytes_written_);
+ rst_sent_ = true;
+}
+
+void ReliableQuicStream::CloseConnection(QuicErrorCode error) {
+ session()->connection()->SendConnectionClose(error);
+}
+
+void ReliableQuicStream::CloseConnectionWithDetails(QuicErrorCode error,
+ const string& details) {
+ session()->connection()->SendConnectionCloseWithDetails(error, details);
+}
+
+QuicVersion ReliableQuicStream::version() const {
+ return session()->connection()->version();
+}
+
+void ReliableQuicStream::WriteOrBufferData(
+ StringPiece data,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ if (data.empty() && !fin) {
+ LOG(DFATAL) << "data.empty() && !fin";
+ return;
+ }
+
+ if (fin_buffered_) {
+ LOG(DFATAL) << "Fin already buffered";
+ return;
+ }
+
+ scoped_refptr<ProxyAckNotifierDelegate> proxy_delegate;
+ if (ack_notifier_delegate != nullptr) {
+ proxy_delegate = new ProxyAckNotifierDelegate(ack_notifier_delegate);
+ }
+
+ QuicConsumedData consumed_data(0, false);
+ fin_buffered_ = fin;
+
+ if (queued_data_.empty()) {
+ struct iovec iov(MakeIovec(data));
+ consumed_data = WritevData(&iov, 1, fin, proxy_delegate.get());
+ DCHECK_LE(consumed_data.bytes_consumed, data.length());
+ }
+
+ bool write_completed;
+ // If there's unconsumed data or an unconsumed fin, queue it.
+ if (consumed_data.bytes_consumed < data.length() ||
+ (fin && !consumed_data.fin_consumed)) {
+ StringPiece remainder(data.substr(consumed_data.bytes_consumed));
+ queued_data_.push_back(PendingData(remainder.as_string(), proxy_delegate));
+ write_completed = false;
+ } else {
+ write_completed = true;
+ }
+
+ if ((proxy_delegate.get() != nullptr) &&
+ (consumed_data.bytes_consumed > 0 || consumed_data.fin_consumed)) {
+ proxy_delegate->WroteData(write_completed);
+ }
+}
+
+void ReliableQuicStream::OnCanWrite() {
+ bool fin = false;
+ while (!queued_data_.empty()) {
+ PendingData* pending_data = &queued_data_.front();
+ ProxyAckNotifierDelegate* delegate = pending_data->delegate.get();
+ if (queued_data_.size() == 1 && fin_buffered_) {
+ fin = true;
+ }
+ struct iovec iov(MakeIovec(pending_data->data));
+ QuicConsumedData consumed_data = WritevData(&iov, 1, fin, delegate);
+ if (consumed_data.bytes_consumed == pending_data->data.size() &&
+ fin == consumed_data.fin_consumed) {
+ queued_data_.pop_front();
+ if (delegate != nullptr) {
+ delegate->WroteData(true);
+ }
+ } else {
+ if (consumed_data.bytes_consumed > 0) {
+ pending_data->data.erase(0, consumed_data.bytes_consumed);
+ if (delegate != nullptr) {
+ delegate->WroteData(false);
+ }
+ }
+ break;
+ }
+ }
+}
+
+void ReliableQuicStream::MaybeSendBlocked() {
+ flow_controller_.MaybeSendBlocked();
+ if (!stream_contributes_to_connection_flow_control_) {
+ return;
+ }
+ connection_flow_controller_->MaybeSendBlocked();
+ // If we are connection level flow control blocked, then add the stream
+ // to the write blocked list. It will be given a chance to write when a
+ // connection level WINDOW_UPDATE arrives.
+ if (connection_flow_controller_->IsBlocked() &&
+ !flow_controller_.IsBlocked()) {
+ session_->MarkWriteBlocked(id(), EffectivePriority());
+ }
+}
+
+QuicConsumedData ReliableQuicStream::WritevData(
+ const struct iovec* iov,
+ int iov_count,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate) {
+ if (write_side_closed_) {
+ DLOG(ERROR) << ENDPOINT << "Attempt to write when the write side is closed";
+ return QuicConsumedData(0, false);
+ }
+
+ // How much data we want to write.
+ size_t write_length = TotalIovecLength(iov, iov_count);
+
+ // A FIN with zero data payload should not be flow control blocked.
+ bool fin_with_zero_data = (fin && write_length == 0);
+
+ if (flow_controller_.IsEnabled()) {
+ // How much data we are allowed to write from flow control.
+ uint64 send_window = flow_controller_.SendWindowSize();
+ // TODO(rjshade): Remove connection_flow_controller_->IsEnabled() check when
+ // removing QUIC_VERSION_19.
+ if (stream_contributes_to_connection_flow_control_ &&
+ connection_flow_controller_->IsEnabled()) {
+ send_window =
+ min(send_window, connection_flow_controller_->SendWindowSize());
+ }
+
+ if (send_window == 0 && !fin_with_zero_data) {
+ // Quick return if we can't send anything.
+ MaybeSendBlocked();
+ return QuicConsumedData(0, false);
+ }
+
+ if (write_length > send_window) {
+ // Don't send the FIN if we aren't going to send all the data.
+ fin = false;
+
+ // Writing more data would be a violation of flow control.
+ write_length = send_window;
+ }
+ }
+
+ // Fill an IOVector with bytes from the iovec.
+ IOVector data;
+ data.AppendIovecAtMostBytes(iov, iov_count, write_length);
+
+ QuicConsumedData consumed_data = session()->WritevData(
+ id(), data, stream_bytes_written_, fin, GetFecProtection(),
+ ack_notifier_delegate);
+ stream_bytes_written_ += consumed_data.bytes_consumed;
+
+ AddBytesSent(consumed_data.bytes_consumed);
+
+ if (consumed_data.bytes_consumed == write_length) {
+ if (!fin_with_zero_data) {
+ MaybeSendBlocked();
+ }
+ if (fin && consumed_data.fin_consumed) {
+ fin_sent_ = true;
+ CloseWriteSide();
+ } else if (fin && !consumed_data.fin_consumed) {
+ session_->MarkWriteBlocked(id(), EffectivePriority());
+ }
+ } else {
+ session_->MarkWriteBlocked(id(), EffectivePriority());
+ }
+ return consumed_data;
+}
+
+FecProtection ReliableQuicStream::GetFecProtection() {
+ return fec_policy_ == FEC_PROTECT_ALWAYS ? MUST_FEC_PROTECT : MAY_FEC_PROTECT;
+}
+
+void ReliableQuicStream::CloseReadSide() {
+ if (read_side_closed_) {
+ return;
+ }
+ DVLOG(1) << ENDPOINT << "Done reading from stream " << id();
+
+ read_side_closed_ = true;
+ if (write_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Closing stream: " << id();
+ session_->CloseStream(id());
+ }
+}
+
+void ReliableQuicStream::CloseWriteSide() {
+ if (write_side_closed_) {
+ return;
+ }
+ DVLOG(1) << ENDPOINT << "Done writing to stream " << id();
+
+ write_side_closed_ = true;
+ if (read_side_closed_) {
+ DVLOG(1) << ENDPOINT << "Closing stream: " << id();
+ session_->CloseStream(id());
+ }
+}
+
+bool ReliableQuicStream::HasBufferedData() const {
+ return !queued_data_.empty();
+}
+
+void ReliableQuicStream::OnClose() {
+ CloseReadSide();
+ CloseWriteSide();
+
+ if (!fin_sent_ && !rst_sent_) {
+ // For flow control accounting, we must tell the peer how many bytes we have
+ // written on this stream before termination. Done here if needed, using a
+ // RST frame.
+ DVLOG(1) << ENDPOINT << "Sending RST in OnClose: " << id();
+ session_->SendRstStream(id(), QUIC_RST_FLOW_CONTROL_ACCOUNTING,
+ stream_bytes_written_);
+ rst_sent_ = true;
+ }
+
+ // We are closing the stream and will not process any further incoming bytes.
+ // As there may be more bytes in flight and we need to ensure that both
+ // endpoints have the same connection level flow control state, mark all
+ // unreceived or buffered bytes as consumed.
+ uint64 bytes_to_consume = flow_controller_.highest_received_byte_offset() -
+ flow_controller_.bytes_consumed();
+ AddBytesConsumed(bytes_to_consume);
+}
+
+void ReliableQuicStream::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ if (!flow_controller_.IsEnabled()) {
+ DLOG(DFATAL) << "Flow control not enabled! " << version();
+ return;
+ }
+
+ if (flow_controller_.UpdateSendWindowOffset(frame.byte_offset)) {
+ // We can write again!
+ // TODO(rjshade): This does not respect priorities (e.g. multiple
+ // outstanding POSTs are unblocked on arrival of
+ // SHLO with initial window).
+ // As long as the connection is not flow control blocked, we can write!
+ OnCanWrite();
+ }
+}
+
+bool ReliableQuicStream::MaybeIncreaseHighestReceivedOffset(uint64 new_offset) {
+ if (!flow_controller_.IsEnabled()) {
+ return false;
+ }
+ uint64 increment =
+ new_offset - flow_controller_.highest_received_byte_offset();
+ if (!flow_controller_.UpdateHighestReceivedOffset(new_offset)) {
+ return false;
+ }
+
+ // If |new_offset| increased the stream flow controller's highest received
+ // offset, then we need to increase the connection flow controller's value
+ // by the incremental difference.
+ if (stream_contributes_to_connection_flow_control_) {
+ connection_flow_controller_->UpdateHighestReceivedOffset(
+ connection_flow_controller_->highest_received_byte_offset() +
+ increment);
+ }
+ return true;
+}
+
+void ReliableQuicStream::AddBytesSent(uint64 bytes) {
+ if (flow_controller_.IsEnabled()) {
+ flow_controller_.AddBytesSent(bytes);
+ if (stream_contributes_to_connection_flow_control_) {
+ connection_flow_controller_->AddBytesSent(bytes);
+ }
+ }
+}
+
+void ReliableQuicStream::AddBytesConsumed(uint64 bytes) {
+ if (flow_controller_.IsEnabled()) {
+ // Only adjust stream level flow controller if we are still reading.
+ if (!read_side_closed_) {
+ flow_controller_.AddBytesConsumed(bytes);
+ }
+
+ if (stream_contributes_to_connection_flow_control_) {
+ connection_flow_controller_->AddBytesConsumed(bytes);
+ }
+ }
+}
+
+bool ReliableQuicStream::IsFlowControlBlocked() {
+ if (flow_controller_.IsBlocked()) {
+ return true;
+ }
+ return stream_contributes_to_connection_flow_control_ &&
+ connection_flow_controller_->IsBlocked();
+}
+
+} // namespace net
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
new file mode 100644
index 0000000..5b5ae0f
--- /dev/null
+++ b/net/quic/reliable_quic_stream.h
@@ -0,0 +1,252 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The base class for client/server reliable streams.
+
+#ifndef NET_QUIC_RELIABLE_QUIC_STREAM_H_
+#define NET_QUIC_RELIABLE_QUIC_STREAM_H_
+
+#include <sys/types.h>
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_piece.h"
+#include "net/base/iovec.h"
+#include "net/base/net_export.h"
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_stream_sequencer.h"
+#include "net/quic/quic_types.h"
+
+namespace net {
+
+namespace test {
+class ReliableQuicStreamPeer;
+} // namespace test
+
+class QuicSession;
+
+class NET_EXPORT_PRIVATE ReliableQuicStream {
+ public:
+ ReliableQuicStream(QuicStreamId id,
+ QuicSession* session);
+
+ virtual ~ReliableQuicStream();
+
+ // Called when a (potentially duplicate) stream frame has been received
+ // for this stream.
+ virtual void OnStreamFrame(const QuicStreamFrame& frame);
+
+ // Called when the connection becomes writeable to allow the stream
+ // to write any pending data.
+ virtual void OnCanWrite();
+
+ // Called by the session just before the stream is deleted.
+ virtual void OnClose();
+
+ // Called when we get a stream reset from the peer.
+ virtual void OnStreamReset(const QuicRstStreamFrame& frame);
+
+ // Called when we get or send a connection close, and should immediately
+ // close the stream. This is not passed through the sequencer,
+ // but is handled immediately.
+ virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer);
+
+ // Called when the final data has been read.
+ virtual void OnFinRead();
+
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) = 0;
+
+ // Called to reset the stream from this end.
+ virtual void Reset(QuicRstStreamErrorCode error);
+
+ // Called to close the entire connection from this end.
+ virtual void CloseConnection(QuicErrorCode error);
+ virtual void CloseConnectionWithDetails(QuicErrorCode error,
+ const string& details);
+
+ // Returns the effective priority for the stream. This value may change
+ // during the life of the stream.
+ virtual QuicPriority EffectivePriority() const = 0;
+
+ QuicStreamId id() const { return id_; }
+
+ QuicRstStreamErrorCode stream_error() const { return stream_error_; }
+ QuicErrorCode connection_error() const { return connection_error_; }
+
+ bool read_side_closed() const { return read_side_closed_; }
+ bool write_side_closed() const { return write_side_closed_; }
+
+ uint64 stream_bytes_read() const { return stream_bytes_read_; }
+ uint64 stream_bytes_written() const { return stream_bytes_written_; }
+
+ QuicVersion version() const;
+
+ void set_fin_sent(bool fin_sent) { fin_sent_ = fin_sent; }
+ void set_rst_sent(bool rst_sent) { rst_sent_ = rst_sent; }
+
+ void set_fec_policy(FecPolicy fec_policy) { fec_policy_ = fec_policy; }
+ FecPolicy fec_policy() const { return fec_policy_; }
+
+ // Adjust our flow control windows according to new offset in |frame|.
+ virtual void OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame);
+
+ int num_frames_received() const;
+
+ int num_duplicate_frames_received() const;
+
+ QuicFlowController* flow_controller() { return &flow_controller_; }
+
+ // Called when we see a frame which could increase the highest offset.
+ // Returns true if the highest offset did increase.
+ bool MaybeIncreaseHighestReceivedOffset(uint64 new_offset);
+ // Called when bytese are sent to the peer.
+ void AddBytesSent(uint64 bytes);
+ // Called by the stream sequencer as bytes are consumed from the buffer.
+ // If our receive window has dropped below the threshold, then send a
+ // WINDOW_UPDATE frame.
+ void AddBytesConsumed(uint64 bytes);
+
+ // Returns true if the stream is flow control blocked, by the stream flow
+ // control window or the connection flow control window.
+ bool IsFlowControlBlocked();
+
+ // Returns true if we have received either a RST or a FIN - either of which
+ // gives a definitive number of bytes which the peer has sent. If this is not
+ // true on stream termination the session must keep track of the stream's byte
+ // offset until a definitive final value arrives.
+ bool HasFinalReceivedByteOffset() const {
+ return fin_received_ || rst_received_;
+ }
+
+ protected:
+ // Sends as much of 'data' to the connection as the connection will consume,
+ // and then buffers any remaining data in queued_data_.
+ void WriteOrBufferData(
+ base::StringPiece data,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // Sends as many bytes in the first |count| buffers of |iov| to the connection
+ // as the connection will consume.
+ // If |ack_notifier_delegate| is provided, then it will be notified once all
+ // the ACKs for this write have been received.
+ // Returns the number of bytes consumed by the connection.
+ QuicConsumedData WritevData(
+ const struct iovec* iov,
+ int iov_count,
+ bool fin,
+ QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
+
+ // Helper method that returns FecProtection to use for writes to the session.
+ FecProtection GetFecProtection();
+
+ // Close the read side of the socket. Further frames will not be accepted.
+ virtual void CloseReadSide();
+
+ // Close the write side of the socket. Further writes will fail.
+ void CloseWriteSide();
+
+ bool HasBufferedData() const;
+
+ bool fin_buffered() const { return fin_buffered_; }
+
+ const QuicSession* session() const { return session_; }
+ QuicSession* session() { return session_; }
+
+ const QuicStreamSequencer* sequencer() const { return &sequencer_; }
+ QuicStreamSequencer* sequencer() { return &sequencer_; }
+
+ // TODO(rjshade): Remove this method when removing QUIC_VERSION_19.
+ void DisableFlowControl() {
+ flow_controller_.Disable();
+ }
+
+ void DisableConnectionFlowControlForThisStream() {
+ stream_contributes_to_connection_flow_control_ = false;
+ }
+
+ private:
+ friend class test::ReliableQuicStreamPeer;
+ friend class QuicStreamUtils;
+ class ProxyAckNotifierDelegate;
+
+ struct PendingData {
+ PendingData(string data_in,
+ scoped_refptr<ProxyAckNotifierDelegate> delegate_in);
+ ~PendingData();
+
+ string data;
+ // Delegate that should be notified when the pending data is acked.
+ // Can be nullptr.
+ scoped_refptr<ProxyAckNotifierDelegate> delegate;
+ };
+
+ // Calls MaybeSendBlocked on our flow controller, and connection level flow
+ // controller. If we are flow control blocked, marks this stream as write
+ // blocked.
+ void MaybeSendBlocked();
+
+ std::list<PendingData> queued_data_;
+
+ QuicStreamSequencer sequencer_;
+ QuicStreamId id_;
+ QuicSession* session_;
+ // Bytes read and written refer to payload bytes only: they do not include
+ // framing, encryption overhead etc.
+ uint64 stream_bytes_read_;
+ uint64 stream_bytes_written_;
+
+ // Stream error code received from a RstStreamFrame or error code sent by the
+ // visitor or sequencer in the RstStreamFrame.
+ QuicRstStreamErrorCode stream_error_;
+ // Connection error code due to which the stream was closed. |stream_error_|
+ // is set to |QUIC_STREAM_CONNECTION_ERROR| when this happens and consumers
+ // should check |connection_error_|.
+ QuicErrorCode connection_error_;
+
+ // True if the read side is closed and further frames should be rejected.
+ bool read_side_closed_;
+ // True if the write side is closed, and further writes should fail.
+ bool write_side_closed_;
+
+ bool fin_buffered_;
+ bool fin_sent_;
+
+ // True if this stream has received (and the sequencer has accepted) a
+ // StreamFrame with the FIN set.
+ bool fin_received_;
+
+ // In combination with fin_sent_, used to ensure that a FIN and/or a RST is
+ // always sent before stream termination.
+ bool rst_sent_;
+
+ // True if this stream has received a RST stream frame.
+ bool rst_received_;
+
+ // FEC policy to be used for this stream.
+ FecPolicy fec_policy_;
+
+ // True if the session this stream is running under is a server session.
+ bool is_server_;
+
+ QuicFlowController flow_controller_;
+
+ // The connection level flow controller. Not owned.
+ QuicFlowController* connection_flow_controller_;
+
+ // Special streams, such as the crypto and headers streams, do not respect
+ // connection level flow control limits (but are stream level flow control
+ // limited).
+ bool stream_contributes_to_connection_flow_control_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReliableQuicStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_RELIABLE_QUIC_STREAM_H_
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
new file mode 100644
index 0000000..336ea53
--- /dev/null
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -0,0 +1,652 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/reliable_quic_stream.h"
+
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/quic_write_blocked_list.h"
+#include "net/quic/spdy_utils.h"
+#include "net/quic/test_tools/quic_config_peer.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+#include "net/test/gtest_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
+
+using base::StringPiece;
+using std::min;
+using testing::CreateFunctor;
+using testing::InSequence;
+using testing::Invoke;
+using testing::Return;
+using testing::SaveArg;
+using testing::StrictMock;
+using testing::WithArgs;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+const char kData1[] = "FooAndBar";
+const char kData2[] = "EepAndBaz";
+const size_t kDataLen = 9;
+const bool kIsServer = true;
+const bool kShouldProcessData = true;
+
+class TestStream : public ReliableQuicStream {
+ public:
+ TestStream(QuicStreamId id,
+ QuicSession* session,
+ bool should_process_data)
+ : ReliableQuicStream(id, session),
+ should_process_data_(should_process_data) {}
+
+ virtual uint32 ProcessRawData(const char* data, uint32 data_len) OVERRIDE {
+ EXPECT_NE(0u, data_len);
+ DVLOG(1) << "ProcessData data_len: " << data_len;
+ data_ += string(data, data_len);
+ return should_process_data_ ? data_len : 0;
+ }
+
+ virtual QuicPriority EffectivePriority() const OVERRIDE {
+ return QuicUtils::HighestPriority();
+ }
+
+ using ReliableQuicStream::WriteOrBufferData;
+ using ReliableQuicStream::CloseReadSide;
+ using ReliableQuicStream::CloseWriteSide;
+ using ReliableQuicStream::OnClose;
+
+ private:
+ bool should_process_data_;
+ string data_;
+};
+
+class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> {
+ public:
+ ReliableQuicStreamTest()
+ : initial_flow_control_window_bytes_(kMaxPacketSize),
+ zero_(QuicTime::Delta::Zero()),
+ supported_versions_(QuicSupportedVersions()) {
+ headers_[":host"] = "www.google.com";
+ headers_[":path"] = "/index.hml";
+ headers_[":scheme"] = "https";
+ headers_["cookie"] =
+ "__utma=208381060.1228362404.1372200928.1372200928.1372200928.1; "
+ "__utmc=160408618; "
+ "GX=DQAAAOEAAACWJYdewdE9rIrW6qw3PtVi2-d729qaa-74KqOsM1NVQblK4VhX"
+ "hoALMsy6HOdDad2Sz0flUByv7etmo3mLMidGrBoljqO9hSVA40SLqpG_iuKKSHX"
+ "RW3Np4bq0F0SDGDNsW0DSmTS9ufMRrlpARJDS7qAI6M3bghqJp4eABKZiRqebHT"
+ "pMU-RXvTI5D5oCF1vYxYofH_l1Kviuiy3oQ1kS1enqWgbhJ2t61_SNdv-1XJIS0"
+ "O3YeHLmVCs62O6zp89QwakfAWK9d3IDQvVSJzCQsvxvNIvaZFa567MawWlXg0Rh"
+ "1zFMi5vzcns38-8_Sns; "
+ "GA=v*2%2Fmem*57968640*47239936%2Fmem*57968640*47114716%2Fno-nm-"
+ "yj*15%2Fno-cc-yj*5%2Fpc-ch*133685%2Fpc-s-cr*133947%2Fpc-s-t*1339"
+ "47%2Fno-nm-yj*4%2Fno-cc-yj*1%2Fceft-as*1%2Fceft-nqas*0%2Fad-ra-c"
+ "v_p%2Fad-nr-cv_p-f*1%2Fad-v-cv_p*859%2Fad-ns-cv_p-f*1%2Ffn-v-ad%"
+ "2Fpc-t*250%2Fpc-cm*461%2Fpc-s-cr*722%2Fpc-s-t*722%2Fau_p*4"
+ "SICAID=AJKiYcHdKgxum7KMXG0ei2t1-W4OD1uW-ecNsCqC0wDuAXiDGIcT_HA2o1"
+ "3Rs1UKCuBAF9g8rWNOFbxt8PSNSHFuIhOo2t6bJAVpCsMU5Laa6lewuTMYI8MzdQP"
+ "ARHKyW-koxuhMZHUnGBJAM1gJODe0cATO_KGoX4pbbFxxJ5IicRxOrWK_5rU3cdy6"
+ "edlR9FsEdH6iujMcHkbE5l18ehJDwTWmBKBzVD87naobhMMrF6VvnDGxQVGp9Ir_b"
+ "Rgj3RWUoPumQVCxtSOBdX0GlJOEcDTNCzQIm9BSfetog_eP_TfYubKudt5eMsXmN6"
+ "QnyXHeGeK2UINUzJ-D30AFcpqYgH9_1BvYSpi7fc7_ydBU8TaD8ZRxvtnzXqj0RfG"
+ "tuHghmv3aD-uzSYJ75XDdzKdizZ86IG6Fbn1XFhYZM-fbHhm3mVEXnyRW4ZuNOLFk"
+ "Fas6LMcVC6Q8QLlHYbXBpdNFuGbuZGUnav5C-2I_-46lL0NGg3GewxGKGHvHEfoyn"
+ "EFFlEYHsBQ98rXImL8ySDycdLEFvBPdtctPmWCfTxwmoSMLHU2SCVDhbqMWU5b0yr"
+ "JBCScs_ejbKaqBDoB7ZGxTvqlrB__2ZmnHHjCr8RgMRtKNtIeuZAo ";
+ }
+
+ void set_supported_versions(const QuicVersionVector& versions) {
+ supported_versions_ = versions;
+ }
+
+ void Initialize(bool stream_should_process_data) {
+ connection_ =
+ new StrictMock<MockConnection>(kIsServer, supported_versions_);
+ session_.reset(new StrictMock<MockSession>(connection_));
+
+ // New streams rely on having the peer's flow control receive window
+ // negotiated in the config.
+ QuicConfigPeer::SetReceivedInitialFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+ QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ session_->config(), initial_flow_control_window_bytes_);
+
+ stream_.reset(new TestStream(kHeadersStreamId, session_.get(),
+ stream_should_process_data));
+ write_blocked_list_ =
+ QuicSessionPeer::GetWriteBlockedStreams(session_.get());
+ }
+
+ bool fin_sent() { return ReliableQuicStreamPeer::FinSent(stream_.get()); }
+ bool rst_sent() { return ReliableQuicStreamPeer::RstSent(stream_.get()); }
+
+ void set_initial_flow_control_window_bytes(uint32 val) {
+ initial_flow_control_window_bytes_ = val;
+ }
+
+ bool HasWriteBlockedStreams() {
+ return write_blocked_list_->HasWriteBlockedCryptoOrHeadersStream() ||
+ write_blocked_list_->HasWriteBlockedDataStreams();
+ }
+
+ protected:
+ MockConnection* connection_;
+ scoped_ptr<MockSession> session_;
+ scoped_ptr<TestStream> stream_;
+ SpdyHeaderBlock headers_;
+ QuicWriteBlockedList* write_blocked_list_;
+ uint32 initial_flow_control_window_bytes_;
+ QuicTime::Delta zero_;
+ QuicVersionVector supported_versions_;
+};
+
+TEST_F(ReliableQuicStreamTest, WriteAllData) {
+ Initialize(kShouldProcessData);
+
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen, true)));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_FALSE(HasWriteBlockedStreams());
+}
+
+TEST_F(ReliableQuicStreamTest, NoBlockingIfNoDataOrFin) {
+ Initialize(kShouldProcessData);
+
+ // Write no data and no fin. If we consume nothing we should not be write
+ // blocked.
+ EXPECT_DFATAL(stream_->WriteOrBufferData(StringPiece(), false, nullptr), "");
+ EXPECT_FALSE(HasWriteBlockedStreams());
+}
+
+TEST_F(ReliableQuicStreamTest, BlockIfOnlySomeDataConsumed) {
+ Initialize(kShouldProcessData);
+
+ // Write some data and no fin. If we consume some but not all of the data,
+ // we should be write blocked a not all the data was consumed.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 2), false, nullptr);
+ ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
+}
+
+TEST_F(ReliableQuicStreamTest, BlockIfFinNotConsumedWithData) {
+ Initialize(kShouldProcessData);
+
+ // Write some data and no fin. If we consume all the data but not the fin,
+ // we should be write blocked because the fin was not consumed.
+ // (This should never actually happen as the fin should be sent out with the
+ // last data)
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(2, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 2), true, nullptr);
+ ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
+}
+
+TEST_F(ReliableQuicStreamTest, BlockIfSoloFinNotConsumed) {
+ Initialize(kShouldProcessData);
+
+ // Write no data and a fin. If we consume nothing we should be write blocked,
+ // as the fin was not consumed.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(StringPiece(), true, nullptr);
+ ASSERT_EQ(1u, write_blocked_list_->NumBlockedStreams());
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferData) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectAlways) {
+ Initialize(kShouldProcessData);
+
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_ALWAYS);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ // Write first data onto stream, which will cause one session write.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) {
+ Initialize(kShouldProcessData);
+
+ // Set FEC policy on stream.
+ ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_OPTIONAL);
+
+ EXPECT_FALSE(HasWriteBlockedStreams());
+ size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+ PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+ QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+ length);
+
+ // Write first data onto stream, which will cause one session write.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).WillOnce(
+ Return(QuicConsumedData(kDataLen - 1, false)));
+ stream_->WriteOrBufferData(kData1, false, nullptr);
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Queue a bytes_consumed write.
+ stream_->WriteOrBufferData(kData2, false, nullptr);
+
+ // Make sure we get the tail of the first write followed by the bytes_consumed
+ InSequence s;
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(1, false)));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+ stream_->OnCanWrite();
+
+ // And finally the end of the bytes_consumed.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+ WillOnce(Return(QuicConsumedData(2, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) {
+ Initialize(kShouldProcessData);
+
+ stream_->CloseReadSide();
+ stream_->CloseWriteSide();
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
+ stream_->OnConnectionClosed(QUIC_INTERNAL_ERROR, false);
+ EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
+ EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error());
+}
+
+TEST_F(ReliableQuicStreamTest, RstAlwaysSentIfNoFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if no FIN has been sent, we send a RST.
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with no FIN.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, false)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 1), false, nullptr);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we send a RST.
+ EXPECT_CALL(*session_, SendRstStream(_, _, _));
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, RstNotSentIfFinSent) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a FIN has been sent, we don't also send a RST.
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Write some data, with FIN.
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(1, true)));
+ stream_->WriteOrBufferData(StringPiece(kData1, 1), true, nullptr);
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Now close the stream, and expect that we do not send a RST.
+ stream_->OnClose();
+ EXPECT_TRUE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, OnlySendOneRst) {
+ // For flow control accounting, a stream must send either a FIN or a RST frame
+ // before termination.
+ // Test that if a stream sends a RST, it doesn't send an additional RST during
+ // OnClose() (this shouldn't be harmful, but we shouldn't do it anyway...)
+
+ Initialize(kShouldProcessData);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_FALSE(rst_sent());
+
+ // Reset the stream.
+ const int expected_resets = 1;
+ EXPECT_CALL(*session_, SendRstStream(_, _, _)).Times(expected_resets);
+ stream_->Reset(QUIC_STREAM_CANCELLED);
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+
+ // Now close the stream (any further resets being sent would break the
+ // expectation above).
+ stream_->OnClose();
+ EXPECT_FALSE(fin_sent());
+ EXPECT_TRUE(rst_sent());
+}
+
+TEST_F(ReliableQuicStreamTest, StreamFlowControlMultipleWindowUpdates) {
+ set_initial_flow_control_window_bytes(1000);
+
+ Initialize(kShouldProcessData);
+
+ // If we receive multiple WINDOW_UPDATES (potentially out of order), then we
+ // want to make sure we latch the largest offset we see.
+
+ // Initially should be default.
+ EXPECT_EQ(
+ initial_flow_control_window_bytes_,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Check a single WINDOW_UPDATE results in correct offset.
+ QuicWindowUpdateFrame window_update_1(stream_->id(), 1234);
+ stream_->OnWindowUpdateFrame(window_update_1);
+ EXPECT_EQ(
+ window_update_1.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+
+ // Now send a few more WINDOW_UPDATES and make sure that only the largest is
+ // remembered.
+ QuicWindowUpdateFrame window_update_2(stream_->id(), 1);
+ QuicWindowUpdateFrame window_update_3(stream_->id(), 9999);
+ QuicWindowUpdateFrame window_update_4(stream_->id(), 5678);
+ stream_->OnWindowUpdateFrame(window_update_2);
+ stream_->OnWindowUpdateFrame(window_update_3);
+ stream_->OnWindowUpdateFrame(window_update_4);
+ EXPECT_EQ(
+ window_update_3.byte_offset,
+ QuicFlowControllerPeer::SendWindowOffset(stream_->flow_controller()));
+}
+
+void SaveProxyAckNotifierDelegate(
+ scoped_refptr<QuicAckNotifier::DelegateInterface>* delegate_out,
+ QuicAckNotifier::DelegateInterface* delegate) {
+ *delegate_out = delegate;
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ const int kDataSize = 16 * 1024;
+ const string kData(kDataSize, 'a');
+
+ const int kFirstWriteSize = 100;
+ const int kSecondWriteSize = 50;
+ const int kLastWriteSize = kDataSize - kFirstWriteSize - kSecondWriteSize;
+
+ // Set a large flow control send window so this doesn't interfere with test.
+ stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kFirstWriteSize, false))));
+ stream_->WriteOrBufferData(kData, false, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(kSecondWriteSize, false)));
+ stream_->OnCanWrite();
+
+ // No ack expected for an empty write.
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->OnCanWrite();
+
+ EXPECT_CALL(*session_,
+ WritevData(kHeadersStreamId, _, _, _, _, proxy_delegate.get()))
+ .WillOnce(Return(QuicConsumedData(kLastWriteSize, false)));
+ stream_->OnCanWrite();
+
+ // There were two writes, so OnAckNotification is not propagated
+ // until the third Ack arrives.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_);
+
+ // The arguments to delegate->OnAckNotification are the sum of the
+ // arguments to proxy_delegate OnAckNotification calls.
+ EXPECT_CALL(*delegate.get(), OnAckNotification(111, 222, 333, 444, zero_));
+ proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_);
+}
+
+// Verify delegate behavior when packets are acked before the
+// WritevData call that sends out the last byte.
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ const int kDataSize = 16 * 1024;
+ const string kData(kDataSize, 'a');
+
+ const int kInitialWriteSize = 100;
+
+ // Set a large flow control send window so this doesn't interfere with test.
+ stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+ session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kInitialWriteSize, false))));
+ stream_->WriteOrBufferData(kData, false, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ // Handle the ack of the first write.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ proxy_delegate = nullptr;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _)).WillOnce(
+ DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataSize - kInitialWriteSize, false))));
+ stream_->OnCanWrite();
+
+ // Handle the ack for the second write.
+ EXPECT_CALL(*delegate.get(), OnAckNotification(101, 202, 303, 404, zero_));
+ proxy_delegate->OnAckNotification(100, 200, 300, 400, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData does not buffer.
+TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferNoBuffer) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, true))));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_FALSE(HasWriteBlockedStreams());
+
+ // Handle the ack.
+ EXPECT_CALL(*delegate.get(), OnAckNotification(1, 2, 3, 4, zero_));
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData buffers all the data.
+TEST_F(ReliableQuicStreamTest, BufferOnWriteAndBufferDataWithAckNotifer) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(0, false)));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, true))));
+ stream_->OnCanWrite();
+
+ // Handle the ack.
+ EXPECT_CALL(*delegate.get(), OnAckNotification(1, 2, 3, 4, zero_));
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+}
+
+// Verify delegate behavior when WriteOrBufferData when the FIN is
+// sent out in a different packet.
+TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) {
+ Initialize(kShouldProcessData);
+
+ scoped_refptr<MockAckNotifierDelegate> delegate(
+ new StrictMock<MockAckNotifierDelegate>);
+
+ scoped_refptr<QuicAckNotifier::DelegateInterface> proxy_delegate;
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(kDataLen, false))));
+ stream_->WriteOrBufferData(kData1, true, delegate.get());
+ EXPECT_TRUE(HasWriteBlockedStreams());
+
+ EXPECT_CALL(*session_, WritevData(kHeadersStreamId, _, _, _, _, _))
+ .WillOnce(DoAll(WithArgs<5>(Invoke(CreateFunctor(
+ &SaveProxyAckNotifierDelegate, &proxy_delegate))),
+ Return(QuicConsumedData(0, true))));
+ stream_->OnCanWrite();
+
+ // Handle the acks.
+ proxy_delegate->OnAckNotification(1, 2, 3, 4, zero_);
+ EXPECT_CALL(*delegate.get(), OnAckNotification(11, 22, 33, 44, zero_));
+ proxy_delegate->OnAckNotification(10, 20, 30, 40, zero_);
+}
+
+// Verify that when we receive a packet which violates flow control (i.e. sends
+// too much data on the stream) that the stream sequencer never sees this frame,
+// as we check for violation and close the connection early.
+TEST_F(ReliableQuicStreamTest,
+ StreamSequencerNeverSeesPacketsViolatingFlowControl) {
+ Initialize(kShouldProcessData);
+
+ // Receive a stream frame that violates flow control: the byte offset is
+ // higher than the receive window offset.
+ QuicStreamFrame frame(stream_->id(), false,
+ kInitialSessionFlowControlWindowForTest + 1,
+ MakeIOVector("."));
+ EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset(
+ stream_->flow_controller()));
+
+ // Stream should not accept the frame, and the connection should be closed.
+ EXPECT_CALL(*connection_,
+ SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA));
+ stream_->OnStreamFrame(frame);
+}
+
+TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromFin) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_no_fin(stream_->id(), false, 1234,
+ MakeIOVector("."));
+ stream_->OnStreamFrame(stream_frame_no_fin);
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+
+ QuicStreamFrame stream_frame_with_fin(stream_->id(), true, 1234,
+ MakeIOVector("."));
+ stream_->OnStreamFrame(stream_frame_with_fin);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+TEST_F(ReliableQuicStreamTest, FinalByteOffsetFromRst) {
+ Initialize(kShouldProcessData);
+
+ EXPECT_FALSE(stream_->HasFinalReceivedByteOffset());
+ QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_TRUE(stream_->HasFinalReceivedByteOffset());
+}
+
+} // namespace
+} // namespace test
+} // namespace net
diff --git a/net/quic/spdy_utils.cc b/net/quic/spdy_utils.cc
new file mode 100644
index 0000000..d863746
--- /dev/null
+++ b/net/quic/spdy_utils.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/spdy_utils.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+
+using std::string;
+
+namespace net {
+
+// static
+string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
+ int length = SpdyFramer::GetSerializedLength(kDefaultSpdyMajorVersion,
+ &headers);
+ SpdyFrameBuilder builder(length, kDefaultSpdyMajorVersion);
+ SpdyFramer::WriteHeaderBlock(&builder, kDefaultSpdyMajorVersion, &headers);
+ scoped_ptr<SpdyFrame> block(builder.take());
+ return string(block->data(), length);
+}
+
+} // namespace net
diff --git a/net/quic/spdy_utils.h b/net/quic/spdy_utils.h
new file mode 100644
index 0000000..efb72a0
--- /dev/null
+++ b/net/quic/spdy_utils.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_SPDY_UTILS_H_
+#define NET_QUIC_SPDY_UTILS_H_
+
+#include <string>
+
+#include "net/base/net_export.h"
+#include "net/spdy/spdy_framer.h"
+
+namespace net {
+
+const SpdyMajorVersion kDefaultSpdyMajorVersion = SPDY3;
+
+class NET_EXPORT_PRIVATE SpdyUtils {
+ public:
+ static std::string SerializeUncompressedHeaders(
+ const SpdyHeaderBlock& headers);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SpdyUtils);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_SPDY_UTILS_H_
diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc
new file mode 100644
index 0000000..cf8df9a
--- /dev/null
+++ b/net/quic/test_tools/crypto_test_utils.cc
@@ -0,0 +1,636 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/crypto_test_utils.h"
+
+#include "net/quic/crypto/channel_id.h"
+#include "net/quic/crypto/common_cert_set.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/crypto/quic_random.h"
+#include "net/quic/quic_clock.h"
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_crypto_server_stream.h"
+#include "net/quic/quic_crypto_stream.h"
+#include "net/quic/quic_server_id.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/simple_quic_framer.h"
+
+using base::StringPiece;
+using std::make_pair;
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+namespace {
+
+const char kServerHostname[] = "test.example.com";
+const uint16 kServerPort = 80;
+
+// CryptoFramerVisitor is a framer visitor that records handshake messages.
+class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
+ public:
+ CryptoFramerVisitor()
+ : error_(false) {
+ }
+
+ virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; }
+
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE {
+ messages_.push_back(message);
+ }
+
+ bool error() const {
+ return error_;
+ }
+
+ const vector<CryptoHandshakeMessage>& messages() const {
+ return messages_;
+ }
+
+ private:
+ bool error_;
+ vector<CryptoHandshakeMessage> messages_;
+};
+
+// MovePackets parses crypto handshake messages from packet number
+// |*inout_packet_index| through to the last packet (or until a packet fails to
+// decrypt) and has |dest_stream| process them. |*inout_packet_index| is updated
+// with an index one greater than the last packet processed.
+void MovePackets(PacketSavingConnection* source_conn,
+ size_t *inout_packet_index,
+ QuicCryptoStream* dest_stream,
+ PacketSavingConnection* dest_conn) {
+ SimpleQuicFramer framer(source_conn->supported_versions());
+ CryptoFramer crypto_framer;
+ CryptoFramerVisitor crypto_visitor;
+
+ // In order to properly test the code we need to perform encryption and
+ // decryption so that the crypters latch when expected. The crypters are in
+ // |dest_conn|, but we don't want to try and use them there. Instead we swap
+ // them into |framer|, perform the decryption with them, and then swap them
+ // back.
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+
+ crypto_framer.set_visitor(&crypto_visitor);
+
+ size_t index = *inout_packet_index;
+ for (; index < source_conn->encrypted_packets_.size(); index++) {
+ if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) {
+ // The framer will be unable to decrypt forward-secure packets sent after
+ // the handshake is complete. Don't treat them as handshake packets.
+ break;
+ }
+
+ for (vector<QuicStreamFrame>::const_iterator
+ i = framer.stream_frames().begin();
+ i != framer.stream_frames().end(); ++i) {
+ scoped_ptr<string> frame_data(i->GetDataAsString());
+ ASSERT_TRUE(crypto_framer.ProcessInput(*frame_data));
+ ASSERT_FALSE(crypto_visitor.error());
+ }
+ }
+ *inout_packet_index = index;
+
+ QuicConnectionPeer::SwapCrypters(dest_conn, framer.framer());
+
+ ASSERT_EQ(0u, crypto_framer.InputBytesRemaining());
+
+ for (vector<CryptoHandshakeMessage>::const_iterator
+ i = crypto_visitor.messages().begin();
+ i != crypto_visitor.messages().end(); ++i) {
+ dest_stream->OnHandshakeMessage(*i);
+ }
+}
+
+// HexChar parses |c| as a hex character. If valid, it sets |*value| to the
+// value of the hex character and returns true. Otherwise it returns false.
+bool HexChar(char c, uint8* value) {
+ if (c >= '0' && c <= '9') {
+ *value = c - '0';
+ return true;
+ }
+ if (c >= 'a' && c <= 'f') {
+ *value = c - 'a' + 10;
+ return true;
+ }
+ if (c >= 'A' && c <= 'F') {
+ *value = c - 'A' + 10;
+ return true;
+ }
+ return false;
+}
+
+// A ChannelIDSource that works in asynchronous mode unless the |callback|
+// argument to GetChannelIDKey is nullptr.
+class AsyncTestChannelIDSource : public ChannelIDSource,
+ public CryptoTestUtils::CallbackSource {
+ public:
+ // Takes ownership of |sync_source|, a synchronous ChannelIDSource.
+ explicit AsyncTestChannelIDSource(ChannelIDSource* sync_source)
+ : sync_source_(sync_source) {}
+ virtual ~AsyncTestChannelIDSource() {}
+
+ // ChannelIDSource implementation.
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* callback) OVERRIDE {
+ // Synchronous mode.
+ if (!callback) {
+ return sync_source_->GetChannelIDKey(hostname, channel_id_key, nullptr);
+ }
+
+ // Asynchronous mode.
+ QuicAsyncStatus status =
+ sync_source_->GetChannelIDKey(hostname, &channel_id_key_, nullptr);
+ if (status != QUIC_SUCCESS) {
+ return QUIC_FAILURE;
+ }
+ callback_.reset(callback);
+ return QUIC_PENDING;
+ }
+
+ // CallbackSource implementation.
+ virtual void RunPendingCallbacks() OVERRIDE {
+ if (callback_.get()) {
+ callback_->Run(&channel_id_key_);
+ callback_.reset();
+ }
+ }
+
+ private:
+ scoped_ptr<ChannelIDSource> sync_source_;
+ scoped_ptr<ChannelIDSourceCallback> callback_;
+ scoped_ptr<ChannelIDKey> channel_id_key_;
+};
+
+} // anonymous namespace
+
+CryptoTestUtils::FakeClientOptions::FakeClientOptions()
+ : dont_verify_certs(false),
+ channel_id_enabled(false),
+ channel_id_source_async(false) {
+}
+
+// static
+int CryptoTestUtils::HandshakeWithFakeServer(
+ PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client) {
+ PacketSavingConnection* server_conn =
+ new PacketSavingConnection(true, client_conn->supported_versions());
+ TestSession server_session(server_conn, DefaultQuicConfig());
+ server_session.InitializeSession();
+ QuicCryptoServerConfig crypto_config(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance());
+
+ SetupCryptoServerConfigForTest(
+ server_session.connection()->clock(),
+ server_session.connection()->random_generator(),
+ server_session.config(), &crypto_config);
+
+ QuicCryptoServerStream server(crypto_config, &server_session);
+ server_session.SetCryptoStream(&server);
+
+ // The client's handshake must have been started already.
+ CHECK_NE(0u, client_conn->packets_.size());
+
+ CommunicateHandshakeMessages(client_conn, client, server_conn, &server);
+
+ CompareClientAndServerKeys(client, &server);
+
+ return client->num_sent_client_hellos();
+}
+
+// static
+int CryptoTestUtils::HandshakeWithFakeClient(
+ PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const FakeClientOptions& options) {
+ PacketSavingConnection* client_conn = new PacketSavingConnection(false);
+ TestClientSession client_session(client_conn, DefaultQuicConfig());
+ QuicCryptoClientConfig crypto_config;
+
+ client_session.config()->SetDefaults();
+ crypto_config.SetDefaults();
+ if (!options.dont_verify_certs) {
+ // TODO(wtc): replace this with ProofVerifierForTesting() when we have
+ // a working ProofSourceForTesting().
+ crypto_config.SetProofVerifier(FakeProofVerifierForTesting());
+ }
+ bool is_https = false;
+ AsyncTestChannelIDSource* async_channel_id_source = nullptr;
+ if (options.channel_id_enabled) {
+ is_https = true;
+
+ ChannelIDSource* source = ChannelIDSourceForTesting();
+ if (options.channel_id_source_async) {
+ async_channel_id_source = new AsyncTestChannelIDSource(source);
+ source = async_channel_id_source;
+ }
+ crypto_config.SetChannelIDSource(source);
+ }
+ QuicServerId server_id(kServerHostname, kServerPort, is_https,
+ PRIVACY_MODE_DISABLED);
+ QuicCryptoClientStream client(server_id, &client_session,
+ ProofVerifyContextForTesting(),
+ &crypto_config);
+ client_session.SetCryptoStream(&client);
+
+ CHECK(client.CryptoConnect());
+ CHECK_EQ(1u, client_conn->packets_.size());
+
+ CommunicateHandshakeMessagesAndRunCallbacks(
+ client_conn, &client, server_conn, server, async_channel_id_source);
+
+ CompareClientAndServerKeys(&client, server);
+
+ if (options.channel_id_enabled) {
+ scoped_ptr<ChannelIDKey> channel_id_key;
+ QuicAsyncStatus status = crypto_config.channel_id_source()->GetChannelIDKey(
+ kServerHostname, &channel_id_key, nullptr);
+ EXPECT_EQ(QUIC_SUCCESS, status);
+ EXPECT_EQ(channel_id_key->SerializeKey(),
+ server->crypto_negotiated_params().channel_id);
+ EXPECT_EQ(options.channel_id_source_async,
+ client.WasChannelIDSourceCallbackRun());
+ }
+
+ return client.num_sent_client_hellos();
+}
+
+// static
+void CryptoTestUtils::SetupCryptoServerConfigForTest(
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicConfig* config,
+ QuicCryptoServerConfig* crypto_config) {
+ config->SetDefaults();
+ QuicCryptoServerConfig::ConfigOptions options;
+ options.channel_id_enabled = true;
+ scoped_ptr<CryptoHandshakeMessage> scfg(
+ crypto_config->AddDefaultConfig(rand, clock, options));
+}
+
+// static
+void CryptoTestUtils::CommunicateHandshakeMessages(
+ PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b) {
+ CommunicateHandshakeMessagesAndRunCallbacks(a_conn, a, b_conn, b, nullptr);
+}
+
+// static
+void CryptoTestUtils::CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b,
+ CallbackSource* callback_source) {
+ size_t a_i = 0, b_i = 0;
+ while (!a->handshake_confirmed()) {
+ ASSERT_GT(a_conn->packets_.size(), a_i);
+ LOG(INFO) << "Processing " << a_conn->packets_.size() - a_i
+ << " packets a->b";
+ MovePackets(a_conn, &a_i, b, b_conn);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+
+ ASSERT_GT(b_conn->packets_.size(), b_i);
+ LOG(INFO) << "Processing " << b_conn->packets_.size() - b_i
+ << " packets b->a";
+ MovePackets(b_conn, &b_i, a, a_conn);
+ if (callback_source) {
+ callback_source->RunPendingCallbacks();
+ }
+ }
+}
+
+// static
+pair<size_t, size_t> CryptoTestUtils::AdvanceHandshake(
+ PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ size_t a_i,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b,
+ size_t b_i) {
+ LOG(INFO) << "Processing " << a_conn->packets_.size() - a_i
+ << " packets a->b";
+ MovePackets(a_conn, &a_i, b, b_conn);
+
+ LOG(INFO) << "Processing " << b_conn->packets_.size() - b_i
+ << " packets b->a";
+ if (b_conn->packets_.size() - b_i == 2) {
+ LOG(INFO) << "here";
+ }
+ MovePackets(b_conn, &b_i, a, a_conn);
+
+ return make_pair(a_i, b_i);
+}
+
+// static
+string CryptoTestUtils::GetValueForTag(const CryptoHandshakeMessage& message,
+ QuicTag tag) {
+ QuicTagValueMap::const_iterator it = message.tag_value_map().find(tag);
+ if (it == message.tag_value_map().end()) {
+ return string();
+ }
+ return it->second;
+}
+
+class MockCommonCertSets : public CommonCertSets {
+ public:
+ MockCommonCertSets(StringPiece cert, uint64 hash, uint32 index)
+ : cert_(cert.as_string()),
+ hash_(hash),
+ index_(index) {
+ }
+
+ virtual StringPiece GetCommonHashes() const OVERRIDE {
+ CHECK(false) << "not implemented";
+ return StringPiece();
+ }
+
+ virtual StringPiece GetCert(uint64 hash, uint32 index) const OVERRIDE {
+ if (hash == hash_ && index == index_) {
+ return cert_;
+ }
+ return StringPiece();
+ }
+
+ virtual bool MatchCert(StringPiece cert,
+ StringPiece common_set_hashes,
+ uint64* out_hash,
+ uint32* out_index) const OVERRIDE {
+ if (cert != cert_) {
+ return false;
+ }
+
+ if (common_set_hashes.size() % sizeof(uint64) != 0) {
+ return false;
+ }
+ bool client_has_set = false;
+ for (size_t i = 0; i < common_set_hashes.size(); i += sizeof(uint64)) {
+ uint64 hash;
+ memcpy(&hash, common_set_hashes.data() + i, sizeof(hash));
+ if (hash == hash_) {
+ client_has_set = true;
+ break;
+ }
+ }
+
+ if (!client_has_set) {
+ return false;
+ }
+
+ *out_hash = hash_;
+ *out_index = index_;
+ return true;
+ }
+
+ private:
+ const string cert_;
+ const uint64 hash_;
+ const uint32 index_;
+};
+
+CommonCertSets* CryptoTestUtils::MockCommonCertSets(StringPiece cert,
+ uint64 hash,
+ uint32 index) {
+ return new class MockCommonCertSets(cert, hash, index);
+}
+
+void CryptoTestUtils::CompareClientAndServerKeys(
+ QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server) {
+ const QuicEncrypter* client_encrypter(
+ client->session()->connection()->encrypter(ENCRYPTION_INITIAL));
+ const QuicDecrypter* client_decrypter(
+ client->session()->connection()->decrypter());
+ const QuicEncrypter* client_forward_secure_encrypter(
+ client->session()->connection()->encrypter(ENCRYPTION_FORWARD_SECURE));
+ const QuicDecrypter* client_forward_secure_decrypter(
+ client->session()->connection()->alternative_decrypter());
+ const QuicEncrypter* server_encrypter(
+ server->session()->connection()->encrypter(ENCRYPTION_INITIAL));
+ const QuicDecrypter* server_decrypter(
+ server->session()->connection()->decrypter());
+ const QuicEncrypter* server_forward_secure_encrypter(
+ server->session()->connection()->encrypter(ENCRYPTION_FORWARD_SECURE));
+ const QuicDecrypter* server_forward_secure_decrypter(
+ server->session()->connection()->alternative_decrypter());
+
+ StringPiece client_encrypter_key = client_encrypter->GetKey();
+ StringPiece client_encrypter_iv = client_encrypter->GetNoncePrefix();
+ StringPiece client_decrypter_key = client_decrypter->GetKey();
+ StringPiece client_decrypter_iv = client_decrypter->GetNoncePrefix();
+ StringPiece client_forward_secure_encrypter_key =
+ client_forward_secure_encrypter->GetKey();
+ StringPiece client_forward_secure_encrypter_iv =
+ client_forward_secure_encrypter->GetNoncePrefix();
+ StringPiece client_forward_secure_decrypter_key =
+ client_forward_secure_decrypter->GetKey();
+ StringPiece client_forward_secure_decrypter_iv =
+ client_forward_secure_decrypter->GetNoncePrefix();
+ StringPiece server_encrypter_key = server_encrypter->GetKey();
+ StringPiece server_encrypter_iv = server_encrypter->GetNoncePrefix();
+ StringPiece server_decrypter_key = server_decrypter->GetKey();
+ StringPiece server_decrypter_iv = server_decrypter->GetNoncePrefix();
+ StringPiece server_forward_secure_encrypter_key =
+ server_forward_secure_encrypter->GetKey();
+ StringPiece server_forward_secure_encrypter_iv =
+ server_forward_secure_encrypter->GetNoncePrefix();
+ StringPiece server_forward_secure_decrypter_key =
+ server_forward_secure_decrypter->GetKey();
+ StringPiece server_forward_secure_decrypter_iv =
+ server_forward_secure_decrypter->GetNoncePrefix();
+
+ StringPiece client_subkey_secret =
+ client->crypto_negotiated_params().subkey_secret;
+ StringPiece server_subkey_secret =
+ server->crypto_negotiated_params().subkey_secret;
+
+
+ const char kSampleLabel[] = "label";
+ const char kSampleContext[] = "context";
+ const size_t kSampleOutputLength = 32;
+ string client_key_extraction;
+ string server_key_extraction;
+ EXPECT_TRUE(client->ExportKeyingMaterial(kSampleLabel,
+ kSampleContext,
+ kSampleOutputLength,
+ &client_key_extraction));
+ EXPECT_TRUE(server->ExportKeyingMaterial(kSampleLabel,
+ kSampleContext,
+ kSampleOutputLength,
+ &server_key_extraction));
+
+ CompareCharArraysWithHexError("client write key",
+ client_encrypter_key.data(),
+ client_encrypter_key.length(),
+ server_decrypter_key.data(),
+ server_decrypter_key.length());
+ CompareCharArraysWithHexError("client write IV",
+ client_encrypter_iv.data(),
+ client_encrypter_iv.length(),
+ server_decrypter_iv.data(),
+ server_decrypter_iv.length());
+ CompareCharArraysWithHexError("server write key",
+ server_encrypter_key.data(),
+ server_encrypter_key.length(),
+ client_decrypter_key.data(),
+ client_decrypter_key.length());
+ CompareCharArraysWithHexError("server write IV",
+ server_encrypter_iv.data(),
+ server_encrypter_iv.length(),
+ client_decrypter_iv.data(),
+ client_decrypter_iv.length());
+ CompareCharArraysWithHexError("client forward secure write key",
+ client_forward_secure_encrypter_key.data(),
+ client_forward_secure_encrypter_key.length(),
+ server_forward_secure_decrypter_key.data(),
+ server_forward_secure_decrypter_key.length());
+ CompareCharArraysWithHexError("client forward secure write IV",
+ client_forward_secure_encrypter_iv.data(),
+ client_forward_secure_encrypter_iv.length(),
+ server_forward_secure_decrypter_iv.data(),
+ server_forward_secure_decrypter_iv.length());
+ CompareCharArraysWithHexError("server forward secure write key",
+ server_forward_secure_encrypter_key.data(),
+ server_forward_secure_encrypter_key.length(),
+ client_forward_secure_decrypter_key.data(),
+ client_forward_secure_decrypter_key.length());
+ CompareCharArraysWithHexError("server forward secure write IV",
+ server_forward_secure_encrypter_iv.data(),
+ server_forward_secure_encrypter_iv.length(),
+ client_forward_secure_decrypter_iv.data(),
+ client_forward_secure_decrypter_iv.length());
+ CompareCharArraysWithHexError("subkey secret",
+ client_subkey_secret.data(),
+ client_subkey_secret.length(),
+ server_subkey_secret.data(),
+ server_subkey_secret.length());
+ CompareCharArraysWithHexError("sample key extraction",
+ client_key_extraction.data(),
+ client_key_extraction.length(),
+ server_key_extraction.data(),
+ server_key_extraction.length());
+}
+
+// static
+QuicTag CryptoTestUtils::ParseTag(const char* tagstr) {
+ const size_t len = strlen(tagstr);
+ CHECK_NE(0u, len);
+
+ QuicTag tag = 0;
+
+ if (tagstr[0] == '#') {
+ CHECK_EQ(static_cast<size_t>(1 + 2*4), len);
+ tagstr++;
+
+ for (size_t i = 0; i < 8; i++) {
+ tag <<= 4;
+
+ uint8 v = 0;
+ CHECK(HexChar(tagstr[i], &v));
+ tag |= v;
+ }
+
+ return tag;
+ }
+
+ CHECK_LE(len, 4u);
+ for (size_t i = 0; i < 4; i++) {
+ tag >>= 8;
+ if (i < len) {
+ tag |= static_cast<uint32>(tagstr[i]) << 24;
+ }
+ }
+
+ return tag;
+}
+
+// static
+CryptoHandshakeMessage CryptoTestUtils::Message(const char* message_tag, ...) {
+ va_list ap;
+ va_start(ap, message_tag);
+
+ CryptoHandshakeMessage message = BuildMessage(message_tag, ap);
+ va_end(ap);
+ return message;
+}
+
+// static
+CryptoHandshakeMessage CryptoTestUtils::BuildMessage(const char* message_tag,
+ va_list ap) {
+ CryptoHandshakeMessage msg;
+ msg.set_tag(ParseTag(message_tag));
+
+ for (;;) {
+ const char* tagstr = va_arg(ap, const char*);
+ if (tagstr == nullptr) {
+ break;
+ }
+
+ if (tagstr[0] == '$') {
+ // Special value.
+ const char* const special = tagstr + 1;
+ if (strcmp(special, "padding") == 0) {
+ const int min_bytes = va_arg(ap, int);
+ msg.set_minimum_size(min_bytes);
+ } else {
+ CHECK(false) << "Unknown special value: " << special;
+ }
+
+ continue;
+ }
+
+ const QuicTag tag = ParseTag(tagstr);
+ const char* valuestr = va_arg(ap, const char*);
+
+ size_t len = strlen(valuestr);
+ if (len > 0 && valuestr[0] == '#') {
+ valuestr++;
+ len--;
+
+ CHECK_EQ(0u, len % 2);
+ scoped_ptr<uint8[]> buf(new uint8[len/2]);
+
+ for (size_t i = 0; i < len/2; i++) {
+ uint8 v = 0;
+ CHECK(HexChar(valuestr[i*2], &v));
+ buf[i] = v << 4;
+ CHECK(HexChar(valuestr[i*2 + 1], &v));
+ buf[i] |= v;
+ }
+
+ msg.SetStringPiece(
+ tag, StringPiece(reinterpret_cast<char*>(buf.get()), len/2));
+ continue;
+ }
+
+ msg.SetStringPiece(tag, valuestr);
+ }
+
+ // The CryptoHandshakeMessage needs to be serialized and parsed to ensure
+ // that any padding is included.
+ scoped_ptr<QuicData> bytes(CryptoFramer::ConstructHandshakeMessage(msg));
+ scoped_ptr<CryptoHandshakeMessage> parsed(
+ CryptoFramer::ParseMessage(bytes->AsStringPiece()));
+ CHECK(parsed.get());
+
+ return *parsed;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/crypto_test_utils.h b/net/quic/test_tools/crypto_test_utils.h
new file mode 100644
index 0000000..c72b904
--- /dev/null
+++ b/net/quic/test_tools/crypto_test_utils.h
@@ -0,0 +1,191 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
+#define NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
+
+#include <stdarg.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class ChannelIDSource;
+class CommonCertSets;
+class ProofSource;
+class ProofVerifier;
+class ProofVerifyContext;
+class QuicClock;
+class QuicConfig;
+class QuicCryptoClientStream;
+class QuicCryptoServerConfig;
+class QuicCryptoServerStream;
+class QuicCryptoStream;
+class QuicRandom;
+
+namespace test {
+
+class PacketSavingConnection;
+
+class CryptoTestUtils {
+ public:
+ // An interface for a source of callbacks. This is used for invoking
+ // callbacks asynchronously.
+ //
+ // Call the RunPendingCallbacks method regularly to run the callbacks from
+ // this source.
+ class CallbackSource {
+ public:
+ virtual ~CallbackSource() {}
+
+ // Runs pending callbacks from this source. If there is no pending
+ // callback, does nothing.
+ virtual void RunPendingCallbacks() = 0;
+ };
+
+ // FakeClientOptions bundles together a number of options for configuring
+ // HandshakeWithFakeClient.
+ struct FakeClientOptions {
+ FakeClientOptions();
+
+ // If dont_verify_certs is true then no ProofVerifier is set on the client.
+ // Thus no certificates will be requested or checked.
+ bool dont_verify_certs;
+
+ // If channel_id_enabled is true then the client will attempt to send a
+ // ChannelID.
+ bool channel_id_enabled;
+
+ // If channel_id_source_async is true then the client will use an async
+ // ChannelIDSource for testing. Ignored if channel_id_enabled is false.
+ bool channel_id_source_async;
+ };
+
+ // returns: the number of client hellos that the client sent.
+ static int HandshakeWithFakeServer(PacketSavingConnection* client_conn,
+ QuicCryptoClientStream* client);
+
+ // returns: the number of client hellos that the client sent.
+ static int HandshakeWithFakeClient(PacketSavingConnection* server_conn,
+ QuicCryptoServerStream* server,
+ const FakeClientOptions& options);
+
+ // SetupCryptoServerConfigForTest configures |config| and |crypto_config|
+ // with sensible defaults for testing.
+ static void SetupCryptoServerConfigForTest(
+ const QuicClock* clock,
+ QuicRandom* rand,
+ QuicConfig* config,
+ QuicCryptoServerConfig* crypto_config);
+
+ // CommunicateHandshakeMessages moves messages from |a| to |b| and back until
+ // |a|'s handshake has completed.
+ static void CommunicateHandshakeMessages(PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b);
+
+ // CommunicateHandshakeMessagesAndRunCallbacks moves messages from |a| to |b|
+ // and back until |a|'s handshake has completed. If |callback_source| is not
+ // nullptr, CommunicateHandshakeMessagesAndRunCallbacks also runs callbacks
+ // from
+ // |callback_source| between processing messages.
+ static void CommunicateHandshakeMessagesAndRunCallbacks(
+ PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b,
+ CallbackSource* callback_source);
+
+ // AdvanceHandshake attempts to moves messages from |a| to |b| and |b| to |a|.
+ // Returns the number of messages moved.
+ static std::pair<size_t, size_t> AdvanceHandshake(
+ PacketSavingConnection* a_conn,
+ QuicCryptoStream* a,
+ size_t a_i,
+ PacketSavingConnection* b_conn,
+ QuicCryptoStream* b,
+ size_t b_i);
+
+ // Returns the value for the tag |tag| in the tag value map of |message|.
+ static std::string GetValueForTag(const CryptoHandshakeMessage& message,
+ QuicTag tag);
+
+ // Returns a |ProofSource| that serves up test certificates.
+ static ProofSource* ProofSourceForTesting();
+
+ // Returns a |ProofVerifier| that uses the QUIC testing root CA.
+ static ProofVerifier* ProofVerifierForTesting();
+
+ // Returns a |ProofVerifyContext| that must be used with the verifier
+ // returned by |ProofVerifierForTesting|.
+ static ProofVerifyContext* ProofVerifyContextForTesting();
+
+ // These functions return a fake |ProofSource|, |ProofVerifier|, or
+ // |ProofVerifyContext| that works with each other. These are suitable for
+ // unit tests that aren't concerned with |ProofSource| and |ProofVerifier|.
+ // TODO(wtc): delete these when Chromium has a working
+ // ProofSourceForTesting().
+ static ProofSource* FakeProofSourceForTesting();
+ static ProofVerifier* FakeProofVerifierForTesting();
+ static ProofVerifyContext* FakeProofVerifyContextForTesting();
+
+ // MockCommonCertSets returns a CommonCertSets that contains a single set with
+ // hash |hash|, consisting of the certificate |cert| at index |index|.
+ static CommonCertSets* MockCommonCertSets(base::StringPiece cert,
+ uint64 hash,
+ uint32 index);
+
+ // ParseTag returns a QuicTag from parsing |tagstr|. |tagstr| may either be
+ // in the format "EXMP" (i.e. ASCII format), or "#11223344" (an explicit hex
+ // format). It CHECK fails if there's a parse error.
+ static QuicTag ParseTag(const char* tagstr);
+
+ // Message constructs a handshake message from a variable number of
+ // arguments. |message_tag| is passed to |ParseTag| and used as the tag of
+ // the resulting message. The arguments are taken in pairs and nullptr
+ // terminated. The first of each pair is the tag of a tag/value and is given
+ // as an argument to |ParseTag|. The second is the value of the tag/value
+ // pair and is either a hex dump, preceeded by a '#', or a raw value.
+ //
+ // Message(
+ // "CHLO",
+ // "NOCE", "#11223344",
+ // "SNI", "www.example.com",
+ // nullptr);
+ static CryptoHandshakeMessage Message(const char* message_tag, ...);
+
+ // BuildMessage is the same as |Message|, but takes the variable arguments
+ // explicitly. TODO(rtenneti): Investigate whether it'd be better for
+ // Message() and BuildMessage() to return a CryptoHandshakeMessage* pointer
+ // instead, to avoid copying the return value.
+ static CryptoHandshakeMessage BuildMessage(const char* message_tag,
+ va_list ap);
+
+ // ChannelIDSourceForTesting returns a ChannelIDSource that generates keys
+ // deterministically based on the hostname given in the GetChannelIDKey call.
+ // This ChannelIDSource works in synchronous mode, i.e., its GetChannelIDKey
+ // method never returns QUIC_PENDING.
+ static ChannelIDSource* ChannelIDSourceForTesting();
+
+ private:
+ static void CompareClientAndServerKeys(QuicCryptoClientStream* client,
+ QuicCryptoServerStream* server);
+
+ DISALLOW_COPY_AND_ASSIGN(CryptoTestUtils);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_
diff --git a/net/quic/test_tools/crypto_test_utils_chromium.cc b/net/quic/test_tools/crypto_test_utils_chromium.cc
new file mode 100644
index 0000000..e475e56
--- /dev/null
+++ b/net/quic/test_tools/crypto_test_utils_chromium.cc
@@ -0,0 +1,145 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/crypto_test_utils.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/base/test_data_directory.h"
+#include "net/cert/cert_verifier.h"
+#include "net/cert/test_root_certs.h"
+#include "net/cert/x509_certificate.h"
+#include "net/http/transport_security_state.h"
+#include "net/quic/crypto/proof_source_chromium.h"
+#include "net/quic/crypto/proof_verifier_chromium.h"
+#include "net/test/cert_test_util.h"
+
+namespace net {
+
+namespace test {
+
+namespace {
+
+class TestProofVerifierChromium : public ProofVerifierChromium {
+ public:
+ TestProofVerifierChromium(CertVerifier* cert_verifier,
+ TransportSecurityState* transport_security_state,
+ const std::string& cert_file)
+ : ProofVerifierChromium(cert_verifier, transport_security_state),
+ cert_verifier_(cert_verifier),
+ transport_security_state_(transport_security_state) {
+ // Load and install the root for the validated chain.
+ scoped_refptr<X509Certificate> root_cert =
+ ImportCertFromFile(GetTestCertsDirectory(), cert_file);
+ scoped_root_.Reset(root_cert.get());
+ }
+ virtual ~TestProofVerifierChromium() {}
+
+ private:
+ ScopedTestRoot scoped_root_;
+ scoped_ptr<CertVerifier> cert_verifier_;
+ scoped_ptr<TransportSecurityState> transport_security_state_;
+};
+
+const char kLeafCert[] = "leaf";
+const char kIntermediateCert[] = "intermediate";
+const char kSignature[] = "signature";
+
+class FakeProofSource : public ProofSource {
+ public:
+ FakeProofSource() : certs_(2) {
+ certs_[0] = kLeafCert;
+ certs_[1] = kIntermediateCert;
+ }
+ virtual ~FakeProofSource() {}
+
+ // ProofSource interface
+ virtual bool GetProof(const std::string& hostname,
+ const std::string& server_config,
+ bool ecdsa_ok,
+ const std::vector<std::string>** out_certs,
+ std::string* out_signature) OVERRIDE {
+ *out_certs = &certs_;
+ *out_signature = kSignature;
+ return true;
+ }
+
+ private:
+ std::vector<std::string> certs_;
+ DISALLOW_COPY_AND_ASSIGN(FakeProofSource);
+};
+
+class FakeProofVerifier : public ProofVerifier {
+ public:
+ FakeProofVerifier() {}
+ virtual ~FakeProofVerifier() {}
+
+ // ProofVerifier interface
+ virtual QuicAsyncStatus VerifyProof(
+ const std::string& hostname,
+ const std::string& server_config,
+ const std::vector<std::string>& certs,
+ const std::string& signature,
+ const ProofVerifyContext* verify_context,
+ std::string* error_details,
+ scoped_ptr<ProofVerifyDetails>* verify_details,
+ ProofVerifierCallback* callback) OVERRIDE {
+ error_details->clear();
+ scoped_ptr<ProofVerifyDetailsChromium> verify_details_chromium(
+ new ProofVerifyDetailsChromium);
+ if (certs.size() != 2 || certs[0] != kLeafCert ||
+ certs[1] != kIntermediateCert || signature != kSignature) {
+ *error_details = "Invalid proof";
+ verify_details_chromium->cert_verify_result.cert_status =
+ CERT_STATUS_INVALID;
+ *verify_details = verify_details_chromium.Pass();
+ return QUIC_FAILURE;
+ }
+ *verify_details = verify_details_chromium.Pass();
+ return QUIC_SUCCESS;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FakeProofVerifier);
+};
+
+} // namespace
+
+// static
+ProofSource* CryptoTestUtils::ProofSourceForTesting() {
+ return new ProofSourceChromium();
+}
+
+// static
+ProofVerifier* CryptoTestUtils::ProofVerifierForTesting() {
+ TestProofVerifierChromium* proof_verifier =
+ new TestProofVerifierChromium(CertVerifier::CreateDefault(),
+ new TransportSecurityState,
+ "quic_root.crt");
+ return proof_verifier;
+}
+
+// static
+ProofVerifyContext* CryptoTestUtils::ProofVerifyContextForTesting() {
+ return new ProofVerifyContextChromium(BoundNetLog());
+}
+
+// static
+ProofSource* CryptoTestUtils::FakeProofSourceForTesting() {
+ return new FakeProofSource();
+}
+
+// static
+ProofVerifier* CryptoTestUtils::FakeProofVerifierForTesting() {
+ return new FakeProofVerifier();
+}
+
+// static
+ProofVerifyContext* CryptoTestUtils::FakeProofVerifyContextForTesting() {
+ return nullptr;
+}
+
+} // namespace test
+
+} // namespace net
diff --git a/net/quic/test_tools/crypto_test_utils_nss.cc b/net/quic/test_tools/crypto_test_utils_nss.cc
new file mode 100644
index 0000000..eaf0ecd
--- /dev/null
+++ b/net/quic/test_tools/crypto_test_utils_nss.cc
@@ -0,0 +1,64 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/crypto_test_utils.h"
+
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "crypto/ec_private_key.h"
+#include "crypto/ec_signature_creator.h"
+#include "net/quic/crypto/channel_id.h"
+#include "net/quic/crypto/channel_id_chromium.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+namespace test {
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ virtual ~TestChannelIDSource() {
+ STLDeleteValues(&hostname_to_key_);
+ }
+
+ // ChannelIDSource implementation.
+
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) OVERRIDE {
+ channel_id_key->reset(new ChannelIDKeyChromium(HostnameToKey(hostname)));
+ return QUIC_SUCCESS;
+ }
+
+ private:
+ typedef std::map<string, crypto::ECPrivateKey*> HostnameToKeyMap;
+
+ crypto::ECPrivateKey* HostnameToKey(const string& hostname) {
+ HostnameToKeyMap::const_iterator it = hostname_to_key_.find(hostname);
+ if (it != hostname_to_key_.end()) {
+ return it->second->Copy();
+ }
+
+ crypto::ECPrivateKey* keypair = crypto::ECPrivateKey::Create();
+ if (!keypair) {
+ return nullptr;
+ }
+ hostname_to_key_[hostname] = keypair;
+ return keypair->Copy();
+ }
+
+ HostnameToKeyMap hostname_to_key_;
+};
+
+// static
+ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
+}
+
+} // namespace test
+
+} // namespace net
diff --git a/net/quic/test_tools/crypto_test_utils_openssl.cc b/net/quic/test_tools/crypto_test_utils_openssl.cc
new file mode 100644
index 0000000..15babd4
--- /dev/null
+++ b/net/quic/test_tools/crypto_test_utils_openssl.cc
@@ -0,0 +1,167 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/crypto_test_utils.h"
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+#include <openssl/sha.h>
+
+#include "crypto/openssl_util.h"
+#include "crypto/scoped_openssl_types.h"
+#include "crypto/secure_hash.h"
+#include "net/quic/crypto/channel_id.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+namespace test {
+
+class TestChannelIDKey : public ChannelIDKey {
+ public:
+ explicit TestChannelIDKey(EVP_PKEY* ecdsa_key) : ecdsa_key_(ecdsa_key) {}
+ virtual ~TestChannelIDKey() OVERRIDE {}
+
+ // ChannelIDKey implementation.
+
+ virtual bool Sign(StringPiece signed_data,
+ string* out_signature) const OVERRIDE {
+ crypto::ScopedEVP_MD_CTX md_ctx(EVP_MD_CTX_create());
+ if (!md_ctx ||
+ EVP_DigestSignInit(md_ctx.get(), nullptr, EVP_sha256(), nullptr,
+ ecdsa_key_.get()) != 1) {
+ return false;
+ }
+
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kContextStr,
+ strlen(ChannelIDVerifier::kContextStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), ChannelIDVerifier::kClientToServerStr,
+ strlen(ChannelIDVerifier::kClientToServerStr) + 1);
+ EVP_DigestUpdate(md_ctx.get(), signed_data.data(), signed_data.size());
+
+ size_t sig_len;
+ if (!EVP_DigestSignFinal(md_ctx.get(), nullptr, &sig_len)) {
+ return false;
+ }
+
+ scoped_ptr<uint8[]> der_sig(new uint8[sig_len]);
+ if (!EVP_DigestSignFinal(md_ctx.get(), der_sig.get(), &sig_len)) {
+ return false;
+ }
+
+ uint8* derp = der_sig.get();
+ crypto::ScopedECDSA_SIG sig(
+ d2i_ECDSA_SIG(nullptr, const_cast<const uint8**>(&derp), sig_len));
+ if (sig.get() == nullptr) {
+ return false;
+ }
+
+ // The signature consists of a pair of 32-byte numbers.
+ static const size_t kSignatureLength = 32 * 2;
+ scoped_ptr<uint8[]> signature(new uint8[kSignatureLength]);
+ memset(signature.get(), 0, kSignatureLength);
+ BN_bn2bin(sig.get()->r, signature.get() + 32 - BN_num_bytes(sig.get()->r));
+ BN_bn2bin(sig.get()->s, signature.get() + 64 - BN_num_bytes(sig.get()->s));
+
+ *out_signature = string(reinterpret_cast<char*>(signature.get()),
+ kSignatureLength);
+
+ return true;
+ }
+
+ virtual string SerializeKey() const OVERRIDE {
+ // i2d_PublicKey will produce an ANSI X9.62 public key which, for a P-256
+ // key, is 0x04 (meaning uncompressed) followed by the x and y field
+ // elements as 32-byte, big-endian numbers.
+ static const int kExpectedKeyLength = 65;
+
+ int len = i2d_PublicKey(ecdsa_key_.get(), nullptr);
+ if (len != kExpectedKeyLength) {
+ return "";
+ }
+
+ uint8 buf[kExpectedKeyLength];
+ uint8* derp = buf;
+ i2d_PublicKey(ecdsa_key_.get(), &derp);
+
+ return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1);
+ }
+
+ private:
+ crypto::ScopedEVP_PKEY ecdsa_key_;
+};
+
+class TestChannelIDSource : public ChannelIDSource {
+ public:
+ virtual ~TestChannelIDSource() {}
+
+ // ChannelIDSource implementation.
+
+ virtual QuicAsyncStatus GetChannelIDKey(
+ const string& hostname,
+ scoped_ptr<ChannelIDKey>* channel_id_key,
+ ChannelIDSourceCallback* /*callback*/) OVERRIDE {
+ channel_id_key->reset(new TestChannelIDKey(HostnameToKey(hostname)));
+ return QUIC_SUCCESS;
+ }
+
+ private:
+ static EVP_PKEY* HostnameToKey(const string& hostname) {
+ // In order to generate a deterministic key for a given hostname the
+ // hostname is hashed with SHA-256 and the resulting digest is treated as a
+ // big-endian number. The most-significant bit is cleared to ensure that
+ // the resulting value is less than the order of the group and then it's
+ // taken as a private key. Given the private key, the public key is
+ // calculated with a group multiplication.
+ SHA256_CTX sha256;
+ SHA256_Init(&sha256);
+ SHA256_Update(&sha256, hostname.data(), hostname.size());
+
+ unsigned char digest[SHA256_DIGEST_LENGTH];
+ SHA256_Final(digest, &sha256);
+
+ // Ensure that the digest is less than the order of the P-256 group by
+ // clearing the most-significant bit.
+ digest[0] &= 0x7f;
+
+ crypto::ScopedBIGNUM k(BN_new());
+ CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != nullptr);
+
+ crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free>::Type p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ CHECK(p256.get());
+
+ crypto::ScopedEC_KEY ecdsa_key(EC_KEY_new());
+ CHECK(ecdsa_key.get() != nullptr &&
+ EC_KEY_set_group(ecdsa_key.get(), p256.get()));
+
+ crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free>::Type point(
+ EC_POINT_new(p256.get()));
+ CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), nullptr, nullptr,
+ nullptr));
+
+ EC_KEY_set_private_key(ecdsa_key.get(), k.get());
+ EC_KEY_set_public_key(ecdsa_key.get(), point.get());
+
+ crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
+ // EVP_PKEY_set1_EC_KEY takes a reference so no |release| here.
+ EVP_PKEY_set1_EC_KEY(pkey.get(), ecdsa_key.get());
+
+ return pkey.release();
+ }
+};
+
+// static
+ChannelIDSource* CryptoTestUtils::ChannelIDSourceForTesting() {
+ return new TestChannelIDSource();
+}
+
+} // namespace test
+
+} // namespace net
diff --git a/net/quic/test_tools/delayed_verify_strike_register_client.cc b/net/quic/test_tools/delayed_verify_strike_register_client.cc
new file mode 100644
index 0000000..b14a118
--- /dev/null
+++ b/net/quic/test_tools/delayed_verify_strike_register_client.cc
@@ -0,0 +1,53 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/delayed_verify_strike_register_client.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+DelayedVerifyStrikeRegisterClient::DelayedVerifyStrikeRegisterClient(
+ unsigned max_entries,
+ uint32 current_time_external,
+ uint32 window_secs,
+ const uint8 orbit[8],
+ StrikeRegister::StartupType startup)
+ : LocalStrikeRegisterClient(max_entries, current_time_external,
+ window_secs, orbit, startup),
+ delay_verifications_(false) {
+}
+
+DelayedVerifyStrikeRegisterClient::~DelayedVerifyStrikeRegisterClient() {}
+
+void DelayedVerifyStrikeRegisterClient::VerifyNonceIsValidAndUnique(
+ StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) {
+ if (delay_verifications_) {
+ pending_verifications_.push_back(VerifyArgs(nonce, now, cb));
+ } else {
+ LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(nonce, now, cb);
+ }
+}
+
+int DelayedVerifyStrikeRegisterClient::PendingVerifications() const {
+ return pending_verifications_.size();
+}
+
+void DelayedVerifyStrikeRegisterClient::RunPendingVerifications() {
+ vector<VerifyArgs> pending;
+ pending_verifications_.swap(pending);
+ for (vector<VerifyArgs>::const_iterator it = pending.begin(),
+ end = pending.end(); it != end; ++it) {
+ LocalStrikeRegisterClient::VerifyNonceIsValidAndUnique(
+ it->nonce, it->now, it->cb);
+ }
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/delayed_verify_strike_register_client.h b/net/quic/test_tools/delayed_verify_strike_register_client.h
new file mode 100644
index 0000000..6ae7ba1
--- /dev/null
+++ b/net/quic/test_tools/delayed_verify_strike_register_client.h
@@ -0,0 +1,65 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_DELAYED_VERIFY_STRIKE_REGISTER_CLIENT_H_
+#define NET_QUIC_TEST_TOOLS_DELAYED_VERIFY_STRIKE_REGISTER_CLIENT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/crypto/local_strike_register_client.h"
+
+namespace net {
+namespace test {
+
+// Test helper that allows delaying execution of nonce verification
+// callbacks until a later time.
+class DelayedVerifyStrikeRegisterClient : public LocalStrikeRegisterClient {
+ public:
+ DelayedVerifyStrikeRegisterClient(unsigned max_entries,
+ uint32 current_time_external,
+ uint32 window_secs,
+ const uint8 orbit[8],
+ StrikeRegister::StartupType startup);
+ virtual ~DelayedVerifyStrikeRegisterClient();
+
+ virtual void VerifyNonceIsValidAndUnique(base::StringPiece nonce,
+ QuicWallTime now,
+ ResultCallback* cb) OVERRIDE;
+
+ // Start queueing verifications instead of executing them immediately.
+ void StartDelayingVerification() {
+ delay_verifications_ = true;
+ }
+ // Number of verifications that are queued.
+ int PendingVerifications() const;
+ // Run all pending verifications.
+ void RunPendingVerifications();
+
+ private:
+ struct VerifyArgs {
+ VerifyArgs(base::StringPiece in_nonce,
+ QuicWallTime in_now,
+ ResultCallback* in_cb)
+ : nonce(in_nonce.as_string()),
+ now(in_now),
+ cb(in_cb) {
+ }
+
+ std::string nonce;
+ QuicWallTime now;
+ ResultCallback* cb;
+ };
+
+ bool delay_verifications_;
+ std::vector<VerifyArgs> pending_verifications_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelayedVerifyStrikeRegisterClient);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_DELAYED_VERIFY_STRIKE_REGISTER_CLIENT_H_
diff --git a/net/quic/test_tools/mock_clock.cc b/net/quic/test_tools/mock_clock.cc
new file mode 100644
index 0000000..47f2380
--- /dev/null
+++ b/net/quic/test_tools/mock_clock.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_clock.h"
+
+namespace net {
+
+MockClock::MockClock() : now_(QuicTime::Zero()) {
+}
+
+MockClock::~MockClock() {
+}
+
+void MockClock::AdvanceTime(QuicTime::Delta delta) {
+ now_ = now_.Add(delta);
+}
+
+QuicTime MockClock::Now() const {
+ return now_;
+}
+
+QuicTime MockClock::ApproximateNow() const {
+ return now_;
+}
+
+QuicWallTime MockClock::WallNow() const {
+ return QuicWallTime::FromUNIXSeconds(
+ now_.Subtract(QuicTime::Zero()).ToSeconds());
+}
+
+base::TimeTicks MockClock::NowInTicks() const {
+ base::TimeTicks ticks;
+ return ticks + base::TimeDelta::FromMicroseconds(
+ now_.Subtract(QuicTime::Zero()).ToMicroseconds());
+}
+
+} // namespace net
diff --git a/net/quic/test_tools/mock_clock.h b/net/quic/test_tools/mock_clock.h
new file mode 100644
index 0000000..d6e490f
--- /dev/null
+++ b/net/quic/test_tools/mock_clock.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
+
+#include "net/quic/quic_clock.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+
+namespace net {
+
+class MockClock : public QuicClock {
+ public:
+ MockClock();
+ virtual ~MockClock();
+
+ void AdvanceTime(QuicTime::Delta delta);
+
+ virtual QuicTime Now() const OVERRIDE;
+
+ virtual QuicTime ApproximateNow() const OVERRIDE;
+
+ virtual QuicWallTime WallNow() const OVERRIDE;
+
+ base::TimeTicks NowInTicks() const;
+
+ private:
+ QuicTime now_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockClock);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_
diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc
new file mode 100644
index 0000000..dd0e30b
--- /dev/null
+++ b/net/quic/test_tools/mock_crypto_client_stream.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_crypto_client_stream.h"
+
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/quic_client_session_base.h"
+#include "net/quic/quic_server_id.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+MockCryptoClientStream::MockCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ HandshakeMode handshake_mode,
+ const ProofVerifyDetails* proof_verify_details)
+ : QuicCryptoClientStream(server_id, session, verify_context,
+ crypto_config),
+ handshake_mode_(handshake_mode),
+ proof_verify_details_(proof_verify_details) {
+}
+
+MockCryptoClientStream::~MockCryptoClientStream() {
+}
+
+void MockCryptoClientStream::OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) {
+ CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE);
+}
+
+bool MockCryptoClientStream::CryptoConnect() {
+ switch (handshake_mode_) {
+ case ZERO_RTT: {
+ encryption_established_ = true;
+ handshake_confirmed_ = false;
+ session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL),
+ ENCRYPTION_INITIAL);
+ session()->OnCryptoHandshakeEvent(
+ QuicSession::ENCRYPTION_FIRST_ESTABLISHED);
+ break;
+ }
+
+ case CONFIRM_HANDSHAKE: {
+ encryption_established_ = true;
+ handshake_confirmed_ = true;
+ crypto_negotiated_params_.key_exchange = kC255;
+ crypto_negotiated_params_.aead = kAESG;
+ if (proof_verify_details_) {
+ client_session()->OnProofVerifyDetailsAvailable(*proof_verify_details_);
+ }
+ SetConfigNegotiated();
+ session()->connection()->SetDecrypter(QuicDecrypter::Create(kNULL),
+ ENCRYPTION_FORWARD_SECURE);
+ session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED);
+ break;
+ }
+
+ case COLD_START: {
+ handshake_confirmed_ = false;
+ encryption_established_ = false;
+ break;
+ }
+ }
+ return true;
+}
+
+void MockCryptoClientStream::SendOnCryptoHandshakeEvent(
+ QuicSession::CryptoHandshakeEvent event) {
+ encryption_established_ = true;
+ if (event == QuicSession::HANDSHAKE_CONFIRMED) {
+ handshake_confirmed_ = true;
+ SetConfigNegotiated();
+ }
+ session()->OnCryptoHandshakeEvent(event);
+}
+
+void MockCryptoClientStream::SetConfigNegotiated() {
+ ASSERT_FALSE(session()->config()->negotiated());
+ QuicTagVector cgst;
+ // TODO(rtenneti): Enable the following code after BBR code is checked in.
+#if 0
+ cgst.push_back(kTBBR);
+#endif
+ cgst.push_back(kQBIC);
+ session()->config()->SetCongestionFeedback(cgst, kQBIC);
+ session()->config()->SetIdleConnectionStateLifetime(
+ QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs),
+ QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs));
+ session()->config()->SetMaxStreamsPerConnection(
+ 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection);
+
+ CryptoHandshakeMessage msg;
+ session()->config()->ToHandshakeMessage(&msg);
+ string error_details;
+ const QuicErrorCode error =
+ session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
+ ASSERT_EQ(QUIC_NO_ERROR, error);
+ ASSERT_TRUE(session()->config()->negotiated());
+}
+
+QuicClientSessionBase* MockCryptoClientStream::client_session() {
+ return reinterpret_cast<QuicClientSessionBase*>(session());
+}
+
+} // namespace net
diff --git a/net/quic/test_tools/mock_crypto_client_stream.h b/net/quic/test_tools/mock_crypto_client_stream.h
new file mode 100644
index 0000000..e940a1e
--- /dev/null
+++ b/net/quic/test_tools/mock_crypto_client_stream.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_H_
+
+#include <string>
+
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_protocol.h"
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_session.h"
+
+namespace net {
+
+class QuicServerId;
+
+class MockCryptoClientStream : public QuicCryptoClientStream {
+ public:
+ // HandshakeMode enumerates the handshake mode MockCryptoClientStream should
+ // mock in CryptoConnect.
+ enum HandshakeMode {
+ // CONFIRM_HANDSHAKE indicates that CryptoConnect will immediately confirm
+ // the handshake and establish encryption. This behavior will never happen
+ // in the field, but is convenient for higher level tests.
+ CONFIRM_HANDSHAKE,
+
+ // ZERO_RTT indicates that CryptoConnect will establish encryption but will
+ // not confirm the handshake.
+ ZERO_RTT,
+
+ // COLD_START indicates that CryptoConnect will neither establish encryption
+ // nor confirm the handshake
+ COLD_START,
+ };
+
+ MockCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicClientSessionBase* session,
+ ProofVerifyContext* verify_context,
+ QuicCryptoClientConfig* crypto_config,
+ HandshakeMode handshake_mode,
+ const ProofVerifyDetails* proof_verify_details_);
+ virtual ~MockCryptoClientStream();
+
+ // CryptoFramerVisitorInterface implementation.
+ virtual void OnHandshakeMessage(
+ const CryptoHandshakeMessage& message) OVERRIDE;
+
+ // QuicCryptoClientStream implementation.
+ virtual bool CryptoConnect() OVERRIDE;
+
+ // Invokes the sessions's CryptoHandshakeEvent method with the specified
+ // event.
+ void SendOnCryptoHandshakeEvent(QuicSession::CryptoHandshakeEvent event);
+
+ HandshakeMode handshake_mode_;
+
+ private:
+ void SetConfigNegotiated();
+ QuicClientSessionBase* client_session();
+
+ const ProofVerifyDetails* proof_verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStream);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_H_
diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/net/quic/test_tools/mock_crypto_client_stream_factory.cc
new file mode 100644
index 0000000..5088c43
--- /dev/null
+++ b/net/quic/test_tools/mock_crypto_client_stream_factory.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
+
+#include "base/lazy_instance.h"
+#include "net/quic/quic_client_session.h"
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_server_id.h"
+
+using std::string;
+
+namespace net {
+
+MockCryptoClientStreamFactory::MockCryptoClientStreamFactory()
+ : handshake_mode_(MockCryptoClientStream::CONFIRM_HANDSHAKE),
+ last_stream_(nullptr),
+ proof_verify_details_(nullptr) {
+}
+
+QuicCryptoClientStream*
+MockCryptoClientStreamFactory::CreateQuicCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicClientSession* session,
+ QuicCryptoClientConfig* crypto_config) {
+ last_stream_ = new MockCryptoClientStream(
+ server_id, session, nullptr, crypto_config, handshake_mode_,
+ proof_verify_details_);
+ return last_stream_;
+}
+
+} // namespace net
diff --git a/net/quic/test_tools/mock_crypto_client_stream_factory.h b/net/quic/test_tools/mock_crypto_client_stream_factory.h
new file mode 100644
index 0000000..721ec25
--- /dev/null
+++ b/net/quic/test_tools/mock_crypto_client_stream_factory.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_FACTORY_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_FACTORY_H_
+
+#include <string>
+
+#include "net/quic/quic_crypto_client_stream.h"
+#include "net/quic/quic_crypto_client_stream_factory.h"
+#include "net/quic/test_tools/mock_crypto_client_stream.h"
+
+namespace net {
+
+class QuicServerId;
+
+class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory {
+ public:
+ MockCryptoClientStreamFactory();
+ virtual ~MockCryptoClientStreamFactory() {}
+
+ virtual QuicCryptoClientStream* CreateQuicCryptoClientStream(
+ const QuicServerId& server_id,
+ QuicClientSession* session,
+ QuicCryptoClientConfig* crypto_config) OVERRIDE;
+
+ void set_handshake_mode(
+ MockCryptoClientStream::HandshakeMode handshake_mode) {
+ handshake_mode_ = handshake_mode;
+ }
+
+ void set_proof_verify_details(
+ const ProofVerifyDetails* proof_verify_details) {
+ proof_verify_details_ = proof_verify_details;
+ }
+
+ MockCryptoClientStream* last_stream() const {
+ return last_stream_;
+ }
+
+ private:
+ MockCryptoClientStream::HandshakeMode handshake_mode_;
+ MockCryptoClientStream* last_stream_;
+ const ProofVerifyDetails* proof_verify_details_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockCryptoClientStreamFactory);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_FACTORY_H_
diff --git a/net/quic/test_tools/mock_quic_dispatcher.cc b/net/quic/test_tools/mock_quic_dispatcher.cc
new file mode 100644
index 0000000..9c9da43
--- /dev/null
+++ b/net/quic/test_tools/mock_quic_dispatcher.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_quic_dispatcher.h"
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+namespace test {
+
+MockQuicDispatcher::MockQuicDispatcher(
+ const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ QuicDispatcher::PacketWriterFactory* packet_writer_factory,
+ QuicConnectionHelperInterface* helper)
+ : QuicDispatcher(config,
+ crypto_config,
+ QuicSupportedVersions(),
+ packet_writer_factory,
+ helper) {
+}
+
+MockQuicDispatcher::~MockQuicDispatcher() {
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/mock_quic_dispatcher.h b/net/quic/test_tools/mock_quic_dispatcher.h
new file mode 100644
index 0000000..f923790
--- /dev/null
+++ b/net/quic/test_tools/mock_quic_dispatcher.h
@@ -0,0 +1,38 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
+
+#include "net/base/ip_endpoint.h"
+#include "net/quic/crypto/quic_crypto_server_config.h"
+#include "net/quic/quic_config.h"
+#include "net/quic/quic_dispatcher.h"
+#include "net/quic/quic_protocol.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+namespace test {
+
+class MockQuicDispatcher : public QuicDispatcher {
+ public:
+ MockQuicDispatcher(const QuicConfig& config,
+ const QuicCryptoServerConfig& crypto_config,
+ PacketWriterFactory* packet_writer_factory,
+ QuicConnectionHelperInterface* helper);
+
+ virtual ~MockQuicDispatcher();
+
+ MOCK_METHOD3(ProcessPacket, void(const IPEndPoint& server_address,
+ const IPEndPoint& client_address,
+ const QuicEncryptedPacket& packet));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockQuicDispatcher);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_QUIC_DISPATCHER_H_
diff --git a/net/quic/test_tools/mock_random.cc b/net/quic/test_tools/mock_random.cc
new file mode 100644
index 0000000..ab71d16
--- /dev/null
+++ b/net/quic/test_tools/mock_random.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/mock_random.h"
+
+namespace net {
+
+MockRandom::MockRandom()
+ : base_(0xDEADBEEF),
+ increment_(0) {
+}
+
+MockRandom::MockRandom(uint32 base)
+ : base_(base),
+ increment_(0) {
+}
+
+void MockRandom::RandBytes(void* data, size_t len) {
+ memset(data, 'r' + increment_, len);
+}
+
+uint64 MockRandom::RandUint64() {
+ return base_ + increment_;
+}
+
+void MockRandom::Reseed(const void* additional_entropy, size_t entropy_len) {
+}
+
+void MockRandom::ChangeValue() {
+ increment_++;
+}
+
+} // namespace net
diff --git a/net/quic/test_tools/mock_random.h b/net/quic/test_tools/mock_random.h
new file mode 100644
index 0000000..53b24b7
--- /dev/null
+++ b/net/quic/test_tools/mock_random.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
+#define NET_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
+
+#include "base/compiler_specific.h"
+#include "net/quic/crypto/quic_random.h"
+
+namespace net {
+
+class MockRandom : public QuicRandom {
+ public:
+ // Initializes base_ to 0xDEADBEEF.
+ MockRandom();
+ explicit MockRandom(uint32 base);
+
+ // QuicRandom:
+ // Fills the |data| buffer with a repeating byte, initially 'r'.
+ virtual void RandBytes(void* data, size_t len) OVERRIDE;
+ // Returns base + the current increment.
+ virtual uint64 RandUint64() OVERRIDE;
+ // Does nothing.
+ virtual void Reseed(const void* additional_entropy,
+ size_t entropy_len) OVERRIDE;
+
+ // ChangeValue increments |increment_|. This causes the value returned by
+ // |RandUint64| and the byte that |RandBytes| fills with, to change.
+ void ChangeValue();
+
+ private:
+ uint32 base_;
+ uint8 increment_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRandom);
+};
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_MOCK_RANDOM_H_
diff --git a/net/quic/test_tools/quic_client_session_peer.cc b/net/quic/test_tools/quic_client_session_peer.cc
new file mode 100644
index 0000000..7554551
--- /dev/null
+++ b/net/quic/test_tools/quic_client_session_peer.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_client_session_peer.h"
+
+#include "net/quic/quic_client_session.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicClientSessionPeer::SetMaxOpenStreams(QuicClientSession* session,
+ size_t max_streams,
+ size_t default_streams) {
+ session->config()->SetMaxStreamsPerConnection(max_streams, default_streams);
+}
+
+// static
+void QuicClientSessionPeer::SetChannelIDSent(QuicClientSession* session,
+ bool channel_id_sent) {
+ session->crypto_stream_->channel_id_sent_ = channel_id_sent;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_client_session_peer.h b/net/quic/test_tools/quic_client_session_peer.h
new file mode 100644
index 0000000..1a721c4
--- /dev/null
+++ b/net/quic/test_tools/quic_client_session_peer.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicClientSession;
+
+namespace test {
+
+class QuicClientSessionPeer {
+ public:
+ static void SetMaxOpenStreams(QuicClientSession* session,
+ size_t max_streams,
+ size_t default_streams);
+
+ static void SetChannelIDSent(QuicClientSession* session,
+ bool channel_id_sent);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicClientSessionPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_
diff --git a/net/quic/test_tools/quic_config_peer.cc b/net/quic/test_tools/quic_config_peer.cc
new file mode 100644
index 0000000..f700171
--- /dev/null
+++ b/net/quic/test_tools/quic_config_peer.cc
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_config_peer.h"
+
+#include "net/quic/quic_config.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicConfigPeer::SetReceivedInitialWindow(QuicConfig* config,
+ size_t initial_window) {
+ config->initial_congestion_window_.SetReceivedValue(initial_window);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes) {
+ config->initial_flow_control_window_bytes_.SetReceivedValue(window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+ QuicConfig* config,
+ uint32 window_bytes) {
+ config->initial_stream_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+ QuicConfig* config,
+ uint32 window_bytes) {
+ config->initial_session_flow_control_window_bytes_.SetReceivedValue(
+ window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedConnectionOptions(
+ QuicConfig* config,
+ const QuicTagVector& options) {
+ config->connection_options_.SetReceivedValues(options);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_config_peer.h b/net/quic/test_tools/quic_config_peer.h
new file mode 100644
index 0000000..8e9a5ea
--- /dev/null
+++ b/net/quic/test_tools/quic_config_peer.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicConfig;
+
+namespace test {
+
+class QuicConfigPeer {
+ public:
+ static void SetReceivedInitialWindow(QuicConfig* config,
+ size_t initial_window);
+
+ // TODO(rjshade): Remove when removing QUIC_VERSION_19.
+ static void SetReceivedInitialFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config,
+ uint32 window_bytes);
+
+ static void SetReceivedConnectionOptions(QuicConfig* config,
+ const QuicTagVector& options);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicConfigPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CONFIG_PEER_H_
diff --git a/net/quic/test_tools/quic_connection_peer.cc b/net/quic/test_tools/quic_connection_peer.cc
new file mode 100644
index 0000000..a148249
--- /dev/null
+++ b/net/quic/test_tools/quic_connection_peer.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_connection_peer.h"
+
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/receive_algorithm_interface.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_packet_writer.h"
+#include "net/quic/quic_received_packet_manager.h"
+#include "net/quic/test_tools/quic_framer_peer.h"
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicConnectionPeer::SendAck(QuicConnection* connection) {
+ connection->SendAck();
+}
+
+// static
+void QuicConnectionPeer::SetReceiveAlgorithm(
+ QuicConnection* connection,
+ ReceiveAlgorithmInterface* receive_algorithm) {
+ connection->received_packet_manager_.receive_algorithm_.reset(
+ receive_algorithm);
+}
+
+// static
+void QuicConnectionPeer::SetSendAlgorithm(
+ QuicConnection* connection,
+ SendAlgorithmInterface* send_algorithm) {
+ connection->sent_packet_manager_.send_algorithm_.reset(send_algorithm);
+}
+
+// static
+QuicAckFrame* QuicConnectionPeer::CreateAckFrame(QuicConnection* connection) {
+ return connection->CreateAckFrame();
+}
+
+// static
+QuicStopWaitingFrame* QuicConnectionPeer::CreateStopWaitingFrame(
+ QuicConnection* connection) {
+ return connection->CreateStopWaitingFrame();
+}
+
+// static
+QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor(
+ QuicConnection* connection) {
+ return connection->visitor_;
+}
+
+// static
+QuicPacketCreator* QuicConnectionPeer::GetPacketCreator(
+ QuicConnection* connection) {
+ return QuicPacketGeneratorPeer::GetPacketCreator(
+ &connection->packet_generator_);
+}
+
+// static
+QuicPacketGenerator* QuicConnectionPeer::GetPacketGenerator(
+ QuicConnection* connection) {
+ return &connection->packet_generator_;
+}
+
+// static
+QuicSentPacketManager* QuicConnectionPeer::GetSentPacketManager(
+ QuicConnection* connection) {
+ return &connection->sent_packet_manager_;
+}
+
+// static
+QuicReceivedPacketManager* QuicConnectionPeer::GetReceivedPacketManager(
+ QuicConnection* connection) {
+ return &connection->received_packet_manager_;
+}
+
+// static
+QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout(
+ QuicConnection* connection) {
+ return connection->idle_network_timeout_;
+}
+
+// static
+bool QuicConnectionPeer::IsSavedForRetransmission(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ return connection->sent_packet_manager_.IsUnacked(sequence_number) &&
+ connection->sent_packet_manager_.HasRetransmittableFrames(
+ sequence_number);
+}
+
+// static
+bool QuicConnectionPeer::IsRetransmission(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ return QuicSentPacketManagerPeer::IsRetransmission(
+ &connection->sent_packet_manager_, sequence_number);
+}
+
+// static
+// TODO(ianswett): Create a GetSentEntropyHash which accepts an AckFrame.
+QuicPacketEntropyHash QuicConnectionPeer::GetSentEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ QuicSentEntropyManager::CumulativeEntropy last_entropy_copy =
+ connection->sent_entropy_manager_.last_cumulative_entropy_;
+ connection->sent_entropy_manager_.UpdateCumulativeEntropy(sequence_number,
+ &last_entropy_copy);
+ return last_entropy_copy.entropy;
+}
+
+// static
+QuicPacketEntropyHash QuicConnectionPeer::PacketEntropy(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ return connection->sent_entropy_manager_.GetPacketEntropy(sequence_number);
+}
+
+// static
+QuicPacketEntropyHash QuicConnectionPeer::ReceivedEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number) {
+ return connection->received_packet_manager_.EntropyHash(
+ sequence_number);
+}
+
+// static
+bool QuicConnectionPeer::IsServer(QuicConnection* connection) {
+ return connection->is_server_;
+}
+
+// static
+void QuicConnectionPeer::SetIsServer(QuicConnection* connection,
+ bool is_server) {
+ connection->is_server_ = is_server;
+ QuicFramerPeer::SetIsServer(&connection->framer_, is_server);
+}
+
+// static
+void QuicConnectionPeer::SetSelfAddress(QuicConnection* connection,
+ const IPEndPoint& self_address) {
+ connection->self_address_ = self_address;
+}
+
+// static
+void QuicConnectionPeer::SetPeerAddress(QuicConnection* connection,
+ const IPEndPoint& peer_address) {
+ connection->peer_address_ = peer_address;
+}
+
+// static
+void QuicConnectionPeer::SwapCrypters(QuicConnection* connection,
+ QuicFramer* framer) {
+ QuicFramerPeer::SwapCrypters(framer, &connection->framer_);
+}
+
+// static
+QuicConnectionHelperInterface* QuicConnectionPeer::GetHelper(
+ QuicConnection* connection) {
+ return connection->helper_;
+}
+
+// static
+QuicFramer* QuicConnectionPeer::GetFramer(QuicConnection* connection) {
+ return &connection->framer_;
+}
+
+// static
+QuicFecGroup* QuicConnectionPeer::GetFecGroup(QuicConnection* connection,
+ int fec_group) {
+ connection->last_header_.fec_group = fec_group;
+ return connection->GetFecGroup();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetAckAlarm(QuicConnection* connection) {
+ return connection->ack_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetPingAlarm(QuicConnection* connection) {
+ return connection->ping_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetResumeWritesAlarm(
+ QuicConnection* connection) {
+ return connection->resume_writes_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetRetransmissionAlarm(
+ QuicConnection* connection) {
+ return connection->retransmission_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetSendAlarm(QuicConnection* connection) {
+ return connection->send_alarm_.get();
+}
+
+// static
+QuicAlarm* QuicConnectionPeer::GetTimeoutAlarm(QuicConnection* connection) {
+ return connection->timeout_alarm_.get();
+}
+
+// static
+QuicPacketWriter* QuicConnectionPeer::GetWriter(QuicConnection* connection) {
+ return connection->writer_;
+}
+
+// static
+void QuicConnectionPeer::SetWriter(QuicConnection* connection,
+ QuicPacketWriter* writer,
+ bool owns_writer) {
+ if (connection->owns_writer_) {
+ delete connection->writer_;
+ }
+ connection->writer_ = writer;
+ connection->owns_writer_ = owns_writer;
+}
+
+// static
+void QuicConnectionPeer::CloseConnection(QuicConnection* connection) {
+ connection->connected_ = false;
+}
+
+// static
+QuicEncryptedPacket* QuicConnectionPeer::GetConnectionClosePacket(
+ QuicConnection* connection) {
+ return connection->connection_close_packet_.get();
+}
+
+// static
+void QuicConnectionPeer::SetSupportedVersions(QuicConnection* connection,
+ QuicVersionVector versions) {
+ connection->framer_.SetSupportedVersions(versions);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_connection_peer.h b/net/quic/test_tools/quic_connection_peer.h
new file mode 100644
index 0000000..c9c4ee7
--- /dev/null
+++ b/net/quic/test_tools/quic_connection_peer.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
+
+#include "base/basictypes.h"
+#include "net/base/ip_endpoint.h"
+#include "net/quic/quic_connection_stats.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+struct QuicAckFrame;
+struct QuicPacketHeader;
+class QuicAlarm;
+class QuicConnection;
+class QuicConnectionHelperInterface;
+class QuicConnectionVisitorInterface;
+class QuicEncryptedPacket;
+class QuicFecGroup;
+class QuicFramer;
+class QuicPacketCreator;
+class QuicPacketGenerator;
+class QuicPacketWriter;
+class QuicReceivedPacketManager;
+class QuicSentPacketManager;
+class ReceiveAlgorithmInterface;
+class SendAlgorithmInterface;
+
+namespace test {
+
+// Peer to make public a number of otherwise private QuicConnection methods.
+class QuicConnectionPeer {
+ public:
+ static void SendAck(QuicConnection* connection);
+
+ static void SetReceiveAlgorithm(QuicConnection* connection,
+ ReceiveAlgorithmInterface* receive_algorithm);
+
+ static void SetSendAlgorithm(QuicConnection* connection,
+ SendAlgorithmInterface* send_algorithm);
+
+ static QuicAckFrame* CreateAckFrame(QuicConnection* connection);
+
+ static QuicStopWaitingFrame* CreateStopWaitingFrame(
+ QuicConnection* connection);
+
+ static QuicConnectionVisitorInterface* GetVisitor(
+ QuicConnection* connection);
+
+ static QuicPacketCreator* GetPacketCreator(QuicConnection* connection);
+
+ static QuicPacketGenerator* GetPacketGenerator(QuicConnection* connection);
+
+ static QuicSentPacketManager* GetSentPacketManager(
+ QuicConnection* connection);
+
+ static QuicReceivedPacketManager* GetReceivedPacketManager(
+ QuicConnection* connection);
+
+ static QuicTime::Delta GetNetworkTimeout(QuicConnection* connection);
+
+ static bool IsSavedForRetransmission(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
+ static bool IsRetransmission(QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
+ static QuicPacketEntropyHash GetSentEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
+ static QuicPacketEntropyHash PacketEntropy(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
+ static QuicPacketEntropyHash ReceivedEntropyHash(
+ QuicConnection* connection,
+ QuicPacketSequenceNumber sequence_number);
+
+ static bool IsServer(QuicConnection* connection);
+
+ static void SetIsServer(QuicConnection* connection, bool is_server);
+
+ static void SetSelfAddress(QuicConnection* connection,
+ const IPEndPoint& self_address);
+
+ static void SetPeerAddress(QuicConnection* connection,
+ const IPEndPoint& peer_address);
+
+ static void SwapCrypters(QuicConnection* connection, QuicFramer* framer);
+
+ static QuicConnectionHelperInterface* GetHelper(QuicConnection* connection);
+
+ static QuicFramer* GetFramer(QuicConnection* connection);
+
+ // Set last_header_->fec_group = fec_group and return connection->GetFecGroup
+ static QuicFecGroup* GetFecGroup(QuicConnection* connection, int fec_group);
+
+ static QuicAlarm* GetAckAlarm(QuicConnection* connection);
+ static QuicAlarm* GetPingAlarm(QuicConnection* connection);
+ static QuicAlarm* GetResumeWritesAlarm(QuicConnection* connection);
+ static QuicAlarm* GetRetransmissionAlarm(QuicConnection* connection);
+ static QuicAlarm* GetSendAlarm(QuicConnection* connection);
+ static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection);
+
+ static QuicPacketWriter* GetWriter(QuicConnection* connection);
+ // If |owns_writer| is true, takes ownership of |writer|.
+ static void SetWriter(QuicConnection* connection,
+ QuicPacketWriter* writer,
+ bool owns_writer);
+ static void CloseConnection(QuicConnection* connection);
+ static QuicEncryptedPacket* GetConnectionClosePacket(
+ QuicConnection* connection);
+
+ static void SetSupportedVersions(QuicConnection* connection,
+ QuicVersionVector versions);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_CONNECTION_PEER_H_
diff --git a/net/quic/test_tools/quic_data_stream_peer.cc b/net/quic/test_tools/quic_data_stream_peer.cc
new file mode 100644
index 0000000..6165675
--- /dev/null
+++ b/net/quic/test_tools/quic_data_stream_peer.cc
@@ -0,0 +1,19 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_data_stream_peer.h"
+
+#include "net/quic/quic_data_stream.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicDataStreamPeer::SetHeadersDecompressed(QuicDataStream* stream,
+ bool headers_decompressed) {
+ stream->headers_decompressed_ = headers_decompressed;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_data_stream_peer.h b/net/quic/test_tools/quic_data_stream_peer.h
new file mode 100644
index 0000000..914b87d
--- /dev/null
+++ b/net/quic/test_tools/quic_data_stream_peer.h
@@ -0,0 +1,29 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_DATA_STREAM_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_DATA_STREAM_PEER_H_
+
+#include "base/basictypes.h"
+
+namespace net {
+
+class QuicDataStream;
+
+namespace test {
+
+class QuicDataStreamPeer {
+ public:
+ static void SetHeadersDecompressed(QuicDataStream* stream,
+ bool headers_decompressed);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicDataStreamPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_DATA_STREAM_PEER_H_
diff --git a/net/quic/test_tools/quic_flow_controller_peer.cc b/net/quic/test_tools/quic_flow_controller_peer.cc
new file mode 100644
index 0000000..35882ed
--- /dev/null
+++ b/net/quic/test_tools/quic_flow_controller_peer.cc
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
+
+#include <list>
+
+#include "net/quic/quic_flow_controller.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicFlowControllerPeer::SetSendWindowOffset(
+ QuicFlowController* flow_controller,
+ uint64 offset) {
+ flow_controller->send_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetReceiveWindowOffset(
+ QuicFlowController* flow_controller,
+ uint64 offset) {
+ flow_controller->receive_window_offset_ = offset;
+}
+
+// static
+void QuicFlowControllerPeer::SetMaxReceiveWindow(
+ QuicFlowController* flow_controller, uint64 window_size) {
+ flow_controller->max_receive_window_ = window_size;
+}
+
+// static
+uint64 QuicFlowControllerPeer::SendWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->send_window_offset_;
+}
+
+// static
+uint64 QuicFlowControllerPeer::SendWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->SendWindowSize();
+}
+
+// static
+uint64 QuicFlowControllerPeer::ReceiveWindowOffset(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_;
+}
+
+// static
+uint64 QuicFlowControllerPeer::ReceiveWindowSize(
+ QuicFlowController* flow_controller) {
+ return flow_controller->receive_window_offset_ -
+ flow_controller->highest_received_byte_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_flow_controller_peer.h b/net/quic/test_tools/quic_flow_controller_peer.h
new file mode 100644
index 0000000..213d40d
--- /dev/null
+++ b/net/quic/test_tools/quic_flow_controller_peer.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicFlowController;
+
+namespace test {
+
+class QuicFlowControllerPeer {
+ public:
+ static void SetSendWindowOffset(QuicFlowController* flow_controller,
+ uint64 offset);
+
+ static void SetReceiveWindowOffset(QuicFlowController* flow_controller,
+ uint64 offset);
+
+ static void SetMaxReceiveWindow(QuicFlowController* flow_controller,
+ uint64 window_size);
+
+ static uint64 SendWindowOffset(QuicFlowController* flow_controller);
+
+ static uint64 SendWindowSize(QuicFlowController* flow_controller);
+
+ static uint64 ReceiveWindowOffset(QuicFlowController* flow_controller);
+
+ static uint64 ReceiveWindowSize(QuicFlowController* flow_controller);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicFlowControllerPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_FLOW_CONTROLLER_PEER_H_
diff --git a/net/quic/test_tools/quic_framer_peer.cc b/net/quic/test_tools/quic_framer_peer.cc
new file mode 100644
index 0000000..c6464cc
--- /dev/null
+++ b/net/quic/test_tools/quic_framer_peer.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_framer_peer.h"
+
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+namespace test {
+
+// static
+QuicPacketSequenceNumber QuicFramerPeer::CalculatePacketSequenceNumberFromWire(
+ QuicFramer* framer,
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber packet_sequence_number) {
+ return framer->CalculatePacketSequenceNumberFromWire(sequence_number_length,
+ packet_sequence_number);
+}
+
+// static
+void QuicFramerPeer::SetLastSerializedConnectionId(
+ QuicFramer* framer, QuicConnectionId connection_id) {
+ framer->last_serialized_connection_id_ = connection_id;
+}
+
+void QuicFramerPeer::SetLastSequenceNumber(
+ QuicFramer* framer,
+ QuicPacketSequenceNumber packet_sequence_number) {
+ framer->last_sequence_number_ = packet_sequence_number;
+}
+
+void QuicFramerPeer::SetIsServer(QuicFramer* framer, bool is_server) {
+ framer->is_server_ = is_server;
+}
+
+void QuicFramerPeer::SwapCrypters(QuicFramer* framer1, QuicFramer* framer2) {
+ for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) {
+ framer1->encrypter_[i].swap(framer2->encrypter_[i]);
+ }
+ framer1->decrypter_.swap(framer2->decrypter_);
+ framer1->alternative_decrypter_.swap(framer2->alternative_decrypter_);
+
+ EncryptionLevel framer2_level = framer2->decrypter_level_;
+ framer2->decrypter_level_ = framer1->decrypter_level_;
+ framer1->decrypter_level_ = framer2_level;
+ framer2_level = framer2->alternative_decrypter_level_;
+ framer2->alternative_decrypter_level_ =
+ framer1->alternative_decrypter_level_;
+ framer1->alternative_decrypter_level_ = framer2_level;
+
+ const bool framer2_latch = framer2->alternative_decrypter_latch_;
+ framer2->alternative_decrypter_latch_ =
+ framer1->alternative_decrypter_latch_;
+ framer1->alternative_decrypter_latch_ = framer2_latch;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_framer_peer.h b/net/quic/test_tools/quic_framer_peer.h
new file mode 100644
index 0000000..9b9c9fa
--- /dev/null
+++ b/net/quic/test_tools/quic_framer_peer.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicFramer;
+
+namespace test {
+
+class QuicFramerPeer {
+ public:
+ static QuicPacketSequenceNumber CalculatePacketSequenceNumberFromWire(
+ QuicFramer* framer,
+ QuicSequenceNumberLength sequence_number_length,
+ QuicPacketSequenceNumber packet_sequence_number);
+ static void SetLastSerializedConnectionId(QuicFramer* framer,
+ QuicConnectionId connection_id);
+ static void SetLastSequenceNumber(
+ QuicFramer* framer,
+ QuicPacketSequenceNumber packet_sequence_number);
+ static void SetIsServer(QuicFramer* framer, bool is_server);
+
+ // SwapCrypters exchanges the state of the crypters of |framer1| with
+ // |framer2|.
+ static void SwapCrypters(QuicFramer* framer1, QuicFramer* framer2);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_
diff --git a/net/quic/test_tools/quic_packet_creator_peer.cc b/net/quic/test_tools/quic_packet_creator_peer.cc
new file mode 100644
index 0000000..a801447
--- /dev/null
+++ b/net/quic/test_tools/quic_packet_creator_peer.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_packet_creator_peer.h"
+
+#include "net/quic/quic_packet_creator.h"
+
+namespace net {
+namespace test {
+
+// static
+bool QuicPacketCreatorPeer::SendVersionInPacket(QuicPacketCreator* creator) {
+ return creator->send_version_in_packet_;
+}
+
+// static
+void QuicPacketCreatorPeer::SetSendVersionInPacket(
+ QuicPacketCreator* creator, bool send_version_in_packet) {
+ creator->send_version_in_packet_ = send_version_in_packet;
+}
+
+// static
+void QuicPacketCreatorPeer::SetSequenceNumberLength(
+ QuicPacketCreator* creator,
+ QuicSequenceNumberLength sequence_number_length) {
+ creator->sequence_number_length_ = sequence_number_length;
+}
+
+// static
+QuicSequenceNumberLength QuicPacketCreatorPeer::GetSequenceNumberLength(
+ QuicPacketCreator* creator) {
+ return creator->sequence_number_length_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_packet_creator_peer.h b/net/quic/test_tools/quic_packet_creator_peer.h
new file mode 100644
index 0000000..b4bf8a7
--- /dev/null
+++ b/net/quic/test_tools/quic_packet_creator_peer.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+class QuicPacketCreator;
+
+namespace test {
+
+class QuicPacketCreatorPeer {
+ public:
+ static bool SendVersionInPacket(QuicPacketCreator* creator);
+
+ static void SetSendVersionInPacket(QuicPacketCreator* creator,
+ bool send_version_in_packet);
+ static void SetSequenceNumberLength(
+ QuicPacketCreator* creator,
+ QuicSequenceNumberLength sequence_number_length);
+ static QuicSequenceNumberLength GetSequenceNumberLength(
+ QuicPacketCreator* creator);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketCreatorPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_
diff --git a/net/quic/test_tools/quic_packet_generator_peer.cc b/net/quic/test_tools/quic_packet_generator_peer.cc
new file mode 100644
index 0000000..ad0693a
--- /dev/null
+++ b/net/quic/test_tools/quic_packet_generator_peer.cc
@@ -0,0 +1,20 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_packet_generator_peer.h"
+
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_packet_generator.h"
+
+namespace net {
+namespace test {
+
+// static
+QuicPacketCreator* QuicPacketGeneratorPeer::GetPacketCreator(
+ QuicPacketGenerator* generator) {
+ return &generator->packet_creator_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_packet_generator_peer.h b/net/quic/test_tools/quic_packet_generator_peer.h
new file mode 100644
index 0000000..0762d1f
--- /dev/null
+++ b/net/quic/test_tools/quic_packet_generator_peer.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicPacketCreator;
+class QuicPacketGenerator;
+
+namespace test {
+
+class QuicPacketGeneratorPeer {
+ public:
+ static QuicPacketCreator* GetPacketCreator(QuicPacketGenerator* generator);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicPacketGeneratorPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_GENERATOR_PEER_H_
diff --git a/net/quic/test_tools/quic_received_packet_manager_peer.cc b/net/quic/test_tools/quic_received_packet_manager_peer.cc
new file mode 100644
index 0000000..2faf84e
--- /dev/null
+++ b/net/quic/test_tools/quic_received_packet_manager_peer.cc
@@ -0,0 +1,30 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_received_packet_manager_peer.h"
+
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_received_packet_manager.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicReceivedPacketManagerPeer::SetCumulativeEntropyUpTo(
+ QuicReceivedPacketManager* received_packet_manager,
+ QuicPacketSequenceNumber peer_least_unacked,
+ QuicPacketEntropyHash entropy_hash) {
+ received_packet_manager->entropy_tracker_.SetCumulativeEntropyUpTo(
+ peer_least_unacked, entropy_hash);
+}
+
+// static
+bool QuicReceivedPacketManagerPeer::DontWaitForPacketsBefore(
+ QuicReceivedPacketManager* received_packet_manager,
+ QuicPacketSequenceNumber least_unacked) {
+ return received_packet_manager->DontWaitForPacketsBefore(least_unacked);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_received_packet_manager_peer.h b/net/quic/test_tools/quic_received_packet_manager_peer.h
new file mode 100644
index 0000000..c3cb2d3
--- /dev/null
+++ b/net/quic/test_tools/quic_received_packet_manager_peer.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_RECEIVED_PACKET_MANAGER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_RECEIVED_PACKET_MANAGER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicReceivedPacketManager;
+
+namespace test {
+
+class QuicReceivedPacketManagerPeer {
+ public:
+ static void SetCumulativeEntropyUpTo(
+ QuicReceivedPacketManager* received_packet_manager,
+ QuicPacketSequenceNumber peer_least_unacked,
+ QuicPacketEntropyHash entropy_hash);
+
+ static bool DontWaitForPacketsBefore(
+ QuicReceivedPacketManager* received_packet_manager,
+ QuicPacketSequenceNumber least_unacked);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicReceivedPacketManagerPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_RECEIVED_PACKET_MANAGER_PEER_H_
diff --git a/net/quic/test_tools/quic_sent_packet_manager_peer.cc b/net/quic/test_tools/quic_sent_packet_manager_peer.cc
new file mode 100644
index 0000000..bebd554
--- /dev/null
+++ b/net/quic/test_tools/quic_sent_packet_manager_peer.cc
@@ -0,0 +1,157 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_sent_packet_manager_peer.h"
+
+#include "base/stl_util.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_sent_packet_manager.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicSentPacketManagerPeer::SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes) {
+ sent_packet_manager->max_tail_loss_probes_ = max_tail_loss_probes;
+}
+
+// static
+void QuicSentPacketManagerPeer::SetSendAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ SendAlgorithmInterface* send_algorithm) {
+ sent_packet_manager->send_algorithm_.reset(send_algorithm);
+}
+
+// static
+void QuicSentPacketManagerPeer::SetIsServer(
+ QuicSentPacketManager* sent_packet_manager,
+ bool is_server) {
+ sent_packet_manager->is_server_ = is_server;
+}
+
+// static
+const LossDetectionInterface* QuicSentPacketManagerPeer::GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->loss_algorithm_.get();
+}
+
+// static
+const SendAlgorithmInterface*
+ QuicSentPacketManagerPeer::GetCongestionControlAlgorithm(
+ const QuicSentPacketManager& sent_packet_manager) {
+ return sent_packet_manager.send_algorithm_.get();
+}
+
+// static
+void QuicSentPacketManagerPeer::SetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector) {
+ sent_packet_manager->loss_algorithm_.reset(loss_detector);
+}
+
+// static
+RttStats* QuicSentPacketManagerPeer::GetRttStats(
+ QuicSentPacketManager* sent_packet_manager) {
+ return &sent_packet_manager->rtt_stats_;
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetNackCount(
+ const QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number) {
+ return sent_packet_manager->unacked_packets_.
+ GetTransmissionInfo(sequence_number).nack_count;
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetPendingRetransmissionCount(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->pending_retransmissions_.size();
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasInFlightPackets();
+}
+
+// static
+QuicTime QuicSentPacketManagerPeer::GetSentTime(
+ const QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK(sent_packet_manager->unacked_packets_.IsUnacked(sequence_number));
+
+ return sent_packet_manager->unacked_packets_.GetTransmissionInfo(
+ sequence_number).sent_time;
+}
+
+// static
+bool QuicSentPacketManagerPeer::IsRetransmission(
+ QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number) {
+ DCHECK(sent_packet_manager->HasRetransmittableFrames(sequence_number));
+ return sent_packet_manager->HasRetransmittableFrames(sequence_number) &&
+ sent_packet_manager->unacked_packets_.GetTransmissionInfo(
+ sequence_number).all_transmissions != nullptr;
+}
+
+// static
+void QuicSentPacketManagerPeer::MarkForRetransmission(
+ QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number,
+ TransmissionType transmission_type) {
+ sent_packet_manager->MarkForRetransmission(sequence_number,
+ transmission_type);
+}
+
+// static
+QuicTime::Delta QuicSentPacketManagerPeer::GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->GetRetransmissionDelay();
+}
+
+// static
+bool QuicSentPacketManagerPeer::HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.HasPendingCryptoPackets();
+}
+
+// static
+size_t QuicSentPacketManagerPeer::GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager) {
+ size_t num_unacked_packets = 0;
+ for (QuicUnackedPacketMap::const_iterator it =
+ sent_packet_manager->unacked_packets_.begin();
+ it != sent_packet_manager->unacked_packets_.end(); ++it) {
+ if (it->retransmittable_frames != nullptr) {
+ ++num_unacked_packets;
+ }
+ }
+ return num_unacked_packets;
+}
+
+// static
+QuicByteCount QuicSentPacketManagerPeer::GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->unacked_packets_.bytes_in_flight();
+}
+
+// static
+QuicSentPacketManager::NetworkChangeVisitor*
+ QuicSentPacketManagerPeer::GetNetworkChangeVisitor(
+ const QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->network_change_visitor_;
+}
+
+// static
+QuicSustainedBandwidthRecorder& QuicSentPacketManagerPeer::GetBandwidthRecorder(
+ QuicSentPacketManager* sent_packet_manager) {
+ return sent_packet_manager->sustained_bandwidth_recorder_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_sent_packet_manager_peer.h b/net/quic/test_tools/quic_sent_packet_manager_peer.h
new file mode 100644
index 0000000..b4c6857
--- /dev/null
+++ b/net/quic/test_tools/quic_sent_packet_manager_peer.h
@@ -0,0 +1,86 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_sent_packet_manager.h"
+
+namespace net {
+
+class SendAlgorithmInterface;
+
+namespace test {
+
+class QuicSentPacketManagerPeer {
+ public:
+ static void SetMaxTailLossProbes(
+ QuicSentPacketManager* sent_packet_manager, size_t max_tail_loss_probes);
+
+ static void SetSendAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ SendAlgorithmInterface* send_algorithm);
+
+ static void SetIsServer(QuicSentPacketManager* sent_packet_manager,
+ bool is_server);
+
+ static const LossDetectionInterface* GetLossAlgorithm(
+ QuicSentPacketManager* sent_packet_manager);
+
+ static const SendAlgorithmInterface* GetCongestionControlAlgorithm(
+ const QuicSentPacketManager& sent_packet_manager);
+
+ static void SetLossAlgorithm(QuicSentPacketManager* sent_packet_manager,
+ LossDetectionInterface* loss_detector);
+
+ static RttStats* GetRttStats(QuicSentPacketManager* sent_packet_manager);
+
+ static size_t GetNackCount(
+ const QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number);
+
+ static size_t GetPendingRetransmissionCount(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasPendingPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicTime GetSentTime(const QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number);
+
+ // Returns true if |sequence_number| is a retransmission of a packet.
+ static bool IsRetransmission(QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number);
+
+ static void MarkForRetransmission(QuicSentPacketManager* sent_packet_manager,
+ QuicPacketSequenceNumber sequence_number,
+ TransmissionType transmission_type);
+
+ static QuicTime::Delta GetRetransmissionDelay(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static bool HasUnackedCryptoPackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static size_t GetNumRetransmittablePackets(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicByteCount GetBytesInFlight(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicSentPacketManager::NetworkChangeVisitor* GetNetworkChangeVisitor(
+ const QuicSentPacketManager* sent_packet_manager);
+
+ static QuicSustainedBandwidthRecorder& GetBandwidthRecorder(
+ QuicSentPacketManager* sent_packet_manager);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManagerPeer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_SENT_PACKET_MANAGER_PEER_H_
diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc
new file mode 100644
index 0000000..14897b2
--- /dev/null
+++ b/net/quic/test_tools/quic_session_peer.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_session_peer.h"
+
+#include "net/quic/quic_session.h"
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicSessionPeer::SetNextStreamId(QuicSession* session, QuicStreamId id) {
+ session->next_stream_id_ = id;
+}
+
+// static
+void QuicSessionPeer::SetMaxOpenStreams(QuicSession* session,
+ uint32 max_streams) {
+ session->max_open_streams_ = max_streams;
+}
+
+// static
+QuicCryptoStream* QuicSessionPeer::GetCryptoStream(QuicSession* session) {
+ return session->GetCryptoStream();
+}
+
+// static
+QuicHeadersStream* QuicSessionPeer::GetHeadersStream(QuicSession* session) {
+ return session->headers_stream_.get();
+}
+
+// static
+void QuicSessionPeer::SetHeadersStream(QuicSession* session,
+ QuicHeadersStream* headers_stream) {
+ session->headers_stream_.reset(headers_stream);
+}
+
+// static
+QuicWriteBlockedList* QuicSessionPeer::GetWriteBlockedStreams(
+ QuicSession* session) {
+ return &session->write_blocked_streams_;
+}
+
+// static
+QuicDataStream* QuicSessionPeer::GetIncomingDataStream(
+ QuicSession* session,
+ QuicStreamId stream_id) {
+ return session->GetIncomingDataStream(stream_id);
+}
+
+// static
+map<QuicStreamId, QuicStreamOffset>&
+QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) {
+ return session->locally_closed_streams_highest_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h
new file mode 100644
index 0000000..e69f6ba
--- /dev/null
+++ b/net/quic/test_tools/quic_session_peer.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_write_blocked_list.h"
+
+namespace net {
+
+class QuicCryptoStream;
+class QuicDataStream;
+class QuicHeadersStream;
+class QuicSession;
+
+namespace test {
+
+class QuicSessionPeer {
+ public:
+ static void SetNextStreamId(QuicSession* session, QuicStreamId id);
+ static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams);
+ static QuicCryptoStream* GetCryptoStream(QuicSession* session);
+ static QuicHeadersStream* GetHeadersStream(QuicSession* session);
+ static void SetHeadersStream(QuicSession* session,
+ QuicHeadersStream* headers_stream);
+ static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session);
+ static QuicDataStream* GetIncomingDataStream(QuicSession* session,
+ QuicStreamId stream_id);
+ static std::map<QuicStreamId, QuicStreamOffset>&
+ GetLocallyClosedStreamsHighestOffset(QuicSession* session);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_
diff --git a/net/quic/test_tools/quic_stream_sequencer_peer.cc b/net/quic/test_tools/quic_stream_sequencer_peer.cc
new file mode 100644
index 0000000..08a5c5b
--- /dev/null
+++ b/net/quic/test_tools/quic_stream_sequencer_peer.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_stream_sequencer_peer.h"
+
+#include "net/quic/quic_stream_sequencer.h"
+
+using std::map;
+using std::string;
+
+namespace net {
+namespace test {
+
+// static
+map<QuicStreamOffset, string>* QuicStreamSequencerPeer::GetBufferedFrames(
+ QuicStreamSequencer* sequencer) {
+ return &(sequencer->buffered_frames_);
+}
+
+// static
+QuicStreamOffset QuicStreamSequencerPeer::GetCloseOffset(
+ QuicStreamSequencer* sequencer) {
+ return sequencer->close_offset_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_stream_sequencer_peer.h b/net/quic/test_tools/quic_stream_sequencer_peer.h
new file mode 100644
index 0000000..7b2b28a
--- /dev/null
+++ b/net/quic/test_tools/quic_stream_sequencer_peer.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicStreamSequencer;
+
+namespace test {
+
+class QuicStreamSequencerPeer {
+ public:
+ static std::map<QuicStreamOffset, std::string>* GetBufferedFrames(
+ QuicStreamSequencer* sequencer);
+
+ static QuicStreamOffset GetCloseOffset(QuicStreamSequencer* sequencer);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicStreamSequencerPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_STREAM_SEQUENCER_PEER_H_
diff --git a/net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc b/net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc
new file mode 100644
index 0000000..fd3678c
--- /dev/null
+++ b/net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h"
+
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_sustained_bandwidth_recorder.h"
+
+namespace net {
+namespace test {
+
+// static
+void QuicSustainedBandwidthRecorderPeer::SetBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32 bandwidth_estimate_kbytes_per_second) {
+ bandwidth_recorder->has_estimate_ = true;
+ bandwidth_recorder->bandwidth_estimate_ =
+ QuicBandwidth::FromKBytesPerSecond(bandwidth_estimate_kbytes_per_second);
+}
+
+// static
+void QuicSustainedBandwidthRecorderPeer::SetMaxBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32 max_bandwidth_estimate_kbytes_per_second,
+ int32 max_bandwidth_timestamp) {
+ bandwidth_recorder->max_bandwidth_estimate_ =
+ QuicBandwidth::FromKBytesPerSecond(
+ max_bandwidth_estimate_kbytes_per_second);
+ bandwidth_recorder->max_bandwidth_timestamp_ = max_bandwidth_timestamp;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h b/net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h
new file mode 100644
index 0000000..161a94e
--- /dev/null
+++ b/net/quic/test_tools/quic_sustained_bandwidth_recorder_peer.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
+
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class QuicSustainedBandwidthRecorder;
+
+namespace test {
+
+class QuicSustainedBandwidthRecorderPeer {
+ public:
+ static void SetBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32 bandwidth_estimate_kbytes_per_second);
+
+ static void SetMaxBandwidthEstimate(
+ QuicSustainedBandwidthRecorder* bandwidth_recorder,
+ int32 max_bandwidth_estimate_kbytes_per_second,
+ int32 max_bandwidth_timestamp);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(QuicSustainedBandwidthRecorderPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_SUSTAINED_BANDWIDTH_RECORDER_PEER_H_
diff --git a/net/quic/test_tools/quic_test_packet_maker.cc b/net/quic/test_tools/quic_test_packet_maker.cc
new file mode 100644
index 0000000..6540426
--- /dev/null
+++ b/net/quic/test_tools/quic_test_packet_maker.cc
@@ -0,0 +1,262 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_test_packet_maker.h"
+
+#include <list>
+
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+using std::make_pair;
+
+namespace net {
+namespace test {
+
+QuicTestPacketMaker::QuicTestPacketMaker(QuicVersion version,
+ QuicConnectionId connection_id,
+ MockClock* clock)
+ : version_(version),
+ connection_id_(connection_id),
+ clock_(clock),
+ spdy_request_framer_(SPDY3),
+ spdy_response_framer_(SPDY3) {
+}
+
+QuicTestPacketMaker::~QuicTestPacketMaker() {
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = include_version;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicRstStreamFrame rst(stream_id, error_code, 0);
+ return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&rst)));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckAndRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback) {
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = include_version;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(MakeAckFrame(largest_received));
+ ack.delta_time_largest_observed = QuicTime::Delta::Zero();
+ if (version_ > QUIC_VERSION_22) {
+ for (QuicPacketSequenceNumber i = least_unacked; i <= largest_received;
+ ++i) {
+ ack.received_packet_times.push_back(make_pair(i, clock_->Now()));
+ }
+ }
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ QuicCongestionFeedbackFrame feedback;
+ if (send_feedback && version_ <= QUIC_VERSION_22) {
+ feedback.type = kTCP;
+ feedback.tcp.receive_window = 256000;
+
+ frames.push_back(QuicFrame(&feedback));
+ }
+
+ QuicStopWaitingFrame stop_waiting;
+ stop_waiting.least_unacked = least_unacked;
+ frames.push_back(QuicFrame(&stop_waiting));
+
+ QuicRstStreamFrame rst(stream_id, error_code, 0);
+ frames.push_back(QuicFrame(&rst));
+
+ QuicFramer framer(SupportedVersions(version_), clock_->Now(), false);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeConnectionClosePacket(
+ QuicPacketSequenceNumber num) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = num;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicConnectionCloseFrame close;
+ close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED;
+ close.error_details = "Time to panic!";
+ return scoped_ptr<QuicEncryptedPacket>(MakePacket(header, QuicFrame(&close)));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id_;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = false;
+ header.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_flag = false;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicAckFrame ack(MakeAckFrame(largest_received));
+ ack.delta_time_largest_observed = QuicTime::Delta::Zero();
+ if (version_ > QUIC_VERSION_22) {
+ for (QuicPacketSequenceNumber i = least_unacked; i <= largest_received;
+ ++i) {
+ ack.received_packet_times.push_back(make_pair(i, clock_->Now()));
+ }
+ }
+
+ QuicFramer framer(SupportedVersions(version_), clock_->Now(), false);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(&ack));
+ QuicCongestionFeedbackFrame feedback;
+ if (send_feedback && version_ <= QUIC_VERSION_22) {
+ feedback.type = kTCP;
+ feedback.tcp.receive_window = 256000;
+ frames.push_back(QuicFrame(&feedback));
+ }
+
+ QuicStopWaitingFrame stop_waiting;
+ stop_waiting.least_unacked = least_unacked;
+ frames.push_back(QuicFrame(&stop_waiting));
+
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+// Returns a newly created packet to send kData on stream 1.
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data) {
+ InitializeHeader(sequence_number, should_include_version);
+ QuicStreamFrame frame(stream_id, fin, offset, MakeIOVector(data));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ InitializeHeader(sequence_number, should_include_version);
+ SpdySynStreamIR syn_stream(stream_id);
+ syn_stream.set_name_value_block(headers);
+ syn_stream.set_fin(fin);
+ syn_stream.set_priority(0);
+ scoped_ptr<SpdySerializedFrame> spdy_frame(
+ spdy_request_framer_.SerializeSynStream(syn_stream));
+ QuicStreamFrame frame(kHeadersStreamId, false, 0,
+ MakeIOVector(base::StringPiece(spdy_frame->data(),
+ spdy_frame->size())));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakeResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers) {
+ InitializeHeader(sequence_number, should_include_version);
+ SpdySynReplyIR syn_reply(stream_id);
+ syn_reply.set_name_value_block(headers);
+ syn_reply.set_fin(fin);
+ scoped_ptr<SpdySerializedFrame> spdy_frame(
+ spdy_response_framer_.SerializeSynReply(syn_reply));
+ QuicStreamFrame frame(kHeadersStreamId, false, 0,
+ MakeIOVector(base::StringPiece(spdy_frame->data(),
+ spdy_frame->size())));
+ return MakePacket(header_, QuicFrame(&frame));
+}
+
+SpdyHeaderBlock QuicTestPacketMaker::GetRequestHeaders(
+ const std::string& method,
+ const std::string& scheme,
+ const std::string& path) {
+ SpdyHeaderBlock headers;
+ headers[":method"] = method;
+ headers[":host"] = "www.google.com";
+ headers[":path"] = path;
+ headers[":scheme"] = scheme;
+ headers[":version"] = "HTTP/1.1";
+ return headers;
+}
+
+SpdyHeaderBlock QuicTestPacketMaker::GetResponseHeaders(
+ const std::string& status) {
+ SpdyHeaderBlock headers;
+ headers[":status"] = status;
+ headers[":version"] = "HTTP/1.1";
+ headers["content-type"] = "text/plain";
+ return headers;
+}
+
+scoped_ptr<QuicEncryptedPacket> QuicTestPacketMaker::MakePacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame) {
+ QuicFramer framer(SupportedVersions(version_), QuicTime::Zero(), false);
+ QuicFrames frames;
+ frames.push_back(frame);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket(
+ ENCRYPTION_NONE, header.packet_sequence_number, *packet));
+}
+
+void QuicTestPacketMaker::InitializeHeader(
+ QuicPacketSequenceNumber sequence_number,
+ bool should_include_version) {
+ header_.public_header.connection_id = connection_id_;
+ header_.public_header.reset_flag = false;
+ header_.public_header.version_flag = should_include_version;
+ header_.public_header.sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER;
+ header_.packet_sequence_number = sequence_number;
+ header_.fec_group = 0;
+ header_.entropy_flag = false;
+ header_.fec_flag = false;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_test_packet_maker.h b/net/quic/test_tools/quic_test_packet_maker.h
new file mode 100644
index 0000000..9120eeb
--- /dev/null
+++ b/net/quic/test_tools/quic_test_packet_maker.h
@@ -0,0 +1,95 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Provides a simple interface for QUIC tests to create a variety of packets.
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "net/base/request_priority.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+
+namespace net {
+namespace test {
+
+class QuicTestPacketMaker {
+ public:
+ QuicTestPacketMaker(QuicVersion version,
+ QuicConnectionId connection_id,
+ MockClock* clock);
+ ~QuicTestPacketMaker();
+
+ scoped_ptr<QuicEncryptedPacket> MakeRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code);
+ scoped_ptr<QuicEncryptedPacket> MakeAckAndRstPacket(
+ QuicPacketSequenceNumber num,
+ bool include_version,
+ QuicStreamId stream_id,
+ QuicRstStreamErrorCode error_code,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback);
+ scoped_ptr<QuicEncryptedPacket> MakeConnectionClosePacket(
+ QuicPacketSequenceNumber num);
+ scoped_ptr<QuicEncryptedPacket> MakeAckPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicPacketSequenceNumber largest_received,
+ QuicPacketSequenceNumber least_unacked,
+ bool send_feedback);
+ scoped_ptr<QuicEncryptedPacket> MakeDataPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ QuicStreamOffset offset,
+ base::StringPiece data);
+ scoped_ptr<QuicEncryptedPacket> MakeRequestHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers);
+ scoped_ptr<QuicEncryptedPacket> MakeResponseHeadersPacket(
+ QuicPacketSequenceNumber sequence_number,
+ QuicStreamId stream_id,
+ bool should_include_version,
+ bool fin,
+ const SpdyHeaderBlock& headers);
+
+ SpdyHeaderBlock GetRequestHeaders(const std::string& method,
+ const std::string& scheme,
+ const std::string& path);
+ SpdyHeaderBlock GetResponseHeaders(const std::string& status);
+
+ private:
+ scoped_ptr<QuicEncryptedPacket> MakePacket(
+ const QuicPacketHeader& header,
+ const QuicFrame& frame);
+
+ void InitializeHeader(QuicPacketSequenceNumber sequence_number,
+ bool should_include_version);
+
+ QuicVersion version_;
+ QuicConnectionId connection_id_;
+ MockClock* clock_; // Owned by QuicStreamFactory.
+ SpdyFramer spdy_request_framer_;
+ SpdyFramer spdy_response_framer_;
+ MockRandom random_generator_;
+ QuicPacketHeader header_;
+
+ DISALLOW_COPY_AND_ASSIGN(QuicTestPacketMaker);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PACKET_MAKER_H_
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
new file mode 100644
index 0000000..0cf13e1
--- /dev/null
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -0,0 +1,678 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/quic_test_utils.h"
+
+#include "base/sha1.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/crypto_handshake.h"
+#include "net/quic/crypto/crypto_utils.h"
+#include "net/quic/crypto/null_encrypter.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_packet_creator.h"
+#include "net/quic/quic_utils.h"
+#include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/spdy/spdy_frame_builder.h"
+
+using base::StringPiece;
+using std::max;
+using std::min;
+using std::string;
+using testing::AnyNumber;
+using testing::_;
+
+namespace net {
+namespace test {
+namespace {
+
+// No-op alarm implementation used by MockHelper.
+class TestAlarm : public QuicAlarm {
+ public:
+ explicit TestAlarm(QuicAlarm::Delegate* delegate)
+ : QuicAlarm(delegate) {
+ }
+
+ virtual void SetImpl() OVERRIDE {}
+ virtual void CancelImpl() OVERRIDE {}
+};
+
+} // namespace
+
+QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed) {
+ QuicAckFrame ack;
+ ack.largest_observed = largest_observed;
+ ack.entropy_hash = 0;
+ return ack;
+}
+
+QuicAckFrame MakeAckFrameWithNackRanges(
+ size_t num_nack_ranges, QuicPacketSequenceNumber least_unacked) {
+ QuicAckFrame ack = MakeAckFrame(2 * num_nack_ranges + least_unacked);
+ // Add enough missing packets to get num_nack_ranges nack ranges.
+ for (QuicPacketSequenceNumber i = 1; i < 2 * num_nack_ranges; i += 2) {
+ ack.missing_packets.insert(least_unacked + i);
+ }
+ return ack;
+}
+
+SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames) {
+ const size_t max_plaintext_size = framer->GetMaxPlaintextSize(kMaxPacketSize);
+ size_t packet_size = GetPacketHeaderSize(header);
+ for (size_t i = 0; i < frames.size(); ++i) {
+ DCHECK_LE(packet_size, max_plaintext_size);
+ bool first_frame = i == 0;
+ bool last_frame = i == frames.size() - 1;
+ const size_t frame_size = framer->GetSerializedFrameLength(
+ frames[i], max_plaintext_size - packet_size, first_frame, last_frame,
+ header.is_in_fec_group,
+ header.public_header.sequence_number_length);
+ DCHECK(frame_size);
+ packet_size += frame_size;
+ }
+ return framer->BuildDataPacket(header, frames, packet_size);
+}
+
+uint64 SimpleRandom::RandUint64() {
+ unsigned char hash[base::kSHA1Length];
+ base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_),
+ hash);
+ memcpy(&seed_, hash, sizeof(seed_));
+ return seed_;
+}
+
+MockFramerVisitor::MockFramerVisitor() {
+ // By default, we want to accept packets.
+ ON_CALL(*this, OnProtocolVersionMismatch(_))
+ .WillByDefault(testing::Return(false));
+
+ // By default, we want to accept packets.
+ ON_CALL(*this, OnUnauthenticatedHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnUnauthenticatedPublicHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPacketHeader(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStreamFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnAckFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnCongestionFeedbackFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnStopWaitingFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnPingFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnRstStreamFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnConnectionCloseFrame(_))
+ .WillByDefault(testing::Return(true));
+
+ ON_CALL(*this, OnGoAwayFrame(_))
+ .WillByDefault(testing::Return(true));
+}
+
+MockFramerVisitor::~MockFramerVisitor() {
+}
+
+bool NoOpFramerVisitor::OnProtocolVersionMismatch(QuicVersion version) {
+ return false;
+}
+
+bool NoOpFramerVisitor::OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPacketHeader(const QuicPacketHeader& header) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStreamFrame(const QuicStreamFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnAckFrame(const QuicAckFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnPingFrame(const QuicPingFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnRstStreamFrame(
+ const QuicRstStreamFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) {
+ return true;
+}
+
+bool NoOpFramerVisitor::OnBlockedFrame(const QuicBlockedFrame& frame) {
+ return true;
+}
+
+MockConnectionVisitor::MockConnectionVisitor() {
+}
+
+MockConnectionVisitor::~MockConnectionVisitor() {
+}
+
+MockHelper::MockHelper() {
+}
+
+MockHelper::~MockHelper() {
+}
+
+const QuicClock* MockHelper::GetClock() const {
+ return &clock_;
+}
+
+QuicRandom* MockHelper::GetRandomGenerator() {
+ return &random_generator_;
+}
+
+QuicAlarm* MockHelper::CreateAlarm(QuicAlarm::Delegate* delegate) {
+ return new TestAlarm(delegate);
+}
+
+void MockHelper::AdvanceTime(QuicTime::Delta delta) {
+ clock_.AdvanceTime(delta);
+}
+
+namespace {
+class NiceMockPacketWriterFactory
+ : public QuicConnection::PacketWriterFactory {
+ public:
+ NiceMockPacketWriterFactory() {}
+ virtual ~NiceMockPacketWriterFactory() {}
+
+ virtual QuicPacketWriter* Create(
+ QuicConnection* /*connection*/) const OVERRIDE {
+ return new testing::NiceMock<MockPacketWriter>();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NiceMockPacketWriterFactory);
+};
+} // namespace
+
+MockConnection::MockConnection(bool is_server)
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
+ new testing::NiceMock<MockHelper>(),
+ NiceMockPacketWriterFactory(),
+ /* owns_writer= */ true,
+ is_server, QuicSupportedVersions()),
+ helper_(helper()) {
+}
+
+MockConnection::MockConnection(IPEndPoint address,
+ bool is_server)
+ : QuicConnection(kTestConnectionId, address,
+ new testing::NiceMock<MockHelper>(),
+ NiceMockPacketWriterFactory(),
+ /* owns_writer= */ true,
+ is_server, QuicSupportedVersions()),
+ helper_(helper()) {
+}
+
+MockConnection::MockConnection(QuicConnectionId connection_id,
+ bool is_server)
+ : QuicConnection(connection_id,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
+ new testing::NiceMock<MockHelper>(),
+ NiceMockPacketWriterFactory(),
+ /* owns_writer= */ true,
+ is_server, QuicSupportedVersions()),
+ helper_(helper()) {
+}
+
+MockConnection::MockConnection(bool is_server,
+ const QuicVersionVector& supported_versions)
+ : QuicConnection(kTestConnectionId,
+ IPEndPoint(TestPeerIPAddress(), kTestPort),
+ new testing::NiceMock<MockHelper>(),
+ NiceMockPacketWriterFactory(),
+ /* owns_writer= */ true,
+ is_server, supported_versions),
+ helper_(helper()) {
+}
+
+MockConnection::~MockConnection() {
+}
+
+void MockConnection::AdvanceTime(QuicTime::Delta delta) {
+ static_cast<MockHelper*>(helper())->AdvanceTime(delta);
+}
+
+PacketSavingConnection::PacketSavingConnection(bool is_server)
+ : MockConnection(is_server) {
+}
+
+PacketSavingConnection::PacketSavingConnection(
+ bool is_server,
+ const QuicVersionVector& supported_versions)
+ : MockConnection(is_server, supported_versions) {
+}
+
+PacketSavingConnection::~PacketSavingConnection() {
+ STLDeleteElements(&packets_);
+ STLDeleteElements(&encrypted_packets_);
+}
+
+void PacketSavingConnection::SendOrQueuePacket(QueuedPacket packet) {
+ packets_.push_back(packet.serialized_packet.packet);
+ QuicEncryptedPacket* encrypted = QuicConnectionPeer::GetFramer(this)->
+ EncryptPacket(packet.encryption_level,
+ packet.serialized_packet.sequence_number,
+ *packet.serialized_packet.packet);
+ encrypted_packets_.push_back(encrypted);
+ // Transfer ownership of the packet to the SentPacketManager and the
+ // ack notifier to the AckNotifierManager.
+ sent_packet_manager_.OnPacketSent(
+ &packet.serialized_packet, 0, QuicTime::Zero(), 1000,
+ NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA);
+}
+
+MockSession::MockSession(QuicConnection* connection)
+ : QuicSession(connection, DefaultQuicConfig()) {
+ InitializeSession();
+ ON_CALL(*this, WritevData(_, _, _, _, _, _))
+ .WillByDefault(testing::Return(QuicConsumedData(0, false)));
+}
+
+MockSession::~MockSession() {
+}
+
+TestSession::TestSession(QuicConnection* connection, const QuicConfig& config)
+ : QuicSession(connection, config), crypto_stream_(nullptr) {
+ InitializeSession();
+}
+
+TestSession::~TestSession() {}
+
+void TestSession::SetCryptoStream(QuicCryptoStream* stream) {
+ crypto_stream_ = stream;
+}
+
+QuicCryptoStream* TestSession::GetCryptoStream() {
+ return crypto_stream_;
+}
+
+TestClientSession::TestClientSession(QuicConnection* connection,
+ const QuicConfig& config)
+ : QuicClientSessionBase(connection, config),
+ crypto_stream_(nullptr) {
+ EXPECT_CALL(*this, OnProofValid(_)).Times(AnyNumber());
+ InitializeSession();
+}
+
+TestClientSession::~TestClientSession() {}
+
+void TestClientSession::SetCryptoStream(QuicCryptoStream* stream) {
+ crypto_stream_ = stream;
+}
+
+QuicCryptoStream* TestClientSession::GetCryptoStream() {
+ return crypto_stream_;
+}
+
+MockPacketWriter::MockPacketWriter() {
+}
+
+MockPacketWriter::~MockPacketWriter() {
+}
+
+MockSendAlgorithm::MockSendAlgorithm() {
+}
+
+MockSendAlgorithm::~MockSendAlgorithm() {
+}
+
+MockLossAlgorithm::MockLossAlgorithm() {
+}
+
+MockLossAlgorithm::~MockLossAlgorithm() {
+}
+
+MockAckNotifierDelegate::MockAckNotifierDelegate() {
+}
+
+MockAckNotifierDelegate::~MockAckNotifierDelegate() {
+}
+
+MockNetworkChangeVisitor::MockNetworkChangeVisitor() {
+}
+
+MockNetworkChangeVisitor::~MockNetworkChangeVisitor() {
+}
+
+namespace {
+
+string HexDumpWithMarks(const char* data, int length,
+ const bool* marks, int mark_length) {
+ static const char kHexChars[] = "0123456789abcdef";
+ static const int kColumns = 4;
+
+ const int kSizeLimit = 1024;
+ if (length > kSizeLimit || mark_length > kSizeLimit) {
+ LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
+ length = min(length, kSizeLimit);
+ mark_length = min(mark_length, kSizeLimit);
+ }
+
+ string hex;
+ for (const char* row = data; length > 0;
+ row += kColumns, length -= kColumns) {
+ for (const char *p = row; p < row + 4; ++p) {
+ if (p < row + length) {
+ const bool mark =
+ (marks && (p - data) < mark_length && marks[p - data]);
+ hex += mark ? '*' : ' ';
+ hex += kHexChars[(*p & 0xf0) >> 4];
+ hex += kHexChars[*p & 0x0f];
+ hex += mark ? '*' : ' ';
+ } else {
+ hex += " ";
+ }
+ }
+ hex = hex + " ";
+
+ for (const char *p = row; p < row + 4 && p < row + length; ++p)
+ hex += (*p >= 0x20 && *p <= 0x7f) ? (*p) : '.';
+
+ hex = hex + '\n';
+ }
+ return hex;
+}
+
+} // namespace
+
+IPAddressNumber TestPeerIPAddress() { return Loopback4(); }
+
+QuicVersion QuicVersionMax() { return QuicSupportedVersions().front(); }
+
+QuicVersion QuicVersionMin() { return QuicSupportedVersions().back(); }
+
+IPAddressNumber Loopback4() {
+ IPAddressNumber addr;
+ CHECK(ParseIPLiteralToNumber("127.0.0.1", &addr));
+ return addr;
+}
+
+IPAddressNumber Loopback6() {
+ IPAddressNumber addr;
+ CHECK(ParseIPLiteralToNumber("::1", &addr));
+ return addr;
+}
+
+void GenerateBody(string* body, int length) {
+ body->clear();
+ body->reserve(length);
+ for (int i = 0; i < length; ++i) {
+ body->append(1, static_cast<char>(32 + i % (126 - 32)));
+ }
+}
+
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId connection_id,
+ bool version_flag,
+ bool reset_flag,
+ QuicPacketSequenceNumber sequence_number,
+ const string& data) {
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id;
+ header.public_header.connection_id_length = PACKET_8BYTE_CONNECTION_ID;
+ header.public_header.version_flag = version_flag;
+ header.public_header.reset_flag = reset_flag;
+ header.public_header.sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER;
+ header.packet_sequence_number = sequence_number;
+ header.entropy_flag = false;
+ header.entropy_hash = 0;
+ header.fec_flag = false;
+ header.is_in_fec_group = NOT_IN_FEC_GROUP;
+ header.fec_group = 0;
+ QuicStreamFrame stream_frame(1, false, 0, MakeIOVector(data));
+ QuicFrame frame(&stream_frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), false);
+ scoped_ptr<QuicPacket> packet(
+ BuildUnsizedDataPacket(&framer, header, frames).packet);
+ EXPECT_TRUE(packet != nullptr);
+ QuicEncryptedPacket* encrypted = framer.EncryptPacket(ENCRYPTION_NONE,
+ sequence_number,
+ *packet);
+ EXPECT_TRUE(encrypted != nullptr);
+ return encrypted;
+}
+
+void CompareCharArraysWithHexError(
+ const string& description,
+ const char* actual,
+ const int actual_len,
+ const char* expected,
+ const int expected_len) {
+ EXPECT_EQ(actual_len, expected_len);
+ const int min_len = min(actual_len, expected_len);
+ const int max_len = max(actual_len, expected_len);
+ scoped_ptr<bool[]> marks(new bool[max_len]);
+ bool identical = (actual_len == expected_len);
+ for (int i = 0; i < min_len; ++i) {
+ if (actual[i] != expected[i]) {
+ marks[i] = true;
+ identical = false;
+ } else {
+ marks[i] = false;
+ }
+ }
+ for (int i = min_len; i < max_len; ++i) {
+ marks[i] = true;
+ }
+ if (identical) return;
+ ADD_FAILURE()
+ << "Description:\n"
+ << description
+ << "\n\nExpected:\n"
+ << HexDumpWithMarks(expected, expected_len, marks.get(), max_len)
+ << "\nActual:\n"
+ << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
+}
+
+bool DecodeHexString(const base::StringPiece& hex, std::string* bytes) {
+ bytes->clear();
+ if (hex.empty())
+ return true;
+ std::vector<uint8> v;
+ if (!base::HexStringToBytes(hex.as_string(), &v))
+ return false;
+ if (!v.empty())
+ bytes->assign(reinterpret_cast<const char*>(&v[0]), v.size());
+ return true;
+}
+
+static QuicPacket* ConstructPacketFromHandshakeMessage(
+ QuicConnectionId connection_id,
+ const CryptoHandshakeMessage& message,
+ bool should_include_version) {
+ CryptoFramer crypto_framer;
+ scoped_ptr<QuicData> data(crypto_framer.ConstructHandshakeMessage(message));
+ QuicFramer quic_framer(QuicSupportedVersions(), QuicTime::Zero(), false);
+
+ QuicPacketHeader header;
+ header.public_header.connection_id = connection_id;
+ header.public_header.reset_flag = false;
+ header.public_header.version_flag = should_include_version;
+ header.packet_sequence_number = 1;
+ header.entropy_flag = false;
+ header.entropy_hash = 0;
+ header.fec_flag = false;
+ header.fec_group = 0;
+
+ QuicStreamFrame stream_frame(kCryptoStreamId, false, 0,
+ MakeIOVector(data->AsStringPiece()));
+
+ QuicFrame frame(&stream_frame);
+ QuicFrames frames;
+ frames.push_back(frame);
+ return BuildUnsizedDataPacket(&quic_framer, header, frames).packet;
+}
+
+QuicPacket* ConstructHandshakePacket(QuicConnectionId connection_id,
+ QuicTag tag) {
+ CryptoHandshakeMessage message;
+ message.set_tag(tag);
+ return ConstructPacketFromHandshakeMessage(connection_id, message, false);
+}
+
+size_t GetPacketLengthForOneStream(
+ QuicVersion version,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length,
+ InFecGroup is_in_fec_group,
+ size_t* payload_length) {
+ *payload_length = 1;
+ const size_t stream_length =
+ NullEncrypter().GetCiphertextSize(*payload_length) +
+ QuicPacketCreator::StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, 0u, is_in_fec_group);
+ const size_t ack_length = NullEncrypter().GetCiphertextSize(
+ QuicFramer::GetMinAckFrameSize(
+ sequence_number_length, PACKET_1BYTE_SEQUENCE_NUMBER)) +
+ GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, is_in_fec_group);
+ if (stream_length < ack_length) {
+ *payload_length = 1 + ack_length - stream_length;
+ }
+
+ return NullEncrypter().GetCiphertextSize(*payload_length) +
+ QuicPacketCreator::StreamFramePacketOverhead(
+ PACKET_8BYTE_CONNECTION_ID, include_version,
+ sequence_number_length, 0u, is_in_fec_group);
+}
+
+TestEntropyCalculator::TestEntropyCalculator() {}
+
+TestEntropyCalculator::~TestEntropyCalculator() {}
+
+QuicPacketEntropyHash TestEntropyCalculator::EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const {
+ return 1u;
+}
+
+MockEntropyCalculator::MockEntropyCalculator() {}
+
+MockEntropyCalculator::~MockEntropyCalculator() {}
+
+QuicConfig DefaultQuicConfig() {
+ QuicConfig config;
+ config.SetDefaults();
+ config.SetInitialFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ config.SetInitialStreamFlowControlWindowToSend(
+ kInitialStreamFlowControlWindowForTest);
+ config.SetInitialSessionFlowControlWindowToSend(
+ kInitialSessionFlowControlWindowForTest);
+ return config;
+}
+
+QuicVersionVector SupportedVersions(QuicVersion version) {
+ QuicVersionVector versions;
+ versions.push_back(version);
+ return versions;
+}
+
+TestWriterFactory::TestWriterFactory() : current_writer_(nullptr) {}
+TestWriterFactory::~TestWriterFactory() {}
+
+QuicPacketWriter* TestWriterFactory::Create(QuicServerPacketWriter* writer,
+ QuicConnection* connection) {
+ return new PerConnectionPacketWriter(this, writer, connection);
+}
+
+void TestWriterFactory::OnPacketSent(WriteResult result) {
+ if (current_writer_ != nullptr && result.status == WRITE_STATUS_ERROR) {
+ current_writer_->connection()->OnWriteError(result.error_code);
+ current_writer_ = nullptr;
+ }
+}
+
+void TestWriterFactory::Unregister(PerConnectionPacketWriter* writer) {
+ if (current_writer_ == writer) {
+ current_writer_ = nullptr;
+ }
+}
+
+TestWriterFactory::PerConnectionPacketWriter::PerConnectionPacketWriter(
+ TestWriterFactory* factory,
+ QuicServerPacketWriter* writer,
+ QuicConnection* connection)
+ : QuicPerConnectionPacketWriter(writer, connection),
+ factory_(factory) {
+}
+
+TestWriterFactory::PerConnectionPacketWriter::~PerConnectionPacketWriter() {
+ factory_->Unregister(this);
+}
+
+WriteResult TestWriterFactory::PerConnectionPacketWriter::WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) {
+ // A DCHECK(factory_current_writer_ == nullptr) would be wrong here -- this
+ // class may be used in a setting where connection()->OnPacketSent() is called
+ // in a different way, so TestWriterFactory::OnPacketSent might never be
+ // called.
+ factory_->current_writer_ = this;
+ return QuicPerConnectionPacketWriter::WritePacket(buffer,
+ buf_len,
+ self_address,
+ peer_address);
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
new file mode 100644
index 0000000..4c57a9b
--- /dev/null
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -0,0 +1,591 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Common utilities for Quic tests
+
+#ifndef NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+#define NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+#include "net/quic/congestion_control/loss_detection_interface.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_ack_notifier.h"
+#include "net/quic/quic_client_session_base.h"
+#include "net/quic/quic_connection.h"
+#include "net/quic/quic_dispatcher.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_per_connection_packet_writer.h"
+#include "net/quic/quic_sent_packet_manager.h"
+#include "net/quic/quic_session.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/mock_random.h"
+#include "net/spdy/spdy_framer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+
+namespace test {
+
+static const QuicConnectionId kTestConnectionId = 42;
+static const int kTestPort = 123;
+static const uint32 kInitialStreamFlowControlWindowForTest =
+ 32 * 1024; // 32 KB
+static const uint32 kInitialSessionFlowControlWindowForTest =
+ 64 * 1024; // 64 KB
+
+// Data stream IDs start at 5: the crypto stream is 1, headers stream is 3.
+static const QuicStreamId kClientDataStreamId1 = 5;
+static const QuicStreamId kClientDataStreamId2 = 7;
+static const QuicStreamId kClientDataStreamId3 = 9;
+static const QuicStreamId kClientDataStreamId4 = 11;
+
+// Returns the test peer IP address.
+IPAddressNumber TestPeerIPAddress();
+
+// Upper limit on versions we support.
+QuicVersion QuicVersionMax();
+
+// Lower limit on versions we support.
+QuicVersion QuicVersionMin();
+
+// Returns an address for 127.0.0.1.
+IPAddressNumber Loopback4();
+
+// Returns an address for ::1.
+IPAddressNumber Loopback6();
+
+void GenerateBody(std::string* body, int length);
+
+// Create an encrypted packet for testing.
+QuicEncryptedPacket* ConstructEncryptedPacket(
+ QuicConnectionId connection_id,
+ bool version_flag,
+ bool reset_flag,
+ QuicPacketSequenceNumber sequence_number,
+ const std::string& data);
+
+void CompareCharArraysWithHexError(const std::string& description,
+ const char* actual,
+ const int actual_len,
+ const char* expected,
+ const int expected_len);
+
+bool DecodeHexString(const base::StringPiece& hex, std::string* bytes);
+
+// Returns the length of a QuicPacket that is capable of holding either a
+// stream frame or a minimal ack frame. Sets |*payload_length| to the number
+// of bytes of stream data that will fit in such a packet.
+size_t GetPacketLengthForOneStream(
+ QuicVersion version,
+ bool include_version,
+ QuicSequenceNumberLength sequence_number_length,
+ InFecGroup is_in_fec_group,
+ size_t* payload_length);
+
+// Returns QuicConfig set to default values.
+QuicConfig DefaultQuicConfig();
+
+// Returns a version vector consisting of |version|.
+QuicVersionVector SupportedVersions(QuicVersion version);
+
+// Testing convenience method to construct a QuicAckFrame with entropy_hash set
+// to 0 and largest_observed from peer set to |largest_observed|.
+QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed);
+
+// Testing convenience method to construct a QuicAckFrame with |num_nack_ranges|
+// nack ranges of width 1 packet, starting from |least_unacked|.
+QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges,
+ QuicPacketSequenceNumber least_unacked);
+
+// Returns a SerializedPacket whose |packet| member is owned by the caller, and
+// is populated with the fields in |header| and |frames|, or is nullptr if the
+// packet could not be created.
+SerializedPacket BuildUnsizedDataPacket(QuicFramer* framer,
+ const QuicPacketHeader& header,
+ const QuicFrames& frames);
+
+template<typename SaveType>
+class ValueRestore {
+ public:
+ ValueRestore(SaveType* name, SaveType value)
+ : name_(name),
+ value_(*name) {
+ *name_ = value;
+ }
+ ~ValueRestore() {
+ *name_ = value_;
+ }
+
+ private:
+ SaveType* name_;
+ SaveType value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueRestore);
+};
+
+// Simple random number generator used to compute random numbers suitable
+// for pseudo-randomly dropping packets in tests. It works by computing
+// the sha1 hash of the current seed, and using the first 64 bits as
+// the next random number, and the next seed.
+class SimpleRandom {
+ public:
+ SimpleRandom() : seed_(0) {}
+
+ // Returns a random number in the range [0, kuint64max].
+ uint64 RandUint64();
+
+ void set_seed(uint64 seed) { seed_ = seed; }
+
+ private:
+ uint64 seed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleRandom);
+};
+
+class MockFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ MockFramerVisitor();
+ virtual ~MockFramerVisitor();
+
+ MOCK_METHOD1(OnError, void(QuicFramer* framer));
+ // The constructor sets this up to return false by default.
+ MOCK_METHOD1(OnProtocolVersionMismatch, bool(QuicVersion version));
+ MOCK_METHOD0(OnPacket, void());
+ MOCK_METHOD1(OnPublicResetPacket, void(const QuicPublicResetPacket& header));
+ MOCK_METHOD1(OnVersionNegotiationPacket,
+ void(const QuicVersionNegotiationPacket& packet));
+ MOCK_METHOD0(OnRevivedPacket, void());
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedHeader, bool(const QuicPacketHeader& header));
+ // The constructor sets this up to return true by default.
+ MOCK_METHOD1(OnUnauthenticatedPublicHeader, bool(
+ const QuicPacketPublicHeader& header));
+ MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
+ MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
+ MOCK_METHOD1(OnFecProtectedPayload, void(base::StringPiece payload));
+ MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
+ MOCK_METHOD1(OnAckFrame, bool(const QuicAckFrame& frame));
+ MOCK_METHOD1(OnCongestionFeedbackFrame,
+ bool(const QuicCongestionFeedbackFrame& frame));
+ MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame));
+ MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame));
+ MOCK_METHOD1(OnFecData, void(const QuicFecData& fec));
+ MOCK_METHOD1(OnRstStreamFrame, bool(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnConnectionCloseFrame,
+ bool(const QuicConnectionCloseFrame& frame));
+ MOCK_METHOD1(OnGoAwayFrame, bool(const QuicGoAwayFrame& frame));
+ MOCK_METHOD1(OnWindowUpdateFrame, bool(const QuicWindowUpdateFrame& frame));
+ MOCK_METHOD1(OnBlockedFrame, bool(const QuicBlockedFrame& frame));
+ MOCK_METHOD0(OnPacketComplete, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockFramerVisitor);
+};
+
+class NoOpFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ NoOpFramerVisitor() {}
+
+ virtual void OnError(QuicFramer* framer) OVERRIDE {}
+ virtual void OnPacket() OVERRIDE {}
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE {}
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) OVERRIDE {}
+ virtual void OnRevivedPacket() OVERRIDE {}
+ virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE;
+ virtual bool OnUnauthenticatedHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE;
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE;
+ virtual void OnFecProtectedPayload(base::StringPiece payload) OVERRIDE {}
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE;
+ virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE;
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) OVERRIDE;
+ virtual bool OnStopWaitingFrame(
+ const QuicStopWaitingFrame& frame) OVERRIDE;
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE;
+ virtual void OnFecData(const QuicFecData& fec) OVERRIDE {}
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE;
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) OVERRIDE;
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE;
+ virtual bool OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) OVERRIDE;
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE;
+ virtual void OnPacketComplete() OVERRIDE {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NoOpFramerVisitor);
+};
+
+class MockConnectionVisitor : public QuicConnectionVisitorInterface {
+ public:
+ MockConnectionVisitor();
+ virtual ~MockConnectionVisitor();
+
+ MOCK_METHOD1(OnStreamFrames, void(const std::vector<QuicStreamFrame>& frame));
+ MOCK_METHOD1(OnWindowUpdateFrames,
+ void(const std::vector<QuicWindowUpdateFrame>& frame));
+ MOCK_METHOD1(OnBlockedFrames,
+ void(const std::vector<QuicBlockedFrame>& frame));
+ MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame));
+ MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame));
+ MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
+ MOCK_METHOD0(OnWriteBlocked, void());
+ MOCK_METHOD0(OnCanWrite, void());
+ MOCK_METHOD1(OnCongestionWindowChange, void(QuicTime now));
+ MOCK_CONST_METHOD0(WillingAndAbleToWrite, bool());
+ MOCK_CONST_METHOD0(HasPendingHandshake, bool());
+ MOCK_CONST_METHOD0(HasOpenDataStreams, bool());
+ MOCK_METHOD1(OnSuccessfulVersionNegotiation,
+ void(const QuicVersion& version));
+ MOCK_METHOD0(OnConfigNegotiated, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockConnectionVisitor);
+};
+
+class MockHelper : public QuicConnectionHelperInterface {
+ public:
+ MockHelper();
+ virtual ~MockHelper();
+ virtual const QuicClock* GetClock() const OVERRIDE;
+ virtual QuicRandom* GetRandomGenerator() OVERRIDE;
+ virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE;
+ void AdvanceTime(QuicTime::Delta delta);
+
+ private:
+ MockClock clock_;
+ MockRandom random_generator_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockHelper);
+};
+
+class MockConnection : public QuicConnection {
+ public:
+ // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123.
+ explicit MockConnection(bool is_server);
+
+ // Uses a MockHelper, ConnectionId of 42.
+ MockConnection(IPEndPoint address, bool is_server);
+
+ // Uses a MockHelper, and 127.0.0.1:123
+ MockConnection(QuicConnectionId connection_id, bool is_server);
+
+ // Uses a Mock helper, ConnectionId of 42, and 127.0.0.1:123.
+ MockConnection(bool is_server, const QuicVersionVector& supported_versions);
+
+ virtual ~MockConnection();
+
+ // If the constructor that uses a MockHelper has been used then this method
+ // will advance the time of the MockClock.
+ void AdvanceTime(QuicTime::Delta delta);
+
+ MOCK_METHOD3(ProcessUdpPacket, void(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet));
+ MOCK_METHOD1(SendConnectionClose, void(QuicErrorCode error));
+ MOCK_METHOD2(SendConnectionCloseWithDetails, void(QuicErrorCode error,
+ const string& details));
+ MOCK_METHOD2(SendConnectionClosePacket, void(QuicErrorCode error,
+ const string& details));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+ MOCK_METHOD3(SendGoAway, void(QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const string& reason));
+ MOCK_METHOD1(SendBlocked, void(QuicStreamId id));
+ MOCK_METHOD2(SendWindowUpdate, void(QuicStreamId id,
+ QuicStreamOffset byte_offset));
+ MOCK_METHOD0(OnCanWrite, void());
+
+ void ProcessUdpPacketInternal(const IPEndPoint& self_address,
+ const IPEndPoint& peer_address,
+ const QuicEncryptedPacket& packet) {
+ QuicConnection::ProcessUdpPacket(self_address, peer_address, packet);
+ }
+
+ virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE {
+ return false;
+ }
+
+ private:
+ scoped_ptr<QuicConnectionHelperInterface> helper_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockConnection);
+};
+
+class PacketSavingConnection : public MockConnection {
+ public:
+ explicit PacketSavingConnection(bool is_server);
+
+ PacketSavingConnection(bool is_server,
+ const QuicVersionVector& supported_versions);
+
+ virtual ~PacketSavingConnection();
+
+ virtual void SendOrQueuePacket(QueuedPacket packet) OVERRIDE;
+
+ std::vector<QuicPacket*> packets_;
+ std::vector<QuicEncryptedPacket*> encrypted_packets_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PacketSavingConnection);
+};
+
+class MockSession : public QuicSession {
+ public:
+ explicit MockSession(QuicConnection* connection);
+ virtual ~MockSession();
+ MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
+ MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
+ MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*());
+ MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
+ MOCK_METHOD6(WritevData,
+ QuicConsumedData(QuicStreamId id,
+ const IOVector& data,
+ QuicStreamOffset offset,
+ bool fin,
+ FecProtection fec_protection,
+ QuicAckNotifier::DelegateInterface*));
+ MOCK_METHOD2(OnStreamHeaders, void(QuicStreamId stream_id,
+ base::StringPiece headers_data));
+ MOCK_METHOD2(OnStreamHeadersPriority, void(QuicStreamId stream_id,
+ QuicPriority priority));
+ MOCK_METHOD3(OnStreamHeadersComplete, void(QuicStreamId stream_id,
+ bool fin,
+ size_t frame_len));
+ MOCK_METHOD3(SendRstStream, void(QuicStreamId stream_id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written));
+ MOCK_METHOD0(IsCryptoHandshakeConfirmed, bool());
+
+ using QuicSession::ActivateStream;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSession);
+};
+
+class TestSession : public QuicSession {
+ public:
+ TestSession(QuicConnection* connection, const QuicConfig& config);
+ virtual ~TestSession();
+
+ MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
+ MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
+
+ void SetCryptoStream(QuicCryptoStream* stream);
+
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
+
+ private:
+ QuicCryptoStream* crypto_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSession);
+};
+
+class TestClientSession : public QuicClientSessionBase {
+ public:
+ TestClientSession(QuicConnection* connection, const QuicConfig& config);
+ virtual ~TestClientSession();
+
+ // QuicClientSessionBase
+ MOCK_METHOD1(OnProofValid,
+ void(const QuicCryptoClientConfig::CachedState& cached));
+ MOCK_METHOD1(OnProofVerifyDetailsAvailable,
+ void(const ProofVerifyDetails& verify_details));
+
+ // TestClientSession
+ MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
+ MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
+
+ void SetCryptoStream(QuicCryptoStream* stream);
+
+ virtual QuicCryptoStream* GetCryptoStream() OVERRIDE;
+
+ private:
+ QuicCryptoStream* crypto_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestClientSession);
+};
+
+class MockPacketWriter : public QuicPacketWriter {
+ public:
+ MockPacketWriter();
+ virtual ~MockPacketWriter();
+
+ MOCK_METHOD4(WritePacket,
+ WriteResult(const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address));
+ MOCK_CONST_METHOD0(IsWriteBlockedDataBuffered, bool());
+ MOCK_CONST_METHOD0(IsWriteBlocked, bool());
+ MOCK_METHOD0(SetWritable, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockPacketWriter);
+};
+
+class MockSendAlgorithm : public SendAlgorithmInterface {
+ public:
+ MockSendAlgorithm();
+ virtual ~MockSendAlgorithm();
+
+ MOCK_METHOD2(SetFromConfig, void(const QuicConfig& config, bool is_server));
+ MOCK_METHOD1(SetNumEmulatedConnections, void(int num_connections));
+ MOCK_METHOD1(SetMaxPacketSize, void(QuicByteCount max_packet_size));
+ MOCK_METHOD2(OnIncomingQuicCongestionFeedbackFrame,
+ void(const QuicCongestionFeedbackFrame&,
+ QuicTime feedback_receive_time));
+ MOCK_METHOD4(OnCongestionEvent, void(bool rtt_updated,
+ QuicByteCount bytes_in_flight,
+ const CongestionVector& acked_packets,
+ const CongestionVector& lost_packets));
+ MOCK_METHOD5(OnPacketSent,
+ bool(QuicTime, QuicByteCount, QuicPacketSequenceNumber,
+ QuicByteCount, HasRetransmittableData));
+ MOCK_METHOD1(OnRetransmissionTimeout, void(bool));
+ MOCK_METHOD0(RevertRetransmissionTimeout, void());
+ MOCK_CONST_METHOD3(TimeUntilSend,
+ QuicTime::Delta(QuicTime now,
+ QuicByteCount bytes_in_flight,
+ HasRetransmittableData));
+ MOCK_CONST_METHOD0(BandwidthEstimate, QuicBandwidth(void));
+ MOCK_CONST_METHOD0(HasReliableBandwidthEstimate, bool());
+ MOCK_METHOD1(OnRttUpdated, void(QuicPacketSequenceNumber));
+ MOCK_CONST_METHOD0(RetransmissionDelay, QuicTime::Delta(void));
+ MOCK_CONST_METHOD0(GetCongestionWindow, QuicByteCount());
+ MOCK_CONST_METHOD0(InSlowStart, bool());
+ MOCK_CONST_METHOD0(InRecovery, bool());
+ MOCK_CONST_METHOD0(GetSlowStartThreshold, QuicByteCount());
+ MOCK_CONST_METHOD0(GetCongestionControlType, CongestionControlType());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm);
+};
+
+class MockLossAlgorithm : public LossDetectionInterface {
+ public:
+ MockLossAlgorithm();
+ virtual ~MockLossAlgorithm();
+
+ MOCK_CONST_METHOD0(GetLossDetectionType, LossDetectionType());
+ MOCK_METHOD4(DetectLostPackets,
+ SequenceNumberSet(const QuicUnackedPacketMap& unacked_packets,
+ const QuicTime& time,
+ QuicPacketSequenceNumber largest_observed,
+ const RttStats& rtt_stats));
+ MOCK_CONST_METHOD0(GetLossTimeout, QuicTime());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockLossAlgorithm);
+};
+
+class TestEntropyCalculator :
+ public QuicReceivedEntropyHashCalculatorInterface {
+ public:
+ TestEntropyCalculator();
+ virtual ~TestEntropyCalculator();
+
+ virtual QuicPacketEntropyHash EntropyHash(
+ QuicPacketSequenceNumber sequence_number) const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestEntropyCalculator);
+};
+
+class MockEntropyCalculator : public TestEntropyCalculator {
+ public:
+ MockEntropyCalculator();
+ virtual ~MockEntropyCalculator();
+
+ MOCK_CONST_METHOD1(
+ EntropyHash,
+ QuicPacketEntropyHash(QuicPacketSequenceNumber sequence_number));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockEntropyCalculator);
+};
+
+class MockAckNotifierDelegate : public QuicAckNotifier::DelegateInterface {
+ public:
+ MockAckNotifierDelegate();
+
+ MOCK_METHOD5(OnAckNotification, void(int num_original_packets,
+ int num_original_bytes,
+ int num_retransmitted_packets,
+ int num_retransmitted_bytes,
+ QuicTime::Delta delta_largest_observed));
+
+ protected:
+ // Object is ref counted.
+ virtual ~MockAckNotifierDelegate();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAckNotifierDelegate);
+};
+
+class MockNetworkChangeVisitor :
+ public QuicSentPacketManager::NetworkChangeVisitor {
+ public:
+ MockNetworkChangeVisitor();
+ virtual ~MockNetworkChangeVisitor();
+
+ MOCK_METHOD1(OnCongestionWindowChange, void(QuicByteCount));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockNetworkChangeVisitor);
+};
+
+// Creates per-connection packet writers that register themselves with the
+// TestWriterFactory on each write so that TestWriterFactory::OnPacketSent can
+// be routed to the appropriate QuicConnection.
+class TestWriterFactory : public QuicDispatcher::PacketWriterFactory {
+ public:
+ TestWriterFactory();
+ virtual ~TestWriterFactory();
+
+ virtual QuicPacketWriter* Create(QuicServerPacketWriter* writer,
+ QuicConnection* connection) OVERRIDE;
+
+ // Calls OnPacketSent on the last QuicConnection to write through one of the
+ // packet writers created by this factory.
+ void OnPacketSent(WriteResult result);
+
+ private:
+ class PerConnectionPacketWriter : public QuicPerConnectionPacketWriter {
+ public:
+ PerConnectionPacketWriter(TestWriterFactory* factory,
+ QuicServerPacketWriter* writer,
+ QuicConnection* connection);
+ virtual ~PerConnectionPacketWriter();
+
+ virtual WriteResult WritePacket(
+ const char* buffer,
+ size_t buf_len,
+ const IPAddressNumber& self_address,
+ const IPEndPoint& peer_address) OVERRIDE;
+
+ private:
+ TestWriterFactory* factory_;
+ };
+
+ // If an asynchronous write is happening and |writer| gets deleted, this
+ // clears the pointer to it to prevent use-after-free.
+ void Unregister(PerConnectionPacketWriter* writer);
+
+ PerConnectionPacketWriter* current_writer_;
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.cc b/net/quic/test_tools/reliable_quic_stream_peer.cc
new file mode 100644
index 0000000..bd1ab00
--- /dev/null
+++ b/net/quic/test_tools/reliable_quic_stream_peer.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
+
+#include <list>
+
+#include "net/quic/reliable_quic_stream.h"
+
+namespace net {
+namespace test {
+
+// static
+void ReliableQuicStreamPeer::SetWriteSideClosed(bool value,
+ ReliableQuicStream* stream) {
+ stream->write_side_closed_ = value;
+}
+
+// static
+void ReliableQuicStreamPeer::SetStreamBytesWritten(
+ QuicStreamOffset stream_bytes_written,
+ ReliableQuicStream* stream) {
+ stream->stream_bytes_written_ = stream_bytes_written;
+}
+
+// static
+void ReliableQuicStreamPeer::CloseReadSide(ReliableQuicStream* stream) {
+ stream->CloseReadSide();
+}
+
+// static
+bool ReliableQuicStreamPeer::FinSent(ReliableQuicStream* stream) {
+ return stream->fin_sent_;
+}
+
+// static
+bool ReliableQuicStreamPeer::RstSent(ReliableQuicStream* stream) {
+ return stream->rst_sent_;
+}
+
+// static
+uint32 ReliableQuicStreamPeer::SizeOfQueuedData(ReliableQuicStream* stream) {
+ uint32 total = 0;
+ std::list<ReliableQuicStream::PendingData>::iterator it =
+ stream->queued_data_.begin();
+ while (it != stream->queued_data_.end()) {
+ total += it->data.size();
+ ++it;
+ }
+ return total;
+}
+
+// static
+void ReliableQuicStreamPeer::SetFecPolicy(ReliableQuicStream* stream,
+ FecPolicy fec_policy) {
+ stream->set_fec_policy(fec_policy);
+}
+
+// static
+bool ReliableQuicStreamPeer::StreamContributesToConnectionFlowControl(
+ ReliableQuicStream* stream) {
+ return stream->stream_contributes_to_connection_flow_control_;
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.h b/net/quic/test_tools/reliable_quic_stream_peer.h
new file mode 100644
index 0000000..8fffbbe
--- /dev/null
+++ b/net/quic/test_tools/reliable_quic_stream_peer.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_RELIABLE_QUIC_STREAM_PEER_H_
+#define NET_QUIC_TEST_TOOLS_RELIABLE_QUIC_STREAM_PEER_H_
+
+#include "base/basictypes.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class ReliableQuicStream;
+
+namespace test {
+
+class ReliableQuicStreamPeer {
+ public:
+ static void SetWriteSideClosed(bool value, ReliableQuicStream* stream);
+ static void SetStreamBytesWritten(QuicStreamOffset stream_bytes_written,
+ ReliableQuicStream* stream);
+ static void CloseReadSide(ReliableQuicStream* stream);
+
+ static bool FinSent(ReliableQuicStream* stream);
+ static bool RstSent(ReliableQuicStream* stream);
+
+ static uint32 SizeOfQueuedData(ReliableQuicStream* stream);
+
+ static void SetFecPolicy(ReliableQuicStream* stream, FecPolicy fec_policy);
+
+ static bool StreamContributesToConnectionFlowControl(
+ ReliableQuicStream* stream);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer);
+};
+
+} // namespace test
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_RELIABLE_QUIC_STREAM_PEER_H_
diff --git a/net/quic/test_tools/simple_quic_framer.cc b/net/quic/test_tools/simple_quic_framer.cc
new file mode 100644
index 0000000..a08d555
--- /dev/null
+++ b/net/quic/test_tools/simple_quic_framer.cc
@@ -0,0 +1,287 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/simple_quic_framer.h"
+
+#include "base/stl_util.h"
+#include "net/quic/crypto/crypto_framer.h"
+#include "net/quic/crypto/quic_decrypter.h"
+#include "net/quic/crypto/quic_encrypter.h"
+
+using base::StringPiece;
+using std::string;
+using std::vector;
+
+namespace net {
+namespace test {
+
+class SimpleFramerVisitor : public QuicFramerVisitorInterface {
+ public:
+ SimpleFramerVisitor()
+ : error_(QUIC_NO_ERROR) {
+ }
+
+ virtual ~SimpleFramerVisitor() OVERRIDE {
+ STLDeleteElements(&stream_data_);
+ }
+
+ virtual void OnError(QuicFramer* framer) OVERRIDE {
+ error_ = framer->error();
+ }
+
+ virtual bool OnProtocolVersionMismatch(QuicVersion version) OVERRIDE {
+ return false;
+ }
+
+ virtual void OnPacket() OVERRIDE {}
+ virtual void OnPublicResetPacket(
+ const QuicPublicResetPacket& packet) OVERRIDE {
+ public_reset_packet_.reset(new QuicPublicResetPacket(packet));
+ }
+ virtual void OnVersionNegotiationPacket(
+ const QuicVersionNegotiationPacket& packet) OVERRIDE {
+ version_negotiation_packet_.reset(
+ new QuicVersionNegotiationPacket(packet));
+ }
+ virtual void OnRevivedPacket() OVERRIDE {}
+
+ virtual bool OnUnauthenticatedPublicHeader(
+ const QuicPacketPublicHeader& header) OVERRIDE {
+ return true;
+ }
+ virtual bool OnUnauthenticatedHeader(
+ const QuicPacketHeader& header) OVERRIDE {
+ return true;
+ }
+ virtual void OnDecryptedPacket(EncryptionLevel level) OVERRIDE {}
+ virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE {
+ has_header_ = true;
+ header_ = header;
+ return true;
+ }
+
+ virtual void OnFecProtectedPayload(StringPiece payload) OVERRIDE {}
+
+ virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE {
+ // Save a copy of the data so it is valid after the packet is processed.
+ stream_data_.push_back(frame.GetDataAsString());
+ QuicStreamFrame stream_frame(frame);
+ // Make sure that the stream frame points to this data.
+ stream_frame.data.Clear();
+ stream_frame.data.Append(const_cast<char*>(stream_data_.back()->data()),
+ stream_data_.back()->size());
+ stream_frames_.push_back(stream_frame);
+ return true;
+ }
+
+ virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE {
+ ack_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnCongestionFeedbackFrame(
+ const QuicCongestionFeedbackFrame& frame) OVERRIDE {
+ feedback_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) OVERRIDE {
+ stop_waiting_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnPingFrame(const QuicPingFrame& frame) OVERRIDE {
+ ping_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual void OnFecData(const QuicFecData& fec) OVERRIDE {
+ fec_data_ = fec;
+ fec_redundancy_ = fec_data_.redundancy.as_string();
+ fec_data_.redundancy = fec_redundancy_;
+ }
+
+ virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE {
+ rst_stream_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnConnectionCloseFrame(
+ const QuicConnectionCloseFrame& frame) OVERRIDE {
+ connection_close_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE {
+ goaway_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnWindowUpdateFrame(
+ const QuicWindowUpdateFrame& frame) OVERRIDE {
+ window_update_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual bool OnBlockedFrame(const QuicBlockedFrame& frame) OVERRIDE {
+ blocked_frames_.push_back(frame);
+ return true;
+ }
+
+ virtual void OnPacketComplete() OVERRIDE {}
+
+ const QuicPacketHeader& header() const { return header_; }
+ const vector<QuicAckFrame>& ack_frames() const { return ack_frames_; }
+ const vector<QuicConnectionCloseFrame>& connection_close_frames() const {
+ return connection_close_frames_;
+ }
+ const vector<QuicCongestionFeedbackFrame>& feedback_frames() const {
+ return feedback_frames_;
+ }
+ const vector<QuicGoAwayFrame>& goaway_frames() const {
+ return goaway_frames_;
+ }
+ const vector<QuicRstStreamFrame>& rst_stream_frames() const {
+ return rst_stream_frames_;
+ }
+ const vector<QuicStreamFrame>& stream_frames() const {
+ return stream_frames_;
+ }
+ const vector<QuicStopWaitingFrame>& stop_waiting_frames() const {
+ return stop_waiting_frames_;
+ }
+ const vector<QuicPingFrame>& ping_frames() const {
+ return ping_frames_;
+ }
+ const QuicFecData& fec_data() const {
+ return fec_data_;
+ }
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const {
+ return version_negotiation_packet_.get();
+ }
+ const QuicPublicResetPacket* public_reset_packet() const {
+ return public_reset_packet_.get();
+ }
+
+ private:
+ QuicErrorCode error_;
+ bool has_header_;
+ QuicPacketHeader header_;
+ QuicFecData fec_data_;
+ scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_;
+ scoped_ptr<QuicPublicResetPacket> public_reset_packet_;
+ string fec_redundancy_;
+ vector<QuicAckFrame> ack_frames_;
+ vector<QuicCongestionFeedbackFrame> feedback_frames_;
+ vector<QuicStopWaitingFrame> stop_waiting_frames_;
+ vector<QuicPingFrame> ping_frames_;
+ vector<QuicStreamFrame> stream_frames_;
+ vector<QuicRstStreamFrame> rst_stream_frames_;
+ vector<QuicGoAwayFrame> goaway_frames_;
+ vector<QuicConnectionCloseFrame> connection_close_frames_;
+ vector<QuicWindowUpdateFrame> window_update_frames_;
+ vector<QuicBlockedFrame> blocked_frames_;
+ vector<string*> stream_data_;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleFramerVisitor);
+};
+
+SimpleQuicFramer::SimpleQuicFramer()
+ : framer_(QuicSupportedVersions(), QuicTime::Zero(), true) {
+}
+
+SimpleQuicFramer::SimpleQuicFramer(const QuicVersionVector& supported_versions)
+ : framer_(supported_versions, QuicTime::Zero(), true) {
+}
+
+SimpleQuicFramer::~SimpleQuicFramer() {
+}
+
+bool SimpleQuicFramer::ProcessPacket(const QuicPacket& packet) {
+ scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket(
+ ENCRYPTION_NONE, 0, packet));
+ return ProcessPacket(*encrypted);
+}
+
+bool SimpleQuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
+ visitor_.reset(new SimpleFramerVisitor);
+ framer_.set_visitor(visitor_.get());
+ return framer_.ProcessPacket(packet);
+}
+
+void SimpleQuicFramer::Reset() {
+ visitor_.reset(new SimpleFramerVisitor);
+}
+
+
+const QuicPacketHeader& SimpleQuicFramer::header() const {
+ return visitor_->header();
+}
+
+const QuicFecData& SimpleQuicFramer::fec_data() const {
+ return visitor_->fec_data();
+}
+
+const QuicVersionNegotiationPacket*
+SimpleQuicFramer::version_negotiation_packet() const {
+ return visitor_->version_negotiation_packet();
+}
+
+const QuicPublicResetPacket* SimpleQuicFramer::public_reset_packet() const {
+ return visitor_->public_reset_packet();
+}
+
+QuicFramer* SimpleQuicFramer::framer() {
+ return &framer_;
+}
+
+size_t SimpleQuicFramer::num_frames() const {
+ return ack_frames().size() +
+ feedback_frames().size() +
+ goaway_frames().size() +
+ rst_stream_frames().size() +
+ stop_waiting_frames().size() +
+ stream_frames().size() +
+ ping_frames().size() +
+ connection_close_frames().size();
+}
+
+const vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const {
+ return visitor_->ack_frames();
+}
+
+const vector<QuicStopWaitingFrame>&
+SimpleQuicFramer::stop_waiting_frames() const {
+ return visitor_->stop_waiting_frames();
+}
+
+const vector<QuicPingFrame>& SimpleQuicFramer::ping_frames() const {
+ return visitor_->ping_frames();
+}
+
+const vector<QuicStreamFrame>& SimpleQuicFramer::stream_frames() const {
+ return visitor_->stream_frames();
+}
+
+const vector<QuicRstStreamFrame>& SimpleQuicFramer::rst_stream_frames() const {
+ return visitor_->rst_stream_frames();
+}
+
+const vector<QuicCongestionFeedbackFrame>&
+SimpleQuicFramer::feedback_frames() const {
+ return visitor_->feedback_frames();
+}
+
+const vector<QuicGoAwayFrame>&
+SimpleQuicFramer::goaway_frames() const {
+ return visitor_->goaway_frames();
+}
+
+const vector<QuicConnectionCloseFrame>&
+SimpleQuicFramer::connection_close_frames() const {
+ return visitor_->connection_close_frames();
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/simple_quic_framer.h b/net/quic/test_tools/simple_quic_framer.h
new file mode 100644
index 0000000..7a31014
--- /dev/null
+++ b/net/quic/test_tools/simple_quic_framer.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+#define NET_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "net/quic/quic_framer.h"
+#include "net/quic/quic_protocol.h"
+
+namespace net {
+
+class CryptoHandshakeMessage;
+struct QuicAckFrame;
+class QuicConnection;
+class QuicConnectionVisitorInterface;
+class QuicPacketCreator;
+class ReceiveAlgorithmInterface;
+class SendAlgorithmInterface;
+
+namespace test {
+
+class SimpleFramerVisitor;
+
+// Peer to make public a number of otherwise private QuicFramer methods.
+class SimpleQuicFramer {
+ public:
+ SimpleQuicFramer();
+ explicit SimpleQuicFramer(const QuicVersionVector& supported_versions);
+ ~SimpleQuicFramer();
+
+ bool ProcessPacket(const QuicEncryptedPacket& packet);
+ bool ProcessPacket(const QuicPacket& packet);
+ void Reset();
+
+ const QuicPacketHeader& header() const;
+ size_t num_frames() const;
+ const std::vector<QuicAckFrame>& ack_frames() const;
+ const std::vector<QuicConnectionCloseFrame>& connection_close_frames() const;
+ const std::vector<QuicCongestionFeedbackFrame>& feedback_frames() const;
+ const std::vector<QuicStopWaitingFrame>& stop_waiting_frames() const;
+ const std::vector<QuicPingFrame>& ping_frames() const;
+ const std::vector<QuicGoAwayFrame>& goaway_frames() const;
+ const std::vector<QuicRstStreamFrame>& rst_stream_frames() const;
+ const std::vector<QuicStreamFrame>& stream_frames() const;
+ const QuicFecData& fec_data() const;
+ const QuicVersionNegotiationPacket* version_negotiation_packet() const;
+ const QuicPublicResetPacket* public_reset_packet() const;
+
+ QuicFramer* framer();
+
+ void SetSupportedVersions(const QuicVersionVector& versions) {
+ framer_.SetSupportedVersions(versions);
+ }
+
+ private:
+ QuicFramer framer_;
+ scoped_ptr<SimpleFramerVisitor> visitor_;
+ DISALLOW_COPY_AND_ASSIGN(SimpleQuicFramer);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_SIMPLE_QUIC_FRAMER_H_
diff --git a/net/quic/test_tools/test_task_runner.cc b/net/quic/test_tools/test_task_runner.cc
new file mode 100644
index 0000000..385fd5d
--- /dev/null
+++ b/net/quic/test_tools/test_task_runner.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/test_tools/test_task_runner.h"
+
+#include <algorithm>
+
+#include "net/quic/test_tools/mock_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace test {
+
+TestTaskRunner::TestTaskRunner(MockClock* clock)
+ : clock_(clock) {
+}
+
+TestTaskRunner::~TestTaskRunner() {
+}
+
+bool TestTaskRunner::PostDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ EXPECT_GE(delay, base::TimeDelta());
+ tasks_.push_back(
+ PostedTask(from_here, task, clock_->NowInTicks(), delay,
+ base::TestPendingTask::NESTABLE));
+ return false;
+}
+
+bool TestTaskRunner::RunsTasksOnCurrentThread() const {
+ return true;
+}
+
+const std::vector<PostedTask>& TestTaskRunner::GetPostedTasks() const {
+ return tasks_;
+}
+
+void TestTaskRunner::RunNextTask() {
+ // Find the next task to run, advance the time to the correct time
+ // and then run the task.
+ std::vector<PostedTask>::iterator next = FindNextTask();
+ DCHECK(next != tasks_.end());
+ clock_->AdvanceTime(QuicTime::Delta::FromMicroseconds(
+ (next->GetTimeToRun() - clock_->NowInTicks()).InMicroseconds()));
+ PostedTask task = *next;
+ tasks_.erase(next);
+ task.task.Run();
+}
+
+namespace {
+
+struct ShouldRunBeforeLessThan {
+ bool operator()(const PostedTask& task1, const PostedTask& task2) const {
+ return task1.ShouldRunBefore(task2);
+ }
+};
+
+} // namespace
+
+std::vector<PostedTask>::iterator TestTaskRunner::FindNextTask() {
+ return std::min_element(
+ tasks_.begin(), tasks_.end(), ShouldRunBeforeLessThan());
+}
+
+} // namespace test
+} // namespace net
diff --git a/net/quic/test_tools/test_task_runner.h b/net/quic/test_tools/test_task_runner.h
new file mode 100644
index 0000000..e25bf2a
--- /dev/null
+++ b/net/quic/test_tools/test_task_runner.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Common utilities for Quic tests
+
+#ifndef NET_QUIC_TEST_TOOLS_TEST_TASK_RUNNER_H_
+#define NET_QUIC_TEST_TOOLS_TEST_TASK_RUNNER_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/task_runner.h"
+#include "base/test/test_pending_task.h"
+
+namespace net {
+
+class MockClock;
+
+namespace test {
+
+typedef base::TestPendingTask PostedTask;
+
+class TestTaskRunner : public base::TaskRunner {
+ public:
+ explicit TestTaskRunner(MockClock* clock);
+
+ // base::TaskRunner implementation.
+ virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) OVERRIDE;
+ virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
+
+ const std::vector<PostedTask>& GetPostedTasks() const;
+
+ void RunNextTask();
+
+ protected:
+ virtual ~TestTaskRunner();
+
+ private:
+ std::vector<PostedTask>::iterator FindNextTask();
+
+ MockClock* const clock_;
+ std::vector<PostedTask> tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestTaskRunner);
+};
+
+} // namespace test
+
+} // namespace net
+
+#endif // NET_QUIC_TEST_TOOLS_TEST_TASK_RUNNER_H_