Repository: incubator-gobblin
Updated Branches:
  refs/heads/master c36f2c87b -> fc389522a


[GOBBLIN-293] Remove stream materialization in GPGFileDecryptor

Closes #2333 from htran1/gpg_memory


Project: http://git-wip-us.apache.org/repos/asf/incubator-gobblin/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-gobblin/commit/fc389522
Tree: http://git-wip-us.apache.org/repos/asf/incubator-gobblin/tree/fc389522
Diff: http://git-wip-us.apache.org/repos/asf/incubator-gobblin/diff/fc389522

Branch: refs/heads/master
Commit: fc389522a6cfb15965f10480a21804fca6660605
Parents: c36f2c8
Author: Hung Tran <[email protected]>
Authored: Mon Apr 16 11:24:20 2018 -0700
Committer: Hung Tran <[email protected]>
Committed: Mon Apr 16 11:24:20 2018 -0700

----------------------------------------------------------------------
 .../apache/gobblin/crypto/GPGFileDecryptor.java | 155 ++++++++++++-------
 .../gobblin/crypto/GPGFileDecryptorTest.java    |  75 ++++++++-
 .../test/resources/crypto/gpg/keyEncrypted.gpg  | Bin 0 -> 1483 bytes
 .../resources/crypto/gpg/passwordEncrypted.gpg  | Bin 0 -> 1198 bytes
 .../test/resources/crypto/gpg/testPrivate.key   |  59 +++++++
 .../test/resources/crypto/gpg/testPublic.key    |  30 ++++
 6 files changed, 260 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-gobblin/blob/fc389522/gobblin-modules/gobblin-crypto/src/main/java/org/apache/gobblin/crypto/GPGFileDecryptor.java
----------------------------------------------------------------------
diff --git 
a/gobblin-modules/gobblin-crypto/src/main/java/org/apache/gobblin/crypto/GPGFileDecryptor.java
 
b/gobblin-modules/gobblin-crypto/src/main/java/org/apache/gobblin/crypto/GPGFileDecryptor.java
index 7d62439..399b3e8 100644
--- 
a/gobblin-modules/gobblin-crypto/src/main/java/org/apache/gobblin/crypto/GPGFileDecryptor.java
+++ 
b/gobblin-modules/gobblin-crypto/src/main/java/org/apache/gobblin/crypto/GPGFileDecryptor.java
@@ -16,15 +16,11 @@
  */
 package org.apache.gobblin.crypto;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.Security;
-
 import java.util.Iterator;
-import lombok.SneakyThrows;
-import lombok.experimental.UtilityClass;
+
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openpgp.PGPCompressedData;
 import org.bouncycastle.openpgp.PGPEncryptedDataList;
@@ -44,8 +40,8 @@ import 
org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBu
 import 
org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
 import 
org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
 import 
org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
-import org.bouncycastle.util.io.Streams;
 
+import lombok.experimental.UtilityClass;
 
 /**
  * A utility class that decrypts both password based and key based encryption 
files.
@@ -67,23 +63,16 @@ public class GPGFileDecryptor {
 
     PGPEncryptedDataList enc = getPGPEncryptedDataList(inputStream);
     PGPPBEEncryptedData pbe = (PGPPBEEncryptedData) enc.get(0);
-
     InputStream clear;
+
     try {
       clear = pbe.getDataStream(new JcePBEDataDecryptorFactoryBuilder(
           new 
JcaPGPDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build())
               
.setProvider(BouncyCastleProvider.PROVIDER_NAME).build(passPhrase.toCharArray()));
 
       JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear);
-      Object pgpfObject = pgpFact.nextObject();
-      if (pgpfObject instanceof PGPCompressedData) {
-        PGPCompressedData cData = (PGPCompressedData) pgpfObject;
-        pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
-        pgpfObject = pgpFact.nextObject();
-      }
 
-      PGPLiteralData ld = (PGPLiteralData) pgpfObject;
-      return ld.getInputStream();
+      return new LazyMaterializeDecryptorInputStream(pgpFact);
     } catch (PGPException e) {
       throw new IOException(e);
     }
@@ -97,55 +86,31 @@ public class GPGFileDecryptor {
    * @return
    * @throws IOException
    */
