From 80c7012c5b308b4e2794bd336ee6170998300028 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 28 Sep 2015 14:58:56 +0200 Subject: [PATCH 001/135] Bug 1208985 - Use height for `scroll-snap-points-y` percentage values; r=kip The CSS scroll snapping specification defines percentages to be `relative to same axis of the padding-box of the scroll container`, which means that y-axis should be based no height, not width. --- layout/base/tests/test_scroll_snapping.html | 33 +++++++++++++++++---- layout/generic/nsGfxScrollFrame.cpp | 2 +- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/layout/base/tests/test_scroll_snapping.html b/layout/base/tests/test_scroll_snapping.html index c17b5eeb7ec..1ba862ee1ca 100644 --- a/layout/base/tests/test_scroll_snapping.html +++ b/layout/base/tests/test_scroll_snapping.html @@ -9,8 +9,8 @@

-
-
+
+
@@ -574,7 +574,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 250px. (Page Down)",
+    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 500px. (Page Down)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -589,7 +589,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 250px. (Page Up)",
+    "description"             : "Ensure that when paging down/up that the farthest snap point before the destination is selected and prioritized over a snap point that is past the destination, even if the snap point past the destination is closer to the destination.  Setup - two snap points between current position and destination and one snap point past the destination which is closer than any of the other points. Scrollable rect size is 500px. (Page Up)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -604,7 +604,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 250px. (Page Down)",
+    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 500px. (Page Down)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -619,7 +619,7 @@ var testCases = [
   },
 
   {
-    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 250px. (Page Up)",
+    "description"             : "Ensure that when paging down/up that the closest snap point past the destination is selected when no snap points exist between the starting position and the destination.  Additionally, a snap point closer to the destination than the one past the snap point, but not in the scrolling direction, must not be selected.  Setup - Two snap points beyond the destination and one snap point in the opposite direction of scrolling which is closest to the destination. Scrollable rect size is 500px. (Page Up)",
     "scrollSnapType"          : "mandatory",
     "scrollSnapPointsX"       : "none",
     "scrollSnapPointsY"       : "none",
@@ -687,6 +687,25 @@ var lastScrollTop;
 var lastScrollLeft;
 var stopFrameCount;
 
+// The tests should work the same way when all the values for the scroll
+// container are provided in percentages. To assert that, we just duplicate all
+// the test cases and replace the pixel values related to the scroll container
+// with percentage values, based on its clientWidth/Height sespectively.
+function addPercentageTests() {
+  var width = sc.clientWidth;
+  var height = sc.clientHeight;
+  var pxRegexp = /(\d+)px/;
+  var rewriteW = (_, w) => (parseInt(w, 10) / width * 100) + "%";
+  var rewriteH = (_, h) => (parseInt(h, 10) / height * 100) + "%";
+  testCases = testCases.concat(testCases.map(testCase => Object.assign({}, testCase, {
+    description: "With Percentages: " + testCase.description,
+    scrollSnapPointsX: testCase.scrollSnapPointsX.replace(pxRegexp, rewriteW),
+    scrollSnapPointsY: testCase.scrollSnapPointsY.replace(pxRegexp, rewriteH),
+    scrollSnapDestination: testCase.scrollSnapDestination
+      .replace(pxRegexp, rewriteW).replace(pxRegexp, rewriteH),
+  })));
+}
+
 function initTest() {
   var testCase = testCases[step];
   sc.style.scrollSnapType = testCase.scrollSnapType;
@@ -726,6 +745,8 @@ function testScrollSnapping() {
   sc = document.getElementById("sc");
   sd = document.getElementById("sd");
 
+  addPercentageTests();
+
   initTest();
 }
 
diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp
index 59bbbce2f99..f82d587d4c6 100644
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -5713,7 +5713,7 @@ ScrollFrameHelper::GetSnapPointForDestination(nsIScrollableFrame::ScrollUnit aUn
   }
   if (styles.mScrollSnapPointsY.GetUnit() != eStyleUnit_None) {
     nscoord interval = nsRuleNode::ComputeCoordPercentCalc(styles.mScrollSnapPointsY,
-                                                           scrollPortSize.width);
+                                                           scrollPortSize.height);
     calcSnapPoints.AddHorizontalEdgeInterval(scrollRange, interval, destPos.y);
   }
 

From c5498a7e6e4ddcb95ae50e3b6c60400a294fef14 Mon Sep 17 00:00:00 2001
From: Richard Barnes 
Date: Mon, 28 Sep 2015 15:15:16 -0400
Subject: [PATCH 002/135] Bug 1208847 - Add telemetry to measure how often
 secure cookies are set from non-secure origins r=mcmanus

---
 netwerk/cookie/nsCookieService.cpp           | 15 +++++++++++++++
 toolkit/components/telemetry/Histograms.json |  8 ++++++++
 2 files changed, 23 insertions(+)

diff --git a/netwerk/cookie/nsCookieService.cpp b/netwerk/cookie/nsCookieService.cpp
index 3bdc40547c8..3c74f817282 100644
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2851,6 +2851,21 @@ nsCookieService::SetCookieInternal(nsIURI                        *aHostURI,
   // so we can handle them separately.
   bool newCookie = ParseAttributes(aCookieHeader, cookieAttributes);
 
+  // Collect telemetry on how often secure cookies are set from non-secure
+  // origins, and vice-versa.
+  //
+  // 0 = nonsecure and "http:"
+  // 1 = nonsecure and "https:"
+  // 2 = secure and "http:"
+  // 3 = secure and "https:"
+  bool isHTTPS;
+  nsresult rv = aHostURI->SchemeIs("https", &isHTTPS);
+  if (NS_SUCCEEDED(rv)) {
+    Telemetry::Accumulate(Telemetry::COOKIE_SCHEME_SECURITY,
+                          ((cookieAttributes.isSecure)? 0x02 : 0x00) |
+                          ((isHTTPS)? 0x01 : 0x00));
+  }
+
   int64_t currentTimeInUsec = PR_Now();
 
   // calculate expiry time of cookie.
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 935845d1ce7..6d05e56aa1d 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7562,6 +7562,14 @@
     "n_values": 10,
     "description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS"
   },
+  "COOKIE_SCHEME_SECURITY": {
+    "alert_emails": ["seceng@mozilla.org"],
+    "expires_in_version": "50",
+    "kind": "enumerated",
+    "n_values": 10,
+    "releaseChannelCollection": "opt-out",
+    "description": "How often are secure cookies set from non-secure origins, and vice-versa? 0=nonsecure/http, 1=nonsecure/https, 2=secure/http, 3=secure/https"
+  },
   "NTLM_MODULE_USED_2": {
     "expires_in_version": "never",
     "kind": "enumerated",

From 23f24fcef23d9e1dc0e21b8f931b1bb97d3d44c1 Mon Sep 17 00:00:00 2001
From: Kartikaya Gupta 
Date: Mon, 28 Sep 2015 15:17:34 -0400
Subject: [PATCH 003/135] Back out the bits of bug 1205087 that cache the
 AnimatedGeometryRoot on DisplayItem. r=backout

The change breaks some scenarios with APZ scrolling, in particular the code
that layerizes the scroll handoff chain for deeply nested scrollable frames.
---
 layout/base/FrameLayerBuilder.cpp | 25 +++++++++++-------
 layout/base/nsDisplayList.cpp     | 43 ++++++++++++-------------------
 layout/base/nsDisplayList.h       | 37 +++++++++++++++++---------
 layout/base/nsLayoutDebugger.cpp  |  9 ++++---
 layout/base/nsLayoutUtils.cpp     | 10 ++++---
 layout/base/nsLayoutUtils.h       |  3 ++-
 layout/generic/nsCanvasFrame.h    |  6 +----
 7 files changed, 70 insertions(+), 63 deletions(-)

diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp
index 69c1f83a9e9..59fc7615bf2 100644
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1042,7 +1042,7 @@ public:
     MOZ_ASSERT_IF(isAtRoot, mContainerReferenceFrame == mBuilder->RootReferenceFrame());
     mContainerAnimatedGeometryRoot = isAtRoot
       ? mContainerReferenceFrame
-      : aContainerItem->AnimatedGeometryRoot();
+      : nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder);
     MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(mBuilder->RootReferenceFrame(),
                                                       mContainerAnimatedGeometryRoot));
     NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(mBuilder),
@@ -2100,7 +2100,8 @@ ContainerState::GetLayerCreationHint(const nsIFrame* aAnimatedGeometryRoot)
   nsIFrame* fParent;
   for (const nsIFrame* f = aAnimatedGeometryRoot;
        f != mContainerAnimatedGeometryRoot;
-       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, fParent)) {
+       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder,
+           fParent, mContainerAnimatedGeometryRoot)) {
     fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (!fParent) {
       break;
@@ -2781,7 +2782,7 @@ PaintedLayerDataTree::GetParentAnimatedGeometryRoot(const nsIFrame* aAnimatedGeo
   }
 
   nsIFrame* agr = Builder()->FindAnimatedGeometryRootFor(
-    const_cast(aAnimatedGeometryRoot));
+    const_cast(aAnimatedGeometryRoot), Builder()->RootReferenceFrame());
   MOZ_ASSERT_IF(agr, nsLayoutUtils::IsAncestorFrameCrossDoc(Builder()->RootReferenceFrame(), agr));
   if (agr != aAnimatedGeometryRoot) {
     return agr;
@@ -2792,7 +2793,7 @@ PaintedLayerDataTree::GetParentAnimatedGeometryRoot(const nsIFrame* aAnimatedGeo
   if (!parent) {
     return nullptr;
   }
-  return Builder()->FindAnimatedGeometryRootFor(parent);
+  return Builder()->FindAnimatedGeometryRootFor(parent, Builder()->RootReferenceFrame());
 }
 
 void
@@ -3694,7 +3695,8 @@ ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
 
     // Try using the actual active scrolled root of the backmost item, as that
     // should result in the least invalidation when scrolling.
-    *aAnimatedGeometryRoot = item->AnimatedGeometryRoot();
+    *aAnimatedGeometryRoot =
+      nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
     return true;
   }
   return false;
@@ -3769,7 +3771,7 @@ GetScrollClipIntersection(nsDisplayListBuilder* aBuilder, const nsIFrame* aAnima
   for (const nsIFrame* f = aAnimatedGeometryRoot;
        f != aStopAtAnimatedGeometryRoot;
        f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(aBuilder,
-           fParent)) {
+           fParent, aStopAtAnimatedGeometryRoot)) {
     fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (!fParent) {
       // This means aStopAtAnimatedGeometryRoot was not an ancestor
@@ -3870,7 +3872,8 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
     bool forceInactive;
     const nsIFrame* animatedGeometryRoot;
     const nsIFrame* animatedGeometryRootForScrollMetadata = nullptr;
-    const nsIFrame* realAnimatedGeometryRootOfItem = item->AnimatedGeometryRoot();
+    const nsIFrame* realAnimatedGeometryRootOfItem =
+      nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
     if (mFlattenToSingleLayer) {
       forceInactive = true;
       animatedGeometryRoot = lastAnimatedGeometryRoot;
@@ -3882,7 +3885,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
         // take ShouldFixToViewport() into account, so it will return something different
         // for fixed background items.
         animatedGeometryRootForScrollMetadata = nsLayoutUtils::GetAnimatedGeometryRootForFrame(
-            mBuilder, item->Frame());
+            mBuilder, item->Frame(), item->ReferenceFrame());
       } else {
         // For inactive layer subtrees, splitting content into PaintedLayers
         // based on animated geometry roots is pointless. It's more efficient
@@ -4693,7 +4696,8 @@ ContainerState::SetupScrollingMetadata(NewLayerEntry* aEntry)
   nsIFrame* fParent;
   for (const nsIFrame* f = aEntry->mAnimatedGeometryRootForScrollMetadata;
        f != mContainerAnimatedGeometryRoot;
-       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(this->mBuilder, fParent)) {
+       f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(this->mBuilder,
+           fParent, mContainerAnimatedGeometryRoot)) {
     fParent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (!fParent) {
       // This means mContainerAnimatedGeometryRoot was not an ancestor
@@ -4837,7 +4841,8 @@ ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer
     if (!e->mOpaqueRegion.IsEmpty()) {
       const nsIFrame* animatedGeometryRootToCover = animatedGeometryRootForOpaqueness;
       if (e->mOpaqueForAnimatedGeometryRootParent &&
-          nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, e->mAnimatedGeometryRoot->GetParent())
+          nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, e->mAnimatedGeometryRoot->GetParent(),
+                                                         mContainerAnimatedGeometryRoot)
             == mContainerAnimatedGeometryRoot) {
         animatedGeometryRootToCover = mContainerAnimatedGeometryRoot;
         data = FindOpaqueRegionEntry(opaqueRegions,
diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp
index 53b60d68a1b..bb52f7481f7 100644
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1030,9 +1030,6 @@ nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParen
     // for background-attachment:fixed elements.
     return true;
   }
-  if (aFrame->IsTransformed()) {
-    return true;
-  }
 
   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
   if (!parent)
@@ -1071,20 +1068,23 @@ nsDisplayListBuilder::IsAnimatedGeometryRoot(nsIFrame* aFrame, nsIFrame** aParen
 
 bool
 nsDisplayListBuilder::GetCachedAnimatedGeometryRoot(const nsIFrame* aFrame,
+                                                    const nsIFrame* aStopAtAncestor,
                                                     nsIFrame** aOutResult)
 {
-  return mAnimatedGeometryRootCache.Get(const_cast(aFrame), aOutResult);
+  AnimatedGeometryRootLookup lookup(aFrame, aStopAtAncestor);
+  return mAnimatedGeometryRootCache.Get(lookup, aOutResult);
 }
 
 static nsIFrame*
 ComputeAnimatedGeometryRootFor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                               const nsIFrame* aStopAtAncestor = nullptr,
                                bool aUseCache = false)
 {
   nsIFrame* cursor = aFrame;
-  while (cursor != aBuilder->RootReferenceFrame()) {
+  while (cursor != aStopAtAncestor) {
     if (aUseCache) {
       nsIFrame* result;
-      if (aBuilder->GetCachedAnimatedGeometryRoot(cursor, &result)) {
+      if (aBuilder->GetCachedAnimatedGeometryRoot(cursor, aStopAtAncestor, &result)) {
         return result;
       }
     }
@@ -1097,14 +1097,15 @@ ComputeAnimatedGeometryRootFor(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
 }
 
 nsIFrame*
-nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame)
+nsDisplayListBuilder::FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor)
 {
   if (aFrame == mCurrentFrame) {
     return mCurrentAnimatedGeometryRoot;
   }
 
-  nsIFrame* result = ComputeAnimatedGeometryRootFor(this, aFrame, true);
-  mAnimatedGeometryRootCache.Put(aFrame, result);
+  nsIFrame* result = ComputeAnimatedGeometryRootFor(this, aFrame, aStopAtAncestor, true);
+  AnimatedGeometryRootLookup lookup(aFrame, aStopAtAncestor);
+  mAnimatedGeometryRootCache.Put(lookup, result);
   return result;
 }
 
@@ -1112,9 +1113,8 @@ void
 nsDisplayListBuilder::RecomputeCurrentAnimatedGeometryRoot()
 {
   mCurrentAnimatedGeometryRoot = ComputeAnimatedGeometryRootFor(this, const_cast(mCurrentFrame));
-  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(RootReferenceFrame(),
-                                                    mCurrentAnimatedGeometryRoot), "Bad");
-  mAnimatedGeometryRootCache.Put(const_cast(mCurrentFrame), mCurrentAnimatedGeometryRoot);
+  AnimatedGeometryRootLookup lookup(mCurrentFrame, nullptr);
+  mAnimatedGeometryRootCache.Put(lookup, mCurrentAnimatedGeometryRoot);
 }
 
 void
@@ -1980,17 +1980,11 @@ void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
   : mFrame(aFrame)
   , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
-  , mAnimatedGeometryRoot(nullptr)
 #ifdef MOZ_DUMP_PAINTING
   , mPainted(false)
 #endif
 {
   mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
-  // This can return the wrong result if the item override ShouldFixToViewport(),
-  // the item needs to set it again in its constructor.
-  mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder);
-  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(),
-                                                      mAnimatedGeometryRoot), "Bad");
   NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 ||
                !aBuilder->IsForPainting(), "dirty rect not set");
   // The dirty rect is for mCurrentFrame, so we have to use
@@ -2130,9 +2124,6 @@ nsDisplayBackgroundImage::nsDisplayBackgroundImage(nsDisplayListBuilder* aBuilde
   MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
 
   mBounds = GetBoundsInternal(aBuilder);
-  if (ShouldFixToViewport(aBuilder)) {
-    mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder);
-  }
 }
 
 nsDisplayBackgroundImage::~nsDisplayBackgroundImage()
@@ -3699,7 +3690,8 @@ RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder,
   LayerState result = LAYER_INACTIVE;
   for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
     if (result == LAYER_INACTIVE &&
-        i->AnimatedGeometryRoot() != aExpectedAnimatedGeometryRootForChildren) {
+        nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) !=
+          aExpectedAnimatedGeometryRootForChildren) {
       result = LAYER_ACTIVE;
     }
 
@@ -3956,7 +3948,8 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder,
   if (NeedsActiveLayer(aBuilder))
     return LAYER_ACTIVE;
 
-  return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, AnimatedGeometryRoot());
+  return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
+    nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder));
 }
 
 bool
@@ -4670,14 +4663,10 @@ nsDisplayTransform::nsDisplayTransform(nsDisplayListBuilder* aBuilder,
 void
 nsDisplayTransform::SetReferenceFrameToAncestor(nsDisplayListBuilder* aBuilder)
 {
-  if (mFrame == aBuilder->RootReferenceFrame()) {
-    return;
-  }
   nsIFrame *outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame);
   mReferenceFrame =
     aBuilder->FindReferenceFrameFor(outerFrame);
   mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
-  mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootForFrame(aBuilder, outerFrame);
   mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame;
 }
 
diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h
index e0631bf1e31..b1b9d8c81d6 100644
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -257,7 +257,7 @@ public:
    * Returns the nearest ancestor frame to aFrame that is considered to have
    * (or will have) animated geometry. This can return aFrame.
    */
