using System;
using System.Diagnostics;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
public abstract class PgpEncryptedData
{
internal class TruncatedStream
: BaseInputStream
{
private const int LookAheadSize = 22;
private const int LookAheadBufSize = 512;
private const int LookAheadBufLimit = LookAheadBufSize - LookAheadSize;
private readonly Stream inStr;
private readonly byte[] lookAhead = new byte[LookAheadBufSize];
private int bufStart, bufEnd;
internal TruncatedStream(
Stream inStr)
{
int numRead = Streams.ReadFully(inStr, lookAhead, 0, lookAhead.Length);
if (numRead < LookAheadSize)
throw new EndOfStreamException();
this.inStr = inStr;
this.bufStart = 0;
this.bufEnd = numRead - LookAheadSize;
}
private int FillBuffer()
{
if (bufEnd < LookAheadBufLimit)
return 0;
Debug.Assert(bufStart == LookAheadBufLimit);
Debug.Assert(bufEnd == LookAheadBufLimit);
Array.Copy(lookAhead, LookAheadBufLimit, lookAhead, 0, LookAheadSize);
bufEnd = Streams.ReadFully(inStr, lookAhead, LookAheadSize, LookAheadBufLimit);
bufStart = 0;
return bufEnd;
}
public override int ReadByte()
{
if (bufStart < bufEnd)
return lookAhead[bufStart++];
if (FillBuffer() < 1)
return -1;
return lookAhead[bufStart++];
}
public override int Read(byte[] buf, int off, int len)
{
int avail = bufEnd - bufStart;
int pos = off;
while (len > avail)
{
Array.Copy(lookAhead, bufStart, buf, pos, avail);
bufStart += avail;
pos += avail;
len -= avail;
if ((avail = FillBuffer()) < 1)
return pos - off;
}
Array.Copy(lookAhead, bufStart, buf, pos, len);
bufStart += len;
return pos + len - off;;
}
internal byte[] GetLookAhead()
{
byte[] temp = new byte[LookAheadSize];
Array.Copy(lookAhead, bufStart, temp, 0, LookAheadSize);
return temp;
}
}
internal InputStreamPacket encData;
internal Stream encStream;
internal TruncatedStream truncStream;
internal PgpEncryptedData(
InputStreamPacket encData)
{
this.encData = encData;
}
/// Return the raw input stream for the data stream.
public virtual Stream GetInputStream()
{
return encData.GetInputStream();
}
/// Return true if the message is integrity protected.
/// True, if there is a modification detection code namespace associated
/// with this stream.
public bool IsIntegrityProtected()
{
return encData is SymmetricEncIntegrityPacket;
}
/// Note: This can only be called after the message has been read.
/// True, if the message verifies, false otherwise
public bool Verify()
{
if (!IsIntegrityProtected())
throw new PgpException("data not integrity protected.");
DigestStream dIn = (DigestStream) encStream;
//
// make sure we are at the end.
//
while (encStream.ReadByte() >= 0)
{
// do nothing
}
//
// process the MDC packet
//
byte[] lookAhead = truncStream.GetLookAhead();
IDigest hash = dIn.ReadDigest();
hash.BlockUpdate(lookAhead, 0, 2);
byte[] digest = DigestUtilities.DoFinal(hash);
byte[] streamDigest = new byte[digest.Length];
Array.Copy(lookAhead, 2, streamDigest, 0, streamDigest.Length);
return Arrays.ConstantTimeAreEqual(digest, streamDigest);
}
}
}