1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-23 11:27:57 +01:00

Native support for notBefore and notAfter signature creation time constraints

This commit is contained in:
Paul Schaub 2021-08-17 14:47:07 +02:00
parent 6a108cb8c0
commit 099b160656
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
6 changed files with 68 additions and 45 deletions

View file

@ -43,8 +43,8 @@ import org.pgpainless.util.Passphrase;
*/ */
public class ConsumerOptions { public class ConsumerOptions {
private Date verifyNotBefore; private Date verifyNotBefore = null;
private Date verifyNotAfter; private Date verifyNotAfter = new Date();
// Set of verification keys // Set of verification keys
private final Set<PGPPublicKeyRing> certificates = new HashSet<>(); private final Set<PGPPublicKeyRing> certificates = new HashSet<>();
@ -59,39 +59,45 @@ public class ConsumerOptions {
/** /**
* Consider signatures made before the given timestamp invalid. * Consider signatures on the message made before the given timestamp invalid.
* * Null means no limitation.
* Note: This method does not have any effect yet.
* TODO: Add support for custom signature validity date ranges
* *
* @param timestamp timestamp * @param timestamp timestamp
* @return options * @return options
*/ */
public ConsumerOptions verifyNotBefore(Date timestamp) { public ConsumerOptions verifyNotBefore(Date timestamp) {
this.verifyNotBefore = timestamp; this.verifyNotBefore = timestamp;
throw new NotYetImplementedException(); return this;
// return this;
} }
public Date getVerifyNotBefore() { /**
* Return the earliest creation date on which signatures on the message are considered valid.
* Signatures made earlier than this date are considered invalid.
*
* @return earliest allowed signature creation date or null
*/
public @Nullable Date getVerifyNotBefore() {
return verifyNotBefore; return verifyNotBefore;
} }
/** /**
* Consider signatures made after the given timestamp invalid. * Consider signatures on the message made after the given timestamp invalid.
* * Null means no limitation.
* Note: This method does not have any effect yet.
* TODO: Add support for custom signature validity date ranges
* *
* @param timestamp timestamp * @param timestamp timestamp
* @return options * @return options
*/ */
public ConsumerOptions verifyNotAfter(Date timestamp) { public ConsumerOptions verifyNotAfter(Date timestamp) {
this.verifyNotAfter = timestamp; this.verifyNotAfter = timestamp;
throw new NotYetImplementedException(); return this;
// return this;
} }
/**
* Return the latest possible creation date on which signatures made on the message are considered valid.
* Signatures made later than this date are considered invalid.
*
* @return Latest possible creation date or null.
*/
public Date getVerifyNotAfter() { public Date getVerifyNotAfter() {
return verifyNotAfter; return verifyNotAfter;
} }

View file

@ -213,7 +213,7 @@ public final class DecryptionStreamFactory {
} }
return new SignatureVerifyingInputStream(literalDataInputStream, return new SignatureVerifyingInputStream(literalDataInputStream,
objectFactory, verifiableOnePassSignatures, resultBuilder); objectFactory, verifiableOnePassSignatures, options, resultBuilder);
} }
private InputStream decrypt(@Nonnull PGPEncryptedDataList encryptedDataList) private InputStream decrypt(@Nonnull PGPEncryptedDataList encryptedDataList)

View file

@ -17,12 +17,12 @@ package org.pgpainless.decryption_verification;
import static org.pgpainless.signature.SignatureValidator.signatureIsEffective; import static org.pgpainless.signature.SignatureValidator.signatureIsEffective;
import static org.pgpainless.signature.SignatureValidator.signatureStructureIsAcceptable; import static org.pgpainless.signature.SignatureValidator.signatureStructureIsAcceptable;
import static org.pgpainless.signature.SignatureValidator.verifySignatureCreationTimeIsInBounds;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -47,6 +47,7 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
private final PGPObjectFactory objectFactory; private final PGPObjectFactory objectFactory;
private final Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures; private final Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures;
private final ConsumerOptions options;
private final OpenPgpMetadata.Builder resultBuilder; private final OpenPgpMetadata.Builder resultBuilder;
private boolean validated = false; private boolean validated = false;
@ -54,9 +55,11 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream, protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream,
@Nonnull PGPObjectFactory objectFactory, @Nonnull PGPObjectFactory objectFactory,
@Nonnull Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures, @Nonnull Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures,
@Nonnull ConsumerOptions options,
@Nonnull OpenPgpMetadata.Builder resultBuilder) { @Nonnull OpenPgpMetadata.Builder resultBuilder) {
super(inputStream); super(inputStream);
this.objectFactory = objectFactory; this.objectFactory = objectFactory;
this.options = options;
this.resultBuilder = resultBuilder; this.resultBuilder = resultBuilder;
this.onePassSignatures = onePassSignatures; this.onePassSignatures = onePassSignatures;
@ -116,7 +119,8 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
try { try {
PGPPublicKey signingKey = onePassSignature.getVerificationKeys().getPublicKey(signature.getKeyID()); PGPPublicKey signingKey = onePassSignature.getVerificationKeys().getPublicKey(signature.getKeyID());
signatureStructureIsAcceptable(signingKey, policy).verify(signature); signatureStructureIsAcceptable(signingKey, policy).verify(signature);
signatureIsEffective(new Date()).verify(signature); verifySignatureCreationTimeIsInBounds(options.getVerifyNotBefore(), options.getVerifyNotAfter()).verify(signature);
signatureIsEffective().verify(signature);
SignatureChainValidator.validateSigningKey(signature, onePassSignature.getVerificationKeys(), PGPainless.getPolicy()); SignatureChainValidator.validateSigningKey(signature, onePassSignature.getVerificationKeys(), PGPainless.getPolicy());

View file

@ -45,6 +45,7 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.policy.Policy; import org.pgpainless.policy.Policy;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.BCUtil; import org.pgpainless.util.BCUtil;
import org.pgpainless.util.DateUtil;
import org.pgpainless.util.NotationRegistry; import org.pgpainless.util.NotationRegistry;
public abstract class SignatureValidator { public abstract class SignatureValidator {
@ -664,6 +665,15 @@ public abstract class SignatureValidator {
}; };
} }
/**
* Verify that a signature is effective right now.
*
* @return validator
*/
public static SignatureValidator signatureIsEffective() {
return signatureIsEffective(new Date());
}
/** /**
* Verify that a signature is effective at the given reference date. * Verify that a signature is effective at the given reference date.
* *
@ -997,4 +1007,21 @@ public abstract class SignatureValidator {
}; };
} }
public static SignatureValidator verifySignatureCreationTimeIsInBounds(Date notBefore, Date notAfter) {
return new SignatureValidator() {
@Override
public void verify(PGPSignature signature) throws SignatureValidationException {
Date timestamp = signature.getCreationTime();
if (notBefore != null && timestamp.before(notBefore)) {
throw new SignatureValidationException("Signature was made before the earliest allowed signature creation time. Created: " +
DateUtil.formatUTCDate(timestamp) + " Earliest allowed: " + DateUtil.formatUTCDate(notBefore));
}
if (notAfter != null && timestamp.after(notAfter)) {
throw new SignatureValidationException("Signature was made after the latest allowed signature creation time. Created: " +
DateUtil.formatUTCDate(timestamp) + " Latest allowed: " + DateUtil.formatUTCDate(notAfter));
}
}
};
}
} }

View file

@ -53,7 +53,7 @@ public class DecryptImpl implements Decrypt {
try { try {
consumerOptions.verifyNotBefore(timestamp); consumerOptions.verifyNotBefore(timestamp);
} catch (NotYetImplementedException e) { } catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption(); throw new SOPGPException.UnsupportedOption();
} }
return this; return this;
} }
@ -63,7 +63,7 @@ public class DecryptImpl implements Decrypt {
try { try {
consumerOptions.verifyNotAfter(timestamp); consumerOptions.verifyNotAfter(timestamp);
} catch (NotYetImplementedException e) { } catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption(); throw new SOPGPException.UnsupportedOption();
} }
return this; return this;
} }
@ -91,7 +91,7 @@ public class DecryptImpl implements Decrypt {
} }
@Override @Override
public DecryptImpl withPassword(String password) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption { public DecryptImpl withPassword(String password) {
consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(password)); consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(password));
String withoutTrailingWhitespace = removeTrailingWhitespace(password); String withoutTrailingWhitespace = removeTrailingWhitespace(password);
if (!password.equals(withoutTrailingWhitespace)) { if (!password.equals(withoutTrailingWhitespace)) {
@ -158,18 +158,11 @@ public class DecryptImpl implements Decrypt {
List<Verification> verificationList = new ArrayList<>(); List<Verification> verificationList = new ArrayList<>();
for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) { for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) {
PGPSignature signature = metadata.getVerifiedSignatures().get(verifiedSigningKey); PGPSignature signature = metadata.getVerifiedSignatures().get(verifiedSigningKey);
Date verifyNotBefore = consumerOptions.getVerifyNotBefore();
Date verifyNotAfter = consumerOptions.getVerifyNotAfter();
if (verifyNotAfter == null || !signature.getCreationTime().after(verifyNotAfter)) {
if (verifyNotBefore == null || !signature.getCreationTime().before(verifyNotBefore)) {
verificationList.add(new Verification( verificationList.add(new Verification(
signature.getCreationTime(), signature.getCreationTime(),
verifiedSigningKey.getSubkeyFingerprint().toString(), verifiedSigningKey.getSubkeyFingerprint().toString(),
verifiedSigningKey.getPrimaryKeyFingerprint().toString())); verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
} }
}
}
if (!consumerOptions.getCertificates().isEmpty()) { if (!consumerOptions.getCertificates().isEmpty()) {
if (verificationList.isEmpty()) { if (verificationList.isEmpty()) {

View file

@ -44,7 +44,7 @@ public class VerifyImpl implements Verify {
try { try {
options.verifyNotBefore(timestamp); options.verifyNotBefore(timestamp);
} catch (NotYetImplementedException e) { } catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption(); throw new SOPGPException.UnsupportedOption();
} }
return this; return this;
} }
@ -54,7 +54,7 @@ public class VerifyImpl implements Verify {
try { try {
options.verifyNotAfter(timestamp); options.verifyNotAfter(timestamp);
} catch (NotYetImplementedException e) { } catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption(); throw new SOPGPException.UnsupportedOption();
} }
return this; return this;
} }
@ -97,18 +97,11 @@ public class VerifyImpl implements Verify {
for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) { for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) {
PGPSignature signature = metadata.getVerifiedSignatures().get(verifiedSigningKey); PGPSignature signature = metadata.getVerifiedSignatures().get(verifiedSigningKey);
Date verifyNotBefore = options.getVerifyNotBefore();
Date verifyNotAfter = options.getVerifyNotAfter();
if (verifyNotAfter == null || !signature.getCreationTime().after(verifyNotAfter)) {
if (verifyNotBefore == null || !signature.getCreationTime().before(verifyNotBefore)) {
verificationList.add(new Verification( verificationList.add(new Verification(
signature.getCreationTime(), signature.getCreationTime(),
verifiedSigningKey.getSubkeyFingerprint().toString(), verifiedSigningKey.getSubkeyFingerprint().toString(),
verifiedSigningKey.getPrimaryKeyFingerprint().toString())); verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
} }
}
}
if (!options.getCertificates().isEmpty()) { if (!options.getCertificates().isEmpty()) {
if (verificationList.isEmpty()) { if (verificationList.isEmpty()) {