136 lines
3.6 KiB
C#
136 lines
3.6 KiB
C#
using System;
|
|
using System.IO;
|
|
|
|
using Org.BouncyCastle.Crypto;
|
|
using Org.BouncyCastle.Crypto.IO;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using Org.BouncyCastle.Security;
|
|
using Org.BouncyCastle.Utilities.IO;
|
|
|
|
namespace Org.BouncyCastle.Bcpg.OpenPgp
|
|
{
|
|
/// <remarks>A password based encryption object.</remarks>
|
|
public class PgpPbeEncryptedData
|
|
: PgpEncryptedData
|
|
{
|
|
private readonly SymmetricKeyEncSessionPacket keyData;
|
|
|
|
internal PgpPbeEncryptedData(
|
|
SymmetricKeyEncSessionPacket keyData,
|
|
InputStreamPacket encData)
|
|
: base(encData)
|
|
{
|
|
this.keyData = keyData;
|
|
}
|
|
|
|
/// <summary>Return the raw input stream for the data stream.</summary>
|
|
public override Stream GetInputStream()
|
|
{
|
|
return encData.GetInputStream();
|
|
}
|
|
|
|
/// <summary>Return the decrypted input stream, using the passed in passphrase.</summary>
|
|
public Stream GetDataStream(
|
|
char[] passPhrase)
|
|
{
|
|
try
|
|
{
|
|
SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm;
|
|
|
|
KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(
|
|
keyAlgorithm, keyData.S2k, passPhrase);
|
|
|
|
|
|
byte[] secKeyData = keyData.GetSecKeyData();
|
|
if (secKeyData != null && secKeyData.Length > 0)
|
|
{
|
|
IBufferedCipher keyCipher = CipherUtilities.GetCipher(
|
|
PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding");
|
|
|
|
keyCipher.Init(false,
|
|
new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()]));
|
|
|
|
byte[] keyBytes = keyCipher.DoFinal(secKeyData);
|
|
|
|
keyAlgorithm = (SymmetricKeyAlgorithmTag) keyBytes[0];
|
|
|
|
key = ParameterUtilities.CreateKeyParameter(
|
|
PgpUtilities.GetSymmetricCipherName(keyAlgorithm),
|
|
keyBytes, 1, keyBytes.Length - 1);
|
|
}
|
|
|
|
|
|
IBufferedCipher c = CreateStreamCipher(keyAlgorithm);
|
|
|
|
byte[] iv = new byte[c.GetBlockSize()];
|
|
|
|
c.Init(false, new ParametersWithIV(key, iv));
|
|
|
|
encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c, null));
|
|
|
|
if (encData is SymmetricEncIntegrityPacket)
|
|
{
|
|
truncStream = new TruncatedStream(encStream);
|
|
|
|
string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
|
|
IDigest digest = DigestUtilities.GetDigest(digestName);
|
|
|
|
encStream = new DigestStream(truncStream, digest, null);
|
|
}
|
|
|
|
if (Streams.ReadFully(encStream, iv, 0, iv.Length) < iv.Length)
|
|
throw new EndOfStreamException("unexpected end of stream.");
|
|
|
|
int v1 = encStream.ReadByte();
|
|
int v2 = encStream.ReadByte();
|
|
|
|
if (v1 < 0 || v2 < 0)
|
|
throw new EndOfStreamException("unexpected end of stream.");
|
|
|
|
|
|
// Note: the oracle attack on the "quick check" bytes is not deemed
|
|
// a security risk for PBE (see PgpPublicKeyEncryptedData)
|
|
|
|
bool repeatCheckPassed =
|
|
iv[iv.Length - 2] == (byte)v1
|
|
&& iv[iv.Length - 1] == (byte)v2;
|
|
|
|
// Note: some versions of PGP appear to produce 0 for the extra
|
|
// bytes rather than repeating the two previous bytes
|
|
bool zeroesCheckPassed =
|
|
v1 == 0
|
|
&& v2 == 0;
|
|
|
|
if (!repeatCheckPassed && !zeroesCheckPassed)
|
|
{
|
|
throw new PgpDataValidationException("quick check failed.");
|
|
}
|
|
|
|
|
|
return encStream;
|
|
}
|
|
catch (PgpException e)
|
|
{
|
|
throw e;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new PgpException("Exception creating cipher", e);
|
|
}
|
|
}
|
|
|
|
private IBufferedCipher CreateStreamCipher(
|
|
SymmetricKeyAlgorithmTag keyAlgorithm)
|
|
{
|
|
string mode = (encData is SymmetricEncIntegrityPacket)
|
|
? "CFB"
|
|
: "OpenPGPCFB";
|
|
|
|
string cName = PgpUtilities.GetSymmetricCipherName(keyAlgorithm)
|
|
+ "/" + mode + "/NoPadding";
|
|
|
|
return CipherUtilities.GetCipher(cName);
|
|
}
|
|
}
|
|
}
|