Add isFinite flag to OpenSSLBIOInputStream.

The BIO created by OpenSSLBIOInputStream currently returns -1 and sets
the retry flag when read() returns zero on the underlying InputStream.
This is correct for “infinite” streams (like a socket), but isn't
correct for streams that have a definitive EOF.

This change adds a flag to OpenSSLBIOInputStream so that cases where the
input is finite (i.e. when parsing a PKCS#7 or X.509 block) can
correctly return 0 at EOF from |BIO_read|.

(cherry picked from commit 66537ee0121bdd14737191d14927da223f0809ee)

Bug: 21396526
Bug: 21209493
Change-Id: Iaad5845621ab8b89b42d5d3ca8e67e297278ca55
diff --git a/src/main/java/org/conscrypt/NativeCrypto.java b/src/main/java/org/conscrypt/NativeCrypto.java
index a4bc2ce..6267d4b 100644
--- a/src/main/java/org/conscrypt/NativeCrypto.java
+++ b/src/main/java/org/conscrypt/NativeCrypto.java
@@ -559,7 +559,7 @@
 
     // --- BIO stream creation -------------------------------------------------
 
-    public static native long create_BIO_InputStream(OpenSSLBIOInputStream is);
+    public static native long create_BIO_InputStream(OpenSSLBIOInputStream is, boolean isFinite);
 
     public static native long create_BIO_OutputStream(OutputStream os);
 
diff --git a/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java b/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
index cb5d0ca..cf220a6 100644
--- a/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
+++ b/src/main/java/org/conscrypt/OpenSSLBIOInputStream.java
@@ -28,10 +28,10 @@
 public class OpenSSLBIOInputStream extends FilterInputStream {
     private long ctx;
 
-    public OpenSSLBIOInputStream(InputStream is) {
+    public OpenSSLBIOInputStream(InputStream is, boolean isFinite) {
         super(is);
 
-        ctx = NativeCrypto.create_BIO_InputStream(this);
+        ctx = NativeCrypto.create_BIO_InputStream(this, isFinite);
     }
 
     public long getBioContext() {
diff --git a/src/main/java/org/conscrypt/OpenSSLBIOSource.java b/src/main/java/org/conscrypt/OpenSSLBIOSource.java
index 61e966b..7bf2c3c 100644
--- a/src/main/java/org/conscrypt/OpenSSLBIOSource.java
+++ b/src/main/java/org/conscrypt/OpenSSLBIOSource.java
@@ -24,7 +24,8 @@
     private OpenSSLBIOInputStream source;
 
     public static OpenSSLBIOSource wrap(ByteBuffer buffer) {
-        return new OpenSSLBIOSource(new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer)));
+        return new OpenSSLBIOSource(
+            new OpenSSLBIOInputStream(new ByteBufferInputStream(buffer), false));
     }
 
     public OpenSSLBIOSource(OpenSSLBIOInputStream source) {
diff --git a/src/main/java/org/conscrypt/OpenSSLX509CRL.java b/src/main/java/org/conscrypt/OpenSSLX509CRL.java
index 3697b11..a063536 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509CRL.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509CRL.java
@@ -52,7 +52,7 @@
     }
 
     public static OpenSSLX509CRL fromX509DerInputStream(InputStream is) throws ParsingException {
-        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         try {
             final long crlCtx = NativeCrypto.d2i_X509_CRL_bio(bis.getBioContext());
@@ -69,7 +69,7 @@
 
     public static List<OpenSSLX509CRL> fromPkcs7DerInputStream(InputStream is)
             throws ParsingException {
-        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         final long[] certRefs;
         try {
@@ -91,7 +91,7 @@
     }
 
     public static OpenSSLX509CRL fromX509PemInputStream(InputStream is) throws ParsingException {
-        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         try {
             final long crlCtx = NativeCrypto.PEM_read_bio_X509_CRL(bis.getBioContext());
@@ -108,7 +108,7 @@
 
     public static List<OpenSSLX509CRL> fromPkcs7PemInputStream(InputStream is)
             throws ParsingException {
-        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         final long[] certRefs;
         try {
diff --git a/src/main/java/org/conscrypt/OpenSSLX509CertPath.java b/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
index 6cea051..7663101 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509CertPath.java
@@ -136,7 +136,7 @@
     }
 
     private static CertPath fromPkiPathEncoding(InputStream inStream) throws CertificateException {
-        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(inStream);
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(inStream, true);
 
         final boolean markable = inStream.markSupported();
         if (markable) {
diff --git a/src/main/java/org/conscrypt/OpenSSLX509Certificate.java b/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
index abd1ef2..aed141d 100644
--- a/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
+++ b/src/main/java/org/conscrypt/OpenSSLX509Certificate.java
@@ -61,7 +61,7 @@
     public static OpenSSLX509Certificate fromX509DerInputStream(InputStream is)
             throws ParsingException {
         @SuppressWarnings("resource")
-        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         try {
             final long certCtx = NativeCrypto.d2i_X509_bio(bis.getBioContext());
@@ -87,7 +87,7 @@
     public static List<OpenSSLX509Certificate> fromPkcs7DerInputStream(InputStream is)
             throws ParsingException {
         @SuppressWarnings("resource")
-        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         final long[] certRefs;
         try {
@@ -116,7 +116,7 @@
     public static OpenSSLX509Certificate fromX509PemInputStream(InputStream is)
             throws ParsingException {
         @SuppressWarnings("resource")
-        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        final OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         try {
             final long certCtx = NativeCrypto.PEM_read_bio_X509(bis.getBioContext());
@@ -134,7 +134,7 @@
     public static List<OpenSSLX509Certificate> fromPkcs7PemInputStream(InputStream is)
             throws ParsingException {
         @SuppressWarnings("resource")
-        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
 
         final long[] certRefs;
         try {
diff --git a/src/main/native/org_conscrypt_NativeCrypto.cpp b/src/main/native/org_conscrypt_NativeCrypto.cpp
index 9cb0638..c7ffdd6 100644
--- a/src/main/native/org_conscrypt_NativeCrypto.cpp
+++ b/src/main/native/org_conscrypt_NativeCrypto.cpp
@@ -1261,8 +1261,9 @@
 
 class BIO_InputStream : public BIO_Stream {
 public:
-    BIO_InputStream(jobject stream) :
-            BIO_Stream(stream) {
+    BIO_InputStream(jobject stream, bool isFinite) :
+            BIO_Stream(stream),
+            isFinite_(isFinite) {
     }
 
     int read(char *buf, int len) {
@@ -1280,7 +1281,13 @@
         return read;
     }
 
+    bool isFinite() const {
+        return isFinite_;
+    }
+
 private:
+    const bool isFinite_;
+
     int read_internal(char *buf, int len, jmethodID method) {
         JNIEnv* env = getJNIEnv();
         if (env == NULL) {
@@ -1385,7 +1392,11 @@
     BIO_InputStream* stream = static_cast<BIO_InputStream*>(b->ptr);
     int ret = stream->read(buf, len);
     if (ret == 0) {
-        // EOF is indicated by -1 with a BIO flag.
+        if (stream->isFinite()) {
+            return 0;
+        }
+        // If the BIO_InputStream is not finite then EOF doesn't mean that
+        // there's nothing more coming.
         BIO_set_retry_read(b);
         return -1;
     }
@@ -5304,7 +5315,9 @@
     return env->NewStringUTF(output);
 }
 
-static jlong NativeCrypto_create_BIO_InputStream(JNIEnv* env, jclass, jobject streamObj) {
+static jlong NativeCrypto_create_BIO_InputStream(JNIEnv* env, jclass,
+                                                 jobject streamObj,
+                                                 jboolean isFinite) {
     JNI_TRACE("create_BIO_InputStream(%p)", streamObj);
 
     if (streamObj == NULL) {
@@ -5317,7 +5330,7 @@
         return 0;
     }
 
-    bio_stream_assign(bio.get(), new BIO_InputStream(streamObj));
+    bio_stream_assign(bio.get(), new BIO_InputStream(streamObj, isFinite));
 
     JNI_TRACE("create_BIO_InputStream(%p) => %p", streamObj, bio.get());
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(bio.release()));
@@ -10620,7 +10633,7 @@
     NATIVE_METHOD(NativeCrypto, OBJ_txt2nid, "(Ljava/lang/String;)I"),
     NATIVE_METHOD(NativeCrypto, OBJ_txt2nid_longName, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(NativeCrypto, OBJ_txt2nid_oid, "(Ljava/lang/String;)Ljava/lang/String;"),
-    NATIVE_METHOD(NativeCrypto, create_BIO_InputStream, ("(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream;)J")),
+    NATIVE_METHOD(NativeCrypto, create_BIO_InputStream, ("(L" TO_STRING(JNI_JARJAR_PREFIX) "org/conscrypt/OpenSSLBIOInputStream;Z)J")),
     NATIVE_METHOD(NativeCrypto, create_BIO_OutputStream, "(Ljava/io/OutputStream;)J"),
     NATIVE_METHOD(NativeCrypto, BIO_read, "(J[B)I"),
     NATIVE_METHOD(NativeCrypto, BIO_write, "(J[BII)V"),
diff --git a/src/test/java/org/conscrypt/NativeCryptoTest.java b/src/test/java/org/conscrypt/NativeCryptoTest.java
index ccad696..9e47e54 100644
--- a/src/test/java/org/conscrypt/NativeCryptoTest.java
+++ b/src/test/java/org/conscrypt/NativeCryptoTest.java
@@ -2824,7 +2824,7 @@
         ByteArrayInputStream is = new ByteArrayInputStream(actual);
 
         @SuppressWarnings("resource")
-        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is);
+        OpenSSLBIOInputStream bis = new OpenSSLBIOInputStream(is, true);
         try {
             byte[] buffer = new byte[1024];
             int numRead = NativeCrypto.BIO_read(bis.getBioContext(), buffer);