summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/gtest
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/gtest')
-rw-r--r--security/manager/ssl/tests/gtest/CertDBTest.cpp60
-rw-r--r--security/manager/ssl/tests/gtest/DataStorageTest.cpp225
-rw-r--r--security/manager/ssl/tests/gtest/DeserializeCertTest.cpp96
-rw-r--r--security/manager/ssl/tests/gtest/MD4Test.cpp76
-rw-r--r--security/manager/ssl/tests/gtest/OCSPCacheTest.cpp337
-rw-r--r--security/manager/ssl/tests/gtest/README.txt2
-rw-r--r--security/manager/ssl/tests/gtest/STSParserTest.cpp144
-rw-r--r--security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp568
-rw-r--r--security/manager/ssl/tests/gtest/moz.build29
9 files changed, 1537 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/gtest/CertDBTest.cpp b/security/manager/ssl/tests/gtest/CertDBTest.cpp
new file mode 100644
index 000000000..e6b773cd6
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/CertDBTest.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsIPrefService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIX509Cert.h"
+#include "nsIX509CertDB.h"
+#include "nsIX509CertList.h"
+#include "nsServiceManagerUtils.h"
+
+TEST(psm_CertDB, Test)
+{
+ {
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ ASSERT_TRUE(prefs) << "couldn't get nsIPrefBranch";
+
+ // When PSM initializes, it attempts to get some localized strings.
+ // As a result, Android flips out if this isn't set.
+ nsresult rv = prefs->SetBoolPref("intl.locale.matchOS", true);
+ ASSERT_TRUE(NS_SUCCEEDED(rv)) << "couldn't set pref 'intl.locale.matchOS'";
+
+ nsCOMPtr<nsIX509CertDB> certdb(do_GetService(NS_X509CERTDB_CONTRACTID));
+ ASSERT_TRUE(certdb) << "couldn't get certdb";
+
+ nsCOMPtr<nsIX509CertList> certList;
+ rv = certdb->GetCerts(getter_AddRefs(certList));
+ ASSERT_TRUE(NS_SUCCEEDED(rv)) << "couldn't get list of certificates";
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = certList->GetEnumerator(getter_AddRefs(enumerator));
+ ASSERT_TRUE(NS_SUCCEEDED(rv)) << "couldn't enumerate certificate list";
+
+ bool foundBuiltIn = false;
+ bool hasMore = false;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
+ nsCOMPtr<nsISupports> supports;
+ ASSERT_TRUE(NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(supports))))
+ << "couldn't get next certificate";
+
+ nsCOMPtr<nsIX509Cert> cert(do_QueryInterface(supports));
+ ASSERT_TRUE(cert) << "couldn't QI to nsIX509Cert";
+
+ ASSERT_TRUE(NS_SUCCEEDED(cert->GetIsBuiltInRoot(&foundBuiltIn))) <<
+ "GetIsBuiltInRoot failed";
+
+ if (foundBuiltIn) {
+ break;
+ }
+ }
+
+ ASSERT_TRUE(foundBuiltIn) << "didn't load any built-in certificates";
+
+ printf("successfully loaded at least one built-in certificate\n");
+
+ } // this scopes the nsCOMPtrs
+}
diff --git a/security/manager/ssl/tests/gtest/DataStorageTest.cpp b/security/manager/ssl/tests/gtest/DataStorageTest.cpp
new file mode 100644
index 000000000..eaab50878
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/DataStorageTest.cpp
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/DataStorage.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+#include "nsStreamUtils.h"
+#include "prtime.h"
+
+using namespace mozilla;
+
+class psm_DataStorageTest : public ::testing::Test
+{
+protected:
+ virtual void SetUp()
+ {
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ NS_ConvertUTF8toUTF16 testName(testInfo->name());
+ storage = DataStorage::Get(testName);
+ storage->Init(dataWillPersist);
+ }
+
+ RefPtr<DataStorage> storage;
+ bool dataWillPersist;
+};
+
+NS_NAMED_LITERAL_CSTRING(testKey, "test");
+NS_NAMED_LITERAL_CSTRING(testValue, "value");
+NS_NAMED_LITERAL_CSTRING(privateTestValue, "private");
+
+TEST_F(psm_DataStorageTest, GetPutRemove)
+{
+ EXPECT_TRUE(dataWillPersist);
+
+ // Test Put/Get on Persistent data
+ EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
+ // Don't re-use testKey / testValue here, to make sure that this works as
+ // expected with objects that have the same semantic value but are not
+ // literally the same object.
+ nsCString result = storage->Get(NS_LITERAL_CSTRING("test"),
+ DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+
+ // Get on Temporary/Private data with the same key should give nothing
+ result = storage->Get(testKey, DataStorage_Temporary);
+ EXPECT_TRUE(result.IsEmpty());
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_TRUE(result.IsEmpty());
+
+ // Put with Temporary/Private data shouldn't affect Persistent data
+ NS_NAMED_LITERAL_CSTRING(temporaryTestValue, "temporary");
+ EXPECT_EQ(NS_OK, storage->Put(testKey, temporaryTestValue,
+ DataStorage_Temporary));
+ EXPECT_EQ(NS_OK, storage->Put(testKey, privateTestValue,
+ DataStorage_Private));
+ result = storage->Get(testKey, DataStorage_Temporary);
+ EXPECT_STREQ("temporary", result.get());
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_STREQ("private", result.get());
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+
+ // Put of a previously-present key overwrites it (if of the same type)
+ NS_NAMED_LITERAL_CSTRING(newValue, "new");
+ EXPECT_EQ(NS_OK, storage->Put(testKey, newValue, DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("new", result.get());
+
+ // Removal should work
+ storage->Remove(testKey, DataStorage_Temporary);
+ result = storage->Get(testKey, DataStorage_Temporary);
+ EXPECT_TRUE(result.IsEmpty());
+ // But removing one type shouldn't affect the others
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_STREQ("private", result.get());
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("new", result.get());
+ // Test removing the other types as well
+ storage->Remove(testKey, DataStorage_Private);
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_TRUE(result.IsEmpty());
+ storage->Remove(testKey, DataStorage_Persistent);
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+}
+
+TEST_F(psm_DataStorageTest, InputValidation)
+{
+ EXPECT_TRUE(dataWillPersist);
+
+ // Keys may not have tabs or newlines
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(NS_LITERAL_CSTRING("key\thas tab"), testValue,
+ DataStorage_Persistent));
+ nsCString result = storage->Get(NS_LITERAL_CSTRING("key\thas tab"),
+ DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(NS_LITERAL_CSTRING("key has\nnewline"), testValue,
+ DataStorage_Persistent));
+ result = storage->Get(NS_LITERAL_CSTRING("keyhas\nnewline"),
+ DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+ // Values may not have newlines
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(testKey, NS_LITERAL_CSTRING("value\nhas newline"),
+ DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ // Values may have tabs
+ EXPECT_TRUE(result.IsEmpty());
+ EXPECT_EQ(NS_OK, storage->Put(testKey,
+ NS_LITERAL_CSTRING("val\thas tab; this is ok"),
+ DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("val\thas tab; this is ok", result.get());
+
+ nsCString longKey("a");
+ for (int i = 0; i < 8; i++) {
+ longKey.Append(longKey);
+ }
+ // A key of length 256 will work
+ EXPECT_EQ(NS_OK, storage->Put(longKey, testValue, DataStorage_Persistent));
+ result = storage->Get(longKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+ longKey.Append("a");
+ // A key longer than that will not work
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(longKey, testValue, DataStorage_Persistent));
+ result = storage->Get(longKey, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+
+ nsCString longValue("a");
+ for (int i = 0; i < 10; i++) {
+ longValue.Append(longValue);
+ }
+ // A value of length 1024 will work
+ EXPECT_EQ(NS_OK, storage->Put(testKey, longValue, DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ(longValue.get(), result.get());
+ longValue.Append("a");
+ // A value longer than that will not work
+ storage->Remove(testKey, DataStorage_Persistent);
+ EXPECT_EQ(NS_ERROR_INVALID_ARG,
+ storage->Put(testKey, longValue, DataStorage_Persistent));
+ result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_TRUE(result.IsEmpty());
+}
+
+TEST_F(psm_DataStorageTest, Eviction)
+{
+ EXPECT_TRUE(dataWillPersist);
+
+ // Eviction is on a per-table basis. Tables shouldn't affect each other.
+ EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
+ for (int i = 0; i < 1025; i++) {
+ EXPECT_EQ(NS_OK, storage->Put(nsPrintfCString("%d", i),
+ nsPrintfCString("%d", i),
+ DataStorage_Temporary));
+ nsCString result = storage->Get(nsPrintfCString("%d", i),
+ DataStorage_Temporary);
+ EXPECT_STREQ(nsPrintfCString("%d", i).get(), result.get());
+ }
+ // We don't know which entry got evicted, but we can count them.
+ int entries = 0;
+ for (int i = 0; i < 1025; i++) {
+ nsCString result = storage->Get(nsPrintfCString("%d", i),
+ DataStorage_Temporary);
+ if (!result.IsEmpty()) {
+ entries++;
+ }
+ }
+ EXPECT_EQ(entries, 1024);
+ nsCString result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+}
+
+TEST_F(psm_DataStorageTest, ClearPrivateData)
+{
+ EXPECT_TRUE(dataWillPersist);
+
+ EXPECT_EQ(NS_OK, storage->Put(testKey, privateTestValue,
+ DataStorage_Private));
+ nsCString result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_STREQ("private", result.get());
+ storage->Observe(nullptr, "last-pb-context-exited", nullptr);
+ result = storage->Get(testKey, DataStorage_Private);
+ EXPECT_TRUE(result.IsEmpty());
+}
+
+TEST_F(psm_DataStorageTest, Shutdown)
+{
+ EXPECT_TRUE(dataWillPersist);
+
+ EXPECT_EQ(NS_OK, storage->Put(testKey, testValue, DataStorage_Persistent));
+ nsCString result = storage->Get(testKey, DataStorage_Persistent);
+ EXPECT_STREQ("value", result.get());
+ // Get "now" (in days) close to when the data was last touched, so we won't
+ // get intermittent failures with the day not matching.
+ int64_t microsecondsPerDay = 24 * 60 * 60 * int64_t(PR_USEC_PER_SEC);
+ int32_t nowInDays = int32_t(PR_Now() / microsecondsPerDay);
+ storage->Observe(nullptr, "profile-before-change", nullptr);
+ nsCOMPtr<nsIFile> backingFile;
+ EXPECT_EQ(NS_OK, NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(backingFile)));
+ const ::testing::TestInfo* const testInfo =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ NS_ConvertUTF8toUTF16 testName(testInfo->name());
+ EXPECT_EQ(NS_OK, backingFile->Append(testName));
+ nsCOMPtr<nsIInputStream> fileInputStream;
+ EXPECT_EQ(NS_OK, NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
+ backingFile));
+ nsCString data;
+ EXPECT_EQ(NS_OK, NS_ConsumeStream(fileInputStream, UINT32_MAX, data));
+ // The data will be of the form 'test\t0\t<days since the epoch>\tvalue'
+ EXPECT_STREQ(nsPrintfCString("test\t0\t%d\tvalue\n", nowInDays).get(),
+ data.get());
+}
diff --git a/security/manager/ssl/tests/gtest/DeserializeCertTest.cpp b/security/manager/ssl/tests/gtest/DeserializeCertTest.cpp
new file mode 100644
index 000000000..868ecdba6
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/DeserializeCertTest.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsSerializationHelper.h"
+
+// These tests verify that we can still deserialize old binary strings
+// generated for security info. This is necessary because service workers
+// stores these strings on disk.
+//
+// If you make a change and start breaking these tests, you will need to
+// add a compat fix for loading the old versions. For things that affect
+// the UUID, but do not break the rest of the format you can simply add
+// another hack condition in nsBinaryInputStream::ReadObject(). If you
+// change the overall format of the serialization then we will need more
+// complex handling in the security info concrete classes.
+//
+// We would like to move away from this binary compatibility requirement
+// in service workers. See bug 1248628.
+
+TEST(psm_DeserializeCert, gecko33)
+{
+ // Gecko 33+ vintage Security info serialized with UUIDs:
+ // - nsISupports 00000000-0000-0000-c000-000000000046
+ // - nsISSLStatus fa9ba95b-ca3b-498a-b889-7c79cf28fee8
+ // - nsIX509Cert f8ed8364-ced9-4c6e-86ba-48af53c393e6
+ nsCString base64Serialization(
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAQAAgAAAAAAAAAAAAAAAAAAAAA"
+ "B4vFIJp5wRkeyPxAQ9RJGKPqbqVvKO0mKuIl8ec8o/uhmCjImkVxP+7sgiYWmMt8F+O2DZM7ZTG6GukivU8OT5gAAAAIAAAWpMII"
+ "FpTCCBI2gAwIBAgIQD4svsaKEC+QtqtsU2TF8ITANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUN"
+ "lcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNzdXJhbmNlIFN"
+ "lcnZlciBDQTAeFw0xNTAyMjMwMDAwMDBaFw0xNjAzMDIxMjAwMDBaMGoxCzAJBgNVBAYTAlVTMRYwFAYDVQQHEw1TYW4gRnJhbmN"
+ "pc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRUwEwYDVQQKEwxGYXN0bHksIEluYy4xFzAVBgNVBAMTDnd3dy5naXRodWIuY29tMII"
+ "BIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+9WUCgrgUNwP/JC3cUefLAXeDpq8Ko/U8p8IRvny0Ri0I6Uq0t+RP/nF0LJ"
+ "Avda8QHYujdgeDTePepBX7+OiwBFhA0YO+rM3C2Z8IRaN/i9eLln+Yyc68+1z+E10s1EXdZrtDGvN6MHqygGsdfkXKfBLUJ1BZEh"
+ "s9sBnfcjq3kh5gZdBArdG9l5NpdmQhtceaFGsPiWuJxGxRzS4i95veUHWkhMpEYDEEBdcDGxqArvQCvzSlngdttQCfx8OUkBTb3B"
+ "A2okpTwwJfqPsxVetA6qR7UNc+fVb6KHwvm0bzi2rQ3xw3D/syRHwdMkpoVDQPCk43H9WufgfBKRen87dFwIDAQABo4ICPzCCAjs"
+ "wHwYDVR0jBBgwFoAUUWj/kK8CB3U8zNllZGKiErhZcjswHQYDVR0OBBYEFGS/RLNGCZvPWh1xSaIEcouINIQjMHsGA1UdEQR0MHK"
+ "CDnd3dy5naXRodWIuY29tggpnaXRodWIuY29tggwqLmdpdGh1Yi5jb22CCyouZ2l0aHViLmlvgglnaXRodWIuaW+CFyouZ2l0aHV"
+ "idXNlcmNvbnRlbnQuY29tghVnaXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwM"
+ "BBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzMuY3J"
+ "sMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzMuY3JsMEIGA1UdIAQ7MDkwNwYJYIZIAYb"
+ "9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgYMGCCsGAQUFBwEBBHcwdTAkBggrBgEFBQc"
+ "wAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME0GCCsGAQUFBzAChkFodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUN"
+ "lcnRTSEEySGlnaEFzc3VyYW5jZVNlcnZlckNBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQAc4dbVmuKvyI7"
+ "KZ4Txk+ZqcAYToJGKUIVaPL94e5SZGweUisjaCbplAOihnf6Mxt8n6vnuH2IsCaz2NRHqhdcosjT3CwAiJpJNkXPKWVL/txgdSTV"
+ "2cqB1GG4esFOalvI52dzn+J4fTIYZvNF+AtGyHSLm2XRXYZCw455laUKf6Sk9RDShDgUvzhOKL4GXfTwKXv12MyMknJybH8UCpjC"
+ "HZmFBVHMcUN/87HsQo20PdOekeEvkjrrMIxW+gxw22Yb67yF/qKgwrWr+43bLN709iyw+LWiU7sQcHL2xk9SYiWQDj2tYz2soObV"
+ "QYTJm0VUZMEVFhtALq46cx92Zu4vFwC8AAwAAAAABAQAA");
+
+ nsCOMPtr<nsISupports> cert;
+ nsresult rv = NS_DeserializeObject(base64Serialization, getter_AddRefs(cert));
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_TRUE(cert);
+}
+
+TEST(psm_DeserializeCert, gecko46)
+{
+ // Gecko 46+ vintage Security info serialized with UUIDs:
+ // - nsISupports 00000000-0000-0000-c000-000000000046
+ // - nsISSLStatus fa9ba95b-ca3b-498a-b889-7c79cf28fee8
+ // - nsIX509Cert bdc3979a-5422-4cd5-8589-696b6e96ea83
+ nsCString base64Serialization(
+ "FnhllAKWRHGAlo+ESXykKAAAAAAAAAAAwAAAAAAAAEaphjojH6pBabDSgSnsfLHeAAQAAgAAAAAAAAAAAAAAAAAAAAA"
+ "B4vFIJp5wRkeyPxAQ9RJGKPqbqVvKO0mKuIl8ec8o/uhmCjImkVxP+7sgiYWmMt8FvcOXmlQiTNWFiWlrbpbqgwAAAAIAAAWzMII"
+ "FrzCCBJegAwIBAgIQB3pdwzYjAfmJ/lT3+G8+ZDANBgkqhkiG9w0BAQsFADBwMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUN"
+ "lcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNzdXJhbmNlIFN"
+ "lcnZlciBDQTAeFw0xNjAxMjAwMDAwMDBaFw0xNzA0MDYxMjAwMDBaMGoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybml"
+ "hMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxGYXN0bHksIEluYy4xFzAVBgNVBAMTDnd3dy5naXRodWIuY29tMII"
+ "BIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+9WUCgrgUNwP/JC3cUefLAXeDpq8Ko/U8p8IRvny0Ri0I6Uq0t+RP/nF0LJ"
+ "Avda8QHYujdgeDTePepBX7+OiwBFhA0YO+rM3C2Z8IRaN/i9eLln+Yyc68+1z+E10s1EXdZrtDGvN6MHqygGsdfkXKfBLUJ1BZEh"
+ "s9sBnfcjq3kh5gZdBArdG9l5NpdmQhtceaFGsPiWuJxGxRzS4i95veUHWkhMpEYDEEBdcDGxqArvQCvzSlngdttQCfx8OUkBTb3B"
+ "A2okpTwwJfqPsxVetA6qR7UNc+fVb6KHwvm0bzi2rQ3xw3D/syRHwdMkpoVDQPCk43H9WufgfBKRen87dFwIDAQABo4ICSTCCAkU"
+ "wHwYDVR0jBBgwFoAUUWj/kK8CB3U8zNllZGKiErhZcjswHQYDVR0OBBYEFGS/RLNGCZvPWh1xSaIEcouINIQjMHsGA1UdEQR0MHK"
+ "CDnd3dy5naXRodWIuY29tggwqLmdpdGh1Yi5jb22CCmdpdGh1Yi5jb22CCyouZ2l0aHViLmlvgglnaXRodWIuaW+CFyouZ2l0aHV"
+ "idXNlcmNvbnRlbnQuY29tghVnaXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwM"
+ "BBggrBgEFBQcDAjB1BgNVHR8EbjBsMDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzUuY3J"
+ "sMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXItZzUuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb"
+ "9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMIGDBggrBgEFBQcBAQR3MHU"
+ "wJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBNBggrBgEFBQcwAoZBaHR0cDovL2NhY2VydHMuZGlnaWNlcnQ"
+ "uY29tL0RpZ2lDZXJ0U0hBMkhpZ2hBc3N1cmFuY2VTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQE"
+ "ATxbRdPg+o49+96/P+rbdp4ie+CGtfCgUubT/Z9C54k+BfQO0nbxVgCSM5WZQuLgo2Q+0lcxisod8zxZeU0j5wviQINwOln/iN89"
+ "Bx3VmDRynTe4CqhsAwOoO1ERmCAmsAJBwY/rNr4mK22p8erBrqMW0nYXYU5NFynI+pNTjojhKD4II8PNV8G2yMWwYOb/u4+WPzUA"
+ "HC9DpZdrWTEH/W69Cr/KxRqGsWPwpgMv2Wqav8jaT35JxqTXjOlhQqzo6fNn3eYOeCf4PkCxZKwckWjy10qDaRbjhwAMHAGj2TPr"
+ "idlvOj/7QyyX5m8up/1US8z1fRW4yoCSOt6V2bwuH6cAvAAMAAAAAAQEAAA==");
+
+ nsCOMPtr<nsISupports> cert;
+ nsresult rv = NS_DeserializeObject(base64Serialization, getter_AddRefs(cert));
+ ASSERT_EQ(NS_OK, rv);
+ ASSERT_TRUE(cert);
+}
diff --git a/security/manager/ssl/tests/gtest/MD4Test.cpp b/security/manager/ssl/tests/gtest/MD4Test.cpp
new file mode 100644
index 000000000..1b635a757
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/MD4Test.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file tests the md4.c implementation.
+
+#include "gtest/gtest.h"
+#include "md4.h"
+#include "mozilla/Casting.h"
+#include "mozilla/PodOperations.h"
+
+struct RFC1320TestParams
+{
+ const char* data;
+ const uint8_t expectedHash[16];
+};
+
+static const RFC1320TestParams RFC1320_TEST_PARAMS[] =
+{
+ {
+ "",
+ { 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
+ 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 }
+ },
+ {
+ "a",
+ { 0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46,
+ 0x24, 0x5e, 0x05, 0xfb, 0xdb, 0xd6, 0xfb, 0x24 }
+ },
+ {
+ "abc",
+ { 0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52,
+ 0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d }
+ },
+ {
+ "message digest",
+ { 0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8,
+ 0x18, 0x87, 0x48, 0x06, 0xe1, 0xc7, 0x01, 0x4b }
+ },
+ {
+ "abcdefghijklmnopqrstuvwxyz",
+ { 0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd,
+ 0xee, 0xa8, 0xed, 0x63, 0xdf, 0x41, 0x2d, 0xa9 },
+ },
+ {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ { 0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35,
+ 0x1c, 0xe6, 0x27, 0xe1, 0x53, 0xe7, 0xf0, 0xe4 },
+ },
+ {
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ { 0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19,
+ 0x9c, 0x3e, 0x7b, 0x16, 0x4f, 0xcc, 0x05, 0x36 },
+ }
+};
+
+class psm_MD4
+ : public ::testing::Test
+ , public ::testing::WithParamInterface<RFC1320TestParams>
+{
+};
+
+TEST_P(psm_MD4, RFC1320TestValues)
+{
+ const RFC1320TestParams& params(GetParam());
+ uint8_t actualHash[16];
+ md4sum(mozilla::BitwiseCast<const uint8_t*, const char*>(params.data),
+ strlen(params.data), actualHash);
+ EXPECT_TRUE(mozilla::PodEqual(actualHash, params.expectedHash))
+ << "MD4 hashes aren't equal for input: '" << params.data << "'";
+}
+
+INSTANTIATE_TEST_CASE_P(psm_MD4, psm_MD4,
+ testing::ValuesIn(RFC1320_TEST_PARAMS));
diff --git a/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
new file mode 100644
index 000000000..2b7dc946c
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/OCSPCacheTest.cpp
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CertVerifier.h"
+#include "OCSPCache.h"
+#include "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Casting.h"
+#include "mozilla/Sprintf.h"
+#include "nss.h"
+#include "pkix/pkixtypes.h"
+#include "pkixtestutil.h"
+#include "prerr.h"
+#include "secerr.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+using mozilla::NeckoOriginAttributes;
+
+template <size_t N>
+inline Input
+LiteralInput(const char(&valueString)[N])
+{
+ // Ideally we would use mozilla::BitwiseCast() here rather than
+ // reinterpret_cast for better type checking, but the |N - 1| part trips
+ // static asserts.
+ return Input(reinterpret_cast<const uint8_t(&)[N - 1]>(valueString));
+}
+
+const int MaxCacheEntries = 1024;
+
+class psm_OCSPCacheTest : public ::testing::Test
+{
+protected:
+ psm_OCSPCacheTest() : now(Now()) { }
+
+ static void SetUpTestCase()
+ {
+ NSS_NoDB_Init(nullptr);
+ }
+
+ const Time now;
+ mozilla::psm::OCSPCache cache;
+};
+
+static void
+PutAndGet(mozilla::psm::OCSPCache& cache, const CertID& certID, Result result,
+ Time time,
+ const NeckoOriginAttributes& originAttributes = NeckoOriginAttributes())
+{
+ // The first time is thisUpdate. The second is validUntil.
+ // The caller is expecting the validUntil returned with Get
+ // to be equal to the passed-in time. Since these values will
+ // be different in practice, make thisUpdate less than validUntil.
+ Time thisUpdate(time);
+ ASSERT_EQ(Success, thisUpdate.SubtractSeconds(10));
+ Result rv = cache.Put(certID, originAttributes, result, thisUpdate, time);
+ ASSERT_TRUE(rv == Success);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, originAttributes, resultOut, timeOut));
+ ASSERT_EQ(result, resultOut);
+ ASSERT_EQ(time, timeOut);
+}
+
+Input fakeIssuer1(LiteralInput("CN=issuer1"));
+Input fakeKey000(LiteralInput("key000"));
+Input fakeKey001(LiteralInput("key001"));
+Input fakeSerial0000(LiteralInput("0000"));
+
+TEST_F(psm_OCSPCacheTest, TestPutAndGet)
+{
+ Input fakeSerial000(LiteralInput("000"));
+ Input fakeSerial001(LiteralInput("001"));
+
+ SCOPED_TRACE("");
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial001),
+ Success, now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial000),
+ NeckoOriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestVariousGets)
+{
+ SCOPED_TRACE("");
+ for (int i = 0; i < MaxCacheEntries; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
+ "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+ Success, timeIn);
+ }
+
+ Time timeIn(now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+
+ // This will be at the end of the list in the cache
+ CertID cert0000(fakeIssuer1, fakeKey000, fakeSerial0000);
+ ASSERT_TRUE(cache.Get(cert0000, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+ // Once we access it, it goes to the front
+ ASSERT_TRUE(cache.Get(cert0000, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+
+ // This will be in the middle
+ Time timeInPlus512(now);
+ ASSERT_EQ(Success, timeInPlus512.AddSeconds(512));
+
+ static const Input fakeSerial0512(LiteralInput("0512"));
+ CertID cert0512(fakeIssuer1, fakeKey000, fakeSerial0512);
+ ASSERT_TRUE(cache.Get(cert0512, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeInPlus512, timeOut);
+ ASSERT_TRUE(cache.Get(cert0512, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeInPlus512, timeOut);
+
+ // We've never seen this certificate
+ static const Input fakeSerial1111(LiteralInput("1111"));
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey000, fakeSerial1111),
+ NeckoOriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestEviction)
+{
+ SCOPED_TRACE("");
+ // By putting more distinct entries in the cache than it can hold,
+ // we cause the least recently used entry to be evicted.
+ for (int i = 0; i < MaxCacheEntries + 1; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
+ "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+ Success, timeIn);
+ }
+
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial0000),
+ NeckoOriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestNoEvictionForRevokedResponses)
+{
+ SCOPED_TRACE("");
+ CertID notEvicted(fakeIssuer1, fakeKey000, fakeSerial0000);
+ Time timeIn(now);
+ PutAndGet(cache, notEvicted, Result::ERROR_REVOKED_CERTIFICATE, timeIn);
+ // By putting more distinct entries in the cache than it can hold,
+ // we cause the least recently used entry that isn't revoked to be evicted.
+ for (int i = 1; i < MaxCacheEntries + 1; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
+ "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+ Success, timeIn);
+ }
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(notEvicted, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+
+ Input fakeSerial0001(LiteralInput("0001"));
+ CertID evicted(fakeIssuer1, fakeKey000, fakeSerial0001);
+ ASSERT_FALSE(cache.Get(evicted, NeckoOriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, TestEverythingIsRevoked)
+{
+ SCOPED_TRACE("");
+ Time timeIn(now);
+ // Fill up the cache with revoked responses.
+ for (int i = 0; i < MaxCacheEntries; i++) {
+ uint8_t serialBuf[8];
+ snprintf(mozilla::BitwiseCast<char*, uint8_t*>(serialBuf), sizeof(serialBuf),
+ "%04d", i);
+ Input fakeSerial;
+ ASSERT_EQ(Success, fakeSerial.Init(serialBuf, 4));
+ Time timeIn(now);
+ ASSERT_EQ(Success, timeIn.AddSeconds(i));
+ PutAndGet(cache, CertID(fakeIssuer1, fakeKey000, fakeSerial),
+ Result::ERROR_REVOKED_CERTIFICATE, timeIn);
+ }
+ static const Input fakeSerial1025(LiteralInput("1025"));
+ CertID good(fakeIssuer1, fakeKey000, fakeSerial1025);
+ // This will "succeed", allowing verification to continue. However,
+ // nothing was actually put in the cache.
+ Time timeInPlus1025(timeIn);
+ ASSERT_EQ(Success, timeInPlus1025.AddSeconds(1025));
+ Time timeInPlus1025Minus50(timeInPlus1025);
+ ASSERT_EQ(Success, timeInPlus1025Minus50.SubtractSeconds(50));
+ Result result = cache.Put(good, NeckoOriginAttributes(), Success, timeInPlus1025Minus50,
+ timeInPlus1025);
+ ASSERT_EQ(Success, result);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_FALSE(cache.Get(good, NeckoOriginAttributes(), resultOut, timeOut));
+
+ static const Input fakeSerial1026(LiteralInput("1026"));
+ CertID revoked(fakeIssuer1, fakeKey000, fakeSerial1026);
+ // This will fail, causing verification to fail.
+ Time timeInPlus1026(timeIn);
+ ASSERT_EQ(Success, timeInPlus1026.AddSeconds(1026));
+ Time timeInPlus1026Minus50(timeInPlus1026);
+ ASSERT_EQ(Success, timeInPlus1026Minus50.SubtractSeconds(50));
+ result = cache.Put(revoked, NeckoOriginAttributes(), Result::ERROR_REVOKED_CERTIFICATE,
+ timeInPlus1026Minus50, timeInPlus1026);
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, result);
+}
+
+TEST_F(psm_OCSPCacheTest, VariousIssuers)
+{
+ SCOPED_TRACE("");
+ Time timeIn(now);
+ static const Input fakeIssuer2(LiteralInput("CN=issuer2"));
+ static const Input fakeSerial001(LiteralInput("001"));
+ CertID subject(fakeIssuer1, fakeKey000, fakeSerial001);
+ PutAndGet(cache, subject, Success, now);
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(subject, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(timeIn, timeOut);
+ // Test that we don't match a different issuer DN
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer2, fakeKey000, fakeSerial001),
+ NeckoOriginAttributes(), resultOut, timeOut));
+ // Test that we don't match a different issuer key
+ ASSERT_FALSE(cache.Get(CertID(fakeIssuer1, fakeKey001, fakeSerial001),
+ NeckoOriginAttributes(), resultOut, timeOut));
+}
+
+TEST_F(psm_OCSPCacheTest, Times)
+{
+ SCOPED_TRACE("");
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+ PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(100));
+ PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
+ // This should not override the more recent entry.
+ ASSERT_EQ(Success,
+ cache.Put(certID, NeckoOriginAttributes(), Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(100),
+ TimeFromElapsedSecondsAD(100)));
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, NeckoOriginAttributes(), resultOut, timeOut));
+ // Here we see the more recent time.
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
+
+ // Result::ERROR_REVOKED_CERTIFICATE overrides everything
+ PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+ TimeFromElapsedSecondsAD(50));
+}
+
+TEST_F(psm_OCSPCacheTest, NetworkFailure)
+{
+ SCOPED_TRACE("");
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+ PutAndGet(cache, certID, Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(100));
+ PutAndGet(cache, certID, Success, TimeFromElapsedSecondsAD(200));
+ // This should not override the already present entry.
+ ASSERT_EQ(Success,
+ cache.Put(certID, NeckoOriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(300),
+ TimeFromElapsedSecondsAD(350)));
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ ASSERT_TRUE(cache.Get(certID, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Success, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(200), timeOut);
+
+ PutAndGet(cache, certID, Result::ERROR_OCSP_UNKNOWN_CERT,
+ TimeFromElapsedSecondsAD(400));
+ // This should not override the already present entry.
+ ASSERT_EQ(Success,
+ cache.Put(certID, NeckoOriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(500),
+ TimeFromElapsedSecondsAD(550)));
+ ASSERT_TRUE(cache.Get(certID, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(400), timeOut);
+
+ PutAndGet(cache, certID, Result::ERROR_REVOKED_CERTIFICATE,
+ TimeFromElapsedSecondsAD(600));
+ // This should not override the already present entry.
+ ASSERT_EQ(Success,
+ cache.Put(certID, NeckoOriginAttributes(), Result::ERROR_CONNECT_REFUSED,
+ TimeFromElapsedSecondsAD(700),
+ TimeFromElapsedSecondsAD(750)));
+ ASSERT_TRUE(cache.Get(certID, NeckoOriginAttributes(), resultOut, timeOut));
+ ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE, resultOut);
+ ASSERT_EQ(TimeFromElapsedSecondsAD(600), timeOut);
+}
+
+TEST_F(psm_OCSPCacheTest, TestOriginAttributes)
+{
+ CertID certID(fakeIssuer1, fakeKey000, fakeSerial0000);
+
+ SCOPED_TRACE("");
+ NeckoOriginAttributes attrs;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ PutAndGet(cache, certID, Success, now, attrs);
+
+ Result resultOut;
+ Time timeOut(Time::uninitialized);
+ attrs.mFirstPartyDomain.AssignLiteral("bar.com");
+ ASSERT_FALSE(cache.Get(certID, attrs, resultOut, timeOut));
+
+ // OCSP cache should not be isolated by containers.
+ attrs.mUserContextId = 1;
+ attrs.mFirstPartyDomain.AssignLiteral("foo.com");
+ ASSERT_TRUE(cache.Get(certID, attrs, resultOut, timeOut));
+}
diff --git a/security/manager/ssl/tests/gtest/README.txt b/security/manager/ssl/tests/gtest/README.txt
new file mode 100644
index 000000000..0e5132269
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/README.txt
@@ -0,0 +1,2 @@
+Please name all test cases in this directory with the prefix "psm". This makes
+it easier to run all PSM related GTests at once.
diff --git a/security/manager/ssl/tests/gtest/STSParserTest.cpp b/security/manager/ssl/tests/gtest/STSParserTest.cpp
new file mode 100644
index 000000000..bedf57fea
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/STSParserTest.cpp
@@ -0,0 +1,144 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+#include "nsNetUtil.h"
+#include "nsISiteSecurityService.h"
+#include "nsIURI.h"
+
+void
+TestSuccess(const char* hdr, bool extraTokens,
+ uint64_t expectedMaxAge, bool expectedIncludeSubdomains,
+ nsISiteSecurityService* sss)
+{
+ nsCOMPtr<nsIURI> dummyUri;
+ nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html");
+ ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Failed to create URI";
+
+ uint64_t maxAge = 0;
+ bool includeSubdomains = false;
+ rv = sss->UnsafeProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri,
+ hdr, 0, &maxAge, &includeSubdomains, nullptr);
+ ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Failed to process valid header: " << hdr;
+
+ ASSERT_EQ(maxAge, expectedMaxAge) << "Did not correctly parse maxAge";
+ EXPECT_EQ(includeSubdomains, expectedIncludeSubdomains) <<
+ "Did not correctly parse presence/absence of includeSubdomains";
+
+ if (extraTokens) {
+ EXPECT_EQ(rv, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA) <<
+ "Extra tokens were expected when parsing, but were not encountered.";
+ } else {
+ EXPECT_EQ(rv, NS_OK) << "Unexpected tokens found during parsing.";
+ }
+
+ printf("%s\n", hdr);
+}
+
+void TestFailure(const char* hdr,
+ nsISiteSecurityService* sss)
+{
+ nsCOMPtr<nsIURI> dummyUri;
+ nsresult rv = NS_NewURI(getter_AddRefs(dummyUri), "https://foo.com/bar.html");
+ ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Failed to create URI";
+
+ rv = sss->UnsafeProcessHeader(nsISiteSecurityService::HEADER_HSTS, dummyUri,
+ hdr, 0, nullptr, nullptr, nullptr);
+ ASSERT_TRUE(NS_FAILED(rv)) << "Parsed invalid header: " << hdr;
+
+ printf("%s\n", hdr);
+}
+
+TEST(psm_STSParser, Test)
+{
+ nsresult rv;
+
+ // grab handle to the service
+ nsCOMPtr<nsISiteSecurityService> sss;
+ sss = do_GetService("@mozilla.org/ssservice;1", &rv);
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ // *** parsing tests
+ printf("*** Attempting to parse valid STS headers ...\n");
+
+ // SHOULD SUCCEED:
+ TestSuccess("max-age=100", false, 100, false, sss);
+ TestSuccess("max-age =100", false, 100, false, sss);
+ TestSuccess(" max-age=100", false, 100, false, sss);
+ TestSuccess("max-age = 100 ", false, 100, false, sss);
+ TestSuccess("max-age = \"100\" ", false, 100, false, sss);
+ TestSuccess("max-age=\"100\"", false, 100, false, sss);
+ TestSuccess(" max-age =\"100\" ", false, 100, false, sss);
+ TestSuccess("\tmax-age\t=\t\"100\"\t", false, 100, false, sss);
+ TestSuccess("max-age = 100 ", false, 100, false, sss);
+
+ TestSuccess("maX-aGe=100", false, 100, false, sss);
+ TestSuccess("MAX-age =100", false, 100, false, sss);
+ TestSuccess("max-AGE=100", false, 100, false, sss);
+ TestSuccess("Max-Age = 100 ", false, 100, false, sss);
+ TestSuccess("MAX-AGE = 100 ", false, 100, false, sss);
+
+ TestSuccess("max-age=100;includeSubdomains", false, 100, true, sss);
+ TestSuccess("max-age=100\t; includeSubdomains", false, 100, true, sss);
+ TestSuccess(" max-age=100; includeSubdomains", false, 100, true, sss);
+ TestSuccess("max-age = 100 ; includeSubdomains", false, 100, true, sss);
+ TestSuccess("max-age = 100 ; includeSubdomains",
+ false, 100, true, sss);
+
+ TestSuccess("maX-aGe=100; includeSUBDOMAINS", false, 100, true, sss);
+ TestSuccess("MAX-age =100; includeSubDomains", false, 100, true, sss);
+ TestSuccess("max-AGE=100; iNcLuDeSuBdoMaInS", false, 100, true, sss);
+ TestSuccess("Max-Age = 100; includesubdomains ", false, 100, true, sss);
+ TestSuccess("INCLUDESUBDOMAINS;MaX-AgE = 100 ", false, 100, true, sss);
+ // Turns out, the actual directive is entirely optional (hence the
+ // trailing semicolon)
+ TestSuccess("max-age=100;includeSubdomains;", true, 100, true, sss);
+
+ // these are weird tests, but are testing that some extended syntax is
+ // still allowed (but it is ignored)
+ TestSuccess("max-age=100 ; includesubdomainsSomeStuff",
+ true, 100, false, sss);
+ TestSuccess("\r\n\t\t \tcompletelyUnrelated = foobar; max-age= 34520103"
+ "\t \t; alsoUnrelated;asIsThis;\tincludeSubdomains\t\t \t",
+ true, 34520103, true, sss);
+ TestSuccess("max-age=100; unrelated=\"quoted \\\"thingy\\\"\"",
+ true, 100, false, sss);
+
+ // SHOULD FAIL:
+ printf("* Attempting to parse invalid STS headers (should not parse)...\n");
+ // invalid max-ages
+ TestFailure("max-age", sss);
+ TestFailure("max-age ", sss);
+ TestFailure("max-age=p", sss);
+ TestFailure("max-age=*1p2", sss);
+ TestFailure("max-age=.20032", sss);
+ TestFailure("max-age=!20032", sss);
+ TestFailure("max-age==20032", sss);
+
+ // invalid headers
+ TestFailure("foobar", sss);
+ TestFailure("maxage=100", sss);
+ TestFailure("maxa-ge=100", sss);
+ TestFailure("max-ag=100", sss);
+ TestFailure("includesubdomains", sss);
+ TestFailure(";", sss);
+ TestFailure("max-age=\"100", sss);
+ // The max-age directive here doesn't conform to the spec, so it MUST
+ // be ignored. Consequently, the REQUIRED max-age directive is not
+ // present in this header, and so it is invalid.
+ TestFailure("max-age=100, max-age=200; includeSubdomains", sss);
+ TestFailure("max-age=100 includesubdomains", sss);
+ TestFailure("max-age=100 bar foo", sss);
+ TestFailure("max-age=100randomstuffhere", sss);
+ // All directives MUST appear only once in an STS header field.
+ TestFailure("max-age=100; max-age=200", sss);
+ TestFailure("includeSubdomains; max-age=200; includeSubdomains", sss);
+ TestFailure("max-age=200; includeSubdomains; includeSubdomains", sss);
+ // The includeSubdomains directive is valueless.
+ TestFailure("max-age=100; includeSubdomains=unexpected", sss);
+ // LWS must have at least one space or horizontal tab
+ TestFailure("\r\nmax-age=200", sss);
+}
diff --git a/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
new file mode 100644
index 000000000..65f5257fb
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/TLSIntoleranceTest.cpp
@@ -0,0 +1,568 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsNSSIOLayer.h"
+#include "sslproto.h"
+#include "sslerr.h"
+
+#include "gtest/gtest.h"
+
+NS_NAMED_LITERAL_CSTRING(HOST, "example.org");
+const int16_t PORT = 443;
+
+class psm_TLSIntoleranceTest : public ::testing::Test
+{
+protected:
+ nsSSLIOLayerHelpers helpers;
+};
+
+TEST_F(psm_TLSIntoleranceTest, FullFallbackProcess)
+{
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, helpers.mVersionFallbackLimit);
+
+ // No adjustment made when there is no entry for the site.
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+
+ ASSERT_TRUE(
+ helpers.rememberStrongCiphersFailed(
+ HOST, PORT, SSL_ERROR_NO_CYPHER_OVERLAP));
+ ASSERT_EQ(SSL_ERROR_NO_CYPHER_OVERLAP,
+ helpers.getIntoleranceReason(HOST, PORT));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+
+ ASSERT_FALSE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+
+ ASSERT_FALSE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+
+ ASSERT_FALSE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ // When rememberIntolerantAtVersion returns false, it also resets the
+ // intolerance information for the server.
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, DisableFallbackWithHighLimit)
+{
+ // this value disables version fallback entirely: with this value, all efforts
+ // to mark an origin as version intolerant fail
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ 0));
+}
+
+TEST_F(psm_TLSIntoleranceTest, FallbackLimitBelowMin)
+{
+ // check that we still respect the minimum version,
+ // when it is higher than the fallback limit
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+ }
+
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ 0));
+}
+
+TEST_F(psm_TLSIntoleranceTest, TolerantOverridesIntolerant1)
+{
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ 0));
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+}
+
+TEST_F(psm_TLSIntoleranceTest, TolerantOverridesIntolerant2)
+{
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ 0));
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_2);
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntolerantDoesNotOverrideTolerant)
+{
+ // No adjustment made when there is no entry for the site.
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+ // false because we reached the floor set by rememberTolerantAtVersion.
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ 0));
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+}
+
+TEST_F(psm_TLSIntoleranceTest, PortIsRelevant)
+{
+ helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, 1,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, 2,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, 1, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, 2, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntoleranceReasonInitial)
+{
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberTolerantAtVersion(HOST, 2, SSL_LIBRARY_VERSION_TLS_1_2);
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 2));
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntoleranceReasonStored)
+{
+ helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_ERROR_BAD_SERVER);
+ ASSERT_EQ(SSL_ERROR_BAD_SERVER, helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ SSL_ERROR_BAD_MAC_READ);
+ ASSERT_EQ(SSL_ERROR_BAD_MAC_READ, helpers.getIntoleranceReason(HOST, 1));
+}
+
+TEST_F(psm_TLSIntoleranceTest, IntoleranceReasonCleared)
+{
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberIntolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT);
+ ASSERT_EQ(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT,
+ helpers.getIntoleranceReason(HOST, 1));
+
+ helpers.rememberTolerantAtVersion(HOST, 1, SSL_LIBRARY_VERSION_TLS_1_2);
+ ASSERT_EQ(0, helpers.getIntoleranceReason(HOST, 1));
+}
+
+TEST_F(psm_TLSIntoleranceTest, StrongCiphersFailed)
+{
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_1;
+
+ ASSERT_TRUE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ // When rememberIntolerantAtVersion returns false, it also resets the
+ // intolerance information for the server.
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, StrongCiphersFailedAt1_1)
+{
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_0;
+
+ // No adjustment made when there is no entry for the site.
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_TRUE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ range.min, range.max, 0));
+ }
+
+ {
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, StrongCiphersFailedWithHighLimit)
+{
+ // this value disables version fallback entirely: with this value, all efforts
+ // to mark an origin as version intolerant fail
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
+ // ...but weak ciphers fallback will not be disabled
+ ASSERT_TRUE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_1,
+ 0));
+ ASSERT_FALSE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ 0));
+}
+
+TEST_F(psm_TLSIntoleranceTest, TolerantDoesNotOverrideWeakCiphersFallback)
+{
+ ASSERT_TRUE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ // No adjustment made when intolerant is zero.
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+}
+
+TEST_F(psm_TLSIntoleranceTest, WeakCiphersFallbackDoesNotOverrideTolerant)
+{
+ // No adjustment made when there is no entry for the site.
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+ // false because strongCipherWorked is set by rememberTolerantAtVersion.
+ ASSERT_FALSE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSForgetIntolerance)
+{
+ {
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+ }
+
+ {
+ helpers.forgetIntolerance(HOST, PORT);
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSForgetStrongCipherFailed)
+{
+ {
+ ASSERT_TRUE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(StrongCiphersFailed, strongCipherStatus);
+ }
+
+ {
+ helpers.forgetIntolerance(HOST, PORT);
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSDontForgetTolerance)
+{
+ {
+ helpers.rememberTolerantAtVersion(HOST, PORT, SSL_LIBRARY_VERSION_TLS_1_1);
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+ }
+
+ {
+ ASSERT_TRUE(helpers.rememberIntolerantAtVersion(HOST, PORT,
+ SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2,
+ 0));
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_1, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+ }
+
+ {
+ helpers.forgetIntolerance(HOST, PORT);
+
+ SSLVersionRange range = { SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_2 };
+ StrongCipherStatus strongCipherStatus = StrongCipherStatusUnknown;
+ helpers.adjustForTLSIntolerance(HOST, PORT, range, strongCipherStatus);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_0, range.min);
+ ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
+ ASSERT_EQ(StrongCiphersWorked, strongCipherStatus);
+ }
+}
+
+TEST_F(psm_TLSIntoleranceTest, TLSPerSiteFallbackLimit)
+{
+ NS_NAMED_LITERAL_CSTRING(example_com, "example.com");
+ NS_NAMED_LITERAL_CSTRING(example_net, "example.net");
+ NS_NAMED_LITERAL_CSTRING(example_org, "example.org");
+
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_0;
+
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.mVersionFallbackLimit = SSL_LIBRARY_VERSION_TLS_1_2;
+
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(example_com);
+
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(NS_LITERAL_CSTRING("example.com,example.net"));
+
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(example_net);
+
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_FALSE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+
+ helpers.setInsecureFallbackSites(EmptyCString());
+
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_com, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_net, SSL_LIBRARY_VERSION_TLS_1_0));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_2));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_1));
+ ASSERT_TRUE(helpers.fallbackLimitReached(example_org, SSL_LIBRARY_VERSION_TLS_1_0));
+}
diff --git a/security/manager/ssl/tests/gtest/moz.build b/security/manager/ssl/tests/gtest/moz.build
new file mode 100644
index 000000000..735ab2971
--- /dev/null
+++ b/security/manager/ssl/tests/gtest/moz.build
@@ -0,0 +1,29 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+SOURCES += [
+ 'CertDBTest.cpp',
+ 'DataStorageTest.cpp',
+ 'DeserializeCertTest.cpp',
+ 'MD4Test.cpp',
+ 'OCSPCacheTest.cpp',
+ 'STSParserTest.cpp',
+ 'TLSIntoleranceTest.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/security/certverifier',
+ '/security/manager/ssl',
+ '/security/pkix/include',
+ '/security/pkix/test/lib',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']