using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Bcpg.OpenPgp
{
/// A PGP signature object.
public class PgpSignature
{
public const int BinaryDocument = 0x00;
public const int CanonicalTextDocument = 0x01;
public const int StandAlone = 0x02;
public const int DefaultCertification = 0x10;
public const int NoCertification = 0x11;
public const int CasualCertification = 0x12;
public const int PositiveCertification = 0x13;
public const int SubkeyBinding = 0x18;
public const int PrimaryKeyBinding = 0x19;
public const int DirectKey = 0x1f;
public const int KeyRevocation = 0x20;
public const int SubkeyRevocation = 0x28;
public const int CertificationRevocation = 0x30;
public const int Timestamp = 0x40;
private readonly SignaturePacket sigPck;
private readonly int signatureType;
private readonly TrustPacket trustPck;
private ISigner sig;
private byte lastb; // Initial value anything but '\r'
internal PgpSignature(
BcpgInputStream bcpgInput)
: this((SignaturePacket)bcpgInput.ReadPacket())
{
}
internal PgpSignature(
SignaturePacket sigPacket)
: this(sigPacket, null)
{
}
internal PgpSignature(
SignaturePacket sigPacket,
TrustPacket trustPacket)
{
if (sigPacket == null)
throw new ArgumentNullException("sigPacket");
this.sigPck = sigPacket;
this.signatureType = sigPck.SignatureType;
this.trustPck = trustPacket;
}
private void GetSig()
{
this.sig = SignerUtilities.GetSigner(
PgpUtilities.GetSignatureName(sigPck.KeyAlgorithm, sigPck.HashAlgorithm));
}
/// The OpenPGP version number for this signature.
public int Version
{
get { return sigPck.Version; }
}
/// The key algorithm associated with this signature.
public PublicKeyAlgorithmTag KeyAlgorithm
{
get { return sigPck.KeyAlgorithm; }
}
/// The hash algorithm associated with this signature.
public HashAlgorithmTag HashAlgorithm
{
get { return sigPck.HashAlgorithm; }
}
public void InitVerify(
PgpPublicKey pubKey)
{
lastb = 0;
if (sig == null)
{
GetSig();
}
try
{
sig.Init(false, pubKey.GetKey());
}
catch (InvalidKeyException e)
{
throw new PgpException("invalid key.", e);
}
}
public void Update(
byte b)
{
if (signatureType == CanonicalTextDocument)
{
doCanonicalUpdateByte(b);
}
else
{
sig.Update(b);
}
}
private void doCanonicalUpdateByte(
byte b)
{
if (b == '\r')
{
doUpdateCRLF();
}
else if (b == '\n')
{
if (lastb != '\r')
{
doUpdateCRLF();
}
}
else
{
sig.Update(b);
}
lastb = b;
}
private void doUpdateCRLF()
{
sig.Update((byte)'\r');
sig.Update((byte)'\n');
}
public void Update(
params byte[] bytes)
{
Update(bytes, 0, bytes.Length);
}
public void Update(
byte[] bytes,
int off,
int length)
{
if (signatureType == CanonicalTextDocument)
{
int finish = off + length;
for (int i = off; i != finish; i++)
{
doCanonicalUpdateByte(bytes[i]);
}
}
else
{
sig.BlockUpdate(bytes, off, length);
}
}
public bool Verify()
{
byte[] trailer = GetSignatureTrailer();
sig.BlockUpdate(trailer, 0, trailer.Length);
return sig.VerifySignature(GetSignature());
}
private void UpdateWithIdData(
int header,
byte[] idBytes)
{
this.Update(
(byte) header,
(byte)(idBytes.Length >> 24),
(byte)(idBytes.Length >> 16),
(byte)(idBytes.Length >> 8),
(byte)(idBytes.Length));
this.Update(idBytes);
}
private void UpdateWithPublicKey(
PgpPublicKey key)
{
byte[] keyBytes = GetEncodedPublicKey(key);
this.Update(
(byte) 0x99,
(byte)(keyBytes.Length >> 8),
(byte)(keyBytes.Length));
this.Update(keyBytes);
}
///
/// Verify the signature as certifying the passed in public key as associated
/// with the passed in user attributes.
///
/// User attributes the key was stored under.
/// The key to be verified.
/// True, if the signature matches, false otherwise.
public bool VerifyCertification(
PgpUserAttributeSubpacketVector userAttributes,
PgpPublicKey key)
{
UpdateWithPublicKey(key);
//
// hash in the userAttributes
//
try
{
MemoryStream bOut = new MemoryStream();
foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray())
{
packet.Encode(bOut);
}
UpdateWithIdData(0xd1, bOut.ToArray());
}
catch (IOException e)
{
throw new PgpException("cannot encode subpacket array", e);
}
this.Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(this.GetSignature());
}
///
/// Verify the signature as certifying the passed in public key as associated
/// with the passed in ID.
///
/// ID the key was stored under.
/// The key to be verified.
/// True, if the signature matches, false otherwise.
public bool VerifyCertification(
string id,
PgpPublicKey key)
{
UpdateWithPublicKey(key);
//
// hash in the id
//
UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id));
Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(GetSignature());
}
/// Verify a certification for the passed in key against the passed in master key.
/// The key we are verifying against.
/// The key we are verifying.
/// True, if the certification is valid, false otherwise.
public bool VerifyCertification(
PgpPublicKey masterKey,
PgpPublicKey pubKey)
{
UpdateWithPublicKey(masterKey);
UpdateWithPublicKey(pubKey);
Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(GetSignature());
}
/// Verify a key certification, such as revocation, for the passed in key.
/// The key we are checking.
/// True, if the certification is valid, false otherwise.
public bool VerifyCertification(
PgpPublicKey pubKey)
{
if (SignatureType != KeyRevocation
&& SignatureType != SubkeyRevocation)
{
throw new InvalidOperationException("signature is not a key signature");
}
UpdateWithPublicKey(pubKey);
Update(sigPck.GetSignatureTrailer());
return sig.VerifySignature(GetSignature());
}
public int SignatureType
{
get { return sigPck.SignatureType; }
}
/// The ID of the key that created the signature.
public long KeyId
{
get { return sigPck.KeyId; }
}
[Obsolete("Use 'CreationTime' property instead")]
public DateTime GetCreationTime()
{
return CreationTime;
}
/// The creation time of this signature.
public DateTime CreationTime
{
get { return DateTimeUtilities.UnixMsToDateTime(sigPck.CreationTime); }
}
public byte[] GetSignatureTrailer()
{
return sigPck.GetSignatureTrailer();
}
///
/// Return true if the signature has either hashed or unhashed subpackets.
///
public bool HasSubpackets
{
get
{
return sigPck.GetHashedSubPackets() != null
|| sigPck.GetUnhashedSubPackets() != null;
}
}
public PgpSignatureSubpacketVector GetHashedSubPackets()
{
return createSubpacketVector(sigPck.GetHashedSubPackets());
}
public PgpSignatureSubpacketVector GetUnhashedSubPackets()
{
return createSubpacketVector(sigPck.GetUnhashedSubPackets());
}
private PgpSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks)
{
return pcks == null ? null : new PgpSignatureSubpacketVector(pcks);
}
public byte[] GetSignature()
{
MPInteger[] sigValues = sigPck.GetSignature();
byte[] signature;
if (sigValues != null)
{
if (sigValues.Length == 1) // an RSA signature
{
signature = sigValues[0].Value.ToByteArrayUnsigned();
}
else
{
try
{
signature = new DerSequence(
new DerInteger(sigValues[0].Value),
new DerInteger(sigValues[1].Value)).GetEncoded();
}
catch (IOException e)
{
throw new PgpException("exception encoding DSA sig.", e);
}
}
}
else
{
signature = sigPck.GetSignatureBytes();
}
return signature;
}
// TODO Handle the encoding stuff by subclassing BcpgObject?
public byte[] GetEncoded()
{
MemoryStream bOut = new MemoryStream();
Encode(bOut);
return bOut.ToArray();
}
public void Encode(
Stream outStream)
{
BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStream);
bcpgOut.WritePacket(sigPck);
if (trustPck != null)
{
bcpgOut.WritePacket(trustPck);
}
}
private byte[] GetEncodedPublicKey(
PgpPublicKey pubKey)
{
try
{
return pubKey.publicPk.GetEncodedContents();
}
catch (IOException e)
{
throw new PgpException("exception preparing key.", e);
}
}
}
}