1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-12-10 06:11:08 +01:00

Prevent decryption of messages using SED instead of SEIP packets and create dedicated exceptions for MDC related errors

This commit is contained in:
Paul Schaub 2021-04-27 12:27:25 +02:00
parent eb47e5caa3
commit 6ee8a9416f
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
9 changed files with 237 additions and 25 deletions

View file

@ -55,6 +55,7 @@ import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.pgpainless.algorithm.CompressionAlgorithm;
import org.pgpainless.algorithm.StreamEncoding;
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
import org.pgpainless.implementation.ImplementationFactory;
import org.pgpainless.key.OpenPgpV4Fingerprint;
import org.pgpainless.key.protection.SecretKeyRingProtector;
@ -201,8 +202,11 @@ public final class DecryptionStreamFactory {
while (encryptedDataIterator.hasNext()) {
PGPEncryptedData encryptedData = encryptedDataIterator.next();
if (encryptedData instanceof PGPPBEEncryptedData) {
if (!encryptedData.isIntegrityProtected()) {
throw new MessageNotIntegrityProtectedException();
}
if (encryptedData instanceof PGPPBEEncryptedData) {
PGPPBEEncryptedData pbeEncryptedData = (PGPPBEEncryptedData) encryptedData;
if (decryptionPassphrase != null) {
PBEDataDecryptorFactory passphraseDecryptor = ImplementationFactory.getInstance()
@ -213,7 +217,6 @@ public final class DecryptionStreamFactory {
throw new PGPException("Data is not encrypted.");
}
resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm);
resultBuilder.setIntegrityProtected(pbeEncryptedData.isIntegrityProtected());
try {
return pbeEncryptedData.getDataStream(passphraseDecryptor);
@ -278,8 +281,6 @@ public final class DecryptionStreamFactory {
LOGGER.log(LEVEL, "Message is encrypted using " + symmetricKeyAlgorithm);
resultBuilder.setSymmetricKeyAlgorithm(symmetricKeyAlgorithm);
resultBuilder.setIntegrityProtected(encryptedSessionKey.isIntegrityProtected());
if (encryptedSessionKey.isIntegrityProtected()) {
IntegrityProtectedInputStream integrityProtected =
new IntegrityProtectedInputStream(encryptedSessionKey.getDataStream(dataDecryptor), encryptedSessionKey);

View file

@ -41,14 +41,12 @@ public class OpenPgpMetadata {
private final List<DetachedSignature> detachedSignatures;
private final SymmetricKeyAlgorithm symmetricKeyAlgorithm;
private final CompressionAlgorithm compressionAlgorithm;
private final boolean integrityProtected;
private final FileInfo fileInfo;
public OpenPgpMetadata(Set<Long> recipientKeyIds,
OpenPgpV4Fingerprint decryptionFingerprint,
SymmetricKeyAlgorithm symmetricKeyAlgorithm,
CompressionAlgorithm algorithm,
boolean integrityProtected,
List<OnePassSignature> onePassSignatures,
List<DetachedSignature> detachedSignatures,
FileInfo fileInfo) {
@ -57,7 +55,6 @@ public class OpenPgpMetadata {
this.decryptionFingerprint = decryptionFingerprint;
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
this.compressionAlgorithm = algorithm;
this.integrityProtected = integrityProtected;
this.detachedSignatures = Collections.unmodifiableList(detachedSignatures);
this.onePassSignatures = Collections.unmodifiableList(onePassSignatures);
this.fileInfo = fileInfo;
@ -83,10 +80,6 @@ public class OpenPgpMetadata {
return compressionAlgorithm;
}
public boolean isIntegrityProtected() {
return integrityProtected;
}
public Set<PGPSignature> getSignatures() {
Set<PGPSignature> signatures = new HashSet<>();
for (DetachedSignature detachedSignature : detachedSignatures) {
@ -245,7 +238,6 @@ public class OpenPgpMetadata {
private final List<OnePassSignature> onePassSignatures = new ArrayList<>();
private SymmetricKeyAlgorithm symmetricKeyAlgorithm = SymmetricKeyAlgorithm.NULL;
private CompressionAlgorithm compressionAlgorithm = CompressionAlgorithm.UNCOMPRESSED;
private boolean integrityProtected = false;
private FileInfo fileInfo;
public Builder addRecipientKeyId(Long keyId) {
@ -272,11 +264,6 @@ public class OpenPgpMetadata {
return this;
}
public Builder setIntegrityProtected(boolean integrityProtected) {
this.integrityProtected = integrityProtected;
return this;
}
public void addDetachedSignature(DetachedSignature signature) {
this.detachedSignatures.add(signature);
}
@ -292,7 +279,7 @@ public class OpenPgpMetadata {
public OpenPgpMetadata build() {
return new OpenPgpMetadata(recipientFingerprints, decryptionFingerprint,
symmetricKeyAlgorithm, compressionAlgorithm, integrityProtected,
symmetricKeyAlgorithm, compressionAlgorithm,
onePassSignatures, detachedSignatures, fileInfo);
}
}

View file

@ -0,0 +1,26 @@
/*
* 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.
*/
package org.pgpainless.exception;
import org.bouncycastle.openpgp.PGPException;
public class MessageNotIntegrityProtectedException extends PGPException {
public MessageNotIntegrityProtectedException() {
super("Message is encrypted using a 'Symmetrically Encrypted Data' (SED) packet, which enables certain types of attacks. " +
"A 'Symmetrically Encrypted Integrity Protected' (SEIP) packet should be used instead.");
}
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
package org.pgpainless.exception;
import java.io.IOException;
/**
* Exception that gets thrown when the verification of a modification detection code failed.
*/
public class ModificationDetectionException extends IOException {
}

View file

@ -20,6 +20,7 @@ import java.io.InputStream;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPException;
import org.pgpainless.exception.ModificationDetectionException;
public class IntegrityProtectedInputStream extends InputStream {
@ -41,10 +42,10 @@ public class IntegrityProtectedInputStream extends InputStream {
if (encryptedData.isIntegrityProtected()) {
try {
if (!encryptedData.verify()) {
throw new PGPException("Modification Detection failed.");
throw new ModificationDetectionException();
}
} catch (PGPException e) {
throw new IOException(e);
throw new IOException("Failed to verify integrity protection", e);
}
}
}