| Index: net/socket/nss_ssl_util.cc
|
| diff --git a/net/socket/nss_ssl_util.cc b/net/socket/nss_ssl_util.cc
|
| index 8547d4dc1cf493f167c4a2ebb731c809029870dc..98a68f329f9669b66b1989f7400a412784cb55ca 100644
|
| --- a/net/socket/nss_ssl_util.cc
|
| +++ b/net/socket/nss_ssl_util.cc
|
| @@ -13,6 +13,7 @@
|
| #include <string>
|
|
|
| #include "base/bind.h"
|
| +#include "base/cpu.h"
|
| #include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/memory/singleton.h"
|
| @@ -27,11 +28,62 @@
|
| #include "base/win/windows_version.h"
|
| #endif
|
|
|
| +namespace {
|
| +
|
| +// CiphersRemove takes a zero-terminated array of cipher suite ids in
|
| +// |to_remove| and sets every instance of them in |ciphers| to zero. It returns
|
| +// true if it found and removed every element of |to_remove|. It assumes that
|
| +// there are no duplicates in |ciphers| nor in |to_remove|.
|
| +bool CiphersRemove(const uint16* to_remove, uint16* ciphers, size_t num) {
|
| + size_t i, found = 0;
|
| +
|
| + for (i = 0; ; i++) {
|
| + if (to_remove[i] == 0)
|
| + break;
|
| +
|
| + for (size_t j = 0; j < num; j++) {
|
| + if (to_remove[i] == ciphers[j]) {
|
| + ciphers[j] = 0;
|
| + found++;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return found == i;
|
| +}
|
| +
|
| +// CiphersCompact takes an array of cipher suite ids in |ciphers|, where some
|
| +// entries are zero, and moves the entries so that all the non-zero elements
|
| +// are compacted at the end of the array.
|
| +void CiphersCompact(uint16* ciphers, size_t num) {
|
| + size_t j = num - 1;
|
| +
|
| + for (size_t i = num - 1; i < num; i--) {
|
| + if (ciphers[i] == 0)
|
| + continue;
|
| + ciphers[j--] = ciphers[i];
|
| + }
|
| +}
|
| +
|
| +// CiphersCopy copies the zero-terminated array |in| to |out|. It returns the
|
| +// number of cipher suite ids copied.
|
| +size_t CiphersCopy(const uint16* in, uint16* out) {
|
| + for (size_t i = 0; ; i++) {
|
| + if (in[i] == 0)
|
| + return i;
|
| + out[i] = in[i];
|
| + }
|
| +}
|
| +
|
| +} // anonymous namespace
|
| +
|
| namespace net {
|
|
|
| class NSSSSLInitSingleton {
|
| public:
|
| - NSSSSLInitSingleton() {
|
| + NSSSSLInitSingleton()
|
| + : num_ciphers_(0) {
|
| crypto::EnsureNSSInit();
|
|
|
| NSS_SetDomesticPolicy();
|
| @@ -86,14 +138,66 @@ class NSSSSLInitSingleton {
|
| // Enable SSL.
|
| SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE);
|
|
|
| + // Calculate the order of ciphers that we'll use for NSS sockets. (Note
|
| + // that, even if a cipher is specified in the ordering, it must still be
|
| + // enabled in order to be included in a ClientHello.)
|
| + //
|
| + // Our top preference cipher suites are either forward-secret AES-GCM or
|
| + // forward-secret ChaCha20-Poly1305. If the local machine has AES-NI then
|
| + // we prefer AES-GCM, otherwise ChaCha20. The remainder of the cipher suite
|
| + // preference is inheriented from NSS. */
|
| + static const uint16 chacha_ciphers[] = {
|
| + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
| + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
| + 0,
|
| + };
|
| + static const uint16 aes_gcm_ciphers[] = {
|
| + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
| + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
| + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
|
| + 0,
|
| + };
|
| + const PRUint16* all_ciphers = SSL_GetImplementedCiphers();
|
| + num_ciphers_ = SSL_GetNumImplementedCiphers();
|
| + ciphers_.reset(new uint16[num_ciphers_]);
|
| + memcpy(ciphers_.get(), all_ciphers, sizeof(uint16)*num_ciphers_);
|
| +
|
| + if (CiphersRemove(chacha_ciphers, ciphers_.get(), num_ciphers_) &&
|
| + CiphersRemove(aes_gcm_ciphers, ciphers_.get(), num_ciphers_)) {
|
| + CiphersCompact(ciphers_.get(), num_ciphers_);
|
| +
|
| + const uint16* preference_ciphers = chacha_ciphers;
|
| + const uint16* other_ciphers = aes_gcm_ciphers;
|
| + base::CPU cpu;
|
| +
|
| + if (cpu.has_aesni() && cpu.has_avx()) {
|
| + preference_ciphers = aes_gcm_ciphers;
|
| + other_ciphers = chacha_ciphers;
|
| + }
|
| + unsigned i = CiphersCopy(preference_ciphers, ciphers_.get());
|
| + CiphersCopy(other_ciphers, &ciphers_[i]);
|
| + } else {
|
| + ciphers_.reset();
|
| + num_ciphers_ = 0;
|
| + }
|
| +
|
| // All other SSL options are set per-session by SSLClientSocket and
|
| // SSLServerSocket.
|
| }
|
|
|
| + const uint16* GetNSSCipherOrder(size_t* out_length) {
|
| + *out_length = num_ciphers_;
|
| + return ciphers_.get();
|
| + }
|
| +
|
| ~NSSSSLInitSingleton() {
|
| // Have to clear the cache, or NSS_Shutdown fails with SEC_ERROR_BUSY.
|
| SSL_ClearSessionCache();
|
| }
|
| +
|
| + private:
|
| + scoped_ptr<uint16[]> ciphers_;
|
| + size_t num_ciphers_;
|
| };
|
|
|
| static base::LazyInstance<NSSSSLInitSingleton> g_nss_ssl_init_singleton =
|
| @@ -112,6 +216,10 @@ void EnsureNSSSSLInit() {
|
| g_nss_ssl_init_singleton.Get();
|
| }
|
|
|
| +const uint16* GetNSSCipherOrder(size_t* out_length) {
|
| + return g_nss_ssl_init_singleton.Get().GetNSSCipherOrder(out_length);
|
| +}
|
| +
|
| // Map a Chromium net error code to an NSS error code.
|
| // See _MD_unix_map_default_error in the NSS source
|
| // tree for inspiration.
|
|
|