2021-06-15 17:56:36 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2021 Paul Schaub.
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
2021-06-15 17:08:40 +02:00
|
|
|
package org.pgpainless.decryption_verification;
|
|
|
|
|
2021-06-16 15:38:02 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2021-06-15 17:08:40 +02:00
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
2021-06-16 15:38:02 +02:00
|
|
|
import java.util.List;
|
2021-06-15 17:08:40 +02:00
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
2021-06-16 15:38:02 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPException;
|
2021-06-15 17:08:40 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
2021-06-16 15:38:02 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
2021-06-15 17:08:40 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSignature;
|
2021-06-16 15:49:43 +02:00
|
|
|
import org.pgpainless.exception.NotYetImplementedException;
|
2021-06-15 17:08:40 +02:00
|
|
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
2021-06-16 15:49:43 +02:00
|
|
|
import org.pgpainless.signature.SignatureUtils;
|
2021-06-15 17:08:40 +02:00
|
|
|
import org.pgpainless.util.Passphrase;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Options for decryption and signature verification.
|
|
|
|
*/
|
|
|
|
public class ConsumerOptions {
|
|
|
|
|
|
|
|
private Date verifyNotBefore;
|
|
|
|
private Date verifyNotAfter;
|
|
|
|
|
|
|
|
// Set of verification keys
|
2021-06-15 17:56:36 +02:00
|
|
|
private final Set<PGPPublicKeyRing> certificates = new HashSet<>();
|
|
|
|
private final Set<PGPSignature> detachedSignatures = new HashSet<>();
|
2021-06-15 17:08:40 +02:00
|
|
|
private MissingPublicKeyCallback missingCertificateCallback = null;
|
|
|
|
|
|
|
|
// Session key for decryption without passphrase/key
|
|
|
|
private byte[] sessionKey = null;
|
|
|
|
|
|
|
|
private final Map<PGPSecretKeyRing, SecretKeyRingProtector> decryptionKeys = new HashMap<>();
|
|
|
|
private final Set<Passphrase> decryptionPassphrases = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Consider signatures made before the given timestamp invalid.
|
|
|
|
*
|
2021-06-16 15:49:43 +02:00
|
|
|
* Note: This method does not have any effect yet.
|
|
|
|
* TODO: Add support for custom signature validity date ranges
|
|
|
|
*
|
2021-06-15 17:08:40 +02:00
|
|
|
* @param timestamp timestamp
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions verifyNotBefore(Date timestamp) {
|
|
|
|
this.verifyNotBefore = timestamp;
|
2021-06-16 15:49:43 +02:00
|
|
|
throw new NotYetImplementedException();
|
|
|
|
// return this;
|
2021-06-15 17:08:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public Date getVerifyNotBefore() {
|
|
|
|
return verifyNotBefore;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
/**
|
|
|
|
* Consider signatures made after the given timestamp invalid.
|
|
|
|
*
|
2021-06-16 15:49:43 +02:00
|
|
|
* Note: This method does not have any effect yet.
|
|
|
|
* TODO: Add support for custom signature validity date ranges
|
|
|
|
*
|
2021-06-15 17:08:40 +02:00
|
|
|
* @param timestamp timestamp
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions verifyNotAfter(Date timestamp) {
|
|
|
|
this.verifyNotAfter = timestamp;
|
2021-06-16 15:49:43 +02:00
|
|
|
throw new NotYetImplementedException();
|
|
|
|
// return this;
|
2021-06-15 17:08:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public Date getVerifyNotAfter() {
|
|
|
|
return verifyNotAfter;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
/**
|
|
|
|
* Add a certificate (public key ring) for signature verification.
|
|
|
|
*
|
|
|
|
* @param verificationCert certificate for signature verification
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addVerificationCert(PGPPublicKeyRing verificationCert) {
|
|
|
|
this.certificates.add(verificationCert);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a set of certificates (public key rings) for signature verification.
|
|
|
|
*
|
|
|
|
* @param verificationCerts certificates for signature verification
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addVerificationCerts(PGPPublicKeyRingCollection verificationCerts) {
|
|
|
|
for (PGPPublicKeyRing certificate : verificationCerts) {
|
|
|
|
addVerificationCert(certificate);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-06-16 15:38:02 +02:00
|
|
|
public ConsumerOptions addVerificationOfDetachedSignatures(InputStream signatureInputStream) throws IOException, PGPException {
|
2021-06-16 15:49:43 +02:00
|
|
|
List<PGPSignature> signatures = SignatureUtils.readSignatures(signatureInputStream);
|
2021-06-16 15:38:02 +02:00
|
|
|
return addVerificationOfDetachedSignatures(signatures);
|
|
|
|
}
|
|
|
|
|
|
|
|
public ConsumerOptions addVerificationOfDetachedSignatures(List<PGPSignature> detachedSignatures) {
|
|
|
|
for (PGPSignature signature : detachedSignatures) {
|
|
|
|
addVerificationOfDetachedSignature(signature);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
/**
|
|
|
|
* Add a detached signature for the signature verification process.
|
|
|
|
*
|
|
|
|
* @param detachedSignature detached signature
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addVerificationOfDetachedSignature(PGPSignature detachedSignature) {
|
|
|
|
detachedSignatures.add(detachedSignature);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set a callback that's used when a certificate (public key) is missing for signature verification.
|
|
|
|
*
|
|
|
|
* @param callback callback
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions setMissingCertificateCallback(MissingPublicKeyCallback callback) {
|
|
|
|
this.missingCertificateCallback = callback;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempt decryption using a session key.
|
|
|
|
*
|
|
|
|
* Note: PGPainless does not yet support decryption with session keys.
|
2021-06-16 15:49:43 +02:00
|
|
|
* TODO: Add support for decryption using session key.
|
2021-06-15 17:08:40 +02:00
|
|
|
*
|
|
|
|
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-2.1">RFC4880 on Session Keys</a>
|
|
|
|
*
|
|
|
|
* @param sessionKey session key
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions setSessionKey(@Nonnull byte[] sessionKey) {
|
|
|
|
this.sessionKey = sessionKey;
|
2021-06-16 15:49:43 +02:00
|
|
|
throw new NotYetImplementedException();
|
2021-06-15 17:08:40 +02:00
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
/**
|
|
|
|
* Return the session key.
|
|
|
|
*
|
|
|
|
* @return session key or null
|
|
|
|
*/
|
|
|
|
public @Nullable byte[] getSessionKey() {
|
|
|
|
if (sessionKey == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] sk = new byte[sessionKey.length];
|
|
|
|
System.arraycopy(sessionKey, 0, sk, 0, sessionKey.length);
|
|
|
|
return sk;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
/**
|
|
|
|
* Add a key for message decryption.
|
|
|
|
* The key is expected to be unencrypted.
|
|
|
|
*
|
|
|
|
* @param key unencrypted key
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addDecryptionKey(@Nonnull PGPSecretKeyRing key) {
|
|
|
|
return addDecryptionKey(key, SecretKeyRingProtector.unprotectedKeys());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a key for message decryption. If the key is encrypted, the {@link SecretKeyRingProtector} is used to decrypt it
|
|
|
|
* when needed.
|
|
|
|
*
|
|
|
|
* @param key key
|
|
|
|
* @param keyRingProtector protector for the secret key
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addDecryptionKey(@Nonnull PGPSecretKeyRing key, @Nonnull SecretKeyRingProtector keyRingProtector) {
|
|
|
|
decryptionKeys.put(key, keyRingProtector);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-06-16 15:38:02 +02:00
|
|
|
/**
|
|
|
|
* Add the keys in the provided key collection for message decryption.
|
|
|
|
*
|
|
|
|
* @param keys key collection
|
|
|
|
* @param keyRingProtector protector for encrypted secret keys
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addDecryptionKeys(@Nonnull PGPSecretKeyRingCollection keys, @Nonnull SecretKeyRingProtector keyRingProtector) {
|
|
|
|
for (PGPSecretKeyRing key : keys) {
|
|
|
|
addDecryptionKey(key, keyRingProtector);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:08:40 +02:00
|
|
|
/**
|
|
|
|
* Add a passphrase for message decryption.
|
|
|
|
*
|
|
|
|
* @param passphrase passphrase
|
|
|
|
* @return options
|
|
|
|
*/
|
|
|
|
public ConsumerOptions addDecryptionPassphrase(@Nonnull Passphrase passphrase) {
|
|
|
|
decryptionPassphrases.add(passphrase);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public @Nonnull Set<PGPSecretKeyRing> getDecryptionKeys() {
|
2021-06-15 17:08:40 +02:00
|
|
|
return Collections.unmodifiableSet(decryptionKeys.keySet());
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public @Nonnull Set<Passphrase> getDecryptionPassphrases() {
|
2021-06-15 17:08:40 +02:00
|
|
|
return Collections.unmodifiableSet(decryptionPassphrases);
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public @Nonnull Set<PGPPublicKeyRing> getCertificates() {
|
2021-06-15 17:08:40 +02:00
|
|
|
return Collections.unmodifiableSet(certificates);
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public @Nullable MissingPublicKeyCallback getMissingCertificateCallback() {
|
2021-06-15 17:08:40 +02:00
|
|
|
return missingCertificateCallback;
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public @Nullable SecretKeyRingProtector getSecretKeyProtector(PGPSecretKeyRing decryptionKeyRing) {
|
2021-06-15 17:08:40 +02:00
|
|
|
return decryptionKeys.get(decryptionKeyRing);
|
|
|
|
}
|
|
|
|
|
2021-06-15 17:56:36 +02:00
|
|
|
public @Nonnull Set<PGPSignature> getDetachedSignatures() {
|
2021-06-15 17:08:40 +02:00
|
|
|
return Collections.unmodifiableSet(detachedSignatures);
|
|
|
|
}
|
|
|
|
}
|