Fix some more negative zeros and add tests for each case.

See https://github.com/openssl/openssl/pull/1672.

Change-Id: I4c93a568b9b7ce582b03e955d3aa9cb6b0e89794
Reviewed-on: https://boringssl-review.googlesource.com/12314
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc
index efb6bc5..672d83f 100644
--- a/crypto/bn/bn_test.cc
+++ b/crypto/bn/bn_test.cc
@@ -1203,6 +1203,37 @@
     return false;
   }
 
+  // Test that |BN_rshift| and |BN_rshift1| will not produce a negative zero.
+  if (!BN_set_word(a.get(), 1)) {
+    return false;
+  }
+
+  BN_set_negative(a.get(), 1);
+  if (!BN_rshift(b.get(), a.get(), 1) ||
+      !BN_rshift1(c.get(), a.get())) {
+    return false;
+  }
+
+  if (!BN_is_zero(b.get()) || BN_is_negative(b.get())) {
+    fprintf(stderr, "BN_rshift(-1, 1) produced the wrong result.\n");
+    return false;
+  }
+
+  if (!BN_is_zero(c.get()) || BN_is_negative(c.get())) {
+    fprintf(stderr, "BN_rshift1(-1) produced the wrong result.\n");
+    return false;
+  }
+
+  // Test that |BN_div_word| will not produce a negative zero.
+  if (BN_div_word(a.get(), 2) == (BN_ULONG)-1) {
+    return false;
+  }
+
+  if (!BN_is_zero(a.get()) || BN_is_negative(a.get())) {
+    fprintf(stderr, "BN_div_word(-1, 2) produced the wrong result.\n");
+    return false;
+  }
+
   return true;
 }
 
diff --git a/crypto/bn/div.c b/crypto/bn/div.c
index 03577f2..ab49281 100644
--- a/crypto/bn/div.c
+++ b/crypto/bn/div.c
@@ -628,6 +628,10 @@
     a->top--;
   }
 
+  if (a->top == 0) {
+    a->neg = 0;
+  }
+
   ret >>= j;
   return ret;
 }
diff --git a/crypto/bn/shift.c b/crypto/bn/shift.c
index defec92..22006d1 100644
--- a/crypto/bn/shift.c
+++ b/crypto/bn/shift.c
@@ -182,6 +182,10 @@
     }
   }
 
+  if (r->top == 0) {
+    r->neg = 0;
+  }
+
   return 1;
 }
 
@@ -215,6 +219,10 @@
   }
   r->top = j;
 
+  if (r->top == 0) {
+    r->neg = 0;
+  }
+
   return 1;
 }