001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org> 002// 003// SPDX-License-Identifier: Apache-2.0 004 005package org.pgpainless.decryption_verification; 006 007import static org.pgpainless.signature.consumer.SignatureValidator.signatureWasCreatedInBounds; 008 009import java.io.FilterInputStream; 010import java.io.IOException; 011import java.io.InputStream; 012import java.util.List; 013import java.util.Map; 014import javax.annotation.Nonnull; 015import javax.annotation.Nullable; 016 017import org.bouncycastle.openpgp.PGPObjectFactory; 018import org.bouncycastle.openpgp.PGPPublicKeyRing; 019import org.bouncycastle.openpgp.PGPSignature; 020import org.bouncycastle.openpgp.PGPSignatureList; 021import org.pgpainless.PGPainless; 022import org.pgpainless.exception.SignatureValidationException; 023import org.pgpainless.policy.Policy; 024import org.pgpainless.signature.consumer.CertificateValidator; 025import org.pgpainless.signature.consumer.DetachedSignatureCheck; 026import org.pgpainless.signature.consumer.OnePassSignatureCheck; 027import org.pgpainless.signature.SignatureUtils; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031public abstract class SignatureInputStream extends FilterInputStream { 032 033 protected SignatureInputStream(InputStream inputStream) { 034 super(inputStream); 035 } 036 037 public static class VerifySignatures extends SignatureInputStream { 038 039 private static final Logger LOGGER = LoggerFactory.getLogger(VerifySignatures.class); 040 041 private final PGPObjectFactory objectFactory; 042 private final List<OnePassSignatureCheck> opSignatures; 043 private final Map<Long, OnePassSignatureCheck> opSignaturesWithMissingCert; 044 private final List<DetachedSignatureCheck> detachedSignatures; 045 private final ConsumerOptions options; 046 private final OpenPgpMetadata.Builder resultBuilder; 047 048 public VerifySignatures( 049 InputStream literalDataStream, 050 @Nullable PGPObjectFactory objectFactory, 051 List<OnePassSignatureCheck> opSignatures, 052 Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert, 053 List<DetachedSignatureCheck> detachedSignatures, 054 ConsumerOptions options, 055 OpenPgpMetadata.Builder resultBuilder) { 056 super(literalDataStream); 057 this.objectFactory = objectFactory; 058 this.opSignatures = opSignatures; 059 this.opSignaturesWithMissingCert = onePassSignaturesWithMissingCert; 060 this.detachedSignatures = detachedSignatures; 061 this.options = options; 062 this.resultBuilder = resultBuilder; 063 } 064 065 @Override 066 public int read() throws IOException { 067 final int data = super.read(); 068 final boolean endOfStream = data == -1; 069 if (endOfStream) { 070 verifyOnePassSignatures(); 071 verifyDetachedSignatures(); 072 } else { 073 byte b = (byte) data; 074 updateOnePassSignatures(b); 075 updateDetachedSignatures(b); 076 } 077 return data; 078 } 079 080 @Override 081 public int read(@Nonnull byte[] b, int off, int len) throws IOException { 082 int read = super.read(b, off, len); 083 084 final boolean endOfStream = read == -1; 085 if (endOfStream) { 086 parseAndCombineSignatures(); 087 verifyOnePassSignatures(); 088 verifyDetachedSignatures(); 089 } else { 090 updateOnePassSignatures(b, off, read); 091 updateDetachedSignatures(b, off, read); 092 } 093 return read; 094 } 095 096 public void parseAndCombineSignatures() { 097 if (objectFactory == null) { 098 return; 099 } 100 // Parse signatures from message 101 PGPSignatureList signatures; 102 try { 103 signatures = parseSignatures(objectFactory); 104 } catch (IOException e) { 105 return; 106 } 107 List<PGPSignature> signatureList = SignatureUtils.toList(signatures); 108 // Set signatures as comparison sigs in OPS checks 109 for (int i = 0; i < opSignatures.size(); i++) { 110 int reversedIndex = opSignatures.size() - i - 1; 111 opSignatures.get(i).setSignature(signatureList.get(reversedIndex)); 112 } 113 114 for (PGPSignature signature : signatureList) { 115 if (opSignaturesWithMissingCert.containsKey(signature.getKeyID())) { 116 OnePassSignatureCheck check = opSignaturesWithMissingCert.remove(signature.getKeyID()); 117 check.setSignature(signature); 118 119 resultBuilder.addInvalidInbandSignature(new SignatureVerification(signature, null), 120 new SignatureValidationException( 121 "Missing verification certificate " + Long.toHexString(signature.getKeyID()))); 122 } 123 } 124 } 125 126 private PGPSignatureList parseSignatures(PGPObjectFactory objectFactory) throws IOException { 127 PGPSignatureList signatureList = null; 128 Object pgpObject = objectFactory.nextObject(); 129 while (pgpObject != null && signatureList == null) { 130 if (pgpObject instanceof PGPSignatureList) { 131 signatureList = (PGPSignatureList) pgpObject; 132 } else { 133 pgpObject = objectFactory.nextObject(); 134 } 135 } 136 137 if (signatureList == null || signatureList.isEmpty()) { 138 throw new IOException("Verification failed - No Signatures found"); 139 } 140 141 return signatureList; 142 } 143 144 145 private synchronized void verifyOnePassSignatures() { 146 Policy policy = PGPainless.getPolicy(); 147 for (OnePassSignatureCheck opSignature : opSignatures) { 148 if (opSignature.getSignature() == null) { 149 LOGGER.warn("Found OnePassSignature without respective signature packet -> skip"); 150 continue; 151 } 152 153 try { 154 signatureWasCreatedInBounds(options.getVerifyNotBefore(), 155 options.getVerifyNotAfter()).verify(opSignature.getSignature()); 156 CertificateValidator.validateCertificateAndVerifyOnePassSignature(opSignature, policy); 157 resultBuilder.addVerifiedInbandSignature( 158 new SignatureVerification(opSignature.getSignature(), opSignature.getSigningKey())); 159 } catch (SignatureValidationException e) { 160 LOGGER.warn("One-pass-signature verification failed for signature made by key {}: {}", 161 opSignature.getSigningKey(), e.getMessage(), e); 162 resultBuilder.addInvalidInbandSignature( 163 new SignatureVerification(opSignature.getSignature(), opSignature.getSigningKey()), e); 164 } 165 } 166 } 167 168 private void verifyDetachedSignatures() { 169 Policy policy = PGPainless.getPolicy(); 170 for (DetachedSignatureCheck s : detachedSignatures) { 171 try { 172 signatureWasCreatedInBounds(options.getVerifyNotBefore(), 173 options.getVerifyNotAfter()).verify(s.getSignature()); 174 CertificateValidator.validateCertificateAndVerifyInitializedSignature(s.getSignature(), 175 (PGPPublicKeyRing) s.getSigningKeyRing(), policy); 176 resultBuilder.addVerifiedDetachedSignature(new SignatureVerification(s.getSignature(), 177 s.getSigningKeyIdentifier())); 178 } catch (SignatureValidationException e) { 179 LOGGER.warn("One-pass-signature verification failed for signature made by key {}: {}", 180 s.getSigningKeyIdentifier(), e.getMessage(), e); 181 resultBuilder.addInvalidDetachedSignature(new SignatureVerification(s.getSignature(), 182 s.getSigningKeyIdentifier()), e); 183 } 184 } 185 } 186 187 private void updateOnePassSignatures(byte data) { 188 for (OnePassSignatureCheck opSignature : opSignatures) { 189 opSignature.getOnePassSignature().update(data); 190 } 191 } 192 193 private void updateOnePassSignatures(byte[] bytes, int offset, int length) { 194 for (OnePassSignatureCheck opSignature : opSignatures) { 195 opSignature.getOnePassSignature().update(bytes, offset, length); 196 } 197 } 198 199 private void updateDetachedSignatures(byte b) { 200 for (DetachedSignatureCheck detachedSignature : detachedSignatures) { 201 detachedSignature.getSignature().update(b); 202 } 203 } 204 205 private void updateDetachedSignatures(byte[] b, int off, int read) { 206 for (DetachedSignatureCheck detachedSignature : detachedSignatures) { 207 detachedSignature.getSignature().update(b, off, read); 208 } 209 } 210 211 } 212}