mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 03:17:58 +01:00
Native support for notBefore and notAfter signature creation time constraints
This commit is contained in:
parent
6a108cb8c0
commit
099b160656
6 changed files with 68 additions and 45 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,17 +158,10 @@ 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();
|
verificationList.add(new Verification(
|
||||||
Date verifyNotAfter = consumerOptions.getVerifyNotAfter();
|
signature.getCreationTime(),
|
||||||
|
verifiedSigningKey.getSubkeyFingerprint().toString(),
|
||||||
if (verifyNotAfter == null || !signature.getCreationTime().after(verifyNotAfter)) {
|
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
|
||||||
if (verifyNotBefore == null || !signature.getCreationTime().before(verifyNotBefore)) {
|
|
||||||
verificationList.add(new Verification(
|
|
||||||
signature.getCreationTime(),
|
|
||||||
verifiedSigningKey.getSubkeyFingerprint().toString(),
|
|
||||||
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!consumerOptions.getCertificates().isEmpty()) {
|
if (!consumerOptions.getCertificates().isEmpty()) {
|
||||||
|
|
|
@ -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,17 +97,10 @@ 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();
|
verificationList.add(new Verification(
|
||||||
Date verifyNotAfter = options.getVerifyNotAfter();
|
signature.getCreationTime(),
|
||||||
|
verifiedSigningKey.getSubkeyFingerprint().toString(),
|
||||||
if (verifyNotAfter == null || !signature.getCreationTime().after(verifyNotAfter)) {
|
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
|
||||||
if (verifyNotBefore == null || !signature.getCreationTime().before(verifyNotBefore)) {
|
|
||||||
verificationList.add(new Verification(
|
|
||||||
signature.getCreationTime(),
|
|
||||||
verifiedSigningKey.getSubkeyFingerprint().toString(),
|
|
||||||
verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.getCertificates().isEmpty()) {
|
if (!options.getCertificates().isEmpty()) {
|
||||||
|
|
Loading…
Reference in a new issue