-  @SneakyThrows (PGPException.class)
   public InputStream decryptFile(InputStream inputStream, InputStream keyIn, 
String passPhrase)
       throws IOException {
+    try {
+      PGPEncryptedDataList enc = getPGPEncryptedDataList(inputStream);
+      Iterator it = enc.getEncryptedDataObjects();
+      PGPPrivateKey sKey = null;
+      PGPPublicKeyEncryptedData pbe = null;
+      PGPSecretKeyRingCollection pgpSec = new 
PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(keyIn), new 
BcKeyFingerprintCalculator());
+
+      while (sKey == null && it.hasNext()) {
+        pbe = (PGPPublicKeyEncryptedData) it.next();
+        sKey = findSecretKey(pgpSec, pbe.getKeyID(), passPhrase);
+      }
 
-    PGPEncryptedDataList enc = getPGPEncryptedDataList(inputStream);
-    Iterator it = enc.getEncryptedDataObjects();
-    PGPPrivateKey sKey = null;
-    PGPPublicKeyEncryptedData pbe =null;
-    PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
-        PGPUtil.getDecoderStream(keyIn), new BcKeyFingerprintCalculator());
-
-    while(sKey == null && it.hasNext()) {
-      pbe = (PGPPublicKeyEncryptedData)it.next();
-      sKey = findSecretKey(pgpSec, pbe.getKeyID(), passPhrase);
-    }
-
-    if (sKey == null) {
-      throw new IllegalArgumentException("secret key for message not found.");
-    }
-
-    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-
-    try (InputStream clear = pbe.getDataStream(
-        new 
JcePublicKeyDataDecryptorFactoryBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(sKey)))
 {
+      if (sKey == null) {
+        throw new IllegalArgumentException("secret key for message not 
found.");
+      }
 
+      InputStream clear = pbe.getDataStream(
+          new 
JcePublicKeyDataDecryptorFactoryBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(sKey));
       JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(clear);
-      Object pgpfObject = pgpFact.nextObject();
-
-      while (pgpfObject != null) {
-        if (pgpfObject instanceof PGPCompressedData) {
-          PGPCompressedData cData = (PGPCompressedData) pgpfObject;
-          pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
-          pgpfObject = pgpFact.nextObject();
-        }
 
-        if (pgpfObject instanceof PGPLiteralData) {
-          Streams.pipeAll(((PGPLiteralData) pgpfObject).getInputStream(), 
outputStream);
-        } else if (pgpfObject instanceof PGPOnePassSignatureList) {
-          throw new PGPException("encrypted message contains 
PGPOnePassSignatureList message - not literal data.");
-        } else if (pgpfObject instanceof PGPSignatureList) {
-          throw new PGPException("encrypted message contains PGPSignatureList 
message - not literal data.");
-        } else {
-          throw new PGPException("message is not a simple encrypted file - 
type unknown.");
-        }
-        pgpfObject = pgpFact.nextObject();
-      }
-      return new ByteArrayInputStream(outputStream.toByteArray());
-    } finally {
-      outputStream.close();
+      return new LazyMaterializeDecryptorInputStream(pgpFact);
+    } catch (PGPException e) {
+      throw new IOException(e);
     }
   }
 
@@ -191,4 +156,78 @@ public class GPGFileDecryptor {
     }
     return enc;
   }
+
+  /**
+   * A class for reading the underlying {@link InputStream}s from the pgp 
object without pre-materializing all of them.
+   * The PGP object may present the decrypted data through multiple {@link 
InputStream}s, but these streams are sequential
+   * and the n+1 stream is not available until the end of the nth stream is 
reached, so the
+   * {@link LazyMaterializeDecryptorInputStream} keeps a reference to the 
{@link JcaPGPObjectFactory} and moves to new
+   * {@link InputStream}s as they are available
+   */
+  private static class LazyMaterializeDecryptorInputStream extends InputStream 
{
+    JcaPGPObjectFactory pgpFact;
+    InputStream currentUnderlyingStream;
+
+    public LazyMaterializeDecryptorInputStream(JcaPGPObjectFactory pgpFact)
+        throws IOException {
+      this.pgpFact = pgpFact;
+
+      moveToNextInputStream();
+    }
+
+    @Override
+    public int read()
+        throws IOException {
+      int value = this.currentUnderlyingStream.read();
+
+      if (value != -1) {
+        return value;
+      } else {
+        moveToNextInputStream();
+
+        if (this.currentUnderlyingStream == null) {
+          return -1;
+        }
+
+        return this.currentUnderlyingStream.read();
+      }
+    }
+
+    /**
+     * Move to the next {@link InputStream} if available, otherwise set {@link 
#currentUnderlyingStream} to null to
+     * indicate that there is no more data.
+     * @throws IOException
+     */
+    private void moveToNextInputStream() throws IOException {
+      Object pgpfObject = this.pgpFact.nextObject();
+
+      // no more data
+      if (pgpfObject == null) {
+        this.currentUnderlyingStream = null;
+        return;
+      }
+
+      if (pgpfObject instanceof PGPCompressedData) {
+        PGPCompressedData cData = (PGPCompressedData) pgpfObject;
+
+        try {
+          this.pgpFact = new JcaPGPObjectFactory(cData.getDataStream());
+        } catch (PGPException e) {
+          throw new IOException("Could not get the PGP data stream", e);
+        }
+
+        pgpfObject = this.pgpFact.nextObject();
+      }
+
+      if (pgpfObject instanceof PGPLiteralData) {
+        this.currentUnderlyingStream = ((PGPLiteralData) 
pgpfObject).getInputStream();
+      } else if (pgpfObject instanceof PGPOnePassSignatureList) {
+        throw new IOException("encrypted message contains 
PGPOnePassSignatureList message - not literal data.");
+      } else if (pgpfObject instanceof PGPSignatureList) {
+        throw new IOException("encrypted message contains PGPSignatureList 
message - not literal data.");
+      } else {
+        throw new IOException("message is not a simple encrypted file - type 
unknown.");
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-gobblin/blob/fc389522/gobblin-modules/gobblin-crypto/src/test/java/org/apache/gobblin/crypto/GPGFileDecryptorTest.java
----------------------------------------------------------------------
diff --git 
a/gobblin-modules/gobblin-crypto/src/test/java/org/apache/gobblin/crypto/GPGFileDecryptorTest.java
 
b/gobblin-modules/gobblin-crypto/src/test/java/org/apache/gobblin/crypto/GPGFileDecryptorTest.java
index f0ed6ac..964bebb 100644
--- 
a/gobblin-modules/gobblin-crypto/src/test/java/org/apache/gobblin/crypto/GPGFileDecryptorTest.java
+++ 
b/gobblin-modules/gobblin-crypto/src/test/java/org/apache/gobblin/crypto/GPGFileDecryptorTest.java
@@ -16,15 +16,18 @@
  */
 package org.apache.gobblin.crypto;
 
-import com.google.common.base.Charsets;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
+import org.bouncycastle.openpgp.PGPException;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
+import com.google.common.base.Charsets;
+
 
 /**
  * Test class for {@link GPGFileDecryptor}
@@ -58,4 +61,74 @@ public class GPGFileDecryptorTest {
     }
   }
 
+  /**
+   * Decrypt a large (~1gb) password encrypted file and check that memory 
usage does not blow up
+   * @throws IOException
+   * @throws PGPException
+   */
+  @Test (enabled=true)
+  public void decryptLargeFileSym() throws IOException, PGPException {
+    System.gc();
+    System.gc();
+
+    long startHeapSize = Runtime.getRuntime().totalMemory();
+
+    try(InputStream is = GPGFileDecryptor.decryptFile(
+        getClass().getResourceAsStream("/crypto/gpg/passwordEncrypted.gpg"), 
"test")) {
+      int value;
+      long bytesRead = 0;
+
+      // the file contains only the character 'a'
+      while ((value = is.read()) != -1) {
+        bytesRead++;
+        Assert.assertTrue(value == 'a');
+      }
+
+      Assert.assertEquals(bytesRead, 1041981183L);
+
+      System.gc();
+      System.gc();
+      long endHeapSize = Runtime.getRuntime().totalMemory();
+
+      // make sure the heap doesn't grow too much
+      Assert.assertTrue(endHeapSize - startHeapSize < 200 * 1024 * 1024,
+          "start heap " + startHeapSize + " end heap " + endHeapSize);
+    }
+  }
+
+  /**
+   * Decrypt a large (~1gb) private key encrypted file and check that memory 
usage does not blow up
+   * @throws IOException
+   * @throws PGPException
+   */
+  @Test (enabled=true)
+  public void decryptLargeFileAsym() throws IOException, PGPException {
+    System.gc();
+    System.gc();
+
+    long startHeapSize = Runtime.getRuntime().totalMemory();
+
+    try(InputStream is = GPGFileDecryptor.decryptFile(
+        getClass().getResourceAsStream("/crypto/gpg/keyEncrypted.gpg"),
+        getClass().getResourceAsStream("/crypto/gpg/testPrivate.key"), 
"gobblin")) {
+      int value;
+      long bytesRead = 0;
+
+      // the file contains only the character 'a'
+      while ((value = is.read()) != -1) {
+        bytesRead++;
+        Assert.assertTrue(value == 'a');
+      }
+
+      Assert.assertEquals(bytesRead, 1041981183L);
+
+      System.gc();
+      System.gc();
+      long endHeapSize = Runtime.getRuntime().totalMemory();
+
+      // make sure the heap doesn't grow too much
+      Assert.assertTrue(endHeapSize - startHeapSize < 200 * 1024 * 1024,
+          "start heap " + startHeapSize + " end heap " + endHeapSize);
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-gobblin/blob/fc389522/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/keyEncrypted.gpg
----------------------------------------------------------------------
diff --git 
a/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/keyEncrypted.gpg 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/keyEncrypted.gpg
new file mode 100644
index 0000000..cb88a58
Binary files /dev/null and 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/keyEncrypted.gpg 
differ

http://git-wip-us.apache.org/repos/asf/incubator-gobblin/blob/fc389522/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/passwordEncrypted.gpg
----------------------------------------------------------------------
diff --git 
a/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/passwordEncrypted.gpg
 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/passwordEncrypted.gpg
new file mode 100644
index 0000000..c017a35
Binary files /dev/null and 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/passwordEncrypted.gpg
 differ

http://git-wip-us.apache.org/repos/asf/incubator-gobblin/blob/fc389522/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPrivate.key
----------------------------------------------------------------------
diff --git 
a/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPrivate.key 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPrivate.key
new file mode 100644
index 0000000..530e2bf
--- /dev/null
+++ 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPrivate.key
@@ -0,0 +1,59 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2.0.14 (GNU/Linux)
+
+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
+=27va
+-----END PGP PRIVATE KEY BLOCK-----

http://git-wip-us.apache.org/repos/asf/incubator-gobblin/blob/fc389522/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPublic.key
----------------------------------------------------------------------
diff --git 
a/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPublic.key 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPublic.key
new file mode 100644
index 0000000..ee68362
--- /dev/null
+++ 
b/gobblin-modules/gobblin-crypto/src/test/resources/crypto/gpg/testPublic.key
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.14 (GNU/Linux)
+
+mQENBFrGokcBCAC7nwmzuuSZiWj03+mlcRuvQHCX0WEnOHxssgBXO30TEn4SeIoi
+fdgsQzVKpUvLdJK6IowQ/uYOO1llG/5EkFZKYBV91+MXuaauo5cpmN7VaFL5Cex1
+5wT2cQq1QUWIsJb0sssnHYlsMU6gNTL/vg5wqtVjK2wvbi2K3q+d3Azph7dYHvUE
+VB0IFFdaBweZHY9uMgtOy3EMB+LuNoRAwkh7go8UJNShI2xCFx/dKcHddRXDax3u
+vXasxnp+bUawFCNx55l0vpsMCmeWCWrLEaSeKKAPEq/cyDFQkOoUSlqsb7LsIT1u
+v6qgOc3Dfex7dwSJyJ/5UvYGyP64jJ0rB7wDABEBAAG0MEdvYmJsaW4gVGVzdCAo
+Z29iYmxpbikgPGdvYmJsaW5AZ29iYmxpbi5nb2JibGluPokBOAQTAQIAIgUCWsai
+RwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQwnCTyiGofW8PEgf+NXJD
+20j/ornWKPmDG7/duwDnSvKbOCCG8/u/WAm7q+mJ9nFSBhY/jKpy1VwtT0h7hy/I
+f0qI8tiXzfE6aX2njIkUWTRj9DipB3I2SLz8d+ysGMTSa0gdmF2tX7hXQAh/gf8+
+GUR1HBIb9vvDHF9EHngP8kWlRvdiget0U77Z+FDRP+qOej1XYgxB6iCw4uI8dcNo
+yghA0weY/1u/GEMoe8aI4mNsOWxAsEybu5q1O3TWkstkm2TQtEgZD6NkWQe5x69s
+vun1oUq12s//7NdbGGfNbwYW4cm9PLmlNBapldfqjP9zmbbf4Y/Cm6ump0KQdeJN
+s4tFWvxeDdWHu6EdEbkBDQRaxqJHAQgAxAnoam2a/drd62+1TXbLJKUmAWaEoWzE
+onqrh0/L+v7CEluNPRArTCm7B54IcolYUJWejAFRRJaLz2zGBndwX4eyZvE2qWSe
+ujZ14Ja2SXR8ZcHXpsPd5TdPixYGKgge1zNZWUEV5ncXWqjKCwISTuxoh0hQu3YJ
+kolquZHJAs8LH3aKTZ9GUkyyS61rMKAarRIjr/+yURIasPPtvdnllYUf9bDOdNdT
+UFHdXwVG16r2/qLL8RZDXaNlKJ5xqYvKo4TdJQj1Cq4dcyNwQntrFU5JJfETaQAD
+ZMa15NSReoToKSMTX+IJ4Bsn8LGG3Qv42qWyBcubq0lM1tnyxmesQQARAQABiQEf
+BBgBAgAJBQJaxqJHAhsMAAoJEMJwk8ohqH1vVzoH/j9b9VK/Sb6CW/SP5+4Xe2Rq
+Ly9yilLJqSFWI27xRmCmvGMUm0sbFR/deitCW05e+BKIoO5yyOe8zAEpr1j+d4TJ
+CqYSyw/9oGLVjAHJnid4SO7uDjGvTInIy8oardvPj8675Ud+H0OO7upWLKqvbrQf
+WzxtS31geiUk2YKcB9dGn6MB3cZoLxjjlWSPkyuL3qbayCa0+Cz6uoKa+93Tt64S
+JamGqZlvmytw1E8y4Tx/7QiNs/7qaVqrYTsBj5tmd7ESmIbEaBPxSQXAR/9zYKrZ
+AEXotsW7COeATq1dC02PftnPugkH6Ue+NY9n8Ltm3+bNCZWOHJSatKGlvB4ODtI=
+=02M+
+-----END PGP PUBLIC KEY BLOCK-----

Reply via email to