-  nsIFrame* FindAnimatedGeometryRootFor(nsIFrame* aFrame);
+  nsIFrame* FindAnimatedGeometryRootFor(nsIFrame* aFrame, const nsIFrame* aStopAtAncestor = nullptr);
 
   /**
    * @return the root of the display list's frame (sub)tree, whose origin
@@ -635,11 +635,11 @@ public:
           aBuilder->mCurrentAnimatedGeometryRoot = aForChild;
         }
       } else {
+        // Stop at the previous animated geometry root to help cases that
+        // aren't immediate descendents.
         aBuilder->mCurrentAnimatedGeometryRoot =
-          aBuilder->FindAnimatedGeometryRootFor(aForChild);
+          aBuilder->FindAnimatedGeometryRootFor(aForChild, aBuilder->mCurrentAnimatedGeometryRoot);
       }
-      MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(),
-                                                        aBuilder->mCurrentAnimatedGeometryRoot), "Bad");
       aBuilder->mCurrentFrame = aForChild;
       aBuilder->mDirtyRect = aDirtyRect;
       aBuilder->mIsAtRootOfPseudoStackingContext = aIsRoot;
@@ -957,6 +957,7 @@ public:
    * true if the cache was hit. Return false if the cache was not hit.
    */
   bool GetCachedAnimatedGeometryRoot(const nsIFrame* aFrame,
+                                     const nsIFrame* aStopAtAncestor,
                                      nsIFrame** aOutResult);
 
   void SetCommittedScrollInfoItemList(nsDisplayList* aScrollInfoItemStorage) {
@@ -1088,8 +1089,27 @@ private:
   // The animated geometry root for mCurrentFrame.
   nsIFrame*                      mCurrentAnimatedGeometryRoot;
 
+  struct AnimatedGeometryRootLookup {
+    const nsIFrame* mFrame;
+    const nsIFrame* mStopAtFrame;
+
+    AnimatedGeometryRootLookup(const nsIFrame* aFrame, const nsIFrame* aStopAtFrame)
+      : mFrame(aFrame)
+      , mStopAtFrame(aStopAtFrame)
+    {
+    }
+
+    PLDHashNumber Hash() const {
+      return mozilla::HashBytes(this, sizeof(this));
+    }
+
+    bool operator==(const AnimatedGeometryRootLookup& aOther) const {
+      return mFrame == aOther.mFrame && mStopAtFrame == aOther.mStopAtFrame;
+    }
+  };
   // Cache for storing animated geometry roots for arbitrary frames
-  nsDataHashtable, nsIFrame*> mAnimatedGeometryRootCache;
+  nsDataHashtable, nsIFrame*>
+                                 mAnimatedGeometryRootCache;
   // will-change budget tracker
   nsDataHashtable, DocumentWillChangeBudget>
                                  mWillChangeBudget;
@@ -1205,7 +1225,6 @@ public:
     : mFrame(aFrame)
     , mClip(nullptr)
     , mReferenceFrame(nullptr)
-    , mAnimatedGeometryRoot(nullptr)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
@@ -1652,11 +1671,6 @@ public:
    */
   virtual const nsIFrame* ReferenceFrameForChildren() const { return mReferenceFrame; }
 
-  nsIFrame* AnimatedGeometryRoot() const {
-    MOZ_ASSERT(mAnimatedGeometryRoot, "Must have cached AGR before accessing it!");
-    return mAnimatedGeometryRoot;
-  }
-
   /**
    * Checks if this display item (or any children) contains content that might
    * be rendered with component alpha (e.g. subpixel antialiasing). Returns the
@@ -1716,7 +1730,6 @@ protected:
   const DisplayItemClip* mClip;
   // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
   const nsIFrame* mReferenceFrame;
-  nsIFrame* mAnimatedGeometryRoot;
   // Result of ToReferenceFrame(mFrame), if mFrame is non-null
   nsPoint   mToReferenceFrame;
   // This is the rectangle that needs to be painted.
diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp
index f518a8c6ed8..c7aa8bac6b5 100644
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -162,7 +162,9 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
   }
   bool snap;
   nsRect rect = aItem->GetBounds(aBuilder, &snap);
-  nsRect layerRect = rect - aItem->AnimatedGeometryRoot()->GetOffsetToCrossDoc(aItem->ReferenceFrame());
+  nsRect layerRect = rect -
+    nsLayoutUtils::GetAnimatedGeometryRootFor(aItem, aBuilder)->
+      GetOffsetToCrossDoc(aItem->ReferenceFrame());
   nscolor color;
   nsRect vis = aItem->GetVisibleRect();
   nsRect component = aItem->GetComponentAlphaBounds(aBuilder);
@@ -177,7 +179,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
     aStream << nsPrintfCString("", string.BeginReading());
   }
 #endif
-  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s ref=0x%p agr=0x%p",
+  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s",
           aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(),
           (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
           rect.x, rect.y, rect.width, rect.height,
@@ -185,8 +187,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem,
           vis.x, vis.y, vis.width, vis.height,
           component.x, component.y, component.width, component.height,
           clip.ToString().get(),
-          aItem->IsUniform(aBuilder, &color) ? " uniform" : "",
-          aItem->ReferenceFrame(), aItem->AnimatedGeometryRoot());
+          aItem->IsUniform(aBuilder, &color) ? " uniform" : "");
 
   nsRegionRectIterator iter(opaque);
   for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index fce3f37e24a..ba9004e2f4d 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1835,9 +1835,10 @@ nsLayoutUtils::IsScrollbarThumbLayerized(nsIFrame* aThumbFrame)
 
 nsIFrame*
 nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder,
-                                               nsIFrame* aFrame)
+                                               nsIFrame* aFrame,
+                                               const nsIFrame* aStopAtAncestor)
 {
-  return aBuilder->FindAnimatedGeometryRootFor(aFrame);
+  return aBuilder->FindAnimatedGeometryRootFor(aFrame, aStopAtAncestor);
 }
 
 nsIFrame*
@@ -1853,9 +1854,10 @@ nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem,
     nsIFrame* viewportFrame =
       nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame);
     NS_ASSERTION(viewportFrame, "no viewport???");
-    return GetAnimatedGeometryRootForFrame(aBuilder, viewportFrame);
+    return GetAnimatedGeometryRootForFrame(aBuilder, viewportFrame,
+        aBuilder->FindReferenceFrameFor(viewportFrame));
   }
-  return GetAnimatedGeometryRootForFrame(aBuilder, f);
+  return GetAnimatedGeometryRootForFrame(aBuilder, f, aItem->ReferenceFrame());
 }
 
 // static
diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h
index 9ec7b9cbe77..d74022b5005 100644
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -558,7 +558,8 @@ public:
    * aStopAtAncestor if no closer ancestor is found.
    */
   static nsIFrame* GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder,
-                                                   nsIFrame* aFrame);
+                                                   nsIFrame* aFrame,
+                                                   const nsIFrame* aStopAtAncestor);
 
   /**
     * GetScrollableFrameFor returns the scrollable frame for a scrolled frame
diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h
index b0d5d22ef00..4e56b80af0a 100644
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -239,11 +239,7 @@ public:
   nsDisplayCanvasBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                  uint32_t aLayer, const nsStyleBackground* aBg)
     : nsDisplayBackgroundImage(aBuilder, aFrame, aLayer, aBg)
-  {
-    if (ShouldFixToViewport(aBuilder)) {
-      mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder);
-    }
-  }
+  {}
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
 

From 98c6366a22b22d2130c1baca8bc4db50378a9a69 Mon Sep 17 00:00:00 2001
From: Henrik Skupin 
Date: Fri, 25 Sep 2015 13:49:13 +0200
Subject: [PATCH 004/135] Bug 1208431 - Allow query_minidump_stackwalk() to
 take a specific manifest file as parameter. r=jlund

---
 .../mozharness/mozilla/testing/testbase.py         | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/testing/mozharness/mozharness/mozilla/testing/testbase.py b/testing/mozharness/mozharness/mozilla/testing/testbase.py
index fc19622f0a2..4ca571aab3c 100755
--- a/testing/mozharness/mozharness/mozilla/testing/testbase.py
+++ b/testing/mozharness/mozharness/mozilla/testing/testbase.py
@@ -642,7 +642,7 @@ Did you run with --create-virtualenv? Is mozinstall in virtualenv_modules?""")
         else:
             self.fatal('could not determine minidump filename')
 
-    def query_minidump_stackwalk(self):
+    def query_minidump_stackwalk(self, manifest=None):
         if self.minidump_stackwalk_path:
             return self.minidump_stackwalk_path
         c = self.config
@@ -650,13 +650,17 @@ Did you run with --create-virtualenv? Is mozinstall in virtualenv_modules?""")
 
         if c.get('download_minidump_stackwalk'):
             minidump_stackwalk_path = self.query_minidump_filename()
-            tooltool_manifest_path = self.query_minidump_tooltool_manifest()
+
+            if not manifest:
+                tooltool_manifest_path = self.query_minidump_tooltool_manifest()
+                manifest = os.path.join(dirs.get('abs_test_install_dir',
+                                                 os.path.join(dirs['abs_work_dir'], 'tests')),
+                                        tooltool_manifest_path),
+
             self.info('grabbing minidump binary from tooltool')
             try:
                 self.tooltool_fetch(
-                    manifest=os.path.join(dirs.get('abs_test_install_dir',
-                                                   os.path.join(dirs['abs_work_dir'], 'tests')),
-                                          tooltool_manifest_path),
+                    manifest=manifest,
                     output_dir=dirs['abs_work_dir'],
                     cache=c.get('tooltool_cache')
                 )

From 50b152df261687044c5c50acf9f5f06bbd323b83 Mon Sep 17 00:00:00 2001
From: David Keeler 
Date: Mon, 24 Aug 2015 15:53:07 -0700
Subject: [PATCH 005/135] bug 1203312 - convert tlsserver to generate
 certificates at build time r=Cykesiopka,mgoodwin

---
 security/manager/ssl/StaticHPKPins.h          |   2 +-
 security/manager/ssl/tests/unit/head_psm.js   |  31 +-
 security/manager/ssl/tests/unit/pycert.py     | 108 ++++--
 security/manager/ssl/tests/unit/pykey.py      |  21 +-
 .../ssl/tests/unit/test_cert_blocklist.js     |  58 +--
 .../ssl/tests/unit/test_cert_chains.js        |   8 +-
 .../ssl/tests/unit/test_cert_overrides.js     |  55 ++-
 .../ssl/tests/unit/test_ocsp_caching.js       |  10 +-
 .../tests/unit/test_ocsp_no_hsts_upgrade.js   |   2 +-
 .../ssl/tests/unit/test_ocsp_required.js      |   2 +-
 .../ssl/tests/unit/test_ocsp_stapling.js      |  12 +-
 .../tests/unit/test_ocsp_stapling_expired.js  |  18 +-
 .../manager/ssl/tests/unit/test_pinning.js    |   2 +-
 .../tlsserver/badSubjectAltNames.pem.certspec |   3 +
 .../unit/tlsserver/beforeEpoch.pem.certspec   |   5 +
 .../tlsserver/beforeEpochINT.pem.certspec     |   5 +
 .../tlsserver/beforeEpochIssuer.pem.certspec  |   4 +
 .../ca-used-as-end-entity.pem.certspec        |   5 +
 .../manager/ssl/tests/unit/tlsserver/cert9.db | Bin 327680 -> 0 bytes
 .../unit/tlsserver/cmd/BadCertServer.cpp      |  56 ++-
 .../tlsserver/cmd/GenerateOCSPResponse.cpp    | 213 -----------
 .../unit/tlsserver/cmd/OCSPStaplingServer.cpp |   4 +-
 .../ssl/tests/unit/tlsserver/default-ee.der   | Bin 640 -> 190 bytes
 .../unit/tlsserver/default-ee.key.keyspec     |   1 +
 .../unit/tlsserver/default-ee.pem.certspec    |   4 +
 .../delegatedSHA1Signer.pem.certspec          |   5 +
 .../tlsserver/delegatedSigner.pem.certspec    |   4 +
 .../tlsserver/eeIssuedByNonCA.pem.certspec    |   4 +
 .../tlsserver/eeIssuedByV1Cert.pem.certspec   |   3 +
 .../ssl/tests/unit/tlsserver/expired-ee.der   | Bin 531 -> 0 bytes
 .../unit/tlsserver/expired-ee.pem.certspec    |   5 +
 .../unit/tlsserver/expiredINT.pem.certspec    |   5 +
 .../unit/tlsserver/expiredissuer.pem.certspec |   4 +
 .../tests/unit/tlsserver/generate_certs.sh    | 354 ------------------
 .../inadequateKeySizeEE.pem.certspec          |   5 +
 .../unit/tlsserver/inadequatekeyusage-ee.der  | Bin 568 -> 0 bytes
 .../inadequatekeyusage-ee.pem.certspec        |   5 +
 ...legatedSignerFromIntermediate.pem.certspec |   4 +
 ...gatedSignerKeyUsageCrlSigning.pem.certspec |   4 +
 ...dDelegatedSignerNoExtKeyUsage.pem.certspec |   3 +
 ...legatedSignerWrongExtKeyUsage.pem.certspec |   4 +
 .../ipAddressAsDNSNameInSAN.pem.certspec      |   3 +
 .../manager/ssl/tests/unit/tlsserver/key4.db  | Bin 524288 -> 0 bytes
 .../tests/unit/tlsserver/lib/TLSServer.cpp    | 231 +++++++++++-
 .../ssl/tests/unit/tlsserver/lib/TLSServer.h  |  10 +
 .../md5signature-expired.pem.certspec         |   6 +
 .../unit/tlsserver/md5signature.pem.certspec  |   5 +
 .../tlsserver/mismatch-expired.pem.certspec   |   5 +
 .../mismatch-notYetValid.pem.certspec         |   5 +
 .../mismatch-untrusted-expired.pem.certspec   |   5 +
 .../tlsserver/mismatch-untrusted.pem.certspec |   4 +
 .../unit/tlsserver/mismatch.pem.certspec      |   4 +
 .../unit/tlsserver/mismatchCN.pem.certspec    |   2 +
 .../ssl/tests/unit/tlsserver/moz.build        |  76 ++++
 .../unit/tlsserver/noValidNames.pem.certspec  |   3 +
 .../unit/tlsserver/notYetValid.pem.certspec   |   5 +
 .../tlsserver/notYetValidINT.pem.certspec     |   5 +
 .../tlsserver/notYetValidIssuer.pem.certspec  |   4 +
 .../tlsserver/nsCertTypeCritical.pem.certspec |   4 +
 ...rtTypeCriticalWithExtKeyUsage.pem.certspec |   6 +
 .../nsCertTypeNotCritical.pem.certspec        |   4 +
 .../ocspEEWithIntermediate.pem.certspec       |   4 +
 .../tlsserver/ocspOtherEndEntity.pem.certspec |   4 +
 .../tests/unit/tlsserver/other-issuer-ee.der  | Bin 630 -> 0 bytes
 .../tlsserver/other-issuer-ee.pem.certspec    |   6 +
 .../tests/unit/tlsserver/other-test-ca.der    | Bin 452 -> 0 bytes
 .../unit/tlsserver/other-test-ca.key.keyspec  |   1 +
 .../unit/tlsserver/other-test-ca.pem.certspec |   7 +
 .../ssl/tests/unit/tlsserver/pkcs11.txt       |   5 -
 ...sa-1016-keysizeDelegatedSigner.key.keyspec |   1 +
 ...a-1016-keysizeDelegatedSigner.pem.certspec |   4 +
 .../tests/unit/tlsserver/same-issuer-ee.der   | Bin 668 -> 0 bytes
 .../tlsserver/same-issuer-ee.pem.certspec     |   4 +
 .../self-signed-EE-with-cA-true.pem.certspec  |   5 +
 .../selfsigned-inadequateEKU.pem.certspec     |   6 +
 .../unit/tlsserver/selfsigned.pem.certspec    |   4 +
 .../ssl/tests/unit/tlsserver/test-ca.der      | Bin 440 -> 0 bytes
 .../tests/unit/tlsserver/test-ca.pem.certspec |   4 +
 .../ssl/tests/unit/tlsserver/test-int-ee.der  | Bin 533 -> 0 bytes
 .../unit/tlsserver/test-int-ee.pem.certspec   |   3 +
 .../ssl/tests/unit/tlsserver/test-int.der     | Bin 499 -> 0 bytes
 .../unit/tlsserver/test-int.pem.certspec      |   5 +
 .../tests/unit/tlsserver/unknown-issuer.der   | Bin 933 -> 0 bytes
 .../unit/tlsserver/unknownissuer.pem.certspec |   4 +
 .../tlsserver/untrusted-expired.pem.certspec  |   5 +
 .../tlsserver/untrustedissuer.pem.certspec    |   4 +
 .../ssl/tests/unit/tlsserver/v1Cert.der       | Bin 407 -> 0 bytes
 .../tests/unit/tlsserver/v1Cert.pem.certspec  |   3 +
 security/manager/tools/genHPKPStaticPins.js   |  21 +-
 89 files changed, 768 insertions(+), 757 deletions(-)
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/badSubjectAltNames.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/beforeEpoch.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/beforeEpochINT.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/beforeEpochIssuer.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/ca-used-as-end-entity.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/cert9.db
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/default-ee.key.keyspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/default-ee.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/delegatedSHA1Signer.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/delegatedSigner.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/eeIssuedByNonCA.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/eeIssuedByV1Cert.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/expired-ee.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/expired-ee.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/expiredINT.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/expiredissuer.pem.certspec
 delete mode 100755 security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/inadequateKeySizeEE.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/invalidDelegatedSignerFromIntermediate.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/invalidDelegatedSignerKeyUsageCrlSigning.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/invalidDelegatedSignerNoExtKeyUsage.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/invalidDelegatedSignerWrongExtKeyUsage.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/ipAddressAsDNSNameInSAN.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/key4.db
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/md5signature-expired.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/md5signature.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/mismatch-expired.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/mismatch-notYetValid.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/mismatch-untrusted-expired.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/mismatch-untrusted.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/mismatch.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/mismatchCN.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/noValidNames.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/notYetValid.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/notYetValidINT.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/notYetValidIssuer.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/nsCertTypeCritical.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/nsCertTypeCriticalWithExtKeyUsage.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/nsCertTypeNotCritical.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/ocspEEWithIntermediate.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/ocspOtherEndEntity.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/other-issuer-ee.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/other-issuer-ee.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/other-test-ca.key.keyspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/other-test-ca.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/pkcs11.txt
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/rsa-1016-keysizeDelegatedSigner.key.keyspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/rsa-1016-keysizeDelegatedSigner.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/same-issuer-ee.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/same-issuer-ee.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/self-signed-EE-with-cA-true.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/selfsigned-inadequateEKU.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/selfsigned.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/test-ca.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/test-ca.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/test-int-ee.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/test-int-ee.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/test-int.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/test-int.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/unknown-issuer.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/unknownissuer.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/untrusted-expired.pem.certspec
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/untrustedissuer.pem.certspec
 delete mode 100644 security/manager/ssl/tests/unit/tlsserver/v1Cert.der
 create mode 100644 security/manager/ssl/tests/unit/tlsserver/v1Cert.pem.certspec

diff --git a/security/manager/ssl/StaticHPKPins.h b/security/manager/ssl/StaticHPKPins.h
index ada30758103..046ced243c2 100644
--- a/security/manager/ssl/StaticHPKPins.h
+++ b/security/manager/ssl/StaticHPKPins.h
@@ -81,7 +81,7 @@ static const char kDigiCert_High_Assurance_EV_Root_CAFingerprint[] =
 
 /* End Entity Test Cert */
 static const char kEnd_Entity_Test_CertFingerprint[] =
-  "lzCakFt+nADIfIkgk+UE/EQ9SaT2nay2yu2iykVbvV8=";
+  "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
 
 /* Entrust Root Certification Authority */
 static const char kEntrust_Root_Certification_AuthorityFingerprint[] =
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
index 6041fdc7e65..5ca6d81aa73 100644
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -435,7 +435,7 @@ function _setupTLSServerTest(serverBinName)
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
   // The trusted CA that is typically used for "good" certificates.
-  addCertFromFile(certdb, "tlsserver/test-ca.der", "CTu,u,u");
+  addCertFromFile(certdb, "tlsserver/test-ca.pem", "CTu,u,u");
 
   const CALLBACK_PORT = 8444;
 
