Gracefully handle DSA objects with missing fields

OpenSSL's APIs are full of empty objects that aren't captured in the
type system. A DSA object may represent any of nothing, a group, a
public key, or a private key.

Since the type system doesn't capture this, better to check which type
we've got and NULL-check the fields we need for the operation.

Change-Id: I32b09ebaad58fcb2a0bfc9ad56d381f95099bf7b
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57225
Reviewed-by: Adam Langley <agl@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/dsa/dsa.c b/crypto/dsa/dsa.c
index 4d9b1f0..0418e09 100644
--- a/crypto/dsa/dsa.c
+++ b/crypto/dsa/dsa.c
@@ -592,6 +592,11 @@
     return NULL;
   }
 
+  if (dsa->priv_key == NULL) {
+    OPENSSL_PUT_ERROR(DSA, DSA_R_MISSING_PARAMETERS);
+    return NULL;
+  }
+
   BIGNUM *kinv = NULL, *r = NULL, *s = NULL;
   BIGNUM m;
   BIGNUM xr;
@@ -687,6 +692,11 @@
     return 0;
   }
 
+  if (dsa->pub_key == NULL) {
+    OPENSSL_PUT_ERROR(DSA, DSA_R_MISSING_PARAMETERS);
+    return 0;
+  }
+
   int ret = 0;
   BIGNUM u1, u2, t1;
   BN_init(&u1);
@@ -842,6 +852,10 @@
 }
 
 int DSA_size(const DSA *dsa) {
+  if (dsa->q == NULL) {
+    return 0;
+  }
+
   size_t order_len = BN_num_bytes(dsa->q);
   // Compute the maximum length of an |order_len| byte integer. Defensively
   // assume that the leading 0x00 is included.
@@ -864,11 +878,6 @@
 
 static int dsa_sign_setup(const DSA *dsa, BN_CTX *ctx, BIGNUM **out_kinv,
                           BIGNUM **out_r) {
-  if (!dsa->p || !dsa->q || !dsa->g) {
-    OPENSSL_PUT_ERROR(DSA, DSA_R_MISSING_PARAMETERS);
-    return 0;
-  }
-
   int ret = 0;
   BIGNUM k;
   BN_init(&k);
diff --git a/crypto/dsa/dsa_test.cc b/crypto/dsa/dsa_test.cc
index dc1956e..878e67d 100644
--- a/crypto/dsa/dsa_test.cc
+++ b/crypto/dsa/dsa_test.cc
@@ -169,7 +169,7 @@
     0xdc, 0xd8, 0xc8,
 };
 
-static bssl::UniquePtr<DSA> GetFIPSDSA(void) {
+static bssl::UniquePtr<DSA> GetFIPSDSAGroup(void) {
   bssl::UniquePtr<DSA> dsa(DSA_new());
   if (!dsa) {
     return nullptr;
@@ -184,6 +184,14 @@
   p.release();
   q.release();
   g.release();
+  return dsa;
+}
+
+static bssl::UniquePtr<DSA> GetFIPSDSA(void) {
+  bssl::UniquePtr<DSA> dsa = GetFIPSDSAGroup();
+  if (!dsa) {
+    return nullptr;
+  }
   bssl::UniquePtr<BIGNUM> pub_key(BN_bin2bn(fips_y, sizeof(fips_y), nullptr));
   bssl::UniquePtr<BIGNUM> priv_key(BN_bin2bn(fips_x, sizeof(fips_x), nullptr));
   if (!pub_key || !priv_key ||
@@ -259,3 +267,35 @@
   EXPECT_EQ(ERR_LIB_DSA, ERR_GET_LIB(err));
   EXPECT_EQ(DSA_R_INVALID_PARAMETERS, ERR_GET_REASON(err));
 }
+
+// Signing and verifying should cleanly fail when the DSA object is empty.
+TEST(DSATest, MissingParameters) {
+  bssl::UniquePtr<DSA> dsa(DSA_new());
+  ASSERT_TRUE(dsa);
+  EXPECT_EQ(-1, DSA_verify(0, fips_digest, sizeof(fips_digest), fips_sig,
+                           sizeof(fips_sig), dsa.get()));
+
+  std::vector<uint8_t> sig(DSA_size(dsa.get()));
+  unsigned sig_len;
+  EXPECT_FALSE(DSA_sign(0, fips_digest, sizeof(fips_digest), sig.data(),
+                        &sig_len, dsa.get()));
+}
+
+// Verifying should cleanly fail when the public key is missing.
+TEST(DSATest, MissingPublic) {
+  bssl::UniquePtr<DSA> dsa = GetFIPSDSAGroup();
+  ASSERT_TRUE(dsa);
+  EXPECT_EQ(-1, DSA_verify(0, fips_digest, sizeof(fips_digest), fips_sig,
+                           sizeof(fips_sig), dsa.get()));
+}
+
+// Signing should cleanly fail when the private key is missing.
+TEST(DSATest, MissingPrivate) {
+  bssl::UniquePtr<DSA> dsa = GetFIPSDSAGroup();
+  ASSERT_TRUE(dsa);
+
+  std::vector<uint8_t> sig(DSA_size(dsa.get()));
+  unsigned sig_len;
+  EXPECT_FALSE(DSA_sign(0, fips_digest, sizeof(fips_digest), sig.data(),
+                        &sig_len, dsa.get()));
+}