org.bouncycastle.bcpg.BCPGInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bcpg-fips Show documentation
Show all versions of bcpg-fips Show documentation
The Bouncy Castle Java APIs for the OpenPGP Protocol. The APIs are designed primarily to be used in conjunction with the BC FIPS provider. The APIs may also be used with other providers although if being used in a FIPS context it is the responsibility of the user to ensure that any other providers used are FIPS certified and used appropriately.
package org.bouncycastle.bcpg;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import org.bouncycastle.util.io.Streams;
/**
* Stream reader for PGP objects
*/
public class BCPGInputStream
extends InputStream implements PacketTags
{
InputStream in;
boolean next = false;
int nextB;
boolean mNext = false;
int mNextB;
public BCPGInputStream(
InputStream in)
{
this.in = in;
}
/**
* If the argument is a {@link BCPGInputStream}, return it.
* Otherwise wrap it in a {@link BCPGInputStream} and then return the result.
*
* @param in input stream
* @return BCPGInputStream
*/
public static BCPGInputStream wrap(InputStream in)
{
if (in instanceof BCPGInputStream)
{
return (BCPGInputStream)in;
}
return new BCPGInputStream(in);
}
public int available()
throws IOException
{
return in.available();
}
public boolean markSupported()
{
return in.markSupported();
}
public synchronized void mark(int readLimit)
{
mNext = next;
mNextB = nextB;
in.mark(readLimit);
}
public synchronized void reset()
throws IOException
{
next = mNext;
nextB = mNextB;
in.reset();
}
public int read()
throws IOException
{
if (next)
{
next = false;
return nextB;
}
else
{
return in.read();
}
}
public int read(
byte[] buf,
int off,
int len)
throws IOException
{
if (len == 0)
{
return 0;
}
if (!next)
{
return in.read(buf, off, len);
}
// We have next byte waiting, so return it
if (nextB < 0)
{
return -1; // EOF
}
buf[off] = (byte)nextB; // May throw NullPointerException...
next = false; // ...so only set this afterwards
return 1;
}
public void readFully(
byte[] buf,
int off,
int len)
throws IOException
{
if (Streams.readFully(this, buf, off, len) < len)
{
throw new EOFException();
}
}
public byte[] readAll()
throws IOException
{
return Streams.readAll(this);
}
public void readFully(
byte[] buf)
throws IOException
{
readFully(buf, 0, buf.length);
}
/**
* Obtains the tag of the next packet in the stream.
*
* @return the {@link PacketTags tag number}.
*
* @throws IOException if an error occurs reading the tag from the stream.
*/
public int nextPacketTag()
throws IOException
{
if (!next)
{
try
{
nextB = in.read();
}
catch (EOFException e)
{
nextB = -1;
}
next = true;
}
if (nextB < 0)
{
return nextB;
}
int maskB = nextB & 0x3f;
if ((nextB & 0x40) == 0) // old
{
maskB >>= 2;
}
return maskB;
}
/**
* Reads the next packet from the stream.
* @throws IOException
*/
public Packet readPacket()
throws IOException
{
int hdr = this.read();
if (hdr < 0)
{
return null;
}
if ((hdr & 0x80) == 0)
{
throw new IOException("invalid header encountered");
}
boolean newPacket = (hdr & 0x40) != 0;
int tag = 0;
int bodyLen = 0;
boolean partial = false;
if (newPacket)
{
tag = hdr & 0x3f;
int l = this.read();
if (l < 192)
{
bodyLen = l;
}
else if (l <= 223)
{
int b = in.read();
bodyLen = ((l - 192) << 8) + (b) + 192;
}
else if (l == 255)
{
bodyLen = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
}
else
{
partial = true;
bodyLen = 1 << (l & 0x1f);
}
}
else
{
int lengthType = hdr & 0x3;
tag = (hdr & 0x3f) >> 2;
switch (lengthType)
{
case 0:
bodyLen = this.read();
break;
case 1:
bodyLen = (this.read() << 8) | this.read();
break;
case 2:
bodyLen = (this.read() << 24) | (this.read() << 16) | (this.read() << 8) | this.read();
break;
case 3:
partial = true;
break;
default:
throw new IOException("unknown length type encountered");
}
}
BCPGInputStream objStream;
if (bodyLen == 0 && partial)
{
objStream = this;
}
else
{
objStream = new BCPGInputStream(
new BufferedInputStream(new PartialInputStream(this, partial, bodyLen)));
}
switch (tag)
{
case RESERVED:
return new InputStreamPacket(objStream);
case PUBLIC_KEY_ENC_SESSION:
return new PublicKeyEncSessionPacket(objStream);
case SIGNATURE:
return new SignaturePacket(objStream);
case SYMMETRIC_KEY_ENC_SESSION:
return new SymmetricKeyEncSessionPacket(objStream);
case ONE_PASS_SIGNATURE:
return new OnePassSignaturePacket(objStream);
case SECRET_KEY:
return new SecretKeyPacket(objStream);
case PUBLIC_KEY:
return new PublicKeyPacket(objStream);
case SECRET_SUBKEY:
return new SecretSubkeyPacket(objStream);
case COMPRESSED_DATA:
return new CompressedDataPacket(objStream);
case SYMMETRIC_KEY_ENC:
return new SymmetricEncDataPacket(objStream);
case MARKER:
return new MarkerPacket(objStream);
case LITERAL_DATA:
return new LiteralDataPacket(objStream);
case TRUST:
return new TrustPacket(objStream);
case USER_ID:
return new UserIDPacket(objStream);
case USER_ATTRIBUTE:
return new UserAttributePacket(objStream);
case PUBLIC_SUBKEY:
return new PublicSubkeyPacket(objStream);
case SYM_ENC_INTEGRITY_PRO:
return new SymmetricEncIntegrityPacket(objStream);
case MOD_DETECTION_CODE:
return new ModDetectionCodePacket(objStream);
case AEAD_ENC_DATA:
return new AEADEncDataPacket(objStream);
case EXPERIMENTAL_1:
case EXPERIMENTAL_2:
case EXPERIMENTAL_3:
case EXPERIMENTAL_4:
return new ExperimentalPacket(tag, objStream);
default:
throw new IOException("unknown packet type encountered: " + tag);
}
}
public int skipMarkerPackets()
throws IOException
{
int tag;
while ((tag = nextPacketTag()) == PacketTags.MARKER)
{
readPacket();
}
return tag;
}
public void close()
throws IOException
{
in.close();
}
/**
* a stream that overlays our input stream, allowing the user to only read a segment of it.
*
* NB: dataLength will be negative if the segment length is in the upper range above 2**31.
*/
private static class PartialInputStream
extends InputStream
{
private BCPGInputStream in;
private boolean partial;
private int dataLength;
PartialInputStream(
BCPGInputStream in,
boolean partial,
int dataLength)
{
this.in = in;
this.partial = partial;
this.dataLength = dataLength;
}
public int available()
throws IOException
{
int avail = in.available();
if (avail <= dataLength || dataLength < 0)
{
return avail;
}
else
{
if (partial && dataLength == 0)
{
return 1;
}
return dataLength;
}
}
private int loadDataLength()
throws IOException
{
int l = in.read();
if (l < 0)
{
return -1;
}
partial = false;
if (l < 192)
{
dataLength = l;
}
else if (l <= 223)
{
dataLength = ((l - 192) << 8) + (in.read()) + 192;
}
else if (l == 255)
{
dataLength = (in.read() << 24) | (in.read() << 16) | (in.read() << 8) | in.read();
}
else
{
partial = true;
dataLength = 1 << (l & 0x1f);
}
return dataLength;
}
public int read(byte[] buf, int offset, int len)
throws IOException
{
do
{
if (dataLength != 0)
{
int readLen = (dataLength > len || dataLength < 0) ? len : dataLength;
readLen = in.read(buf, offset, readLen);
if (readLen < 0)
{
throw new EOFException("premature end of stream in PartialInputStream");
}
dataLength -= readLen;
if (partial && dataLength == 0)
{
loadDataLength();
}
return readLen;
}
}
while (partial && loadDataLength() >= 0);
return -1;
}
public int read()
throws IOException
{
do
{
if (dataLength != 0)
{
int ch = in.read();
if (ch < 0)
{
throw new EOFException("premature end of stream in PartialInputStream");
}
dataLength--;
if (partial && dataLength == 0)
{
loadDataLength();
}
return ch;
}
}
while (partial && loadDataLength() >= 0);
return -1;
}
}
}