@@ -639,9 +639,8 @@ FakeSSLStatus.prototype = {
 
 // Utility functions for adding tests relating to certificate error overrides
 
-// Helper function for add_cert_override_test and
-// add_prevented_cert_override_test. Probably doesn't need to be called
-// directly.
+// Helper function for add_cert_override_test. Probably doesn't need to be
+// called directly.
 function add_cert_override(aHost, aExpectedBits, aSecurityInfo) {
   let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                                .SSLStatus;
@@ -668,6 +667,28 @@ function add_cert_override_test(aHost, aExpectedBits, aExpectedError) {
   add_connection_test(aHost, PRErrorCodeSuccess);
 }
 
+// Helper function for add_prevented_cert_override_test. This is much like
+// add_cert_override except it may not be the case that the connection has an
+// SSLStatus set on it. In this case, the error was not overridable anyway, so
+// we consider it a success.
+function attempt_adding_cert_override(aHost, aExpectedBits, aSecurityInfo) {
+  let sslstatus = aSecurityInfo.QueryInterface(Ci.nsISSLStatusProvider)
+                               .SSLStatus;
+  if (sslstatus) {
+    let bits =
+      (sslstatus.isUntrusted ? Ci.nsICertOverrideService.ERROR_UNTRUSTED : 0) |
+      (sslstatus.isDomainMismatch ? Ci.nsICertOverrideService.ERROR_MISMATCH : 0) |
+      (sslstatus.isNotValidAtThisTime ? Ci.nsICertOverrideService.ERROR_TIME : 0);
+    Assert.equal(bits, aExpectedBits,
+                 "Actual and expected override bits should match");
+    let cert = sslstatus.serverCert;
+    let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
+                                .getService(Ci.nsICertOverrideService);
+    certOverrideService.rememberValidityOverride(aHost, 8443, cert, aExpectedBits,
+                                                 true);
+  }
+}
+
 // Given a host, expected error bits (see nsICertOverrideService.idl), and
 // an expected error code, tests that an initial connection to the host fails
 // with the expected errors and that adding an override does not result in a
@@ -676,6 +697,6 @@ function add_cert_override_test(aHost, aExpectedBits, aExpectedError) {
 // overridable, even if an entry is added to the override service.
 function add_prevented_cert_override_test(aHost, aExpectedBits, aExpectedError) {
   add_connection_test(aHost, aExpectedError, null,
-                      add_cert_override.bind(this, aHost, aExpectedBits));
+                      attempt_adding_cert_override.bind(this, aHost, aExpectedBits));
   add_connection_test(aHost, aExpectedError);
 }
diff --git a/security/manager/ssl/tests/unit/pycert.py b/security/manager/ssl/tests/unit/pycert.py
index 86889ba3425..0a28a663f4e 100755
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -16,7 +16,8 @@ subject:
 [validity:]
 [issuerKey:]
 [subjectKey:]
-[signature:{sha1WithRSAEncryption,sha256WithRSAEncryption,ecdsaWithSHA256}]
+[signature:{sha256WithRSAEncryption,sha1WithRSAEncryption,
+            md5WithRSAEncryption,ecdsaWithSHA256}]
 [extension:>]
 [...]
 
@@ -31,6 +32,7 @@ subjectAlternativeName:[,...]
 authorityInformationAccess:
 certificatePolicies:
 nameConstraints:{permitted,excluded}:[,...]
+nsCertType:sslServer
 
 Where:
   [] indicates an optional field or component of a field
@@ -64,6 +66,9 @@ be 'utf8String' or 'printableString'. If the given string does not
 contain a '/', it is assumed to represent a common name.
 DirectoryNames also use this format. When specifying a directoryName in
 a nameConstraints extension, the implicit form may not be used.
+
+If an extension name has '[critical]' after it, it will be marked as
+critical. Otherwise (by default), it will not be marked as critical.
 """
 
 from pyasn1.codec.der import decoder
@@ -178,6 +183,14 @@ class UnknownDNTypeError(UnknownBaseError):
         self.category = 'DN'
 
 
+class UnknownNSCertTypeError(UnknownBaseError):
+    """Helper exception type to handle unknown nsCertType types."""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'nsCertType'
+
+
 def getASN1Tag(asn1Type):
     """Helper function for returning the base tag value of a given
     type from the pyasn1 package"""
@@ -254,27 +267,29 @@ def stringToDN(string, tag=None):
     name.setComponentByPosition(0, rdns)
     return name
 
-def stringToAlgorithmIdentifier(string):
+def stringToAlgorithmIdentifiers(string):
     """Helper function that converts a description of an algorithm
-    to a representation usable by the pyasn1 package"""
+    to a representation usable by the pyasn1 package and a hash
+    algorithm name for use by pykey."""
     algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+    algorithmName = None
     algorithm = None
-    name = None
     if string == 'sha1WithRSAEncryption':
-        name = 'SHA-1'
+        algorithmName = 'SHA-1'
         algorithm = rfc2459.sha1WithRSAEncryption
     elif string == 'sha256WithRSAEncryption':
-        name = 'SHA-256'
+        algorithmName = 'SHA-256'
         algorithm = univ.ObjectIdentifier('1.2.840.113549.1.1.11')
+    elif string == 'md5WithRSAEncryption':
+        algorithmName = 'MD5'
+        algorithm = rfc2459.md5WithRSAEncryption
     elif string == 'ecdsaWithSHA256':
-        # Note that this value is only used by pykey.py to tell if
-        # ECDSA is allowed.  It does not conform to the pyECC syntax.
-        name = 'SHA-256'
+        algorithmName = 'sha256'
         algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.2')
     else:
         raise UnknownAlgorithmTypeError(string)
     algorithmIdentifier.setComponentByName('algorithm', algorithm)
-    return (algorithmIdentifier, name)
+    return (algorithmIdentifier, algorithmName)
 
 def datetimeToTime(dt):
     """Takes a datetime object and returns an rfc2459.Time object with
@@ -380,22 +395,28 @@ class Certificate:
             self.notAfter = self.now + delta
 
     def decodeExtension(self, extension):
-        extensionType = extension.split(':')[0]
-        value = ':'.join(extension.split(':')[1:])
+        match = re.search('([a-zA-Z]+)(\[critical\])?:(.*)', extension)
+        if not match:
+            raise UnknownExtensionTypeError(extension)
+        extensionType = match.group(1)
+        critical = match.group(2)
+        value = match.group(3)
         if extensionType == 'basicConstraints':
-            self.addBasicConstraints(value)
+            self.addBasicConstraints(value, critical)
         elif extensionType == 'keyUsage':
-            self.addKeyUsage(value)
+            self.addKeyUsage(value, critical)
         elif extensionType == 'extKeyUsage':
-            self.addExtKeyUsage(value)
+            self.addExtKeyUsage(value, critical)
         elif extensionType == 'subjectAlternativeName':
-            self.addSubjectAlternativeName(value)
+            self.addSubjectAlternativeName(value, critical)
         elif extensionType == 'authorityInformationAccess':
-            self.addAuthorityInformationAccess(value)
+            self.addAuthorityInformationAccess(value, critical)
         elif extensionType == 'certificatePolicies':
-            self.addCertificatePolicies(value)
+            self.addCertificatePolicies(value, critical)
         elif extensionType == 'nameConstraints':
-            self.addNameConstraints(value)
+            self.addNameConstraints(value, critical)
+        elif extensionType == 'nsCertType':
+            self.addNSCertType(value, critical)
         else:
             raise UnknownExtensionTypeError(extensionType)
 
@@ -407,16 +428,20 @@ class Certificate:
         else:
             raise UnknownKeyTargetError(subjectOrIssuer)
 
-    def addExtension(self, extensionType, extensionValue):
+    def addExtension(self, extensionType, extensionValue, critical):
         if not self.extensions:
             self.extensions = []
         encapsulated = univ.OctetString(encoder.encode(extensionValue))
         extension = rfc2459.Extension()
         extension.setComponentByName('extnID', extensionType)
+        # critical is either the string '[critical]' or None.
+        # We only care whether or not it is truthy.
+        if critical:
+            extension.setComponentByName('critical', True)
         extension.setComponentByName('extnValue', encapsulated)
         self.extensions.append(extension)
 
-    def addBasicConstraints(self, basicConstraints):
+    def addBasicConstraints(self, basicConstraints, critical):
         cA = basicConstraints.split(',')[0]
         pathLenConstraint = basicConstraints.split(',')[1]
         basicConstraintsExtension = rfc2459.BasicConstraints()
@@ -427,11 +452,11 @@ class Certificate:
                     subtypeSpec=constraint.ValueRangeConstraint(0, 64))
             basicConstraintsExtension.setComponentByName('pathLenConstraint',
                                                          pathLenConstraintValue)
-        self.addExtension(rfc2459.id_ce_basicConstraints, basicConstraintsExtension)
+        self.addExtension(rfc2459.id_ce_basicConstraints, basicConstraintsExtension, critical)
 
-    def addKeyUsage(self, keyUsage):
+    def addKeyUsage(self, keyUsage, critical):
         keyUsageExtension = rfc2459.KeyUsage(keyUsage)
-        self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension)
+        self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension, critical)
 
     def keyPurposeToOID(self, keyPurpose):
         if keyPurpose == 'serverAuth':
@@ -452,15 +477,15 @@ class Certificate:
             return rfc2459.id_kp_timeStamping
         raise UnknownKeyPurposeTypeError(keyPurpose)
 
-    def addExtKeyUsage(self, extKeyUsage):
+    def addExtKeyUsage(self, extKeyUsage, critical):
         extKeyUsageExtension = rfc2459.ExtKeyUsageSyntax()
         count = 0
         for keyPurpose in extKeyUsage.split(','):
             extKeyUsageExtension.setComponentByPosition(count, self.keyPurposeToOID(keyPurpose))
             count += 1
-        self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension)
+        self.addExtension(rfc2459.id_ce_extKeyUsage, extKeyUsageExtension, critical)
 
-    def addSubjectAlternativeName(self, dNSNames):
+    def addSubjectAlternativeName(self, dNSNames, critical):
         subjectAlternativeName = rfc2459.SubjectAltName()
         count = 0
         for dNSName in dNSNames.split(','):
@@ -471,15 +496,15 @@ class Certificate:
             generalName.setComponentByName('dNSName', dNSName.decode(encoding='string_escape'))
             subjectAlternativeName.setComponentByPosition(count, generalName)
             count += 1
-        self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName)
+        self.addExtension(rfc2459.id_ce_subjectAltName, subjectAlternativeName, critical)
 
-    def addAuthorityInformationAccess(self, ocspURI):
+    def addAuthorityInformationAccess(self, ocspURI, critical):
         sequence = univ.Sequence()
         accessDescription = stringToAccessDescription(ocspURI)
         sequence.setComponentByPosition(0, accessDescription)
-        self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence)
+        self.addExtension(rfc2459.id_pe_authorityInfoAccess, sequence, critical)
 
-    def addCertificatePolicies(self, policyOID):
+    def addCertificatePolicies(self, policyOID, critical):
         policies = rfc2459.CertificatePolicies()
         policy = rfc2459.PolicyInformation()
         if policyOID == 'any':
@@ -487,9 +512,9 @@ class Certificate:
         policyIdentifier = rfc2459.CertPolicyId(policyOID)
         policy.setComponentByName('policyIdentifier', policyIdentifier)
         policies.setComponentByPosition(0, policy)
-        self.addExtension(rfc2459.id_ce_certificatePolicies, policies)
+        self.addExtension(rfc2459.id_ce_certificatePolicies, policies, critical)
 
-    def addNameConstraints(self, constraints):
+    def addNameConstraints(self, constraints, critical):
         nameConstraints = NameConstraints()
         if constraints.startswith('permitted:'):
             (subtreesType, subtreesTag) = ('permittedSubtrees', 0)
@@ -514,7 +539,13 @@ class Certificate:
             generalSubtrees.setComponentByPosition(pos, generalSubtree)
             pos = pos + 1
         nameConstraints.setComponentByName(subtreesType, generalSubtrees)
-        self.addExtension(rfc2459.id_ce_nameConstraints, nameConstraints)
+        self.addExtension(rfc2459.id_ce_nameConstraints, nameConstraints, critical)
+
+    def addNSCertType(self, certType, critical):
+        if certType != 'sslServer':
+            raise UnknownNSCertTypeError(certType)
+        self.addExtension(univ.ObjectIdentifier('2.16.840.1.113730.1.1'), univ.BitString("'01'B"),
+            critical)
 
     def getVersion(self):
         return rfc2459.Version(self.versionValue).subtype(
@@ -523,9 +554,6 @@ class Certificate:
     def getSerialNumber(self):
         return decoder.decode(self.serialNumber)[0]
 
-    def getSignature(self):
-        return stringToAlgorithmIdentifier(self.signature)
-
     def getIssuer(self):
         return stringToDN(self.issuer)
 
@@ -545,8 +573,7 @@ class Certificate:
         return stringToDN(self.subject)
 
     def toDER(self):
-        (signatureOID, hashAlg) = self.getSignature()
-
+        (signatureOID, hashName) = stringToAlgorithmIdentifiers(self.signature)
         tbsCertificate = rfc2459.TBSCertificate()
         tbsCertificate.setComponentByName('version', self.getVersion())
         tbsCertificate.setComponentByName('serialNumber', self.getSerialNumber())
@@ -568,8 +595,7 @@ class Certificate:
         certificate.setComponentByName('tbsCertificate', tbsCertificate)
         certificate.setComponentByName('signatureAlgorithm', signatureOID)
         tbsDER = encoder.encode(tbsCertificate)
-
-        certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER, hashAlg))
+        certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER, hashName))
         return encoder.encode(certificate)
 
     def toPEM(self):
diff --git a/security/manager/ssl/tests/unit/pykey.py b/security/manager/ssl/tests/unit/pykey.py
index d31c9ebdbe5..bbdc30c0e95 100755
--- a/security/manager/ssl/tests/unit/pykey.py
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -66,13 +66,6 @@ class UnknownKeySpecificationError(UnknownBaseError):
         UnknownBaseError.__init__(self, value)
         self.category = 'key specification'
 
-class ParameterError(UnknownBaseError):
-    """Exception type indicating that the key was misconfigured"""
-
-    def __init__(self, value):
-        UnknownBaseError.__init__(self, value)
-        self.category = 'key parameter'
-
 class RSAPublicKey(univ.Sequence):
     """Helper type for encoding an RSA public key"""
     componentType = namedtype.NamedTypes(
@@ -560,12 +553,12 @@ class RSAKey:
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
 
-    def sign(self, data, digest):
+    def sign(self, data, hashAlgorithmName):
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
         rsaPrivateKey = rsa.PrivateKey(self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q)
-        signature = rsa.sign(data, rsaPrivateKey, digest)
+        signature = rsa.sign(data, rsaPrivateKey, hashAlgorithmName)
         return byteStringToHexifiedBitString(signature)
 
 
@@ -671,14 +664,10 @@ class ECCKey:
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
 
-    def sign(self, data, digest):
+    def sign(self, data, hashAlgorithmName):
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
-        # This should really only be used with SHA-256
-        if digest != "SHA-256":
-            raise ParameterError(digest)
-
         # There is some non-determinism in ECDSA signatures. Work around
         # this by patching ecc.ecdsa.urandom to not be random.
         with mock.patch('ecc.ecdsa.urandom', side_effect=notRandom):
@@ -688,9 +677,9 @@ class ECCKey:
             # Also patch in secp256k1 if applicable.
             if self.keyOID == secp256k1:
                 with mock.patch('ecc.curves.DOMAINS', {256: secp256k1Params}):
-                    x, y = encoding.dec_point(self.key.sign(data, 'sha256'))
+                    x, y = encoding.dec_point(self.key.sign(data, hashAlgorithmName))
             else:
-                x, y = encoding.dec_point(self.key.sign(data, 'sha256'))
+                x, y = encoding.dec_point(self.key.sign(data, hashAlgorithmName))
             point = ECPoint()
             point.setComponentByName('x', x)
             point.setComponentByName('y', y)
diff --git a/security/manager/ssl/tests/unit/test_cert_blocklist.js b/security/manager/ssl/tests/unit/test_cert_blocklist.js
index b854f597d8b..1a4a1ea80a2 100644
--- a/security/manager/ssl/tests/unit/test_cert_blocklist.js
+++ b/security/manager/ssl/tests/unit/test_cert_blocklist.js
@@ -94,15 +94,15 @@ var blocklist_contents =
     "and serial" +
     // some mixed
     // In this case, the issuer name and the valid serialNumber correspond
-    // to test-int.der in tlsserver/
-    "" +
+    // to test-int.pem in tlsserver/
+    "" +
     "oops! more nonsense." +
-    "X1o=" +
+    "Y1HQqXGtw7ek2v/QAqBL8jf6rbA=" +
     // ... and some good
     // In this case, the issuer name and the valid serialNumber correspond
-    // to other-test-ca.der in tlsserver/ (for testing root revocation)
-    "" +
-    "AKEIivg=" +
+    // to other-test-ca.pem in tlsserver/ (for testing root revocation)
+    "" +
+    "Szin5enUEn9TnVq29c4IMPNFuqE=" +
     // This item corresponds to an entry in sample_revocations.txt where:
     // isser name is "another imaginary issuer" base-64 encoded, and
     // serialNumbers are:
@@ -113,8 +113,9 @@ var blocklist_contents =
     "" +
     "c2VyaWFsMi4=" +
     "YW5vdGhlciBzZXJpYWwu" +
-    "" +
+    // This item revokes same-issuer-ee.pem by subject and serial number.
+    "" +
     "";
 testserver.registerPathHandler("/push_blocked_cert/",
   function serveResponse(request, response) {
@@ -136,13 +137,12 @@ var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
 converter.charset = "UTF-8";
 
 function verify_cert(file, expectedError) {
-  let cert_der = readFile(do_get_file(file));
-  let ee = certDB.constructX509(cert_der, cert_der.length);
+  let ee = constructCertFromFile(file);
   checkCertErrorGeneric(certDB, ee, expectedError, certificateUsageSSLServer);
 }
 
 function load_cert(cert, trust) {
-  let file = "tlsserver/" + cert + ".der";
+  let file = "tlsserver/" + cert + ".pem";
   addCertFromFile(certDB, file, trust);
 }
 
@@ -201,20 +201,20 @@ function run_test() {
   ok(test_is_revoked(certList, "another imaginary issuer", "serial2."),
      "issuer / serial pair should be blocked");
 
-  // Soon we'll load a blocklist which revokes test-int.der, which issued
-  // test-int-ee.der.
+  // Soon we'll load a blocklist which revokes test-int.pem, which issued
+  // test-int-ee.pem.
   // Check the cert validates before we load the blocklist
-  let file = "tlsserver/test-int-ee.der";
+  let file = "tlsserver/test-int-ee.pem";
   verify_cert(file, PRErrorCodeSuccess);
 
-  // The blocklist also revokes other-test-ca.der, which issued other-ca-ee.der.
+  // The blocklist also revokes other-test-ca.pem, which issued other-ca-ee.pem.
   // Check the cert validates before we load the blocklist
-  file = "tlsserver/other-issuer-ee.der";
+  file = "tlsserver/other-issuer-ee.pem";
   verify_cert(file, PRErrorCodeSuccess);
 
-  // The blocklist will revoke same-issuer-ee.der via subject / pubKeyHash.
+  // The blocklist will revoke same-issuer-ee.pem via subject / pubKeyHash.
   // Check the cert validates before we load the blocklist
-  file = "tlsserver/same-issuer-ee.der";
+  file = "tlsserver/same-issuer-ee.pem";
   verify_cert(file, PRErrorCodeSuccess);
 
   // blocklist load is async so we must use add_test from here
@@ -271,35 +271,35 @@ function run_test() {
       contents = contents + (contents.length == 0 ? "" : "\n") + line.value;
     } while (hasmore);
     let expected = "# Auto generated contents. Do not edit.\n" +
-                  "MCIxIDAeBgNVBAMTF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5\n"+
-                  "\t2ETEb0QP574JkM+35JVwS899PLUmt1rrJyWOV6GRfAE=\n"+
-                  "MBgxFjAUBgNVBAMTDU90aGVyIHRlc3QgQ0E=\n" +
-                  " AKEIivg=\n" +
-                  "MBIxEDAOBgNVBAMTB1Rlc3QgQ0E=\n" +
-                  " X1o=\n" +
+                  "MCIxIDAeBgNVBAMMF0Fub3RoZXIgVGVzdCBFbmQtZW50aXR5\n"+
+                  "\tVCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=\n"+
+                  "MBIxEDAOBgNVBAMMB1Rlc3QgQ0E=\n" +
+                  " Y1HQqXGtw7ek2v/QAqBL8jf6rbA=\n" +
+                  "MBgxFjAUBgNVBAMMDU90aGVyIHRlc3QgQ0E=\n" +
+                  " Szin5enUEn9TnVq29c4IMPNFuqE=\n" +
                   "YW5vdGhlciBpbWFnaW5hcnkgaXNzdWVy\n" +
                   " YW5vdGhlciBzZXJpYWwu\n" +
                   " c2VyaWFsMi4=";
     equal(contents, expected, "revocations.txt should be as expected");
 
     // Check the blocklisted intermediate now causes a failure
-    let file = "tlsserver/test-int-ee.der";
+    let file = "tlsserver/test-int-ee.pem";
     verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
 
     // Check the ee with the blocklisted root also causes a failure
-    file = "tlsserver/other-issuer-ee.der";
+    file = "tlsserver/other-issuer-ee.pem";
     verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
 
     // Check the ee blocked by subject / pubKey causes a failure
-    file = "tlsserver/same-issuer-ee.der";
+    file = "tlsserver/same-issuer-ee.pem";
     verify_cert(file, SEC_ERROR_REVOKED_CERTIFICATE);
 
     // Check a non-blocklisted chain still validates OK
-    file = "tlsserver/default-ee.der";
+    file = "tlsserver/default-ee.pem";
     verify_cert(file, PRErrorCodeSuccess);
 
     // Check a bad cert is still bad (unknown issuer)
-    file = "tlsserver/unknown-issuer.der";
+    file = "tlsserver/unknownissuer.pem";
     verify_cert(file, SEC_ERROR_UNKNOWN_ISSUER);
 
     // check that save with no further update is a no-op
diff --git a/security/manager/ssl/tests/unit/test_cert_chains.js b/security/manager/ssl/tests/unit/test_cert_chains.js
index 02e23cec850..215c04f790a 100644
--- a/security/manager/ssl/tests/unit/test_cert_chains.js
+++ b/security/manager/ssl/tests/unit/test_cert_chains.js
@@ -9,16 +9,16 @@ function build_cert_chain(certNames) {
   let certList = Cc["@mozilla.org/security/x509certlist;1"]
                    .createInstance(Ci.nsIX509CertList);
   certNames.forEach(function(certName) {
-    let cert = constructCertFromFile("tlsserver/" + certName + ".der");
+    let cert = constructCertFromFile("tlsserver/" + certName + ".pem");
     certList.addCert(cert);
   });
   return certList;
 }
 
 function test_cert_equals() {
-  let certA = constructCertFromFile("tlsserver/default-ee.der");
-  let certB = constructCertFromFile("tlsserver/default-ee.der");
-  let certC = constructCertFromFile("tlsserver/expired-ee.der");
+  let certA = constructCertFromFile("tlsserver/default-ee.pem");
+  let certB = constructCertFromFile("tlsserver/default-ee.pem");
+  let certC = constructCertFromFile("tlsserver/expired-ee.pem");
 
   ok(certA != certB,
      "Cert objects constructed from the same file should not be equal" +
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js
index e60c6681965..a46d0d29547 100644
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -13,20 +13,6 @@
 
 do_get_profile();
 
-function add_non_overridable_test(aHost, aExpectedError) {
-  add_connection_test(
-    aHost, aExpectedError, null,
-    function (securityInfo) {
-      // bug 754369 - no SSLStatus probably means this is a non-overridable
-      // error, which is what we're testing (although it would be best to test
-      // this directly).
-      securityInfo.QueryInterface(Ci.nsISSLStatusProvider);
-      equal(securityInfo.SSLStatus, null,
-            "As a proxy to checking that the connection error is" +
-            " non-overridable, SSLStatus should be null");
-    });
-}
-
 function check_telemetry() {
   let histogram = Cc["@mozilla.org/base/telemetry;1"]
                     .getService(Ci.nsITelemetry)
@@ -70,11 +56,11 @@ function check_telemetry() {
                            .snapshot();
   equal(keySizeHistogram.counts[0], 0,
         "Actual and expected unchecked key size counts should match");
-  equal(keySizeHistogram.counts[1], 0,
+  equal(keySizeHistogram.counts[1], 12,
         "Actual and expected successful verifications of 2048-bit keys should match");
-  equal(keySizeHistogram.counts[2], 12,
+  equal(keySizeHistogram.counts[2], 0,
         "Actual and expected successful verifications of 1024-bit keys should match");
-  equal(keySizeHistogram.counts[3], 48,
+  equal(keySizeHistogram.counts[3], 54,
         "Actual and expected key size verification failures should match");
 
   run_next_test();
@@ -147,22 +133,24 @@ function add_simple_tests() {
                          Ci.nsICertOverrideService.ERROR_UNTRUSTED,
                          SEC_ERROR_UNKNOWN_ISSUER);
 
-  add_non_overridable_test("inadequatekeyusage.example.com",
-                           SEC_ERROR_INADEQUATE_KEY_USAGE);
+  add_prevented_cert_override_test("inadequatekeyusage.example.com",
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   SEC_ERROR_INADEQUATE_KEY_USAGE);
 
   // This is intended to test the case where a verification has failed for one
   // overridable reason (e.g. unknown issuer) but then, in the process of
   // reporting that error, a non-overridable error is encountered. The
   // non-overridable error should be prioritized.
   add_test(function() {
-    let rootCert = constructCertFromFile("tlsserver/test-ca.der");
+    let rootCert = constructCertFromFile("tlsserver/test-ca.pem");
     setCertTrust(rootCert, ",,");
     run_next_test();
   });
-  add_non_overridable_test("nsCertTypeCritical.example.com",
-                           SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
+  add_prevented_cert_override_test("nsCertTypeCritical.example.com",
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION);
   add_test(function() {
-    let rootCert = constructCertFromFile("tlsserver/test-ca.der");
+    let rootCert = constructCertFromFile("tlsserver/test-ca.pem");
     setCertTrust(rootCert, "CTu,,");
     run_next_test();
   });
@@ -188,7 +176,7 @@ function add_simple_tests() {
     let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
                                 .getService(Ci.nsICertOverrideService);
     certOverrideService.clearValidityOverride("end-entity-issued-by-v1-cert.example.com", 8443);
-    let v1Cert = constructCertFromFile("tlsserver/v1Cert.der");
+    let v1Cert = constructCertFromFile("tlsserver/v1Cert.pem");
     setCertTrust(v1Cert, "CTu,,");
     clearSessionCache();
     run_next_test();
@@ -197,7 +185,7 @@ function add_simple_tests() {
                       PRErrorCodeSuccess);
   // Reset the trust for that certificate.
   add_test(function() {
-    let v1Cert = constructCertFromFile("tlsserver/v1Cert.der");
+    let v1Cert = constructCertFromFile("tlsserver/v1Cert.pem");
     setCertTrust(v1Cert, ",,");
     clearSessionCache();
     run_next_test();
@@ -209,10 +197,11 @@ function add_simple_tests() {
                          Ci.nsICertOverrideService.ERROR_UNTRUSTED,
                          SEC_ERROR_CA_CERT_INVALID);
 
-  // This host presents a 1008-bit RSA key. NSS determines this key is too
+  // This host presents a 1016-bit RSA key. NSS determines this key is too
   // small and terminates the connection. The error is not overridable.
-  add_non_overridable_test("inadequate-key-size-ee.example.com",
-                           SSL_ERROR_WEAK_SERVER_CERT_KEY);
+  add_prevented_cert_override_test("inadequate-key-size-ee.example.com",
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   SSL_ERROR_WEAK_SERVER_CERT_KEY);
 
   add_cert_override_test("ipAddressAsDNSNameInSAN.example.com",
                          Ci.nsICertOverrideService.ERROR_MISMATCH,
@@ -263,13 +252,13 @@ function add_distrust_tests() {
   // Before we specifically distrust this certificate, it should be trusted.
   add_connection_test("untrusted.example.com", PRErrorCodeSuccess);
 
-  add_distrust_test("tlsserver/default-ee.der", "untrusted.example.com",
+  add_distrust_test("tlsserver/default-ee.pem", "untrusted.example.com",
                     SEC_ERROR_UNTRUSTED_CERT);
 
-  add_distrust_test("tlsserver/other-test-ca.der",
+  add_distrust_test("tlsserver/other-test-ca.pem",
                     "untrustedissuer.example.com", SEC_ERROR_UNTRUSTED_ISSUER);
 
-  add_distrust_test("tlsserver/test-ca.der",
+  add_distrust_test("tlsserver/test-ca.pem",
                     "ca-used-as-end-entity.example.com",
                     SEC_ERROR_UNTRUSTED_ISSUER);
 }
@@ -283,7 +272,9 @@ function add_distrust_test(certFileName, hostName, expectedResult) {
     clearSessionCache();
     run_next_test();
   });
-  add_non_overridable_test(hostName, expectedResult);
+  add_prevented_cert_override_test(hostName,
+                                   Ci.nsICertOverrideService.ERROR_UNTRUSTED,
+                                   expectedResult);
   add_test(function () {
     setCertTrust(certToDistrust, "u,,");
     run_next_test();
diff --git a/security/manager/ssl/tests/unit/test_ocsp_caching.js b/security/manager/ssl/tests/unit/test_ocsp_caching.js
index 8826ba38a74..66fb9157079 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_caching.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_caching.js
@@ -24,7 +24,7 @@ function respondWithSHA1OCSP(request, response) {
   response.setStatusLine(request.httpVersion, 200, "OK");
   response.setHeader("Content-Type", "application/ocsp-response");
 
-  let args = [ ["good-delegated", "localhostAndExampleCom", "delegatedSHA1Signer" ] ];
+  let args = [ ["good-delegated", "default-ee", "delegatedSHA1Signer" ] ];
   let responses = generateOCSPResponses(args, "tlsserver");
   response.write(responses[0]);
 }
@@ -37,7 +37,7 @@ function respondWithError(request, response) {
 }
 
 function generateGoodOCSPResponse() {
-  let args = [ ["good", "localhostAndExampleCom", "unused" ] ];
+  let args = [ ["good", "default-ee", "unused" ] ];
   let responses = generateOCSPResponses(args, "tlsserver");
   return responses[0];
 }
@@ -54,7 +54,7 @@ function add_ocsp_test(aHost, aExpectedResult, aResponses, aMessage) {
         // check the number of requests matches the size of aResponses
         equal(gFetchCount, aResponses.length,
               "should have made " + aResponses.length +
-              " OCSP request" + aResponses.length == 1 ? "" : "s");
+              " OCSP request" + (aResponses.length == 1 ? "" : "s"));
       });
 }
 
@@ -134,6 +134,10 @@ function add_tests() {
                   respondWithError,
                   respondWithError,
                   respondWithError,
+                  respondWithError,
+                  respondWithError,
+                  respondWithError,
+                  respondWithError,
                 ],
                 "No stapled response -> a fetch should have been attempted");
 
diff --git a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
index 17594cd71b1..9d6ee731407 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_no_hsts_upgrade.js
@@ -16,7 +16,7 @@ function run_test() {
   // get a TLS connection.
   add_tls_server_setup("OCSPStaplingServer");
 
-  let args = [["good", "localhostAndExampleCom", "unused"]];
+  let args = [["good", "default-ee", "unused"]];
   let ocspResponses = generateOCSPResponses(args, "tlsserver");
   let goodOCSPResponse = ocspResponses[0];
 
diff --git a/security/manager/ssl/tests/unit/test_ocsp_required.js b/security/manager/ssl/tests/unit/test_ocsp_required.js
index c21db90c200..a019a7fbe2a 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_required.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_required.js
@@ -20,7 +20,7 @@ function run_test() {
   // get a TLS connection.
   add_tls_server_setup("OCSPStaplingServer");
 
-  let args = [["bad-signature", "localhostAndExampleCom", "unused"]];
+  let args = [["bad-signature", "default-ee", "unused"]];
   let ocspResponses = generateOCSPResponses(args, "tlsserver");
   let ocspResponseBadSignature = ocspResponses[0];
 
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling.js b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
index 3b6939eddc6..39b7fed5ab6 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
@@ -21,7 +21,7 @@ function add_ocsp_test(aHost, aExpectedResult, aStaplingEnabled) {
     });
 }
 
-function add_tests(certDB, otherTestCA) {
+function add_tests() {
   // In the absence of OCSP stapling, these should actually all work.
   add_ocsp_test("ocsp-stapling-good.example.com",
                 PRErrorCodeSuccess, false);
@@ -73,6 +73,9 @@ function add_tests(certDB, otherTestCA) {
 
   // This stapled response is from a CA that is untrusted and did not issue
   // the server's certificate.
+  let certDB = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+  let otherTestCA = constructCertFromFile("tlsserver/other-test-ca.pem");
   add_test(function() {
     certDB.setCertTrust(otherTestCA, Ci.nsIX509Cert.CA_CERT,
                         Ci.nsIX509CertDB.UNTRUSTED);
@@ -186,11 +189,6 @@ function check_ocsp_stapling_telemetry() {
 function run_test() {
   do_get_profile();
 
-  let certDB = Cc["@mozilla.org/security/x509certdb;1"]
-                  .getService(Ci.nsIX509CertDB);
-  let otherTestCAFile = do_get_file("tlsserver/other-test-ca.der", false);
-  let otherTestCADER = readFile(otherTestCAFile);
-  let otherTestCA = certDB.constructX509(otherTestCADER, otherTestCADER.length);
 
   let fakeOCSPResponder = new HttpServer();
   fakeOCSPResponder.registerPrefixHandler("/", function (request, response) {
@@ -202,7 +200,7 @@ function run_test() {
 
   add_tls_server_setup("OCSPStaplingServer");
 
-  add_tests(certDB, otherTestCA);
+  add_tests();
 
   add_test(function () {
     fakeOCSPResponder.stop(check_ocsp_stapling_telemetry);
diff --git a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
index 8a91e050d09..e447b9b779b 100644
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -24,18 +24,18 @@ function add_ocsp_test(aHost, aExpectedResult, aOCSPResponseToServe,
     function() {
       equal(gOCSPRequestCount, aExpectedRequestCount,
             "Should have made " + aExpectedRequestCount +
-            " fallback OCSP request" + aExpectedRequestCount == 1 ? "" : "s");
+            " fallback OCSP request" + (aExpectedRequestCount == 1 ? "" : "s"));
     });
 }
 
 do_get_profile();
 Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
 Services.prefs.setIntPref("security.OCSP.enabled", 1);
-var args = [["good", "localhostAndExampleCom", "unused"],
-             ["expiredresponse", "localhostAndExampleCom", "unused"],
-             ["oldvalidperiod", "localhostAndExampleCom", "unused"],
-             ["revoked", "localhostAndExampleCom", "unused"],
-             ["unknown", "localhostAndExampleCom", "unused"],
+var args = [["good", "default-ee", "unused"],
+             ["expiredresponse", "default-ee", "unused"],
+             ["oldvalidperiod", "default-ee", "unused"],
+             ["revoked", "default-ee", "unused"],
+             ["unknown", "default-ee", "unused"],
             ];
 var ocspResponses = generateOCSPResponses(args, "tlsserver");
 // Fresh response, certificate is good.
@@ -53,9 +53,9 @@ var ocspResponseUnknown = ocspResponses[4];
 var willNotRetry = 1;
 // but sometimes, since a bad response is in the cache, OCSP fetch will be
 // attempted for each validation - in practice, for these test certs, this
-// means 4 requests because various hash algorithm combinations are tried
-// (for sha-1 telemetry)
-var willRetry = 4;
+// means 8 requests because various hash algorithm and key size combinations
+// are tried.
+var willRetry = 8;
 
 function run_test() {
   let ocspResponder = new HttpServer();
diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js
index b771be2b046..33fef075482 100644
--- a/security/manager/ssl/tests/unit/test_pinning.js
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -249,7 +249,7 @@ function run_test() {
   add_tls_server_setup("BadCertServer");
 
   // Add a user-specified trust anchor.
-  addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
+  addCertFromFile(certdb, "tlsserver/other-test-ca.pem", "CTu,u,u");
 
   test_strict();
   test_mitm();
diff --git a/security/manager/ssl/tests/unit/tlsserver/badSubjectAltNames.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/badSubjectAltNames.pem.certspec
new file mode 100644
index 00000000000..1b368c26f1a
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/badSubjectAltNames.pem.certspec
@@ -0,0 +1,3 @@
+issuer:Test CA
+subject:EE with bad subjectAltNames
+extension:subjectAlternativeName:*.*.example.com
diff --git a/security/manager/ssl/tests/unit/tlsserver/beforeEpoch.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/beforeEpoch.pem.certspec
new file mode 100644
index 00000000000..ac97b2231a2
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/beforeEpoch.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Before UNIX Epoch Test End-entity
+validity:19460214-20310101
+extension:subjectAlternativeName:before-epoch.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/tlsserver/beforeEpochINT.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/beforeEpochINT.pem.certspec
new file mode 100644
index 00000000000..835e63f2b63
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/beforeEpochINT.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Before UNIX Epoch Test Intermediate
+validity:19460214-20310101
+extension:basicConstraints:cA,
+extension:keyUsage:cRLSign,keyCertSign
diff --git a/security/manager/ssl/tests/unit/tlsserver/beforeEpochIssuer.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/beforeEpochIssuer.pem.certspec
new file mode 100644
index 00000000000..9aabe21628e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/beforeEpochIssuer.pem.certspec
@@ -0,0 +1,4 @@
+issuer:Before UNIX Epoch Test Intermediate
+subject:Test End-entity with Before UNIX Epoch issuer
+extension:subjectAlternativeName:before-epoch-issuer.example.com
+extension:authorityInformationAccess:http://localhost:8888/
diff --git a/security/manager/ssl/tests/unit/tlsserver/ca-used-as-end-entity.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/ca-used-as-end-entity.pem.certspec
new file mode 100644
index 00000000000..8e16705b503
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/ca-used-as-end-entity.pem.certspec
@@ -0,0 +1,5 @@
+issuer:Test CA
+subject:Test Intermediate used as End-Entity
+extension:basicConstraints:cA,
+extension:authorityInformationAccess:http://localhost:8888/
+extension:subjectAlternativeName:ca-used-as-end-entity.example.com
diff --git a/security/manager/ssl/tests/unit/tlsserver/cert9.db b/security/manager/ssl/tests/unit/tlsserver/cert9.db
deleted file mode 100644
index ecc9900149d2b8e3277e64d3ac05c851c41c1a00..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 327680
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%0AwG; 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 diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp index b980e443bb2..adcfa2461b4 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp @@ -27,7 +27,7 @@ struct BadCertHost // Hostname, cert nickname pairs. const BadCertHost sBadCertHosts[] = { - { "expired.example.com", "expired" }, + { "expired.example.com", "expired-ee" }, { "notyetvalid.example.com", "notYetValid" }, { "before-epoch.example.com", "beforeEpoch" }, { "selfsigned.example.com", "selfsigned" }, @@ -38,7 +38,7 @@ const BadCertHost sBadCertHosts[] = { "notyetvalidissuer.example.com", "notYetValidIssuer" }, { "before-epoch-issuer.example.com", "beforeEpochIssuer" }, { "md5signature.example.com", "md5signature" }, - { "untrusted.example.com", "localhostAndExampleCom" }, + { "untrusted.example.com", "default-ee" }, { "untrustedissuer.example.com", "untrustedissuer" }, { "mismatch-expired.example.com", "mismatch-expired" }, { "mismatch-notYetValid.example.com", "mismatch-notYetValid" }, @@ -46,23 +46,23 @@ const BadCertHost sBadCertHosts[] = { "untrusted-expired.example.com", "untrusted-expired" }, { "md5signature-expired.example.com", "md5signature-expired" }, { "mismatch-untrusted-expired.example.com", "mismatch-untrusted-expired" }, - { "inadequatekeyusage.example.com", "inadequatekeyusage" }, + { "inadequatekeyusage.example.com", "inadequatekeyusage-ee" }, { "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" }, { "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" }, { "ca-used-as-end-entity.example.com", "ca-used-as-end-entity" }, { "ca-used-as-end-entity-name-mismatch.example.com", "ca-used-as-end-entity" }, // All of include-subdomains.pinning.example.com is pinned to End Entity - // Test Cert with nick localhostAndExampleCom. Any other nick will only + // Test Cert with nick default-ee. Any other nick will only // pass pinning when security.cert_pinning.enforcement.level != strict and // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h. - { "include-subdomains.pinning.example.com", "localhostAndExampleCom" }, - { "good.include-subdomains.pinning.example.com", "localhostAndExampleCom" }, - { "bad.include-subdomains.pinning.example.com", "otherIssuerEE" }, - { "bad.include-subdomains.pinning.example.com.", "otherIssuerEE" }, - { "bad.include-subdomains.pinning.example.com..", "otherIssuerEE" }, - { "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" }, - { "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" }, - { "test-mode.pinning.example.com", "otherIssuerEE" }, + { "include-subdomains.pinning.example.com", "default-ee" }, + { "good.include-subdomains.pinning.example.com", "default-ee" }, + { "bad.include-subdomains.pinning.example.com", "other-issuer-ee" }, + { "bad.include-subdomains.pinning.example.com.", "other-issuer-ee" }, + { "bad.include-subdomains.pinning.example.com..", "other-issuer-ee" }, + { "exclude-subdomains.pinning.example.com", "default-ee" }, + { "sub.exclude-subdomains.pinning.example.com", "other-issuer-ee" }, + { "test-mode.pinning.example.com", "other-issuer-ee" }, { "unknownissuer.include-subdomains.pinning.example.com", "unknownissuer" }, { "unknownissuer.test-mode.pinning.example.com", "unknownissuer" }, { "nsCertTypeNotCritical.example.com", "nsCertTypeNotCritical" }, @@ -78,13 +78,37 @@ const BadCertHost sBadCertHosts[] = }; int32_t -DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr, - uint32_t aSrvNameArrSize, void *aArg) +DoSNISocketConfigBySubjectCN(PRFileDesc* aFd, const SECItem* aSrvNameArr, + uint32_t aSrvNameArrSize) { - const BadCertHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize, + for (uint32_t i = 0; i < aSrvNameArrSize; i++) { + ScopedPORTString name((char*)PORT_ZAlloc(aSrvNameArr[i].len + 1)); + if (name) { + PORT_Memcpy(name, aSrvNameArr[i].data, aSrvNameArr[i].len); + if (SECSuccess == ConfigSecureServerWithNamedCert(aFd, name, + nullptr, nullptr)) { + return 0; + } + } + } + + return SSL_SNI_SEND_ALERT; +} + +int32_t +DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr, + uint32_t aSrvNameArrSize, void* aArg) +{ + const BadCertHost* host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sBadCertHosts); if (!host) { - return SSL_SNI_SEND_ALERT; + // No static cert <-> hostname mapping found. This happens when we use a + // collection of certificates in a given directory and build a cert DB at + // runtime, rather than using an NSS cert DB populated at build time. + // (This will be the default in the future.) + // For all given server names, check if the runtime-built cert DB contains + // a certificate with a matching subject CN. + return DoSNISocketConfigBySubjectCN(aFd, aSrvNameArr, aSrvNameArrSize); } if (gDebugLevel >= DEBUG_VERBOSE) { diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp index 96001e781d6..19a7e2660f0 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp @@ -110,219 +110,6 @@ WriteResponse(const char* filename, const SECItem* item) return true; } -template -SECStatus -ReadFileToBuffer(const char* basePath, const char* filename, char (&buf)[N]) -{ - static_assert(N > 0, "input buffer too small for ReadFileToBuffer"); - if (PR_snprintf(buf, N - 1, "%s/%s", basePath, filename) == 0) { - PrintPRError("PR_snprintf failed"); - return SECFailure; - } - ScopedPRFileDesc fd(PR_OpenFile(buf, PR_RDONLY, 0)); - if (!fd) { - PrintPRError("PR_Open failed"); - return SECFailure; - } - int32_t fileSize = PR_Available(fd); - if (fileSize < 0) { - PrintPRError("PR_Available failed"); - return SECFailure; - } - if (static_cast(fileSize) > N - 1) { - PR_fprintf(PR_STDERR, "file too large - not reading\n"); - return SECFailure; - } - int32_t bytesRead = PR_Read(fd, buf, fileSize); - if (bytesRead != fileSize) { - PrintPRError("PR_Read failed"); - return SECFailure; - } - buf[bytesRead] = 0; - return SECSuccess; -} - -namespace mozilla { - -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRDir, PRDir, PR_CloseDir); -MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPORTString, unsigned char, PORT_Free); - -}; - -void -AddKeyFromFile(const char* basePath, const char* filename) -{ - const char* PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----"; - const char* PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----"; - - char buf[16384] = { 0 }; - SECStatus rv = ReadFileToBuffer(basePath, filename, buf); - if (rv != SECSuccess) { - return; - } - if (strncmp(buf, PRIVATE_KEY_HEADER, strlen(PRIVATE_KEY_HEADER)) != 0) { - PR_fprintf(PR_STDERR, "invalid key - not importing\n"); - return; - } - const char* bufPtr = buf + strlen(PRIVATE_KEY_HEADER); - size_t bufLen = strlen(buf); - char base64[16384] = { 0 }; - char* base64Ptr = base64; - while (bufPtr < buf + bufLen) { - if (strncmp(bufPtr, PRIVATE_KEY_FOOTER, strlen(PRIVATE_KEY_FOOTER)) == 0) { - break; - } - if (*bufPtr != '\r' && *bufPtr != '\n') { - *base64Ptr = *bufPtr; - base64Ptr++; - } - bufPtr++; - } - - unsigned int binLength; - ScopedPORTString bin(ATOB_AsciiToData(base64, &binLength)); - if (!bin || binLength == 0) { - PrintPRError("ATOB_AsciiToData failed"); - return; - } - ScopedSECItem secitem(SECITEM_AllocItem(nullptr, nullptr, binLength)); - if (!secitem) { - PrintPRError("SECITEM_AllocItem failed"); - return; - } - memcpy(secitem->data, bin, binLength); - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); - if (!slot) { - PrintPRError("PK11_GetInternalKeySlot failed"); - return; - } - if (PK11_NeedUserInit(slot)) { - if (PK11_InitPin(slot, nullptr, nullptr) != SECSuccess) { - PrintPRError("PK11_InitPin failed"); - return; - } - } - SECKEYPrivateKey* privateKey; - if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, secitem, nullptr, nullptr, - true, false, KU_ALL, - &privateKey, nullptr) - != SECSuccess) { - PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed"); - return; - } - SECKEY_DestroyPrivateKey(privateKey); -} - -SECStatus -DecodeCertCallback(void* arg, SECItem** certs, int numcerts) -{ - if (numcerts != 1) { - PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); - return SECFailure; - } - SECItem* certDEROut = static_cast(arg); - return SECITEM_CopyItem(nullptr, certDEROut, *certs); -} - -void -AddCertificateFromFile(const char* basePath, const char* filename) -{ - char buf[16384] = { 0 }; - SECStatus rv = ReadFileToBuffer(basePath, filename, buf); - if (rv != SECSuccess) { - return; - } - SECItem certDER; - rv = CERT_DecodeCertPackage(buf, strlen(buf), DecodeCertCallback, &certDER); - if (rv != SECSuccess) { - PrintPRError("CERT_DecodeCertPackage failed"); - return; - } - ScopedCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), - &certDER, nullptr, false, - true)); - PORT_Free(certDER.data); - if (!cert) { - PrintPRError("CERT_NewTempCertificate failed"); - return; - } - const char* extension = strstr(filename, ".pem"); - if (!extension) { - PR_SetError(SEC_ERROR_INVALID_ARGS, 0); - return; - } - size_t nicknameLength = extension - filename; - memset(buf, 0, sizeof(buf)); - memcpy(buf, filename, nicknameLength); - buf[nicknameLength] = 0; - ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); - if (!slot) { - PrintPRError("PK11_GetInternalKeySlot failed"); - return; - } - rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, buf, false); - if (rv != SECSuccess) { - PrintPRError("PK11_ImportCert failed"); - } -} - -SECStatus -InitializeNSS(const char* nssCertDBDir) -{ - // First attempt to initialize NSS in read-only mode, in case the specified - // directory contains NSS DBs that are tracked by revision control. - // If this succeeds, we're done. - if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, NSS_INIT_READONLY) - == SECSuccess) { - return SECSuccess; - } - // Otherwise, create a new read-write DB and load all .pem and .key files. - if (NSS_Initialize(nssCertDBDir, "", "", SECMOD_DB, 0) != SECSuccess) { - PrintPRError("NSS_Initialize failed"); - return SECFailure; - } - const char* basePath = nssCertDBDir; - // The NSS cert DB path could have been specified as "sql:path". Trim off - // the leading "sql:" if so. - if (strncmp(basePath, "sql:", 4) == 0) { - basePath = basePath + 4; - } - ScopedPRDir fdDir(PR_OpenDir(basePath)); - if (!fdDir) { - PrintPRError("PR_OpenDir failed"); - return SECFailure; - } - // On the B2G ICS emulator, operations taken in AddCertificateFromFile or - // AddKeyFromFile appear to interact poorly with readdir (more specifically, - // something is causing readdir to never return null - it indefinitely loops - // through every file in the directory, which causes timeouts). Rather than - // waste more time chasing this down, loading certificates and keys happens in - // two phases: filename collection and then loading. (This is probably a good - // idea anyway because readdir isn't reentrant. Something could change later - // such that it gets called as a result of calling AddCertificateFromFile or - // AddKeyFromFile.) - std::vector certificates; - std::vector keys; - for (PRDirEntry* dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH); dirEntry; - dirEntry = PR_ReadDir(fdDir, PR_SKIP_BOTH)) { - size_t nameLength = strlen(dirEntry->name); - if (nameLength > 4) { - if (strncmp(dirEntry->name + nameLength - 4, ".pem", 4) == 0) { - certificates.push_back(dirEntry->name); - } else if (strncmp(dirEntry->name + nameLength - 4, ".key", 4) == 0) { - keys.push_back(dirEntry->name); - } - } - } - for (std::string& certificate : certificates) { - AddCertificateFromFile(basePath, certificate.c_str()); - } - for (std::string& key : keys) { - AddKeyFromFile(basePath, key.c_str()); - } - return SECSuccess; -} - int main(int argc, char* argv[]) { diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp index c0c1ee6947a..0d61fdef0f0 100644 --- a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp +++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp @@ -27,7 +27,7 @@ const OCSPHost sOCSPHosts[] = { "ocsp-stapling-unknown.example.com", ORTUnknown, nullptr }, { "ocsp-stapling-unknown-old.example.com", ORTUnknownOld, nullptr }, { "ocsp-stapling-good-other.example.com", ORTGoodOtherCert, "ocspOtherEndEntity" }, - { "ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "otherCA" }, + { "ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "other-test-ca" }, { "ocsp-stapling-expired.example.com", ORTExpired, nullptr }, { "ocsp-stapling-expired-fresh-ca.example.com", ORTExpiredFreshCA, nullptr }, { "ocsp-stapling-none.example.com", ORTNone, nullptr }, @@ -52,7 +52,7 @@ const OCSPHost sOCSPHosts[] = { "ocsp-stapling-delegated-keyUsage-crlSigning.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerKeyUsageCrlSigning" }, { "ocsp-stapling-delegated-wrong-extKeyUsage.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerWrongExtKeyUsage" }, { "ocsp-stapling-ancient-valid.example.com", ORTAncientAlmostExpired, nullptr}, - { "keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, "rsa-1008-keysizeDelegatedSigner" }, + { "keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, "rsa-1016-keysizeDelegatedSigner" }, { "revoked-ca-cert-used-as-end-entity.example.com", ORTRevoked, "ca-used-as-end-entity" }, { nullptr, ORTNull, nullptr } }; diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.der b/security/manager/ssl/tests/unit/tlsserver/default-ee.der index 7f89b7e37c393fcfaad3d327f859be9ea460c135..3a9b8fa9bc0418d36bc7ea8c66727483a02c25d3 100644 GIT binary patch literal 190 zcmXZU%MHRn420o(s+bC*gjWxop@AhkAuEC%t!o`M7y6SFTshpd$$W6u-n|0PAnn#^kq o9`#EmM-4G7tVMGf5KB%@Ynu~^OI*=!y6v{#tlIzYcio|F-sjgsvj6}9 literal 640 zcmXqLVyZD{Vtl%QnTe5!i77tZfR~L^tIebBJ1-+6H!FjIkfDG99~*Nh3$rkLNNRD3 zg0rK6oH(zciJ^g!fq|Kkp^15vIIjsZ*Fefp!axk7haarRH7`XsHLoPIq|%^qKC%sr ztPISJy$lA8olK353@4wKyk?9l%6a}ad-F?U$>}R6{hjpYT*<-dSB~i!bsykW{^GJ% z$I&}ya;9K~{JV+PS<%O8*p?}qp7)z?YJ8Qw<4C`YvmVE*R^8i9ohJ)cY9wnOoE`Ny z?d!txeV#fs$8We^7rXVRnd7ngmQQn*`G2f!-zR3OrXlr?XUQ(l|AnhWnV1FdMYX=MQf`UX58UkEcY{%2t_U_cIeVB9hTgWM$f%ijKjcLjQno6oWD zth5zRyFF>{iE}~=H`Fhd;ZsvqvwNES*J#~=!kWv*;SP<*U)_vj*qBw{&ni}Zb$@8i zY_302X*0hEAHMv-(_1@2L$gsw_p|oRRJGcee5N&wwk}%tSq>|q05#O)X#fBK diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.key.keyspec b/security/manager/ssl/tests/unit/tlsserver/default-ee.key.keyspec new file mode 100644 index 00000000000..4ad96d51599 --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/default-ee.key.keyspec @@ -0,0 +1 @@ +default diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/default-ee.pem.certspec new file mode 100644 index 00000000000..554339ff526 --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/default-ee.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Test End-entity +extension:subjectAlternativeName:localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com +extension:authorityInformationAccess:http://localhost:8888/ diff --git a/security/manager/ssl/tests/unit/tlsserver/delegatedSHA1Signer.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/delegatedSHA1Signer.pem.certspec new file mode 100644 index 00000000000..bdf3e2ee4dc --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/delegatedSHA1Signer.pem.certspec @@ -0,0 +1,5 @@ +issuer:Test CA +subject:Test SHA1 Delegated Responder +subjectKey:alternate +signature:sha1WithRSAEncryption +extension:extKeyUsage:OCSPSigning diff --git a/security/manager/ssl/tests/unit/tlsserver/delegatedSigner.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/delegatedSigner.pem.certspec new file mode 100644 index 00000000000..19971eeb4dd --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/delegatedSigner.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test CA +subject:Test Delegated Responder +subjectKey:alternate +extension:extKeyUsage:OCSPSigning diff --git a/security/manager/ssl/tests/unit/tlsserver/eeIssuedByNonCA.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/eeIssuedByNonCA.pem.certspec new file mode 100644 index 00000000000..63c36d34b36 --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/eeIssuedByNonCA.pem.certspec @@ -0,0 +1,4 @@ +issuer:Test End-entity +subject:EE Issued by non-CA +extension:subjectAlternativeName:localhost,*.example.com +extension:authorityInformationAccess:http://localhost:8888/ diff --git a/security/manager/ssl/tests/unit/tlsserver/eeIssuedByV1Cert.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/eeIssuedByV1Cert.pem.certspec new file mode 100644 index 00000000000..9ed9b33db7a --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/eeIssuedByV1Cert.pem.certspec @@ -0,0 +1,3 @@ +issuer:V1 Cert +subject:EE Issued by V1 Cert +extension:subjectAlternativeName:localhost,*.example.com diff --git a/security/manager/ssl/tests/unit/tlsserver/expired-ee.der b/security/manager/ssl/tests/unit/tlsserver/expired-ee.der deleted file mode 100644 index fd9e7e9e940b712ac7337700a8ce57a600e05625..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 531 zcmXqLV&XSwVysxe%*4pV#1tQ6z{|#|)#lOmotKf3o0Y*p$WXw5kBvE$g;|(AB(=Ci z!P(J3PMp`m(7@ctz`)GN(9|GGoYxSUYoKJPU?2z4Bko#JkXe+Pq5#(GnwO%Rnpcup zQfbgQAK4~GRtDzAUIv54PNv32hWnZPJytgyQ#s$bd#^dutkrYkvQNV9Eew6SyS>ie z7dMhyH6u)>?hx1Jg2{F~SEH^w%!N3uuW)^?^pCmsu#v}F zMJ6fcf<^SXAVZGN?kCspNjFJ({mcC9jGjgXpNp(-3KguLt+f8Tl*uG=>V|n-dFD@< zm>C%u7pE8`fqfw>$Rcha+9aF`@rhn)MPhD2PO4sVey)KL8;3Rtv@W@P-&!eqdJ90OHJ>!i)nk?&RJi)5*izz zmh<$NQ8Cq8<%r fCi%U#@I4;hdFA?_N$x8Fx*xc1 diff --git a/security/manager/ssl/tests/unit/tlsserver/expired-ee.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/expired-ee.pem.certspec new file mode 100644 index 00000000000..0a03bc36f4d --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/expired-ee.pem.certspec @@ -0,0 +1,5 @@ +issuer:Test CA +subject:Expired Test End-entity +validity:20130101-20140101 +extension:subjectAlternativeName:expired.example.com +extension:authorityInformationAccess:http://localhost:8888/ diff --git a/security/manager/ssl/tests/unit/tlsserver/expiredINT.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/expiredINT.pem.certspec new file mode 100644 index 00000000000..38a0abd8a48 --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/expiredINT.pem.certspec @@ -0,0 +1,5 @@ +issuer:Test CA +subject:Expired Test Intermediate +validity:20110101-20130101 +extension:basicConstraints:cA, +extension:keyUsage:cRLSign,keyCertSign diff --git a/security/manager/ssl/tests/unit/tlsserver/expiredissuer.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/expiredissuer.pem.certspec new file mode 100644 index 00000000000..855f454221d --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/expiredissuer.pem.certspec @@ -0,0 +1,4 @@ +issuer:Expired Test Intermediate +subject:Test End-entity with expired issuer +extension:subjectAlternativeName:expiredissuer.example.com +extension:authorityInformationAccess:http://localhost:8888/ diff --git a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh deleted file mode 100755 index 8f7ba11b62f..00000000000 --- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh +++ /dev/null @@ -1,354 +0,0 @@ -#!/bin/bash -# -# 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/. -# -# Usage: ./generate_certs.sh [--clobber] -# e.g. (from the root of mozilla-central) -# `./security/manager/ssl/tests/unit/tlsserver/generate_certs.sh \ -# obj-x86_64-unknown-linux-gnu/ \ -# security/manager/ssl/tests/unit/tlsserver/` -# -# The --clobber switch is optional. If specified, the existing database of -# keys and certificates is removed and repopulated. By default, existing -# databases are preserved and only keys and certificates that don't already -# exist in the database are added. -# NB: If --clobber is specified, the following files to be overwritten if they -# are in the output directory: -# cert9.db, key4.db, pkcs11.txt, test-ca.der, other-test-ca.der, default-ee.der -# (if --clobber is not specified, then only cert9.db and key4.db are modified) -# NB: If --clobber is specified, you must run genHPKPStaticPins.js after -# running this file, since its output (StaticHPKPins.h) depends on -# default-ee.der - -set -x -set -e - -if [ $# -lt 2 ]; then - echo "Usage: `basename ${0}` [--clobber]" - exit $E_BADARGS -fi - -OBJDIR=${1} -OUTPUT_DIR=${2} -CLOBBER=0 -if [ "${3}" == "--clobber" ]; then - CLOBBER=1 -fi -# Use the SQL DB so we can run tests on Android. -DB_ARGUMENT="sql:$OUTPUT_DIR" -RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh" -CERTUTIL="$OBJDIR/dist/bin/certutil" -# On BSD, mktemp requires either a template or a prefix. -MKTEMP="mktemp temp.XXXX" - -NOISE_FILE=`$MKTEMP` -# Make a good effort at putting something unique in the noise file. -date +%s%N > "$NOISE_FILE" -PASSWORD_FILE=`$MKTEMP` - -function cleanup { - rm -f "$NOISE_FILE" "$PASSWORD_FILE" -} - -if [ ! -f "$RUN_MOZILLA" ]; then - echo "Could not find run-mozilla.sh at \'$RUN_MOZILLA\' - I'll try without it" - RUN_MOZILLA="" -fi - -if [ ! -f "$CERTUTIL" ]; then - echo "Could not find certutil at \'$CERTUTIL\'" - exit $E_BADARGS -fi - -if [ ! -d "$OUTPUT_DIR" ]; then - echo "Could not find output directory at \'$OUTPUT_DIR\'" - exit $E_BADARGS -fi - -if [ -f "$OUTPUT_DIR/cert9.db" -o -f "$OUTPUT_DIR/key4.db" -o -f "$OUTPUT_DIR/pkcs11.txt" ]; then - if [ $CLOBBER -eq 1 ]; then - echo "Found pre-existing NSS DBs. Clobbering old certificates." - rm -f "$OUTPUT_DIR/cert9.db" "$OUTPUT_DIR/key4.db" "$OUTPUT_DIR/pkcs11.txt" - $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -N -f $PASSWORD_FILE - else - echo "Found pre-existing NSS DBs. Only generating newly added certificates." - echo "(re-run with --clobber to remove and regenerate old certificates)" - fi -else - echo "No pre-existing NSS DBs found. Creating new ones." - $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -N -f $PASSWORD_FILE -fi - -COMMON_ARGS="-v 360 -w -1 -2 -z $NOISE_FILE" - -function export_cert { - NICKNAME="${1}" - DERFILE="${2}" - - $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -L -n $NICKNAME -r > $OUTPUT_DIR/$DERFILE -} - -# Bash doesn't actually allow return values in a sane way, so just use a -# global variable. -function cert_already_exists { - NICKNAME="${1}" - ALREADY_EXISTS=1 - $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -L -n $NICKNAME || ALREADY_EXISTS=0 -} - -function make_CA { - CA_RESPONSES="y\n1\ny" - NICKNAME="${1}" - SUBJECT="${2}" - DERFILE="${3}" - - cert_already_exists $NICKNAME - if [ $ALREADY_EXISTS -eq 1 ]; then - echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" - return - fi - - echo -e "$CA_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ - -n $NICKNAME \ - -s "$SUBJECT" \ - -t "CT,," \ - -x $COMMON_ARGS - export_cert $NICKNAME $DERFILE -} - -SERIALNO=$RANDOM - -function make_INT { - INT_RESPONSES="y\n0\ny\n2\n7\nhttp://localhost:8888/\n\nn\nn\n" - NICKNAME="${1}" - SUBJECT="${2}" - CA="${3}" - EXTRA_ARGS="${4}" - - cert_already_exists $NICKNAME - if [ $ALREADY_EXISTS -eq 1 ]; then - echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" - return - fi - - echo -e "$INT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ - -n $NICKNAME \ - -s "$SUBJECT" \ - -c $CA \ - -t ",," \ - -m $SERIALNO \ - --extAIA \ - $COMMON_ARGS \ - $EXTRA_ARGS - SERIALNO=$(($SERIALNO + 1)) -} - -# This creates an X.509 version 1 certificate (note --certVersion 1 and a lack -# of extensions). -function make_V1 { - NICKNAME="${1}" - SUBJECT="${2}" - CA="${3}" - - cert_already_exists $NICKNAME - if [ $ALREADY_EXISTS -eq 1 ]; then - echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" - return - fi - - $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ - -n $NICKNAME \ - -s "$SUBJECT" \ - -c $CA \ - -t ",," \ - -m $SERIALNO \ - --certVersion 1 \ - -v 360 -w -1 -z $NOISE_FILE - - SERIALNO=$(($SERIALNO + 1)) -} - -function make_EE { - CERT_RESPONSES="n\n\ny\n2\n7\nhttp://localhost:8888/\n\nn\nn\n" - NICKNAME="${1}" - SUBJECT="${2}" - CA="${3}" - SUBJECT_ALT_NAME="${4}" - EXTRA_ARGS="${5} ${6}" - - [ -z "$SUBJECT_ALT_NAME" ] && SUBJECT_ALT_NAME_PART="" || SUBJECT_ALT_NAME_PART="-8 $SUBJECT_ALT_NAME" - - cert_already_exists $NICKNAME - if [ $ALREADY_EXISTS -eq 1 ]; then - echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" - return - fi - - echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ - -n $NICKNAME \ - -s "$SUBJECT" \ - $SUBJECT_ALT_NAME_PART \ - -c $CA \ - -t ",," \ - -m $SERIALNO \ - --extAIA \ - $COMMON_ARGS \ - $EXTRA_ARGS - SERIALNO=$(($SERIALNO + 1)) -} - -function make_EE_with_nsCertType { - NICKNAME="${1}" - SUBJECT="${2}" - CA="${3}" - SUBJECT_ALT_NAME="${4}" - NS_CERT_TYPE_CRITICAL="${5}" - EXTRA_ARGS="${6}" - # This adds the Netscape certificate type extension with the "sslServer" - # bit asserted. Its criticality depends on if "y" or "n" was passed as - # an argument to this function. - CERT_RESPONSES="n\n\ny\n1\n8\n$NS_CERT_TYPE_CRITICAL\n" - - cert_already_exists $NICKNAME - if [ $ALREADY_EXISTS -eq 1 ]; then - echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" - return - fi - - echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ - -n $NICKNAME \ - -s "$SUBJECT" \ - -8 $SUBJECT_ALT_NAME \ - -c $CA \ - -t ",," \ - -m $SERIALNO \ - -5 \ - $COMMON_ARGS \ - $EXTRA_ARGS - SERIALNO=$(($SERIALNO + 1)) -} - -function make_delegated { - CERT_RESPONSES="n\n\ny\n" - NICKNAME="${1}" - SUBJECT="${2}" - CA="${3}" - EXTRA_ARGS="${4}" - - cert_already_exists $NICKNAME - if [ $ALREADY_EXISTS -eq 1 ]; then - echo "cert \"$NICKNAME\" already exists - not regenerating it (use --clobber to force regeneration)" - return - fi - - echo -e "$CERT_RESPONSES" | $RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -S \ - -n $NICKNAME \ - -s "$SUBJECT" \ - -c $CA \ - -t ",," \ - -m $SERIALNO \ - $COMMON_ARGS \ - $EXTRA_ARGS - SERIALNO=$(($SERIALNO + 1)) -} - -make_CA testCA 'CN=Test CA' test-ca.der -make_CA otherCA 'CN=Other test CA' other-test-ca.der - -make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com" -# Make another EE cert using testCA for subject / pubkey revocation -make_EE sameIssuerEE 'CN=Another Test End-entity' testCA "localhost,*.example.com" -# Make an EE cert issued by otherCA -make_EE otherIssuerEE 'CN=Wrong CA Pin Test End-Entity' otherCA "*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com" - -export_cert localhostAndExampleCom default-ee.der -export_cert sameIssuerEE same-issuer-ee.der -export_cert otherIssuerEE other-issuer-ee.der - -# A cert that is like localhostAndExampleCom, but with a different serial number for -# testing the "OCSP response is from the right issuer, but it is for the wrong cert" -# case. -make_EE ocspOtherEndEntity 'CN=Other Cert' testCA "localhost,*.example.com" - -make_INT testINT 'CN=Test Intermediate' testCA -export_cert testINT test-int.der -make_EE ocspEEWithIntermediate 'CN=Test End-entity with Intermediate' testINT "localhost,*.example.com" -make_EE expired 'CN=Expired Test End-entity' testCA "expired.example.com" "-w -400" -export_cert expired expired-ee.der -make_EE notYetValid 'CN=Not Yet Valid Test End-entity' testCA "notyetvalid.example.com" "-w 400" -make_EE mismatch 'CN=Mismatch Test End-entity' testCA "doesntmatch.example.com,*.alsodoesntmatch.example.com" -make_EE mismatchCN 'CN=doesntmatch.example.com' testCA -make_EE ipAddressAsDNSNameInSAN 'CN=127.0.0.1' testCA "127.0.0.1" -make_EE noValidNames 'CN=End-entity with no valid names' testCA -make_EE selfsigned 'CN=Self-signed Test End-entity' testCA "selfsigned.example.com" "-x" -# If the certificate 'CN=Test Intermediate' isn't loaded into memory, -# this certificate will have an unknown issuer. -# deletedINT is never kept in the database, so it always gets regenerated. -# That's ok, because if unknownissuer was already in the database, it won't -# get regenerated. Either way, deletedINT will then be removed again. -make_INT deletedINT 'CN=Test Intermediate to delete' testCA -make_EE unknownissuer 'CN=Test End-entity from unknown issuer' deletedINT "unknownissuer.example.com,unknownissuer.include-subdomains.pinning.example.com,unknownissuer.test-mode.pinning.example.com" -export_cert unknownissuer unknown-issuer.der - -$RUN_MOZILLA $CERTUTIL -d $DB_ARGUMENT -D -n deletedINT - -# certutil doesn't expose a way to directly specify a notBefore time. -# Workaround this by just providing a large enough warp that the notBefore time -# falls before the UNIX Epoch. -make_EE beforeEpoch 'CN=Before UNIX Epoch Test End-entity' testCA "before-epoch.example.com" "-w -720 -v 960" -make_INT beforeEpochINT 'CN=Before UNIX Epoch Test Intermediate' testCA "-w -720 -v 960" -make_EE beforeEpochIssuer 'CN=Test End-entity with Before UNIX Epoch issuer' beforeEpochINT "before-epoch-issuer.example.com" - -make_INT expiredINT 'CN=Expired Test Intermediate' testCA "-w -400" -make_EE expiredissuer 'CN=Test End-entity with expired issuer' expiredINT "expiredissuer.example.com" -make_INT notYetValidINT 'CN=Not Yet Valid Test Intermediate' testCA "-w 400" -make_EE notYetValidIssuer 'CN=Test End-entity with not yet valid issuer' notYetValidINT "notyetvalidissuer.example.com" -NSS_ALLOW_WEAK_SIGNATURE_ALG=1 make_EE md5signature 'CN=Test End-entity with MD5 signature' testCA "md5signature.example.com" "-Z MD5" -make_EE untrustedissuer 'CN=Test End-entity with untrusted issuer' otherCA "untrustedissuer.example.com" - -make_EE mismatch-expired 'CN=Mismatch-Expired Test End-entity' testCA "doesntmatch.example.com" "-w -400" -make_EE mismatch-notYetValid 'CN=Mismatch-Not Yet Valid Test End-entity' testCA "doesntmatch.example.com" "-w 400" -make_EE mismatch-untrusted 'CN=Mismatch-Untrusted Test End-entity' otherCA "doesntmatch.example.com" -make_EE untrusted-expired 'CN=Untrusted-Expired Test End-entity' otherCA "untrusted-expired.example.com" "-w -400" -make_EE mismatch-untrusted-expired 'CN=Mismatch-Untrusted-Expired Test End-entity' otherCA "doesntmatch.example.com" "-w -400" -NSS_ALLOW_WEAK_SIGNATURE_ALG=1 make_EE md5signature-expired 'CN=Test MD5Signature-Expired End-entity' testCA "md5signature-expired.example.com" "-Z MD5" "-w -400" - -make_EE inadequatekeyusage 'CN=Inadequate Key Usage Test End-entity' testCA "inadequatekeyusage.example.com" "--keyUsage crlSigning" -export_cert inadequatekeyusage inadequatekeyusage-ee.der -make_EE selfsigned-inadequateEKU 'CN=Self-signed Inadequate EKU Test End-entity' unused "selfsigned-inadequateEKU.example.com" "--keyUsage keyEncipherment,dataEncipherment --extKeyUsage serverAuth" "-x" - -make_delegated delegatedSigner 'CN=Test Delegated Responder' testCA "--extKeyUsage ocspResponder" -make_delegated delegatedSHA1Signer 'CN=Test SHA1 Delegated Responder' testCA "--extKeyUsage ocspResponder -Z SHA1" -make_delegated invalidDelegatedSignerNoExtKeyUsage 'CN=Test Invalid Delegated Responder No extKeyUsage' testCA -make_delegated invalidDelegatedSignerFromIntermediate 'CN=Test Invalid Delegated Responder From Intermediate' testINT "--extKeyUsage ocspResponder" -make_delegated invalidDelegatedSignerKeyUsageCrlSigning 'CN=Test Invalid Delegated Responder keyUsage crlSigning' testCA "--keyUsage crlSigning" -make_delegated invalidDelegatedSignerWrongExtKeyUsage 'CN=Test Invalid Delegated Responder Wrong extKeyUsage' testCA "--extKeyUsage codeSigning" - -make_INT self-signed-EE-with-cA-true 'CN=Test Self-signed End-entity with CA true' unused "-x -8 self-signed-end-entity-with-cA-true.example.com" -make_INT ca-used-as-end-entity 'CN=Test Intermediate used as End-Entity' testCA "-8 ca-used-as-end-entity.example.com" - -make_delegated rsa-1008-keysizeDelegatedSigner 'CN=RSA 1008 Key Size Test Delegated Responder' testCA "--extKeyUsage ocspResponder -g 1008" -make_EE inadequateKeySizeEE 'CN=Inadequate Key Size End-Entity' testINT "inadequate-key-size-ee.example.com" "-g 1008" - -make_EE_with_nsCertType nsCertTypeCritical 'CN=nsCertType Critical' testCA "localhost,*.example.com" "y" -make_EE_with_nsCertType nsCertTypeNotCritical 'CN=nsCertType Not Critical' testCA "localhost,*.example.com" "n" -make_EE_with_nsCertType nsCertTypeCriticalWithExtKeyUsage 'CN=nsCertType Critical With extKeyUsage' testCA "localhost,*.example.com" "y" "--extKeyUsage serverAuth" - -# Make an X.509 version 1 certificate that will issue another certificate. -# By default, this causes an error in verification that we allow overrides for. -# However, if the v1 certificate is a trust anchor, then verification succeeds. -make_V1 v1Cert 'CN=V1 Cert' testCA -export_cert v1Cert v1Cert.der -make_EE eeIssuedByV1Cert 'CN=EE Issued by V1 Cert' v1Cert "localhost,*.example.com" - -make_EE eeIssuedByNonCA 'CN=EE Issued by non-CA' localhostAndExampleCom "localhost,*.example.com" - -# Make a valid EE using testINT to test OneCRL revocation of testINT -make_EE eeIssuedByIntermediate 'CN=EE issued by intermediate' testINT "localhost" -export_cert eeIssuedByIntermediate test-int-ee.der - -make_EE badSubjectAltNames 'CN=EE with bad subjectAltNames' testCA "*.*.example.com" - -cleanup diff --git a/security/manager/ssl/tests/unit/tlsserver/inadequateKeySizeEE.pem.certspec b/security/manager/ssl/tests/unit/tlsserver/inadequateKeySizeEE.pem.certspec new file mode 100644 index 00000000000..02b595dc9aa --- /dev/null +++ b/security/manager/ssl/tests/unit/tlsserver/inadequateKeySizeEE.pem.certspec @@ -0,0 +1,5 @@ +issuer:Test Intermediate +subject:Inadequate Key Size End-Entity +subjectKey:rsa1016 +extension:subjectAlternativeName:inadequate-key-size-ee.example.com +extension:authorityInformationAccess:http://localhost:8888/ diff --git a/security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der b/security/manager/ssl/tests/unit/tlsserver/inadequatekeyusage-ee.der deleted file mode 100644 index 4daa9a5eaa1e2575ed47c5dd86a4754a3ab01214..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 568 zcmXqLVlpvkVw}5xnTe5!i77tIfR~L^tIebBJ1-+6H!FjIkfDG99~*Nh3$rkLNNRD3 zg0rK6oH(zciJ^g!fq|Kkp{ZGvIIjsZ*FfJ;*FYPhN5wNQF(tLIG_fRA!8^55A+$I# zJyij0kZWFwZfaghW=W+%<9uXm8Ce;a8+#cH8atU98yPnJIlSrV#Ik8MwcGe=Yc@m~ zxlia|iF?=KHs{1#rK>gX-Bq;k%^g+fpKw-L6w0f&`Yv{EJ_9nO>&uVZ)K-emV!K`ms*jSTac5g zmzFej@Cnx4)PK;iT%+>yxxP7oXGEv$WFA?Z_`19bY+L zC9R+(0w30YoMDon++}R(7#uF8^!(cPTgd0Jekh_7`G0AOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY z0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0wD1JTY+>XC2c)DdYNTz zJU=~YOj2&VM|^JlKdZ8Ph^K3qr+%2Lo4=?2KfkJPo|>p16f$I}Ye>tbg=S;;-QBhmlfF33M4GE*;Qxh|Ca)Po`$HnI+DT#&~65!!ELSGoE z5yZbi-#ng4uq<2@gnJAZ?vWnPPf8d67R7dZ2;+b4VQb+fuc+PEfFAp2>^y#2QbMkH z4~2GnZzJDsn?LoxcHuECrf}&>+6D&nnRddP zOuT{kj3L_U&&?+Oby;pWSGT~BFf09d0{w?>w|Ex%&z3qD{sQ$YOzM|-)K7WTPkF+h zQon`ArVbqHz@-j$)PYAG?5RUMb>O3ey>Khy&r!G|LG8gL{`@)AE=+>jhDlKSFbQfS zCPD4QB&e;J1hp5Fpf+O?3Di+E-kPjmd@EbwTXBVzE38~$f^|mbGd&&|{ z%O=nd7L)pA(J8gBt?+l-3V*k)@OPsq{4IZeBlUaOLMb*u{n>1x6k9087XGPh;h)MD z-Yy*B+j4}vafG{Zgm0UW#Ia@Da;Pmur_{cpQ|cWPol<{-=#+XFMW@vJDLSP9MW>YF zpHm@<=v4e>LLSktir3;&%WqXVqfY$ zL;b<%SE)OW=#;wmh)$`ykmxi~e3~RaP5#fR@Q(23_wvQ#@TqYUIq^gyUntI_Zd*dg zPW@iCE!W<{mZ7NaZSZdoe&WBSu}tx~9J=txE1&~|{6bxWx|2WuoKQc3A0Pk%AOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY z0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&@c)E>DjU6<-THJvM~~7Kw9<{GD+G~(;L@k1 zcS^66Hk2MOJy^Q4^lRx`!B)Xq@*DY%Y$5NGSIKi^9eIe{MQ$Qjk_*U6aw1toW|1jm zEE!G?B|XSNB#X2l^+|0~jZ_f)5_}T86g(6(3oZ&y362Q%3XBDP1)72-g4u#80)Ze; zkS<6Ns0e5RAAz%gE3p25!XDm95C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH z0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI z5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5CDPyxqz~q zJY8-m_qK=d#QKw=J9X0S@^Vu*;mI)-hO6*oPfEHAb)s_2HO*OgvdiAkNqDmM^}xZx zlLZYij?{_97MHw1!jlCj);Lfn%r(#T?S&_TV~cpyiJ_`yZ#(M5pz*{FE_Kq!e8T|_ zb>iSU!<;QVu}`kB6`nAErLu%4#?`l&)QPeI!-}9zlzuq=9!Q-SW&N0IBRol5rE4ub zi5|+c5}x=T%Cr=oIQy+1K%E%QCBrO)C)vG+nNug4QCDV}2~TWZ#hFqkIw#%@G!dR0 zY>GA(o>aFc8VOIf?HO(;JX!Z!(Li{z;%kJy@MKZVg#N;l*_U4Q6P`>vc}`DwQob-m zS9l`GjOZ&o$^SdHkMJbRF{iiiBsKDhj_@Rb)~YQ$iQIKiOL!9WvZ$Bv#CP57p28FN zht)lVCyupqG=(Re@Aox?C)U9))P*OeiI=+zPx{9kRui7+#Gh9co^;=OtefyeWmk4r z;fb8nmM+wZ_OJ3H72!#1#xZ5#$y?@lCE-cSoOz1ElLvgZg7Bo7rz9^txs>uwPIz+W zN)LvToV<$m@ryH12Nrc;QU`)M45SV=)WMoMSWyQ{>M(#hSWpLZ>R?74 zOsRtjbugw5M%2NOIv7v~ed^GkI`pFsdelLeI`pLueW*ik>Yzg%w5fv@b?8MMdQyiT z)IpOvXix`r>d>7!s8I)1>d=ikbfpemsDlc1P^Jz_)IpItC{PD^>L5oQ7;KRmXU>I7MV)MlM!SvIfQg2?MWtS zK^l;~Nexnkqzk?aJ_ue29tv&>ngnMB#|4K3y9HYWYXnOKa|9KFGC`prOOPsv7eokx z1w#a`0(${dU?DIN^cH9cR0QfpeD9LHkH_#$IM60`KbrY?wqSZyTI*V2((K=YPI*QgoqSZmP+KX16Xtfiq zT+zxAt!&Y1D_U8il_^>Y(K=AH+K5(b(P|}HEk)}9(P|-D%|)x3Xf+kBCZg3?v>J(4 zL(ys=TJ=S1f6>}cwCaggUD4WCwDu9Ly+x~zXw??2TB5a=XzeLldx%y|(W)U@)kSM} z(W)j|RYhwz(b`qCb`h;AqE%V6Dv4G_(W)R?T95{0Z&HI) zA?bqef)9chf`@{ef+oRP!EwPM!EV78!5YC5!5l$_piEFG$P%Oq;sp_cV8IZ9tH55s z6j%rh1ib|s0u=$h^n2-t(idnD_yGbS00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY z0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00RG8 z0;2!lyN76X7p-oh)m5~*h*oFO>LgkRi&jU`I!LrSh*o>i$`h@2qLnLJIii&IFeRmQo<_yR5+_}v%pl~ zT=JuYR`eJ7yws{_ZK)@zRGMEJQ=CTXmy`;wmFyI}ElDiiN3JO8EtpnvrnHA3tF*2- zxVV4eI5MPIk+c_#DDe}lD_&kyQIuKCD|uM-rl=cvQJ`8pkt{E~RXDP=wfI@l@Zt+4 z3k#1IIhAfHybAqndn9ZEeD#){7EKbrrclo`by%Rd-{$JHm+1?*1Q!lD%N!L zr5ClY)%ITdLaa#b??Q!!ov-g4&QxLaxVl09SkP_PD+Y;IoT;GE|HmS>f$x!mqjd{Z zoR)@O*}Qw66BTMERBQc++Z^#|{F7e6%6k`Xsr5WQm~kJOh0Dny9}AeHXBoFYT5I1T8**m#+L}(t;5cqS!hp%ZNoycPVOg*r>hn2GpN62-B#J{B##R0r@PEI z2X}WqnSHdW%cP-Cav4h(+EKyCD(cI@s&*Tn`dc|Ghb$~^M1EB#mZLUn-p>I=C4+d4=zI%7L zJCh1?GtTFpdA4JJkCO5MUVp!^x^Xfti=cwPdy!j4KZX6%EGK`g(~r5Wy~JqGKq}O3 zRzH6Kx#RH@TC?ir6(~>KQQl%ThKa>tPXX$5iXR_sp@TLfkUf%$NS} z9z0uHCO>dT)y1gz@@rO9xVF@E{Htp#4}DhlO7c6pV1D9uMN>;ETvnLf{r+h=gIs2d zW8>~RgWLfEcUw>)KdQ^P%1O!%vv!R5#t0fW z;i<>-+vZeI-nQ~$XvD*Qc@q`ZA8qoS68On>vKbZD{G6anMl|^(7=@kc`Ip9(P~#Q7 zOsNpZv3R|E-sCIyZY7NNXn3JeCa0TVLWNcrC6|(#1m%YsktPH4dNU__4**_`|hmERfYYI%Cbt0f}i#`<$lJ9Ag3(KCqaa27L z@Z>1t+p`%{4oE)ILnt4@iW?=)sy;mTpXg16J0}ju zHJXLa|Muv8(b&&Xid~Ls4A-H;?0s`f2K7}m@juk9c9;K91-t1{PTIn^c->lXf92)# z5s7-aJM71rUp-u!p+$wpf}=xKF1xHe;vAXTbI^SAwOy6%dQqX)&-EK`R@9{fLEO5L zt=3Vob1cnzQbEVrz;fwUs|$+n>KFfg?k|>Yp8$&QD8;WqTD=o7HUwTJgEPf4I=~lc~8-wBY!V@i~f9ccprFR#D4ze#xr;lH^H6@p)lwyAs7->y7=|AVa~ zpEXZD1DzjSrY`yYR<3>{*Z|zSO`$sBMVVUM%gU*f0Q%x-46z`sU`_6UM z3)7US@Vu&SotbT@Uwrj<+JwmRnu(6b2PjgZ?!cSCT%{m|qQZuenW=NP4sE*gMS%+O zdd*J7jg}j19{T#+9Q@eV$Ge&>PlcaV-1zzDvX=-BwY8o&wYu;xwaH#`R5S_cXg|#x>J24L9YQ zUN>E6`o(m=={S}tOOfftG-aM*PG+_+H!_onGsNbB&j+3%QV4TmD&a#Y4;2iJ@n7jL z7~0SO-cTq1Hvgmk`u;RO7e9T!TEFpr5Byg9#gR+MB0;TS4e2OoBcn(?!F_?3z^rsk zX+Y_V(yapJ(uUH?l20X9OZpW5ELm7GqGVr5eu;hY>f*AZ(?y$$-HR29`NhUXEk#3% zYzlJ=hZVjrtS;(a*i<;L;77sj0)qlZ!HR;|f|>$BflL0T{HfzEjN6&-o8K)zHQ#dF zn{i>|IC;f+(Rp9<4vo{wYtCDmN6&qfYo4o;yFNEL_eAcbT<@G6IkU2_W$(`k&gqep znZwF%&5p`;%$ksukkyt|o2{31KWj~va^{Q70hwJgt1{Cv8#1S5`i|W;wld><#NsLa^O)N=t zO!$=$lixiuH&!j!BI1hM6RjL|CTc)bmq_2p0g(-n(;{C+Rz;>ooR8Qx;`NC05$O?D5tR}C5#2^)j0hM$ zb9lxGwc!^=*bIL=e8+J0@b}?FxO(`m@XYW_;j_bohE)%n8+JLYdRWLX&0*QYSYaQ- z!ot|0xuId9t)Y9vdWK#NogeZgkqWL&k-0gBJ%E1RV@o9Lx*)7Ca(Y zC#X5dA*fGaWS~Rft-z&$KLQU276sf1s2Tce=$(L)0LOsk0nq`vykERKJYBnAyyd)T zUJb8==V-UmPQX3NU1{gUZL^E9>&Lyvb>ZrB;y5mx2b|Sh8mE>s-nY(o?T|-9>U{aW zhQ1Sg-Fz8C5{G#Bl=~zOk@u+|V(jz8XT6W2cZ;{Fx1#q(?_}>&-jlt(yf%AH^*rOb z*~`aE*(=4%-1E7oujc@dG!I{ommXD~T|62*rn$d%Kkwen?X`QQyTAK3_jGqFx0!Ak zt~*?3y4kqCbqjD)bG^WR#I9pAY#*^Fu-({e*?hL4ZMkhCYdx#n)|mCg*27kwRqvGR z6gGJN;9RGkgReTV2e%I1J6Ox{izC-j%W=QsILGUb3mwA;9UQdSq1oZ!pb>*~1{DnA zIec@7ba1dQvX8X?VSm`6kNqwCrKZ{@-;56#FLfR38swtk{BhuxfzxdKY^tth?F zuCRChll)WT=8Vh9bIQ}td6ma?7+u+&~D zUq&4mwKzE<*&*>~;+=#Q2?D+|Uq9|)TwUzM*u)r<7{%z*(VL@EqkJQ~M!X)eeZj=GN%8gJZy9*q*Rm@3hBh{@`JQxr4qAYIa!SQ1ribA%T5C00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1pWy0mvd$0q>hSBNXpKQ zC2ZM=w%o)d^fQhv*Op_?c!TXc#%o;Xa~Q91ox^9m#C1NK@dDR5@r>uV&LtSnI_n9s z1oeZ-Vli50ZNiq7MGWOWb!)Vmra8Z_g*>k+|=kz-59_ z*Kv1SE{~fmzV9B#b{?Y^*OBfqTt~V`aUJO%!F8ltgX>85ug-d+_|A72+X=>@4tx}k z#KZPMTqYO?+V5`9Pi8Z@iDKRT*v@0@!*!&)7uS*Q9$ZJd)wqsycjG$J-PKu_xGe3& zc7m~^9p4|Gc=3(19hV8lwvM|Kw)T=&wXN9BV^rZf(%pjVNOv=?Bi&87j&wKTI?~# z9qBH^b)>trvrdTbd`qyMU@UIWNAa+d#c#d{mkGwg4!hfP65_c`vF-wF=P~BvI?|no z>qvJlt|Q$!xQ=vZ<2urv)k)__@XW+^f-$2#AH~B-66;ptGQpVMZg(a#-kwd^@x;2* zu${-Kz;&cM71xpO6kJETlW`sCPQrDhTi#ih_>gKMwiApAl6)c_j##%0mkGxB4!ffp z$2OiV)+Mo>#}ME;(k;bxq+5dPNVgc*k!}&LBi+Kzy2Jx#0k#v2{Pui89;R4#94-@# zymq_WGUJ&^JeI9kHy7J^j2v7?y4kpnbhB_B>1N_O(jANINH?RiF2OSf+X+UxB%g>! z;$eFqs{l*O6`#t|Q$BFQJ>k;)@>{24g#q z;fU)k^N%scMR zOin;g0{!Q;2{UZxF-&nC>6+j=(ly3)q-%uhNY@b8k*+~!T|!qM+X+Vh4tx}kgl<1v zCK!4hc4x8qtVD_D$aJxt$LNdeNVgBJBi-J(j&yZ!9qDT0I?~nZtV=vc)(hJSM$h(q zLLP}5rw1+*49#}C6Fe4^V4vrAjGqz%8t|Q$?xQ=um;yTiOfa^&2erH{L(Wl?T zc7lGlJ)e+A{P8aR4lWb)+Z}di$Mfy&#Ltn@Z(%!+eiPS`ZZobU-5a=$bg$z&(!GZ3 zNcU=IUHlvw{R*}d^vmt}ggoNs$mmVDOwccN*xfdn&5yVH&sB|n5!-q63%HJS8*v@! zp2u~hdk)u;ZUe3(-Lsu_@zdY*GuTehPq*h2^05E&EDrq?E)(>V?RRI{v+R;N61w%+ z&ZD2eb);K|>qz%Ft|Q%ATt~Xca2@F$?W{}a9>I2kUek{6508ZIU${)r4|mwzj>WX+ zh+p}kAHsGX{UEL*-2=Febob*r(%pyaNOv!;Bi%inb@3}d^lEG;=)2qV33()LoL#s~ z(08`moyp?x6VYqR;zx!Z*v_MG$91H;4cC$GR$NEARk)6Hx8OR`-P~E1c;MWG?F4#&_iUyJKVcMYy1-PO2`bXVaz(p`z`NOwhN zUE->?9NP){vi5vJ9*G-gDJ~QACGB_TCDk-D$XvbSrQj=}yIUq&uavF7esKWNatsliKqMc_coYD92@jKC$EOtYkJ<;$wyh z*v_Mu;X2YCkLyU6#C4=Az;&csit9+Xq_Zw@St`bMf?m{tkK&Q|n4u7t33@??-QyGZ zwjA**KlFTT=h4UEI?~Oqs{T*O6{Et|Q&7&bq`WTbbBS(8spt6Y_{(`Jrdv zGC?2Had+F~L_+-4(e!j|=g~*wI?_$Ub)=h$>qs{R*OBfhTt~XeoplMGBy1<>i5>VT z9*Ga=5^$NI^E>QrpO|dN5x+J;kH>Z%Jr38AZY-`N-56X)x+8HN=|8j&8((R7xNLQ`1F7e5hDz+2!ZXNh2 z9*GB*uDDFlyL8x{YtM;KlK4zm1>1RaWn4$PO1O@66>%NuD&RWOmB)3YE7w_<_)M6A z?F60Po=?alapTZ%nV|jMad%sSNEYkT+OVBR`;F^-4(%7NbNIBMxXx$Oe&9MMp7tHr zxdiQ7XI-pI`-<%Z?Mnwfibt$V`;5y3t+m7MoOnLL7r!P<`-JU0+DBYRx*u>I>AuHx zr27unk?vbuN4jr1>*C82?KQR&v{&uDQGWonV`MsxI2*)ZzujbGTL)&=h2?w zI?`>yb)@?g*OBfMTt~W(aUJPC>a2@DV5L38c7pbx10TgB{*Z%qAD0Q*y$-vxs0>E6V3q}z<^NcTo(UHsYv?K-v-v}^78ggoL8IcQgL znV?D*#zw@E)%pf9d@@(NFeOR->*hHjqNwj=JlbElj&u*>I?_Fa>qz$?t|Q$8 zxQ=x9ch)8TYVX5#g0{Brn*OBh(&bq|o?ka32Xe&GLQ9Ke4EGuxCpe^sPJ2RP+#FzMp zY#FxmXiIS&=`O)_q`Mf`k?tZ~N4g7f9qBIUtV?`EHXqvw+PwCBLLP~`{#;xpXmdL5 z&f_u@#ILy1W@9^#HVfC0?o3=qx-)Pc=~m)8(w&a$NOxLiUHpnWtpeK#+SCqw6p#4n zVcHa2CTNp8>`o-d^AjYlYLl>?M=Qs5q&pGUk?sUsN4jOWj&#TOlXIp2=igIf^4iVM zCCJXQ_@XAjW};M2&RMuQ^^cML*_$uEq?O<*QQUEBdp=<&e#k|ya zIR&`P;iGTN$GkNgePjN=XHI+`E^~?8&a(KiHV2!D><(zuyDfgC&B9e8v*Xsbwn_2g z*U`|+_wC=UcA1A>zW?{k=ZwK+4j+9tKIX02=)3X%J#*sIaG6V_c9zADkSW+qjOu_! zz1!js4U%z{NNT^eU2+1CE%9Umdd*()-Rh8e=r#L)&wLIampOd&E%=zXX2;<&`mz;V z<`OZTW$`s@BsLS#?a=|wafi4VhNE)m*U7C)ATU^5Zi0S&#|5|5=pxJm?e*g7f6mdO)8DT7{} zZ}(oe&ph<%{J&>D#~+tDeDuxsn73y8;xZ?G2rhF8pU$%QQ&MkiCcN6C3Eyq;?cs^5 zgh$7%x%NzZ@vArJCHeO6b-T<%FUkLV=5t(enZtL%Wj@;(mpSoHxXdL6cb3J^T{~hk zF{lF?^=^x=Z4S6f*mvBT;G%C<`_Er|^g4Wdvv!$>UWfnp%;#`%nZxJcGM~-HWlp>; zE^`T1XIcDe0u!4Fq5~TBZi{b^fw)T8bl5tPXUikRU$KZ@d~avgKJ(Cv@Bg0p97|m0 z@CV>BpKXE5oOp9w<`QO|W$|kOrr1oFv_})Z+v2apGsacIsN>cImzONQJfT?y6%m(Y-yI@ZA$;!)Y%CsIkHRExH)0idct?7ZIt41rN z#iiDy>ZIh1x-iNjxjgB9l0)Kxg!c)4{7rnh_?WmuaXn+l#x%s3j2s_*JDM9cH}Y-d zkcjmo{vHuId>{Hhs*fIaYM4P-Y3R*RcF3&Ym%*Mvs{?-qh6n5p=r%OPzuv#UUy<)M zUt-8~pB5ii@8w=!y+S>AqW=SVl6$RtU$=3tO|DihQ=A_;J2@>K{CRMY<5ow-LGcbX z4%+tFyhfgd-6ZY-CW5m-t;fiUM6FW&l(#Ul^Ql1+8WHzf2Qx+e_6lJ{Q~v2=*j7h?0c|pk3M61 zpYCm-Q>=YWd!SZDugARx_gviLQ;(sVn>6VfQR@5DyLV4jJE5khTF~uEH|wrbx;*SM zNM(WYJLMrt>lA-0j!@X6pejE~?zmiEMjpB${=fYwGc5B^aGjt@lXxbi{i{EGqHH`7 zt*A0!vd`qNs&oZ4B9L$u|Fj#yme<$*l~AFzZmJ&F#bnsu{)@_o$E1&@2t@9wMrXfw~IMQy0Yf>R@mtxa{4 zqWjySDc!MC^4T&>Icko}-)7I-)^uHZZ{qnykKb?JId2YM*SBA)OYf*q?}sO)P3fal zexY#gZ0lJ)>$jT7^?kUw%j0U^Wbf(4-Wt;eguiG-Q@UZN93PE<)KUgCt-Grf0vR^;)Js zMsH5>h#7*Wbj41|C1jYgyPJZgUnAY})9a5fa@<3T!+aP98jdv+*i-rkJMA8OTiTS{ z#y(AQ_44S^W_8Qu*uGDJOz+HV$I{j&eX*YRyw9=ko6wXlolYr!#FJr4jcX4UJX*KV zsLEjdgs*1x<5D!6`+3~#{`leF4P9%iKiEo}(plc^M@c~THJ#dLmJ8)~5BT=t*4Dh= zPRaylxm(}^8)Y=5Gk!{0EXTY5DvS7hYqpl}!s#=ry5?ofb?WVSb(PUPc1B8-|D$qg zQ^v>{KO9i{^WdZrH?Hm;UeP3X^UTkaE?0Zm*UjcnOBjBp%%IFk!CCZo{Ld4T9bUnd zfwHZ@>@&H_Z`blufqCXP{WpzxRsK2i`Mm7lGqWD(t{>?lZAhDO=|#Z}!zM&7)Uo;a zvGCIK?FH_M)!K8W{7{_xQ#bq(8gejpNLeIR*%k}Qp!&vb$hpe zHCG18V4JG15f&v;8_qay?3Z!CAxdrhd5(6)6GZvrcK>)cau}+;3AR zcobh7m8WvH=3Uz*_3rCDdoC+}cMwfE2tOrHhAAHzHKlIy_vyDT$1Jp!Gvf4@M{`WH zdX=d!Tr#P))Mn5bX;TiZ_Vh}YlY8Kn>a|t5Wwobyipp+{mx|^Y3vQ=`^>P@4rgXqg z$>+#0rS*x1oqJZioE`M0Yv%j$TgE6Y(N>cmHC|y+*$V%t^u2P@rYu}#Qh)u(s~CSZIz9Lp>ylBd-omY zzl!d*HD3G1mWkx!gz3_zEMHKtv~c+rz0y9J^O9;`Xr2D>V9edzvWW3Ful~Lok=74Q z$-_=5)AjMyZS}41%75;+N|o=!{^a+%UNe95j*!|5ws%%)d@Y`$FKtS{%0oMAno^e; zo$;U4c=_NsZQgCC6+3Sj_bfg*bC*gGfu^*>PASv%@w;V)Wj~$oQ}RPzJ#O+V3CwwH z_T}iP;K!e=r_WGLHkc%B%3iblFFxtt)u%CFRjy`5o8rm&s>F}!>I=K+2F0Gz3NJxZ za}g?7{JOb-yiRZMJ<{)u5hhIpF&~^J}^fHhgd0Cv8f(SG$a(>iu5^ zPJgCacg^>0$>Pkih(W_9jKA;s{zCt_GiXXqr&EeQZ8vS)FHv?;%Dzom11 zdDoS%T_*Cb+BmRx&i9;acHl|%(9mL4x0MQPbm_6}cD*pvLpE>Uo%sFoE+8VTodVX?62`>CT)D$*J@^?&Va(*myhV4K({xuv3eD^j_2066Cvs zb9tSc-;x13vvzwvSmCOYx}jj^(uGG=9%f6MvbK0vp3@eZ<>$Kd)0-P!9OQn?&C1ZT zZ;1$M{rIi0A`(qW;HQ-3`nZ4Hv^zPp^)-twk1WWzez;GM8~ZKSc0F?H;ZD<=v-gK( zNt<#_EZJ>S-AP{kWW~K7*Ns$i9KX`YsOQeDk0$;7;Q5Tt!Dz~X*ePYYK6Xtuou7Z~ z{F{U=shjTj?jXHiw<^WAt}U2lb!dUt!QKpMQ+8DzKP5MJuJ0aKqe zu%f-%+4t5VH@65hr44pUnXZq0a}RtetjoEz$1LvWK$Tl>hO|B%In7#?__}+hcVD#& z&C;g4T(G{O-;rl&r>2+odH1U7qyFjGoSNaq0sBY%c32Ydmb%5Qu~W))eO$45bJ(&8 zn)6$Kw{7xqJ9mIoIsY+c@`vk!OD-ew9!{SpZOTVa$8!yvM;p5@O4P|7Gw>?0>fY&` zi6M46k%EBrdt&9$lvda&Wx76A+TPG>D!Z2Wv?3>eCaYK1@_;E(v3vH-x;3tMb%f!8 zyV9mSMfa~9p1aNaHJZ{AJEct5$L5V|OPFQ- zU27gxnsm)u(znv1cu@acht}K=TTxZsH&N+JeN7b3iegge}y6P`Rj%2@lu zuCoIxSm_r1J*J{52Xs26_{CTm9*I1@>Z%@}c8)NgJbYI7Dd7`0?%GuEoHM+5R7-=t z|2_L<(kzF636OKT+KW45SmTMmu3K*!vpUYA&$gepdN03{78BJ-ecWb&pHh}bbw;n| zrpGkfswp3D`|P_{Ccmk1QkUjk2Bn3+S5hO_I6#4x8O#~)6E|ZhVQ(ff`&B14k?SI z(R6l~!%uFEv&iqd)wK86&C9nR@K4yaeRhCCi(G$*9p28wtUiJ#m$VgihHqSyZje(G^GiC zN?ERtrL*?;zR}QSbm8Q>!ya~i4}*r5c>kPs&a1p`s`4Dm>v7ViTsBL^%`fNWNBY+h z)Av6fyRpIdX~RXQz{x|;O=<3yw_p{T(il6XOxMS=`|J!Z{hYEh&RuOegZ{DDByjHc z_XGPazk4L~#aFU4RN9pLwXg45)1tUAdc(!moyt#cE<608Id09~s_X|vp@&)zzC}|S zVW*Vo`WUJh{$N7Zbd@8==hU#2+;r-~pXK@TH!H;jK0O{c?|P%ODO1L6h-qBC#HHk} zm2ux@@)51UzsF0xU*_9CJ-vCb?RqVm(hxhPOxH)V@BJMW==B;cDjyWCB>&8N{qeNf z4t?Jq(Zz2$Ki_XIkv3&r#HE%iwOwmodK3AK4=={FJ+hi{tzqJ?dgsKuT9)%&(Ub<* zDP_7o^0LM}q909`A8c1QH&u-ga{;lkUT47?*5~){lv!z>7N8HtF z7<1tB6CxyUWs3gO8{H2L+0$JA$$ksx2z7nzho4fGNA>La2dmGRd1g$@)VNZ7y@pUI zzp-YA>F&MR<2G}5r|tVH&2n5${^&PGCz|$Zram{AeRUxxyXTh2Lx;W{(ZA|!X|&c| zbm`IScK2kK8=ma{$M+L`m8I50 z;KPAaJ~igfHRDzFRo;0P-+s1bSf5iF$9=tG9ST4C>_=1f!A~j6_3>!tilKKt2YkvD zY+&sjc-5lIcl*eu{C)GjPu}%xh4$v-(r)mn$I{bMS*uEB6zwg~-g02c^V`b~#q_$^ zSB;se?lgf(eKye>JEct5$HA<+WtFXGwdGmUQWvF#-0jVsy86bd0*&<8_=@TZRW)f- zy2Qy}cT=6SF!Jt=_0J|5l$~jPReGs8y4C2*@%vBO4mP1Fb+A*)bbTy8Rr^WZfZL~R zkjLMS9~$Vl>V}-LAWah*4n0vfsd&hiHs$U8-xpjR6Tn_xA9~2VV!v{ePaAVuZh5Zk-H3Q)*+UlGhWBJ9~Hy78LFIEh&?Cv-zSnsK+gVoXlQa=|z z$?vOjQcEY$^FdbDD-BbV9eFq8vX1m>iHPbt_|Wy>8E8r^?36NHAC->$(oMg2)u!QC zQPYnf6&f!pw;P<+ZFs`j$l9B%_N!Q$Wz_=LQ!nGHrfRQ#I<02%E7r3b^BorhMo(+1 z%>H@7eEMlLWiRZMGF=~6X}PDBH9gzl;W_#*mD$Trb}^c`_tmH;ehCrN$wm6JpGupu zd#cZr(gzk1Bfrl#;BS68>`C+4S+PiWimLW^$yG&o$us5Sik7kEUPlxa1k%4kT z>lxP)^_ruthk7mPUfFZ1n(E7#=Z2q`O>}&&SRJ)ym$V^G<=21fp*;Mkd!zHOxfK^l z)vPnIJ(sIRjOe{1N~!9B92!ysJESa3ige;3&HJd^O_ZxwE(s{QX7$8=5}r&-m+{FJg>9}P2CnbKn`W^CU7|RPm*H{t69{C zaXI06Umd+(K6l5t@0{3H***B$QZ%J1c1oG9kCn;C`6*XZ8fG3l_+cjBWWh4)&*Kix zs2Fqon91?4!OMK5P1!5XS#C-U{gBqHtyhPd&N#7Xt6Tb!{(5g2y$?QO9jT`e zQl{(U2$LV{%Abbk46pjwi{ICGLPPl7+ecGuSswI0(cjlwE|xZB*y9t1eM7$#glu*? z>uB#VJ1;@CIv{42^8lNp1CkhLve1-Wu~W))eJl+Qj#tk-xN z%5;59)^*-hI(fz@$EUS@zb~5BqWO-dqj-4F^E{IWEAD>YmM3k>H^aG6&ql?v9d}j* z3}+8tYsLHS<&;ph^RnC^dYRV5n`lavPNx)q|Dgo)&kt;cDu{+Y#DTU8e= zi+|*^@yEE#Y34g4)7N~LHswYB*yfTuk9OTpD{cwlac}+zeiOb=XW8=u-WDT69GElF zl*;%iWqDNJr82m6W1eZVWy*}4r;q8|m5zP=S+(``%)#S2~r=F(Q9!QmL`!t(QR;ZLGEkoMcKX%UvVYX3e)@th z0=Gkb%NL${K4lQ$H8R{kG~DmAv>}J>GOJFxdWYUv6p-pw{cfhcY5%rxueON2njtfL ztgBE)Ln>m2ltq%ey(ay@lgkZX@*k!7z9)_pmmEIG$|*RzFl3`bR!+Fo$Dtl`S}sf( z-Ml{4YDb;5bIXy6o-QEfvZ|`>B*U7P$fwJYE>5oAV48M)u z82#Ygeq);A0=^CZz<_9j%6?D#Pb;jHM#=xUX5jna*@{;d$2a8<9o2OL*FL5~+gA6- zI^(?Vlhog%Ddq7~%5;5Xy~$lLv90gi{=i(x#m4 z7<^^vz_RdMA7eN9jOR^@S8x7W|9QrO+#SWG`7UbP(Ufx7DP_7odd_o9tskW`GJBi0 z;MwX8zjF3m&B|8#*pwx|x^ntMmwVEtG^$&DrD#FltF~1I4~|UOkgGDd#NqzNuip%} z##=fUDn_Fz8Q3Xhx;}cC4!K{Z`>@R2chQe^)icUsoA{}d3RTswAGpkVxlHgu+LWiV z{RUq7Fr&qGgk?qJ@XP9Q>=`ab-Hw=L)oX1X;(vTNnv#y4Ql{&p_o{7=zBHCr&S|=1 z>TtcG+Q4S^$+E#`LY*CqeQqxOeL&ijX0Lku_HsY}AcnME`jIwHe&VVbYrBte@wrj< zoIOc#v@e>HhMiKT>*K2~yS!a)*qka`^D2J!dnfbgBd$6DQ`H9_|J62s(}c59zivz? zOFQu!EnuUI`{K(QkFS`S?{iICUNm!~skP1_ULVaHWrienNSQ8=-76P0-XFf~;*t-O zqo0>XPrM(tv|IAFH^**WOnvceu*n>0l!3KN`p;0XSIlYLUCA~`oOd)xHP!y8b4%NZ zqm$-n`Jyj%5_CG`|LCi8?il4?Upe9R@}<=!%;6R-6JoafEE#&}jJnlP=Pm0J2aJ(6 zWQF^XZPpE4(<_JRyih3rrt-PZ*qL`t9vq(HfAL}QYQBA$VJUt{Ssv0$^R|9%+n#UW zc2uBuAmrAEDG%TN>Y?sco1gLQZF))0VQE7$T%WqAY_k6Cf48W%ux;TzLDebCr9+lW! zxxjOI^et&KoVq_zDJ?R^)5Ii*Lx?6{S>Q)$8J;u7!M?!n!iZ?6tt*KKT>VIh7< zSuTvHCmw1!&#&_Lo9Q{wK)2Am_^8>)E9=JisrB|>bkXNxsk9+&ht-Z>fB44J#glr( z>lL*w_}rb|I?`bL!B2BiE-s7-I8tU*CT=s=JF+lZ*VAgDR2|4s5NKHe~7I<9^4d`-R*&wajD8lm?55cTPRqzxwh}lM{gllgxgi*LU)< zL&|hnJZyNQ58o!??m6Y_RpB)chkow5%OWe}X6O)w^dlMHL#?F^c{p?zFJ^b;-h`zW zKXUsT{%Rw>I%;@bxmkX)pNY+vu-j#ZxpJ=RZ^gzIX+xG)KX`epw?V3P#f0UDZ}c89J8sVp)ybCR5Yq2V*WF=xWrlg! zA!WKKPM$e3YLdN{hn~#@nt>VvN7-S zlSOCuCY8TM|HtObM_;8?SPU%@7(FU8%*75V(?#)+bJ*JN7Aw@%pK9d%R5X}dwN^R3 z=6clp3jVw6L46#fqzzeT`|?(Lz_-jXbB7<`rtcrDu-ffK=Bm>{E(gQR<`ozilo{r9 zI^_T8B}Y3~|Ecp%yl^VFKd^Srtj9w>+kXgnTs(v|UC=c!GPFkO2bAYa`QuclyiKwQ z`pvKizjyaT;>4Kwsyf>162GLc|2qPGc#(}CQkDmEGs_uDd#sZSW(?Z%bhq|ydP?r+);XCJ?@cOp6<(Kxoy1yTe}(rCYKpx zb-duT|L0I-xF}XP#x#DJwmP)Y(@-=0qF=vl+pO;;mG9W|l>6*T7t?5|&-yNH{foYM zgU?yxa~rM?`8-v{KcZ?+o$=YgQ%28^+xOTou*@J6JEJU)mSH7wt1o-}Foyt1;` zrOa^bf8E&a{_Pi8E{damzIHq3-q89_zh=01M(~lQvDn>7BbVy3^VXU%5qVBld)y*gJ}y!mDB0g z$7V!z+gW?JJmzjd>+O!tmdR}U z+HXF^=yQiL*db-QC?+1a`<~Em{%M~P4)YHFG9UjcS34mtcXI#zU!t>T+H8>e#B^ZZ z$4fi?uKVfPwtkwYmp}X7-un7I{K%^5Ungz;F|LGDW|)p0Ql^Wda>dBW**R89*Y1wJ z!m>yyDE>Hu`J1z`X6@3sEpg>hv!qELh)a5MeBkpw+bzq-<&{rdy~TaXkhh$k)pJ%Y ze;#sT`kOMt(byqnx+uP@e_m3j@--&Q=SGcn*B-~dS;o~bnmTy-wiL#+t|#AlOB*sR zw)NAwPxiFjS6zH6=$9<6O|+Q0kk!?3&g)Soyk{4CmKmmDhm`4}cxuIurghhUoEoQY zM+RDb*kJ4%{q;`7k+(b4V$}-A?Uec&-m`(>*|mkVpG?DEg4byQC(Ms}w1zGm;`Hj= zoChD5^<7(Ln2H@zri5%`UuTxcaa@juH^6Yi? zImcUH!ul^z;JobdHOBO?@|2rFUC*kGls05p!N_^BlTT_ayAjgE`(!`&LEA>=2V}BX zS68LfR5pKnS!Os2Kcp-V=CiX*`mfzouzT0hss*0kHoxcQhJL%>*zM4hi8V7OhFCaD z8?sx;uZVi9OBa{US>9;)czeq5!x2yI6_;tY-BNm)y70rcGK1uf7o7J0pp^_4MfIwR zBJZ#(Rti&DdymZ-dSZX@Rg<^Rx(~IpH9z5f&(cTQjK}?g+Ey71x)65vC$0D4fo)YQ ztk-C4(UPD1{-yE0Hlto;21(c%WpT9F6{E8y-($V**Kv{K3@@wR?>2I8ubBP*y-y}= z?C!8|yQnzi}zO0}*RiXVhA^k%EhmNsOq=~b6*G4FZIUY0{x z;X{<3%}m!HU(y;unlF(}WfOL1a zNT;MAjij_RERgPy1`!Y>qy=fDkrL?;kyc7T1Sy}jzxz4%|Hs<<;9l~Im!IRjX0CJ1 z+%xB-sHPKa!T)@@JjWI^Dj4OGvk^w`i(eQh_!bCqbe+(LlLRRbSx`;UNN&A!$KRg* zG!3<$b|IqiIU5Qp7-V@XBuu7Q8HtoFTtoI_RhpnG^4zmM>G+Tg=gDT`Ovvve!~U^S=p$WLskjcR7>8`|fV<%Wnxes;TE&ipC0eTTlm z4gu`;m|&3kt&lL8Vt3;@M-EUp0F`knW2!gVmO1Jp1Rn ze9ulCLb0XLMfkL8bH@oSaB{@d zK5(OG@Y6T?JS50*@7qHu${UPQ=Uy4m1`^gVE3|HMN!w?^pEc@5eCI7e`-P7PxuEsn zc1T#7xo2KZhP6=gyT$WGRF;LW80RI_wrnp1JE_>nd!0{{_<$gBjnKH%v@WVO-DTQB zbMHneb9D04r+D#b)IKcX!=8}=)0^Bh9Oy_w7);S~%`rrO);dF*hgVOVlDYH~xtsfU zEjRCpg}XwZ*HP<%7@K&>j$Pi~WsD2pIYIXN(DT9P7=^O$!PD0P!taRuX`X=Tjc;Xy zMMvyN!i>Ob5_UFxoNOAk1)R1FznLFl3{ADaE1c5mhM9pFQGFJdv!(F8t*}%LyCO)=q2HV^_O(BAf!7(FnA6&2U~ekVB%}cu-VOAS?nXAylvJI?B|K9g9 z3#i=twyium2AE$@eh>s;emZN;Krvh0-N#es2ti6@r+ooA-k^UgBuu85@VPemnK;Su z4A~oZteuiw)OQ7{R?Lt3cyQ<+RqkE^Cb^W35@p~0+~Bx@>#U|r+36MA2Br9o?#Z-%a9z-Wx7&mnVgriQsBlI072S^wmb^&W@2`y zALmFyIV@Z#>yD|2OzvKYy&I3CDi7J?)%j1zKl-LP^39HhO?l%044y-}!|JpDLjLS~ z;*TNhZ}($FEZZ+_BO(@Q9JAGYJuzA|So)oT^K&sUz2>cqu;@6) zY1G6vG;P6qVGx6#?e6qrJ4mecxl;8ndQ^4MXd^YiVsH5+!SmPhfbS}4Ii%tFUkgih zy?VeBM_kyUVWFFk00wD57yD*{E-a?V=l=G7&10>zNpAki4mfzV2O`Gu{8!H!_*aXC zIeAT&fFSS4g}h&_p`CblTHzi`;jAvA60h#35tMH!!jO~B-3K`{tA0BqET%}9J^A_x z{N)s1#3$uZQ<1LRBp$_c3F)YCTb&WPwsCzR$oPj^v=}&N`wCMdh7=Ohoy7w_*9GMj zglow5smiU8FqxvdJ|z~T$lbewJ_ya#Vgq5TEJgGSBZ$AsA0Q@Q#T4cPDJh5VAxo#A zFrQ2C^E?fnd82mSn8y(r%@RxOhg=yqc_$d8d@CeOrpS=hQeAh7ntZCCW$V)Qtf0)h zXh!-4SH=_g{-vOwvVhyC=xQygh)?-Xr?)b;gA0Zo$|n%eyr_{pHM)GCVNHC1#7Nz{ z6%r;>6gwR5DL6&&ac5!Ia+f$zLlmV2c5Rl>QU)|7Bak(97L+3I~{HH?!1dOy207|3qnjke!(m z-IABL&w)=@9V-P?ZiO!jpmS3(+eia)cxz5Y@9$m8L-UjqC6=!AiX#5;pTMBIw?o3x z%&~lbFt3j*Nd?Z1$3G?a^!sJ}D+Y&A&DNeFV>VzE)ByzPM_&3^ZQ#I0*3mq1Brxa# z!CvFBMcoDnJ&@`pZsB(YFufv_;XuWL!yshO7rU03aKQHR+2@j@MxRntW9k-Yy zDJ))=SUsU2wE1Ykn8S0~TkPNt*WrxNhKT>lu^dRpp=jMN@EtGA6d$}nbN2ksT=M$Y z=OP51+~HEXHKdP}<6w~dzlwb$105DqocGg6SKBNt7xg={&LjE|?{qpBf$oDPrSIDV zGF|Wg0t9)0Z#7wSl%_L8dg-4!Bf+m5iaeCwZ*@@7whIaIfKI9uwz&Zbiz)Iu zxO>sZ_CHH|OX-IuXu>7QX7Z)-lQ8u&KB6xdnw%6skS+$s)IH<7zjMZxol8S0bdcgC zKcOCq2ee9&M20rAwtzvhw<-yfDc)T(M2wPoz2J`XT1@%#6&k}y>34QKSs`>YUR3Qg z(_SD*t9gNLnc2NK(-?x5wiWd%CBmrsyIW~^d!9{fp#e5|V35qMkT98|cGIuQ))^u4 zuVd=;AtI{NR95EL;%S%T@21~%5DliI0ztZ*BJMuZE9f16bmqv*YVy^5TV?#Nwi4v`jRLN{xR$Jze0RRN3yhixIK|21(tnBrNHv60T_3UDy(770lyv z&-)VP!Oa_iAg+C zVO;Ixs|opeQEA;-iHgD|X|oBf z#laxxkV+^>Sem)ab|9}B`?a9h>|;}nz(8dE&CRwmrjjVjdOpU{Fy9NHa!c|D@u&Ra zsT)W?yKKM5ZP`Ixe3$qsAHMNsA}$`0h##0<4BBvRKI{O4kl#v6MDWm+nlUh2z0$g; zuLTT;oh{RIH@u3P%g%C)$blF~h#pSypS&D6c>oF!qurRN`K9}SK7c4r;M`KZa20e9 zOfPyXBP==!3{E;bA-^pN>ZKx^3TqRfx1(Hpjg6o*)<4o8YQh4TUgwkfn_>;a9iDF* zNwsY$kM{H#W>ikaQC^(XRdkFdiXs>UolhxjbK?~YSWHnOH!rw` zPHtkyzMj|k-0^{{>co$Lgv5V!l=*FG$1{rrd4gHqvI#7uYaYCzt1F3UYMg^?8wqdvA7ik{-&2ZL&xPD~5d2q{ zyismgLQ~}_7Erohm}E@U>{tdAVE@)e6b_(Q@g7H%b&}6KMhE;lJ22!YSSW}2b^gpO-&hn zs1#W~=zD;H_}&EY2HVptYO3>hp(d@Qx*mx~#%`RB(Met+_!=L^3znwOyoSLb{@ay= z#T1J_d7ZLHjZ{ym=PSlY7k%Xa>Cv@&@I&~AoT}nW%VTAriNklaPt_5dt%dx2SYV{@ zp!ZYmB?6j#4rxB3lJgv?im#dQO~D}OWJ)MVSeiMJas^gX_V*#WqX~mMj|FwVa(r74%0snR zF%#HTB1z`}Qu5ssUIvL)qMT$Ng9iIqcjE+Il*R_9qBOtM>N~~L4gp|#ZfL`~`7{p< zLW&CqvQ8(o9F{3BtK&^};1qdbk&atTxlY^WP6uKnA_FlNV{+`KF|K>gvLggOGA+zR zZK3wYG)2(om5i8)*%WpG({tU*2#bzKZ{LlYBdX^OMQrV=fg`#mhEg+f4OFqDwY z>S~YzF}Cv;8V?-qorE-zt)(jz#=4a$z3$$lt1at0G?0l+MB?Of*k5>{7ii}$xkZ|=nyMa6HDNim6^49|k0&vi*D@Jyhm-NCqBu~w zU#MSQP3yG^-2Iu-bJ+FNDDtr6a9lH;{&R{=+1JSUF)#=^kn$!ZOe;m!vG4n~cM_TA zJESmaWJ=YYd`!OzlS~(y<#j(mxt9;P2ovY)eB!R`JU{Yuz1FnuS!2e%vdYJzuT+B= z2r4EXs6kAT<@PZNiz$|teDcz6&Lcr%7T9B(;E$4ILOzq-{wB=p^}1Y=SQK(L9rNgLv z=DT`1qY5OTcOT7Y4zIHKy-=z(fgr20+-h~Ljo=kJeMV9vwOC5)=WusE(?^`(Qzgm> zzdZ+opz|pIF8BX?Pz;7%PAQintARe6%=4sgivOWsEq+}VSFBhdO%>l(L%xkP;D=8N zU8)xc&o)P#XNvnJvKhsLWsg2G^?z%Ov$L4}ph-3b20_PBLP5gP%++_%XEk~^M3`D> z2Jj7clNB*2V_009kEZOm9iGs6P5~+T%$XIVN{68KQD$CVRpVTxdFbj1?vstCt5D6<2)$?Vyu>s z4vl(Eu-2Zgr?{00>dXlyUeYU$qZZ&nh5;n&7s<&<6H;&y*`}U#n7c5~g2s(%o3KAAmBt5gQa&$9}k=2!}U;lQG zfRQQwLBje$uyhVhZv!_HV89J>_-K8PmA}-+9ayFvR;DT=HTBI0I_UHH;yU zbpL8V|5r(vOtA*;%bQmw{?hGW=Wz17EFCM2M=I?yADJ$s8j)NTct?Sh^z;H3`R9E8 zYW#Mpfkk!k(wT)H#iuN?p}Rg-gn$cFRUBYgN9jsR46aSoH2792GDK zI)(BkBuu7A(bFmt9M;%1T4((7EawPesX(G83?aWEH6>C}R*+7bE7HPZxf%_tj-wDp0^)ac zqTxk2qsjz*=iEXdNM&`hy#&8Sg(5Ps_^_C=u(x4>c{m>zkHf9JmJM>T*}x#GTOnaG zMHIhr?xlk|%I6_C{PIuIS42@d<%zSHcXesrP1etx0RFsCROfj>{PIGM|7V3)qsK?0 zGKquV(F5wC~M+a@T-%Vel^<;RBo2$$&~Aw<~ew{9UQAU7y4(N z?eXc+znltGUDg(+S>wU<V< z@Hsq0ILyV#Uk5XHQd0foyq#K)(rJNgVInCUnG_9(QTBe;-A{YIXd%^>O~I~9y`Kd) zzY&7Vvj}tJX=YuvAoLG%Z6TKDI3V3KEuPPVV7Q{OJDaYpI9^gqCb+9Ly8H?WHqNyX49Qr69f|;F$E--`r7XEYwKd zmi^q!TS!Fo6*r6cG2SneFP%Q-^I>K~;~>Z&IJS#I&xCj z>INh%rigfNKi`de$AjCSUajx zT#8i6JimOO)`}Sa=`xmp`E3?RU@LUuAJ31T|8%huF$!}hPyY@X0k5bL*)u3Oki znZ2N~bE*V_gkOvoA;&liu5#42_?)b3a(dbnSwnl?FpAZYLzGSY2@FELed55feHz$~ z$?7WKGD+*>|r+yxCVgTh%ov))2XbfW|A zK#-z+#|1BJ?(fy{&&8T}?M64VGh+NE3;CHml1ILbVs{7fYRZ2?{?Q}FJ=j8K8Z&&8 z$8C>}D(;r(z$Nq2M1S&mkNKRpNSgjx9uVa4SH2fM^-c=o1r$yC8`Z>{q^MR%G{%)N zbLz_HuU-a~j_fNi}SnNr?ilZ`^X%5W*l z1qA73cSbX2`J)X!LH6q+H>IibP-%BQrok05%XP8Qoc;!w9ueAbZaz^DgDEO{1}#cQ z{6JJwsZF1l;YhTFuR$l%_(`dKn6yqFM@#_3C^JJlg?@*2n&w&`{ z?IMN>iCCl&#LmqJY~HHl)*f0rAW8W%cY0iAf2=2c2#-uH07i4Q@bGx@*xhVy6cPwOhL>_V^BINo&>7F;8Hh4tUgW_dc`9tUg zln@pqJP;lVOXg-sy5Y!mSH@2fG;GLw^F8o&aKXkw4M8}8_5r;CH2yw*AN@FdBYakT z#Ga&i?|UnI6?Afx*I zTWRN-oT9q2-O~g3bre(}U_-95JfVZ_WO!kGh~RO(?kk03VbIT^q3l0Jasi=)Zuoyw z{!yTGNxK7soT3F<7G?$rZ;ZK++NOLCZM$)o_j$dGmnUg4(DYJzXkGLpCNok{XE^U3 zjt>Gwa37W?v9M9Rb^2S@iZ*!&C3MG!qJ*Uh7!J4>t`1UQNMXHbMmQ-rPk)nCA>G*Z z&5}ZG%aYN67>E*_oX=8JhKZ8NrLLYAkUD`*Tidu8oq2D1&W@B`KXw#MABJ?ZQ~eET z^^oVUKVCjQ7`iF@0;V}3wpQ>VHx@2}d6tK`TS$~uCx_i)1GuvSlbZ?%vhRw>4@_~W z*>3(bKB6vndQv|7gg*I=h|Xmq=E7#g5d;#d+PqlXoFpYz{;YX&?x-F}*OwWh3l|I;*SfMOTxL{W!QS4Eqc8YI&p>|gQ1Hz{FRWiTUv1XUE4yVDKp zQacmKt;te7vxi9iYF+fZY-q$Fuf76&5Emb&^y;E4-hQiRud5^7&lMO$9lTuOOP9HXE)JMQs8zV?LvD`EJimawceI`E#$bF%i) zm<(3=ItZz08^TNA+WjYe4jH9o#9UAN-cthsAb%i{xTSXM7oQfGEXZ zsRln46l#Cih&Fa6XPzLn7!z5o710Sd0WU%M_K+Du>HnXUe{_D4uX?RRX+(HRV>jKU z{j@!EsG^ent}sKOQkbTek5cdz5GA(#6$W)~Wv*=>H~-z2&)8**(@XsWsE8c9mey)o zKAb`*{}nU*zl#4u2}@UR(D8Upc5Huq(u-mA#(RO_6Gpa1ZnzP!6^F6x-q}WCEDb%G}#-z9s?QM4<%e~ zVvVP3jp`6cpIaeeQPR+$lOVb~P`rUg^v&r3lOwAFH@!uF?+!Q?u9k&LURyeuX@#1~{!J&#YSD(2~=<} z77g<7No%4=DAQYoPqA3Ui*b8gJSajtE-1(RDDjZ9SfO6fGyDc6O#Vpsw)spW^v-iQ zz2`H=gx}@I?`#E*UV3)dd%pl1>{|f#bdYKoi4JZi{_h_CwBr&x<|7Bv~d%^(8j5ob!%~0(IZTK$|&)((P6)Ve&_LK6ue#C;fMsgSJ7hap#hR zO)}5_l29EFG9+W6T;u_VW(J@Xp9KZ{;(G>+z-D=PuV6iM|Dt6nxs_J0rUCn)NJIUS}VfZCsa#t5Si!%P{pa9*I}CKD)Ldy zGay)|am--4fhVQlttZ{6GtbEMJk%hR|4JPGU&a5Sgr%!Td($0X2XvR|UoT1elMF5T z?e~;4Gt_^GwNe}?()R;A1gs~q%1VhXb9~DG0<)hp7YY9$GB1MYt^n(tI^G$gBmu;F zT%fGye~`j|vVJ7k3eYB4`>`@6hfSAn8Yr#084G?KprTga6>m5FIr@revUU z+czXLm{>&KA2_1>WQH>ace{zp=>!*|1t5^lw?e|ABuXVSvr_FSCsy8*2w__ca*AgU zRvvLO2Awx&zSKBB100pT-nM6;5l6p<>&}QRY6J2ztcC*mUc2+5rghV+#%;*1MyS)j zMkT+kHIL|@_#>T(YtMUGg#tBsE+1@jvXWq)&%dfgLm2wL`|3T&Sp$6A=>Z)j?_(pQ zhP)@~m+T~@-F^68;y+SZ&#A?Rcc#LP|LT_i${e=1K?#dLj`A@N|2#L$PI&H$cF~p~ zMT`6fJ}?_Ke-Bkc$n^9i3aH>qw!fyT`TIJazOXW2`KIZi-^hrRfLZeK?dshpPLY(G z5Xygr4&R`J$sc=5J3@8~o)LNEBD7W)1se%Ri8jXX) zNc3A{B$?*e?qEOEi|}lFH&wK{BQKT;^5j_Pzfy;9P{QPoOol>jlA>6vwgt)rIAn~n zgYk?Or&u<&wTywQaK2HKKw7HLx&M0W=hN2obGm)uRj;eBhggA?Ub0nWZ8fV27VmGv*ynnYt_~-p9BETtb(k(2T@*DMM z2HERR38KfR&S8=R2n83#h}LwtQZ?nKeCzV3?w~2c+f29NVI9AIHWA?#9`Zy$4H>tjlfYjG9Xw34$@o=VFWyO50m0 zVe-dbzc&X#y=!2L*TEsD=9oqXS(PKg%jqA0DCNx_(ZfrC*ZDFWLQ4<| zf3zl1HL}VmQx3$ovv@eJr$8ue{*&^Ln!42cIGW~o-qE%g>G-(sT;!eQED7~O@3v1` z-ub*d9RfTGlTkG?%V)H@jkN`quYAj;Nb+oK3^jB6kBHNXyrZcj zn;!Vg5zdnMjnyjP`mqyXdzX&UlN$dsT)1-jrj}CeShxG+&~ea#m!|^rqZJ6G<*ksg zC~27DpQ)UUJixrMk_Io-7*oi`F#NFh4tHMKSqF0B0^r;kJC9cNnzCav?4<~?Hpve! zU7pz*6sO)rAs{u!(vpsZ)Ehc|SVYv?R#?#LPy8`Kzx?a0p*)(3(h1TrJcSs#(E#^V z7d3;&Jlc=uJ(m`sdLKN5vuoy=Lv8D~;}%9EaURi=_gYoVFlTuep2<#N915W{hn_n( zDPi$Pw2J`gsTFrqy;vrE=f@gqLfzcN!6QE!V%utJ%?hx5fhYsmu2x;O#b%Ymgwith z9*e=fkENMoBmFfIAseAV>}(04gw7wnsU=MQxR3FwLxpVXVRVYB+eHoXv1mcE3+5g= z*@@EbePNVGfCplZF*PqjWM{Nw@%z613c+*k%SWI(IW(t!!_JjLYGnqwh8t>jyOyvV zZd?^viS<{$zq{bjiVlWB~a(5yWI)NBUOIX&ANYh?r-}Rs3@$>}Et&_Zn2+@7+P^xeJ zQQ^*WF|nC_ULZ;*#9>8NO(CV`i5=zGUFtK3$Kk5}pNJy_?(d#+%60BTD4`>WZ&Jc^ zyz%|3?UJlKgVa~`QU>enlejE?peDpLr*}_K^v4$i*a3eLZH*O-ly}vec;Cy=Vm*O# zW1|l}M5zfg)B}}hlu1NuLMTmctvF2n7*?|Hwz5NTPQ1kWEC*xEZ_cMt>HSp?YH9T- z%ZcK>mq1#QiW%g-duD)7(QTO`S?qAVoRO;1ZaX6rEFAfU?)s?}gwpswDgWpphSgm+ znx9zpkAJZ+34I?o?o*CWr|NP&TzGYM@U`Y)1z_xbdNJ}@&;!e@TCB%tMmc6vqt2P) zk~oBSgrYB=&?iJ-Kq#Sei2ttmKa{X^b&LLyax3D2HH_(RQwZ!ukE!*CWJq#GJ>RHY z=pgv30EV&^YLWO!5WO#NWy{{--k^16#Wx_ zO!_*Evs|uOW~s&Uuw^}l1cg6% z#2*viqD811d)0qH_-4l@vo!j7c(`q??w(s$PgR)izB}Ol>W7V^Q<74z!1Qi{JlQaIXY+doL>~azaiw zPY_P~usqht>~yx<2h@iO!KQPAtUkxN_3EwGwkv|1 zBghRci9J?dnH+RCB`7!`l)C@wLpLf8i$A)KDrvG&OdGHV5v&+4)%u4SAp3uXi=Ray z)foJ~B)S1asnSapaVFL)r5(S%x~O5kZIbZf<5@z&be;n0tJsNzzbK)@h@rHE#UHJj z3tz+#QO%O`$+qHyc{rEG4##Oz_h%a3Cj=kf^p~kyiJnw{eM#QgI_|Ocw zRb_#+RPy_vHjcd#_;q40PnB*7$2Br**k zN;_n9{_j0Gd_{Rj-x%~5@AYE_=ot>A>bWm;X@0($z7RrQ2x- z*jM!LTeb|9fFeK&&Og5N^`<$SI*=$>q=y4hu4H^u_^3%Apgt=vZgeQfv+Ql|78jpO zTQQr9F?fXc*V0TK+Is$);{Tk;gynpWb_?-j4@Sst{HT@Eda?7_+ItYcOYnKa4w0Nu zNW}6_AjqX&-K%(obCm33%_q~Y%#Oa+*)mz(%Vt6c0WD#@FVP{8YPUkdqNH$=)+Z{e zdPh2&BlkgVkC_4&W;csEf4{1jrDn1ee84pi7GWn#JeS1z;+^Hxq$K9i_d%LyN4w!R zfpHtbGjBexLh22jNh~7DW6fjpN9#vf^IiRf;sEso*=e^j?_yW4CkgLPgf3<#!k&uG z&w394Nhuu{b2ewHujTSQJ?b6fu#gj*(tVvW3d}hjP|{}3+rNIbL5C7UQNrSn%JZxE z6@9ks&#c3Pj^d^@qo|!c&BZq+*~TJke62hIzvY4ltV3H70@Fh;g%UU&<{!n|I<*~7 z+6IGUv}+R&a0eii(5b{XDPi(QPf8Z6g_OMdxUE+j_sb^ku`O(MiWEvNaK*)f#XK@l zfV6x?z0tDq(n0IH-nrvu7ra8jFU{An&Jwg^SJ@F%OjBPWl+dxnHz{G-Up+zZpqkze zHJ|835{O+gVEP>Xs6%g;+rji|TH!-fYX}hK#|Ff@yeH8iX9X)Ax=dYNR(h&_Z%$@o zEHV2oL)+VCA(Tr0>X!d^T*9=!8a91xZzJ`)_pl4KRc;mGOSOH*>ncM${5;toaDF`q zsz8)n?hI^C_KpqBg)*=t32;Ie3L*0ttnb8JCQTB|6d zRq{>{5kd)_PW<=m`G*pguC94CR1B`HZZR-#)xt0-58Kr7z4t;D3yg#KZn(@*#}9~7 zYV%k#Dv-pb>i*S~s^awGv67iUx2Wb6k;@Qete2W3#CqhQt>>@F^GEB)_ruy%#Q`Rg zgBhRH_`0p79WyA~oednay)VU8q9U3BoBD6X^;(}K;}d)%MGiEqNY!rRCwpRw<#K{s z&v|}VZ^1zzWp9OqMM)$NHw|(3GY0+%e!4UJ4>sU@ai{O}#7WKgL4!(J9C<)WvK4^^ zHJ>iOiFk~-m!gSCG~zb&)~DCR^TV^T1d@5xkC1xHK#xih3oAh@n?G4Unn&6`lhOWe z>#`MNZtu*%_prUiNrv2y;y0!pL0N4j;Q3YNQu!EZ(2+26B?n`4I6u7p9Om9thKhTK z8{PzKr~F@gg3$TIA~z{v@yFKeIfW1(l%W9S0CCU4#YGu1w;1p|iQcDdtK$~?q5vQ* zb46v6mGboA^G<3C9S0njK-3L~~*AF_#{jFjI?SLqkoJWt(2=mR0eb%?*gq3F{-l_&jwFUf+^MjlF zoW#%%p@dE-hSCz2^`mhoT=|8^x>#T2ckFlPGufAW4pZstsIi_|C)N*4qyTpe=Fyzu z!z@OMzi7$dv%Ypn4_6|$PPxhmAJrzBT6`#$1EGYDD85MvlRplo=}l$KG+J044f3=k zk8LmKuo!r8jgI{eHzQ~JDcuI7C1tFQU$uA6uH|KqCjqhrmci+M0^NsCbp-_h&-oe5 zNgq{BD!2{RYNmkMQ$E9G8DWb-M!_i&qQwInmFw*|U~a{684PA;^(z|czrNm! z{wL)hT}t>ZK}7kA9Qo@T)GPk?IbXic4t@@PJFX39G?d$Mm6ZpmC4H3fr)x|-%@Dl{ zJ%LZm@9G&mTS<9+NScr$wr#9B|MExZpyIzP{tqQAU7d%aOrR7)JDsME3VmhOdXD* z=LXy&?@L1^ir!@MBeckiaOKJ7qnI$}Lm-82g@i>(%rq{RLTcXqgNdLJ6H! zEF$uMwS>tZWA4(Ld?;ar!#Zr+91eIXj`^@!q;K8!K_%ZycnU^;e;_S|zCJ-(XxUB{ zqI-S3+;euuTv(SPf@+fzD1Sw$YPJb^o+A`GtoSA+OzX!!_{!#f_1t8~=PpK16GvIP z35#U69(moLq+mKdYjj~?m+o(z>w{`@!xd275u ztF%|`ZB=|ft0aPWIW71rw)e?}eg!s!lIQku3CsR!wyW%Xb5gZGtwi>`lG)}o2PoC@ z8Sh3~>GQ8?rMmO!Kw4s#+di}jMHn5CV7&iStb)ufb_z$$!Bs$|m^nPuiGT}2$$cv& zO#bM!_H#TDQT|l!EMnE5I~dQrXIn?);-sd2AM;g|kWvj0CFeuC`KK9-qg8u80@)tH zKFgNDSQ7ftzZj?eQ93!TsxtU^n3n?(pn?Yu>Hn`AqXXOX7S(S z@*hfAx;nC~%o>r~Lt9hToe6iV4CW@rXHG9ZP6f%5O)iDw9Wek=3W=^?tL5a9ch1`r zDD3x)n1|uZ+#8uMdV|}=uF|Dl4zV5%XzTf_5&hBr>YGZC@f@ReOTU7f914rGfcd9E z++15bv_84P9ryRcfZG~jVSx|LwnH@%XVr#EeUL_n`A*v&9GTr)@qgcLM1YiG z5*8(^HnKH-4D>S2?8F6c=P1*=_rzFK(7x%m>UGr}m9@awC?Z|tv7zl1SZ%kcfRGJ{Sn zhN6VU9~liN_Zc;6sEs#NeMda^^R+*Mh|$%}!pCH3j1nY70e3Ypv4nD}mYP|{IrG?~ zWvp25oOfAKjGA?@Tyj0^vNeIcOcx3rTYQreCV%{n`)iEl1?nz*oK4`A+*Z8=Q^-*1 zb_L1E!ami0P`w3E#hFl4+gZzlYP(UWl``Y#X({+Z#j_`@=E(fl2h2%&!XcE*(0%A; z#bH@LW?MY?7EFf}@XW3P<212?z>9La^h+u2m%_S*81#;^zoG>Y;OTfUP|*I)AwWR? zzyBlAfu56WAPFm4F8mZD>qa&Q%8Q`*N?TNb=S>obNnTfLlZ@hJg?EokNHcxmh>;!a~-9+>e~qqS~Dq zY93MuFOK|mCxPi!N|@G7f@!n9IH`Kj|`GMG=N*0cpvM zyNLBM&NoNJOWAc8vrp(meu#gzDfa#|_`;a*`+5!#O2%6$Ve-fGrprBCYySjOM8e{1 z!Wgk5cBz<{hiOc)*by1#@0~b-DEIw8PCF!b4X79kbnc@0s;T)pm`raCGaLD84EU%S z{#9{?|D^n9=Molw+<#z;RBPTa6V2Na=ItJ=lepTSqt=<9YDLCxZ~V&%aL1rOrPuH1 zDpP#fcEy2W#CklVU%f};w?=B6a|#?>aY%5<0&4?>YVtB`jUtoJKq_gue!HsJ})m zSDy~peu!RxX+?~$(nKkdEzMJpp-fL2PfnpeTgrJalED1aY4#X?hZNk_6e zF@OfK9(pM2`QH@(qy5$EzVP<*Iv#9U3@%-VW5;hjXG48*E@cIH!#JY%(?G9)3f^*L zGD!bMk%MO}Q#Hnngje@vIXqH{a!Y5TRAmm0Tqp#R?p8=xlvL@hw{@W_qu`r#-ViRE zmk~haa1MCru*9r1GOPT>^*a#cm7}~M1#8b)oZH1A>p1JX7Yt0sKCkn^!v>5bf$eds zkb2YpYg7u`+6wXg$^PozwjSwdyl(0YCmNb@+arcon@^Gm2P~_8mSG)!#>AlmqLlRT z9x#pjF{k@eZqq4oQd>n&Bb6zO*31o+XJsv;0P^H|D9!DZu=wL0@5m4CtRkphYqspM zifO)TKVEhw;U6JHQmuy;1bqk0&JvMmG0i(j8FJndWa=;36#jMo{QbgS=CXT^$M+SX zr`Hfl=mcXC;s2{8O#bM1077)f)k=RDRN46QG{w+1NA7#pFjce`-KSsvFX}OYv^?vi zvgF5LQ|&rA1$`B-V3SAK_THb8O!~;A^|{t=fdN7Z9btTv5~lT|6K=3kqi#%`<^;#l zbvDu7J4tod_+iT!UYBV-znNE9K$H*P98doM&W?<&iH^PmG^ z1k>S{&a8Xfc)y8Bc$Wazj}!{h`frTT`uKHfIU+4PzPN6+j!PBO$W{}pgv-SSK7vq^ z->M}{{y0V`T0?z6u5?-M@HxKjA+i46c%{q_&`2bsroo``4&ct^y|aQIn;@>X)=WD+ z=GWK_i|#C=wPoW|&QbcWai&lqDPN&v|4I2rsnwC(7eqq{$R-PnqtuUuCG5qXly?>= z*H1pLi&IVdVAcvWF7Z0TdYfO3yPn0US%x@Um2sZ(P`Pz~Eg61($yJvtfCX)h7Z|L`-_M{`VaF)by7%Ax%?`#-?p2Po1p_yjv`?vE%o_oR##s# z4&*5vpa&0cv3@{UaTHEV2*QX)g;);>wDmw55hO6_5At(JR(q;Hs=s#93aYaGjTODE zm&eZ25x*K-h}0<~n?`B_2-)3Aq2n2-Ur2F~Bi7DXXgaQl=2O_kUaN2J!$PP6i4hGY zz7-M{C0#LN2R_b)x4d}qyPi0u1HbJ~rv2<&Ms%Kx;_oL}vY9}T=yK_k+)crYZ%TXb zjlJivW~MFdr4BFcCKd!QxMi8@LF!HPuTja%ZzT)~8u?%L6ioR!!lg;1%4(Sh#t*Hh z$LzrYjhtaUo)}0>@a5D7WQ`5gK$QH#l#UZ_eksxK+ONkZ5q$LB4@R8VwL+pTXU!G$ zjN>7c&{@VJ!Z#>k@yEiS;Tjia{D*FyD6jdnuZ4)fAl8PbnDqIao=?Muy|u!C2SN!QW_*(pCV$*{>uREWz4wT3AkqdB zXw{hhz+dWMimW;^)28BEZL$gwrNC#F`H)_G5h^2^7{+&az7MmfW{*mR*$)cPCg+bt z3L%uxX~s7xVe-csf~v&8DuOnM;j(4_x^!94ey%?eKj7Ndl}^KTIOzyP`EK!teBJ|V zH|n4cYh@#T^~JkCGTF)!=2=ZWjxmaUazZGfFbKoqG8&i9g^TQ9%=d@n51fXW^4cN?QezRQK~q|tF4 z4qq%fV!rLLtOi?RhkmU$CZbYQl@Ejx=T=IX{Bd$#ER8s<3oaW|z46?Y)=5>}%6)UK zwml2&y0l3{@)=OY{U)b%=IJCo+p^x@*G9dxtLc7 zzO)3!_hXl{U1Y_+@S2Z#xpS^z+Ct7a)cY&3kdxK|-VBH`mqL}VT5Aa>>C;Df4FZ(`T7?`)e=~mB0E7aEp#f zb>ugeIGi~efZrb#dr}DHv*V;gTi*wZ)5?mXaE6R$|QV^s)m_74i|DLeKCU zTEgUyl6r5t3{MymMJv?iHZqY@E*_^~d_<3qd6|W;s{PNTe|8aZ4n&AKNU zvQYYIi+<<9^8w~uY7D%mn}32I*iKkr9)q?q_crD(3X4UZC^mg3!dQNe3KKjeWp`(qV#w9HNDEI%d_f}C^u2I)8-QArM(kU%n(nxoA zcS$!Q-3`(qA>Ae2DGkz~gmiw${{MGumAzqqCmww7gJXC=%z3YCt-0=4*St;3#D?CY z!@x96M|y2LdGOGcxa4npeu@VHawWvuR(Nd1K}{5$55*&jHS_r;p@A<->9pYlgb+Zf zGsLjV_7r>XjLW@&|1~e4)E_Z4G$uCOy9Py#30k)F+bX*T3}C1~(6gY;8|6U4rY=6l zGIgR2J1xjtjTj)_ujTYZT~wv=h(>^w1=Y=$o&e6uAG5%}IbGhb`2RS!Jw?=qfdyYj zFD+Hh>7(Z^&I?fyff0xp9%ln-_K$RhQ;(cI#09)DnGC9^WrKq5&1LShg&YYI5s?a`0vb3=!m69s)xIfXFCZcbT3oZCf z(WHkA1e6O)o+(kAEfklt?}cJ9qNc$k~?`l`2byWI^irwwz=})pS2TucTZ5fe)G9}!AM7E@Spl) z9Gw!d-{k(M%lV)V`zpj^7%d@j4vxaqFDUbk%@yO5kFgYPj&R&Xa?}_&X=P$7@N0~0 z9E=%F7KH*(XT(Uy4w}4U`J2z>&n%zPAC0yOePfY;GC$Mpj#t-aHQPDzo3XuSK1iSK z3F>=`_^4}z0dal~B*a7}&Q81u@TtouFq4RUvxGm<0mOn&QRFRD9@;B zlXgG6&x{bdJMY{(87b~qy8fn?KdtyvoQ$&>e2?m4NS}Ak8v&u^9T^L`Ou$c?RE~&;odyJ(FyrBl6K=;KmF`e z{S`gtKATJq%Rv(w!&}-rmd^hk%cu0mPzG`FDc$;zQbCEDp_#tEhw-Y zLrG@KkFhLBMhSFvW<*t&3_UvL?cAu2wy^4lseOLg7v47&kFnH@Dx7;~t%WycKpP0$kH}uPJ`D2Ws%H zg1WQ)3mt(<=+Bfr6EJ}t!G*Q4uXk;LxmhkBV+l1PxoEQ7$%gNtntQPeM^bG&&1?sb zPawd(Y|Pu4n0eQF?0;=NKT7;R^+#G%MUcffWV8uf(8>4FI;1T_5!)z;vWP`=rtZE(AC z@t%X-$5@I$5Q!@>H7SxO-J&jr*R3s?%qyE-=ew5TH@o3QfPA@Q`J2z>&n%zPAIp6V zfW!1tuC$cNXEGpc0q|Wh_pH09by4;azvsEFJbDJaC&&D;nOz$`Wg!h1nUD71Znq!T zFuOo(PecSa7>wlIw7=JHK9@hSd{TdWM)lSDCH|6ci7V|p0^xQYDfq2&cS*;&=vb?h zTw|MBD)D4cQ7S_h|=-amP{uK{lx`McQL5UvqWd@N}g3G%!rB``skP7G-yJ zaL6a+g!9gPq99l~y<=(ltET_N@=4c^8}boJ#ITs-VNn`54j)H-t5O+wV3ouWBfFWc z8L2pw9%G3&RSOU<(&QEX!3>fx@l^bhEk&zgjnM`3O$gLHo&DW?e=m!FW%;D}oU?C% z2^b~h5Esbsl4BN?*q*MV*<$!a&NRe_o1#ZCiic z`FU2C7|z;U_G<9zj-~m(vV2m16eMw-U#_p%l)%y1JT|A`Fw)&H6-=7hXgDs5;dwS$ z^%%>!w;^V#_H@_xnt(@+T_jQWan zsrj6BK^>F0RZXf(1S~_#*p&LmFCp)eg=-$Y=xGx1m2fIxP=0$HK3Kyh17eJ+xM{*W ziSB!DE=;=!d^ZW<^_$b>&n%y!>aHM`>c9t+A7{ZD=IUFrIzTFWfDcOQpOd_?F#GV@ zvGuW7y2LpigvTCne+_Gd(|y@%m)E6rur~ao2iGerY}TB>%@G_9@qo zD<-jgC973A<{pWoVh%&CdWA~0pUIiM<&&DQ78AF(9wX@}B=180F>Dl%dGib2bWL11 zkcX#1?Uk}%$3k9G>8HUvlE(i^@+py|29S+Ur7LH2tx_y=&m+D0TDKkXEI8`Zz!5PL z$bQt@$4Fwn-$^+a$}a%TTP=T4WmLC!^J3_mW; z4NzriIIvtCK+2TwqHrZ!6yLEl{H?-&V)>N*XdNpe&L{Q-rLLTL<0ei4kc2N{AZJEC zXYShJdzmg+-D51d%?||4)csA@^xMbY(`!rOa@zZ7tmF@~2on zsXubFlV(`9!l20T;=_SQzzvv`*hHc`f0_gbGv@b4jc$L8Woki`T{g{3o)3c6P4rrG zl}VvoH%IDZ6?&FQ1#Bzy-TbQ8Z$6hlv3yd0eE$s+yrmbw@_GTOApvN)8KN)X)`vU5 zC?9wPqbYS5?=hC{e{!?s||TdxmC#N zyfZ*_teHO2K-lzd9!cN5KiBIwr_1{l|4*Ct1ar~Z7LnG~W0mM*T{@fV&sw3|98Z9y zBXMLmhVaTBeTLFip0(ZC9$R)Iy`zGlemm#-S=gj6H?Nj3S<5b;FTe&bLiLfgl(X|^KS_~{L}^ZFLd}stmM8v-eT`F$#t&?kD}Xj89b$RY-4-$b?BcDwS!LdYt_*46h{|q zk722TjKYn^y+6fPUdI8{iL>9W_isLzOxoIvY>fYg zcXK36-9T^JJ9}jgJUaW$poHN&KPB2~mneWStoyP6#M82o@?8e%Yvo=3_Zf0`Pf5Lg z^SS()LMsrAB!I3>Wb9ow0M<7!>1D<|V1d!sRPoKZcf!r3I-Y@4)9{{s?b>~y! z^_$P-Pb{C*ABiZ3Xl6+;NnGO4<L@YJ+!XO0|2$qPL8laNWYE1s*FNu@8)8iWET_ z2r8>CVkkb9#NbfhV^AA>)%i=pVpv8L?pXfjbNLg?C-ujgJiyz+Be~mPCSVv&u1hv# zjD+WwTF9n-^Rc)<3WbkOGmfTR3vhn>@@Ybf=tg{LQMYHGSB~@AxwsnJh@TW%_ipmV zOX*)%{7Ki3w$W>r!v0VN8ZO=oQsxrb!rM^eLjCq0C6fq~gKr!j-90=JZiPD0aHPh8 z9s4SK4h{R-lMjzJY%A)L6%Spt6Z>xWu$SV$vV2m1q#K-Af%LF(cj30_I`&=@NBVvV))W<#@W0oJ^$9zs*QWBHrY z<^77&|Cgc5(~Y47kd9S^f~8W&xs3L%=koEbitb5zsLnXmDF@X7kFg9&X#&z-S!7Y& z;Rcur(AVT%%ymgQ{~)d2TN!_ySD$#t@;9f;pIJUd)f)oqZ(k;LruKmC@Y+(&PK0yj z8rhV&dINv8@=iIZ19^;PL%v+c41LV0u_~bm4l^s{cUKIk$!ffn*!9<=4dM;;cdbY6 zm)3Jn@_&YGpVA*8GVJsriA@xK$P4(NSQ&!LO!% zR()KoEfVhafW{1NST-_N^O3@BhZ%El>AN#`EMgJIQ1|AbF7@cjjHUXqSW4qr7uF`(TCoi9SJ^~u91>9qjRm5lj5&PfU4)Za z_;H^8o6qG>v3yd0>_^T|nwpx~k7+t)%jzWtoo)I)ZM%6OOekz+loLPwsH31&lCVzY zviDM((frHj-N92)`P&53k$0EP!eJ(D-mmYRO}(W4J(f>-{+PVC*h%tzmb5=P>Meh~ z9P?g0m2C`al`2;R)*Ob^nDS$>)ImV__qTLBX2bAfk7Rnc>OJW(RV(xyle3O&`!SZbMNAQV z&ChK|PhyG%l}rNJFa~cIa_5hskSo=g27h>ldP)3yET3}ycv!>)GEvd0={el!K;kI@ z2E2+Gaa)%A0_T7y{M4~E>@k+Y5t`zk%Y5FA+XRH*)ssHg?ya>g)Ar}ySF`?VFLmp4 z$5Q-XSw5*hHa_$5aB>_9{>}*wldkPW5v`{Oo>ZVKqDGd!%1wy)=y{twqH-s-(Tl9e zGlgvGbg*T}FH%+u-Q|?JwI(r|#rSt`O7Qy4>GFQX|I^TAx(D=8KPq}XI)N_IzCx5= z76$kJE5yO;`nm=bDcJas$71O`?m;xcOe5!=zq+IiROWbx=iUV#JV@Y`G<<+P2=y3Aiu8|CePx3{ z;Yx)sJ#jpmNKKk{TUA^xh?0guX209r?K<-M&F7L)SC@{B>Aze*QdxB~)ErsHQUpZE zP3j4Tm-|>@+E$CF1}ks@&db#9NqNqjHd!E09oa_U%GpKliY_30RR z!BS&)EPwO4{F&ub`r|Sd`!xic4ztsSvkz0?eqn^(>}+&>rrAC_cmdIN@}oQN>Ecal z3F8_|84`%K>SACL4QeTXjB!W~=T+XZ(Vyzg-?9A7=kh0(PwJ0NX)GuNGV@3xbVki$ ztdR;IE=+aPx=cIWFXUooE6+S1izRu~bKhMDulS)32O+fT^mxfa1ihA5$YbuxRd`n% z&`hv*Z=A;hVE}ODa&WP(Hajw+*R{|nRNj;)k=7AQ6Pn}0WpKNf)?9xQhd6gW7fUymCFi3twi?h6hM000gN$r}a$ z$=l7#>Hq)w|9<@L+dqEl|MGM9M*%P#Z#D0J9T*TW8$jj9-?{sbe`-BebUPdn;>~K4 zI?eQiF>Bx$;E&IbZ{Gcj^~sZ6TS3i)zm81bQX!n=eg@v`Bf6x4o=ZPqyhtO3s)`A+ zni5E2fC=;wJnTyg)5*k^J^K+8rI~kaslbb8tyGjk;Zaz~J&DFb#cd{YvtBad74^F^ z+;h#L2WbwK<;~xOpyHUOT$U>=ika3*FG