blob: 812bcad8c17ca2d20e4af4250a2fd6ca28d894d5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2012 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.signedcontent;
import java.io.FilterInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.eclipse.osgi.signedcontent.InvalidContentException;
import org.eclipse.osgi.signedcontent.SignerInfo;
import org.eclipse.osgi.storage.bundlefile.BundleEntry;
import org.eclipse.osgi.storage.bundlefile.BundleFile;
import org.eclipse.osgi.util.NLS;
/**
* This InputStream will calculate the digest of bytes as they are read. At the
* end of the InputStream, it will calculate the digests and throw an exception
* if the calculated digest do not match the expected digests.
*/
class DigestedInputStream extends FilterInputStream {
private final MessageDigest digests[];
private final byte result[][];
private final BundleEntry entry;
private final BundleFile bundleFile;
private long remaining;
/**
* Constructs an InputStream that uses another InputStream as a source and
* calculates the digest. At the end of the stream an exception will be
* thrown if the calculated digest doesn't match the passed digest.
*
* @param in the stream to use as an input source.
* @param signerInfos the signers.
* @param results the expected digest.
* @throws IOException
* @throws NoSuchAlgorithmException
*/
DigestedInputStream(BundleEntry entry, BundleFile bundleFile, SignerInfo[] signerInfos, byte results[][], long size) throws IOException, NoSuchAlgorithmException {
super(entry.getInputStream());
this.entry = entry;
this.bundleFile = bundleFile;
this.remaining = size;
this.digests = new MessageDigest[signerInfos.length];
for (int i = 0; i < signerInfos.length; i++)
this.digests[i] = MessageDigest.getInstance(signerInfos[i].getMessageDigestAlgorithm());
this.result = results;
}
/**
* Not supported.
*/
@Override
public synchronized void mark(int readlimit) {
// Noop, we don't want to support this
}
/**
* Always returns false.
*/
@Override
public boolean markSupported() {
return false;
}
/**
* Read a byte from the InputStream. Digests are calculated on reads. At the
* end of the stream the calculated digests must match the expected digests.
*
* @return the character read or -1 at end of stream.
* @throws IOException if there was an problem reading the byte or at the
* end of the stream the calculated digests do not match the
* expected digests.
* @see java.io.InputStream#read()
*/
@Override
public int read() throws IOException {
if (remaining <= 0)
return -1;
int c = super.read();
if (c != -1) {
for (MessageDigest digest : digests) {
digest.update((byte) c);
}
remaining--;
} else {
// We hit eof so set remaining to zero
remaining = 0;
}
if (remaining == 0)
verifyDigests();
return c;
}
private void verifyDigests() throws InvalidContentException {
// Check the digest at end of file
for (int i = 0; i < digests.length; i++) {
byte rc[] = digests[i].digest();
if (!MessageDigest.isEqual(result[i], rc))
throw new InvalidContentException(NLS.bind(SignedContentMessages.File_In_Jar_Is_Tampered, entry.getName(), bundleFile.getBaseFile()), null);
}
}
/**
* Read bytes from the InputStream. Digests are calculated on reads. At the
* end of the stream the calculated digests must match the expected digests.
*
* @return the number of characters read or -1 at end of stream.
* @throws IOException if there was an problem reading or at the
* end of the stream the calculated digests do not match the
* expected digests.
* @see java.io.InputStream#read()
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (remaining <= 0)
return -1;
int rc = super.read(b, off, len);
if (rc != -1) {
for (MessageDigest digest : digests) {
digest.update(b, off, rc);
}
remaining -= rc;
} else {
// We hit eof so set remaining to zero
remaining = 0;
}
if (remaining <= 0)
verifyDigests();
return rc;
}
/**
* Not supported.
*
* @throws IOException always thrown if this method is called since mark/reset is not supported.
* @see java.io.InputStream#reset()
*/
@Override
public synchronized void reset() throws IOException {
// Throw IOException, we don't want to support this
throw new IOException("Reset not supported"); //$NON-NLS-1$
}
/**
* This method is implemented as a read into a bitbucket.
*/
@Override
public long skip(long n) throws IOException {
byte buffer[] = new byte[4096];
long count = 0;
while (n - count > 0) {
int rc = (n - count) > buffer.length ? buffer.length : (int) (n - count);
rc = read(buffer, 0, rc);
if (rc == -1)
break;
count += rc;
n -= rc;
}
return count;
}
}