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

Lower required Android API to 9 and use Animal Sniffer #18

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchProviderException;
import java.time.Instant;
import java.util.Date;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import name.neuhalfen.projects.crypto.bouncycastle.openpgp.decrypting.DecryptionStreamFactory;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeySelectionStrategy;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.Rfc4880KeySelectionStrategy;
Expand All @@ -25,8 +26,7 @@ public final class BuildDecryptionInputStreamAPI {
@Nonnull
private SignatureValidationStrategy signatureCheckingMode;

private KeySelectionStrategy keySelectionStrategy = new Rfc4880KeySelectionStrategy(
Instant.now());
private KeySelectionStrategy keySelectionStrategy = new Rfc4880KeySelectionStrategy(new Date());

/**
* Start building by passing in the keyring config.
Expand All @@ -50,12 +50,11 @@ public final class ValidationWithKeySelectionStrategy extends ValidationImpl {

ValidationWithKeySelectionStrategy() {
super();
BuildDecryptionInputStreamAPI.this.keySelectionStrategy = new Rfc4880KeySelectionStrategy(
Instant.now());
BuildDecryptionInputStreamAPI.this.keySelectionStrategy = new Rfc4880KeySelectionStrategy(new Date());
}

public Validation setReferenceDateForKeyValidityTo(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing public Validation setReferenceDateForKeyValidityTo(Instant) to public Validation setReferenceDateForKeyValidityTo(Date) would break the published API. Although 2.1 is not yet used widely this is not good. Unfortunately Instant got added to android in API Level 26.

The best way would be to loosen your "support (very) old devices" requirement.

Instant dateOfTimestampVerification) {
Date dateOfTimestampVerification) {
if (dateOfTimestampVerification == null) {
throw new IllegalArgumentException("dateOfTimestampVerification must not be null");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;

import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.DefaultPGPAlgorithmSuites;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPAlgorithmSuite;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.encrypting.PGPEncryptingStream;
Expand Down Expand Up @@ -66,11 +67,11 @@ public final class WithKeySelectionStrategy extends WithAlgorithmSuiteImpl {
private WithKeySelectionStrategy() {
super();
BuildEncryptionOutputStreamAPI.this.keySelectionStrategy = new Rfc4880KeySelectionStrategy(
Instant.now());
new Date());
}

public WithAlgorithmSuite setReferenceDateForKeyValidityTo(
Instant dateOfTimestampVerification) {
Date dateOfTimestampVerification) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above (breaks API)

if (dateOfTimestampVerification == null) {
throw new IllegalArgumentException("dateOfTimestampVerification must not be null");
}
Expand All @@ -87,7 +88,7 @@ public WithAlgorithmSuite withKeySelectionStrategy(final KeySelectionStrategy st
}
BuildEncryptionOutputStreamAPI.this.keySelectionStrategy = strategy;
LOGGER.trace("WithKeySelectionStrategy: override strategy to {}",
strategy.getClass().toGenericString());
strategy.getClass().getName());
return new WithAlgorithmSuiteImpl();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks;

import java.io.IOException;
import java.time.Instant;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.annotation.Nullable;

import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyFlags;
Expand All @@ -30,22 +28,22 @@ public class Rfc4880KeySelectionStrategy implements KeySelectionStrategy {
private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory
.getLogger(Rfc4880KeySelectionStrategy.class);

private final Instant dateOfTimestampVerification;
private final Date dateOfTimestampVerification;

/**
* The date used for key expiration date checks as "now".
*
* @return dateOfTimestampVerification
*/
protected Instant getDateOfTimestampVerification() {
protected Date getDateOfTimestampVerification() {
return dateOfTimestampVerification;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also breaks API, it is possible but unlikely that this will cause a major fuss (I'll skip over the next "breaking changes" )

}


/**
* @param dateOfTimestampVerification The date used for key expiration date checks as "now".
*/
public Rfc4880KeySelectionStrategy(final Instant dateOfTimestampVerification) {
public Rfc4880KeySelectionStrategy(final Date dateOfTimestampVerification) {
this.dateOfTimestampVerification = dateOfTimestampVerification;
}

Expand Down Expand Up @@ -97,12 +95,17 @@ public Set<PGPPublicKey> validPublicKeysForVerifyingSignatures(String uid,
final Set<PGPPublicKeyRing> publicKeyrings = this
.publicKeyRingsForUid(PURPOSE.FOR_SIGNING, uid, keyringConfig);

return publicKeyrings.stream()
.flatMap(keyring -> StreamSupport.stream(keyring.spliterator(), false))
.filter(this::isVerificationKey)
.filter(this::isNotRevoked)
.filter(this::isNotExpired)
.collect(Collectors.toSet());
Set<PGPPublicKey> validKeys = new HashSet<>();
for (PGPPublicKeyRing p : publicKeyrings) {
Iterator<PGPPublicKey> keys = p.iterator();
while (keys.hasNext()) {
PGPPublicKey key = keys.next();
if (isVerificationKey(key) && isNotRevoked(key) && isNotExpired(key)) {
validKeys.add(key);
}
}
}
return validKeys;
}

@Nullable
Expand All @@ -116,52 +119,61 @@ public PGPPublicKey selectPublicKey(PURPOSE purpose, String uid, KeyringConfig k

final PGPSecretKeyRingCollection secretKeyRings = keyringConfig.getSecretKeyRings();

PGPPublicKey publicKey = null;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment to the method (if there is none yet) to say that the last matching key is selected ("use the newest").

switch (purpose) {
case FOR_SIGNING:
return publicKeyrings.stream()
.flatMap(keyring -> StreamSupport.stream(keyring.spliterator(), false))
.filter(this::isVerificationKey)
.filter(this::isNotRevoked)
.filter(this::isNotExpired)
.filter(hasPrivateKey(secretKeyRings))
.reduce((a, b) -> b)
.orElse(null);
for (PGPPublicKeyRing ring : publicKeyrings) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move this to a dedicated method?

Iterator<PGPPublicKey> iterator = ring.iterator();
while (iterator.hasNext()) {
PGPPublicKey key = iterator.next();
if (isVerificationKey(key) &&
isNotRevoked(key) &&
isNotExpired(key) &&
hasPrivateKey(key, secretKeyRings)) {
publicKey = key;
}
}
}
return publicKey;

case FOR_ENCRYPTION:
return publicKeyrings.stream()
.flatMap(keyring -> StreamSupport.stream(keyring.spliterator(), false))
.filter(this::isEncryptionKey)
.filter(this::isNotRevoked)
.filter(this::isNotExpired)
.reduce((a, b) -> b)
.orElse(null);
for (PGPPublicKeyRing ring : publicKeyrings) {
Iterator<PGPPublicKey> iterator = ring.iterator();
while (iterator.hasNext()) {
PGPPublicKey key = iterator.next();
if (isEncryptionKey(key) &&
isNotRevoked(key) &&
isNotExpired(key)) {
publicKey = key;
}
}
}
return publicKey;

default:
return null;
}
}

protected boolean hasPrivateKey(PGPPublicKey pubKey, PGPSecretKeyRingCollection secretKeyRings) {
boolean result = false;
try {
final boolean hasPrivateKey = secretKeyRings.contains(pubKey.getKeyID());

protected Predicate<PGPPublicKey> hasPrivateKey(final PGPSecretKeyRingCollection secretKeyRings) {
return pubKey -> {
try {
final boolean hasPrivateKey = secretKeyRings.contains(pubKey.getKeyID());
if (!hasPrivateKey) {
LOGGER.trace("Skipping pubkey {} (no private key found)",
Long.toHexString(pubKey.getKeyID()));
}

if (!hasPrivateKey) {
LOGGER.trace("Skipping pubkey {} (no private key found)",
Long.toHexString(pubKey.getKeyID()));
}
result = hasPrivateKey;
} catch (PGPException e) {
// ignore this for filtering
LOGGER.debug("Failed to test for private key for pubkey " + pubKey.getKeyID());
}

return hasPrivateKey;
} catch (PGPException e) {
// ignore this for filtering
LOGGER.debug("Failed to test for private key for pubkey " + pubKey.getKeyID());
return false;
}
};
return result;
}


protected boolean isNotMasterKey(PGPPublicKey pubKey) {
return !pubKey.isMasterKey();
}
Expand All @@ -179,10 +191,9 @@ protected boolean isExpired(PGPPublicKey pubKey) {
final boolean isExpired;

if (hasExpiryDate) {
final Instant expiryDate = pubKey.getCreationTime().toInstant()
.plusSeconds(pubKey.getValidSeconds());
final Date expiryDate = new Date(pubKey.getCreationTime().getTime() + 1000L * pubKey.getValidSeconds());
isExpired = expiryDate
.isBefore(getDateOfTimestampVerification());
.before(getDateOfTimestampVerification());

if (isExpired) {
LOGGER.trace("Skipping pubkey {} (expired since {})",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import javax.annotation.Nonnull;

import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallback;

/**
Expand All @@ -27,12 +28,12 @@ public FileBasedKeyringConfig(@Nonnull KeyringConfigCallback callback,
@Nonnull
@Override
protected InputStream getPublicKeyRingStream() throws IOException {
return Files.newInputStream(publicKeyring.toPath());
return new FileInputStream(publicKeyring);
}

@Nonnull
@Override
protected InputStream getSecretKeyRingStream() throws IOException {
return Files.newInputStream(secretKeyring.toPath());
return new FileInputStream(secretKeyring);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,21 @@ public void addPublicKey(byte[] encodedPublicKey) throws IOException, PGPExcepti
throw new IllegalArgumentException("encodedPublicKey must not be null");
}

try (
final InputStream raw = new ByteArrayInputStream(encodedPublicKey);
final InputStream decoded = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(raw)
) {
InputStream raw = null;
InputStream decoded = null;
try {
raw = new ByteArrayInputStream(encodedPublicKey);
decoded = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(raw);
PGPPublicKeyRing pgpPub = new PGPPublicKeyRing(decoded, getKeyFingerPrintCalculator());
this.publicKeyRings = PGPPublicKeyRingCollection
.addPublicKeyRing(this.publicKeyRings, pgpPub);
.addPublicKeyRing(this.publicKeyRings, pgpPub);
}
catch (IOException e) {
if (decoded != null) {
decoded.close();
}
raw.close();
throw e;
}
}

Expand All @@ -82,15 +90,22 @@ public void addSecretKey(byte[] encodedPrivateKey) throws IOException, PGPExcept
throw new IllegalArgumentException("encodedPrivateKey must not be null");
}

try (
final InputStream raw = new ByteArrayInputStream(encodedPrivateKey);
final InputStream decoded = org.bouncycastle.openpgp.PGPUtil
.getDecoderStream(raw)
) {
InputStream raw = null;
InputStream decoded = null;
try {
raw = new ByteArrayInputStream(encodedPrivateKey);
decoded = org.bouncycastle.openpgp.PGPUtil
.getDecoderStream(raw);
PGPSecretKeyRing pgpPRivate = new PGPSecretKeyRing(decoded, getKeyFingerPrintCalculator());
this.secretKeyRings =
PGPSecretKeyRingCollection
.addSecretKeyRing(this.secretKeyRings, pgpPRivate);
PGPSecretKeyRingCollection
.addSecretKeyRing(this.secretKeyRings, pgpPRivate);
} catch (IOException e) {
if (decoded != null) {
decoded.close();
}
raw.close();
throw e;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,9 @@ void explodeAndReencrypt(InputStream inputStream)
numFiles++;

LOGGER.debug("found file '{}'", entry.getName());

try (
final OutputStream outputStream = entityHandlingStrategy
.createOutputStream(sanitizedFileName)
) {
OutputStream outputStream = null;
try {
outputStream = entityHandlingStrategy.createOutputStream(sanitizedFileName);
if (outputStream == null) {
LOGGER.trace("Ignore {}", entry.getName());
} else {
Expand All @@ -75,6 +73,10 @@ void explodeAndReencrypt(InputStream inputStream)
encryptedSmallFromZIP.flush();
encryptedSmallFromZIP.close();
}
} finally {
if (outputStream != null) {
outputStream.close();
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeySelectionStrategy;
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig;
import org.bouncycastle.openpgp.PGPException;
Expand Down Expand Up @@ -134,8 +135,10 @@ public static SignatureValidationStrategy requireSignatureFromAllUids(
throw new PGPException("Could not find public-key for userid '" + userId + "'");
}

Set<Long> keysForUid = availableKeys.stream().map(key -> key.getKeyID())
.collect(Collectors.toSet());
Set<Long> keysForUid = new HashSet<>();
for (PGPPublicKey p : availableKeys) {
keysForUid.add(p.getKeyID());
}

keyIdsByUid.put(userId, keysForUid);
}
Expand Down
Loading