Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow skip in SignatureValidatingInputStream #42

Open
wants to merge 31 commits into
base: master
Choose a base branch
from

Conversation

unverbraucht
Copy link

InputStream has a default implementation of skip() that just reads byte by byte. It's not particularly efficient but will work. I want to read a tar archive from a SignatureValidatingInputStream, and the library will call skip() to gloss over directory entries.

With the current implementation I have to write the whole stream to a file on storage, unarchive it and then delete it again.

BTW hard to say how much headache you saved me from with this library, deeply appreciated. BC is arcane.

@unverbraucht
Copy link
Author

Oh, totally forgot: I put my code under the License.

@neuhalje
Copy link
Owner

neuhalje commented Mar 7, 2020

Thank you for the flowers :-) Yeah, BC is one of the more interesting libraries. Since I am right now a bit busy -- could you write a unit test that verifies that skip works as expected? Much appreciated!

@unverbraucht
Copy link
Author

Sure :)
So this is interesting - it works for me in production but the unit test fails (!). I get this stack trace:

java.io.IOException: Signature verification failed because all of the following signatures (by userId) are missing.

	at name.neuhalfen.projects.crypto.bouncycastle.openpgp.decrypting.SignatureValidatingInputStream.validateSignature(SignatureValidatingInputStream.java:83)
	at name.neuhalfen.projects.crypto.bouncycastle.openpgp.decrypting.SignatureValidatingInputStream.read(SignatureValidatingInputStream.java:66)
	at org.bouncycastle.util.io.Streams.pipeAll(Unknown Source)
	at org.bouncycastle.util.io.Streams.readAll(Unknown Source)
	at name.neuhalfen.projects.crypto.bouncycastle.openpgp.roundtrip.EncryptionDecryptionRoundtripIntegrationTest.encryptAndSignArmored_thenDecryptAndVerifyWithSkip_yieldsOriginalPlaintext(EncryptionDecryptionRoundtripIntegrationTest.java:127)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:27)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: name.neuhalfen.projects.crypto.bouncycastle.openpgp.validation.SignaturesMissingException: Signature verification failed because all of the following signatures (by userId) are missing.
	at name.neuhalfen.projects.crypto.bouncycastle.openpgp.validation.RequireSpecificSignatureValidationForUserIdsStrategy.createMissingSignaturesException(RequireSpecificSignatureValidationForUserIdsStrategy.java:99)
	at name.neuhalfen.projects.crypto.bouncycastle.openpgp.validation.RequireSpecificSignatureValidationForUserIdsStrategy.validateSignatures(RequireSpecificSignatureValidationForUserIdsStrategy.java:77)
	at name.neuhalfen.projects.crypto.bouncycastle.openpgp.decrypting.SignatureValidatingInputStream.validateSignature(SignatureValidatingInputStream.java:81)
	... 38 more

I'm not sure if I understood the test setup correctly but it definitely is caused by using skip on the decrypted IS. I'll leave this then for now and look into migrating my code away from using skip, maybe storing it on disk first. If you could have a look at the test to see if nothings wrong with it then this can be closed and rejected :)

@neuhalje
Copy link
Owner

sorry for keeping you waiting $DAY_JOB ...

@ispringer
Copy link
Contributor

Hi @unverbraucht, I think the following skip implementation (not tested!) will fix the issue you are seeing:

// NOTE: We cannot simply delegate to super.skip, since we need to ensure our own read 
//       impl, which updates the one-pass signatures, is used to read the bytes being 
//       skipped.
@Override
public long skip(long n) throws IOException {
    if (n <= 0) {
        return 0;
    }

    // buffer to be reused repeatedly 
    final byte[] buffer = new byte[(int) Math.min(4096, n)];

    long remaining = n;
    while (remaining > 0) {
        final int read = read(buffer, 0, (int) Math.min(buffer.length, remaining));
        final boolean endOfStream = read == -1;
        if (endOfStream) {
            break;
        }
        remaining -= read;
    }

    return n - remaining;
}

ispringer and others added 21 commits April 2, 2020 10:58
…e, as we should not close a stream that is going to be wrapped by additional streams, which haven't even been created yet; instead the wrapping streams should be responsible for closing the streams they wrap once they are fully done with them; all tests pass
…ption if called on an older public key with no hashed subpackets (fixes neuhalje#48); make KeyFlag#fromInteger return an immutable Set
Bouncy Castle to 1.65
- Created a wrapping MDC validating stream
- It confirms data integrity if MDC packet is present
- Added a test case to confirm exception if data is violated
For whatever reasons travis was not testing
* Sauhardstark-master:
  Added comments regarding the bit changed (595th)
  Added accidentally removed NOMPD tag
  Updated minor comments
  Removed unneccessary comments
  Added the MDC verification if present - Created a wrapping MDC validating stream - It confirms data integrity if MDC packet is present - Added a test case to confirm exception if data is violated
* Neustradamus-bouncycastle:
  Bouncy Castle to 1.65
…mdesmons-master

* 'master' of https://github.com/mdesmons/bouncy-gpg:
  added comment to extractPublicKeyFlags
  Fix for issue neuhalje#50, where we cannot encrypt if a key doesn't have a KeyFlags subpacket
* mdesmons-master:
  added comment to extractPublicKeyFlags
  Fix for issue neuhalje#50, where we cannot encrypt if a key doesn't have a KeyFlags subpacket
…to ispringer-issue-48

* 'issue-48' of https://github.com/ispringer/bouncy-gpg:
  fix so KeyFlag#extractPublicKeyFlags does not throw a NullPointerException if called on an older public key with no hashed subpackets (fixes neuhalje#48); make KeyFlag#fromInteger return an immutable Set
* ispringer-issue-48:
  fix so KeyFlag#extractPublicKeyFlags does not throw a NullPointerException if called on an older public key with no hashed subpackets (fixes neuhalje#48); make KeyFlag#fromInteger return an immutable Set
@neuhalje
Copy link
Owner

ping :-)

neuhalje and others added 8 commits July 12, 2020 21:26
…to ispringer-issue-46

* 'issue-46' of https://github.com/ispringer/bouncy-gpg:
  fixes neuhalje#46 by reverting 012c3f9; that commit did not make sense, as we should not close a stream that is going to be wrapped by additional streams, which haven't even been created yet; instead the wrapping streams should be responsible for closing the streams they wrap once they are fully done with them; all tests pass
* ispringer-issue-46:
  fixes neuhalje#46 by reverting 012c3f9; that commit did not make sense, as we should not close a stream that is going to be wrapped by additional streams, which haven't even been created yet; instead the wrapping streams should be responsible for closing the streams they wrap once they are fully done with them; all tests pass
Starting with BouncyCastle 1.66 the validity of this key is correctly calculated. This lead the test to fail.
@unverbraucht
Copy link
Author

Hey, sorry for dropping the ball on this. @ispringer your implementation seems to work great, thanks. All unit tests pass. @neuhalje please review when you find the time :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants