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 {
private Date verifyNotBefore;
private Date verifyNotAfter;
private Date verifyNotBefore = null;
private Date verifyNotAfter = new Date();
// Set of verification keys
private final Set<PGPPublicKeyRing> certificates = new HashSet<>();
@ -59,39 +59,45 @@ public class ConsumerOptions {
/**
* Consider signatures made before the given timestamp invalid.
*
* Note: This method does not have any effect yet.
* TODO: Add support for custom signature validity date ranges
* Consider signatures on the message made before the given timestamp invalid.
* Null means no limitation.
*
* @param timestamp timestamp
* @return options
*/
public ConsumerOptions verifyNotBefore(Date 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;
}
/**
* Consider signatures made after the given timestamp invalid.
*
* Note: This method does not have any effect yet.
* TODO: Add support for custom signature validity date ranges
* Consider signatures on the message made after the given timestamp invalid.
* Null means no limitation.
*
* @param timestamp timestamp
* @return options
*/
public ConsumerOptions verifyNotAfter(Date 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() {
return verifyNotAfter;
}

View File

@ -213,7 +213,7 @@ public final class DecryptionStreamFactory {
}
return new SignatureVerifyingInputStream(literalDataInputStream,
objectFactory, verifiableOnePassSignatures, resultBuilder);
objectFactory, verifiableOnePassSignatures, options, resultBuilder);
}
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.signatureStructureIsAcceptable;
import static org.pgpainless.signature.SignatureValidator.verifySignatureCreationTimeIsInBounds;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.SignatureException;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -47,6 +47,7 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
private final PGPObjectFactory objectFactory;
private final Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures;
private final ConsumerOptions options;
private final OpenPgpMetadata.Builder resultBuilder;
private boolean validated = false;
@ -54,9 +55,11 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream,
@Nonnull PGPObjectFactory objectFactory,
@Nonnull Map<OpenPgpV4Fingerprint, OnePassSignature> onePassSignatures,
@Nonnull ConsumerOptions options,
@Nonnull OpenPgpMetadata.Builder resultBuilder) {
super(inputStream);
this.objectFactory = objectFactory;
this.options = options;
this.resultBuilder = resultBuilder;
this.onePassSignatures = onePassSignatures;
@ -116,7 +119,8 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
try {
PGPPublicKey signingKey = onePassSignature.getVerificationKeys().getPublicKey(signature.getKeyID());
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());

View File

@ -45,6 +45,7 @@ import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
import org.pgpainless.util.BCUtil;
import org.pgpainless.util.DateUtil;
import org.pgpainless.util.NotationRegistry;
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.
*
@ -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 {
consumerOptions.verifyNotBefore(timestamp);
} catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption();
throw new SOPGPException.UnsupportedOption();
}
return this;
}
@ -63,7 +63,7 @@ public class DecryptImpl implements Decrypt {
try {
consumerOptions.verifyNotAfter(timestamp);
} catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption();
throw new SOPGPException.UnsupportedOption();
}
return this;
}
@ -91,7 +91,7 @@ public class DecryptImpl implements Decrypt {
}
@Override
public DecryptImpl withPassword(String password) throws SOPGPException.PasswordNotHumanReadable, SOPGPException.UnsupportedOption {
public DecryptImpl withPassword(String password) {
consumerOptions.addDecryptionPassphrase(Passphrase.fromPassword(password));
String withoutTrailingWhitespace = removeTrailingWhitespace(password);
if (!password.equals(withoutTrailingWhitespace)) {
@ -158,17 +158,10 @@ public class DecryptImpl implements Decrypt {
List<Verification> verificationList = new ArrayList<>();
for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) {
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(
signature.getCreationTime(),
verifiedSigningKey.getSubkeyFingerprint().toString(),
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
}
}
verificationList.add(new Verification(
signature.getCreationTime(),
verifiedSigningKey.getSubkeyFingerprint().toString(),
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
}
if (!consumerOptions.getCertificates().isEmpty()) {

View File

@ -44,7 +44,7 @@ public class VerifyImpl implements Verify {
try {
options.verifyNotBefore(timestamp);
} catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption();
throw new SOPGPException.UnsupportedOption();
}
return this;
}
@ -54,7 +54,7 @@ public class VerifyImpl implements Verify {
try {
options.verifyNotAfter(timestamp);
} catch (NotYetImplementedException e) {
// throw new SOPGPException.UnsupportedOption();
throw new SOPGPException.UnsupportedOption();
}
return this;
}
@ -97,17 +97,10 @@ public class VerifyImpl implements Verify {
for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) {
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(
signature.getCreationTime(),
verifiedSigningKey.getSubkeyFingerprint().toString(),
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
}
}
verificationList.add(new Verification(
signature.getCreationTime(),
verifiedSigningKey.getSubkeyFingerprint().toString(),
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
}
if (!options.getCertificates().isEmpty()) {