001/* 002 * Copyright 2018 Paul Schaub. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.pgpainless.decryption_verification; 017 018import javax.annotation.Nonnull; 019import java.io.FilterInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.security.SignatureException; 023import java.util.Map; 024import java.util.logging.Level; 025import java.util.logging.Logger; 026 027import org.bouncycastle.openpgp.PGPException; 028import org.bouncycastle.openpgp.PGPObjectFactory; 029import org.bouncycastle.openpgp.PGPOnePassSignature; 030import org.bouncycastle.openpgp.PGPSignature; 031import org.bouncycastle.openpgp.PGPSignatureList; 032import org.pgpainless.key.OpenPgpV4Fingerprint; 033 034public class SignatureVerifyingInputStream extends FilterInputStream { 035 036 private static final Logger LOGGER = Logger.getLogger(SignatureVerifyingInputStream.class.getName()); 037 private static final Level LEVEL = Level.INFO; 038 039 private final PGPObjectFactory objectFactory; 040 private final Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures; 041 private final OpenPgpMetadata.Builder resultBuilder; 042 043 private boolean validated = false; 044 045 protected SignatureVerifyingInputStream(@Nonnull InputStream inputStream, 046 @Nonnull PGPObjectFactory objectFactory, 047 @Nonnull Map<OpenPgpV4Fingerprint, PGPOnePassSignature> onePassSignatures, 048 @Nonnull OpenPgpMetadata.Builder resultBuilder) { 049 super(inputStream); 050 this.objectFactory = objectFactory; 051 this.resultBuilder = resultBuilder; 052 this.onePassSignatures = onePassSignatures; 053 054 LOGGER.log(LEVEL, "Begin verifying OnePassSignatures"); 055 } 056 057 private void updateOnePassSignatures(byte data) { 058 for (PGPOnePassSignature signature : onePassSignatures.values()) { 059 signature.update(data); 060 } 061 } 062 063 private void updateOnePassSignatures(byte[] b, int off, int len) { 064 for (PGPOnePassSignature signature : onePassSignatures.values()) { 065 signature.update(b, off, len); 066 } 067 } 068 069 private void validateOnePassSignatures() throws IOException { 070 071 if (validated) { 072 LOGGER.log(LEVEL, "Validated signatures already. Skip"); 073 return; 074 } 075 076 validated = true; 077 078 if (onePassSignatures.isEmpty()) { 079 LOGGER.log(LEVEL, "No One-Pass-Signatures found -> No validation"); 080 return; 081 } 082 083 try { 084 PGPSignatureList signatureList = null; 085 Object obj = objectFactory.nextObject(); 086 while (obj != null && signatureList == null) { 087 if (obj instanceof PGPSignatureList) { 088 signatureList = (PGPSignatureList) obj; 089 } else { 090 obj = objectFactory.nextObject(); 091 } 092 } 093 094 if (signatureList == null || signatureList.isEmpty()) { 095 throw new IOException("Verification failed - No Signatures found"); 096 } 097 098 for (PGPSignature signature : signatureList) { 099 OpenPgpV4Fingerprint fingerprint = null; 100 for (OpenPgpV4Fingerprint f : onePassSignatures.keySet()) { 101 if (f.getKeyId() == signature.getKeyID()) { 102 fingerprint = f; 103 break; 104 } 105 } 106 107 PGPOnePassSignature onePassSignature; 108 if (fingerprint == null || (onePassSignature = onePassSignatures.get(fingerprint)) == null) { 109 LOGGER.log(LEVEL, "Found Signature without respective OnePassSignature packet -> skip"); 110 continue; 111 } 112 113 if (!onePassSignature.verify(signature)) { 114 throw new SignatureException("Bad Signature of key " + signature.getKeyID()); 115 } else { 116 LOGGER.log(LEVEL, "Verified signature of key " + Long.toHexString(signature.getKeyID())); 117 resultBuilder.addVerifiedSignatureFingerprint(fingerprint); 118 } 119 } 120 } catch (PGPException | SignatureException e) { 121 throw new IOException(e.getMessage(), e); 122 } 123 124 } 125 126 @Override 127 public int read() throws IOException { 128 final int data = super.read(); 129 final boolean endOfStream = data == -1; 130 if (endOfStream) { 131 validateOnePassSignatures(); 132 } else { 133 updateOnePassSignatures((byte) data); 134 } 135 return data; 136 } 137 138 @Override 139 public int read(byte[] b) throws IOException { 140 return read(b, 0, b.length); 141 } 142 143 @Override 144 public int read(byte[] b, int off, int len) throws IOException { 145 int read = super.read(b, off, len); 146 147 final boolean endOfStream = read == -1; 148 if (endOfStream) { 149 validateOnePassSignatures(); 150 } else { 151 updateOnePassSignatures(b, off, read); 152 } 153 return read; 154 } 155 156 @Override 157 public long skip(long n) { 158 throw new UnsupportedOperationException("skip() is not supported"); 159 } 160 161 @Override 162 public synchronized void mark(int readlimit) { 163 throw new UnsupportedOperationException("mark() not supported"); 164 } 165 166 @Override 167 public synchronized void reset() { 168 throw new UnsupportedOperationException("reset() is not supported"); 169 } 170 171 @Override 172 public boolean markSupported() { 173 return false; 174 } 175}