summaryrefslogtreecommitdiff
path: root/chromium/net/quic
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/quic')
-rw-r--r--chromium/net/quic/blocked_list.h91
-rw-r--r--chromium/net/quic/blocked_list_test.cc83
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator.cc78
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator.h64
-rw-r--r--chromium/net/quic/congestion_control/available_channel_estimator_test.cc109
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator.cc110
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator.h61
-rw-r--r--chromium/net/quic/congestion_control/channel_estimator_test.cc224
-rw-r--r--chromium/net/quic/congestion_control/cube_root.cc87
-rw-r--r--chromium/net/quic/congestion_control/cube_root.h21
-rw-r--r--chromium/net/quic/congestion_control/cube_root_test.cc47
-rw-r--r--chromium/net/quic/congestion_control/cubic.cc198
-rw-r--r--chromium/net/quic/congestion_control/cubic.h87
-rw-r--r--chromium/net/quic/congestion_control/cubic_test.cc150
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_receiver.cc35
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_receiver.h44
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_sender.cc121
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_sender.h65
-rw-r--r--chromium/net/quic/congestion_control/fix_rate_test.cc120
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start.cc112
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start.h67
-rw-r--r--chromium/net/quic/congestion_control/hybrid_slow_start_test.cc108
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.cc174
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up.h65
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_bitrate_ramp_up_test.cc404
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.cc258
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector.h173
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_overuse_detector_test.cc1114
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe.cc117
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe.h58
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_probe_test.cc81
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver.cc48
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver.h46
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_receiver_test.cc55
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.cc505
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender.h100
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_sender_test.cc565
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine.cc163
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine.h94
-rw-r--r--chromium/net/quic/congestion_control/inter_arrival_state_machine_test.cc126
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket.cc50
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket.h50
-rw-r--r--chromium/net/quic/congestion_control/leaky_bucket_test.cc75
-rw-r--r--chromium/net/quic/congestion_control/paced_sender.cc50
-rw-r--r--chromium/net/quic/congestion_control/paced_sender.h41
-rw-r--r--chromium/net/quic/congestion_control/paced_sender_test.cc77
-rw-r--r--chromium/net/quic/congestion_control/quic_congestion_control_test.cc136
-rw-r--r--chromium/net/quic/congestion_control/quic_congestion_manager.cc214
-rw-r--r--chromium/net/quic/congestion_control/quic_congestion_manager.h121
-rw-r--r--chromium/net/quic/congestion_control/quic_congestion_manager_test.cc236
-rw-r--r--chromium/net/quic/congestion_control/quic_max_sized_map.h77
-rw-r--r--chromium/net/quic/congestion_control/quic_max_sized_map_test.cc66
-rw-r--r--chromium/net/quic/congestion_control/receive_algorithm_interface.cc27
-rw-r--r--chromium/net/quic/congestion_control/receive_algorithm_interface.h44
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_interface.cc34
-rw-r--r--chromium/net/quic/congestion_control/send_algorithm_interface.h90
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender.cc271
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender.h118
-rw-r--r--chromium/net/quic/congestion_control/tcp_cubic_sender_test.cc256
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver.cc36
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver.h41
-rw-r--r--chromium/net/quic/congestion_control/tcp_receiver_test.cc38
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter.h69
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_nss.cc387
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_openssl.cc152
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_decrypter_test.cc386
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter.h74
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_nss.cc397
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_openssl.cc165
-rw-r--r--chromium/net/quic/crypto/aes_128_gcm_12_encrypter_test.cc348
-rw-r--r--chromium/net/quic/crypto/cert_compressor.cc646
-rw-r--r--chromium/net/quic/crypto/cert_compressor.h55
-rw-r--r--chromium/net/quic/crypto/cert_compressor_test.cc140
-rw-r--r--chromium/net/quic/crypto/channel_id.cc14
-rw-r--r--chromium/net/quic/crypto/channel_id.h64
-rw-r--r--chromium/net/quic/crypto/channel_id_nss.cc83
-rw-r--r--chromium/net/quic/crypto/channel_id_openssl.cc89
-rw-r--r--chromium/net/quic/crypto/channel_id_test.cc303
-rw-r--r--chromium/net/quic/crypto/common_cert_set.cc158
-rw-r--r--chromium/net/quic/crypto/common_cert_set.h47
-rw-r--r--chromium/net/quic/crypto/common_cert_set_0.c223
-rw-r--r--chromium/net/quic/crypto/common_cert_set_1_50.inc9794
-rw-r--r--chromium/net/quic/crypto/common_cert_set_51_100.inc11308
-rw-r--r--chromium/net/quic/crypto/common_cert_set_test.cc109
-rw-r--r--chromium/net/quic/crypto/crypto_framer.cc292
-rw-r--r--chromium/net/quic/crypto/crypto_framer.h118
-rw-r--r--chromium/net/quic/crypto/crypto_framer_test.cc485
-rw-r--r--chromium/net/quic/crypto/crypto_handshake.cc897
-rw-r--r--chromium/net/quic/crypto/crypto_handshake.h399
-rw-r--r--chromium/net/quic/crypto/crypto_handshake_test.cc360
-rw-r--r--chromium/net/quic/crypto/crypto_protocol.h131
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer.cc90
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer.h49
-rw-r--r--chromium/net/quic/crypto/crypto_secret_boxer_test.cc41
-rw-r--r--chromium/net/quic/crypto/crypto_server_config.cc1070
-rw-r--r--chromium/net/quic/crypto/crypto_server_config.h364
-rw-r--r--chromium/net/quic/crypto/crypto_server_config_protobuf.cc20
-rw-r--r--chromium/net/quic/crypto/crypto_server_config_protobuf.h101
-rw-r--r--chromium/net/quic/crypto/crypto_server_test.cc256
-rw-r--r--chromium/net/quic/crypto/crypto_utils.cc114
-rw-r--r--chromium/net/quic/crypto/crypto_utils.h69
-rw-r--r--chromium/net/quic/crypto/crypto_utils_test.cc49
-rw-r--r--chromium/net/quic/crypto/curve25519_key_exchange.cc86
-rw-r--r--chromium/net/quic/crypto/curve25519_key_exchange.h49
-rw-r--r--chromium/net/quic/crypto/curve25519_key_exchange_test.cc42
-rw-r--r--chromium/net/quic/crypto/ephemeral_key_source.h42
-rw-r--r--chromium/net/quic/crypto/key_exchange.h47
-rw-r--r--chromium/net/quic/crypto/null_decrypter.cc74
-rw-r--r--chromium/net/quic/crypto/null_decrypter.h38
-rw-r--r--chromium/net/quic/crypto/null_decrypter_test.cc66
-rw-r--r--chromium/net/quic/crypto/null_encrypter.cc61
-rw-r--r--chromium/net/quic/crypto/null_encrypter.h41
-rw-r--r--chromium/net/quic/crypto/null_encrypter_test.cc48
-rw-r--r--chromium/net/quic/crypto/p256_key_exchange.h80
-rw-r--r--chromium/net/quic/crypto/p256_key_exchange_nss.cc233
-rw-r--r--chromium/net/quic/crypto/p256_key_exchange_openssl.cc119
-rw-r--r--chromium/net/quic/crypto/p256_key_exchange_test.cc44
-rw-r--r--chromium/net/quic/crypto/proof_source.h62
-rw-r--r--chromium/net/quic/crypto/proof_source_chromium.cc24
-rw-r--r--chromium/net/quic/crypto/proof_source_chromium.h39
-rw-r--r--chromium/net/quic/crypto/proof_test.cc443
-rw-r--r--chromium/net/quic/crypto/proof_verifier.cc15
-rw-r--r--chromium/net/quic/crypto/proof_verifier.h89
-rw-r--r--chromium/net/quic/crypto/proof_verifier_chromium.cc259
-rw-r--r--chromium/net/quic/crypto/proof_verifier_chromium.h91
-rw-r--r--chromium/net/quic/crypto/quic_decrypter.cc25
-rw-r--r--chromium/net/quic/crypto/quic_decrypter.h72
-rw-r--r--chromium/net/quic/crypto/quic_encrypter.cc25
-rw-r--r--chromium/net/quic/crypto/quic_encrypter.h87
-rw-r--r--chromium/net/quic/crypto/quic_random.cc66
-rw-r--r--chromium/net/quic/crypto/quic_random.h41
-rw-r--r--chromium/net/quic/crypto/quic_random_test.cc40
-rw-r--r--chromium/net/quic/crypto/scoped_evp_cipher_ctx.cc22
-rw-r--r--chromium/net/quic/crypto/scoped_evp_cipher_ctx.h30
-rw-r--r--chromium/net/quic/crypto/source_address_token.cc46
-rw-r--r--chromium/net/quic/crypto/source_address_token.h48
-rw-r--r--chromium/net/quic/crypto/strike_register.cc465
-rw-r--r--chromium/net/quic/crypto/strike_register.h189
-rw-r--r--chromium/net/quic/crypto/strike_register_test.cc303
-rw-r--r--chromium/net/quic/quic_alarm.cc48
-rw-r--r--chromium/net/quic/quic_alarm.h77
-rw-r--r--chromium/net/quic/quic_alarm_test.cc126
-rw-r--r--chromium/net/quic/quic_bandwidth.cc102
-rw-r--r--chromium/net/quic/quic_bandwidth.h85
-rw-r--r--chromium/net/quic/quic_bandwidth_test.cc82
-rw-r--r--chromium/net/quic/quic_blocked_writer_interface.h28
-rw-r--r--chromium/net/quic/quic_client_session.cc392
-rw-r--r--chromium/net/quic/quic_client_session.h172
-rw-r--r--chromium/net/quic/quic_client_session_test.cc134
-rw-r--r--chromium/net/quic/quic_clock.cc29
-rw-r--r--chromium/net/quic/quic_clock.h37
-rw-r--r--chromium/net/quic/quic_clock_test.cc38
-rw-r--r--chromium/net/quic/quic_config.cc355
-rw-r--r--chromium/net/quic/quic_config.h197
-rw-r--r--chromium/net/quic/quic_config_test.cc167
-rw-r--r--chromium/net/quic/quic_connection.cc1661
-rw-r--r--chromium/net/quic/quic_connection.h698
-rw-r--r--chromium/net/quic/quic_connection_helper.cc153
-rw-r--r--chromium/net/quic/quic_connection_helper.h72
-rw-r--r--chromium/net/quic/quic_connection_helper_test.cc474
-rw-r--r--chromium/net/quic/quic_connection_logger.cc416
-rw-r--r--chromium/net/quic/quic_connection_logger.h78
-rw-r--r--chromium/net/quic/quic_connection_test.cc2445
-rw-r--r--chromium/net/quic/quic_crypto_client_stream.cc375
-rw-r--r--chromium/net/quic/quic_crypto_client_stream.h123
-rw-r--r--chromium/net/quic/quic_crypto_client_stream_factory.h31
-rw-r--r--chromium/net/quic/quic_crypto_client_stream_test.cc194
-rw-r--r--chromium/net/quic/quic_crypto_server_stream.cc142
-rw-r--r--chromium/net/quic/quic_crypto_server_stream.h57
-rw-r--r--chromium/net/quic/quic_crypto_server_stream_test.cc265
-rw-r--r--chromium/net/quic/quic_crypto_stream.cc71
-rw-r--r--chromium/net/quic/quic_crypto_stream.h70
-rw-r--r--chromium/net/quic/quic_crypto_stream_test.cc114
-rw-r--r--chromium/net/quic/quic_data_reader.cc133
-rw-r--r--chromium/net/quic/quic_data_reader.h126
-rw-r--r--chromium/net/quic/quic_data_writer.cc152
-rw-r--r--chromium/net/quic/quic_data_writer.h80
-rw-r--r--chromium/net/quic/quic_data_writer_test.cc46
-rw-r--r--chromium/net/quic/quic_fec_group.cc166
-rw-r--r--chromium/net/quic/quic_fec_group.h98
-rw-r--r--chromium/net/quic/quic_fec_group_test.cc219
-rw-r--r--chromium/net/quic/quic_framer.cc1859
-rw-r--r--chromium/net/quic/quic_framer.h455
-rw-r--r--chromium/net/quic/quic_framer_test.cc3639
-rw-r--r--chromium/net/quic/quic_http_stream.cc512
-rw-r--r--chromium/net/quic/quic_http_stream.h149
-rw-r--r--chromium/net/quic/quic_http_stream_test.cc562
-rw-r--r--chromium/net/quic/quic_network_transaction_unittest.cc752
-rw-r--r--chromium/net/quic/quic_packet_creator.cc300
-rw-r--r--chromium/net/quic/quic_packet_creator.h182
-rw-r--r--chromium/net/quic/quic_packet_creator_test.cc333
-rw-r--r--chromium/net/quic/quic_packet_generator.cc221
-rw-r--r--chromium/net/quic/quic_packet_generator.h145
-rw-r--r--chromium/net/quic/quic_packet_generator_test.cc536
-rw-r--r--chromium/net/quic/quic_protocol.cc429
-rw-r--r--chromium/net/quic/quic_protocol.h853
-rw-r--r--chromium/net/quic/quic_protocol_test.cc146
-rw-r--r--chromium/net/quic/quic_received_packet_manager.cc188
-rw-r--r--chromium/net/quic/quic_received_packet_manager.h124
-rw-r--r--chromium/net/quic/quic_received_packet_manager_test.cc119
-rw-r--r--chromium/net/quic/quic_reliable_client_stream.cc62
-rw-r--r--chromium/net/quic/quic_reliable_client_stream.h87
-rw-r--r--chromium/net/quic/quic_reliable_client_stream_test.cc82
-rw-r--r--chromium/net/quic/quic_sent_entropy_manager.cc87
-rw-r--r--chromium/net/quic/quic_sent_entropy_manager.h62
-rw-r--r--chromium/net/quic/quic_sent_entropy_manager_test.cc73
-rw-r--r--chromium/net/quic/quic_session.cc414
-rw-r--r--chromium/net/quic/quic_session.h266
-rw-r--r--chromium/net/quic/quic_session_test.cc287
-rw-r--r--chromium/net/quic/quic_spdy_compressor.cc50
-rw-r--r--chromium/net/quic/quic_spdy_compressor.h38
-rw-r--r--chromium/net/quic/quic_spdy_compressor_test.cc46
-rw-r--r--chromium/net/quic/quic_spdy_decompressor.cc140
-rw-r--r--chromium/net/quic/quic_spdy_decompressor.h65
-rw-r--r--chromium/net/quic/quic_spdy_decompressor_test.cc103
-rw-r--r--chromium/net/quic/quic_stats.cc25
-rw-r--r--chromium/net/quic/quic_stats.h50
-rw-r--r--chromium/net/quic/quic_stream_factory.cc489
-rw-r--r--chromium/net/quic/quic_stream_factory.h183
-rw-r--r--chromium/net/quic/quic_stream_factory_test.cc365
-rw-r--r--chromium/net/quic/quic_stream_sequencer.cc279
-rw-r--r--chromium/net/quic/quic_stream_sequencer.h102
-rw-r--r--chromium/net/quic/quic_stream_sequencer_test.cc577
-rw-r--r--chromium/net/quic/quic_time.cc163
-rw-r--r--chromium/net/quic/quic_time.h184
-rw-r--r--chromium/net/quic/quic_time_test.cc113
-rw-r--r--chromium/net/quic/quic_utils.cc270
-rw-r--r--chromium/net/quic/quic_utils.h81
-rw-r--r--chromium/net/quic/quic_utils_test.cc73
-rw-r--r--chromium/net/quic/reliable_quic_stream.cc436
-rw-r--r--chromium/net/quic/reliable_quic_stream.h199
-rw-r--r--chromium/net/quic/reliable_quic_stream_test.cc535
-rw-r--r--chromium/net/quic/spdy_utils.cc25
-rw-r--r--chromium/net/quic/spdy_utils.h23
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils.cc505
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils.h132
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_chromium.cc53
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_nss.cc138
-rw-r--r--chromium/net/quic/test_tools/crypto_test_utils_openssl.cc173
-rw-r--r--chromium/net/quic/test_tools/mock_clock.cc38
-rw-r--r--chromium/net/quic/test_tools/mock_clock.h38
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream.cc75
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream.h57
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream_factory.cc27
-rw-r--r--chromium/net/quic/test_tools/mock_crypto_client_stream_factory.h39
-rw-r--r--chromium/net/quic/test_tools/mock_random.cc32
-rw-r--r--chromium/net/quic/test_tools/mock_random.h38
-rw-r--r--chromium/net/quic/test_tools/quic_client_session_peer.cc21
-rw-r--r--chromium/net/quic/test_tools/quic_client_session_peer.h29
-rw-r--r--chromium/net/quic/test_tools/quic_connection_peer.cc181
-rw-r--r--chromium/net/quic/test_tools/quic_connection_peer.h105
-rw-r--r--chromium/net/quic/test_tools/quic_framer_peer.cc42
-rw-r--r--chromium/net/quic/test_tools/quic_framer_peer.h37
-rw-r--r--chromium/net/quic/test_tools/quic_packet_creator_peer.cc30
-rw-r--r--chromium/net/quic/test_tools/quic_packet_creator_peer.h32
-rw-r--r--chromium/net/quic/test_tools/quic_received_packet_manager_peer.cc30
-rw-r--r--chromium/net/quic/test_tools/quic_received_packet_manager_peer.h35
-rw-r--r--chromium/net/quic/test_tools/quic_session_peer.cc37
-rw-r--r--chromium/net/quic/test_tools/quic_session_peer.h34
-rw-r--r--chromium/net/quic/test_tools/quic_test_utils.cc453
-rw-r--r--chromium/net/quic/test_tools/quic_test_utils.h376
-rw-r--r--chromium/net/quic/test_tools/reliable_quic_stream_peer.cc32
-rw-r--r--chromium/net/quic/test_tools/reliable_quic_stream_peer.h32
-rw-r--r--chromium/net/quic/test_tools/simple_quic_framer.cc198
-rw-r--r--chromium/net/quic/test_tools/simple_quic_framer.h58
-rw-r--r--chromium/net/quic/test_tools/test_task_runner.cc68
-rw-r--r--chromium/net/quic/test_tools/test_task_runner.h54
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, &param,
+ 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, &param,
+ 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,
+ &params->aead, NULL) ||
+ !QuicUtils::FindMutualTag(
+ requested_config->kexs, their_key_exchanges, num_their_key_exchanges,
+ QuicUtils::LOCAL_PRIORITY, &params->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,
+ &params->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, &params->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, &params->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,
+ &params->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_, &params_, &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_, &params_, &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], &timestamp)) {
+ 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(&current_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_