diff options
Diffstat (limited to 'chromium/net/quic')
267 files changed, 73138 insertions, 0 deletions
diff --git a/chromium/net/quic/blocked_list.h b/chromium/net/quic/blocked_list.h new file mode 100644 index 00000000000..3a7f989eeec --- /dev/null +++ b/chromium/net/quic/blocked_list.h @@ -0,0 +1,91 @@ +// 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 combined list/hash set for read or write-blocked entities. + +#ifndef NET_QUIC_BLOCKED_LIST_H_ +#define NET_QUIC_BLOCKED_LIST_H_ + +#include <list> + +#include "base/containers/hash_tables.h" +#include "base/logging.h" + +namespace net { + +template <typename Object> +class BlockedList { + public: + // Called to add an object to the blocked list. This indicates + // the object should be notified when it can use the socket again. + // + // If this object is already on the list, it will not be added again. + void AddBlockedObject(Object object) { + // Only add the object to the list if we successfully add it to the set. + if (object_set_.insert(object).second) { + object_list_.push_back(object); + } + } + + // Called to remove an object from a blocked list. This should be + // called in the event the object is being deleted before the list is. + void RemoveBlockedObject(Object object) { + // Remove the object from the set. We'll check the set before calling + // OnCanWrite on a object from the list. + // + // There is potentially ordering unfairness should a session be removed and + // then readded (as it keeps its position in the list) but it's not worth + // the overhead to walk the list and remove it. + object_set_.erase(object); + } + + // Called when the socket is usable and some objects can access it. Returns + // the first object and removes it from the list. + Object GetNextBlockedObject() { + DCHECK(!IsEmpty()); + + // Walk the list to find the first object which was not removed from the + // set. + while (!object_list_.empty()) { + Object object = *object_list_.begin(); + object_list_.pop_front(); + int removed = object_set_.erase(object); + if (removed > 0) { + return object; + } + } + + // This is a bit of a hack: It's illegal to call GetNextBlockedObject() if + // the list is empty (see DCHECK above) but we must return something. This + // compiles for ints (returns 0) and pointers in the case that someone has a + // bug in their call site. + return 0; + }; + + // Returns the number of objects in the blocked list. + int NumObjects() { + return object_set_.size(); + }; + + // Returns true if there are no objects in the list, false otherwise. + bool IsEmpty() { + return object_set_.empty(); + }; + + private: + // A set tracking the objects. This is the authoritative container for + // determining if an object is blocked. Objects in the list will always + // be in the set. + base::hash_set<Object> object_set_; + // A list tracking the order in which objects were added to the list. + // Objects are added to the back and pulled off the front, but only get + // resumption calls if they're still in the set. + // It's possible to be in the list twice, but only the first entry will get an + // OnCanWrite call. + std::list<Object> object_list_; +}; + +} // namespace net + +#endif // NET_QUIC_BLOCKED_LIST_H_ diff --git a/chromium/net/quic/blocked_list_test.cc b/chromium/net/quic/blocked_list_test.cc new file mode 100644 index 00000000000..074b6f52782 --- /dev/null +++ b/chromium/net/quic/blocked_list_test.cc @@ -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. + +#include "net/quic/blocked_list.h" +#include "net/quic/quic_connection.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(COMPILER_GCC) +namespace BASE_HASH_NAMESPACE { +template<> +struct hash<const int*> { + std::size_t operator()(const int* ptr) const { + return hash<size_t>()(reinterpret_cast<size_t>(ptr)); + } +}; +} +#endif + +namespace net { +namespace test { +namespace { + +class BlockedListTest : public ::testing::Test { + protected: + BlockedListTest() : + item1_(0), + item2_(0), + item3_(0) { + } + + BlockedList<const int*> list_; + const int item1_; + const int item2_; + const int item3_; +}; + +TEST_F(BlockedListTest, BasicAdd) { + list_.AddBlockedObject(&item1_); + list_.AddBlockedObject(&item3_); + list_.AddBlockedObject(&item2_); + ASSERT_EQ(3, list_.NumObjects()); + ASSERT_FALSE(list_.IsEmpty()); + + EXPECT_EQ(&item1_, list_.GetNextBlockedObject()); + EXPECT_EQ(&item3_, list_.GetNextBlockedObject()); + EXPECT_EQ(&item2_, list_.GetNextBlockedObject()); +} + +TEST_F(BlockedListTest, AddAndRemove) { + list_.AddBlockedObject(&item1_); + list_.AddBlockedObject(&item3_); + list_.AddBlockedObject(&item2_); + ASSERT_EQ(3, list_.NumObjects()); + + list_.RemoveBlockedObject(&item3_); + ASSERT_EQ(2, list_.NumObjects()); + + EXPECT_EQ(&item1_, list_.GetNextBlockedObject()); + EXPECT_EQ(&item2_, list_.GetNextBlockedObject()); +} + +TEST_F(BlockedListTest, DuplicateAdd) { + list_.AddBlockedObject(&item1_); + list_.AddBlockedObject(&item3_); + list_.AddBlockedObject(&item2_); + + list_.AddBlockedObject(&item3_); + list_.AddBlockedObject(&item2_); + list_.AddBlockedObject(&item1_); + + ASSERT_EQ(3, list_.NumObjects()); + ASSERT_FALSE(list_.IsEmpty()); + + // Call in the original insert order. + EXPECT_EQ(&item1_, list_.GetNextBlockedObject()); + EXPECT_EQ(&item3_, list_.GetNextBlockedObject()); + EXPECT_EQ(&item2_, list_.GetNextBlockedObject()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.cc b/chromium/net/quic/congestion_control/available_channel_estimator.cc new file mode 100644 index 00000000000..ef091abce43 --- /dev/null +++ b/chromium/net/quic/congestion_control/available_channel_estimator.cc @@ -0,0 +1,78 @@ +// 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/available_channel_estimator.h" + +static const int kNumberOfSamples = 9; + +namespace net { + +AvailableChannelEstimator::AvailableChannelEstimator( + QuicPacketSequenceNumber sequence_number, + QuicTime first_send_time, + QuicTime first_receive_time) + : first_sequence_number_(sequence_number), + first_send_time_(first_send_time), + first_receive_time_(first_receive_time), + last_incorporated_sequence_number_(sequence_number), + last_time_sent_(QuicTime::Zero()), + last_receive_time_(QuicTime::Zero()), + number_of_sequence_numbers_(0), + received_bytes_(0) { +} + +void AvailableChannelEstimator::OnIncomingFeedback( + QuicPacketSequenceNumber sequence_number, + QuicByteCount packet_size, + QuicTime sent_time, + QuicTime receive_time) { + if (sequence_number <= first_sequence_number_) { + // Ignore pre-probe feedback. + return; + } + if (sequence_number <= last_incorporated_sequence_number_) { + // Ignore old feedback; will remove duplicates. + return; + } + // Remember the highest received sequence number. + last_incorporated_sequence_number_ = sequence_number; + if (number_of_sequence_numbers_ < kNumberOfSamples) { + // We don't care how many sequence numbers we have after we pass + // kNumberOfSamples. + number_of_sequence_numbers_++; + } + last_receive_time_ = receive_time; + last_time_sent_ = sent_time; + received_bytes_ += packet_size; + // TODO(pwestin): the variance here should give us information about accuracy. +} + +AvailableChannelEstimateState + AvailableChannelEstimator::GetAvailableChannelEstimate( + QuicBandwidth* bandwidth) const { + if (number_of_sequence_numbers_ < 2) { + return kAvailableChannelEstimateUnknown; + } + QuicTime::Delta send_delta = last_time_sent_.Subtract(first_send_time_); + QuicTime::Delta receive_delta = + last_receive_time_.Subtract(first_receive_time_); + + // TODO(pwestin): room for improvement here. Keeping it simple for now. + *bandwidth = QuicBandwidth::FromBytesAndTimeDelta(received_bytes_, + receive_delta); + + QuicTime::Delta diff = receive_delta.Subtract(send_delta); + QuicTime::Delta ten_percent_of_send_time = + QuicTime::Delta::FromMicroseconds(send_delta.ToMicroseconds() / 10); + + if (diff < ten_percent_of_send_time) { + return kAvailableChannelEstimateSenderLimited; + } + if (number_of_sequence_numbers_ < kNumberOfSamples) { + return kAvailableChannelEstimateUncertain; + } + return kAvailableChannelEstimateGood; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/available_channel_estimator.h b/chromium/net/quic/congestion_control/available_channel_estimator.h new file mode 100644 index 00000000000..e2ad19a8b66 --- /dev/null +++ b/chromium/net/quic/congestion_control/available_channel_estimator.h @@ -0,0 +1,64 @@ +// 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. + +// Based on the inter arrival time of the received packets relative to the time +// those packets where sent we can estimate the available capacity of the +// channel. +// We can only use packet trains that are sent out faster than the acctual +// available channel capacity. +// Note 1: this is intended to be a temporary class created when you send out a +// channel probing burst. Once the last packet have arrived you ask it for an +// estimate. +// Note 2: During this phase we should not update the overuse detector. +#ifndef NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_ +#define NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_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 { + +enum NET_EXPORT_PRIVATE AvailableChannelEstimateState { + kAvailableChannelEstimateUnknown = 0, + kAvailableChannelEstimateUncertain = 1, + kAvailableChannelEstimateGood = 2, + kAvailableChannelEstimateSenderLimited = 3, +}; + +class NET_EXPORT_PRIVATE AvailableChannelEstimator { + public: + explicit AvailableChannelEstimator( + QuicPacketSequenceNumber first_sequence_number, + QuicTime first_send_time, + QuicTime first_receive_time); + + // Update the statistics with each receive time, for every packet we get a + // feedback message for. + void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number, + QuicByteCount packet_size, + QuicTime sent_time, + QuicTime receive_time); + + // Get the current estimated available channel capacity. + // bandwidth_estimate is invalid if kAvailableChannelEstimateUnknown + // is returned. + AvailableChannelEstimateState GetAvailableChannelEstimate( + QuicBandwidth* bandwidth_estimate) const; + + private: + const QuicPacketSequenceNumber first_sequence_number_; + const QuicTime first_send_time_; + const QuicTime first_receive_time_; + QuicPacketSequenceNumber last_incorporated_sequence_number_; + QuicTime last_time_sent_; + QuicTime last_receive_time_; + int number_of_sequence_numbers_; + QuicByteCount received_bytes_; +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_AVAILABLE_CHANNEL_ESTIMATOR_H_ diff --git a/chromium/net/quic/congestion_control/available_channel_estimator_test.cc b/chromium/net/quic/congestion_control/available_channel_estimator_test.cc new file mode 100644 index 00000000000..b4a4b9c341c --- /dev/null +++ b/chromium/net/quic/congestion_control/available_channel_estimator_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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/available_channel_estimator.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class AvailableChannelEstimatorTest : public ::testing::Test { + protected: + virtual void SetUp() { + srand(1234); + packet_size_ = 1200; + sequence_number_ = 1; + QuicTime receive_time = receive_clock_.Now(); + QuicTime sent_time = send_clock_.Now(); + estimator_.reset(new AvailableChannelEstimator(sequence_number_, + sent_time, + receive_time)); + } + + MockClock send_clock_; + MockClock receive_clock_; + QuicPacketSequenceNumber sequence_number_; + QuicByteCount packet_size_; + scoped_ptr<AvailableChannelEstimator> estimator_; +}; + +TEST_F(AvailableChannelEstimatorTest, SimpleBasic) { + QuicBandwidth bandwidth = QuicBandwidth::Zero(); + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1); + receive_clock_.AdvanceTime(received_delta); + send_clock_.AdvanceTime(send_delta); + QuicTime receive_time = receive_clock_.Now(); + QuicTime sent_time = send_clock_.Now(); + estimator_->OnIncomingFeedback(++sequence_number_, + packet_size_, + sent_time, + receive_time); + EXPECT_EQ(kAvailableChannelEstimateUnknown, + estimator_->GetAvailableChannelEstimate(&bandwidth)); + + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.Now(); + send_clock_.AdvanceTime(send_delta); + sent_time = send_clock_.Now(); + + estimator_->OnIncomingFeedback(++sequence_number_, + packet_size_, + sent_time, + receive_time); + EXPECT_EQ(kAvailableChannelEstimateUncertain, + estimator_->GetAvailableChannelEstimate(&bandwidth)); + + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), + bandwidth); +} + +// TODO(pwestin): simulate cross traffic. +TEST_F(AvailableChannelEstimatorTest, SimpleUncertainEstimate) { + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1); + + for (int i = 0; i < 8; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.Now(); + send_clock_.AdvanceTime(send_delta); + QuicTime sent_time = send_clock_.Now(); + estimator_->OnIncomingFeedback(++sequence_number_, + packet_size_, + sent_time, + receive_time); + } + QuicBandwidth bandwidth = QuicBandwidth::Zero(); + EXPECT_EQ(kAvailableChannelEstimateUncertain, + estimator_->GetAvailableChannelEstimate(&bandwidth)); + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), + bandwidth); +} + +TEST_F(AvailableChannelEstimatorTest, SimpleGoodEstimate) { + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(1); + + for (int i = 0; i < 100; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.Now(); + send_clock_.AdvanceTime(send_delta); + QuicTime sent_time = send_clock_.Now(); + estimator_->OnIncomingFeedback(++sequence_number_, + packet_size_, + sent_time, + receive_time); + } + QuicBandwidth bandwidth = QuicBandwidth::Zero(); + EXPECT_EQ(kAvailableChannelEstimateGood, + estimator_->GetAvailableChannelEstimate(&bandwidth)); + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), + bandwidth); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/channel_estimator.cc b/chromium/net/quic/congestion_control/channel_estimator.cc new file mode 100644 index 00000000000..54b96ae4e18 --- /dev/null +++ b/chromium/net/quic/congestion_control/channel_estimator.cc @@ -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. + +#include "net/quic/congestion_control/channel_estimator.h" + +// To get information about bandwidth, our send rate for a pair of packets must +// be much faster (ideally back to back) than the receive rate. In that +// scenario, the arriving packet pair will tend to arrive at the max bandwidth +// of the channel. Said another way, when our inter-departure time is a small +// fraction of the inter-arrival time for the same pair of packets, then we can +// get an estimate of bandwidth from that interarrival time. The following +// constant is the threshold ratio for deriving bandwidth information. +static const int kInterarrivalRatioThresholdForBandwidthEstimation = 5; +static const size_t kMinNumberOfSamples = 10; +static const size_t kMaxNumberOfSamples = 100; + +namespace net { + +ChannelEstimator::ChannelEstimator() + : last_sequence_number_(0), + last_send_time_(QuicTime::Zero()), + last_receive_time_(QuicTime::Zero()), + sorted_bitrate_estimates_(kMaxNumberOfSamples) { +} + +ChannelEstimator::~ChannelEstimator() { +} + +void ChannelEstimator::OnAcknowledgedPacket( + QuicPacketSequenceNumber sequence_number, + QuicByteCount packet_size, + QuicTime send_time, + QuicTime receive_time) { + if (last_sequence_number_ > sequence_number) { + // Old packet. The sequence_number use the full 64 bits even though it's + // less on the wire. + return; + } + if (last_sequence_number_ != sequence_number - 1) { + DLOG(INFO) << "Skip channel estimator due to lost packet(s)"; + } else if (last_send_time_.IsInitialized()) { + QuicTime::Delta sent_delta = send_time.Subtract(last_send_time_); + QuicTime::Delta received_delta = receive_time.Subtract(last_receive_time_); + if (received_delta.ToMicroseconds() > + kInterarrivalRatioThresholdForBandwidthEstimation * + sent_delta.ToMicroseconds()) { + UpdateFilter(received_delta, packet_size, sequence_number); + } + } + last_sequence_number_ = sequence_number; + last_send_time_ = send_time; + last_receive_time_ = receive_time; +} + +ChannelEstimateState ChannelEstimator::GetChannelEstimate( + QuicBandwidth* estimate) const { + if (sorted_bitrate_estimates_.Size() < kMinNumberOfSamples) { + // Not enough data to make an estimate. + return kChannelEstimateUnknown; + } + // Our data is stored in a sorted map, we need to iterate through our map to + // find the estimated bitrates at our targeted percentiles. + + // Calculate 25th percentile. + size_t beginning_window = sorted_bitrate_estimates_.Size() / 4; + // Calculate 50th percentile. + size_t median = sorted_bitrate_estimates_.Size() / 2; + // Calculate 75th percentile. + size_t end_window = sorted_bitrate_estimates_.Size() - beginning_window; + + QuicBandwidth bitrate_25th_percentile = QuicBandwidth::Zero(); + QuicBandwidth median_bitrate = QuicBandwidth::Zero(); + QuicBandwidth bitrate_75th_percentile = QuicBandwidth::Zero(); + QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber>::ConstIterator it = + sorted_bitrate_estimates_.Begin(); + + for (size_t i = 0; i <= end_window; ++i, ++it) { + DCHECK(it != sorted_bitrate_estimates_.End()); + if (i == beginning_window) { + bitrate_25th_percentile = it->first; + } + if (i == median) { + median_bitrate = it->first; + } + if (i == end_window) { + bitrate_75th_percentile = it->first; + } + } + *estimate = median_bitrate; + DLOG(INFO) << "Channel estimate is:" + << median_bitrate.ToKBitsPerSecond() << " Kbit/s"; + // If the bitrates in our 25th to 75th percentile window varies more than + // 25% of the median bitrate we consider the estimate to be uncertain. + if (bitrate_75th_percentile.Subtract(bitrate_25th_percentile) > + median_bitrate.Scale(0.25f)) { + return kChannelEstimateUncertain; + } + return kChannelEstimateGood; +} + +void ChannelEstimator::UpdateFilter(QuicTime::Delta received_delta, + QuicByteCount size_delta, + QuicPacketSequenceNumber sequence_number) { + QuicBandwidth estimate = + QuicBandwidth::FromBytesAndTimeDelta(size_delta, received_delta); + sorted_bitrate_estimates_.Insert(estimate, sequence_number); +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/channel_estimator.h b/chromium/net/quic/congestion_control/channel_estimator.h new file mode 100644 index 00000000000..9be8031145b --- /dev/null +++ b/chromium/net/quic/congestion_control/channel_estimator.h @@ -0,0 +1,61 @@ +// 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. + +// Based on the inter arrival time of the received packets relative to the time +// those packets where sent we can estimate the max capacity of the channel. +// We can only use packet pair that are sent out faster than the acctual +// channel capacity. +#ifndef NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_ +#define NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_ + +#include <list> +#include <map> + +#include "base/basictypes.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/quic_max_sized_map.h" +#include "net/quic/quic_bandwidth.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +enum ChannelEstimateState { + kChannelEstimateUnknown = 0, + kChannelEstimateUncertain = 1, + kChannelEstimateGood = 2 +}; + +class NET_EXPORT_PRIVATE ChannelEstimator { + public: + ChannelEstimator(); + ~ChannelEstimator(); + + // This method should be called each time we acquire a receive time for a + // packet we previously sent. It calculates deltas between consecutive + // receive times, and may use that to update the channel bandwidth estimate. + void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount packet_size, + QuicTime send_time, + QuicTime receive_time); + + // Get the current estimated state and channel capacity. + // Note: estimate will not be valid when kChannelEstimateUnknown is returned. + ChannelEstimateState GetChannelEstimate(QuicBandwidth* estimate) const; + + private: + void UpdateFilter(QuicTime::Delta received_delta, QuicByteCount size_delta, + QuicPacketSequenceNumber sequence_number); + + QuicPacketSequenceNumber last_sequence_number_; + QuicTime last_send_time_; + QuicTime last_receive_time_; + QuicMaxSizedMap<QuicBandwidth, QuicPacketSequenceNumber> + sorted_bitrate_estimates_; + + DISALLOW_COPY_AND_ASSIGN(ChannelEstimator); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_CHANNEL_ESTIMATOR_H_ diff --git a/chromium/net/quic/congestion_control/channel_estimator_test.cc b/chromium/net/quic/congestion_control/channel_estimator_test.cc new file mode 100644 index 00000000000..460e436589e --- /dev/null +++ b/chromium/net/quic/congestion_control/channel_estimator_test.cc @@ -0,0 +1,224 @@ +// 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/logging.h" +#include "net/quic/congestion_control/channel_estimator.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class ChannelEstimatorTest : public ::testing::Test { + protected: + virtual void SetUp() { + srand(1234); + packet_size_ = 1200; + sequence_number_ = 1; + } + + QuicPacketSequenceNumber sequence_number_; + QuicByteCount packet_size_; + MockClock send_clock_; + MockClock receive_clock_; + ChannelEstimator channel_estimator_; +}; + +TEST_F(ChannelEstimatorTest, SimpleNonDetect) { + // In this test, the send times differ by the same delta as the receive times, + // so we haven't sent packets closely enough to detect "spreading," or + // effective bandwidth. + QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); + + for (int i = 0; i < 1000; ++i) { + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + send_clock_.AdvanceTime(delta); + receive_clock_.AdvanceTime(delta); + } + QuicBandwidth estimate = QuicBandwidth::Zero(); + EXPECT_EQ(kChannelEstimateUnknown, + channel_estimator_.GetChannelEstimate(&estimate)); + EXPECT_TRUE(estimate.IsZero()); +} + +TEST_F(ChannelEstimatorTest, SimplePacketPairDetect) { + // In this test, we start by sending packet pairs back-to-back and + // add a receive side spreading that indicate an effective bandwidth. + // We do 2 testes with different effective bandwidth to make sure that we + // detect the new effective bandwidth. + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); + + for (int i = 0; i < 100; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.ApproximateNow(); + QuicTime send_time = send_clock_.ApproximateNow(); + + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + send_clock_.AdvanceTime(send_delta); + } + QuicBandwidth estimate = QuicBandwidth::Zero(); + EXPECT_EQ(kChannelEstimateGood, + channel_estimator_.GetChannelEstimate(&estimate)); + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), + estimate); + received_delta = QuicTime::Delta::FromMilliseconds(1); + for (int i = 0; i < 100; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.ApproximateNow(); + QuicTime send_time = send_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + send_clock_.AdvanceTime(send_delta); + } + EXPECT_EQ(kChannelEstimateGood, + channel_estimator_.GetChannelEstimate(&estimate)); + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, received_delta), + estimate); +} + +TEST_F(ChannelEstimatorTest, SimpleFlatSlope) { + // In this test, we send packet pairs back-to-back and add a slowly increasing + // receive side spreading. We expect the estimate to be good and that our + // mean receive side spreading is returned as the estimate. + QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta received_delta = initial_received_delta; + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); + + for (int i = 0; i < 100; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.ApproximateNow(); + QuicTime send_time = send_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + send_clock_.AdvanceTime(send_delta); + received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(10)); + } + QuicBandwidth estimate = QuicBandwidth::Zero(); + EXPECT_EQ(kChannelEstimateGood, + channel_estimator_.GetChannelEstimate(&estimate)); + + // Calculate our mean receive delta. + QuicTime::Delta increased_received_delta = + received_delta.Subtract(initial_received_delta); + QuicTime::Delta mean_received_delta = initial_received_delta.Add( + QuicTime::Delta::FromMicroseconds( + increased_received_delta.ToMicroseconds() / 2)); + + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, + mean_received_delta), estimate); +} + +TEST_F(ChannelEstimatorTest, SimpleMediumSlope) { + // In this test, we send packet pairs back-to-back and add an increasing + // receive side spreading. We expect the estimate to be uncertaint and that + // our mean receive side spreading is returned as the estimate. + QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta received_delta = initial_received_delta; + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); + + for (int i = 0; i < 100; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.ApproximateNow(); + QuicTime send_time = send_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + send_clock_.AdvanceTime(send_delta); + received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(50)); + } + QuicBandwidth estimate = QuicBandwidth::Zero(); + EXPECT_EQ(kChannelEstimateUncertain, + channel_estimator_.GetChannelEstimate(&estimate)); + + // Calculate our mean receive delta. + QuicTime::Delta increased_received_delta = + received_delta.Subtract(initial_received_delta); + QuicTime::Delta mean_received_delta = initial_received_delta.Add( + QuicTime::Delta::FromMicroseconds( + increased_received_delta.ToMicroseconds() / 2)); + + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, + mean_received_delta), estimate); +} + +TEST_F(ChannelEstimatorTest, SimpleSteepSlope) { + // In this test, we send packet pairs back-to-back and add a rapidly + // increasing receive side spreading. We expect the estimate to be uncertain + // and that our mean receive side spreading is returned as the estimate. + QuicTime::Delta initial_received_delta = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta received_delta = initial_received_delta; + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); + + for (int i = 0; i < 100; ++i) { + receive_clock_.AdvanceTime(received_delta); + QuicTime receive_time = receive_clock_.ApproximateNow(); + QuicTime send_time = send_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + channel_estimator_.OnAcknowledgedPacket(sequence_number_++, + packet_size_, + send_time, + receive_time); + send_clock_.AdvanceTime(send_delta); + received_delta = received_delta.Add(QuicTime::Delta::FromMicroseconds(100)); + } + QuicBandwidth estimate = QuicBandwidth::Zero(); + EXPECT_EQ(kChannelEstimateUncertain, + channel_estimator_.GetChannelEstimate(&estimate)); + + // Calculate our mean receive delta. + QuicTime::Delta increased_received_delta = + received_delta.Subtract(initial_received_delta); + QuicTime::Delta mean_received_delta = initial_received_delta.Add( + QuicTime::Delta::FromMicroseconds( + increased_received_delta.ToMicroseconds() / 2)); + + EXPECT_EQ(QuicBandwidth::FromBytesAndTimeDelta(packet_size_, + mean_received_delta), estimate); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/cube_root.cc b/chromium/net/quic/congestion_control/cube_root.cc new file mode 100644 index 00000000000..c563ad9cf5e --- /dev/null +++ b/chromium/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/chromium/net/quic/congestion_control/cube_root.h b/chromium/net/quic/congestion_control/cube_root.h new file mode 100644 index 00000000000..3b3736c1c59 --- /dev/null +++ b/chromium/net/quic/congestion_control/cube_root.h @@ -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. + +#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); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_CUBE_ROOT_H_ diff --git a/chromium/net/quic/congestion_control/cube_root_test.cc b/chromium/net/quic/congestion_control/cube_root_test.cc new file mode 100644 index 00000000000..8f4729cb7a7 --- /dev/null +++ b/chromium/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/chromium/net/quic/congestion_control/cubic.cc b/chromium/net/quic/congestion_control/cubic.cc new file mode 100644 index 00000000000..04b1e937cfd --- /dev/null +++ b/chromium/net/quic/congestion_control/cubic.cc @@ -0,0 +1,198 @@ +// 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 "base/basictypes.h" +#include "base/logging.h" +#include "base/time/time.h" + +namespace net { + +// 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 = (1ull << kCubeScale) / kCubeCongestionWindowScale; +const uint32 kBeta = 717; // Back off factor after loss. +const uint32 kBetaLastMax = 871; // Additional back off factor after loss for + // the stored max value. + +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 + +Cubic::Cubic(const QuicClock* clock) + : clock_(clock), + epoch_(QuicTime::Zero()), + last_update_time_(QuicTime::Zero()) { + Reset(); +} + +// Calculate the cube root using a table lookup followed by one Newton-Raphson +// iteration. +uint32 Cubic::CubeRoot(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); +} + +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; +} + +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_ = + (kBetaLastMax * current_congestion_window) >> 10; + } else { + last_max_congestion_window_ = current_congestion_window; + } + epoch_ = QuicTime::Zero(); // Reset time. + return (current_congestion_window * kBeta) >> 10; +} + +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 std::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. + DLOG(INFO) << "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(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; + + // We have a new cubic congestion window. + last_target_congestion_window_ = target_congestion_window; + + // Update estimated TCP congestion_window. + // Note: we do a normal Reno congestion avoidance calculation not the + // calculation described in section 3.3 TCP-friendly region of the document. + while (acked_packets_count_ >= estimated_tcp_congestion_window_) { + acked_packets_count_ -= estimated_tcp_congestion_window_; + estimated_tcp_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_; + } + DLOG(INFO) << "Target congestion_window:" << target_congestion_window; + return target_congestion_window; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/cubic.h b/chromium/net/quic/congestion_control/cubic.h new file mode 100644 index 00000000000..e365bbe0ed7 --- /dev/null +++ b/chromium/net/quic/congestion_control/cubic.h @@ -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. +// +// 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_time.h" + +namespace net { + +// TCP congestion window in QUIC is in packets, not bytes. +typedef uint32 QuicTcpCongestionWindow; + +class NET_EXPORT_PRIVATE Cubic { + public: + explicit Cubic(const QuicClock* clock); + + // 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); + + protected: + // Calculates the cubic root using a table lookup followed by one Newton- + // Raphson iteration. + uint32 CubeRoot(uint64 a); + + private: + static const QuicTime::Delta MaxCubicTimeInterval() { + return QuicTime::Delta::FromMilliseconds(30); + } + + const QuicClock* clock_; + + // 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_; + + DISALLOW_COPY_AND_ASSIGN(Cubic); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_CUBIC_H_ diff --git a/chromium/net/quic/congestion_control/cubic_test.cc b/chromium/net/quic/congestion_control/cubic_test.cc new file mode 100644 index 00000000000..84f2a7bb53d --- /dev/null +++ b/chromium/net/quic/congestion_control/cubic_test.cc @@ -0,0 +1,150 @@ +// 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 "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/cubic.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class CubicPeer : public Cubic { + public: + explicit CubicPeer(QuicClock* clock) + : Cubic(clock) { + } + using Cubic::CubeRoot; +}; + +class CubicTest : public ::testing::Test { + protected: + CubicTest() + : one_ms_(QuicTime::Delta::FromMilliseconds(1)), + hundred_ms_(QuicTime::Delta::FromMilliseconds(100)) { + } + virtual void SetUp() { + cubic_.reset(new CubicPeer(&clock_)); + } + const QuicTime::Delta one_ms_; + const QuicTime::Delta hundred_ms_; + MockClock clock_; + scoped_ptr<CubicPeer> cubic_; +}; + +TEST_F(CubicTest, CubeRootLow) { + for (uint32 i = 1; i < 256; ++i) { + uint64 cube = i * i * i; + uint8 cube_root = cubic_->CubeRoot(cube); + EXPECT_EQ(i, cube_root); + } +} + +TEST_F(CubicTest, CubeRootHigh) { + // 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 = cubic_->CubeRoot(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 = cubic_->CubeRoot(cube); + uint32 margin = cube_root >> 9; + EXPECT_LE(i - margin, cube_root); + EXPECT_GE(i + margin, cube_root); + } +} + +TEST_F(CubicTest, AboveOrgin) { + // 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; ++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); + EXPECT_EQ(expected_cwnd, current_cwnd); + expected_cwnd++; + } + // Cubic phase. + for (int j = 48; j < 100; ++j) { + 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); + } + float elapsed_time_s = 10.0f + 0.1f; // We need to add the RTT here. + expected_cwnd = 11 + (elapsed_time_s * elapsed_time_s * elapsed_time_s * 410) + / 1024; + EXPECT_EQ(expected_cwnd, current_cwnd); +} + +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 = current_cwnd * 717 / 1024; + EXPECT_EQ(expected_cwnd, + cubic_->CongestionWindowAfterPacketLoss(current_cwnd)); + expected_cwnd = current_cwnd * 717 / 1024; + EXPECT_EQ(expected_cwnd, + cubic_->CongestionWindowAfterPacketLoss(current_cwnd)); +} + +TEST_F(CubicTest, BelowOrgin) { + // 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 = current_cwnd * 717 / 1024; + EXPECT_EQ(expected_cwnd, + cubic_->CongestionWindowAfterPacketLoss(current_cwnd)); + current_cwnd = expected_cwnd; + // First update after epoch. + current_cwnd = cubic_->CongestionWindowAfterAck(current_cwnd, rtt_min); + // Cubic phase. + for (int i = 0; i < 54; ++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); + } + expected_cwnd = 422; + EXPECT_EQ(expected_cwnd, current_cwnd); +} + +} // namespace testing +} // namespace net diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.cc b/chromium/net/quic/congestion_control/fix_rate_receiver.cc new file mode 100644 index 00000000000..950b49c0d6b --- /dev/null +++ b/chromium/net/quic/congestion_control/fix_rate_receiver.cc @@ -0,0 +1,35 @@ +// 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/fix_rate_receiver.h" + +#include "base/basictypes.h" +#include "net/quic/congestion_control/receive_algorithm_interface.h" + +namespace { + static const int kInitialBitrate = 100000; // In bytes per second. +} + +namespace net { + +FixRateReceiver::FixRateReceiver() + : configured_rate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)) { +} + +bool FixRateReceiver::GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) { + feedback->type = kFixRate; + feedback->fix_rate.bitrate = configured_rate_; + return true; +} + +void FixRateReceiver::RecordIncomingPacket( + QuicByteCount /*bytes*/, + QuicPacketSequenceNumber /*sequence_number*/, + QuicTime /*timestamp*/, + bool /*recovered*/) { + // Nothing to do for this simple implementation. +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/fix_rate_receiver.h b/chromium/net/quic/congestion_control/fix_rate_receiver.h new file mode 100644 index 00000000000..690f13880f1 --- /dev/null +++ b/chromium/net/quic/congestion_control/fix_rate_receiver.h @@ -0,0 +1,44 @@ +// 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. +// +// Fix rate receive side congestion control, used for initial testing. + +#ifndef NET_QUIC_CONGESTION_CONTROL_FIX_RATE_RECEIVER_H_ +#define NET_QUIC_CONGESTION_CONTROL_FIX_RATE_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_bandwidth.h" + +namespace net { + +namespace test { +class FixRateReceiverPeer; +} // namespace test + +class NET_EXPORT_PRIVATE FixRateReceiver : public ReceiveAlgorithmInterface { + public: + FixRateReceiver(); + + // Implements ReceiveAlgorithmInterface. + virtual bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) OVERRIDE; + + // Implements ReceiveAlgorithmInterface. + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool recovered) OVERRIDE; + private: + friend class test::FixRateReceiverPeer; + + QuicBandwidth configured_rate_; + + DISALLOW_COPY_AND_ASSIGN(FixRateReceiver); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_FIX_RATE_RECEIVER_H_ diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.cc b/chromium/net/quic/congestion_control/fix_rate_sender.cc new file mode 100644 index 00000000000..dff52cf305d --- /dev/null +++ b/chromium/net/quic/congestion_control/fix_rate_sender.cc @@ -0,0 +1,121 @@ +// 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/fix_rate_sender.h" + +#include <math.h> + +#include "base/logging.h" +#include "net/quic/quic_protocol.h" + +namespace { + const int kInitialBitrate = 100000; // In bytes per second. + const uint64 kWindowSizeUs = 10000; // 10 ms. +} + +namespace net { + +FixRateSender::FixRateSender(const QuicClock* clock) + : bitrate_(QuicBandwidth::FromBytesPerSecond(kInitialBitrate)), + fix_rate_leaky_bucket_(bitrate_), + paced_sender_(bitrate_), + data_in_flight_(0), + latest_rtt_(QuicTime::Delta::Zero()) { + DLOG(INFO) << "FixRateSender"; +} + +FixRateSender::~FixRateSender() { +} + +void FixRateSender::OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& /*sent_packets*/) { + DCHECK(feedback.type == kFixRate) << + "Invalid incoming CongestionFeedbackType:" << feedback.type; + if (feedback.type == kFixRate) { + bitrate_ = feedback.fix_rate.bitrate; + fix_rate_leaky_bucket_.SetDrainingRate(feedback_receive_time, bitrate_); + paced_sender_.UpdateBandwidthEstimate(feedback_receive_time, bitrate_); + } + // Silently ignore invalid messages in release mode. +} + +void FixRateSender::OnIncomingAck( + QuicPacketSequenceNumber /*acked_sequence_number*/, + QuicByteCount bytes_acked, + QuicTime::Delta rtt) { + // RTT can't be negative. + DCHECK_LE(0, rtt.ToMicroseconds()); + + data_in_flight_ -= bytes_acked; + if (rtt.IsInfinite()) { + return; + } + latest_rtt_ = rtt; +} + +void FixRateSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { + // Ignore losses for fix rate sender. +} + +void FixRateSender::SentPacket(QuicTime sent_time, + QuicPacketSequenceNumber /*sequence_number*/, + QuicByteCount bytes, + Retransmission is_retransmission) { + fix_rate_leaky_bucket_.Add(sent_time, bytes); + paced_sender_.SentPacket(sent_time, bytes); + if (is_retransmission == NOT_RETRANSMISSION) { + data_in_flight_ += bytes; + } +} + +void FixRateSender::AbandoningPacket( + QuicPacketSequenceNumber /*sequence_number*/, + QuicByteCount /*abandoned_bytes*/) { +} + +QuicTime::Delta FixRateSender::TimeUntilSend( + QuicTime now, + Retransmission /*is_retransmission*/, + HasRetransmittableData /*has_retransmittable_data*/, + IsHandshake /* handshake */) { + if (CongestionWindow() > fix_rate_leaky_bucket_.BytesPending(now)) { + if (CongestionWindow() <= data_in_flight_) { + // We need an ack before we send more. + return QuicTime::Delta::Infinite(); + } + return paced_sender_.TimeUntilSend(now, QuicTime::Delta::Zero()); + } + QuicTime::Delta time_remaining = fix_rate_leaky_bucket_.TimeRemaining(now); + if (time_remaining.IsZero()) { + // We need an ack before we send more. + return QuicTime::Delta::Infinite(); + } + return paced_sender_.TimeUntilSend(now, time_remaining); +} + +QuicByteCount FixRateSender::CongestionWindow() { + QuicByteCount window_size_bytes = bitrate_.ToBytesPerPeriod( + QuicTime::Delta::FromMicroseconds(kWindowSizeUs)); + // Make sure window size is not less than a packet. + return std::max(kMaxPacketSize, window_size_bytes); +} + +QuicBandwidth FixRateSender::BandwidthEstimate() { + return bitrate_; +} + +QuicTime::Delta FixRateSender::SmoothedRtt() { + // TODO(satyamshekhar): Calculate and return smoothed rtt. + return latest_rtt_; +} + +QuicTime::Delta FixRateSender::RetransmissionDelay() { + // TODO(pwestin): Calculate and return retransmission delay. + // Use 2 * the latest RTT for now. + return latest_rtt_.Add(latest_rtt_); +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/fix_rate_sender.h b/chromium/net/quic/congestion_control/fix_rate_sender.h new file mode 100644 index 00000000000..38cebad165f --- /dev/null +++ b/chromium/net/quic/congestion_control/fix_rate_sender.h @@ -0,0 +1,65 @@ +// 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. +// +// Fix rate send side congestion control, used for testing. + +#ifndef NET_QUIC_CONGESTION_CONTROL_FIX_RATE_SENDER_H_ +#define NET_QUIC_CONGESTION_CONTROL_FIX_RATE_SENDER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_time.h" +#include "net/quic/congestion_control/leaky_bucket.h" +#include "net/quic/congestion_control/paced_sender.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" + +namespace net { + +class NET_EXPORT_PRIVATE FixRateSender : public SendAlgorithmInterface { + public: + explicit FixRateSender(const QuicClock* clock); + virtual ~FixRateSender(); + + // Start implementation of SendAlgorithmInterface. + virtual void OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& sent_packets) OVERRIDE; + virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount acked_bytes, + QuicTime::Delta rtt) OVERRIDE; + virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE; + virtual void SentPacket(QuicTime sent_time, + QuicPacketSequenceNumber equence_number, + QuicByteCount bytes, + Retransmission is_retransmission) OVERRIDE; + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) OVERRIDE; + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data, + IsHandshake handshake) OVERRIDE; + virtual QuicBandwidth BandwidthEstimate() OVERRIDE; + virtual QuicTime::Delta SmoothedRtt() OVERRIDE; + virtual QuicTime::Delta RetransmissionDelay() OVERRIDE; + // End implementation of SendAlgorithmInterface. + + private: + QuicByteCount CongestionWindow(); + + QuicBandwidth bitrate_; + LeakyBucket fix_rate_leaky_bucket_; + PacedSender paced_sender_; + QuicByteCount data_in_flight_; + QuicTime::Delta latest_rtt_; + + DISALLOW_COPY_AND_ASSIGN(FixRateSender); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_FIX_RATE_SENDER_H_ diff --git a/chromium/net/quic/congestion_control/fix_rate_test.cc b/chromium/net/quic/congestion_control/fix_rate_test.cc new file mode 100644 index 00000000000..f914ed671a6 --- /dev/null +++ b/chromium/net/quic/congestion_control/fix_rate_test.cc @@ -0,0 +1,120 @@ +// 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. + +// Test for FixRate sender and receiver. + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/fix_rate_receiver.h" +#include "net/quic/congestion_control/fix_rate_sender.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class FixRateReceiverPeer : public FixRateReceiver { + public: + FixRateReceiverPeer() + : FixRateReceiver() { + } + void SetBitrate(QuicBandwidth fix_rate) { + FixRateReceiver::configured_rate_ = fix_rate; + } +}; + +class FixRateTest : public ::testing::Test { + protected: + FixRateTest() + : rtt_(QuicTime::Delta::FromMilliseconds(30)), + sender_(new FixRateSender(&clock_)), + receiver_(new FixRateReceiverPeer()), + start_(clock_.Now()) { + // Make sure clock does not start at 0. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); + } + const QuicTime::Delta rtt_; + MockClock clock_; + SendAlgorithmInterface::SentPacketsMap unused_packet_map_; + scoped_ptr<FixRateSender> sender_; + scoped_ptr<FixRateReceiverPeer> receiver_; + const QuicTime start_; +}; + +TEST_F(FixRateTest, ReceiverAPI) { + QuicCongestionFeedbackFrame feedback; + QuicTime timestamp(QuicTime::Zero()); + receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(300)); + receiver_->RecordIncomingPacket(1, 1, timestamp, false); + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + EXPECT_EQ(kFixRate, feedback.type); + EXPECT_EQ(300000u, feedback.fix_rate.bitrate.ToBytesPerSecond()); +} + +TEST_F(FixRateTest, SenderAPI) { + QuicCongestionFeedbackFrame feedback; + feedback.type = kFixRate; + feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(300); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), + unused_packet_map_); + EXPECT_EQ(300000, sender_->BandwidthEstimate().ToBytesPerSecond()); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->SentPacket(clock_.Now(), 1, kMaxPacketSize, NOT_RETRANSMISSION); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_->SentPacket(clock_.Now(), 2, kMaxPacketSize, NOT_RETRANSMISSION); + sender_->SentPacket(clock_.Now(), 3, 600, NOT_RETRANSMISSION); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), + sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(2)); + EXPECT_EQ(QuicTime::Delta::Infinite(), sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(8)); + sender_->OnIncomingAck(1, kMaxPacketSize, rtt_); + sender_->OnIncomingAck(2, kMaxPacketSize, rtt_); + sender_->OnIncomingAck(3, 600, rtt_); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); +} + +TEST_F(FixRateTest, FixRatePacing) { + const QuicByteCount packet_size = 1200; + const QuicBandwidth bitrate = QuicBandwidth::FromKBytesPerSecond(240); + const int64 num_packets = 200; + QuicCongestionFeedbackFrame feedback; + receiver_->SetBitrate(QuicBandwidth::FromKBytesPerSecond(240)); + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), + unused_packet_map_); + QuicTime acc_advance_time(QuicTime::Zero()); + QuicPacketSequenceNumber sequence_number = 0; + for (int i = 0; i < num_packets; i += 2) { + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE).IsZero()); + sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, + NOT_RETRANSMISSION); + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE).IsZero()); + sender_->SentPacket(clock_.Now(), sequence_number++, packet_size, + NOT_RETRANSMISSION); + QuicTime::Delta advance_time = sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + clock_.AdvanceTime(advance_time); + sender_->OnIncomingAck(sequence_number - 1, packet_size, rtt_); + sender_->OnIncomingAck(sequence_number - 2, packet_size, rtt_); + acc_advance_time = acc_advance_time.Add(advance_time); + } + EXPECT_EQ(num_packets * packet_size * 1000000 / bitrate.ToBytesPerSecond(), + static_cast<uint64>(acc_advance_time.Subtract(start_) + .ToMicroseconds())); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.cc b/chromium/net/quic/congestion_control/hybrid_slow_start.cc new file mode 100644 index 00000000000..8968dc94775 --- /dev/null +++ b/chromium/net/quic/congestion_control/hybrid_slow_start.cc @@ -0,0 +1,112 @@ +// 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" + +namespace net { + +// Note(pwestin): the magic clamping numbers come from the original code in +// tcp_cubic.c. +// Number of delay samples for detecting the increase of delay. +const int kHybridStartMinSamples = 8; +const int kHybridStartDelayFactorExp = 4; // 2^4 = 16 +const int kHybridStartDelayMinThresholdUs = 2000; +const int kHybridStartDelayMaxThresholdUs = 16000; + +HybridSlowStart::HybridSlowStart(const QuicClock* clock) + : clock_(clock), + started_(false), + found_ack_train_(false), + found_delay_(false), + round_start_(QuicTime::Zero()), + end_sequence_number_(0), + last_time_(QuicTime::Zero()), + sample_count_(0), + current_rtt_(QuicTime::Delta::Zero()) { +} + +void HybridSlowStart::Restart() { + found_ack_train_ = false; + found_delay_ = false; +} + +void HybridSlowStart::Reset(QuicPacketSequenceNumber end_sequence_number) { + DLOG(INFO) << "Reset hybrid slow start @" << end_sequence_number; + round_start_ = last_time_ = clock_->ApproximateNow(); + end_sequence_number_ = end_sequence_number; + current_rtt_ = QuicTime::Delta::Zero(); + sample_count_ = 0; + started_ = true; +} + +bool HybridSlowStart::EndOfRound(QuicPacketSequenceNumber ack) { + return end_sequence_number_ <= ack; +} + +void HybridSlowStart::Update(QuicTime::Delta rtt, QuicTime::Delta delay_min) { + // The original code doesn't invoke this until we hit 16 packet per burst. + // Since the code handles lower than 16 grecefully and I removed that + // limit. + if (found_ack_train_ || found_delay_) { + return; + } + 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 beeing spread out + // more than the capacity. + // This first trigger will not come into play until we hit roughly 4.8 Mbit/s. + // TODO(pwestin): we need to make sure our pacing don't trigger this detector. + if (current_time.Subtract(last_time_).ToMicroseconds() <= + kHybridStartDelayMinThresholdUs) { + last_time_ = current_time; + if (current_time.Subtract(round_start_).ToMicroseconds() >= + (delay_min.ToMicroseconds() >> 1)) { + found_ack_train_ = true; + } + } + // Second detection parameter - delay increase detection. + // Compare the minimum delay (current_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. + sample_count_++; + if (sample_count_ <= kHybridStartMinSamples) { + if (current_rtt_.IsZero() || current_rtt_ > rtt) { + current_rtt_ = rtt; + } + } + // We only need to check this once. + if (sample_count_ == kHybridStartMinSamples) { + int accepted_variance_us = delay_min.ToMicroseconds() >> + kHybridStartDelayFactorExp; + accepted_variance_us = std::min(accepted_variance_us, + kHybridStartDelayMaxThresholdUs); + QuicTime::Delta accepted_variance = QuicTime::Delta::FromMicroseconds( + std::max(accepted_variance_us, kHybridStartDelayMinThresholdUs)); + + if (current_rtt_ > delay_min.Add(accepted_variance)) { + found_delay_ = true; + } + } +} + +bool HybridSlowStart::Exit() { + // If either one of the two conditions are met we exit from slow start + // immediately. + if (found_ack_train_ || found_delay_) { + return true; + } + return false; +} + +QuicTime::Delta HybridSlowStart::SmoothedRtt() { + // TODO(satyamshekhar): Calculate and return smooth average of rtt over time. + return current_rtt_; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/hybrid_slow_start.h b/chromium/net/quic/congestion_control/hybrid_slow_start.h new file mode 100644 index 00000000000..b0e424883da --- /dev/null +++ b/chromium/net/quic/congestion_control/hybrid_slow_start.h @@ -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. +// +// 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); + + // Start a new slow start phase. + void Restart(); + + // Returns true if this ack the last sequence number of our current slow start + // round. + // Call Reset if this returns true. + bool EndOfRound(QuicPacketSequenceNumber ack); + + // Call for each round (burst) in the slow start phase. + void Reset(QuicPacketSequenceNumber end_sequence_number); + + // rtt: it the RTT for this ack packet. + // delay_min: is the lowest delay (RTT) we have seen during the session. + void Update(QuicTime::Delta rtt, QuicTime::Delta delay_min); + + // Returns true when we should exit slow start. + bool Exit(); + + bool started() { return started_; } + + QuicTime::Delta SmoothedRtt(); + + private: + const QuicClock* clock_; + bool started_; + bool found_ack_train_; + bool found_delay_; + QuicTime round_start_; // Beginning of each slow start round. + QuicPacketSequenceNumber end_sequence_number_; // End of slow start round. + QuicTime last_time_; // Last time when the ACK spacing was close. + uint8 sample_count_; // Number of samples to decide current RTT. + QuicTime::Delta current_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/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc b/chromium/net/quic/congestion_control/hybrid_slow_start_test.cc new file mode 100644 index 00000000000..0a9f91fd3f0 --- /dev/null +++ b/chromium/net/quic/congestion_control/hybrid_slow_start_test.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 "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() { + slowStart_.reset(new HybridSlowStart(&clock_)); + } + const QuicTime::Delta one_ms_; + const QuicTime::Delta rtt_; + MockClock clock_; + scoped_ptr<HybridSlowStart> slowStart_; +}; + +TEST_F(HybridSlowStartTest, Simple) { + QuicPacketSequenceNumber sequence_number = 1; + QuicPacketSequenceNumber end_sequence_number = 3; + slowStart_->Reset(end_sequence_number); + + EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++)); + + // Test duplicates. + EXPECT_FALSE(slowStart_->EndOfRound(sequence_number)); + + EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++)); + EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++)); + + // Test without a new registered end_sequence_number; + EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++)); + + end_sequence_number = 20; + slowStart_->Reset(end_sequence_number); + while (sequence_number < end_sequence_number) { + EXPECT_FALSE(slowStart_->EndOfRound(sequence_number++)); + } + EXPECT_TRUE(slowStart_->EndOfRound(sequence_number++)); +} + +TEST_F(HybridSlowStartTest, AckTrain) { + // At a typical RTT 60 ms, assuming that the inter arrival 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) { + slowStart_->Reset(end_sequence_number); + do { + clock_.AdvanceTime(one_ms_); + slowStart_->Update(rtt_, rtt_); + EXPECT_FALSE(slowStart_->Exit()); + } while (!slowStart_->EndOfRound(sequence_number++)); + end_sequence_number *= 2; // Exponential growth. + } + slowStart_->Reset(end_sequence_number); + + for (int n = 0; n < 29 && !slowStart_->EndOfRound(sequence_number++); ++n) { + clock_.AdvanceTime(one_ms_); + slowStart_->Update(rtt_, rtt_); + EXPECT_FALSE(slowStart_->Exit()); + } + clock_.AdvanceTime(one_ms_); + slowStart_->Update(rtt_, rtt_); + EXPECT_TRUE(slowStart_->Exit()); +} + +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; + slowStart_->Reset(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) { + slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n)), rtt_); + EXPECT_FALSE(slowStart_->Exit()); + } + slowStart_->Reset(end_sequence_number++); + for (int n = 1; n < kHybridStartMinSamples; ++n) { + slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(n + 4)), + rtt_); + EXPECT_FALSE(slowStart_->Exit()); + } + // Expect to trigger since all packets in this burst was above the long term + // RTT provided. + slowStart_->Update(rtt_.Add(QuicTime::Delta::FromMilliseconds(4)), rtt_); + EXPECT_TRUE(slowStart_->Exit()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc new file mode 100644 index 00000000000..0a15a74dffc --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc @@ -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. + +#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "net/quic/congestion_control/cube_root.h" +#include "net/quic/quic_protocol.h" + +namespace { +// 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. +// TODO(pwestin): Tuning parameter, currently close to TCP cubic at 100ms RTT. +const int kPacedCubeScale = 6000; +const uint64 kCubeFactor = (GG_UINT64_C(1) << kCubeScale) / kPacedCubeScale; +} // namespace + +namespace net { + +InterArrivalBitrateRampUp::InterArrivalBitrateRampUp(const QuicClock* clock) + : clock_(clock), + current_rate_(QuicBandwidth::Zero()), + channel_estimate_(QuicBandwidth::Zero()), + available_channel_estimate_(QuicBandwidth::Zero()), + halfway_point_(QuicBandwidth::Zero()), + epoch_(QuicTime::Zero()), + last_update_time_(QuicTime::Zero()) { +} + +void InterArrivalBitrateRampUp::Reset(QuicBandwidth new_rate, + QuicBandwidth available_channel_estimate, + QuicBandwidth channel_estimate) { + epoch_ = clock_->ApproximateNow(); + last_update_time_ = epoch_; + available_channel_estimate_ = std::max(new_rate, available_channel_estimate); + channel_estimate_ = std::max(channel_estimate, available_channel_estimate_); + + halfway_point_ = available_channel_estimate_.Add( + (channel_estimate_.Subtract(available_channel_estimate_)).Scale(0.5f)); + + if (new_rate < available_channel_estimate_) { + time_to_origin_point_ = CalcuateTimeToOriginPoint( + available_channel_estimate_.Subtract(new_rate)); + } else if (new_rate >= channel_estimate_) { + time_to_origin_point_ = 0; + } else if (new_rate >= halfway_point_) { + time_to_origin_point_ = + CalcuateTimeToOriginPoint(channel_estimate_.Subtract(new_rate)); + } else { + time_to_origin_point_ = CalcuateTimeToOriginPoint( + new_rate.Subtract(available_channel_estimate_)); + } + current_rate_ = new_rate; + DLOG(INFO) << "Reset; time to origin point:" << time_to_origin_point_; +} + +void InterArrivalBitrateRampUp::UpdateChannelEstimate( + QuicBandwidth channel_estimate) { + if (available_channel_estimate_ > channel_estimate || + current_rate_ > channel_estimate || + channel_estimate_ == channel_estimate) { + // Ignore, because one of the following reasons: + // 1) channel estimate is bellow our current available estimate which we + // value higher that this estimate. + // 2) channel estimate is bellow our current send rate. + // 3) channel estimate has not changed. + return; + } + if (available_channel_estimate_ == halfway_point_ && + channel_estimate_ == halfway_point_) { + // First time we get a usable channel estimate. + channel_estimate_ = channel_estimate; + halfway_point_ = available_channel_estimate_.Add( + (channel_estimate_.Subtract(available_channel_estimate_).Scale(0.5f))); + DLOG(INFO) << "UpdateChannelEstimate; first usable value:" + << channel_estimate.ToKBitsPerSecond() << " Kbits/s"; + return; + } + if (current_rate_ < halfway_point_) { + // Update channel estimate without recalculating if we are bellow the + // halfway point. + channel_estimate_ = channel_estimate; + return; + } + // We are between halfway point and our channel_estimate. + epoch_ = clock_->ApproximateNow(); + last_update_time_ = epoch_; + channel_estimate_ = channel_estimate; + + time_to_origin_point_ = + CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate_)); + + DLOG(INFO) << "UpdateChannelEstimate; time to origin point:" + << time_to_origin_point_; +} + +QuicBandwidth InterArrivalBitrateRampUp::GetNewBitrate( + QuicBandwidth sent_bitrate) { + DCHECK(epoch_.IsInitialized()); + QuicTime current_time = clock_->ApproximateNow(); + // Cubic is "independent" of RTT, the update is limited by the time elapsed. + if (current_time.Subtract(last_update_time_) <= MaxCubicTimeInterval()) { + return current_rate_; + } + QuicTime::Delta time_from_last_update = + current_time.Subtract(last_update_time_); + + last_update_time_ = current_time; + + if (!sent_bitrate.IsZero() && + sent_bitrate.Add(sent_bitrate) < current_rate_) { + // Don't go up in bitrate when we are not sending. + // We need to update the epoch to reflect this state. + epoch_ = epoch_.Add(time_from_last_update); + DLOG(INFO) << "Don't increase; our sent bitrate is:" + << sent_bitrate.ToKBitsPerSecond() << " Kbits/s" + << " current target rate is:" + << current_rate_.ToKBitsPerSecond() << " Kbits/s"; + return current_rate_; + } + QuicTime::Delta time_from_epoch = current_time.Subtract(epoch_); + + // Change the time unit from microseconds to 2^10 fractions per second. This + // is done to allow us to use shift as a divide operator. + int64 elapsed_time = (time_from_epoch.ToMicroseconds() << 10) / + kNumMicrosPerSecond; + + int64 offset = time_to_origin_point_ - elapsed_time; + // Note: using int64 since QuicBandwidth can't be negative + int64 delta_pace_kbps = (kPacedCubeScale * offset * offset * offset) >> + kCubeScale; + + bool start_bellow_halfway_point = false; + if (current_rate_ < halfway_point_) { + start_bellow_halfway_point = true; + + // available_channel_estimate_ is the orgin of the cubic function. + QuicBandwidth current_rate = QuicBandwidth::FromBytesPerSecond( + available_channel_estimate_.ToBytesPerSecond() - + (delta_pace_kbps << 10)); + + if (start_bellow_halfway_point && current_rate >= halfway_point_) { + // We passed the halfway point, recalculate with new orgin. + epoch_ = clock_->ApproximateNow(); + // channel_estimate_ is the new orgin of the cubic function. + if (current_rate >= channel_estimate_) { + time_to_origin_point_ = 0; + } else { + time_to_origin_point_ = + CalcuateTimeToOriginPoint(channel_estimate_.Subtract(current_rate)); + } + DLOG(INFO) << "Passed the halfway point; time to origin point:" + << time_to_origin_point_; + } + current_rate_ = current_rate; + } else { + // channel_estimate_ is the orgin of the cubic function. + current_rate_ = QuicBandwidth::FromBytesPerSecond( + channel_estimate_.ToBytesPerSecond() - (delta_pace_kbps << 10)); + } + return current_rate_; +} + +uint32 InterArrivalBitrateRampUp::CalcuateTimeToOriginPoint( + QuicBandwidth rate_difference) const { + return CubeRoot::Root(kCubeFactor * rate_difference.ToKBytesPerSecond()); +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h new file mode 100644 index 00000000000..931992391a5 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h @@ -0,0 +1,65 @@ +// 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. + +// Ramp up bitrate from a start point normally our "current_rate" as long as we +// have no packet loss or delay events. +// The first half of the ramp up curve follows a cubic function with its orgin +// at the estimated available bandwidth, onece the bitrate pass the halfway +// point between the estimated available bandwidth and the estimated max +// bandwidth it will follw a new cubic function with its orgin at the estimated +// max bandwidth. +#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_ +#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_ + +#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_time.h" + +namespace net { + +class NET_EXPORT_PRIVATE InterArrivalBitrateRampUp { + public: + explicit InterArrivalBitrateRampUp(const QuicClock* clock); + + // Call after a decision to lower the bitrate and after a probe. + void Reset(QuicBandwidth current_rate, + QuicBandwidth available_channel_estimate, + QuicBandwidth channel_estimate); + + // Call everytime we get a new channel estimate. + void UpdateChannelEstimate(QuicBandwidth channel_estimate); + + // Compute a new send pace to use. + QuicBandwidth GetNewBitrate(QuicBandwidth sent_bitrate); + + private: + uint32 CalcuateTimeToOriginPoint(QuicBandwidth rate_difference) const; + + static const QuicTime::Delta MaxCubicTimeInterval() { + return QuicTime::Delta::FromMilliseconds(30); + } + + const QuicClock* clock_; + + QuicBandwidth current_rate_; + QuicBandwidth channel_estimate_; + QuicBandwidth available_channel_estimate_; + QuicBandwidth halfway_point_; + + // Time when this cycle started, after a Reset. + QuicTime epoch_; + + // Time when we updated current_rate_. + QuicTime last_update_time_; + + // Time to origin point of cubic function in 2^10 fractions of a second. + uint32 time_to_origin_point_; + + DISALLOW_COPY_AND_ASSIGN(InterArrivalBitrateRampUp); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_BITRATE_RAMP_UP_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc new file mode 100644 index 00000000000..acae78d2056 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc @@ -0,0 +1,404 @@ +// 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 "base/logging.h" +#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class InterArrivalBitrateRampUpTest : public ::testing::Test { + protected: + InterArrivalBitrateRampUpTest() + : one_ms_(QuicTime::Delta::FromMilliseconds(1)), + hundred_ms_(QuicTime::Delta::FromMilliseconds(100)), + bitrate_ramp_up_(&clock_) { + } + virtual void SetUp() { + clock_.AdvanceTime(one_ms_); + } + const QuicTime::Delta one_ms_; + const QuicTime::Delta hundred_ms_; + MockClock clock_; + InterArrivalBitrateRampUp bitrate_ramp_up_; +}; + +TEST_F(InterArrivalBitrateRampUpTest, GoodEstimates) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); + QuicBandwidth available_channel_estimate = + QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400); + QuicBandwidth halfway_point = available_channel_estimate.Add( + channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + channel_estimate); + + // First concave growth, towards available_channel_estimate. + for (int i = 0; i < 25; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(available_channel_estimate, sent_bitrate); + EXPECT_LE(start_rate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + + // First convex growth, from available_channel_estimate. + for (int j = 0; j < 25; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + + // Second concave growth, towards channel_estimate. + for (int i = 0; i < 24; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(channel_estimate, sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + + // Second convex growth, from channel_estimate. + for (int j = 0; j < 25; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000, + sent_bitrate.ToBytesPerSecond(), 10000); + + // Verify that we increase cubic. + for (int j = 0; j < 23; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000, + sent_bitrate.ToBytesPerSecond(), 10000); +} + +TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesLimitedSendRate) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); + QuicBandwidth available_channel_estimate = + QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth max_sent_rate = + QuicBandwidth::FromKBytesPerSecond(125); + QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400); + QuicBandwidth halfway_point = available_channel_estimate.Add( + channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + channel_estimate); + + // First concave growth, towards available_channel_estimate. + // Should pass without being affected by the max_sent_rate. + for (int i = 0; i < 25; ++i) { + clock_.AdvanceTime(hundred_ms_); + // Cap our previus sent rate. + sent_bitrate = std::min(sent_bitrate, max_sent_rate); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(available_channel_estimate, sent_bitrate); + EXPECT_LE(start_rate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + // Cap our previus sent rate. + sent_bitrate = std::min(sent_bitrate, max_sent_rate); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + + // First convex growth, from available_channel_estimate. + for (int j = 0; j < 25; ++j) { + clock_.AdvanceTime(hundred_ms_); + // Cap our previus sent rate. + sent_bitrate = std::min(sent_bitrate, max_sent_rate); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = std::min(sent_bitrate, max_sent_rate); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + // We expect 2 * sent_bitrate to cap the rate. + EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate); + // Remove our sent cap. + // Expect bitrate to continue to ramp from its previous rate. + for (int j = 0; j < 5; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_LE(max_sent_rate.Add(max_sent_rate), sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); +} + +TEST_F(InterArrivalBitrateRampUpTest, GoodEstimatesCloseToChannelEstimate) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); + QuicBandwidth available_channel_estimate = + QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250); + QuicBandwidth halfway_point = available_channel_estimate.Add( + channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + channel_estimate); + + // First concave growth, towards available_channel_estimate. + for (int i = 0; i < 25; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(available_channel_estimate, sent_bitrate); + EXPECT_LE(start_rate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + + // First convex growth, from available_channel_estimate. + for (int j = 0; j < 15; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + + // Second concave growth, towards channel_estimate. + for (int i = 0; i < 14; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(channel_estimate, sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + + // Second convex growth, from channel_estimate. + for (int j = 0; j < 25; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000, + sent_bitrate.ToBytesPerSecond(), 10000); + + // Verify that we increase cubic. + for (int j = 0; j < 24; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 780000, + sent_bitrate.ToBytesPerSecond(), 20000); +} + +TEST_F(InterArrivalBitrateRampUpTest, UncertainEstimates) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); + QuicBandwidth available_channel_estimate = + QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth channel_estimate = + QuicBandwidth::FromKBytesPerSecond(400 * 0.7f); + QuicBandwidth halfway_point = available_channel_estimate.Add( + channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + channel_estimate); + + // First concave growth, towards available_channel_estimate. + for (int i = 0; i < 20; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(available_channel_estimate, sent_bitrate); + EXPECT_LE(start_rate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + + // First convex growth, from available_channel_estimate. + for (int j = 0; j < 23; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + + // Second concave growth, towards channel_estimate. + for (int i = 0; i < 12; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(channel_estimate, sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + + // Second convex growth, from channel_estimate. + for (int j = 0; j < 30; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 100000, + sent_bitrate.ToBytesPerSecond(), 10000); + + // Verify that we increase cubic. + for (int j = 0; j < 23; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond() + 750000, + sent_bitrate.ToBytesPerSecond(), 20000); +} + +TEST_F(InterArrivalBitrateRampUpTest, UnknownEstimates) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(100); + QuicBandwidth available_channel_estimate = + QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(400); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + available_channel_estimate); + + // First convex growth, from start_rate. + for (int j = 0; j < 20; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(start_rate, sent_bitrate); + EXPECT_GE(available_channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(available_channel_estimate.ToBytesPerSecond(), + sent_bitrate.ToBytesPerSecond(), 10000); + + // Verify that we increase cubic. + for (int j = 0; j < 31; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(channel_estimate, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_NEAR(channel_estimate.ToBytesPerSecond(), + sent_bitrate.ToBytesPerSecond(), 10000); +} + +TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateHigher) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth available_channel_estimate = start_rate; + QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250); + QuicBandwidth halfway_point = available_channel_estimate.Add( + channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + channel_estimate); + + // Convex growth, from available_channel_estimate. + for (int j = 0; j < 16; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + + // Increse channel estimate. + channel_estimate = QuicBandwidth::FromKBytesPerSecond(300); + bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate); + + // Concave growth, towards channel_estimate. + for (int i = 0; i < 22; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(channel_estimate, sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); +} + +TEST_F(InterArrivalBitrateRampUpTest, UpdatingChannelEstimateLower) { + QuicBandwidth start_rate = QuicBandwidth::FromKBytesPerSecond(200); + QuicBandwidth available_channel_estimate = start_rate; + QuicBandwidth channel_estimate = QuicBandwidth::FromKBytesPerSecond(250); + QuicBandwidth halfway_point = available_channel_estimate.Add( + channel_estimate.Subtract(available_channel_estimate).Scale(0.5f)); + QuicBandwidth sent_bitrate = QuicBandwidth::Zero(); + bitrate_ramp_up_.Reset(start_rate, + available_channel_estimate, + channel_estimate); + + // Convex growth, from available_channel_estimate. + for (int j = 0; j < 16; ++j) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(available_channel_estimate, sent_bitrate); + EXPECT_GE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + + // Decrese channel estimate. + channel_estimate = QuicBandwidth::FromKBytesPerSecond(240); + bitrate_ramp_up_.UpdateChannelEstimate(channel_estimate); + + // Concave growth, towards channel_estimate. + for (int i = 0; i < 11; ++i) { + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_GE(channel_estimate, sent_bitrate); + EXPECT_LE(halfway_point, sent_bitrate); + } + clock_.AdvanceTime(hundred_ms_); + sent_bitrate = bitrate_ramp_up_.GetNewBitrate(sent_bitrate); + EXPECT_LE(channel_estimate, sent_bitrate); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc new file mode 100644 index 00000000000..73e005d788a --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc @@ -0,0 +1,258 @@ +// 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/inter_arrival_overuse_detector.h" + +#include <math.h> +#include <stdlib.h> + +// Initial noise variance, equal to a standard deviation of 1 millisecond. +static const float kInitialVarianceNoise = 1000000.0; + +// Minimum variance of the time delta. +static const int kMinVarianceDelta = 10000; + +// Threshold for accumulated delta. +static const int kThresholdAccumulatedDeltasUs = 1000; + +// The higher the beta parameter, the lower is the effect of the input and the +// more damping of the noise. And the longer time for a detection. +static const float kBeta = 0.98f; + +// Same as above, described as numerator and denominator. +static const int kBetaNumerator = 49; +static const int kBetaDenominator = 50; + +// Trigger a signal when the accumulated time drift is larger than +// 5 x standard deviation. +// A lower value triggers earlier with more false detect as a side effect. +static const int kDetectDriftStandardDeviation = 5; + +// Trigger an overuse when the time difference between send time and receive +// is larger than 7 x standard deviation. +// A lower value triggers earlier with more false detect as a side effect. +static const float kDetectTimeDiffStandardDeviation = 7; + +// Trigger an overuse when the mean of the time difference diverges too far +// from 0. +// A higher value trigger earlier with more false detect as a side effect. +static const int kDetectSlopeFactor = 14; + +// We need to get some initial statistics before the detection can start. +static const int kMinSamplesBeforeDetect = 10; + +namespace net { + +InterArrivalOveruseDetector::InterArrivalOveruseDetector() + : last_sequence_number_(0), + num_of_deltas_(0), + accumulated_deltas_(QuicTime::Delta::Zero()), + delta_mean_(0.0), + delta_variance_(kInitialVarianceNoise), + delta_overuse_counter_(0), + delta_estimate_(kBandwidthSteady), + slope_overuse_counter_(0), + slope_estimate_(kBandwidthSteady), + send_receive_offset_(QuicTime::Delta::Infinite()), + estimated_congestion_delay_(QuicTime::Delta::Zero()) { +} + +void InterArrivalOveruseDetector::OnAcknowledgedPacket( + QuicPacketSequenceNumber sequence_number, + QuicTime send_time, + bool last_of_send_time, + QuicTime receive_time) { + if (last_sequence_number_ >= sequence_number) { + // This is an old packet and should be ignored. Note that we are called + // with a full 64 bit sequence number, even if the wire format may only + // convey some low-order bits of that number. + DLOG(INFO) << "Skip old packet"; + return; + } + + last_sequence_number_ = sequence_number; + + if (current_packet_group_.send_time != send_time) { + // First value in this group. If the last packet of a group is lost we + // overwrite the old value and start over with a new measurement. + current_packet_group_.send_time = send_time; + // The receive_time value might not be first in a packet burst if that + // packet was lost, however we will still use it in this calculation. + UpdateSendReceiveTimeOffset(receive_time.Subtract(send_time)); + } + if (!last_of_send_time) { + // We expect more packet with this send time. + return; + } + // First packet of a later group, the previous group sample is ready. + if (previous_packet_group_.send_time.IsInitialized()) { + QuicTime::Delta sent_delta = send_time.Subtract( + previous_packet_group_.send_time); + QuicTime::Delta receive_delta = receive_time.Subtract( + previous_packet_group_.last_receive_time); + // We assume that groups of packets are sent together as bursts (tagged + // with identical send times) because it is too computationally expensive + // to pace them out individually. The received_delta is then the total + // delta between the receipt of the last packet of the previous group, and + // the last packet of the current group. Assuming we are transmitting + // these bursts on average at the available bandwidth rate, there should be + // no change in overall spread (both deltas should be the same). + UpdateFilter(receive_delta, sent_delta); + } + // Save current as previous. + previous_packet_group_ = current_packet_group_; + previous_packet_group_.last_receive_time = receive_time; +} + +void InterArrivalOveruseDetector::UpdateSendReceiveTimeOffset( + QuicTime::Delta offset) { + // Note the send and receive time can have a randomly large offset, however + // they are stable in relation to each other, hence no or extremely low clock + // drift relative to the duration of our stream. + if (offset.ToMicroseconds() < send_receive_offset_.ToMicroseconds()) { + send_receive_offset_ = offset; + } + estimated_congestion_delay_ = offset.Subtract(send_receive_offset_); +} + +BandwidthUsage InterArrivalOveruseDetector::GetState( + QuicTime::Delta* estimated_congestion_delay) { + *estimated_congestion_delay = estimated_congestion_delay_; + int64 sigma_delta = sqrt(static_cast<double>(delta_variance_)); + DetectSlope(sigma_delta); + DetectDrift(sigma_delta); + return std::max(slope_estimate_, delta_estimate_); +} + +void InterArrivalOveruseDetector::UpdateFilter(QuicTime::Delta received_delta, + QuicTime::Delta sent_delta) { + ++num_of_deltas_; + QuicTime::Delta time_diff = received_delta.Subtract(sent_delta); + UpdateDeltaEstimate(time_diff); + accumulated_deltas_ = accumulated_deltas_.Add(time_diff); +} + +void InterArrivalOveruseDetector::UpdateDeltaEstimate( + QuicTime::Delta residual) { + DCHECK_EQ(1, kBetaDenominator - kBetaNumerator); + int64 residual_us = residual.ToMicroseconds(); + delta_mean_ = + (kBetaNumerator * delta_mean_ + residual_us) / kBetaDenominator; + delta_variance_ = + (kBetaNumerator * delta_variance_ + + (delta_mean_ - residual_us) * (delta_mean_ - residual_us)) / + kBetaDenominator; + + if (delta_variance_ < kMinVarianceDelta) { + delta_variance_ = kMinVarianceDelta; + } +} + +void InterArrivalOveruseDetector::DetectDrift(int64 sigma_delta) { + // We have 2 drift detectors. The accumulate of deltas and the absolute time + // differences. + if (num_of_deltas_ < kMinSamplesBeforeDetect) { + return; + } + if (delta_overuse_counter_ > 0 && + accumulated_deltas_.ToMicroseconds() > kThresholdAccumulatedDeltasUs) { + if (delta_estimate_ != kBandwidthDraining) { + DLOG(INFO) << "Bandwidth estimate drift: Draining buffer(s) " + << accumulated_deltas_.ToMilliseconds() << " ms"; + delta_estimate_ = kBandwidthDraining; + } + return; + } + if ((sigma_delta * kDetectTimeDiffStandardDeviation > + estimated_congestion_delay_.ToMicroseconds()) && + (sigma_delta * kDetectDriftStandardDeviation > + abs(accumulated_deltas_.ToMicroseconds()))) { + if (delta_estimate_ != kBandwidthSteady) { + DLOG(INFO) << "Bandwidth estimate drift: Steady" + << " mean:" << delta_mean_ + << " sigma:" << sigma_delta + << " offset:" << send_receive_offset_.ToMicroseconds() + << " delta:" << estimated_congestion_delay_.ToMicroseconds() + << " drift:" << accumulated_deltas_.ToMicroseconds(); + delta_estimate_ = kBandwidthSteady; + // Reset drift counter. + accumulated_deltas_ = QuicTime::Delta::Zero(); + delta_overuse_counter_ = 0; + } + return; + } + if (accumulated_deltas_.ToMicroseconds() > 0) { + if (delta_estimate_ != kBandwidthOverUsing) { + ++delta_overuse_counter_; + DLOG(INFO) << "Bandwidth estimate drift: Over using" + << " mean:" << delta_mean_ + << " sigma:" << sigma_delta + << " offset:" << send_receive_offset_.ToMicroseconds() + << " delta:" << estimated_congestion_delay_.ToMicroseconds() + << " drift:" << accumulated_deltas_.ToMicroseconds(); + delta_estimate_ = kBandwidthOverUsing; + } + } else { + if (delta_estimate_ != kBandwidthUnderUsing) { + --delta_overuse_counter_; + DLOG(INFO) << "Bandwidth estimate drift: Under using" + << " mean:" << delta_mean_ + << " sigma:" << sigma_delta + << " offset:" << send_receive_offset_.ToMicroseconds() + << " delta:" << estimated_congestion_delay_.ToMicroseconds() + << " drift:" << accumulated_deltas_.ToMicroseconds(); + delta_estimate_ = kBandwidthUnderUsing; + } + // Adding decay of negative accumulated_deltas_ since it could be caused by + // a starting with full buffers. This way we will always converge to 0. + accumulated_deltas_ = accumulated_deltas_.Add( + QuicTime::Delta::FromMicroseconds(sigma_delta >> 3)); + } +} + +void InterArrivalOveruseDetector::DetectSlope(int64 sigma_delta) { + // We use the mean change since it has a constant expected mean 0 + // regardless of number of packets and spread. It is also safe to use during + // packet loss, since a lost packet only results in a missed filter update + // not a drift. + if (num_of_deltas_ < kMinSamplesBeforeDetect) { + return; + } + if (slope_overuse_counter_ > 0 && delta_mean_ > 0) { + if (slope_estimate_ != kBandwidthDraining) { + DLOG(INFO) << "Bandwidth estimate slope: Draining buffer(s)"; + } + slope_estimate_ = kBandwidthDraining; + return; + } + if (sigma_delta > abs(delta_mean_) * kDetectSlopeFactor) { + if (slope_estimate_ != kBandwidthSteady) { + DLOG(INFO) << "Bandwidth estimate slope: Steady" + << " mean:" << delta_mean_ + << " sigma:" << sigma_delta; + slope_overuse_counter_ = 0; + slope_estimate_ = kBandwidthSteady; + } + return; + } + if (delta_mean_ > 0) { + if (slope_estimate_ != kBandwidthOverUsing) { + ++slope_overuse_counter_; + DLOG(INFO) << "Bandwidth estimate slope: Over using" + << " mean:" << delta_mean_ + << " sigma:" << sigma_delta; + slope_estimate_ = kBandwidthOverUsing; + } + } else { + if (slope_estimate_ != kBandwidthUnderUsing) { + --slope_overuse_counter_; + DLOG(INFO) << "Bandwidth estimate slope: Under using" + << " mean:" << delta_mean_ + << " sigma:" << sigma_delta; + slope_estimate_ = kBandwidthUnderUsing; + } + } +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h new file mode 100644 index 00000000000..185236279bb --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h @@ -0,0 +1,173 @@ +// 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 class is a helper class to the inter arrival congestion control. It +// provide a signal to the inter arrival congestion control of the estimated +// state of our transport channel. The estimate is based on the inter arrival +// time of the received packets relative to the time those packets were sent; +// we can estimate the build up of buffers on the network before packets are +// lost. +// +// Note: this class is not thread-safe. + +#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_ +#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_ + +#include "base/basictypes.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +enum NET_EXPORT_PRIVATE RateControlRegion { + kRateControlRegionUnknown = 0, + kRateControlRegionUnderMax = 1, + kRateControlRegionNearMax = 2 +}; + +// Note: Order is important. +enum NET_EXPORT_PRIVATE BandwidthUsage { + kBandwidthSteady = 0, + kBandwidthUnderUsing = 1, + kBandwidthDraining = 2, + kBandwidthOverUsing = 3, +}; + +// Normal state transition diagram +// +// kBandwidthUnderUsing +// | +// | +// kBandwidthSteady +// | ^ +// | | +// kBandwidthOverUsing | +// | | +// | | +// kBandwidthDraining +// +// The above transitions is in normal operation, with extreme values we don't +// enforce the state transitions, hence you could in extreme scenarios go +// between any states. +// +// kBandwidthSteady When the packets arrive in the same pace as we sent +// them. In this state we can increase our send pace. +// +// kBandwidthOverUsing When the packets arrive slower than the pace we sent +// them. In this state we should decrease our send pace. +// When we enter into this state we will also get an +// estimate on how much delay we have built up. The +// reduction in send pace should be chosen to drain the +// built up delay within reasonable time. +// +// kBandwidthUnderUsing When the packets arrive faster than the pace we sent +// them. In this state another stream disappeared from +// a shared link leaving us more available bandwidth. +// In this state we should hold our pace to make sure we +// fully drain the buffers before we start increasing +// our send rate. We do this to avoid operating with +// semi-full buffers. +// +// kBandwidthDraining We can only be in this state after we have been in a +// overuse state. In this state we should hold our pace +// to make sure we fully drain the buffers before we +// start increasing our send rate. We do this to avoid +// operating with semi-full buffers. + +class NET_EXPORT_PRIVATE InterArrivalOveruseDetector { + public: + InterArrivalOveruseDetector(); + + // Update the statistics with the received delta times, call for every + // received delta time. This function assumes that there is no re-orderings. + // If multiple packets are sent at the same time (identical send_time) + // last_of_send_time should be set to false for all but the last calls to + // this function. If there is only one packet sent at a given time + // last_of_send_time must be true. + // received_delta is the time difference between receiving this packet and the + // previously received packet. + void OnAcknowledgedPacket(QuicPacketSequenceNumber sequence_number, + QuicTime send_time, + bool last_of_send_time, + QuicTime receive_time); + + // Get the current estimated state and update the estimated congestion delay. + // |estimated_congestion_delay| will be updated with the estimated built up + // buffer delay; it must not be NULL as it will be updated with the estimate. + // Note 1: estimated_buffer_delay will only be valid when kBandwidthOverUsing + // is returned. + // Note 2: it's assumed that the pacer lower its send pace to drain the + // built up buffer within reasonable time. The pacer should use the + // estimated_buffer_delay as a guidance on how much to back off. + // Note 3: The absolute value of estimated_congestion_delay is less reliable + // than the state itself. It is also biased to low since we can't know + // how full the buffers are when the flow starts. + BandwidthUsage GetState(QuicTime::Delta* estimated_congestion_delay); + + private: + struct PacketGroup { + PacketGroup() + : send_time(QuicTime::Zero()), + last_receive_time(QuicTime::Zero()) { + } + QuicTime send_time; + QuicTime last_receive_time; + }; + + // Update the statistics with the absolute receive time relative to the + // absolute send time. + void UpdateSendReceiveTimeOffset(QuicTime::Delta offset); + + // Update the filter with this new data point. + void UpdateFilter(QuicTime::Delta received_delta, + QuicTime::Delta sent_delta); + + // Update the estimate with this residual. + void UpdateDeltaEstimate(QuicTime::Delta residual); + + // Estimate the state based on the slope of the changes. + void DetectSlope(int64 sigma_delta); + + // Estimate the state based on the accumulated drift of the changes. + void DetectDrift(int64 sigma_delta); + + // Current grouping of packets that were sent at the same time. + PacketGroup current_packet_group_; + // Grouping of packets that were sent at the same time, just before the + // current_packet_group_ above. + PacketGroup previous_packet_group_; + // Sequence number of the last acknowledged packet. + QuicPacketSequenceNumber last_sequence_number_; + // Number of received delta times with unique send time. + int num_of_deltas_; + // Estimated accumulation of received delta times. + // Note: Can be negative and can drift over time which is why we bias it + // towards 0 and reset it given some triggers. + QuicTime::Delta accumulated_deltas_; + // Current running mean of our received delta times. + int delta_mean_; + // Current running variance of our received delta times. + int64 delta_variance_; + // Number of overuse signals currently triggered in this state. + // Note: negative represent underuse. + int delta_overuse_counter_; + // State estimated by the delta times. + BandwidthUsage delta_estimate_; + // Number of overuse signals currently triggered in this state. + // Note: negative represent underuse. + int slope_overuse_counter_; + // State estimated by the slope of the delta times. + BandwidthUsage slope_estimate_; + // Lowest offset between send and receive time ever received in this session. + QuicTime::Delta send_receive_offset_; + // Last received time difference between our normalized send and receive time. + QuicTime::Delta estimated_congestion_delay_; + + DISALLOW_COPY_AND_ASSIGN(InterArrivalOveruseDetector); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_OVERUSE_DETECTOR_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc new file mode 100644 index 00000000000..8d37749a577 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc @@ -0,0 +1,1114 @@ +// 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 <stdlib.h> + +#include <cmath> + +#include "base/logging.h" +#include "base/rand_util.h" +#include "net/quic/congestion_control/inter_arrival_overuse_detector.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const double kPi = 3.14159265; + +namespace net { +namespace test { + +class InterArrivalOveruseDetectorTest : public ::testing::Test { + protected: + InterArrivalOveruseDetectorTest(); + + QuicTime::Delta GaussianRandom(QuicTime::Delta mean, + QuicTime::Delta standard_deviation); + + int Run100000Samples(int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation); + + int RunUntilOveruse(int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst, + QuicTime::Delta *estimated_buffer_delay); + + int RunUntilSteady(int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst); + + int RunUntilNotDraining(int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst); + + int RunUntilUnderusing(int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst); + + void RunXBursts(int bursts, + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst); + + QuicPacketSequenceNumber sequence_number_; + MockClock send_clock_; + MockClock receive_clock_; + QuicTime::Delta drift_from_mean_; + InterArrivalOveruseDetector overuse_detector_; + unsigned int seed_; +}; + +InterArrivalOveruseDetectorTest::InterArrivalOveruseDetectorTest() + : sequence_number_(1), + drift_from_mean_(QuicTime::Delta::Zero()), + seed_(1234) { +} + +QuicTime::Delta InterArrivalOveruseDetectorTest::GaussianRandom( + QuicTime::Delta mean, + QuicTime::Delta standard_deviation) { + // Creating a Normal distribution variable from two independent uniform + // variables based on the Box-Muller transform. + double uniform1 = base::RandDouble(); + double uniform2 = base::RandDouble(); + + QuicTime::Delta random = QuicTime::Delta::FromMicroseconds( + static_cast<int>(standard_deviation.ToMicroseconds() * + sqrt(-2 * log(uniform1)) * cos(2 * kPi * uniform2))). + Add(mean).Subtract(drift_from_mean_); + if (random < QuicTime::Delta::Zero()) { + // Don't do negative deltas. + drift_from_mean_ = drift_from_mean_.Subtract(mean); + return QuicTime::Delta::Zero(); + } + drift_from_mean_ = drift_from_mean_.Add(random).Subtract(mean); + return random; +} + +int InterArrivalOveruseDetectorTest::Run100000Samples( + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation) { + int unique_overuse = 0; + int last_overuse = -1; + for (int i = 0; i < 100000; ++i) { + // Assume that we send out the packets with perfect pacing. + send_clock_.AdvanceTime(mean); + QuicTime send_time = send_clock_.ApproximateNow(); + // Do only one random delta for all packets in a burst. + receive_clock_.AdvanceTime(GaussianRandom(mean, standard_deviation)); + QuicTime receive_time = receive_clock_.ApproximateNow(); + for (int j = 0; j <= packets_per_burst; ++j) { + overuse_detector_.OnAcknowledgedPacket(sequence_number_++, + send_time, + (j == packets_per_burst), + receive_time); + } + // We expect to randomly hit a few false detects, count the unique + // overuse events, hence not multiple signals in a row. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + if (kBandwidthOverUsing == overuse_detector_.GetState( + &estimated_buffer_delay)) { + if (last_overuse + 1 != i) { + unique_overuse++; + } + last_overuse = i; + } + } + return unique_overuse; +} + +int InterArrivalOveruseDetectorTest::RunUntilOveruse( + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst, + QuicTime::Delta *estimated_buffer_delay) { + // Simulate a higher send pace, that is too high. + for (int i = 0; i < 1000; ++i) { + send_clock_.AdvanceTime(mean); + QuicTime send_time = send_clock_.ApproximateNow(); + // Do only one random delta for all packets in a burst. + receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst), + standard_deviation)); + QuicTime receive_time = receive_clock_.ApproximateNow(); + for (int j = 0; j <= packets_per_burst; ++j) { + overuse_detector_.OnAcknowledgedPacket(sequence_number_++, + send_time, + (j == packets_per_burst), + receive_time); + } + if (kBandwidthOverUsing == overuse_detector_.GetState( + estimated_buffer_delay)) { + return i + 1; + } + } + return -1; +} + +int InterArrivalOveruseDetectorTest::RunUntilSteady( + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst) { + // Simulate a lower send pace, that is lower than the capacity. + for (int i = 0; i < 1000; ++i) { + send_clock_.AdvanceTime(mean); + QuicTime send_time = send_clock_.ApproximateNow(); + // Do only one random delta for all packets in a burst. + receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst), + standard_deviation)); + QuicTime receive_time = receive_clock_.ApproximateNow(); + for (int j = 0; j <= packets_per_burst; ++j) { + overuse_detector_.OnAcknowledgedPacket(sequence_number_++, + send_time, + (j == packets_per_burst), + receive_time); + } + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + if (kBandwidthSteady == + overuse_detector_.GetState(&estimated_buffer_delay)) { + return i + 1; + } + } + return -1; +} + +int InterArrivalOveruseDetectorTest::RunUntilNotDraining( + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst) { + // Simulate a lower send pace, that is lower than the capacity. + for (int i = 0; i < 1000; ++i) { + send_clock_.AdvanceTime(mean); + QuicTime send_time = send_clock_.ApproximateNow(); + // Do only one random delta for all packets in a burst. + receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst), + standard_deviation)); + QuicTime receive_time = receive_clock_.ApproximateNow(); + for (int j = 0; j <= packets_per_burst; ++j) { + overuse_detector_.OnAcknowledgedPacket(sequence_number_++, + send_time, + (j == packets_per_burst), + receive_time); + } + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + if (kBandwidthDraining > + overuse_detector_.GetState(&estimated_buffer_delay)) { + return i + 1; + } + } + return -1; +} + +int InterArrivalOveruseDetectorTest::RunUntilUnderusing( + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst) { + // Simulate a lower send pace, that is lower than the capacity. + for (int i = 0; i < 1000; ++i) { + send_clock_.AdvanceTime(mean); + QuicTime send_time = send_clock_.ApproximateNow(); + // Do only one random delta for all packets in a burst. + receive_clock_.AdvanceTime(GaussianRandom(mean.Subtract(drift_per_burst), + standard_deviation)); + QuicTime receive_time = receive_clock_.ApproximateNow(); + for (int j = 0; j <= packets_per_burst; ++j) { + overuse_detector_.OnAcknowledgedPacket(sequence_number_++, + send_time, + (j == packets_per_burst), + receive_time); + } + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + if (kBandwidthUnderUsing == overuse_detector_.GetState( + &estimated_buffer_delay)) { + return i + 1; + } + } + return -1; +} + +void InterArrivalOveruseDetectorTest::RunXBursts( + int bursts, + int packets_per_burst, + QuicTime::Delta mean, + QuicTime::Delta standard_deviation, + QuicTime::Delta drift_per_burst) { + for (int i = 0; i < bursts; ++i) { + send_clock_.AdvanceTime(mean); + QuicTime send_time = send_clock_.ApproximateNow(); + // Do only one random delta for all packets in a burst. + receive_clock_.AdvanceTime(GaussianRandom(mean.Add(drift_per_burst), + standard_deviation)); + QuicTime receive_time = receive_clock_.ApproximateNow(); + for (int j = 0; j <= packets_per_burst; ++j) { + overuse_detector_.OnAcknowledgedPacket(sequence_number_++, + send_time, + (j == packets_per_burst), + receive_time); + } + } +} + +// TODO(pwestin): test packet loss impact on accuracy. +// TODO(pwestin): test colored noise by dropping late frames. + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_TestNoise) { + int count[100]; + memset(count, 0, sizeof(count)); + for (int i = 0; i < 10000; ++i) { + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(30); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(10); + count[GaussianRandom(mean, standard_deviation).ToMilliseconds()]++; + } + for (int j = 0; j < 100; ++j) { + DLOG(INFO) << j << ":" << count[j]; + } +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruse) { + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); + + for (int i = 0; i < 1000; ++i) { + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + true, + receive_time); + send_clock_.AdvanceTime(delta); + receive_clock_.AdvanceTime(delta); + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + EXPECT_EQ(kBandwidthSteady, + overuse_detector_.GetState(&estimated_buffer_delay)); + } +} + +TEST_F(InterArrivalOveruseDetectorTest, + DISABLED_SimpleNonOveruseSendClockAhead) { + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); + send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234)); + + for (int i = 0; i < 1000; ++i) { + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + true, + receive_time); + send_clock_.AdvanceTime(delta); + receive_clock_.AdvanceTime(delta); + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + EXPECT_EQ(kBandwidthSteady, + overuse_detector_.GetState(&estimated_buffer_delay)); + } +} + +TEST_F(InterArrivalOveruseDetectorTest, + DISABLED_SimpleNonOveruseSendClockBehind) { + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta delta = QuicTime::Delta::FromMilliseconds(10); + receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1234)); + + for (int i = 0; i < 1000; ++i) { + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + true, + receive_time); + send_clock_.AdvanceTime(delta); + receive_clock_.AdvanceTime(delta); + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + EXPECT_EQ(kBandwidthSteady, + overuse_detector_.GetState(&estimated_buffer_delay)); + } +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleNonOveruseWithVariance) { + QuicPacketSequenceNumber sequence_number = 1; + for (int i = 0; i < 1000; ++i) { + if (i % 2) { + receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + } else { + receive_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(15)); + } + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + true, + receive_time); + send_clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + EXPECT_EQ(kBandwidthSteady, + overuse_detector_.GetState(&estimated_buffer_delay)); + } +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_SimpleOveruse) { + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta send_delta = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + + for (int i = 0; i < 100000; ++i) { + send_clock_.AdvanceTime(send_delta); + receive_clock_.AdvanceTime(received_delta); + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + // Sending 2 packets the same time as that is what we expect to do. + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + false, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + true, + receive_time); + + EXPECT_EQ(kBandwidthSteady, + overuse_detector_.GetState(&estimated_buffer_delay)); + } + // Simulate a higher send pace, that is too high by receiving 1 millisecond + // late per packet. + received_delta = QuicTime::Delta::FromMilliseconds(6); + send_clock_.AdvanceTime(send_delta); + receive_clock_.AdvanceTime(received_delta); + QuicTime send_time = send_clock_.ApproximateNow(); + QuicTime receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + false, + receive_time); + receive_clock_.AdvanceTime(received_delta); + receive_time = receive_clock_.ApproximateNow(); + overuse_detector_.OnAcknowledgedPacket(sequence_number++, + send_time, + true, + receive_time); + EXPECT_EQ(kBandwidthOverUsing, + overuse_detector_.GetState(&estimated_buffer_delay)); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance10Kbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 6 updates with 5 milliseconds before + // detection. + // Resulting in a minimal buffer build up of 30 milliseconds. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(6, bursts_until_overuse); + EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(6, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(7, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance10Kbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1000); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 6 updates with 50 milliseconds before + // detection. + // Resulting in a minimal buffer build up of 300 milliseconds. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(6, bursts_until_overuse); + EXPECT_NEAR(400, estimated_buffer_delay.ToMilliseconds(), 150); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(6, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(7, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance100Kbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 6 updates with 5 milliseconds + // before detection. + // Resulting in a minimal buffer build up of 30 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(6, bursts_until_overuse); + EXPECT_NEAR(40, estimated_buffer_delay.ToMilliseconds(), 15); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(1, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(7, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(5, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Kbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(100); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(50); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(50); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 4 updates with 50 milliseconds + // before detection. + // Resulting in a minimal buffer build up of 200 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(4, bursts_until_overuse); + EXPECT_NEAR(300, estimated_buffer_delay.ToMilliseconds(), 150); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(1, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(5, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(5, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance1Mbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 4 updates with 5 millisecond + // before detection. + // Resulting in a minimal buffer build up of 20 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(4, bursts_until_overuse); + EXPECT_NEAR(30, estimated_buffer_delay.ToMilliseconds(), 15); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(10, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(14, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(4, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1Mbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 6 updates with 1 millisecond + // before detection. + // Resulting in a minimal buffer build up of 6 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(6, bursts_until_overuse); + EXPECT_NEAR(8, estimated_buffer_delay.ToMilliseconds(), 3); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(10, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(16, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(4, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20Mbit) { + int packets_per_burst = 2; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 4 updates with 500 microsecond + // before detection. + // Resulting in a minimal buffer build up of 2 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(4, bursts_until_overuse); + EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(100, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_NEAR(100, bursts_until_not_draining, 10); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(1, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance100Mbit) { + int packets_per_burst = 10; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 4 updates with 500 microsecond + // before detection. + // Resulting in a minimal buffer build up of 2 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(4, bursts_until_overuse); + EXPECT_NEAR(3, estimated_buffer_delay.ToMilliseconds(), 2); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(100, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_NEAR(100, bursts_until_not_draining, 10); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(1, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_VeryHighVariance100Mbit) { + int packets_per_burst = 10; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(500); + + // We get false overuse in this scenario due to that the standard deviation is + // higher than our mean and the fact that a delta time can't be negative. This + // results in an under estimated standard deviation in the estimator causing + // false detects. + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(2000, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 25 updates with 500 microsecond + // before detection. + // Resulting in a minimal buffer build up of 12.5 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(17, bursts_until_overuse); + EXPECT_NEAR(22, estimated_buffer_delay.ToMilliseconds(), 15); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(100, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(117, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(1, bursts_until_underusing); +} + +// +// Tests simulating big drop in bitrate. +// + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTo100Kbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(100); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 1 update with 100 millisecond + // before detection. + // Resulting in a minimal buffer build up of 100 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(1, bursts_until_overuse); + EXPECT_NEAR(104, estimated_buffer_delay.ToMilliseconds(), 3); + + // Back off 20% lower than estimate to drain. + mean = QuicTime::Delta::FromMilliseconds(100); + drift_per_burst = QuicTime::Delta::FromMilliseconds(20); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(5, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(3, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_HighVariance20MbitTo1Mbit) { + int packets_per_burst = 2; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMicroseconds(500); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(10); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 1 update with 10 milliseconds + // before detection. + // Resulting in a minimal buffer build up of 10 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(1, bursts_until_overuse); + EXPECT_NEAR(12, estimated_buffer_delay.ToMilliseconds(), 2); + + // Back off 20% lower than estimate to drain. + mean = QuicTime::Delta::FromMilliseconds(10); + drift_per_burst = QuicTime::Delta::FromMilliseconds(2); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(5, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(3, bursts_until_underusing); +} + +// +// Tests that we can detect slow drifts. +// + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitSmallSteps) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(100); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 41 updates with 100 microseconds before + // detection. + // Resulting in a minimal buffer build up of 4.1 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(41, bursts_until_overuse); + EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(10, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(29, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(71, bursts_until_underusing); +} + +TEST_F(InterArrivalOveruseDetectorTest, DISABLED_LowVariance1MbitTinySteps) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMicroseconds(10); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a higher send pace, that is too high. + // With current tuning we require 345 updates with 10 microseconds before + // detection. + // Resulting in a minimal buffer build up of 3.45 ms. + QuicTime::Delta estimated_buffer_delay(QuicTime::Delta::Zero()); + int bursts_until_overuse = RunUntilOveruse(packets_per_burst, + mean, + standard_deviation, + drift_per_burst, + &estimated_buffer_delay); + EXPECT_GE(345, bursts_until_overuse); + EXPECT_NEAR(7, estimated_buffer_delay.ToMilliseconds(), 3); + + // Simulate an RTT of 100 ms. Hence overusing for additional 100 ms before + // detection. + RunXBursts(10, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + int bursts_until_not_draining = RunUntilNotDraining(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(18, bursts_until_not_draining); + + // After draining the buffer additionally we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(683, bursts_until_underusing); +} + +// +// Tests simulating starting with full buffers. +// + +TEST_F(InterArrivalOveruseDetectorTest, + DISABLED_StartedWithFullBuffersHighVariance1Mbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(5); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a lower send pace. + // Draining the buffer until we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(6, bursts_until_underusing); + + // After draining the buffers we are back in a normal state. + drift_per_burst = QuicTime::Delta::FromMilliseconds(0); + int bursts_until_steady = RunUntilSteady(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(6, bursts_until_steady); +} + +TEST_F(InterArrivalOveruseDetectorTest, + DISABLED_StartedWithFullBuffersHighVariance1MbitSlowDrift) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(5); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a faster receive pace. + // Draining the buffer until we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(21, bursts_until_underusing); + + // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before + // detection. + drift_per_burst = QuicTime::Delta::FromMilliseconds(-1); + RunXBursts(10, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + drift_per_burst = QuicTime::Delta::FromMilliseconds(0); + int bursts_until_steady = RunUntilSteady(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(4, bursts_until_steady); +} + +TEST_F(InterArrivalOveruseDetectorTest, + DISABLED_StartedWithFullBuffersLowVariance1Mbit) { + int packets_per_burst = 1; + QuicTime::Delta mean = QuicTime::Delta::FromMilliseconds(10); + QuicTime::Delta standard_deviation = QuicTime::Delta::FromMilliseconds(1); + QuicTime::Delta drift_per_burst = QuicTime::Delta::FromMilliseconds(1); + + int overuse_signals = Run100000Samples(packets_per_burst, + mean, + standard_deviation); + EXPECT_GE(1, overuse_signals); + + // Simulate a lower send pace. + // Draining the buffer until we detect an underuse. + int bursts_until_underusing = RunUntilUnderusing(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(5, bursts_until_underusing); + + // Simulate an RTT of 100 ms. Hence underusing for additional 100 ms before + // detection. + drift_per_burst = QuicTime::Delta::FromMilliseconds(-1); + RunXBursts(10, + packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + + // After draining the buffers we are back in a normal state. + drift_per_burst = QuicTime::Delta::FromMilliseconds(0); + int bursts_until_steady = RunUntilSteady(packets_per_burst, + mean, + standard_deviation, + drift_per_burst); + EXPECT_GE(41, bursts_until_steady); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.cc b/chromium/net/quic/congestion_control/inter_arrival_probe.cc new file mode 100644 index 00000000000..6d4c07315d0 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_probe.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/congestion_control/inter_arrival_probe.h" + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace { +const int kProbeSizePackets = 10; +const net::QuicByteCount kMinPacketSize = 500; +const int64 kDefaultBytesPerSecond = 40000; +const float kUncertainScaleFactor = 0.5; // TODO(pwestin): revisit this factor. +} + +namespace net { + +InterArrivalProbe::InterArrivalProbe() + : estimate_available_(false), + available_channel_estimate_(QuicBandwidth::Zero()), + unacked_data_(0) { +} + +InterArrivalProbe::~InterArrivalProbe() { +} + +bool InterArrivalProbe::GetEstimate(QuicBandwidth* available_channel_estimate) { + if (!estimate_available_) { + return false; + } + *available_channel_estimate = available_channel_estimate_; + return true; +} + +void InterArrivalProbe::OnSentPacket(QuicByteCount bytes) { + if (!estimate_available_) { + unacked_data_ += bytes; + } +} + +void InterArrivalProbe::OnAcknowledgedPacket(QuicByteCount bytes) { + if (!estimate_available_) { + DCHECK_LE(bytes, unacked_data_); + unacked_data_ -= bytes; + } +} + +QuicByteCount InterArrivalProbe::GetAvailableCongestionWindow() { + if (estimate_available_) { + return 0; + } + return (kProbeSizePackets * kMaxPacketSize) - unacked_data_; +} + +void InterArrivalProbe::OnIncomingFeedback( + QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes_sent, + QuicTime time_sent, + QuicTime time_received) { + if (estimate_available_) { + return; + } + + if (available_channel_estimator_.get() == NULL) { + if (bytes_sent < kMinPacketSize) { + // Packet too small to start the probe phase. + return; + } + first_sequence_number_ = sequence_number; + available_channel_estimator_.reset(new AvailableChannelEstimator( + sequence_number, time_sent, time_received)); + return; + } + + available_channel_estimator_->OnIncomingFeedback(sequence_number, + bytes_sent, + time_sent, + time_received); + if (sequence_number < kProbeSizePackets - 1 + first_sequence_number_) { + // We need more feedback before we have a probe estimate. + return; + } + // Get the current estimated available channel capacity. + // available_channel_estimate is invalid if kAvailableChannelEstimateUnknown + // is returned. + QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); + AvailableChannelEstimateState available_channel_estimate_state = + available_channel_estimator_->GetAvailableChannelEstimate( + &available_channel_estimate); + switch (available_channel_estimate_state) { + case kAvailableChannelEstimateUnknown: + // Backup when we miss our probe. + available_channel_estimate_ = + QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond); + break; + case kAvailableChannelEstimateUncertain: + available_channel_estimate_ = + available_channel_estimate.Scale(kUncertainScaleFactor); + break; + case kAvailableChannelEstimateGood: + available_channel_estimate_ = available_channel_estimate; + break; + case kAvailableChannelEstimateSenderLimited: + available_channel_estimate_ = + std::max(available_channel_estimate, + QuicBandwidth::FromBytesPerSecond(kDefaultBytesPerSecond)); + break; + } + estimate_available_ = true; + available_channel_estimator_.reset(NULL); + DLOG(INFO) << "Probe estimate:" + << available_channel_estimate_.ToKBitsPerSecond() + << " Kbits/s"; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe.h b/chromium/net/quic/congestion_control/inter_arrival_probe.h new file mode 100644 index 00000000000..5788a68a926 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_probe.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. + +// Class that handle the initial probing phase of inter arrival congestion +// control. +#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_ +#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/available_channel_estimator.h" +#include "net/quic/quic_bandwidth.h" + +namespace net { + +class NET_EXPORT_PRIVATE InterArrivalProbe { + public: + InterArrivalProbe(); + ~InterArrivalProbe(); + + // Call every time a packet is sent to the network. + void OnSentPacket(QuicByteCount bytes); + + // Call once for each sent packet that we receive an acknowledgement from + // the peer for. + void OnAcknowledgedPacket(QuicByteCount bytes); + + // Call to get the number of bytes that can be sent as part of this probe. + QuicByteCount GetAvailableCongestionWindow(); + + // Call once for each sent packet we receive a congestion feedback from the + // peer for. + // If a peer sends both and ack and feedback for a sent packet, both + // OnAcknowledgedPacket and OnIncomingFeedback should be called. + void OnIncomingFeedback(QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes_sent, + QuicTime time_sent, + QuicTime time_received); + + // Returns false as long as we are probing, available_channel_estimate is + // invalid during that time. When the probe is completed this function return + // true and available_channel_estimate contains the estimate. + bool GetEstimate(QuicBandwidth* available_channel_estimate); + + private: + scoped_ptr<AvailableChannelEstimator> available_channel_estimator_; + QuicPacketSequenceNumber first_sequence_number_; + bool estimate_available_; + QuicBandwidth available_channel_estimate_; + QuicByteCount unacked_data_; + + DISALLOW_COPY_AND_ASSIGN(InterArrivalProbe); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_PROBE_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc b/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc new file mode 100644 index 00000000000..18b97d33090 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_probe_test.cc @@ -0,0 +1,81 @@ +// 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/inter_arrival_probe.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class InterArrivalProbeTest : public ::testing::Test { + protected: + InterArrivalProbeTest() : start_(QuicTime::Zero()) { + } + + InterArrivalProbe probe_; + QuicTime start_; +}; + +TEST_F(InterArrivalProbeTest, CongestionWindow) { + for (size_t i = 0; i < 10; i++) { + probe_.OnSentPacket(kMaxPacketSize); + EXPECT_EQ((9 - i) * kMaxPacketSize, probe_.GetAvailableCongestionWindow()); + } + probe_.OnAcknowledgedPacket(kMaxPacketSize); + EXPECT_EQ(kMaxPacketSize, probe_.GetAvailableCongestionWindow()); + + probe_.OnSentPacket(kMaxPacketSize); + EXPECT_EQ(0u, probe_.GetAvailableCongestionWindow()); +} + +TEST_F(InterArrivalProbeTest, Estimate) { + QuicPacketSequenceNumber sequence_number = 1; + QuicByteCount bytes_sent = kMaxPacketSize; + QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); + QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); + QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); + + for (size_t i = 0; i < 10; ++i) { + EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate)); + + probe_.OnIncomingFeedback(sequence_number++, + bytes_sent, + time_sent, + time_received); + time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1)); + time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); + } + EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); + EXPECT_EQ(kMaxPacketSize * 100, + static_cast<uint64>(available_channel_estimate.ToBytesPerSecond())); +} + +TEST_F(InterArrivalProbeTest, EstimateWithLoss) { + QuicPacketSequenceNumber sequence_number = 1; + QuicByteCount bytes_sent = kMaxPacketSize; + QuicTime time_received = start_.Add(QuicTime::Delta::FromMilliseconds(10)); + QuicTime time_sent = start_.Add(QuicTime::Delta::FromMilliseconds(1)); + QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); + + for (size_t i = 0; i < 6; ++i) { + EXPECT_FALSE(probe_.GetEstimate(&available_channel_estimate)); + + probe_.OnIncomingFeedback(sequence_number, + bytes_sent, + time_sent, + time_received); + sequence_number += 2; + time_sent = time_sent.Add(QuicTime::Delta::FromMilliseconds(1)); + time_received = time_received.Add(QuicTime::Delta::FromMilliseconds(10)); + } + EXPECT_TRUE(probe_.GetEstimate(&available_channel_estimate)); + EXPECT_EQ(kMaxPacketSize * 50, + static_cast<uint64>(available_channel_estimate.ToBytesPerSecond())); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver.cc new file mode 100644 index 00000000000..770b287db0b --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_receiver.cc @@ -0,0 +1,48 @@ +// 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/inter_arrival_receiver.h" + +#include "base/basictypes.h" + +namespace net { + +InterArrivalReceiver::InterArrivalReceiver() + : accumulated_number_of_recoverd_lost_packets_(0) { +} + +InterArrivalReceiver::~InterArrivalReceiver() { +} + +bool InterArrivalReceiver::GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) { + if (received_packet_times_.size() <= 1) { + // Don't waste resources by sending a feedback frame for only one packet. + return false; + } + feedback->type = kInterArrival; + feedback->inter_arrival.accumulated_number_of_lost_packets = + accumulated_number_of_recoverd_lost_packets_; + + // Copy our current receive set to our feedback message, we will not resend + // this data if it is lost. + feedback->inter_arrival.received_packet_times = received_packet_times_; + + // Prepare for the next set of arriving packets by clearing our current set. + received_packet_times_.clear(); + return true; +} + +void InterArrivalReceiver::RecordIncomingPacket( + QuicByteCount /*bytes*/, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived) { + if (revived) { + ++accumulated_number_of_recoverd_lost_packets_; + } + received_packet_times_.insert(std::make_pair(sequence_number, timestamp)); +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver.h b/chromium/net/quic/congestion_control/inter_arrival_receiver.h new file mode 100644 index 00000000000..a9de62cb1ea --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_receiver.h @@ -0,0 +1,46 @@ +// 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_INTER_ARRIVAL_RECEIVER_H_ +#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_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 InterArrivalReceiver + : public ReceiveAlgorithmInterface { + public: + InterArrivalReceiver(); + virtual ~InterArrivalReceiver(); + + // Start implementation of ReceiveAlgorithmInterface. + virtual bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) OVERRIDE; + + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived) OVERRIDE; + // End implementation of ReceiveAlgorithmInterface. + + private: + // We need to keep track of FEC recovered packets. + int accumulated_number_of_recoverd_lost_packets_; + + // The set of received packets since the last feedback was sent, along with + // their arrival times. + TimeMap received_packet_times_; + + DISALLOW_COPY_AND_ASSIGN(InterArrivalReceiver); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_RECEIVER_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc b/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc new file mode 100644 index 00000000000..927fb6d92f7 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc @@ -0,0 +1,55 @@ +// 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/logging.h" +#include "net/quic/congestion_control/inter_arrival_receiver.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class InterArrivalReceiverTest : public ::testing::Test { + protected: + InterArrivalReceiver receiver_; + MockClock clock_; +}; + +TEST_F(InterArrivalReceiverTest, SimpleReceiver) { + QuicTime start = clock_.ApproximateNow(); + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); + clock_.AdvanceTime(received_delta); + QuicTime receive_timestamp = clock_.ApproximateNow(); + receiver_.RecordIncomingPacket(1, 1, receive_timestamp, false); + + QuicCongestionFeedbackFrame feedback; + ASSERT_FALSE(receiver_.GenerateCongestionFeedback(&feedback)); + + clock_.AdvanceTime(received_delta); + receive_timestamp = clock_.ApproximateNow(); + // Packet not received; but rather revived by FEC. + receiver_.RecordIncomingPacket(1, 2, receive_timestamp, true); + clock_.AdvanceTime(received_delta); + receive_timestamp = clock_.ApproximateNow(); + receiver_.RecordIncomingPacket(1, 3, receive_timestamp, false); + + ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback)); + + EXPECT_EQ(kInterArrival, feedback.type); + EXPECT_EQ(1, feedback.inter_arrival.accumulated_number_of_lost_packets); + EXPECT_EQ(3u, feedback.inter_arrival.received_packet_times.size()); + TimeMap::iterator it = feedback.inter_arrival.received_packet_times.begin(); + EXPECT_EQ(1u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start)); + it = feedback.inter_arrival.received_packet_times.begin(); + it++; + EXPECT_EQ(2u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), it->second.Subtract(start)); + it++; + EXPECT_EQ(3u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), it->second.Subtract(start)); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.cc b/chromium/net/quic/congestion_control/inter_arrival_sender.cc new file mode 100644 index 00000000000..1aa7ab90e9b --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_sender.cc @@ -0,0 +1,505 @@ +// 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/inter_arrival_sender.h" + +namespace net { + +namespace { +const int64 kProbeBitrateKBytesPerSecond = 1200; // 9.6 Mbit/s +const float kPacketLossBitrateReduction = 0.7f; +const float kUncertainSafetyMargin = 0.7f; +const float kMaxBitrateReduction = 0.9f; +const float kMinBitrateReduction = 0.05f; +const uint64 kMinBitrateKbit = 10; +const int kInitialRttMs = 60; // At a typical RTT 60 ms. +const float kAlpha = 0.125f; +const float kOneMinusAlpha = 1 - kAlpha; + +static const int kBitrateSmoothingPeriodMs = 1000; +static const int kMinBitrateSmoothingPeriodMs = 500; + +} // namespace + +InterArrivalSender::InterArrivalSender(const QuicClock* clock) + : probing_(true), + current_bandwidth_(QuicBandwidth::Zero()), + smoothed_rtt_(QuicTime::Delta::Zero()), + channel_estimator_(new ChannelEstimator()), + bitrate_ramp_up_(new InterArrivalBitrateRampUp(clock)), + overuse_detector_(new InterArrivalOveruseDetector()), + probe_(new InterArrivalProbe()), + state_machine_(new InterArrivalStateMachine(clock)), + paced_sender_(new PacedSender(QuicBandwidth::FromKBytesPerSecond( + kProbeBitrateKBytesPerSecond))), + accumulated_number_of_lost_packets_(0), + bandwidth_usage_state_(kBandwidthSteady), + back_down_time_(QuicTime::Zero()), + back_down_bandwidth_(QuicBandwidth::Zero()), + back_down_congestion_delay_(QuicTime::Delta::Zero()) { +} + +InterArrivalSender::~InterArrivalSender() { +} + +// TODO(pwestin): this is really inefficient (4% CPU on the GFE loadtest). +// static +QuicBandwidth InterArrivalSender::CalculateSentBandwidth( + const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, + QuicTime feedback_receive_time) { + const QuicTime::Delta kBitrateSmoothingPeriod = + QuicTime::Delta::FromMilliseconds(kBitrateSmoothingPeriodMs); + const QuicTime::Delta kMinBitrateSmoothingPeriod = + QuicTime::Delta::FromMilliseconds(kMinBitrateSmoothingPeriodMs); + + QuicByteCount sum_bytes_sent = 0; + + // Sum packet from new until they are kBitrateSmoothingPeriod old. + SendAlgorithmInterface::SentPacketsMap::const_reverse_iterator history_rit = + sent_packets_map.rbegin(); + + QuicTime::Delta max_diff = QuicTime::Delta::Zero(); + for (; history_rit != sent_packets_map.rend(); ++history_rit) { + QuicTime::Delta diff = + feedback_receive_time.Subtract(history_rit->second->SendTimestamp()); + if (diff > kBitrateSmoothingPeriod) { + break; + } + sum_bytes_sent += history_rit->second->BytesSent(); + max_diff = diff; + } + if (max_diff < kMinBitrateSmoothingPeriod) { + // No estimate. + return QuicBandwidth::Zero(); + } + return QuicBandwidth::FromBytesAndTimeDelta(sum_bytes_sent, max_diff); +} + +void InterArrivalSender::OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& sent_packets) { + DCHECK(feedback.type == kInterArrival); + + if (feedback.type != kInterArrival) { + return; + } + + QuicBandwidth sent_bandwidth = CalculateSentBandwidth(sent_packets, + feedback_receive_time); + + TimeMap::const_iterator received_it; + for (received_it = feedback.inter_arrival.received_packet_times.begin(); + received_it != feedback.inter_arrival.received_packet_times.end(); + ++received_it) { + QuicPacketSequenceNumber sequence_number = received_it->first; + + SentPacketsMap::const_iterator sent_it = sent_packets.find(sequence_number); + if (sent_it == sent_packets.end()) { + // Too old data; ignore and move forward. + DLOG(INFO) << "Too old feedback move forward, sequence_number:" + << sequence_number; + continue; + } + QuicTime time_received = received_it->second; + QuicTime time_sent = sent_it->second->SendTimestamp(); + QuicByteCount bytes_sent = sent_it->second->BytesSent(); + + channel_estimator_->OnAcknowledgedPacket( + sequence_number, bytes_sent, time_sent, time_received); + if (probing_) { + probe_->OnIncomingFeedback( + sequence_number, bytes_sent, time_sent, time_received); + } else { + bool last_of_send_time = false; + SentPacketsMap::const_iterator next_sent_it = ++sent_it; + if (next_sent_it == sent_packets.end()) { + // No more sent packets; hence this must be the last. + last_of_send_time = true; + } else { + if (time_sent != next_sent_it->second->SendTimestamp()) { + // Next sent packet have a different send time. + last_of_send_time = true; + } + } + overuse_detector_->OnAcknowledgedPacket( + sequence_number, time_sent, last_of_send_time, time_received); + } + } + if (probing_) { + probing_ = ProbingPhase(feedback_receive_time); + return; + } + + bool packet_loss_event = false; + if (accumulated_number_of_lost_packets_ != + feedback.inter_arrival.accumulated_number_of_lost_packets) { + accumulated_number_of_lost_packets_ = + feedback.inter_arrival.accumulated_number_of_lost_packets; + packet_loss_event = true; + } + InterArrivalState state = state_machine_->GetInterArrivalState(); + + if (state == kInterArrivalStatePacketLoss || + state == kInterArrivalStateCompetingTcpFLow) { + if (packet_loss_event) { + if (!state_machine_->PacketLossEvent()) { + // Less than one RTT since last PacketLossEvent. + return; + } + EstimateBandwidthAfterLossEvent(feedback_receive_time); + } else { + EstimateNewBandwidth(feedback_receive_time, sent_bandwidth); + } + return; + } + EstimateDelayBandwidth(feedback_receive_time, sent_bandwidth); +} + +bool InterArrivalSender::ProbingPhase(QuicTime feedback_receive_time) { + QuicBandwidth available_channel_estimate = QuicBandwidth::Zero(); + if (!probe_->GetEstimate(&available_channel_estimate)) { + // Continue probing phase. + return true; + } + QuicBandwidth channel_estimate = QuicBandwidth::Zero(); + ChannelEstimateState channel_estimator_state = + channel_estimator_->GetChannelEstimate(&channel_estimate); + + QuicBandwidth new_rate = + available_channel_estimate.Scale(kUncertainSafetyMargin); + + switch (channel_estimator_state) { + case kChannelEstimateUnknown: + channel_estimate = available_channel_estimate; + break; + case kChannelEstimateUncertain: + channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin); + break; + case kChannelEstimateGood: + // Do nothing. + break; + } + new_rate = std::max(new_rate, + QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit)); + + bitrate_ramp_up_->Reset(new_rate, available_channel_estimate, + channel_estimate); + + current_bandwidth_ = new_rate; + paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_rate); + DLOG(INFO) << "Probe result; new rate:" + << new_rate.ToKBitsPerSecond() << " Kbits/s " + << " available estimate:" + << available_channel_estimate.ToKBitsPerSecond() << " Kbits/s " + << " channel estimate:" + << channel_estimate.ToKBitsPerSecond() << " Kbits/s "; + return false; +} + +void InterArrivalSender::OnIncomingAck( + QuicPacketSequenceNumber /*acked_sequence_number*/, + QuicByteCount acked_bytes, + QuicTime::Delta rtt) { + // RTT can't be negative. + DCHECK_LE(0, rtt.ToMicroseconds()); + + if (probing_) { + probe_->OnAcknowledgedPacket(acked_bytes); + } + + if (rtt.IsInfinite()) { + return; + } + + if (smoothed_rtt_.IsZero()) { + smoothed_rtt_ = rtt; + } else { + smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( + kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + + kAlpha * rtt.ToMicroseconds()); + } + state_machine_->set_rtt(smoothed_rtt_); +} + +void InterArrivalSender::OnIncomingLoss(QuicTime ack_receive_time) { + // Packet loss was reported. + if (!probing_) { + if (!state_machine_->PacketLossEvent()) { + // Less than one RTT since last PacketLossEvent. + return; + } + // Calculate new pace rate. + EstimateBandwidthAfterLossEvent(ack_receive_time); + } +} + +void InterArrivalSender::SentPacket(QuicTime sent_time, + QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes, + Retransmission /*retransmit*/) { + if (probing_) { + probe_->OnSentPacket(bytes); + } + paced_sender_->SentPacket(sent_time, bytes); +} + +void InterArrivalSender::AbandoningPacket( + QuicPacketSequenceNumber /*sequence_number*/, + QuicByteCount abandoned_bytes) { + // TODO(pwestin): use for out outer_congestion_window_ logic. + if (probing_) { + probe_->OnAcknowledgedPacket(abandoned_bytes); + } +} + +QuicTime::Delta InterArrivalSender::TimeUntilSend( + QuicTime now, + Retransmission /*retransmit*/, + HasRetransmittableData has_retransmittable_data, + IsHandshake /* handshake */) { + // TODO(pwestin): implement outer_congestion_window_ logic. + QuicTime::Delta outer_window = QuicTime::Delta::Zero(); + + if (probing_) { + if (has_retransmittable_data == HAS_RETRANSMITTABLE_DATA && + probe_->GetAvailableCongestionWindow() == 0) { + outer_window = QuicTime::Delta::Infinite(); + } + } + return paced_sender_->TimeUntilSend(now, outer_window); +} + +void InterArrivalSender::EstimateDelayBandwidth(QuicTime feedback_receive_time, + QuicBandwidth sent_bandwidth) { + QuicTime::Delta estimated_congestion_delay = QuicTime::Delta::Zero(); + BandwidthUsage new_bandwidth_usage_state = + overuse_detector_->GetState(&estimated_congestion_delay); + + switch (new_bandwidth_usage_state) { + case kBandwidthDraining: + case kBandwidthUnderUsing: + // Hold our current bitrate. + break; + case kBandwidthOverUsing: + if (!state_machine_->IncreasingDelayEvent()) { + // Less than one RTT since last IncreasingDelayEvent. + return; + } + EstimateBandwidthAfterDelayEvent(feedback_receive_time, + estimated_congestion_delay); + break; + case kBandwidthSteady: + // Calculate new pace rate. + if (bandwidth_usage_state_ == kBandwidthDraining || + bandwidth_usage_state_ == kBandwidthOverUsing) { + EstimateNewBandwidthAfterDraining(feedback_receive_time, + estimated_congestion_delay); + } else { + EstimateNewBandwidth(feedback_receive_time, sent_bandwidth); + } + break; + } + bandwidth_usage_state_ = new_bandwidth_usage_state; +} + +QuicBandwidth InterArrivalSender::BandwidthEstimate() { + return current_bandwidth_; +} + +QuicTime::Delta InterArrivalSender::SmoothedRtt() { + if (smoothed_rtt_.IsZero()) { + return QuicTime::Delta::FromMilliseconds(kInitialRttMs); + } + return smoothed_rtt_; +} + +QuicTime::Delta InterArrivalSender::RetransmissionDelay() { + // TODO(pwestin): Calculate and return retransmission delay. + // Use 2 * the smoothed RTT for now. + return smoothed_rtt_.Add(smoothed_rtt_); +} + +void InterArrivalSender::EstimateNewBandwidth(QuicTime feedback_receive_time, + QuicBandwidth sent_bandwidth) { + QuicBandwidth new_bandwidth = bitrate_ramp_up_->GetNewBitrate(sent_bandwidth); + if (current_bandwidth_ == new_bandwidth) { + return; + } + current_bandwidth_ = new_bandwidth; + state_machine_->IncreaseBitrateDecision(); + + QuicBandwidth channel_estimate = QuicBandwidth::Zero(); + ChannelEstimateState channel_estimator_state = + channel_estimator_->GetChannelEstimate(&channel_estimate); + + if (channel_estimator_state == kChannelEstimateGood) { + bitrate_ramp_up_->UpdateChannelEstimate(channel_estimate); + } + paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, + current_bandwidth_); + DLOG(INFO) << "New bandwidth estimate in steady state:" + << current_bandwidth_.ToKBitsPerSecond() + << " Kbits/s"; +} + +// Did we drain the network buffers in our expected pace? +void InterArrivalSender::EstimateNewBandwidthAfterDraining( + QuicTime feedback_receive_time, + QuicTime::Delta estimated_congestion_delay) { + if (current_bandwidth_ > back_down_bandwidth_) { + // Do nothing, our current bandwidth is higher than our bandwidth at the + // previous back down. + DLOG(INFO) << "Current bandwidth estimate is higher than before draining"; + return; + } + if (estimated_congestion_delay >= back_down_congestion_delay_) { + // Do nothing, our estimated delay have increased. + DLOG(INFO) << "Current delay estimate is higher than before draining"; + return; + } + DCHECK(back_down_time_.IsInitialized()); + QuicTime::Delta buffer_reduction = + back_down_congestion_delay_.Subtract(estimated_congestion_delay); + QuicTime::Delta elapsed_time = + feedback_receive_time.Subtract(back_down_time_).Subtract(SmoothedRtt()); + + QuicBandwidth new_estimate = QuicBandwidth::Zero(); + if (buffer_reduction >= elapsed_time) { + // We have drained more than the elapsed time... go back to our old rate. + new_estimate = back_down_bandwidth_; + } else { + float fraction_of_rate = + static_cast<float>(buffer_reduction.ToMicroseconds()) / + elapsed_time.ToMicroseconds(); // < 1.0 + + QuicBandwidth draining_rate = back_down_bandwidth_.Scale(fraction_of_rate); + QuicBandwidth max_estimated_draining_rate = + back_down_bandwidth_.Subtract(current_bandwidth_); + if (draining_rate > max_estimated_draining_rate) { + // We drained faster than our old send rate, go back to our old rate. + new_estimate = back_down_bandwidth_; + } else { + // Use our drain rate and our kMinBitrateReduction to go to our + // new estimate. + new_estimate = std::max(current_bandwidth_, + current_bandwidth_.Add(draining_rate).Scale( + 1.0f - kMinBitrateReduction)); + DLOG(INFO) << "Draining calculation; current rate:" + << current_bandwidth_.ToKBitsPerSecond() << " Kbits/s " + << "draining rate:" + << draining_rate.ToKBitsPerSecond() << " Kbits/s " + << "new estimate:" + << new_estimate.ToKBitsPerSecond() << " Kbits/s " + << " buffer reduction:" + << buffer_reduction.ToMicroseconds() << " us " + << " elapsed time:" + << elapsed_time.ToMicroseconds() << " us "; + } + } + if (new_estimate == current_bandwidth_) { + return; + } + + QuicBandwidth channel_estimate = QuicBandwidth::Zero(); + ChannelEstimateState channel_estimator_state = + channel_estimator_->GetChannelEstimate(&channel_estimate); + + // TODO(pwestin): we need to analyze channel_estimate too. + switch (channel_estimator_state) { + case kChannelEstimateUnknown: + channel_estimate = current_bandwidth_; + break; + case kChannelEstimateUncertain: + channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin); + break; + case kChannelEstimateGood: + // Do nothing, estimate is accurate. + break; + } + bitrate_ramp_up_->Reset(new_estimate, back_down_bandwidth_, channel_estimate); + state_machine_->IncreaseBitrateDecision(); + paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, new_estimate); + current_bandwidth_ = new_estimate; + DLOG(INFO) << "New bandwidth estimate after draining:" + << new_estimate.ToKBitsPerSecond() << " Kbits/s"; +} + +void InterArrivalSender::EstimateBandwidthAfterDelayEvent( + QuicTime feedback_receive_time, + QuicTime::Delta estimated_congestion_delay) { + QuicByteCount estimated_byte_buildup = + current_bandwidth_.ToBytesPerPeriod(estimated_congestion_delay); + + // To drain all build up buffer within one RTT we need to reduce the + // bitrate with the following. + // TODO(pwestin): this is a crude first implementation. + int64 draining_rate_per_rtt = (estimated_byte_buildup * + kNumMicrosPerSecond) / SmoothedRtt().ToMicroseconds(); + + float decrease_factor = + draining_rate_per_rtt / current_bandwidth_.ToBytesPerSecond(); + + decrease_factor = std::max(decrease_factor, kMinBitrateReduction); + decrease_factor = std::min(decrease_factor, kMaxBitrateReduction); + back_down_congestion_delay_ = estimated_congestion_delay; + QuicBandwidth new_target_bitrate = + current_bandwidth_.Scale(1.0f - decrease_factor); + + // While in delay sensing mode send at least one packet per RTT. + QuicBandwidth min_delay_bitrate = + QuicBandwidth::FromBytesAndTimeDelta(kMaxPacketSize, SmoothedRtt()); + new_target_bitrate = std::max(new_target_bitrate, min_delay_bitrate); + + ResetCurrentBandwidth(feedback_receive_time, new_target_bitrate); + + DLOG(INFO) << "New bandwidth estimate after delay event:" + << current_bandwidth_.ToKBitsPerSecond() + << " Kbits/s min delay bitrate:" + << min_delay_bitrate.ToKBitsPerSecond() + << " Kbits/s RTT:" + << SmoothedRtt().ToMicroseconds() + << " us"; +} + +void InterArrivalSender::EstimateBandwidthAfterLossEvent( + QuicTime feedback_receive_time) { + ResetCurrentBandwidth(feedback_receive_time, + current_bandwidth_.Scale(kPacketLossBitrateReduction)); + DLOG(INFO) << "New bandwidth estimate after loss event:" + << current_bandwidth_.ToKBitsPerSecond() + << " Kbits/s"; +} + +void InterArrivalSender::ResetCurrentBandwidth(QuicTime feedback_receive_time, + QuicBandwidth new_rate) { + new_rate = std::max(new_rate, + QuicBandwidth::FromKBitsPerSecond(kMinBitrateKbit)); + QuicBandwidth channel_estimate = QuicBandwidth::Zero(); + ChannelEstimateState channel_estimator_state = + channel_estimator_->GetChannelEstimate(&channel_estimate); + + switch (channel_estimator_state) { + case kChannelEstimateUnknown: + channel_estimate = current_bandwidth_; + break; + case kChannelEstimateUncertain: + channel_estimate = channel_estimate.Scale(kUncertainSafetyMargin); + break; + case kChannelEstimateGood: + // Do nothing. + break; + } + back_down_time_ = feedback_receive_time; + back_down_bandwidth_ = current_bandwidth_; + bitrate_ramp_up_->Reset(new_rate, current_bandwidth_, channel_estimate); + if (new_rate != current_bandwidth_) { + current_bandwidth_ = new_rate; + paced_sender_->UpdateBandwidthEstimate(feedback_receive_time, + current_bandwidth_); + state_machine_->DecreaseBitrateDecision(); + } +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender.h b/chromium/net/quic/congestion_control/inter_arrival_sender.h new file mode 100644 index 00000000000..ad28ecd215a --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_sender.h @@ -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. + +#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_ +#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/channel_estimator.h" +#include "net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h" +#include "net/quic/congestion_control/inter_arrival_overuse_detector.h" +#include "net/quic/congestion_control/inter_arrival_probe.h" +#include "net/quic/congestion_control/inter_arrival_state_machine.h" +#include "net/quic/congestion_control/paced_sender.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" +#include "net/quic/quic_bandwidth.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 InterArrivalSender : public SendAlgorithmInterface { + public: + explicit InterArrivalSender(const QuicClock* clock); + virtual ~InterArrivalSender(); + + static QuicBandwidth CalculateSentBandwidth( + const SendAlgorithmInterface::SentPacketsMap& sent_packets_map, + QuicTime feedback_receive_time); + + // Start implementation of SendAlgorithmInterface. + virtual void OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& sent_packets) OVERRIDE; + + virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount acked_bytes, + QuicTime::Delta rtt) OVERRIDE; + + virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE; + + virtual void SentPacket(QuicTime sent_time, + QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes, + Retransmission is_retransmit) OVERRIDE; + + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) OVERRIDE; + + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data, + IsHandshake handshake) OVERRIDE; + + virtual QuicBandwidth BandwidthEstimate() OVERRIDE; + virtual QuicTime::Delta SmoothedRtt() OVERRIDE; + virtual QuicTime::Delta RetransmissionDelay() OVERRIDE; + // End implementation of SendAlgorithmInterface. + + private: + void EstimateDelayBandwidth(QuicTime feedback_receive_time, + QuicBandwidth sent_bandwidth); + void EstimateNewBandwidth(QuicTime feedback_receive_time, + QuicBandwidth sent_bandwidth); + void EstimateNewBandwidthAfterDraining( + QuicTime feedback_receive_time, + QuicTime::Delta estimated_congestion_delay); + void EstimateBandwidthAfterLossEvent(QuicTime feedback_receive_time); + void EstimateBandwidthAfterDelayEvent( + QuicTime feedback_receive_time, + QuicTime::Delta estimated_congestion_delay); + void ResetCurrentBandwidth(QuicTime feedback_receive_time, + QuicBandwidth new_rate); + bool ProbingPhase(QuicTime feedback_receive_time); + + bool probing_; // Are we currently in the probing phase? + QuicBandwidth current_bandwidth_; + QuicTime::Delta smoothed_rtt_; + scoped_ptr<ChannelEstimator> channel_estimator_; + scoped_ptr<InterArrivalBitrateRampUp> bitrate_ramp_up_; + scoped_ptr<InterArrivalOveruseDetector> overuse_detector_; + scoped_ptr<InterArrivalProbe> probe_; + scoped_ptr<InterArrivalStateMachine> state_machine_; + scoped_ptr<PacedSender> paced_sender_; + int accumulated_number_of_lost_packets_; + BandwidthUsage bandwidth_usage_state_; + QuicTime back_down_time_; // Time when we decided to back down. + QuicBandwidth back_down_bandwidth_; // Bandwidth before backing down. + QuicTime::Delta back_down_congestion_delay_; // Delay when backing down. + + DISALLOW_COPY_AND_ASSIGN(InterArrivalSender); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_SENDER_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc b/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc new file mode 100644 index 00000000000..d0faca0f8f1 --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_sender_test.cc @@ -0,0 +1,565 @@ +// 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/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "net/quic/congestion_control/inter_arrival_sender.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class InterArrivalSenderTest : public ::testing::Test { + protected: + InterArrivalSenderTest() + : rtt_(QuicTime::Delta::FromMilliseconds(60)), + one_ms_(QuicTime::Delta::FromMilliseconds(1)), + one_s_(QuicTime::Delta::FromMilliseconds(1000)), + nine_ms_(QuicTime::Delta::FromMilliseconds(9)), + send_start_time_(send_clock_.Now()), + sender_(&send_clock_), + sequence_number_(1), + acked_sequence_number_(1), + feedback_sequence_number_(1) { + send_clock_.AdvanceTime(one_ms_); + receive_clock_.AdvanceTime(one_ms_); + } + + virtual ~InterArrivalSenderTest() { + STLDeleteValues(&sent_packets_); + } + + void SendAvailableCongestionWindow() { + while (sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()) { + QuicByteCount bytes_in_packet = kMaxPacketSize; + sent_packets_[sequence_number_] = + new class SendAlgorithmInterface::SentPacket( + bytes_in_packet, send_clock_.Now()); + + sender_.SentPacket(send_clock_.Now(), sequence_number_, bytes_in_packet, + NOT_RETRANSMISSION); + sequence_number_++; + } + EXPECT_FALSE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + + void AckNPackets(int n) { + for (int i = 0; i < n; ++i) { + sender_.OnIncomingAck(acked_sequence_number_++, kMaxPacketSize, rtt_); + } + } + + void SendDelaySpikeFeedbackMessage(QuicTime::Delta spike_time) { + QuicCongestionFeedbackFrame feedback; + feedback.type = kInterArrival; + feedback.inter_arrival.accumulated_number_of_lost_packets = 0; + receive_clock_.AdvanceTime(spike_time); + QuicTime receive_time = receive_clock_.ApproximateNow(); + feedback.inter_arrival.received_packet_times.insert( + std::pair<QuicPacketSequenceNumber, QuicTime>( + feedback_sequence_number_, receive_time)); + feedback_sequence_number_++; + + // We need to send feedback for 2 packets since they where sent at the + // same time. + feedback.inter_arrival.received_packet_times.insert( + std::pair<QuicPacketSequenceNumber, QuicTime>( + feedback_sequence_number_, receive_time)); + feedback_sequence_number_++; + + sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(), + sent_packets_); + } + + void SendFeedbackMessageNPackets(int n, + QuicTime::Delta delta_odd, + QuicTime::Delta delta_even) { + QuicCongestionFeedbackFrame feedback; + feedback.type = kInterArrival; + feedback.inter_arrival.accumulated_number_of_lost_packets = 0; + for (int i = 0; i < n; ++i) { + if (feedback_sequence_number_ % 2) { + receive_clock_.AdvanceTime(delta_even); + } else { + receive_clock_.AdvanceTime(delta_odd); + } + QuicTime receive_time = receive_clock_.ApproximateNow(); + feedback.inter_arrival.received_packet_times.insert( + std::pair<QuicPacketSequenceNumber, QuicTime>( + feedback_sequence_number_, receive_time)); + feedback_sequence_number_++; + } + sender_.OnIncomingQuicCongestionFeedbackFrame(feedback, send_clock_.Now(), + sent_packets_); + } + + QuicTime::Delta SenderDeltaSinceStart() { + return send_clock_.ApproximateNow().Subtract(send_start_time_); + } + + const QuicTime::Delta rtt_; + const QuicTime::Delta one_ms_; + const QuicTime::Delta one_s_; + const QuicTime::Delta nine_ms_; + MockClock send_clock_; + MockClock receive_clock_; + const QuicTime send_start_time_; + InterArrivalSender sender_; + QuicPacketSequenceNumber sequence_number_; + QuicPacketSequenceNumber acked_sequence_number_; + QuicPacketSequenceNumber feedback_sequence_number_; + SendAlgorithmInterface::SentPacketsMap sent_packets_; +}; + +TEST_F(InterArrivalSenderTest, ProbeFollowedByFullRampUpCycle) { + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // Send 5 bursts. + for (int i = 0; i < 4; ++i) { + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + SendAvailableCongestionWindow(); + + // We have now sent our probe. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); + + AckNPackets(10); + SendFeedbackMessageNPackets(10, one_ms_, nine_ms_); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // We should now have our probe rate. + QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); + int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + acc_arrival_time.ToMicroseconds(); + EXPECT_NEAR(0.7f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + DLOG(INFO) << "After probe"; + // Send 50 bursts, make sure that we move fast in the beginning. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(0.875f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10); + + // Send 50 bursts, make sure that we slow down towards the probe rate. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(0.95f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1100, 10); + + // Send 50 bursts, make sure that we move very slow close to the probe rate. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(0.99f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 1560, 10); + DLOG(INFO) << "Near available channel estimate"; + + // Send 50 bursts, make sure that we move very slow close to the probe rate. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(1.00f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2000, 100); + DLOG(INFO) << "At available channel estimate"; + + // Send 50 bursts, make sure that we move very slow close to the probe rate. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(1.01f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2500, 100); + + // Send 50 bursts, make sure that we accelerate after the probe rate. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(1.01f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2900, 100); + + // Send 50 bursts, make sure that we accelerate after the probe rate. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(1.03f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 2000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 3400, 100); + + int64 max_rate = kMaxPacketSize * kNumMicrosPerSecond / + one_ms_.ToMicroseconds(); + + int64 halfway_rate = probe_rate + (max_rate - probe_rate) / 2; + + // Send until we reach halfway point. + for (int i = 0; i < 570; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(halfway_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 5000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 6600, 100); + DLOG(INFO) << "Near halfway point"; + + // Send until we reach max channel capacity. + for (int i = 0; i < 1500; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(max_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 5000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 10000, 200); +} + +TEST_F(InterArrivalSenderTest, DelaySpikeFollowedBySlowDrain) { + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // Send 5 bursts. + for (int i = 0; i < 4; ++i) { + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + SendAvailableCongestionWindow(); + + // We have now sent our probe. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); + + AckNPackets(10); + SendFeedbackMessageNPackets(10, one_ms_, nine_ms_); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // We should now have our probe rate. + QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); + int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + acc_arrival_time.ToMicroseconds(); + EXPECT_NEAR(0.7f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + + // Send 50 bursts, make sure that we move fast in the beginning. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(0.875f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10); + + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + + int64 rate_at_introduced_delay_spike = 0.875f * probe_rate; + QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100); + SendDelaySpikeFeedbackMessage(spike_time); + + // Backing as much as we can, currently 90%. + EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10); + + // Run until we are catched up after our introduced delay spike. + while (send_clock_.Now() < receive_clock_.Now()) { + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, one_ms_); + } + // Expect that we go back to 67% of the rate before the spike. + EXPECT_NEAR(0.67f * rate_at_introduced_delay_spike, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 820, 10); + + // Send 100 bursts, make sure that we slow down towards the rate we had + // before the spike. + for (int i = 0; i < 100; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(0.97f * rate_at_introduced_delay_spike, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 2100, 10); +} + +TEST_F(InterArrivalSenderTest, DelaySpikeFollowedByImmediateDrain) { + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // Send 5 bursts. + for (int i = 0; i < 4; ++i) { + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + SendAvailableCongestionWindow(); + + // We have now sent our probe. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); + + AckNPackets(10); + SendFeedbackMessageNPackets(10, one_ms_, nine_ms_); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // We should now have our probe rate. + QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(41); + int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + acc_arrival_time.ToMicroseconds(); + EXPECT_NEAR(0.7f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + + // Send 50 bursts, make sure that we move fast in the beginning. + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, time_until_send.Subtract(one_ms_)); + } + EXPECT_NEAR(0.875f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 600, 10); + + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + AckNPackets(2); + + int64 rate_at_introduced_delay_spike = 0.875f * probe_rate; + QuicTime::Delta spike_time = QuicTime::Delta::FromMilliseconds(100); + SendDelaySpikeFeedbackMessage(spike_time); + + // Backing as much as we can, currently 90%. + EXPECT_NEAR(0.1f * rate_at_introduced_delay_spike, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 610, 10); + + // Move send time forward. + send_clock_.AdvanceTime(spike_time); + // Make sure our clocks are aligned again. + receive_clock_.AdvanceTime(send_clock_.Now().Subtract(receive_clock_.Now())); + + SendAvailableCongestionWindow(); + AckNPackets(2); + SendFeedbackMessageNPackets(2, one_ms_, one_ms_); + // We should now be back where we introduced the delay spike. + EXPECT_NEAR(rate_at_introduced_delay_spike, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + EXPECT_NEAR(SenderDeltaSinceStart().ToMilliseconds(), 710, 10); +} + +TEST_F(InterArrivalSenderTest, MinBitrateDueToDelay) { + QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10); + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // Send 5 bursts. + for (int i = 0; i < 4; ++i) { + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + SendAvailableCongestionWindow(); + + AckNPackets(10); + + // One second spread per packet is expected to result in an estimate at + // our minimum bitrate. + SendFeedbackMessageNPackets(10, one_s_, one_s_); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate()); +} + +TEST_F(InterArrivalSenderTest, MinBitrateDueToLoss) { + QuicBandwidth expected_min_bitrate = QuicBandwidth::FromKBitsPerSecond(10); + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + // Send 5 bursts. + for (int i = 0; i < 4; ++i) { + SendAvailableCongestionWindow(); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + SendAvailableCongestionWindow(); + + AckNPackets(10); + SendFeedbackMessageNPackets(10, nine_ms_, nine_ms_); + send_clock_.AdvanceTime(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + QuicTime::Delta acc_arrival_time = QuicTime::Delta::FromMilliseconds(81); + int64 probe_rate = kMaxPacketSize * 9 * kNumMicrosPerSecond / + acc_arrival_time.ToMicroseconds(); + EXPECT_NEAR(0.7f * probe_rate, + sender_.BandwidthEstimate().ToBytesPerSecond(), 1000); + + for (int i = 0; i < 15; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_.OnIncomingLoss(send_clock_.Now()); + sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + acked_sequence_number_ += 2; // Create a loss by not acking both packets. + SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); + } + // Test that our exponentail back off stop at expected_min_bitrate. + EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate()); + + for (int i = 0; i < 50; ++i) { + SendAvailableCongestionWindow(); + QuicTime::Delta time_until_send = sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + send_clock_.AdvanceTime(time_until_send); + EXPECT_TRUE(sender_.TimeUntilSend(send_clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + sender_.OnIncomingLoss(send_clock_.Now()); + sender_.OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + acked_sequence_number_ += 2; // Create a loss by not acking both packets. + SendFeedbackMessageNPackets(2, nine_ms_, nine_ms_); + + // Make sure our bitrate is fixed at the expected_min_bitrate. + EXPECT_EQ(expected_min_bitrate, sender_.BandwidthEstimate()); + } +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc new file mode 100644 index 00000000000..7095e60306b --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_state_machine.cc @@ -0,0 +1,163 @@ +// 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/inter_arrival_state_machine.h" + +#include "base/logging.h" + +namespace { +const int kIncreaseEventsBeforeDowngradingState = 5; +const int kDecreaseEventsBeforeUpgradingState = 2; +// Note: Can not be higher than kDecreaseEventsBeforeUpgradingState; +const int kLossEventsBeforeUpgradingState = 2; +// Timeout old loss and delay events after this time. +const int kEventTimeoutMs = 10000; +// A reasonable arbitrary chosen value for initial round trip time. +const int kInitialRttMs = 80; +} + +namespace net { + +InterArrivalStateMachine::InterArrivalStateMachine(const QuicClock* clock) + : clock_(clock), + current_state_(kInterArrivalStateStable), + smoothed_rtt_(QuicTime::Delta::FromMilliseconds(kInitialRttMs)), + decrease_event_count_(0), + last_decrease_event_(QuicTime::Zero()), + increase_event_count_(0), + last_increase_event_(QuicTime::Zero()), + loss_event_count_(0), + last_loss_event_(QuicTime::Zero()), + delay_event_count_(0), + last_delay_event_(QuicTime::Zero()) { +} + +InterArrivalState InterArrivalStateMachine::GetInterArrivalState() { + return current_state_; +} + +void InterArrivalStateMachine::IncreaseBitrateDecision() { + // Multiple increase event without packet loss or delay events will drive + // state back to stable. + QuicTime current_time = clock_->ApproximateNow(); + if (current_time.Subtract(last_increase_event_) < smoothed_rtt_) { + // Less than one RTT have passed; ignore this event. + return; + } + last_increase_event_ = current_time; + increase_event_count_++; + decrease_event_count_ = 0; // Reset previous decrease events. + + if (increase_event_count_ < kIncreaseEventsBeforeDowngradingState) { + // Not enough increase events to change state. + return; + } + increase_event_count_ = 0; // Reset increase events. + + switch (current_state_) { + case kInterArrivalStateStable: + // Keep this state. + break; + case kInterArrivalStatePacketLoss: + current_state_ = kInterArrivalStateStable; + break; + case kInterArrivalStateDelay: + current_state_ = kInterArrivalStateStable; + break; + case kInterArrivalStateCompetingFlow: + current_state_ = kInterArrivalStateDelay; + break; + case kInterArrivalStateCompetingTcpFLow: + current_state_ = kInterArrivalStateDelay; + break; + } +} + +void InterArrivalStateMachine::DecreaseBitrateDecision() { + DCHECK(kDecreaseEventsBeforeUpgradingState >= + kLossEventsBeforeUpgradingState); + + QuicTime current_time = clock_->ApproximateNow(); + if (current_time.Subtract(last_decrease_event_) < smoothed_rtt_) { + // Less than one RTT have passed; ignore this event. + return; + } + last_decrease_event_ = current_time; + decrease_event_count_++; + increase_event_count_ = 0; // Reset previous increase events. + if (decrease_event_count_ < kDecreaseEventsBeforeUpgradingState) { + // Not enough decrease events to change state. + return; + } + decrease_event_count_ = 0; // Reset decrease events. + + switch (current_state_) { + case kInterArrivalStateStable: + if (delay_event_count_ == 0 && loss_event_count_ > 0) { + // No recent delay events; only packet loss events. + current_state_ = kInterArrivalStatePacketLoss; + } else { + current_state_ = kInterArrivalStateDelay; + } + break; + case kInterArrivalStatePacketLoss: + // Keep this state. + break; + case kInterArrivalStateDelay: + if (loss_event_count_ >= kLossEventsBeforeUpgradingState) { + // We have packet loss events. Assume fighting with TCP. + current_state_ = kInterArrivalStateCompetingTcpFLow; + } else { + current_state_ = kInterArrivalStateCompetingFlow; + } + break; + case kInterArrivalStateCompetingFlow: + if (loss_event_count_ >= kLossEventsBeforeUpgradingState) { + // We have packet loss events. Assume fighting with TCP. + current_state_ = kInterArrivalStateCompetingTcpFLow; + } + break; + case kInterArrivalStateCompetingTcpFLow: + // Keep this state. + break; + } +} + +void InterArrivalStateMachine::set_rtt(QuicTime::Delta rtt) { + smoothed_rtt_ = rtt; +} + +bool InterArrivalStateMachine::PacketLossEvent() { + QuicTime current_time = clock_->ApproximateNow(); + if (current_time.Subtract(last_loss_event_) < smoothed_rtt_) { + // Less than one RTT have passed; ignore this event. + return false; + } + last_loss_event_ = current_time; + loss_event_count_++; + if (current_time.Subtract(last_delay_event_) > + QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) { + // Delay event have timed out. + delay_event_count_ = 0; + } + return true; +} + +bool InterArrivalStateMachine::IncreasingDelayEvent() { + QuicTime current_time = clock_->ApproximateNow(); + if (current_time.Subtract(last_delay_event_) < smoothed_rtt_) { + // Less than one RTT have passed; ignore this event. + return false; + } + last_delay_event_ = current_time; + delay_event_count_++; + if (current_time.Subtract(last_loss_event_) > + QuicTime::Delta::FromMilliseconds(kEventTimeoutMs)) { + // Loss event have timed out. + loss_event_count_ = 0; + } + return true; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine.h b/chromium/net/quic/congestion_control/inter_arrival_state_machine.h new file mode 100644 index 00000000000..829aa29938a --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_state_machine.h @@ -0,0 +1,94 @@ +// 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. + +// State machine for congestion control. The state is updated by calls from +// other modules as they detect events, or decide on taking specific actions. +// Events include things like packet loss, or growing delay, while decisions +// include decisions to increase or decrease bitrates. +// This class should be called for every event and decision made by the +// congestion control, this class will coalesce all calls relative to the +// smoothed RTT. + +#ifndef NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_ +#define NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_ + +#include "net/base/net_export.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_time.h" + +namespace net { + +// State transition diagram. +// +// kInterArrivalStatePacketLoss +// | +// kInterArrivalStateStable +// | +// kInterArrivalStateDelay +// | | +// kInterArrivalStateCompetingFlow -> kInterArrivalStateCompetingTcpFLow + +enum NET_EXPORT_PRIVATE InterArrivalState { + // We are on a network with a delay that is too small to be reliably detected, + // such as a local ethernet. + kInterArrivalStatePacketLoss = 1, + // We are on an underutilized network operating with low delay and low loss. + kInterArrivalStateStable = 2, + // We are on a network where we can detect delay changes and suffer only + // low loss. Nothing indicates that we are competing with another flow. + kInterArrivalStateDelay = 3, + // We are on a network where we can detect delay changes and suffer only + // low loss. We have indications that we are compete with another flow. + kInterArrivalStateCompetingFlow = 4, + // We are on a network where we can detect delay changes, however we suffer + // packet loss due to sharing the bottleneck link with another flow, which is + // most likely a TCP flow. + kInterArrivalStateCompetingTcpFLow = 5, +}; + +class NET_EXPORT_PRIVATE InterArrivalStateMachine { + public: + explicit InterArrivalStateMachine(const QuicClock* clock); + + InterArrivalState GetInterArrivalState(); + + // Inter arrival congestion control decided to increase bitrate. + void IncreaseBitrateDecision(); + + // Inter arrival congestion control decided to decrease bitrate. + void DecreaseBitrateDecision(); + + // Estimated smoothed round trip time. + // This should be called whenever the smoothed RTT estimate is updated. + void set_rtt(QuicTime::Delta rtt); + + // This method is called when a packet loss was reported. + bool PacketLossEvent(); + + // This method is called when we believe that packet transit delay is + // increasing, presumably due to a growing queue along the path. + bool IncreasingDelayEvent(); + + private: + const QuicClock* clock_; + InterArrivalState current_state_; + QuicTime::Delta smoothed_rtt_; + + int decrease_event_count_; + QuicTime last_decrease_event_; + + int increase_event_count_; + QuicTime last_increase_event_; + + int loss_event_count_; + QuicTime last_loss_event_; + + int delay_event_count_; + QuicTime last_delay_event_; + + DISALLOW_COPY_AND_ASSIGN(InterArrivalStateMachine); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_INTER_ARRIVAL_STATE_MACHINE_H_ diff --git a/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc b/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc new file mode 100644 index 00000000000..1a4bc8523ea --- /dev/null +++ b/chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc @@ -0,0 +1,126 @@ +// 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/inter_arrival_state_machine.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class InterArrivalStateMachineTest : public ::testing::Test { + protected: + InterArrivalStateMachineTest() { + } + + virtual void SetUp() { + state_machine_.reset(new InterArrivalStateMachine(&clock_)); + } + + MockClock clock_; + scoped_ptr<InterArrivalStateMachine> state_machine_; +}; + +TEST_F(InterArrivalStateMachineTest, SimplePacketLoss) { + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); + state_machine_->set_rtt(rtt); + state_machine_->IncreaseBitrateDecision(); + + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateStable, + state_machine_->GetInterArrivalState()); + + // Make sure we switch to state packet loss. + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStatePacketLoss, + state_machine_->GetInterArrivalState()); + + // Make sure we stay in state packet loss. + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStatePacketLoss, + state_machine_->GetInterArrivalState()); + + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStatePacketLoss, + state_machine_->GetInterArrivalState()); +} + +TEST_F(InterArrivalStateMachineTest, SimpleDelay) { + QuicTime::Delta rtt = QuicTime::Delta::FromMilliseconds(100); + state_machine_->set_rtt(rtt); + state_machine_->IncreaseBitrateDecision(); + + clock_.AdvanceTime(rtt); + state_machine_->IncreasingDelayEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateStable, + state_machine_->GetInterArrivalState()); + + // Make sure we switch to state delay. + clock_.AdvanceTime(rtt); + state_machine_->IncreasingDelayEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateDelay, + state_machine_->GetInterArrivalState()); + + clock_.AdvanceTime(rtt); + state_machine_->IncreasingDelayEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateDelay, + state_machine_->GetInterArrivalState()); + + // Make sure we switch to state competing flow(s). + clock_.AdvanceTime(rtt); + state_machine_->IncreasingDelayEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateCompetingFlow, + state_machine_->GetInterArrivalState()); + + // Make sure we stay in state competing flow(s). + clock_.AdvanceTime(rtt); + state_machine_->IncreasingDelayEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateCompetingFlow, + state_machine_->GetInterArrivalState()); + + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateCompetingFlow, + state_machine_->GetInterArrivalState()); + + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateCompetingFlow, + state_machine_->GetInterArrivalState()); + + // Make sure we switch to state competing TCP flow(s). + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateCompetingTcpFLow, + state_machine_->GetInterArrivalState()); + + // Make sure we stay in state competing TCP flow(s). + clock_.AdvanceTime(rtt); + state_machine_->PacketLossEvent(); + state_machine_->DecreaseBitrateDecision(); + EXPECT_EQ(kInterArrivalStateCompetingTcpFLow, + state_machine_->GetInterArrivalState()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/leaky_bucket.cc b/chromium/net/quic/congestion_control/leaky_bucket.cc new file mode 100644 index 00000000000..06c1f87f3ae --- /dev/null +++ b/chromium/net/quic/congestion_control/leaky_bucket.cc @@ -0,0 +1,50 @@ +// 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) { + Update(now); + return QuicTime::Delta::FromMicroseconds( + (bytes_ * base::Time::kMicrosecondsPerSecond) / + draining_rate_.ToBytesPerSecond()); +} + +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/chromium/net/quic/congestion_control/leaky_bucket.h b/chromium/net/quic/congestion_control/leaky_bucket.h new file mode 100644 index 00000000000..63ef8102a39 --- /dev/null +++ b/chromium/net/quic/congestion_control/leaky_bucket.h @@ -0,0 +1,50 @@ +// 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_clock.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); + + // 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/chromium/net/quic/congestion_control/leaky_bucket_test.cc b/chromium/net/quic/congestion_control/leaky_bucket_test.cc new file mode 100644 index 00000000000..977ef94b6c1 --- /dev/null +++ b/chromium/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/chromium/net/quic/congestion_control/paced_sender.cc b/chromium/net/quic/congestion_control/paced_sender.cc new file mode 100644 index 00000000000..dd116a80955 --- /dev/null +++ b/chromium/net/quic/congestion_control/paced_sender.cc @@ -0,0 +1,50 @@ +// 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/paced_sender.h" + +#include "net/quic/quic_protocol.h" + +namespace net { + +// To prevent too aggressive pacing we allow the following packet burst size. +const int64 kMinPacketBurstSize = 2; +// Max estimated time between calls to TimeUntilSend and +// AvailableCongestionWindow. +const int64 kMaxSchedulingDelayUs = 2000; + +PacedSender::PacedSender(QuicBandwidth estimate) + : leaky_bucket_(estimate), + pace_(estimate) { +} + +void PacedSender::UpdateBandwidthEstimate(QuicTime now, + QuicBandwidth estimate) { + leaky_bucket_.SetDrainingRate(now, estimate); + pace_ = estimate; +} + +void PacedSender::SentPacket(QuicTime now, QuicByteCount bytes) { + leaky_bucket_.Add(now, bytes); +} + +QuicTime::Delta PacedSender::TimeUntilSend(QuicTime now, + QuicTime::Delta time_until_send) { + if (time_until_send.ToMicroseconds() >= kMaxSchedulingDelayUs) { + return time_until_send; + } + // Pace the data. + QuicByteCount pacing_window = pace_.ToBytesPerPeriod( + QuicTime::Delta::FromMicroseconds(kMaxSchedulingDelayUs)); + QuicByteCount min_window_size = kMinPacketBurstSize * kMaxPacketSize; + pacing_window = std::max(pacing_window, min_window_size); + + if (pacing_window > leaky_bucket_.BytesPending(now)) { + // We have not filled our pacing window yet. + return time_until_send; + } + return leaky_bucket_.TimeRemaining(now); +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/paced_sender.h b/chromium/net/quic/congestion_control/paced_sender.h new file mode 100644 index 00000000000..5f61b76f0ab --- /dev/null +++ b/chromium/net/quic/congestion_control/paced_sender.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. +// +// Helper class that limits the congestion window to pace the packets. + +#ifndef NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_ +#define NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_ + +#include "base/basictypes.h" +#include "net/base/net_export.h" +#include "net/quic/congestion_control/leaky_bucket.h" +#include "net/quic/quic_bandwidth.h" +#include "net/quic/quic_time.h" + +namespace net { + +class NET_EXPORT_PRIVATE PacedSender { + public: + explicit PacedSender(QuicBandwidth bandwidth_estimate); + + // The estimated bandidth from the congestion algorithm changed. + void UpdateBandwidthEstimate(QuicTime now, QuicBandwidth bandwidth_estimate); + + // A packet of size bytes was sent. + void SentPacket(QuicTime now, QuicByteCount bytes); + + // Return time until we can send based on the pacing. + QuicTime::Delta TimeUntilSend(QuicTime now, QuicTime::Delta time_until_send); + + private: + // Helper object to track the rate data can leave the buffer for pacing. + LeakyBucket leaky_bucket_; + QuicBandwidth pace_; + + DISALLOW_COPY_AND_ASSIGN(PacedSender); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_PACED_SENDER_H_ diff --git a/chromium/net/quic/congestion_control/paced_sender_test.cc b/chromium/net/quic/congestion_control/paced_sender_test.cc new file mode 100644 index 00000000000..cc9297f8af3 --- /dev/null +++ b/chromium/net/quic/congestion_control/paced_sender_test.cc @@ -0,0 +1,77 @@ +// 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/paced_sender.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +const int kHundredKBytesPerS = 100; + +class PacedSenderTest : public ::testing::Test { + protected: + PacedSenderTest() + : zero_time_(QuicTime::Delta::Zero()), + paced_sender_(new PacedSender( + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS))) { + } + + const QuicTime::Delta zero_time_; + MockClock clock_; + scoped_ptr<PacedSender> paced_sender_; +}; + +TEST_F(PacedSenderTest, Basic) { + paced_sender_->UpdateBandwidthEstimate(clock_.Now(), + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS * 10)); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); + paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); + paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize); + EXPECT_EQ(static_cast<int64>(kMaxPacketSize * 2), + paced_sender_->TimeUntilSend( + clock_.Now(), zero_time_).ToMicroseconds()); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); +} + +TEST_F(PacedSenderTest, LowRate) { + paced_sender_->UpdateBandwidthEstimate(clock_.Now(), + QuicBandwidth::FromKBytesPerSecond(kHundredKBytesPerS)); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); + paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); + paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize); + EXPECT_EQ(static_cast<int64>(kMaxPacketSize * 20), + paced_sender_->TimeUntilSend( + clock_.Now(), zero_time_).ToMicroseconds()); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(24)); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); +} + +TEST_F(PacedSenderTest, HighRate) { + QuicBandwidth bandwidth_estimate = QuicBandwidth::FromKBytesPerSecond( + kHundredKBytesPerS * 100); + paced_sender_->UpdateBandwidthEstimate(clock_.Now(), bandwidth_estimate); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); + for (int i = 0; i < 16; ++i) { + paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize); + EXPECT_TRUE(paced_sender_->TimeUntilSend( + clock_.Now(), zero_time_).IsZero()); + } + paced_sender_->SentPacket(clock_.Now(), kMaxPacketSize); + EXPECT_EQ(2040, paced_sender_->TimeUntilSend( + clock_.Now(), zero_time_).ToMicroseconds()); + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(20400)); + EXPECT_TRUE(paced_sender_->TimeUntilSend(clock_.Now(), zero_time_).IsZero()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/quic_congestion_control_test.cc b/chromium/net/quic/congestion_control/quic_congestion_control_test.cc new file mode 100644 index 00000000000..0051acab1f7 --- /dev/null +++ b/chromium/net/quic/congestion_control/quic_congestion_control_test.cc @@ -0,0 +1,136 @@ +// 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. + +// Test of the full congestion control chain. + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/quic_congestion_manager.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::max; + +namespace net { +namespace test { + +class QuicCongestionManagerPeer : public QuicCongestionManager { + public: + explicit QuicCongestionManagerPeer(const QuicClock* clock, + CongestionFeedbackType congestion_type) + : QuicCongestionManager(clock, congestion_type) { + } + using QuicCongestionManager::BandwidthEstimate; +}; + +class QuicCongestionControlTest : public ::testing::Test { + protected: + QuicCongestionControlTest() + : start_(clock_.ApproximateNow()) { + } + + void SetUpCongestionType(CongestionFeedbackType congestion_type) { + manager_.reset(new QuicCongestionManagerPeer(&clock_, congestion_type)); + } + + MockClock clock_; + QuicTime start_; + scoped_ptr<QuicCongestionManagerPeer> manager_; +}; + +TEST_F(QuicCongestionControlTest, FixedRateSenderAPI) { + SetUpCongestionType(kFixRate); + QuicCongestionFeedbackFrame congestion_feedback; + congestion_feedback.type = kFixRate; + congestion_feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(30); + manager_->OnIncomingQuicCongestionFeedbackFrame(congestion_feedback, + clock_.Now()); + EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket(1, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(40), + manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(35)); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_EQ(QuicTime::Delta::Infinite(), + manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)); +} + +TEST_F(QuicCongestionControlTest, FixedRatePacing) { + SetUpCongestionType(kFixRate); + QuicAckFrame ack; + ack.received_info.largest_observed = 0; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + + QuicCongestionFeedbackFrame feedback; + feedback.type = kFixRate; + feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(100); + manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); + + QuicTime acc_advance_time(QuicTime::Zero()); + for (QuicPacketSequenceNumber i = 1; i <= 100; ++i) { + EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket(i, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION); + QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + clock_.AdvanceTime(advance_time); + acc_advance_time = acc_advance_time.Add(advance_time); + // Ack the packet we sent. + ack.received_info.largest_observed = max( + i, ack.received_info.largest_observed); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + } + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(1200), + acc_advance_time.Subtract(start_)); +} + +TEST_F(QuicCongestionControlTest, Pacing) { + SetUpCongestionType(kFixRate); + QuicAckFrame ack; + ack.received_info.largest_observed = 0; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + + QuicCongestionFeedbackFrame feedback; + feedback.type = kFixRate; + // Test a high bitrate (8Mbit/s) to trigger pacing. + feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(1000); + manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); + + QuicTime acc_advance_time(QuicTime::Zero()); + for (QuicPacketSequenceNumber i = 1; i <= 100;) { + EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION); + EXPECT_TRUE(manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket(i++, clock_.Now(), kMaxPacketSize, NOT_RETRANSMISSION); + QuicTime::Delta advance_time = manager_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + clock_.AdvanceTime(advance_time); + acc_advance_time = acc_advance_time.Add(advance_time); + // Ack the packets we sent. + ack.received_info.largest_observed = max( + i - 2, ack.received_info.largest_observed); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + ack.received_info.largest_observed = max( + i - 1, ack.received_info.largest_observed); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + } + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(120), + acc_advance_time.Subtract(start_)); +} + +// TODO(pwestin): add TCP tests. + +// TODO(pwestin): add InterArrival tests. + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/quic_congestion_manager.cc b/chromium/net/quic/congestion_control/quic_congestion_manager.cc new file mode 100644 index 00000000000..ba6bab83ba3 --- /dev/null +++ b/chromium/net/quic/congestion_control/quic_congestion_manager.cc @@ -0,0 +1,214 @@ +// 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/quic_congestion_manager.h" + +#include <algorithm> +#include <map> + +#include "base/stl_util.h" +#include "net/quic/congestion_control/receive_algorithm_interface.h" +#include "net/quic/congestion_control/send_algorithm_interface.h" + +namespace { +static const int kBitrateSmoothingPeriodMs = 1000; +static const int kHistoryPeriodMs = 5000; + +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; +static const size_t kTailDropWindowSize = 5; +static const size_t kTailDropMaxRetransmissions = 4; + +COMPILE_ASSERT(kHistoryPeriodMs >= kBitrateSmoothingPeriodMs, + history_must_be_longer_or_equal_to_the_smoothing_period); +} // namespace + +using std::map; +using std::min; + +namespace net { + +QuicCongestionManager::QuicCongestionManager( + const QuicClock* clock, + CongestionFeedbackType type) + : clock_(clock), + receive_algorithm_(ReceiveAlgorithmInterface::Create(clock, type)), + send_algorithm_(SendAlgorithmInterface::Create(clock, type)), + largest_missing_(0), + current_rtt_(QuicTime::Delta::Infinite()) { +} + +QuicCongestionManager::~QuicCongestionManager() { + STLDeleteValues(&packet_history_map_); +} + +void QuicCongestionManager::SentPacket(QuicPacketSequenceNumber sequence_number, + QuicTime sent_time, + QuicByteCount bytes, + Retransmission retransmission) { + DCHECK(!ContainsKey(pending_packets_, sequence_number)); + send_algorithm_->SentPacket(sent_time, sequence_number, bytes, + retransmission); + + packet_history_map_[sequence_number] = + new class SendAlgorithmInterface::SentPacket(bytes, sent_time); + pending_packets_[sequence_number] = bytes; + CleanupPacketHistory(); +} + +// Called when a packet is timed out. +void QuicCongestionManager::AbandoningPacket( + QuicPacketSequenceNumber sequence_number) { + PendingPacketsMap::iterator it = pending_packets_.find(sequence_number); + if (it != pending_packets_.end()) { + // Shouldn't this report loss as well? (decrease cgst window). + send_algorithm_->AbandoningPacket(sequence_number, it->second); + pending_packets_.erase(it); + } +} + +void QuicCongestionManager::OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& frame, QuicTime feedback_receive_time) { + send_algorithm_->OnIncomingQuicCongestionFeedbackFrame( + frame, feedback_receive_time, packet_history_map_); +} + +void QuicCongestionManager::OnIncomingAckFrame(const QuicAckFrame& frame, + QuicTime ack_receive_time) { + // We calculate the RTT based on the highest ACKed sequence number, the lower + // sequence numbers will include the ACK aggregation delay. + SendAlgorithmInterface::SentPacketsMap::iterator history_it = + packet_history_map_.find(frame.received_info.largest_observed); + // TODO(satyamshekhar): largest_observed might be missing. + if (history_it != packet_history_map_.end() && + !frame.received_info.delta_time_largest_observed.IsInfinite()) { + QuicTime::Delta send_delta = ack_receive_time.Subtract( + history_it->second->SendTimestamp()); + if (send_delta > frame.received_info.delta_time_largest_observed) { + current_rtt_ = send_delta.Subtract( + frame.received_info.delta_time_largest_observed); + } + } + // We want to. + // * Get all packets lower(including) than largest_observed + // from pending_packets_. + // * Remove all missing packets. + // * Send each ACK in the list to send_algorithm_. + PendingPacketsMap::iterator it, it_upper; + it = pending_packets_.begin(); + it_upper = pending_packets_.upper_bound(frame.received_info.largest_observed); + + bool new_packet_loss_reported = false; + while (it != it_upper) { + QuicPacketSequenceNumber sequence_number = it->first; + if (!IsAwaitingPacket(frame.received_info, sequence_number)) { + // Not missing, hence implicitly acked. + send_algorithm_->OnIncomingAck(sequence_number, it->second, current_rtt_); + pending_packets_.erase(it++); // Must be incremented post to work. + } else { + if (sequence_number > largest_missing_) { + // We have a new loss reported. + new_packet_loss_reported = true; + largest_missing_ = sequence_number; + } + ++it; + } + } + if (new_packet_loss_reported) { + send_algorithm_->OnIncomingLoss(ack_receive_time); + } +} + +QuicTime::Delta QuicCongestionManager::TimeUntilSend( + QuicTime now, + Retransmission retransmission, + HasRetransmittableData retransmittable, + IsHandshake handshake) { + return send_algorithm_->TimeUntilSend(now, retransmission, retransmittable, + handshake); +} + +bool QuicCongestionManager::GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) { + return receive_algorithm_->GenerateCongestionFeedback(feedback); +} + +void QuicCongestionManager::RecordIncomingPacket( + QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived) { + receive_algorithm_->RecordIncomingPacket(bytes, sequence_number, timestamp, + revived); +} + +const QuicTime::Delta QuicCongestionManager::rtt() { + return current_rtt_; +} + +const QuicTime::Delta QuicCongestionManager::DefaultRetransmissionTime() { + return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); +} + +const QuicTime::Delta QuicCongestionManager::GetRetransmissionDelay( + size_t unacked_packets_count, + size_t number_retransmissions) { + QuicTime::Delta retransmission_delay = send_algorithm_->RetransmissionDelay(); + if (retransmission_delay.IsZero()) { + // We are in the initial state, use default timeout values. + if (unacked_packets_count <= kTailDropWindowSize) { + if (number_retransmissions <= kTailDropMaxRetransmissions) { + return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + } + number_retransmissions -= kTailDropMaxRetransmissions; + } + retransmission_delay = + QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); + } + // Calcluate exponential back off. + retransmission_delay = QuicTime::Delta::FromMilliseconds( + retransmission_delay.ToMilliseconds() * static_cast<size_t>( + (1 << min<size_t>(number_retransmissions, kMaxRetransmissions)))); + + // TODO(rch): This code should move to |send_algorithm_|. + if (retransmission_delay.ToMilliseconds() < kMinRetransmissionTimeMs) { + return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs); + } + if (retransmission_delay.ToMilliseconds() > kMaxRetransmissionTimeMs) { + return QuicTime::Delta::FromMilliseconds(kMaxRetransmissionTimeMs); + } + return retransmission_delay; +} + +const QuicTime::Delta QuicCongestionManager::SmoothedRtt() { + return send_algorithm_->SmoothedRtt(); +} + +QuicBandwidth QuicCongestionManager::BandwidthEstimate() { + return send_algorithm_->BandwidthEstimate(); +} + +void QuicCongestionManager::CleanupPacketHistory() { + const QuicTime::Delta kHistoryPeriod = + QuicTime::Delta::FromMilliseconds(kHistoryPeriodMs); + QuicTime now = clock_->ApproximateNow(); + + SendAlgorithmInterface::SentPacketsMap::iterator history_it = + packet_history_map_.begin(); + for (; history_it != packet_history_map_.end(); ++history_it) { + if (now.Subtract(history_it->second->SendTimestamp()) <= kHistoryPeriod) { + return; + } + delete history_it->second; + packet_history_map_.erase(history_it); + history_it = packet_history_map_.begin(); + } +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/quic_congestion_manager.h b/chromium/net/quic/congestion_control/quic_congestion_manager.h new file mode 100644 index 00000000000..8bfa3c1d425 --- /dev/null +++ b/chromium/net/quic/congestion_control/quic_congestion_manager.h @@ -0,0 +1,121 @@ +// 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. + +// 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 the interface from the QuicConnection into the QUIC +// congestion control code. It wraps the SendAlgorithmInterface and +// ReceiveAlgorithmInterface and provides a single interface +// for consumers. + +#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_CONGESTION_MANAGER_H_ +#define NET_QUIC_CONGESTION_CONTROL_QUIC_CONGESTION_MANAGER_H_ + +#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_protocol.h" + +namespace net { + +namespace test { +class QuicConnectionPeer; +class QuicCongestionManagerPeer; +} // namespace test + +class QuicClock; +class ReceiveAlgorithmInterface; + +class NET_EXPORT_PRIVATE QuicCongestionManager { + public: + QuicCongestionManager(const QuicClock* clock, + CongestionFeedbackType congestion_type); + virtual ~QuicCongestionManager(); + + // Called when we have received an ack frame from peer. + virtual void OnIncomingAckFrame(const QuicAckFrame& frame, + QuicTime ack_receive_time); + + // Called when a congestion feedback frame is received from peer. + virtual void OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& frame, + 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. + virtual void SentPacket(QuicPacketSequenceNumber sequence_number, + QuicTime sent_time, + QuicByteCount bytes, + Retransmission retransmission); + + // Called when a packet is timed out. + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number); + + // 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, + Retransmission retransmission, + HasRetransmittableData retransmittable, + IsHandshake handshake); + + // 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); + + // Should be called for each incoming packet. + // bytes: the packet size in bytes including Quic Headers. + // sequence_number: the unique sequence number from the QUIC packet header. + // timestamp: the arrival time of the packet. + // revived: true if the packet was lost and then recovered with help of a + // FEC packet. + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived); + + const QuicTime::Delta DefaultRetransmissionTime(); + + const QuicTime::Delta GetRetransmissionDelay( + size_t unacked_packets_count, + size_t number_retransmissions); + + // Returns the estimated smoothed RTT calculated by the congestion algorithm. + const QuicTime::Delta SmoothedRtt(); + + // Returns the estimated bandwidth calculated by the congestion algorithm. + QuicBandwidth BandwidthEstimate(); + + private: + friend class test::QuicConnectionPeer; + friend class test::QuicCongestionManagerPeer; + typedef std::map<QuicPacketSequenceNumber, size_t> PendingPacketsMap; + + // Get the current(last) rtt. Infinite is returned if invalid. + const QuicTime::Delta rtt(); + + void CleanupPacketHistory(); + + const QuicClock* clock_; + scoped_ptr<ReceiveAlgorithmInterface> receive_algorithm_; + scoped_ptr<SendAlgorithmInterface> send_algorithm_; + SendAlgorithmInterface::SentPacketsMap packet_history_map_; + PendingPacketsMap pending_packets_; + QuicPacketSequenceNumber largest_missing_; + QuicTime::Delta current_rtt_; + + DISALLOW_COPY_AND_ASSIGN(QuicCongestionManager); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_CONGESTION_MANAGER_H_ diff --git a/chromium/net/quic/congestion_control/quic_congestion_manager_test.cc b/chromium/net/quic/congestion_control/quic_congestion_manager_test.cc new file mode 100644 index 00000000000..1cf44a2bdf8 --- /dev/null +++ b/chromium/net/quic/congestion_control/quic_congestion_manager_test.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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/inter_arrival_sender.h" +#include "net/quic/congestion_control/quic_congestion_manager.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/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::StrictMock; + +namespace net { +namespace test { + +class QuicCongestionManagerPeer : public QuicCongestionManager { + public: + explicit QuicCongestionManagerPeer(const QuicClock* clock, + CongestionFeedbackType congestion_type) + : QuicCongestionManager(clock, congestion_type) { + } + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { + this->send_algorithm_.reset(send_algorithm); + } + + using QuicCongestionManager::rtt; + const SendAlgorithmInterface::SentPacketsMap& packet_history_map() { + return packet_history_map_; + } + private: + DISALLOW_COPY_AND_ASSIGN(QuicCongestionManagerPeer); +}; + +class QuicCongestionManagerTest : public ::testing::Test { + protected: + void SetUpCongestionType(CongestionFeedbackType congestion_type) { + manager_.reset(new QuicCongestionManagerPeer(&clock_, congestion_type)); + } + + static const HasRetransmittableData kIgnored = HAS_RETRANSMITTABLE_DATA; + + MockClock clock_; + scoped_ptr<QuicCongestionManagerPeer> manager_; +}; + +TEST_F(QuicCongestionManagerTest, Bandwidth) { + SetUpCongestionType(kFixRate); + QuicAckFrame ack; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + + QuicCongestionFeedbackFrame feedback; + feedback.type = kFixRate; + feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(100); + manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); + + for (int i = 1; i <= 100; ++i) { + QuicTime::Delta advance_time = manager_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE); + clock_.AdvanceTime(advance_time); + EXPECT_TRUE(manager_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket(i, clock_.Now(), 1000, NOT_RETRANSMISSION); + // Ack the packet we sent. + ack.received_info.largest_observed = i; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + } + EXPECT_EQ(100, manager_->BandwidthEstimate().ToKBytesPerSecond()); + EXPECT_NEAR(100, + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToKBytesPerSecond(), + 4); +} + +TEST_F(QuicCongestionManagerTest, BandwidthWith1SecondGap) { + SetUpCongestionType(kFixRate); + QuicAckFrame ack; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + + QuicCongestionFeedbackFrame feedback; + feedback.type = kFixRate; + feedback.fix_rate.bitrate = QuicBandwidth::FromKBytesPerSecond(100); + manager_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); + + for (QuicPacketSequenceNumber sequence_number = 1; sequence_number <= 100; + ++sequence_number) { + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + EXPECT_TRUE(manager_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket( + sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION); + // Ack the packet we sent. + ack.received_info.largest_observed = sequence_number; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + } + EXPECT_EQ(100000, manager_->BandwidthEstimate().ToBytesPerSecond()); + EXPECT_NEAR(100000, + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToBytesPerSecond(), + 2000); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(500)); + EXPECT_NEAR(50000, + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToBytesPerSecond(), + 1000); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501)); + EXPECT_NEAR(100000, manager_->BandwidthEstimate().ToBytesPerSecond(), 2000); + EXPECT_TRUE(InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).IsZero()); + for (int i = 1; i <= 150; ++i) { + EXPECT_TRUE(manager_->TimeUntilSend( + clock_.Now(), NOT_RETRANSMISSION, kIgnored, NOT_HANDSHAKE).IsZero()); + manager_->SentPacket(i + 100, clock_.Now(), 1000, NOT_RETRANSMISSION); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + // Ack the packet we sent. + ack.received_info.largest_observed = i + 100; + manager_->OnIncomingAckFrame(ack, clock_.Now()); + } + EXPECT_EQ(100, manager_->BandwidthEstimate().ToKBytesPerSecond()); + EXPECT_NEAR(100, + InterArrivalSender::CalculateSentBandwidth( + manager_->packet_history_map(), + clock_.Now()).ToKBytesPerSecond(), + 2); +} + +TEST_F(QuicCongestionManagerTest, Rtt) { + SetUpCongestionType(kFixRate); + + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + manager_->SetSendAlgorithm(send_algorithm); + + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(15); + + EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1); + EXPECT_CALL(*send_algorithm, + OnIncomingAck(sequence_number, _, expected_rtt)).Times(1); + + manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20)); + + QuicAckFrame ack; + ack.received_info.largest_observed = sequence_number; + ack.received_info.delta_time_largest_observed = + QuicTime::Delta::FromMilliseconds(5); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + EXPECT_EQ(manager_->rtt(), expected_rtt); +} + +TEST_F(QuicCongestionManagerTest, RttWithInvalidDelta) { + // Expect that the RTT is infinite since the delta_time_largest_observed is + // larger than the local time elapsed aka invalid. + SetUpCongestionType(kFixRate); + + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + manager_->SetSendAlgorithm(send_algorithm); + + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::Infinite(); + + EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1); + EXPECT_CALL(*send_algorithm, + OnIncomingAck(sequence_number, _, expected_rtt)).Times(1); + + manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + + QuicAckFrame ack; + ack.received_info.largest_observed = sequence_number; + ack.received_info.delta_time_largest_observed = + QuicTime::Delta::FromMilliseconds(11); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + EXPECT_EQ(manager_->rtt(), expected_rtt); +} + +TEST_F(QuicCongestionManagerTest, RttInfiniteDelta) { + // Expect that the RTT is infinite since the delta_time_largest_observed is + // infinite aka invalid. + SetUpCongestionType(kFixRate); + + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + manager_->SetSendAlgorithm(send_algorithm); + + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::Infinite(); + + EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1); + EXPECT_CALL(*send_algorithm, + OnIncomingAck(sequence_number, _, expected_rtt)).Times(1); + + manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(10)); + + QuicAckFrame ack; + ack.received_info.largest_observed = sequence_number; + ack.received_info.delta_time_largest_observed = QuicTime::Delta::Infinite(); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + EXPECT_EQ(manager_->rtt(), expected_rtt); +} + +TEST_F(QuicCongestionManagerTest, RttZeroDelta) { + // Expect that the RTT is the time between send and receive since the + // delta_time_largest_observed is zero. + SetUpCongestionType(kFixRate); + + MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; + manager_->SetSendAlgorithm(send_algorithm); + + QuicPacketSequenceNumber sequence_number = 1; + QuicTime::Delta expected_rtt = QuicTime::Delta::FromMilliseconds(10); + + EXPECT_CALL(*send_algorithm, SentPacket(_, _, _, _)).Times(1); + EXPECT_CALL(*send_algorithm, + OnIncomingAck(sequence_number, _, expected_rtt)).Times(1); + + manager_->SentPacket(sequence_number, clock_.Now(), 1000, NOT_RETRANSMISSION); + clock_.AdvanceTime(expected_rtt); + + QuicAckFrame ack; + ack.received_info.largest_observed = sequence_number; + ack.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + manager_->OnIncomingAckFrame(ack, clock_.Now()); + EXPECT_EQ(manager_->rtt(), expected_rtt); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map.h b/chromium/net/quic/congestion_control/quic_max_sized_map.h new file mode 100644 index 00000000000..a4ed7769d61 --- /dev/null +++ b/chromium/net/quic/congestion_control/quic_max_sized_map.h @@ -0,0 +1,77 @@ +// 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. + +// Simple max sized map. Automatically deletes the oldest element when the +// max limit is reached. +// Note: the ConstIterator will NOT be valid after an Insert or RemoveAll. +#ifndef NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_ +#define NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_ + +#include <stdlib.h> + +#include <list> +#include <map> + +#include "base/basictypes.h" + +namespace net { + +template <class Key, class Value> +class QuicMaxSizedMap { + public: + typedef typename std::multimap<Key, Value>::const_iterator ConstIterator; + + explicit QuicMaxSizedMap(size_t max_numer_of_items) + : max_numer_of_items_(max_numer_of_items) { + } + + size_t MaxSize() const { + return max_numer_of_items_; + } + + size_t Size() const { + return table_.size(); + } + + void Insert(const Key& k, const Value& value) { + if (Size() == MaxSize()) { + ListIterator list_it = insert_order_.begin(); + table_.erase(*list_it); + insert_order_.pop_front(); + } + TableIterator it = table_.insert(std::pair<Key, Value>(k, value)); + insert_order_.push_back(it); + } + + void RemoveAll() { + table_.clear(); + insert_order_.clear(); + } + + // STL style const_iterator support. + ConstIterator Find(const Key& k) const { + return table_.find(k); + } + + ConstIterator Begin() const { + return ConstIterator(table_.begin()); + } + + ConstIterator End() const { + return ConstIterator(table_.end()); + } + + private: + typedef typename std::multimap<Key, Value>::iterator TableIterator; + typedef typename std::list<TableIterator>::iterator ListIterator; + + const size_t max_numer_of_items_; + std::multimap<Key, Value> table_; + std::list<TableIterator> insert_order_; + + DISALLOW_COPY_AND_ASSIGN(QuicMaxSizedMap); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_QUIC_MAX_SIZED_MAP_H_ diff --git a/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc b/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc new file mode 100644 index 00000000000..89c05cccbaf --- /dev/null +++ b/chromium/net/quic/congestion_control/quic_max_sized_map_test.cc @@ -0,0 +1,66 @@ +// 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/logging.h" +#include "net/quic/congestion_control/quic_max_sized_map.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class QuicMaxSizedMapTest : public ::testing::Test { +}; + +TEST_F(QuicMaxSizedMapTest, Basic) { + QuicMaxSizedMap<int, int> test_map(100); + EXPECT_EQ(100u, test_map.MaxSize()); + EXPECT_EQ(0u, test_map.Size()); + test_map.Insert(1, 2); + test_map.Insert(1, 3); + EXPECT_EQ(100u, test_map.MaxSize()); + EXPECT_EQ(2u, test_map.Size()); + test_map.RemoveAll(); + EXPECT_EQ(100u, test_map.MaxSize()); + EXPECT_EQ(0u, test_map.Size()); +} + +TEST_F(QuicMaxSizedMapTest, Find) { + QuicMaxSizedMap<int, int> test_map(100); + test_map.Insert(1, 2); + test_map.Insert(1, 3); + test_map.Insert(2, 4); + test_map.Insert(3, 5); + QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Find(2); + EXPECT_TRUE(it != test_map.End()); + EXPECT_EQ(4, it->second); + it = test_map.Find(1); + EXPECT_TRUE(it != test_map.End()); + EXPECT_EQ(2, it->second); + ++it; + EXPECT_TRUE(it != test_map.End()); + EXPECT_EQ(3, it->second); +} + +TEST_F(QuicMaxSizedMapTest, Sort) { + QuicMaxSizedMap<int, int> test_map(100); + test_map.Insert(9, 9); + test_map.Insert(8, 8); + test_map.Insert(7, 7); + test_map.Insert(6, 6); + test_map.Insert(2, 2); + test_map.Insert(4, 4); + test_map.Insert(5, 5); + test_map.Insert(3, 3); + test_map.Insert(0, 0); + test_map.Insert(1, 1); + QuicMaxSizedMap<int, int>::ConstIterator it = test_map.Begin(); + for (int i = 0; i < 10; ++i, ++it) { + EXPECT_TRUE(it != test_map.End()); + EXPECT_EQ(i, it->first); + EXPECT_EQ(i, it->second); + } +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.cc b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc new file mode 100644 index 00000000000..e8dacf0725b --- /dev/null +++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.cc @@ -0,0 +1,27 @@ +// 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/fix_rate_receiver.h" +#include "net/quic/congestion_control/tcp_receiver.h" + +namespace net { + +// Factory for receive side congestion control algorithm. +ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create( + const QuicClock* clock, + CongestionFeedbackType type) { + switch (type) { + case kTCP: + return new TcpReceiver(); + case kInterArrival: + break; // TODO(pwestin) Implement. + case kFixRate: + return new FixRateReceiver(); + } + return NULL; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/receive_algorithm_interface.h b/chromium/net/quic/congestion_control/receive_algorithm_interface.h new file mode 100644 index 00000000000..e2bae4bb793 --- /dev/null +++ b/chromium/net/quic/congestion_control/receive_algorithm_interface.h @@ -0,0 +1,44 @@ +// 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(const QuicClock* clock, + 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. + // revived: is set if the packet is lost and then recovered with help of FEC + // (Forward Error Correction) packet(s). + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_RECEIVE_ALGORITHM_INTERFACE_H_ diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.cc b/chromium/net/quic/congestion_control/send_algorithm_interface.cc new file mode 100644 index 00000000000..ce24a00b1ac --- /dev/null +++ b/chromium/net/quic/congestion_control/send_algorithm_interface.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/congestion_control/send_algorithm_interface.h" + +#include "net/quic/congestion_control/cubic.h" +#include "net/quic/congestion_control/fix_rate_sender.h" +#include "net/quic/congestion_control/tcp_cubic_sender.h" + +namespace net { + +const bool kUseReno = false; +// TODO(ianswett): Increase the max congestion window once the RTO logic is +// improved, particularly in cases when RTT is larger than the RTO. b/10075719 +// Maximum number of outstanding packets for tcp. +const QuicTcpCongestionWindow kMaxTcpCongestionWindow = 50; + +// Factory for send side congestion control algorithm. +SendAlgorithmInterface* SendAlgorithmInterface::Create( + const QuicClock* clock, + CongestionFeedbackType type) { + switch (type) { + case kTCP: + return new TcpCubicSender(clock, kUseReno, kMaxTcpCongestionWindow); + case kInterArrival: + break; // TODO(pwestin) Implement. + case kFixRate: + return new FixRateSender(clock); + } + return NULL; +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/send_algorithm_interface.h b/chromium/net/quic/congestion_control/send_algorithm_interface.h new file mode 100644 index 00000000000..8896b2b06df --- /dev/null +++ b/chromium/net/quic/congestion_control/send_algorithm_interface.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. +// +// 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 <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_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +class NET_EXPORT_PRIVATE SendAlgorithmInterface { + public: + class SentPacket { + public: + SentPacket(QuicByteCount bytes, QuicTime timestamp) + : bytes_sent_(bytes), + send_timestamp_(timestamp) { + } + QuicByteCount BytesSent() { return bytes_sent_; } + QuicTime& SendTimestamp() { return send_timestamp_; } + + private: + QuicByteCount bytes_sent_; + QuicTime send_timestamp_; + }; + + typedef std::map<QuicPacketSequenceNumber, SentPacket*> SentPacketsMap; + + static SendAlgorithmInterface* Create(const QuicClock* clock, + CongestionFeedbackType type); + + virtual ~SendAlgorithmInterface() {} + + // Called when we receive congestion feedback from remote peer. + virtual void OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& sent_packets) = 0; + + // Called for each received ACK, with sequence number from remote peer. + virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount acked_bytes, + QuicTime::Delta rtt) = 0; + + virtual void OnIncomingLoss(QuicTime ack_receive_time) = 0; + + // Inform that we sent x bytes to the wire, and if that was a retransmission. + // Note: this function must be called for every packet sent to the wire. + virtual void SentPacket(QuicTime sent_time, + QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes, + Retransmission is_retransmission) = 0; + + // Called when a packet is timed out. + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) = 0; + + // Calculate the time until we can send the next packet. + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data, + IsHandshake handshake) = 0; + + // What's the current estimated bandwidth in bytes per second. + // Returns 0 when it does not have an estimate. + virtual QuicBandwidth BandwidthEstimate() = 0; + + // TODO(satyamshekhar): Monitor MinRtt. + virtual QuicTime::Delta SmoothedRtt() = 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() = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_INTERFACE_H_ diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc new file mode 100644 index 00000000000..1e98c12cbf3 --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.cc @@ -0,0 +1,271 @@ +// 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" + +namespace net { + +namespace { +// Constants based on TCP defaults. +const int64 kHybridStartLowWindow = 16; +const QuicByteCount kMaxSegmentSize = kMaxPacketSize; +const QuicByteCount kDefaultReceiveWindow = 64000; +const int64 kInitialCongestionWindow = 10; +const int kMaxBurstLength = 3; +const int kInitialRttMs = 60; // At a typical RTT 60 ms. +const float kAlpha = 0.125f; +const float kOneMinusAlpha = (1 - kAlpha); +const float kBeta = 0.25f; +const float kOneMinusBeta = (1 - kBeta); +}; // namespace + +TcpCubicSender::TcpCubicSender( + const QuicClock* clock, + bool reno, + QuicTcpCongestionWindow max_tcp_congestion_window) + : hybrid_slow_start_(clock), + cubic_(clock), + reno_(reno), + congestion_window_count_(0), + receiver_congestion_window_(kDefaultReceiveWindow), + last_received_accumulated_number_of_lost_packets_(0), + bytes_in_flight_(0), + update_end_sequence_number_(true), + end_sequence_number_(0), + congestion_window_(kInitialCongestionWindow), + slowstart_threshold_(max_tcp_congestion_window), + max_tcp_congestion_window_(max_tcp_congestion_window), + delay_min_(QuicTime::Delta::Zero()), + smoothed_rtt_(QuicTime::Delta::Zero()), + mean_deviation_(QuicTime::Delta::Zero()) { +} + +TcpCubicSender::~TcpCubicSender() { +} + +void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& /*sent_packets*/) { + if (last_received_accumulated_number_of_lost_packets_ != + feedback.tcp.accumulated_number_of_lost_packets) { + int recovered_lost_packets = + last_received_accumulated_number_of_lost_packets_ - + feedback.tcp.accumulated_number_of_lost_packets; + last_received_accumulated_number_of_lost_packets_ = + feedback.tcp.accumulated_number_of_lost_packets; + if (recovered_lost_packets > 0) { + OnIncomingLoss(feedback_receive_time); + } + } + receiver_congestion_window_ = feedback.tcp.receive_window; +} + +void TcpCubicSender::OnIncomingAck( + QuicPacketSequenceNumber acked_sequence_number, QuicByteCount acked_bytes, + QuicTime::Delta rtt) { + bytes_in_flight_ -= acked_bytes; + CongestionAvoidance(acked_sequence_number); + AckAccounting(rtt); + if (end_sequence_number_ == acked_sequence_number) { + DLOG(INFO) << "Start update end sequence number @" << acked_sequence_number; + update_end_sequence_number_ = true; + } +} + +void TcpCubicSender::OnIncomingLoss(QuicTime /*ack_receive_time*/) { + // In a normal TCP we would need to know the lowest missing packet to detect + // if we receive 3 missing packets. Here we get a missing packet for which we + // enter TCP Fast Retransmit immediately. + if (reno_) { + congestion_window_ = congestion_window_ >> 1; + slowstart_threshold_ = congestion_window_; + } else { + congestion_window_ = + cubic_.CongestionWindowAfterPacketLoss(congestion_window_); + slowstart_threshold_ = congestion_window_; + } + // Sanity, make sure that we don't end up with an empty window. + if (congestion_window_ == 0) { + congestion_window_ = 1; + } + DLOG(INFO) << "Incoming loss; congestion window:" << congestion_window_; +} + +void TcpCubicSender::SentPacket(QuicTime /*sent_time*/, + QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes, + Retransmission is_retransmission) { + bytes_in_flight_ += bytes; + if (is_retransmission == NOT_RETRANSMISSION && update_end_sequence_number_) { + end_sequence_number_ = sequence_number; + if (AvailableCongestionWindow() == 0) { + update_end_sequence_number_ = false; + DLOG(INFO) << "Stop update end sequence number @" << sequence_number; + } + } +} + +void TcpCubicSender::AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) { + bytes_in_flight_ -= abandoned_bytes; +} + +QuicTime::Delta TcpCubicSender::TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data, + IsHandshake handshake) { + if (is_retransmission == IS_RETRANSMISSION || + has_retransmittable_data == NO_RETRANSMITTABLE_DATA || + handshake == IS_HANDSHAKE) { + // For TCP we can always send a retransmission, or an ACK immediately. + // We also immediately send any handshake packet (CHLO, etc.). We provide + // this special dispensation for handshake messages in QUIC, although the + // concept is not present in TCP. + return QuicTime::Delta::Zero(); + } + if (AvailableCongestionWindow() == 0) { + return QuicTime::Delta::Infinite(); + } + return QuicTime::Delta::Zero(); +} + +QuicByteCount TcpCubicSender::AvailableCongestionWindow() { + if (bytes_in_flight_ > CongestionWindow()) { + return 0; + } + return CongestionWindow() - bytes_in_flight_; +} + +QuicByteCount TcpCubicSender::CongestionWindow() { + // What's the current congestion window in bytes. + return std::min(receiver_congestion_window_, + congestion_window_ * kMaxSegmentSize); +} + +QuicBandwidth TcpCubicSender::BandwidthEstimate() { + // TODO(pwestin): make a long term estimate, based on RTT and loss rate? or + // instantaneous estimate? + // Throughput ~= (1/RTT)*sqrt(3/2p) + return QuicBandwidth::Zero(); +} + +QuicTime::Delta TcpCubicSender::SmoothedRtt() { + if (smoothed_rtt_.IsZero()) { + return QuicTime::Delta::FromMilliseconds(kInitialRttMs); + } + return smoothed_rtt_; +} + +QuicTime::Delta TcpCubicSender::RetransmissionDelay() { + return QuicTime::Delta::FromMicroseconds( + smoothed_rtt_.ToMicroseconds() + 4 * mean_deviation_.ToMicroseconds()); +} + +void TcpCubicSender::Reset() { + delay_min_ = QuicTime::Delta::Zero(); + hybrid_slow_start_.Restart(); +} + +bool TcpCubicSender::IsCwndLimited() const { + const QuicByteCount congestion_window_bytes = congestion_window_ * + kMaxSegmentSize; + if (bytes_in_flight_ >= congestion_window_bytes) { + return true; + } + const QuicByteCount tcp_max_burst = kMaxBurstLength * kMaxSegmentSize; + const QuicByteCount left = congestion_window_bytes - bytes_in_flight_; + return left <= tcp_max_burst; +} + +// Called when we receive and ack. Normal TCP tracks how many packets one ack +// represents, but quic has a separate ack for each packet. +void TcpCubicSender::CongestionAvoidance(QuicPacketSequenceNumber ack) { + if (!IsCwndLimited()) { + // We don't update the congestion window unless we are close to using the + // window we have available. + return; + } + if (congestion_window_ < slowstart_threshold_) { + // Slow start. + if (hybrid_slow_start_.EndOfRound(ack)) { + hybrid_slow_start_.Reset(end_sequence_number_); + } + // congestion_window_cnt is the number of acks since last change of snd_cwnd + if (congestion_window_ < max_tcp_congestion_window_) { + // TCP slow start, exponentail growth, increase by one for each ACK. + congestion_window_++; + } + DLOG(INFO) << "Slow start; congestion window:" << congestion_window_; + } else { + if (congestion_window_ < max_tcp_congestion_window_) { + if (reno_) { + // Classic Reno congestion avoidance provided for testing. + if (congestion_window_count_ >= congestion_window_) { + congestion_window_++; + congestion_window_count_ = 0; + } else { + congestion_window_count_++; + } + DLOG(INFO) << "Reno; congestion window:" << congestion_window_; + } else { + congestion_window_ = cubic_.CongestionWindowAfterAck(congestion_window_, + delay_min_); + DLOG(INFO) << "Cubic; congestion window:" << congestion_window_; + } + } + } +} + +// TODO(pwestin): what is the timout value? +void TcpCubicSender::OnTimeOut() { + cubic_.Reset(); + congestion_window_ = 1; +} + +void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) { + if (rtt.IsInfinite() || rtt.IsZero()) { + return; + } + // RTT can't be negative. + DCHECK_LT(0, rtt.ToMicroseconds()); + + // TODO(pwestin): Discard delay samples right after fast recovery, + // during 1 second?. + + // First time call or link delay decreases. + if (delay_min_.IsZero() || delay_min_ > rtt) { + delay_min_ = rtt; + } + // First time call. + if (smoothed_rtt_.IsZero()) { + smoothed_rtt_ = rtt; + mean_deviation_ = QuicTime::Delta::FromMicroseconds( + rtt.ToMicroseconds() / 2); + } else { + mean_deviation_ = QuicTime::Delta::FromMicroseconds( + kOneMinusBeta * mean_deviation_.ToMicroseconds() + + kBeta * abs(smoothed_rtt_.ToMicroseconds() - rtt.ToMicroseconds())); + smoothed_rtt_ = QuicTime::Delta::FromMicroseconds( + kOneMinusAlpha * smoothed_rtt_.ToMicroseconds() + + kAlpha * rtt.ToMicroseconds()); + DLOG(INFO) << "Cubic; mean_deviation_:" << mean_deviation_.ToMicroseconds(); + } + + // Hybrid start triggers when cwnd is larger than some threshold. + if (congestion_window_ <= slowstart_threshold_ && + congestion_window_ >= kHybridStartLowWindow) { + if (!hybrid_slow_start_.started()) { + // Time to start the hybrid slow start. + hybrid_slow_start_.Reset(end_sequence_number_); + } + hybrid_slow_start_.Update(rtt, delay_min_); + if (hybrid_slow_start_.Exit()) { + slowstart_threshold_ = congestion_window_; + } + } +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender.h b/chromium/net/quic/congestion_control/tcp_cubic_sender.h new file mode 100644 index 00000000000..c22813a2cfd --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender.h @@ -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. +// +// TCP cubic send side congestion algorithm, emulates the behaviour 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_protocol.h" +#include "net/quic/quic_time.h" + +namespace net { + +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, + bool reno, + QuicTcpCongestionWindow max_tcp_congestion_window); + virtual ~TcpCubicSender(); + + // Start implementation of SendAlgorithmInterface. + virtual void OnIncomingQuicCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback, + QuicTime feedback_receive_time, + const SentPacketsMap& sent_packets) OVERRIDE; + virtual void OnIncomingAck(QuicPacketSequenceNumber acked_sequence_number, + QuicByteCount acked_bytes, + QuicTime::Delta rtt) OVERRIDE; + virtual void OnIncomingLoss(QuicTime ack_receive_time) OVERRIDE; + virtual void SentPacket(QuicTime sent_time, + QuicPacketSequenceNumber sequence_number, + QuicByteCount bytes, + Retransmission is_retransmission) OVERRIDE; + virtual void AbandoningPacket(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes) OVERRIDE; + virtual QuicTime::Delta TimeUntilSend( + QuicTime now, + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data, + IsHandshake handshake) OVERRIDE; + virtual QuicBandwidth BandwidthEstimate() OVERRIDE; + virtual QuicTime::Delta SmoothedRtt() OVERRIDE; + virtual QuicTime::Delta RetransmissionDelay() OVERRIDE; + // End implementation of SendAlgorithmInterface. + + private: + friend class test::TcpCubicSenderPeer; + + QuicByteCount AvailableCongestionWindow(); + QuicByteCount CongestionWindow(); + void Reset(); + void AckAccounting(QuicTime::Delta rtt); + void CongestionAvoidance(QuicPacketSequenceNumber ack); + bool IsCwndLimited() const; + void OnTimeOut(); + + HybridSlowStart hybrid_slow_start_; + Cubic cubic_; + + // Reno provided for testing. + const bool reno_; + + // ACK counter for the Reno implementation. + int64 congestion_window_count_; + + // Receiver side advertised window. + QuicByteCount receiver_congestion_window_; + + // Receiver side advertised packet loss. + int last_received_accumulated_number_of_lost_packets_; + + // Bytes in flight, aka bytes on the wire. + QuicByteCount bytes_in_flight_; + + // We need to keep track of the end sequence number of each RTT "burst". + bool update_end_sequence_number_; + QuicPacketSequenceNumber end_sequence_number_; + + // Congestion window in packets. + QuicTcpCongestionWindow congestion_window_; + + // Slow start congestion window in packets. + QuicTcpCongestionWindow slowstart_threshold_; + + // Maximum number of outstanding packets for tcp. + QuicTcpCongestionWindow max_tcp_congestion_window_; + + // Min RTT during this session. + QuicTime::Delta delay_min_; + + // Smoothed RTT during this session. + 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_; + + DISALLOW_COPY_AND_ASSIGN(TcpCubicSender); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TCP_CUBIC_SENDER_H_ diff --git a/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc new file mode 100644 index 00000000000..e9f88937426 --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -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. + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/congestion_control/tcp_cubic_sender.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 { + +const uint32 kDefaultWindowTCP = 10 * kMaxPacketSize; +const QuicByteCount kNoNBytesInFlight = 0; + +class TcpCubicSenderPeer : public TcpCubicSender { + public: + // TODO(ianswett): Remove 10000 once b/10075719 is fixed. + TcpCubicSenderPeer(const QuicClock* clock, bool reno) + : TcpCubicSender(clock, reno, 10000) { + } + using TcpCubicSender::AvailableCongestionWindow; + using TcpCubicSender::CongestionWindow; + using TcpCubicSender::AckAccounting; +}; + +class TcpCubicSenderTest : public ::testing::Test { + protected: + TcpCubicSenderTest() + : rtt_(QuicTime::Delta::FromMilliseconds(60)), + one_ms_(QuicTime::Delta::FromMilliseconds(1)), + sender_(new TcpCubicSenderPeer(&clock_, true)), + receiver_(new TcpReceiver()), + sequence_number_(1), + acked_sequence_number_(0) { + } + + void SendAvailableCongestionWindow() { + QuicByteCount bytes_to_send = sender_->AvailableCongestionWindow(); + while (bytes_to_send > 0) { + QuicByteCount bytes_in_packet = std::min(kMaxPacketSize, bytes_to_send); + sender_->SentPacket(clock_.Now(), sequence_number_++, bytes_in_packet, + NOT_RETRANSMISSION); + bytes_to_send -= bytes_in_packet; + if (bytes_to_send > 0) { + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + } + } + } + // Normal is that TCP acks every other segment. + void AckNPackets(int n) { + for (int i = 0; i < n; ++i) { + acked_sequence_number_++; + sender_->OnIncomingAck(acked_sequence_number_, kMaxPacketSize, rtt_); + } + clock_.AdvanceTime(one_ms_); // 1 millisecond. + } + + const QuicTime::Delta rtt_; + const QuicTime::Delta one_ms_; + MockClock clock_; + SendAlgorithmInterface::SentPacketsMap not_used_; + scoped_ptr<TcpCubicSenderPeer> sender_; + scoped_ptr<TcpReceiver> receiver_; + QuicPacketSequenceNumber sequence_number_; + QuicPacketSequenceNumber acked_sequence_number_; +}; + +TEST_F(TcpCubicSenderTest, SimpleSender) { + QuicCongestionFeedbackFrame feedback; + // At startup make sure we are at the default. + EXPECT_EQ(kDefaultWindowTCP, + sender_->AvailableCongestionWindow()); + // At startup make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + // Get default QuicCongestionFeedbackFrame from receiver. + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), + not_used_); + // Make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + // And that window is un-affected. + EXPECT_EQ(kDefaultWindowTCP, sender_->AvailableCongestionWindow()); + + // A retransmitt should always retun 0. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + IS_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); +} + +TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { + const int kNumberOfAck = 20; + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + // Get default QuicCongestionFeedbackFrame from receiver. + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), + not_used_); + // Make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + for (int n = 0; n < kNumberOfAck; ++n) { + // Send our full congestion window. + SendAvailableCongestionWindow(); + AckNPackets(2); + } + QuicByteCount bytes_to_send = sender_->CongestionWindow(); + EXPECT_EQ(kDefaultWindowTCP + kMaxPacketSize * 2 * kNumberOfAck, + bytes_to_send); +} + +TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { + // 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 kNumberOfAck = 65; + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + // Get default QuicCongestionFeedbackFrame from receiver. + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), + not_used_); + // Make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + for (int n = 0; n < kNumberOfAck; ++n) { + // Send our full congestion window. + SendAvailableCongestionWindow(); + AckNPackets(2); + } + QuicByteCount expected_congestion_window = + kDefaultWindowTCP + (kMaxPacketSize * 2 * kNumberOfAck); + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + // We should now have fallen out of slow start. + SendAvailableCongestionWindow(); + AckNPackets(2); + expected_congestion_window += kMaxPacketSize; + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + + // Testing Reno phase. + // We should need 141(65*2+1+10) ACK:ed packets before increasing window by + // one. + for (int m = 0; m < 70; ++m) { + SendAvailableCongestionWindow(); + AckNPackets(2); + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + } + SendAvailableCongestionWindow(); + AckNPackets(2); + expected_congestion_window += kMaxPacketSize; + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); +} + +TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { + // Make sure that we fall out of slow start when we encounter a packet loss. + const int kNumberOfAck = 10; + QuicCongestionFeedbackFrame feedback; + // At startup make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + // Get default QuicCongestionFeedbackFrame from receiver. + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now(), + not_used_); + // Make sure we can send. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), + NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsZero()); + + for (int i = 0; i < kNumberOfAck; ++i) { + // Send our full congestion window. + SendAvailableCongestionWindow(); + AckNPackets(2); + } + SendAvailableCongestionWindow(); + QuicByteCount expected_congestion_window = kDefaultWindowTCP + + (kMaxPacketSize * 2 * kNumberOfAck); + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + + sender_->OnIncomingLoss(clock_.Now()); + + // Make sure that we should not send right now. + EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE).IsInfinite()); + + // We should now have fallen out of slow start. + // We expect window to be cut in half. + expected_congestion_window /= 2; + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + + // Testing Reno phase. + // We need to ack half of the pending packet before we can send agin. + int number_of_packets_in_window = expected_congestion_window / kMaxPacketSize; + AckNPackets(number_of_packets_in_window); + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + EXPECT_EQ(0u, sender_->AvailableCongestionWindow()); + + AckNPackets(1); + expected_congestion_window += kMaxPacketSize; + number_of_packets_in_window++; + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + + // We should need number_of_packets_in_window ACK:ed packets before + // increasing window by one. + for (int k = 0; k < number_of_packets_in_window; ++k) { + SendAvailableCongestionWindow(); + AckNPackets(1); + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); + } + SendAvailableCongestionWindow(); + AckNPackets(1); + expected_congestion_window += kMaxPacketSize; + EXPECT_EQ(expected_congestion_window, sender_->CongestionWindow()); +} + +TEST_F(TcpCubicSenderTest, RetransmissionDelay) { + const int64 kRttMs = 10; + const int64 kDeviationMs = 3; + EXPECT_EQ(QuicTime::Delta::Zero(), sender_->RetransmissionDelay()); + + sender_->AckAccounting(QuicTime::Delta::FromMilliseconds(kRttMs)); + + // 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 finaly the + // smoothed rtt is added which is the inital 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_->AckAccounting( + QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs)); + sender_->AckAccounting( + QuicTime::Delta::FromMilliseconds(kRttMs - kDeviationMs)); + } + expected_delay = QuicTime::Delta::FromMilliseconds(kRttMs + kDeviationMs * 4); + + EXPECT_NEAR(kRttMs, sender_->SmoothedRtt().ToMilliseconds(), 1); + EXPECT_NEAR(expected_delay.ToMilliseconds(), + sender_->RetransmissionDelay().ToMilliseconds(), + 1); +} +} // namespace test +} // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_receiver.cc b/chromium/net/quic/congestion_control/tcp_receiver.cc new file mode 100644 index 00000000000..465e40f194a --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_receiver.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/basictypes.h" +#include "net/quic/congestion_control/tcp_receiver.h" + +namespace net { + +// Originally 64K bytes for TCP, setting it to 256K to support higher bitrates. +const QuicByteCount kReceiveWindowTCP = 256000; + +TcpReceiver::TcpReceiver() + : accumulated_number_of_recoverd_lost_packets_(0), + receive_window_(kReceiveWindowTCP) { +} + +bool TcpReceiver::GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) { + feedback->type = kTCP; + feedback->tcp.accumulated_number_of_lost_packets = + accumulated_number_of_recoverd_lost_packets_; + feedback->tcp.receive_window = receive_window_; + return true; +} + +void TcpReceiver::RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived) { + if (revived) { + ++accumulated_number_of_recoverd_lost_packets_; + } +} + +} // namespace net diff --git a/chromium/net/quic/congestion_control/tcp_receiver.h b/chromium/net/quic/congestion_control/tcp_receiver.h new file mode 100644 index 00000000000..695ffbbac41 --- /dev/null +++ b/chromium/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(); + + // Start implementation of SendAlgorithmInterface. + virtual bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) OVERRIDE; + + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp, + bool revived) OVERRIDE; + + private: + // We need to keep track of FEC recovered packets. + int accumulated_number_of_recoverd_lost_packets_; + QuicByteCount receive_window_; + + DISALLOW_COPY_AND_ASSIGN(TcpReceiver); +}; + +} // namespace net +#endif // NET_QUIC_CONGESTION_CONTROL_TCP_RECEIVER_H_ diff --git a/chromium/net/quic/congestion_control/tcp_receiver_test.cc b/chromium/net/quic/congestion_control/tcp_receiver_test.cc new file mode 100644 index 00000000000..305ff75eb8b --- /dev/null +++ b/chromium/net/quic/congestion_control/tcp_receiver_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 "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, false); + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + EXPECT_EQ(kTCP, feedback.type); + EXPECT_EQ(256000u, feedback.tcp.receive_window); + EXPECT_EQ(0, feedback.tcp.accumulated_number_of_lost_packets); + receiver_->RecordIncomingPacket(1, 2, timestamp, true); + ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); + EXPECT_EQ(kTCP, feedback.type); + EXPECT_EQ(256000u, feedback.tcp.receive_window); + EXPECT_EQ(1, feedback.tcp.accumulated_number_of_lost_packets); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h new file mode 100644 index 00000000000..5dc12c8f9c9 --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h @@ -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. + +#ifndef NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "net/quic/crypto/quic_decrypter.h" + +#if defined(USE_OPENSSL) +#include "net/quic/crypto/scoped_evp_cipher_ctx.h" +#endif + +namespace net { + +namespace test { +class Aes128Gcm12DecrypterPeer; +} // namespace test + +// 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 QuicDecrypter { + public: + enum { + // Authentication tags are truncated to 96 bits. + kAuthTagSize = 12, + }; + + Aes128Gcm12Decrypter(); + virtual ~Aes128Gcm12Decrypter(); + + // Returns true if the underlying crypto library supports AES GCM. + static bool IsSupported(); + + // 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: + // The 128-bit AES key. + unsigned char key_[16]; + // The nonce prefix. + unsigned char nonce_prefix_[4]; + +#if defined(USE_OPENSSL) + ScopedEVPCipherCtx ctx_; +#endif +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_AES_128_GCM_12_DECRYPTER_H_ diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc new file mode 100644 index 00000000000..e3fc1b6f0ae --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc @@ -0,0 +1,387 @@ +// 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 <nss.h> +#include <pk11pub.h> +#include <secerr.h> + +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.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 { + +// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR +// and GCM types, so define them here. +#if !defined(CKM_AES_CTR) +#define CKM_AES_CTR 0x00001086 +#define CKM_AES_GCM 0x00001087 + +struct CK_AES_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +}; + +struct CK_GCM_PARAMS { + CK_BYTE_PTR pIv; + CK_ULONG ulIvLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +}; +#endif // CKM_AES_CTR + +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); + +// 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_; + } + + static CK_MECHANISM_TYPE aes_key_mechanism() { + return aes_key_mechanism_; + } + + 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"); + if (pk11_decrypt_func_ == NULL) { + aes_key_mechanism_ = CKM_AES_ECB; + } +#endif + } + + // |pk11_decrypt_func_| stores the runtime symbol resolution of PK11_Decrypt. + static PK11_DecryptFunction pk11_decrypt_func_; + + // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of + // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in + // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15. + static CK_MECHANISM_TYPE aes_key_mechanism_; +}; + +// static +PK11_DecryptFunction GcmSupportChecker::pk11_decrypt_func_ = NULL; + +// static +CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM; + +base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = + LAZY_INSTANCE_INITIALIZER; + +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; +const size_t kAESNonceSize = 12; + +// 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 != NULL) { + 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) { + DLOG(INFO) << "ulIvLen is not equal to 12"; + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + SECItem my_param = { siBuffer, NULL, 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) { + DLOG(INFO) << "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) { + DLOG(INFO) << "PK11_CipherOp failed"; + return SECFailure; + } + + PK11_Finalize(ctx.get()); + + if (output_len != sizeof(ghash_key)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "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) { + DLOG(INFO) << "PK11_CipherOp failed"; + return SECFailure; + } + if (output_len != sizeof(tag_mask)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "PK11_CipherOp failed"; + return SECFailure; + } + + PK11_Finalize(ctx.get()); + + if (static_cast<unsigned int>(output_len) != + enc_len - Aes128Gcm12Decrypter::kAuthTagSize) { + DLOG(INFO) << "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() { + ignore_result(g_gcm_support_checker.Get()); +} + +Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} + +// static +bool Aes128Gcm12Decrypter::IsSupported() { + // NSS 3.15 supports CKM_AES_GCM directly. + // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM. + // Versions earlier than NSS 3.14 are not supported. + return NSS_VersionCheck("3.14") != PR_FALSE; +} + +bool Aes128Gcm12Decrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), sizeof(key_)); + if (key.size() != sizeof(key_)) { + return false; + } + memcpy(key_, key.data(), key.size()); + return true; +} + +bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); + if (nonce_prefix.size() != kNoncePrefixSize) { + return false; + } + COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + uint8* output, + size_t* output_length) { + if (ciphertext.length() < kAuthTagSize || + nonce.size() != kNoncePrefixSize + 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() - kAuthTagSize; + + // Import key_ into NSS. + SECItem key_item; + key_item.type = siBuffer; + key_item.data = key_; + key_item.len = sizeof(key_); + PK11SlotInfo* slot = PK11_GetInternalSlot(); + // 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 aes_key(PK11_ImportSymKey( + slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap, + CKA_DECRYPT, &key_item, NULL)); + PK11_FreeSlot(slot); + slot = NULL; + if (!aes_key) { + DLOG(INFO) << "PK11_ImportSymKey failed"; + return false; + } + + CK_GCM_PARAMS gcm_params = {0}; + 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 = kAuthTagSize * 8; + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&gcm_params); + param.len = sizeof(gcm_params); + + unsigned int output_len; + // If an incorrect authentication tag causes a decryption failure, the NSS + // error is SEC_ERROR_BAD_DATA (-8190). + if (My_Decrypt(aes_key.get(), CKM_AES_GCM, ¶m, + output, &output_len, ciphertext.length(), + reinterpret_cast<const unsigned char*>(ciphertext.data()), + ciphertext.length()) != SECSuccess) { + DLOG(INFO) << "My_Decrypt failed: NSS error " << PORT_GetError(); + return false; + } + + if (output_len != plaintext_size) { + DLOG(INFO) << "Wrong output length"; + return false; + } + *output_length = output_len; + return true; +} + +QuicData* Aes128Gcm12Decrypter::DecryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { + if (ciphertext.length() < kAuthTagSize) { + return NULL; + } + size_t plaintext_size; + scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); + + uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; + COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); + memcpy(nonce, nonce_prefix_, kNoncePrefixSize); + memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), + associated_data, ciphertext, + reinterpret_cast<uint8*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } + return new QuicData(plaintext.release(), plaintext_size, true); +} + +StringPiece Aes128Gcm12Decrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + kNoncePrefixSize); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc new file mode 100644 index 00000000000..cbc7a8426d3 --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_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/aes_128_gcm_12_decrypter.h" + +#include <openssl/evp.h> + +#include "base/memory/scoped_ptr.h" + +using base::StringPiece; + +namespace net { + +namespace { + +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; +const size_t kAESNonceSize = 12; + +} // namespace + +Aes128Gcm12Decrypter::Aes128Gcm12Decrypter() {} + +Aes128Gcm12Decrypter::~Aes128Gcm12Decrypter() {} + +// static +bool Aes128Gcm12Decrypter::IsSupported() { return true; } + +bool Aes128Gcm12Decrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), sizeof(key_)); + if (key.size() != sizeof(key_)) { + return false; + } + memcpy(key_, key.data(), key.size()); + + // Set the cipher type and the key. + if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_, + NULL) == 0) { + return false; + } + + // Set the IV (nonce) length. + if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize, + NULL) == 0) { + return false; + } + + return true; +} + +bool Aes128Gcm12Decrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); + if (nonce_prefix.size() != kNoncePrefixSize) { + return false; + } + COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool Aes128Gcm12Decrypter::Decrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece ciphertext, + uint8* output, + size_t* output_length) { + if (ciphertext.length() < kAuthTagSize || + nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { + return false; + } + const size_t plaintext_size = ciphertext.length() - kAuthTagSize; + + // Set the IV (nonce). + if (EVP_DecryptInit_ex( + ctx_.get(), NULL, NULL, NULL, + reinterpret_cast<const uint8*>(nonce.data())) == 0) { + return false; + } + + // Set the authentication tag. + if (EVP_CIPHER_CTX_ctrl( + ctx_.get(), EVP_CTRL_GCM_SET_TAG, kAuthTagSize, + const_cast<char*>(ciphertext.data()) + plaintext_size) == 0) { + return false; + } + + // If we pass a NULL, zero-length associated data to OpenSSL then it breaks. + // Thus we only set non-empty associated data. + if (!associated_data.empty()) { + // Set the associated data. The second argument (output buffer) must be + // NULL. + int unused_len; + if (EVP_DecryptUpdate( + ctx_.get(), NULL, &unused_len, + reinterpret_cast<const uint8*>(associated_data.data()), + associated_data.size()) == 0) { + return false; + } + } + + int len; + if (EVP_DecryptUpdate( + ctx_.get(), output, &len, + reinterpret_cast<const uint8*>(ciphertext.data()), + plaintext_size) == 0) { + return false; + } + output += len; + + if (EVP_DecryptFinal_ex(ctx_.get(), output, &len) == 0) { + return false; + } + output += len; + + *output_length = plaintext_size; + + return true; +} + +QuicData* Aes128Gcm12Decrypter::DecryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece ciphertext) { + if (ciphertext.length() < kAuthTagSize) { + return NULL; + } + size_t plaintext_size; + scoped_ptr<char[]> plaintext(new char[ciphertext.length()]); + + uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; + COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); + memcpy(nonce, nonce_prefix_, kNoncePrefixSize); + memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + if (!Decrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), + associated_data, ciphertext, + reinterpret_cast<uint8*>(plaintext.get()), + &plaintext_size)) { + return NULL; + } + return new QuicData(plaintext.release(), plaintext_size, true); +} + +StringPiece Aes128Gcm12Decrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128Gcm12Decrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + kNoncePrefixSize); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc new file mode 100644 index 00000000000..cea6a6e5b6c --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc @@ -0,0 +1,386 @@ +// 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, +}; + +// Returns true if |ch| is a lowercase hexadecimal digit. +bool IsHexDigit(char ch) { + return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'); +} + +// Converts a lowercase hexadecimal digit to its integer value. +int HexDigitToInt(char ch) { + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } + return ch - 'a' + 10; +} + +// |in| is a string consisting of lowercase hexadecimal digits, where +// every two digits represent one byte. |out| is a buffer of size |max_len|. +// Converts |in| to bytes and stores the bytes in the |out| buffer. The +// number of bytes converted is returned in |*out_len|. Returns true on +// success, false on failure. +bool DecodeHexString(const char* in, + char* out, + size_t* out_len, + size_t max_len) { + if (!in) { + *out_len = (size_t)-1; + return true; + } + *out_len = 0; + while (*in != '\0') { + if (!IsHexDigit(*in) || !IsHexDigit(*(in + 1))) { + return false; + } + if (*out_len >= max_len) { + return false; + } + out[*out_len] = HexDigitToInt(*in) * 16 + HexDigitToInt(*(in + 1)); + (*out_len)++; + in += 2; + } + return true; +} + +} // 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 NULL; + } + return new QuicData(plaintext.release(), plaintext_size, true); +} + +TEST(Aes128Gcm12DecrypterTest, Decrypt) { + if (!Aes128Gcm12Decrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + char key[1024]; + size_t key_len; + char iv[1024]; + size_t iv_len; + char ct[1024]; + size_t ct_len; + char aad[1024]; + size_t aad_len; + char tag[1024]; + size_t tag_len; + char pt[1024]; + size_t pt_len; + + for (size_t i = 0; i < arraysize(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vector = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vector[j].key != NULL; j++) { + // Decode the test vector. + ASSERT_TRUE( + DecodeHexString(test_vector[j].key, key, &key_len, sizeof(key))); + ASSERT_TRUE(DecodeHexString(test_vector[j].iv, iv, &iv_len, sizeof(iv))); + ASSERT_TRUE(DecodeHexString(test_vector[j].ct, ct, &ct_len, sizeof(ct))); + ASSERT_TRUE( + DecodeHexString(test_vector[j].aad, aad, &aad_len, sizeof(aad))); + ASSERT_TRUE( + DecodeHexString(test_vector[j].tag, tag, &tag_len, sizeof(tag))); + ASSERT_TRUE(DecodeHexString(test_vector[j].pt, pt, &pt_len, sizeof(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_len * 8); + EXPECT_EQ(test_info.iv_len, iv_len * 8); + EXPECT_EQ(test_info.pt_len, ct_len * 8); + EXPECT_EQ(test_info.aad_len, aad_len * 8); + EXPECT_EQ(test_info.tag_len, tag_len * 8); + if (pt_len != static_cast<size_t>(-1)) { + EXPECT_EQ(test_info.pt_len, pt_len * 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_len); + tag_len = Aes128Gcm12Decrypter::kAuthTagSize; + + Aes128Gcm12Decrypter decrypter; + ASSERT_TRUE(decrypter.SetKey(StringPiece(key, key_len))); + string ciphertext(ct, ct_len); + ciphertext.append(tag, tag_len); + scoped_ptr<QuicData> decrypted(DecryptWithNonce( + &decrypter, StringPiece(iv, iv_len), + // OpenSSL fails if NULL is set as the AAD, as opposed to a + // zero-length, non-NULL pointer. + StringPiece(aad_len ? aad : NULL, aad_len), ciphertext)); + if (!decrypted.get()) { + EXPECT_EQ((size_t)-1, pt_len); + continue; + } + ASSERT_NE((size_t)-1, pt_len); + + ASSERT_EQ(pt_len, decrypted->length()); + test::CompareCharArraysWithHexError("plaintext", decrypted->data(), + pt_len, pt, pt_len); + } + } +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h new file mode 100644 index 00000000000..451f84df6f8 --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h @@ -0,0 +1,74 @@ +// 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 <string> + +#include "base/compiler_specific.h" +#include "net/quic/crypto/quic_encrypter.h" + +#if defined(USE_OPENSSL) +#include "net/quic/crypto/scoped_evp_cipher_ctx.h" +#endif + +namespace net { + +namespace test { +class Aes128Gcm12EncrypterPeer; +} // namespace test + +// 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 QuicEncrypter { + public: + enum { + // Authentication tags are truncated to 96 bits. + kAuthTagSize = 12, + }; + + Aes128Gcm12Encrypter(); + virtual ~Aes128Gcm12Encrypter(); + + // Returns true if the underlying crypto library supports AES GCM. + static bool IsSupported(); + + // 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: + // The 128-bit AES key. + unsigned char key_[16]; + // The nonce prefix. + unsigned char nonce_prefix_[4]; + // last_seq_num_ is the last sequence number observed. + QuicPacketSequenceNumber last_seq_num_; + +#if defined(USE_OPENSSL) + ScopedEVPCipherCtx ctx_; +#endif +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_ diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc new file mode 100644 index 00000000000..1cd3540c884 --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc @@ -0,0 +1,397 @@ +// 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 <nss.h> +#include <pk11pub.h> +#include <secerr.h> + +#include "base/lazy_instance.h" +#include "base/memory/scoped_ptr.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 { + +// The pkcs11t.h header in NSS versions older than 3.14 does not have the CTR +// and GCM types, so define them here. +#if !defined(CKM_AES_CTR) +#define CKM_AES_CTR 0x00001086 +#define CKM_AES_GCM 0x00001087 + +struct CK_AES_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +}; + +struct CK_GCM_PARAMS { + CK_BYTE_PTR pIv; + CK_ULONG ulIvLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +}; +#endif // CKM_AES_CTR + +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); + +// 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_; + } + + static CK_MECHANISM_TYPE aes_key_mechanism() { + return aes_key_mechanism_; + } + + 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"); + if (pk11_encrypt_func_ == NULL) { + aes_key_mechanism_ = CKM_AES_ECB; + } +#endif + } + + // |pk11_encrypt_func_| stores the runtime symbol resolution of PK11_Encrypt. + static PK11_EncryptFunction pk11_encrypt_func_; + + // The correct value for |aes_key_mechanism_| is CKM_AES_GCM, but because of + // NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=853285 (to be fixed in + // NSS 3.15), use CKM_AES_ECB for NSS versions older than 3.15. + static CK_MECHANISM_TYPE aes_key_mechanism_; +}; + +// static +PK11_EncryptFunction GcmSupportChecker::pk11_encrypt_func_ = NULL; + +// static +CK_MECHANISM_TYPE GcmSupportChecker::aes_key_mechanism_ = CKM_AES_GCM; + +base::LazyInstance<GcmSupportChecker>::Leaky g_gcm_support_checker = + LAZY_INSTANCE_INITIALIZER; + +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; +const size_t kAESNonceSize = 12; + +// 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 != NULL) { + 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)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "ulIvLen is not equal to 12"; + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + SECItem my_param = { siBuffer, NULL, 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) { + DLOG(INFO) << "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) { + DLOG(INFO) << "PK11_CipherOp failed"; + return SECFailure; + } + + PK11_Finalize(ctx.get()); + + if (output_len != sizeof(ghash_key)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "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) { + DLOG(INFO) << "PK11_CipherOp failed"; + return SECFailure; + } + if (output_len != sizeof(tag_mask)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "PK11_CipherOp failed"; + return SECFailure; + } + + PK11_Finalize(ctx.get()); + + if (static_cast<unsigned int>(output_len) != data_len) { + DLOG(INFO) << "Wrong output length"; + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if ((max_len - Aes128Gcm12Encrypter::kAuthTagSize) < + static_cast<unsigned int>(output_len)) { + DLOG(INFO) << "(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() : last_seq_num_(0) { + ignore_result(g_gcm_support_checker.Get()); +} + +Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} + +// static +bool Aes128Gcm12Encrypter::IsSupported() { + // NSS 3.15 supports CKM_AES_GCM directly. + // NSS 3.14 supports CKM_AES_CTR, which can be used to emulate CKM_AES_GCM. + // Versions earlier than NSS 3.14 are not supported. + return NSS_VersionCheck("3.14") != PR_FALSE; +} + +bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), sizeof(key_)); + if (key.size() != sizeof(key_)) { + return false; + } + memcpy(key_, key.data(), key.size()); + return true; +} + +bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); + if (nonce_prefix.size() != kNoncePrefixSize) { + return false; + } + COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { + if (nonce.size() != kNoncePrefixSize + 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 = sizeof(key_); + PK11SlotInfo* slot = PK11_GetInternalSlot(); + // 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 aes_key(PK11_ImportSymKey( + slot, GcmSupportChecker::aes_key_mechanism(), PK11_OriginUnwrap, + CKA_ENCRYPT, &key_item, NULL)); + PK11_FreeSlot(slot); + slot = NULL; + if (!aes_key) { + DLOG(INFO) << "PK11_ImportSymKey failed"; + return false; + } + + CK_GCM_PARAMS gcm_params = {0}; + 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 = kAuthTagSize * 8; + + SECItem param; + param.type = siBuffer; + param.data = reinterpret_cast<unsigned char*>(&gcm_params); + param.len = sizeof(gcm_params); + + unsigned int output_len; + if (My_Encrypt(aes_key.get(), CKM_AES_GCM, ¶m, + output, &output_len, ciphertext_size, + reinterpret_cast<const unsigned char*>(plaintext.data()), + plaintext.size()) != SECSuccess) { + DLOG(INFO) << "My_Encrypt failed"; + return false; + } + + if (output_len != ciphertext_size) { + DLOG(INFO) << "Wrong output length"; + return false; + } + + return true; +} + +QuicData* Aes128Gcm12Encrypter::EncryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (last_seq_num_ != 0 && sequence_number <= last_seq_num_) { + DLOG(FATAL) << "Sequence numbers regressed"; + return NULL; + } + last_seq_num_ = sequence_number; + + uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; + COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); + memcpy(nonce, nonce_prefix_, kNoncePrefixSize); + memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), + associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return NULL; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; } + +size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const { + return kNoncePrefixSize; +} + +size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - kAuthTagSize; +} + +// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its +// corresponding plaintext. +size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + kAuthTagSize; +} + +StringPiece Aes128Gcm12Encrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + kNoncePrefixSize); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc new file mode 100644 index 00000000000..79d0ec1a8a0 --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc @@ -0,0 +1,165 @@ +// 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> +#include <string.h> + +#include "base/memory/scoped_ptr.h" + +using base::StringPiece; + +namespace net { + +namespace { + +const size_t kKeySize = 16; +const size_t kNoncePrefixSize = 4; +const size_t kAESNonceSize = 12; + +} // namespace + +Aes128Gcm12Encrypter::Aes128Gcm12Encrypter() : last_seq_num_(0) {} + +Aes128Gcm12Encrypter::~Aes128Gcm12Encrypter() {} + +// static +bool Aes128Gcm12Encrypter::IsSupported() { return true; } + +bool Aes128Gcm12Encrypter::SetKey(StringPiece key) { + DCHECK_EQ(key.size(), sizeof(key_)); + if (key.size() != sizeof(key_)) { + return false; + } + memcpy(key_, key.data(), key.size()); + + // Set the cipher type and the key. + if (EVP_EncryptInit_ex(ctx_.get(), EVP_aes_128_gcm(), NULL, key_, + NULL) == 0) { + return false; + } + + // Set the IV (nonce) length. + if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_SET_IVLEN, kAESNonceSize, + NULL) == 0) { + return false; + } + + return true; +} + +bool Aes128Gcm12Encrypter::SetNoncePrefix(StringPiece nonce_prefix) { + DCHECK_EQ(nonce_prefix.size(), kNoncePrefixSize); + if (nonce_prefix.size() != kNoncePrefixSize) { + return false; + } + COMPILE_ASSERT(sizeof(nonce_prefix_) == kNoncePrefixSize, bad_nonce_length); + memcpy(nonce_prefix_, nonce_prefix.data(), nonce_prefix.size()); + return true; +} + +bool Aes128Gcm12Encrypter::Encrypt(StringPiece nonce, + StringPiece associated_data, + StringPiece plaintext, + unsigned char* output) { + if (nonce.size() != kNoncePrefixSize + sizeof(QuicPacketSequenceNumber)) { + return false; + } + + // Set the IV (nonce). + if (EVP_EncryptInit_ex( + ctx_.get(), NULL, NULL, NULL, + reinterpret_cast<const unsigned char*>(nonce.data())) == 0) { + return false; + } + + // If we pass a NULL, zero-length associated data to OpenSSL then it breaks. + // Thus we only set non-empty associated data. + if (!associated_data.empty()) { + // Set the associated data. The second argument (output buffer) must be + // NULL. + int unused_len; + if (EVP_EncryptUpdate( + ctx_.get(), NULL, &unused_len, + reinterpret_cast<const unsigned char*>(associated_data.data()), + associated_data.size()) == 0) { + return false; + } + } + + int len; + if (EVP_EncryptUpdate( + ctx_.get(), output, &len, + reinterpret_cast<const unsigned char*>(plaintext.data()), + plaintext.size()) == 0) { + return false; + } + output += len; + + if (EVP_EncryptFinal_ex(ctx_.get(), output, &len) == 0) { + return false; + } + output += len; + + if (EVP_CIPHER_CTX_ctrl(ctx_.get(), EVP_CTRL_GCM_GET_TAG, kAuthTagSize, + output) == 0) { + return false; + } + + return true; +} + +QuicData* Aes128Gcm12Encrypter::EncryptPacket( + QuicPacketSequenceNumber sequence_number, + StringPiece associated_data, + StringPiece plaintext) { + size_t ciphertext_size = GetCiphertextSize(plaintext.length()); + scoped_ptr<char[]> ciphertext(new char[ciphertext_size]); + + if (last_seq_num_ != 0 && sequence_number <= last_seq_num_) { + DLOG(FATAL) << "Sequence numbers regressed"; + return NULL; + } + last_seq_num_ = sequence_number; + + uint8 nonce[kNoncePrefixSize + sizeof(sequence_number)]; + COMPILE_ASSERT(sizeof(nonce) == kAESNonceSize, bad_sequence_number_size); + memcpy(nonce, nonce_prefix_, kNoncePrefixSize); + memcpy(nonce + kNoncePrefixSize, &sequence_number, sizeof(sequence_number)); + if (!Encrypt(StringPiece(reinterpret_cast<char*>(nonce), sizeof(nonce)), + associated_data, plaintext, + reinterpret_cast<unsigned char*>(ciphertext.get()))) { + return NULL; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +size_t Aes128Gcm12Encrypter::GetKeySize() const { return kKeySize; } + +size_t Aes128Gcm12Encrypter::GetNoncePrefixSize() const { + return kNoncePrefixSize; +} + +size_t Aes128Gcm12Encrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { + return ciphertext_size - kAuthTagSize; +} + +// An AEAD_AES_128_GCM_12 ciphertext is exactly 12 bytes longer than its +// corresponding plaintext. +size_t Aes128Gcm12Encrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + kAuthTagSize; +} + +StringPiece Aes128Gcm12Encrypter::GetKey() const { + return StringPiece(reinterpret_cast<const char*>(key_), sizeof(key_)); +} + +StringPiece Aes128Gcm12Encrypter::GetNoncePrefix() const { + return StringPiece(reinterpret_cast<const char*>(nonce_prefix_), + kNoncePrefixSize); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc new file mode 100644 index 00000000000..0c9928bc570 --- /dev/null +++ b/chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc @@ -0,0 +1,348 @@ +// 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, +}; + +// Returns true if |ch| is a lowercase hexadecimal digit. +bool IsHexDigit(char ch) { + return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'); +} + +// Converts a lowercase hexadecimal digit to its integer value. +int HexDigitToInt(char ch) { + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } + return ch - 'a' + 10; +} + +// |in| is a string consisting of lowercase hexadecimal digits, where +// every two digits represent one byte. |out| is a buffer of size |max_len|. +// Converts |in| to bytes and stores the bytes in the |out| buffer. The +// number of bytes converted is returned in |*out_len|. Returns true on +// success, false on failure. +bool DecodeHexString(const char* in, + char* out, + size_t* out_len, + size_t max_len) { + *out_len = 0; + while (*in != '\0') { + if (!IsHexDigit(*in) || !IsHexDigit(*(in + 1))) { + return false; + } + if (*out_len >= max_len) { + return false; + } + out[*out_len] = HexDigitToInt(*in) * 16 + HexDigitToInt(*(in + 1)); + (*out_len)++; + in += 2; + } + return true; +} + +} // 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 NULL; + } + + return new QuicData(ciphertext.release(), ciphertext_size, true); +} + +TEST(Aes128Gcm12EncrypterTest, Encrypt) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + char key[1024]; + size_t key_len; + char iv[1024]; + size_t iv_len; + char pt[1024]; + size_t pt_len; + char aad[1024]; + size_t aad_len; + char ct[1024]; + size_t ct_len; + char tag[1024]; + size_t tag_len; + + for (size_t i = 0; i < arraysize(test_group_array); i++) { + SCOPED_TRACE(i); + const TestVector* test_vector = test_group_array[i]; + const TestGroupInfo& test_info = test_group_info[i]; + for (size_t j = 0; test_vector[j].key != NULL; j++) { + // Decode the test vector. + ASSERT_TRUE( + DecodeHexString(test_vector[j].key, key, &key_len, sizeof(key))); + ASSERT_TRUE(DecodeHexString(test_vector[j].iv, iv, &iv_len, sizeof(iv))); + ASSERT_TRUE(DecodeHexString(test_vector[j].pt, pt, &pt_len, sizeof(pt))); + ASSERT_TRUE( + DecodeHexString(test_vector[j].aad, aad, &aad_len, sizeof(aad))); + ASSERT_TRUE(DecodeHexString(test_vector[j].ct, ct, &ct_len, sizeof(ct))); + ASSERT_TRUE( + DecodeHexString(test_vector[j].tag, tag, &tag_len, sizeof(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_len * 8); + EXPECT_EQ(test_info.iv_len, iv_len * 8); + EXPECT_EQ(test_info.pt_len, pt_len * 8); + EXPECT_EQ(test_info.aad_len, aad_len * 8); + EXPECT_EQ(test_info.pt_len, ct_len * 8); + EXPECT_EQ(test_info.tag_len, tag_len * 8); + + Aes128Gcm12Encrypter encrypter; + ASSERT_TRUE(encrypter.SetKey(StringPiece(key, key_len))); + scoped_ptr<QuicData> encrypted(EncryptWithNonce( + &encrypter, StringPiece(iv, iv_len), + // OpenSSL fails if NULL is set as the AAD, as opposed to a + // zero-length, non-NULL pointer. This deliberately tests that we + // handle this case. + StringPiece(aad_len ? aad : NULL, aad_len), StringPiece(pt, pt_len))); + 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_len); + tag_len = Aes128Gcm12Encrypter::kAuthTagSize; + + ASSERT_EQ(ct_len + tag_len, encrypted->length()); + test::CompareCharArraysWithHexError("ciphertext", encrypted->data(), + ct_len, ct, ct_len); + test::CompareCharArraysWithHexError( + "authentication tag", encrypted->data() + ct_len, tag_len, tag, + tag_len); + } + } +} + +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/chromium/net/quic/crypto/cert_compressor.cc b/chromium/net/quic/crypto/cert_compressor.cc new file mode 100644 index 00000000000..023701bff95 --- /dev/null +++ b/chromium/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_(NULL), 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_ = NULL; + } + + 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/chromium/net/quic/crypto/cert_compressor.h b/chromium/net/quic/crypto/cert_compressor.h new file mode 100644 index 00000000000..7b7e2f0eb05 --- /dev/null +++ b/chromium/net/quic/crypto/cert_compressor.h @@ -0,0 +1,55 @@ +// 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); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CERT_COMPRESSOR_H_ diff --git a/chromium/net/quic/crypto/cert_compressor_test.cc b/chromium/net/quic/crypto/cert_compressor_test.cc new file mode 100644 index 00000000000..5bfef8f56ea --- /dev/null +++ b/chromium/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(), NULL); + EXPECT_EQ("00", base::HexEncode(compressed.data(), compressed.size())); + + vector<string> chain2, cached_certs; + ASSERT_TRUE( + CertCompressor::DecompressChain(compressed, cached_certs, NULL, &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(), NULL); + 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, NULL, &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, NULL); + + 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, NULL, &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, NULL, &chain)); + + /* no terminator */ + const string no_terminator("01"); + EXPECT_FALSE(CertCompressor::DecompressChain( + base::HexEncode(no_terminator.data(), no_terminator.size()), + cached_certs, NULL, &chain)); + + /* hash truncated */ + const string hash_truncated("0200"); + EXPECT_FALSE(CertCompressor::DecompressChain( + base::HexEncode(hash_truncated.data(), hash_truncated.size()), + cached_certs, NULL, &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, NULL, &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, NULL, &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, NULL, &chain)); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/channel_id.cc b/chromium/net/quic/crypto/channel_id.cc new file mode 100644 index 00000000000..e707bf01cb3 --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/channel_id.h b/chromium/net/quic/crypto/channel_id.h new file mode 100644 index 00000000000..2d0c29de25a --- /dev/null +++ b/chromium/net/quic/crypto/channel_id.h @@ -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. + +#ifndef NET_QUIC_CRYPTO_CHANNEL_ID_H_ +#define NET_QUIC_CRYPTO_CHANNEL_ID_H_ + +#include <string> + +#include "base/strings/string_piece.h" +#include "net/base/net_export.h" + +namespace net { + +// ChannelIDSigner is an abstract interface that implements signing by +// ChannelID keys. +class NET_EXPORT_PRIVATE ChannelIDSigner { + public: + virtual ~ChannelIDSigner() { } + + // Sign signs |signed_data| using the ChannelID key for |hostname| and puts + // the serialized public key into |out_key| and the signature into + // |out_signature|. It returns true on success. + virtual bool Sign(const std::string& hostname, + base::StringPiece signed_data, + std::string* out_key, + std::string* out_signature) = 0; + + // GetKeyForHostname returns the ChannelID key that |ChannelIDSigner| will use + // for the given hostname. + virtual std::string GetKeyForHostname(const std::string& hostname) = 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); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CHANNEL_ID_H_ diff --git a/chromium/net/quic/crypto/channel_id_nss.cc b/chromium/net/quic/crypto/channel_id_nss.cc new file mode 100644 index 00000000000..d9fc7f855f3 --- /dev/null +++ b/chromium/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, NULL) == + SECSuccess; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/channel_id_openssl.cc b/chromium/net/quic/crypto/channel_id_openssl.cc new file mode 100644 index 00000000000..241acae4941 --- /dev/null +++ b/chromium/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" + +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> p256( + EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (p256.get() == NULL) { + return false; + } + + crypto::ScopedOpenSSL<BIGNUM, BN_free> 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()) == NULL || + BN_bin2bn(key_bytes + 32, 32, y.get()) == NULL || + BN_bin2bn(signature_bytes + 0, 32, sig.r) == NULL || + BN_bin2bn(signature_bytes + 32, 32, sig.s) == NULL) { + return false; + } + + crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> point( + EC_POINT_new(p256.get())); + if (point.get() == NULL || + !EC_POINT_set_affine_coordinates_GFp(p256.get(), point.get(), x.get(), + y.get(), NULL)) { + return false; + } + + crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ecdsa_key(EC_KEY_new()); + if (ecdsa_key.get() == NULL || + !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/chromium/net/quic/crypto/channel_id_test.cc b/chromium/net/quic/crypto/channel_id_test.cc new file mode 100644 index 00000000000..d29bfced059 --- /dev/null +++ b/chromium/net/quic/crypto/channel_id_test.cc @@ -0,0 +1,303 @@ +// 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 "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 } +}; + +// Returns true if |ch| is a lowercase hexadecimal digit. +bool IsHexDigit(char ch) { + return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'); +} + +// Converts a lowercase hexadecimal digit to its integer value. +int HexDigitToInt(char ch) { + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } + return ch - 'a' + 10; +} + +// |in| is a string consisting of lowercase hexadecimal digits, where +// every two digits represent one byte. |out| is a buffer of size |max_len|. +// Converts |in| to bytes and stores the bytes in the |out| buffer. The +// number of bytes converted is returned in |*out_len|. Returns true on +// success, false on failure. +bool DecodeHexString(const char* in, + char* out, + size_t* out_len, + size_t max_len) { + if (!in) { + *out_len = (size_t)-1; + return true; + } + *out_len = 0; + while (*in != '\0') { + if (!IsHexDigit(*in) || !IsHexDigit(*(in + 1))) { + return false; + } + if (*out_len >= max_len) { + return false; + } + out[*out_len] = HexDigitToInt(*in) * 16 + HexDigitToInt(*(in + 1)); + (*out_len)++; + in += 2; + } + return true; +} + +} // namespace + +// A known answer test for ChannelIDVerifier. +TEST(ChannelIDTest, VerifyKnownAnswerTest) { + char msg[1024]; + size_t msg_len; + char key[64]; + size_t qx_len; + size_t qy_len; + char signature[64]; + size_t r_len; + size_t s_len; + + for (size_t i = 0; test_vector[i].msg != NULL; i++) { + SCOPED_TRACE(i); + // Decode the test vector. + ASSERT_TRUE( + DecodeHexString(test_vector[i].msg, msg, &msg_len, sizeof(msg))); + ASSERT_TRUE(DecodeHexString(test_vector[i].qx, key, &qx_len, sizeof(key))); + ASSERT_TRUE(DecodeHexString(test_vector[i].qy, key + qx_len, &qy_len, + sizeof(key) - qx_len)); + ASSERT_TRUE(DecodeHexString(test_vector[i].r, signature, &r_len, + sizeof(signature))); + ASSERT_TRUE(DecodeHexString(test_vector[i].s, signature + r_len, &s_len, + sizeof(signature) - r_len)); + + // The test vector's lengths should look sane. + EXPECT_EQ(sizeof(key) / 2, qx_len); + EXPECT_EQ(sizeof(key) / 2, qy_len); + EXPECT_EQ(sizeof(signature) / 2, r_len); + EXPECT_EQ(sizeof(signature) / 2, s_len); + + EXPECT_EQ(test_vector[i].result, + ChannelIDVerifier::VerifyRaw( + StringPiece(key, sizeof(key)), + StringPiece(msg, msg_len), + StringPiece(signature, sizeof(signature)), + false)); + } +} + +TEST(ChannelIDTest, SignAndVerify) { + scoped_ptr<ChannelIDSigner> signer( + CryptoTestUtils::ChannelIDSignerForTesting()); + + const string signed_data = "signed data"; + const string hostname = "foo.example.com"; + string key, signature; + ASSERT_TRUE(signer->Sign(hostname, signed_data, &key, &signature)); + + EXPECT_EQ(key, signer->GetKeyForHostname(hostname)); + + 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/chromium/net/quic/crypto/common_cert_set.cc b/chromium/net/quic/crypto/common_cert_set.cc new file mode 100644 index 00000000000..f631cd6fc46 --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/common_cert_set.h b/chromium/net/quic/crypto/common_cert_set.h new file mode 100644 index 00000000000..a9e93045f57 --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/common_cert_set_0.c b/chromium/net/quic/crypto/common_cert_set_0.c new file mode 100644 index 00000000000..1133733cf7f --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/common_cert_set_1_50.inc b/chromium/net/quic/crypto/common_cert_set_1_50.inc new file mode 100644 index 00000000000..5ee471ba76b --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/common_cert_set_51_100.inc b/chromium/net/quic/crypto/common_cert_set_51_100.inc new file mode 100644 index 00000000000..3c79622493c --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/common_cert_set_test.cc b/chromium/net/quic/crypto/common_cert_set_test.cc new file mode 100644 index 00000000000..325d0b2c1f3 --- /dev/null +++ b/chromium/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_TRUE(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/chromium/net/quic/crypto/crypto_framer.cc b/chromium/net/quic/crypto/crypto_framer.cc new file mode 100644 index 00000000000..dd5d24f9ae2 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_framer.cc @@ -0,0 +1,292 @@ +// 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_handshake.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: + explicit OneShotVisitor(CryptoHandshakeMessage* out) + : out_(out), + error_(false) { + } + + virtual void OnError(CryptoFramer* framer) OVERRIDE { error_ = true; } + + virtual void OnHandshakeMessage( + const CryptoHandshakeMessage& message) OVERRIDE { + *out_ = message; + } + + bool error() const { return error_; } + + private: + CryptoHandshakeMessage* const out_; + bool error_; +}; + +} // namespace + +CryptoFramer::CryptoFramer() + : visitor_(NULL), + num_entries_(0), + values_len_(0) { + Clear(); +} + +CryptoFramer::~CryptoFramer() {} + +// static +CryptoHandshakeMessage* CryptoFramer::ParseMessage(StringPiece in) { + scoped_ptr<CryptoHandshakeMessage> msg(new CryptoHandshakeMessage); + OneShotVisitor visitor(msg.get()); + CryptoFramer framer; + + framer.set_visitor(&visitor); + if (!framer.ProcessInput(in) || visitor.error() || + framer.InputBytesRemaining()) { + return NULL; + } + + return msg.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 NULL; + } + + + QuicDataWriter writer(len); + if (!writer.WriteUInt32(message.tag())) { + DCHECK(false) << "Failed to write message tag."; + return NULL; + } + if (!writer.WriteUInt16(num_entries)) { + DCHECK(false) << "Failed to write size."; + return NULL; + } + if (!writer.WriteUInt16(0)) { + DCHECK(false) << "Failed to write padding."; + return NULL; + } + + 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 NULL; + } + + if (it->first > kPAD && need_pad_tag) { + need_pad_tag = false; + if (!WritePadTag(&writer, pad_length, &end_offset)) { + return NULL; + } + } + + if (!writer.WriteUInt32(it->first)) { + DCHECK(false) << "Failed to write tag."; + return NULL; + } + end_offset += it->second.length(); + if (!writer.WriteUInt32(end_offset)) { + DCHECK(false) << "Failed to write end offset."; + return NULL; + } + } + + if (need_pad_tag) { + if (!WritePadTag(&writer, pad_length, &end_offset)) { + return NULL; + } + } + + // 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 NULL; + } + } + + if (!writer.WriteBytes(it->second.data(), it->second.length())) { + DCHECK(false) << "Failed to write value."; + return NULL; + } + } + + if (need_pad_value) { + if (!writer.WriteRepeatedByte('-', pad_length)) { + DCHECK(false) << "Failed to write padding."; + return NULL; + } + } + + 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/chromium/net/quic/crypto/crypto_framer.h b/chromium/net/quic/crypto/crypto_framer.h new file mode 100644 index 00000000000..b070c66e277 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_framer.h @@ -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. + +#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/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/crypto/crypto_protocol.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 NULL 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 NULL 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); + + void set_error(QuicErrorCode error) { error_ = error; } + + // 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/chromium/net/quic/crypto/crypto_framer_test.cc b/chromium/net/quic/crypto/crypto_framer_test.cc new file mode 100644 index 00000000000..4486f595eee --- /dev/null +++ b/chromium/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() != NULL); + 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() != NULL); + + 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() != NULL); + + 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() == NULL); +} + +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() != NULL); + + 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() != NULL); + + 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/chromium/net/quic/crypto/crypto_handshake.cc b/chromium/net/quic/crypto/crypto_handshake.cc new file mode 100644 index 00000000000..d6a76f9f511 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_handshake.cc @@ -0,0 +1,897 @@ +// 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 <ctype.h> + +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "crypto/secure_hash.h" +#include "net/base/net_util.h" +#include "net/quic/crypto/cert_compressor.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_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +using base::StringPiece; +using base::StringPrintf; +using std::map; +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::Insert(QuicTagValueMap::const_iterator begin, + QuicTagValueMap::const_iterator end) { + tag_value_map_.insert(begin, end); +} + +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 = NULL; + *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 kKATO: + case kVERS: + // 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 kPDMD: + // 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 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; + } + + if (!done) { + // If there's no specific format for this tag, or the value is invalid, + // then just use hex. + ret += base::HexEncode(it->second.data(), it->second.size()); + } + ret += "\n"; + } + --indent; + ret += string(2 * indent, ' ') + ">"; + return ret; +} + +QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() + : version(0), + key_exchange(0), + aead(0) { +} + +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() + : version(0), + common_cert_sets(CommonCertSets::GetInstanceQUIC()) { +} + +QuicCryptoConfig::~QuicCryptoConfig() {} + +QuicCryptoClientConfig::QuicCryptoClientConfig() {} + +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() || !server_config_valid_) { + return false; + } + + const CryptoHandshakeMessage* scfg = GetServerConfig(); + if (!scfg) { + // Should be impossible short of cache corruption. + DCHECK(false); + return false; + } + + uint64 expiry_seconds; + if (scfg->GetUint64(kEXPY, &expiry_seconds) != QUIC_NO_ERROR || + now.ToUNIXSeconds() >= expiry_seconds) { + return false; + } + + return true; +} + +const CryptoHandshakeMessage* +QuicCryptoClientConfig::CachedState::GetServerConfig() const { + if (server_config_.empty()) { + return NULL; + } + + 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::SetProofValid() { + server_config_valid_ = true; +} + +void QuicCryptoClientConfig::CachedState::SetProofInvalid() { + server_config_valid_ = false; + ++generation_counter_; +} + +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::SetDefaults() { + // Version must be 0. + // TODO(agl): this version stuff is obsolete now. + version = QuicCryptoConfig::CONFIG_VERSION; + + // Key exchange methods. + kexs.resize(2); + kexs[0] = kC255; + kexs[1] = kP256; + + // Authenticated encryption algorithms. + aead.resize(1); + aead[0] = kAESG; +} + +QuicCryptoClientConfig::CachedState* QuicCryptoClientConfig::LookupOrCreate( + const string& server_hostname) { + map<string, CachedState*>::const_iterator it = + cached_states_.find(server_hostname); + if (it != cached_states_.end()) { + return it->second; + } + + CachedState* cached = new CachedState; + cached_states_.insert(make_pair(server_hostname, cached)); + return cached; +} + +void QuicCryptoClientConfig::FillInchoateClientHello( + const string& server_hostname, + 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_hostname)) { + out->SetStringPiece(kSNI, server_hostname); + } + out->SetValue(kVERS, version); + + if (!cached->source_address_token().empty()) { + out->SetStringPiece(kSourceAddressTokenTag, cached->source_address_token()); + } + + if (proof_verifier_.get()) { + // Don't request ECDSA proofs on platforms that do not support ECDSA + // certificates. + bool disableECDSA = false; +#if defined(OS_WIN) + if (base::win::GetVersion() < base::win::VERSION_VISTA) + disableECDSA = true; +#endif + if (disableECDSA) { + out->SetTaglist(kPDMD, kX59R, 0); + } else { + out->SetTaglist(kPDMD, kX509, 0); + } + + if (!cached->proof_valid()) { + // If we are expecting a certificate chain, double the size of the client + // hello so that the response from the server can be larger - hopefully + // including the whole certificate chain. + out->set_minimum_size(kClientHelloMinimumSize * 2); + } + } + + 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 string& server_hostname, + QuicGuid guid, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* out_params, + CryptoHandshakeMessage* out, + string* error_details) const { + DCHECK(error_details != NULL); + + FillInchoateClientHello(server_hostname, 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; + } + + size_t key_exchange_index; + if (!QuicUtils::FindMutualTag( + aead, their_aeads, num_their_aeads, QuicUtils::PEER_PRIORITY, + &out_params->aead, NULL) || + !QuicUtils::FindMutualTag( + kexs, their_key_exchanges, num_their_key_exchanges, + QuicUtils::PEER_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()); + + bool do_channel_id = false; + if (channel_id_signer_.get()) { + 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) { + for (size_t i = 0; i < num_their_proof_demands; i++) { + if (their_proof_demands[i] == kCHID) { + do_channel_id = true; + break; + } + } + } + } + + if (do_channel_id) { + // 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*>(&guid), sizeof(guid)); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(cached->server_config()); + + string key, signature; + if (!channel_id_signer_->Sign(server_hostname, hkdf_input, + &key, &signature)) { + *error_details = "Channel ID signature failed"; + return QUIC_INTERNAL_ERROR; + } + + cetv.SetStringPiece(kCIDK, key); + cetv.SetStringPiece(kCIDS, signature); + + CrypterPair crypters; + CryptoUtils::DeriveKeys(out_params->initial_premaster_secret, + out_params->aead, out_params->client_nonce, + out_params->server_nonce, hkdf_input, + CryptoUtils::CLIENT, &crypters); + + const QuicData& cetv_plaintext = cetv.GetSerialized(); + scoped_ptr<QuicData> cetv_ciphertext(crypters.encrypter->EncryptPacket( + 0 /* sequence number */, + StringPiece() /* associated data */, + cetv_plaintext.AsStringPiece())); + + out->SetStringPiece(kCETV, cetv_ciphertext->AsStringPiece()); + out->MarkDirty(); + + out->set_minimum_size(orig_min_size); + } + + out_params->hkdf_input_suffix.clear(); + out_params->hkdf_input_suffix.append(reinterpret_cast<char*>(&guid), + sizeof(guid)); + 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); + + 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); + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessRejection( + CachedState* cached, + const CryptoHandshakeMessage& rej, + QuicWallTime now, + QuicCryptoNegotiatedParameters* out_params, + string* error_details) { + DCHECK(error_details != NULL); + + if (rej.tag() != kREJ) { + *error_details = "Message is not REJ"; + return QUIC_CRYPTO_INTERNAL_ERROR; + } + + StringPiece scfg; + if (!rej.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 (rej.GetStringPiece(kSourceAddressTokenTag, &token)) { + cached->set_source_address_token(token); + } + + StringPiece nonce; + if (rej.GetStringPiece(kServerNonceTag, &nonce)) { + out_params->server_nonce = nonce.as_string(); + } + + StringPiece proof, cert_bytes; + if (rej.GetStringPiece(kPROF, &proof) && + rej.GetStringPiece(kCertificateTag, &cert_bytes)) { + vector<string> certs; + if (!CertCompressor::DecompressChain(cert_bytes, out_params->cached_certs, + common_cert_sets, &certs)) { + *error_details = "Certificate data invalid"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + cached->SetProof(certs, proof); + } + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicCryptoClientConfig::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + QuicGuid guid, + QuicCryptoNegotiatedParameters* out_params, + string* error_details) { + DCHECK(error_details != NULL); + + if (server_hello.tag() != kSHLO) { + *error_details = "Bad tag"; + return QUIC_INVALID_CRYPTO_MESSAGE_TYPE; + } + + // 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); + + 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); + + return QUIC_NO_ERROR; +} + +ProofVerifier* QuicCryptoClientConfig::proof_verifier() const { + return proof_verifier_.get(); +} + +void QuicCryptoClientConfig::SetProofVerifier(ProofVerifier* verifier) { + proof_verifier_.reset(verifier); +} + +ChannelIDSigner* QuicCryptoClientConfig::channel_id_signer() const { + return channel_id_signer_.get(); +} + +void QuicCryptoClientConfig::SetChannelIDSigner(ChannelIDSigner* signer) { + channel_id_signer_.reset(signer); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_handshake.h b/chromium/net/quic/crypto/crypto_handshake.h new file mode 100644 index 00000000000..fdc92a0fc38 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_handshake.h @@ -0,0 +1,399 @@ +// 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 <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/cert/cert_verify_result.h" +#include "net/cert/x509_certificate.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/crypto/proof_verifier.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class ChannelIDSigner; +class CommonCertSets; +class KeyExchange; +class ProofVerifier; +class QuicDecrypter; +class QuicEncrypter; +class QuicRandom; + +// 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_; } + + void Insert(QuicTagValueMap::const_iterator begin, + QuicTagValueMap::const_iterator end); + + // 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_; +}; + +// 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(); + + uint16 version; + QuicTag key_exchange; + QuicTag aead; + std::string initial_premaster_secret; + std::string forward_secure_premaster_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 GUID, + // 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; +}; + +// QuicCryptoConfig contains common configuration between clients and servers. +class NET_EXPORT_PRIVATE QuicCryptoConfig { + public: + enum { + // CONFIG_VERSION is the one (and, for the moment, only) version number that + // we implement. + CONFIG_VERSION = 0, + }; + + // 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(); + + // Protocol version + uint16 version; + // 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); +}; + +// 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 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; + + // GetServerConfig returns the parsed contents of |server_config|, or NULL + // 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); + + // 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); + + private: + std::string server_config_id_; // An opaque id from the server. + 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_; + }; + + QuicCryptoClientConfig(); + ~QuicCryptoClientConfig(); + + // Sets the members to reasonable, default values. + void SetDefaults(); + + // LookupOrCreate returns a CachedState for the given hostname. If no such + // CachedState currently exists, it will be created and cached. + CachedState* LookupOrCreate(const std::string& server_hostname); + + // FillInchoateClientHello sets |out| to be a CHLO message that elicits a + // source-address token or SCFG from a server. If |cached| is non-NULL, 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|. + void FillInchoateClientHello(const std::string& server_hostname, + 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 + // |server_hostname| in order to perform a handshake. This can be checked + // with the |IsComplete| member of |CachedState|. + // + // |clock| 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. + QuicErrorCode FillClientHello(const std::string& server_hostname, + QuicGuid guid, + const CachedState* cached, + QuicWallTime now, + QuicRandom* rand, + 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. + QuicErrorCode ProcessRejection(CachedState* cached, + const CryptoHandshakeMessage& rej, + QuicWallTime now, + QuicCryptoNegotiatedParameters* out_params, + std::string* error_details); + + // ProcessServerHello processes the message in |server_hello|, 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. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + QuicGuid guid, + 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); + + ChannelIDSigner* channel_id_signer() const; + + // SetChannelIDSigner sets a ChannelIDSigner that will be called when the + // server supports channel IDs to sign a message proving possession of the + // given ChannelID. This object takes ownership of |signer|. + void SetChannelIDSigner(ChannelIDSigner* signer); + + private: + // cached_states_ maps from the server hostname to the cached information + // about that server. + std::map<std::string, CachedState*> cached_states_; + + scoped_ptr<ProofVerifier> proof_verifier_; + scoped_ptr<ChannelIDSigner> channel_id_signer_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientConfig); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_HANDSHAKE_H_ diff --git a/chromium/net/quic/crypto/crypto_handshake_test.cc b/chromium/net/quic/crypto/crypto_handshake_test.cc new file mode 100644 index 00000000000..3ad50bb7f5a --- /dev/null +++ b/chromium/net/quic/crypto/crypto_handshake_test.cc @@ -0,0 +1,360 @@ +// 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 <stdarg.h> + +#include "base/stl_util.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.h" +#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/crypto_server_config_protobuf.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_time.h" +#include "net/quic/test_tools/mock_clock.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) {} + + string NewSourceAddressToken(IPEndPoint ip, + QuicRandom* rand, + QuicWallTime now) { + return server_config_->NewSourceAddressToken(ip, rand, now); + } + + bool ValidateSourceAddressToken(StringPiece srct, + IPEndPoint ip, + QuicWallTime now) { + return server_config_->ValidateSourceAddressToken(srct, ip, now); + } + + // CheckConfigs compares the state of the Configs in |server_config_| to the + // description given as arguments. The arguments are given as NULL-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(NULL); // 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, + // NULL); + 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_; +}; + +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, SourceAddressTokens) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + QuicRandom* rand = QuicRandom::GetInstance(); + QuicCryptoServerConfig server(QuicCryptoServerConfig::TESTING, rand); + IPAddressNumber ip; + CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); + IPEndPoint ip4 = IPEndPoint(ip, 1); + CHECK(ParseIPLiteralToNumber("2001:db8:0::42", &ip)); + IPEndPoint ip6 = IPEndPoint(ip, 2); + MockClock clock; + clock.AdvanceTime(QuicTime::Delta::FromSeconds(1000000)); + QuicCryptoServerConfigPeer peer(&server); + + QuicWallTime now = clock.WallNow(); + const QuicWallTime original_time = now; + + const string token4 = peer.NewSourceAddressToken(ip4, rand, now); + const string token6 = peer.NewSourceAddressToken(ip6, rand, now); + EXPECT_TRUE(peer.ValidateSourceAddressToken(token4, ip4, now)); + EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip6, now)); + EXPECT_TRUE(peer.ValidateSourceAddressToken(token6, ip6, now)); + + now = original_time.Add(QuicTime::Delta::FromSeconds(86400 * 7)); + EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, now)); + + now = original_time.Subtract(QuicTime::Delta::FromSeconds(3600 * 2)); + EXPECT_FALSE(peer.ValidateSourceAddressToken(token4, ip4, 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 NULL-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(NULL); // calls |config_.SetConfigs| with no protobufs. + // + // // Calls |config_.SetConfigs| with two protobufs: one for a Config with + // // a |primary_time| of 900, and another with a |primary_time| of 1000. + // CheckConfigs( + // "id1", 900, + // "id2", 1000, + // NULL); + // + // If the server config id starts with "INVALID" then the generated protobuf + // will be invalid. + void SetConfigs(const char* server_config_id1, ...) { + va_list ap; + va_start(ap, server_config_id1); + bool has_invalid = false; + + 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; + } + + int primary_time = va_arg(ap, int); + + QuicCryptoServerConfig::ConfigOptions options; + options.id = server_config_id; + QuicServerConfigProtobuf* protobuf( + QuicCryptoServerConfig::DefaultConfig(rand_, &clock_, options)); + protobuf->set_primary_time(primary_time); + if (string(server_config_id).find("INVALID") == 0) { + protobuf->clear_key(); + has_invalid = true; + } + protobufs.push_back(protobuf); + } + + ASSERT_EQ(!has_invalid, 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(NULL); +} + +TEST_F(CryptoServerConfigsTest, MakePrimaryFirst) { + // Make sure that "b" is primary even though "a" comes first. + SetConfigs("a", 1100, + "b", 900, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, MakePrimarySecond) { + // Make sure that a remains primary after b is added. + SetConfigs("a", 900, + "b", 1100, + NULL); + test_peer_.CheckConfigs( + "a", true, + "b", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, Delete) { + // Ensure that configs get deleted when removed. + SetConfigs("a", 800, + "b", 900, + "c", 1100, + NULL); + SetConfigs("b", 900, + "c", 1100, + NULL); + test_peer_.CheckConfigs( + "b", true, + "c", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, DontDeletePrimary) { + // Ensure that the primary config isn't deleted when removed. + SetConfigs("a", 800, + "b", 900, + "c", 1100, + NULL); + SetConfigs("a", 800, + "c", 1100, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); +} + +TEST_F(CryptoServerConfigsTest, AdvancePrimary) { + // Check that a new primary config is enabled at the right time. + SetConfigs("a", 900, + "b", 1100, + NULL); + test_peer_.SelectNewPrimaryConfig(1000); + test_peer_.CheckConfigs( + "a", true, + "b", false, + NULL); + test_peer_.SelectNewPrimaryConfig(1101); + test_peer_.CheckConfigs( + "a", false, + "b", true, + NULL); +} + +TEST_F(CryptoServerConfigsTest, InvalidConfigs) { + // Ensure that invalid configs don't change anything. + SetConfigs("a", 800, + "b", 900, + "c", 1100, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); + SetConfigs("a", 800, + "c", 1100, + "INVALID1", 1000, + NULL); + test_peer_.CheckConfigs( + "a", false, + "b", true, + "c", false, + NULL); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_protocol.h b/chromium/net/quic/crypto/crypto_protocol.h new file mode 100644 index 00000000000..586569a6a0a --- /dev/null +++ b/chromium/net/quic/crypto/crypto_protocol.h @@ -0,0 +1,131 @@ +// 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 <map> +#include <string> +#include <vector> + +#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) ((d << 24) + (c << 16) + (b << 8) + a) + +namespace net { + +typedef std::string ServerConfigID; +typedef std::map<QuicTag, std::string> QuicTagValueMap; + +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 + +// 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', 'L'); // null algorithm +const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12 + +// Congestion control feedback types +const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic +const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival + +// 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 kVERS = TAG('V', 'E', 'R', 'S'); // Version +const QuicTag kNONC = TAG('N', 'O', 'N', 'C'); // The client's nonce +const QuicTag kSSID = TAG('S', 'S', 'I', 'D'); // Session ID +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 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 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 + +// CETV tags +const QuicTag kCIDK = TAG('C', 'I', 'D', 'K'); // ChannelID key +const QuicTag kCIDS = TAG('C', 'I', 'D', 'S'); // ChannelID signature + +// 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. +const size_t kClientHelloMinimumSize = 512; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.cc b/chromium/net/quic/crypto/crypto_secret_boxer.cc new file mode 100644 index 00000000000..73562c638bc --- /dev/null +++ b/chromium/net/quic/crypto/crypto_secret_boxer.cc @@ -0,0 +1,90 @@ +// 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 "crypto/secure_hash.h" +#include "crypto/sha2.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. +static const size_t kBoxNonceSize = 16; + +// static +size_t CryptoSecretBoxer::GetKeySize() { return kKeySize; } + +void CryptoSecretBoxer::SetKey(StringPiece key) { + DCHECK_EQ(static_cast<size_t>(kKeySize), key.size()); + key_ = key.as_string(); +} + +// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Encrypter to Box the +// plaintext. This is temporary solution for tests to pass. +string CryptoSecretBoxer::Box(QuicRandom* rand, StringPiece plaintext) const { + string ret; + const size_t len = kBoxNonceSize + plaintext.size() + crypto::kSHA256Length; + ret.resize(len); + char* data = &ret[0]; + + // Generate nonce. + rand->RandBytes(data, kBoxNonceSize); + memcpy(data + kBoxNonceSize, plaintext.data(), plaintext.size()); + + // Compute sha256 for nonce + plaintext. + scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create( + crypto::SecureHash::SHA256)); + sha256->Update(data, kBoxNonceSize + plaintext.size()); + sha256->Finish(data + kBoxNonceSize + plaintext.size(), + crypto::kSHA256Length); + + return ret; +} + +// TODO(rtenneti): Delete sha256 based code. Use Aes128Gcm12Decrypter to Unbox +// the plaintext. This is temporary solution for tests to pass. +bool CryptoSecretBoxer::Unbox(StringPiece ciphertext, + string* out_storage, + StringPiece* out) const { + if (ciphertext.size() < kBoxNonceSize + crypto::kSHA256Length) { + return false; + } + + const size_t plaintext_len = + ciphertext.size() - kBoxNonceSize - crypto::kSHA256Length; + out_storage->resize(plaintext_len); + char* data = const_cast<char*>(out_storage->data()); + + // Copy plaintext from ciphertext. + if (plaintext_len != 0) { + memcpy(data, ciphertext.data() + kBoxNonceSize, plaintext_len); + } + + // Compute sha256 for nonce + plaintext. + scoped_ptr<crypto::SecureHash> sha256(crypto::SecureHash::Create( + crypto::SecureHash::SHA256)); + sha256->Update(ciphertext.data(), ciphertext.size() - crypto::kSHA256Length); + char sha256_bytes[crypto::kSHA256Length]; + sha256->Finish(sha256_bytes, sizeof(sha256_bytes)); + + // Verify sha256. + if (0 != memcmp(ciphertext.data() + ciphertext.size() - crypto::kSHA256Length, + sha256_bytes, crypto::kSHA256Length)) { + return false; + } + + out->set(data, plaintext_len); + return true; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_secret_boxer.h b/chromium/net/quic/crypto/crypto_secret_boxer.h new file mode 100644 index 00000000000..ba9baf2bb2a --- /dev/null +++ b/chromium/net/quic/crypto/crypto_secret_boxer.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_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: + // 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_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_SECRET_BOXER_H_ diff --git a/chromium/net/quic/crypto/crypto_secret_boxer_test.cc b/chromium/net/quic/crypto/crypto_secret_boxer_test.cc new file mode 100644 index 00000000000..427d052d011 --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/crypto_server_config.cc b/chromium/net/quic/crypto/crypto_server_config.cc new file mode 100644 index 00000000000..f270ddeb31a --- /dev/null +++ b/chromium/net/quic/crypto/crypto_server_config.cc @@ -0,0 +1,1070 @@ +// 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.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/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/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/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/quic_clock.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using crypto::SecureHash; +using std::map; +using std::string; +using std::vector; + +namespace net { + +// static +const char QuicCryptoServerConfig::TESTING[] = "secret string for testing"; + +QuicCryptoServerConfig::ConfigOptions::ConfigOptions() + : expiry_time(QuicWallTime::Zero()), + channel_id_enabled(false) { } + +QuicCryptoServerConfig::QuicCryptoServerConfig( + StringPiece source_address_token_secret, + QuicRandom* rand) + : replay_protection_(true), + configs_lock_(), + primary_config_(NULL), + next_config_promotion_time_(QuicWallTime::Zero()), + strike_register_lock_(), + server_nonce_strike_register_lock_(), + 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) { + crypto::HKDF hkdf(source_address_token_secret, StringPiece() /* no salt */, + "QUIC source address token key", + CryptoSecretBoxer::GetKeySize(), + 0 /* no fixed IV needed */); + source_address_token_boxer_.SetKey(hkdf.server_write_key()); + + // 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_ = NULL; +} + +// static +QuicServerConfigProtobuf* QuicCryptoServerConfig::DefaultConfig( + 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(); + + const string p256_private_key = P256KeyExchange::NewPrivateKey(); + scoped_ptr<P256KeyExchange> p256(P256KeyExchange::New(p256_private_key)); + StringPiece p256_public_value = p256->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()); + 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); + msg.SetTaglist(kKEXS, kC255, kP256, 0); + msg.SetTaglist(kAEAD, kAESG, 0); + msg.SetValue(kVERS, static_cast<uint16>(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()); + } + + if (options.id.empty()) { + char scid_bytes[16]; + rand->RandBytes(scid_bytes, sizeof(scid_bytes)); + msg.SetStringPiece(kSCID, StringPiece(scid_bytes, sizeof(scid_bytes))); + } else { + msg.SetStringPiece(kSCID, options.id); + } + + 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); + } + + 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); + 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 NULL; + } + + scoped_refptr<Config> config(ParseConfigProtobuf(protobuf)); + if (!config.get()) { + LOG(WARNING) << "Failed to parse server config message"; + return NULL; + } + + { + 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 NULL; + } + + configs_[config->id] = config; + SelectNewPrimaryConfig(now); + DCHECK(primary_config_.get()); + } + + return msg.release(); +} + +CryptoHandshakeMessage* QuicCryptoServerConfig::AddDefaultConfig( + QuicRandom* rand, + const QuicClock* clock, + const ConfigOptions& options) { + scoped_ptr<QuicServerConfigProtobuf> config( + DefaultConfig(rand, clock, options)); + return AddConfig(config.get(), clock->WallNow()); +} + +bool QuicCryptoServerConfig::SetConfigs( + const vector<QuicServerConfigProtobuf*>& protobufs, + const QuicWallTime now) { + vector<scoped_refptr<Config> > new_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; + } + new_configs.push_back(config); + } + + if (!ok) { + LOG(WARNING) << "Rejecting QUIC configs because of above errors"; + } else { + base::AutoLock locked(configs_lock_); + typedef ConfigMap::iterator ConfigMapIterator; + vector<ConfigMapIterator> to_delete; + + DCHECK_EQ(protobufs.size(), new_configs.size()); + + // First, look for any configs that have been removed. + for (ConfigMapIterator i = configs_.begin(); + i != configs_.end(); ++i) { + const scoped_refptr<Config> old_config = i->second; + bool found = false; + + for (vector<scoped_refptr<Config> >::const_iterator j = + new_configs.begin(); + j != new_configs.end(); ++j) { + if ((*j)->id == old_config->id) { + found = true; + break; + } + } + + if (!found) { + // We cannot remove the primary config. This has probably happened + // because our source of config information failed for a time and we're + // suddenly seeing a jump in time. No matter - we'll configure a new + // primary config and then we'll be able to delete it next time. + if (!old_config->is_primary) { + to_delete.push_back(i); + } + } + } + + for (vector<ConfigMapIterator>::const_iterator i = to_delete.begin(); + i != to_delete.end(); ++i) { + configs_.erase(*i); + } + + // Find any configs that need to be added. + for (vector<scoped_refptr<Config> >::const_iterator i = new_configs.begin(); + i != new_configs.end(); ++i) { + const scoped_refptr<Config> new_config = *i; + if (configs_.find(new_config->id) != configs_.end()) { + continue; + } + + configs_[new_config->id] = new_config; + } + + SelectNewPrimaryConfig(now); + } + + return ok; +} + +// 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; +}; + +QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + QuicVersion version, + QuicGuid guid, + const IPEndPoint& client_ip, + const QuicClock* clock, + QuicRandom* rand, + QuicCryptoNegotiatedParameters *params, + CryptoHandshakeMessage* out, + string* error_details) const { + DCHECK(error_details); + + 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); + } + + primary_config = primary_config_; + + 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. Otherwise we'll give it a copy of |primary_config_| + // to use. + requested_config = it->second; + } + } + } + + ClientHelloInfo info(client_ip, now); + QuicErrorCode error = EvaluateClientHello( + client_hello, primary_config->orbit, &info, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + out->Clear(); + + if (!info.valid_source_address_token || + !info.client_nonce_well_formed || + !info.unique || + !requested_config.get()) { + BuildRejection(version, primary_config.get(), client_hello, info, rand, + 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, NULL) || + !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(guid) + client_hello_serialized.length() + + requested_config->serialized.size()); + hkdf_suffix.append(reinterpret_cast<char*>(&guid), sizeof(guid)); + 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*>(&guid), sizeof(guid)); + hkdf_input.append(client_hello_serialized.data(), + client_hello_serialized.length()); + hkdf_input.append(requested_config->serialized); + + CrypterPair crypters; + CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, + info.client_nonce, info.server_nonce, hkdf_input, + CryptoUtils::SERVER, &crypters); + + 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); + + CryptoUtils::DeriveKeys(params->initial_premaster_secret, params->aead, + info.client_nonce, info.server_nonce, hkdf_input, + CryptoUtils::SERVER, ¶ms->initial_crypters); + + 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); + + 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); + + out->set_tag(kSHLO); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(client_ip, rand, info.now)); + out->SetStringPiece(kPUBS, forward_secure_public_value); + return QUIC_NO_ERROR; +} + +// 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) { + return a->primary_time.IsBefore(b->primary_time); +} + +void QuicCryptoServerConfig::SelectNewPrimaryConfig( + const QuicWallTime now) const { + vector<scoped_refptr<Config> > configs; + configs.reserve(configs_.size()); + scoped_refptr<Config> first_config = NULL; + + for (ConfigMap::const_iterator it = configs_.begin(); + it != configs_.end(); ++it) { + const scoped_refptr<Config> config(it->second); + if (!first_config.get()) { + first_config = config; + } + if (config->primary_time.IsZero()) { + continue; + } + configs.push_back(it->second); + } + + if (configs.size() == 0) { + // Tests don't set |primary_time_|. For that case we promote the first + // Config and leave it as primary forever. + if (!primary_config_.get() && first_config.get()) { + primary_config_ = first_config; + primary_config_->is_primary = true; + } + return; + } + + std::sort(configs.begin(), configs.end(), ConfigPrimaryTimeLessThan); + + for (size_t i = 0; i < configs.size(); ++i) { + const scoped_refptr<Config> config(configs[i]); + + if (!config->primary_time.IsAfter(now)) { + 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; + if (i == 0) { + // There was no previous Config, so this will have to be primary. + new_primary = config; + + // 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 { + new_primary = configs[i - 1]; + 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; + + return; + } + + // All config's primary times are in the past. We should make the most recent + // primary. + scoped_refptr<Config> new_primary = configs[configs.size() - 1]; + if (primary_config_.get()) { + primary_config_->is_primary = false; + } + primary_config_ = new_primary; + new_primary->is_primary = true; + next_config_promotion_time_ = QuicWallTime::Zero(); +} + +QuicErrorCode QuicCryptoServerConfig::EvaluateClientHello( + const CryptoHandshakeMessage& client_hello, + const uint8* orbit, + ClientHelloInfo* info, + string* error_details) const { + if (client_hello.size() < kClientHelloMinimumSize) { + *error_details = "Client hello too small"; + return QUIC_CRYPTO_INVALID_VALUE_LENGTH; + } + + StringPiece srct; + if (client_hello.GetStringPiece(kSourceAddressTokenTag, &srct) && + ValidateSourceAddressToken(srct, info->client_ip, info->now)) { + info->valid_source_address_token = true; + } + + if (client_hello.GetStringPiece(kSNI, &info->sni) && + !CryptoUtils::IsValidSNI(info->sni)) { + *error_details = "Invalid SNI name"; + return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; + } + + // The client nonce is used first to try and establish uniqueness. + bool unique_by_strike_register = false; + + if (client_hello.GetStringPiece(kNONC, &info->client_nonce) && + info->client_nonce.size() == kNonceSize) { + info->client_nonce_well_formed = true; + if (replay_protection_) { + base::AutoLock auto_lock(strike_register_lock_); + + if (strike_register_.get() == NULL) { + strike_register_.reset(new StrikeRegister( + strike_register_max_entries_, + static_cast<uint32>(info->now.ToUNIXSeconds()), + strike_register_window_secs_, + orbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP)); + } + + unique_by_strike_register = strike_register_->Insert( + reinterpret_cast<const uint8*>(info->client_nonce.data()), + static_cast<uint32>(info->now.ToUNIXSeconds())); + } + } + + client_hello.GetStringPiece(kServerNonceTag, &info->server_nonce); + + // If the client nonce didn't establish uniqueness then an echoed server + // nonce may. + bool unique_by_server_nonce = false; + if (replay_protection_ && + !unique_by_strike_register && + !info->server_nonce.empty()) { + unique_by_server_nonce = ValidateServerNonce(info->server_nonce, info->now); + } + + info->unique = !replay_protection_ || + unique_by_strike_register || + unique_by_server_nonce; + + return QUIC_NO_ERROR; +} + +void QuicCryptoServerConfig::BuildRejection( + QuicVersion version, + const scoped_refptr<Config>& config, + const CryptoHandshakeMessage& client_hello, + const ClientHelloInfo& info, + QuicRandom* rand, + CryptoHandshakeMessage* out) const { + out->set_tag(kREJ); + out->SetStringPiece(kSCFG, config->serialized); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(info.client_ip, rand, info.now)); + if (replay_protection_) { + out->SetStringPiece(kServerNonceTag, NewServerNonce(rand, info.now)); + } + + // The client may have requested a certificate chain. + const QuicTag* their_proof_demands; + size_t num_their_proof_demands; + + if (proof_source_.get() == NULL || + client_hello.GetTaglist(kPDMD, &their_proof_demands, + &num_their_proof_demands) != + QUIC_NO_ERROR) { + return; + } + + bool x509_supported = false, x509_ecdsa_supported = false; + for (size_t i = 0; i < num_their_proof_demands; i++) { + switch (their_proof_demands[i]) { + case kX509: + x509_supported = true; + 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(version, info.sni.as_string(), + config->serialized, x509_ecdsa_supported, + &certs, &signature)) { + return; + } + + StringPiece their_common_set_hashes; + StringPiece their_cached_cert_hashes; + client_hello.GetStringPiece(kCCS, &their_common_set_hashes); + client_hello.GetStringPiece(kCCRT, &their_cached_cert_hashes); + + const string compressed = CertCompressor::CompressChain( + *certs, their_common_set_hashes, their_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. + const size_t kREJOverheadBytes = 112; + // 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() - kREJOverheadBytes; + COMPILE_ASSERT(kClientHelloMinimumSize >= 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 NULL; + } + + scoped_refptr<Config> config(new Config); + config->serialized = protobuf->config(); + + if (protobuf->has_primary_time()) { + config->primary_time = + QuicWallTime::FromUNIXSeconds(protobuf->primary_time()); + } + + StringPiece scid; + if (!msg->GetStringPiece(kSCID, &scid)) { + LOG(WARNING) << "Server config message is missing SCID"; + return NULL; + } + 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 NULL; + } + 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 NULL; + } + + StringPiece orbit; + if (!msg->GetStringPiece(kORBT, &orbit)) { + LOG(WARNING) << "Server config message is missing OBIT"; + return NULL; + } + + if (orbit.size() != kOrbitSize) { + LOG(WARNING) << "Orbit value in server config is the wrong length." + " Got " << orbit.size() << " want " << kOrbitSize; + return NULL; + } + COMPILE_ASSERT(sizeof(config->orbit) == kOrbitSize, orbit_incorrect_size); + memcpy(config->orbit, orbit.data(), sizeof(config->orbit)); + + { + base::AutoLock locked(strike_register_lock_); + if (strike_register_.get()) { + const uint8* orbit = strike_register_->orbit(); + if (0 != memcmp(orbit, config->orbit, kOrbitSize)) { + LOG(WARNING) + << "Server config has different orbit than current config. " + "Switching orbits at run-time is not supported."; + return NULL; + } + } + } + + if (kexs_len != protobuf->key_size()) { + LOG(WARNING) << "Server config has " << kexs_len + << " key exchange methods configured, but " + << protobuf->key_size() << " private keys"; + return NULL; + } + + 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 NULL; + } + + 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 NULL; + } + break; + case kP256: + ka.reset(P256KeyExchange::New(private_key)); + if (!ka.get()) { + LOG(WARNING) << "Server config contained an invalid P-256" + " private key."; + return NULL; + } + break; + default: + LOG(WARNING) << "Server config message contains unknown key exchange " + "method: " << tag; + return NULL; + } + + 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 NULL; + } + } + + config->key_exchanges.push_back(ka.release()); + } + + if (msg->GetUint16(kVERS, &config->version) != QUIC_NO_ERROR) { + LOG(WARNING) << "Server config message is missing version"; + return NULL; + } + + if (config->version != QuicCryptoConfig::CONFIG_VERSION) { + LOG(WARNING) << "Server config specifies an unsupported version"; + return NULL; + } + + 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::set_replay_protection(bool on) { + replay_protection_ = on; +} + +void QuicCryptoServerConfig::set_strike_register_max_entries( + uint32 max_entries) { + base::AutoLock locker(strike_register_lock_); + DCHECK(!strike_register_.get()); + strike_register_max_entries_ = max_entries; +} + +void QuicCryptoServerConfig::set_strike_register_window_secs( + uint32 window_secs) { + base::AutoLock locker(strike_register_lock_); + DCHECK(!strike_register_.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; +} + +string QuicCryptoServerConfig::NewSourceAddressToken( + const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now) const { + SourceAddressToken source_address_token; + source_address_token.set_ip(ip.ToString()); + source_address_token.set_timestamp(now.ToUNIXSeconds()); + + return source_address_token_boxer_.Box( + rand, source_address_token.SerializeAsString()); +} + +bool QuicCryptoServerConfig::ValidateSourceAddressToken( + StringPiece token, + const IPEndPoint& ip, + QuicWallTime now) const { + string storage; + StringPiece plaintext; + if (!source_address_token_boxer_.Unbox(token, &storage, &plaintext)) { + return false; + } + + SourceAddressToken source_address_token; + if (!source_address_token.ParseFromArray(plaintext.data(), + plaintext.size())) { + return false; + } + + if (source_address_token.ip() != ip.ToString()) { + // It's for a different IP address. + return false; + } + + 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 false; + } + + if (now.IsAfter(timestamp) && + delta.ToSeconds() > source_address_token_lifetime_secs_) { + return false; + } + + return true; +} + +// 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))); +} + +bool QuicCryptoServerConfig::ValidateServerNonce(StringPiece token, + QuicWallTime now) const { + string storage; + StringPiece plaintext; + if (!server_nonce_boxer_.Unbox(token, &storage, &plaintext)) { + return false; + } + + // 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 false; + } + + 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); + + bool is_unique; + { + base::AutoLock auto_lock(server_nonce_strike_register_lock_); + if (server_nonce_strike_register_.get() == NULL) { + 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)); + } + is_unique = server_nonce_strike_register_->Insert( + server_nonce, static_cast<uint32>(now.ToUNIXSeconds())); + } + + return is_unique; +} + +QuicCryptoServerConfig::Config::Config() + : channel_id_enabled(false), + is_primary(false), + primary_time(QuicWallTime::Zero()) {} + +QuicCryptoServerConfig::Config::~Config() { STLDeleteElements(&key_exchanges); } + +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_server_config.h b/chromium/net/quic/crypto/crypto_server_config.h new file mode 100644 index 00000000000..364c200a149 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_server_config.h @@ -0,0 +1,364 @@ +// 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_H_ +#define NET_QUIC_CRYPTO_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_protocol.h" +#include "net/quic/crypto/crypto_secret_boxer.h" +#include "net/quic/quic_time.h" + +namespace net { + +class EphemeralKeySource; +class KeyExchange; +class ProofSource; +class QuicClock; +class QuicDecrypter; +class QuicEncrypter; +class QuicRandom; +class QuicServerConfigProtobuf; +class StrikeRegister; + +struct ClientHelloInfo; + +namespace test { +class QuicCryptoServerConfigPeer; +} // namespace test + +// 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; + }; + + // |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[]; + + // DefaultConfig generates a QuicServerConfigProtobuf protobuf suitable for + // using in tests. + static QuicServerConfigProtobuf* DefaultConfig( + 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); + + // 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. + // + // client_hello: the incoming client hello message. + // version: the QUIC version for the connection. TODO(wtc): Remove once + // QUIC_VERSION_7 and before are removed. + // guid: the GUID for the connection, which is used in key derivation. + // 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. + // 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 CryptoHandshakeMessage& client_hello, + QuicVersion version, + QuicGuid guid, + const IPEndPoint& client_ip, + const QuicClock* clock, + QuicRandom* rand, + QuicCryptoNegotiatedParameters* params, + CryptoHandshakeMessage* out, + std::string* error_details) 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); + + // 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_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); + + 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; + + private: + friend class base::RefCounted<Config>; + virtual ~Config(); + + DISALLOW_COPY_AND_ASSIGN(Config); + }; + + typedef std::map<ServerConfigID, scoped_refptr<Config> > ConfigMap; + + // 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|. + QuicErrorCode EvaluateClientHello( + const CryptoHandshakeMessage& client_hello, + const uint8* orbit, + ClientHelloInfo* info, + std::string* error_details) const; + + // BuildRejection sets |out| to be a REJ message in reply to |client_hello|. + void BuildRejection( + QuicVersion version, + const scoped_refptr<Config>& config, + const CryptoHandshakeMessage& client_hello, + const ClientHelloInfo& info, + QuicRandom* rand, + 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 NULL. + scoped_refptr<Config> ParseConfigProtobuf(QuicServerConfigProtobuf* protobuf); + + // NewSourceAddressToken returns a fresh source address token for the given + // IP address. + std::string NewSourceAddressToken(const IPEndPoint& ip, + QuicRandom* rand, + QuicWallTime now) const; + + // ValidateSourceAddressToken returns true 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|. + bool ValidateSourceAddressToken(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 true. + // Otherwise it returns false. + bool 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_ == NULL + // 2) primary_config_ != NULL -> 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_; + + mutable base::Lock strike_register_lock_; + // strike_register_ contains a data structure that keeps track of previously + // observed client nonces in order to prevent replay attacks. + mutable scoped_ptr<StrikeRegister> strike_register_; + + // source_address_token_boxer_ is used to protect the source-address tokens + // that are given to clients. + CryptoSecretBoxer 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. + 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_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_H_ diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.cc b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc new file mode 100644 index 00000000000..a3418e4228a --- /dev/null +++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.cc @@ -0,0 +1,20 @@ +// 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()) { +} + +QuicServerConfigProtobuf::~QuicServerConfigProtobuf() { + STLDeleteElements(&keys_); +} + +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_server_config_protobuf.h b/chromium/net/quic/crypto/crypto_server_config_protobuf.h new file mode 100644 index 00000000000..6340ae023c4 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_server_config_protobuf.h @@ -0,0 +1,101 @@ +// 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_ = config.as_string(); + } + + 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; + } + + 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_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_SERVER_CONFIG_PROTOBUF_H_ diff --git a/chromium/net/quic/crypto/crypto_server_test.cc b/chromium/net/quic/crypto/crypto_server_test.cc new file mode 100644 index 00000000000..6744d12e5e0 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_server_test.cc @@ -0,0 +1,256 @@ +// 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/strings/string_number_conversions.h" +#include "net/quic/crypto/crypto_server_config.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/test_tools/crypto_test_utils.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::StringPiece; +using std::string; + +namespace net { +namespace test { + +class CryptoServerTest : public ::testing::Test { + public: + CryptoServerTest() + : rand_(QuicRandom::GetInstance()), + config_(QuicCryptoServerConfig::TESTING, rand_), + addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? + ip_ : IPAddressNumber(), 1) { + config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); + } + + virtual void SetUp() { + scoped_ptr<CryptoHandshakeMessage> msg( + config_.AddDefaultConfig(rand_, &clock_, + QuicCryptoServerConfig::ConfigOptions())); + + 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(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + ShouldSucceed(client_hello); + // The message should be rejected because the source-address token is + // missing. + ASSERT_EQ(kREJ, out_.tag()); + + 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()); + } + + void ShouldSucceed(const CryptoHandshakeMessage& message) { + string error_details; + QuicErrorCode error = config_.ProcessClientHello( + message, QuicVersionMax(), 1 /* GUID */, addr_, + &clock_, rand_, ¶ms_, &out_, &error_details); + + ASSERT_EQ(error, QUIC_NO_ERROR) + << "Message failed with error " << error_details << ": " + << message.DebugString(); + } + + void ShouldFailMentioning(const char* error_substr, + const CryptoHandshakeMessage& message) { + string error_details; + QuicErrorCode error = config_.ProcessClientHello( + message, QuicVersionMax(), 1 /* GUID */, addr_, + &clock_, rand_, ¶ms_, &out_, &error_details); + + 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; + } + + protected: + QuicRandom* const rand_; + MockClock clock_; + QuicCryptoServerConfig config_; + QuicCryptoNegotiatedParameters params_; + CryptoHandshakeMessage out_; + IPAddressNumber ip_; + IPEndPoint addr_; + 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_; +}; + +TEST_F(CryptoServerTest, BadSNI) { + static const char* kBadSNIs[] = { + "", + "foo", + "#00", + "#ff00", + "127.0.0.1", + "ffee::1", + }; + + for (size_t i = 0; i < arraysize(kBadSNIs); i++) { + ShouldFailMentioning("SNI", InchoateClientHello( + "CHLO", + "SNI", kBadSNIs[i], + NULL)); + } +} + +// 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", + NULL)); + + 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()); +} + +TEST_F(CryptoServerTest, TooSmall) { + ShouldFailMentioning("too small", CryptoTestUtils::Message( + "CHLO", + NULL)); +} + +TEST_F(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], + NULL)); + } +} + +TEST_F(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], + NULL)); + } +} + +TEST_F(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(), + "$padding", static_cast<int>(kClientHelloMinimumSize), + NULL); + ShouldSucceed(msg); + // The message should be rejected because the strike-register is still + // quiescent. + ASSERT_EQ(kREJ, out_.tag()); + + config_.set_replay_protection(false); + + ShouldSucceed(msg); + // The message should be accepted now. + ASSERT_EQ(kSHLO, out_.tag()); + + ShouldSucceed(msg); + // The message should accepted twice when replay protection is off. + ASSERT_EQ(kSHLO, out_.tag()); +} + +class CryptoServerTestNoConfig : public CryptoServerTest { + public: + virtual void SetUp() { + // Deliberately don't add a config so that we can test this situation. + } +}; + +TEST_F(CryptoServerTestNoConfig, DontCrash) { + ShouldFailMentioning("No config", InchoateClientHello( + "CHLO", + NULL)); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_utils.cc b/chromium/net/quic/crypto/crypto_utils.cc new file mode 100644 index 00000000000..469e582268f --- /dev/null +++ b/chromium/net/quic/crypto/crypto_utils.cc @@ -0,0 +1,114 @@ +// 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::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_canon::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_canon::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 +void CryptoUtils::DeriveKeys(StringPiece premaster_secret, + QuicTag aead, + StringPiece client_nonce, + StringPiece server_nonce, + const string& hkdf_input, + Perspective perspective, + CrypterPair* out) { + out->encrypter.reset(QuicEncrypter::Create(aead)); + out->decrypter.reset(QuicDecrypter::Create(aead)); + size_t key_bytes = out->encrypter->GetKeySize(); + size_t nonce_prefix_bytes = out->encrypter->GetNoncePrefixSize(); + + 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); + if (perspective == SERVER) { + out->encrypter->SetKey(hkdf.server_write_key()); + out->encrypter->SetNoncePrefix(hkdf.server_write_iv()); + out->decrypter->SetKey(hkdf.client_write_key()); + out->decrypter->SetNoncePrefix(hkdf.client_write_iv()); + } else { + out->encrypter->SetKey(hkdf.client_write_key()); + out->encrypter->SetNoncePrefix(hkdf.client_write_iv()); + out->decrypter->SetKey(hkdf.server_write_key()); + out->decrypter->SetNoncePrefix(hkdf.server_write_iv()); + } +} + +} // namespace net diff --git a/chromium/net/quic/crypto/crypto_utils.h b/chromium/net/quic/crypto/crypto_utils.h new file mode 100644 index 00000000000..6dfce2a58e3 --- /dev/null +++ b/chromium/net/quic/crypto/crypto_utils.h @@ -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. +// +// 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 |out->encrypter| and |out->decrypter| 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. + static void DeriveKeys(base::StringPiece premaster_secret, + QuicTag aead, + base::StringPiece client_nonce, + base::StringPiece server_nonce, + const std::string& hkdf_input, + Perspective perspective, + CrypterPair* out); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_UTILS_H_ diff --git a/chromium/net/quic/crypto/crypto_utils_test.cc b/chromium/net/quic/crypto/crypto_utils_test.cc new file mode 100644 index 00000000000..17eb19250ee --- /dev/null +++ b/chromium/net/quic/crypto/crypto_utils_test.cc @@ -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. + +#include "net/quic/crypto/crypto_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)); + } +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/curve25519_key_exchange.cc b/chromium/net/quic/crypto/curve25519_key_exchange.cc new file mode 100644 index 00000000000..3b8880453a3 --- /dev/null +++ b/chromium/net/quic/crypto/curve25519_key_exchange.cc @@ -0,0 +1,86 @@ +// 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/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 NULL; + } + + 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/chromium/net/quic/crypto/curve25519_key_exchange.h b/chromium/net/quic/crypto/curve25519_key_exchange.h new file mode 100644 index 00000000000..ecc0880ce67 --- /dev/null +++ b/chromium/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, NULL 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/chromium/net/quic/crypto/curve25519_key_exchange_test.cc b/chromium/net/quic/crypto/curve25519_key_exchange_test.cc new file mode 100644 index 00000000000..93ef63010bd --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/ephemeral_key_source.h b/chromium/net/quic/crypto/ephemeral_key_source.h new file mode 100644 index 00000000000..2700be0fe1b --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/key_exchange.h b/chromium/net/quic/crypto/key_exchange.h new file mode 100644 index 00000000000..8690f0eff2c --- /dev/null +++ b/chromium/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/chromium/net/quic/crypto/null_decrypter.cc b/chromium/net/quic/crypto/null_decrypter.cc new file mode 100644 index 00000000000..7bda75fe80d --- /dev/null +++ b/chromium/net/quic/crypto/null_decrypter.cc @@ -0,0 +1,74 @@ +// 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 { + +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 (!reader.ReadUInt128(&hash)) { + return false; + } + + StringPiece plaintext = reader.ReadRemainingPayload(); + + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + + if (hash != QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length())) { + 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 (!reader.ReadUInt128(&hash)) { + return NULL; + } + + StringPiece plaintext = reader.ReadRemainingPayload(); + + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + + if (hash != QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length())) { + return NULL; + } + return new QuicData(plaintext.data(), plaintext.length()); +} + +StringPiece NullDecrypter::GetKey() const { return StringPiece(); } + +StringPiece NullDecrypter::GetNoncePrefix() const { return StringPiece(); } + +} // namespace net diff --git a/chromium/net/quic/crypto/null_decrypter.h b/chromium/net/quic/crypto/null_decrypter.h new file mode 100644 index 00000000000..01beb2d7110 --- /dev/null +++ b/chromium/net/quic/crypto/null_decrypter.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_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 { + +// 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: + 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; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ diff --git a/chromium/net/quic/crypto/null_decrypter_test.cc b/chromium/net/quic/crypto/null_decrypter_test.cc new file mode 100644 index 00000000000..e9b9647d202 --- /dev/null +++ b/chromium/net/quic/crypto/null_decrypter_test.cc @@ -0,0 +1,66 @@ +// 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 { + +TEST(NullDecrypterTest, Decrypt) { + unsigned char expected[] = { + // fnv hash + 0xa0, 0x6f, 0x44, 0x8a, + 0x44, 0xf8, 0x18, 0x3b, + 0x47, 0x91, 0xb2, 0x13, + 0x6b, 0x09, 0xbb, 0xae, + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted(decrypter.DecryptPacket( + 0, "hello world!", StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_TRUE(decrypted.get()); + EXPECT_EQ("goodbye!", decrypted->AsStringPiece()); +} + +TEST(NullDecrypterTest, BadHash) { + unsigned char expected[] = { + // fnv hash + 0x46, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted(decrypter.DecryptPacket( + 0, "hello world!", StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_FALSE(decrypted.get()); +} + +TEST(NullDecrypterTest, ShortInput) { + unsigned char expected[] = { + // fnv hash (truncated) + 0x46, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted(decrypter.DecryptPacket( + 0, "hello world!", StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_FALSE(decrypted.get()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/null_encrypter.cc b/chromium/net/quic/crypto/null_encrypter.cc new file mode 100644 index 00000000000..a0a680d9c71 --- /dev/null +++ b/chromium/net/quic/crypto/null_encrypter.cc @@ -0,0 +1,61 @@ +// 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 kHashSize = 16; // size of uint128 serialized + +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::SerializeUint128(hash, output); + memcpy(output + sizeof(hash), plaintext.data(), plaintext.size()); + return true; +} + +QuicData* NullEncrypter::EncryptPacket( + QuicPacketSequenceNumber /*sequence_number*/, + StringPiece associated_data, + StringPiece plaintext) { + const size_t len = plaintext.size() + sizeof(uint128); + 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 - kHashSize; +} + +size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const { + return plaintext_size + kHashSize; +} + +StringPiece NullEncrypter::GetKey() const { return StringPiece(); } + +StringPiece NullEncrypter::GetNoncePrefix() const { return StringPiece(); } + +} // namespace net diff --git a/chromium/net/quic/crypto/null_encrypter.h b/chromium/net/quic/crypto/null_encrypter.h new file mode 100644 index 00000000000..ed05e1faaa0 --- /dev/null +++ b/chromium/net/quic/crypto/null_encrypter.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_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: + 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; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ diff --git a/chromium/net/quic/crypto/null_encrypter_test.cc b/chromium/net/quic/crypto/null_encrypter_test.cc new file mode 100644 index 00000000000..4c00f918177 --- /dev/null +++ b/chromium/net/quic/crypto/null_encrypter_test.cc @@ -0,0 +1,48 @@ +// 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 { + +TEST(NullEncrypterTest, Encrypt) { + unsigned char expected[] = { + // fnv hash + 0xa0, 0x6f, 0x44, 0x8a, + 0x44, 0xf8, 0x18, 0x3b, + 0x47, 0x91, 0xb2, 0x13, + 0x6b, 0x09, 0xbb, 0xae, + // 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(NullEncrypterTest, GetMaxPlaintextSize) { + NullEncrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +} + +TEST(NullEncrypterTest, GetCiphertextSize) { + NullEncrypter encrypter; + EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/p256_key_exchange.h b/chromium/net/quic/crypto/p256_key_exchange.h new file mode 100644 index 00000000000..8145cc05249 --- /dev/null +++ b/chromium/net/quic/crypto/p256_key_exchange.h @@ -0,0 +1,80 @@ +// 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" +// Forward declaration for openssl/*.h +typedef struct ec_key_st EC_KEY; +extern "C" void EC_KEY_free(EC_KEY* key); +#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, NULL 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::ScopedOpenSSL<EC_KEY, EC_KEY_free> 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]; +}; + +} // namespace net +#endif // NET_QUIC_CRYPTO_P256_KEY_EXCHANGE_H_ + diff --git a/chromium/net/quic/crypto/p256_key_exchange_nss.cc b/chromium/net/quic/crypto/p256_key_exchange_nss.cc new file mode 100644 index 00000000000..dede5ba7b21 --- /dev/null +++ b/chromium/net/quic/crypto/p256_key_exchange_nss.cc @@ -0,0 +1,233 @@ +// 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) { + DLOG(INFO) << "Key pair is too small."; + return NULL; + } + + 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) { + DLOG(INFO) << "Key pair does not contain key material."; + return NULL; + } + + StringPiece private_piece(key.data(), size); + key.remove_prefix(size); + if (key.empty()) { + DLOG(INFO) << "Key pair does not contain public key."; + return NULL; + } + + 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()) { + DLOG(INFO) << "Can't decrypt private key."; + return NULL; + } + + // 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) { + DLOG(INFO) << "Key is invalid."; + return NULL; + } + + // 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) { + DLOG(INFO) << "Can't get P-256's OID."; + return NULL; + } + + 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) { + DLOG(INFO) << "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()) { + DLOG(INFO) << "Can't generate new key pair."; + return string(); + } + + vector<uint8> private_key; + if (!key_pair->ExportEncryptedPrivateKey(kExportPassword, + 1 /* iteration */, + &private_key)) { + DLOG(INFO) << "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)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "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, + NULL, + NULL, + CKM_ECDH1_DERIVE, /* mechanism */ + CKM_GENERIC_SECRET_KEY_GEN, /* target */ + CKA_DERIVE, + 0, + CKD_NULL, /* kdf */ + NULL, + NULL)); + + if (!premaster_secret.get()) { + DLOG(INFO) << "Can't derive ECDH shared key."; + return false; + } + + if (PK11_ExtractKeyValue(premaster_secret.get()) != SECSuccess) { + DLOG(INFO) << "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) { + DLOG(INFO) << "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/chromium/net/quic/crypto/p256_key_exchange_openssl.cc b/chromium/net/quic/crypto/p256_key_exchange_openssl.cc new file mode 100644 index 00000000000..6eaa981a86d --- /dev/null +++ b/chromium/net/quic/crypto/p256_key_exchange_openssl.cc @@ -0,0 +1,119 @@ +// 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()) { + DLOG(INFO) << "Private key is empty"; + return NULL; + } + + const uint8* keyp = reinterpret_cast<const uint8*>(key.data()); + crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> private_key( + d2i_ECPrivateKey(NULL, &keyp, key.size())); + if (!private_key.get() || !EC_KEY_check_key(private_key.get())) { + DLOG(INFO) << "Private key is invalid."; + return NULL; + } + + 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), NULL) != sizeof(public_key)) { + DLOG(INFO) << "Can't get public key."; + return NULL; + } + + return new P256KeyExchange(private_key.release(), public_key); +} + +// static +string P256KeyExchange::NewPrivateKey() { + crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> key( + EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (!key.get() || !EC_KEY_generate_key(key.get())) { + DLOG(INFO) << "Can't generate a new private key."; + return string(); + } + + int key_len = i2d_ECPrivateKey(key.get(), NULL); + if (key_len <= 0) { + DLOG(INFO) << "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)) { + DLOG(INFO) << "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) { + DLOG(INFO) << "Peer public value is invalid"; + return false; + } + + crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> 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(), NULL)) { + DLOG(INFO) << "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(), + NULL) != sizeof(result)) { + DLOG(INFO) << "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/chromium/net/quic/crypto/p256_key_exchange_test.cc b/chromium/net/quic/crypto/p256_key_exchange_test.cc new file mode 100644 index 00000000000..8ea59dacdbe --- /dev/null +++ b/chromium/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() != NULL); + ASSERT_TRUE(bob.get() != NULL); + + 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/chromium/net/quic/crypto/proof_source.h b/chromium/net/quic/crypto/proof_source.h new file mode 100644 index 00000000000..ba5087b0f61 --- /dev/null +++ b/chromium/net/quic/crypto/proof_source.h @@ -0,0 +1,62 @@ +// 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" +#include "net/quic/quic_protocol.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. + // + // |version| is the QUIC version for the connection. TODO(wtc): Remove once + // QUIC_VERSION_7 and before are removed. + // + // 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(QuicVersion version, + 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/chromium/net/quic/crypto/proof_source_chromium.cc b/chromium/net/quic/crypto/proof_source_chromium.cc new file mode 100644 index 00000000000..4c1fe263b62 --- /dev/null +++ b/chromium/net/quic/crypto/proof_source_chromium.cc @@ -0,0 +1,24 @@ +// 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(QuicVersion version, + 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/chromium/net/quic/crypto/proof_source_chromium.h b/chromium/net/quic/crypto/proof_source_chromium.h new file mode 100644 index 00000000000..2b93e2d9a4c --- /dev/null +++ b/chromium/net/quic/crypto/proof_source_chromium.h @@ -0,0 +1,39 @@ +// 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(QuicVersion version, + 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/chromium/net/quic/crypto/proof_test.cc b/chromium/net/quic/crypto/proof_test.cc new file mode 100644 index 00000000000..97b0dcb4ea9 --- /dev/null +++ b/chromium/net/quic/crypto/proof_test.cc @@ -0,0 +1,443 @@ +// 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 { + +class ProofTest : public ::testing::TestWithParam<QuicVersion> { + protected: + ProofTest() { + version_ = GetParam(); + } + + QuicVersion version_; +}; + +// Run all ProofTests with QUIC versions 7 and 8. +INSTANTIATE_TEST_CASE_P(ProofTests, + ProofTest, + ::testing::Values(QUIC_VERSION_7, QUIC_VERSION_8)); + +TEST_P(ProofTest, Verify) { + // TODO(rtenneti): Enable testing of ProofVerifier. +#if 0 + 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; + CertVerifyResult cert_verify_result; + + ASSERT_TRUE(source->GetProof(version_, hostname, server_config, + false /* no ECDSA */, &first_certs, + &first_signature)); + ASSERT_TRUE(source->GetProof(version_, 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); + + int rv; + TestCompletionCallback callback; + rv = verifier->VerifyProof(version_, hostname, server_config, *certs, + signature, &error_details, &cert_verify_result, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(OK, rv); + ASSERT_EQ("", error_details); + ASSERT_FALSE(IsCertStatusError(cert_verify_result.cert_status)); + + rv = verifier->VerifyProof(version_, "foo.com", server_config, *certs, + signature, &error_details, &cert_verify_result, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + rv = verifier->VerifyProof(version_, hostname, + server_config.substr(1, string::npos), + *certs, signature, &error_details, + &cert_verify_result, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + const string corrupt_signature = "1" + signature; + rv = verifier->VerifyProof(version_, hostname, server_config, *certs, + corrupt_signature, &error_details, + &cert_verify_result, callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); + + vector<string> wrong_certs; + for (size_t i = 1; i < certs->size(); i++) { + wrong_certs.push_back((*certs)[i]); + } + rv = verifier->VerifyProof(version_, "foo.com", server_config, wrong_certs, + signature, &error_details, &cert_verify_result, + callback.callback()); + rv = callback.GetResult(rv); + ASSERT_EQ(ERR_FAILED, rv); + ASSERT_NE("", error_details); +#endif // 0 +} + +// 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, + std::string* error_details) + : comp_callback_(comp_callback), + ok_(ok), + error_details_(error_details) {} + + virtual void Run(bool ok, + const std::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_; + std::string* const error_details_; +}; + +// RunVerification runs |verifier->VerifyProof| and asserts that the result +// matches |expected_ok|. +static void RunVerification(QuicVersion version, + ProofVerifier* verifier, + const std::string& hostname, + const std::string& server_config, + const vector<std::string>& certs, + const std::string& proof, + bool expected_ok) { + scoped_ptr<ProofVerifyDetails> details; + TestCompletionCallback comp_callback; + bool ok; + std::string error_details; + TestProofVerifierCallback* callback = + new TestProofVerifierCallback(&comp_callback, &ok, &error_details); + + ProofVerifier::Status status = verifier->VerifyProof( + version, hostname, server_config, certs, proof, &error_details, &details, + callback); + + switch (status) { + case ProofVerifier::FAILURE: + ASSERT_FALSE(expected_ok); + ASSERT_NE("", error_details); + return; + case ProofVerifier::SUCCESS: + ASSERT_TRUE(expected_ok); + ASSERT_EQ("", error_details); + return; + case ProofVerifier::PENDING: + comp_callback.WaitForResult(); + ASSERT_EQ(expected_ok, ok); + break; + } +} + +static string PEMCertFileToDER(const string& file_name) { + base::FilePath certs_dir = GetTestCertsDirectory(); + scoped_refptr<X509Certificate> cert = + ImportCertFromFile(certs_dir, file_name); + CHECK_NE(static_cast<X509Certificate*>(NULL), cert); + + string der_bytes; + CHECK(X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_bytes)); + return der_bytes; +} + +// A known answer test that allows us to test ProofVerifier without a working +// ProofSource. +TEST_P(ProofTest, VerifyRSAKnownAnswerTest) { + // These sample signatures were generated by running the Proof.Verify test + // and dumping the bytes of the |signature| output of ProofSource::GetProof(). + // sLen = special value -2 used by OpenSSL. + static const unsigned char signature_data_0[] = { + 0x4c, 0x68, 0x3c, 0xc2, 0x1f, 0x31, 0x73, 0xa5, 0x29, 0xd3, + 0x56, 0x75, 0xb1, 0xbf, 0xbd, 0x31, 0x17, 0xfb, 0x2e, 0x24, + 0xb3, 0xc4, 0x0d, 0xfa, 0x56, 0xb8, 0x65, 0x94, 0x12, 0x38, + 0x6e, 0xff, 0xb3, 0x10, 0x2e, 0xf8, 0x5c, 0xc1, 0x21, 0x9d, + 0x29, 0x0c, 0x3a, 0x0a, 0x1a, 0xbf, 0x6b, 0x1c, 0x63, 0x77, + 0xf7, 0x86, 0xd3, 0xa4, 0x36, 0xf2, 0xb1, 0x6f, 0xac, 0xc3, + 0x23, 0x8d, 0xda, 0xe6, 0xd5, 0x83, 0xba, 0xdf, 0x28, 0x3e, + 0x7f, 0x4e, 0x79, 0xfc, 0xba, 0xdb, 0xf7, 0xd0, 0x4b, 0xad, + 0x79, 0xd0, 0xeb, 0xcf, 0xfa, 0x6e, 0x84, 0x44, 0x7a, 0x26, + 0xb1, 0x29, 0xa3, 0x08, 0xa8, 0x63, 0xfd, 0xed, 0x85, 0xff, + 0x9a, 0xe6, 0x79, 0x8b, 0xb6, 0x81, 0x13, 0x2c, 0xde, 0xe2, + 0xd8, 0x31, 0x29, 0xa4, 0xe0, 0x1b, 0x75, 0x2d, 0x8a, 0xf8, + 0x27, 0x55, 0xbc, 0xc7, 0x3b, 0x1e, 0xc1, 0x42, + }; + static const unsigned char signature_data_1[] = { + 0xbb, 0xd1, 0x17, 0x43, 0xf3, 0x42, 0x16, 0xe9, 0xf9, 0x76, + 0xe6, 0xe3, 0xaa, 0x50, 0x47, 0x5f, 0x93, 0xb6, 0x7d, 0x35, + 0x03, 0x49, 0x0a, 0x07, 0x61, 0xd5, 0xf1, 0x9c, 0x6b, 0xaf, + 0xaa, 0xd7, 0x64, 0xe4, 0x0a, 0x0c, 0xab, 0x97, 0xfb, 0x4e, + 0x5c, 0x14, 0x08, 0xf6, 0xb9, 0xa9, 0x1d, 0xa9, 0xf8, 0x6d, + 0xb0, 0x2b, 0x2a, 0x0e, 0xc4, 0xd0, 0xd2, 0xe9, 0x96, 0x4f, + 0x44, 0x70, 0x90, 0x46, 0xb9, 0xd5, 0x89, 0x72, 0xb9, 0xa8, + 0xe4, 0xfb, 0x88, 0xbc, 0x69, 0x7f, 0xc9, 0xdc, 0x84, 0x87, + 0x18, 0x21, 0x9b, 0xde, 0x22, 0x33, 0xde, 0x16, 0x3f, 0xe6, + 0xfd, 0x27, 0x56, 0xd3, 0xa4, 0x97, 0x91, 0x65, 0x1a, 0xe7, + 0x5e, 0x80, 0x9a, 0xbf, 0xbf, 0x1a, 0x29, 0x8a, 0xbe, 0xa2, + 0x8c, 0x9c, 0x23, 0xf4, 0xcb, 0xba, 0x79, 0x31, 0x28, 0xab, + 0x77, 0x94, 0x92, 0xb2, 0xc2, 0x35, 0xb2, 0xfa, + }; + static const unsigned char signature_data_2[] = { + 0x7e, 0x17, 0x01, 0xcb, 0x76, 0x9e, 0x9f, 0xce, 0xeb, 0x66, + 0x3e, 0xaa, 0xc9, 0x36, 0x5b, 0x7e, 0x48, 0x25, 0x99, 0xf8, + 0x0d, 0xe1, 0xa8, 0x48, 0x93, 0x3c, 0xe8, 0x97, 0x2e, 0x98, + 0xd6, 0x73, 0x0f, 0xd0, 0x74, 0x9c, 0x17, 0xef, 0xee, 0xf8, + 0x0e, 0x2a, 0x27, 0x3f, 0xc6, 0x55, 0xc6, 0xb9, 0xfe, 0x17, + 0xcc, 0xeb, 0x5d, 0xa1, 0xdc, 0xbd, 0x64, 0xd9, 0x5e, 0xec, + 0x57, 0x9d, 0xc3, 0xdc, 0x11, 0xbf, 0x23, 0x02, 0x58, 0xc4, + 0xf1, 0x18, 0xc1, 0x6f, 0x3f, 0xef, 0x18, 0x4d, 0xa6, 0x1e, + 0xe8, 0x25, 0x32, 0x8f, 0x92, 0x1e, 0xad, 0xbc, 0xbe, 0xde, + 0x83, 0x2a, 0x92, 0xd5, 0x59, 0x6f, 0xe4, 0x95, 0x6f, 0xe6, + 0xb1, 0xf9, 0xaf, 0x3f, 0xdb, 0x69, 0x6f, 0xae, 0xa6, 0x36, + 0xd2, 0x50, 0x81, 0x78, 0x41, 0x13, 0x2c, 0x65, 0x9c, 0x9e, + 0xf4, 0xd2, 0xd5, 0x58, 0x5b, 0x8b, 0x87, 0xcf, + }; + static const unsigned char signature_data_4[] = { + 0x9e, 0xe6, 0x74, 0x3b, 0x8f, 0xb8, 0x66, 0x77, 0x57, 0x09, + 0x8a, 0x04, 0xe9, 0xf0, 0x7c, 0x91, 0xa9, 0x5c, 0xe9, 0xdf, + 0x12, 0x4d, 0x23, 0x82, 0x8c, 0x29, 0x72, 0x7f, 0xc2, 0x20, + 0xa7, 0xb3, 0xe5, 0xbc, 0xcf, 0x3c, 0x0d, 0x8f, 0xae, 0x46, + 0x6a, 0xb9, 0xee, 0x0c, 0xe1, 0x13, 0x21, 0xc0, 0x7e, 0x45, + 0x24, 0x24, 0x4b, 0x72, 0x43, 0x5e, 0xc4, 0x0d, 0xdf, 0x6c, + 0xd8, 0xaa, 0x35, 0x97, 0x05, 0x40, 0x76, 0xd3, 0x2c, 0xee, + 0x82, 0x16, 0x6a, 0x43, 0xf9, 0xa2, 0xd0, 0x41, 0x3c, 0xed, + 0x3f, 0x40, 0x10, 0x95, 0xc7, 0xa9, 0x1f, 0x04, 0xdb, 0xd5, + 0x98, 0x9f, 0xe2, 0xbf, 0x77, 0x3d, 0xc9, 0x9a, 0xaf, 0xf7, + 0xef, 0x63, 0x0b, 0x7d, 0xc8, 0x37, 0xda, 0x37, 0x23, 0x88, + 0x78, 0xc8, 0x8b, 0xf5, 0xb9, 0x36, 0x5d, 0x72, 0x1f, 0xfc, + 0x14, 0xff, 0xa7, 0x81, 0x27, 0x49, 0xae, 0xe1, + }; + static const unsigned char signature_data_5[] = { + 0x5e, 0xc2, 0xab, 0x6b, 0x16, 0xe6, 0x55, 0xf3, 0x16, 0x46, + 0x35, 0xdc, 0xcc, 0xde, 0xd0, 0xbd, 0x6c, 0x66, 0xb2, 0x3d, + 0xd3, 0x14, 0x78, 0xed, 0x47, 0x55, 0xfb, 0xdb, 0xe1, 0x7d, + 0xbf, 0x31, 0xf6, 0xf4, 0x10, 0x4c, 0x8d, 0x22, 0x17, 0xaa, + 0xe1, 0x85, 0xc7, 0x96, 0x4c, 0x42, 0xfb, 0xf4, 0x63, 0x53, + 0x8a, 0x79, 0x01, 0x63, 0x48, 0xa8, 0x3a, 0xbc, 0xc9, 0xd2, + 0xf5, 0xec, 0xe9, 0x09, 0x71, 0xaf, 0xce, 0x34, 0x56, 0xe5, + 0x00, 0xbe, 0xee, 0x3c, 0x1c, 0xc4, 0xa0, 0x07, 0xd5, 0x77, + 0xb8, 0x83, 0x57, 0x7d, 0x1a, 0xc9, 0xd0, 0xc0, 0x59, 0x9a, + 0x88, 0x19, 0x3f, 0xb9, 0xf0, 0x45, 0x37, 0xc3, 0x00, 0x8b, + 0xb3, 0x89, 0xf4, 0x89, 0x07, 0xa9, 0xc3, 0x26, 0xbf, 0x81, + 0xaf, 0x6b, 0x47, 0xbc, 0x16, 0x55, 0x37, 0x0a, 0xbe, 0x0e, + 0xc5, 0x75, 0x3f, 0x3d, 0x8e, 0xe8, 0x44, 0xe3, + }; + static const unsigned char signature_data_6[] = { + 0x8e, 0x5c, 0x78, 0x63, 0x74, 0x99, 0x2e, 0x96, 0xc0, 0x14, + 0x8d, 0xb5, 0x13, 0x74, 0xa3, 0xa4, 0xe0, 0x43, 0x3e, 0x85, + 0xba, 0x8f, 0x3c, 0x5e, 0x14, 0x64, 0x0e, 0x5e, 0xff, 0x89, + 0x88, 0x8a, 0x65, 0xe2, 0xa2, 0x79, 0xe4, 0xe9, 0x3a, 0x7f, + 0xf6, 0x9d, 0x3d, 0xe2, 0xb0, 0x8a, 0x35, 0x55, 0xed, 0x21, + 0xee, 0x20, 0xd8, 0x8a, 0x60, 0x47, 0xca, 0x52, 0x54, 0x91, + 0x99, 0x69, 0x8d, 0x16, 0x34, 0x69, 0xe1, 0x46, 0x56, 0x67, + 0x5f, 0x50, 0xf0, 0x94, 0xe7, 0x8b, 0xf2, 0x6a, 0x73, 0x0f, + 0x30, 0x30, 0xde, 0x59, 0xdc, 0xc7, 0xfe, 0xb6, 0x83, 0xe1, + 0x86, 0x1d, 0x88, 0xd3, 0x2f, 0x2f, 0x74, 0x68, 0xbd, 0x6c, + 0xd1, 0x46, 0x76, 0x06, 0xa9, 0xd4, 0x03, 0x3f, 0xda, 0x7d, + 0xa7, 0xff, 0x48, 0xe4, 0xb4, 0x42, 0x06, 0xac, 0x19, 0x12, + 0xe6, 0x05, 0xae, 0xbe, 0x29, 0x94, 0x8f, 0x99, + }; + + scoped_ptr<ProofVerifier> verifier( + CryptoTestUtils::ProofVerifierForTesting()); + + const string server_config = "server config bytes"; + const string hostname = "test.example.com"; + string error_details; + CertVerifyResult cert_verify_result; + + vector<string> certs(2); + certs[0] = PEMCertFileToDER("quic_test.example.com.crt"); + certs[1] = PEMCertFileToDER("quic_intermediate.crt"); + + // Signatures are nondeterministic, so we test multiple signatures on the + // same server_config. + vector<string> signatures(3); + if (version_ < QUIC_VERSION_8) { + 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)); + } else { + signatures[0].assign(reinterpret_cast<const char*>(signature_data_4), + sizeof(signature_data_4)); + signatures[1].assign(reinterpret_cast<const char*>(signature_data_5), + sizeof(signature_data_5)); + signatures[2].assign(reinterpret_cast<const char*>(signature_data_6), + sizeof(signature_data_6)); + } + + for (size_t i = 0; i < signatures.size(); i++) { + const string& signature = signatures[i]; + + RunVerification( + version_, verifier.get(), hostname, server_config, certs, signature, + true); + RunVerification( + version_, verifier.get(), "foo.com", server_config, certs, signature, + false); + RunVerification( + version_, verifier.get(), hostname, + server_config.substr(1, string::npos), certs, signature, false); + + const string corrupt_signature = "1" + signature; + RunVerification( + version_, 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(version_, verifier.get(), hostname, server_config, + wrong_certs, signature, false); + } +} + +// A known answer test that allows us to test ProofVerifier without a working +// ProofSource. +TEST_P(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, 0x20, 0x15, 0xb7, 0x9f, 0xe3, 0xd9, 0x7a, + 0x3c, 0x3b, 0x18, 0xb0, 0xdb, 0x60, 0x23, 0x56, 0xa0, 0x06, + 0x4e, 0x70, 0xa3, 0xf7, 0x4b, 0xe5, 0x0d, 0x69, 0xf0, 0x35, + 0x8c, 0xae, 0xb5, 0x54, 0x32, 0xe9, 0x02, 0x21, 0x00, 0xf7, + 0xe3, 0x06, 0x99, 0x16, 0x56, 0x7e, 0xab, 0x33, 0x53, 0x0d, + 0xde, 0xbe, 0xef, 0x6d, 0xb0, 0xc7, 0xa6, 0x63, 0xaf, 0x8d, + 0xab, 0x34, 0xa9, 0xc0, 0x63, 0x88, 0x47, 0x17, 0x4c, 0x4c, + 0x04, + }; + static const unsigned char signature_data_1[] = { + 0x30, 0x44, 0x02, 0x20, 0x69, 0x60, 0x55, 0xbb, 0x11, 0x93, + 0x6a, 0xdc, 0x9b, 0x61, 0x2c, 0x60, 0x19, 0xbc, 0x15, 0x55, + 0xcf, 0xf2, 0x8e, 0x2e, 0x27, 0x0b, 0x69, 0xef, 0x33, 0x25, + 0x1e, 0x5d, 0x8c, 0x00, 0x11, 0xef, 0x02, 0x20, 0x0c, 0x26, + 0xfe, 0x0b, 0x06, 0x8f, 0xe8, 0xe2, 0x02, 0x63, 0xe5, 0x43, + 0x0d, 0xc9, 0x80, 0x4d, 0xe9, 0x6f, 0x6e, 0x18, 0xdb, 0xb0, + 0x04, 0x2a, 0x45, 0x37, 0x1a, 0x60, 0x0e, 0xc6, 0xc4, 0x8f, + }; + static const unsigned char signature_data_2[] = { + 0x30, 0x45, 0x02, 0x21, 0x00, 0xd5, 0x43, 0x36, 0x60, 0x50, + 0xce, 0xe0, 0x00, 0x51, 0x02, 0x84, 0x95, 0x51, 0x47, 0xaf, + 0xe4, 0xf9, 0xe1, 0x23, 0xae, 0x21, 0xb4, 0x98, 0xd1, 0xa3, + 0x5f, 0x3b, 0xf3, 0x6a, 0x65, 0x44, 0x6b, 0x02, 0x20, 0x30, + 0x7e, 0xb4, 0xea, 0xf0, 0xda, 0xdb, 0xbd, 0x38, 0xb9, 0x7a, + 0x5d, 0x12, 0x04, 0x0e, 0xc2, 0xf0, 0xb1, 0x0e, 0x25, 0xf8, + 0x0a, 0x27, 0xa3, 0x16, 0x94, 0xac, 0x1e, 0xb8, 0x6e, 0x00, + 0x05, + }; + + scoped_ptr<ProofVerifier> verifier( + CryptoTestUtils::ProofVerifierForTesting()); + + const string server_config = "server config bytes"; + const string hostname = "test.example.com"; + string error_details; + CertVerifyResult cert_verify_result; + + vector<string> certs(2); + certs[0] = PEMCertFileToDER("quic_test_ecc.example.com.crt"); + certs[1] = PEMCertFileToDER("quic_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( + version_, verifier.get(), hostname, server_config, certs, signature, + true); + RunVerification( + version_, verifier.get(), "foo.com", server_config, certs, signature, + false); + RunVerification( + version_, 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( + version_, verifier.get(), hostname, server_config, certs, + corrupt_signature, false); + + // Prepending a "1" makes the DER invalid. + const string bad_der_signature1 = "1" + signature; + RunVerification( + version_, 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( + version_, verifier.get(), hostname, server_config, wrong_certs, + signature, false); + } +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/crypto/proof_verifier.cc b/chromium/net/quic/crypto/proof_verifier.cc new file mode 100644 index 00000000000..7bccba2bdb8 --- /dev/null +++ b/chromium/net/quic/crypto/proof_verifier.cc @@ -0,0 +1,15 @@ +// 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.h" + +namespace net { + +ProofVerifyDetails::~ProofVerifyDetails() {} + +ProofVerifierCallback::~ProofVerifierCallback() {} + +ProofVerifier::~ProofVerifier() {} + +} // namespace net diff --git a/chromium/net/quic/crypto/proof_verifier.h b/chromium/net/quic/crypto/proof_verifier.h new file mode 100644 index 00000000000..ecab113e694 --- /dev/null +++ b/chromium/net/quic/crypto/proof_verifier.h @@ -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. + +#ifndef NET_QUIC_CRYPTO_PROOF_VERIFIER_H_ +#define NET_QUIC_CRYPTO_PROOF_VERIFIER_H_ + +#include <string> +#include <vector> + +#include "net/base/completion_callback.h" +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class CertVerifyResult; + +// 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 CachedInfo for the origin in question. +class ProofVerifyDetails { + public: + virtual ~ProofVerifyDetails(); +}; + +// 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: + // Status enumerates the possible results of verifying a proof. + enum Status { + SUCCESS = 0, + FAILURE = 1, + // PENDING results from a verification which will occur asynchonously. When + // the verification is complete, |callback|'s |Run| method will be called. + PENDING = 2, + }; + + 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 + // SUCCESS. On failure, it returns ERROR and sets |*error_details| to a + // description of the problem. In either case it may set |*details|, which the + // caller takes ownership of. + // + // This function may also return PENDING, in which case the ProofVerifier + // will call back, on the original thread, via |callback| when complete. + // + // This function takes ownership of |callback|. It will be deleted even if + // the call returns immediately. + // + // The signature uses SHA-256 as the hash function and PSS padding in the + // case of RSA. + // + // |version| is the QUIC version for the connection. TODO(wtc): Remove once + // QUIC_VERSION_7 and before are removed. + virtual Status VerifyProof(QuicVersion version, + 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>* details, + ProofVerifierCallback* callback) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_PROOF_VERIFIER_H_ diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.cc b/chromium/net/quic/crypto/proof_verifier_chromium.cc new file mode 100644 index 00000000000..88653053f3e --- /dev/null +++ b/chromium/net/quic/crypto/proof_verifier_chromium.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/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/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/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 { + +ProofVerifierChromium::ProofVerifierChromium(CertVerifier* cert_verifier, + const BoundNetLog& net_log) + : cert_verifier_(cert_verifier), + next_state_(STATE_NONE), + net_log_(net_log) { +} + +ProofVerifierChromium::~ProofVerifierChromium() { + verifier_.reset(); +} + +ProofVerifierChromium::Status ProofVerifierChromium::VerifyProof( + QuicVersion version, + const string& hostname, + const string& server_config, + const vector<string>& certs, + const string& signature, + std::string* error_details, + scoped_ptr<ProofVerifyDetails>* details, + ProofVerifierCallback* callback) { + DCHECK(error_details); + DCHECK(details); + DCHECK(callback); + + callback_.reset(callback); + error_details->clear(); + + DCHECK_EQ(STATE_NONE, next_state_); + if (STATE_NONE != next_state_) { + *error_details = "Certificate is already set and VerifyProof has begun"; + DLOG(WARNING) << *error_details; + return 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; + details->reset(verify_details_.release()); + return 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; + details->reset(verify_details_.release()); + return FAILURE; + } + + // We call VerifySignature first to avoid copying of server_config and + // signature. + if (!VerifySignature(version, 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; + details->reset(verify_details_.release()); + return FAILURE; + } + + hostname_ = hostname; + + next_state_ = STATE_VERIFY_CERT; + switch (DoLoop(OK)) { + case OK: + details->reset(verify_details_.release()); + return SUCCESS; + case ERR_IO_PENDING: + return PENDING; + default: + *error_details = error_details_; + details->reset(verify_details_.release()); + return FAILURE; + } +} + +int ProofVerifierChromium::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::OnIOComplete(int result) { + int rv = DoLoop(result); + if (rv != ERR_IO_PENDING) { + scoped_ptr<ProofVerifyDetails> scoped_details(verify_details_.release()); + callback_->Run(rv == OK, error_details_, &scoped_details); + callback_.reset(); + } +} + +int ProofVerifierChromium::DoVerifyCert(int result) { + next_state_ = STATE_VERIFY_CERT_COMPLETE; + + int flags = 0; + verifier_.reset(new SingleRequestCertVerifier(cert_verifier_)); + return verifier_->Verify( + cert_.get(), + hostname_, + flags, + SSLConfigService::GetCRLSet().get(), + &verify_details_->cert_verify_result, + base::Bind(&ProofVerifierChromium::OnIOComplete, + base::Unretained(this)), + net_log_); +} + +int ProofVerifierChromium::DoVerifyCertComplete(int result) { + verifier_.reset(); + + if (result <= ERR_FAILED) { + error_details_ = StringPrintf("Failed to verify certificate chain: %s", + ErrorToString(result)); + DLOG(WARNING) << error_details_; + result = ERR_FAILED; + } + + // Exit DoLoop and return the result to the caller to VerifyProof. + DCHECK_EQ(STATE_NONE, next_state_); + return result; +} + +bool ProofVerifierChromium::VerifySignature(QuicVersion version, + 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. + unsigned int salt_len = + version >= QUIC_VERSION_8 ? hash_len : signature.size() - hash_len - 2; + + bool ok = verifier.VerifyInitRSAPSS( + hash_alg, mask_hash_alg, salt_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; + } + + DLOG(INFO) << "VerifyFinal success"; + return true; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.h b/chromium/net/quic/crypto/proof_verifier_chromium.h new file mode 100644 index 00000000000..8786e52e7dd --- /dev/null +++ b/chromium/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 <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/completion_callback.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 SingleRequestCertVerifier; + +// ProofVerifyDetailsChromium is the implementation-specific information that a +// ProofVerifierChromium returns about a certificate verification. +struct ProofVerifyDetailsChromium : public ProofVerifyDetails { + public: + CertVerifyResult cert_verify_result; +}; + +// ProofVerifierChromium implements the QUIC ProofVerifier interface. +// TODO(rtenneti): Add support for multiple requests for one ProofVerifier. +class NET_EXPORT_PRIVATE ProofVerifierChromium : public ProofVerifier { + public: + ProofVerifierChromium(CertVerifier* cert_verifier, + const BoundNetLog& net_log); + virtual ~ProofVerifierChromium(); + + // ProofVerifier interface + virtual Status VerifyProof(QuicVersion version, + 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>* details, + ProofVerifierCallback* callback) OVERRIDE; + + 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(QuicVersion version, + const std::string& signed_data, + const std::string& signature, + const std::string& cert); + + // |cert_verifier_| and |verifier_| are used for verifying certificates. + CertVerifier* const cert_verifier_; + scoped_ptr<SingleRequestCertVerifier> verifier_; + + // |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(ProofVerifierChromium); +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_PROOF_VERIFIER_CHROMIUM_H_ diff --git a/chromium/net/quic/crypto/quic_decrypter.cc b/chromium/net/quic/crypto/quic_decrypter.cc new file mode 100644 index 00000000000..fb19d4c0d70 --- /dev/null +++ b/chromium/net/quic/crypto/quic_decrypter.cc @@ -0,0 +1,25 @@ +// 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/null_decrypter.h" + +namespace net { + +// static +QuicDecrypter* QuicDecrypter::Create(QuicTag algorithm) { + switch (algorithm) { + case kAESG: + return new Aes128Gcm12Decrypter(); + case kNULL: + return new NullDecrypter(); + default: + LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return NULL; + } +} + +} // namespace net diff --git a/chromium/net/quic/crypto/quic_decrypter.h b/chromium/net/quic/crypto/quic_decrypter.h new file mode 100644 index 00000000000..124d98fea8d --- /dev/null +++ b/chromium/net/quic/crypto/quic_decrypter.h @@ -0,0 +1,72 @@ +// 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/crypto/crypto_protocol.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 NULL 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/chromium/net/quic/crypto/quic_encrypter.cc b/chromium/net/quic/crypto/quic_encrypter.cc new file mode 100644 index 00000000000..489da8ed2ec --- /dev/null +++ b/chromium/net/quic/crypto/quic_encrypter.cc @@ -0,0 +1,25 @@ +// 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/null_encrypter.h" + +namespace net { + +// static +QuicEncrypter* QuicEncrypter::Create(QuicTag algorithm) { + switch (algorithm) { + case kAESG: + return new Aes128Gcm12Encrypter(); + case kNULL: + return new NullEncrypter(); + default: + LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return NULL; + } +} + +} // namespace net diff --git a/chromium/net/quic/crypto/quic_encrypter.h b/chromium/net/quic/crypto/quic_encrypter.h new file mode 100644 index 00000000000..edddf3628b6 --- /dev/null +++ b/chromium/net/quic/crypto/quic_encrypter.h @@ -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. + +#ifndef NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ + +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.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 NULL 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/chromium/net/quic/crypto/quic_random.cc b/chromium/net/quic/crypto/quic_random.cc new file mode 100644 index 00000000000..c96f01a9139 --- /dev/null +++ b/chromium/net/quic/crypto/quic_random.cc @@ -0,0 +1,66 @@ +// 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 bool RandBool() 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; +} + +bool DefaultRandom::RandBool() { + char value; + RandBytes(&value, sizeof(value)); + return (value & 1) == 1; +} + +void DefaultRandom::Reseed(const void* additional_entropy, size_t entropy_len) { + // No such function exists in crypto/random.h. +} + +DefaultRandom::DefaultRandom() { +} + +} // namespace + +// static +QuicRandom* QuicRandom::GetInstance() { return DefaultRandom::GetInstance(); } + +} // namespace net diff --git a/chromium/net/quic/crypto/quic_random.h b/chromium/net/quic/crypto/quic_random.h new file mode 100644 index 00000000000..68640c1695d --- /dev/null +++ b/chromium/net/quic/crypto/quic_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_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; + + // Returns a random boolean value. + virtual bool RandBool() = 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/chromium/net/quic/crypto/quic_random_test.cc b/chromium/net/quic/crypto/quic_random_test.cc new file mode 100644 index 00000000000..425089a10e5 --- /dev/null +++ b/chromium/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_TRUE(memcmp(buf1, buf2, sizeof(buf1)) == 0); + + QuicRandom* rng = QuicRandom::GetInstance(); + rng->RandBytes(buf1, sizeof(buf1)); + EXPECT_FALSE(memcmp(buf1, buf2, sizeof(buf1)) == 0); +} + +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/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc new file mode 100644 index 00000000000..b904f870fdd --- /dev/null +++ b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc @@ -0,0 +1,22 @@ +// 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/scoped_evp_cipher_ctx.h" + +#include <openssl/evp.h> + +namespace net { + +ScopedEVPCipherCtx::ScopedEVPCipherCtx() + : ctx_(EVP_CIPHER_CTX_new()) { } + +ScopedEVPCipherCtx::~ScopedEVPCipherCtx() { + EVP_CIPHER_CTX_free(ctx_); +} + +EVP_CIPHER_CTX* ScopedEVPCipherCtx::get() const { + return ctx_; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h new file mode 100644 index 00000000000..ec0fd51b92a --- /dev/null +++ b/chromium/net/quic/crypto/scoped_evp_cipher_ctx.h @@ -0,0 +1,30 @@ +// 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_SCOPED_EVP_CIPHER_CTX_H_ +#define NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_ + +typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; + +namespace net { + +// TODO(wtc): this is the same as the ScopedCipherCTX class defined in +// crypto/encryptor_openssl.cc. Eliminate the duplicate code. +// crypto::ScopedOpenSSL is not suitable for EVP_CIPHER_CTX because +// there are no EVP_CIPHER_CTX_create and EVP_CIPHER_CTX_destroy +// functions. +class ScopedEVPCipherCtx { + public: + ScopedEVPCipherCtx(); + ~ScopedEVPCipherCtx(); + + EVP_CIPHER_CTX* get() const; + + private: + EVP_CIPHER_CTX* const ctx_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_SCOPED_EVP_CIPHER_CTX_H_ diff --git a/chromium/net/quic/crypto/source_address_token.cc b/chromium/net/quic/crypto/source_address_token.cc new file mode 100644 index 00000000000..d15afebf2a7 --- /dev/null +++ b/chromium/net/quic/crypto/source_address_token.cc @@ -0,0 +1,46 @@ +// 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 { + +SourceAddressToken::SourceAddressToken() { +} + +SourceAddressToken::~SourceAddressToken() { +} + +string SourceAddressToken::SerializeAsString() const { + return ip_ + " " + base::Int64ToString(timestamp_); +} + +bool SourceAddressToken::ParseFromArray(const char* plaintext, + size_t plaintext_length) { + string data(plaintext, plaintext_length); + vector<string> results; + base::SplitString(data, ' ', &results); + if (results.size() < 2) { + return false; + } + + int64 timestamp; + if (!base::StringToInt64(results[1], ×tamp)) { + return false; + } + + ip_ = results[0]; + timestamp_ = timestamp; + return true; +} + +} // namespace net diff --git a/chromium/net/quic/crypto/source_address_token.h b/chromium/net/quic/crypto/source_address_token.h new file mode 100644 index 00000000000..8c509651e28 --- /dev/null +++ b/chromium/net/quic/crypto/source_address_token.h @@ -0,0 +1,48 @@ +// 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" + +namespace net { + +// TODO(rtenneti): sync with server more rationally. +class SourceAddressToken { + public: + SourceAddressToken(); + ~SourceAddressToken(); + + std::string SerializeAsString() const; + + bool ParseFromArray(const char* plaintext, size_t plaintext_length); + + std::string ip() const { + return ip_; + } + + int64 timestamp() const { + return timestamp_; + } + + void set_ip(base::StringPiece ip) { + ip_ = ip.as_string(); + } + + void set_timestamp(int64 timestamp) { + timestamp_ = timestamp; + } + + private: + std::string ip_; + int64 timestamp_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_SOURCE_ADDRESS_TOKEN_H_ diff --git a/chromium/net/quic/crypto/strike_register.cc b/chromium/net/quic/crypto/strike_register.cc new file mode 100644 index 00000000000..97aca184cd0 --- /dev/null +++ b/chromium/net/quic/crypto/strike_register.cc @@ -0,0 +1,465 @@ +// 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 "base/logging.h" + +using std::pair; +using std::set; +using std::vector; + +namespace net { + +// static +const uint32 StrikeRegister::kExternalNodeSize = 24; +// static +const uint32 StrikeRegister::kNil = (1 << 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] &= 0xffffff00; + data_[0] |= critbyte; + } + + void SetOtherBits(uint8 otherbits) { + data_[1] &= 0xffffff00; + data_[1] |= 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 |creation_time_external_|. This allows us +// to consider times that are before |creation_time_external_|. +static const uint32 kCreationTimeFromInternalEpoch = 63115200.0; // 2 years. + +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), + // 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. + creation_time_external_(current_time), + internal_epoch_(current_time > kCreationTimeFromInternalEpoch + ? current_time - kCreationTimeFromInternalEpoch + : 0), + horizon_(ExternalTimeToInternal(current_time) + window_secs), + horizon_valid_(startup == DENY_REQUESTS_AT_STARTUP) { + memcpy(orbit_, orbit, sizeof(orbit_)); + + // TODO(rtenneti): Remove the following check, Added the following to silence + // "is not used" error. + CHECK_GE(creation_time_external_, 0u); + + // 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. + 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; +} + +bool StrikeRegister::Insert(const uint8 nonce[32], + const uint32 current_time_external) { + 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 false; + } + const uint32 nonce_time = ExternalTimeToInternal(TimeFromBytes(nonce)); + // We have dropped one or more nonces with a time value of |horizon_|, so + // we have to reject anything with a timestamp less than or equal to that. + if (horizon_valid_ && nonce_time <= horizon_) { + return false; + } + + // 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 false; + } + + // We strip the orbit out of the nonce. + uint8 value[24]; + memcpy(value, &nonce_time, 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; + return true; + } + + 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 false; + } + + // 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); + + return true; +} + +const uint8* StrikeRegister::orbit() const { + return orbit_; +} + +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]); +} + +uint32 StrikeRegister::ExternalTimeToInternal(uint32 external_time) { + 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_; + if (index == kNil) { + DropNode(); + return GetFreeExternalNode(); + } + + external_node_free_head_ = external_node_next_ptr(index); + return index; +} + +uint32 StrikeRegister::GetFreeInternalNode() { + uint32 index = internal_node_free_head_; + if (index == kNil) { + DropNode(); + return GetFreeInternalNode(); + } + + internal_node_free_head_ = internal_nodes_[index].next(); + return index; +} + +void StrikeRegister::DropNode() { + // DropNode 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 = NULL; + 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); + horizon_ = TimeFromBytes(ext_node); + + 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/chromium/net/quic/crypto/strike_register.h b/chromium/net/quic/crypto/strike_register.h new file mode 100644 index 00000000000..98bc04cb630 --- /dev/null +++ b/chromium/net/quic/crypto/strike_register.h @@ -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. + +#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 { + +// 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; + + // 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 false if any of these are true. It is also free to return + // false 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 true. + bool Insert(const uint8 nonce[32], const uint32 current_time); + + // orbit returns a pointer to the 8-byte orbit value for this + // strike-register. + const uint8* orbit() 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]); + + // ExternalTimeToInternal converts an external time value into an internal + // time value using |creation_time_external_|. + uint32 ExternalTimeToInternal(uint32 external_time); + + // 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(); + + // DropNode removes the oldest node in the tree and updates |horizon_| + // accordingly. + void DropNode(); + + 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_; + // creation_time_external_ contains the uint32, external time when this + // object was created (i.e. the value passed to the constructor). This is + // used to translate external times to internal times. + const uint32 creation_time_external_; + // internal_epoch_ contains the external time value of the start of internal + // time. + const uint32 internal_epoch_; + uint8 orbit_[8]; + uint32 horizon_; + bool horizon_valid_; + + 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_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_STRIKE_REGISTER_H_ diff --git a/chromium/net/quic/crypto/strike_register_test.cc b/chromium/net/quic/crypto/strike_register_test.cc new file mode 100644 index 00000000000..ddfa96535de --- /dev/null +++ b/chromium/net/quic/crypto/strike_register_test.cc @@ -0,0 +1,303 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +namespace { + +using net::StrikeRegister; +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); + ASSERT_FALSE(set.Insert(nonce, 1000)); + SetNonce(nonce, 1000, kOrbit); + ASSERT_FALSE(set.Insert(nonce, 1000)); +} + +TEST(StrikeRegisterTest, NoStartupMode) { + // Check that a strike register works immediately if NO_STARTUP_PERIOD_NEEDED + // is specified. + StrikeRegister set(10 /* max size */, 0 /* current time */, + 100 /* window secs */, kOrbit, + StrikeRegister::NO_STARTUP_PERIOD_NEEDED); + uint8 nonce[32]; + SetNonce(nonce, 0, kOrbit); + ASSERT_TRUE(set.Insert(nonce, 0)); + ASSERT_FALSE(set.Insert(nonce, 0)); +} + +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); + ASSERT_FALSE(set.Insert(nonce, 1000)); + SetNonce(nonce, 999, kOrbit); + ASSERT_FALSE(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); + ASSERT_FALSE(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); + ASSERT_TRUE(set.Insert(nonce, 1100)); +} + +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); + ASSERT_TRUE(set.Insert(nonce, 1100)); + ASSERT_FALSE(set.Insert(nonce, 1100)); +} + +TEST(StrikeRegisterTest, HorizonUpdating) { + StrikeRegister set(5 /* max size */, 1000 /* current time */, + 100 /* window secs */, kOrbit, + StrikeRegister::DENY_REQUESTS_AT_STARTUP); + uint8 nonce[6][32]; + for (unsigned i = 0; i < 5; i++) { + SetNonce(nonce[i], 1101, kOrbit); + nonce[i][31] = i; + ASSERT_TRUE(set.Insert(nonce[i], 1100)); + } + + // This should push the oldest value out and force the horizon to be updated. + SetNonce(nonce[5], 1102, kOrbit); + ASSERT_TRUE(set.Insert(nonce[5], 1100)); + + // This should be behind the horizon now: + SetNonce(nonce[5], 1101, kOrbit); + nonce[5][31] = 10; + ASSERT_FALSE(set.Insert(nonce[5], 1100)); +} + +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)); + 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)) { + memcpy(orbit_, orbit, sizeof(orbit_)); + } + + bool Insert(const uint8 nonce_bytes[32], const uint32 current_time_external) { + 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 false; + } + const uint32 nonce_time = + ExternalTimeToInternal(TimeFromBytes(nonce_bytes)); + // We have dropped one or more nonces with a time value of |horizon_|, so + // we have to reject anything with a timestamp less than or equal to that. + if (nonce_time <= horizon_) { + return false; + } + + // 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 false; + } + + string nonce; + nonce.reserve(32); + nonce += + string(reinterpret_cast<const char*>(&nonce_time), sizeof(nonce_time)); + nonce += + string(reinterpret_cast<const char*>(nonce_bytes) + sizeof(nonce_time), + 32 - sizeof(nonce_time)); + + set<string>::const_iterator it = nonces_.find(nonce); + if (it != nonces_.end()) { + return false; + } + + if (nonces_.size() == max_entries_) { + DropOldestEntry(); + } + + nonces_.insert(nonce); + return true; + } + + 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) { + 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<string>::iterator oldest = nonces_.begin(), it; + uint32 oldest_time = + TimeFromBytes(reinterpret_cast<const uint8*>(oldest->data())); + + for (it = oldest; it != nonces_.end(); it++) { + uint32 t = TimeFromBytes(reinterpret_cast<const uint8*>(it->data())); + if (t < oldest_time || + (t == oldest_time && memcmp(it->data(), oldest->data(), 32) < 0)) { + oldest_time = t; + oldest = it; + } + } + + nonces_.erase(oldest); + horizon_ = oldest_time; + } + + const unsigned max_entries_; + const unsigned window_secs_; + const uint32 creation_time_; + uint8 orbit_[8]; + uint32 horizon_; + + set<string> nonces_; +}; + +TEST(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 bool r2 = s2->Insert(nonce, time); + const bool r1 = s1->Insert(nonce, time); + + if (r1 != r2) { + break; + } + + if (i % 10 == 0) { + s1->Validate(); + } + } + + if (i != kMaxIterations) { + FAIL() << "Failed after " << i << " iterations"; + } +} + +} // anonymous namespace diff --git a/chromium/net/quic/quic_alarm.cc b/chromium/net/quic/quic_alarm.cc new file mode 100644 index 00000000000..e1d4a16ae9c --- /dev/null +++ b/chromium/net/quic/quic_alarm.cc @@ -0,0 +1,48 @@ +// 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(); +} + +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/chromium/net/quic/quic_alarm.h b/chromium/net/quic/quic_alarm.h new file mode 100644 index 00000000000..bee51577395 --- /dev/null +++ b/chromium/net/quic/quic_alarm.h @@ -0,0 +1,77 @@ +// 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(); + + 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/chromium/net/quic/quic_alarm_test.cc b/chromium/net/quic/quic_alarm_test.cc new file mode 100644 index 00000000000..cd7d8a559be --- /dev/null +++ b/chromium/net/quic/quic_alarm_test.cc @@ -0,0 +1,126 @@ +// 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: + 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, 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/chromium/net/quic/quic_bandwidth.cc b/chromium/net/quic/quic_bandwidth.cc new file mode 100644 index 00000000000..37b3b3883f2 --- /dev/null +++ b/chromium/net/quic/quic_bandwidth.cc @@ -0,0 +1,102 @@ +// 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); +} + +} // namespace net diff --git a/chromium/net/quic/quic_bandwidth.h b/chromium/net/quic/quic_bandwidth.h new file mode 100644 index 00000000000..71c4b2d9268 --- /dev/null +++ b/chromium/net/quic/quic_bandwidth.h @@ -0,0 +1,85 @@ +// 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; + + 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/chromium/net/quic/quic_bandwidth_test.cc b/chromium/net/quic/quic_bandwidth_test.cc new file mode 100644 index 00000000000..1090f0b4b27 --- /dev/null +++ b/chromium/net/quic/quic_bandwidth_test.cc @@ -0,0 +1,82 @@ +// 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))); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_blocked_writer_interface.h b/chromium/net/quic/quic_blocked_writer_interface.h new file mode 100644 index 00000000000..a694193fe31 --- /dev/null +++ b/chromium/net/quic/quic_blocked_writer_interface.h @@ -0,0 +1,28 @@ +// 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. This methods + // should return false if the socket has become blocked while writing. + virtual bool OnCanWrite() = 0; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_BLOCKED_WRITER_INTERFACE_H_ diff --git a/chromium/net/quic/quic_client_session.cc b/chromium/net/quic/quic_client_session.cc new file mode 100644 index 00000000000..67620787307 --- /dev/null +++ b/chromium/net/quic/quic_client_session.cc @@ -0,0 +1,392 @@ +// 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/quic/quic_connection_helper.h" +#include "net/quic/quic_crypto_client_stream_factory.h" +#include "net/quic/quic_stream_factory.h" +#include "net/ssl/ssl_info.h" +#include "net/udp/datagram_client_socket.h" + +namespace net { + +namespace { + +// 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_(NULL) {} + +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, + QuicCryptoClientStreamFactory* crypto_client_stream_factory, + const string& server_hostname, + const QuicConfig& config, + QuicCryptoClientConfig* crypto_config, + NetLog* net_log) + : QuicSession(connection, config, false), + weak_factory_(this), + stream_factory_(stream_factory), + socket_(socket.Pass()), + read_buffer_(new IOBufferWithSize(kMaxPacketSize)), + read_pending_(false), + num_total_streams_(0), + net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)), + logger_(net_log_) { + crypto_stream_.reset( + crypto_client_stream_factory ? + crypto_client_stream_factory->CreateQuicCryptoClientStream( + server_hostname, this, crypto_config) : + new QuicCryptoClientStream(server_hostname, this, crypto_config)); + + connection->set_debug_visitor(&logger_); + // TODO(rch): pass in full host port proxy pair + net_log_.BeginEvent( + NetLog::TYPE_QUIC_SESSION, + NetLog::StringCallback("host", &server_hostname)); +} + +QuicClientSession::~QuicClientSession() { + connection()->set_debug_visitor(NULL); + 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 (IsEncryptionEstablished()) + RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED); + if (IsCryptoHandshakeConfirmed()) + RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED); + else + RecordHandshakeState(STATE_FAILED); + + UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos", + crypto_stream_->num_sent_client_hellos()); + if (IsCryptoHandshakeConfirmed()) { + UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellosCryptoHandshakeConfirmed", + crypto_stream_->num_sent_client_hellos()); + } +} + +int QuicClientSession::TryCreateStream(StreamRequest* request, + QuicReliableClientStream** stream) { + if (!crypto_stream_->encryption_established()) { + DLOG(DFATAL) << "Encryption not established."; + return ERR_CONNECTION_CLOSED; + } + + if (goaway_received()) { + DLOG(INFO) << "Going away."; + return ERR_CONNECTION_CLOSED; + } + + if (!connection()->connected()) { + DLOG(INFO) << "Already closed."; + 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::CreateOutgoingReliableStream() { + if (!crypto_stream_->encryption_established()) { + DLOG(INFO) << "Encryption not active so no outgoing stream created."; + return NULL; + } + if (GetNumOpenStreams() >= get_max_open_streams()) { + DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already " << GetNumOpenStreams() << " open."; + return NULL; + } + if (goaway_received()) { + DLOG(INFO) << "Failed to create a new outgoing stream. " + << "Already received goaway."; + return NULL; + } + + return CreateOutgoingReliableStreamImpl(); +} + +QuicReliableClientStream* +QuicClientSession::CreateOutgoingReliableStreamImpl() { + DCHECK(connection()->connected()); + QuicReliableClientStream* stream = + new QuicReliableClientStream(GetNextStreamId(), this, net_log_); + ActivateStream(stream); + ++num_total_streams_; + return stream; +} + +QuicCryptoClientStream* QuicClientSession::GetCryptoStream() { + return crypto_stream_.get(); +}; + +bool QuicClientSession::GetSSLInfo(SSLInfo* ssl_info) { + DCHECK(crypto_stream_.get()); + return crypto_stream_->GetSSLInfo(ssl_info); +} + +int QuicClientSession::CryptoConnect(const CompletionCallback& callback) { + RecordHandshakeState(STATE_STARTED); + 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 (IsEncryptionEstablished()) { + return OK; + } + + callback_ = callback; + return ERR_IO_PENDING; +} + +ReliableQuicStream* QuicClientSession::CreateIncomingReliableStream( + QuicStreamId id) { + DLOG(ERROR) << "Server push not supported"; + return NULL; +} + +void QuicClientSession::CloseStream(QuicStreamId stream_id) { + QuicSession::CloseStream(stream_id); + + if (GetNumOpenStreams() < get_max_open_streams() && + !stream_requests_.empty() && + crypto_stream_->encryption_established() && + !goaway_received() && + 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()) { + // 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); + } + QuicSession::OnCryptoHandshakeEvent(event); +} + +void QuicClientSession::OnCryptoHandshakeMessageSent( + const CryptoHandshakeMessage& message) { + logger_.OnCryptoHandshakeMessageSent(message); +} + +void QuicClientSession::OnCryptoHandshakeMessageReceived( + const CryptoHandshakeMessage& message) { + logger_.OnCryptoHandshakeMessageReceived(message); +} + +void QuicClientSession::ConnectionClose(QuicErrorCode error, bool from_peer) { + logger_.OnConnectionClose(error, from_peer); + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.ConnectionCloseErrorCode", + error); + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.QuicVersion", + connection()->version()); + if (!callback_.is_null()) { + base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR); + } + QuicSession::ConnectionClose(error, from_peer); + NotifyFactoryOfSessionCloseLater(); +} + +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) { + return; + } + + // Data was read, process it. + // Schedule the work through the message loop to avoid recursive + // callbacks. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&QuicClientSession::OnReadComplete, + weak_factory_.GetWeakPtr(), rv)); +} + +void QuicClientSession::CloseSessionOnError(int error) { + UMA_HISTOGRAM_SPARSE_SLOWLY("Net.QuicSession.CloseSessionOnError", -error); + CloseSessionOnErrorInner(error); + NotifyFactoryOfSessionClose(); +} + +void QuicClientSession::CloseSessionOnErrorInner(int error) { + if (!callback_.is_null()) { + base::ResetAndReturn(&callback_).Run(error); + } + while (!streams()->empty()) { + ReliableQuicStream* stream = streams()->begin()->second; + QuicStreamId id = stream->id(); + static_cast<QuicReliableClientStream*>(stream)->OnError(error); + CloseStream(id); + } + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_CLOSE_ON_ERROR, + NetLog::IntegerCallback("net_error", error)); + + connection()->CloseConnection(QUIC_INTERNAL_ERROR, false); + DCHECK(!connection()->connected()); +} + +base::Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetString("host_port_pair", pair.ToString()); + dict->SetString("version", QuicVersionToString(connection()->version())); + dict->SetInteger("open_streams", GetNumOpenStreams()); + dict->SetInteger("total_streams", num_total_streams_); + dict->SetString("peer_address", peer_address().ToString()); + dict->SetString("guid", base::Uint64ToString(guid())); + 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) { + DLOG(INFO) << "Closing session on read error: " << result; + CloseSessionOnErrorInner(result); + NotifyFactoryOfSessionCloseLater(); + return; + } + + scoped_refptr<IOBufferWithSize> buffer(read_buffer_); + read_buffer_ = new IOBufferWithSize(kMaxPacketSize); + QuicEncryptedPacket packet(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()) { + stream_factory_->OnSessionClose(this); + return; + } + StartReading(); +} + +void QuicClientSession::NotifyFactoryOfSessionCloseLater() { + DCHECK_EQ(0u, GetNumOpenStreams()); + DCHECK(!connection()->connected()); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&QuicClientSession::NotifyFactoryOfSessionClose, + weak_factory_.GetWeakPtr())); +} + +void QuicClientSession::NotifyFactoryOfSessionClose() { + DCHECK_EQ(0u, GetNumOpenStreams()); + DCHECK(stream_factory_); + // Will delete |this|. + stream_factory_->OnSessionClose(this); +} + +} // namespace net diff --git a/chromium/net/quic/quic_client_session.h b/chromium/net/quic/quic_client_session.h new file mode 100644 index 00000000000..555837fe9a5 --- /dev/null +++ b/chromium/net/quic/quic_client_session.h @@ -0,0 +1,172 @@ +// 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/containers/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/completion_callback.h" +#include "net/quic/quic_connection_logger.h" +#include "net/quic/quic_crypto_client_stream.h" +#include "net/quic/quic_reliable_client_stream.h" +#include "net/quic/quic_session.h" + +namespace net { + +class DatagramClientSocket; +class QuicConnectionHelper; +class QuicCryptoClientStreamFactory; +class QuicStreamFactory; +class SSLInfo; + +namespace test { +class QuicClientSessionPeer; +} // namespace test + +class NET_EXPORT_PRIVATE QuicClientSession : public QuicSession { + public: + // 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| and |helper|, 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, + QuicCryptoClientStreamFactory* crypto_client_stream_factory, + const std::string& server_hostname, + const QuicConfig& config, + QuicCryptoClientConfig* crypto_config, + NetLog* net_log); + + virtual ~QuicClientSession(); + + // 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 QuicReliableClientStream* CreateOutgoingReliableStream() OVERRIDE; + virtual QuicCryptoClientStream* GetCryptoStream() OVERRIDE; + virtual void CloseStream(QuicStreamId stream_id) 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) OVERRIDE; + + // QuicConnectionVisitorInterface methods: + virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE; + + // Performs a crypto handshake with the server. + int CryptoConnect(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 HostPortPair& pair) const; + + const BoundNetLog& net_log() const { return net_log_; } + + base::WeakPtr<QuicClientSession> GetWeakPtr(); + + protected: + // QuicSession methods: + virtual ReliableQuicStream* CreateIncomingReliableStream( + QuicStreamId id) OVERRIDE; + + private: + friend class test::QuicClientSessionPeer; + + typedef std::list<StreamRequest*> StreamRequestQueue; + + QuicReliableClientStream* CreateOutgoingReliableStreamImpl(); + // A completion callback invoked when a read completes. + void OnReadComplete(int result); + + void CloseSessionOnErrorInner(int error); + + // Posts a task to notify the factory that this session has been closed. + void NotifyFactoryOfSessionCloseLater(); + + // Notifies the factory that this session has been closed which will + // delete |this|. + void NotifyFactoryOfSessionClose(); + + base::WeakPtrFactory<QuicClientSession> weak_factory_; + scoped_ptr<QuicCryptoClientStream> crypto_stream_; + QuicStreamFactory* stream_factory_; + scoped_ptr<DatagramClientSocket> socket_; + scoped_refptr<IOBufferWithSize> read_buffer_; + StreamRequestQueue stream_requests_; + bool read_pending_; + CompletionCallback callback_; + size_t num_total_streams_; + BoundNetLog net_log_; + QuicConnectionLogger logger_; + + DISALLOW_COPY_AND_ASSIGN(QuicClientSession); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CLIENT_SESSION_H_ diff --git a/chromium/net/quic/quic_client_session_test.cc b/chromium/net/quic/quic_client_session_test.cc new file mode 100644 index 00000000000..09e7d210116 --- /dev/null +++ b/chromium/net/quic/quic_client_session_test.cc @@ -0,0 +1,134 @@ +// 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 "net/base/capturing_net_log.h" +#include "net/base/test_completion_callback.h" +#include "net/quic/crypto/aes_128_gcm_12_encrypter.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/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/udp/datagram_client_socket.h" + +using testing::_; + +namespace net { +namespace test { +namespace { + +const char kServerHostname[] = "www.example.com"; + +class QuicClientSessionTest : public ::testing::Test { + protected: + QuicClientSessionTest() + : guid_(1), + connection_(new PacketSavingConnection(guid_, IPEndPoint(), false)), + session_(connection_, scoped_ptr<DatagramClientSocket>(), NULL, + NULL, kServerHostname, DefaultQuicConfig(), &crypto_config_, + &net_log_) { + session_.config()->SetDefaults(); + crypto_config_.SetDefaults(); + } + + void CompleteCryptoHandshake() { + ASSERT_EQ(ERR_IO_PENDING, + session_.CryptoConnect(callback_.callback())); + CryptoTestUtils::HandshakeWithFakeServer( + connection_, session_.GetCryptoStream()); + ASSERT_EQ(OK, callback_.WaitForResult()); + } + + QuicGuid guid_; + PacketSavingConnection* connection_; + CapturingNetLog net_log_; + QuicClientSession session_; + MockClock clock_; + MockRandom random_; + QuicConnectionVisitorInterface* visitor_; + TestCompletionCallback callback_; + QuicCryptoClientConfig crypto_config_; +}; + +TEST_F(QuicClientSessionTest, CryptoConnect) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); +} + +TEST_F(QuicClientSessionTest, MaxNumStreams) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); + + std::vector<QuicReliableClientStream*> streams; + for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) { + QuicReliableClientStream* stream = session_.CreateOutgoingReliableStream(); + EXPECT_TRUE(stream); + streams.push_back(stream); + } + EXPECT_FALSE(session_.CreateOutgoingReliableStream()); + + // Close a stream and ensure I can now open a new one. + session_.CloseStream(streams[0]->id()); + EXPECT_TRUE(session_.CreateOutgoingReliableStream()); +} + +TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); + + std::vector<QuicReliableClientStream*> streams; + for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) { + QuicReliableClientStream* stream = session_.CreateOutgoingReliableStream(); + 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 != NULL); +} + +TEST_F(QuicClientSessionTest, GoAwayReceived) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + 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(NULL, session_.CreateOutgoingReliableStream()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_clock.cc b/chromium/net/quic/quic_clock.cc new file mode 100644 index 00000000000..865562369fa --- /dev/null +++ b/chromium/net/quic/quic_clock.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/quic_clock.h" + +#include "base/time/time.h" + +namespace net { + +QuicClock::QuicClock() { +} + +QuicClock::~QuicClock() {} + +QuicTime QuicClock::ApproximateNow() const { + // Chrome does not have a distinct notion of ApproximateNow(). + 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/chromium/net/quic/quic_clock.h b/chromium/net/quic/quic_clock.h new file mode 100644 index 00000000000..e6135a930d0 --- /dev/null +++ b/chromium/net/quic/quic_clock.h @@ -0,0 +1,37 @@ +// 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; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CLOCK_H_ diff --git a/chromium/net/quic/quic_clock_test.cc b/chromium/net/quic/quic_clock_test.cc new file mode 100644 index 00000000000..6ee4540889f --- /dev/null +++ b/chromium/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/chromium/net/quic/quic_config.cc b/chromium/net/quic/quic_config.cc new file mode 100644 index 00000000000..545d4c165e4 --- /dev/null +++ b/chromium/net/quic/quic_config.cc @@ -0,0 +1,355 @@ +// 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" + +using std::string; + +namespace net { + +QuicNegotiableValue::QuicNegotiableValue(QuicTag tag, Presence presence) + : tag_(tag), + presence_(presence), + negotiated_(false) { +} + +QuicNegotiableUint32::QuicNegotiableUint32(QuicTag tag, Presence presence) + : QuicNegotiableValue(tag, presence) { +} + +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::ReadUint32( + const CryptoHandshakeMessage& msg, + uint32* out, + string* error_details) const { + DCHECK(error_details != NULL); + QuicErrorCode error = msg.GetUint32(tag_, out); + switch (error) { + case QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND: + if (presence_ == QuicNegotiableValue::PRESENCE_REQUIRED) { + *error_details = "Missing " + QuicUtils::TagToString(tag_); + break; + } + error = QUIC_NO_ERROR; + *out = default_value_; + + case QUIC_NO_ERROR: + break; + default: + *error_details = "Bad " + QuicUtils::TagToString(tag_); + break; + } + return error; +} + +QuicErrorCode QuicNegotiableUint32::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + string* error_details) { + DCHECK(!negotiated_); + DCHECK(error_details != NULL); + uint32 value; + QuicErrorCode error = ReadUint32(client_hello, &value, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + negotiated_ = true; + negotiated_value_ = std::min(value, max_value_); + + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicNegotiableUint32::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + string* error_details) { + DCHECK(!negotiated_); + DCHECK(error_details != NULL); + uint32 value; + QuicErrorCode error = ReadUint32(server_hello, &value, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + if (value > max_value_) { + *error_details = "Invalid value received for " + + QuicUtils::TagToString(tag_); + return QUIC_INVALID_NEGOTIATED_VALUE; + } + + negotiated_ = true; + negotiated_value_ = value; + return QUIC_NO_ERROR; +} + +QuicNegotiableTag::QuicNegotiableTag(QuicTag tag, Presence presence) + : QuicNegotiableValue(tag, presence) { +} + +QuicNegotiableTag::~QuicNegotiableTag() {} + +void QuicNegotiableTag::set(const QuicTagVector& possible, + QuicTag default_value) { + DCHECK(std::find(possible.begin(), possible.end(), default_value) != + possible.end()); + 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 != NULL); + 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::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + string* error_details) { + DCHECK(!negotiated_); + DCHECK(error_details != NULL); + const QuicTag* received_tags; + size_t received_tags_length; + QuicErrorCode error = ReadVector(client_hello, &received_tags, + &received_tags_length, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + QuicTag negotiated_tag; + if (!QuicUtils::FindMutualTag(possible_values_, + received_tags, + received_tags_length, + QuicUtils::LOCAL_PRIORITY, + &negotiated_tag, + NULL)) { + *error_details = "Unsuported " + QuicUtils::TagToString(tag_); + return QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP; + } + + negotiated_ = true; + negotiated_tag_ = negotiated_tag; + return QUIC_NO_ERROR; +} + +QuicErrorCode QuicNegotiableTag::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + string* error_details) { + DCHECK(!negotiated_); + DCHECK(error_details != NULL); + const QuicTag* received_tags; + size_t received_tags_length; + QuicErrorCode error = ReadVector(server_hello, &received_tags, + &received_tags_length, error_details); + if (error != QUIC_NO_ERROR) { + return error; + } + + if (received_tags_length != 1 || + std::find(possible_values_.begin(), possible_values_.end(), + *received_tags) == possible_values_.end()) { + *error_details = "Invalid " + QuicUtils::TagToString(tag_); + return QUIC_INVALID_NEGOTIATED_VALUE; + } + + negotiated_ = true; + negotiated_tag_ = *received_tags; + return QUIC_NO_ERROR; +} + +QuicConfig::QuicConfig() : + congestion_control_(kCGST, QuicNegotiableValue::PRESENCE_REQUIRED), + idle_connection_state_lifetime_seconds_( + kICSL, QuicNegotiableValue::PRESENCE_REQUIRED), + keepalive_timeout_seconds_(kKATO, QuicNegotiableValue::PRESENCE_OPTIONAL), + max_streams_per_connection_(kMSPC, QuicNegotiableValue::PRESENCE_REQUIRED), + max_time_before_crypto_handshake_(QuicTime::Delta::Zero()) { + idle_connection_state_lifetime_seconds_.set(0, 0); + keepalive_timeout_seconds_.set(0, 0); +} + +QuicConfig::~QuicConfig() {} + +void QuicConfig::set_congestion_control( + const QuicTagVector& congestion_control, + QuicTag default_congestion_control) { + congestion_control_.set(congestion_control, default_congestion_control); +} + +QuicTag QuicConfig::congestion_control() const { + return congestion_control_.GetTag(); +} + +void QuicConfig::set_idle_connection_state_lifetime( + 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::idle_connection_state_lifetime() const { + return QuicTime::Delta::FromSeconds( + idle_connection_state_lifetime_seconds_.GetUint32()); +} + +QuicTime::Delta QuicConfig::keepalive_timeout() const { + return QuicTime::Delta::FromSeconds( + keepalive_timeout_seconds_.GetUint32()); +} + +void QuicConfig::set_max_streams_per_connection(size_t max_streams, + size_t default_streams) { + max_streams_per_connection_.set(max_streams, default_streams); +} + +uint32 QuicConfig::max_streams_per_connection() const { + return max_streams_per_connection_.GetUint32(); +} + +void QuicConfig::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 QuicConfig::max_time_before_crypto_handshake() const { + return max_time_before_crypto_handshake_; +} + +bool QuicConfig::negotiated() { + return congestion_control_.negotiated() && + idle_connection_state_lifetime_seconds_.negotiated() && + keepalive_timeout_seconds_.negotiated() && + max_streams_per_connection_.negotiated(); +} + +void QuicConfig::SetDefaults() { + congestion_control_.set(QuicTagVector(1, kQBIC), kQBIC); + idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs, + kDefaultInitialTimeoutSecs); + // kKATO is optional. Return 0 if not negotiated. + keepalive_timeout_seconds_.set(0, 0); + max_streams_per_connection_.set(kDefaultMaxStreamsPerConnection, + kDefaultMaxStreamsPerConnection); + max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds( + kDefaultMaxTimeForCryptoHandshakeSecs); +} + +void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { + congestion_control_.ToHandshakeMessage(out); + idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); + keepalive_timeout_seconds_.ToHandshakeMessage(out); + max_streams_per_connection_.ToHandshakeMessage(out); +} + +QuicErrorCode QuicConfig::ProcessClientHello( + const CryptoHandshakeMessage& client_hello, + string* error_details) { + DCHECK(error_details != NULL); + + QuicErrorCode error = QUIC_NO_ERROR; + if (error == QUIC_NO_ERROR) { + error = congestion_control_.ProcessClientHello(client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = idle_connection_state_lifetime_seconds_.ProcessClientHello( + client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = keepalive_timeout_seconds_.ProcessClientHello( + client_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = max_streams_per_connection_.ProcessClientHello( + client_hello, error_details); + } + return error; +} + +QuicErrorCode QuicConfig::ProcessServerHello( + const CryptoHandshakeMessage& server_hello, + string* error_details) { + DCHECK(error_details != NULL); + + QuicErrorCode error = QUIC_NO_ERROR; + if (error == QUIC_NO_ERROR) { + error = congestion_control_.ProcessServerHello(server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = idle_connection_state_lifetime_seconds_.ProcessServerHello( + server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = keepalive_timeout_seconds_.ProcessServerHello( + server_hello, error_details); + } + if (error == QUIC_NO_ERROR) { + error = max_streams_per_connection_.ProcessServerHello( + server_hello, error_details); + } + return error; +} + +} // namespace net + diff --git a/chromium/net/quic/quic_config.h b/chromium/net/quic/quic_config.h new file mode 100644 index 00000000000..c07f332ac6f --- /dev/null +++ b/chromium/net/quic/quic_config.h @@ -0,0 +1,197 @@ +// 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/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.h" +#include "net/quic/quic_utils.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicNegotiableValue { + public: + enum Presence { + // 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, + }; + + QuicNegotiableValue(QuicTag tag, Presence presence); + + bool negotiated() const { + return negotiated_; + } + + protected: + const QuicTag tag_; + const Presence presence_; + bool negotiated_; +}; + +class NET_EXPORT_PRIVATE QuicNegotiableUint32 : public QuicNegotiableValue { + public: + QuicNegotiableUint32(QuicTag name, Presence presence); + + // 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. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + + // Sets |negotiated_value_| to the minimum of |max_value_| and the + // corresponding value from |client_hello|. If the corresponding value is + // missing and PRESENCE_OPTIONAL then |negotiated_value_| is set to + // |default_value_|. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + std::string* error_details); + + // Sets the |negotiated_value_| to the corresponding value from + // |server_hello|. Returns error if the value received in |server_hello| is + // greater than |max_value_|. If the corresponding value is missing and + // PRESENCE_OPTIONAL then |negotiated_value_| is set to |0|, + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + std::string* error_details); + + private: + // 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 |max_value_|. + QuicErrorCode ReadUint32(const CryptoHandshakeMessage& msg, + uint32* out, + std::string* error_details) const; + + uint32 max_value_; + uint32 default_value_; + uint32 negotiated_value_; +}; + +class NET_EXPORT_PRIVATE QuicNegotiableTag : public QuicNegotiableValue { + public: + QuicNegotiableTag(QuicTag name, Presence presence); + ~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. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + + // 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_|. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + std::string* error_details); + + // Sets the value for |name_| tag in |server_hello| as |negotiated_value_|. + // Returns error if the value received in |server_hello| isn't present in + // |possible_values_|. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + std::string* error_details); + + 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_; +}; + +// QuicConfig contains non-crypto configuration options that are negotiated in +// the crypto handshake. +class NET_EXPORT_PRIVATE QuicConfig { + public: + QuicConfig(); + ~QuicConfig(); + + void set_congestion_control(const QuicTagVector& congestion_control, + QuicTag default_congestion_control); + + QuicTag congestion_control() const; + + void set_idle_connection_state_lifetime( + QuicTime::Delta max_idle_connection_state_lifetime, + QuicTime::Delta default_idle_conection_state_lifetime); + + QuicTime::Delta idle_connection_state_lifetime() const; + + QuicTime::Delta keepalive_timeout() const; + + void set_max_streams_per_connection(size_t max_streams, + size_t default_streams); + + uint32 max_streams_per_connection() const; + + void set_max_time_before_crypto_handshake( + QuicTime::Delta max_time_before_crypto_handshake); + + QuicTime::Delta max_time_before_crypto_handshake() const; + + bool negotiated(); + + // SetDefaults sets the members to sensible, default values. + void SetDefaults(); + + // ToHandshakeMessage serializes the settings in this object as a series of + // tags /value pairs and adds them to |out|. + void ToHandshakeMessage(CryptoHandshakeMessage* out) const; + + // Calls ProcessClientHello on each negotiable parameter. On failure returns + // the corresponding QuicErrorCode and sets detailed error in |error_details|. + QuicErrorCode ProcessClientHello(const CryptoHandshakeMessage& client_hello, + std::string* error_details); + + // Calls ProcessServerHello on each negotiable parameter. On failure returns + // the corresponding QuicErrorCode and sets detailed error in |error_details|. + QuicErrorCode ProcessServerHello(const CryptoHandshakeMessage& server_hello, + std::string* error_details); + + private: + // Congestion control feedback type. + QuicNegotiableTag congestion_control_; + // 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_; + // Maximum time till the session can be alive before crypto handshake is + // finished. (Not negotiated). + QuicTime::Delta max_time_before_crypto_handshake_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONFIG_H_ diff --git a/chromium/net/quic/quic_config_test.cc b/chromium/net/quic/quic_config_test.cc new file mode 100644 index 00000000000..eeaa97deaab --- /dev/null +++ b/chromium/net/quic/quic_config_test.cc @@ -0,0 +1,167 @@ +// 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.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_time.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_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5), + QuicTime::Delta::FromSeconds(2)); + config_.set_max_streams_per_connection(4, 2); + 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); + + 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(kINAR); + cgst.push_back(kQBIC); + client_config.set_congestion_control(cgst, kQBIC); + client_config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); + client_config.set_max_streams_per_connection( + 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); + + CryptoHandshakeMessage msg; + client_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + EXPECT_EQ(kQBIC, config_.congestion_control()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs), + config_.idle_connection_state_lifetime()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + config_.max_streams_per_connection()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); +} + +TEST_F(QuicConfigTest, ProcessServerHello) { + QuicConfig server_config; + QuicTagVector cgst; + cgst.push_back(kQBIC); + server_config.set_congestion_control(cgst, kQBIC); + server_config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2), + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2)); + server_config.set_max_streams_per_connection( + kDefaultMaxStreamsPerConnection / 2, + kDefaultMaxStreamsPerConnection / 2); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_TRUE(config_.negotiated()); + EXPECT_EQ(kQBIC, config_.congestion_control()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs / 2), + config_.idle_connection_state_lifetime()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection / 2, + config_.max_streams_per_connection()); + EXPECT_EQ(QuicTime::Delta::FromSeconds(0), config_.keepalive_timeout()); +} + +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_.ProcessClientHello(msg, &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_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error); +} + +TEST_F(QuicConfigTest, OutOfBoundSHLO) { + QuicConfig server_config; + server_config.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs)); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &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(kINAR); + server_config.set_congestion_control(cgst, kQBIC); + + CryptoHandshakeMessage msg; + server_config.ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = config_.ProcessServerHello(msg, &error_details); + EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error); +} + +TEST_F(QuicConfigTest, NoOverLapInCGST) { + QuicConfig server_config; + server_config.SetDefaults(); + QuicTagVector cgst; + cgst.push_back(kINAR); + server_config.set_congestion_control(cgst, kINAR); + + CryptoHandshakeMessage msg; + string error_details; + server_config.ToHandshakeMessage(&msg); + const QuicErrorCode error = config_.ProcessClientHello(msg, &error_details); + LOG(INFO) << QuicUtils::ErrorToString(error); + EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_connection.cc b/chromium/net/quic/quic_connection.cc new file mode 100644 index 00000000000..718b78864c9 --- /dev/null +++ b/chromium/net/quic/quic_connection.cc @@ -0,0 +1,1661 @@ +// 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 <algorithm> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_utils.h" + +using base::hash_map; +using base::hash_set; +using base::StringPiece; +using std::list; +using std::make_pair; +using std::min; +using std::max; +using std::vector; +using std::set; +using std::string; + +namespace net { +namespace { + +// The largest gap in packets we'll accept without closing the connection. +// This will likely have to be tuned. +const QuicPacketSequenceNumber kMaxPacketGap = 5000; + +// We want to make sure if we get a large nack packet, we don't queue up too +// many packets at once. 10 is arbitrary. +const int kMaxRetransmissionsPerAck = 10; + +// TCP retransmits after 2 nacks. We allow for a third in case of out-of-order +// delivery. +// TODO(ianswett): Change to match TCP's rule of retransmitting once an ack +// at least 3 sequence numbers larger arrives. +const size_t kNumberOfNacksBeforeRetransmission = 3; + +// The maxiumum number of packets we'd like to queue. We may end up queueing +// more in the case of many control frames. +// 6 is arbitrary. +const int kMaxPacketsToSerializeAtOnce = 6; + +// Limit the number of packets we send per retransmission-alarm so we +// eventually cede. 10 is arbitrary. +const size_t kMaxPacketsPerRetransmissionAlarm = 10; + +// 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; + +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_; +}; + +// 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 { + return connection_->OnRetransmissionTimeout(); + } + + private: + QuicConnection* connection_; +}; + +// 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_->OnCanWrite(); + // Never reschedule the alarm, since OnCanWrite does that. + return QuicTime::Zero(); + } + + private: + QuicConnection* connection_; +}; + +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_; +}; + +} // namespace + +#define ENDPOINT (is_server_ ? "Server: " : " Client: ") + +QuicConnection::QuicConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper, + bool is_server, + QuicVersion version) + : framer_(version, + helper->GetClock()->ApproximateNow(), + is_server), + helper_(helper), + encryption_level_(ENCRYPTION_NONE), + clock_(helper->GetClock()), + random_generator_(helper->GetRandomGenerator()), + guid_(guid), + peer_address_(address), + largest_seen_packet_with_ack_(0), + handling_retransmission_timeout_(false), + write_blocked_(false), + ack_alarm_(helper->CreateAlarm(new AckAlarm(this))), + retransmission_alarm_(helper->CreateAlarm(new RetransmissionAlarm(this))), + send_alarm_(helper->CreateAlarm(new SendAlarm(this))), + timeout_alarm_(helper->CreateAlarm(new TimeoutAlarm(this))), + debug_visitor_(NULL), + packet_creator_(guid_, &framer_, random_generator_, is_server), + packet_generator_(this, NULL, &packet_creator_), + idle_network_timeout_( + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)), + overall_connection_timeout_(QuicTime::Delta::Infinite()), + creation_time_(clock_->ApproximateNow()), + time_of_last_received_packet_(clock_->ApproximateNow()), + time_of_last_sent_packet_(clock_->ApproximateNow()), + congestion_manager_(clock_, kTCP), + version_negotiation_state_(START_NEGOTIATION), + max_packets_per_retransmission_alarm_(kMaxPacketsPerRetransmissionAlarm), + is_server_(is_server), + connected_(true), + received_truncated_ack_(false), + send_ack_in_response_to_packet_(false), + address_migrating_(false) { + helper_->SetConnection(this); + timeout_alarm_->Set(clock_->ApproximateNow().Add(idle_network_timeout_)); + framer_.set_visitor(this); + framer_.set_received_entropy_calculator(&received_packet_manager_); + + /* + if (FLAGS_fake_packet_loss_percentage > 0) { + int32 seed = RandomBase::WeakSeed32(); + LOG(INFO) << ENDPOINT << "Seeding packet loss with " << seed; + random_.reset(new MTRandom(seed)); + } + */ +} + +QuicConnection::~QuicConnection() { + STLDeleteElements(&undecryptable_packets_); + STLDeleteValues(&unacked_packets_); + STLDeleteValues(&group_map_); + for (QueuedPacketList::iterator it = queued_packets_.begin(); + it != queued_packets_.end(); ++it) { + delete it->packet; + } +} + +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. + for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { + const QuicVersion& version = kSupportedQuicVersions[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; + } + SendConnectionClose(framer->error()); +} + +void QuicConnection::OnPacket() { + DCHECK(last_stream_frames_.empty() && + last_goaway_frames_.empty() && + last_rst_frames_.empty() && + last_ack_frames_.empty() && + last_congestion_frames_.empty()); +} + +void QuicConnection::OnPublicResetPacket( + const QuicPublicResetPacket& packet) { + if (debug_visitor_) { + debug_visitor_->OnPublicResetPacket(packet); + } + CloseConnection(QUIC_PUBLIC_RESET, true); +} + +bool QuicConnection::OnProtocolVersionMismatch(QuicVersion 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_) { + 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)) { + // Drop packets which can't be parsed due to version mismatch. + 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; + + // 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_) { + 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; + } + + version_negotiation_state_ = NEGOTIATION_IN_PROGRESS; + RetransmitUnackedPackets(ALL_PACKETS); +} + +void QuicConnection::OnRevivedPacket() { +} + +bool QuicConnection::OnPacketHeader(const QuicPacketHeader& header) { + if (debug_visitor_) { + 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.guid != guid_) { + DLOG(INFO) << ENDPOINT << "Ignoring packet from unexpected GUID: " + << header.public_header.guid << " instead of " << guid_; + return false; + } + + if (!Near(header.packet_sequence_number, + last_header_.packet_sequence_number)) { + DLOG(INFO) << 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)) { + return false; + } + + if (version_negotiation_state_ != NEGOTIATED_VERSION) { + if (is_server_) { + if (!header.public_header.version_flag) { + DLOG(WARNING) << ENDPOINT << "Got packet 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; + } + } 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_creator_.StopSendingVersion(); + version_negotiation_state_ = NEGOTIATED_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 != NULL) { + group->Update(last_header_, payload); + } +} + +bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnStreamFrame(frame); + } + last_stream_frames_.push_back(frame); + return true; +} + +bool QuicConnection::OnAckFrame(const QuicAckFrame& incoming_ack) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnAckFrame(incoming_ack); + } + DVLOG(1) << ENDPOINT << "OnAckFrame: " << incoming_ack; + + if (last_header_.packet_sequence_number <= largest_seen_packet_with_ack_) { + DLOG(INFO) << 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; + + received_truncated_ack_ = + incoming_ack.received_info.missing_packets.size() >= + QuicFramer::GetMaxUnackedPackets(last_header_); + + received_packet_manager_.UpdatePacketInformationReceivedByPeer(incoming_ack); + received_packet_manager_.UpdatePacketInformationSentByPeer(incoming_ack); + // Possibly close any FecGroups which are now irrelevant. + CloseFecGroupsBefore(incoming_ack.sent_info.least_unacked + 1); + + sent_entropy_manager_.ClearEntropyBefore( + received_packet_manager_.least_packet_awaited_by_peer() - 1); + + SequenceNumberSet acked_packets; + HandleAckForSentPackets(incoming_ack, &acked_packets); + HandleAckForSentFecPackets(incoming_ack, &acked_packets); + if (acked_packets.size() > 0) { + visitor_->OnAck(acked_packets); + } + congestion_manager_.OnIncomingAckFrame(incoming_ack, + time_of_last_received_packet_); +} + +bool QuicConnection::OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& feedback) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnCongestionFeedbackFrame(feedback); + } + last_congestion_frames_.push_back(feedback); + return connected_; +} + +bool QuicConnection::ValidateAckFrame(const QuicAckFrame& incoming_ack) { + if (incoming_ack.received_info.largest_observed > + packet_creator_.sequence_number()) { + DLOG(ERROR) << ENDPOINT << "Peer's observed unsent packet:" + << incoming_ack.received_info.largest_observed << " vs " + << packet_creator_.sequence_number(); + // We got an error for data we have not sent. Error out. + return false; + } + + if (incoming_ack.received_info.largest_observed < + received_packet_manager_.peer_largest_observed_packet()) { + DLOG(ERROR) << ENDPOINT << "Peer's largest_observed packet decreased:" + << incoming_ack.received_info.largest_observed << " vs " + << received_packet_manager_.peer_largest_observed_packet(); + // 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; + } + + // We can't have too many unacked packets, or our ack frames go over + // kMaxPacketSize. + DCHECK_LE(incoming_ack.received_info.missing_packets.size(), + QuicFramer::GetMaxUnackedPackets(last_header_)); + + if (incoming_ack.sent_info.least_unacked < + received_packet_manager_.peer_least_packet_awaiting_ack()) { + DLOG(ERROR) << ENDPOINT << "Peer's sent low least_unacked: " + << incoming_ack.sent_info.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 (incoming_ack.sent_info.least_unacked > + last_header_.packet_sequence_number) { + DLOG(ERROR) << ENDPOINT << "Peer sent least_unacked:" + << incoming_ack.sent_info.least_unacked + << " greater than the enclosing packet sequence number:" + << last_header_.packet_sequence_number; + return false; + } + + if (!incoming_ack.received_info.missing_packets.empty() && + *incoming_ack.received_info.missing_packets.rbegin() > + incoming_ack.received_info.largest_observed) { + DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: " + << *incoming_ack.received_info.missing_packets.rbegin() + << " greater than largest observed: " + << incoming_ack.received_info.largest_observed; + return false; + } + + if (!incoming_ack.received_info.missing_packets.empty() && + *incoming_ack.received_info.missing_packets.begin() < + received_packet_manager_.least_packet_awaited_by_peer()) { + DLOG(ERROR) << ENDPOINT << "Peer sent missing packet: " + << *incoming_ack.received_info.missing_packets.begin() + << "smaller than least_packet_awaited_by_peer_: " + << received_packet_manager_.least_packet_awaited_by_peer(); + return false; + } + + if (!sent_entropy_manager_.IsValidEntropy( + incoming_ack.received_info.largest_observed, + incoming_ack.received_info.missing_packets, + incoming_ack.received_info.entropy_hash)) { + DLOG(ERROR) << ENDPOINT << "Peer sent invalid entropy."; + return false; + } + + return true; +} + +void QuicConnection::HandleAckForSentPackets(const QuicAckFrame& incoming_ack, + SequenceNumberSet* acked_packets) { + int retransmitted_packets = 0; + // 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. + UnackedPacketMap::iterator it = unacked_packets_.begin(); + while (it != unacked_packets_.end()) { + QuicPacketSequenceNumber sequence_number = it->first; + if (sequence_number > + received_packet_manager_.peer_largest_observed_packet()) { + // These are very new sequence_numbers. + break; + } + RetransmittableFrames* unacked = it->second; + if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) { + // Packet was acked, so remove it from our unacked packet list. + DVLOG(1) << ENDPOINT <<"Got an ack for packet " << sequence_number; + acked_packets->insert(sequence_number); + delete unacked; + unacked_packets_.erase(it++); + retransmission_map_.erase(sequence_number); + } else { + // This is a packet which we planned on retransmitting and has not been + // seen at the time of this ack being sent out. See if it's our new + // lowest unacked packet. + DVLOG(1) << ENDPOINT << "still missing packet " << sequence_number; + ++it; + // The peer got packets after this sequence number. This is an explicit + // nack. + RetransmissionMap::iterator retransmission_it = + retransmission_map_.find(sequence_number); + ++(retransmission_it->second.number_nacks); + if (retransmission_it->second.number_nacks >= + kNumberOfNacksBeforeRetransmission && + retransmitted_packets < kMaxRetransmissionsPerAck) { + ++retransmitted_packets; + DVLOG(1) << ENDPOINT << "Trying to retransmit packet " + << sequence_number + << " as it has been nacked 3 or more times."; + // RetransmitPacket will retransmit with a new sequence_number. + RetransmitPacket(sequence_number); + } + } + } +} + +void QuicConnection::HandleAckForSentFecPackets( + const QuicAckFrame& incoming_ack, SequenceNumberSet* acked_packets) { + UnackedPacketMap::iterator it = unacked_fec_packets_.begin(); + while (it != unacked_fec_packets_.end()) { + QuicPacketSequenceNumber sequence_number = it->first; + if (sequence_number > + received_packet_manager_.peer_largest_observed_packet()) { + break; + } + if (!IsAwaitingPacket(incoming_ack.received_info, sequence_number)) { + DVLOG(1) << ENDPOINT << "Got an ack for fec packet: " << sequence_number; + acked_packets->insert(sequence_number); + unacked_fec_packets_.erase(it++); + } else { + DVLOG(1) << ENDPOINT << "Still missing ack for fec packet: " + << sequence_number; + ++it; + } + } +} + +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 != NULL) { + group->UpdateFec(last_header_.packet_sequence_number, + last_header_.entropy_flag, fec); + } +} + +bool QuicConnection::OnRstStreamFrame(const QuicRstStreamFrame& frame) { + DCHECK(connected_); + if (debug_visitor_) { + debug_visitor_->OnRstStreamFrame(frame); + } + DLOG(INFO) << 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_) { + debug_visitor_->OnConnectionCloseFrame(frame); + } + DLOG(INFO) << ENDPOINT << "Connection closed with error " + << QuicUtils::ErrorToString(frame.error_code) + << " " << frame.error_details; + CloseConnection(frame.error_code, true); + DCHECK(!connected_); + return false; +} + +bool QuicConnection::OnGoAwayFrame(const QuicGoAwayFrame& frame) { + DCHECK(connected_); + DLOG(INFO) << ENDPOINT << "Go away received with error " + << QuicUtils::ErrorToString(frame.error_code) + << " and reason:" << frame.reason_phrase; + last_goaway_frames_.push_back(frame); + return connected_; +} + +void QuicConnection::OnPacketComplete() { + // Don't do anything if this packet closed the connection. + if (!connected_) { + ClearLastFrames(); + return; + } + + DLOG(INFO) << ENDPOINT << (last_packet_revived_ ? "Revived" : "Got") + << " packet " << last_header_.packet_sequence_number + << " with " << last_ack_frames_.size() << " acks, " + << last_congestion_frames_.size() << " congestions, " + << last_goaway_frames_.size() << " goaways, " + << last_rst_frames_.size() << " rsts, " + << last_stream_frames_.size() + << " stream frames for " << last_header_.public_header.guid; + if (!last_packet_revived_) { + congestion_manager_.RecordIncomingPacket( + last_size_, last_header_.packet_sequence_number, + time_of_last_received_packet_, last_packet_revived_); + } + + // Must called before ack processing, because processing acks removes entries + // from unacket_packets_, increasing the least_unacked. + const bool last_packet_should_instigate_ack = ShouldLastPacketInstigateAck(); + + if ((last_stream_frames_.empty() || + visitor_->OnPacket(self_address_, peer_address_, + last_header_, last_stream_frames_))) { + received_packet_manager_.RecordPacketReceived( + last_header_, time_of_last_received_packet_); + } + + // Process stream resets, then acks, then congestion feedback. + 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) { + congestion_manager_.OnIncomingQuicCongestionFeedbackFrame( + last_congestion_frames_[i], time_of_last_received_packet_); + } + + MaybeSendInResponseToPacket(last_packet_should_instigate_ack); + + ClearLastFrames(); +} + +void QuicConnection::ClearLastFrames() { + last_stream_frames_.clear(); + last_goaway_frames_.clear(); + last_rst_frames_.clear(); + last_ack_frames_.clear(); + last_congestion_frames_.clear(); +} + +QuicAckFrame* QuicConnection::CreateAckFrame() { + QuicAckFrame* outgoing_ack = new QuicAckFrame(); + received_packet_manager_.UpdateReceivedPacketInfo( + &(outgoing_ack->received_info), clock_->ApproximateNow()); + UpdateSentPacketInfo(&(outgoing_ack->sent_info)); + DVLOG(1) << ENDPOINT << "Creating ack frame: " << *outgoing_ack; + return outgoing_ack; +} + +QuicCongestionFeedbackFrame* QuicConnection::CreateFeedbackFrame() { + return new QuicCongestionFeedbackFrame(outgoing_congestion_feedback_); +} + +bool QuicConnection::ShouldLastPacketInstigateAck() { + if (!last_stream_frames_.empty() || + !last_goaway_frames_.empty() || + !last_rst_frames_.empty()) { + return true; + } + + // If the peer is still waiting for a packet that we are no + // longer planning to send, we should send an ack to raise + // the high water mark. + if (!last_ack_frames_.empty() && + !last_ack_frames_.back().received_info.missing_packets.empty() && + !unacked_packets_.empty()) { + if (unacked_packets_.begin()->first > + *last_ack_frames_.back().received_info.missing_packets.begin()) { + return true; + } + } + + return false; +} + +void QuicConnection::MaybeSendInResponseToPacket( + bool last_packet_should_instigate_ack) { + // TODO(ianswett): Better merge these two blocks to queue up an ack if + // necessary, then either only send the ack or bundle it with other data. + if (!last_ack_frames_.empty()) { + // Now the we have received an ack, we might be able to send packets which + // are queued locally, or drain streams which are blocked. + QuicTime::Delta delay = congestion_manager_.TimeUntilSend( + time_of_last_received_packet_, NOT_RETRANSMISSION, + HAS_RETRANSMITTABLE_DATA, NOT_HANDSHAKE); + if (delay.IsZero()) { + send_alarm_->Cancel(); + WriteIfNotBlocked(); + } else if (!delay.IsInfinite()) { + send_alarm_->Cancel(); + send_alarm_->Set(time_of_last_received_packet_.Add(delay)); + } + } + + if (!last_packet_should_instigate_ack) { + return; + } + + if (send_ack_in_response_to_packet_) { + SendAck(); + } else if (!last_stream_frames_.empty()) { + // TODO(alyssar) this case should really be "if the packet contained any + // non-ack frame", rather than "if the packet contained a stream frame" + if (!ack_alarm_->IsSet()) { + ack_alarm_->Set(clock_->ApproximateNow().Add( + congestion_manager_.DefaultRetransmissionTime())); + } + } + send_ack_in_response_to_packet_ = !send_ack_in_response_to_packet_; +} + +void QuicConnection::SendVersionNegotiationPacket() { + QuicVersionVector supported_versions; + for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { + supported_versions.push_back(kSupportedQuicVersions[i]); + } + QuicEncryptedPacket* encrypted = + packet_creator_.SerializeVersionNegotiationPacket(supported_versions); + // TODO(satyamshekhar): implement zero server state negotiation. + int error; + helper_->WritePacketToWire(*encrypted, &error); + delete encrypted; +} + +QuicConsumedData QuicConnection::SendStreamData(QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin) { + return packet_generator_.ConsumeData(id, data, offset, fin); +} + +void QuicConnection::SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error) { + packet_generator_.AddControlFrame( + QuicFrame(new QuicRstStreamFrame(id, error))); +} + +const QuicConnectionStats& QuicConnection::GetStats() { + // Update rtt and estimated bandwidth. + stats_.rtt = congestion_manager_.SmoothedRtt().ToMicroseconds(); + stats_.estimated_bandwidth = + congestion_manager_.BandwidthEstimate().ToBytesPerSecond(); + return stats_; +} + +void QuicConnection::ProcessUdpPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) { + if (!connected_) { + return; + } + if (debug_visitor_) { + debug_visitor_->OnPacketReceived(self_address, peer_address, packet); + } + last_packet_revived_ = false; + last_size_ = packet.length(); + + address_migrating_ = false; + + if (peer_address_.address().empty()) { + peer_address_ = peer_address; + } + if (self_address_.address().empty()) { + self_address_ = self_address; + } + + if (!(peer_address == peer_address_ && self_address == self_address_)) { + address_migrating_ = true; + } + + 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 (encryption_level_ != ENCRYPTION_FORWARD_SECURE && + framer_.error() == QUIC_DECRYPTION_FAILURE && + undecryptable_packets_.size() < kMaxUndecryptablePackets) { + QueueUndecryptablePacket(packet); + } + DVLOG(1) << ENDPOINT << "Unable to process packet. Last packet processed: " + << last_header_.packet_sequence_number; + return; + } + MaybeProcessUndecryptablePackets(); + MaybeProcessRevivedPacket(); +} + +bool QuicConnection::OnCanWrite() { + write_blocked_ = false; + return DoWrite(); +} + +bool QuicConnection::WriteIfNotBlocked() { + if (write_blocked_) { + return false; + } + return DoWrite(); +} + +bool QuicConnection::DoWrite() { + DCHECK(!write_blocked_); + WriteQueuedPackets(); + + // 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(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + packet_generator_.StartBatchOperations(); + bool all_bytes_written = visitor_->OnCanWrite(); + packet_generator_.FinishBatchOperations(); + + // 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 (!write_blocked_ && !all_bytes_written && + CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + // 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 quic connections have had a chance to use the socket. + send_alarm_->Cancel(); + send_alarm_->Set(clock_->ApproximateNow()); + } + } + + return !write_blocked_; +} + +bool QuicConnection::ProcessValidatedPacket() { + if (address_migrating_) { + SendConnectionCloseWithDetails( + QUIC_ERROR_MIGRATING_ADDRESS, + "Address migration is not yet a supported feature"); + return false; + } + time_of_last_received_packet_ = clock_->Now(); + DVLOG(1) << ENDPOINT << "time of last received packet: " + << time_of_last_received_packet_.ToDebuggingValue(); + return true; +} + +bool QuicConnection::WriteQueuedPackets() { + DCHECK(!write_blocked_); + + size_t num_queued_packets = queued_packets_.size() + 1; + QueuedPacketList::iterator packet_iterator = queued_packets_.begin(); + while (!write_blocked_ && packet_iterator != queued_packets_.end()) { + // Ensure that from one iteration of this loop to the next we + // succeeded in sending a packet so we don't infinitely loop. + // TODO(rch): clean up and close the connection if we really hit this. + DCHECK_LT(queued_packets_.size(), num_queued_packets); + num_queued_packets = queued_packets_.size(); + if (WritePacket(packet_iterator->encryption_level, + packet_iterator->sequence_number, + packet_iterator->packet, + packet_iterator->retransmittable, + NO_FORCE)) { + packet_iterator = queued_packets_.erase(packet_iterator); + } else { + // Continue, because some queued packets may still be writable. + // This can happen if a retransmit send fail. + ++packet_iterator; + } + } + + return !write_blocked_; +} + +bool QuicConnection::MaybeRetransmitPacketForRTO( + QuicPacketSequenceNumber sequence_number) { + DCHECK_EQ(ContainsKey(unacked_packets_, sequence_number), + ContainsKey(retransmission_map_, sequence_number)); + + if (!ContainsKey(unacked_packets_, sequence_number)) { + DVLOG(2) << ENDPOINT << "alarm fired for " << sequence_number + << " but it has been acked or already retransmitted with" + << " different sequence number."; + // So no extra delay is added for this packet. + return true; + } + + RetransmissionMap::iterator retransmission_it = + retransmission_map_.find(sequence_number); + // If the packet hasn't been acked and we're getting truncated acks, ignore + // any RTO for packets larger than the peer's largest observed packet; it may + // have been received by the peer and just wasn't acked due to the ack frame + // running out of space. + if (received_truncated_ack_ && sequence_number > + received_packet_manager_.peer_largest_observed_packet() && + // We allow retransmission of already retransmitted packets so that we + // retransmit packets that were retransmissions of the packet with + // sequence number < the largest observed field of the truncated ack. + retransmission_it->second.number_retransmissions == 0) { + return false; + } else { + ++stats_.rto_count; + RetransmitPacket(sequence_number); + return true; + } +} + +void QuicConnection::RetransmitUnackedPackets( + RetransmissionType retransmission_type) { + if (unacked_packets_.empty()) { + return; + } + UnackedPacketMap::iterator next_it = unacked_packets_.begin(); + QuicPacketSequenceNumber end_sequence_number = + unacked_packets_.rbegin()->first; + do { + UnackedPacketMap::iterator current_it = next_it; + ++next_it; + + if (retransmission_type == ALL_PACKETS || + current_it->second->encryption_level() == ENCRYPTION_INITIAL) { + // TODO(satyamshekhar): Think about congestion control here. + // Specifically, about the retransmission count of packets being sent + // proactively to achieve 0 (minimal) RTT. + RetransmitPacket(current_it->first); + } + } while (next_it != unacked_packets_.end() && + next_it->first <= end_sequence_number); +} + +void QuicConnection::RetransmitPacket( + QuicPacketSequenceNumber sequence_number) { + UnackedPacketMap::iterator unacked_it = + unacked_packets_.find(sequence_number); + RetransmissionMap::iterator retransmission_it = + retransmission_map_.find(sequence_number); + // There should always be an entry corresponding to |sequence_number| in + // both |retransmission_map_| and |unacked_packets_|. Retransmissions due to + // RTO for sequence numbers that are already acked or retransmitted are + // ignored by MaybeRetransmitPacketForRTO. + DCHECK(unacked_it != unacked_packets_.end()); + DCHECK(retransmission_it != retransmission_map_.end()); + RetransmittableFrames* unacked = unacked_it->second; + // TODO(pwestin): Need to fix potential issue with FEC and a 1 packet + // congestion window see b/8331807 for details. + congestion_manager_.AbandoningPacket(sequence_number); + + // Re-packetize the frames with a new sequence number for retransmission. + // Retransmitted data packets do not use FEC, even when it's enabled. + SerializedPacket serialized_packet = + packet_creator_.SerializeAllFrames(unacked->frames()); + RetransmissionInfo retransmission_info(serialized_packet.sequence_number); + retransmission_info.number_retransmissions = + retransmission_it->second.number_retransmissions + 1; + // Remove info with old sequence number. + unacked_packets_.erase(unacked_it); + retransmission_map_.erase(retransmission_it); + DVLOG(1) << ENDPOINT << "Retransmitting unacked packet " << sequence_number + << " as " << serialized_packet.sequence_number; + DCHECK(unacked_packets_.empty() || + unacked_packets_.rbegin()->first < serialized_packet.sequence_number); + unacked_packets_.insert(make_pair(serialized_packet.sequence_number, + unacked)); + retransmission_map_.insert(make_pair(serialized_packet.sequence_number, + retransmission_info)); + if (debug_visitor_) { + debug_visitor_->OnPacketRetransmitted(sequence_number, + serialized_packet.sequence_number); + } + SendOrQueuePacket(unacked->encryption_level(), + serialized_packet.sequence_number, + serialized_packet.packet, + serialized_packet.entropy_hash, + HAS_RETRANSMITTABLE_DATA); +} + +bool QuicConnection::CanWrite(Retransmission retransmission, + HasRetransmittableData retransmittable, + IsHandshake handshake) { + // TODO(ianswett): If the packet is a retransmit, the current send alarm may + // be too long. + if (write_blocked_ || send_alarm_->IsSet()) { + return false; + } + + QuicTime now = clock_->Now(); + QuicTime::Delta delay = congestion_manager_.TimeUntilSend( + now, retransmission, retransmittable, handshake); + if (delay.IsInfinite()) { + return false; + } + + // If the scheduler requires a delay, then we can not send this packet now. + if (!delay.IsZero()) { + send_alarm_->Cancel(); + send_alarm_->Set(now.Add(delay)); + return false; + } + return true; +} + +bool QuicConnection::IsRetransmission( + QuicPacketSequenceNumber sequence_number) { + RetransmissionMap::iterator it = retransmission_map_.find(sequence_number); + return it != retransmission_map_.end() && + it->second.number_retransmissions > 0; +} + +void QuicConnection::SetupRetransmission( + QuicPacketSequenceNumber sequence_number, + EncryptionLevel level) { + RetransmissionMap::iterator it = retransmission_map_.find(sequence_number); + if (it == retransmission_map_.end()) { + DVLOG(1) << ENDPOINT << "Will not retransmit packet " << sequence_number; + return; + } + + RetransmissionInfo retransmission_info = it->second; + // TODO(rch): consider using a much smaller retransmisison_delay + // for the ENCRYPTION_NONE packets. + size_t effective_retransmission_count = + level == ENCRYPTION_NONE ? 0 : retransmission_info.number_retransmissions; + QuicTime::Delta retransmission_delay = + congestion_manager_.GetRetransmissionDelay( + unacked_packets_.size(), + effective_retransmission_count); + + retransmission_timeouts_.push(RetransmissionTime( + sequence_number, + clock_->ApproximateNow().Add(retransmission_delay), + false)); + + // Do not set the retransmisson alarm if we're already handling the + // retransmission alarm because the retransmission alarm will be reset when + // OnRetransmissionTimeout completes. + if (!handling_retransmission_timeout_ && !retransmission_alarm_->IsSet()) { + retransmission_alarm_->Set( + clock_->ApproximateNow().Add(retransmission_delay)); + } + // TODO(satyamshekhar): restore packet reordering with Ian's TODO in + // SendStreamData(). +} + +void QuicConnection::SetupAbandonFecTimer( + QuicPacketSequenceNumber sequence_number) { + DCHECK(ContainsKey(unacked_fec_packets_, sequence_number)); + QuicTime::Delta retransmission_delay = + QuicTime::Delta::FromMilliseconds( + congestion_manager_.DefaultRetransmissionTime().ToMilliseconds() * 3); + retransmission_timeouts_.push(RetransmissionTime( + sequence_number, + clock_->ApproximateNow().Add(retransmission_delay), + true)); +} + +void QuicConnection::DropPacket(QuicPacketSequenceNumber sequence_number) { + UnackedPacketMap::iterator unacked_it = + unacked_packets_.find(sequence_number); + // Packet was not meant to be retransmitted. + if (unacked_it == unacked_packets_.end()) { + DCHECK(!ContainsKey(retransmission_map_, sequence_number)); + return; + } + // Delete the unacked packet. + delete unacked_it->second; + unacked_packets_.erase(unacked_it); + retransmission_map_.erase(sequence_number); + return; +} + +bool QuicConnection::WritePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + HasRetransmittableData retransmittable, + Force forced) { + if (!connected_) { + DLOG(INFO) << ENDPOINT + << "Not sending packet as connection is disconnected."; + delete packet; + // Returning true because we deleted the packet and the caller shouldn't + // delete it again. + return true; + } + + if (encryption_level_ == ENCRYPTION_FORWARD_SECURE && + level == ENCRYPTION_NONE) { + // Drop packets that are NULL encrypted since the peer won't accept them + // anymore. + DLOG(INFO) << ENDPOINT << "Dropped packet: " << sequence_number + << " since the packet is NULL encrypted."; + DropPacket(sequence_number); + delete packet; + return true; + } + + Retransmission retransmission = IsRetransmission(sequence_number) ? + IS_RETRANSMISSION : NOT_RETRANSMISSION; + IsHandshake handshake = level == ENCRYPTION_NONE ? IS_HANDSHAKE + : NOT_HANDSHAKE; + + // If we are not forced and we can't write, then simply return false; + if (forced == NO_FORCE && + !CanWrite(retransmission, retransmittable, handshake)) { + return false; + } + + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.EncryptPacket(level, sequence_number, *packet)); + DLOG(INFO) << ENDPOINT << "Sending packet number " << sequence_number + << " : " << (packet->is_fec_packet() ? "FEC " : + (retransmittable == HAS_RETRANSMITTABLE_DATA + ? "data bearing " : " ack only ")) + << ", encryption level: " + << QuicUtils::EncryptionLevelToString(level) + << ", length:" << packet->length(); + DVLOG(2) << ENDPOINT << "packet(" << sequence_number << "): " << std::endl + << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece()); + + DCHECK(encrypted->length() <= kMaxPacketSize) + << "Packet " << sequence_number << " will not be read; too large: " + << packet->length() << " " << encrypted->length() << " " + << " forced: " << (forced == FORCE ? "yes" : "no"); + + int error; + QuicTime now = clock_->Now(); + if (!retransmission) { + time_of_last_sent_packet_ = now; + } + DVLOG(1) << ENDPOINT << "time of last sent packet: " + << now.ToDebuggingValue(); + if (WritePacketToWire(sequence_number, level, *encrypted, &error) == -1) { + if (helper_->IsWriteBlocked(error)) { + // TODO(satyashekhar): It might be more efficient (fewer system calls), if + // all connections share this variable i.e this becomes a part of + // PacketWriterInterface. + write_blocked_ = true; + // 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. + return helper_->IsWriteBlockedDataBuffered(); + } + // We can't send an error as the socket is presumably borked. + CloseConnection(QUIC_PACKET_WRITE_ERROR, false); + return false; + } + + // Set the retransmit alarm only when we have sent the packet to the client + // and not when it goes to the pending queue, otherwise we will end up adding + // an entry to retransmission_timeout_ every time we attempt a write. + if (retransmittable == HAS_RETRANSMITTABLE_DATA) { + SetupRetransmission(sequence_number, level); + } else if (packet->is_fec_packet()) { + SetupAbandonFecTimer(sequence_number); + } + + congestion_manager_.SentPacket(sequence_number, now, packet->length(), + retransmission); + + stats_.bytes_sent += encrypted->length(); + ++stats_.packets_sent; + + if (retransmission == IS_RETRANSMISSION) { + stats_.bytes_retransmitted += encrypted->length(); + ++stats_.packets_retransmitted; + } + + delete packet; + return true; +} + +int QuicConnection::WritePacketToWire(QuicPacketSequenceNumber sequence_number, + EncryptionLevel level, + const QuicEncryptedPacket& packet, + int* error) { + int bytes_written = helper_->WritePacketToWire(packet, error); + if (debug_visitor_) { + // WritePacketToWire returned -1, then |error| will be populated with + // an error code, which we want to pass along to the visitor. + debug_visitor_->OnPacketSent(sequence_number, level, packet, + bytes_written == -1 ? *error : bytes_written); + } + return bytes_written; +} + +bool QuicConnection::OnSerializedPacket( + const SerializedPacket& serialized_packet) { + if (serialized_packet.retransmittable_frames != NULL) { + DCHECK(unacked_packets_.empty() || + unacked_packets_.rbegin()->first < + serialized_packet.sequence_number); + // Retransmitted frames will be sent with the same encryption level as the + // original. + serialized_packet.retransmittable_frames->set_encryption_level( + encryption_level_); + unacked_packets_.insert( + make_pair(serialized_packet.sequence_number, + serialized_packet.retransmittable_frames)); + // All unacked packets might be retransmitted. + retransmission_map_.insert( + make_pair(serialized_packet.sequence_number, + RetransmissionInfo(serialized_packet.sequence_number))); + } else if (serialized_packet.packet->is_fec_packet()) { + unacked_fec_packets_.insert(make_pair( + serialized_packet.sequence_number, + serialized_packet.retransmittable_frames)); + } + return SendOrQueuePacket(encryption_level_, + serialized_packet.sequence_number, + serialized_packet.packet, + serialized_packet.entropy_hash, + serialized_packet.retransmittable_frames != NULL ? + HAS_RETRANSMITTABLE_DATA : + NO_RETRANSMITTABLE_DATA); +} + +bool QuicConnection::SendOrQueuePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + QuicPacketEntropyHash entropy_hash, + HasRetransmittableData retransmittable) { + sent_entropy_manager_.RecordPacketEntropyHash(sequence_number, entropy_hash); + if (!WritePacket(level, sequence_number, packet, retransmittable, NO_FORCE)) { + queued_packets_.push_back(QueuedPacket(sequence_number, packet, level, + retransmittable)); + return false; + } + return true; +} + +bool QuicConnection::ShouldSimulateLostPacket() { + // TODO(rch): enable this + return false; + /* + return FLAGS_fake_packet_loss_percentage > 0 && + random_->Rand32() % 100 < FLAGS_fake_packet_loss_percentage; + */ +} + +void QuicConnection::UpdateSentPacketInfo(SentPacketInfo* sent_info) { + if (!unacked_packets_.empty()) { + sent_info->least_unacked = unacked_packets_.begin()->first; + } else { + // If there are no unacked packets, set the least unacked packet to + // sequence_number() + 1 since that will be the sequence number of this + // ack packet whenever it is sent. + sent_info->least_unacked = packet_creator_.sequence_number() + 1; + } + sent_info->entropy_hash = sent_entropy_manager_.EntropyHash( + sent_info->least_unacked - 1); +} + +void QuicConnection::SendAck() { + ack_alarm_->Cancel(); + + // TODO(rch): delay this until the CreateFeedbackFrame + // method is invoked. This requires changes SetShouldSendAck + // to be a no-arg method, and re-jiggering its implementation. + bool send_feedback = false; + if (congestion_manager_.GenerateCongestionFeedback( + &outgoing_congestion_feedback_)) { + DVLOG(1) << ENDPOINT << "Sending feedback " + << outgoing_congestion_feedback_; + send_feedback = true; + } + + packet_generator_.SetShouldSendAck(send_feedback); +} + +void QuicConnection::MaybeAbandonFecPacket( + QuicPacketSequenceNumber sequence_number) { + if (!ContainsKey(unacked_fec_packets_, sequence_number)) { + DVLOG(2) << ENDPOINT << "no need to abandon fec packet: " + << sequence_number << "; it's already acked'"; + return; + } + congestion_manager_.AbandoningPacket(sequence_number); + // TODO(satyashekhar): Should this decrease the congestion window? +} + +QuicTime QuicConnection::OnRetransmissionTimeout() { + // This guards against registering the alarm later than we should. + // + // If we have packet A and B in the list and we call + // MaybeRetransmitPacketForRTO on A, that may trigger a call to + // SetRetransmissionAlarm if A is retransmitted as C. In that case we + // don't want to register the alarm under SetRetransmissionAlarm; we + // want to set it to the RTO of B when we return from this function. + handling_retransmission_timeout_ = true; + + for (size_t i = 0; i < max_packets_per_retransmission_alarm_ && + !retransmission_timeouts_.empty(); ++i) { + RetransmissionTime retransmission_time = retransmission_timeouts_.top(); + DCHECK(retransmission_time.scheduled_time.IsInitialized()); + if (retransmission_time.scheduled_time > clock_->ApproximateNow()) { + break; + } + retransmission_timeouts_.pop(); + + if (retransmission_time.for_fec) { + MaybeAbandonFecPacket(retransmission_time.sequence_number); + continue; + } else if ( + !MaybeRetransmitPacketForRTO(retransmission_time.sequence_number)) { + DLOG(INFO) << ENDPOINT << "MaybeRetransmitPacketForRTO failed: " + << "adding an extra delay for " + << retransmission_time.sequence_number; + retransmission_time.scheduled_time = clock_->ApproximateNow().Add( + congestion_manager_.DefaultRetransmissionTime()); + retransmission_timeouts_.push(retransmission_time); + } + } + + handling_retransmission_timeout_ = false; + + if (retransmission_timeouts_.empty()) { + return QuicTime::Zero(); + } + + // We have packets remaining. Return the absolute RTO of the oldest packet + // on the list. + return retransmission_timeouts_.top().scheduled_time; +} + +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; +} + +void QuicConnection::SetDecrypter(QuicDecrypter* decrypter) { + framer_.SetDecrypter(decrypter); +} + +void QuicConnection::SetAlternativeDecrypter(QuicDecrypter* decrypter, + bool latch_once_used) { + framer_.SetAlternativeDecrypter(decrypter, 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."; + char* data = new char[packet.length()]; + memcpy(data, packet.data(), packet.length()); + undecryptable_packets_.push_back( + new QuicEncryptedPacket(data, packet.length(), true)); +} + +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!"; + 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) { + STLDeleteElements(&undecryptable_packets_); + } +} + +void QuicConnection::MaybeProcessRevivedPacket() { + QuicFecGroup* group = GetFecGroup(); + if (!connected_ || group == NULL || !group->CanRevive()) { + return; + } + QuicPacketHeader revived_header; + char revived_payload[kMaxPacketSize]; + size_t len = group->Revive(&revived_header, revived_payload, kMaxPacketSize); + revived_header.public_header.guid = guid_; + revived_header.public_header.version_flag = false; + revived_header.public_header.reset_flag = false; + 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); + delete group; + + last_packet_revived_ = true; + if (debug_visitor_) { + 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 NULL; + } + 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 NULL; + } + // 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::SendConnectionClosePacket(QuicErrorCode error, + const string& details) { + DLOG(INFO) << ENDPOINT << "Force closing with error " + << QuicUtils::ErrorToString(error) << " (" << error << ") " + << details; + QuicConnectionCloseFrame frame; + frame.error_code = error; + frame.error_details = details; + UpdateSentPacketInfo(&frame.ack_frame.sent_info); + received_packet_manager_.UpdateReceivedPacketInfo( + &frame.ack_frame.received_info, clock_->ApproximateNow()); + + SerializedPacket serialized_packet = + packet_creator_.SerializeConnectionClose(&frame); + + // We need to update the sent entropy hash for all sent packets. + sent_entropy_manager_.RecordPacketEntropyHash( + serialized_packet.sequence_number, + serialized_packet.entropy_hash); + + if (!WritePacket(encryption_level_, + serialized_packet.sequence_number, + serialized_packet.packet, + serialized_packet.retransmittable_frames != NULL ? + HAS_RETRANSMITTABLE_DATA : NO_RETRANSMITTABLE_DATA, + FORCE)) { + delete serialized_packet.packet; + } +} + +void QuicConnection::SendConnectionCloseWithDetails(QuicErrorCode error, + const string& details) { + if (!write_blocked_) { + SendConnectionClosePacket(error, details); + } + CloseConnection(error, false); +} + +void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { + DCHECK(connected_); + connected_ = false; + visitor_->ConnectionClose(error, from_peer); +} + +void QuicConnection::SendGoAway(QuicErrorCode error, + QuicStreamId last_good_stream_id, + const string& reason) { + DLOG(INFO) << ENDPOINT << "Going away with error " + << QuicUtils::ErrorToString(error) + << " (" << error << ")"; + 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; + } +} + +bool QuicConnection::HasQueuedData() const { + return !queued_packets_.empty() || packet_generator_.HasQueuedFrames(); +} + +void QuicConnection::SetIdleNetworkTimeout(QuicTime::Delta timeout) { + if (timeout < idle_network_timeout_) { + idle_network_timeout_ = timeout; + CheckForTimeout(); + } else { + idle_network_timeout_ = timeout; + } +} + +void QuicConnection::SetOverallConnectionTimeout(QuicTime::Delta timeout) { + if (timeout < overall_connection_timeout_) { + overall_connection_timeout_ = timeout; + CheckForTimeout(); + } else { + overall_connection_timeout_ = timeout; + } +} + +bool QuicConnection::CheckForTimeout() { + QuicTime now = clock_->ApproximateNow(); + QuicTime time_of_last_packet = std::max(time_of_last_received_packet_, + time_of_last_sent_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 delta = now.Subtract(time_of_last_packet); + DVLOG(1) << ENDPOINT << "last packet " + << time_of_last_packet.ToDebuggingValue() + << " now:" << now.ToDebuggingValue() + << " delta:" << delta.ToMicroseconds() + << " network_timeout: " << idle_network_timeout_.ToMicroseconds(); + if (delta >= idle_network_timeout_) { + DVLOG(1) << ENDPOINT << "Connection timedout due to no network activity."; + SendConnectionClose(QUIC_CONNECTION_TIMED_OUT); + return true; + } + + // Next timeout delta. + QuicTime::Delta timeout = idle_network_timeout_.Subtract(delta); + + if (!overall_connection_timeout_.IsInfinite()) { + QuicTime::Delta connected_time = now.Subtract(creation_time_); + DVLOG(1) << ENDPOINT << "connection time: " + << connected_time.ToMilliseconds() << " overall timeout: " + << overall_connection_timeout_.ToMilliseconds(); + if (connected_time >= overall_connection_timeout_) { + DVLOG(1) << ENDPOINT << + "Connection timedout due to overall connection timeout."; + SendConnectionClose(QUIC_CONNECTION_TIMED_OUT); + return true; + } + + // Take the min timeout. + QuicTime::Delta connection_timeout = + overall_connection_timeout_.Subtract(connected_time); + if (connection_timeout < timeout) { + timeout = connection_timeout; + } + } + + timeout_alarm_->Cancel(); + timeout_alarm_->Set(clock_->ApproximateNow().Add(timeout)); + return false; +} + +} // namespace net diff --git a/chromium/net/quic/quic_connection.h b/chromium/net/quic/quic_connection.h new file mode 100644 index 00000000000..20878d91bc7 --- /dev/null +++ b/chromium/net/quic/quic_connection.h @@ -0,0 +1,698 @@ +// 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 <deque> +#include <list> +#include <map> +#include <queue> +#include <set> +#include <vector> + +#include "base/containers/hash_tables.h" +#include "net/base/ip_endpoint.h" +#include "net/base/linked_hash_map.h" +#include "net/quic/congestion_control/quic_congestion_manager.h" +#include "net/quic/quic_alarm.h" +#include "net/quic/quic_blocked_writer_interface.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_packet_generator.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_stats.h" + +namespace net { + +class QuicClock; +class QuicConnection; +class QuicFecGroup; +class QuicRandom; + +namespace test { +class QuicConnectionPeer; +} // namespace test + +class NET_EXPORT_PRIVATE QuicConnectionVisitorInterface { + public: + virtual ~QuicConnectionVisitorInterface() {} + + // A simple visitor interface for dealing with data frames. The session + // should determine if all frames will be accepted, and return true if so. + // If any frames can't be processed or buffered, none of the data should + // be used, and the callee should return false. + virtual bool OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const std::vector<QuicStreamFrame>& frame) = 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 ConnectionClose(QuicErrorCode error, + bool from_peer) = 0; + + // Called when packets are acked by the peer. + virtual void OnAck(const SequenceNumberSet& acked_packets) = 0; + + // Called when a blocked socket becomes writable. If all pending bytes for + // this visitor are consumed by the connection successfully this should + // return true, otherwise it should return false. + virtual bool OnCanWrite() = 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 QuicConnectionDebugVisitorInterface + : public QuicPacketGenerator::DebugDelegateInterface { + public: + virtual ~QuicConnectionDebugVisitorInterface() {} + + // Called when a packet has been sent. + virtual void OnPacketSent(QuicPacketSequenceNumber sequence_number, + EncryptionLevel level, + const QuicEncryptedPacket& packet, + int rv) = 0; + + // Called when the contents of a packet have been retransmitted as + // a new packet. + virtual void OnPacketRetransmitted( + QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number) = 0; + + // 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) = 0; + + // Called when the protocol version on the received packet doensn't match + // current protocol version of the connection. + virtual void OnProtocolVersionMismatch(QuicVersion version) = 0; + + // Called when the complete header of a packet has been parsed. + virtual void OnPacketHeader(const QuicPacketHeader& header) = 0; + + // Called when a StreamFrame has been parsed. + virtual void OnStreamFrame(const QuicStreamFrame& frame) = 0; + + // Called when a AckFrame has been parsed. + virtual void OnAckFrame(const QuicAckFrame& frame) = 0; + + // Called when a CongestionFeedbackFrame has been parsed. + virtual void OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& frame) = 0; + + // Called when a RstStreamFrame has been parsed. + virtual void OnRstStreamFrame(const QuicRstStreamFrame& frame) = 0; + + // Called when a ConnectionCloseFrame has been parsed. + virtual void OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) = 0; + + // Called when a public reset packet has been received. + virtual void OnPublicResetPacket(const QuicPublicResetPacket& packet) = 0; + + // Called when a version negotiation packet has been received. + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) = 0; + + // 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) = 0; +}; + +class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { + public: + virtual ~QuicConnectionHelperInterface() {} + + // Sets the QuicConnection to be used by this helper. This method + // must only be called once. + virtual void SetConnection(QuicConnection* connection) = 0; + + // 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; + + // Sends the packet out to the peer, possibly simulating packet + // loss if FLAGS_fake_packet_loss_percentage is set. If the write + // succeeded, returns the number of bytes written. If the write + // failed, returns -1 and the error code will be copied to |*error|. + virtual int WritePacketToWire(const QuicEncryptedPacket& packet, + int* error) = 0; + + // Returns true if the helper buffers and subsequently rewrites data + // when an attempt to write results in the underlying socket becoming + // write blocked. + virtual bool IsWriteBlockedDataBuffered() = 0; + + // Returns true if |error| represents a write-block error code such + // as EAGAIN or ERR_IO_PENDING. + virtual bool IsWriteBlocked(int error) = 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: + enum Force { + NO_FORCE, + FORCE + }; + + enum RetransmissionType { + INITIAL_ENCRYPTION_ONLY, + ALL_PACKETS + }; + + // Constructs a new QuicConnection for the specified |guid| and |address|. + // |helper| will be owned by this connection. + QuicConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper, + bool is_server, + QuicVersion version); + virtual ~QuicConnection(); + + static void DeleteEnclosedFrame(QuicFrame* frame); + + // Send the data payload 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. + QuicConsumedData SendStreamData(QuicStreamId id, + base::StringPiece data, + QuicStreamOffset offset, + bool fin); + // Send a stream reset frame to the peer. + virtual void SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error); + + // 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. + void CloseConnection(QuicErrorCode error, bool from_peer); + 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. Returns false if the socket has become blocked. + virtual bool OnCanWrite() OVERRIDE; + + // If the socket is not blocked, this allows queued writes to happen. Returns + // false if the socket has become blocked. + bool WriteIfNotBlocked(); + + // 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. + bool ProcessValidatedPacket(); + + // The version of the protocol this connection is using. + QuicVersion version() const { return framer_.version(); } + + // 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 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 OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; + virtual bool OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) OVERRIDE; + virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE; + virtual void OnFecData(const QuicFecData& fec) OVERRIDE; + virtual void OnPacketComplete() OVERRIDE; + + // QuicPacketGenerator::DelegateInterface + virtual bool CanWrite( + Retransmission is_retransmission, + HasRetransmittableData has_retransmittable_data, + IsHandshake handshake) OVERRIDE; + virtual QuicAckFrame* CreateAckFrame() OVERRIDE; + virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() OVERRIDE; + virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE; + + // Accessors + void set_visitor(QuicConnectionVisitorInterface* visitor) { + visitor_ = visitor; + } + void set_debug_visitor(QuicConnectionDebugVisitorInterface* debug_visitor) { + debug_visitor_ = debug_visitor; + packet_generator_.set_debug_delegate(debug_visitor); + } + const IPEndPoint& self_address() const { return self_address_; } + const IPEndPoint& peer_address() const { return peer_address_; } + QuicGuid guid() const { return guid_; } + const QuicClock* clock() const { return clock_; } + QuicRandom* random_generator() const { return random_generator_; } + + // Called by a RetransmissionAlarm when the timer goes off. If the peer + // appears to be sending truncated acks, this returns false to indicate + // failure, otherwise it calls MaybeRetransmitPacket and returns true. + bool MaybeRetransmitPacketForRTO(QuicPacketSequenceNumber sequence_number); + + // Called to retransmit a packet, in the case a packet was sufficiently + // nacked by the peer, or not acked within the time out window. + void RetransmitPacket(QuicPacketSequenceNumber sequence_number); + + QuicPacketCreator::Options* options() { return packet_creator_.options(); } + + bool connected() { return connected_; } + + size_t NumFecGroups() const { return group_map_.size(); } + + // Testing only. + size_t NumQueuedPackets() const { return queued_packets_.size(); } + + // Returns true if the connection has queued packets or frames. + bool HasQueuedData() const; + + // 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); + + // If the connection has timed out, this will close the connection and return + // true. Otherwise, it will return false and will reset the timeout alarm. + bool CheckForTimeout(); + + // Returns true of the next packet to be sent should be "lost" by + // not actually writing it to the wire. + bool ShouldSimulateLostPacket(); + + // Sets up a packet with an QuicAckFrame and sends it out. + void SendAck(); + + // Called when an RTO fires. Returns the time when this alarm + // should next fire, or 0 if no retransmission alarm should be set. + QuicTime OnRetransmissionTimeout(); + + // Retransmits unacked packets which were sent with initial encryption, if + // |initial_encryption_only| is true, otherwise retransmits all unacked + // packets. Used when the negotiated protocol version is different than what + // was initially assumed and when the visitor wants to re-transmit packets + // with initial encryption when the initial encrypter changes. + void RetransmitUnackedPackets(RetransmissionType retransmission_type); + + // 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. + void SetDecrypter(QuicDecrypter* decrypter); + + // SetAlternativeDecrypter sets a decrypter that may be used to decrypt + // future packets and takes ownership of it. 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, + bool latch_once_used); + + const QuicDecrypter* decrypter() const; + const QuicDecrypter* alternative_decrypter() const; + + protected: + // Send a packet to the peer using encryption |level|. If |sequence_number| + // is present in the |retransmission_map_|, then contents of this packet will + // be retransmitted with a new sequence number if it's not acked by the peer. + // Deletes |packet| via WritePacket call or transfers ownership to + // QueuedPacket, ultimately deleted via WritePacket. Also, it updates the + // entropy map corresponding to |sequence_number| using |entropy_hash|. + // TODO(wtc): none of the callers check the return value. + virtual bool SendOrQueuePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + QuicPacketEntropyHash entropy_hash, + HasRetransmittableData retransmittable); + + // Writes the given packet to socket, encrypted with |level|, with the help + // of helper. Returns true on successful write, false otherwise. However, + // behavior is undefined if connection is not established or broken. In any + // circumstances, a return value of true implies that |packet| has been + // deleted and should not be accessed. If |sequence_number| is present in + // |retransmission_map_| it also sets up retransmission of the given packet + // in case of successful write. If |force| is FORCE, then the packet will be + // sent immediately and the send scheduler will not be consulted. + bool WritePacket(EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + HasRetransmittableData retransmittable, + Force force); + + int WritePacketToWire(QuicPacketSequenceNumber sequence_number, + EncryptionLevel level, + const QuicEncryptedPacket& packet, + int* error); + + // Make sure an ack we got from our peer is sane. + bool ValidateAckFrame(const QuicAckFrame& incoming_ack); + + QuicConnectionHelperInterface* helper() { return helper_.get(); } + + // 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); + + QuicFramer framer_; + + private: + friend class test::QuicConnectionPeer; + + // Packets which have not been written to the wire. + // Owns the QuicPacket* packet. + struct QueuedPacket { + QueuedPacket(QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + EncryptionLevel level, + HasRetransmittableData retransmittable) + : sequence_number(sequence_number), + packet(packet), + encryption_level(level), + retransmittable(retransmittable) { + } + + QuicPacketSequenceNumber sequence_number; + QuicPacket* packet; + const EncryptionLevel encryption_level; + HasRetransmittableData retransmittable; + }; + + struct RetransmissionInfo { + explicit RetransmissionInfo(QuicPacketSequenceNumber sequence_number) + : sequence_number(sequence_number), + number_nacks(0), + number_retransmissions(0) { + } + + QuicPacketSequenceNumber sequence_number; + size_t number_nacks; + size_t number_retransmissions; + }; + + struct RetransmissionTime { + RetransmissionTime(QuicPacketSequenceNumber sequence_number, + const QuicTime& scheduled_time, + bool for_fec) + : sequence_number(sequence_number), + scheduled_time(scheduled_time), + for_fec(for_fec) { } + + QuicPacketSequenceNumber sequence_number; + QuicTime scheduled_time; + bool for_fec; + }; + + class RetransmissionTimeComparator { + public: + bool operator()(const RetransmissionTime& lhs, + const RetransmissionTime& rhs) const { + DCHECK(lhs.scheduled_time.IsInitialized() && + rhs.scheduled_time.IsInitialized()); + return lhs.scheduled_time > rhs.scheduled_time; + } + }; + + typedef std::list<QueuedPacket> QueuedPacketList; + typedef linked_hash_map<QuicPacketSequenceNumber, + RetransmittableFrames*> UnackedPacketMap; + typedef std::map<QuicFecGroupNumber, QuicFecGroup*> FecGroupMap; + typedef base::hash_map<QuicPacketSequenceNumber, + RetransmissionInfo> RetransmissionMap; + typedef std::priority_queue<RetransmissionTime, + std::vector<RetransmissionTime>, + RetransmissionTimeComparator> + RetransmissionTimeouts; + + // Sends a version negotiation packet to the peer. + void SendVersionNegotiationPacket(); + + void SetupRetransmission(QuicPacketSequenceNumber sequence_number, + EncryptionLevel level); + bool IsRetransmission(QuicPacketSequenceNumber sequence_number); + + void SetupAbandonFecTimer(QuicPacketSequenceNumber sequence_number); + + // Clears any accumulated frames from the last received packet. + void ClearLastFrames(); + + // Called from OnCanWrite and WriteIfNotBlocked to write queued packets. + // Returns false if the socket has become blocked. + bool DoWrite(); + + // Drop packet corresponding to |sequence_number| by deleting entries from + // |unacked_packets_| and |retransmission_map_|, if present. We need to drop + // all packets with encryption level NONE after the default level has been set + // to FORWARD_SECURE. + void DropPacket(QuicPacketSequenceNumber sequence_number); + + // Writes as many queued packets as possible. The connection must not be + // blocked when this is called. + bool WriteQueuedPackets(); + + // 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 HandleAckForSentPackets(const QuicAckFrame& incoming_ack, + SequenceNumberSet* acked_packets); + void HandleAckForSentFecPackets(const QuicAckFrame& incoming_ack, + SequenceNumberSet* acked_packets); + + // Update the |sent_info| for an outgoing ack. + void UpdateSentPacketInfo(SentPacketInfo* sent_info); + + // Checks if the last packet should instigate an ack. + bool ShouldLastPacketInstigateAck(); + + // 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(bool last_packet_should_instigate_ack); + + void MaybeAbandonFecPacket(QuicPacketSequenceNumber sequence_number); + + // Get the FEC group associate with the last processed packet or NULL, if the + // group has already been deleted. + QuicFecGroup* GetFecGroup(); + + // Closes any FEC groups protecting packets before |sequence_number|. + void CloseFecGroupsBefore(QuicPacketSequenceNumber sequence_number); + + scoped_ptr<QuicConnectionHelperInterface> helper_; + EncryptionLevel encryption_level_; + const QuicClock* clock_; + QuicRandom* random_generator_; + + const QuicGuid guid_; + // Address on the last successfully processed packet received from the + // client. + IPEndPoint self_address_; + IPEndPoint peer_address_; + + bool last_packet_revived_; // True if the last packet was revived from FEC. + size_t last_size_; // Size of the last received packet. + QuicPacketHeader last_header_; + std::vector<QuicStreamFrame> last_stream_frames_; + std::vector<QuicAckFrame> last_ack_frames_; + std::vector<QuicCongestionFeedbackFrame> last_congestion_frames_; + std::vector<QuicRstStreamFrame> last_rst_frames_; + std::vector<QuicGoAwayFrame> last_goaway_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_; + + // When new packets are created which may be retransmitted, they are added + // to this map, which contains owning pointers to the contained frames. + UnackedPacketMap unacked_packets_; + + // Pending fec packets that have not been acked yet. These packets need to be + // cleared out of the cgst_window after a timeout since FEC packets are never + // retransmitted. + // Ask: What should be the timeout for these packets? + UnackedPacketMap unacked_fec_packets_; + + // 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_; + + // Heap of packets that we might need to retransmit, and the time at + // which we should retransmit them. Every time a packet is sent it is added + // to this heap which is O(log(number of pending packets to be retransmitted)) + // which might be costly. This should be optimized to O(1) by maintaining a + // priority queue of lists of packets to be retransmitted, where list x + // contains all packets that have been retransmitted x times. + RetransmissionTimeouts retransmission_timeouts_; + + // Map from sequence number to the retransmission info. + RetransmissionMap retransmission_map_; + + // True while OnRetransmissionTimeout is running to prevent + // SetRetransmissionAlarm from being called erroneously. + bool handling_retransmission_timeout_; + + // 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_; + + // True when the socket becomes unwritable. + bool write_blocked_; + + FecGroupMap group_map_; + + QuicReceivedPacketManager received_packet_manager_; + QuicSentEntropyManager sent_entropy_manager_; + + // 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 fires when the connection may have timed out. + scoped_ptr<QuicAlarm> timeout_alarm_; + + QuicConnectionVisitorInterface* visitor_; + QuicConnectionDebugVisitorInterface* debug_visitor_; + QuicPacketCreator packet_creator_; + 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_; + // Connection creation time. + QuicTime creation_time_; + + // 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 time that we last sent a packet for this connection. + QuicTime time_of_last_sent_packet_; + + // Congestion manager which controls the rate the connection sends packets + // as well as collecting and generating congestion feedback. + QuicCongestionManager congestion_manager_; + + // The state of connection in version negotiation finite state machine. + QuicVersionNegotiationState version_negotiation_state_; + + size_t max_packets_per_retransmission_alarm_; + + // 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_; + + // True if the last ack received from the peer may have been truncated. False + // otherwise. + bool received_truncated_ack_; + bool send_ack_in_response_to_packet_; + + // Set to true if the udp packet headers have a new self or peer address. + // This is checked later on validating a data or version negotiation packet. + bool address_migrating_; + + DISALLOW_COPY_AND_ASSIGN(QuicConnection); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONNECTION_H_ diff --git a/chromium/net/quic/quic_connection_helper.cc b/chromium/net/quic/quic_connection_helper.cc new file mode 100644 index 00000000000..c3e796fa321 --- /dev/null +++ b/chromium/net/quic/quic_connection_helper.cc @@ -0,0 +1,153 @@ +// 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/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_posted_(false), + weak_factory_(this) {} + + protected: + virtual void SetImpl() OVERRIDE { + DCHECK(deadline().IsInitialized()); + if (task_posted_) { + // 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; + } + + 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_posted_ = true; + } + + 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_posted_); + task_posted_ = false; + // 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_; + bool task_posted_; + base::WeakPtrFactory<QuicChromeAlarm> weak_factory_; +}; + +} // namespace + +QuicConnectionHelper::QuicConnectionHelper(base::TaskRunner* task_runner, + const QuicClock* clock, + QuicRandom* random_generator, + DatagramClientSocket* socket) + : weak_factory_(this), + task_runner_(task_runner), + socket_(socket), + clock_(clock), + random_generator_(random_generator) { +} + +QuicConnectionHelper::~QuicConnectionHelper() { +} + +void QuicConnectionHelper::SetConnection(QuicConnection* connection) { + connection_ = connection; +} + +const QuicClock* QuicConnectionHelper::GetClock() const { + return clock_; +} + +QuicRandom* QuicConnectionHelper::GetRandomGenerator() { + return random_generator_; +} + +int QuicConnectionHelper::WritePacketToWire( + const QuicEncryptedPacket& packet, + int* error) { + if (connection_->ShouldSimulateLostPacket()) { + DLOG(INFO) << "Dropping packet due to fake packet loss."; + *error = 0; + return packet.length(); + } + + scoped_refptr<StringIOBuffer> buf( + new StringIOBuffer(std::string(packet.data(), + packet.length()))); + int rv = socket_->Write(buf.get(), + packet.length(), + base::Bind(&QuicConnectionHelper::OnWriteComplete, + weak_factory_.GetWeakPtr())); + if (rv >= 0) { + *error = 0; + } else { + *error = rv; + rv = -1; + } + return rv; +} + +bool QuicConnectionHelper::IsWriteBlockedDataBuffered() { + // Chrome sockets' Write() methods buffer the data until the Write is + // permitted. + return true; +} + +bool QuicConnectionHelper::IsWriteBlocked(int error) { + return error == ERR_IO_PENDING; +} + +QuicAlarm* QuicConnectionHelper::CreateAlarm(QuicAlarm::Delegate* delegate) { + return new QuicChromeAlarm(clock_, task_runner_, delegate); +} + +void QuicConnectionHelper::OnWriteComplete(int result) { + // TODO(rch): Inform the connection about the result. + connection_->OnCanWrite(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_connection_helper.h b/chromium/net/quic/quic_connection_helper.h new file mode 100644 index 00000000000..6667b958ab8 --- /dev/null +++ b/chromium/net/quic/quic_connection_helper.h @@ -0,0 +1,72 @@ +// 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/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; + +namespace test { +class QuicConnectionHelperPeer; +} // namespace test + +class NET_EXPORT_PRIVATE QuicConnectionHelper + : public QuicConnectionHelperInterface { + public: + QuicConnectionHelper(base::TaskRunner* task_runner, + const QuicClock* clock, + QuicRandom* random_generator, + DatagramClientSocket* socket); + + virtual ~QuicConnectionHelper(); + + // QuicConnectionHelperInterface + virtual void SetConnection(QuicConnection* connection) OVERRIDE; + virtual const QuicClock* GetClock() const OVERRIDE; + virtual QuicRandom* GetRandomGenerator() OVERRIDE; + virtual int WritePacketToWire(const QuicEncryptedPacket& packet, + int* error) OVERRIDE; + virtual bool IsWriteBlockedDataBuffered() OVERRIDE; + virtual bool IsWriteBlocked(int error) OVERRIDE; + virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE; + + private: + friend class test::QuicConnectionHelperPeer; + + // A completion callback invoked when a write completes. + void OnWriteComplete(int result); + + base::WeakPtrFactory<QuicConnectionHelper> weak_factory_; + base::TaskRunner* task_runner_; + DatagramClientSocket* socket_; + QuicConnection* connection_; + const QuicClock* clock_; + QuicRandom* random_generator_; + + DISALLOW_COPY_AND_ASSIGN(QuicConnectionHelper); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONNECTION_HELPER_H_ diff --git a/chromium/net/quic/quic_connection_helper_test.cc b/chromium/net/quic/quic_connection_helper_test.cc new file mode 100644 index 00000000000..4822ea67541 --- /dev/null +++ b/chromium/net/quic/quic_connection_helper_test.cc @@ -0,0 +1,474 @@ +// 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 <vector> + +#include "net/base/net_errors.h" +#include "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/test_tools/mock_clock.h" +#include "net/quic/test_tools/quic_connection_peer.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 "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +namespace net { +namespace test { + +const char kData[] = "foo"; +const bool kFromPeer = true; + +class TestDelegate : public QuicAlarm::Delegate { + public: + TestDelegate() : fired_(false) {} + + virtual QuicTime OnAlarm() OVERRIDE { + fired_ = true; + return QuicTime::Zero(); + } + + bool fired() const { return fired_; } + + private: + bool fired_; +}; + +class TestConnection : public QuicConnection { + public: + TestConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelper* helper) + : QuicConnection(guid, address, helper, false, QuicVersionMax()) { + } + + void SendAck() { + QuicConnectionPeer::SendAck(this); + } + + void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { + QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); + } + + using QuicConnection::SendOrQueuePacket; +}; + +class QuicConnectionHelperTest : public ::testing::Test { + protected: + // 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; + }; + + QuicConnectionHelperTest() + : guid_(2), + framer_(QuicVersionMax(), QuicTime::Zero(), false), + net_log_(BoundNetLog()), + frame_(1, false, 0, kData) { + Initialize(); + } + + ~QuicConnectionHelperTest() { + for (size_t i = 0; i < writes_.size(); i++) { + delete writes_[i].packet; + } + } + + // Adds a packet to the list of expected writes. + void AddWrite(IoMode mode, QuicEncryptedPacket* packet) { + writes_.push_back(PacketToWrite(mode, packet)); + } + + // 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(); + } + + // 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(NULL, 0, mock_writes_.get(), + writes_.size())); + + socket_.reset(new MockUDPClientSocket(socket_data_.get(), + net_log_.net_log())); + socket_->Connect(IPEndPoint()); + runner_ = new TestTaskRunner(&clock_); + helper_ = new QuicConnectionHelper(runner_.get(), &clock_, + &random_generator_, socket_.get()); + send_algorithm_ = new testing::StrictMock<MockSendAlgorithm>(); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)). + WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); + connection_.reset(new TestConnection(guid_, IPEndPoint(), helper_)); + connection_->set_visitor(&visitor_); + connection_->SetSendAlgorithm(send_algorithm_); + } + + // Returns a newly created packet to send kData on stream 1. + QuicEncryptedPacket* ConstructDataPacket( + QuicPacketSequenceNumber sequence_number) { + InitializeHeader(sequence_number); + + return ConstructPacket(header_, QuicFrame(&frame_)); + } + + // Returns a newly created packet to send kData on stream 1. + QuicPacket* ConstructRawDataPacket( + QuicPacketSequenceNumber sequence_number) { + InitializeHeader(sequence_number); + + QuicFrames frames; + frames.push_back(QuicFrame(&frame_)); + return framer_.BuildUnsizedDataPacket(header_, frames).packet; + } + + // Returns a newly created packet to send ack data. + QuicEncryptedPacket* ConstructAckPacket( + QuicPacketSequenceNumber sequence_number) { + InitializeHeader(sequence_number); + + QuicAckFrame ack(0, QuicTime::Zero(), sequence_number); + ack.sent_info.entropy_hash = 0; + ack.received_info.entropy_hash = 0; + + QuicCongestionFeedbackFrame feedback; + feedback.type = kTCP; + feedback.tcp.accumulated_number_of_lost_packets = 0; + feedback.tcp.receive_window = 16000 << 4; + + QuicFrames frames; + frames.push_back(QuicFrame(&ack)); + frames.push_back(QuicFrame(&feedback)); + scoped_ptr<QuicPacket> packet( + framer_.BuildUnsizedDataPacket(header_, frames).packet); + return framer_.EncryptPacket( + ENCRYPTION_NONE, header_.packet_sequence_number, *packet); + } + + // Returns a newly created packet to send a connection close frame. + QuicEncryptedPacket* ConstructClosePacket( + QuicPacketSequenceNumber sequence_number, + QuicPacketSequenceNumber least_waiting) { + InitializeHeader(sequence_number); + + QuicFrames frames; + QuicAckFrame ack(0, QuicTime::Zero(), least_waiting + 1); + ack.sent_info.entropy_hash = 0; + ack.received_info.entropy_hash = 0; + QuicConnectionCloseFrame close; + close.error_code = QUIC_CONNECTION_TIMED_OUT; + close.ack_frame = ack; + + return ConstructPacket(header_, QuicFrame(&close)); + } + + testing::StrictMock<MockSendAlgorithm>* send_algorithm_; + scoped_refptr<TestTaskRunner> runner_; + QuicConnectionHelper* helper_; + scoped_ptr<MockUDPClientSocket> socket_; + scoped_ptr<MockWrite[]> mock_writes_; + MockClock clock_; + MockRandom random_generator_; + scoped_ptr<TestConnection> connection_; + testing::StrictMock<MockConnectionVisitor> visitor_; + + private: + void InitializeHeader(QuicPacketSequenceNumber sequence_number) { + header_.public_header.guid = guid_; + header_.public_header.reset_flag = false; + header_.public_header.version_flag = true; + header_.packet_sequence_number = sequence_number; + header_.entropy_flag = false; + header_.fec_flag = false; + header_.is_in_fec_group = NOT_IN_FEC_GROUP; + header_.fec_group = 0; + } + + QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header, + const QuicFrame& frame) { + QuicFrames frames; + frames.push_back(frame); + scoped_ptr<QuicPacket> packet( + framer_.BuildUnsizedDataPacket(header_, frames).packet); + return framer_.EncryptPacket( + ENCRYPTION_NONE, header_.packet_sequence_number, *packet); + } + + QuicGuid guid_; + QuicFramer framer_; + QuicPacketHeader header_; + BoundNetLog net_log_; + QuicStreamFrame frame_; + scoped_ptr<StaticSocketDataProvider> socket_data_; + std::vector<PacketToWrite> writes_; +}; + +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)); + + // The timeout alarm task is always posted. + ASSERT_EQ(1u, runner_->GetPostedTasks().size()); + + QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1); + alarm->Set(clock_.Now().Add(delta)); + + // Verify that the alarm task has been posted. + ASSERT_EQ(2u, runner_->GetPostedTasks().size()); + EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()), + runner_->GetPostedTasks()[1].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(2u, runner_->GetPostedTasks().size()); + EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()), + runner_->GetPostedTasks()[1].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(2u, runner_->GetPostedTasks().size()); + EXPECT_EQ(base::TimeDelta::FromMicroseconds(delta.ToMicroseconds()), + runner_->GetPostedTasks()[1].delay); + + runner_->RunNextTask(); + EXPECT_EQ(QuicTime::Zero().Add(delta), clock_.Now()); + EXPECT_FALSE(delegate->fired()); + + // The alarm task should be posted again. + ASSERT_EQ(2u, runner_->GetPostedTasks().size()); + + runner_->RunNextTask(); + EXPECT_EQ(QuicTime::Zero().Add(new_delta), clock_.Now()); + EXPECT_TRUE(delegate->fired()); +} + +TEST_F(QuicConnectionHelperTest, TestRetransmission) { + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(2)); + Initialize(); + + EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + + QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + QuicTime start = clock_.ApproximateNow(); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)); + // Send a packet. + connection_->SendStreamData(1, kData, 0, false); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION)); + // Since no ack was received, the retransmission alarm will fire and + // retransmit it. + runner_->RunNextTask(); + + EXPECT_EQ(kDefaultRetransmissionTime, + clock_.ApproximateNow().Subtract(start)); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, TestMultipleRetransmission) { + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(2)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(3)); + Initialize(); + + EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + + QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + QuicTime start = clock_.ApproximateNow(); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)); + // Send a packet. + connection_->SendStreamData(1, kData, 0, false); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, IS_RETRANSMISSION)); + // Since no ack was received, the retransmission alarm will fire and + // retransmit it. + runner_->RunNextTask(); + + EXPECT_EQ(kDefaultRetransmissionTime, + clock_.ApproximateNow().Subtract(start)); + + // Since no ack was received, the retransmission alarm will fire and + // retransmit it. + EXPECT_CALL(*send_algorithm_, SentPacket(_, 3, _, IS_RETRANSMISSION)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(2, _)); + runner_->RunNextTask(); + + EXPECT_EQ(kDefaultRetransmissionTime.Add(kDefaultRetransmissionTime), + clock_.ApproximateNow().Subtract(start)); + + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, InitialTimeout) { + AddWrite(SYNCHRONOUS, ConstructClosePacket(1, 0)); + Initialize(); + + // Verify that a single task was posted. + ASSERT_EQ(1u, runner_->GetPostedTasks().size()); + EXPECT_EQ(base::TimeDelta::FromSeconds(kDefaultInitialTimeoutSecs), + runner_->GetPostedTasks().front().delay); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); + // After we run the next task, we should close the connection. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + + runner_->RunNextTask(); + EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds( + kDefaultInitialTimeoutSecs)), + clock_.ApproximateNow()); + EXPECT_FALSE(connection_->connected()); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, WritePacketToWire) { + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + Initialize(); + + int len = GetWrite(0)->length(); + int error = 0; + EXPECT_EQ(len, helper_->WritePacketToWire(*GetWrite(0), &error)); + EXPECT_EQ(0, error); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, WritePacketToWireAsync) { + AddWrite(ASYNC, ConstructClosePacket(1, 0)); + Initialize(); + + EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true)); + int error = 0; + EXPECT_EQ(-1, helper_->WritePacketToWire(*GetWrite(0), &error)); + EXPECT_EQ(ERR_IO_PENDING, error); + base::MessageLoop::current()->RunUntilIdle(); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, TimeoutAfterSend) { + AddWrite(SYNCHRONOUS, ConstructAckPacket(1)); + AddWrite(SYNCHRONOUS, ConstructClosePacket(2, 1)); + Initialize(); + + EXPECT_TRUE(connection_->connected()); + QuicTime start = clock_.ApproximateNow(); + + // When we send a packet, the timeout will change to 5000 + + // kDefaultInitialTimeoutSecs. + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(5000)); + EXPECT_EQ(5000u, clock_.ApproximateNow().Subtract(start).ToMicroseconds()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); + + // Send an ack so we don't set the retransmission alarm. + connection_->SendAck(); + + // The original alarm will fire. We should not time out because we had a + // network event at t=5000. The alarm will reregister. + runner_->RunNextTask(); + + EXPECT_EQ(QuicTime::Zero().Add(QuicTime::Delta::FromSeconds( + kDefaultInitialTimeoutSecs)), + clock_.ApproximateNow()); + EXPECT_TRUE(connection_->connected()); + + // This time, we should time out. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, !kFromPeer)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 2, _, NOT_RETRANSMISSION)); + runner_->RunNextTask(); + EXPECT_EQ(kDefaultInitialTimeoutSecs * 1000000 + 5000, + clock_.ApproximateNow().Subtract( + QuicTime::Zero()).ToMicroseconds()); + EXPECT_FALSE(connection_->connected()); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicConnectionHelperTest, SendSchedulerDelayThenSend) { + AddWrite(SYNCHRONOUS, ConstructDataPacket(1)); + Initialize(); + + // Test that if we send a packet with a delay, it ends up queued. + EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL( + *send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + + QuicPacket* packet = ConstructRawDataPacket(1); + connection_->SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, 0, HAS_RETRANSMITTABLE_DATA); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, NOT_RETRANSMISSION)); + EXPECT_EQ(1u, connection_->NumQueuedPackets()); + + // Advance the clock to fire the alarm, and configure the scheduler + // to permit the packet to be sent. + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(testing::Return(true)); + runner_->RunNextTask(); + EXPECT_EQ(0u, connection_->NumQueuedPackets()); + EXPECT_TRUE(AtEof()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_connection_logger.cc b/chromium/net/quic/quic_connection_logger.cc new file mode 100644 index 00000000000..b5dfcd61028 --- /dev/null +++ b/chromium/net/quic/quic_connection_logger.cc @@ -0,0 +1,416 @@ +// 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 "base/bind.h" +#include "base/callback.h" +#include "base/metrics/histogram.h" +#include "base/strings/string_number_conversions.h" +#include "base/values.h" +#include "net/base/net_log.h" +#include "net/quic/crypto/crypto_handshake.h" + +namespace net { + +namespace { + +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, + EncryptionLevel level, + size_t packet_size, + int rv, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetInteger("encryption_level", level); + dict->SetString("packet_sequence_number", + base::Uint64ToString(sequence_number)); + dict->SetInteger("size", packet_size); + if (rv < 0) { + dict->SetInteger("net_error", rv); + } + return dict; +} + +base::Value* NetLogQuicPacketRetransmittedCallback( + QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetString("old_packet_sequence_number", + base::Uint64ToString(old_sequence_number)); + dict->SetString("new_packet_sequence_number", + base::Uint64ToString(new_sequence_number)); + return dict; +} + +base::Value* NetLogQuicPacketHeaderCallback(const QuicPacketHeader* header, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + dict->SetString("guid", + base::Uint64ToString(header->public_header.guid)); + 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.length()); + return dict; +} + +base::Value* NetLogQuicAckFrameCallback(const QuicAckFrame* 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->sent_info.least_unacked)); + base::DictionaryValue* received_info = new base::DictionaryValue(); + dict->Set("received_info", received_info); + received_info->SetString( + "largest_observed", + base::Uint64ToString(frame->received_info.largest_observed)); + base::ListValue* missing = new base::ListValue(); + received_info->Set("missing_packets", missing); + const SequenceNumberSet& missing_packets = + frame->received_info.missing_packets; + for (SequenceNumberSet::const_iterator it = missing_packets.begin(); + it != missing_packets.end(); ++it) { + missing->AppendString(base::Uint64ToString(*it)); + } + return dict; +} + +base::Value* NetLogQuicCongestionFeedbackFrameCallback( + const QuicCongestionFeedbackFrame* frame, + NetLog::LogLevel /* log_level */) { + base::DictionaryValue* dict = new base::DictionaryValue(); + switch (frame->type) { + case kInterArrival: { + dict->SetString("type", "InterArrival"); + dict->SetInteger("accumulated_number_of_lost_packets", + frame->inter_arrival.accumulated_number_of_lost_packets); + base::ListValue* received = new base::ListValue(); + dict->Set("received_packets", received); + for (TimeMap::const_iterator it = + frame->inter_arrival.received_packet_times.begin(); + it != frame->inter_arrival.received_packet_times.end(); ++it) { + std::string value = base::Uint64ToString(it->first) + "@" + + base::Uint64ToString(it->second.ToDebuggingValue()); + received->AppendString(value); + } + break; + } + case kFixRate: + dict->SetString("type", "FixRate"); + dict->SetInteger("bitrate_in_bytes_per_second", + frame->fix_rate.bitrate.ToBytesPerSecond()); + break; + case kTCP: + dict->SetString("type", "TCP"); + dict->SetInteger("accumulated_number_of_lost_packets", + frame->tcp.accumulated_number_of_lost_packets); + 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* 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* NetLogQuicConnectionClosedCallback( + 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; +} + +void UpdatePacketGapSentHistogram(size_t num_consecutive_missing_packets) { + UMA_HISTOGRAM_COUNTS("Net.QuicSession.PacketGapSent", + num_consecutive_missing_packets); +} + +} // namespace + +QuicConnectionLogger::QuicConnectionLogger(const BoundNetLog& net_log) + : net_log_(net_log), + last_received_packet_sequence_number_(0), + largest_received_packet_sequence_number_(0), + largest_received_missing_packet_sequence_number_(0), + out_of_order_recieved_packet_count_(0) { +} + +QuicConnectionLogger::~QuicConnectionLogger() { + UMA_HISTOGRAM_COUNTS("Net.QuicSession.OutOfOrderPacketsReceived", + out_of_order_recieved_packet_count_); +} + +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)); + 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: + 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: + break; + default: + DCHECK(false) << "Illegal frame type: " << frame.type; + } +} + +void QuicConnectionLogger::OnPacketSent( + QuicPacketSequenceNumber sequence_number, + EncryptionLevel level, + const QuicEncryptedPacket& packet, + int rv) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_PACKET_SENT, + base::Bind(&NetLogQuicPacketSentCallback, sequence_number, level, + packet.length(), rv)); +} + +void QuicConnectionLogger:: OnPacketRetransmitted( + QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_PACKET_RETRANSMITTED, + base::Bind(&NetLogQuicPacketRetransmittedCallback, + old_sequence_number, new_sequence_number)); +} + +void QuicConnectionLogger::OnPacketReceived(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_PACKET_RECEIVED, + base::Bind(&NetLogQuicPacketCallback, &self_address, &peer_address, + packet.length())); +} + +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)); + 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 < last_received_packet_sequence_number_) { + ++out_of_order_recieved_packet_count_; + } + 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)); + + if (frame.received_info.missing_packets.empty()) + return; + + SequenceNumberSet missing_packets = frame.received_info.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::OnRstStreamFrame(const QuicRstStreamFrame& frame) { + 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::OnPublicResetPacket( + const QuicPublicResetPacket& packet) { + net_log_.AddEvent(NetLog::TYPE_QUIC_SESSION_PUBLIC_RESET_PACKET_RECEIVED); +} + +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)); +} + +void QuicConnectionLogger::OnCryptoHandshakeMessageSent( + const CryptoHandshakeMessage& message) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_CRYPTO_HANDSHAKE_MESSAGE_SENT, + base::Bind(&NetLogQuicCryptoHandshakeMessageCallback, &message)); +} + +void QuicConnectionLogger::OnConnectionClose(QuicErrorCode error, + bool from_peer) { + net_log_.AddEvent( + NetLog::TYPE_QUIC_SESSION_CLOSED, + base::Bind(&NetLogQuicConnectionClosedCallback, error, from_peer)); +} + +} // namespace net diff --git a/chromium/net/quic/quic_connection_logger.h b/chromium/net/quic/quic_connection_logger.h new file mode 100644 index 00000000000..f9080d643e3 --- /dev/null +++ b/chromium/net/quic/quic_connection_logger.h @@ -0,0 +1,78 @@ +// 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 "net/quic/quic_connection.h" + +namespace net { + +class BoundNetLog; +class CryptoHandshakeMessage; + +// This class is a debug visitor of a QuicConnection which logs +// events to |net_log|. +class NET_EXPORT_PRIVATE QuicConnectionLogger + : public QuicConnectionDebugVisitorInterface { + 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, + EncryptionLevel level, + const QuicEncryptedPacket& packet, + int rv) OVERRIDE; + virtual void OnPacketRetransmitted( + QuicPacketSequenceNumber old_sequence_number, + QuicPacketSequenceNumber new_sequence_number) OVERRIDE; + virtual void OnPacketReceived(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicEncryptedPacket& packet) 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 OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; + virtual void OnConnectionCloseFrame( + const QuicConnectionCloseFrame& 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; + + void OnCryptoHandshakeMessageReceived( + const CryptoHandshakeMessage& message); + void OnCryptoHandshakeMessageSent( + const CryptoHandshakeMessage& message); + void OnConnectionClose(QuicErrorCode error, bool from_peer); + + private: + BoundNetLog net_log_; + // The last packet sequence number received. + QuicPacketSequenceNumber last_received_packet_sequence_number_; + // The largest packet sequence number received. In case of that a packet is + // received late, 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 out_of_order_recieved_packet_count_; + DISALLOW_COPY_AND_ASSIGN(QuicConnectionLogger); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONNECTION_LOGGER_H_ diff --git a/chromium/net/quic/quic_connection_test.cc b/chromium/net/quic/quic_connection_test.cc new file mode 100644 index 00000000000..bd698c3eb1d --- /dev/null +++ b/chromium/net/quic/quic_connection_test.cc @@ -0,0 +1,2445 @@ +// 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 "net/base/net_errors.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/crypto/quic_random.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_test_utils.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::_; +using testing::AnyNumber; +using testing::Between; +using testing::ContainerEq; +using testing::DoAll; +using testing::InSequence; +using testing::InvokeWithoutArgs; +using testing::Return; +using testing::StrictMock; +using testing::SaveArg; + +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; + +class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { + public: + explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback) + : feedback_(feedback) { + } + + bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* congestion_feedback) { + if (feedback_ == NULL) { + return false; + } + *congestion_feedback = *feedback_; + return true; + } + + MOCK_METHOD4(RecordIncomingPacket, + void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool)); + + 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_; +}; + +// 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 NULL; + } + if (!CheckTag(ciphertext, GetTag(ciphertext))) { + return NULL; + } + 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 {} + }; + + TestConnectionHelper(MockClock* clock, MockRandom* random_generator) + : clock_(clock), + random_generator_(random_generator), + blocked_(false), + is_server_(true), + use_tagging_decrypter_(false), + packets_write_attempts_(0) { + clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + + // QuicConnectionHelperInterface + virtual void SetConnection(QuicConnection* connection) OVERRIDE {} + + virtual const QuicClock* GetClock() const OVERRIDE { + return clock_; + } + + virtual QuicRandom* GetRandomGenerator() OVERRIDE { + return random_generator_; + } + + virtual int WritePacketToWire(const QuicEncryptedPacket& packet, + int* error) OVERRIDE { + ++packets_write_attempts_; + + if (packet.length() >= sizeof(final_bytes_of_last_packet_)) { + memcpy(&final_bytes_of_last_packet_, packet.data() + packet.length() - 4, + sizeof(final_bytes_of_last_packet_)); + } + + QuicFramer framer(QuicVersionMax(), QuicTime::Zero(), is_server_); + if (use_tagging_decrypter_) { + framer.SetDecrypter(new TaggingDecrypter); + } + FramerVisitorCapturingFrames visitor; + framer.set_visitor(&visitor); + EXPECT_TRUE(framer.ProcessPacket(packet)); + header_ = *visitor.header(); + frame_count_ = visitor.frame_count(); + if (visitor.ack()) { + ack_.reset(new QuicAckFrame(*visitor.ack())); + } + if (visitor.feedback()) { + feedback_.reset(new QuicCongestionFeedbackFrame(*visitor.feedback())); + } + if (visitor.stream_frames() != NULL && !visitor.stream_frames()->empty()) { + stream_frames_ = *visitor.stream_frames(); + } + if (visitor.version_negotiation_packet() != NULL) { + version_negotiation_packet_.reset(new QuicVersionNegotiationPacket( + *visitor.version_negotiation_packet())); + } + if (blocked_) { + *error = ERR_IO_PENDING; + return -1; + } + *error = 0; + last_packet_size_ = packet.length(); + return last_packet_size_; + } + + virtual bool IsWriteBlockedDataBuffered() OVERRIDE { + return false; + } + + virtual bool IsWriteBlocked(int error) OVERRIDE { + return error == ERR_IO_PENDING; + } + + virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate) OVERRIDE { + return new TestAlarm(delegate); + } + + QuicPacketHeader* header() { return &header_; } + + size_t frame_count() const { return frame_count_; } + + QuicAckFrame* ack() { return ack_.get(); } + + QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); } + + const vector<QuicStreamFrame>* stream_frames() const { + return &stream_frames_; + } + + size_t last_packet_size() { + return last_packet_size_; + } + + QuicVersionNegotiationPacket* version_negotiation_packet() { + return version_negotiation_packet_.get(); + } + + void set_blocked(bool blocked) { blocked_ = blocked; } + + void set_is_server(bool is_server) { is_server_ = 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_; } + + void use_tagging_decrypter() { + use_tagging_decrypter_ = true; + } + + uint32 packets_write_attempts() { return packets_write_attempts_; } + + private: + MockClock* clock_; + MockRandom* random_generator_; + QuicPacketHeader header_; + size_t frame_count_; + scoped_ptr<QuicAckFrame> ack_; + scoped_ptr<QuicCongestionFeedbackFrame> feedback_; + vector<QuicStreamFrame> stream_frames_; + scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; + size_t last_packet_size_; + bool blocked_; + bool is_server_; + uint32 final_bytes_of_last_packet_; + bool use_tagging_decrypter_; + uint32 packets_write_attempts_; + + DISALLOW_COPY_AND_ASSIGN(TestConnectionHelper); +}; + +class TestConnection : public QuicConnection { + public: + TestConnection(QuicGuid guid, + IPEndPoint address, + TestConnectionHelper* helper, + bool is_server) + : QuicConnection(guid, address, helper, is_server, QuicVersionMax()), + helper_(helper) { + helper_->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); + } + + QuicConsumedData SendStreamData1() { + return SendStreamData(1u, "food", 0, !kFin); + } + + QuicConsumedData SendStreamData2() { + return SendStreamData(2u, "food2", 0, !kFin); + } + + bool is_server() { + return QuicConnectionPeer::IsServer(this); + } + + void set_version(QuicVersion version) { + framer_.set_version(version); + } + + void set_is_server(bool is_server) { + helper_->set_is_server(!is_server); + QuicPacketCreatorPeer::SetIsServer( + QuicConnectionPeer::GetPacketCreator(this), is_server); + QuicConnectionPeer::SetIsServer(this, is_server); + } + + QuicAlarm* GetAckAlarm() { + return QuicConnectionPeer::GetAckAlarm(this); + } + + QuicAlarm* GetRetransmissionAlarm() { + return QuicConnectionPeer::GetRetransmissionAlarm(this); + } + + QuicAlarm* GetSendAlarm() { + return QuicConnectionPeer::GetSendAlarm(this); + } + + QuicAlarm* GetTimeoutAlarm() { + return QuicConnectionPeer::GetTimeoutAlarm(this); + } + + using QuicConnection::SendOrQueuePacket; + using QuicConnection::SelectMutualVersion; + + private: + TestConnectionHelper* helper_; + + DISALLOW_COPY_AND_ASSIGN(TestConnection); +}; + +class QuicConnectionTest : public ::testing::Test { + protected: + QuicConnectionTest() + : guid_(42), + framer_(QuicVersionMax(), QuicTime::Zero(), false), + creator_(guid_, &framer_, QuicRandom::GetInstance(), false), + send_algorithm_(new StrictMock<MockSendAlgorithm>), + helper_(new TestConnectionHelper(&clock_, &random_generator_)), + connection_(guid_, IPEndPoint(), helper_, false), + frame1_(1, false, 0, data1), + frame2_(1, false, 3, data2), + accept_packet_(true) { + connection_.set_visitor(&visitor_); + connection_.SetSendAlgorithm(send_algorithm_); + // Simplify tests by not sending feedback unless specifically configured. + SetFeedback(NULL); + EXPECT_CALL( + *send_algorithm_, TimeUntilSend(_, _, _, _)).WillRepeatedly(Return( + QuicTime::Delta::Zero())); + EXPECT_CALL(*receive_algorithm_, + RecordIncomingPacket(_, _, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( + Return(QuicTime::Delta::Zero())); + } + + QuicAckFrame* outgoing_ack() { + outgoing_ack_.reset(QuicConnectionPeer::CreateAckFrame(&connection_)); + return outgoing_ack_.get(); + } + + QuicAckFrame* last_ack() { + return helper_->ack(); + } + + QuicCongestionFeedbackFrame* last_feedback() { + return helper_->feedback(); + } + + QuicPacketHeader* last_header() { + return helper_->header(); + } + + size_t last_sent_packet_size() { + return helper_->last_packet_size(); + } + + uint32 final_bytes_of_last_packet() { + return helper_->final_bytes_of_last_packet(); + } + + void use_tagging_decrypter() { + helper_->use_tagging_decrypter(); + } + + void ProcessPacket(QuicPacketSequenceNumber number) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)) + .WillOnce(Return(accept_packet_)); + ProcessDataPacket(number, 0, !kEntropyFlag); + } + + QuicPacketEntropyHash ProcessFramePacket(QuicFrame frame) { + QuicFrames frames; + frames.push_back(QuicFrame(frame)); + QuicPacketCreatorPeer::SetSendVersionInPacket(&creator_, + connection_.is_server()); + SerializedPacket serialized_packet = 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 ProcessFecProtectedPacket(QuicPacketSequenceNumber number, + bool expect_revival) { + if (expect_revival) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly( + Return(accept_packet_)); + } else { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce( + Return(accept_packet_)); + } + return ProcessDataPacket(number, 1, !kEntropyFlag); + } + + 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 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_, OnPacket(_, _, _, _)).WillOnce(DoAll( + SaveArg<2>(&revived_header_), Return(accept_packet_))); + } + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(accept_packet_)) + .RetiresOnSaturation(); + return ProcessDataPacket(number, 1, entropy_flag); + } + + // Sends an FEC packet that covers the packets that would have been sent. + size_t ProcessFecPacket(QuicPacketSequenceNumber number, + QuicPacketSequenceNumber min_protected_packet, + bool expect_revival, + bool entropy_flag) { + if (expect_revival) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(DoAll( + SaveArg<2>(&revived_header_), Return(accept_packet_))); + } + + // Construct the decrypted data packet so we can compute the correct + // redundancy. + scoped_ptr<QuicPacket> data_packet(ConstructDataPacket(number, 1, + !kEntropyFlag)); + + header_.public_header.guid = guid_; + header_.public_header.reset_flag = false; + header_.public_header.version_flag = false; + header_.entropy_flag = entropy_flag; + header_.fec_flag = true; + header_.packet_sequence_number = number; + 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.guid_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_, SentPacket(_, _, _, _)).WillOnce( + SaveArg<2>(&packet_size)); + connection_.SendStreamData(id, data, offset, fin); + if (last_packet != NULL) { + *last_packet = + QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number(); + } + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber()); + return packet_size; + } + + void SendAckPacketToPeer() { + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + connection_.SendAck(); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(AnyNumber()); + } + + QuicPacketEntropyHash ProcessAckPacket(QuicAckFrame* frame, + bool expect_writes) { + if (expect_writes) { + EXPECT_CALL(visitor_, OnCanWrite()).Times(1).WillOnce(Return(true)); + } + return ProcessFramePacket(QuicFrame(frame)); + } + + QuicPacketEntropyHash ProcessGoAwayPacket(QuicGoAwayFrame* frame) { + return ProcessFramePacket(QuicFrame(frame)); + } + + bool IsMissing(QuicPacketSequenceNumber number) { + return IsAwaitingPacket(outgoing_ack()->received_info, number); + } + + QuicPacket* ConstructDataPacket(QuicPacketSequenceNumber number, + QuicFecGroupNumber fec_group, + bool entropy_flag) { + header_.public_header.guid = guid_; + header_.public_header.reset_flag = false; + header_.public_header.version_flag = false; + 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 = + framer_.BuildUnsizedDataPacket(header_, frames).packet; + EXPECT_TRUE(packet != NULL); + return packet; + } + + QuicPacket* ConstructClosePacket(QuicPacketSequenceNumber number, + QuicFecGroupNumber fec_group) { + header_.public_header.guid = guid_; + 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; + qccf.ack_frame = QuicAckFrame(0, QuicTime::Zero(), 1); + + QuicFrames frames; + QuicFrame frame(&qccf); + frames.push_back(frame); + QuicPacket* packet = + framer_.BuildUnsizedDataPacket(header_, frames).packet; + EXPECT_TRUE(packet != NULL); + return packet; + } + + void SetFeedback(QuicCongestionFeedbackFrame* feedback) { + receive_algorithm_ = new TestReceiveAlgorithm(feedback); + connection_.SetReceiveAlgorithm(receive_algorithm_); + } + + QuicGuid guid_; + QuicFramer framer_; + QuicPacketCreator creator_; + + MockSendAlgorithm* send_algorithm_; + TestReceiveAlgorithm* receive_algorithm_; + MockClock clock_; + MockRandom random_generator_; + TestConnectionHelper* helper_; + TestConnection connection_; + testing::StrictMock<MockConnectionVisitor> visitor_; + + QuicPacketHeader header_; + QuicPacketHeader revived_header_; + QuicStreamFrame frame1_; + QuicStreamFrame frame2_; + scoped_ptr<QuicAckFrame> outgoing_ack_; + bool accept_packet_; + + private: + DISALLOW_COPY_AND_ASSIGN(QuicConnectionTest); +}; + +TEST_F(QuicConnectionTest, PacketsInOrder) { + ProcessPacket(1); + EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed); + EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); + + ProcessPacket(2); + EXPECT_EQ(2u, outgoing_ack()->received_info.largest_observed); + EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); + + ProcessPacket(3); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); +} + +TEST_F(QuicConnectionTest, PacketsRejected) { + ProcessPacket(1); + EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed); + EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); + + accept_packet_ = false; + ProcessPacket(2); + // We should not have an ack for two. + EXPECT_EQ(1u, outgoing_ack()->received_info.largest_observed); + EXPECT_EQ(0u, outgoing_ack()->received_info.missing_packets.size()); +} + +TEST_F(QuicConnectionTest, PacketsOutOfOrder) { + ProcessPacket(3); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_FALSE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(1); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_FALSE(IsMissing(2)); + EXPECT_FALSE(IsMissing(1)); +} + +TEST_F(QuicConnectionTest, DuplicatePacket) { + ProcessPacket(3); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + // Send packet 3 again, but do not set the expectation that + // the visitor OnPacket() will be called. + ProcessDataPacket(3, 0, !kEntropyFlag); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); +} + +TEST_F(QuicConnectionTest, PacketsOutOfOrderWithAdditionsAndLeastAwaiting) { + ProcessPacket(3); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_TRUE(IsMissing(2)); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(2); + EXPECT_EQ(3u, outgoing_ack()->received_info.largest_observed); + EXPECT_TRUE(IsMissing(1)); + + ProcessPacket(5); + EXPECT_EQ(5u, outgoing_ack()->received_info.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. + creator_.set_sequence_number(5); + QuicAckFrame frame(0, QuicTime::Zero(), 4); + ProcessAckPacket(&frame, true); + + // Force an ack to be sent. + SendAckPacketToPeer(); + EXPECT_TRUE(IsMissing(4)); +} + +TEST_F(QuicConnectionTest, RejectPacketTooFarOut) { + // Call ProcessDataPacket rather than ProcessPacket, as we should not get a + // packet call to the visitor. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_PACKET_HEADER, false)); + ProcessDataPacket(6000, 0, !kEntropyFlag); +} + +TEST_F(QuicConnectionTest, TruncatedAck) { + EXPECT_CALL(visitor_, OnAck(_)).Times(testing::AnyNumber()); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + for (int i = 0; i < 200; ++i) { + SendStreamDataToPeer(1, "foo", i * 3, !kFin, NULL); + } + + QuicAckFrame frame(0, QuicTime::Zero(), 1); + frame.received_info.largest_observed = 192; + InsertMissingPacketsBetween(&frame.received_info, 1, 192); + frame.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 191); + + ProcessAckPacket(&frame, true); + + EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); + + frame.received_info.missing_packets.erase(191); + frame.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 190); + + ProcessAckPacket(&frame, true); + EXPECT_FALSE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); +} + +TEST_F(QuicConnectionTest, AckReceiptCausesAckSendBadEntropy) { + ProcessPacket(1); + // Delay sending, then queue up an ack. + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).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(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + // Skip a packet and then record an ack. + creator_.set_sequence_number(2); + QuicAckFrame frame(0, QuicTime::Zero(), 3); + ProcessAckPacket(&frame, true); +} + +TEST_F(QuicConnectionTest, AckReceiptCausesAckSend) { + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + QuicPacketSequenceNumber largest_observed; + QuicByteCount packet_size; + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION)) + .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size))); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); + connection_.SendStreamData(1, "foo", 0, !kFin); + QuicAckFrame frame(1, QuicTime::Zero(), largest_observed); + frame.received_info.missing_packets.insert(largest_observed); + frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( + &connection_, largest_observed - 1); + ProcessAckPacket(&frame, true); + ProcessAckPacket(&frame, true); + // Third nack should retransmit the largest observed packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size - kQuicVersionSize, + IS_RETRANSMISSION)); + ProcessAckPacket(&frame, true); + + // Now if the peer sends an ack which still reports the retransmitted packet + // as missing, then that will count as a packet which instigates an ack. + ProcessAckPacket(&frame, true); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION)); + ProcessAckPacket(&frame, true); + + // But an ack with no new missing packest will not send an ack. + frame.received_info.missing_packets.clear(); + ProcessAckPacket(&frame, true); + ProcessAckPacket(&frame, true); +} + +TEST_F(QuicConnectionTest, LeastUnackedLower) { + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + SendStreamDataToPeer(1, "bar", 3, !kFin, NULL); + SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); + + // Start out saying the least unacked is 2 + creator_.set_sequence_number(5); + QuicAckFrame frame(0, QuicTime::Zero(), 2); + ProcessAckPacket(&frame, true); + + // Change it to 1, but lower the sequence number to fake out-of-order packets. + // This should be fine. + creator_.set_sequence_number(1); + QuicAckFrame frame2(0, QuicTime::Zero(), 1); + // The scheduler will not process out of order acks. + ProcessAckPacket(&frame2, false); + + // 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(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + creator_.set_sequence_number(7); + ProcessAckPacket(&frame2, false); +} + +TEST_F(QuicConnectionTest, LargestObservedLower) { + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + SendStreamDataToPeer(1, "bar", 3, !kFin, NULL); + SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); + + // Start out saying the largest observed is 2. + QuicAckFrame frame(2, QuicTime::Zero(), 0); + frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( + &connection_, 2); + EXPECT_CALL(visitor_, OnAck(_)); + ProcessAckPacket(&frame, true); + + // Now change it to 1, and it should cause a connection error. + QuicAckFrame frame2(1, QuicTime::Zero(), 0); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + ProcessAckPacket(&frame2, false); +} + +TEST_F(QuicConnectionTest, LeastUnackedGreaterThanPacketSequenceNumber) { + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + // Create an ack with least_unacked is 2 in packet number 1. + creator_.set_sequence_number(0); + QuicAckFrame frame(0, QuicTime::Zero(), 2); + ProcessAckPacket(&frame, false); +} + +TEST_F(QuicConnectionTest, + NackSequenceNumberGreaterThanLargestReceived) { + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + SendStreamDataToPeer(1, "bar", 3, !kFin, NULL); + SendStreamDataToPeer(1, "eep", 6, !kFin, NULL); + + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + QuicAckFrame frame(0, QuicTime::Zero(), 1); + frame.received_info.missing_packets.insert(3); + ProcessAckPacket(&frame, false); +} + +TEST_F(QuicConnectionTest, AckUnsentData) { + // Ack a packet which has not been sent. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_ACK_DATA, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + QuicAckFrame frame(1, QuicTime::Zero(), 0); + ProcessAckPacket(&frame, false); +} + +TEST_F(QuicConnectionTest, AckAll) { + ProcessPacket(1); + + creator_.set_sequence_number(1); + QuicAckFrame frame1(0, QuicTime::Zero(), 1); + ProcessAckPacket(&frame1, true); +} + +TEST_F(QuicConnectionTest, BasicSending) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6); + QuicPacketSequenceNumber last_packet; + SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1 + EXPECT_EQ(1u, last_packet); + SendAckPacketToPeer(); // Packet 2 + + EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); + + SendAckPacketToPeer(); // Packet 3 + EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); + + SendStreamDataToPeer(1u, "bar", 3, !kFin, &last_packet); // Packet 4 + EXPECT_EQ(4u, last_packet); + SendAckPacketToPeer(); // Packet 5 + EXPECT_EQ(1u, last_ack()->sent_info.least_unacked); + + SequenceNumberSet expected_acks; + expected_acks.insert(1); + + // Peer acks up to packet 3. + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + QuicAckFrame frame(3, QuicTime::Zero(), 0); + frame.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 3); + ProcessAckPacket(&frame, true); + 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, last_ack()->sent_info.least_unacked); + + expected_acks.clear(); + expected_acks.insert(4); + + // Peer acks up to packet 4, the last packet. + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + QuicAckFrame frame2(6, QuicTime::Zero(), 0); + frame2.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 6); + ProcessAckPacket(&frame2, true); // Acks don't instigate acks. + + // Verify that we did not send an ack. + EXPECT_EQ(6u, last_header()->packet_sequence_number); + + // So the last ack has not changed. + EXPECT_EQ(4u, last_ack()->sent_info.least_unacked); + + // If we force an ack, we shouldn't change our retransmit state. + SendAckPacketToPeer(); // Packet 7 + EXPECT_EQ(7u, last_ack()->sent_info.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(8u, last_ack()->sent_info.least_unacked); +} + +TEST_F(QuicConnectionTest, FECSending) { + // All packets carry version info till version is negotiated. + size_t payload_length; + connection_.options()->max_packet_length = + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + IN_FEC_GROUP, &payload_length); + // And send FEC every two packets. + connection_.options()->max_packets_per_fec_group = 2; + + // Send 4 data packets and 2 FEC packets. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(6); + // The first stream frame will consume 2 fewer bytes than the other three. + const string payload(payload_length * 4 - 6, 'a'); + connection_.SendStreamData(1, payload, 0, !kFin); + // Expect the FEC group to be closed after SendStreamData. + EXPECT_FALSE(creator_.ShouldSendFec(true)); +} + +TEST_F(QuicConnectionTest, FECQueueing) { + // All packets carry version info till version is negotiated. + size_t payload_length; + connection_.options()->max_packet_length = + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + IN_FEC_GROUP, &payload_length); + // And send FEC every two packets. + connection_.options()->max_packets_per_fec_group = 2; + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + helper_->set_blocked(true); + const string payload(payload_length, 'a'); + connection_.SendStreamData(1, payload, 0, !kFin); + EXPECT_FALSE(creator_.ShouldSendFec(true)); + // Expect the first data packet and the fec packet to be queued. + EXPECT_EQ(2u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, AbandonFECFromCongestionWindow) { + connection_.options()->max_packets_per_fec_group = 1; + // 1 Data and 1 FEC packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(2); + connection_.SendStreamData(1, "foo", 0, !kFin); + + // Larger timeout for FEC bytes to expire. + const QuicTime::Delta retransmission_time = + QuicTime::Delta::FromMilliseconds(5000); + clock_.AdvanceTime(retransmission_time); + + // Send only data packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + // Abandon both FEC and data packet. + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); + + connection_.OnRetransmissionTimeout(); +} + +TEST_F(QuicConnectionTest, DontAbandonAckedFEC) { + connection_.options()->max_packets_per_fec_group = 1; + const QuicPacketSequenceNumber sequence_number = + QuicConnectionPeer::GetPacketCreator(&connection_)->sequence_number() + 1; + + // 1 Data and 1 FEC packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(2); + connection_.SendStreamData(1, "foo", 0, !kFin); + + QuicAckFrame ack_fec(2, QuicTime::Zero(), 1); + // Data packet missing. + ack_fec.received_info.missing_packets.insert(1); + ack_fec.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + + EXPECT_CALL(visitor_, OnAck(_)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + + ProcessAckPacket(&ack_fec, true); + + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(5000); + clock_.AdvanceTime(kDefaultRetransmissionTime); + + // Abandon only data packet, FEC has been acked. + EXPECT_CALL(*send_algorithm_, AbandoningPacket(sequence_number, _)).Times(1); + // Send only data packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + connection_.OnRetransmissionTimeout(); +} + +TEST_F(QuicConnectionTest, FramePacking) { + // Block the connection. + connection_.GetSendAlarm()->Set( + clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); + + // 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::SendStreamData1)), + IgnoreResult(InvokeWithoutArgs(&connection_, + &TestConnection::SendStreamData2)), + Return(true))); + + // Unblock the connection. + connection_.GetSendAlarm()->Cancel(); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, NOT_RETRANSMISSION)) + .Times(1); + connection_.OnCanWrite(); + 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(3u, helper_->frame_count()); + EXPECT_TRUE(helper_->ack()); + EXPECT_EQ(2u, helper_->stream_frames()->size()); + EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id); + EXPECT_EQ(2u, (*helper_->stream_frames())[1].stream_id); +} + +TEST_F(QuicConnectionTest, FramePackingFEC) { + // Enable fec. + connection_.options()->max_packets_per_fec_group = 6; + // Block the connection. + connection_.GetSendAlarm()->Set( + clock_.ApproximateNow().Add(QuicTime::Delta::FromSeconds(1))); + + // 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::SendStreamData1)), + IgnoreResult(InvokeWithoutArgs(&connection_, + &TestConnection::SendStreamData2)), + Return(true))); + + // Unblock the connection. + connection_.GetSendAlarm()->Cancel(); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, NOT_RETRANSMISSION)).Times(2); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_FALSE(connection_.HasQueuedData()); + + // Parse the last packet and ensure it's in an fec group. + EXPECT_EQ(1u, helper_->header()->fec_group); + EXPECT_EQ(0u, helper_->frame_count()); +} + +TEST_F(QuicConnectionTest, OnCanWrite) { + // Visitor's OnCanWill send data, but will return false. + EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll( + IgnoreResult(InvokeWithoutArgs(&connection_, + &TestConnection::SendStreamData1)), + IgnoreResult(InvokeWithoutArgs(&connection_, + &TestConnection::SendStreamData2)), + Return(false))); + + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + + // Unblock the connection. + connection_.OnCanWrite(); + // Parse the last packet and ensure it's the two stream frames from + // two different streams. + EXPECT_EQ(2u, helper_->frame_count()); + EXPECT_EQ(2u, helper_->stream_frames()->size()); + EXPECT_EQ(1u, (*helper_->stream_frames())[0].stream_id); + EXPECT_EQ(2u, (*helper_->stream_frames())[1].stream_id); +} + +TEST_F(QuicConnectionTest, RetransmitOnNack) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(2); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(2, _)).Times(1); + QuicPacketSequenceNumber last_packet; + QuicByteCount second_packet_size; + SendStreamDataToPeer(1, "foo", 0, !kFin, &last_packet); // Packet 1 + second_packet_size = + SendStreamDataToPeer(1, "foos", 3, !kFin, &last_packet); // Packet 2 + SendStreamDataToPeer(1, "fooos", 7, !kFin, &last_packet); // Packet 3 + + SequenceNumberSet expected_acks; + expected_acks.insert(1); + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + + // Peer acks one but not two or three. Right now we only retransmit on + // explicit nack, so it should not trigger a retransimission. + QuicAckFrame ack_one(1, QuicTime::Zero(), 0); + ack_one.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + ProcessAckPacket(&ack_one, true); + ProcessAckPacket(&ack_one, true); + ProcessAckPacket(&ack_one, true); + + expected_acks.clear(); + expected_acks.insert(3); + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + + // Peer acks up to 3 with two explicitly missing. Two nacks should cause no + // change. + QuicAckFrame nack_two(3, QuicTime::Zero(), 0); + nack_two.received_info.missing_packets.insert(2); + nack_two.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + ProcessAckPacket(&nack_two, true); + ProcessAckPacket(&nack_two, true); + + // The third nack should trigger a retransimission. + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, second_packet_size - kQuicVersionSize, + IS_RETRANSMISSION)).Times(1); + ProcessAckPacket(&nack_two, true); +} + +TEST_F(QuicConnectionTest, RetransmitNackedLargestObserved) { + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + QuicPacketSequenceNumber largest_observed; + QuicByteCount packet_size; + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION)) + .WillOnce(DoAll(SaveArg<1>(&largest_observed), SaveArg<2>(&packet_size))); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); + connection_.SendStreamData(1, "foo", 0, !kFin); + QuicAckFrame frame(1, QuicTime::Zero(), largest_observed); + frame.received_info.missing_packets.insert(largest_observed); + frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( + &connection_, largest_observed - 1); + ProcessAckPacket(&frame, true); + ProcessAckPacket(&frame, true); + // Third nack should retransmit the largest observed packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, packet_size - kQuicVersionSize, + IS_RETRANSMISSION)); + ProcessAckPacket(&frame, true); +} + +TEST_F(QuicConnectionTest, RetransmitNackedPacketsOnTruncatedAck) { + for (int i = 0; i < 200; ++i) { + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + connection_.SendStreamData(1, "foo", i * 3, !kFin); + } + + // Make a truncated ack frame. + QuicAckFrame frame(0, QuicTime::Zero(), 1); + frame.received_info.largest_observed = 192; + InsertMissingPacketsBetween(&frame.received_info, 1, 192); + frame.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 192) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 191); + + + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(visitor_, OnAck(_)).Times(1); + ProcessAckPacket(&frame, true); + EXPECT_TRUE(QuicConnectionPeer::GetReceivedTruncatedAck(&connection_)); + + QuicConnectionPeer::SetMaxPacketsPerRetransmissionAlarm(&connection_, 200); + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + clock_.AdvanceTime(kDefaultRetransmissionTime); + // Only packets that are less than largest observed should be retransmitted. + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(191); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(191); + connection_.OnRetransmissionTimeout(); + + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( + 2 * kDefaultRetransmissionTime.ToMicroseconds())); + // Retransmit already retransmitted packets event though the sequence number + // greater than the largest observed. + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(191); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(191); + connection_.OnRetransmissionTimeout(); +} + +TEST_F(QuicConnectionTest, LimitPacketsPerNack) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(12, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(11); + int offset = 0; + // Send packets 1 to 12 + for (int i = 0; i < 12; ++i) { + SendStreamDataToPeer(1, "foo", offset, !kFin, NULL); + offset += 3; + } + + // Ack 12, nack 1-11 + QuicAckFrame nack(12, QuicTime::Zero(), 0); + for (int i = 1; i < 12; ++i) { + nack.received_info.missing_packets.insert(i); + } + + nack.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 12) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 11); + SequenceNumberSet expected_acks; + expected_acks.insert(12); + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + + // Nack three times. + ProcessAckPacket(&nack, true); + ProcessAckPacket(&nack, true); + // The third call should trigger retransmitting 10 packets. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(10); + ProcessAckPacket(&nack, true); + + // The fourth call should trigger retransmitting the 11th packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + ProcessAckPacket(&nack, true); +} + +// Test sending multiple acks from the connection to the session. +TEST_F(QuicConnectionTest, MultipleAcks) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(6); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + QuicPacketSequenceNumber last_packet; + SendStreamDataToPeer(1u, "foo", 0, !kFin, &last_packet); // Packet 1 + EXPECT_EQ(1u, last_packet); + SendStreamDataToPeer(3u, "foo", 0, !kFin, &last_packet); // Packet 2 + EXPECT_EQ(2u, last_packet); + SendAckPacketToPeer(); // Packet 3 + SendStreamDataToPeer(5u, "foo", 0, !kFin, &last_packet); // Packet 4 + EXPECT_EQ(4u, last_packet); + SendStreamDataToPeer(1u, "foo", 3, !kFin, &last_packet); // Packet 5 + EXPECT_EQ(5u, last_packet); + SendStreamDataToPeer(3u, "foo", 3, !kFin, &last_packet); // Packet 6 + EXPECT_EQ(6u, last_packet); + + // Client will ack packets 1, [!2], 3, 4, 5 + QuicAckFrame frame1(5, QuicTime::Zero(), 0); + frame1.received_info.missing_packets.insert(2); + frame1.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 5) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 2) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 1); + + // The connection should pass up acks for 1, 4, 5. 2 is not acked, and 3 was + // an ackframe so should not be passed up. + SequenceNumberSet expected_acks; + expected_acks.insert(1); + expected_acks.insert(4); + expected_acks.insert(5); + + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + ProcessAckPacket(&frame1, true); + + // Now the client implicitly acks 2, and explicitly acks 6 + QuicAckFrame frame2(6, QuicTime::Zero(), 0); + frame2.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 6); + expected_acks.clear(); + // Both acks should be passed up. + expected_acks.insert(2); + expected_acks.insert(6); + + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + ProcessAckPacket(&frame2, true); +} + +TEST_F(QuicConnectionTest, DontLatchUnackedPacket) { + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); // Packet 1; + SendAckPacketToPeer(); // Packet 2 + + // This sets least unacked to 3 (unsent packet), since we don't need + // an ack for Packet 2 (ack packet). + SequenceNumberSet expected_acks; + expected_acks.insert(1); + // Peer acks packet 1. + EXPECT_CALL(visitor_, OnAck(ContainerEq(expected_acks))); + QuicAckFrame frame(1, QuicTime::Zero(), 0); + frame.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( + &connection_, 1); + ProcessAckPacket(&frame, true); + + // Verify that our internal state has least-unacked as 3. + EXPECT_EQ(3u, outgoing_ack()->sent_info.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 + // Since this was an ack packet, we set least_unacked to 4. + EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked); + // Check that the outgoing ack had its sequence number as least_unacked. + EXPECT_EQ(3u, last_ack()->sent_info.least_unacked); + + SendStreamDataToPeer(1, "bar", 3, false, NULL); // Packet 4 + EXPECT_EQ(4u, outgoing_ack()->sent_info.least_unacked); + SendAckPacketToPeer(); // Packet 5 + EXPECT_EQ(4u, last_ack()->sent_info.least_unacked); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterFecPacket) { + // Don't send missing packet 1. + ProcessFecPacket(2, 1, true, !kEntropyFlag); + EXPECT_FALSE(revived_header_.entropy_flag); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketThenFecPacket) { + ProcessFecProtectedPacket(1, false, kEntropyFlag); + // Don't send missing packet 2. + ProcessFecPacket(3, 1, true, !kEntropyFlag); + EXPECT_TRUE(revived_header_.entropy_flag); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacketsThenFecPacket) { + ProcessFecProtectedPacket(1, false, !kEntropyFlag); + // Don't send missing packet 2. + ProcessFecProtectedPacket(3, false, !kEntropyFlag); + ProcessFecPacket(4, 1, true, kEntropyFlag); + EXPECT_TRUE(revived_header_.entropy_flag); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPacket) { + // Don't send missing packet 1. + ProcessFecPacket(3, 1, false, !kEntropyFlag); + // out of order + ProcessFecProtectedPacket(2, true, !kEntropyFlag); + EXPECT_FALSE(revived_header_.entropy_flag); +} + +TEST_F(QuicConnectionTest, ReviveMissingPacketAfterDataPackets) { + ProcessFecProtectedPacket(1, false, !kEntropyFlag); + // Don't send missing packet 2. + ProcessFecPacket(6, 1, false, kEntropyFlag); + ProcessFecProtectedPacket(3, false, kEntropyFlag); + ProcessFecProtectedPacket(4, false, kEntropyFlag); + ProcessFecProtectedPacket(5, true, !kEntropyFlag); + EXPECT_TRUE(revived_header_.entropy_flag); +} + +TEST_F(QuicConnectionTest, TestRetransmit) { + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + + QuicTime default_retransmission_time = clock_.ApproximateNow().Add( + kDefaultRetransmissionTime); + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + EXPECT_EQ(1u, outgoing_ack()->sent_info.least_unacked); + + EXPECT_EQ(1u, last_header()->packet_sequence_number); + EXPECT_EQ(default_retransmission_time, + connection_.GetRetransmissionAlarm()->deadline()); + // Simulate the retransimission alarm firing + clock_.AdvanceTime(kDefaultRetransmissionTime); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); + connection_.RetransmitPacket(1); + EXPECT_EQ(2u, last_header()->packet_sequence_number); + EXPECT_EQ(2u, outgoing_ack()->sent_info.least_unacked); +} + +TEST_F(QuicConnectionTest, RetransmitWithSameEncryptionLevel) { + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + + QuicTime default_retransmission_time = clock_.ApproximateNow().Add( + kDefaultRetransmissionTime); + 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(1, "foo", 0, !kFin, NULL); + EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); + + connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + EXPECT_EQ(0x02020202u, final_bytes_of_last_packet()); + + EXPECT_EQ(default_retransmission_time, + connection_.GetRetransmissionAlarm()->deadline()); + // Simulate the retransimission alarm firing + clock_.AdvanceTime(kDefaultRetransmissionTime); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + connection_.RetransmitPacket(1); + // Packet should have been sent with ENCRYPTION_NONE. + EXPECT_EQ(0x01010101u, final_bytes_of_last_packet()); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + connection_.RetransmitPacket(2); + // Packet should have been sent with ENCRYPTION_INITIAL. + EXPECT_EQ(0x02020202u, final_bytes_of_last_packet()); +} + +TEST_F(QuicConnectionTest, + DropRetransmitsForNullEncryptedPacketAfterForwardSecure) { + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); + QuicPacketSequenceNumber sequence_number; + SendStreamDataToPeer(1, "foo", 0, !kFin, &sequence_number); + + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + new TaggingEncrypter(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(sequence_number, _)).Times(1); + + const QuicTime::Delta kDefaultRetransmissionTime = + QuicTime::Delta::FromMilliseconds(500); + QuicTime default_retransmission_time = clock_.ApproximateNow().Add( + kDefaultRetransmissionTime); + + EXPECT_EQ(default_retransmission_time, + connection_.GetRetransmissionAlarm()->deadline()); + // Simulate the retransimission alarm firing + clock_.AdvanceTime(kDefaultRetransmissionTime); + connection_.OnRetransmissionTimeout(); +} + +TEST_F(QuicConnectionTest, RetransmitPacketsWithInitialEncryption) { + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_NONE, new TaggingEncrypter(0x01)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_NONE); + + SendStreamDataToPeer(1, "foo", 0, !kFin, NULL); + + connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + + SendStreamDataToPeer(2, "bar", 0, !kFin, NULL); + + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(1); + + connection_.RetransmitUnackedPackets(QuicConnection::INITIAL_ENCRYPTION_ONLY); +} + +TEST_F(QuicConnectionTest, BufferNonDecryptablePackets) { + 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, false, 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)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + connection_.SetEncrypter(ENCRYPTION_INITIAL, new TaggingEncrypter(tag)); + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(2).WillRepeatedly( + Return(true)); + ProcessDataPacketAtLevel(2, false, kEntropyFlag, ENCRYPTION_INITIAL); + + // Finally, process a third packet and note that we do not + // reprocess the buffered packet. + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillOnce(Return(true)); + ProcessDataPacketAtLevel(3, false, kEntropyFlag, ENCRYPTION_INITIAL); +} + +TEST_F(QuicConnectionTest, TestRetransmitOrder) { + QuicByteCount first_packet_size; + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce( + SaveArg<2>(&first_packet_size)); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); + + connection_.SendStreamData(1, "first_packet", 0, !kFin); + QuicByteCount second_packet_size; + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).WillOnce( + SaveArg<2>(&second_packet_size)); + connection_.SendStreamData(1, "second_packet", 12, !kFin); + 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)); + { + InSequence s; + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, first_packet_size, _)); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, second_packet_size, _)); + } + connection_.OnRetransmissionTimeout(); +} + +TEST_F(QuicConnectionTest, TestRetransmissionCountCalculation) { + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(2); + QuicPacketSequenceNumber original_sequence_number; + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, NOT_RETRANSMISSION)) + .WillOnce(SaveArg<1>(&original_sequence_number)); + connection_.SendStreamData(1, "foo", 0, !kFin); + EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, original_sequence_number)); + EXPECT_EQ(0u, QuicConnectionPeer::GetRetransmissionCount( + &connection_, original_sequence_number)); + // Force retransmission due to RTO. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); + QuicPacketSequenceNumber rto_sequence_number; + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, IS_RETRANSMISSION)) + .WillOnce(SaveArg<1>(&rto_sequence_number)); + connection_.OnRetransmissionTimeout(); + EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, original_sequence_number)); + ASSERT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, rto_sequence_number)); + EXPECT_EQ(1u, QuicConnectionPeer::GetRetransmissionCount( + &connection_, rto_sequence_number)); + // Once by explicit nack. + QuicPacketSequenceNumber nack_sequence_number; + // Ack packets might generate some other packets, which are not + // retransmissions. (More ack packets). + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, NOT_RETRANSMISSION)) + .Times(AnyNumber()); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, IS_RETRANSMISSION)) + .WillOnce(SaveArg<1>(&nack_sequence_number)); + QuicAckFrame ack(rto_sequence_number, QuicTime::Zero(), 0); + // Ack the retransmitted packet. + ack.received_info.missing_packets.insert(rto_sequence_number); + ack.received_info.entropy_hash = QuicConnectionPeer::GetSentEntropyHash( + &connection_, rto_sequence_number - 1); + for (int i = 0; i < 3; i++) { + ProcessAckPacket(&ack, true); + } + EXPECT_FALSE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, rto_sequence_number)); + EXPECT_TRUE(QuicConnectionPeer::IsSavedForRetransmission( + &connection_, nack_sequence_number)); + EXPECT_EQ(2u, QuicConnectionPeer::GetRetransmissionCount( + &connection_, nack_sequence_number)); +} + +TEST_F(QuicConnectionTest, SetRTOAfterWritingToSocket) { + helper_->set_blocked(true); + connection_.SendStreamData(1, "foo", 0, !kFin); + // Make sure that RTO is not started when the packet is queued. + EXPECT_EQ(0u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_)); + + // Test that RTO is started once we write to the socket. + helper_->set_blocked(false); + EXPECT_CALL(visitor_, OnCanWrite()); + connection_.OnCanWrite(); + EXPECT_EQ(1u, QuicConnectionPeer::GetNumRetransmissionTimeouts(&connection_)); +} + +TEST_F(QuicConnectionTest, TestQueued) { + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + helper_->set_blocked(true); + connection_.SendStreamData(1, "foo", 0, !kFin); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Attempt to send all packets, but since we're actually still + // blocked, they should all remain queued. + EXPECT_FALSE(connection_.OnCanWrite()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Unblock the writes and actually send. + helper_->set_blocked(false); + EXPECT_CALL(visitor_, OnCanWrite()); + EXPECT_TRUE(connection_.OnCanWrite()); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, CloseFecGroup) { + // 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 + QuicAckFrame frame(0, QuicTime::Zero(), 5); + creator_.set_sequence_number(4); + ProcessAckPacket(&frame, true); + ASSERT_EQ(0u, connection_.NumFecGroups()); +} + +TEST_F(QuicConnectionTest, NoQuicCongestionFeedbackFrame) { + SendAckPacketToPeer(); + EXPECT_TRUE(last_feedback() == NULL); +} + +TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) { + QuicCongestionFeedbackFrame info; + info.type = kFixRate; + info.fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(123); + SetFeedback(&info); + + SendAckPacketToPeer(); + EXPECT_EQ(kFixRate, last_feedback()->type); + EXPECT_EQ(info.fix_rate.bitrate, last_feedback()->fix_rate.bitrate); +} + +TEST_F(QuicConnectionTest, UpdateQuicCongestionFeedbackFrame) { + SendAckPacketToPeer(); + EXPECT_CALL(*receive_algorithm_, RecordIncomingPacket(_, _, _, _)); + ProcessPacket(1); +} + +TEST_F(QuicConnectionTest, DontUpdateQuicCongestionFeedbackFrameForRevived) { + 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); +} + +TEST_F(QuicConnectionTest, InitialTimeout) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + + QuicTime default_timeout = clock_.ApproximateNow().Add( + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)); + EXPECT_EQ(default_timeout, connection_.GetTimeoutAlarm()->deadline()); + + // Simulate the timeout alarm firing + clock_.AdvanceTime( + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)); + EXPECT_TRUE(connection_.CheckForTimeout()); + EXPECT_FALSE(connection_.connected()); +} + +TEST_F(QuicConnectionTest, TimeoutAfterSend) { + EXPECT_TRUE(connection_.connected()); + + QuicTime default_timeout = clock_.ApproximateNow().Add( + QuicTime::Delta::FromSeconds(kDefaultInitialTimeoutSecs)); + + // 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 retransimission 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( + kDefaultInitialTimeoutSecs * 1000000 - 5000)); + EXPECT_EQ(default_timeout, clock_.ApproximateNow()); + EXPECT_FALSE(connection_.CheckForTimeout()); + 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_, ConnectionClose(QUIC_CONNECTION_TIMED_OUT, false)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); + EXPECT_EQ(default_timeout.Add(QuicTime::Delta::FromMilliseconds(5)), + clock_.ApproximateNow()); + EXPECT_TRUE(connection_.CheckForTimeout()); + EXPECT_FALSE(connection_.connected()); +} + +// TODO(ianswett): Add scheduler tests when should_retransmit is false. +TEST_F(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_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelay) { + // Test that if we send a packet with a delay, it ends up queued. + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerForce) { + // Test that if we force send a packet, it is not queued. + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, IS_RETRANSMISSION, _, _)).Times(0); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + // XXX: fixme. was: connection_.SendOrQueuePacket(1, packet, kForce); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerEAGAIN) { + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + helper_->set_blocked(true); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenSend) { + // Test that if we send a packet with a delay, it ends up queued. + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Advance the clock to fire the alarm, and configure the scheduler + // to permit the packet to be sent. + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); + connection_.GetSendAlarm()->Cancel(); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)); + EXPECT_CALL(visitor_, OnCanWrite()); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenRetransmit) { + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, NOT_RETRANSMISSION, _, _)) + .WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, AbandoningPacket(1, _)).Times(1); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, 1, _, NOT_RETRANSMISSION)); + connection_.SendStreamData(1, "foo", 0, !kFin); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + // Advance the time for retransmission of lost packet. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(501)); + // Test that if we send a retransmit with a delay, it ends up queued. + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, IS_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + connection_.OnRetransmissionTimeout(); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Advance the clock to fire the alarm, and configure the scheduler + // to permit the packet to be sent. + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, IS_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::Zero())); + + // Ensure the scheduler is notified this is a retransmit. + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, IS_RETRANSMISSION)); + clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds(1)); + connection_.GetSendAlarm()->Cancel(); + EXPECT_CALL(visitor_, OnCanWrite()); + connection_.OnCanWrite(); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayAndQueue) { + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Attempt to send another packet and make sure that it gets queued. + packet = ConstructDataPacket(2, 0, !kEntropyFlag); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 2, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(2u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndSend) { + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Now send non-retransmitting information, that we're not going to + // retransmit 3. The far end should stop waiting for it. + QuicAckFrame frame(0, QuicTime::Zero(), 1); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, _)); + ProcessAckPacket(&frame, true); + + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + // Ensure alarm is not set + EXPECT_FALSE(connection_.GetSendAlarm()->IsSet()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenAckAndHold) { + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // Now send non-retransmitting information, that we're not going to + // retransmit 3. The far end should stop waiting for it. + QuicAckFrame frame(0, QuicTime::Zero(), 1); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(1))); + ProcessAckPacket(&frame, false); + + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, SendSchedulerDelayThenOnCanWrite) { + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + + // OnCanWrite should not send the packet (because of the delay) + // but should still return true. + EXPECT_TRUE(connection_.OnCanWrite()); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, TestQueueLimitsOnSendStreamData) { + // All packets carry version info till version is negotiated. + size_t payload_length; + connection_.options()->max_packet_length = + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + NOT_IN_FEC_GROUP, &payload_length); + + // Queue the first packet. + EXPECT_CALL(*send_algorithm_, + TimeUntilSend(_, NOT_RETRANSMISSION, _, _)).WillOnce( + testing::Return(QuicTime::Delta::FromMicroseconds(10))); + const string payload(payload_length, 'a'); + EXPECT_EQ(0u, + connection_.SendStreamData(1, payload, 0, !kFin).bytes_consumed); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); +} + +TEST_F(QuicConnectionTest, LoopThroughSendingPackets) { + // All packets carry version info till version is negotiated. + size_t payload_length; + connection_.options()->max_packet_length = + GetPacketLengthForOneStream(connection_.version(), kIncludeVersion, + NOT_IN_FEC_GROUP, &payload_length); + + // Queue the first packet. + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(7); + // The first stream frame will consume 2 fewer bytes than the other six. + const string payload(payload_length * 7 - 12, 'a'); + EXPECT_EQ(payload.size(), + connection_.SendStreamData(1, payload, 0, !kFin).bytes_consumed); +} + +TEST_F(QuicConnectionTest, NoAckForClose) { + ProcessPacket(1); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(0); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true)); + EXPECT_CALL(*send_algorithm_, SentPacket(_, _, _, _)).Times(0); + ProcessClosePacket(2, 0); +} + +TEST_F(QuicConnectionTest, SendWhenDisconnected) { + EXPECT_TRUE(connection_.connected()); + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, false)); + connection_.CloseConnection(QUIC_PEER_GOING_AWAY, false); + EXPECT_FALSE(connection_.connected()); + QuicPacket* packet = ConstructDataPacket(1, 0, !kEntropyFlag); + EXPECT_CALL(*send_algorithm_, SentPacket(_, 1, _, _)).Times(0); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, 1, packet, kTestEntropyHash, HAS_RETRANSMITTABLE_DATA); +} + +TEST_F(QuicConnectionTest, PublicReset) { + QuicPublicResetPacket header; + header.public_header.guid = guid_; + 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_, ConnectionClose(QUIC_PUBLIC_RESET, true)); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *packet); +} + +TEST_F(QuicConnectionTest, GoAway) { + 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_F(QuicConnectionTest, MissingPacketsBeforeLeastUnacked) { + QuicAckFrame ack(0, QuicTime::Zero(), 4); + // Set the sequence number of the ack packet to be least unacked (4) + creator_.set_sequence_number(3); + ProcessAckPacket(&ack, true); + EXPECT_TRUE(outgoing_ack()->received_info.missing_packets.empty()); +} + +TEST_F(QuicConnectionTest, ReceivedEntropyHashCalculation) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true)); + ProcessDataPacket(1, 1, kEntropyFlag); + ProcessDataPacket(4, 1, kEntropyFlag); + ProcessDataPacket(3, 1, !kEntropyFlag); + ProcessDataPacket(7, 1, kEntropyFlag); + EXPECT_EQ(146u, outgoing_ack()->received_info.entropy_hash); +} + +TEST_F(QuicConnectionTest, UpdateEntropyForReceivedPackets) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true)); + ProcessDataPacket(1, 1, kEntropyFlag); + ProcessDataPacket(5, 1, kEntropyFlag); + ProcessDataPacket(4, 1, !kEntropyFlag); + EXPECT_EQ(34u, outgoing_ack()->received_info.entropy_hash); + // Make 4th packet my least unacked, and update entropy for 2, 3 packets. + QuicAckFrame ack(0, QuicTime::Zero(), 4); + QuicPacketEntropyHash kRandomEntropyHash = 129u; + ack.sent_info.entropy_hash = kRandomEntropyHash; + creator_.set_sequence_number(5); + QuicPacketEntropyHash six_packet_entropy_hash = 0; + if (ProcessAckPacket(&ack, true)) { + six_packet_entropy_hash = 1 << 6; + }; + + EXPECT_EQ((kRandomEntropyHash + (1 << 5) + six_packet_entropy_hash), + outgoing_ack()->received_info.entropy_hash); +} + +TEST_F(QuicConnectionTest, UpdateEntropyHashUptoCurrentPacket) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true)); + ProcessDataPacket(1, 1, kEntropyFlag); + ProcessDataPacket(5, 1, !kEntropyFlag); + ProcessDataPacket(22, 1, kEntropyFlag); + EXPECT_EQ(66u, outgoing_ack()->received_info.entropy_hash); + creator_.set_sequence_number(22); + QuicPacketEntropyHash kRandomEntropyHash = 85u; + // Current packet is the least unacked packet. + QuicAckFrame ack(0, QuicTime::Zero(), 23); + ack.sent_info.entropy_hash = kRandomEntropyHash; + QuicPacketEntropyHash ack_entropy_hash = ProcessAckPacket(&ack, true); + EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash), + outgoing_ack()->received_info.entropy_hash); + ProcessDataPacket(25, 1, kEntropyFlag); + EXPECT_EQ((kRandomEntropyHash + ack_entropy_hash + (1 << (25 % 8))), + outgoing_ack()->received_info.entropy_hash); +} + +TEST_F(QuicConnectionTest, EntropyCalculationForTruncatedAck) { + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).WillRepeatedly(Return(true)); + QuicPacketEntropyHash entropy[51]; + entropy[0] = 0; + for (int i = 1; i < 51; ++i) { + bool should_send = i % 10 != 0; + 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); + } + // Till 50 since 50th packet is not sent. + for (int i = 1; i < 50; ++i) { + EXPECT_EQ(entropy[i], QuicConnectionPeer::ReceivedEntropyHash( + &connection_, i)); + } +} + +TEST_F(QuicConnectionTest, CheckSentEntropyHash) { + creator_.set_sequence_number(1); + SequenceNumberSet missing_packets; + QuicPacketEntropyHash entropy_hash = 0; + QuicPacketSequenceNumber max_sequence_number = 51; + for (QuicPacketSequenceNumber i = 1; i <= max_sequence_number; ++i) { + bool is_missing = i % 10 != 0; + bool entropy_flag = (i & (i - 1)) != 0; + QuicPacketEntropyHash packet_entropy_hash = 0; + if (entropy_flag) { + packet_entropy_hash = 1 << (i % 8); + } + QuicPacket* packet = ConstructDataPacket(i, 0, entropy_flag); + connection_.SendOrQueuePacket( + ENCRYPTION_NONE, i, packet, packet_entropy_hash, + HAS_RETRANSMITTABLE_DATA); + + if (is_missing) { + missing_packets.insert(i); + continue; + } + + entropy_hash ^= packet_entropy_hash; + } + EXPECT_TRUE(QuicConnectionPeer::IsValidEntropy( + &connection_, max_sequence_number, missing_packets, entropy_hash)) + << ""; +} + +TEST_F(QuicConnectionTest, ServerSendsVersionNegotiationPacket) { + framer_.set_version_for_tests(QUIC_VERSION_UNSUPPORTED); + + QuicPacketHeader header; + header.public_header.guid = guid_; + 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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); + + framer_.set_version(QuicVersionMax()); + connection_.set_is_server(true); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + EXPECT_TRUE(helper_->version_negotiation_packet() != NULL); + + size_t num_versions = arraysize(kSupportedQuicVersions); + EXPECT_EQ(num_versions, + helper_->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], + helper_->version_negotiation_packet()->versions[i]); + } +} + +TEST_F(QuicConnectionTest, ClientHandlesVersionNegotiation) { + // Start out with some unsupported version. + QuicConnectionPeer::GetFramer(&connection_)->set_version_for_tests( + QUIC_VERSION_UNSUPPORTED); + + QuicPacketHeader header; + header.public_header.guid = guid_; + 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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + encrypted.reset(framer_.EncryptPacket(ENCRYPTION_NONE, 12, *packet)); + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(1); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); + + ASSERT_FALSE(QuicPacketCreatorPeer::SendVersionInPacket( + QuicConnectionPeer::GetPacketCreator(&connection_))); +} + +TEST_F(QuicConnectionTest, BadVersionNegotiation) { + QuicPacketHeader header; + header.public_header.guid = guid_; + 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_, ConnectionClose(QUIC_INVALID_VERSION_NEGOTIATION_PACKET, + false)); + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.BuildVersionNegotiationPacket( + header.public_header, supported_versions)); + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); +} + +TEST_F(QuicConnectionTest, CheckSendStats) { + EXPECT_CALL(*send_algorithm_, AbandoningPacket(_, _)).Times(3); + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, NOT_RETRANSMISSION)); + connection_.SendStreamData(1u, "first", 0, !kFin); + size_t first_packet_size = last_sent_packet_size(); + + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, NOT_RETRANSMISSION)); + connection_.SendStreamData(1u, "second", 0, !kFin); + size_t second_packet_size = last_sent_packet_size(); + + // 2 retransmissions due to rto, 1 due to explicit nack. + EXPECT_CALL(*send_algorithm_, + SentPacket(_, _, _, IS_RETRANSMISSION)).Times(3); + + // Retransmit due to RTO. + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(10)); + connection_.OnRetransmissionTimeout(); + + // Retransmit due to explicit nacks + QuicAckFrame nack_three(4, QuicTime::Zero(), 0); + nack_three.received_info.missing_packets.insert(3); + nack_three.received_info.entropy_hash = + QuicConnectionPeer::GetSentEntropyHash(&connection_, 4) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 3) ^ + QuicConnectionPeer::GetSentEntropyHash(&connection_, 2); + QuicFrame frame(&nack_three); + EXPECT_CALL(visitor_, OnAck(_)); + EXPECT_CALL(*send_algorithm_, OnIncomingAck(_, _, _)).Times(1); + EXPECT_CALL(*send_algorithm_, OnIncomingLoss(_)).Times(1); + EXPECT_CALL(visitor_, OnCanWrite()).Times(3).WillRepeatedly(Return(true)); + + ProcessFramePacket(frame); + ProcessFramePacket(frame); + ProcessFramePacket(frame); + + EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce( + Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce( + Return(QuicBandwidth::Zero())); + + 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(2u, stats.rto_count); +} + +TEST_F(QuicConnectionTest, CheckReceiveStats) { + 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); // Fec packet + + EXPECT_CALL(*send_algorithm_, SmoothedRtt()).WillOnce( + Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, BandwidthEstimate()).WillOnce( + Return(QuicBandwidth::Zero())); + + 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); +} + +TEST_F(QuicConnectionTest, TestFecGroupLimits) { + // Create and return a group for 1 + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) != NULL); + + // Create and return a group for 2 + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != NULL); + + // Create and return a group for 4. This should remove 1 but not 2. + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 1) == NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) != NULL); + + // Create and return a group for 3. This will kill off 2. + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 2) == NULL); + + // Verify that adding 5 kills off 3, despite 4 being created before 3. + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 5) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 4) != NULL); + ASSERT_TRUE(QuicConnectionPeer::GetFecGroup(&connection_, 3) == NULL); +} + +TEST_F(QuicConnectionTest, DontProcessFramesIfPacketClosedConnection) { + // Construct a packet with stream frame and connection close frame. + header_.public_header.guid = guid_; + 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; + qccf.ack_frame = QuicAckFrame(0, QuicTime::Zero(), 1); + QuicFrame close_frame(&qccf); + QuicFrame stream_frame(&frame1_); + + QuicFrames frames; + frames.push_back(stream_frame); + frames.push_back(close_frame); + scoped_ptr<QuicPacket> packet( + framer_.BuildUnsizedDataPacket(header_, frames).packet); + EXPECT_TRUE(NULL != packet.get()); + scoped_ptr<QuicEncryptedPacket> encrypted(framer_.EncryptPacket( + ENCRYPTION_NONE, 1, *packet)); + + EXPECT_CALL(visitor_, ConnectionClose(QUIC_PEER_GOING_AWAY, true)); + EXPECT_CALL(visitor_, OnPacket(_, _, _, _)).Times(0); + + connection_.ProcessUdpPacket(IPEndPoint(), IPEndPoint(), *encrypted); +} + +TEST_F(QuicConnectionTest, SelectMutualVersion) { + // 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_F(QuicConnectionTest, ConnectionCloseWhenNotWriteBlocked) { + helper_->set_blocked(false); // Already default. + + // Send a packet (but write will not block). + connection_.SendStreamData(1, "foo", 0, !kFin); + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + EXPECT_EQ(1u, helper_->packets_write_attempts()); + + // Send an erroneous packet to close the connection. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_PACKET_HEADER, false)); + ProcessDataPacket(6000, 0, !kEntropyFlag); + EXPECT_EQ(2u, helper_->packets_write_attempts()); +} + +TEST_F(QuicConnectionTest, ConnectionCloseWhenWriteBlocked) { + EXPECT_EQ(0u, connection_.NumQueuedPackets()); + helper_->set_blocked(true); + + // Send a packet to so that write will really block. + connection_.SendStreamData(1, "foo", 0, !kFin); + EXPECT_EQ(1u, connection_.NumQueuedPackets()); + EXPECT_EQ(1u, helper_->packets_write_attempts()); + + // Send an erroneous packet to close the connection. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_PACKET_HEADER, false)); + ProcessDataPacket(6000, 0, !kEntropyFlag); + EXPECT_EQ(1u, helper_->packets_write_attempts()); +} + +TEST_F(QuicConnectionTest, ConnectionCloseWhenNothingPending) { + helper_->set_blocked(true); + + // Send an erroneous packet to close the connection. + EXPECT_CALL(visitor_, ConnectionClose(QUIC_INVALID_PACKET_HEADER, false)); + ProcessDataPacket(6000, 0, !kEntropyFlag); + EXPECT_EQ(1u, helper_->packets_write_attempts()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_crypto_client_stream.cc b/chromium/net/quic/quic_crypto_client_stream.cc new file mode 100644 index 00000000000..585b293560b --- /dev/null +++ b/chromium/net/quic/quic_crypto_client_stream.cc @@ -0,0 +1,375 @@ +// 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 "net/base/completion_callback.h" +#include "net/base/net_errors.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/crypto/proof_verifier.h" +#include "net/quic/crypto/proof_verifier_chromium.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_session.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "net/ssl/ssl_info.h" + +namespace net { + +namespace { + +// Copies CertVerifyResult from |verify_details| to |cert_verify_result|. +void CopyCertVerifyResult( + const ProofVerifyDetails* verify_details, + scoped_ptr<CertVerifyResult>* cert_verify_result) { + const CertVerifyResult* cert_verify_result_other = + &(reinterpret_cast<const ProofVerifyDetailsChromium*>( + verify_details))->cert_verify_result; + CertVerifyResult* result_copy = new CertVerifyResult; + result_copy->CopyFrom(*cert_verify_result_other); + cert_verify_result->reset(result_copy); +} + +} // namespace + +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_ == NULL) { + return; + } + + stream_->verify_ok_ = ok; + stream_->verify_error_details_ = error_details; + stream_->verify_details_.reset(details->release()); + stream_->proof_verify_callback_ = NULL; + stream_->DoHandshakeLoop(NULL); + + // The ProofVerifier owns this object and will delete it when this method + // returns. +} + +void QuicCryptoClientStream::ProofVerifierCallbackImpl::Cancel() { + stream_ = NULL; +} + + +QuicCryptoClientStream::QuicCryptoClientStream( + const string& server_hostname, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) + : QuicCryptoStream(session), + next_state_(STATE_IDLE), + num_client_hellos_(0), + crypto_config_(crypto_config), + server_hostname_(server_hostname), + generation_counter_(0), + proof_verify_callback_(NULL) { +} + +QuicCryptoClientStream::~QuicCryptoClientStream() { + if (proof_verify_callback_) { + proof_verify_callback_->Cancel(); + } +} + +void QuicCryptoClientStream::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QuicCryptoStream::OnHandshakeMessage(message); + + DoHandshakeLoop(&message); +} + +bool QuicCryptoClientStream::CryptoConnect() { + next_state_ = STATE_SEND_CHLO; + DoHandshakeLoop(NULL); + return true; +} + +int QuicCryptoClientStream::num_sent_client_hellos() const { + return num_client_hellos_; +} + +// TODO(rtenneti): Add unittests for GetSSLInfo which exercise the various ways +// we learn about SSL info (sync vs async vs cached). +bool QuicCryptoClientStream::GetSSLInfo(SSLInfo* ssl_info) { + 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(rtenneti): Figure out what to set for the following. + // Temporarily hard coded cipher_suite as 0xc031 to represent + // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (from + // net/ssl/ssl_cipher_suite_names.cc) and encryption as 256. + int cipher_suite = 0xc02f; + int ssl_connection_status = 0; + ssl_connection_status |= + (cipher_suite & SSL_CONNECTION_CIPHERSUITE_MASK) << + SSL_CONNECTION_CIPHERSUITE_SHIFT; + ssl_connection_status |= + (SSL_CONNECTION_VERSION_TLS1_2 & 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 = false; + ssl_info->security_bits = 256; + ssl_info->handshake_type = SSLInfo::HANDSHAKE_FULL; + return true; +} + +// 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) { + CryptoHandshakeMessage out; + QuicErrorCode error; + string error_details; + QuicCryptoClientConfig::CachedState* cached = + crypto_config_->LookupOrCreate(server_hostname_); + + if (in != NULL) { + DVLOG(1) << "Client received: " << in->DebugString(); + } + + for (;;) { + const State state = next_state_; + next_state_ = STATE_IDLE; + switch (state) { + case STATE_SEND_CHLO: { + // 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_++; + + if (!cached->IsComplete(session()->connection()->clock()->WallNow())) { + crypto_config_->FillInchoateClientHello( + server_hostname_, cached, &crypto_negotiated_params_, &out); + next_state_ = STATE_RECV_REJ; + DVLOG(1) << "Client Sending: " << out.DebugString(); + SendHandshakeMessage(out); + return; + } + session()->config()->ToHandshakeMessage(&out); + error = crypto_config_->FillClientHello( + server_hostname_, + session()->connection()->guid(), + cached, + session()->connection()->clock()->WallNow(), + session()->connection()->random_generator(), + &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; + } + if (cached->proof_verify_details()) { + CopyCertVerifyResult(cached->proof_verify_details(), + &cert_verify_result_); + } else { + cert_verify_result_.reset(); + } + next_state_ = STATE_RECV_SHLO; + DVLOG(1) << "Client Sending: " << out.DebugString(); + SendHandshakeMessage(out); + // Be prepared to decrypt with the new server write key. + session()->connection()->SetAlternativeDecrypter( + crypto_negotiated_params_.initial_crypters.decrypter.release(), + 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); + } + return; + } + case STATE_RECV_REJ: + // 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) { + CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected REJ"); + return; + } + error = crypto_config_->ProcessRejection( + cached, *in, session()->connection()->clock()->WallNow(), + &crypto_negotiated_params_, &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails(error, error_details); + return; + } + if (!cached->proof_valid()) { + ProofVerifier* verifier = crypto_config_->proof_verifier(); + if (!verifier) { + // If no verifier is set then we don't check the certificates. + cached->SetProofValid(); + } else if (!cached->signature().empty()) { + next_state_ = STATE_VERIFY_PROOF; + break; + } + } + next_state_ = STATE_SEND_CHLO; + break; + case STATE_VERIFY_PROOF: { + 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; + + ProofVerifier::Status status = verifier->VerifyProof( + session()->connection()->version(), + server_hostname_, + cached->server_config(), + cached->certs(), + cached->signature(), + &verify_error_details_, + &verify_details_, + proof_verify_callback); + + switch (status) { + case ProofVerifier::PENDING: + proof_verify_callback_ = proof_verify_callback; + DVLOG(1) << "Doing VerifyProof"; + return; + case ProofVerifier::FAILURE: + break; + case ProofVerifier::SUCCESS: + verify_ok_ = true; + break; + } + break; + } + case STATE_VERIFY_PROOF_COMPLETE: + if (!verify_ok_) { + CopyCertVerifyResult(verify_details_.get(), &cert_verify_result_); + 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 { + cached->SetProofValid(); + cached->SetProofVerifyDetails(verify_details_.release()); + next_state_ = STATE_SEND_CHLO; + } + break; + case STATE_RECV_SHLO: { + // 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 NULL 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() == NULL) { + // The rejection was sent encrypted! + CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, + "encrypted REJ message"); + return; + } + next_state_ = STATE_RECV_REJ; + break; + } + if (in->tag() != kSHLO) { + CloseConnectionWithDetails(QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + "Expected SHLO or REJ"); + return; + } + // alternative_decrypter will be NULL 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() != NULL) { + // The server hello was sent without encryption. + CloseConnectionWithDetails(QUIC_CRYPTO_ENCRYPTION_LEVEL_INCORRECT, + "unencrypted SHLO message"); + return; + } + error = crypto_config_->ProcessServerHello( + *in, session()->connection()->guid(), &crypto_negotiated_params_, + &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } + error = session()->config()->ProcessServerHello(*in, &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails( + error, "Server hello invalid: " + error_details); + return; + } + 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(), 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); + return; + } + case STATE_IDLE: + // This means that the peer sent us a message that we weren't expecting. + CloseConnection(QUIC_INVALID_CRYPTO_MESSAGE_TYPE); + return; + } + } +} + +} // namespace net diff --git a/chromium/net/quic/quic_crypto_client_stream.h b/chromium/net/quic/quic_crypto_client_stream.h new file mode 100644 index 00000000000..5a9042b925c --- /dev/null +++ b/chromium/net/quic/quic_crypto_client_stream.h @@ -0,0 +1,123 @@ +// 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/cert/cert_verify_result.h" +#include "net/cert/x509_certificate.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/proof_verifier.h" +#include "net/quic/quic_config.h" +#include "net/quic/quic_crypto_stream.h" + +namespace net { + +class ProofVerifyDetails; +class QuicSession; +class SSLInfo; + +namespace test { +class CryptoTestUtils; +} // namespace test + +class NET_EXPORT_PRIVATE QuicCryptoClientStream : public QuicCryptoStream { + public: + QuicCryptoClientStream(const string& server_hostname, + QuicSession* session, + 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; + + // Gets the SSL connection information. + bool GetSSLInfo(SSLInfo* ssl_info); + + private: + // 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 ProofVerifierCallbackImpl; + + enum State { + STATE_IDLE, + STATE_SEND_CHLO, + STATE_RECV_REJ, + STATE_VERIFY_PROOF, + STATE_VERIFY_PROOF_COMPLETE, + STATE_RECV_SHLO, + }; + + // DoHandshakeLoop performs a step of the handshake state machine. Note that + // |in| may be NULL if the call did not result from a received message + void DoHandshakeLoop(const CryptoHandshakeMessage* in); + + 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 + std::string server_hostname_; + + // Generation counter from QuicCryptoClientConfig's CachedState. + uint64 generation_counter_; + + // 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_; + + // The result of certificate verification. + scoped_ptr<CertVerifyResult> cert_verify_result_; + + DISALLOW_COPY_AND_ASSIGN(QuicCryptoClientStream); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_H_ diff --git a/chromium/net/quic/quic_crypto_client_stream_factory.h b/chromium/net/quic/quic_crypto_client_stream_factory.h new file mode 100644 index 00000000000..4fa5f573ea0 --- /dev/null +++ b/chromium/net/quic/quic_crypto_client_stream_factory.h @@ -0,0 +1,31 @@ +// 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 QuicCryptoClientStream; +class QuicSession; + +// 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 string& server_hostname, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CRYPTO_CLIENT_STREAM_FACTORY_H_ diff --git a/chromium/net/quic/quic_crypto_client_stream_test.cc b/chromium/net/quic/quic_crypto_client_stream_test.cc new file mode 100644 index 00000000000..9f9e7f75c57 --- /dev/null +++ b/chromium/net/quic/quic_crypto_client_stream_test.cc @@ -0,0 +1,194 @@ +// 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_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/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"; + +class TestQuicVisitor : public NoOpFramerVisitor { + public: + TestQuicVisitor() + : frame_valid_(false) { + } + + // NoOpFramerVisitor + virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE { + frame_ = frame; + frame_valid_ = true; + return true; + } + + bool frame_valid() const { + return frame_valid_; + } + QuicStreamFrame* frame() { return &frame_; } + + private: + QuicStreamFrame frame_; + bool frame_valid_; + + DISALLOW_COPY_AND_ASSIGN(TestQuicVisitor); +}; + +class QuicCryptoClientStreamTest : public ::testing::Test { + public: + QuicCryptoClientStreamTest() + : addr_(), + connection_(new PacketSavingConnection(1, addr_, true)), + session_(new TestSession(connection_, DefaultQuicConfig(), true)), + stream_(new QuicCryptoClientStream(kServerHostname, session_.get(), + &crypto_config_)) { + session_->SetCryptoStream(stream_.get()); + crypto_config_.SetDefaults(); + } + + void CompleteCryptoHandshake() { + EXPECT_TRUE(stream_->CryptoConnect()); + CryptoTestUtils::HandshakeWithFakeServer(connection_, stream_.get()); + } + + void ConstructHandshakeMessage() { + CryptoFramer framer; + message_data_.reset(framer.ConstructHandshakeMessage(message_)); + } + + IPEndPoint addr_; + PacketSavingConnection* connection_; + scoped_ptr<TestSession> session_; + scoped_ptr<QuicCryptoClientStream> stream_; + CryptoHandshakeMessage message_; + scoped_ptr<QuicData> message_data_; + QuicCryptoClientConfig crypto_config_; +}; + +TEST_F(QuicCryptoClientStreamTest, NotInitiallyConected) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + EXPECT_FALSE(stream_->encryption_established()); + EXPECT_FALSE(stream_->handshake_confirmed()); +} + +TEST_F(QuicCryptoClientStreamTest, ConnectedAfterSHLO) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); + EXPECT_TRUE(stream_->encryption_established()); + EXPECT_TRUE(stream_->handshake_confirmed()); +} + +TEST_F(QuicCryptoClientStreamTest, MessageAfterHandshake) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); + + EXPECT_CALL(*connection_, SendConnectionClose( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); + message_.set_tag(kCHLO); + ConstructHandshakeMessage(); + stream_->ProcessData(message_data_->data(), message_data_->length()); +} + +TEST_F(QuicCryptoClientStreamTest, BadMessageType) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + EXPECT_TRUE(stream_->CryptoConnect()); + + message_.set_tag(kCHLO); + ConstructHandshakeMessage(); + + EXPECT_CALL(*connection_, SendConnectionCloseWithDetails( + QUIC_INVALID_CRYPTO_MESSAGE_TYPE, "Expected REJ")); + stream_->ProcessData(message_data_->data(), message_data_->length()); +} + +TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); + + const QuicConfig* config = session_->config(); + EXPECT_EQ(kQBIC, config->congestion_control()); + EXPECT_EQ(kDefaultTimeoutSecs, + config->idle_connection_state_lifetime().ToSeconds()); + EXPECT_EQ(kDefaultMaxStreamsPerConnection, + config->max_streams_per_connection()); + EXPECT_EQ(0, config->keepalive_timeout().ToSeconds()); + + const QuicCryptoNegotiatedParameters& crypto_params( + stream_->crypto_negotiated_params()); + EXPECT_EQ(kAESG, crypto_params.aead); + EXPECT_EQ(kC255, crypto_params.key_exchange); +} + +TEST_F(QuicCryptoClientStreamTest, InvalidHostname) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + stream_.reset(new QuicCryptoClientStream("invalid", session_.get(), + &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(1, addr_, true); + session_.reset(new TestSession(connection_, QuicConfig(), true)); + stream_.reset(new QuicCryptoClientStream(kServerHostname, session_.get(), + &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()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_crypto_server_stream.cc b/chromium/net/quic/quic_crypto_server_stream.cc new file mode 100644 index 00000000000..a23a34d5b49 --- /dev/null +++ b/chromium/net/quic/quic_crypto_server_stream.cc @@ -0,0 +1,142 @@ +// 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_server_config.h" +#include "net/quic/crypto/crypto_utils.h" +#include "net/quic/quic_config.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_session.h" + +namespace net { + +QuicCryptoServerStream::QuicCryptoServerStream( + const QuicCryptoServerConfig& crypto_config, + QuicSession* session) + : QuicCryptoStream(session), + crypto_config_(crypto_config) { +} + +QuicCryptoServerStream::~QuicCryptoServerStream() { +} + +void QuicCryptoServerStream::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + QuicCryptoStream::OnHandshakeMessage(message); + + // 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; + } + + string error_details; + CryptoHandshakeMessage reply; + + QuicErrorCode error = ProcessClientHello(message, &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(); + error = config->ProcessClientHello(message, &error_details); + if (error != QUIC_NO_ERROR) { + CloseConnectionWithDetails(error, error_details); + return; + } + + 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()); + SendHandshakeMessage(reply); + + 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(), + false /* don't latch */); + + encryption_established_ = true; + handshake_confirmed_ = true; + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); +} + +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, + CryptoHandshakeMessage* reply, + string* error_details) { + return crypto_config_.ProcessClientHello( + message, + session()->connection()->version(), + session()->connection()->guid(), + session()->connection()->peer_address(), + session()->connection()->clock(), + session()->connection()->random_generator(), + &crypto_negotiated_params_, reply, error_details); +} + +} // namespace net diff --git a/chromium/net/quic/quic_crypto_server_stream.h b/chromium/net/quic/quic_crypto_server_stream.h new file mode 100644 index 00000000000..f1e30cb558a --- /dev/null +++ b/chromium/net/quic/quic_crypto_server_stream.h @@ -0,0 +1,57 @@ +// 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/quic_config.h" +#include "net/quic/quic_crypto_stream.h" + +namespace net { + +class CryptoHandshakeMessage; +class QuicCryptoServerConfig; +class QuicSession; + +namespace test { +class CryptoTestUtils; +} // namespace test + +class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { + public: + QuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config, + QuicSession* session); + explicit QuicCryptoServerStream(QuicSession* session); + virtual ~QuicCryptoServerStream(); + + // 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; + + protected: + virtual QuicErrorCode ProcessClientHello( + const CryptoHandshakeMessage& message, + CryptoHandshakeMessage* reply, + std::string* error_details); + + const QuicCryptoServerConfig* crypto_config() { return &crypto_config_; } + + private: + friend class test::CryptoTestUtils; + + // crypto_config_ contains crypto parameters for the handshake. + const QuicCryptoServerConfig& crypto_config_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CRYPTO_SERVER_STREAM_H_ diff --git a/chromium/net/quic/quic_crypto_server_stream_test.cc b/chromium/net/quic/quic_crypto_server_stream_test.cc new file mode 100644 index 00000000000..3bb2593f1b2 --- /dev/null +++ b/chromium/net/quic/quic_crypto_server_stream_test.cc @@ -0,0 +1,265 @@ +// 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_server_config.h" +#include "net/quic/crypto/crypto_utils.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/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 testing::_; + +namespace net { +namespace test { +namespace { + +// TODO(agl): Use rch's utility class for parsing a message when committed. +class TestQuicVisitor : public NoOpFramerVisitor { + public: + TestQuicVisitor() {} + + // NoOpFramerVisitor + virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE { + frame_ = frame; + return true; + } + + QuicStreamFrame* frame() { return &frame_; } + + private: + QuicStreamFrame frame_; + + DISALLOW_COPY_AND_ASSIGN(TestQuicVisitor); +}; + +class QuicCryptoServerStreamTest : public ::testing::Test { + public: + QuicCryptoServerStreamTest() + : guid_(1), + addr_(ParseIPLiteralToNumber("192.0.2.33", &ip_) ? + ip_ : IPAddressNumber(), 1), + connection_(new PacketSavingConnection(guid_, addr_, true)), + session_(connection_, QuicConfig(), true), + crypto_config_(QuicCryptoServerConfig::TESTING, + QuicRandom::GetInstance()), + stream_(crypto_config_, &session_) { + 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(rtenneti): Enable testing of ProofSource. + // crypto_config_.SetProofSource(CryptoTestUtils::ProofSourceForTesting()); + + CryptoTestUtils::SetupCryptoServerConfigForTest( + connection_->clock(), connection_->random_generator(), + session_.config(), &crypto_config_); + } + + void ConstructHandshakeMessage() { + CryptoFramer framer; + message_data_.reset(framer.ConstructHandshakeMessage(message_)); + } + + int CompleteCryptoHandshake() { + return CryptoTestUtils::HandshakeWithFakeClient(connection_, &stream_, + client_options_); + } + + protected: + IPAddressNumber ip_; + QuicGuid guid_; + IPEndPoint addr_; + PacketSavingConnection* connection_; + TestSession session_; + QuicConfig config_; + QuicCryptoServerConfig crypto_config_; + QuicCryptoServerStream stream_; + CryptoHandshakeMessage message_; + scoped_ptr<QuicData> message_data_; + CryptoTestUtils::FakeClientOptions client_options_; +}; + +TEST_F(QuicCryptoServerStreamTest, NotInitiallyConected) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + EXPECT_FALSE(stream_.encryption_established()); + EXPECT_FALSE(stream_.handshake_confirmed()); +} + +TEST_F(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + // 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()); +} + +TEST_F(QuicCryptoServerStreamTest, ZeroRTT) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + QuicGuid guid(1); + IPAddressNumber ip; + ParseIPLiteralToNumber("127.0.0.1", &ip); + IPEndPoint addr(ip, 0); + PacketSavingConnection* client_conn = + new PacketSavingConnection(guid, addr, false); + PacketSavingConnection* server_conn = + new PacketSavingConnection(guid, addr, false); + client_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000)); + server_conn->AdvanceTime(QuicTime::Delta::FromSeconds(100000)); + + QuicConfig client_config; + client_config.SetDefaults(); + scoped_ptr<TestSession> client_session( + new TestSession(client_conn, client_config, false)); + QuicCryptoClientConfig client_crypto_config; + client_crypto_config.SetDefaults(); + + scoped_ptr<QuicCryptoClientStream> client(new QuicCryptoClientStream( + "test.example.com", client_session.get(), &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_, true)); + 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(guid, addr, false); + server_conn = new PacketSavingConnection(guid, addr, 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 TestSession(client_conn, client_config, false)); + server_session.reset(new TestSession(server_conn, config_, true)); + client.reset(new QuicCryptoClientStream( + "test.example.com", client_session.get(), &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()); + + CryptoTestUtils::CommunicateHandshakeMessages( + client_conn, client.get(), server_conn, server.get()); + EXPECT_EQ(1, client->num_sent_client_hellos()); +} + +TEST_F(QuicCryptoServerStreamTest, MessageAfterHandshake) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + CompleteCryptoHandshake(); + EXPECT_CALL(*connection_, SendConnectionClose( + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE)); + message_.set_tag(kCHLO); + ConstructHandshakeMessage(); + stream_.ProcessData(message_data_->data(), message_data_->length()); +} + +TEST_F(QuicCryptoServerStreamTest, BadMessageType) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + message_.set_tag(kSHLO); + ConstructHandshakeMessage(); + EXPECT_CALL(*connection_, SendConnectionClose( + QUIC_INVALID_CRYPTO_MESSAGE_TYPE)); + stream_.ProcessData(message_data_->data(), message_data_->length()); +} + +TEST_F(QuicCryptoServerStreamTest, WithoutCertificates) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + crypto_config_.SetProofSource(NULL); + 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_F(QuicCryptoServerStreamTest, ChannelID) { + if (!Aes128Gcm12Encrypter::IsSupported()) { + LOG(INFO) << "AES GCM not supported. Test skipped."; + return; + } + + client_options_.channel_id_enabled = true; + // TODO(rtenneti): Enable testing of ProofVerifier. + // CompleteCryptoHandshake verifies + // stream_.crypto_negotiated_params().channel_id is correct. + EXPECT_EQ(2, CompleteCryptoHandshake()); + EXPECT_TRUE(stream_.encryption_established()); + EXPECT_TRUE(stream_.handshake_confirmed()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_crypto_stream.cc b/chromium/net/quic/quic_crypto_stream.cc new file mode 100644 index 00000000000..569648f1baa --- /dev/null +++ b/chromium/net/quic/quic_crypto_stream.cc @@ -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. + +#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/quic_connection.h" +#include "net/quic/quic_session.h" + +using std::string; +using base::StringPiece; + +namespace net { + +QuicCryptoStream::QuicCryptoStream(QuicSession* session) + : ReliableQuicStream(kCryptoStreamId, session), + encryption_established_(false), + handshake_confirmed_(false) { + crypto_framer_.set_visitor(this); +} + +void QuicCryptoStream::OnError(CryptoFramer* framer) { + session()->ConnectionClose(framer->error(), false); +} + +void QuicCryptoStream::OnHandshakeMessage( + const CryptoHandshakeMessage& message) { + session()->OnCryptoHandshakeMessageReceived(message); +} + +uint32 QuicCryptoStream::ProcessData(const char* data, + uint32 data_len) { + // Do not process handshake messages after the handshake is confirmed. + if (handshake_confirmed()) { + CloseConnection(QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE); + return 0; + } + if (!crypto_framer_.ProcessInput(StringPiece(data, data_len))) { + CloseConnection(crypto_framer_.error()); + return 0; + } + return data_len; +} + +void QuicCryptoStream::CloseConnection(QuicErrorCode error) { + session()->connection()->SendConnectionClose(error); +} + +void QuicCryptoStream::CloseConnectionWithDetails(QuicErrorCode error, + const string& details) { + session()->connection()->SendConnectionCloseWithDetails(error, details); +} + +void QuicCryptoStream::SendHandshakeMessage( + const CryptoHandshakeMessage& message) { + session()->OnCryptoHandshakeMessageSent(message); + const QuicData& data = message.GetSerialized(); + // TODO(wtc): check the return value. + WriteData(string(data.data(), data.length()), false); +} + +const QuicCryptoNegotiatedParameters& +QuicCryptoStream::crypto_negotiated_params() const { + return crypto_negotiated_params_; +} + +} // namespace net diff --git a/chromium/net/quic/quic_crypto_stream.h b/chromium/net/quic/quic_crypto_stream.h new file mode 100644 index 00000000000..c402b0d9b44 --- /dev/null +++ b/chromium/net/quic/quic_crypto_stream.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_QUIC_CRYPTO_STREAM_H_ +#define NET_QUIC_QUIC_CRYPTO_STREAM_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 ProcessData(const char* data, uint32 data_len) OVERRIDE; + + // Sends |message| to the peer. + // TODO(wtc): return a success/failure status. + void SendHandshakeMessage(const CryptoHandshakeMessage& message); + + bool encryption_established() { return encryption_established_; } + bool handshake_confirmed() { return handshake_confirmed_; } + + const QuicCryptoNegotiatedParameters& crypto_negotiated_params() const; + + protected: + // Closes the connection + void CloseConnection(QuicErrorCode error); + void CloseConnectionWithDetails(QuicErrorCode error, const string& details); + + 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/chromium/net/quic/quic_crypto_stream_test.cc b/chromium/net/quic/quic_crypto_stream_test.cc new file mode 100644 index 00000000000..cc69304b749 --- /dev/null +++ b/chromium/net/quic/quic_crypto_stream_test.cc @@ -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. + +#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 "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() + : addr_(IPAddressNumber(), 1), + connection_(new MockConnection(1, addr_, false)), + session_(connection_, true), + 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: + IPEndPoint addr_; + 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, OnErrorClosesConnection) { + CryptoFramer framer; + EXPECT_CALL(session_, ConnectionClose(QUIC_NO_ERROR, false)); + stream_.OnError(&framer); +} + +TEST_F(QuicCryptoStreamTest, ProcessData) { + EXPECT_EQ(message_data_->length(), + stream_.ProcessData(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_.ProcessData(bad.data(), bad.length())); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_data_reader.cc b/chromium/net/quic/quic_data_reader.cc new file mode 100644 index 00000000000..3bb7fc3f8ab --- /dev/null +++ b/chromium/net/quic/quic_data_reader.cc @@ -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. + +#include "net/quic/quic_data_reader.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::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/chromium/net/quic/quic_data_reader.h b/chromium/net/quic/quic_data_reader.h new file mode 100644 index 00000000000..fff07ddc703 --- /dev/null +++ b/chromium/net/quic/quic_data_reader.h @@ -0,0 +1,126 @@ +// 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 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_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_DATA_READER_H_ diff --git a/chromium/net/quic/quic_data_writer.cc b/chromium/net/quic/quic_data_writer.cc new file mode 100644 index 00000000000..e52cd03248b --- /dev/null +++ b/chromium/net/quic/quic_data_writer.cc @@ -0,0 +1,152 @@ +// 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_ = NULL; + 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::WriteUInt128(uint128 value) { + return WriteUInt64(Uint128Low64(value)) && WriteUInt64(Uint128High64(value)); +} + +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()); +} + +char* QuicDataWriter::BeginWrite(size_t length) { + if (length_ > capacity_) { + return NULL; + } + + if (capacity_ - length_ < length) { + return NULL; + } + +#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) { + DCHECK_LT(offset, capacity_); + 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/chromium/net/quic/quic_data_writer.h b/chromium/net/quic/quic_data_writer.h new file mode 100644 index 00000000000..f3408d12215 --- /dev/null +++ b/chromium/net/quic/quic_data_writer.h @@ -0,0 +1,80 @@ +// 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); + bool WriteUInt128(uint128 value); + bool WriteStringPiece16(base::StringPiece val); + 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_; + } + + protected: + const char* end_of_payload() const { return buffer_ + length_; } + + private: + // Returns the location that the data should be written at, or NULL 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. +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_DATA_WRITER_H_ diff --git a/chromium/net/quic/quic_data_writer_test.cc b/chromium/net/quic/quic_data_writer_test.cc new file mode 100644 index 00000000000..4fbd7e4efdf --- /dev/null +++ b/chromium/net/quic/quic_data_writer_test.cc @@ -0,0 +1,46 @@ +// 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 "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)); + + char* data = writer.take(); + + EXPECT_EQ(1, data[0]); + EXPECT_EQ(2, data[1]); + EXPECT_EQ(3, data[2]); + EXPECT_EQ(4, data[3]); + + delete[] data; +} + +TEST(QuicDataWriterDeathTest, WriteUint8ToOffset) { + QuicDataWriter writer(4); + +#if !defined(WIN32) && defined(GTEST_HAS_DEATH_TEST) +#if !defined(DCHECK_ALWAYS_ON) + EXPECT_DEBUG_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed"); +#else + EXPECT_DEATH(writer.WriteUInt8ToOffset(5, 4), "Check failed"); +#endif +#endif +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_fec_group.cc b/chromium/net/quic/quic_fec_group.cc new file mode 100644 index 00000000000..f5f4a77b908 --- /dev/null +++ b/chromium/net/quic/quic_fec_group.cc @@ -0,0 +1,166 @@ +// 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/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), + entropy_parity_(false) { +} + +QuicFecGroup::~QuicFecGroup() {} + +bool QuicFecGroup::Update(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, header.entropy_flag)) { + return false; + } + received_packets_.insert(header.packet_sequence_number); + return true; +} + +bool QuicFecGroup::UpdateFec( + QuicPacketSequenceNumber fec_packet_sequence_number, + bool fec_packet_entropy, + 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, fec_packet_entropy)) { + return false; + } + min_protected_packet_ = fec.fec_group; + max_protected_packet_ = fec_packet_sequence_number - 1; + 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 = entropy_parity_; + + 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 recevied the FEC packet, we must check + // the packets we have received. + return *received_packets_.begin() < num; +} + +bool QuicFecGroup::UpdateParity(StringPiece payload, bool entropy) { + 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_.size() == 0 && + 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()); + } + entropy_parity_ = entropy; + 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; + } + // xor of boolean values. + entropy_parity_ = (entropy_parity_ != entropy); + 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/chromium/net/quic/quic_fec_group.h b/chromium/net/quic/quic_fec_group.h new file mode 100644 index 00000000000..d905d03236e --- /dev/null +++ b/chromium/net/quic/quic_fec_group.h @@ -0,0 +1,98 @@ +// 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 <set> + +#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. + // Returns false if this packet has already been seen, true otherwise. + bool Update(const QuicPacketHeader& header, + base::StringPiece decrypted_payload); + + // Updates the FEC group based on the delivery of an FEC packet. + // 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. + // |fec_packet_entropy|: XOR of entropy of all packets in the fec group. + bool UpdateFec(QuicPacketSequenceNumber fec_packet_sequence_number, + bool fec_packet_entropy, + 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_); + } + + bool entropy_parity() const { + return entropy_parity_; + } + + QuicPacketSequenceNumber min_protected_packet() const { + return min_protected_packet_; + } + + size_t NumReceivedPackets() const { + return received_packets_.size(); + } + + private: + bool UpdateParity(base::StringPiece payload, bool entropy); + // 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_; + bool entropy_parity_; + + DISALLOW_COPY_AND_ASSIGN(QuicFecGroup); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_FEC_GROUP_H_ diff --git a/chromium/net/quic/quic_fec_group_test.cc b/chromium/net/quic/quic_fec_group_test.cc new file mode 100644 index 00000000000..d9c303aee5b --- /dev/null +++ b/chromium/net/quic/quic_fec_group_test.cc @@ -0,0 +1,219 @@ +// 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 <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/quic_fec_group.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, +}; + +const bool kTestFecPacketEntropy = false; + +} // 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]); + bool entropy_redundancy = false; + 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; + } + entropy_redundancy = (entropy_redundancy != kEntropyFlag[packet]); + } + + 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(num_packets, entropy_redundancy, fec)); + } else { + QuicPacketHeader header; + header.packet_sequence_number = packet; + header.entropy_flag = kEntropyFlag[packet]; + ASSERT_TRUE(group.Update(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(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(num_packets, entropy_redundancy, 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; + EXPECT_EQ(kEntropyFlag[lost_packet], 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(header, data1); + + QuicFecData fec; + fec.fec_group = 1; + fec.redundancy = redundancy; + + header.packet_sequence_number = 2; + ASSERT_FALSE(group.UpdateFec(2, kTestFecPacketEntropy, fec)); +} + +TEST_F(QuicFecGroupTest, ProtectsPacketsBefore) { + QuicPacketHeader header; + header.packet_sequence_number = 3; + + QuicFecGroup group; + ASSERT_TRUE(group.Update(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(header, kData[0])); + + header.packet_sequence_number = 7; + ASSERT_TRUE(group.Update(header, kData[0])); + + header.packet_sequence_number = 5; + ASSERT_TRUE(group.Update(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(3, kTestFecPacketEntropy, 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)); +} + +} // namespace net diff --git a/chromium/net/quic/quic_framer.cc b/chromium/net/quic/quic_framer.cc new file mode 100644 index 00000000000..8796456bcdc --- /dev/null +++ b/chromium/net/quic/quic_framer.cc @@ -0,0 +1,1859 @@ +// 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 "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" + +using base::StringPiece; +using std::make_pair; +using std::map; +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 QuicGuid k1ByteGuidMask = GG_UINT64_C(0x00000000000000FF); +const QuicGuid k4ByteGuidMask = GG_UINT64_C(0x00000000FFFFFFFF); + +// Mask to determine if it's a special frame type(Stream, Ack, or +// Congestion Control) by checking if the first bit is 0, then shifting right. +const uint8 kQuicFrameType0BitMask = 0x01; + +// Default frame type shift and mask. +const uint8 kQuicDefaultFrameTypeShift = 3; +const uint8 kQuicDefaultFrameTypeMask = 0x07; + +// 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; + + +const uint32 kInvalidDeltaTime = 0xffffffff; + +// 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; +} + +} // namespace + +QuicFramer::QuicFramer(QuicVersion version, + QuicTime creation_time, + bool is_server) + : visitor_(NULL), + fec_builder_(NULL), + error_(QUIC_NO_ERROR), + last_sequence_number_(0), + last_serialized_guid_(0), + quic_version_(version), + decrypter_(QuicDecrypter::Create(kNULL)), + alternative_decrypter_latch_(false), + is_server_(is_server), + creation_time_(creation_time) { + DCHECK(IsSupportedVersion(version)); + encrypter_[ENCRYPTION_NONE].reset(QuicEncrypter::Create(kNULL)); +} + +QuicFramer::~QuicFramer() {} + +// static +size_t QuicFramer::GetMinStreamFrameSize(QuicVersion version, + QuicStreamId stream_id, + QuicStreamOffset offset, + bool last_frame_in_packet) { + return kQuicFrameTypeSize + GetStreamIdSize(stream_id) + + GetStreamOffsetSize(offset) + + (last_frame_in_packet ? 0 : kQuicStreamPayloadLengthSize); +} + +// static +size_t QuicFramer::GetMinAckFrameSize() { + return kQuicFrameTypeSize + kQuicEntropyHashSize + + PACKET_6BYTE_SEQUENCE_NUMBER + kQuicEntropyHashSize + + PACKET_6BYTE_SEQUENCE_NUMBER + kQuicDeltaTimeLargestObservedSize + + kNumberOfMissingPacketsSize; +} + +// static +size_t QuicFramer::GetMinRstStreamFrameSize() { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicErrorCodeSize + + kQuicErrorDetailsLengthSize; +} + +// static +size_t QuicFramer::GetMinConnectionCloseFrameSize() { + return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize + + GetMinAckFrameSize(); +} + +// static +size_t QuicFramer::GetMinGoAwayFrameSize() { + return kQuicFrameTypeSize + kQuicErrorCodeSize + kQuicErrorDetailsLengthSize + + kQuicMaxStreamIdSize; +} + +// static +// TODO(satyamshekhar): 16 - Crypto hash for integrity. Not a static value. Use +// QuicEncrypter::GetMaxPlaintextSize. +// 16 is a conservative estimate in the case of AEAD_AES_128_GCM_12, which uses +// 12-byte tags. +size_t QuicFramer::GetMaxUnackedPackets(QuicPacketHeader header) { + return (kMaxPacketSize - GetPacketHeaderSize(header) - + GetMinAckFrameSize() - 16) / PACKET_6BYTE_SEQUENCE_NUMBER; +} + +// 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_GUID + + number_versions * kQuicVersionSize; +} + +// static +bool QuicFramer::CanTruncate(const QuicFrame& frame, size_t free_bytes) { + // TODO(ianswett): GetMinConnectionCloseFrameSize may be incorrect, because + // checking for it here results in frames not being added, but the resulting + // frames do actually fit. + if ((frame.type == ACK_FRAME || frame.type == CONNECTION_CLOSE_FRAME) && + free_bytes >= GetMinAckFrameSize()) { + return true; + } + return false; +} + +bool QuicFramer::IsSupportedVersion(const QuicVersion version) const { + for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { + if (version == kSupportedQuicVersions[i]) { + return true; + } + } + return false; +} + +size_t QuicFramer::GetSerializedFrameLength( + const QuicFrame& frame, size_t free_bytes, bool first_frame) { + if (frame.type == PADDING_FRAME) { + // PADDING implies end of packet. + return free_bytes; + } + // See if it fits as the non-last frame. + size_t frame_len = ComputeFrameLength(frame, false); + // STREAM frames save two bytes when they're the last frame in the packet. + if (frame_len > free_bytes && frame.type == STREAM_FRAME) { + frame_len = ComputeFrameLength(frame, true); + } + if (frame_len > free_bytes) { + // Only truncate the first frame in a packet, so if subsequent ones go + // over, stop including more frames. + if (!first_frame) { + return 0; + } + if (CanTruncate(frame, free_bytes)) { + // Truncate the frame so the packet will not exceed kMaxPacketSize. + // Note that we may not use every byte of the writer in this case. + DLOG(INFO) << "Truncating large frame"; + return free_bytes; + } + } + return frame_len; +} + +QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash( + const QuicPacketHeader& header) const { + if (!header.entropy_flag) { + // TODO(satyamshekhar): Return some more better value here (something that + // is not a constant). + return 0; + } + return 1 << (header.packet_sequence_number % 8); +} + +SerializedPacket QuicFramer::BuildUnsizedDataPacket( + const QuicPacketHeader& header, + const QuicFrames& frames) { + const size_t max_plaintext_size = GetMaxPlaintextSize(kMaxPacketSize); + size_t packet_size = GetPacketHeaderSize(header); + for (size_t i = 0; i < frames.size(); ++i) { + DCHECK_LE(packet_size, max_plaintext_size); + const size_t frame_size = GetSerializedFrameLength( + frames[i], max_plaintext_size - packet_size, i == 0); + DCHECK(frame_size); + packet_size += frame_size; + } + return BuildDataPacket(header, frames, packet_size); +} + +SerializedPacket QuicFramer::BuildDataPacket( + const QuicPacketHeader& header, + const QuicFrames& frames, + size_t packet_size) { + QuicDataWriter writer(packet_size); + const SerializedPacket kNoPacket(0, NULL, 0, NULL); + if (!WritePacketHeader(header, &writer)) { + return kNoPacket; + } + + for (size_t i = 0; i < frames.size(); ++i) { + const QuicFrame& frame = frames[i]; + + const bool last_frame_in_packet = i == (frames.size() - 1); + if (!AppendTypeByte(frame, last_frame_in_packet, &writer)) { + return kNoPacket; + } + + switch (frame.type) { + case PADDING_FRAME: + writer.WritePadding(); + break; + case STREAM_FRAME: + if (!AppendStreamFramePayload( + *frame.stream_frame, last_frame_in_packet, &writer)) { + return kNoPacket; + } + break; + case ACK_FRAME: + if (!AppendAckFramePayload(*frame.ack_frame, &writer)) { + return kNoPacket; + } + break; + case CONGESTION_FEEDBACK_FRAME: + if (!AppendQuicCongestionFeedbackFramePayload( + *frame.congestion_feedback_frame, &writer)) { + return kNoPacket; + } + break; + case RST_STREAM_FRAME: + if (!AppendRstStreamFramePayload(*frame.rst_stream_frame, &writer)) { + return kNoPacket; + } + break; + case CONNECTION_CLOSE_FRAME: + if (!AppendConnectionCloseFramePayload( + *frame.connection_close_frame, &writer)) { + return kNoPacket; + } + break; + case GOAWAY_FRAME: + if (!AppendGoAwayFramePayload(*frame.goaway_frame, &writer)) { + return kNoPacket; + } + break; + default: + RaiseError(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.guid_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, packet, + GetPacketEntropyHash(header), NULL); +} + +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); + SerializedPacket kNoPacket = SerializedPacket(0, NULL, 0, NULL); + if (!WritePacketHeader(header, &writer)) { + return kNoPacket; + } + + if (!writer.WriteBytes(fec.redundancy.data(), fec.redundancy.length())) { + return kNoPacket; + } + + return SerializedPacket( + header.packet_sequence_number, + QuicPacket::NewFecPacket(writer.take(), len, true, + header.public_header.guid_length, + header.public_header.version_flag, + header.public_header.sequence_number_length), + GetPacketEntropyHash(header), NULL); +} + +// static +QuicEncryptedPacket* QuicFramer::BuildPublicResetPacket( + const QuicPublicResetPacket& packet) { + DCHECK(packet.public_header.reset_flag); + size_t len = GetPublicResetPacketSize(); + QuicDataWriter writer(len); + + uint8 flags = static_cast<uint8>(PACKET_PUBLIC_FLAGS_RST | + PACKET_PUBLIC_FLAGS_8BYTE_GUID | + PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE); + if (!writer.WriteUInt8(flags)) { + return NULL; + } + + if (!writer.WriteUInt64(packet.public_header.guid)) { + return NULL; + } + + if (!writer.WriteUInt64(packet.nonce_proof)) { + return NULL; + } + + if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + packet.rejected_sequence_number, + &writer)) { + return NULL; + } + + 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_GUID | + PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE); + if (!writer.WriteUInt8(flags)) { + return NULL; + } + + if (!writer.WriteUInt64(header.guid)) { + return NULL; + } + + for (size_t i = 0; i < supported_versions.size(); ++i) { + if (!writer.WriteUInt32(QuicVersionToQuicTag(supported_versions[i]))) { + return NULL; + } + } + + return new QuicEncryptedPacket(writer.take(), len, true); +} + +bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) { + // TODO(satyamshekhar): Don't RaiseError (and close the connection) for + // invalid (unauthenticated) packets. + 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."; + return RaiseError(QUIC_INVALID_PACKET_HEADER); + } + + if (is_server_ && public_header.version_flag && + public_header.versions[0] != quic_version_) { + if (!visitor_->OnProtocolVersionMismatch(public_header.versions[0])) { + reader_.reset(NULL); + 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(NULL); + 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)) { + DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessPacketHeader sets the error. + 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()) { + 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); + if (!reader_->ReadUInt64(&packet.nonce_proof)) { + set_detailed_error("Unable to read nonce proof."); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + // TODO(satyamshekhar): validate nonce to protect against DoS. + + if (!reader_->ReadUInt48(&packet.rejected_sequence_number)) { + set_detailed_error("Unable to read rejected sequence number."); + return RaiseError(QUIC_INVALID_PUBLIC_RST_PACKET); + } + 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()) { + DCHECK_NE(QUIC_NO_ERROR, error_); // ProcessFrameData sets the error. + DLOG(WARNING) << "Unable to process frame data."; + return false; + } + + visitor_->OnPacketComplete(); + reader_.reset(NULL); + return true; +} + +bool QuicFramer::WritePacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer) { + 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; + } + switch (header.public_header.sequence_number_length) { + case PACKET_1BYTE_SEQUENCE_NUMBER: + public_flags |= PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE; + break; + case PACKET_2BYTE_SEQUENCE_NUMBER: + public_flags |= PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE; + break; + case PACKET_4BYTE_SEQUENCE_NUMBER: + public_flags |= PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE; + break; + case PACKET_6BYTE_SEQUENCE_NUMBER: + public_flags |= PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE; + break; + } + + switch (header.public_header.guid_length) { + case PACKET_0BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_0BYTE_GUID)) { + return false; + } + break; + case PACKET_1BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_1BYTE_GUID)) { + return false; + } + if (!writer->WriteUInt8(header.public_header.guid & k1ByteGuidMask)) { + return false; + } + break; + case PACKET_4BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_4BYTE_GUID)) { + return false; + } + if (!writer->WriteUInt32(header.public_header.guid & k4ByteGuidMask)) { + return false; + } + break; + case PACKET_8BYTE_GUID: + if (!writer->WriteUInt8(public_flags | PACKET_PUBLIC_FLAGS_8BYTE_GUID)) { + return false; + } + if (!writer->WriteUInt64(header.public_header.guid)) { + return false; + } + break; + } + last_serialized_guid_ = header.public_header.guid; + + 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; +} + +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 (!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_GUID) { + case PACKET_PUBLIC_FLAGS_8BYTE_GUID: + if (!reader_->ReadUInt64(&public_header->guid)) { + set_detailed_error("Unable to read GUID."); + return false; + } + public_header->guid_length = PACKET_8BYTE_GUID; + break; + case PACKET_PUBLIC_FLAGS_4BYTE_GUID: + // If the guid is truncated, expect to read the last serialized guid. + if (!reader_->ReadBytes(&public_header->guid, PACKET_4BYTE_GUID)) { + set_detailed_error("Unable to read GUID."); + return false; + } + if ((public_header->guid & k4ByteGuidMask) != + (last_serialized_guid_ & k4ByteGuidMask)) { + set_detailed_error( + "Truncated 4 byte GUID does not match previous guid."); + return false; + } + public_header->guid_length = PACKET_4BYTE_GUID; + public_header->guid = last_serialized_guid_; + break; + case PACKET_PUBLIC_FLAGS_1BYTE_GUID: + if (!reader_->ReadBytes(&public_header->guid, PACKET_1BYTE_GUID)) { + set_detailed_error("Unable to read GUID."); + return false; + } + if ((public_header->guid & k1ByteGuidMask) != + (last_serialized_guid_ & k1ByteGuidMask)) { + set_detailed_error( + "Truncated 1 byte GUID does not match previous guid."); + return false; + } + public_header->guid_length = PACKET_1BYTE_GUID; + public_header->guid = last_serialized_guid_; + break; + case PACKET_PUBLIC_FLAGS_0BYTE_GUID: + public_header->guid_length = PACKET_0BYTE_GUID; + public_header->guid = last_serialized_guid_; + break; + } + + switch (public_flags & PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE) { + case PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE: + public_header->sequence_number_length = PACKET_6BYTE_SEQUENCE_NUMBER; + break; + case PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE: + public_header->sequence_number_length = PACKET_4BYTE_SEQUENCE_NUMBER; + break; + case PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE: + public_header->sequence_number_length = PACKET_2BYTE_SEQUENCE_NUMBER; + break; + case PACKET_PUBLIC_FLAGS_1BYTE_SEQUENCE: + public_header->sequence_number_length = PACKET_1BYTE_SEQUENCE_NUMBER; + break; + } + + // 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 +bool QuicFramer::ReadGuidFromPacket(const QuicEncryptedPacket& packet, + QuicGuid* guid) { + QuicDataReader reader(packet.data(), packet.length()); + uint8 public_flags; + if (!reader.ReadBytes(&public_flags, 1)) { + return false; + } + // Ensure it's an 8 byte guid. + if ((public_flags & PACKET_PUBLIC_FLAGS_8BYTE_GUID) != + PACKET_PUBLIC_FLAGS_8BYTE_GUID) { + return false; + } + + return reader.ReadUInt64(guid); +} + +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 (!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() { + if (reader_->IsDoneReading()) { + set_detailed_error("Unable to read frame type."); + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + 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 & kQuicFrameType0BitMask) == 0) { + QuicStreamFrame frame; + if (!ProcessStreamFrame(frame_type, &frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnStreamFrame(frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + frame_type >>= 1; + if ((frame_type & kQuicFrameType0BitMask) == 0) { + QuicAckFrame frame; + if (!ProcessAckFrame(&frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnAckFrame(frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + frame_type >>= 1; + if ((frame_type & kQuicFrameType0BitMask) == 0) { + QuicCongestionFeedbackFrame frame; + if (!ProcessQuicCongestionFeedbackFrame(&frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + if (!visitor_->OnCongestionFeedbackFrame(frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + frame_type >>= 1; + + switch (frame_type) { + // STREAM_FRAME, ACK_FRAME, and CONGESTION_FEEDBACK_FRAME are handled + // above. + 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)) { + DLOG(INFO) << "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_->OnAckFrame(frame.ack_frame)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + + if (!visitor_->OnConnectionCloseFrame(frame)) { + DLOG(INFO) << "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)) { + DLOG(INFO) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + continue; + } + + 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 >> 1; + // 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; + } + + 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; + } + } + + return true; +} + +bool QuicFramer::ProcessAckFrame(QuicAckFrame* frame) { + if (!ProcessSentInfo(&frame->sent_info)) { + return false; + } + if (!ProcessReceivedInfo(&frame->received_info)) { + return false; + } + return true; +} + +bool QuicFramer::ProcessReceivedInfo(ReceivedPacketInfo* received_info) { + if (!reader_->ReadBytes(&received_info->entropy_hash, 1)) { + set_detailed_error("Unable to read entropy hash for received packets."); + return false; + } + + if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + &received_info->largest_observed)) { + set_detailed_error("Unable to read largest observed."); + return false; + } + + uint32 delta_time_largest_observed_us; + if (!reader_->ReadUInt32(&delta_time_largest_observed_us)) { + set_detailed_error("Unable to read delta time largest observed."); + return false; + } + + if (delta_time_largest_observed_us == kInvalidDeltaTime) { + received_info->delta_time_largest_observed = QuicTime::Delta::Infinite(); + } else { + received_info->delta_time_largest_observed = + QuicTime::Delta::FromMicroseconds(delta_time_largest_observed_us); + } + + uint8 num_missing_packets; + if (!reader_->ReadBytes(&num_missing_packets, 1)) { + set_detailed_error("Unable to read num missing packets."); + return false; + } + + for (int i = 0; i < num_missing_packets; ++i) { + QuicPacketSequenceNumber sequence_number; + if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + &sequence_number)) { + set_detailed_error("Unable to read sequence number in missing packets."); + return false; + } + received_info->missing_packets.insert(sequence_number); + } + + return true; +} + +bool QuicFramer::ProcessSentInfo(SentPacketInfo* sent_info) { + if (!reader_->ReadBytes(&sent_info->entropy_hash, 1)) { + set_detailed_error("Unable to read entropy hash for sent packets."); + return false; + } + + if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + &sent_info->least_unacked)) { + set_detailed_error("Unable to read least unacked."); + return false; + } + + return true; +} + +bool QuicFramer::ProcessQuicCongestionFeedbackFrame( + 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 kInterArrival: { + CongestionFeedbackMessageInterArrival* inter_arrival = + &frame->inter_arrival; + if (!reader_->ReadUInt16( + &inter_arrival->accumulated_number_of_lost_packets)) { + set_detailed_error( + "Unable to read accumulated number of lost packets."); + return false; + } + 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 > 0u) { + uint64 smallest_received; + if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + &smallest_received)) { + set_detailed_error("Unable to read smallest received."); + return false; + } + + uint64 time_received_us; + if (!reader_->ReadUInt64(&time_received_us)) { + set_detailed_error("Unable to read time received."); + return false; + } + QuicTime time_received = creation_time_.Add( + QuicTime::Delta::FromMicroseconds(time_received_us)); + + inter_arrival->received_packet_times.insert( + make_pair(smallest_received, time_received)); + + for (int i = 0; i < num_received_packets - 1; ++i) { + uint16 sequence_delta; + if (!reader_->ReadUInt16(&sequence_delta)) { + set_detailed_error( + "Unable to read sequence delta in received packets."); + return false; + } + + int32 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; + } + QuicPacketSequenceNumber packet = smallest_received + sequence_delta; + inter_arrival->received_packet_times.insert( + make_pair(packet, time_received.Add( + QuicTime::Delta::FromMicroseconds(time_delta_us)))); + } + } + break; + } + case kFixRate: { + uint32 bitrate = 0; + if (!reader_->ReadUInt32(&bitrate)) { + set_detailed_error("Unable to read bitrate."); + return false; + } + frame->fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(bitrate); + break; + } + case kTCP: { + CongestionFeedbackMessageTCP* tcp = &frame->tcp; + if (!reader_->ReadUInt16(&tcp->accumulated_number_of_lost_packets)) { + set_detailed_error( + "Unable to read accumulated number of lost packets."); + return false; + } + 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; + } + + 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(); + + if (!ProcessAckFrame(&frame->ack_frame)) { + DLOG(WARNING) << "Unable to process ack frame."; + return false; + } + + 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; +} + +// static +StringPiece QuicFramer::GetAssociatedDataFromEncryptedPacket( + const QuicEncryptedPacket& encrypted, + QuicGuidLength guid_length, + bool includes_version, + QuicSequenceNumberLength sequence_number_length) { + return StringPiece(encrypted.data() + kStartOfHashData, + GetStartOfEncryptedData( + guid_length, includes_version, sequence_number_length) + - kStartOfHashData); +} + +void QuicFramer::SetDecrypter(QuicDecrypter* decrypter) { + DCHECK(alternative_decrypter_.get() == NULL); + decrypter_.reset(decrypter); +} + +void QuicFramer::SetAlternativeDecrypter(QuicDecrypter* decrypter, + bool latch_once_used) { + alternative_decrypter_.reset(decrypter); + 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() != NULL); + return encrypter_[level].get(); +} + +void QuicFramer::SwapCryptersForTest(QuicFramer* other) { + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { + encrypter_[i].swap(other->encrypter_[i]); + } + decrypter_.swap(other->decrypter_); + alternative_decrypter_.swap(other->alternative_decrypter_); + + const bool other_latch = other->alternative_decrypter_latch_; + other->alternative_decrypter_latch_ = alternative_decrypter_latch_; + alternative_decrypter_latch_ = other_latch; +} + +QuicEncryptedPacket* QuicFramer::EncryptPacket( + EncryptionLevel level, + QuicPacketSequenceNumber packet_sequence_number, + const QuicPacket& packet) { + DCHECK(encrypter_[level].get() != NULL); + + scoped_ptr<QuicData> out(encrypter_[level]->EncryptPacket( + packet_sequence_number, packet.AssociatedData(), packet.Plaintext())); + if (out.get() == NULL) { + RaiseError(QUIC_ENCRYPTION_FAILURE); + return NULL; + } + 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. At the moment, the NullEncrypter has a tag length of 16 + // bytes and AES-GCM has a tag length of 12. We take the minimum plaintext + // length just to be safe. + size_t min_plaintext_size = ciphertext_size; + + for (int i = ENCRYPTION_NONE; i < NUM_ENCRYPTION_LEVELS; i++) { + if (encrypter_[i].get() != NULL) { + 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() != NULL); + decrypted_.reset(decrypter_->DecryptPacket( + header.packet_sequence_number, + GetAssociatedDataFromEncryptedPacket( + packet, + header.public_header.guid_length, + header.public_header.version_flag, + header.public_header.sequence_number_length), + encrypted)); + if (decrypted_.get() == NULL && alternative_decrypter_.get() != NULL) { + decrypted_.reset(alternative_decrypter_->DecryptPacket( + header.packet_sequence_number, + GetAssociatedDataFromEncryptedPacket( + packet, + header.public_header.guid_length, + header.public_header.version_flag, + header.public_header.sequence_number_length), + encrypted)); + if (decrypted_.get() != NULL) { + if (alternative_decrypter_latch_) { + // Switch to the alternative decrypter and latch so that we cannot + // switch back. + decrypter_.reset(alternative_decrypter_.release()); + } else { + // Switch the alternative decrypter so that we use it first next time. + decrypter_.swap(alternative_decrypter_); + } + } + } + + if (decrypted_.get() == NULL) { + return false; + } + + reader_.reset(new QuicDataReader(decrypted_->data(), decrypted_->length())); + return true; +} + +size_t QuicFramer::ComputeFrameLength(const QuicFrame& frame, + bool last_frame_in_packet) { + switch (frame.type) { + case STREAM_FRAME: + return GetMinStreamFrameSize(quic_version_, + frame.stream_frame->stream_id, + frame.stream_frame->offset, + last_frame_in_packet) + + frame.stream_frame->data.size(); + case ACK_FRAME: { + const QuicAckFrame& ack = *frame.ack_frame; + return GetMinAckFrameSize() + PACKET_6BYTE_SEQUENCE_NUMBER * + ack.received_info.missing_packets.size(); + } + 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 kInterArrival: { + const CongestionFeedbackMessageInterArrival& inter_arrival = + congestion_feedback.inter_arrival; + len += 2; + len += 1; // Number received packets. + if (inter_arrival.received_packet_times.size() > 0) { + len += PACKET_6BYTE_SEQUENCE_NUMBER; // Smallest received. + len += 8; // Time. + // 2 bytes per sequence number delta plus 4 bytes per delta time. + len += PACKET_6BYTE_SEQUENCE_NUMBER * + (inter_arrival.received_packet_times.size() - 1); + } + break; + } + case kFixRate: + len += 4; + break; + case kTCP: + len += 4; + break; + default: + set_detailed_error("Illegal feedback type."); + DLOG(INFO) << "Illegal feedback type: " << congestion_feedback.type; + break; + } + return len; + } + case RST_STREAM_FRAME: + return GetMinRstStreamFrameSize() + + frame.rst_stream_frame->error_details.size(); + case CONNECTION_CLOSE_FRAME: { + const QuicAckFrame& ack = frame.connection_close_frame->ack_frame; + return GetMinConnectionCloseFrameSize() + + frame.connection_close_frame->error_details.size() + + PACKET_6BYTE_SEQUENCE_NUMBER * + ack.received_info.missing_packets.size(); + } + case GOAWAY_FRAME: + return GetMinGoAwayFrameSize() + frame.goaway_frame->reason_phrase.size(); + 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 last_frame_in_packet, + QuicDataWriter* writer) { + uint8 type_byte = 0; + switch (frame.type) { + case STREAM_FRAME: { + if (frame.stream_frame == NULL) { + 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 |= last_frame_in_packet ? 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 <<= 1; // Leaves the last bit as a 0. + break; + } + case ACK_FRAME: { + // TODO(ianswett): Use extra 5 bits in the ack framing. + type_byte = 0x01; + break; + } + case CONGESTION_FEEDBACK_FRAME: { + // TODO(ianswett): Use extra 5 bits in the congestion feedback framing. + type_byte = 0x03; + break; + } + default: + type_byte = + frame.type << kQuicDefaultFrameTypeShift | kQuicDefaultFrameTypeMask; + 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: + NOTREACHED() << "sequence_number_length: " << sequence_number_length; + return false; + } +} + +bool QuicFramer::AppendStreamFramePayload( + const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer) { + if (!writer->WriteBytes(&frame.stream_id, GetStreamIdSize(frame.stream_id))) { + return false; + } + if (!writer->WriteBytes(&frame.offset, GetStreamOffsetSize(frame.offset))) { + return false; + } + if (!last_frame_in_packet) { + if (!writer->WriteUInt16(frame.data.size())) { + return false; + } + } + if (!writer->WriteBytes(frame.data.data(), frame.data.size())) { + return false; + } + return true; +} + +QuicPacketSequenceNumber QuicFramer::CalculateLargestObserved( + const SequenceNumberSet& missing_packets, + SequenceNumberSet::const_iterator largest_written) { + SequenceNumberSet::const_iterator it = largest_written; + QuicPacketSequenceNumber previous_missing = *it; + ++it; + + // See if the next thing is a gap in the missing packets: if it's a + // non-missing packet we can return it. + if (it != missing_packets.end() && previous_missing + 1 != *it) { + return *it - 1; + } + + // Otherwise return the largest missing packet, as indirectly observed. + return *largest_written; +} + +// TODO(ianswett): Use varints or another more compact approach for all deltas. +bool QuicFramer::AppendAckFramePayload( + const QuicAckFrame& frame, + QuicDataWriter* writer) { + // TODO(satyamshekhar): Decide how often we really should send this + // entropy_hash update. + if (!writer->WriteUInt8(frame.sent_info.entropy_hash)) { + return false; + } + + if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + frame.sent_info.least_unacked, writer)) { + return false; + } + + size_t received_entropy_offset = writer->length(); + if (!writer->WriteUInt8(frame.received_info.entropy_hash)) { + return false; + } + + size_t largest_observed_offset = writer->length(); + if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + frame.received_info.largest_observed, + writer)) { + return false; + } + uint32 delta_time_largest_observed_us = kInvalidDeltaTime; + if (!frame.received_info.delta_time_largest_observed.IsInfinite()) { + delta_time_largest_observed_us = + frame.received_info.delta_time_largest_observed.ToMicroseconds(); + } + + size_t delta_time_largest_observed_offset = writer->length(); + if (!writer->WriteUInt32(delta_time_largest_observed_us)) { + return false; + } + + // We don't check for overflowing uint8 here, because we only can fit 192 acks + // per packet, so if we overflow we will be truncated. + uint8 num_missing_packets = frame.received_info.missing_packets.size(); + size_t num_missing_packets_offset = writer->length(); + if (!writer->WriteBytes(&num_missing_packets, 1)) { + return false; + } + + SequenceNumberSet::const_iterator it = + frame.received_info.missing_packets.begin(); + int num_missing_packets_written = 0; + for (; it != frame.received_info.missing_packets.end(); ++it) { + if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + *it, writer)) { + // We are truncating. + QuicPacketSequenceNumber largest_observed = + CalculateLargestObserved(frame.received_info.missing_packets, --it); + // Overwrite entropy hash for received packets. + writer->WriteUInt8ToOffset( + entropy_calculator_->EntropyHash(largest_observed), + received_entropy_offset); + // Overwrite largest_observed. + writer->WriteUInt48ToOffset(largest_observed & k6ByteSequenceNumberMask, + largest_observed_offset); + writer->WriteUInt32ToOffset(kInvalidDeltaTime, + delta_time_largest_observed_offset); + writer->WriteUInt8ToOffset(num_missing_packets_written, + num_missing_packets_offset); + return true; + } + ++num_missing_packets_written; + DCHECK_GE(numeric_limits<uint8>::max(), num_missing_packets_written); + } + + return true; +} + +bool QuicFramer::AppendQuicCongestionFeedbackFramePayload( + const QuicCongestionFeedbackFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteBytes(&frame.type, 1)) { + return false; + } + + switch (frame.type) { + case kInterArrival: { + const CongestionFeedbackMessageInterArrival& inter_arrival = + frame.inter_arrival; + if (!writer->WriteUInt16( + inter_arrival.accumulated_number_of_lost_packets)) { + return false; + } + DCHECK_GE(numeric_limits<uint8>::max(), + inter_arrival.received_packet_times.size()); + if (inter_arrival.received_packet_times.size() > + numeric_limits<uint8>::max()) { + return false; + } + // TODO(ianswett): Make num_received_packets a varint. + uint8 num_received_packets = + inter_arrival.received_packet_times.size(); + if (!writer->WriteBytes(&num_received_packets, 1)) { + return false; + } + if (num_received_packets > 0) { + TimeMap::const_iterator it = + inter_arrival.received_packet_times.begin(); + + QuicPacketSequenceNumber lowest_sequence = it->first; + if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, + lowest_sequence, writer)) { + return false; + } + + QuicTime lowest_time = it->second; + if (!writer->WriteUInt64( + lowest_time.Subtract(creation_time_).ToMicroseconds())) { + return false; + } + + for (++it; it != inter_arrival.received_packet_times.end(); ++it) { + QuicPacketSequenceNumber sequence_delta = it->first - lowest_sequence; + DCHECK_GE(numeric_limits<uint16>::max(), sequence_delta); + if (sequence_delta > numeric_limits<uint16>::max()) { + return false; + } + if (!writer->WriteUInt16(static_cast<uint16>(sequence_delta))) { + return false; + } + + int32 time_delta_us = + it->second.Subtract(lowest_time).ToMicroseconds(); + if (!writer->WriteBytes(&time_delta_us, sizeof(time_delta_us))) { + return false; + } + } + } + break; + } + case kFixRate: { + const CongestionFeedbackMessageFixRate& fix_rate = + frame.fix_rate; + if (!writer->WriteUInt32(fix_rate.bitrate.ToBytesPerSecond())) { + return false; + } + break; + } + 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(tcp.accumulated_number_of_lost_packets)) { + return false; + } + if (!writer->WriteUInt16(receive_window)) { + return false; + } + break; + } + default: + return false; + } + + return true; +} + +bool QuicFramer::AppendRstStreamFramePayload( + const QuicRstStreamFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteUInt32(frame.stream_id)) { + 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::AppendConnectionCloseFramePayload( + 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; + } + AppendAckFramePayload(frame.ack_frame, writer); + return true; +} + +bool QuicFramer::AppendGoAwayFramePayload(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::RaiseError(QuicErrorCode error) { + DLOG(INFO) << detailed_error_; + set_error(error); + visitor_->OnError(this); + reader_.reset(NULL); + return false; +} + +} // namespace net diff --git a/chromium/net/quic/quic_framer.h b/chromium/net/quic/quic_framer.h new file mode 100644 index 00000000000..841172698df --- /dev/null +++ b/chromium/net/quic/quic_framer.h @@ -0,0 +1,455 @@ +// 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 = 4; +// Size in bytes reserved for the number of missing packets in ack frames. +const size_t kNumberOfMissingPacketsSize = 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 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 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 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. + QuicFramer(QuicVersion quic_version, + QuicTime creation_time, + bool is_server); + + virtual ~QuicFramer(); + + // Returns true if |version| is a supported protocol version. + bool IsSupportedVersion(const QuicVersion version) const; + + // Calculates the largest observed packet to advertise in the case an Ack + // Frame was truncated. last_written in this case is the iterator for the + // last missing packet which fit in the outgoing ack. + static QuicPacketSequenceNumber CalculateLargestObserved( + const SequenceNumberSet& missing_packets, + SequenceNumberSet::const_iterator last_written); + + // 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; + } + + QuicVersion version() const { + return quic_version_; + } + + void set_version(const QuicVersion version) { + DCHECK(IsSupportedVersion(version)); + quic_version_ = 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(QuicVersion version, + QuicStreamId stream_id, + QuicStreamOffset offset, + bool last_frame_in_packet); + // Size in bytes of all ack frame fields without the missing packets. + static size_t GetMinAckFrameSize(); + // 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(); + // The maximum number of nacks which can be transmitted in a single ack packet + // without exceeding kMaxPacketSize. + static size_t GetMaxUnackedPackets(QuicPacketHeader header); + // 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); + + + static bool CanTruncate(const QuicFrame& frame, size_t free_bytes); + + // 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); + + // Returns the associated data from the encrypted packet |encrypted| as a + // stringpiece. + static base::StringPiece GetAssociatedDataFromEncryptedPacket( + const QuicEncryptedPacket& encrypted, + QuicGuidLength guid_length, + bool includes_version, + QuicSequenceNumberLength sequence_number_length); + + // Returns a SerializedPacket whose |packet| member is owned by the caller, + // and is populated with the fields in |header| and |frames|, or is NULL if + // the packet could not be created. + // TODO(ianswett): Used for testing only. + SerializedPacket BuildUnsizedDataPacket(const QuicPacketHeader& header, + const QuicFrames& frames); + + // Returns a SerializedPacket whose |packet| member is owned by the caller, + // is created from the first |num_frames| frames, or is NULL 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 NULL 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 not + // obsolete. + void SetDecrypter(QuicDecrypter* decrypter); + + // SetAlternativeDecrypter sets a decrypter that may be used to decrypt + // future packets and takes ownership of it. 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, + 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; + + // SwapCryptersForTest exchanges the state of the crypters with |other|. To + // be used in tests only. + void SwapCryptersForTest(QuicFramer* other); + + // 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_; } + + // Read the full 8 byte guid from a packet header. + // Return true on success, else false. + static bool ReadGuidFromPacket(const QuicEncryptedPacket& packet, + QuicGuid* guid); + + private: + friend class test::QuicFramerPeer; + + 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 WritePacketHeader(const QuicPacketHeader& header, + QuicDataWriter* writer); + + bool ProcessPublicHeader(QuicPacketPublicHeader* header); + + bool ProcessPacketHeader(QuicPacketHeader* header, + const QuicEncryptedPacket& packet); + + bool ProcessPacketSequenceNumber( + QuicSequenceNumberLength sequence_number_length, + QuicPacketSequenceNumber* sequence_number); + bool ProcessFrameData(); + bool ProcessStreamFrame(uint8 frame_type, QuicStreamFrame* frame); + bool ProcessAckFrame(QuicAckFrame* frame); + bool ProcessReceivedInfo(ReceivedPacketInfo* received_info); + bool ProcessSentInfo(SentPacketInfo* sent_info); + bool ProcessQuicCongestionFeedbackFrame( + QuicCongestionFeedbackFrame* congestion_feedback); + bool ProcessRstStreamFrame(QuicRstStreamFrame* frame); + bool ProcessConnectionCloseFrame(QuicConnectionCloseFrame* frame); + bool ProcessGoAwayFrame(QuicGoAwayFrame* 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; + + // Computes the wire size in bytes of the payload of |frame|. + size_t ComputeFrameLength(const QuicFrame& frame, bool last_frame_in_packet); + + static bool AppendPacketSequenceNumber( + QuicSequenceNumberLength sequence_number_length, + QuicPacketSequenceNumber packet_sequence_number, + QuicDataWriter* writer); + + bool AppendTypeByte(const QuicFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* writer); + bool AppendStreamFramePayload(const QuicStreamFrame& frame, + bool last_frame_in_packet, + QuicDataWriter* builder); + bool AppendAckFramePayload(const QuicAckFrame& frame, + QuicDataWriter* builder); + bool AppendQuicCongestionFeedbackFramePayload( + const QuicCongestionFeedbackFrame& frame, + QuicDataWriter* builder); + bool AppendRstStreamFramePayload(const QuicRstStreamFrame& frame, + QuicDataWriter* builder); + bool AppendConnectionCloseFramePayload( + const QuicConnectionCloseFrame& frame, + QuicDataWriter* builder); + bool AppendGoAwayFramePayload(const QuicGoAwayFrame& 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. + QuicGuid last_serialized_guid_; + // Buffer containing decrypted payload data during parsing. + scoped_ptr<QuicData> decrypted_; + // Version of the protocol being used. + QuicVersion quic_version_; + // 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_; + // 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_; + // The time this frames was created. Time written to the wire will be + // written as a delta from this value. + QuicTime creation_time_; + + DISALLOW_COPY_AND_ASSIGN(QuicFramer); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_FRAMER_H_ diff --git a/chromium/net/quic/quic_framer_test.cc b/chromium/net/quic/quic_framer_test.cc new file mode 100644 index 00000000000..e4148fea31d --- /dev/null +++ b/chromium/net/quic/quic_framer_test.cc @@ -0,0 +1,3639 @@ +// 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 <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_framer.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" + +using base::hash_set; +using base::StringPiece; +using std::make_pair; +using std::map; +using std::numeric_limits; +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 flags offset in the header. +const size_t kPublicFlagsOffset = 0; +// Index into the guid offset in the header. +const size_t kGuidOffset = kPublicFlagsSize; +// Index into the version string in the header. (if present). +const size_t kVersionOffset = kGuidOffset + PACKET_8BYTE_GUID; + +// Index into the sequence number offset in the header. +size_t GetSequenceNumberOffset(QuicGuidLength guid_length, + bool include_version) { + return kGuidOffset + guid_length + + (include_version ? kQuicVersionSize : 0); +} + +size_t GetSequenceNumberOffset(bool include_version) { + return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version); +} + +// Index into the private flags offset in the data packet header. +size_t GetPrivateFlagsOffset(QuicGuidLength guid_length, bool include_version) { + return GetSequenceNumberOffset(guid_length, include_version) + + PACKET_6BYTE_SEQUENCE_NUMBER; +} + +size_t GetPrivateFlagsOffset(bool include_version) { + return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version); +} + +size_t GetPrivateFlagsOffset(bool include_version, + QuicSequenceNumberLength sequence_number_length) { + return GetSequenceNumberOffset(PACKET_8BYTE_GUID, include_version) + + sequence_number_length; +} + +// Index into the fec group offset in the header. +size_t GetFecGroupOffset(QuicGuidLength guid_length, bool include_version) { + return GetPrivateFlagsOffset(guid_length, include_version) + + kPrivateFlagsSize; +} + +size_t GetFecGroupOffset(bool include_version) { + return GetPrivateFlagsOffset(PACKET_8BYTE_GUID, include_version) + + kPrivateFlagsSize; +} + +size_t GetFecGroupOffset(bool include_version, + QuicSequenceNumberLength sequence_number_length) { + return GetPrivateFlagsOffset(include_version, sequence_number_length) + + kPrivateFlagsSize; +} + +// Index into the nonce proof of the public reset packet. +// Public resets always have full guids. +const size_t kPublicResetPacketNonceProofOffset = + kGuidOffset + PACKET_8BYTE_GUID; + +// Index into the rejected sequence number of the public reset packet. +const size_t kPublicResetPacketRejectedSequenceNumberOffset = + kPublicResetPacketNonceProofOffset + kPublicResetNonceSize; + +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) { + } + + virtual ~TestQuicVisitor() { + STLDeleteElements(&stream_frames_); + STLDeleteElements(&ack_frames_); + STLDeleteElements(&congestion_feedback_frames_); + STLDeleteElements(&fec_data_); + } + + virtual void OnError(QuicFramer* f) OVERRIDE { + DLOG(INFO) << "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 { + DLOG(INFO) << "QuicFramer Version Mismatch, version: " << version; + version_mismatch_++; + return true; + } + + 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 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; + } + + // 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_; + + scoped_ptr<QuicPacketHeader> 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<QuicFecData*> fec_data_; + string fec_protected_payload_; + QuicRstStreamFrame rst_stream_frame_; + QuicConnectionCloseFrame connection_close_frame_; + QuicGoAwayFrame goaway_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_(QuicVersionMax(), start_, true) { + framer_.SetDecrypter(decrypter_); + framer_.SetEncrypter(ENCRYPTION_NONE, encrypter_); + framer_.set_visitor(&visitor_); + framer_.set_received_entropy_calculator(&entropy_calculator_); + + version_ = GetParam(); + framer_.set_version(version_); + } + + 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_GUID, + includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) != + decrypter_->associated_data_) { + LOG(ERROR) << "Decrypted incorrect associated data. expected " + << QuicFramer::GetAssociatedDataFromEncryptedPacket( + encrypted, PACKET_8BYTE_GUID, + includes_version, PACKET_6BYTE_SEQUENCE_NUMBER) + << " actual: " << decrypter_->associated_data_; + return false; + } + StringPiece ciphertext(encrypted.AsStringPiece().substr( + GetStartOfEncryptedData(PACKET_8BYTE_GUID, 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; + } + + void ValidateTruncatedAck(const QuicAckFrame* ack, size_t keys) { + for (size_t i = 1; i < keys; ++i) { + EXPECT_TRUE(ContainsKey(ack->received_info.missing_packets, i)) << i; + } + EXPECT_EQ(keys, ack->received_info.largest_observed); + } + + 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; + } + + 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 guid) + 0x3C, + // guid + 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), 0, + kMaxPacketSize - GetPacketHeaderSize( + PACKET_8BYTE_GUID, !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.guid); + // 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 guid) + 0x3C, + // guid + 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } 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, PacketHeaderWith4ByteGuid) { + QuicFramerPeer::SetLastSerializedGuid(&framer_, + GG_UINT64_C(0xFEDCBA9876543210)); + + unsigned char packet[] = { + // public flags (4 byte guid) + 0x38, + // guid + 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(PACKET_4BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } else if (i < GetPrivateFlagsOffset(PACKET_4BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read sequence number."; + } else if (i < GetFecGroupOffset(PACKET_4BYTE_GUID, !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, PacketHeader1ByteGuid) { + QuicFramerPeer::SetLastSerializedGuid(&framer_, + GG_UINT64_C(0xFEDCBA9876543210)); + + unsigned char packet[] = { + // public flags (1 byte guid) + 0x34, + // guid + 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(PACKET_1BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } else if (i < GetPrivateFlagsOffset(PACKET_1BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read sequence number."; + } else if (i < GetFecGroupOffset(PACKET_1BYTE_GUID, !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, PacketHeaderWith0ByteGuid) { + QuicFramerPeer::SetLastSerializedGuid(&framer_, + GG_UINT64_C(0xFEDCBA9876543210)); + + unsigned char packet[] = { + // public flags (0 byte guid) + 0x30, + // guid + // 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(PACKET_0BYTE_GUID, + !kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } else if (i < GetPrivateFlagsOffset(PACKET_0BYTE_GUID, !kIncludeVersion)) { + expected_error = "Unable to read sequence number."; + } else if (i < GetFecGroupOffset(PACKET_0BYTE_GUID, !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) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (version) + 0x3D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + // 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + EXPECT_FALSE(visitor_.header_->public_header.reset_flag); + EXPECT_TRUE(visitor_.header_->public_header.version_flag); + EXPECT_EQ(QUIC_VERSION_7, 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_GUID, kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < kVersionOffset) { + expected_error = "Unable to read GUID."; + } 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 guid and 4 byte sequence number) + 0x2C, + // guid + 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } 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 guid and 2 byte sequence number) + 0x1C, + // guid + 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } 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 guid and 1 byte sequence number) + 0x0C, + // guid + 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_INVALID_FRAME_DATA, framer_.error()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), + visitor_.header_->public_header.guid); + 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_GUID, !kIncludeVersion, + PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + ++i) { + string expected_error; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < GetSequenceNumberOffset(!kIncludeVersion)) { + expected_error = "Unable to read GUID."; + } 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, unknown flag at bit 6 + 0x40, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame count + 0x01, + // frame type (stream frame) + 0x01, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 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', '!', + }; + CheckProcessingFails(packet, + arraysize(packet), + "Illegal public flags value.", + QUIC_INVALID_PACKET_HEADER); +}; + +TEST_P(QuicFramerTest, InvalidPublicFlagWithMatchingVersions) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid and version flag and an unknown flag) + 0x4D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame count + 0x01, + // frame type (stream frame) + 0x01, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 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', '!', + }; + CheckProcessingFails(packet, + arraysize(packet), + "Illegal public flags value.", + QUIC_INVALID_PACKET_HEADER); +}; + +TEST_P(QuicFramerTest, LargePublicFlagWithMismatchedVersions) { + unsigned char packet[] = { + // public flags (8 byte guid, version flag and an unknown flag) + 0x7D, + // guid + 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) + 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_EQ(0, visitor_.frame_count_); + EXPECT_EQ(1, visitor_.version_mismatch_); +}; + +TEST_P(QuicFramerTest, InvalidPrivateFlag) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x10, + + // frame count + 0x01, + // frame type (stream frame) + 0x01, + // stream id + 0x04, 0x03, 0x02, 0x01, + // fin + 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', '!', + }; + CheckProcessingFails(packet, + arraysize(packet), + "Illegal private flags value.", + QUIC_INVALID_PACKET_HEADER); +}; + +TEST_P(QuicFramerTest, InvalidFECGroupOffset) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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 guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (padding frame) + 0x07, + // Ignored data (which in this case is a stream frame) + 0x01, + 0x04, 0x03, 0x02, 0x01, + 0x01, + 0x54, 0x76, 0x10, 0x32, + 0xDC, 0xFE, 0x98, 0xBA, + 0x0c, 0x00, + '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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + "Unable to read frame type.", QUIC_INVALID_FRAME_DATA); +} + +TEST_P(QuicFramerTest, StreamFrame) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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, 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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read fin."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrame3ByteStreamId) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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, 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(static_cast<uint64>(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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + const size_t stream_id_size = 3; + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size - 1) { + expected_error = "Unable to read fin."; + } 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrame2ByteStreamId) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xFA, + // 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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + const size_t stream_id_size = 2; + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size - 1) { + expected_error = "Unable to read fin."; + } 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrame1ByteStreamId) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xF8, + // 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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + const size_t stream_id_size = 1; + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + stream_id_size) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + stream_id_size - 1) { + expected_error = "Unable to read fin."; + } 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, StreamFrameWithVersion) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (version, 8 byte guid) + 0x3D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (stream frame with fin) + 0xFE, + // 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_.get()->public_header.version_flag); + EXPECT_EQ(QUIC_VERSION_7, visitor_.header_.get()->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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); + + // Now test framing boundaries + for (size_t i = 0; i < GetMinStreamFrameSize(framer_.version()); ++i) { + string expected_error; + if (i < kQuicFrameTypeSize) { + expected_error = "Unable to read frame type."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + kQuicMaxStreamOffsetSize) { + expected_error = "Unable to read offset."; + } else { + expected_error = "Unable to read frame data."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, RejectPacket) { + visitor_.accept_packet_ = false; + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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, 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, RevivedStreamFrame) { + unsigned char payload[] = { + // frame type (stream frame with fin) + 0xFE, + // 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.guid = 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.guid); + 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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); +} + +TEST_P(QuicFramerTest, StreamFrameInFecGroup) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xFE, + // 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_GUID, !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); + EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); +} + +TEST_P(QuicFramerTest, AckFrame) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (ack frame) + static_cast<unsigned char>(0x01), + // entropy hash of sent packets till least awaiting - 1. + 0xAB, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // entropy hash of all received packets. + 0xBA, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Infinite delta time. + 0xFF, 0xFF, 0xFF, 0xFF, + // num missing packets + 0x01, + // missing packet + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + 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(0xAB, frame.sent_info.entropy_hash); + EXPECT_EQ(0xBA, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); + + const size_t kSentEntropyOffset = kQuicFrameTypeSize; + const size_t kLeastUnackedOffset = kSentEntropyOffset + kQuicEntropyHashSize; + const size_t kReceivedEntropyOffset = kLeastUnackedOffset + + PACKET_6BYTE_SEQUENCE_NUMBER; + 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 + + kNumberOfMissingPacketsSize; + // Now test framing boundaries + const size_t missing_packets_size = 1 * PACKET_6BYTE_SEQUENCE_NUMBER; + for (size_t i = 0; + i < QuicFramer::GetMinAckFrameSize() + missing_packets_size; ++i) { + string expected_error; + if (i < kSentEntropyOffset) { + expected_error = "Unable to read frame type."; + } else if (i < kLeastUnackedOffset) { + expected_error = "Unable to read entropy hash for sent packets."; + } else if (i < kReceivedEntropyOffset) { + expected_error = "Unable to read least unacked."; + } 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 packets."; + } else { + expected_error = "Unable to read sequence number in missing packets."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // congestion feedback type (tcp) + 0x00, + // ack_frame.feedback.tcp.accumulated_number_of_lost_packets + 0x01, 0x02, + // 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(0x0201, + frame.tcp.accumulated_number_of_lost_packets); + EXPECT_EQ(0x4030u, frame.tcp.receive_window); + + // Now test framing boundaries + for (size_t i = 0; i < 6; ++i) { + string expected_error; + if (i < 1) { + expected_error = "Unable to read frame type."; + } else if (i < 2) { + expected_error = "Unable to read congestion feedback type."; + } else if (i < 4) { + expected_error = "Unable to read accumulated number of lost packets."; + } else if (i < 6) { + expected_error = "Unable to read receive window."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // congestion feedback type (inter arrival) + 0x01, + // accumulated_number_of_lost_packets + 0x02, 0x03, + // num received packets + 0x03, + // lowest sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // receive time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0x07, + // sequence delta + 0x01, 0x00, + // time delta + 0x01, 0x00, 0x00, 0x00, + // sequence delta (skip one packet) + 0x03, 0x00, + // time delta + 0x02, 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_.congestion_feedback_frames_.size()); + const QuicCongestionFeedbackFrame& frame = + *visitor_.congestion_feedback_frames_[0]; + ASSERT_EQ(kInterArrival, frame.type); + EXPECT_EQ(0x0302, frame.inter_arrival. + accumulated_number_of_lost_packets); + ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size()); + TimeMap::const_iterator iter = + frame.inter_arrival.received_packet_times.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABA), iter->first); + EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59687), + iter->second.Subtract(start_).ToMicroseconds()); + ++iter; + EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), iter->first); + EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59688), + iter->second.Subtract(start_).ToMicroseconds()); + ++iter; + EXPECT_EQ(GG_UINT64_C(0x0123456789ABD), iter->first); + EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59689), + iter->second.Subtract(start_).ToMicroseconds()); + + // Now test framing boundaries + for (size_t i = 0; i < 31; ++i) { + string expected_error; + if (i < 1) { + expected_error = "Unable to read frame type."; + } else if (i < 2) { + expected_error = "Unable to read congestion feedback type."; + } else if (i < 4) { + expected_error = "Unable to read accumulated number of lost packets."; + } else if (i < 5) { + expected_error = "Unable to read num received packets."; + } else if (i < 11) { + expected_error = "Unable to read smallest received."; + } else if (i < 19) { + expected_error = "Unable to read time received."; + } else if (i < 21) { + expected_error = "Unable to read sequence delta in received packets."; + } else if (i < 25) { + expected_error = "Unable to read time delta in received packets."; + } else if (i < 27) { + expected_error = "Unable to read sequence delta in received packets."; + } else if (i < 31) { + expected_error = "Unable to read time delta in received packets."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + +TEST_P(QuicFramerTest, CongestionFeedbackFrameFixRate) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // congestion feedback type (fix rate) + 0x02, + // bitrate_in_bytes_per_second; + 0x01, 0x02, 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(kFixRate, frame.type); + EXPECT_EQ(static_cast<uint32>(0x04030201), + frame.fix_rate.bitrate.ToBytesPerSecond()); + + // Now test framing boundaries + for (size_t i = 0; i < 6; ++i) { + string expected_error; + if (i < 1) { + expected_error = "Unable to read frame type."; + } else if (i < 2) { + expected_error = "Unable to read congestion feedback type."; + } else if (i < 6) { + expected_error = "Unable to read bitrate."; + } + CheckProcessingFails( + packet, + i + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_FRAME_DATA); + } +} + + +TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // 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_FRAME_DATA, framer_.error()); +} + +TEST_P(QuicFramerTest, RstStreamFrame) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x27), + // stream id + 0x04, 0x03, 0x02, 0x01, + // 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); + + // Now test framing boundaries + for (size_t i = 2; i < 24; ++i) { + string expected_error; + if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize) { + expected_error = "Unable to read stream_id."; + } else if (i < kQuicFrameTypeSize + kQuicMaxStreamIdSize + + 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_GUID, !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 guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x2F), + // error code + 0x11, 0x00, 0x00, 0x00, + + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + + // Ack frame. + // entropy hash of sent packets till least awaiting - 1. + 0xBF, + // least packet sequence number awaiting an ack + 0xA0, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // entropy hash of all received packets. + 0xEB, + // largest observed packet sequence number + 0xBF, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // Infinite delta time. + 0xFF, 0xFF, 0xFF, 0xFF, + // num missing packets + 0x01, + // missing packet + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + 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(1u, visitor_.ack_frames_.size()); + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(0xBF, frame.sent_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789AA0), frame.sent_info.least_unacked); + EXPECT_EQ(0xEB, frame.received_info.entropy_hash); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABF), frame.received_info.largest_observed); + ASSERT_EQ(1u, frame.received_info.missing_packets.size()); + SequenceNumberSet::const_iterator missing_iter = + frame.received_info.missing_packets.begin(); + EXPECT_EQ(GG_UINT64_C(0x0123456789ABE), *missing_iter); + + // Now test framing boundaries + for (size_t i = kQuicFrameTypeSize; + i < QuicFramer::GetMinConnectionCloseFrameSize() - + QuicFramer::GetMinAckFrameSize(); ++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_GUID, !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 guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x37), + // 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), + expected_error, QUIC_INVALID_GOAWAY_DATA); + } +} + +TEST_P(QuicFramerTest, PublicResetPacket) { + unsigned char packet[] = { + // public flags (public reset, 8 byte guid) + 0x3E, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + 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.guid); + 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); + + // Now test framing boundaries + for (size_t i = 0; i < GetPublicResetPacketSize(); ++i) { + string expected_error; + DLOG(INFO) << "iteration: " << i; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PACKET_HEADER); + } else if (i < kPublicResetPacketNonceProofOffset) { + expected_error = "Unable to read GUID."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PACKET_HEADER); + } else if (i < kPublicResetPacketRejectedSequenceNumberOffset) { + expected_error = "Unable to read nonce proof."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PUBLIC_RST_PACKET); + } else { + expected_error = "Unable to read rejected sequence number."; + CheckProcessingFails(packet, i, expected_error, + QUIC_INVALID_PUBLIC_RST_PACKET); + } + } +} + +TEST_P(QuicFramerTest, VersionNegotiationPacket) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (version, 8 byte guid) + 0x3D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + '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(QUIC_VERSION_7, + visitor_.version_negotiation_packet_->versions[0]); + + for (size_t i = 0; i <= kPublicFlagsSize + PACKET_8BYTE_GUID; ++i) { + string expected_error; + QuicErrorCode error_code = QUIC_INVALID_PACKET_HEADER; + if (i < kGuidOffset) { + expected_error = "Unable to read public flags."; + } else if (i < kVersionOffset) { + expected_error = "Unable to read GUID."; + } 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 guid) + 0x3C, + // guid + 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.guid = 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 guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags + 0x00, + + // frame type (padding frame) + static_cast<unsigned char>(0x07), + }; + + uint64 header_size = + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, Build4ByteSequenceNumberPaddingFramePacket) { + QuicPacketHeader header; + header.public_header.guid = 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 guid and 4 byte sequence number) + 0x2C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + // private flags + 0x00, + + // frame type (padding frame) + static_cast<unsigned char>(0x07), + }; + + uint64 header_size = + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_4BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, Build2ByteSequenceNumberPaddingFramePacket) { + QuicPacketHeader header; + header.public_header.guid = 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 guid and 2 byte sequence number) + 0x1C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, + // private flags + 0x00, + + // frame type (padding frame) + static_cast<unsigned char>(0x07), + }; + + uint64 header_size = + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_2BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, Build1ByteSequenceNumberPaddingFramePacket) { + QuicPacketHeader header; + header.public_header.guid = 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 guid and 1 byte sequence number) + 0x0C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, + // private flags + 0x00, + + // frame type (padding frame) + static_cast<unsigned char>(0x07), + }; + + uint64 header_size = + GetPacketHeaderSize(PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_1BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + memset(packet + header_size + 1, 0x00, kMaxPacketSize - header_size - 1); + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacket) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + QuicPacketHeader header; + header.public_header.guid = 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 = "hello world!"; + + QuicFrames frames; + frames.push_back(QuicFrame(&stream_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xBE, + // 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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildStreamFramePacketWithVersionFlag) { + QuicPacketHeader header; + header.public_header.guid = 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 = "hello world!"; + + QuicFrames frames; + frames.push_back(QuicFrame(&stream_frame)); + + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + unsigned char packet[] = { + // public flags (version, 8 byte guid) + 0x3D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (stream frame with fin and no length) + 0xBE, + // 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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildVersionNegotiationPacket) { + QuicPacketPublicHeader header; + header.guid = GG_UINT64_C(0xFEDCBA9876543210); + header.reset_flag = false; + header.version_flag = true; + + unsigned char packet[] = { + // public flags (version, 8 byte guid) + 0x3D, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // version tag + 'Q', '0', '0', '7', + }; + + QuicVersionVector versions; + versions.push_back(QUIC_VERSION_7); + scoped_ptr<QuicEncryptedPacket> data( + framer_.BuildVersionNegotiationPacket(header, versions)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildAckFramePacket) { + QuicPacketHeader header; + header.public_header.guid = 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.received_info.entropy_hash = 0x43; + ack_frame.received_info.largest_observed = GG_UINT64_C(0x770123456789ABF); + ack_frame.received_info.delta_time_largest_observed = QuicTime::Delta::Zero(); + ack_frame.received_info.missing_packets.insert( + GG_UINT64_C(0x770123456789ABE)); + ack_frame.sent_info.entropy_hash = 0x14; + ack_frame.sent_info.least_unacked = GG_UINT64_C(0x770123456789AA0); + + QuicFrames frames; + frames.push_back(QuicFrame(&ack_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // packet sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // private flags (entropy) + 0x01, + + // frame type (ack frame) + static_cast<unsigned char>(0x01), + // 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, + // Zero delta time. + 0x0, 0x0, 0x0, 0x0, + // num missing packets + 0x01, + // missing packet + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { + QuicPacketHeader header; + header.public_header.guid = 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.accumulated_number_of_lost_packets = 0x0201; + congestion_feedback_frame.tcp.receive_window = 0x4030; + + QuicFrames frames; + frames.push_back(QuicFrame(&congestion_feedback_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // congestion feedback type (TCP) + 0x00, + // accumulated number of lost packets + 0x01, 0x02, + // TCP receive window + 0x03, 0x04, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { + QuicPacketHeader header; + header.public_header.guid = 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 frame; + frame.type = kInterArrival; + frame.inter_arrival.accumulated_number_of_lost_packets = 0x0302; + frame.inter_arrival.received_packet_times.insert( + make_pair(GG_UINT64_C(0x0123456789ABA), + start_.Add(QuicTime::Delta::FromMicroseconds( + GG_UINT64_C(0x07E1D2C3B4A59687))))); + frame.inter_arrival.received_packet_times.insert( + make_pair(GG_UINT64_C(0x0123456789ABB), + start_.Add(QuicTime::Delta::FromMicroseconds( + GG_UINT64_C(0x07E1D2C3B4A59688))))); + frame.inter_arrival.received_packet_times.insert( + make_pair(GG_UINT64_C(0x0123456789ABD), + start_.Add(QuicTime::Delta::FromMicroseconds( + GG_UINT64_C(0x07E1D2C3B4A59689))))); + QuicFrames frames; + frames.push_back(QuicFrame(&frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // congestion feedback type (inter arrival) + 0x01, + // accumulated_number_of_lost_packets + 0x02, 0x03, + // num received packets + 0x03, + // lowest sequence number + 0xBA, 0x9A, 0x78, 0x56, + 0x34, 0x12, + // receive time + 0x87, 0x96, 0xA5, 0xB4, + 0xC3, 0xD2, 0xE1, 0x07, + // sequence delta + 0x01, 0x00, + // time delta + 0x01, 0x00, 0x00, 0x00, + // sequence delta (skip one packet) + 0x03, 0x00, + // time delta + 0x02, 0x00, 0x00, 0x00, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketFixRate) { + QuicPacketHeader header; + header.public_header.guid = 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 = kFixRate; + congestion_feedback_frame.fix_rate.bitrate + = QuicBandwidth::FromBytesPerSecond(0x04030201); + + QuicFrames frames; + frames.push_back(QuicFrame(&congestion_feedback_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0x03, + // congestion feedback type (fix rate) + 0x02, + // bitrate_in_bytes_per_second; + 0x01, 0x02, 0x03, 0x04, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) { + QuicPacketHeader header; + header.public_header.guid = 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>(kFixRate + 1); + + QuicFrames frames; + frames.push_back(QuicFrame(&congestion_feedback_frame)); + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data == NULL); +} + +TEST_P(QuicFramerTest, BuildRstFramePacket) { + QuicPacketHeader header; + header.public_header.guid = 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"; + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x27), + // stream id + 0x04, 0x03, 0x02, 0x01, + // 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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildCloseFramePacket) { + QuicPacketHeader header; + header.public_header.guid = 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"; + + QuicAckFrame* ack_frame = &close_frame.ack_frame; + ack_frame->received_info.entropy_hash = 0x43; + ack_frame->received_info.largest_observed = GG_UINT64_C(0x0123456789ABF); + ack_frame->received_info.missing_packets.insert(GG_UINT64_C(0x0123456789ABE)); + ack_frame->sent_info.entropy_hash = 0xE0; + ack_frame->sent_info.least_unacked = GG_UINT64_C(0x0123456789AA0); + + QuicFrames frames; + frames.push_back(QuicFrame(&close_frame)); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x2F), + // error code + 0x08, 0x07, 0x06, 0x05, + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + + // Ack frame. + // entropy hash of sent packets till least awaiting - 1. + 0xE0, + // 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, + // Infinite delta time. + 0xFF, 0xFF, 0xFF, 0xFF, + // num missing packets + 0x01, + // missing packet + 0xBE, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + scoped_ptr<QuicPacket> data( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildGoAwayPacket) { + QuicPacketHeader header; + header.public_header.guid = 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 guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x37), + // 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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildPublicResetPacket) { + QuicPublicResetPacket reset_packet; + reset_packet.public_header.guid = 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 GUID) + 0x3E, + // guid + 0x10, 0x32, 0x54, 0x76, + 0x98, 0xBA, 0xDC, 0xFE, + // nonce proof + 0x89, 0x67, 0x45, 0x23, + 0x01, 0xEF, 0xCD, 0xAB, + // rejected sequence number + 0xBC, 0x9A, 0x78, 0x56, + 0x34, 0x12, + }; + + scoped_ptr<QuicEncryptedPacket> data( + framer_.BuildPublicResetPacket(reset_packet)); + ASSERT_TRUE(data != NULL); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); +} + +TEST_P(QuicFramerTest, BuildFecPacket) { + QuicPacketHeader header; + header.public_header.guid = 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 guid) + 0x3C, + // guid + 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 != NULL); + + 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 guid) + 0x3C, + // guid + 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_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER)); + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); + + ASSERT_TRUE(encrypted.get() != NULL); + 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 guid) + 0x3D, + // guid + 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_GUID, kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER)); + scoped_ptr<QuicEncryptedPacket> encrypted( + framer_.EncryptPacket(ENCRYPTION_NONE, sequence_number, *raw)); + + ASSERT_TRUE(encrypted.get() != NULL); + EXPECT_TRUE(CheckEncryption(sequence_number, raw.get())); +} + +// TODO(rch): re-enable after https://codereview.chromium.org/11820005/ +// lands. Currently this is causing valgrind problems, but it should be +// fixed in the followup CL. +TEST_P(QuicFramerTest, DISABLED_CalculateLargestReceived) { + SequenceNumberSet missing; + missing.insert(1); + missing.insert(5); + missing.insert(7); + + // These two we just walk to the next gap, and return the largest seen. + EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(1))); + EXPECT_EQ(6u, QuicFramer::CalculateLargestObserved(missing, missing.find(5))); + + missing.insert(2); + // For 1, we can't go forward as 2 would be implicitly acked so we return the + // largest missing packet. + EXPECT_EQ(1u, QuicFramer::CalculateLargestObserved(missing, missing.find(1))); + // For 2, we've seen 3 and 4, so can admit to a largest observed. + EXPECT_EQ(4u, QuicFramer::CalculateLargestObserved(missing, missing.find(2))); +} + +// TODO(rch) enable after landing the revised truncation CL. +TEST_P(QuicFramerTest, DISABLED_Truncation) { + QuicPacketHeader header; + header.public_header.guid = 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; + + QuicConnectionCloseFrame close_frame; + QuicAckFrame* ack_frame = &close_frame.ack_frame; + close_frame.error_code = static_cast<QuicErrorCode>(0x05); + close_frame.error_details = "because I can"; + ack_frame->received_info.largest_observed = 201; + ack_frame->sent_info.least_unacked = 0; + for (uint64 i = 1; i < ack_frame->received_info.largest_observed; ++i) { + ack_frame->received_info.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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(raw_ack_packet != NULL); + + scoped_ptr<QuicEncryptedPacket> ack_packet( + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet)); + + // Create a packet with just connection close. + frames.clear(); + frame.type = CONNECTION_CLOSE_FRAME; + frame.connection_close_frame = &close_frame; + frames.push_back(frame); + + scoped_ptr<QuicPacket> raw_close_packet( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(raw_close_packet != NULL); + + scoped_ptr<QuicEncryptedPacket> close_packet( + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_close_packet)); + + // Now make sure we can turn our ack packet back into an ack frame + ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); + + // And do the same for the close frame. + ASSERT_TRUE(framer_.ProcessPacket(*close_packet)); +} + +TEST_P(QuicFramerTest, CleanTruncation) { + QuicPacketHeader header; + header.public_header.guid = 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; + QuicAckFrame* ack_frame = &close_frame.ack_frame; + close_frame.error_code = static_cast<QuicErrorCode>(0x05); + close_frame.error_details = "because I can"; + ack_frame->received_info.largest_observed = 201; + ack_frame->sent_info.least_unacked = 0; + for (uint64 i = 1; i < ack_frame->received_info.largest_observed; ++i) { + ack_frame->received_info.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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(raw_ack_packet != NULL); + + scoped_ptr<QuicEncryptedPacket> ack_packet( + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_ack_packet)); + + // Create a packet with just connection close. + frames.clear(); + frame.type = CONNECTION_CLOSE_FRAME; + frame.connection_close_frame = &close_frame; + frames.push_back(frame); + + scoped_ptr<QuicPacket> raw_close_packet( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(raw_close_packet != NULL); + + scoped_ptr<QuicEncryptedPacket> close_packet( + framer_.EncryptPacket(ENCRYPTION_NONE, header.packet_sequence_number, + *raw_close_packet)); + + // Now make sure we can turn our ack packet back into an ack frame + ASSERT_TRUE(framer_.ProcessPacket(*ack_packet)); + + // And do the same for the close frame. + ASSERT_TRUE(framer_.ProcessPacket(*close_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( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(raw_ack_packet != NULL); + EXPECT_EQ(original_raw_length, raw_ack_packet->length()); + + frames.clear(); + frame.type = CONNECTION_CLOSE_FRAME; + frame.connection_close_frame = &visitor_.connection_close_frame_; + frames.push_back(frame); + + original_raw_length = raw_close_packet->length(); + raw_close_packet.reset( + framer_.BuildUnsizedDataPacket(header, frames).packet); + ASSERT_TRUE(raw_ack_packet != NULL); + EXPECT_EQ(original_raw_length, raw_close_packet->length()); +} + +TEST_P(QuicFramerTest, EntropyFlagTest) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xBE, + // 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) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xBE, + // 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) { + // Set a specific version. + framer_.set_version(QUIC_VERSION_7); + + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + 0xFE, + // 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) + 0x02, + // 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()); + + QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); +} + +TEST_P(QuicFramerTest, ConnectionCloseWithInvalidAck) { + unsigned char packet[] = { + // public flags (8 byte guid) + 0x3C, + // guid + 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) + static_cast<unsigned char>(0x2F), + // error code + 0x11, 0x00, 0x00, 0x00, + // error details length + 0x0d, 0x00, + // error details + 'b', 'e', 'c', 'a', + 'u', 's', 'e', ' ', + 'I', ' ', 'c', 'a', + 'n', + + // Ack frame. + // entropy hash of sent packets till least awaiting - 1. + 0xE0, + // 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, + // Infinite delta time. + 0xFF, 0xFF, 0xFF, 0xFF, + // 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, OnAckFrame(_)).WillOnce(Return(false)); + EXPECT_CALL(visitor, OnConnectionCloseFrame(_)).Times(0); + EXPECT_CALL(visitor, OnPacketComplete()); + + 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/chromium/net/quic/quic_http_stream.cc b/chromium/net/quic/quic_http_stream.cc new file mode 100644 index 00000000000..73581248d5b --- /dev/null +++ b/chromium/net/quic/quic_http_stream.cc @@ -0,0 +1,512 @@ +// 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/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_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), + stream_(NULL), + request_info_(NULL), + request_body_stream_(NULL), + response_info_(NULL), + response_status_(OK), + response_headers_received_(false), + read_buf_(new GrowableIOBuffer()), + user_buffer_len_(0), + weak_factory_(this) { + DCHECK(session_); +} + +QuicHttpStream::~QuicHttpStream() { + Close(false); +} + +int QuicHttpStream::InitializeStream(const HttpRequestInfo* request_info, + RequestPriority priority, + const BoundNetLog& stream_net_log, + const CompletionCallback& callback) { + DCHECK(!stream_); + if (!session_) + return ERR_CONNECTION_CLOSED; + + stream_net_log_ = stream_net_log; + request_info_ = request_info; + + int rv = stream_request_.StartRequest( + session_, &stream_, base::Bind(&QuicHttpStream::OnStreamReady, + weak_factory_.GetWeakPtr())); + if (rv == ERR_IO_PENDING) + callback_ = callback; + + if (rv == OK) + stream_->SetDelegate(this); + + return rv; +} + +void QuicHttpStream::OnStreamReady(int rv) { + DCHECK(rv == OK || !stream_); + if (rv == OK) + stream_->SetDelegate(this); + + ResetAndReturn(&callback_).Run(rv); +} + +int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers, + HttpResponseInfo* response, + const CompletionCallback& callback) { + CHECK(stream_); + CHECK(!request_body_stream_); + CHECK(!response_info_); + CHECK(!callback.is_null()); + CHECK(response); + + // Store the serialized request headers. + SpdyHeaderBlock headers; + CreateSpdyHeadersFromHttpRequest(*request_info_, request_headers, + &headers, 3, /*direct=*/true); + request_ = stream_->compressor()->CompressHeaders(headers); + // Log the actual request with the URL Request's net log. + stream_net_log_.AddEvent( + NetLog::TYPE_HTTP_TRANSACTION_SPDY_SEND_REQUEST_HEADERS, + base::Bind(&SpdyHeaderBlockNetLogCallback, &headers)); + // Also log to the QuicSession's net log. + stream_->net_log().AddEvent( + NetLog::TYPE_QUIC_HTTP_STREAM_SEND_REQUEST_HEADERS, + base::Bind(&SpdyHeaderBlockNetLogCallback, &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 kMaxPacketSize as the buffer size, since the request + // body data is written with this size at a time. + // TODO(rch): use a smarter value since we can't write an entire + // packet due to overhead. + raw_request_body_buf_ = new IOBufferWithSize(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_ == NULL) + 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; +} + +const HttpResponseInfo* QuicHttpStream::GetResponseInfo() const { + return response_info_; +} + +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_) { + stream_->SetDelegate(NULL); + stream_->Close(QUIC_STREAM_NO_ERROR); + stream_ = NULL; + } +} + +HttpStream* QuicHttpStream::RenewStreamForAuth() { + return NULL; +} + +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; +} + +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) { + // Nothing to do here (yet). +} + +int QuicHttpStream::OnSendData() { + // TODO(rch): Change QUIC IO to provide notifications to the streams. + NOTREACHED(); + return OK; +} + +int QuicHttpStream::OnSendDataComplete(int status, bool* eof) { + // TODO(rch): Change QUIC IO to provide notifications to the streams. + NOTREACHED(); + return OK; +} + +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) { + read_buf_->SetCapacity(read_buf_->capacity() + kHeaderBufInitialSize); + } + 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); + } + user_buffer_ = NULL; + user_buffer_len_ = 0; + DoCallback(length); + return OK; +} + +void QuicHttpStream::OnClose(QuicErrorCode error) { + if (error != QUIC_NO_ERROR) { + response_status_ = ERR_QUIC_PROTOCOL_ERROR; + } else if (!response_headers_received_) { + response_status_ = ERR_ABORTED; + } + + stream_ = NULL; + if (!callback_.is_null()) + DoCallback(response_status_); +} + +void QuicHttpStream::OnError(int error) { + stream_ = NULL; + response_status_ = error; + if (!callback_.is_null()) + DoCallback(response_status_); +} + +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; + + bool has_upload_data = request_body_stream_ != NULL; + + next_state_ = STATE_SEND_HEADERS_COMPLETE; + QuicConsumedData rv = stream_->WriteData(request_, !has_upload_data); + return rv.bytes_consumed; +} + +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) { + base::StringPiece data(request_body_buf_->data(), len); + QuicConsumedData rv = stream_->WriteData(data, eof); + request_body_buf_->DidConsume(rv.bytes_consumed); + if (eof) { + next_state_ = STATE_OPEN; + return OK; + } + next_state_ = STATE_SEND_BODY_COMPLETE; + return rv.bytes_consumed; + } + + next_state_ = STATE_SEND_BODY_COMPLETE; + return OK; +} + +int QuicHttpStream::DoSendBodyComplete(int rv) { + if (rv < 0) + return rv; + + next_state_ = STATE_READ_REQUEST_BODY; + 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, 3, 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_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/chromium/net/quic/quic_http_stream.h b/chromium/net/quic/quic_http_stream.h new file mode 100644 index 00000000000..cc2b973e87f --- /dev/null +++ b/chromium/net/quic/quic_http_stream.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. + +#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 { + +// 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 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 const HttpResponseInfo* GetResponseInfo() const 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 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 OnSendData() OVERRIDE; + virtual int OnSendDataComplete(int status, bool* eof) OVERRIDE; + virtual int OnDataReceived(const char* data, int length) OVERRIDE; + virtual void OnClose(QuicErrorCode error) OVERRIDE; + virtual void OnError(int error) OVERRIDE; + + private: + 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_; + + const base::WeakPtr<QuicClientSession> session_; + 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_; + // |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_; + + 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_; + + // 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_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_HTTP_STREAM_H_ diff --git a/chromium/net/quic/quic_http_stream_test.cc b/chromium/net/quic/quic_http_stream_test.cc new file mode 100644 index 00000000000..1e4ac916bbf --- /dev/null +++ b/chromium/net/quic/quic_http_stream_test.cc @@ -0,0 +1,562 @@ +// 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/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/quic_client_session.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_connection_helper.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_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::_; + +namespace net { +namespace test { +namespace { + +const char kUploadData[] = "hello world!"; + +class TestQuicConnection : public QuicConnection { + public: + TestQuicConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelper* helper) + : QuicConnection(guid, address, helper, false, QuicVersionMax()) { + } + + 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: + explicit TestReceiveAlgorithm(QuicCongestionFeedbackFrame* feedback) + : feedback_(feedback) { + } + + bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* congestion_feedback) { + if (feedback_ == NULL) { + return false; + } + *congestion_feedback = *feedback_; + return true; + } + + MOCK_METHOD4(RecordIncomingPacket, + void(QuicByteCount, QuicPacketSequenceNumber, QuicTime, bool)); + + private: + MockClock clock_; + QuicCongestionFeedbackFrame* feedback_; + + DISALLOW_COPY_AND_ASSIGN(TestReceiveAlgorithm); +}; + +// 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; + } +}; + +} // namespace + +class QuicHttpStreamTest : public ::testing::TestWithParam<bool> { + protected: + const static bool kFin = 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)), + guid_(2), + framer_(QuicVersionMax(), QuicTime::Zero(), false), + creator_(guid_, &framer_, &random_, false) { + IPAddressNumber ip; + CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); + peer_addr_ = IPEndPoint(ip, 443); + self_addr_ = IPEndPoint(ip, 8435); + } + + ~QuicHttpStreamTest() { + for (size_t i = 0; i < writes_.size(); i++) { + delete writes_[i].packet; + } + } + + // Adds a packet to the list of expected writes. + void AddWrite(IoMode mode, QuicEncryptedPacket* packet) { + writes_.push_back(PacketToWrite(mode, packet)); + } + + // 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(const 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(NULL, 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(NULL); + EXPECT_CALL(*send_algorithm_, RetransmissionDelay()).WillRepeatedly( + testing::Return(QuicTime::Delta::Zero())); + EXPECT_CALL(*send_algorithm_, TimeUntilSend(_, _, _, _)). + WillRepeatedly(testing::Return(QuicTime::Delta::Zero())); + helper_ = new QuicConnectionHelper(runner_.get(), &clock_, + &random_generator_, socket); + connection_ = new TestQuicConnection(guid_, peer_addr_, helper_); + connection_->set_visitor(&visitor_); + connection_->SetSendAlgorithm(send_algorithm_); + connection_->SetReceiveAlgorithm(receive_algorithm_); + crypto_config_.SetDefaults(); + session_.reset( + new QuicClientSession(connection_, + scoped_ptr<DatagramClientSocket>(socket), NULL, + &crypto_client_stream_factory_, + "www.google.com", DefaultQuicConfig(), + &crypto_config_, NULL)); + session_->GetCryptoStream()->CryptoConnect(); + EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed()); + stream_.reset(use_closing_stream_ ? + new AutoClosingStream(session_->GetWeakPtr()) : + new QuicHttpStream(session_->GetWeakPtr())); + } + + void SetRequestString(const std::string& method, const std::string& path) { + SpdyHeaderBlock headers; + headers[":method"] = method; + headers[":host"] = "www.google.com"; + headers[":path"] = path; + headers[":scheme"] = "http"; + headers[":version"] = "HTTP/1.1"; + request_data_ = SerializeHeaderBlock(headers); + } + + void SetResponseString(const std::string& status, const std::string& body) { + SpdyHeaderBlock headers; + headers[":status"] = status; + headers[":version"] = "HTTP/1.1"; + headers["content-type"] = "text/plain"; + response_data_ = SerializeHeaderBlock(headers) + body; + } + + std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) { + QuicSpdyCompressor compressor; + return compressor.CompressHeaders(headers); + } + + // Returns a newly created packet to send kData on stream 1. + QuicEncryptedPacket* ConstructDataPacket( + QuicPacketSequenceNumber sequence_number, + bool should_include_version, + bool fin, + QuicStreamOffset offset, + base::StringPiece data) { + InitializeHeader(sequence_number, should_include_version); + QuicStreamFrame frame(3, fin, offset, data); + return ConstructPacket(header_, QuicFrame(&frame)); + } + + // Returns a newly created packet to send ack data. + QuicEncryptedPacket* ConstructAckPacket( + QuicPacketSequenceNumber sequence_number, + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked) { + InitializeHeader(sequence_number, false); + + QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); + ack.sent_info.entropy_hash = 0; + ack.received_info.entropy_hash = 0; + + return ConstructPacket(header_, QuicFrame(&ack)); + } + + // Returns a newly created packet to send ack data. + QuicEncryptedPacket* ConstructRstPacket( + QuicPacketSequenceNumber sequence_number, + QuicStreamId stream_id) { + InitializeHeader(sequence_number, false); + + QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); + return ConstructPacket(header_, QuicFrame(&rst)); + } + + BoundNetLog net_log_; + bool use_closing_stream_; + MockSendAlgorithm* send_algorithm_; + TestReceiveAlgorithm* receive_algorithm_; + scoped_refptr<TestTaskRunner> runner_; + scoped_ptr<MockWrite[]> mock_writes_; + MockClock clock_; + MockRandom random_generator_; + TestQuicConnection* connection_; + QuicConnectionHelper* helper_; + testing::StrictMock<MockConnectionVisitor> visitor_; + scoped_ptr<QuicHttpStream> stream_; + scoped_ptr<QuicClientSession> session_; + QuicCryptoClientConfig crypto_config_; + TestCompletionCallback callback_; + HttpRequestInfo request_; + HttpRequestHeaders headers_; + HttpResponseInfo response_; + scoped_refptr<IOBufferWithSize> read_buffer_; + std::string request_data_; + std::string response_data_; + + private: + void InitializeHeader(QuicPacketSequenceNumber sequence_number, + bool should_include_version) { + header_.public_header.guid = guid_; + header_.public_header.reset_flag = false; + header_.public_header.version_flag = should_include_version; + header_.packet_sequence_number = sequence_number; + header_.fec_group = 0; + header_.entropy_flag = false; + header_.fec_flag = false; + } + + QuicEncryptedPacket* ConstructPacket(const QuicPacketHeader& header, + const QuicFrame& frame) { + QuicFrames frames; + frames.push_back(frame); + scoped_ptr<QuicPacket> packet( + framer_.BuildUnsizedDataPacket(header_, frames).packet); + return framer_.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet); + } + + const QuicGuid guid_; + QuicFramer framer_; + IPEndPoint self_addr_; + IPEndPoint peer_addr_; + MockRandom random_; + MockCryptoClientStreamFactory crypto_client_stream_factory_; + QuicPacketCreator creator_; + QuicPacketHeader header_; + scoped_ptr<StaticSocketDataProvider> socket_data_; + std::vector<PacketToWrite> writes_; +}; + +TEST_F(QuicHttpStreamTest, RenewStreamForAuth) { + Initialize(); + EXPECT_EQ(NULL, stream_->RenewStreamForAuth()); +} + +TEST_F(QuicHttpStreamTest, CanFindEndOfResponse) { + Initialize(); + EXPECT_TRUE(stream_->CanFindEndOfResponse()); +} + +TEST_F(QuicHttpStreamTest, IsConnectionReusable) { + Initialize(); + EXPECT_FALSE(stream_->IsConnectionReusable()); +} + +TEST_F(QuicHttpStreamTest, GetRequest) { + SetRequestString("GET", "/"); + AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, + request_data_)); + 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())); + EXPECT_EQ(&response_, stream_->GetResponseInfo()); + + // Ack the request. + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); + ProcessPacket(*ack); + + EXPECT_EQ(ERR_IO_PENDING, + stream_->ReadResponseHeaders(callback_.callback())); + + // Send the response without a body. + SetResponseString("404 Not Found", std::string()); + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket(2, false, kFin, 0, response_data_)); + ProcessPacket(*resp); + + // 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")); + + // 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()); +} + +TEST_F(QuicHttpStreamTest, GetRequestFullResponseInSinglePacket) { + SetRequestString("GET", "/"); + AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_)); + 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())); + EXPECT_EQ(&response_, stream_->GetResponseInfo()); + + // Ack the request. + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); + ProcessPacket(*ack); + + EXPECT_EQ(ERR_IO_PENDING, + stream_->ReadResponseHeaders(callback_.callback())); + + // Send the response with a body. + SetResponseString("200 OK", "hello world!"); + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket(2, false, kFin, 0, response_data_)); + ProcessPacket(*resp); + + // 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. + // Since the body has already arrived, this should return immediately. + EXPECT_EQ(12, stream_->ReadResponseBody(read_buffer_.get(), + read_buffer_->size(), + callback_.callback())); + EXPECT_TRUE(stream_->IsResponseBodyComplete()); + EXPECT_TRUE(AtEof()); +} + +TEST_F(QuicHttpStreamTest, SendPostRequest) { + SetRequestString("POST", "/"); + AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, kFin, + request_data_.length(), + kUploadData)); + AddWrite(SYNCHRONOUS, ConstructAckPacket(3, 3, 1)); + + Initialize(); + + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back( + new UploadBytesElementReader(kUploadData, strlen(kUploadData))); + UploadDataStream upload_data_stream(&element_readers, 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())); + EXPECT_EQ(&response_, stream_->GetResponseInfo()); + + // Ack both packets in the request. + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); + ProcessPacket(*ack); + + // Send the response headers (but not the body). + SetResponseString("200 OK", std::string()); + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket(2, false, !kFin, 0, response_data_)); + ProcessPacket(*resp); + + // 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!"; + scoped_ptr<QuicEncryptedPacket> resp_body( + ConstructDataPacket(3, false, kFin, response_data_.length(), + kResponseBody)); + ProcessPacket(*resp_body); + + // 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_F(QuicHttpStreamTest, SendChunkedPostRequest) { + SetRequestString("POST", "/"); + size_t chunk_size = strlen(kUploadData); + AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, !kFin, 0, request_data_)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(2, true, !kFin, + request_data_.length(), + kUploadData)); + AddWrite(SYNCHRONOUS, ConstructDataPacket(3, true, kFin, + request_data_.length() + chunk_size, + kUploadData)); + AddWrite(SYNCHRONOUS, 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())); + EXPECT_EQ(&response_, stream_->GetResponseInfo()); + + upload_data_stream.AppendChunk(kUploadData, chunk_size, true); + + // Ack both packets in the request. + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); + ProcessPacket(*ack); + + // Send the response headers (but not the body). + SetResponseString("200 OK", std::string()); + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket(2, false, !kFin, 0, response_data_)); + ProcessPacket(*resp); + + // 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!"; + scoped_ptr<QuicEncryptedPacket> resp_body( + ConstructDataPacket(3, false, kFin, response_data_.length(), + kResponseBody)); + ProcessPacket(*resp_body); + + // 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_F(QuicHttpStreamTest, DestroyedEarly) { + SetRequestString("GET", "/"); + AddWrite(SYNCHRONOUS, ConstructDataPacket(1, true, kFin, 0, request_data_)); + 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())); + EXPECT_EQ(&response_, stream_->GetResponseInfo()); + + // Ack the request. + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0, 0)); + ProcessPacket(*ack); + EXPECT_EQ(ERR_IO_PENDING, + stream_->ReadResponseHeaders(callback_.callback())); + + // Send the response with a body. + SetResponseString("404 OK", "hello world!"); + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket(2, false, kFin, 0, response_data_)); + + // In the course of processing this packet, the QuicHttpStream close itself. + ProcessPacket(*resp); + + EXPECT_TRUE(AtEof()); +} + +} // namespace test + +} // namespace net diff --git a/chromium/net/quic/quic_network_transaction_unittest.cc b/chromium/net/quic/quic_network_transaction_unittest.cc new file mode 100644 index 00000000000..0722b58121f --- /dev/null +++ b/chromium/net/quic/quic_network_transaction_unittest.cc @@ -0,0 +1,752 @@ +// 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/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_unittest.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/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_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 kQuicAlternateProtocolHttpsHeader[] = + "Alternate-Protocol: 443:quic\r\n\r\n"; +} // namespace + +namespace net { +namespace test { + +class QuicNetworkTransactionTest : public PlatformTest { + protected: + QuicNetworkTransactionTest() + : clock_(new MockClock), + ssl_config_service_(new SSLConfigServiceDefaults), + proxy_service_(ProxyService::CreateDirect()), + compressor_(new QuicSpdyCompressor()), + auth_handler_factory_( + HttpAuthHandlerFactory::CreateDefault(&host_resolver_)), + hanging_data_(NULL, 0, NULL, 0) { + request_.method = "GET"; + request_.url = GURL("http://www.google.com/"); + request_.load_flags = 0; + } + + 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(); + HttpStreamFactory::set_use_alternate_protocols(false); + HttpStreamFactory::SetNextProtos(std::vector<NextProto>()); + } + + scoped_ptr<QuicEncryptedPacket> ConstructRstPacket( + QuicPacketSequenceNumber num, + QuicStreamId stream_id) { + QuicPacketHeader header; + header.public_header.guid = 0xDEADBEEF; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.packet_sequence_number = num; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); + return scoped_ptr<QuicEncryptedPacket>( + ConstructPacket(header, QuicFrame(&rst))); + } + + scoped_ptr<QuicEncryptedPacket> ConstructConnectionClosePacket( + QuicPacketSequenceNumber num) { + QuicPacketHeader header; + header.public_header.guid = 0xDEADBEEF; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.packet_sequence_number = num; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicAckFrame ack_frame(0, QuicTime::Zero(), 0); + QuicConnectionCloseFrame close; + close.error_code = QUIC_CRYPTO_VERSION_NOT_SUPPORTED; + close.error_details = "Time to panic!"; + close.ack_frame = ack_frame; + return scoped_ptr<QuicEncryptedPacket>( + ConstructPacket(header, QuicFrame(&close))); + } + + scoped_ptr<QuicEncryptedPacket> ConstructAckPacket( + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked) { + QuicPacketHeader header; + header.public_header.guid = 0xDEADBEEF; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.packet_sequence_number = 2; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); + + QuicCongestionFeedbackFrame feedback; + feedback.type = kTCP; + feedback.tcp.accumulated_number_of_lost_packets = 0; + feedback.tcp.receive_window = 256000; + + QuicFramer framer(QuicVersionMax(), QuicTime::Zero(), false); + QuicFrames frames; + frames.push_back(QuicFrame(&ack)); + frames.push_back(QuicFrame(&feedback)); + scoped_ptr<QuicPacket> packet( + framer.BuildUnsizedDataPacket(header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + } + + std::string GetRequestString(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 SerializeHeaderBlock(headers); + } + + std::string GetResponseString(const std::string& status, + const std::string& body) { + SpdyHeaderBlock headers; + headers[":status"] = status; + headers[":version"] = "HTTP/1.1"; + headers["content-type"] = "text/plain"; + return compressor_->CompressHeaders(headers) + body; + } + + std::string SerializeHeaderBlock(const SpdyHeaderBlock& headers) { + QuicSpdyCompressor compressor; + return compressor.CompressHeaders(headers); + } + + // Returns a newly created packet to send kData on stream 1. + QuicEncryptedPacket* ConstructDataPacket( + 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, data); + return ConstructPacket(header_, QuicFrame(&frame)).release(); + } + + scoped_ptr<QuicEncryptedPacket> ConstructPacket( + const QuicPacketHeader& header, + const QuicFrame& frame) { + QuicFramer framer(QuicVersionMax(), QuicTime::Zero(), false); + QuicFrames frames; + frames.push_back(frame); + scoped_ptr<QuicPacket> packet( + framer.BuildUnsizedDataPacket(header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + } + + void InitializeHeader(QuicPacketSequenceNumber sequence_number, + bool should_include_version) { + header_.public_header.guid = random_generator_.RandUint64(); + header_.public_header.reset_flag = false; + header_.public_header.version_flag = should_include_version; + header_.packet_sequence_number = sequence_number; + header_.fec_group = 0; + header_.entropy_flag = false; + header_.fec_flag = false; + } + + void CreateSession() { + CreateSessionWithFactory(&socket_factory_); + } + + void CreateSessionWithFactory(ClientSocketFactory* socket_factory) { + 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(); + + session_ = new HttpNetworkSession(params_); + } + + void CheckWasQuicResponse(const scoped_ptr<HttpNetworkTransaction>& trans) { + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers.get() != NULL); + 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 != NULL); + ASSERT_TRUE(response->headers.get() != NULL); + 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); + } + + void ExpectBrokenAlternateProtocolMapping() { + ASSERT_TRUE(session_->http_server_properties()->HasAlternateProtocol( + HostPortPair::FromURL(request_.url))); + const PortAlternateProtocolPair alternate = + session_->http_server_properties()->GetAlternateProtocol( + HostPortPair::FromURL(request_.url)); + EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol); + } + + void AddHangingNonAlternateProtocolSocketData() { + MockConnect hanging_connect(SYNCHRONOUS, ERR_IO_PENDING); + hanging_data_.set_connect_data(hanging_connect); + socket_factory_.AddSocketDataProvider(&hanging_data_); + } + + QuicPacketHeader header_; + scoped_refptr<HttpNetworkSession> session_; + MockClientSocketFactory socket_factory_; + MockCryptoClientStreamFactory crypto_client_stream_factory_; + MockClock* clock_; // Owned by QuicStreamFactory after CreateSession. + MockHostResolver host_resolver_; + MockCertVerifier cert_verifier_; + TransportSecurityState transport_security_state_; + scoped_refptr<SSLConfigServiceDefaults> ssl_config_service_; + scoped_ptr<ProxyService> proxy_service_; + scoped_ptr<QuicSpdyCompressor> compressor_; + scoped_ptr<HttpAuthHandlerFactory> auth_handler_factory_; + MockRandom random_generator_; + HttpServerPropertiesImpl http_server_properties; + HttpNetworkSession::Params params_; + HttpRequestInfo request_; + CapturingBoundNetLog net_log_; + StaticSocketDataProvider hanging_data_; +}; + +TEST_F(QuicNetworkTransactionTest, ForceQuic) { + params_.origin_to_force_quic_on = + HostPortPair::FromString("www.google.com:80"); + + QuicStreamId stream_id = 3; + scoped_ptr<QuicEncryptedPacket> req( + ConstructDataPacket(1, stream_id, true, true, 0, + GetRequestString("GET", "http", "/"))); + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, req->data(), req->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket( + 1, stream_id, false, true, 0, GetResponseString("200 OK", "hello!"))); + MockRead quic_reads[] = { + MockRead(SYNCHRONOUS, resp->data(), resp->length()), + MockRead(ASYNC, OK), // EOF + }; + + DelayedSocketData quic_data( + 1, // wait for one write to finish before reading. + quic_reads, arraysize(quic_reads), + quic_writes, arraysize(quic_writes)); + + socket_factory_.AddSocketDataProvider(&quic_data); + + // 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(stream_id, static_cast<QuicStreamId>(log_stream_id)); +} + +TEST_F(QuicNetworkTransactionTest, ForceQuicWithErrorConnecting) { + params_.origin_to_force_quic_on = + HostPortPair::FromString("www.google.com:80"); + + MockRead quic_reads[] = { + MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), + }; + StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads), + NULL, 0); + socket_factory_.AddSocketDataProvider(&quic_data); + + 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_F(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), NULL, 0); + socket_factory_.AddSocketDataProvider(&data); + SSLSocketDataProvider ssl(ASYNC, OK); + socket_factory_.AddSSLSocketDataProvider(&ssl); + + CreateSession(); + + SendRequestAndExpectHttpResponse("hello world"); +} + +TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuic) { + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + + 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), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + scoped_ptr<QuicEncryptedPacket> req( + ConstructDataPacket(1, 3, true, true, 0, + GetRequestString("GET", "http", "/"))); + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, req->data(), req->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket( + 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); + MockRead quic_reads[] = { + MockRead(SYNCHRONOUS, resp->data(), resp->length()), + MockRead(ASYNC, OK), // EOF + }; + + DelayedSocketData quic_data( + 1, // wait for one write to finish before reading. + quic_reads, arraysize(quic_reads), + quic_writes, arraysize(quic_writes)); + + socket_factory_.AddSocketDataProvider(&quic_data); + + // The non-alternate protocol job needs to hang in order to guarantee that + // the alternate-protocol job will "win". + AddHangingNonAlternateProtocolSocketData(); + + CreateSession(); + + SendRequestAndExpectHttpResponse("hello world"); + SendRequestAndExpectQuicResponse("hello!"); +} + +TEST_F(QuicNetworkTransactionTest, UseAlternateProtocolForQuicForHttps) { + params_.origin_to_force_quic_on = + HostPortPair::FromString("www.google.com:443"); + params_.enable_quic_https = true; + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + + 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), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + scoped_ptr<QuicEncryptedPacket> req( + ConstructDataPacket(1, 3, true, true, 0, + GetRequestString("GET", "https", "/"))); + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, req->data(), req->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket( + 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); + MockRead quic_reads[] = { + MockRead(SYNCHRONOUS, resp->data(), resp->length()), + MockRead(ASYNC, OK), // EOF + }; + + DelayedSocketData quic_data( + 1, // wait for one write to finish before reading. + quic_reads, arraysize(quic_reads), + quic_writes, arraysize(quic_writes)); + + socket_factory_.AddSocketDataProvider(&quic_data); + + // The non-alternate protocol job needs to hang in order to guarantee that + // the alternate-protocol job will "win". + AddHangingNonAlternateProtocolSocketData(); + + CreateSession(); + + // TODO(rtenneti): Test QUIC over HTTPS, GetSSLInfo(). + SendRequestAndExpectHttpResponse("hello world"); +} + +TEST_F(QuicNetworkTransactionTest, HungAlternateProtocol) { + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + 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); + + // 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_F(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) { + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + + scoped_ptr<QuicEncryptedPacket> req( + ConstructDataPacket(1, 3, true, true, 0, + GetRequestString("GET", "http", "/"))); + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, req->data(), req->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket( + 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); + MockRead quic_reads[] = { + MockRead(SYNCHRONOUS, resp->data(), resp->length()), + MockRead(ASYNC, OK), // EOF + }; + + DelayedSocketData quic_data( + 1, // wait for one write to finish before reading. + quic_reads, arraysize(quic_reads), + quic_writes, arraysize(quic_writes)); + + socket_factory_.AddSocketDataProvider(&quic_data); + + // The non-alternate protocol job needs to hang in order to guarantee that + // the alternate-protocol job will "win". + AddHangingNonAlternateProtocolSocketData(); + + CreateSession(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); + SendRequestAndExpectQuicResponse("hello!"); +} + +TEST_F(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) { + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + + scoped_ptr<QuicEncryptedPacket> req( + ConstructDataPacket(1, 3, true, true, 0, + GetRequestString("GET", "http", "/"))); + scoped_ptr<QuicEncryptedPacket> ack(ConstructAckPacket(1, 0)); + + MockWrite quic_writes[] = { + MockWrite(SYNCHRONOUS, req->data(), req->length()), + MockWrite(SYNCHRONOUS, ack->data(), ack->length()), + }; + + scoped_ptr<QuicEncryptedPacket> resp( + ConstructDataPacket( + 1, 3, false, true, 0, GetResponseString("200 OK", "hello!"))); + MockRead quic_reads[] = { + MockRead(SYNCHRONOUS, resp->data(), resp->length()), + MockRead(ASYNC, OK), // EOF + }; + + DelayedSocketData quic_data( + 1, // wait for one write to finish before reading. + quic_reads, arraysize(quic_reads), + quic_writes, arraysize(quic_writes)); + + socket_factory_.AddSocketDataProvider(&quic_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, &address, CompletionCallback(), NULL, + net_log_.bound()); + + CreateSession(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); + SendRequestAndExpectQuicResponse("hello!"); +} + +TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocol) { + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + + // 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), + NULL, 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), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + CreateSession(); + AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START); + SendRequestAndExpectHttpResponse("hello from http"); + ExpectBrokenAlternateProtocolMapping(); +} + +TEST_F(QuicNetworkTransactionTest, BrokenAlternateProtocolReadError) { + HttpStreamFactory::EnableNpnSpdy(); // Enables QUIC too. + + // Alternate-protocol job + MockRead quic_reads[] = { + MockRead(ASYNC, ERR_SOCKET_NOT_CONNECTED), + }; + StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads), + NULL, 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), + NULL, 0); + socket_factory_.AddSocketDataProvider(&http_data); + + CreateSession(); + + AddQuicAlternateProtocolMapping(MockCryptoClientStream::COLD_START); + SendRequestAndExpectHttpResponse("hello from http"); + ExpectBrokenAlternateProtocolMapping(); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_packet_creator.cc b/chromium/net/quic/quic_packet_creator.cc new file mode 100644 index 00000000000..6d696768612 --- /dev/null +++ b/chromium/net/quic/quic_packet_creator.cc @@ -0,0 +1,300 @@ +// 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/logging.h" +#include "net/quic/crypto/quic_random.h" +#include "net/quic/quic_fec_group.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using std::make_pair; +using std::min; +using std::pair; +using std::vector; + +namespace net { + +QuicPacketCreator::QuicPacketCreator(QuicGuid guid, + QuicFramer* framer, + QuicRandom* random_generator, + bool is_server) + : guid_(guid), + framer_(framer), + random_generator_(random_generator), + sequence_number_(0), + fec_group_number_(0), + is_server_(is_server), + send_version_in_packet_(!is_server), + packet_size_(GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + options_.send_sequence_number_length, + NOT_IN_FEC_GROUP)) { + framer_->set_fec_builder(this); +} + +QuicPacketCreator::~QuicPacketCreator() { +} + +void QuicPacketCreator::OnBuiltFecProtectedPayload( + const QuicPacketHeader& header, StringPiece payload) { + if (fec_group_.get()) { + fec_group_->Update(header, payload); + } +} + +bool QuicPacketCreator::ShouldSendFec(bool force_close) const { + return fec_group_.get() != NULL && fec_group_->NumReceivedPackets() > 0 && + (force_close || + fec_group_->NumReceivedPackets() >= options_.max_packets_per_fec_group); +} + +void QuicPacketCreator::MaybeStartFEC() { + if (options_.max_packets_per_fec_group > 0 && fec_group_.get() == NULL) { + DCHECK(queued_frames_.empty()); + // Set the fec group number to the sequence number of the next packet. + fec_group_number_ = sequence_number() + 1; + fec_group_.reset(new QuicFecGroup()); + packet_size_ = GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + options_.send_sequence_number_length, + IN_FEC_GROUP); + DCHECK_LE(packet_size_, options_.max_packet_length); + } +} + +// 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; + } +} + +bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, + QuicStreamOffset offset) const { + return BytesFree() > + QuicFramer::GetMinStreamFrameSize(framer_->version(), id, offset, true); +} + +// static +size_t QuicPacketCreator::StreamFramePacketOverhead( + QuicVersion version, + QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length, + InFecGroup is_in_fec_group) { + return GetPacketHeaderSize(guid_length, include_version, + sequence_number_length, is_in_fec_group) + + // Assumes this is a stream with a single lone packet. + QuicFramer::GetMinStreamFrameSize(version, 1u, 0u, true); +} + +size_t QuicPacketCreator::CreateStreamFrame(QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin, + QuicFrame* frame) { + DCHECK_GT(options_.max_packet_length, + StreamFramePacketOverhead( + framer_->version(), PACKET_8BYTE_GUID, kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, IN_FEC_GROUP)); + DCHECK(HasRoomForStreamFrame(id, offset)); + + const size_t free_bytes = BytesFree(); + size_t bytes_consumed = 0; + + if (data.size() != 0) { + // When a STREAM frame is the last frame in a packet, it consumes two fewer + // bytes of framing overhead. + // Anytime more data is available than fits in with the extra two bytes, + // the frame will be the last, and up to two extra bytes are consumed. + // TODO(ianswett): If QUIC pads, the 1 byte PADDING frame does not fit when + // 1 byte is available, because then the STREAM frame isn't the last. + + // The minimum frame size(0 bytes of data) if it's not the last frame. + size_t min_frame_size = QuicFramer::GetMinStreamFrameSize( + framer_->version(), id, offset, false); + // Check if it's the last frame in the packet. + if (data.size() + min_frame_size > free_bytes) { + // The minimum frame size(0 bytes of data) if it is the last frame. + size_t min_last_frame_size = QuicFramer::GetMinStreamFrameSize( + framer_->version(), id, offset, true); + bytes_consumed = + min<size_t>(free_bytes - min_last_frame_size, data.size()); + } else { + DCHECK_LT(data.size(), BytesFree()); + bytes_consumed = data.size(); + } + + bool set_fin = fin && bytes_consumed == data.size(); // Last frame. + StringPiece data_frame(data.data(), bytes_consumed); + *frame = QuicFrame(new QuicStreamFrame(id, set_fin, offset, data_frame)); + } else { + DCHECK(fin); + // Create a new packet for the fin, if necessary. + *frame = QuicFrame(new QuicStreamFrame(id, true, offset, "")); + } + + return bytes_consumed; +} + +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()); + for (size_t i = 0; i < frames.size(); ++i) { + bool success = AddFrame(frames[i], false); + DCHECK(success); + } + SerializedPacket packet = SerializePacket(); + DCHECK(packet.retransmittable_frames == NULL); + return packet; +} + +bool QuicPacketCreator::HasPendingFrames() { + return !queued_frames_.empty(); +} + +size_t QuicPacketCreator::BytesFree() const { + const size_t max_plaintext_size = + framer_->GetMaxPlaintextSize(options_.max_packet_length); + if (packet_size_ > max_plaintext_size) { + return 0; + } + return max_plaintext_size - packet_size_; +} + +bool QuicPacketCreator::AddSavedFrame(const QuicFrame& frame) { + return AddFrame(frame, true); +} + +SerializedPacket QuicPacketCreator::SerializePacket() { + DCHECK_EQ(false, queued_frames_.empty()); + QuicPacketHeader header; + FillPacketHeader(fec_group_number_, false, false, &header); + + SerializedPacket serialized = framer_->BuildDataPacket( + header, queued_frames_, packet_size_); + queued_frames_.clear(); + packet_size_ = GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + options_.send_sequence_number_length, + fec_group_.get() != NULL ? + IN_FEC_GROUP : NOT_IN_FEC_GROUP); + serialized.retransmittable_frames = queued_retransmittable_frames_.release(); + return serialized; +} + +SerializedPacket QuicPacketCreator::SerializeFec() { + DCHECK_LT(0u, fec_group_->NumReceivedPackets()); + DCHECK_EQ(0u, queued_frames_.size()); + QuicPacketHeader header; + FillPacketHeader(fec_group_number_, true, + fec_group_->entropy_parity(), &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(NULL); + fec_group_number_ = 0; + // Reset packet_size_, since the next packet may not have an FEC group. + packet_size_ = GetPacketHeaderSize(options_.send_guid_length, + send_version_in_packet_, + options_.send_sequence_number_length, + NOT_IN_FEC_GROUP); + DCHECK(serialized.packet); + DCHECK_GE(options_.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(is_server_); + QuicPacketPublicHeader header; + header.guid = guid_; + header.reset_flag = false; + header.version_flag = true; + header.versions = supported_versions; + QuicEncryptedPacket* encrypted = + framer_->BuildVersionNegotiationPacket(header, supported_versions); + DCHECK(encrypted); + DCHECK_GE(options_.max_packet_length, encrypted->length()); + return encrypted; +} + +void QuicPacketCreator::FillPacketHeader(QuicFecGroupNumber fec_group, + bool fec_flag, + bool fec_entropy_flag, + QuicPacketHeader* header) { + header->public_header.guid = guid_; + 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_; + + bool entropy_flag; + if (header->packet_sequence_number == 1) { + DCHECK(!fec_flag); + // TODO(satyamshekhar): No entropy in the first message. + // For crypto tests to pass. Fix this by using deterministic QuicRandom. + entropy_flag = 0; + } else if (fec_flag) { + // FEC packets don't have an entropy of their own. Entropy flag for FEC + // packets is the XOR of entropy of previous packets. + entropy_flag = fec_entropy_flag; + } else { + entropy_flag = random_generator_->RandBool(); + } + header->entropy_flag = entropy_flag; + 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) { + return frame.type != ACK_FRAME && frame.type != CONGESTION_FEEDBACK_FRAME && + frame.type != PADDING_FRAME; +} + +bool QuicPacketCreator::AddFrame(const QuicFrame& frame, + bool save_retransmittable_frames) { + size_t frame_len = framer_->GetSerializedFrameLength( + frame, BytesFree(), queued_frames_.empty()); + if (frame_len == 0) { + return false; + } + packet_size_ += frame_len; + + if (save_retransmittable_frames && ShouldRetransmit(frame)) { + if (queued_retransmittable_frames_.get() == NULL) { + 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; +} + +} // namespace net diff --git a/chromium/net/quic/quic_packet_creator.h b/chromium/net/quic/quic_packet_creator.h new file mode 100644 index 00000000000..a1d74fa532f --- /dev/null +++ b/chromium/net/quic/quic_packet_creator.h @@ -0,0 +1,182 @@ +// 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 QuicRandom; + +class NET_EXPORT_PRIVATE QuicPacketCreator : public QuicFecBuilderInterface { + public: + // Options for controlling how packets are created. + struct Options { + Options() + : max_packet_length(kMaxPacketSize), + random_reorder(false), + max_packets_per_fec_group(0), + send_guid_length(PACKET_8BYTE_GUID), + send_sequence_number_length(PACKET_6BYTE_SEQUENCE_NUMBER) { + } + + size_t max_packet_length; + bool random_reorder; // Inefficient: rewrite if used at scale. + // 0 indicates fec is disabled. + size_t max_packets_per_fec_group; + // Length of guid to send over the wire. + QuicGuidLength send_guid_length; + QuicSequenceNumberLength send_sequence_number_length; + }; + + // QuicRandom* required for packet entropy. + QuicPacketCreator(QuicGuid guid, + QuicFramer* framer, + QuicRandom* random_generator, + bool is_server); + + virtual ~QuicPacketCreator(); + + // QuicFecBuilderInterface + virtual void OnBuiltFecProtectedPayload(const QuicPacketHeader& header, + base::StringPiece payload) OVERRIDE; + + // 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; + + // Starts a new FEC group with the next serialized packet, if FEC is enabled + // and there is not already an FEC group open. + void MaybeStartFEC(); + + // Makes the framer not serialize the protocol version in sent packets. + void StopSendingVersion(); + + // The overhead the framing will add for a packet with one frame. + static size_t StreamFramePacketOverhead( + QuicVersion version, + QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length, + 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, + base::StringPiece data, + QuicStreamOffset offset, + bool fin, + 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); + + // Returns true if there are frames pending to be serialized. + bool HasPendingFrames(); + + // Returns the number of bytes which are free to frames in the current packet. + size_t BytesFree() const; + + // 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 NULL. 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); + + QuicPacketSequenceNumber sequence_number() const { + return sequence_number_; + } + + void set_sequence_number(QuicPacketSequenceNumber s) { + sequence_number_ = s; + } + + Options* options() { + return &options_; + } + + private: + friend class test::QuicPacketCreatorPeer; + + static bool ShouldRetransmit(const QuicFrame& frame); + + void FillPacketHeader(QuicFecGroupNumber fec_group, + bool fec_flag, + bool fec_entropy_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); + + Options options_; + QuicGuid guid_; + QuicFramer* framer_; + QuicRandom* random_generator_; + QuicPacketSequenceNumber sequence_number_; + QuicFecGroupNumber fec_group_number_; + scoped_ptr<QuicFecGroup> fec_group_; + // bool to keep track if this packet creator is being used the server. + bool is_server_; + // Controls whether protocol version should be included while serializing the + // packet. + bool send_version_in_packet_; + 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/chromium/net/quic/quic_packet_creator_test.cc b/chromium/net/quic/quic_packet_creator_test.cc new file mode 100644 index 00000000000..1d793d23e18 --- /dev/null +++ b/chromium/net/quic/quic_packet_creator_test.cc @@ -0,0 +1,333 @@ +// 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/crypto/quic_random.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_packet_creator_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" + +using base::StringPiece; +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 { + +class QuicPacketCreatorTest : public ::testing::TestWithParam<bool> { + protected: + QuicPacketCreatorTest() + : server_framer_(QuicVersionMax(), QuicTime::Zero(), true), + client_framer_(QuicVersionMax(), QuicTime::Zero(), false), + id_(1), + sequence_number_(0), + guid_(2), + data_("foo"), + creator_(guid_, &client_framer_, QuicRandom::GetInstance(), false) { + client_framer_.set_visitor(&framer_visitor_); + server_framer_.set_visitor(&framer_visitor_); + } + ~QuicPacketCreatorTest() { + } + + 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); + EXPECT_EQ(data, frame.stream_frame->data); + EXPECT_EQ(offset, frame.stream_frame->offset); + EXPECT_EQ(fin, frame.stream_frame->fin); + } + + QuicFrames frames_; + QuicFramer server_framer_; + QuicFramer client_framer_; + testing::StrictMock<MockFramerVisitor> framer_visitor_; + QuicStreamId id_; + QuicPacketSequenceNumber sequence_number_; + QuicGuid guid_; + string data_; + QuicPacketCreator creator_; +}; + +TEST_F(QuicPacketCreatorTest, SerializeFrames) { + frames_.push_back(QuicFrame(new QuicAckFrame(0u, QuicTime::Zero(), 0u))); + frames_.push_back(QuicFrame(new QuicStreamFrame( + 0u, false, 0u, StringPiece("")))); + frames_.push_back(QuicFrame(new QuicStreamFrame( + 0u, true, 0u, StringPiece("")))); + 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_, 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_F(QuicPacketCreatorTest, SerializeWithFEC) { + creator_.options()->max_packets_per_fec_group = 6; + ASSERT_FALSE(creator_.ShouldSendFec(false)); + creator_.MaybeStartFEC(); + + frames_.push_back(QuicFrame(new QuicStreamFrame( + 0u, false, 0u, StringPiece("")))); + SerializedPacket serialized = creator_.SerializeAllFrames(frames_); + delete frames_[0].stream_frame; + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + 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; + + ASSERT_FALSE(creator_.ShouldSendFec(false)); + ASSERT_TRUE(creator_.ShouldSendFec(true)); + + serialized = creator_.SerializeFec(); + ASSERT_EQ(2u, serialized.sequence_number); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnFecData(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + } + ProcessPacket(serialized.packet); + delete serialized.packet; +} + +TEST_F(QuicPacketCreatorTest, SerializeConnectionClose) { + QuicConnectionCloseFrame frame; + frame.error_code = QUIC_NO_ERROR; + frame.ack_frame = QuicAckFrame(0u, QuicTime::Zero(), 0u); + + 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_, OnPacketHeader(_)); + EXPECT_CALL(framer_visitor_, OnAckFrame(_)); + EXPECT_CALL(framer_visitor_, OnConnectionCloseFrame(_)); + EXPECT_CALL(framer_visitor_, OnPacketComplete()); + + ProcessPacket(serialized.packet); + delete serialized.packet; +} + +TEST_F(QuicPacketCreatorTest, CreateStreamFrame) { + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "test", 0u, false, &frame); + EXPECT_EQ(4u, consumed); + CheckStreamFrame(frame, 1u, "test", 0u, false); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, CreateStreamFrameFin) { + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "test", 10u, true, &frame); + EXPECT_EQ(4u, consumed); + CheckStreamFrame(frame, 1u, "test", 10u, true); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, CreateStreamFrameFinOnly) { + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "", 0u, true, &frame); + EXPECT_EQ(0u, consumed); + CheckStreamFrame(frame, 1u, string(), 0u, true); + delete frame.stream_frame; +} + +TEST_F(QuicPacketCreatorTest, CreateAllFreeBytesForStreamFrames) { + QuicStreamId kStreamId = 1u; + QuicStreamOffset kOffset = 1u; + for (int i = 0; i < 100; ++i) { + creator_.options()->max_packet_length = i; + const size_t max_plaintext_size = client_framer_.GetMaxPlaintextSize(i); + const bool should_have_room = max_plaintext_size > + (QuicFramer::GetMinStreamFrameSize( + client_framer_.version(), kStreamId, kOffset, true) + + GetPacketHeaderSize(creator_.options()->send_guid_length, + kIncludeVersion, + creator_.options()->send_sequence_number_length, + NOT_IN_FEC_GROUP)); + ASSERT_EQ(should_have_room, + creator_.HasRoomForStreamFrame(kStreamId, kOffset)); + if (should_have_room) { + QuicFrame frame; + size_t bytes_consumed = creator_.CreateStreamFrame( + kStreamId, "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_F(QuicPacketCreatorTest, SerializeVersionNegotiationPacket) { + QuicPacketCreatorPeer::SetIsServer(&creator_, true); + QuicVersionVector versions; + versions.push_back(QuicVersionMax()); + scoped_ptr<QuicEncryptedPacket> encrypted( + creator_.SerializeVersionNegotiationPacket(versions)); + + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + EXPECT_CALL(framer_visitor_, OnVersionNegotiationPacket(_)); + } + client_framer_.ProcessPacket(*encrypted.get()); +} + +INSTANTIATE_TEST_CASE_P(ToggleVersionSerialization, + QuicPacketCreatorTest, + ::testing::Values(false, true)); + +TEST_P(QuicPacketCreatorTest, SerializeFrame) { + if (!GetParam()) { + creator_.StopSendingVersion(); + } + frames_.push_back(QuicFrame(new QuicStreamFrame( + 0u, false, 0u, StringPiece("")))); + SerializedPacket serialized = creator_.SerializeAllFrames(frames_); + delete frames_[0].stream_frame; + + QuicPacketHeader header; + { + InSequence s; + EXPECT_CALL(framer_visitor_, OnPacket()); + 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(), header.public_header.version_flag); + delete serialized.packet; +} + +TEST_P(QuicPacketCreatorTest, CreateStreamFrameTooLarge) { + if (!GetParam()) { + creator_.StopSendingVersion(); + } + // A string larger than fits into a frame. + size_t payload_length; + creator_.options()->max_packet_length = GetPacketLengthForOneStream( + client_framer_.version(), + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + NOT_IN_FEC_GROUP, &payload_length); + QuicFrame frame; + const string too_long_payload(payload_length * 2, 'a'); + size_t consumed = creator_.CreateStreamFrame( + 1u, 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()) { + creator_.StopSendingVersion(); + } + const size_t max_plaintext_size = + client_framer_.GetMaxPlaintextSize(creator_.options()->max_packet_length); + EXPECT_FALSE(creator_.HasPendingFrames()); + EXPECT_EQ(max_plaintext_size - + GetPacketHeaderSize( + creator_.options()->send_guid_length, + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + PACKET_6BYTE_SEQUENCE_NUMBER, + NOT_IN_FEC_GROUP), + creator_.BytesFree()); + + // Add a variety of frame types and then a padding frame. + QuicAckFrame ack_frame; + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&ack_frame))); + EXPECT_TRUE(creator_.HasPendingFrames()); + + QuicCongestionFeedbackFrame congestion_feedback; + congestion_feedback.type = kFixRate; + EXPECT_TRUE(creator_.AddSavedFrame(QuicFrame(&congestion_feedback))); + EXPECT_TRUE(creator_.HasPendingFrames()); + + QuicFrame frame; + size_t consumed = creator_.CreateStreamFrame(1u, "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_.options()->send_guid_length, + QuicPacketCreatorPeer::SendVersionInPacket(&creator_), + PACKET_6BYTE_SEQUENCE_NUMBER, + NOT_IN_FEC_GROUP), + creator_.BytesFree()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_packet_generator.cc b/chromium/net/quic/quic_packet_generator.cc new file mode 100644 index 00000000000..7600010a067 --- /dev/null +++ b/chromium/net/quic/quic_packet_generator.cc @@ -0,0 +1,221 @@ +// 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/logging.h" +#include "net/quic/quic_fec_group.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; + +namespace net { + +QuicPacketGenerator::QuicPacketGenerator(DelegateInterface* delegate, + DebugDelegateInterface* debug_delegate, + QuicPacketCreator* creator) + : delegate_(delegate), + debug_delegate_(debug_delegate), + packet_creator_(creator), + should_flush_(true), + should_send_ack_(false), + should_send_feedback_(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 NUM_FRAME_TYPES: + DCHECK(false) << "Cannot delete type: " << it->type; + } + } +} + +void QuicPacketGenerator::SetShouldSendAck(bool also_send_feedback) { + should_send_ack_ = true; + should_send_feedback_ = also_send_feedback; + SendQueuedFrames(); +} + + +void QuicPacketGenerator::AddControlFrame(const QuicFrame& frame) { + queued_control_frames_.push_back(frame); + SendQueuedFrames(); +} + +QuicConsumedData QuicPacketGenerator::ConsumeData(QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin) { + SendQueuedFrames(); + + size_t total_bytes_consumed = 0; + bool fin_consumed = false; + + while (delegate_->CanWrite(NOT_RETRANSMISSION, HAS_RETRANSMITTABLE_DATA, + NOT_HANDSHAKE)) { + QuicFrame frame; + size_t bytes_consumed = packet_creator_->CreateStreamFrame( + id, data, offset + total_bytes_consumed, fin, &frame); + bool success = AddFrame(frame); + DCHECK(success); + + total_bytes_consumed += bytes_consumed; + fin_consumed = fin && bytes_consumed == data.size(); + data.remove_prefix(bytes_consumed); + DCHECK(data.empty() || packet_creator_->BytesFree() == 0u); + + // TODO(ianswett): Restore packet reordering. + if (should_flush_ || !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. + break; + } + } + + // Ensure the FEC group is closed at the end of this method unless other + // writes are pending. + if (should_flush_ && packet_creator_->ShouldSendFec(true)) { + SerializedPacket serialized_fec = packet_creator_->SerializeFec(); + DCHECK(serialized_fec.packet); + delegate_->OnSerializedPacket(serialized_fec); + } + + DCHECK(!should_flush_ || !packet_creator_->HasPendingFrames()); + return QuicConsumedData(total_bytes_consumed, fin_consumed); +} + +bool QuicPacketGenerator::CanSendWithNextPendingFrameAddition() const { + DCHECK(HasPendingFrames()); + HasRetransmittableData retransmittable = + (should_send_ack_ || should_send_feedback_) ? NO_RETRANSMITTABLE_DATA + : HAS_RETRANSMITTABLE_DATA; + if (retransmittable == HAS_RETRANSMITTABLE_DATA) { + DCHECK(!queued_control_frames_.empty()); // These are retransmittable. + } + return delegate_->CanWrite(NOT_RETRANSMISSION, retransmittable, + NOT_HANDSHAKE); +} + +void QuicPacketGenerator::SendQueuedFrames() { + packet_creator_->MaybeStartFEC(); + // Only add pending frames if we are SURE we can then send the whole packet. + while (HasPendingFrames() && CanSendWithNextPendingFrameAddition()) { + if (!AddNextPendingFrame()) { + // Packet was full, so serialize and send it. + SerializeAndSendPacket(); + } + } + + if (should_flush_) { + if (packet_creator_->HasPendingFrames()) { + SerializeAndSendPacket(); + } + + // Ensure the FEC group is closed at the end of this method unless other + // writes are pending. + if (packet_creator_->ShouldSendFec(true)) { + SerializedPacket serialized_fec = packet_creator_->SerializeFec(); + DCHECK(serialized_fec.packet); + delegate_->OnSerializedPacket(serialized_fec); + packet_creator_->MaybeStartFEC(); + } + } +} + +void QuicPacketGenerator::StartBatchOperations() { + should_flush_ = false; +} + +void QuicPacketGenerator::FinishBatchOperations() { + should_flush_ = true; + SendQueuedFrames(); +} + +bool QuicPacketGenerator::HasQueuedFrames() const { + return packet_creator_->HasPendingFrames() || HasPendingFrames(); +} + +bool QuicPacketGenerator::HasPendingFrames() const { + return should_send_ack_ || should_send_feedback_ || + !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_; + } + + DCHECK(!queued_control_frames_.empty()); + 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); + + if (packet_creator_->ShouldSendFec(false)) { + SerializedPacket serialized_fec = packet_creator_->SerializeFec(); + DCHECK(serialized_fec.packet); + delegate_->OnSerializedPacket(serialized_fec); + packet_creator_->MaybeStartFEC(); + } +} + +} // namespace net diff --git a/chromium/net/quic/quic_packet_generator.h b/chromium/net/quic/quic_packet_generator.h new file mode 100644 index 00000000000..e8b09e64634 --- /dev/null +++ b/chromium/net/quic/quic_packet_generator.h @@ -0,0 +1,145 @@ +// 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. +// * AddControlFram 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" + +namespace net { + +class NET_EXPORT_PRIVATE QuicPacketGenerator { + public: + class NET_EXPORT_PRIVATE DelegateInterface { + public: + virtual ~DelegateInterface() {} + virtual bool CanWrite(Retransmission retransmission, + HasRetransmittableData retransmittable, + IsHandshake handshake) = 0; + virtual QuicAckFrame* CreateAckFrame() = 0; + virtual QuicCongestionFeedbackFrame* CreateFeedbackFrame() = 0; + // Takes ownership of |packet.packet| and |packet.retransmittable_frames|. + virtual bool OnSerializedPacket(const SerializedPacket& packet) = 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 DebugDelegateInterface { + public: + virtual ~DebugDelegateInterface() {} + + // Called when a frame has been added to the current packet. + virtual void OnFrameAddedToPacket(const QuicFrame& frame) = 0; + }; + + QuicPacketGenerator(DelegateInterface* delegate, + DebugDelegateInterface* debug_delegate, + QuicPacketCreator* creator); + + virtual ~QuicPacketGenerator(); + + void SetShouldSendAck(bool also_send_feedback); + void AddControlFrame(const QuicFrame& frame); + QuicConsumedData ConsumeData(QuicStreamId id, + base::StringPiece data, + QuicStreamOffset offset, + bool fin); + + // Disables flushing. + void StartBatchOperations(); + // Enables flushing and flushes queued data. + void FinishBatchOperations(); + + bool HasQueuedFrames() const; + + void set_debug_delegate(DebugDelegateInterface* debug_delegate) { + debug_delegate_ = debug_delegate; + } + + private: + void SendQueuedFrames(); + + // 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_; + DebugDelegateInterface* debug_delegate_; + + QuicPacketCreator* packet_creator_; + QuicFrames queued_control_frames_; + bool should_flush_; + // Flags to indicate the need for just-in-time construction of a frame. + bool should_send_ack_; + bool should_send_feedback_; + // 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_; + + DISALLOW_COPY_AND_ASSIGN(QuicPacketGenerator); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_PACKET_GENERATOR_H_ diff --git a/chromium/net/quic/quic_packet_generator_test.cc b/chromium/net/quic/quic_packet_generator_test.cc new file mode 100644 index 00000000000..45556f174e9 --- /dev/null +++ b/chromium/net/quic/quic_packet_generator_test.cc @@ -0,0 +1,536 @@ +// 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_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::_; + +namespace net { +namespace test { +namespace { + +class MockDelegate : public QuicPacketGenerator::DelegateInterface { + public: + MockDelegate() {} + virtual ~MockDelegate() {} + + MOCK_METHOD3(CanWrite, bool(Retransmission retransmission, + HasRetransmittableData retransmittable, + IsHandshake handshake)); + + MOCK_METHOD0(CreateAckFrame, QuicAckFrame*()); + MOCK_METHOD0(CreateFeedbackFrame, QuicCongestionFeedbackFrame*()); + MOCK_METHOD1(OnSerializedPacket, bool(const SerializedPacket& packet)); + + void SetCanWriteAnything() { + EXPECT_CALL(*this, CanWrite(NOT_RETRANSMISSION, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*this, CanWrite(NOT_RETRANSMISSION, NO_RETRANSMITTABLE_DATA, _)) + .WillRepeatedly(Return(true)); + } + + void SetCanNotWrite() { + EXPECT_CALL(*this, CanWrite(NOT_RETRANSMISSION, _, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*this, CanWrite(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, CanWrite(NOT_RETRANSMISSION, _, _)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*this, CanWrite(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_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_stream_frames; + + QuicFecGroupNumber fec_group; +}; + +} // namespace + +class QuicPacketGeneratorTest : public ::testing::Test { + protected: + QuicPacketGeneratorTest() + : framer_(QuicVersionMax(), QuicTime::Zero(), false), + creator_(42, &framer_, &random_, false), + generator_(&delegate_, NULL, &creator_), + packet_(0, NULL, 0, NULL), + packet2_(0, NULL, 0, NULL), + packet3_(0, NULL, 0, NULL), + packet4_(0, NULL, 0, NULL), + packet5_(0, NULL, 0, NULL) { + } + + ~QuicPacketGeneratorTest() { + 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; + } + + QuicAckFrame* CreateAckFrame() { + // TODO(rch): Initialize this so it can be verified later. + return new QuicAckFrame(0, QuicTime::Zero(), 0); + } + + QuicCongestionFeedbackFrame* CreateFeedbackFrame() { + QuicCongestionFeedbackFrame* frame = new QuicCongestionFeedbackFrame; + frame->type = kFixRate; + frame->fix_rate.bitrate = QuicBandwidth::FromBytesPerSecond(42); + return frame; + } + + QuicRstStreamFrame* CreateRstStreamFrame() { + return new QuicRstStreamFrame(1, QUIC_STREAM_NO_ERROR); + } + + 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 + + num_retransmittable_frames; + + if (num_retransmittable_frames == 0) { + ASSERT_TRUE(packet.retransmittable_frames == NULL); + } else { + ASSERT_TRUE(packet.retransmittable_frames != NULL); + EXPECT_EQ(num_retransmittable_frames, + packet.retransmittable_frames->frames().size()); + } + + ASSERT_TRUE(packet.packet != NULL); + 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.fec_group, simple_framer_.header().fec_group); + } + + void CheckPacketHasSingleStreamFrame(const SerializedPacket& packet) { + ASSERT_TRUE(packet.retransmittable_frames != NULL); + EXPECT_EQ(1u, packet.retransmittable_frames->frames().size()); + ASSERT_TRUE(packet.packet != NULL); + 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 == NULL); + ASSERT_TRUE(packet.packet != NULL); + ASSERT_TRUE(simple_framer_.ProcessPacket(*packet.packet)); + EXPECT_TRUE(simple_framer_.header().fec_flag); + EXPECT_EQ(fec_group, simple_framer_.fec_data().fec_group); + } + + StringPiece CreateData(size_t len) { + data_array_.reset(new char[len]); + memset(data_array_.get(), '?', len); + return StringPiece(data_array_.get(), len); + } + + QuicFramer framer_; + MockRandom random_; + QuicPacketCreator creator_; + testing::StrictMock<MockDelegate> delegate_; + QuicPacketGenerator generator_; + SimpleQuicFramer simple_framer_; + SerializedPacket packet_; + SerializedPacket packet2_; + SerializedPacket packet3_; + SerializedPacket packet4_; + SerializedPacket packet5_; + + private: + scoped_ptr<char[]> data_array_; +}; + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_NotWritable) { + delegate_.SetCanNotWrite(); + + generator_.SetShouldSendAck(false); + EXPECT_TRUE(generator_.HasQueuedFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldNotFlush) { + delegate_.SetCanWriteOnlyNonRetransmittable(); + generator_.StartBatchOperations(); + + EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame())); + + generator_.SetShouldSendAck(false); + EXPECT_TRUE(generator_.HasQueuedFrames()); +} + +TEST_F(QuicPacketGeneratorTest, ShouldSendAck_WritableAndShouldFlush) { + delegate_.SetCanWriteOnlyNonRetransmittable(); + + EXPECT_CALL(delegate_, CreateAckFrame()).WillOnce(Return(CreateAckFrame())); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + + generator_.SetShouldSendAck(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); + 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_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + + generator_.SetShouldSendAck(true); + EXPECT_FALSE(generator_.HasQueuedFrames()); + + PacketContents contents; + contents.num_ack_frames = 1; + contents.num_feedback_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_WritableAndShouldFlush) { + delegate_.SetCanWriteAnything(); + + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + + 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(1, "foo", 2, true); + 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(1, "foo", 2, true); + 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( + DoAll(SaveArg<0>(&packet_), Return(true))); + QuicConsumedData consumed = generator_.ConsumeData(1, "foo", 2, true); + 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(1, "foo", 2, true); + QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false); + 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(1, "foo", 2, true); + QuicConsumedData consumed = generator_.ConsumeData(3, "quux", 7, false); + 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( + DoAll(SaveArg<0>(&packet_), Return(true))); + 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_.options()->max_packets_per_fec_group = 2; + + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet2_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet3_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet4_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet5_), Return(true))); + } + + // Send enough data to create 3 packets: two full and one partial. + size_t data_len = 2 * kMaxPacketSize + 100; + QuicConsumedData consumed = + generator_.ConsumeData(3, CreateData(data_len), 0, true); + 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(); + + // Send FEC every six packets. + creator_.options()->max_packets_per_fec_group = 6; + + { + InSequence dummy; + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet2_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet3_), Return(true))); + } + + // Send enough data to create 2 packets: one full and one partial. + size_t data_len = 1 * kMaxPacketSize + 100; + QuicConsumedData consumed = + generator_.ConsumeData(3, CreateData(data_len), 0, true); + 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, NotWritableThenBatchOperations) { + delegate_.SetCanNotWrite(); + + generator_.SetShouldSendAck(true); + 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, "quux", 7, false); + generator_.AddControlFrame(QuicFrame(CreateGoAwayFrame())); + + // All five frames will be flushed out in a single packet. + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet_), Return(true))); + 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); + 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( + DoAll(SaveArg<0>(&packet_), Return(true))); + EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce( + DoAll(SaveArg<0>(&packet2_), Return(true))); + } + + // Send enough data to exceed one packet + size_t data_len = kMaxPacketSize + 100; + QuicConsumedData consumed = + generator_.ConsumeData(3, CreateData(data_len), 0, true); + 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/chromium/net/quic/quic_protocol.cc b/chromium/net/quic/quic_protocol.cc new file mode 100644 index 00000000000..4c2e5527971 --- /dev/null +++ b/chromium/net/quic/quic_protocol.cc @@ -0,0 +1,429 @@ +// 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(QuicPacketHeader header) { + return GetPacketHeaderSize(header.public_header.guid_length, + header.public_header.version_flag, + header.public_header.sequence_number_length, + header.is_in_fec_group); +} + +size_t GetPacketHeaderSize(QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length, + InFecGroup is_in_fec_group) { + return kPublicFlagsSize + guid_length + + (include_version ? kQuicVersionSize : 0) + sequence_number_length + + kPrivateFlagsSize + (is_in_fec_group == IN_FEC_GROUP ? kFecGroupSize : 0); +} + +size_t GetPublicResetPacketSize() { + return kPublicFlagsSize + PACKET_8BYTE_GUID + kPublicResetNonceSize + + PACKET_6BYTE_SEQUENCE_NUMBER; +} + +size_t GetStartOfFecProtectedData( + QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length) { + return GetPacketHeaderSize( + guid_length, include_version, sequence_number_length, IN_FEC_GROUP); +} + +size_t GetStartOfEncryptedData( + QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length) { + // Don't include the fec size, since encryption starts before private flags. + return GetPacketHeaderSize( + guid_length, include_version, sequence_number_length, NOT_IN_FEC_GROUP) - + kPrivateFlagsSize; +} + +QuicPacketPublicHeader::QuicPacketPublicHeader() + : guid(0), + guid_length(PACKET_8BYTE_GUID), + reset_flag(false), + version_flag(false), + sequence_number_length(PACKET_6BYTE_SEQUENCE_NUMBER) { +} + +QuicPacketPublicHeader::QuicPacketPublicHeader( + const QuicPacketPublicHeader& other) + : guid(other.guid), + guid_length(other.guid_length), + reset_flag(other.reset_flag), + version_flag(other.version_flag), + sequence_number_length(other.sequence_number_length), + versions(other.versions) { +} + +QuicPacketPublicHeader::~QuicPacketPublicHeader() {} + +QuicPacketPublicHeader& QuicPacketPublicHeader::operator=( + const QuicPacketPublicHeader& other) { + guid = other.guid; + reset_flag = other.reset_flag; + version_flag = other.version_flag; + versions = other.versions; + return *this; +} + +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) { +} + +QuicStreamFrame::QuicStreamFrame() {} + +QuicStreamFrame::QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + StringPiece data) + : stream_id(stream_id), + fin(fin), + offset(offset), + data(data) { +} + +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; +} + +QuicVersion QuicVersionMax() { return kSupportedQuicVersions[0]; } + +QuicVersion QuicVersionMin() { + return kSupportedQuicVersions[arraysize(kSupportedQuicVersions) - 1]; +} + +QuicTag QuicVersionToQuicTag(const QuicVersion version) { + switch (version) { + case QUIC_VERSION_7: + return MakeQuicTag('Q', '0', '0', '7'); + case QUIC_VERSION_8: + return MakeQuicTag('Q', '0', '0', '8'); + 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) { + const QuicTag quic_tag_v7 = MakeQuicTag('Q', '0', '0', '7'); + const QuicTag quic_tag_v8 = MakeQuicTag('Q', '0', '0', '8'); + + if (version_tag == quic_tag_v7) { + return QUIC_VERSION_7; + } else if (version_tag == quic_tag_v8) { + return QUIC_VERSION_8; + } else { + // Reading from the client so this should not be considered an ERROR. + DLOG(INFO) << "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_7); + RETURN_STRING_LITERAL(QUIC_VERSION_8); + default: + return "QUIC_VERSION_UNSUPPORTED"; + } +} + +string QuicVersionArrayToString(const QuicVersion versions[], + int num_versions) { + string result = ""; + for (int i = 0; i < num_versions; ++i) { + const QuicVersion& version = versions[i]; + result.append(QuicVersionToString(version)); + result.append(","); + } + return result; +} + +ostream& operator<<(ostream& os, const QuicPacketHeader& header) { + os << "{ guid: " << header.public_header.guid + << ", guid_length:" << header.public_header.guid_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; +} + +// TODO(ianswett): Initializing largest_observed to 0 should not be necessary. +ReceivedPacketInfo::ReceivedPacketInfo() + : largest_observed(0), + delta_time_largest_observed(QuicTime::Delta::Infinite()) { +} + +ReceivedPacketInfo::~ReceivedPacketInfo() {} + +bool IsAwaitingPacket(const ReceivedPacketInfo& received_info, + QuicPacketSequenceNumber sequence_number) { + return sequence_number > received_info.largest_observed || + ContainsKey(received_info.missing_packets, sequence_number); +} + +void InsertMissingPacketsBetween(ReceivedPacketInfo* received_info, + QuicPacketSequenceNumber lower, + QuicPacketSequenceNumber higher) { + for (QuicPacketSequenceNumber i = lower; i < higher; ++i) { + received_info->missing_packets.insert(i); + } +} + +SentPacketInfo::SentPacketInfo() {} + +SentPacketInfo::~SentPacketInfo() {} + +// Testing convenience method. +QuicAckFrame::QuicAckFrame(QuicPacketSequenceNumber largest_observed, + QuicTime largest_observed_receive_time, + QuicPacketSequenceNumber least_unacked) { + received_info.largest_observed = largest_observed; + received_info.entropy_hash = 0; + sent_info.least_unacked = least_unacked; + sent_info.entropy_hash = 0; +} + +ostream& operator<<(ostream& os, const SentPacketInfo& 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 ReceivedPacketInfo& received_info) { + os << "entropy_hash: " << static_cast<int>(received_info.entropy_hash) + << " largest_observed: " << received_info.largest_observed + << " missing_packets: [ "; + for (SequenceNumberSet::const_iterator it = + received_info.missing_packets.begin(); + it != received_info.missing_packets.end(); ++it) { + os << *it << " "; + } + os << " ] "; + return os; +} + +QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() { +} + +QuicCongestionFeedbackFrame::~QuicCongestionFeedbackFrame() { +} + +ostream& operator<<(ostream& os, + const QuicCongestionFeedbackFrame& congestion_frame) { + os << "type: " << congestion_frame.type; + switch (congestion_frame.type) { + case kInterArrival: { + const CongestionFeedbackMessageInterArrival& inter_arrival = + congestion_frame.inter_arrival; + os << " accumulated_number_of_lost_packets: " + << inter_arrival.accumulated_number_of_lost_packets; + os << " received packets: [ "; + for (TimeMap::const_iterator it = + inter_arrival.received_packet_times.begin(); + it != inter_arrival.received_packet_times.end(); ++it) { + os << it->first << "@" << it->second.ToDebuggingValue() << " "; + } + os << "]"; + break; + } + case kFixRate: { + os << " bitrate_in_bytes_per_second: " + << congestion_frame.fix_rate.bitrate.ToBytesPerSecond(); + break; + } + case kTCP: { + const CongestionFeedbackMessageTCP& tcp = congestion_frame.tcp; + os << " accumulated_number_of_lost_packets: " + << congestion_frame.tcp.accumulated_number_of_lost_packets; + os << " receive_window: " << tcp.receive_window; + break; + } + } + return os; +} + +ostream& operator<<(ostream& os, const QuicAckFrame& ack_frame) { + os << "sent info { " << ack_frame.sent_info << " } " + << "received info { " << ack_frame.received_info << " }\n"; + return os; +} + +CongestionFeedbackMessageFixRate::CongestionFeedbackMessageFixRate() + : bitrate(QuicBandwidth::Zero()) { +} + +CongestionFeedbackMessageInterArrival:: +CongestionFeedbackMessageInterArrival() {} + +CongestionFeedbackMessageInterArrival:: +~CongestionFeedbackMessageInterArrival() {} + +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()); +} + +QuicFecData::QuicFecData() {} + +QuicData::~QuicData() { + if (owns_buffer_) { + delete [] const_cast<char*>(buffer_); + } +} + +StringPiece QuicPacket::FecProtectedData() const { + const size_t start_of_fec = GetStartOfFecProtectedData( + guid_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( + guid_length_, includes_version_, sequence_number_length_) - + kStartOfHashData); +} + +StringPiece QuicPacket::BeforePlaintext() const { + return StringPiece(data(), GetStartOfEncryptedData(guid_length_, + includes_version_, + sequence_number_length_)); +} + +StringPiece QuicPacket::Plaintext() const { + const size_t start_of_encrypted_data = + GetStartOfEncryptedData( + guid_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) { +} + +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 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 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 StringPiece. + string* stream_data = new string(stream_frame->data.data(), + stream_frame->data.size()); + // Ensure the frame's StringPiece points to the owned copy of the data. + stream_frame->data = StringPiece(*stream_data); + stream_data_.push_back(stream_data); + frames_.push_back(QuicFrame(stream_frame)); + 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; +} + +ostream& operator<<(ostream& os, const QuicEncryptedPacket& s) { + os << s.length() << "-byte data"; + return os; +} + +ostream& operator<<(ostream& os, const QuicConsumedData& s) { + os << "bytes_consumed: " << s.bytes_consumed + << " fin_consumed: " << s.fin_consumed; + return os; +} + +} // namespace net diff --git a/chromium/net/quic/quic_protocol.h b/chromium/net/quic/quic_protocol.h new file mode 100644 index 00000000000..737bb16106f --- /dev/null +++ b/chromium/net/quic/quic_protocol.h @@ -0,0 +1,853 @@ +// 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 <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/net_export.h" +#include "net/quic/quic_bandwidth.h" +#include "net/quic/quic_time.h" + +namespace net { + +using ::operator<<; + +class QuicPacket; +struct QuicPacketHeader; + +typedef uint64 QuicGuid; +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; + +// TODO(rch): Consider Quic specific names for these constants. +// Maximum size in bytes of a QUIC packet. +const QuicByteCount kMaxPacketSize = 1200; + +// 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; +// Number of bytes reserved for the nonce proof in public reset packet. +const size_t kPublicResetNonceSize = 8; + +// 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 = 100; +// Limit on the delta between header IDs. +const QuicHeaderId kMaxHeaderIdDelta = 100; + +// Reserved ID for the crypto stream. +// TODO(rch): ensure that this is not usable by any other streams. +const QuicStreamId kCryptoStreamId = 1; + +// This is the default network timeout a for connection till the crypto +// handshake succeeds and the negotiated timeout from the handshake is received. +const int64 kDefaultInitialTimeoutSecs = 120; // 2 mins. +const int64 kDefaultTimeoutSecs = 60 * 10; // 10 minutes. +const int64 kDefaultMaxTimeForCryptoHandshakeSecs = 5; // 5 secs. + +enum Retransmission { + NOT_RETRANSMISSION, + IS_RETRANSMISSION, +}; + +enum HasRetransmittableData { + NO_RETRANSMITTABLE_DATA, + HAS_RETRANSMITTABLE_DATA, +}; + +enum IsHandshake { + NOT_HANDSHAKE, + IS_HANDSHAKE +}; + +enum QuicFrameType { + PADDING_FRAME = 0, + STREAM_FRAME, + ACK_FRAME, + CONGESTION_FEEDBACK_FRAME, + RST_STREAM_FRAME, + CONNECTION_CLOSE_FRAME, + GOAWAY_FRAME, + NUM_FRAME_TYPES +}; + +enum QuicGuidLength { + PACKET_0BYTE_GUID = 0, + PACKET_1BYTE_GUID = 1, + PACKET_4BYTE_GUID = 4, + PACKET_8BYTE_GUID = 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 +}; + +// 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 GUID as follows: + // ----00--: 0 bytes + // ----01--: 1 byte + // ----10--: 4 bytes + // ----11--: 8 bytes + PACKET_PUBLIC_FLAGS_0BYTE_GUID = 0, + PACKET_PUBLIC_FLAGS_1BYTE_GUID = 1 << 2, + PACKET_PUBLIC_FLAGS_4BYTE_GUID = 1 << 3, + PACKET_PUBLIC_FLAGS_8BYTE_GUID = 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 = 0, + PACKET_PUBLIC_FLAGS_2BYTE_SEQUENCE = 1 << 4, + PACKET_PUBLIC_FLAGS_4BYTE_SEQUENCE = 1 << 5, + PACKET_PUBLIC_FLAGS_6BYTE_SEQUENCE = 1 << 5 | 1 << 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_7 = 7, + QUIC_VERSION_8 = 8, // Current 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). +static const QuicVersion kSupportedQuicVersions[] = + {QUIC_VERSION_8, QUIC_VERSION_7}; + +typedef std::vector<QuicVersion> QuicVersionVector; + +// Upper limit on versions we support. +NET_EXPORT_PRIVATE QuicVersion QuicVersionMax(); + +// Lower limit on versions we support. +NET_EXPORT_PRIVATE QuicVersion QuicVersionMin(); + +// 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 QuicVersionArray. +NET_EXPORT_PRIVATE std::string QuicVersionArrayToString( + const QuicVersion versions[], int num_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); + +// Size in bytes of the data or fec packet header. +NET_EXPORT_PRIVATE size_t GetPacketHeaderSize(QuicPacketHeader header); + +NET_EXPORT_PRIVATE size_t GetPacketHeaderSize( + QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length, + InFecGroup is_in_fec_group); + +// Size in bytes of the public reset packet. +NET_EXPORT_PRIVATE size_t GetPublicResetPacketSize(); + +// Index of the first byte in a QUIC packet of FEC protected data. +NET_EXPORT_PRIVATE size_t GetStartOfFecProtectedData( + QuicGuidLength guid_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( + QuicGuidLength guid_length, + bool include_version, + QuicSequenceNumberLength sequence_number_length); + +enum QuicRstStreamErrorCode { + QUIC_STREAM_NO_ERROR = 0, + + // There was some server error which halted stream processing. + QUIC_SERVER_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, + + // No error. Used as bound while iterating. + QUIC_STREAM_LAST_ERROR, +}; + +enum QuicErrorCode { + QUIC_NO_ERROR = 0, + + // Connection has reached an invalid state. + QUIC_INTERNAL_ERROR, + // There were data frames after the a fin or reset. + QUIC_STREAM_DATA_AFTER_TERMINATION, + // Control frame is malformed. + QUIC_INVALID_PACKET_HEADER, + // Frame data is malformed. + QUIC_INVALID_FRAME_DATA, + // FEC data is malformed. + QUIC_INVALID_FEC_DATA, + // Stream rst data is malformed + QUIC_INVALID_RST_STREAM_DATA, + // Connection close data is malformed. + QUIC_INVALID_CONNECTION_CLOSE_DATA, + // GoAway data is malformed. + QUIC_INVALID_GOAWAY_DATA, + // Ack data is malformed. + QUIC_INVALID_ACK_DATA, + // Version negotiation packet is malformed. + QUIC_INVALID_VERSION_NEGOTIATION_PACKET, + // Public RST packet is malformed. + QUIC_INVALID_PUBLIC_RST_PACKET, + // There was an error decrypting. + QUIC_DECRYPTION_FAILURE, + // There was an error encrypting. + QUIC_ENCRYPTION_FAILURE, + // The packet exceeded kMaxPacketSize. + QUIC_PACKET_TOO_LARGE, + // Data was sent for a stream which did not exist. + QUIC_PACKET_FOR_NONEXISTENT_STREAM, + // The peer is going away. May be a client or server. + QUIC_PEER_GOING_AWAY, + // A stream ID was invalid. + QUIC_INVALID_STREAM_ID, + // Too many streams already open. + QUIC_TOO_MANY_OPEN_STREAMS, + // Received public reset for this connection. + QUIC_PUBLIC_RESET, + // Invalid protocol version. + QUIC_INVALID_VERSION, + // Stream reset before headers decompressed. + QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED, + // The Header ID for a stream was too far from the previous. + QUIC_INVALID_HEADER_ID, + // Negotiable parameter received during handshake had invalid value. + QUIC_INVALID_NEGOTIATED_VALUE, + // There was an error decompressing data. + QUIC_DECOMPRESSION_FAILURE, + // We hit our prenegotiated (or default) timeout + QUIC_CONNECTION_TIMED_OUT, + // There was an error encountered migrating addresses + QUIC_ERROR_MIGRATING_ADDRESS, + // There was an error while writing the packet. + QUIC_PACKET_WRITE_ERROR, + + + // Crypto errors. + + // Hanshake failed. + QUIC_HANDSHAKE_FAILED, + // Handshake message contained out of order tags. + QUIC_CRYPTO_TAGS_OUT_OF_ORDER, + // Handshake message contained too many entries. + QUIC_CRYPTO_TOO_MANY_ENTRIES, + // Handshake message contained an invalid value length. + QUIC_CRYPTO_INVALID_VALUE_LENGTH, + // A crypto message was received after the handshake was complete. + QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE, + // A crypto message was received with an illegal message tag. + QUIC_INVALID_CRYPTO_MESSAGE_TYPE, + // A crypto message was received with an illegal parameter. + QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, + // A crypto message was received with a mandatory parameter missing. + QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, + // A crypto message was received with a parameter that has no overlap + // with the local parameter. + QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, + // A crypto message was received that contained a parameter with too few + // values. + QUIC_CRYPTO_MESSAGE_INDEX_NOT_FOUND, + // An internal error occured in crypto processing. + QUIC_CRYPTO_INTERNAL_ERROR, + // A crypto handshake message specified an unsupported version. + QUIC_CRYPTO_VERSION_NOT_SUPPORTED, + // There was no intersection between the crypto primitives supported by the + // peer and ourselves. + QUIC_CRYPTO_NO_SUPPORT, + // The server rejected our client hello messages too many times. + QUIC_CRYPTO_TOO_MANY_REJECTS, + // The client rejected the server's certificate chain or signature. + QUIC_PROOF_INVALID, + // A crypto message was received with a duplicate tag. + QUIC_CRYPTO_DUPLICATE_TAG, + // 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, + // The server config for a server has expired. + QUIC_CRYPTO_SERVER_CONFIG_EXPIRED, + + // No error. Used as bound while iterating. + QUIC_LAST_ERROR, +}; + +struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { + QuicPacketPublicHeader(); + explicit QuicPacketPublicHeader(const QuicPacketPublicHeader& other); + ~QuicPacketPublicHeader(); + + QuicPacketPublicHeader& operator=(const QuicPacketPublicHeader& other); + + // Universal header. All QuicPacket headers will have a guid and public flags. + QuicGuid guid; + QuicGuidLength guid_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) + : public_header(header) {} + QuicPacketPublicHeader public_header; + QuicPacketSequenceNumber rejected_sequence_number; + QuicPublicResetNonceProof nonce_proof; +}; + +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 { +}; + +struct NET_EXPORT_PRIVATE QuicStreamFrame { + QuicStreamFrame(); + QuicStreamFrame(QuicStreamId stream_id, + bool fin, + QuicStreamOffset offset, + base::StringPiece data); + + QuicStreamId stream_id; + bool fin; + QuicStreamOffset offset; // Location of this data in the stream. + base::StringPiece data; +}; + +// TODO(ianswett): Re-evaluate the trade-offs of hash_set vs set when framing +// is finalized. +typedef std::set<QuicPacketSequenceNumber> SequenceNumberSet; +// TODO(pwestin): Add a way to enforce the max size of this map. +typedef std::map<QuicPacketSequenceNumber, QuicTime> TimeMap; + +struct NET_EXPORT_PRIVATE ReceivedPacketInfo { + ReceivedPacketInfo(); + ~ReceivedPacketInfo(); + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const ReceivedPacketInfo& 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; +}; + +// 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 ReceivedPacketInfo& received_info, + QuicPacketSequenceNumber sequence_number); + +// Inserts missing packets between [lower, higher). +void NET_EXPORT_PRIVATE InsertMissingPacketsBetween( + ReceivedPacketInfo* received_info, + QuicPacketSequenceNumber lower, + QuicPacketSequenceNumber higher); + +struct NET_EXPORT_PRIVATE SentPacketInfo { + SentPacketInfo(); + ~SentPacketInfo(); + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const SentPacketInfo& 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() {} + // Testing convenience method to construct a QuicAckFrame with all packets + // from least_unacked to largest_observed acked. + QuicAckFrame(QuicPacketSequenceNumber largest_observed, + QuicTime largest_observed_receive_time, + QuicPacketSequenceNumber least_unacked); + + NET_EXPORT_PRIVATE friend std::ostream& operator<<( + std::ostream& os, const QuicAckFrame& s); + + SentPacketInfo sent_info; + ReceivedPacketInfo received_info; +}; + +// 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. +enum CongestionFeedbackType { + kTCP, // Used to mimic TCP. + kInterArrival, // Use additional inter arrival information. + kFixRate, // Provided for testing. +}; + +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP { + uint16 accumulated_number_of_lost_packets; + QuicByteCount receive_window; +}; + +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { + CongestionFeedbackMessageInterArrival(); + ~CongestionFeedbackMessageInterArrival(); + uint16 accumulated_number_of_lost_packets; + // The set of received packets since the last feedback was sent, along with + // their arrival times. + TimeMap received_packet_times; +}; + +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageFixRate { + CongestionFeedbackMessageFixRate(); + QuicBandwidth bitrate; +}; + +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 inter arrival struct + // is non-trivial, C++ prohibits it. + CongestionFeedbackMessageTCP tcp; + CongestionFeedbackMessageInterArrival inter_arrival; + CongestionFeedbackMessageFixRate fix_rate; +}; + +struct NET_EXPORT_PRIVATE QuicRstStreamFrame { + QuicRstStreamFrame() {} + QuicRstStreamFrame(QuicStreamId stream_id, QuicRstStreamErrorCode error_code) + : stream_id(stream_id), error_code(error_code) { + DCHECK_LE(error_code, std::numeric_limits<uint8>::max()); + } + + QuicStreamId stream_id; + QuicRstStreamErrorCode error_code; + std::string error_details; +}; + +struct NET_EXPORT_PRIVATE QuicConnectionCloseFrame { + QuicErrorCode error_code; + std::string error_details; + QuicAckFrame ack_frame; +}; + +struct NET_EXPORT_PRIVATE QuicGoAwayFrame { + QuicGoAwayFrame() {} + QuicGoAwayFrame(QuicErrorCode error_code, + QuicStreamId last_good_stream_id, + const std::string& reason); + + QuicErrorCode error_code; + QuicStreamId last_good_stream_id; + std::string reason_phrase; +}; + +// 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) + : type(PADDING_FRAME), + padding_frame(padding_frame) { + } + explicit QuicFrame(QuicStreamFrame* stream_frame) + : type(STREAM_FRAME), + stream_frame(stream_frame) { + } + explicit QuicFrame(QuicAckFrame* frame) + : type(ACK_FRAME), + ack_frame(frame) { + } + explicit QuicFrame(QuicCongestionFeedbackFrame* frame) + : type(CONGESTION_FEEDBACK_FRAME), + congestion_feedback_frame(frame) { + } + explicit QuicFrame(QuicRstStreamFrame* frame) + : type(RST_STREAM_FRAME), + rst_stream_frame(frame) { + } + explicit QuicFrame(QuicConnectionCloseFrame* frame) + : type(CONNECTION_CLOSE_FRAME), + connection_close_frame(frame) { + } + explicit QuicFrame(QuicGoAwayFrame* frame) + : type(GOAWAY_FRAME), + goaway_frame(frame) { + } + + QuicFrameType type; + union { + QuicPaddingFrame* padding_frame; + QuicStreamFrame* stream_frame; + QuicAckFrame* ack_frame; + QuicCongestionFeedbackFrame* congestion_feedback_frame; + QuicRstStreamFrame* rst_stream_frame; + QuicConnectionCloseFrame* connection_close_frame; + QuicGoAwayFrame* goaway_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; +}; + +struct NET_EXPORT_PRIVATE QuicPacketData { + std::string data; +}; + +class NET_EXPORT_PRIVATE QuicData { + public: + QuicData(const char* buffer, size_t length) + : buffer_(buffer), + length_(length), + owns_buffer_(false) {} + + QuicData(char* buffer, size_t length, bool owns_buffer) + : buffer_(buffer), + length_(length), + owns_buffer_(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, + QuicGuidLength guid_length, + bool includes_version, + QuicSequenceNumberLength sequence_number_length) { + return new QuicPacket(buffer, length, owns_buffer, guid_length, + includes_version, sequence_number_length, false); + } + + static QuicPacket* NewFecPacket( + char* buffer, + size_t length, + bool owns_buffer, + QuicGuidLength guid_length, + bool includes_version, + QuicSequenceNumberLength sequence_number_length) { + return new QuicPacket(buffer, length, owns_buffer, guid_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_; } + + bool includes_version() const { return includes_version_; } + + char* mutable_data() { return buffer_; } + + private: + QuicPacket(char* buffer, + size_t length, + bool owns_buffer, + QuicGuidLength guid_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), + guid_length_(guid_length), + includes_version_(includes_version), + sequence_number_length_(sequence_number_length) {} + + char* buffer_; + const bool is_fec_packet_; + const QuicGuidLength guid_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) + : QuicData(buffer, length) {} + + QuicEncryptedPacket(char* buffer, size_t length, bool owns_buffer) + : QuicData(buffer, length, owns_buffer) {} + + // 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_; } + + void set_encryption_level(EncryptionLevel level); + EncryptionLevel encryption_level() const { + return encryption_level_; + } + + private: + QuicFrames frames_; + EncryptionLevel encryption_level_; + // 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, + QuicPacket* packet, + QuicPacketEntropyHash entropy_hash, + RetransmittableFrames* retransmittable_frames) + : sequence_number(sequence_number), + packet(packet), + entropy_hash(entropy_hash), + retransmittable_frames(retransmittable_frames) {} + + QuicPacketSequenceNumber sequence_number; + QuicPacket* packet; + QuicPacketEntropyHash entropy_hash; + RetransmittableFrames* retransmittable_frames; +}; + +// A struct for functions which consume data payloads and fins. +// The first member of the pair indicates bytes consumed. +// The second member of the pair indicates if an incoming fin was consumed. +struct QuicConsumedData { + QuicConsumedData(size_t bytes_consumed, bool fin_consumed) + : bytes_consumed(bytes_consumed), + fin_consumed(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); + + size_t bytes_consumed; + bool fin_consumed; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_PROTOCOL_H_ diff --git a/chromium/net/quic/quic_protocol_test.cc b/chromium/net/quic/quic_protocol_test.cc new file mode 100644 index 00000000000..b073d859f5d --- /dev/null +++ b/chromium/net/quic/quic_protocol_test.cc @@ -0,0 +1,146 @@ +// 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, 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) { + ReceivedPacketInfo received_info; + received_info.largest_observed = 10u; + EXPECT_TRUE(IsAwaitingPacket(received_info, 11u)); + EXPECT_FALSE(IsAwaitingPacket(received_info, 1u)); + + received_info.missing_packets.insert(10); + EXPECT_TRUE(IsAwaitingPacket(received_info, 10u)); +} + +TEST(QuicProtocolTest, InsertMissingPacketsBetween) { + ReceivedPacketInfo received_info; + InsertMissingPacketsBetween(&received_info, 4u, 10u); + EXPECT_EQ(6u, received_info.missing_packets.size()); + + QuicPacketSequenceNumber i = 4; + for (SequenceNumberSet::iterator it = received_info.missing_packets.begin(); + it != received_info.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', '0', '7'), + QuicVersionToQuicTag(QUIC_VERSION_7)); + + // 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) { + const 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_7, + QuicTagToQuicVersion(MakeQuicTag('Q', '0', '0', '7'))); + + for (size_t i = 0; i < arraysize(kSupportedQuicVersions); ++i) { + const 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_7", + QuicVersionToString(QUIC_VERSION_7)); + EXPECT_EQ("QUIC_VERSION_UNSUPPORTED", + QuicVersionToString(QUIC_VERSION_UNSUPPORTED)); + + QuicVersion single_version[] = {QUIC_VERSION_7}; + EXPECT_EQ("QUIC_VERSION_7,", QuicVersionArrayToString(single_version, + arraysize(single_version))); + QuicVersion multiple_versions[] = {QUIC_VERSION_8, QUIC_VERSION_7}; + EXPECT_EQ("QUIC_VERSION_8,QUIC_VERSION_7,", + QuicVersionArrayToString(multiple_versions, + arraysize(multiple_versions))); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_received_packet_manager.cc b/chromium/net/quic/quic_received_packet_manager.cc new file mode 100644 index 00000000000..9a136db5202 --- /dev/null +++ b/chromium/net/quic/quic_received_packet_manager.cc @@ -0,0 +1,188 @@ +// 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 "base/logging.h" +#include "net/base/linked_hash_map.h" + +using std::make_pair; +using std::max; +using std::min; + +namespace net { + +QuicReceivedPacketManager::QuicReceivedPacketManager() + : packets_entropy_hash_(0), + largest_sequence_number_(0), + peer_largest_observed_packet_(0), + least_packet_awaited_by_peer_(1), + peer_least_packet_awaiting_ack_(0), + time_largest_observed_(QuicTime::Zero()) { + received_info_.largest_observed = 0; + received_info_.entropy_hash = 0; +} + +QuicReceivedPacketManager::~QuicReceivedPacketManager() {} + +void QuicReceivedPacketManager::RecordPacketReceived( + const QuicPacketHeader& header, QuicTime receipt_time) { + QuicPacketSequenceNumber sequence_number = header.packet_sequence_number; + DCHECK(IsAwaitingPacket(sequence_number)); + + InsertMissingPacketsBetween( + &received_info_, + max(received_info_.largest_observed + 1, peer_least_packet_awaiting_ack_), + header.packet_sequence_number); + + if (received_info_.largest_observed > header.packet_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"; + received_info_.missing_packets.erase(sequence_number); + } + if (header.packet_sequence_number > received_info_.largest_observed) { + received_info_.largest_observed = header.packet_sequence_number; + time_largest_observed_ = receipt_time; + } + RecordPacketEntropyHash(sequence_number, header.entropy_hash); +} + +bool QuicReceivedPacketManager::IsAwaitingPacket( + QuicPacketSequenceNumber sequence_number) { + return ::net::IsAwaitingPacket(received_info_, sequence_number); +} + +void QuicReceivedPacketManager::UpdateReceivedPacketInfo( + ReceivedPacketInfo* received_info, QuicTime approximate_now) { + *received_info = received_info_; + received_info->entropy_hash = EntropyHash(received_info_.largest_observed); + if (time_largest_observed_ == QuicTime::Zero()) { + // We have not received any new higher sequence numbers since we sent our + // last ACK. + received_info->delta_time_largest_observed = QuicTime::Delta::Infinite(); + } else { + received_info->delta_time_largest_observed = + approximate_now.Subtract(time_largest_observed_); + + time_largest_observed_ = QuicTime::Zero(); + } +} + +void QuicReceivedPacketManager::RecordPacketEntropyHash( + QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + if (sequence_number < largest_sequence_number_) { + DLOG(INFO) << "Ignoring received packet entropy for sequence_number:" + << sequence_number << " less than largest_peer_sequence_number:" + << largest_sequence_number_; + return; + } + packets_entropy_.insert(make_pair(sequence_number, entropy_hash)); + packets_entropy_hash_ ^= entropy_hash; + 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); +} + +QuicPacketEntropyHash QuicReceivedPacketManager::EntropyHash( + QuicPacketSequenceNumber sequence_number) const { + DCHECK_LE(sequence_number, received_info_.largest_observed); + DCHECK_GE(sequence_number, largest_sequence_number_); + if (sequence_number == received_info_.largest_observed) { + return packets_entropy_hash_; + } + + ReceivedEntropyMap::const_iterator it = + packets_entropy_.upper_bound(sequence_number); + // When this map is empty we should only query entropy for + // |largest_received_sequence_number_|. + // TODO(rtenneti): add support for LOG_IF_EVERY_N_SEC to chromium. + // LOG_IF_EVERY_N_SEC(WARNING, it != packets_entropy_.end(), 10) + LOG_IF(WARNING, it != packets_entropy_.end()) + << "largest_received: " << received_info_.largest_observed + << " sequence_number: " << sequence_number; + + // TODO(satyamshekhar): Make this O(1). + QuicPacketEntropyHash hash = packets_entropy_hash_; + for (; it != packets_entropy_.end(); ++it) { + hash ^= it->second; + } + return hash; +} + +void QuicReceivedPacketManager::RecalculateEntropyHash( + QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash) { + DCHECK_LE(peer_least_unacked, received_info_.largest_observed); + if (peer_least_unacked < largest_sequence_number_) { + DLOG(INFO) << "Ignoring received peer_least_unacked:" << peer_least_unacked + << " less than largest_peer_sequence_number:" + << largest_sequence_number_; + return; + } + largest_sequence_number_ = peer_least_unacked; + packets_entropy_hash_ = entropy_hash; + ReceivedEntropyMap::iterator it = + packets_entropy_.lower_bound(peer_least_unacked); + // TODO(satyamshekhar): Make this O(1). + for (; it != packets_entropy_.end(); ++it) { + packets_entropy_hash_ ^= it->second; + } + // Discard entropies before least unacked. + packets_entropy_.erase( + packets_entropy_.begin(), + packets_entropy_.lower_bound( + min(peer_least_unacked, received_info_.largest_observed))); +} + +void QuicReceivedPacketManager::UpdatePacketInformationReceivedByPeer( + const QuicAckFrame& incoming_ack) { + // ValidateAck should fail if largest_observed ever shrinks. + DCHECK_LE(peer_largest_observed_packet_, + incoming_ack.received_info.largest_observed); + peer_largest_observed_packet_ = incoming_ack.received_info.largest_observed; + + if (incoming_ack.received_info.missing_packets.empty()) { + least_packet_awaited_by_peer_ = peer_largest_observed_packet_ + 1; + } else { + least_packet_awaited_by_peer_ = + *(incoming_ack.received_info.missing_packets.begin()); + } +} + +bool QuicReceivedPacketManager::DontWaitForPacketsBefore( + QuicPacketSequenceNumber least_unacked) { + size_t missing_packets_count = received_info_.missing_packets.size(); + received_info_.missing_packets.erase( + received_info_.missing_packets.begin(), + received_info_.missing_packets.lower_bound(least_unacked)); + return missing_packets_count != received_info_.missing_packets.size(); +} + +void QuicReceivedPacketManager::UpdatePacketInformationSentByPeer( + const QuicAckFrame& incoming_ack) { + // ValidateAck() should fail if peer_least_packet_awaiting_ack_ shrinks. + DCHECK_LE(peer_least_packet_awaiting_ack_, + incoming_ack.sent_info.least_unacked); + if (incoming_ack.sent_info.least_unacked > peer_least_packet_awaiting_ack_) { + bool missed_packets = + DontWaitForPacketsBefore(incoming_ack.sent_info.least_unacked); + if (missed_packets || incoming_ack.sent_info.least_unacked > + received_info_.largest_observed + 1) { + 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. + RecalculateEntropyHash(incoming_ack.sent_info.least_unacked, + incoming_ack.sent_info.entropy_hash); + } + peer_least_packet_awaiting_ack_ = incoming_ack.sent_info.least_unacked; + } + DCHECK(received_info_.missing_packets.empty() || + *received_info_.missing_packets.begin() >= + peer_least_packet_awaiting_ack_); +} + +} // namespace net diff --git a/chromium/net/quic/quic_received_packet_manager.h b/chromium/net/quic/quic_received_packet_manager.h new file mode 100644 index 00000000000..a762ae804c7 --- /dev/null +++ b/chromium/net/quic/quic_received_packet_manager.h @@ -0,0 +1,124 @@ +// 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 "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +namespace test { +class QuicReceivedPacketManagerPeer; +} // namespace test + +// 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: + QuicReceivedPacketManager(); + virtual ~QuicReceivedPacketManager(); + + // Updates the internal state concerning which packets have been acked. + void RecordPacketReceived(const QuicPacketHeader& header, + QuicTime receipt_time); + + // Checks if we're still waiting for the packet with |sequence_number|. + bool IsAwaitingPacket(QuicPacketSequenceNumber sequence_number); + + // Update the |received_info| for an outgoing ack. + void UpdateReceivedPacketInfo(ReceivedPacketInfo* received_info, + QuicTime approximate_now); + + // 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; + + // These two are called by OnAckFrame. + // + // Updates internal state based on |incoming_ack.received_info|. + void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& incoming_ack); + // Updates internal state based on |incoming_ack.sent_info|. + void UpdatePacketInformationSentByPeer(const QuicAckFrame& incoming_ack); + + QuicPacketSequenceNumber peer_largest_observed_packet() { + return peer_largest_observed_packet_; + } + + QuicPacketSequenceNumber least_packet_awaited_by_peer() { + return least_packet_awaited_by_peer_; + } + + QuicPacketSequenceNumber peer_least_packet_awaiting_ack() { + return peer_least_packet_awaiting_ack_; + } + + private: + friend class test::QuicReceivedPacketManagerPeer; + + typedef std::map<QuicPacketSequenceNumber, + QuicPacketEntropyHash> ReceivedEntropyMap; + + // Record the received entropy hash against |sequence_number|. + void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash); + + // Recalculate the entropy hash and clears old packet entropies, + // now that the sender sent us the |entropy_hash| for packets up to, + // but not including, |peer_least_unacked|. + void RecalculateEntropyHash(QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash); + + // 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); + + // TODO(satyamshekhar): Can be optimized using an interval set like data + // structure. + // Map of received sequence numbers to their corresponding entropy. + // Every received packet has an entry, and packets without the entropy bit set + // have an entropy value of 0. + // TODO(ianswett): When the entropy flag is off, the entropy should not be 0. + ReceivedEntropyMap packets_entropy_; + + // Cumulative hash of entropy of all received packets. + QuicPacketEntropyHash packets_entropy_hash_; + + // The largest sequence number cleared by RecalculateEntropyHash. + // Received entropy cannot be calculated for numbers less than it. + QuicPacketSequenceNumber largest_sequence_number_; + + + // Track some peer state so we can do less bookkeeping. + // Largest sequence number that the peer has observed. Mostly received, + // missing in case of truncated acks. + QuicPacketSequenceNumber peer_largest_observed_packet_; + // Least sequence number which the peer is still waiting for. + QuicPacketSequenceNumber least_packet_awaited_by_peer_; + // 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. + ReceivedPacketInfo received_info_; + + // 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_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_RECEIVED_PACKET_MANAGER_H_ diff --git a/chromium/net/quic/quic_received_packet_manager_test.cc b/chromium/net/quic/quic_received_packet_manager_test.cc new file mode 100644 index 00000000000..76be470c265 --- /dev/null +++ b/chromium/net/quic/quic_received_packet_manager_test.cc @@ -0,0 +1,119 @@ +// 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/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 { +namespace { + +class QuicReceivedPacketManagerTest : public ::testing::Test { + protected: + void RecordPacketEntropyHash(QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + QuicPacketHeader header; + header.packet_sequence_number = sequence_number; + header.entropy_hash = entropy_hash; + received_manager_.RecordPacketReceived(header, QuicTime::Zero());; + } + + 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) { + RecordPacketEntropyHash(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; + } + EXPECT_EQ(hash, received_manager_.EntropyHash(i)); + } +} + +TEST_F(QuicReceivedPacketManagerTest, EntropyHashBelowLeastObserved) { + EXPECT_EQ(0, received_manager_.EntropyHash(0)); + RecordPacketEntropyHash(4, 5); + EXPECT_EQ(0, received_manager_.EntropyHash(3)); +} + +TEST_F(QuicReceivedPacketManagerTest, EntropyHashAboveLargestObserved) { + EXPECT_EQ(0, received_manager_.EntropyHash(0)); + RecordPacketEntropyHash(4, 5); + EXPECT_EQ(0, received_manager_.EntropyHash(3)); +} + +TEST_F(QuicReceivedPacketManagerTest, RecalculateEntropyHash) { + 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(5, 34)); + entropies.push_back(make_pair(6, 29)); + + QuicPacketEntropyHash entropy_hash = 0; + for (size_t i = 0; i < entropies.size(); ++i) { + RecordPacketEntropyHash(entropies[i].first, entropies[i].second); + entropy_hash ^= entropies[i].second; + } + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + + // Now set the entropy hash up to 4 to be 100. + entropy_hash ^= 100; + for (size_t i = 0; i < 3; ++i) { + entropy_hash ^= entropies[i].second; + } + QuicReceivedPacketManagerPeer::RecalculateEntropyHash( + &received_manager_, 4, 100); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); + + QuicReceivedPacketManagerPeer::RecalculateEntropyHash( + &received_manager_, 1, 50); + EXPECT_EQ(entropy_hash, received_manager_.EntropyHash(6)); +} + +TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) { + QuicPacketHeader header; + header.packet_sequence_number = 2u; + received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + header.packet_sequence_number = 7u; + received_manager_.RecordPacketReceived(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)); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_reliable_client_stream.cc b/chromium/net/quic/quic_reliable_client_stream.cc new file mode 100644 index 00000000000..1951ae85a55 --- /dev/null +++ b/chromium/net/quic/quic_reliable_client_stream.cc @@ -0,0 +1,62 @@ +// 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/quic/quic_session.h" + +namespace net { + +QuicReliableClientStream::QuicReliableClientStream(QuicStreamId id, + QuicSession* session, + const BoundNetLog& net_log) + : ReliableQuicStream(id, session), + net_log_(net_log), + delegate_(NULL) { +} + +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_) + return ERR_ABORTED; + + int rv = delegate_->OnDataReceived(data, data_len); + if (rv != OK) { + DLOG(ERROR) << "Delegate refused data, rv: " << rv; + Close(QUIC_BAD_APPLICATION_PAYLOAD); + return 0; + } + return data_len; +} + +void QuicReliableClientStream::TerminateFromPeer(bool half_close) { + if (delegate_) { + delegate_->OnClose(connection_error()); + delegate_ = NULL; + } + ReliableQuicStream::TerminateFromPeer(half_close); +} + +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_ = NULL; + delegate->OnError(error); + } +} + +} // namespace net diff --git a/chromium/net/quic/quic_reliable_client_stream.h b/chromium/net/quic/quic_reliable_client_stream.h new file mode 100644 index 00000000000..77ac787f10d --- /dev/null +++ b/chromium/net/quic/quic_reliable_client_stream.h @@ -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. +// +// 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/reliable_quic_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 ReliableQuicStream { + public: + // Delegate handles protocol specific behavior of a quic stream. + class NET_EXPORT_PRIVATE Delegate { + public: + Delegate() {} + + // Called when stream is ready to send data. + // Returns network error code. OK when it successfully sent data. + // ERR_IO_PENDING when performing operation asynchronously. + virtual int OnSendData() = 0; + + // Called when data has been sent. |status| indicates network error + // or number of bytes that has been sent. On return, |eof| is set to true + // if no more data is available to send. + // Returns network error code. OK when it successfully sent data. + virtual int OnSendDataComplete(int status, bool* eof) = 0; + + // 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; + + protected: + virtual ~Delegate() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Delegate); + }; + + QuicReliableClientStream(QuicStreamId id, + QuicSession* session, + const BoundNetLog& net_log); + + virtual ~QuicReliableClientStream(); + + // ReliableQuicStream + virtual uint32 ProcessData(const char* data, uint32 data_len) OVERRIDE; + virtual void TerminateFromPeer(bool half_close) OVERRIDE; + using ReliableQuicStream::WriteData; + + // 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); + + const BoundNetLog& net_log() const { return net_log_; } + + private: + BoundNetLog net_log_; + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(QuicReliableClientStream); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_RELIABLE_CLIENT_STREAM_H_ diff --git a/chromium/net/quic/quic_reliable_client_stream_test.cc b/chromium/net/quic/quic_reliable_client_stream_test.cc new file mode 100644 index 00000000000..12402fd23eb --- /dev/null +++ b/chromium/net/quic/quic_reliable_client_stream_test.cc @@ -0,0 +1,82 @@ +// 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/quic/quic_client_session.h" +#include "net/quic/quic_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::Return; +using testing::StrEq; + +namespace net { +namespace test { +namespace { + +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)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockDelegate); +}; + +class QuicReliableClientStreamTest : public ::testing::Test { + public: + QuicReliableClientStreamTest() + : session_(new MockConnection(1, IPEndPoint(), false), false), + stream_(1, &session_, BoundNetLog()) { + stream_.SetDelegate(&delegate_); + } + + testing::StrictMock<MockDelegate> delegate_; + MockSession session_; + QuicReliableClientStream stream_; + QuicCryptoClientConfig crypto_config_; +}; + +TEST_F(QuicReliableClientStreamTest, TerminateFromPeer) { + EXPECT_CALL(delegate_, OnClose(QUIC_NO_ERROR)); + + stream_.TerminateFromPeer(true); +} + +TEST_F(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_F(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_F(QuicReliableClientStreamTest, OnError) { + EXPECT_CALL(delegate_, OnError(ERR_INTERNET_DISCONNECTED)); + + stream_.OnError(ERR_INTERNET_DISCONNECTED); + EXPECT_FALSE(stream_.GetDelegate()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_sent_entropy_manager.cc b/chromium/net/quic/quic_sent_entropy_manager.cc new file mode 100644 index 00000000000..0f33e2d56e9 --- /dev/null +++ b/chromium/net/quic/quic_sent_entropy_manager.cc @@ -0,0 +1,87 @@ +// 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() + : packets_entropy_hash_(0) {} + +QuicSentEntropyManager::~QuicSentEntropyManager() {} + +void QuicSentEntropyManager::RecordPacketEntropyHash( + QuicPacketSequenceNumber sequence_number, + QuicPacketEntropyHash entropy_hash) { + // TODO(satyamshekhar): Check this logic again when/if we enable packet + // reordering. + packets_entropy_hash_ ^= entropy_hash; + packets_entropy_.insert( + make_pair(sequence_number, + make_pair(entropy_hash, packets_entropy_hash_))); + DVLOG(2) << "setting cumulative sent entropy hash to: " + << static_cast<int>(packets_entropy_hash_) + << " updated with sequence number " << sequence_number + << " entropy hash: " << static_cast<int>(entropy_hash); +} + +QuicPacketEntropyHash QuicSentEntropyManager::EntropyHash( + QuicPacketSequenceNumber sequence_number) const { + SentEntropyMap::const_iterator it = + packets_entropy_.find(sequence_number); + if (it == packets_entropy_.end()) { + // Should only happen when we have not received ack for any packet. + DCHECK_EQ(0u, sequence_number); + return 0; + } + return it->second.second; +} + +bool QuicSentEntropyManager::IsValidEntropy( + QuicPacketSequenceNumber sequence_number, + const SequenceNumberSet& missing_packets, + QuicPacketEntropyHash entropy_hash) const { + SentEntropyMap::const_iterator entropy_it = + packets_entropy_.find(sequence_number); + if (entropy_it == packets_entropy_.end()) { + DCHECK_EQ(0u, sequence_number); + // Close connection if something goes wrong. + return 0 == sequence_number; + } + QuicPacketEntropyHash expected_entropy_hash = entropy_it->second.second; + for (SequenceNumberSet::const_iterator it = missing_packets.begin(); + it != missing_packets.end(); ++it) { + entropy_it = packets_entropy_.find(*it); + DCHECK(entropy_it != packets_entropy_.end()); + expected_entropy_hash ^= entropy_it->second.first; + } + 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) { + if (packets_entropy_.empty()) { + return; + } + SentEntropyMap::iterator it = packets_entropy_.begin(); + while (it->first < sequence_number) { + packets_entropy_.erase(it); + it = packets_entropy_.begin(); + DCHECK(it != packets_entropy_.end()); + } + DVLOG(2) << "Cleared entropy before: " + << packets_entropy_.begin()->first; +} + +} // namespace net diff --git a/chromium/net/quic/quic_sent_entropy_manager.h b/chromium/net/quic/quic_sent_entropy_manager.h new file mode 100644 index 00000000000..4f684fc9a73 --- /dev/null +++ b/chromium/net/quic/quic_sent_entropy_manager.h @@ -0,0 +1,62 @@ +// 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 "net/base/linked_hash_map.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +// 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); + + QuicPacketEntropyHash EntropyHash( + QuicPacketSequenceNumber sequence_number) const; + + // Returns true if |entropy_hash| matches the expected sent entropy hash + // up to |sequence_number| removing sequence numbers from |missing_packets|. + bool IsValidEntropy(QuicPacketSequenceNumber sequence_number, + const SequenceNumberSet& missing_packets, + QuicPacketEntropyHash entropy_hash) const; + + // Removes not required entries from |packets_entropy_| before + // |sequence_number|. + void ClearEntropyBefore(QuicPacketSequenceNumber sequence_number); + + QuicPacketEntropyHash packets_entropy_hash() const { + return packets_entropy_hash_; + } + + private: + typedef linked_hash_map<QuicPacketSequenceNumber, + std::pair<QuicPacketEntropyHash, + QuicPacketEntropyHash> > SentEntropyMap; + + // Linked hash map from sequence numbers to the sent entropy hash up to the + // sequence number in the key. + SentEntropyMap packets_entropy_; + + // Cumulative hash of entropy of all sent packets. + QuicPacketEntropyHash packets_entropy_hash_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SENT_ENTROPY_MANAGER_H_ diff --git a/chromium/net/quic/quic_sent_entropy_manager_test.cc b/chromium/net/quic/quic_sent_entropy_manager_test.cc new file mode 100644 index 00000000000..e4e9847b516 --- /dev/null +++ b/chromium/net/quic/quic_sent_entropy_manager_test.cc @@ -0,0 +1,73 @@ +// 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_.EntropyHash(0)); + + 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)); + + for (size_t i = 0; i < entropies.size(); ++i) { + entropy_manager_.RecordPacketEntropyHash(entropies[i].first, + entropies[i].second); + } + + QuicPacketEntropyHash hash = 0; + for (size_t i = 0; i < entropies.size(); ++i) { + hash ^= entropies[i].second; + EXPECT_EQ(hash, entropy_manager_.EntropyHash(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 < 10; ++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 < 10; ++i) { + if (missing_packets.find(i + 1) == missing_packets.end()) { + entropy_hash ^= entropies[i]; + } + } + + EXPECT_TRUE(entropy_manager_.IsValidEntropy(10, missing_packets, + entropy_hash)); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_session.cc b/chromium/net/quic/quic_session.cc new file mode 100644 index 00000000000..5abf30593ed --- /dev/null +++ b/chromium/net/quic/quic_session.cc @@ -0,0 +1,414 @@ +// 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/ssl/ssl_info.h" + +using base::StringPiece; +using base::hash_map; +using base::hash_set; +using std::make_pair; +using std::vector; + +namespace net { + +const size_t kMaxPrematurelyClosedStreamsTracked = 20; + +#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 bool OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const vector<QuicStreamFrame>& frame) OVERRIDE { + bool accepted = session_->OnPacket(self_address, peer_address, header, + frame); + session_->PostProcessAfterData(); + return accepted; + } + 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 OnAck(const SequenceNumberSet& acked_packets) OVERRIDE { + session_->OnAck(acked_packets); + session_->PostProcessAfterData(); + } + + virtual bool OnCanWrite() OVERRIDE { + bool rc = session_->OnCanWrite(); + session_->PostProcessAfterData(); + return rc; + } + + virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE { + session_->ConnectionClose(error, from_peer); + // The session will go away, so don't bother with cleanup. + } + + private: + QuicSession* session_; +}; + +QuicSession::QuicSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server) + : connection_(connection), + visitor_shim_(new VisitorShim(this)), + config_(config), + max_open_streams_(config_.max_streams_per_connection()), + next_stream_id_(is_server ? 2 : 3), + is_server_(is_server), + largest_peer_created_stream_id_(0), + error_(QUIC_NO_ERROR), + goaway_received_(false), + goaway_sent_(false) { + + connection_->set_visitor(visitor_shim_.get()); + connection_->SetIdleNetworkTimeout(config_.idle_connection_state_lifetime()); + if (connection_->connected()) { + connection_->SetOverallConnectionTimeout( + config_.max_time_before_crypto_handshake()); + } + // TODO(satyamshekhar): Set congestion control and ICSL also. +} + +QuicSession::~QuicSession() { + STLDeleteElements(&closed_streams_); + STLDeleteValues(&stream_map_); +} + +bool QuicSession::OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const vector<QuicStreamFrame>& frames) { + if (header.public_header.guid != connection()->guid()) { + DLOG(INFO) << ENDPOINT << "Got packet header for invalid GUID: " + << header.public_header.guid; + return false; + } + + for (size_t i = 0; i < frames.size(); ++i) { + // TODO(rch) deal with the error case of stream id 0 + if (IsClosedStream(frames[i].stream_id)) { + // If we get additional frames for a stream where we didn't process + // headers, it's highly likely our compression context will end up + // permanently out of sync with the peer's, so we give up and close the + // connection. + if (ContainsKey(prematurely_closed_streams_, frames[i].stream_id)) { + connection()->SendConnectionClose( + QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); + return false; + } + continue; + } + + ReliableQuicStream* stream = GetStream(frames[i].stream_id); + if (stream == NULL) return false; + if (!stream->WillAcceptStreamFrame(frames[i])) return false; + + // TODO(alyssar) check against existing connection address: if changed, make + // sure we update the connection. + } + + for (size_t i = 0; i < frames.size(); ++i) { + ReliableQuicStream* stream = GetStream(frames[i].stream_id); + if (stream) { + stream->OnStreamFrame(frames[i]); + } + } + + while (!decompression_blocked_streams_.empty()) { + QuicHeaderId header_id = decompression_blocked_streams_.begin()->first; + if (header_id != decompressor_.current_header_id()) { + break; + } + QuicStreamId stream_id = decompression_blocked_streams_.begin()->second; + decompression_blocked_streams_.erase(header_id); + ReliableQuicStream* stream = GetStream(stream_id); + if (!stream) { + connection()->SendConnectionClose( + QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); + return false; + } + stream->OnDecompressorAvailable(); + } + return true; +} + +void QuicSession::OnRstStream(const QuicRstStreamFrame& frame) { + ReliableQuicStream* stream = GetStream(frame.stream_id); + if (!stream) { + return; // Errors are handled by GetStream. + } + stream->OnStreamReset(frame.error_code); +} + +void QuicSession::OnGoAway(const QuicGoAwayFrame& frame) { + DCHECK(frame.last_good_stream_id < next_stream_id_); + goaway_received_ = true; +} + +void QuicSession::ConnectionClose(QuicErrorCode error, bool from_peer) { + if (error_ == QUIC_NO_ERROR) { + error_ = error; + } + + while (stream_map_.size() != 0) { + ReliableStreamMap::iterator it = stream_map_.begin(); + QuicStreamId id = it->first; + it->second->ConnectionClose(error, from_peer); + // The stream should call CloseStream as part of ConnectionClose. + if (stream_map_.find(id) != stream_map_.end()) { + LOG(DFATAL) << ENDPOINT << "Stream failed to close under ConnectionClose"; + CloseStream(id); + } + } +} + +bool QuicSession::OnCanWrite() { + // We latch this here rather than doing a traditional loop, because streams + // may be modifying the list as we loop. + int remaining_writes = write_blocked_streams_.NumObjects(); + + while (!connection_->HasQueuedData() && + remaining_writes > 0) { + DCHECK(!write_blocked_streams_.IsEmpty()); + ReliableQuicStream* stream = + GetStream(write_blocked_streams_.GetNextBlockedObject()); + if (stream != NULL) { + // If the stream can't write all bytes, it'll re-add itself to the blocked + // list. + stream->OnCanWrite(); + } + --remaining_writes; + } + + return write_blocked_streams_.IsEmpty(); +} + +QuicConsumedData QuicSession::WriteData(QuicStreamId id, + StringPiece data, + QuicStreamOffset offset, + bool fin) { + return connection_->SendStreamData(id, data, offset, fin); +} + +void QuicSession::SendRstStream(QuicStreamId id, + QuicRstStreamErrorCode error) { + connection_->SendRstStream(id, error); + CloseStream(id); +} + +void QuicSession::SendGoAway(QuicErrorCode error_code, const string& reason) { + goaway_sent_ = true; + connection_->SendGoAway(error_code, largest_peer_created_stream_id_, reason); +} + +void QuicSession::CloseStream(QuicStreamId stream_id) { + DLOG(INFO) << ENDPOINT << "Closing stream " << stream_id; + + ReliableStreamMap::iterator it = stream_map_.find(stream_id); + if (it == stream_map_.end()) { + DLOG(INFO) << ENDPOINT << "Stream is already closed: " << stream_id; + return; + } + ReliableQuicStream* stream = it->second; + if (!stream->headers_decompressed()) { + if (prematurely_closed_streams_.size() == + kMaxPrematurelyClosedStreamsTracked) { + prematurely_closed_streams_.erase(prematurely_closed_streams_.begin()); + } + prematurely_closed_streams_.insert(make_pair(stream->id(), true)); + } + closed_streams_.push_back(it->second); + stream_map_.erase(it); + stream->OnClose(); +} + +bool QuicSession::IsEncryptionEstablished() { + return GetCryptoStream()->encryption_established(); +} + +bool QuicSession::IsCryptoHandshakeConfirmed() { + return GetCryptoStream()->handshake_confirmed(); +} + +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( + QuicConnection::INITIAL_ENCRYPTION_ONLY); + break; + + case HANDSHAKE_CONFIRMED: + LOG_IF(DFATAL, !config_.negotiated()) << ENDPOINT + << "Handshake confirmed without parameter negotiation."; + connection_->SetIdleNetworkTimeout( + config_.idle_connection_state_lifetime()); + connection_->SetOverallConnectionTimeout(QuicTime::Delta::Infinite()); + max_open_streams_ = config_.max_streams_per_connection(); + 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(ReliableQuicStream* stream) { + DLOG(INFO) << ENDPOINT << "num_streams: " << stream_map_.size() + << ". activating " << stream->id(); + DCHECK(stream_map_.count(stream->id()) == 0); + 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(); + } + + ReliableStreamMap::iterator it = stream_map_.find(stream_id); + if (it != stream_map_.end()) { + return it->second; + } + + if (IsClosedStream(stream_id)) { + return NULL; + } + + 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. + connection()->SendConnectionClose(QUIC_PACKET_FOR_NONEXISTENT_STREAM); + return NULL; + } + + return GetIncomingReliableStream(stream_id); +} + +ReliableQuicStream* QuicSession::GetIncomingReliableStream( + QuicStreamId stream_id) { + if (IsClosedStream(stream_id)) { + return NULL; + } + + if (goaway_sent_) { + // We've already sent a GoAway + SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY); + return NULL; + } + + implicitly_created_streams_.erase(stream_id); + if (stream_id > largest_peer_created_stream_id_) { + // TODO(rch) add unit test for this + if (stream_id - largest_peer_created_stream_id_ > kMaxStreamIdDelta) { + connection()->SendConnectionClose(QUIC_INVALID_STREAM_ID); + return NULL; + } + if (largest_peer_created_stream_id_ != 0) { + 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; + } + ReliableQuicStream* stream = CreateIncomingReliableStream(stream_id); + if (stream == NULL) { + return NULL; + } + ActivateStream(stream); + return stream; +} + +bool QuicSession::IsClosedStream(QuicStreamId id) { + DCHECK_NE(0u, id); + if (id == kCryptoStreamId) { + return false; + } + if (stream_map_.count(id) != 0) { + // 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) { + write_blocked_streams_.AddBlockedObject(id); +} + +void QuicSession::MarkDecompressionBlocked(QuicHeaderId header_id, + QuicStreamId stream_id) { + decompression_blocked_streams_[header_id] = stream_id; +} + +bool QuicSession::GetSSLInfo(SSLInfo* ssl_info) { + NOTIMPLEMENTED(); + return false; +} + +void QuicSession::PostProcessAfterData() { + STLDeleteElements(&closed_streams_); + closed_streams_.clear(); +} + +} // namespace net diff --git a/chromium/net/quic/quic_session.h b/chromium/net/quic/quic_session.h new file mode 100644 index 00000000000..24a6deb45d1 --- /dev/null +++ b/chromium/net/quic/quic_session.h @@ -0,0 +1,266 @@ +// 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/base/linked_hash_map.h" +#include "net/quic/blocked_list.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_crypto_stream.h" +#include "net/quic/quic_packet_creator.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/quic_spdy_compressor.h" +#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/reliable_quic_stream.h" + +namespace net { + +class QuicCryptoStream; +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, + bool is_server); + + virtual ~QuicSession(); + + // QuicConnectionVisitorInterface methods: + virtual bool OnPacket(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const std::vector<QuicStreamFrame>& frame) OVERRIDE; + virtual void OnRstStream(const QuicRstStreamFrame& frame) OVERRIDE; + virtual void OnGoAway(const QuicGoAwayFrame& frame) OVERRIDE; + virtual void ConnectionClose(QuicErrorCode error, bool from_peer) OVERRIDE; + // Not needed for HTTP. + virtual void OnAck(const SequenceNumberSet& acked_packets) OVERRIDE {} + virtual bool OnCanWrite() OVERRIDE; + + // 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. + virtual QuicConsumedData WriteData(QuicStreamId id, + base::StringPiece data, + QuicStreamOffset offset, + bool fin); + // Called by streams when they want to close the stream in both directions. + virtual void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error); + + // 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 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(); + } + QuicGuid guid() const { return connection_->guid(); } + + QuicPacketCreator::Options* options() { return connection()->options(); } + + // Returns the number of currently open streams, including those which have + // been implicitly created. + virtual size_t GetNumOpenStreams() const; + + void MarkWriteBlocked(QuicStreamId id); + + // Marks that |stream_id| is blocked waiting to decompress the + // headers identified by |decompression_id|. + void MarkDecompressionBlocked(QuicHeaderId decompression_id, + QuicStreamId stream_id); + + bool goaway_received() const { + return goaway_received_; + } + + bool goaway_sent() const { + return goaway_sent_; + } + + QuicSpdyDecompressor* decompressor() { return &decompressor_; } + QuicSpdyCompressor* compressor() { return &compressor_; } + + // Gets the SSL connection information. + virtual bool GetSSLInfo(SSLInfo* ssl_info); + + QuicErrorCode error() const { return error_; } + + protected: + // Creates a new stream, owned by the caller, to handle a peer-initiated + // stream. Returns NULL and does error handling if the stream can not be + // created. + virtual ReliableQuicStream* CreateIncomingReliableStream(QuicStreamId id) = 0; + + // Create a new stream, owned by the caller, to handle a locally-initiated + // stream. Returns NULL if max streams have already been opened. + virtual ReliableQuicStream* CreateOutgoingReliableStream() = 0; + + // Return the reserved crypto stream. + virtual QuicCryptoStream* GetCryptoStream() = 0; + + // Adds 'stream' to the active stream map. + virtual void ActivateStream(ReliableQuicStream* stream); + + // Returns the stream id for a new stream. + QuicStreamId GetNextStreamId(); + + ReliableQuicStream* GetIncomingReliableStream(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, ReliableQuicStream*>* streams() { + return &stream_map_; + } + + const base::hash_map<QuicStreamId, ReliableQuicStream*>* streams() const { + return &stream_map_; + } + + std::vector<ReliableQuicStream*>* closed_streams() { + return &closed_streams_; + } + + size_t get_max_open_streams() const { + return max_open_streams_; + } + + private: + friend class test::QuicSessionPeer; + friend class VisitorShim; + + typedef base::hash_map<QuicStreamId, ReliableQuicStream*> ReliableStreamMap; + + scoped_ptr<QuicConnection> connection_; + + // Tracks the last 20 streams which closed without decompressing headers. + // This is for best-effort detection of an unrecoverable compression context. + // Ideally this would be a linked_hash_set as the boolean is unused. + linked_hash_map<QuicStreamId, bool> prematurely_closed_streams_; + + // A shim to stand between the connection and the session, to handle stream + // deletions. + scoped_ptr<VisitorShim> visitor_shim_; + + std::vector<ReliableQuicStream*> closed_streams_; + + QuicSpdyDecompressor decompressor_; + QuicSpdyCompressor compressor_; + + 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. + ReliableStreamMap stream_map_; + QuicStreamId next_stream_id_; + bool is_server_; + + // 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. + BlockedList<QuicStreamId> write_blocked_streams_; + + // A map of headers waiting to be compressed, and the streams + // they are associated with. + map<uint32, QuicStreamId> decompression_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_; + + DISALLOW_COPY_AND_ASSIGN(QuicSession); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SESSION_H_ diff --git a/chromium/net/quic/quic_session_test.cc b/chromium/net/quic/quic_session_test.cc new file mode 100644 index 00000000000..e417c436840 --- /dev/null +++ b/chromium/net/quic/quic_session_test.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/quic_session.h" + +#include <set> +#include <vector> + +#include "base/containers/hash_tables.h" +#include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_protocol.h" +#include "net/quic/test_tools/quic_connection_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "net/spdy/spdy_framer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::hash_map; +using std::set; +using std::vector; +using testing::_; +using testing::InSequence; + +namespace net { +namespace test { +namespace { + +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()->ToHandshakeMessage(&msg); + const QuicErrorCode error = session()->config()->ProcessClientHello( + msg, &error_details); + EXPECT_EQ(QUIC_NO_ERROR, error); + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + } +}; + +class TestStream : public ReliableQuicStream { + public: + TestStream(QuicStreamId id, QuicSession* session) + : ReliableQuicStream(id, session) { + } + + using ReliableQuicStream::CloseWriteSide; + + virtual uint32 ProcessData(const char* data, uint32 data_len) { + return data_len; + } + + MOCK_METHOD0(OnCanWrite, void()); +}; + +class TestSession : public QuicSession { + public: + TestSession(QuicConnection* connection, bool is_server) + : QuicSession(connection, DefaultQuicConfig(), is_server), + crypto_stream_(this) { + } + + virtual QuicCryptoStream* GetCryptoStream() OVERRIDE { + return &crypto_stream_; + } + + virtual TestStream* CreateOutgoingReliableStream() OVERRIDE { + TestStream* stream = new TestStream(GetNextStreamId(), this); + ActivateStream(stream); + return stream; + } + + virtual TestStream* CreateIncomingReliableStream(QuicStreamId id) OVERRIDE { + return new TestStream(id, this); + } + + bool IsClosedStream(QuicStreamId id) { + return QuicSession::IsClosedStream(id); + } + + ReliableQuicStream* GetIncomingReliableStream(QuicStreamId stream_id) { + return QuicSession::GetIncomingReliableStream(stream_id); + } + + // Helper method for gmock + void MarkTwoWriteBlocked() { + this->MarkWriteBlocked(2); + } + + TestCryptoStream crypto_stream_; +}; + +class QuicSessionTest : public ::testing::Test { + protected: + QuicSessionTest() + : guid_(1), + connection_(new MockConnection(guid_, IPEndPoint(), false)), + session_(connection_, true) { + } + + 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); + } + + QuicGuid guid_; + MockConnection* connection_; + TestSession session_; + QuicConnectionVisitorInterface* visitor_; + hash_map<QuicStreamId, ReliableQuicStream*>* streams_; + set<QuicStreamId> closed_streams_; +}; + +TEST_F(QuicSessionTest, IsCryptoHandshakeConfirmed) { + EXPECT_FALSE(session_.IsCryptoHandshakeConfirmed()); + CryptoHandshakeMessage message; + session_.crypto_stream_.OnHandshakeMessage(message); + EXPECT_TRUE(session_.IsCryptoHandshakeConfirmed()); +} + +TEST_F(QuicSessionTest, IsClosedStreamDefault) { + // Ensure that no streams are initially closed. + for (int i = kCryptoStreamId; i < 100; i++) { + EXPECT_FALSE(session_.IsClosedStream(i)); + } +} + +TEST_F(QuicSessionTest, IsClosedStreamLocallyCreated) { + TestStream* stream2 = session_.CreateOutgoingReliableStream(); + EXPECT_EQ(2u, stream2->id()); + TestStream* stream4 = session_.CreateOutgoingReliableStream(); + EXPECT_EQ(4u, stream4->id()); + + CheckClosedStreams(); + CloseStream(4); + CheckClosedStreams(); + CloseStream(2); + CheckClosedStreams(); +} + +TEST_F(QuicSessionTest, IsClosedStreamPeerCreated) { + session_.GetIncomingReliableStream(3); + session_.GetIncomingReliableStream(5); + + CheckClosedStreams(); + CloseStream(3); + CheckClosedStreams(); + CloseStream(5); + // Create stream id 9, and implicitly 7 + session_.GetIncomingReliableStream(9); + CheckClosedStreams(); + // Close 9, but make sure 7 is still not closed + CloseStream(9); + CheckClosedStreams(); +} + +TEST_F(QuicSessionTest, StreamIdTooLarge) { + session_.GetIncomingReliableStream(3); + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_STREAM_ID)); + session_.GetIncomingReliableStream(105); +} + +TEST_F(QuicSessionTest, DecompressionError) { + ReliableQuicStream* stream = session_.GetIncomingReliableStream(3); + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE)); + const char data[] = + "\1\0\0\0" // headers id + "\0\0\0\4" // length + "abcd"; // invalid compressed data + stream->ProcessRawData(data, arraysize(data)); +} + +TEST_F(QuicSessionTest, OnCanWrite) { + TestStream* stream2 = session_.CreateOutgoingReliableStream(); + TestStream* stream4 = session_.CreateOutgoingReliableStream(); + TestStream* stream6 = session_.CreateOutgoingReliableStream(); + + session_.MarkWriteBlocked(2); + session_.MarkWriteBlocked(6); + session_.MarkWriteBlocked(4); + + InSequence s; + EXPECT_CALL(*stream2, OnCanWrite()).WillOnce( + // Reregister, to test the loop limit. + testing::InvokeWithoutArgs(&session_, &TestSession::MarkTwoWriteBlocked)); + EXPECT_CALL(*stream6, OnCanWrite()); + EXPECT_CALL(*stream4, OnCanWrite()); + + EXPECT_FALSE(session_.OnCanWrite()); +} + +TEST_F(QuicSessionTest, OnCanWriteWithClosedStream) { + TestStream* stream2 = session_.CreateOutgoingReliableStream(); + TestStream* stream4 = session_.CreateOutgoingReliableStream(); + session_.CreateOutgoingReliableStream(); // stream 6 + + session_.MarkWriteBlocked(2); + session_.MarkWriteBlocked(6); + session_.MarkWriteBlocked(4); + CloseStream(6); + + InSequence s; + EXPECT_CALL(*stream2, OnCanWrite()); + EXPECT_CALL(*stream4, OnCanWrite()); + EXPECT_TRUE(session_.OnCanWrite()); +} + +// Regression test for http://crbug.com/248737 +TEST_F(QuicSessionTest, OutOfOrderHeaders) { + QuicSpdyCompressor compressor; + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "http"; + vector<QuicStreamFrame> frames; + QuicPacketHeader header; + header.public_header.guid = session_.guid(); + + TestStream* stream2 = session_.CreateOutgoingReliableStream(); + TestStream* stream4 = session_.CreateOutgoingReliableStream(); + stream2->CloseWriteSide(); + stream4->CloseWriteSide(); + + // Create frame with headers for stream2. + string compressed_headers1 = compressor.CompressHeaders(headers); + QuicStreamFrame frame1(stream2->id(), false, 0, compressed_headers1); + + // Create frame with headers for stream4. + string compressed_headers2 = compressor.CompressHeaders(headers); + QuicStreamFrame frame2(stream4->id(), true, 0, compressed_headers2); + + // Process the second frame first. This will cause the headers to + // be queued up and processed after the first frame is processed. + frames.push_back(frame2); + session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames); + + // Process the first frame, and un-cork the buffered headers. + frames[0] = frame1; + session_.OnPacket(IPEndPoint(), IPEndPoint(), header, frames); + + // Ensure that the streams actually close and we don't DCHECK. + session_.ConnectionClose(QUIC_CONNECTION_TIMED_OUT, true); +} + +TEST_F(QuicSessionTest, SendGoAway) { + // After sending a GoAway, ensure new incoming streams cannot be created and + // result in a RST being sent. + 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)); + EXPECT_FALSE(session_.GetIncomingReliableStream(3u)); +} + +TEST_F(QuicSessionTest, IncreasedTimeoutAfterCryptoHandshake) { + EXPECT_EQ(kDefaultInitialTimeoutSecs, + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); + CryptoHandshakeMessage msg; + session_.crypto_stream_.OnHandshakeMessage(msg); + EXPECT_EQ(kDefaultTimeoutSecs, + QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_spdy_compressor.cc b/chromium/net/quic/quic_spdy_compressor.cc new file mode 100644 index 00000000000..7efd45ce76f --- /dev/null +++ b/chromium/net/quic/quic_spdy_compressor.cc @@ -0,0 +1,50 @@ +// 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_spdy_compressor.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" + +using std::string; + +namespace net { + +QuicSpdyCompressor::QuicSpdyCompressor() + : spdy_framer_(SPDY3), + header_sequence_id_(1) { + spdy_framer_.set_enable_compression(true); +} + +QuicSpdyCompressor::~QuicSpdyCompressor() { +} + +string QuicSpdyCompressor::CompressHeaders( + const SpdyHeaderBlock& headers) { + // TODO(rch): Modify the SpdyFramer to expose a + // CreateCompressedHeaderBlock method, or some such. + SpdyStreamId stream_id = 3; // unused. + scoped_ptr<SpdyFrame> frame(spdy_framer_.CreateHeaders( + stream_id, CONTROL_FLAG_NONE, true, &headers)); + + // The size of the spdy HEADER frame's fixed prefix which + // needs to be stripped off from the resulting frame. + const size_t header_frame_prefix_len = 12; + string serialized = string(frame->data() + header_frame_prefix_len, + frame->size() - header_frame_prefix_len); + uint32 serialized_len = serialized.length(); + char id_str[sizeof(header_sequence_id_)]; + memcpy(&id_str, &header_sequence_id_, sizeof(header_sequence_id_)); + char len_str[sizeof(serialized_len)]; + memcpy(&len_str, &serialized_len, sizeof(serialized_len)); + string compressed; + compressed.reserve(arraysize(id_str) + arraysize(len_str) + serialized_len); + compressed.append(id_str, arraysize(id_str)); + compressed.append(len_str, arraysize(len_str)); + compressed.append(serialized); + ++header_sequence_id_; + return compressed; +} + +} // namespace net diff --git a/chromium/net/quic/quic_spdy_compressor.h b/chromium/net/quic/quic_spdy_compressor.h new file mode 100644 index 00000000000..c88c47eae7e --- /dev/null +++ b/chromium/net/quic/quic_spdy_compressor.h @@ -0,0 +1,38 @@ +// 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_SPDY_COMPRESSOR_H_ +#define NET_QUIC_QUIC_SPDY_COMPRESSOR_H_ + +#include <string> + +#include "net/base/net_export.h" +#include "net/quic/quic_protocol.h" +#include "net/spdy/spdy_framer.h" + +namespace net { + +// Handles the compression of request/response headers blocks. The +// serialized format is: +// uint32 - Header ID +// uint32 - Compressed header length +// ... - Compressed data +// +class NET_EXPORT_PRIVATE QuicSpdyCompressor { + public: + QuicSpdyCompressor(); + ~QuicSpdyCompressor(); + + std::string CompressHeaders(const SpdyHeaderBlock& headers); + + private: + SpdyFramer spdy_framer_; + QuicHeaderId header_sequence_id_; + + DISALLOW_COPY_AND_ASSIGN(QuicSpdyCompressor); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SPDY_COMPRESSOR_H_ diff --git a/chromium/net/quic/quic_spdy_compressor_test.cc b/chromium/net/quic/quic_spdy_compressor_test.cc new file mode 100644 index 00000000000..93066f29091 --- /dev/null +++ b/chromium/net/quic/quic_spdy_compressor_test.cc @@ -0,0 +1,46 @@ +// 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 <string> + +#include "net/quic/quic_spdy_compressor.h" +#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/spdy_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { +namespace { + +class QuicSpdyCompressorTest : public ::testing::Test { + protected: + TestDecompressorVisitor visitor_; +}; + +TEST_F(QuicSpdyCompressorTest, Compress) { + QuicSpdyCompressor compressor; + QuicSpdyDecompressor decompressor; + + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "https"; + + string compressed_headers = compressor.CompressHeaders(headers); + EXPECT_EQ('\1', compressed_headers[0]); + EXPECT_EQ('\0', compressed_headers[1]); + EXPECT_EQ('\0', compressed_headers[2]); + EXPECT_EQ('\0', compressed_headers[3]); + string compressed_data = compressed_headers.substr(4); + decompressor.DecompressData(compressed_data, &visitor_); + EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), + visitor_.data()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_spdy_decompressor.cc b/chromium/net/quic/quic_spdy_decompressor.cc new file mode 100644 index 00000000000..f8bd4c142a8 --- /dev/null +++ b/chromium/net/quic/quic_spdy_decompressor.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/quic_spdy_decompressor.h" + +#include <algorithm> + +#include "base/logging.h" + +using base::StringPiece; +using std::min; + +namespace net { + +class SpdyFramerVisitor : public SpdyFramerVisitorInterface { + public: + explicit SpdyFramerVisitor(QuicSpdyDecompressor::Visitor* visitor) + : visitor_(visitor), + error_(false) { + } + + virtual void OnError(SpdyFramer* framer) OVERRIDE { + error_ = true; + } + virtual void OnDataFrameHeader(SpdyStreamId stream_id, + size_t length, + bool fin) OVERRIDE {} + virtual void OnStreamFrameData(SpdyStreamId stream_id, + const char* data, + size_t len, + bool fin) OVERRIDE {} + virtual bool OnControlFrameHeaderData(SpdyStreamId stream_id, + const char* header_data, + size_t len) OVERRIDE; + virtual void OnSynStream(SpdyStreamId stream_id, + SpdyStreamId associated_stream_id, + SpdyPriority priority, + uint8 credential_slot, + bool fin, + bool unidirectional) OVERRIDE {} + virtual void OnSynReply(SpdyStreamId stream_id, bool fin) OVERRIDE {} + virtual void OnRstStream(SpdyStreamId stream_id, + SpdyRstStreamStatus status) OVERRIDE {} + virtual void OnSetting(SpdySettingsIds id, + uint8 flags, + uint32 value) OVERRIDE {} + virtual void OnPing(uint32 unique_id) OVERRIDE {} + virtual void OnGoAway(SpdyStreamId last_accepted_stream_id, + SpdyGoAwayStatus status) OVERRIDE {} + virtual void OnHeaders(SpdyStreamId stream_id, bool fin) OVERRIDE {} + virtual void OnWindowUpdate(SpdyStreamId stream_id, + uint32 delta_window_size) OVERRIDE {} + virtual bool OnCredentialFrameData(const char* credential_data, + size_t len) OVERRIDE { + return false; + } + virtual void OnPushPromise(SpdyStreamId stream_id, + SpdyStreamId promised_stream_id) OVERRIDE {} + void set_visitor(QuicSpdyDecompressor::Visitor* visitor) { + DCHECK(visitor); + visitor_ = visitor; + } + + private: + QuicSpdyDecompressor::Visitor* visitor_; + bool error_; +}; + +bool SpdyFramerVisitor::OnControlFrameHeaderData(SpdyStreamId stream_id, + const char* header_data, + size_t len) { + DCHECK(visitor_); + return visitor_->OnDecompressedData(StringPiece(header_data, len)); +} + +QuicSpdyDecompressor::QuicSpdyDecompressor() + : spdy_framer_(SPDY3), + spdy_visitor_(new SpdyFramerVisitor(NULL)), + current_header_id_(1), + has_current_compressed_size_(false), + current_compressed_size_(0), + compressed_bytes_consumed_(0) { + spdy_framer_.set_visitor(spdy_visitor_.get()); +} + +QuicSpdyDecompressor::~QuicSpdyDecompressor() { +} + +size_t QuicSpdyDecompressor::DecompressData(StringPiece data, + Visitor* visitor) { + spdy_visitor_->set_visitor(visitor); + size_t bytes_consumed = 0; + + if (!has_current_compressed_size_) { + const size_t kCompressedBufferSizeSize = sizeof(uint32); + DCHECK_GT(kCompressedBufferSizeSize, compressed_size_buffer_.length()); + size_t missing_size = + kCompressedBufferSizeSize - compressed_size_buffer_.length(); + if (data.length() < missing_size) { + data.AppendToString(&compressed_size_buffer_); + return data.length(); + } + bytes_consumed += missing_size; + data.substr(0, missing_size).AppendToString(&compressed_size_buffer_); + DCHECK_EQ(kCompressedBufferSizeSize, compressed_size_buffer_.length()); + memcpy(¤t_compressed_size_, compressed_size_buffer_.data(), + kCompressedBufferSizeSize); + compressed_size_buffer_.clear(); + has_current_compressed_size_ = true; + data = data.substr(missing_size); + compressed_bytes_consumed_ = 0; + } + + size_t bytes_to_consume = + min(current_compressed_size_ - compressed_bytes_consumed_, + static_cast<uint32>(data.length())); + if (bytes_to_consume > 0) { + if (!spdy_framer_.IncrementallyDecompressControlFrameHeaderData( + current_header_id_, data.data(), bytes_to_consume)) { + visitor->OnDecompressionError(); + return bytes_consumed; + } + compressed_bytes_consumed_ += bytes_to_consume; + bytes_consumed += bytes_to_consume; + } + if (current_compressed_size_ - compressed_bytes_consumed_ == 0) { + ResetForNextHeaders(); + } + return bytes_consumed; +} + +void QuicSpdyDecompressor::ResetForNextHeaders() { + has_current_compressed_size_ = false; + current_compressed_size_ = 0; + compressed_bytes_consumed_ = 0; + ++current_header_id_; +} + +} // namespace net diff --git a/chromium/net/quic/quic_spdy_decompressor.h b/chromium/net/quic/quic_spdy_decompressor.h new file mode 100644 index 00000000000..a56c4799015 --- /dev/null +++ b/chromium/net/quic/quic_spdy_decompressor.h @@ -0,0 +1,65 @@ +// 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_SPDY_DECOMPRESSOR_H_ +#define NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_ + +#include <string> + +#include "base/basictypes.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" +#include "net/spdy/spdy_framer.h" + +namespace net { + +class SpdyFramerVisitor; + +// Handles the compression of request/response headers blocks. +class NET_EXPORT_PRIVATE QuicSpdyDecompressor { + public: + // Interface that receives callbacks with decompressed data as it + // becomes available. + class NET_EXPORT_PRIVATE Visitor { + public: + virtual ~Visitor() {} + virtual bool OnDecompressedData(base::StringPiece data) = 0; + virtual void OnDecompressionError() = 0; + }; + + QuicSpdyDecompressor(); + ~QuicSpdyDecompressor(); + + // Decompresses the data in |data| and invokes |OnDecompressedData|, + // possibly multiple times, on |visitor|. Returns number of bytes + // consumed from |data|. + size_t DecompressData(base::StringPiece data, Visitor* visitor); + + QuicHeaderId current_header_id() { return current_header_id_; } + + private: + void ResetForNextHeaders(); + + SpdyFramer spdy_framer_; + scoped_ptr<SpdyFramerVisitor> spdy_visitor_; + // ID of the header currently being parsed. + QuicHeaderId current_header_id_; + // True when the size of the headers has been parsed. + bool has_current_compressed_size_; + // Size of the headers being parsed. + uint32 current_compressed_size_; + // Buffer into which the partial compressed size is written until + // it is fully parsed. + std::string compressed_size_buffer_; + // Number of compressed bytes consumed, out of the total in + // |current_compressed_size_|. + uint32 compressed_bytes_consumed_; + DISALLOW_COPY_AND_ASSIGN(QuicSpdyDecompressor); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_SPDY_DECOMPRESSOR_H_ diff --git a/chromium/net/quic/quic_spdy_decompressor_test.cc b/chromium/net/quic/quic_spdy_decompressor_test.cc new file mode 100644 index 00000000000..de9156bdfa4 --- /dev/null +++ b/chromium/net/quic/quic_spdy_decompressor_test.cc @@ -0,0 +1,103 @@ +// 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 <string> + +#include "net/quic/quic_spdy_compressor.h" +#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/spdy_utils.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace net { +namespace test { +namespace { + +class QuicSpdyDecompressorTest : public ::testing::Test { + protected: + QuicSpdyDecompressor decompressor_; + QuicSpdyCompressor compressor_; + TestDecompressorVisitor visitor_; +}; + +TEST_F(QuicSpdyDecompressorTest, Decompress) { + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "https"; + + EXPECT_EQ(1u, decompressor_.current_header_id()); + string compressed_headers = compressor_.CompressHeaders(headers).substr(4); + EXPECT_EQ(compressed_headers.length(), + decompressor_.DecompressData(compressed_headers, &visitor_)); + + EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data()); + EXPECT_EQ(2u, decompressor_.current_header_id()); +} + +TEST_F(QuicSpdyDecompressorTest, DecompressPartial) { + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "https"; + string compressed_headers = compressor_.CompressHeaders(headers).substr(4); + + for (size_t i = 0; i < compressed_headers.length(); ++i) { + QuicSpdyDecompressor decompressor; + TestDecompressorVisitor visitor; + + EXPECT_EQ(1u, decompressor.current_header_id()); + + string partial_compressed_headers = compressed_headers.substr(0, i); + EXPECT_EQ(partial_compressed_headers.length(), + decompressor.DecompressData(partial_compressed_headers, + &visitor)); + EXPECT_EQ(1u, decompressor.current_header_id()) << "i: " << i; + + string remaining_compressed_headers = + compressed_headers.substr(partial_compressed_headers.length()); + EXPECT_EQ(remaining_compressed_headers.length(), + decompressor.DecompressData(remaining_compressed_headers, + &visitor)); + EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor.data()); + + EXPECT_EQ(2u, decompressor.current_header_id()); + } +} + +TEST_F(QuicSpdyDecompressorTest, DecompressAndIgnoreTrailingData) { + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "https"; + + string compressed_headers = compressor_.CompressHeaders(headers).substr(4); + EXPECT_EQ(compressed_headers.length(), + decompressor_.DecompressData(compressed_headers + "abc123", + &visitor_)); + + EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers), visitor_.data()); +} + +TEST_F(QuicSpdyDecompressorTest, DecompressError) { + SpdyHeaderBlock headers; + headers[":host"] = "www.google.com"; + headers[":path"] = "/index.hml"; + headers[":scheme"] = "https"; + + EXPECT_EQ(1u, decompressor_.current_header_id()); + string compressed_headers = compressor_.CompressHeaders(headers).substr(4); + compressed_headers[compressed_headers.length() - 1] ^= 0x01; + EXPECT_NE(compressed_headers.length(), + decompressor_.DecompressData(compressed_headers, &visitor_)); + + EXPECT_TRUE(visitor_.error()); + EXPECT_EQ("", visitor_.data()); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_stats.cc b/chromium/net/quic/quic_stats.cc new file mode 100644 index 00000000000..7404d927843 --- /dev/null +++ b/chromium/net/quic/quic_stats.cc @@ -0,0 +1,25 @@ +// 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_stats.h" + +namespace net { + +QuicConnectionStats::QuicConnectionStats() + : bytes_sent(0), + packets_sent(0), + bytes_received(0), + packets_received(0), + bytes_retransmitted(0), + packets_retransmitted(0), + packets_revived(0), + packets_dropped(0), + rto_count(0), + rtt(0), + estimated_bandwidth(0) { +} + +QuicConnectionStats::~QuicConnectionStats() {} + +} // namespace net diff --git a/chromium/net/quic/quic_stats.h b/chromium/net/quic/quic_stats.h new file mode 100644 index 00000000000..252791e80de --- /dev/null +++ b/chromium/net/quic/quic_stats.h @@ -0,0 +1,50 @@ +// 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_STATS_H_ +#define NET_QUIC_QUIC_STATS_H_ + +#include "base/basictypes.h" +#include "net/base/net_export.h" + +namespace net { +// TODO(satyamshekhar): Add more interesting stats: +// 1. (C/S)HLO retransmission count. +// 2. SHLO received to first stream packet processed time. +// 3. CHLO sent to SHLO received time. +// 4. Number of migrations. +// 5. Number of out of order packets. +// 6. Avg packet size. +// 7. Number of connections that require more that 1-RTT. +// 8. Avg number of streams / session. +// 9. Number of duplicates received. +// 10. Fraction of traffic sent/received that was not data (protocol overhead). +// 11. Fraction of data transferred that was padding. + +// Structure to hold stats for a QuicConnection. +struct NET_EXPORT_PRIVATE QuicConnectionStats { + QuicConnectionStats(); + ~QuicConnectionStats(); + + uint64 bytes_sent; // includes retransmissions, fec. + uint32 packets_sent; + + uint64 bytes_received; // includes duplicate data for a stream, fec. + uint32 packets_received; // includes dropped packets + + uint64 bytes_retransmitted; + uint32 packets_retransmitted; + + uint32 packets_revived; + uint32 packets_dropped; // duplicate or less than least unacked. + uint32 rto_count; + + uint32 rtt; + uint64 estimated_bandwidth; + // TODO(satyamshekhar): Add window_size, mss and mtu. +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_STATS_H_ diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc new file mode 100644 index 00000000000..86bd8a18aaa --- /dev/null +++ b/chromium/net/quic/quic_stream_factory.cc @@ -0,0 +1,489 @@ +// 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/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/rand_util.h" +#include "base/stl_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/quic/crypto/proof_verifier_chromium.h" +#include "net/quic/crypto/quic_random.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_http_stream.h" +#include "net/quic/quic_protocol.h" +#include "net/socket/client_socket_factory.h" + +namespace net { + +// 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 HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const BoundNetLog& net_log); + + ~Job(); + + int Run(const CompletionCallback& callback); + + int DoLoop(int rv); + int DoResolveHost(); + int DoResolveHostComplete(int rv); + int DoConnect(); + int DoConnectComplete(int rv); + + void OnIOComplete(int rv); + + CompletionCallback callback() { + return callback_; + } + + const HostPortProxyPair& host_port_proxy_pair() const { + return host_port_proxy_pair_; + } + + private: + enum IoState { + STATE_NONE, + STATE_RESOLVE_HOST, + STATE_RESOLVE_HOST_COMPLETE, + STATE_CONNECT, + STATE_CONNECT_COMPLETE, + }; + IoState io_state_; + + QuicStreamFactory* factory_; + SingleRequestHostResolver host_resolver_; + const HostPortProxyPair host_port_proxy_pair_; + bool is_https_; + CertVerifier* cert_verifier_; + const BoundNetLog net_log_; + QuicClientSession* session_; + CompletionCallback callback_; + AddressList address_list_; + DISALLOW_COPY_AND_ASSIGN(Job); +}; + +QuicStreamFactory::Job::Job( + QuicStreamFactory* factory, + HostResolver* host_resolver, + const HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const BoundNetLog& net_log) + : factory_(factory), + host_resolver_(host_resolver), + host_port_proxy_pair_(host_port_proxy_pair), + is_https_(is_https), + cert_verifier_(cert_verifier), + net_log_(net_log) { +} + +QuicStreamFactory::Job::~Job() { +} + +int QuicStreamFactory::Job::Run(const CompletionCallback& callback) { + io_state_ = STATE_RESOLVE_HOST; + 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_CONNECT: + CHECK_EQ(OK, rv); + rv = DoConnect(); + 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() { + io_state_ = STATE_RESOLVE_HOST_COMPLETE; + return host_resolver_.Resolve( + HostResolver::RequestInfo(host_port_proxy_pair_.first), &address_list_, + base::Bind(&QuicStreamFactory::Job::OnIOComplete, + base::Unretained(this)), + net_log_); +} + +int QuicStreamFactory::Job::DoResolveHostComplete(int rv) { + if (rv != OK) + return rv; + + // TODO(rch): remove this code! + AddressList::iterator it = address_list_.begin(); + while (it != address_list_.end()) { + if (it->GetFamily() == ADDRESS_FAMILY_IPV6) { + it = address_list_.erase(it); + } else { + it++; + } + } + + DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_)); + io_state_ = STATE_CONNECT; + return OK; +} + +QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory) + : factory_(factory) {} + +QuicStreamRequest::~QuicStreamRequest() { + if (factory_ && !callback_.is_null()) + factory_->CancelRequest(this); +} + +int QuicStreamRequest::Request( + const HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const BoundNetLog& net_log, + const CompletionCallback& callback) { + DCHECK(!stream_); + DCHECK(callback_.is_null()); + int rv = factory_->Create(host_port_proxy_pair, is_https, cert_verifier, + net_log, this); + if (rv == ERR_IO_PENDING) { + host_port_proxy_pair_ = host_port_proxy_pair; + is_https_ = is_https; + cert_verifier_ = cert_verifier; + net_log_ = net_log; + callback_ = callback; + } else { + factory_ = NULL; + } + 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_ = NULL; + callback_.Run(rv); +} + +scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() { + DCHECK(stream_); + return stream_.Pass(); +} + +int QuicStreamFactory::Job::DoConnect() { + io_state_ = STATE_CONNECT_COMPLETE; + + session_ = factory_->CreateSession(host_port_proxy_pair_, is_https_, + cert_verifier_, address_list_, net_log_); + session_->StartReading(); + int rv = session_->CryptoConnect( + base::Bind(&QuicStreamFactory::Job::OnIOComplete, + base::Unretained(this))); + return rv; +} + +int QuicStreamFactory::Job::DoConnectComplete(int rv) { + if (rv != OK) + return rv; + + DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_)); + factory_->ActivateSession(host_port_proxy_pair_, session_); + + return OK; +} + +QuicStreamFactory::QuicStreamFactory( + HostResolver* host_resolver, + ClientSocketFactory* client_socket_factory, + QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory, + QuicRandom* random_generator, + QuicClock* clock) + : host_resolver_(host_resolver), + client_socket_factory_(client_socket_factory), + quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory), + random_generator_(random_generator), + clock_(clock), + weak_factory_(this) { + config_.SetDefaults(); + config_.set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(30), + QuicTime::Delta::FromSeconds(30)); +} + +QuicStreamFactory::~QuicStreamFactory() { + STLDeleteElements(&all_sessions_); + STLDeleteValues(&active_jobs_); + STLDeleteValues(&all_crypto_configs_); +} + +int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const BoundNetLog& net_log, + QuicStreamRequest* request) { + if (HasActiveSession(host_port_proxy_pair)) { + request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log)); + return OK; + } + + if (HasActiveJob(host_port_proxy_pair)) { + Job* job = active_jobs_[host_port_proxy_pair]; + active_requests_[request] = job; + job_requests_map_[job].insert(request); + return ERR_IO_PENDING; + } + + scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_proxy_pair, + is_https, cert_verifier, 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_[host_port_proxy_pair] = job.release(); + } + if (rv == OK) { + DCHECK(HasActiveSession(host_port_proxy_pair)); + request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log)); + } + return rv; +} + +void QuicStreamFactory::OnJobComplete(Job* job, int rv) { + if (rv == OK) { + // 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->host_port_proxy_pair())); + (*it)->set_stream(CreateIfSessionExists(job->host_port_proxy_pair(), + (*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->host_port_proxy_pair()); + job_requests_map_.erase(job); + delete job; + return; +} + +// Returns a newly created QuicHttpStream owned by the caller, if a +// matching session already exists. Returns NULL otherwise. +scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists( + const HostPortProxyPair& host_port_proxy_pair, + const BoundNetLog& net_log) { + if (!HasActiveSession(host_port_proxy_pair)) { + DLOG(INFO) << "No active session"; + return scoped_ptr<QuicHttpStream>(); + } + + QuicClientSession* session = active_sessions_[host_port_proxy_pair]; + DCHECK(session); + return scoped_ptr<QuicHttpStream>(new QuicHttpStream(session->GetWeakPtr())); +} + +void QuicStreamFactory::OnIdleSession(QuicClientSession* session) { +} + +void QuicStreamFactory::OnSessionClose(QuicClientSession* session) { + DCHECK_EQ(0u, session->GetNumOpenStreams()); + 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); + } + all_sessions_.erase(session); + session_aliases_.erase(session); + delete session; +} + +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())->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 HostPortProxyPair& pair = it->first; + const QuicClientSession* session = it->second; + + list->Append(session->GetInfoAsValue(pair.first)); + } + return list; +} + +void QuicStreamFactory::OnIPAddressChanged() { + CloseAllSessions(ERR_NETWORK_CHANGED); +} + +bool QuicStreamFactory::HasActiveSession( + const HostPortProxyPair& host_port_proxy_pair) { + return ContainsKey(active_sessions_, host_port_proxy_pair); +} + +QuicClientSession* QuicStreamFactory::CreateSession( + const HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const AddressList& address_list, + const BoundNetLog& net_log) { + QuicGuid guid = random_generator_->RandUint64(); + IPEndPoint addr = *address_list.begin(); + scoped_ptr<DatagramClientSocket> socket( + client_socket_factory_->CreateDatagramClientSocket( + DatagramSocket::DEFAULT_BIND, base::Bind(&base::RandInt), + net_log.net_log(), net_log.source())); + socket->Connect(addr); + + // We should adaptively set this buffer size, but for now, we'll use a size + // that is more than large enough for a 100 packet congestion 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(kMaxPacketSize * 100); // Support 100 packets. + socket->SetReceiveBufferSize(kSocketBufferSize); + // TODO(jar): What should the UDP send buffer be set to? If the send buffer + // is too large, then we might(?) wastefully queue packets in the OS, when + // we'd rather construct packets just in time. We do however expect that the + // calculated send rate (paced, or ack clocked), will be well below the egress + // rate of the local machine, so that *shouldn't* be a problem. + // If the buffer setting is too small, then we will starve our outgoing link + // on a fast connection, because we won't respond fast enough to the many + // async callbacks to get data from us. On the other hand, until we have real + // pacing support (beyond ack-clocked pacing), we get a bit of adhoc-pacing by + // requiring the application to refill this OS buffer (ensuring that we don't + // blast a pile of packets at the kernel's max egress rate). + // socket->SetSendBufferSize(????); + + QuicConnectionHelper* helper = new QuicConnectionHelper( + base::MessageLoop::current()->message_loop_proxy().get(), + clock_.get(), + random_generator_, + socket.get()); + + QuicConnection* connection = new QuicConnection(guid, addr, helper, false, + QuicVersionMax()); + + QuicCryptoClientConfig* crypto_config = + GetOrCreateCryptoConfig(host_port_proxy_pair); + DCHECK(crypto_config); + + QuicClientSession* session = + new QuicClientSession(connection, socket.Pass(), this, + quic_crypto_client_stream_factory_, + host_port_proxy_pair.first.host(), config_, + crypto_config, net_log.net_log()); + all_sessions_.insert(session); // owning pointer + if (is_https) { + crypto_config->SetProofVerifier( + new ProofVerifierChromium(cert_verifier, net_log)); + } + return session; +} + +bool QuicStreamFactory::HasActiveJob( + const HostPortProxyPair& host_port_proxy_pair) { + return ContainsKey(active_jobs_, host_port_proxy_pair); +} + +void QuicStreamFactory::ActivateSession( + const HostPortProxyPair& host_port_proxy_pair, + QuicClientSession* session) { + DCHECK(!HasActiveSession(host_port_proxy_pair)); + active_sessions_[host_port_proxy_pair] = session; + session_aliases_[session].insert(host_port_proxy_pair); +} + +QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig( + const HostPortProxyPair& host_port_proxy_pair) { + QuicCryptoClientConfig* crypto_config; + if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) { + crypto_config = all_crypto_configs_[host_port_proxy_pair]; + DCHECK(crypto_config); + } else { + crypto_config = new QuicCryptoClientConfig(); + crypto_config->SetDefaults(); + all_crypto_configs_[host_port_proxy_pair] = crypto_config; + } + return crypto_config; +} + +} // namespace net diff --git a/chromium/net/quic/quic_stream_factory.h b/chromium/net/quic/quic_stream_factory.h new file mode 100644 index 00000000000..963a60369ac --- /dev/null +++ b/chromium/net/quic/quic_stream_factory.h @@ -0,0 +1,183 @@ +// 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 <map> +#include <string> + +#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/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 ClientSocketFactory; +class HostResolver; +class QuicClock; +class QuicClientSession; +class QuicCryptoClientStreamFactory; +class QuicRandom; +class QuicStreamFactory; + +// 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 and |cert_verifier| can be null. + int Request(const HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + 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_; + HostPortProxyPair host_port_proxy_pair_; + bool is_https_; + CertVerifier* cert_verifier_; + 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: + QuicStreamFactory( + HostResolver* host_resolver, + ClientSocketFactory* client_socket_factory, + QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory, + QuicRandom* random_generator, + QuicClock* clock); + virtual ~QuicStreamFactory(); + + // Creates a new QuicHttpStream to |host_port_proxy_pair| which will be + // owned by |request|. |is_https| specifies if the protocol is https or not. + // |cert_verifier| is used by ProofVerifier for verifying the certificate + // chain and signature. For http, this can be null. 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 HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const BoundNetLog& net_log, + QuicStreamRequest* request); + + // Returns a newly created QuicHttpStream owned by the caller, if a + // matching session already exists. Returns NULL otherwise. + scoped_ptr<QuicHttpStream> CreateIfSessionExists( + const HostPortProxyPair& host_port_proxy_pair, + const BoundNetLog& net_log); + + // Called by a session when it becomes idle. + void OnIdleSession(QuicClientSession* session); + + // Called by a session after it shuts down. + void OnSessionClose(QuicClientSession* session); + + // Cancels a pending request. + void CancelRequest(QuicStreamRequest* request); + + // Closes all current sessions. + void CloseAllSessions(int error); + + base::Value* QuicStreamFactoryInfoToValue() const; + + // NetworkChangeNotifier::IPAddressObserver methods: + + // Until the servers support roaming, close all connections when the local + // IP address changes. + virtual void OnIPAddressChanged() OVERRIDE; + + private: + class Job; + + typedef std::map<HostPortProxyPair, QuicClientSession*> SessionMap; + typedef std::set<HostPortProxyPair> AliasSet; + typedef std::map<QuicClientSession*, AliasSet> SessionAliasMap; + typedef std::set<QuicClientSession*> SessionSet; + typedef std::map<HostPortProxyPair, QuicCryptoClientConfig*> CryptoConfigMap; + typedef std::map<HostPortProxyPair, Job*> JobMap; + typedef std::map<QuicStreamRequest*, Job*> RequestMap; + typedef std::set<QuicStreamRequest*> RequestSet; + typedef std::map<Job*, RequestSet> JobRequestsMap; + + void OnJobComplete(Job* job, int rv); + bool HasActiveSession(const HostPortProxyPair& host_port_proxy_pair); + bool HasActiveJob(const HostPortProxyPair& host_port_proxy_pair); + QuicClientSession* CreateSession( + const HostPortProxyPair& host_port_proxy_pair, + bool is_https, + CertVerifier* cert_verifier, + const AddressList& address_list, + const BoundNetLog& net_log); + void ActivateSession(const HostPortProxyPair& host_port_proxy_pair, + QuicClientSession* session); + + QuicCryptoClientConfig* GetOrCreateCryptoConfig( + const HostPortProxyPair& host_port_proxy_pair); + + HostResolver* host_resolver_; + ClientSocketFactory* client_socket_factory_; + QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_; + QuicRandom* random_generator_; + scoped_ptr<QuicClock> clock_; + + // Contains owning pointers to all sessions that currently exist. + SessionSet all_sessions_; + // Contains non-owning pointers to currently active session + // (not going away session, once they're implemented). + SessionMap active_sessions_; + SessionAliasMap session_aliases_; + + // Contains owning pointers to QuicCryptoClientConfig. QuicCryptoClientConfig + // contains configuration and cached state about servers. + // TODO(rtenneti): Persist all_crypto_configs_ to disk and decide when to + // clear the data in the map. + CryptoConfigMap all_crypto_configs_; + + QuicConfig config_; + + JobMap active_jobs_; + JobRequestsMap job_requests_map_; + RequestMap active_requests_; + + base::WeakPtrFactory<QuicStreamFactory> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuicStreamFactory); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_STREAM_FACTORY_H_ diff --git a/chromium/net/quic/quic_stream_factory_test.cc b/chromium/net/quic/quic_stream_factory_test.cc new file mode 100644 index 00000000000..2f46772d0fc --- /dev/null +++ b/chromium/net/quic/quic_stream_factory_test.cc @@ -0,0 +1,365 @@ +// 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/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/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/quic_encrypter.h" +#include "net/quic/quic_http_stream.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_utils.h" +#include "net/socket/socket_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class QuicStreamFactoryTest : public ::testing::Test { + protected: + QuicStreamFactoryTest() + : clock_(new MockClock()), + factory_(&host_resolver_, &socket_factory_, + &crypto_client_stream_factory_, + &random_generator_, clock_), + host_port_proxy_pair_(HostPortPair("www.google.com", 443), + ProxyServer::Direct()), + is_https_(false), + cert_verifier_(CertVerifier::CreateDefault()) { + } + + scoped_ptr<QuicEncryptedPacket> ConstructRstPacket( + QuicPacketSequenceNumber num, + QuicStreamId stream_id) { + QuicPacketHeader header; + header.public_header.guid = 0xDEADBEEF; + header.public_header.reset_flag = false; + header.public_header.version_flag = true; + header.packet_sequence_number = num; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicRstStreamFrame rst(stream_id, QUIC_STREAM_NO_ERROR); + return scoped_ptr<QuicEncryptedPacket>( + ConstructPacket(header, QuicFrame(&rst))); + } + + scoped_ptr<QuicEncryptedPacket> ConstructAckPacket( + QuicPacketSequenceNumber largest_received, + QuicPacketSequenceNumber least_unacked) { + QuicPacketHeader header; + header.public_header.guid = 0xDEADBEEF; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.packet_sequence_number = 2; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicAckFrame ack(largest_received, QuicTime::Zero(), least_unacked); + QuicCongestionFeedbackFrame feedback; + feedback.type = kTCP; + feedback.tcp.accumulated_number_of_lost_packets = 0; + feedback.tcp.receive_window = 16000; + + QuicFramer framer(QuicVersionMax(), QuicTime::Zero(), false); + QuicFrames frames; + frames.push_back(QuicFrame(&ack)); + frames.push_back(QuicFrame(&feedback)); + scoped_ptr<QuicPacket> packet( + framer.BuildUnsizedDataPacket(header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + } + + // Returns a newly created packet to send congestion feedback data. + scoped_ptr<QuicEncryptedPacket> ConstructFeedbackPacket( + QuicPacketSequenceNumber sequence_number) { + QuicPacketHeader header; + header.public_header.guid = 0xDEADBEEF; + header.public_header.reset_flag = false; + header.public_header.version_flag = false; + header.packet_sequence_number = sequence_number; + header.entropy_flag = false; + header.fec_flag = false; + header.fec_group = 0; + + QuicCongestionFeedbackFrame frame; + frame.type = kTCP; + frame.tcp.accumulated_number_of_lost_packets = 0; + frame.tcp.receive_window = 16000; + + return scoped_ptr<QuicEncryptedPacket>( + ConstructPacket(header, QuicFrame(&frame))); + } + + scoped_ptr<QuicEncryptedPacket> ConstructPacket( + const QuicPacketHeader& header, + const QuicFrame& frame) { + QuicFramer framer(QuicVersionMax(), QuicTime::Zero(), false); + QuicFrames frames; + frames.push_back(frame); + scoped_ptr<QuicPacket> packet( + framer.BuildUnsizedDataPacket(header, frames).packet); + return scoped_ptr<QuicEncryptedPacket>(framer.EncryptPacket( + ENCRYPTION_NONE, header.packet_sequence_number, *packet)); + } + + MockHostResolver host_resolver_; + DeterministicMockClientSocketFactory socket_factory_; + MockCryptoClientStreamFactory crypto_client_stream_factory_; + MockRandom random_generator_; + MockClock* clock_; // Owned by factory_. + QuicStreamFactory factory_; + HostPortProxyPair host_port_proxy_pair_; + bool is_https_; + scoped_ptr<CertVerifier> cert_verifier_; + BoundNetLog net_log_; + TestCompletionCallback callback_; +}; + +TEST_F(QuicStreamFactoryTest, CreateIfSessionExists) { + EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_, + net_log_).get()); +} + +TEST_F(QuicStreamFactoryTest, Create) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, + cert_verifier_.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 = factory_.CreateIfSessionExists(host_port_proxy_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_proxy_pair_, is_https_, + cert_verifier_.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_F(QuicStreamFactoryTest, MaxOpenStream) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + 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_proxy_pair_, is_https_, + cert_verifier_.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_proxy_pair_, is_https_, + cert_verifier_.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_F(QuicStreamFactoryTest, CreateError) { + DeterministicSocketData socket_data(NULL, 0, NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + + host_resolver_.rules()->AddSimulatedFailure("www.google.com"); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, + cert_verifier_.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_F(QuicStreamFactoryTest, CancelCreate) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + { + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, + cert_verifier_.get(), net_log_, + callback_.callback())); + } + + socket_data.StopAfter(1); + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + + scoped_ptr<QuicHttpStream> stream( + factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_)); + EXPECT_TRUE(stream.get()); + stream.reset(); + + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); +} + +TEST_F(QuicStreamFactoryTest, CloseAllSessions) { + MockRead reads[] = { + MockRead(ASYNC, 0, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + MockRead reads2[] = { + MockRead(ASYNC, 0, 0) // EOF + }; + DeterministicSocketData socket_data2(reads2, arraysize(reads2), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, + cert_verifier_.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_proxy_pair_, is_https_, + cert_verifier_.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_F(QuicStreamFactoryTest, OnIPAddressChanged) { + MockRead reads[] = { + MockRead(ASYNC, 0, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + MockRead reads2[] = { + MockRead(ASYNC, 0, 0) // EOF + }; + DeterministicSocketData socket_data2(reads2, arraysize(reads2), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data2.StopAfter(1); + + QuicStreamRequest request(&factory_); + EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_, + cert_verifier_.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())); + + // 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_proxy_pair_, is_https_, + cert_verifier_.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()); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_stream_sequencer.cc b/chromium/net/quic/quic_stream_sequencer.cc new file mode 100644 index 00000000000..7cf67d351e3 --- /dev/null +++ b/chromium/net/quic/quic_stream_sequencer.cc @@ -0,0 +1,279 @@ +// 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 "net/quic/reliable_quic_stream.h" + +using std::min; +using std::numeric_limits; + +namespace net { + +QuicStreamSequencer::QuicStreamSequencer(ReliableQuicStream* quic_stream) + : stream_(quic_stream), + num_bytes_consumed_(0), + max_frame_memory_(numeric_limits<size_t>::max()), + close_offset_(numeric_limits<QuicStreamOffset>::max()) { +} + +QuicStreamSequencer::QuicStreamSequencer(size_t max_frame_memory, + ReliableQuicStream* quic_stream) + : stream_(quic_stream), + num_bytes_consumed_(0), + max_frame_memory_(max_frame_memory), + close_offset_(numeric_limits<QuicStreamOffset>::max()) { + if (max_frame_memory < kMaxPacketSize) { + LOG(DFATAL) << "Setting max frame memory to " << max_frame_memory + << ". Some frames will be impossible to handle."; + } +} + +QuicStreamSequencer::~QuicStreamSequencer() { +} + +bool QuicStreamSequencer::WillAcceptStreamFrame( + const QuicStreamFrame& frame) const { + size_t data_len = frame.data.size(); + DCHECK_LE(data_len, max_frame_memory_); + + if (IsDuplicate(frame)) { + return true; + } + QuicStreamOffset byte_offset = frame.offset; + if (data_len > max_frame_memory_) { + // We're never going to buffer this frame and we can't pass it up. + // The stream might only consume part of it and we'd need a partial ack. + // + // Ideally this should never happen, as we check that + // max_frame_memory_ > kMaxPacketSize and lower levels should reject + // frames larger than that. + return false; + } + if (byte_offset + data_len - num_bytes_consumed_ > max_frame_memory_) { + // We can buffer this but not right now. Toss it. + // It might be worth trying an experiment where we try best-effort buffering + return false; + } + return true; +} + +bool QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame) { + if (!WillAcceptStreamFrame(frame)) { + // This should not happen, as WillAcceptFrame should be called before + // OnStreamFrame. Error handling should be done by the caller. + return false; + } + if (IsDuplicate(frame)) { + // Silently ignore duplicates. + return true; + } + + if (frame.fin) { + CloseStreamAtOffset(frame.offset + frame.data.size()); + } + + QuicStreamOffset byte_offset = frame.offset; + const char* data = frame.data.data(); + size_t data_len = frame.data.size(); + + if (data_len == 0) { + // TODO(rch): Close the stream if there was no data and no fin. + return true; + } + + if (byte_offset == num_bytes_consumed_) { + DVLOG(1) << "Processing byte offset " << byte_offset; + size_t bytes_consumed = stream_->ProcessRawData(data, data_len); + num_bytes_consumed_ += bytes_consumed; + + if (MaybeCloseStream()) { + return true; + } + if (bytes_consumed > data_len) { + stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM); + return false; + } else if (bytes_consumed == data_len) { + FlushBufferedFrames(); + return true; // it's safe to ack this frame. + } else { + // Set ourselves up to buffer what's left + data_len -= bytes_consumed; + data += bytes_consumed; + byte_offset += bytes_consumed; + } + } + DVLOG(1) << "Buffering packet at offset " << byte_offset; + frames_.insert(make_pair(byte_offset, string(data, data_len))); + return true; +} + +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_->Close(QUIC_MULTIPLE_TERMINATION_OFFSETS); + return; + } + + close_offset_ = offset; + + MaybeCloseStream(); +} + +bool QuicStreamSequencer::MaybeCloseStream() { + if (IsHalfClosed()) { + 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_->TerminateFromPeer(true); + return true; + } + return false; +} + +int QuicStreamSequencer::GetReadableRegions(iovec* iov, size_t iov_len) { + FrameMap::iterator it = frames_.begin(); + size_t index = 0; + QuicStreamOffset offset = num_bytes_consumed_; + while (it != 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) { + FrameMap::iterator it = 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 != 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 + num_bytes_consumed_ += it->second.size(); + frames_.erase(it); + it = frames_.begin(); + frame_offset = 0; + } + } + // We've finished copying. If we have a partial frame, update it. + if (frame_offset != 0) { + frames_.insert(make_pair(it->first + frame_offset, + it->second.substr(frame_offset))); + frames_.erase(frames_.begin()); + num_bytes_consumed_ += frame_offset; + } + return num_bytes_consumed_ - initial_bytes_consumed; +} + +void QuicStreamSequencer::MarkConsumed(size_t num_bytes_consumed) { + size_t end_offset = num_bytes_consumed_ + num_bytes_consumed; + while (!frames_.empty() && end_offset != num_bytes_consumed_) { + FrameMap::iterator it = frames_.begin(); + if (it->first != num_bytes_consumed_) { + LOG(DFATAL) << "Invalid argument to MarkConsumed. " + << " num_bytes_consumed_: " << num_bytes_consumed_ + << " end_offset: " << end_offset + << " offset: " << it->first + << " length: " << it->second.length(); + stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM); + return; + } + + if (it->first + it->second.length() <= end_offset) { + num_bytes_consumed_ += it->second.length(); + // This chunk is entirely consumed. + frames_.erase(it); + continue; + } + + // Partially consume this frame. + size_t delta = end_offset - it->first; + num_bytes_consumed_ += delta; + frames_.insert(make_pair(end_offset, it->second.substr(delta))); + frames_.erase(it); + break; + } +} + +bool QuicStreamSequencer::HasBytesToRead() const { + FrameMap::const_iterator it = frames_.begin(); + + return it != frames_.end() && it->first == num_bytes_consumed_; +} + +bool QuicStreamSequencer::IsHalfClosed() const { + return num_bytes_consumed_ >= close_offset_; +} + +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_ || + frames_.find(frame.offset) != frames_.end(); +} + +void QuicStreamSequencer::FlushBufferedFrames() { + FrameMap::iterator it = frames_.find(num_bytes_consumed_); + while (it != 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()); + num_bytes_consumed_ += bytes_consumed; + if (MaybeCloseStream()) { + return; + } + if (bytes_consumed > data->size()) { + stream_->Close(QUIC_SERVER_ERROR_PROCESSING_STREAM); // Programming error + return; + } else if (bytes_consumed == data->size()) { + frames_.erase(it); + it = frames_.find(num_bytes_consumed_); + } else { + string new_data = it->second.substr(bytes_consumed); + frames_.erase(it); + frames_.insert(make_pair(num_bytes_consumed_, new_data)); + return; + } + } +} + +} // namespace net diff --git a/chromium/net/quic/quic_stream_sequencer.h b/chromium/net/quic/quic_stream_sequencer.h new file mode 100644 index 00000000000..fe9fba56830 --- /dev/null +++ b/chromium/net/quic/quic_stream_sequencer.h @@ -0,0 +1,102 @@ +// 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 "base/memory/scoped_ptr.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: + static size_t kMaxUdpPacketSize; + + explicit QuicStreamSequencer(ReliableQuicStream* quic_stream); + QuicStreamSequencer(size_t max_frame_memory, + ReliableQuicStream* quic_stream); + + virtual ~QuicStreamSequencer(); + + // Returns the expected value of OnStreamFrame for this frame. + bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const; + + // 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 either be buffered, and + // this will return true, or it will be rejected and this will return false. + bool 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); + + // Consumes |num_bytes| data. Used in conjunction with |GetReadableRegions| + // to do zero-copy reads. + void MarkConsumed(size_t num_bytes); + + // Returns true if the sequncer has bytes available for reading. + bool HasBytesToRead() const; + + // Returns true if the sequencer has delivered a half close. + bool IsHalfClosed() const; + + // Returns true if the sequencer has received this frame before. + bool IsDuplicate(const QuicStreamFrame& frame) const; + + // Calls |ProcessRawData| on |stream_| for each buffered frame that may + // be processed. + void FlushBufferedFrames(); + + private: + friend class test::QuicStreamSequencerPeer; + + // TODO(alyssar) use something better than strings. + typedef map<QuicStreamOffset, string> FrameMap; + + // Wait until we've seen 'offset' bytes, and then terminate the stream. + void CloseStreamAtOffset(QuicStreamOffset offset); + + bool MaybeCloseStream(); + + ReliableQuicStream* stream_; // The stream which owns this sequencer. + QuicStreamOffset num_bytes_consumed_; // The last data consumed by the stream + FrameMap frames_; // sequence number -> frame + size_t max_frame_memory_; // the maximum memory the sequencer can buffer. + // The offset, if any, we got a stream termination for. When this many bytes + // have been processed, the stream will be half closed. + QuicStreamOffset close_offset_; +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_STREAM_SEQUENCER_H_ diff --git a/chromium/net/quic/quic_stream_sequencer_test.cc b/chromium/net/quic/quic_stream_sequencer_test.cc new file mode 100644 index 00000000000..0d40db92636 --- /dev/null +++ b/chromium/net/quic/quic_stream_sequencer_test.cc @@ -0,0 +1,577 @@ +// 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/rand_util.h" +#include "net/quic/reliable_quic_stream.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::StringPiece; +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 QuicStreamSequencerPeer : public QuicStreamSequencer { + public: + explicit QuicStreamSequencerPeer(ReliableQuicStream* stream) + : QuicStreamSequencer(stream) { + } + + QuicStreamSequencerPeer(int32 max_mem, ReliableQuicStream* stream) + : QuicStreamSequencer(max_mem, stream) { + } + + virtual bool OnFinFrame(QuicStreamOffset byte_offset, + const char* data) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.offset = byte_offset; + frame.data = StringPiece(data); + frame.fin = true; + return OnStreamFrame(frame); + } + + virtual bool OnFrame(QuicStreamOffset byte_offset, + const char* data) { + QuicStreamFrame frame; + frame.stream_id = 1; + frame.offset = byte_offset; + frame.data = StringPiece(data); + frame.fin = false; + return OnStreamFrame(frame); + } + + void SetMemoryLimit(size_t limit) { + max_frame_memory_ = limit; + } + + const ReliableQuicStream* stream() const { return stream_; } + uint64 num_bytes_consumed() const { return num_bytes_consumed_; } + const FrameMap* frames() const { return &frames_; } + int32 max_frame_memory() const { return max_frame_memory_; } + QuicStreamOffset close_offset() const { return close_offset_; } +}; + +class MockStream : public ReliableQuicStream { + public: + MockStream(QuicSession* session, QuicStreamId id) + : ReliableQuicStream(id, session) { + } + + MOCK_METHOD1(TerminateFromPeer, void(bool half_close)); + MOCK_METHOD2(ProcessData, uint32(const char* data, uint32 data_len)); + MOCK_METHOD1(Close, void(QuicRstStreamErrorCode error)); + MOCK_METHOD0(OnCanWrite, void()); +}; + +namespace { + +static const char kPayload[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +class QuicStreamSequencerTest : public ::testing::Test { + protected: + QuicStreamSequencerTest() + : session_(NULL), + stream_(session_, 1), + sequencer_(new QuicStreamSequencerPeer(&stream_)) { + } + + 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; + } + + QuicSession* session_; + testing::StrictMock<MockStream> stream_; + scoped_ptr<QuicStreamSequencerPeer> sequencer_; +}; + +TEST_F(QuicStreamSequencerTest, RejectOldFrame) { + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)) + .WillOnce(Return(3)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); + // Ignore this - it matches a past sequence number and we should not see it + // again. + EXPECT_TRUE(sequencer_->OnFrame(0, "def")); + EXPECT_EQ(0u, sequencer_->frames()->size()); +} + +TEST_F(QuicStreamSequencerTest, RejectOverlyLargeFrame) { + // TODO(rch): enable when chromium supports EXPECT_DFATAL. + /* + EXPECT_DFATAL(sequencer_.reset(new QuicStreamSequencerPeer(2, &stream_)), + "Setting max frame memory to 2. " + "Some frames will be impossible to handle."); + + EXPECT_DEBUG_DEATH(sequencer_->OnFrame(0, "abc"), ""); + */ +} + +TEST_F(QuicStreamSequencerTest, DropFramePastBuffering) { + sequencer_->SetMemoryLimit(3); + + EXPECT_FALSE(sequencer_->OnFrame(3, "abc")); +} + +TEST_F(QuicStreamSequencerTest, RejectBufferedFrame) { + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(1u, sequencer_->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. + EXPECT_TRUE(sequencer_->OnFrame(0, "def")); + EXPECT_EQ(1u, sequencer_->frames()->size()); +} + +TEST_F(QuicStreamSequencerTest, FullFrameConsumed) { + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); +} + +TEST_F(QuicStreamSequencerTest, EmptyFrame) { + EXPECT_TRUE(sequencer_->OnFrame(0, "")); + EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); +} + +TEST_F(QuicStreamSequencerTest, EmptyFinFrame) { + EXPECT_CALL(stream_, TerminateFromPeer(true)); + EXPECT_TRUE(sequencer_->OnFinFrame(0, "")); + EXPECT_EQ(0u, sequencer_->frames()->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); +} + +TEST_F(QuicStreamSequencerTest, PartialFrameConsumed) { + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(2)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_EQ(2u, sequencer_->num_bytes_consumed()); + EXPECT_EQ("c", sequencer_->frames()->find(2)->second); +} + +TEST_F(QuicStreamSequencerTest, NextxFrameNotConsumed) { + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(0)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + EXPECT_EQ("abc", sequencer_->frames()->find(0)->second); +} + +TEST_F(QuicStreamSequencerTest, FutureFrameNotProcessed) { + EXPECT_TRUE(sequencer_->OnFrame(3, "abc")); + EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + EXPECT_EQ("abc", sequencer_->frames()->find(3)->second); +} + +TEST_F(QuicStreamSequencerTest, OutOfOrderFrameProcessed) { + // Buffer the first + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); + EXPECT_EQ(1u, sequencer_->frames()->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + // Buffer the second + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + EXPECT_EQ(2u, sequencer_->frames()->size()); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("ghi"), 3)).WillOnce(Return(3)); + + // Ack right away + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(9u, sequencer_->num_bytes_consumed()); + + EXPECT_EQ(0u, sequencer_->frames()->size()); +} + +TEST_F(QuicStreamSequencerTest, OutOfOrderFramesProcessedWithBuffering) { + sequencer_->SetMemoryLimit(9); + + // Too far to buffer. + EXPECT_FALSE(sequencer_->OnFrame(9, "jkl")); + + // We can afford to buffer this. + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); + EXPECT_EQ(0u, sequencer_->num_bytes_consumed()); + + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + + // Ack right away + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); + + // We should be willing to buffer this now. + EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); + EXPECT_EQ(3u, sequencer_->num_bytes_consumed()); + + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("ghi"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("jkl"), 3)).WillOnce(Return(3)); + + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + EXPECT_EQ(12u, sequencer_->num_bytes_consumed()); + EXPECT_EQ(0u, sequencer_->frames()->size()); +} + +TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithReadv) { + sequencer_->SetMemoryLimit(9); + char buffer[20]; + iovec iov[2]; + iov[0].iov_base = &buffer[0]; + iov[0].iov_len = 1; + iov[1].iov_base = &buffer[1]; + iov[1].iov_len = 2; + + // Push abc - process. + // Push jkl - buffer (not next data) + // Push def - don't process. + // Push mno - drop (too far out) + // Push ghi - buffer (def not processed) + // Read 2. + // Push mno - buffer (not all read) + // Read all + // Push pqr - process + + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(0)); + EXPECT_CALL(stream_, ProcessData(StrEq("pqr"), 3)).WillOnce(Return(3)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); + EXPECT_FALSE(sequencer_->OnFrame(12, "mno")); + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); + + // Read 3 bytes. + EXPECT_EQ(3, sequencer_->Readv(iov, 2)); + EXPECT_EQ(0, strncmp(buffer, "def", 3)); + + // Now we have space to bufer this. + EXPECT_TRUE(sequencer_->OnFrame(12, "mno")); + + // Read the remaining 9 bytes. + iov[1].iov_len = 19; + EXPECT_EQ(9, sequencer_->Readv(iov, 2)); + EXPECT_EQ(0, strncmp(buffer, "ghijklmno", 9)); + + EXPECT_TRUE(sequencer_->OnFrame(15, "pqr")); +} + +// Same as above, just using a different method for reading. +TEST_F(QuicStreamSequencerTest, OutOfOrderFramesBlockignWithGetReadableRegion) { + sequencer_->SetMemoryLimit(9); + + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(0)); + EXPECT_CALL(stream_, ProcessData(StrEq("pqr"), 3)).WillOnce(Return(3)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); + EXPECT_FALSE(sequencer_->OnFrame(12, "mno")); + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); + + // Read 3 bytes. + const char* expected[] = {"def", "ghi", "jkl"}; + ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); + char buffer[9]; + iovec read_iov = { &buffer[0], 3 }; + ASSERT_EQ(3, sequencer_->Readv(&read_iov, 1)); + + // Now we have space to bufer this. + EXPECT_TRUE(sequencer_->OnFrame(12, "mno")); + + // Read the remaining 9 bytes. + const char* expected2[] = {"ghi", "jkl", "mno"}; + ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2))); + read_iov.iov_len = 9; + ASSERT_EQ(9, sequencer_->Readv(&read_iov, 1)); + + EXPECT_TRUE(sequencer_->OnFrame(15, "pqr")); +} + +// Same as above, just using a different method for reading. +TEST_F(QuicStreamSequencerTest, MarkConsumed) { + sequencer_->SetMemoryLimit(9); + + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(0)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + EXPECT_TRUE(sequencer_->OnFrame(6, "ghi")); + + // Peek into the data. + const char* expected[] = {"abc", "def", "ghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); + + // Consume 1 byte. + sequencer_->MarkConsumed(1); + // Verify data. + const char* expected2[] = {"bc", "def", "ghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected2, arraysize(expected2))); + + // Consume 2 bytes. + sequencer_->MarkConsumed(2); + // Verify data. + const char* expected3[] = {"def", "ghi"}; + ASSERT_TRUE(VerifyReadableRegions(expected3, arraysize(expected3))); + + // Consume 5 bytes. + sequencer_->MarkConsumed(5); + // Verify data. + const char* expected4[] = {"i"}; + ASSERT_TRUE(VerifyReadableRegions(expected4, arraysize(expected4))); +} + +TEST_F(QuicStreamSequencerTest, MarkConsumedError) { + // TODO(rch): enable when chromium supports EXPECT_DFATAL. + /* + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(0)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(sequencer_->OnFrame(9, "jklmnopqrstuvwxyz")); + + // Peek into the data. Only the first chunk should be readable + // because of the missing data. + const char* expected[] = {"abc"}; + ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); + + // Now, attempt to mark consumed more data than was readable + // and expect the stream to be closed. + EXPECT_CALL(stream_, Close(QUIC_SERVER_ERROR_PROCESSING_STREAM)); + EXPECT_DFATAL(sequencer_->MarkConsumed(4), + "Invalid argument to MarkConsumed. num_bytes_consumed_: 3 " + "end_offset: 4 offset: 9 length: 17"); + */ +} + +TEST_F(QuicStreamSequencerTest, MarkConsumedWithMissingPacket) { + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(0)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + // Missing packet: 6, ghi + EXPECT_TRUE(sequencer_->OnFrame(9, "jkl")); + + const char* expected[] = {"abc", "def"}; + ASSERT_TRUE(VerifyReadableRegions(expected, arraysize(expected))); + + sequencer_->MarkConsumed(6); +} + +TEST_F(QuicStreamSequencerTest, BasicHalfCloseOrdered) { + InSequence s; + + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, TerminateFromPeer(true)); + EXPECT_TRUE(sequencer_->OnFinFrame(0, "abc")); + + EXPECT_EQ(3u, sequencer_->close_offset()); +} + +TEST_F(QuicStreamSequencerTest, BasicHalfCloseUnorderedWithFlush) { + sequencer_->OnFinFrame(6, ""); + EXPECT_EQ(6u, sequencer_->close_offset()); + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, ProcessData(StrEq("def"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, TerminateFromPeer(true)); + + EXPECT_TRUE(sequencer_->OnFrame(3, "def")); + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); +} + +TEST_F(QuicStreamSequencerTest, BasicHalfUnordered) { + sequencer_->OnFinFrame(3, ""); + EXPECT_EQ(3u, sequencer_->close_offset()); + InSequence s; + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(3)); + EXPECT_CALL(stream_, TerminateFromPeer(true)); + + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); +} + +TEST_F(QuicStreamSequencerTest, TerminateWithReadv) { + char buffer[3]; + + sequencer_->OnFinFrame(3, ""); + EXPECT_EQ(3u, sequencer_->close_offset()); + + EXPECT_FALSE(sequencer_->IsHalfClosed()); + + EXPECT_CALL(stream_, ProcessData(StrEq("abc"), 3)).WillOnce(Return(0)); + EXPECT_TRUE(sequencer_->OnFrame(0, "abc")); + + iovec iov = { &buffer[0], 3 }; + int bytes_read = sequencer_->Readv(&iov, 1); + EXPECT_EQ(3, bytes_read); + EXPECT_TRUE(sequencer_->IsHalfClosed()); +} + +TEST_F(QuicStreamSequencerTest, MutipleOffsets) { + sequencer_->OnFinFrame(3, ""); + EXPECT_EQ(3u, sequencer_->close_offset()); + + EXPECT_CALL(stream_, Close(QUIC_MULTIPLE_TERMINATION_OFFSETS)); + sequencer_->OnFinFrame(5, ""); + EXPECT_EQ(3u, sequencer_->close_offset()); + + EXPECT_CALL(stream_, Close(QUIC_MULTIPLE_TERMINATION_OFFSETS)); + sequencer_->OnFinFrame(1, ""); + EXPECT_EQ(3u, sequencer_->close_offset()); + + sequencer_->OnFinFrame(3, ""); + EXPECT_EQ(3u, sequencer_->close_offset()); +} + +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_, ProcessData(StrEq(*data), data->size())) + .WillOnce(Return(data->size())); + } + + while (list_.size() != 0) { + int index = OneToN(list_.size()) - 1; + LOG(ERROR) << "Sending index " << index << " " + << list_[index].second.data(); + EXPECT_TRUE(sequencer_->OnFrame(list_[index].first, + list_[index].second.data())); + + list_.erase(list_.begin() + index); + } +} + +// All frames are processed as soon as we have sequential data. +// Buffering, so some frames are rejected. +TEST_F(QuicSequencerRandomTest, RandomFramesDroppingNoBackup) { + sequencer_->SetMemoryLimit(26); + + InSequence s; + for (size_t i = 0; i < list_.size(); ++i) { + string* data = &list_[i].second; + EXPECT_CALL(stream_, ProcessData(StrEq(*data), data->size())) + .WillOnce(Return(data->size())); + } + + while (list_.size() != 0) { + int index = OneToN(list_.size()) - 1; + LOG(ERROR) << "Sending index " << index << " " + << list_[index].second.data(); + bool acked = sequencer_->OnFrame(list_[index].first, + list_[index].second.data()); + + if (acked) { + list_.erase(list_.begin() + index); + } + } +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_time.cc b/chromium/net/quic/quic_time.cc new file mode 100644 index 00000000000..a56775663e4 --- /dev/null +++ b/chromium/net/quic/quic_time.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_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()); +} + +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()); +} + +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/chromium/net/quic/quic_time.h b/chromium/net/quic/quic_time.h new file mode 100644 index 00000000000..cdb7f83e81e --- /dev/null +++ b/chromium/net/quic/quic_time.h @@ -0,0 +1,184 @@ +// 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; + + 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(); + + // 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/chromium/net/quic/quic_time_test.cc b/chromium/net/quic/quic_time_test.cc new file mode 100644 index 00000000000..c4ea0e20047 --- /dev/null +++ b/chromium/net/quic/quic_time_test.cc @@ -0,0 +1,113 @@ +// 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 { + +class QuicTimeDeltaTest : public ::testing::Test { + protected: +}; + +TEST_F(QuicTimeDeltaTest, Zero) { + EXPECT_TRUE(QuicTime::Delta::Zero().IsZero()); + EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite()); + EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsZero()); +} + +TEST_F(QuicTimeDeltaTest, Infinite) { + EXPECT_TRUE(QuicTime::Delta::Infinite().IsInfinite()); + EXPECT_FALSE(QuicTime::Delta::Zero().IsInfinite()); + EXPECT_FALSE(QuicTime::Delta::FromMilliseconds(1).IsInfinite()); +} + +TEST_F(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_F(QuicTimeDeltaTest, Add) { + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(2000), + QuicTime::Delta::Zero().Add(QuicTime::Delta::FromMilliseconds(2))); +} + +TEST_F(QuicTimeDeltaTest, Subtract) { + EXPECT_EQ(QuicTime::Delta::FromMicroseconds(1000), + QuicTime::Delta::FromMilliseconds(2).Subtract( + QuicTime::Delta::FromMilliseconds(1))); +} + +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, 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); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/quic_utils.cc b/chromium/net/quic/quic_utils.cc new file mode 100644 index 00000000000..17957440a3d --- /dev/null +++ b/chromium/net/quic/quic_utils.cc @@ -0,0 +1,270 @@ +// 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 "base/logging.h" +#include "base/port.h" +#include "base/strings/stringprintf.h" +#include "base/strings/string_number_conversions.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 +uint128 QuicUtils::ParseUint128(const uint8* in) { + uint64 lo, hi; + memcpy(&lo, in, sizeof(lo)); + memcpy(&hi, in + sizeof(lo), sizeof(hi)); + return uint128(hi, lo); +} + +#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_SERVER_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_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_INVALID_FEC_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_ACK_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_TOO_MANY_OPEN_STREAMS); + RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); + RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); + RETURN_STRING_LITERAL(QUIC_STREAM_RST_BEFORE_HEADERS_DECOMPRESSED); + 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_ERROR_MIGRATING_ADDRESS); + RETURN_STRING_LITERAL(QUIC_PACKET_WRITE_ERROR); + 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_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 +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 && 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; +} + +} // namespace net diff --git a/chromium/net/quic/quic_utils.h b/chromium/net/quic/quic_utils.h new file mode 100644 index 00000000000..6650dbf453b --- /dev/null +++ b/chromium/net/quic/quic_utils.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. +// +// 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-NULL 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); + + // ParseUint128 parses a little-endian uint128 from |in| and returns it. + static uint128 ParseUint128(const uint8* in); + + // 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); + + // 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); + } +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_UTILS_H_ diff --git a/chromium/net/quic/quic_utils_test.cc b/chromium/net/quic/quic_utils_test.cc new file mode 100644 index 00000000000..0e50aec0ec1 --- /dev/null +++ b/chromium/net/quic/quic_utils_test.cc @@ -0,0 +1,73 @@ +// 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 "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)))); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/reliable_quic_stream.cc b/chromium/net/quic/reliable_quic_stream.cc new file mode 100644 index 00000000000..c2cb3f1ff52 --- /dev/null +++ b/chromium/net/quic/reliable_quic_stream.cc @@ -0,0 +1,436 @@ +// 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_session.h" +#include "net/quic/quic_spdy_decompressor.h" + +using base::StringPiece; +using std::min; + +namespace net { + +ReliableQuicStream::ReliableQuicStream(QuicStreamId id, + QuicSession* session) + : sequencer_(this), + id_(id), + session_(session), + visitor_(NULL), + stream_bytes_read_(0), + stream_bytes_written_(0), + headers_decompressed_(false), + headers_id_(0), + decompression_failed_(false), + 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) { +} + +ReliableQuicStream::~ReliableQuicStream() { +} + +bool ReliableQuicStream::WillAcceptStreamFrame( + const QuicStreamFrame& frame) const { + if (read_side_closed_) { + return true; + } + if (frame.stream_id != id_) { + LOG(ERROR) << "Error!"; + return false; + } + return sequencer_.WillAcceptStreamFrame(frame); +} + +bool ReliableQuicStream::OnStreamFrame(const QuicStreamFrame& frame) { + DCHECK_EQ(frame.stream_id, id_); + if (read_side_closed_) { + DLOG(INFO) << "Ignoring frame " << frame.stream_id; + // We don't want to be reading: blackhole the data. + return true; + } + // Note: This count include duplicate data received. + stream_bytes_read_ += frame.data.length(); + + bool accepted = sequencer_.OnStreamFrame(frame); + + return accepted; +} + +void ReliableQuicStream::OnStreamReset(QuicRstStreamErrorCode error) { + stream_error_ = error; + TerminateFromPeer(false); // Full close. +} + +void ReliableQuicStream::ConnectionClose(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; + } + + if (from_peer) { + TerminateFromPeer(false); + } else { + CloseWriteSide(); + CloseReadSide(); + } +} + +void ReliableQuicStream::TerminateFromPeer(bool half_close) { + if (!half_close) { + CloseWriteSide(); + } + CloseReadSide(); +} + +void ReliableQuicStream::Close(QuicRstStreamErrorCode error) { + stream_error_ = error; + if (error != QUIC_STREAM_NO_ERROR) { + // Sending a RstStream results in calling CloseStream. + session()->SendRstStream(id(), error); + } else { + session_->CloseStream(id()); + } +} + +size_t ReliableQuicStream::Readv(const struct iovec* iov, size_t iov_len) { + if (headers_decompressed_ && decompressed_headers_.empty()) { + return sequencer_.Readv(iov, iov_len); + } + 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); + return bytes_consumed; +} + +int ReliableQuicStream::GetReadableRegions(iovec* iov, size_t iov_len) { + if (headers_decompressed_ && decompressed_headers_.empty()) { + 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 ReliableQuicStream::IsHalfClosed() const { + if (!headers_decompressed_ || !decompressed_headers_.empty()) { + return false; + } + return sequencer_.IsHalfClosed(); +} + +bool ReliableQuicStream::HasBytesToRead() const { + return !decompressed_headers_.empty() || sequencer_.HasBytesToRead(); +} + +const IPEndPoint& ReliableQuicStream::GetPeerAddress() const { + return session_->peer_address(); +} + +QuicSpdyCompressor* ReliableQuicStream::compressor() { + return session_->compressor(); +} + +bool ReliableQuicStream::GetSSLInfo(SSLInfo* ssl_info) { + return session_->GetSSLInfo(ssl_info); +} + +QuicConsumedData ReliableQuicStream::WriteData(StringPiece data, bool fin) { + DCHECK(data.size() > 0 || fin); + return WriteOrBuffer(data, fin); +} + +QuicConsumedData ReliableQuicStream::WriteOrBuffer(StringPiece data, bool fin) { + DCHECK(!fin_buffered_); + + QuicConsumedData consumed_data(0, false); + fin_buffered_ = fin; + + if (queued_data_.empty()) { + consumed_data = WriteDataInternal(string(data.data(), data.length()), fin); + DCHECK_LE(consumed_data.bytes_consumed, data.length()); + } + + // If there's unconsumed data or an unconsumed fin, queue it. + if (consumed_data.bytes_consumed < data.length() || + (fin && !consumed_data.fin_consumed)) { + queued_data_.push_back( + string(data.data() + consumed_data.bytes_consumed, + data.length() - consumed_data.bytes_consumed)); + } + + return QuicConsumedData(data.size(), true); +} + +void ReliableQuicStream::OnCanWrite() { + bool fin = false; + while (!queued_data_.empty()) { + const string& data = queued_data_.front(); + if (queued_data_.size() == 1 && fin_buffered_) { + fin = true; + } + QuicConsumedData consumed_data = WriteDataInternal(data, fin); + if (consumed_data.bytes_consumed == data.size() && + fin == consumed_data.fin_consumed) { + queued_data_.pop_front(); + } else { + queued_data_.front().erase(0, consumed_data.bytes_consumed); + break; + } + } +} + +QuicConsumedData ReliableQuicStream::WriteDataInternal( + StringPiece data, bool fin) { + if (write_side_closed_) { + DLOG(ERROR) << "Attempt to write when the write side is closed"; + return QuicConsumedData(0, false); + } + + QuicConsumedData consumed_data = + session()->WriteData(id(), data, stream_bytes_written_, fin); + stream_bytes_written_ += consumed_data.bytes_consumed; + if (consumed_data.bytes_consumed == data.length()) { + if (fin && consumed_data.fin_consumed) { + fin_sent_ = true; + CloseWriteSide(); + } else if (fin && !consumed_data.fin_consumed) { + session_->MarkWriteBlocked(id()); + } + } else { + session_->MarkWriteBlocked(id()); + } + return consumed_data; +} + +void ReliableQuicStream::CloseReadSide() { + if (read_side_closed_) { + return; + } + DLOG(INFO) << "Done reading from stream " << id(); + + read_side_closed_ = true; + if (write_side_closed_) { + DLOG(INFO) << "Closing stream: " << id(); + session_->CloseStream(id()); + } +} + +uint32 ReliableQuicStream::ProcessRawData(const char* data, uint32 data_len) { + if (id() == kCryptoStreamId) { + if (data_len == 0) { + return 0; + } + // The crypto stream does not use compression. + return ProcessData(data, data_len); + } + uint32 total_bytes_consumed = 0; + if (headers_id_ == 0u) { + // The headers ID has not yet been read. Strip it from the beginning of + // the data stream. + DCHECK_GT(4u, headers_id_buffer_.length()); + size_t missing_size = 4 - headers_id_buffer_.length(); + if (data_len < missing_size) { + StringPiece(data, data_len).AppendToString(&headers_id_buffer_); + return data_len; + } + total_bytes_consumed += missing_size; + StringPiece(data, missing_size).AppendToString(&headers_id_buffer_); + DCHECK_EQ(4u, headers_id_buffer_.length()); + memcpy(&headers_id_, headers_id_buffer_.data(), 4); + headers_id_buffer_.clear(); + data += missing_size; + data_len -= missing_size; + } + DCHECK_NE(0u, headers_id_); + if (data_len == 0) { + return total_bytes_consumed; + } + + // Once the headers are finished, we simply pass the data through. + if (headers_decompressed_) { + // Some buffered header data remains. + if (!decompressed_headers_.empty()) { + ProcessHeaderData(); + } + if (decompressed_headers_.empty()) { + DVLOG(1) << "Delegating procesing to ProcessData"; + total_bytes_consumed += ProcessData(data, data_len); + } + return total_bytes_consumed; + } + + QuicHeaderId current_header_id = + session_->decompressor()->current_header_id(); + // Ensure that this header id looks sane. + if (headers_id_ < current_header_id || + headers_id_ > kMaxHeaderIdDelta + current_header_id) { + DVLOG(1) << "Invalid headers for stream: " << id() + << " header_id: " << headers_id_ + << " current_header_id: " << current_header_id; + session_->connection()->SendConnectionClose(QUIC_INVALID_HEADER_ID); + return total_bytes_consumed; + } + + // If we are head-of-line blocked on decompression, then back up. + if (current_header_id != headers_id_) { + session_->MarkDecompressionBlocked(headers_id_, id()); + DVLOG(1) << "Unable to decompress header data for stream: " << id() + << " header_id: " << headers_id_; + return total_bytes_consumed; + } + + // Decompressed data will be delivered to decompressed_headers_. + size_t bytes_consumed = session_->decompressor()->DecompressData( + StringPiece(data, data_len), this); + DCHECK_NE(0u, bytes_consumed); + if (bytes_consumed > data_len) { + DCHECK(false) << "DecompressData returned illegal value"; + OnDecompressionError(); + return total_bytes_consumed; + } + total_bytes_consumed += bytes_consumed; + data += bytes_consumed; + data_len -= bytes_consumed; + + if (decompression_failed_) { + // The session will have been closed in OnDecompressionError. + return total_bytes_consumed; + } + + // Headers are complete if the decompressor has moved on to the + // next stream. + headers_decompressed_ = + session_->decompressor()->current_header_id() != headers_id_; + if (!headers_decompressed_) { + DCHECK_EQ(0u, data_len); + } + + ProcessHeaderData(); + + if (!headers_decompressed_ || !decompressed_headers_.empty()) { + return total_bytes_consumed; + } + + // We have processed all of the decompressed data but we might + // have some more raw data to process. + if (data_len > 0) { + total_bytes_consumed += ProcessData(data, data_len); + } + + // The sequencer will push any additional buffered frames if this data + // has been completely consumed. + return total_bytes_consumed; +} + +uint32 ReliableQuicStream::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 ReliableQuicStream::OnDecompressorAvailable() { + DCHECK_EQ(headers_id_, + session_->decompressor()->current_header_id()); + DCHECK(!headers_decompressed_); + DCHECK(!decompression_failed_); + DCHECK_EQ(0u, decompressed_headers_.length()); + + while (!headers_decompressed_) { + struct iovec iovec; + if (sequencer_.GetReadableRegions(&iovec, 1) == 0) { + return; + } + + size_t bytes_consumed = session_->decompressor()->DecompressData( + StringPiece(static_cast<char*>(iovec.iov_base), + iovec.iov_len), + this); + DCHECK_LE(bytes_consumed, iovec.iov_len); + if (decompression_failed_) { + return; + } + sequencer_.MarkConsumed(bytes_consumed); + + headers_decompressed_ = + session_->decompressor()->current_header_id() != headers_id_; + } + + // Either the headers are complete, or the all data as been consumed. + ProcessHeaderData(); // Unprocessed headers remain in decompressed_headers_. + if (IsHalfClosed()) { + TerminateFromPeer(true); + } else if (headers_decompressed_ && decompressed_headers_.empty()) { + sequencer_.FlushBufferedFrames(); + } +} + +bool ReliableQuicStream::OnDecompressedData(StringPiece data) { + data.AppendToString(&decompressed_headers_); + return true; +} + +void ReliableQuicStream::OnDecompressionError() { + DCHECK(!decompression_failed_); + decompression_failed_ = true; + session_->connection()->SendConnectionClose(QUIC_DECOMPRESSION_FAILURE); +} + + +void ReliableQuicStream::CloseWriteSide() { + if (write_side_closed_) { + return; + } + DLOG(INFO) << "Done writing to stream " << id(); + + write_side_closed_ = true; + if (read_side_closed_) { + DLOG(INFO) << "Closing stream: " << id(); + session_->CloseStream(id()); + } +} + +void ReliableQuicStream::OnClose() { + CloseReadSide(); + CloseWriteSide(); + + 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_ = NULL; + visitor->OnClose(this); + } +} + +} // namespace net diff --git a/chromium/net/quic/reliable_quic_stream.h b/chromium/net/quic/reliable_quic_stream.h new file mode 100644 index 00000000000..352325de36c --- /dev/null +++ b/chromium/net/quic/reliable_quic_stream.h @@ -0,0 +1,199 @@ +// 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/strings/string_piece.h" +#include "net/base/iovec.h" +#include "net/base/net_export.h" +#include "net/quic/quic_spdy_compressor.h" +#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/quic_stream_sequencer.h" + +namespace net { + +namespace test { +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 ReliableQuicStream : public + QuicSpdyDecompressor::Visitor { + public: + // Visitor receives callbacks from the stream. + class Visitor { + public: + Visitor() {} + + // Called when the stream is closed. + virtual void OnClose(ReliableQuicStream* stream) = 0; + + protected: + virtual ~Visitor() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Visitor); + }; + + ReliableQuicStream(QuicStreamId id, + QuicSession* session); + + virtual ~ReliableQuicStream(); + + bool WillAcceptStreamFrame(const QuicStreamFrame& frame) const; + virtual bool OnStreamFrame(const QuicStreamFrame& frame); + + 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 client. + virtual void OnStreamReset(QuicRstStreamErrorCode error); + + // 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 ConnectionClose(QuicErrorCode error, bool from_peer); + + // Called when we should process a stream termination or + // stream close from the peer. + virtual void TerminateFromPeer(bool half_close); + + virtual uint32 ProcessRawData(const char* data, uint32 data_len); + virtual uint32 ProcessHeaderData(); + + virtual uint32 ProcessData(const char* data, uint32 data_len) = 0; + + virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; + virtual void OnDecompressionError() OVERRIDE; + + // Called to close the stream from this end. + virtual void Close(QuicRstStreamErrorCode error); + + // 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); + virtual bool IsHalfClosed() const; + virtual bool HasBytesToRead() const; + + // Called by the session when a decompression blocked stream + // becomes unblocked. + virtual void OnDecompressorAvailable(); + + 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() { return stream_bytes_read_; } + uint64 stream_bytes_written() { return stream_bytes_written_; } + + const IPEndPoint& GetPeerAddress() const; + + Visitor* visitor() { return visitor_; } + void set_visitor(Visitor* visitor) { visitor_ = visitor; } + + QuicSpdyCompressor* compressor(); + + // Gets the SSL connection information. + bool GetSSLInfo(SSLInfo* ssl_info); + + bool headers_decompressed() const { return headers_decompressed_; } + + protected: + // 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. + // + // The default implementation always consumed all bytes and any fin, but + // this behavior is not guaranteed for subclasses so callers should check the + // return value. + virtual QuicConsumedData WriteData(base::StringPiece data, bool fin); + + // 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 fin_buffered() { return fin_buffered_; } + + QuicSession* session() { return session_; } + + // Sends as much of 'data' to the connection as the connection will consume, + // and then buffers any remaining data in queued_data_. + // Returns (data.size(), true) as it always consumed all data: it returns for + // convenience to have the same return type as WriteDataInternal. + QuicConsumedData WriteOrBuffer(base::StringPiece data, bool fin); + + // Sends as much of 'data' to the connection as the connection will consume. + // Returns the number of bytes consumed by the connection. + QuicConsumedData WriteDataInternal(base::StringPiece data, bool fin); + + private: + friend class test::ReliableQuicStreamPeer; + friend class QuicStreamUtils; + + std::list<string> queued_data_; + + QuicStreamSequencer sequencer_; + QuicStreamId id_; + QuicSession* session_; + // Optional visitor of this stream to be notified when the stream is closed. + Visitor* visitor_; + // 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_; + // True if the headers have been completely decompresssed. + bool headers_decompressed_; + // ID of the header block sent by the peer, once parsed. + QuicHeaderId headers_id_; + // Buffer into which we write bytes from the headers_id_ + // until it is fully parsed. + string headers_id_buffer_; + // 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_; + + // 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_; +}; + +} // namespace net + +#endif // NET_QUIC_RELIABLE_QUIC_STREAM_H_ diff --git a/chromium/net/quic/reliable_quic_stream_test.cc b/chromium/net/quic/reliable_quic_stream_test.cc new file mode 100644 index 00000000000..7167a222345 --- /dev/null +++ b/chromium/net/quic/reliable_quic_stream_test.cc @@ -0,0 +1,535 @@ +// 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_connection.h" +#include "net/quic/quic_spdy_compressor.h" +#include "net/quic/quic_spdy_decompressor.h" +#include "net/quic/quic_utils.h" +#include "net/quic/spdy_utils.h" +#include "net/quic/test_tools/quic_session_peer.h" +#include "net/quic/test_tools/quic_test_utils.h" +#include "testing/gmock/include/gmock/gmock.h" + +using base::StringPiece; +using std::min; +using testing::_; +using testing::InSequence; +using testing::Return; +using testing::SaveArg; +using testing::StrEq; +using testing::StrictMock; + +namespace net { +namespace test { +namespace { + +const char kData1[] = "FooAndBar"; +const char kData2[] = "EepAndBaz"; +const size_t kDataLen = 9; +const QuicGuid kGuid = 42; +const QuicGuid kStreamId = 3; +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 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::WriteData; + using ReliableQuicStream::CloseReadSide; + using ReliableQuicStream::CloseWriteSide; + + const string& data() const { return data_; } + + private: + bool should_process_data_; + string data_; +}; + +class ReliableQuicStreamTest : public ::testing::TestWithParam<bool> { + public: + ReliableQuicStreamTest() { + 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>( + kGuid, IPEndPoint(), kIsServer); + session_.reset(new testing::StrictMock<MockSession>( + connection_, kIsServer)); + stream_.reset(new TestStream(kStreamId, session_.get(), + stream_should_process_data)); + stream2_.reset(new TestStream(kStreamId + 2, session_.get(), + stream_should_process_data)); + compressor_.reset(new QuicSpdyCompressor()); + decompressor_.reset(new QuicSpdyDecompressor); + write_blocked_list_ = + QuicSessionPeer::GetWriteblockedStreams(session_.get()); + } + + protected: + MockConnection* connection_; + scoped_ptr<MockSession> session_; + scoped_ptr<TestStream> stream_; + scoped_ptr<TestStream> stream2_; + scoped_ptr<QuicSpdyCompressor> compressor_; + scoped_ptr<QuicSpdyDecompressor> decompressor_; + SpdyHeaderBlock headers_; + BlockedList<QuicStreamId>* write_blocked_list_; +}; + +TEST_F(ReliableQuicStreamTest, WriteAllData) { + Initialize(kShouldProcessData); + + connection_->options()->max_packet_length = + 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + // TODO(rch): figure out how to get StrEq working here. + //EXPECT_CALL(*session_, WriteData(kStreamId, StrEq(kData1), _, _)).WillOnce( + EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce( + Return(QuicConsumedData(kDataLen, true))); + EXPECT_EQ(kDataLen, stream_->WriteData(kData1, false).bytes_consumed); + EXPECT_TRUE(write_blocked_list_->IsEmpty()); +} + +// TODO(rtenneti): Death tests crash on OS_ANDROID. +#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID) +TEST_F(ReliableQuicStreamTest, NoBlockingIfNoDataOrFin) { + Initialize(kShouldProcessData); + + // Write no data and no fin. If we consume nothing we should not be write + // blocked. + EXPECT_DEBUG_DEATH({ + EXPECT_CALL(*session_, WriteData(kStreamId, _, _, _)).WillOnce( + Return(QuicConsumedData(0, false))); + stream_->WriteData(StringPiece(), false); + EXPECT_TRUE(write_blocked_list_->IsEmpty()); + }, ""); +} +#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG) && !defined(OS_ANDROID) + +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_, WriteData(kStreamId, _, _, _)).WillOnce( + Return(QuicConsumedData(1, false))); + stream_->WriteData(StringPiece(kData1, 2), false); + ASSERT_EQ(1, write_blocked_list_->NumObjects()); +} + + +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_, WriteData(kStreamId, _, _, _)).WillOnce( + Return(QuicConsumedData(2, false))); + stream_->WriteData(StringPiece(kData1, 2), true); + ASSERT_EQ(1, write_blocked_list_->NumObjects()); +} + +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_, WriteData(kStreamId, _, _, _)).WillOnce( + Return(QuicConsumedData(0, false))); + stream_->WriteData(StringPiece(), true); + ASSERT_EQ(1, write_blocked_list_->NumObjects()); +} + +TEST_F(ReliableQuicStreamTest, WriteData) { + Initialize(kShouldProcessData); + + EXPECT_TRUE(write_blocked_list_->IsEmpty()); + connection_->options()->max_packet_length = + 1 + QuicPacketCreator::StreamFramePacketOverhead( + connection_->version(), PACKET_8BYTE_GUID, !kIncludeVersion, + PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP); + // TODO(rch): figure out how to get StrEq working here. + //EXPECT_CALL(*session_, WriteData(_, StrEq(kData1), _, _)).WillOnce( + EXPECT_CALL(*session_, WriteData(_, _, _, _)).WillOnce( + Return(QuicConsumedData(kDataLen - 1, false))); + // The return will be kDataLen, because the last byte gets buffered. + EXPECT_EQ(kDataLen, stream_->WriteData(kData1, false).bytes_consumed); + EXPECT_FALSE(write_blocked_list_->IsEmpty()); + + // Queue a bytes_consumed write. + EXPECT_EQ(kDataLen, stream_->WriteData(kData2, false).bytes_consumed); + + // Make sure we get the tail of the first write followed by the bytes_consumed + InSequence s; + //EXPECT_CALL(*session_, WriteData(_, StrEq(&kData1[kDataLen - 1]), _, _)). + EXPECT_CALL(*session_, WriteData(_, _, _, _)). + WillOnce(Return(QuicConsumedData(1, false))); + //EXPECT_CALL(*session_, WriteData(_, StrEq(kData2), _, _)). + EXPECT_CALL(*session_, WriteData(_, _, _, _)). + WillOnce(Return(QuicConsumedData(kDataLen - 2, false))); + stream_->OnCanWrite(); + + // And finally the end of the bytes_consumed + //EXPECT_CALL(*session_, WriteData(_, StrEq(&kData2[kDataLen - 2]), _, _)). + EXPECT_CALL(*session_, WriteData(_, _, _, _)). + 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_->ConnectionClose(QUIC_INTERNAL_ERROR, false); + EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error()); + EXPECT_EQ(QUIC_NO_ERROR, stream_->connection_error()); +} + +TEST_F(ReliableQuicStreamTest, ProcessHeaders) { + Initialize(kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + QuicStreamFrame frame(kStreamId, false, 0, compressed_headers); + + stream_->OnStreamFrame(frame); + EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_), stream_->data()); +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersWithInvalidHeaderId) { + Initialize(kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + compressed_headers.replace(0, 1, 1, '\xFF'); // Illegal header id. + QuicStreamFrame frame(kStreamId, false, 0, compressed_headers); + + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID)); + stream_->OnStreamFrame(frame); +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBody) { + Initialize(kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + string body = "this is the body"; + string data = compressed_headers + body; + QuicStreamFrame frame(kStreamId, false, 0, data); + + stream_->OnStreamFrame(frame); + EXPECT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, + stream_->data()); +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyFragments) { + Initialize(kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + string body = "this is the body"; + string data = compressed_headers + body; + + for (size_t fragment_size = 1; fragment_size < data.size(); ++fragment_size) { + Initialize(kShouldProcessData); + for (size_t offset = 0; offset < data.size(); offset += fragment_size) { + size_t remaining_data = data.length() - offset; + StringPiece fragment(data.data() + offset, + min(fragment_size, remaining_data)); + QuicStreamFrame frame(kStreamId, false, offset, fragment); + + stream_->OnStreamFrame(frame); + } + ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, + stream_->data()) << "fragment_size: " << fragment_size; + } + + for (size_t split_point = 1; split_point < data.size() - 1; ++split_point) { + Initialize(kShouldProcessData); + + StringPiece fragment1(data.data(), split_point); + QuicStreamFrame frame1(kStreamId, false, 0, fragment1); + stream_->OnStreamFrame(frame1); + + StringPiece fragment2(data.data() + split_point, data.size() - split_point); + QuicStreamFrame frame2(kStreamId, false, split_point, fragment2); + stream_->OnStreamFrame(frame2); + + ASSERT_EQ(SpdyUtils::SerializeUncompressedHeaders(headers_) + body, + stream_->data()) << "split_point: " << split_point; + } +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersAndBodyReadv) { + Initialize(!kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + string body = "this is the body"; + string data = compressed_headers + body; + QuicStreamFrame frame(kStreamId, false, 0, data); + string uncompressed_headers = + SpdyUtils::SerializeUncompressedHeaders(headers_); + string uncompressed_data = uncompressed_headers + body; + + stream_->OnStreamFrame(frame); + EXPECT_EQ(uncompressed_headers, stream_->data()); + + char buffer[2048]; + ASSERT_LT(data.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(uncompressed_headers.length(), bytes_read); + EXPECT_EQ(uncompressed_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_F(ReliableQuicStreamTest, ProcessHeadersAndBodyIncrementalReadv) { + Initialize(!kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + string body = "this is the body"; + string data = compressed_headers + body; + QuicStreamFrame frame(kStreamId, false, 0, data); + string uncompressed_headers = + SpdyUtils::SerializeUncompressedHeaders(headers_); + string uncompressed_data = uncompressed_headers + body; + + stream_->OnStreamFrame(frame); + EXPECT_EQ(uncompressed_headers, stream_->data()); + + char buffer[1]; + struct iovec vec; + vec.iov_base = buffer; + vec.iov_len = arraysize(buffer); + for (size_t i = 0; i < uncompressed_data.length(); ++i) { + size_t bytes_read = stream_->Readv(&vec, 1); + ASSERT_EQ(1u, bytes_read); + EXPECT_EQ(uncompressed_data.data()[i], buffer[0]); + } +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersUsingReadvWithMultipleIovecs) { + Initialize(!kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + string body = "this is the body"; + string data = compressed_headers + body; + QuicStreamFrame frame(kStreamId, false, 0, data); + string uncompressed_headers = + SpdyUtils::SerializeUncompressedHeaders(headers_); + string uncompressed_data = uncompressed_headers + body; + + stream_->OnStreamFrame(frame); + EXPECT_EQ(uncompressed_headers, stream_->data()); + + 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); + for (size_t i = 0; i < uncompressed_data.length(); i += 2) { + size_t bytes_read = stream_->Readv(vec, 2); + ASSERT_EQ(2u, bytes_read) << i; + ASSERT_EQ(uncompressed_data.data()[i], buffer1[0]) << i; + ASSERT_EQ(uncompressed_data.data()[i + 1], buffer2[0]) << i; + } +} + +TEST_F(ReliableQuicStreamTest, ProcessCorruptHeadersEarly) { + Initialize(kShouldProcessData); + + string compressed_headers1 = compressor_->CompressHeaders(headers_); + QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1); + string decompressed_headers1 = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + headers_["content-type"] = "text/plain"; + string compressed_headers2 = compressor_->CompressHeaders(headers_); + // Corrupt the compressed data. + compressed_headers2[compressed_headers2.length() - 1] ^= 0xA1; + QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2); + string decompressed_headers2 = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + // Deliver frame2 to stream2 out of order. The decompressor is not + // available yet, so no data will be processed. The compressed data + // will be buffered until OnDecompressorAvailable() is called + // to process it. + stream2_->OnStreamFrame(frame2); + EXPECT_EQ("", stream2_->data()); + + // Now deliver frame1 to stream1. The decompressor is available so + // the data will be processed, and the decompressor will become + // available for stream2. + stream_->OnStreamFrame(frame1); + EXPECT_EQ(decompressed_headers1, stream_->data()); + + // Verify that the decompressor is available, and inform stream2 + // that it can now decompress the buffered compressed data. Since + // the compressed data is corrupt, the stream will shutdown the session. + EXPECT_EQ(2u, session_->decompressor()->current_header_id()); + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_DECOMPRESSION_FAILURE)); + stream2_->OnDecompressorAvailable(); + EXPECT_EQ("", stream2_->data()); +} + +TEST_F(ReliableQuicStreamTest, ProcessPartialHeadersEarly) { + Initialize(kShouldProcessData); + + string compressed_headers1 = compressor_->CompressHeaders(headers_); + QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1); + string decompressed_headers1 = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + headers_["content-type"] = "text/plain"; + string compressed_headers2 = compressor_->CompressHeaders(headers_); + string partial_compressed_headers = + compressed_headers2.substr(0, compressed_headers2.length() / 2); + QuicStreamFrame frame2(stream2_->id(), false, 0, partial_compressed_headers); + string decompressed_headers2 = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + // Deliver frame2 to stream2 out of order. The decompressor is not + // available yet, so no data will be processed. The compressed data + // will be buffered until OnDecompressorAvailable() is called + // to process it. + stream2_->OnStreamFrame(frame2); + EXPECT_EQ("", stream2_->data()); + + // Now deliver frame1 to stream1. The decompressor is available so + // the data will be processed, and the decompressor will become + // available for stream2. + stream_->OnStreamFrame(frame1); + EXPECT_EQ(decompressed_headers1, stream_->data()); + + // Verify that the decompressor is available, and inform stream2 + // that it can now decompress the buffered compressed data. Since + // the compressed data is incomplete it will not be passed to + // the stream. + EXPECT_EQ(2u, session_->decompressor()->current_header_id()); + stream2_->OnDecompressorAvailable(); + EXPECT_EQ("", stream2_->data()); + + // Now send remaining data and verify that we have now received the + // compressed headers. + string remaining_compressed_headers = + compressed_headers2.substr(partial_compressed_headers.length()); + + QuicStreamFrame frame3(stream2_->id(), false, + partial_compressed_headers.length(), + remaining_compressed_headers); + stream2_->OnStreamFrame(frame3); + EXPECT_EQ(decompressed_headers2, stream2_->data()); +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersEarly) { + Initialize(kShouldProcessData); + + string compressed_headers1 = compressor_->CompressHeaders(headers_); + QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers1); + string decompressed_headers1 = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + headers_["content-type"] = "text/plain"; + string compressed_headers2 = compressor_->CompressHeaders(headers_); + QuicStreamFrame frame2(stream2_->id(), false, 0, compressed_headers2); + string decompressed_headers2 = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + // Deliver frame2 to stream2 out of order. The decompressor is not + // available yet, so no data will be processed. The compressed data + // will be buffered until OnDecompressorAvailable() is called + // to process it. + stream2_->OnStreamFrame(frame2); + EXPECT_EQ("", stream2_->data()); + + // Now deliver frame1 to stream1. The decompressor is available so + // the data will be processed, and the decompressor will become + // available for stream2. + stream_->OnStreamFrame(frame1); + EXPECT_EQ(decompressed_headers1, stream_->data()); + + // Verify that the decompressor is available, and inform stream2 + // that it can now decompress the buffered compressed data. + EXPECT_EQ(2u, session_->decompressor()->current_header_id()); + stream2_->OnDecompressorAvailable(); + EXPECT_EQ(decompressed_headers2, stream2_->data()); +} + +TEST_F(ReliableQuicStreamTest, ProcessHeadersDelay) { + Initialize(!kShouldProcessData); + + string compressed_headers = compressor_->CompressHeaders(headers_); + QuicStreamFrame frame1(stream_->id(), false, 0, compressed_headers); + string decompressed_headers = + SpdyUtils::SerializeUncompressedHeaders(headers_); + + // Send the headers to the stream and verify they were decompressed. + stream_->OnStreamFrame(frame1); + EXPECT_EQ(2u, session_->decompressor()->current_header_id()); + + // Verify that we are now able to handle the body data, + // even though the stream has not processed the headers. + EXPECT_CALL(*connection_, SendConnectionClose(QUIC_INVALID_HEADER_ID)) + .Times(0); + QuicStreamFrame frame2(stream_->id(), false, compressed_headers.length(), + "body data"); + stream_->OnStreamFrame(frame2); +} + +} // namespace +} // namespace test +} // namespace net diff --git a/chromium/net/quic/spdy_utils.cc b/chromium/net/quic/spdy_utils.cc new file mode 100644 index 00000000000..350819e9143 --- /dev/null +++ b/chromium/net/quic/spdy_utils.cc @@ -0,0 +1,25 @@ +// 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(SPDY3, &headers); + SpdyFrameBuilder builder(length); + SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers); + scoped_ptr<SpdyFrame> block(builder.take()); + return string(block->data(), length); +} + +} // namespace net diff --git a/chromium/net/quic/spdy_utils.h b/chromium/net/quic/spdy_utils.h new file mode 100644 index 00000000000..ffc78f08da2 --- /dev/null +++ b/chromium/net/quic/spdy_utils.h @@ -0,0 +1,23 @@ +// 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 { + +class NET_EXPORT_PRIVATE SpdyUtils { + public: + static std::string SerializeUncompressedHeaders( + const SpdyHeaderBlock& headers); +}; + +} // namespace net + +#endif // NET_QUIC_SPDY_UTILS_H_ diff --git a/chromium/net/quic/test_tools/crypto_test_utils.cc b/chromium/net/quic/test_tools/crypto_test_utils.cc new file mode 100644 index 00000000000..73b5bab080a --- /dev/null +++ b/chromium/net/quic/test_tools/crypto_test_utils.cc @@ -0,0 +1,505 @@ +// 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/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/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::string; +using std::vector; + +namespace net { +namespace test { + +namespace { + +// 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 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; + 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++) { + ASSERT_TRUE(framer.ProcessPacket(*source_conn->encrypted_packets_[index])); + for (vector<QuicStreamFrame>::const_iterator + i = framer.stream_frames().begin(); + i != framer.stream_frames().end(); ++i) { + ASSERT_TRUE(crypto_framer.ProcessInput(i->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; +} + +} // anonymous namespace + +CryptoTestUtils::FakeClientOptions::FakeClientOptions() + : dont_verify_certs(false), + channel_id_enabled(false) { +} + +// static +int CryptoTestUtils::HandshakeWithFakeServer( + PacketSavingConnection* client_conn, + QuicCryptoClientStream* client) { + QuicGuid guid(1); + IPAddressNumber ip; + CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); + IPEndPoint addr = IPEndPoint(ip, 1); + PacketSavingConnection* server_conn = + new PacketSavingConnection(guid, addr, true); + TestSession server_session(server_conn, QuicConfig(), true); + + 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) { + QuicGuid guid(1); + IPAddressNumber ip; + CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip)); + IPEndPoint addr = IPEndPoint(ip, 1); + PacketSavingConnection* client_conn = + new PacketSavingConnection(guid, addr, false); + TestSession client_session(client_conn, QuicConfig(), false); + QuicCryptoClientConfig crypto_config; + + client_session.config()->SetDefaults(); + crypto_config.SetDefaults(); + // TODO(rtenneti): Enable testing of ProofVerifier. + // if (!options.dont_verify_certs) { + // crypto_config.SetProofVerifier(ProofVerifierForTesting()); + // } + if (options.channel_id_enabled) { + crypto_config.SetChannelIDSigner(ChannelIDSignerForTesting()); + } + QuicCryptoClientStream client("test.example.com", &client_session, + &crypto_config); + client_session.SetCryptoStream(&client); + + CHECK(client.CryptoConnect()); + CHECK_EQ(1u, client_conn->packets_.size()); + + CommunicateHandshakeMessages(client_conn, &client, server_conn, server); + + CompareClientAndServerKeys(&client, server); + + if (options.channel_id_enabled) { + EXPECT_EQ(crypto_config.channel_id_signer()->GetKeyForHostname( + "test.example.com"), + server->crypto_negotiated_params().channel_id); + } + + 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) { + 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); + + ASSERT_GT(b_conn->packets_.size(), b_i); + 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); + } +} + +// 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(); + + 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()); +} + +// 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 == NULL) { + 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(len % 2 == 0); + 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/chromium/net/quic/test_tools/crypto_test_utils.h b/chromium/net/quic/test_tools/crypto_test_utils.h new file mode 100644 index 00000000000..7b0c95274d5 --- /dev/null +++ b/chromium/net/quic/test_tools/crypto_test_utils.h @@ -0,0 +1,132 @@ +// 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 <vector> + +#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 ChannelIDSigner; +class CommonCertSets; +class ProofSource; +class ProofVerifier; +class QuicClock; +class QuicConfig; +class QuicCryptoClientStream; +class QuicCryptoServerConfig; +class QuicCryptoServerStream; +class QuicCryptoStream; +class QuicRandom; + +namespace test { + +class PacketSavingConnection; + +class CryptoTestUtils { + public: + // 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. The key will be the same as is returned by + // ChannelIDSigner's |GetKeyForHostname|. + bool channel_id_enabled; + }; + + // 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); + + // 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(); + + // 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 NULL + // 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", + // NULL); + 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); + + // ChannelIDSignerForTesting returns a ChannelIDSigner that generates keys + // deterministically based on the hostname given in the Sign call. + static ChannelIDSigner* ChannelIDSignerForTesting(); + + private: + static void CompareClientAndServerKeys(QuicCryptoClientStream* client, + QuicCryptoServerStream* server); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_CRYPTO_TEST_UTILS_H_ diff --git a/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc b/chromium/net/quic/test_tools/crypto_test_utils_chromium.cc new file mode 100644 index 00000000000..8aaef425dac --- /dev/null +++ b/chromium/net/quic/test_tools/crypto_test_utils_chromium.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/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/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 { + +class TestProofVerifierChromium : public ProofVerifierChromium { + public: + TestProofVerifierChromium(CertVerifier* cert_verifier, + const std::string& cert_file) + : ProofVerifierChromium(cert_verifier, BoundNetLog()), + cert_verifier_(cert_verifier) { + // 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_; +}; + +// static +ProofSource* CryptoTestUtils::ProofSourceForTesting() { + return new ProofSourceChromium(); +} + +// static +ProofVerifier* CryptoTestUtils::ProofVerifierForTesting() { + TestProofVerifierChromium* proof_verifier = new TestProofVerifierChromium( + CertVerifier::CreateDefault(), "quic_root.crt"); + return proof_verifier; +} + +} // namespace test + +} // namespace net diff --git a/chromium/net/quic/test_tools/crypto_test_utils_nss.cc b/chromium/net/quic/test_tools/crypto_test_utils_nss.cc new file mode 100644 index 00000000000..88c87679b93 --- /dev/null +++ b/chromium/net/quic/test_tools/crypto_test_utils_nss.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/test_tools/crypto_test_utils.h" + +#include <keyhi.h> +#include <pk11pub.h> +#include <sechash.h> + +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "crypto/ec_private_key.h" +#include "net/quic/crypto/channel_id.h" + +using base::StringPiece; +using std::string; + +namespace net { + +namespace test { + +// TODO(rtenneti): Implement NSS support ChannelIDSigner. Convert Sign() to be +// asynchronous using completion callback. After porting TestChannelIDSigner, +// implement real ChannelIDSigner. +class TestChannelIDSigner : public ChannelIDSigner { + public: + virtual ~TestChannelIDSigner() { + STLDeleteValues(&hostname_to_key_); + } + + // ChannelIDSigner implementation. + + virtual bool Sign(const string& hostname, + StringPiece signed_data, + string* out_key, + string* out_signature) OVERRIDE { + crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname); + if (!ecdsa_keypair) { + return false; + } + + *out_key = SerializeKey(ecdsa_keypair->public_key()); + if (out_key->empty()) { + return false; + } + + 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); + HASH_Update(sha256, + reinterpret_cast<const unsigned char*>( + ChannelIDVerifier::kContextStr), + strlen(ChannelIDVerifier::kContextStr) + 1); + HASH_Update(sha256, + reinterpret_cast<const unsigned char*>( + ChannelIDVerifier::kClientToServerStr), + strlen(ChannelIDVerifier::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); + + // The signature consists of a pair of 32-byte numbers. + static const unsigned int kSignatureLength = 32 * 2; + string signature; + SECItem sig_item = { + siBuffer, + reinterpret_cast<unsigned char*>( + WriteInto(&signature, kSignatureLength + 1)), + kSignatureLength + }; + + if (PK11_Sign(ecdsa_keypair->key(), &sig_item, &hash_item) != SECSuccess) { + return false; + } + *out_signature = signature; + return true; + } + + virtual string GetKeyForHostname(const string& hostname) OVERRIDE { + crypto::ECPrivateKey* ecdsa_keypair = HostnameToKey(hostname); + if (!ecdsa_keypair) { + return ""; + } + return SerializeKey(ecdsa_keypair->public_key()); + } + + 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; + } + + crypto::ECPrivateKey* keypair = crypto::ECPrivateKey::Create(); + if (!keypair) { + return NULL; + } + hostname_to_key_[hostname] = keypair; + return keypair; + } + + static string SerializeKey(const SECKEYPublicKey* public_key) { + // public_key->u.ec.publicValue is 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 unsigned int kExpectedKeyLength = 65; + + const unsigned char* const data = public_key->u.ec.publicValue.data; + const unsigned int len = public_key->u.ec.publicValue.len; + if (len != kExpectedKeyLength || data[0] != 0x04) { + return ""; + } + + string key(reinterpret_cast<const char*>(data + 1), kExpectedKeyLength - 1); + return key; + } + + HostnameToKeyMap hostname_to_key_; +}; + +// static +ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() { + return new TestChannelIDSigner(); +} + +} // namespace test + +} // namespace net diff --git a/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc new file mode 100644 index 00000000000..bb08a044aa4 --- /dev/null +++ b/chromium/net/quic/test_tools/crypto_test_utils_openssl.cc @@ -0,0 +1,173 @@ +// 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/secure_hash.h" +#include "net/quic/crypto/channel_id.h" + +using base::StringPiece; +using std::string; + +namespace { + +void EvpMdCtxCleanUp(EVP_MD_CTX* ctx) { + (void)EVP_MD_CTX_cleanup(ctx); +} + +} // namespace anonymous + +namespace net { + +namespace test { + +class TestChannelIDSigner : public ChannelIDSigner { + public: + virtual ~TestChannelIDSigner() { } + + // ChannelIDSigner implementation. + + virtual bool Sign(const string& hostname, + StringPiece signed_data, + string* out_key, + string* out_signature) OVERRIDE { + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key( + HostnameToKey(hostname)); + + *out_key = SerializeKey(ecdsa_key.get()); + if (out_key->empty()) { + return false; + } + + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + crypto::ScopedOpenSSL<EVP_MD_CTX, EvpMdCtxCleanUp> + md_ctx_cleanup(&md_ctx); + + if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, + ecdsa_key.get()) != 1) { + return false; + } + + EVP_DigestUpdate(&md_ctx, ChannelIDVerifier::kContextStr, + strlen(ChannelIDVerifier::kContextStr) + 1); + EVP_DigestUpdate(&md_ctx, ChannelIDVerifier::kClientToServerStr, + strlen(ChannelIDVerifier::kClientToServerStr) + 1); + EVP_DigestUpdate(&md_ctx, signed_data.data(), signed_data.size()); + + size_t sig_len; + if (!EVP_DigestSignFinal(&md_ctx, NULL, &sig_len)) { + return false; + } + + scoped_ptr<uint8[]> der_sig(new uint8[sig_len]); + if (!EVP_DigestSignFinal(&md_ctx, der_sig.get(), &sig_len)) { + return false; + } + + uint8* derp = der_sig.get(); + crypto::ScopedOpenSSL<ECDSA_SIG, ECDSA_SIG_free> sig( + d2i_ECDSA_SIG(NULL, const_cast<const uint8**>(&derp), sig_len)); + if (sig.get() == NULL) { + 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 GetKeyForHostname(const string& hostname) OVERRIDE { + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> ecdsa_key( + HostnameToKey(hostname)); + return SerializeKey(ecdsa_key.get()); + } + + 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::ScopedOpenSSL<BIGNUM, BN_free> k(BN_new()); + CHECK(BN_bin2bn(digest, sizeof(digest), k.get()) != NULL); + + crypto::ScopedOpenSSL<EC_GROUP, EC_GROUP_free> p256( + EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + CHECK(p256.get()); + + crypto::ScopedOpenSSL<EC_KEY, EC_KEY_free> ecdsa_key(EC_KEY_new()); + CHECK(ecdsa_key.get() != NULL && + EC_KEY_set_group(ecdsa_key.get(), p256.get())); + + crypto::ScopedOpenSSL<EC_POINT, EC_POINT_free> point( + EC_POINT_new(p256.get())); + CHECK(EC_POINT_mul(p256.get(), point.get(), k.get(), NULL, NULL, NULL)); + + EC_KEY_set_private_key(ecdsa_key.get(), k.get()); + EC_KEY_set_public_key(ecdsa_key.get(), point.get()); + + crypto::ScopedOpenSSL<EVP_PKEY, EVP_PKEY_free> 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 string SerializeKey(EVP_PKEY* key) { + // 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(key, NULL); + if (len != kExpectedKeyLength) { + return ""; + } + + uint8 buf[kExpectedKeyLength]; + uint8* derp = buf; + i2d_PublicKey(key, &derp); + + return string(reinterpret_cast<char*>(buf + 1), kExpectedKeyLength - 1); + } +}; + +// static +ChannelIDSigner* CryptoTestUtils::ChannelIDSignerForTesting() { + return new TestChannelIDSigner(); +} + +} // namespace test + +} // namespace net diff --git a/chromium/net/quic/test_tools/mock_clock.cc b/chromium/net/quic/test_tools/mock_clock.cc new file mode 100644 index 00000000000..47f23808877 --- /dev/null +++ b/chromium/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/chromium/net/quic/test_tools/mock_clock.h b/chromium/net/quic/test_tools/mock_clock.h new file mode 100644 index 00000000000..5c9631ae84c --- /dev/null +++ b/chromium/net/quic/test_tools/mock_clock.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_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_; +}; + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_MOCK_CLOCK_H_ diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc new file mode 100644 index 00000000000..79c33531714 --- /dev/null +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.cc @@ -0,0 +1,75 @@ +// 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 "testing/gtest/include/gtest/gtest.h" + +namespace net { + +MockCryptoClientStream::MockCryptoClientStream( + const string& server_hostname, + QuicSession* session, + QuicCryptoClientConfig* crypto_config, + HandshakeMode handshake_mode) + : QuicCryptoClientStream(server_hostname, session, crypto_config), + handshake_mode_(handshake_mode) { +} + +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()->OnCryptoHandshakeEvent( + QuicSession::ENCRYPTION_FIRST_ESTABLISHED); + break; + } + + case CONFIRM_HANDSHAKE: { + encryption_established_ = true; + handshake_confirmed_ = true; + SetConfigNegotiated(); + session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + break; + } + + case COLD_START: { + handshake_confirmed_ = false; + encryption_established_ = false; + break; + } + } + return true; +} + +void MockCryptoClientStream::SetConfigNegotiated() { + ASSERT_FALSE(session()->config()->negotiated()); + QuicTagVector cgst; + cgst.push_back(kINAR); + cgst.push_back(kQBIC); + session()->config()->set_congestion_control(cgst, kQBIC); + session()->config()->set_idle_connection_state_lifetime( + QuicTime::Delta::FromSeconds(2 * kDefaultTimeoutSecs), + QuicTime::Delta::FromSeconds(kDefaultTimeoutSecs)); + session()->config()->set_max_streams_per_connection( + 2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection); + + CryptoHandshakeMessage msg; + session()->config()->ToHandshakeMessage(&msg); + string error_details; + const QuicErrorCode error = + session()->config()->ProcessClientHello(msg, &error_details); + ASSERT_EQ(QUIC_NO_ERROR, error); + ASSERT_TRUE(session()->config()->negotiated()); +} + +} // namespace net diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream.h b/chromium/net/quic/test_tools/mock_crypto_client_stream.h new file mode 100644 index 00000000000..2b73b8fd584 --- /dev/null +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream.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_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/quic_crypto_client_stream.h" +#include "net/quic/quic_session.h" + +namespace net { + +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 string& server_hostname, + QuicSession* session, + QuicCryptoClientConfig* crypto_config, + HandshakeMode handshake_mode); + virtual ~MockCryptoClientStream(); + + // CryptoFramerVisitorInterface implementation. + virtual void OnHandshakeMessage( + const CryptoHandshakeMessage& message) OVERRIDE; + + // QuicCryptoClientStream implementation. + virtual bool CryptoConnect() OVERRIDE; + + HandshakeMode handshake_mode_; + + private: + void SetConfigNegotiated(); +}; + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_H_ diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc new file mode 100644 index 00000000000..7578790e136 --- /dev/null +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc @@ -0,0 +1,27 @@ +// 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_crypto_client_stream.h" + +using std::string; + +namespace net { + +MockCryptoClientStreamFactory::MockCryptoClientStreamFactory() + : handshake_mode_(MockCryptoClientStream::CONFIRM_HANDSHAKE) { +} + +QuicCryptoClientStream* +MockCryptoClientStreamFactory::CreateQuicCryptoClientStream( + const string& server_hostname, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) { + return new MockCryptoClientStream(server_hostname, session, crypto_config, + handshake_mode_); +} + +} // namespace net diff --git a/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h new file mode 100644 index 00000000000..e3f2a4aba5c --- /dev/null +++ b/chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h @@ -0,0 +1,39 @@ +// 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/quic_session.h" +#include "net/quic/test_tools/mock_crypto_client_stream.h" + +namespace net { + +class MockCryptoClientStreamFactory : public QuicCryptoClientStreamFactory { + public: + MockCryptoClientStreamFactory(); + + virtual ~MockCryptoClientStreamFactory() {} + + virtual QuicCryptoClientStream* CreateQuicCryptoClientStream( + const string& server_hostname, + QuicSession* session, + QuicCryptoClientConfig* crypto_config) OVERRIDE; + + void set_handshake_mode( + MockCryptoClientStream::HandshakeMode handshake_mode) { + handshake_mode_ = handshake_mode; + } + + private: + MockCryptoClientStream::HandshakeMode handshake_mode_; +}; + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_MOCK_CRYPTO_CLIENT_STREAM_FACTORY_H_ diff --git a/chromium/net/quic/test_tools/mock_random.cc b/chromium/net/quic/test_tools/mock_random.cc new file mode 100644 index 00000000000..19a2832bbaa --- /dev/null +++ b/chromium/net/quic/test_tools/mock_random.cc @@ -0,0 +1,32 @@ +// 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() + : increment_(0) { +} + +void MockRandom::RandBytes(void* data, size_t len) { + memset(data, 'r' + increment_, len); +} + +uint64 MockRandom::RandUint64() { + return 0xDEADBEEF + increment_; +} + +bool MockRandom::RandBool() { + return false; +} + +void MockRandom::Reseed(const void* additional_entropy, size_t entropy_len) { +} + +void MockRandom::ChangeValue() { + increment_++; +} + +} // namespace net diff --git a/chromium/net/quic/test_tools/mock_random.h b/chromium/net/quic/test_tools/mock_random.h new file mode 100644 index 00000000000..544f5ce048a --- /dev/null +++ b/chromium/net/quic/test_tools/mock_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_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: + MockRandom(); + + // QuicRandom: + // Fills the |data| buffer with a repeating byte, initially 'r'. + virtual void RandBytes(void* data, size_t len) OVERRIDE; + // Returns 0xDEADBEEF + the current increment. + virtual uint64 RandUint64() OVERRIDE; + // Returns false. + virtual bool RandBool() 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: + uint8 increment_; +}; + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_MOCK_RANDOM_H_ diff --git a/chromium/net/quic/test_tools/quic_client_session_peer.cc b/chromium/net/quic/test_tools/quic_client_session_peer.cc new file mode 100644 index 00000000000..e88da49e15e --- /dev/null +++ b/chromium/net/quic/test_tools/quic_client_session_peer.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/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()->set_max_streams_per_connection(max_streams, + default_streams); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_client_session_peer.h b/chromium/net/quic/test_tools/quic_client_session_peer.h new file mode 100644 index 00000000000..7217d9d8600 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_client_session_peer.h @@ -0,0 +1,29 @@ +// 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); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicClientSessionPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_CLIENT_SESSION_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_connection_peer.cc b/chromium/net/quic/test_tools/quic_connection_peer.cc new file mode 100644 index 00000000000..610c505161b --- /dev/null +++ b/chromium/net/quic/test_tools/quic_connection_peer.cc @@ -0,0 +1,181 @@ +// 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/quic_congestion_manager.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/test_tools/quic_framer_peer.h" + +namespace net { +namespace test { + +// static +void QuicConnectionPeer::SendAck(QuicConnection* connection) { + connection->SendAck(); +} + +// static +void QuicConnectionPeer::SetReceiveAlgorithm( + QuicConnection* connection, + ReceiveAlgorithmInterface* receive_algorithm) { + connection->congestion_manager_.receive_algorithm_.reset(receive_algorithm); +} + +// static +void QuicConnectionPeer::SetSendAlgorithm( + QuicConnection* connection, + SendAlgorithmInterface* send_algorithm) { + connection->congestion_manager_.send_algorithm_.reset(send_algorithm); +} + +// static +QuicAckFrame* QuicConnectionPeer::CreateAckFrame(QuicConnection* connection) { + return connection->CreateAckFrame(); +} + +// static +QuicConnectionVisitorInterface* QuicConnectionPeer::GetVisitor( + QuicConnection* connection) { + return connection->visitor_; +} + +// static +QuicPacketCreator* QuicConnectionPeer::GetPacketCreator( + QuicConnection* connection) { + return &connection->packet_creator_; +} + +bool QuicConnectionPeer::GetReceivedTruncatedAck(QuicConnection* connection) { + return connection->received_truncated_ack_; +} + +// static +size_t QuicConnectionPeer::GetNumRetransmissionTimeouts( + QuicConnection* connection) { + return connection->retransmission_timeouts_.size(); +} + +// static +QuicTime::Delta QuicConnectionPeer::GetNetworkTimeout( + QuicConnection* connection) { + return connection->idle_network_timeout_; +} + +// static +bool QuicConnectionPeer::IsSavedForRetransmission( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number) { + return ContainsKey(connection->retransmission_map_, sequence_number); +} + +// static +size_t QuicConnectionPeer::GetRetransmissionCount( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number) { + QuicConnection::RetransmissionMap::iterator it = + connection->retransmission_map_.find(sequence_number); + DCHECK(connection->retransmission_map_.end() != it); + return it->second.number_retransmissions; +} + +// static +QuicPacketEntropyHash QuicConnectionPeer::GetSentEntropyHash( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number) { + return connection->sent_entropy_manager_.EntropyHash(sequence_number); +} + +// static +bool QuicConnectionPeer::IsValidEntropy( + QuicConnection* connection, + QuicPacketSequenceNumber largest_observed, + const SequenceNumberSet& missing_packets, + QuicPacketEntropyHash entropy_hash) { + return connection->sent_entropy_manager_.IsValidEntropy( + largest_observed, missing_packets, entropy_hash); +} + +// 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::SwapCrypters(QuicConnection* connection, + QuicFramer* framer) { + framer->SwapCryptersForTest(&connection->framer_); +} + +// static +void QuicConnectionPeer:: SetMaxPacketsPerRetransmissionAlarm( + QuicConnection* connection, + int max_packets) { + connection->max_packets_per_retransmission_alarm_ = max_packets; +} + +// static +QuicConnectionHelperInterface* QuicConnectionPeer::GetHelper( + QuicConnection* connection) { + return connection->helper_.get(); +} + +// static +QuicFramer* QuicConnectionPeer::GetFramer(QuicConnection* connection) { + return &connection->framer_; +} + +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::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(); +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_connection_peer.h b/chromium/net/quic/test_tools/quic_connection_peer.h new file mode 100644 index 00000000000..4438353ddaa --- /dev/null +++ b/chromium/net/quic/test_tools/quic_connection_peer.h @@ -0,0 +1,105 @@ +// 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_protocol.h" +#include "net/quic/quic_stats.h" + +namespace net { + +struct QuicAckFrame; +struct QuicPacketHeader; +class QuicAlarm; +class QuicConnection; +class QuicConnectionHelperInterface; +class QuicConnectionVisitorInterface; +class QuicFecGroup; +class QuicFramer; +class QuicPacketCreator; +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 QuicConnectionVisitorInterface* GetVisitor( + QuicConnection* connection); + + static QuicPacketCreator* GetPacketCreator(QuicConnection* connection); + + static bool GetReceivedTruncatedAck(QuicConnection* connection); + + static size_t GetNumRetransmissionTimeouts(QuicConnection* connection); + + static QuicTime::Delta GetNetworkTimeout(QuicConnection* connection); + + static bool IsSavedForRetransmission( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number); + + static size_t GetRetransmissionCount( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number); + + static QuicPacketEntropyHash GetSentEntropyHash( + QuicConnection* connection, + QuicPacketSequenceNumber sequence_number); + + static bool IsValidEntropy(QuicConnection* connection, + QuicPacketSequenceNumber largest_observed, + const SequenceNumberSet& missing_packets, + QuicPacketEntropyHash entropy_hash); + + 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 SwapCrypters(QuicConnection* connection, QuicFramer* framer); + + static void SetMaxPacketsPerRetransmissionAlarm(QuicConnection* connection, + int max_packets); + + 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* GetRetransmissionAlarm(QuicConnection* connection); + static QuicAlarm* GetSendAlarm(QuicConnection* connection); + static QuicAlarm* GetTimeoutAlarm(QuicConnection* connection); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicConnectionPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_framer_peer.cc b/chromium/net/quic/test_tools/quic_framer_peer.cc new file mode 100644 index 00000000000..5ec52dc7512 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_framer_peer.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/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::SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid) { + framer->last_serialized_guid_ = guid; +} + +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::SetVersion(QuicFramer* framer, QuicVersion version) { + framer->quic_version_ = version; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_framer_peer.h b/chromium/net/quic/test_tools/quic_framer_peer.h new file mode 100644 index 00000000000..0508f5c7a7e --- /dev/null +++ b/chromium/net/quic/test_tools/quic_framer_peer.h @@ -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. + +#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 SetLastSerializedGuid(QuicFramer* framer, QuicGuid guid); + static void SetLastSequenceNumber( + QuicFramer* framer, + QuicPacketSequenceNumber packet_sequence_number); + static void SetIsServer(QuicFramer* framer, bool is_server); + static void SetVersion(QuicFramer* framer, QuicVersion version); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicFramerPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_FRAMER_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.cc b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc new file mode 100644 index 00000000000..4451f02be84 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_packet_creator_peer.cc @@ -0,0 +1,30 @@ +// 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::SetIsServer(QuicPacketCreator* creator, + bool is_server) { + creator->is_server_ = is_server; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_packet_creator_peer.h b/chromium/net/quic/test_tools/quic_packet_creator_peer.h new file mode 100644 index 00000000000..816afa96189 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_packet_creator_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_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 SetIsServer(QuicPacketCreator* creator, bool is_server); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicPacketCreatorPeer); +}; + +} // namespace test + +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_PACKET_CREATOR_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc new file mode 100644 index 00000000000..d25a209b5f4 --- /dev/null +++ b/chromium/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::RecalculateEntropyHash( + QuicReceivedPacketManager* received_packet_manager, + QuicPacketSequenceNumber peer_least_unacked, + QuicPacketEntropyHash entropy_hash) { + received_packet_manager->RecalculateEntropyHash(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/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h b/chromium/net/quic/test_tools/quic_received_packet_manager_peer.h new file mode 100644 index 00000000000..4607a0c902d --- /dev/null +++ b/chromium/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 RecalculateEntropyHash( + 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/chromium/net/quic/test_tools/quic_session_peer.cc b/chromium/net/quic/test_tools/quic_session_peer.cc new file mode 100644 index 00000000000..66caa15a4b6 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_session_peer.cc @@ -0,0 +1,37 @@ +// 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 +ReliableQuicStream* QuicSessionPeer::CreateIncomingReliableStream( + QuicSession* session, QuicStreamId id) { + return session->CreateIncomingReliableStream(id); +} + +// static +BlockedList<QuicStreamId>* QuicSessionPeer::GetWriteblockedStreams( + QuicSession* session) { + return &session->write_blocked_streams_; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_session_peer.h b/chromium/net/quic/test_tools/quic_session_peer.h new file mode 100644 index 00000000000..6f9a8f394e2 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_session_peer.h @@ -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. + +#ifndef NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_ +#define NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_ + +#include "net/quic/blocked_list.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class QuicSession; +class ReliableQuicStream; + +namespace test { + +class QuicSessionPeer { + public: + static void SetNextStreamId(QuicSession* session, QuicStreamId id); + static void SetMaxOpenStreams(QuicSession* session, uint32 max_streams); + static ReliableQuicStream* CreateIncomingReliableStream(QuicSession* session, + QuicStreamId id); + static BlockedList<QuicStreamId>* GetWriteblockedStreams( + QuicSession* session); + + private: + DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_SESSION_PEER_H_ diff --git a/chromium/net/quic/test_tools/quic_test_utils.cc b/chromium/net/quic/test_tools/quic_test_utils.cc new file mode 100644 index 00000000000..2562b07d599 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_test_utils.cc @@ -0,0 +1,453 @@ +// 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/stl_util.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/spdy/spdy_frame_builder.h" + +using base::StringPiece; +using std::max; +using std::min; +using std::string; +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 + +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, 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, 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::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::OnRstStreamFrame( + const QuicRstStreamFrame& frame) { + return true; +} + +bool NoOpFramerVisitor::OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) { + return true; +} + +bool NoOpFramerVisitor::OnGoAwayFrame(const QuicGoAwayFrame& frame) { + return true; +} + +FramerVisitorCapturingFrames::FramerVisitorCapturingFrames() : frame_count_(0) { +} + +FramerVisitorCapturingFrames::~FramerVisitorCapturingFrames() { +} + +bool FramerVisitorCapturingFrames::OnPacketHeader( + const QuicPacketHeader& header) { + header_ = header; + frame_count_ = 0; + return true; +} + +bool FramerVisitorCapturingFrames::OnStreamFrame(const QuicStreamFrame& frame) { + // TODO(ianswett): Own the underlying string, so it will not exist outside + // this callback. + stream_frames_.push_back(frame); + ++frame_count_; + return true; +} + +bool FramerVisitorCapturingFrames::OnAckFrame(const QuicAckFrame& frame) { + ack_.reset(new QuicAckFrame(frame)); + ++frame_count_; + return true; +} + +bool FramerVisitorCapturingFrames::OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& frame) { + feedback_.reset(new QuicCongestionFeedbackFrame(frame)); + ++frame_count_; + return true; +} + +bool FramerVisitorCapturingFrames::OnRstStreamFrame( + const QuicRstStreamFrame& frame) { + rst_.reset(new QuicRstStreamFrame(frame)); + ++frame_count_; + return true; +} + +bool FramerVisitorCapturingFrames::OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) { + close_.reset(new QuicConnectionCloseFrame(frame)); + ++frame_count_; + return true; +} + +bool FramerVisitorCapturingFrames::OnGoAwayFrame(const QuicGoAwayFrame& frame) { + goaway_.reset(new QuicGoAwayFrame(frame)); + ++frame_count_; + return true; +} + +void FramerVisitorCapturingFrames::OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) { + version_negotiation_packet_.reset(new QuicVersionNegotiationPacket(packet)); + frame_count_ = 0; +} + +FramerVisitorCapturingPublicReset::FramerVisitorCapturingPublicReset() { +} + +FramerVisitorCapturingPublicReset::~FramerVisitorCapturingPublicReset() { +} + +void FramerVisitorCapturingPublicReset::OnPublicResetPacket( + const QuicPublicResetPacket& public_reset) { + public_reset_packet_ = public_reset; +} + +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); +} + +MockConnection::MockConnection(QuicGuid guid, + IPEndPoint address, + bool is_server) + : QuicConnection(guid, address, new testing::NiceMock<MockHelper>(), + is_server, QuicVersionMax()), + has_mock_helper_(true) { +} + +MockConnection::MockConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper, + bool is_server) + : QuicConnection(guid, address, helper, is_server, QuicVersionMax()), + has_mock_helper_(false) { +} + +MockConnection::~MockConnection() { +} + +void MockConnection::AdvanceTime(QuicTime::Delta delta) { + CHECK(has_mock_helper_) << "Cannot advance time unless a MockClock is being" + " used"; + static_cast<MockHelper*>(helper())->AdvanceTime(delta); +} + +PacketSavingConnection::PacketSavingConnection(QuicGuid guid, + IPEndPoint address, + bool is_server) + : MockConnection(guid, address, is_server) { +} + +PacketSavingConnection::~PacketSavingConnection() { + STLDeleteElements(&packets_); + STLDeleteElements(&encrypted_packets_); +} + +bool PacketSavingConnection::SendOrQueuePacket( + EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + QuicPacketEntropyHash entropy_hash, + HasRetransmittableData retransmittable) { + packets_.push_back(packet); + QuicEncryptedPacket* encrypted = + framer_.EncryptPacket(level, sequence_number, *packet); + encrypted_packets_.push_back(encrypted); + return true; +} + +MockSession::MockSession(QuicConnection* connection, bool is_server) + : QuicSession(connection, DefaultQuicConfig(), is_server) { + ON_CALL(*this, WriteData(_, _, _, _)) + .WillByDefault(testing::Return(QuicConsumedData(0, false))); +} + +MockSession::~MockSession() { +} + +TestSession::TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server) + : QuicSession(connection, config, is_server), + crypto_stream_(NULL) { +} + +TestSession::~TestSession() {} + +void TestSession::SetCryptoStream(QuicCryptoStream* stream) { + crypto_stream_ = stream; +} + +QuicCryptoStream* TestSession::GetCryptoStream() { + return crypto_stream_; +} + +MockSendAlgorithm::MockSendAlgorithm() { +} + +MockSendAlgorithm::~MockSendAlgorithm() { +} + +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 + +void CompareCharArraysWithHexError( + const string& description, + const char* actual, + const int actual_len, + const char* expected, + const int 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); +} + +void CompareQuicDataWithHexError( + const string& description, + QuicData* actual, + QuicData* expected) { + CompareCharArraysWithHexError( + description, + actual->data(), actual->length(), + expected->data(), expected->length()); +} + +static QuicPacket* ConstructPacketFromHandshakeMessage( + QuicGuid guid, + const CryptoHandshakeMessage& message, + bool should_include_version) { + CryptoFramer crypto_framer; + scoped_ptr<QuicData> data(crypto_framer.ConstructHandshakeMessage(message)); + QuicFramer quic_framer(QuicVersionMax(), QuicTime::Zero(), false); + + QuicPacketHeader header; + header.public_header.guid = guid; + 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, + data->AsStringPiece()); + + QuicFrame frame(&stream_frame); + QuicFrames frames; + frames.push_back(frame); + return quic_framer.BuildUnsizedDataPacket(header, frames).packet; +} + +QuicPacket* ConstructHandshakePacket(QuicGuid guid, QuicTag tag) { + CryptoHandshakeMessage message; + message.set_tag(tag); + return ConstructPacketFromHandshakeMessage(guid, message, false); +} + +size_t GetPacketLengthForOneStream(QuicVersion version, + bool include_version, + InFecGroup is_in_fec_group, + size_t* payload_length) { + *payload_length = 1; + const size_t stream_length = + NullEncrypter().GetCiphertextSize(*payload_length) + + QuicPacketCreator::StreamFramePacketOverhead( + version, PACKET_8BYTE_GUID, include_version, + PACKET_6BYTE_SEQUENCE_NUMBER, is_in_fec_group); + const size_t ack_length = NullEncrypter().GetCiphertextSize( + QuicFramer::GetMinAckFrameSize()) + + GetPacketHeaderSize(PACKET_8BYTE_GUID, include_version, + PACKET_6BYTE_SEQUENCE_NUMBER, is_in_fec_group); + if (stream_length < ack_length) { + *payload_length = 1 + ack_length - stream_length; + } + + return NullEncrypter().GetCiphertextSize(*payload_length) + + QuicPacketCreator::StreamFramePacketOverhead( + version, PACKET_8BYTE_GUID, include_version, + PACKET_6BYTE_SEQUENCE_NUMBER, is_in_fec_group); +} + +// Size in bytes of the stream frame fields for an arbitrary StreamID and +// offset and the last frame in a packet. +size_t GetMinStreamFrameSize(QuicVersion version) { + return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; +} + +QuicPacketEntropyHash TestEntropyCalculator::EntropyHash( + QuicPacketSequenceNumber sequence_number) const { + return 1u; +} + +QuicConfig DefaultQuicConfig() { + QuicConfig config; + config.SetDefaults(); + return config; +} + +bool TestDecompressorVisitor::OnDecompressedData(StringPiece data) { + data.AppendToString(&data_); + return true; +} + +void TestDecompressorVisitor::OnDecompressionError() { + error_ = true; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/quic_test_utils.h b/chromium/net/quic/test_tools/quic_test_utils.h new file mode 100644 index 00000000000..65fba73d3b9 --- /dev/null +++ b/chromium/net/quic/test_tools/quic_test_utils.h @@ -0,0 +1,376 @@ +// 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/send_algorithm_interface.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_framer.h" +#include "net/quic/quic_session.h" +#include "net/quic/quic_spdy_decompressor.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 { + +void CompareCharArraysWithHexError(const std::string& description, + const char* actual, + const int actual_len, + const char* expected, + const int expected_len); + +void CompareQuicDataWithHexError(const std::string& description, + QuicData* actual, + QuicData* expected); + +// 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, + InFecGroup is_in_fec_group, + size_t* payload_length); + +// Size in bytes of the stream frame fields for an arbitrary StreamID and +// offset and the last frame in a packet. +size_t GetMinStreamFrameSize(QuicVersion version); + +string SerializeUncompressedHeaders(const SpdyHeaderBlock& headers); + +// Returns QuicConfig set to default values. +QuicConfig DefaultQuicConfig(); + +class MockFramerVisitor : public QuicFramerVisitorInterface { + public: + MockFramerVisitor(); + ~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(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(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_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 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 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 void OnPacketComplete() OVERRIDE {} + + private: + DISALLOW_COPY_AND_ASSIGN(NoOpFramerVisitor); +}; + +class FramerVisitorCapturingPublicReset : public NoOpFramerVisitor { + public: + FramerVisitorCapturingPublicReset(); + virtual ~FramerVisitorCapturingPublicReset(); + + virtual void OnPublicResetPacket( + const QuicPublicResetPacket& packet) OVERRIDE; + + const QuicPublicResetPacket public_reset_packet() { + return public_reset_packet_; + } + + private: + QuicPublicResetPacket public_reset_packet_; +}; + +class FramerVisitorCapturingFrames : public NoOpFramerVisitor { + public: + FramerVisitorCapturingFrames(); + virtual ~FramerVisitorCapturingFrames(); + + // NoOpFramerVisitor + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE; + virtual bool OnPacketHeader(const QuicPacketHeader& header) OVERRIDE; + virtual bool OnStreamFrame(const QuicStreamFrame& frame) OVERRIDE; + virtual bool OnAckFrame(const QuicAckFrame& frame) OVERRIDE; + virtual bool OnCongestionFeedbackFrame( + const QuicCongestionFeedbackFrame& frame) OVERRIDE; + virtual bool OnRstStreamFrame(const QuicRstStreamFrame& frame) OVERRIDE; + virtual bool OnConnectionCloseFrame( + const QuicConnectionCloseFrame& frame) OVERRIDE; + virtual bool OnGoAwayFrame(const QuicGoAwayFrame& frame) OVERRIDE; + + size_t frame_count() const { return frame_count_; } + QuicPacketHeader* header() { return &header_; } + const std::vector<QuicStreamFrame>* stream_frames() const { + return &stream_frames_; + } + QuicAckFrame* ack() { return ack_.get(); } + QuicCongestionFeedbackFrame* feedback() { return feedback_.get(); } + QuicRstStreamFrame* rst() { return rst_.get(); } + QuicConnectionCloseFrame* close() { return close_.get(); } + QuicGoAwayFrame* goaway() { return goaway_.get(); } + QuicVersionNegotiationPacket* version_negotiation_packet() { + return version_negotiation_packet_.get(); + } + + private: + size_t frame_count_; + QuicPacketHeader header_; + std::vector<QuicStreamFrame> stream_frames_; + scoped_ptr<QuicAckFrame> ack_; + scoped_ptr<QuicCongestionFeedbackFrame> feedback_; + scoped_ptr<QuicRstStreamFrame> rst_; + scoped_ptr<QuicConnectionCloseFrame> close_; + scoped_ptr<QuicGoAwayFrame> goaway_; + scoped_ptr<QuicVersionNegotiationPacket> version_negotiation_packet_; + + DISALLOW_COPY_AND_ASSIGN(FramerVisitorCapturingFrames); +}; + +class MockConnectionVisitor : public QuicConnectionVisitorInterface { + public: + MockConnectionVisitor(); + virtual ~MockConnectionVisitor(); + + MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const std::vector<QuicStreamFrame>& frame)); + MOCK_METHOD1(OnRstStream, void(const QuicRstStreamFrame& frame)); + MOCK_METHOD1(OnGoAway, void(const QuicGoAwayFrame& frame)); + MOCK_METHOD2(ConnectionClose, void(QuicErrorCode error, bool from_peer)); + MOCK_METHOD1(OnAck, void(const SequenceNumberSet& acked_packets)); + MOCK_METHOD0(OnCanWrite, bool()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockConnectionVisitor); +}; + +class MockHelper : public QuicConnectionHelperInterface { + public: + MockHelper(); + virtual ~MockHelper(); + + MOCK_METHOD1(SetConnection, void(QuicConnection* connection)); + const QuicClock* GetClock() const; + QuicRandom* GetRandomGenerator(); + void AdvanceTime(QuicTime::Delta delta); + MOCK_METHOD2(WritePacketToWire, int(const QuicEncryptedPacket& packet, + int* error)); + MOCK_METHOD0(IsWriteBlockedDataBuffered, bool()); + MOCK_METHOD1(IsWriteBlocked, bool(int)); + virtual QuicAlarm* CreateAlarm(QuicAlarm::Delegate* delegate); + + private: + MockClock clock_; + MockRandom random_generator_; +}; + +class MockConnection : public QuicConnection { + public: + // Uses a MockHelper. + MockConnection(QuicGuid guid, IPEndPoint address, bool is_server); + MockConnection(QuicGuid guid, + IPEndPoint address, + QuicConnectionHelperInterface* helper, + bool is_server); + 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(SendRstStream, void(QuicStreamId id, + QuicRstStreamErrorCode error)); + MOCK_METHOD3(SendGoAway, void(QuicErrorCode error, + QuicStreamId last_good_stream_id, + const string& reason)); + MOCK_METHOD0(OnCanWrite, bool()); + + 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: + const bool has_mock_helper_; + + DISALLOW_COPY_AND_ASSIGN(MockConnection); +}; + +class PacketSavingConnection : public MockConnection { + public: + PacketSavingConnection(QuicGuid guid, IPEndPoint address, bool is_server); + virtual ~PacketSavingConnection(); + + virtual bool SendOrQueuePacket( + EncryptionLevel level, + QuicPacketSequenceNumber sequence_number, + QuicPacket* packet, + QuicPacketEntropyHash entropy_hash, + HasRetransmittableData has_retransmittable_data) OVERRIDE; + + std::vector<QuicPacket*> packets_; + std::vector<QuicEncryptedPacket*> encrypted_packets_; + + private: + DISALLOW_COPY_AND_ASSIGN(PacketSavingConnection); +}; + +class MockSession : public QuicSession { + public: + MockSession(QuicConnection* connection, bool is_server); + virtual ~MockSession(); + + MOCK_METHOD4(OnPacket, bool(const IPEndPoint& self_address, + const IPEndPoint& peer_address, + const QuicPacketHeader& header, + const std::vector<QuicStreamFrame>& frame)); + MOCK_METHOD2(ConnectionClose, void(QuicErrorCode error, bool from_peer)); + MOCK_METHOD1(CreateIncomingReliableStream, + ReliableQuicStream*(QuicStreamId id)); + MOCK_METHOD0(GetCryptoStream, QuicCryptoStream*()); + MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*()); + MOCK_METHOD4(WriteData, QuicConsumedData(QuicStreamId id, + base::StringPiece data, + QuicStreamOffset offset, + bool fin)); + MOCK_METHOD0(IsHandshakeComplete, bool()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockSession); +}; + +class TestSession : public QuicSession { + public: + TestSession(QuicConnection* connection, + const QuicConfig& config, + bool is_server); + virtual ~TestSession(); + + MOCK_METHOD1(CreateIncomingReliableStream, + ReliableQuicStream*(QuicStreamId id)); + MOCK_METHOD0(CreateOutgoingReliableStream, ReliableQuicStream*()); + + void SetCryptoStream(QuicCryptoStream* stream); + + virtual QuicCryptoStream* GetCryptoStream(); + + private: + QuicCryptoStream* crypto_stream_; + DISALLOW_COPY_AND_ASSIGN(TestSession); +}; + +class MockSendAlgorithm : public SendAlgorithmInterface { + public: + MockSendAlgorithm(); + virtual ~MockSendAlgorithm(); + + MOCK_METHOD3(OnIncomingQuicCongestionFeedbackFrame, + void(const QuicCongestionFeedbackFrame&, + QuicTime feedback_receive_time, + const SentPacketsMap&)); + MOCK_METHOD3(OnIncomingAck, + void(QuicPacketSequenceNumber, QuicByteCount, QuicTime::Delta)); + MOCK_METHOD1(OnIncomingLoss, void(QuicTime)); + MOCK_METHOD4(SentPacket, void(QuicTime sent_time, QuicPacketSequenceNumber, + QuicByteCount, Retransmission)); + MOCK_METHOD2(AbandoningPacket, void(QuicPacketSequenceNumber sequence_number, + QuicByteCount abandoned_bytes)); + MOCK_METHOD4(TimeUntilSend, QuicTime::Delta(QuicTime now, Retransmission, + HasRetransmittableData, + IsHandshake)); + MOCK_METHOD0(BandwidthEstimate, QuicBandwidth(void)); + MOCK_METHOD0(SmoothedRtt, QuicTime::Delta(void)); + MOCK_METHOD0(RetransmissionDelay, QuicTime::Delta(void)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockSendAlgorithm); +}; + +class TestEntropyCalculator : + public QuicReceivedEntropyHashCalculatorInterface { + public: + TestEntropyCalculator() { } + virtual ~TestEntropyCalculator() { } + + virtual QuicPacketEntropyHash EntropyHash( + QuicPacketSequenceNumber sequence_number) const OVERRIDE; +}; + +class TestDecompressorVisitor : public QuicSpdyDecompressor::Visitor { + public: + virtual ~TestDecompressorVisitor() {} + virtual bool OnDecompressedData(base::StringPiece data) OVERRIDE; + virtual void OnDecompressionError() OVERRIDE; + + string data() { return data_; } + bool error() { return error_; } + + private: + string data_; + bool error_; +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_QUIC_TEST_UTILS_H_ diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc new file mode 100644 index 00000000000..31a64e94a45 --- /dev/null +++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.cc @@ -0,0 +1,32 @@ +// 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 "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; +} + +void ReliableQuicStreamPeer::SetHeadersDecompressed( + ReliableQuicStream* stream, + bool headers_decompressed) { + stream->headers_decompressed_ = headers_decompressed; +} + +} // namespace test +} // namespace net diff --git a/chromium/net/quic/test_tools/reliable_quic_stream_peer.h b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h new file mode 100644 index 00000000000..346a9b46411 --- /dev/null +++ b/chromium/net/quic/test_tools/reliable_quic_stream_peer.h @@ -0,0 +1,32 @@ +// 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 SetHeadersDecompressed(ReliableQuicStream* stream, + bool headers_decompressed); + + private: + DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer); +}; + +} // namespace test +} // namespace net + +#endif // NET_QUIC_TEST_TOOLS_RELIABLE_QUIC_STREAM_PEER_H_ diff --git a/chromium/net/quic/test_tools/simple_quic_framer.cc b/chromium/net/quic/test_tools/simple_quic_framer.cc new file mode 100644 index 00000000000..46be3a83c5a --- /dev/null +++ b/chromium/net/quic/test_tools/simple_quic_framer.cc @@ -0,0 +1,198 @@ +// 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 "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 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 {} + virtual void OnVersionNegotiationPacket( + const QuicVersionNegotiationPacket& packet) OVERRIDE {} + virtual void OnRevivedPacket() 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.data.as_string()); + QuicStreamFrame stream_frame(frame); + // Make sure that the stream frame points to this data. + stream_frame.data = stream_data_.back(); + 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 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 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 QuicFecData& fec_data() const { + return fec_data_; + } + + private: + QuicErrorCode error_; + bool has_header_; + QuicPacketHeader header_; + QuicFecData fec_data_; + string fec_redundancy_; + vector<QuicAckFrame> ack_frames_; + vector<QuicCongestionFeedbackFrame> feedback_frames_; + vector<QuicStreamFrame> stream_frames_; + vector<QuicRstStreamFrame> rst_stream_frames_; + vector<QuicGoAwayFrame> goaway_frames_; + vector<QuicConnectionCloseFrame> connection_close_frames_; + vector<string> stream_data_; + + DISALLOW_COPY_AND_ASSIGN(SimpleFramerVisitor); +}; + +SimpleQuicFramer::SimpleQuicFramer() + : framer_(QuicVersionMax(), 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); +} + +const QuicPacketHeader& SimpleQuicFramer::header() const { + return visitor_->header(); +} + +const QuicFecData& SimpleQuicFramer::fec_data() const { + return visitor_->fec_data(); +} + +QuicFramer* SimpleQuicFramer::framer() { + return &framer_; +} + +size_t SimpleQuicFramer::num_frames() const { + return ack_frames().size() + + stream_frames().size() + + feedback_frames().size() + + rst_stream_frames().size() + + goaway_frames().size() + + connection_close_frames().size(); +} + +const vector<QuicAckFrame>& SimpleQuicFramer::ack_frames() const { + return visitor_->ack_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/chromium/net/quic/test_tools/simple_quic_framer.h b/chromium/net/quic/test_tools/simple_quic_framer.h new file mode 100644 index 00000000000..4416019dba3 --- /dev/null +++ b/chromium/net/quic/test_tools/simple_quic_framer.h @@ -0,0 +1,58 @@ +// 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/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(); + ~SimpleQuicFramer(); + + bool ProcessPacket(const QuicEncryptedPacket& packet); + bool ProcessPacket(const QuicPacket& packet); + + 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<QuicGoAwayFrame>& goaway_frames() const; + const std::vector<QuicRstStreamFrame>& rst_stream_frames() const; + const std::vector<QuicStreamFrame>& stream_frames() const; + const QuicFecData& fec_data() const; + QuicFramer* framer(); + + 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/chromium/net/quic/test_tools/test_task_runner.cc b/chromium/net/quic/test_tools/test_task_runner.cc new file mode 100644 index 00000000000..385fd5dfa82 --- /dev/null +++ b/chromium/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/chromium/net/quic/test_tools/test_task_runner.h b/chromium/net/quic/test_tools/test_task_runner.h new file mode 100644 index 00000000000..e25bf2aef46 --- /dev/null +++ b/chromium/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_ |