using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Bcpg.Sig;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Bcpg
{
/// Generic signature packet.
public class SignaturePacket
: ContainedPacket //, PublicKeyAlgorithmTag
{
private int version;
private int signatureType;
private long creationTime;
private long keyId;
private PublicKeyAlgorithmTag keyAlgorithm;
private HashAlgorithmTag hashAlgorithm;
private MPInteger[] signature;
private byte[] fingerprint;
private SignatureSubpacket[] hashedData;
private SignatureSubpacket[] unhashedData;
private byte[] signatureEncoding;
internal SignaturePacket(
BcpgInputStream bcpgIn)
{
version = bcpgIn.ReadByte();
if (version == 3 || version == 2)
{
// int l =
bcpgIn.ReadByte();
signatureType = bcpgIn.ReadByte();
creationTime = (((long)bcpgIn.ReadByte() << 24) | ((long)bcpgIn.ReadByte() << 16)
| ((long)bcpgIn.ReadByte() << 8) | (uint)bcpgIn.ReadByte()) * 1000L;
keyId |= (long)bcpgIn.ReadByte() << 56;
keyId |= (long)bcpgIn.ReadByte() << 48;
keyId |= (long)bcpgIn.ReadByte() << 40;
keyId |= (long)bcpgIn.ReadByte() << 32;
keyId |= (long)bcpgIn.ReadByte() << 24;
keyId |= (long)bcpgIn.ReadByte() << 16;
keyId |= (long)bcpgIn.ReadByte() << 8;
keyId |= (uint)bcpgIn.ReadByte();
keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
}
else if (version == 4)
{
signatureType = bcpgIn.ReadByte();
keyAlgorithm = (PublicKeyAlgorithmTag) bcpgIn.ReadByte();
hashAlgorithm = (HashAlgorithmTag) bcpgIn.ReadByte();
int hashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
byte[] hashed = new byte[hashedLength];
bcpgIn.ReadFully(hashed);
//
// read the signature sub packet data.
//
SignatureSubpacketsParser sIn = new SignatureSubpacketsParser(
new MemoryStream(hashed, false));
IList v = Platform.CreateArrayList();
SignatureSubpacket sub;
while ((sub = sIn.ReadPacket()) != null)
{
v.Add(sub);
}
hashedData = new SignatureSubpacket[v.Count];
for (int i = 0; i != hashedData.Length; i++)
{
SignatureSubpacket p = (SignatureSubpacket)v[i];
if (p is IssuerKeyId)
{
keyId = ((IssuerKeyId)p).KeyId;
}
else if (p is SignatureCreationTime)
{
creationTime = DateTimeUtilities.DateTimeToUnixMs(
((SignatureCreationTime)p).GetTime());
}
hashedData[i] = p;
}
int unhashedLength = (bcpgIn.ReadByte() << 8) | bcpgIn.ReadByte();
byte[] unhashed = new byte[unhashedLength];
bcpgIn.ReadFully(unhashed);
sIn = new SignatureSubpacketsParser(new MemoryStream(unhashed, false));
v.Clear();
while ((sub = sIn.ReadPacket()) != null)
{
v.Add(sub);
}
unhashedData = new SignatureSubpacket[v.Count];
for (int i = 0; i != unhashedData.Length; i++)
{
SignatureSubpacket p = (SignatureSubpacket)v[i];
if (p is IssuerKeyId)
{
keyId = ((IssuerKeyId)p).KeyId;
}
unhashedData[i] = p;
}
}
else
{
throw new Exception("unsupported version: " + version);
}
fingerprint = new byte[2];
bcpgIn.ReadFully(fingerprint);
switch (keyAlgorithm)
{
case PublicKeyAlgorithmTag.RsaGeneral:
case PublicKeyAlgorithmTag.RsaSign:
MPInteger v = new MPInteger(bcpgIn);
signature = new MPInteger[]{ v };
break;
case PublicKeyAlgorithmTag.Dsa:
MPInteger r = new MPInteger(bcpgIn);
MPInteger s = new MPInteger(bcpgIn);
signature = new MPInteger[]{ r, s };
break;
case PublicKeyAlgorithmTag.ElGamalEncrypt: // yep, this really does happen sometimes.
case PublicKeyAlgorithmTag.ElGamalGeneral:
MPInteger p = new MPInteger(bcpgIn);
MPInteger g = new MPInteger(bcpgIn);
MPInteger y = new MPInteger(bcpgIn);
signature = new MPInteger[]{ p, g, y };
break;
case PublicKeyAlgorithmTag.ECDsa:
MPInteger ecR = new MPInteger(bcpgIn);
MPInteger ecS = new MPInteger(bcpgIn);
signature = new MPInteger[]{ ecR, ecS };
break;
default:
if (keyAlgorithm >= PublicKeyAlgorithmTag.Experimental_1 && keyAlgorithm <= PublicKeyAlgorithmTag.Experimental_11)
{
signature = null;
MemoryStream bOut = new MemoryStream();
int ch;
while ((ch = bcpgIn.ReadByte()) >= 0)
{
bOut.WriteByte((byte) ch);
}
signatureEncoding = bOut.ToArray();
}
else
{
throw new IOException("unknown signature key algorithm: " + keyAlgorithm);
}
break;
}
}
/**
* Generate a version 4 signature packet.
*
* @param signatureType
* @param keyAlgorithm
* @param hashAlgorithm
* @param hashedData
* @param unhashedData
* @param fingerprint
* @param signature
*/
public SignaturePacket(
int signatureType,
long keyId,
PublicKeyAlgorithmTag keyAlgorithm,
HashAlgorithmTag hashAlgorithm,
SignatureSubpacket[] hashedData,
SignatureSubpacket[] unhashedData,
byte[] fingerprint,
MPInteger[] signature)
: this(4, signatureType, keyId, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerprint, signature)
{
}
/**
* Generate a version 2/3 signature packet.
*
* @param signatureType
* @param keyAlgorithm
* @param hashAlgorithm
* @param fingerprint
* @param signature
*/
public SignaturePacket(
int version,
int signatureType,
long keyId,
PublicKeyAlgorithmTag keyAlgorithm,
HashAlgorithmTag hashAlgorithm,
long creationTime,
byte[] fingerprint,
MPInteger[] signature)
: this(version, signatureType, keyId, keyAlgorithm, hashAlgorithm, null, null, fingerprint, signature)
{
this.creationTime = creationTime;
}
public SignaturePacket(
int version,
int signatureType,
long keyId,
PublicKeyAlgorithmTag keyAlgorithm,
HashAlgorithmTag hashAlgorithm,
SignatureSubpacket[] hashedData,
SignatureSubpacket[] unhashedData,
byte[] fingerprint,
MPInteger[] signature)
{
this.version = version;
this.signatureType = signatureType;
this.keyId = keyId;
this.keyAlgorithm = keyAlgorithm;
this.hashAlgorithm = hashAlgorithm;
this.hashedData = hashedData;
this.unhashedData = unhashedData;
this.fingerprint = fingerprint;
this.signature = signature;
if (hashedData != null)
{
setCreationTime();
}
}
public int Version
{
get { return version; }
}
public int SignatureType
{
get { return signatureType; }
}
/**
* return the keyId
* @return the keyId that created the signature.
*/
public long KeyId
{
get { return keyId; }
}
/**
* return the signature trailer that must be included with the data
* to reconstruct the signature
*
* @return byte[]
*/
public byte[] GetSignatureTrailer()
{
byte[] trailer = null;
if (version == 3)
{
trailer = new byte[5];
long time = creationTime / 1000L;
trailer[0] = (byte)signatureType;
trailer[1] = (byte)(time >> 24);
trailer[2] = (byte)(time >> 16);
trailer[3] = (byte)(time >> 8);
trailer[4] = (byte)(time);
}
else
{
MemoryStream sOut = new MemoryStream();
sOut.WriteByte((byte)this.Version);
sOut.WriteByte((byte)this.SignatureType);
sOut.WriteByte((byte)this.KeyAlgorithm);
sOut.WriteByte((byte)this.HashAlgorithm);
MemoryStream hOut = new MemoryStream();
SignatureSubpacket[] hashed = this.GetHashedSubPackets();
for (int i = 0; i != hashed.Length; i++)
{
hashed[i].Encode(hOut);
}
byte[] data = hOut.ToArray();
sOut.WriteByte((byte)(data.Length >> 8));
sOut.WriteByte((byte)data.Length);
sOut.Write(data, 0, data.Length);
byte[] hData = sOut.ToArray();
sOut.WriteByte((byte)this.Version);
sOut.WriteByte((byte)0xff);
sOut.WriteByte((byte)(hData.Length>> 24));
sOut.WriteByte((byte)(hData.Length >> 16));
sOut.WriteByte((byte)(hData.Length >> 8));
sOut.WriteByte((byte)(hData.Length));
trailer = sOut.ToArray();
}
return trailer;
}
public PublicKeyAlgorithmTag KeyAlgorithm
{
get { return keyAlgorithm; }
}
public HashAlgorithmTag HashAlgorithm
{
get { return hashAlgorithm; }
}
/**
* return the signature as a set of integers - note this is normalised to be the
* ASN.1 encoding of what appears in the signature packet.
*/
public MPInteger[] GetSignature()
{
return signature;
}
/**
* Return the byte encoding of the signature section.
* @return uninterpreted signature bytes.
*/
public byte[] GetSignatureBytes()
{
if (signatureEncoding != null)
{
return (byte[]) signatureEncoding.Clone();
}
MemoryStream bOut = new MemoryStream();
BcpgOutputStream bcOut = new BcpgOutputStream(bOut);
foreach (MPInteger sigObj in signature)
{
try
{
bcOut.WriteObject(sigObj);
}
catch (IOException e)
{
throw new Exception("internal error: " + e);
}
}
return bOut.ToArray();
}
public SignatureSubpacket[] GetHashedSubPackets()
{
return hashedData;
}
public SignatureSubpacket[] GetUnhashedSubPackets()
{
return unhashedData;
}
/// Return the creation time in milliseconds since 1 Jan., 1970 UTC.
public long CreationTime
{
get { return creationTime; }
}
public override void Encode(
BcpgOutputStream bcpgOut)
{
MemoryStream bOut = new MemoryStream();
BcpgOutputStream pOut = new BcpgOutputStream(bOut);
pOut.WriteByte((byte) version);
if (version == 3 || version == 2)
{
pOut.Write(
5, // the length of the next block
(byte) signatureType);
pOut.WriteInt((int)(creationTime / 1000L));
pOut.WriteLong(keyId);
pOut.Write(
(byte) keyAlgorithm,
(byte) hashAlgorithm);
}
else if (version == 4)
{
pOut.Write(
(byte) signatureType,
(byte) keyAlgorithm,
(byte) hashAlgorithm);
EncodeLengthAndData(pOut, GetEncodedSubpackets(hashedData));
EncodeLengthAndData(pOut, GetEncodedSubpackets(unhashedData));
}
else
{
throw new IOException("unknown version: " + version);
}
pOut.Write(fingerprint);
if (signature != null)
{
pOut.WriteObjects(signature);
}
else
{
pOut.Write(signatureEncoding);
}
bcpgOut.WritePacket(PacketTag.Signature, bOut.ToArray(), true);
}
private static void EncodeLengthAndData(
BcpgOutputStream pOut,
byte[] data)
{
pOut.WriteShort((short) data.Length);
pOut.Write(data);
}
private static byte[] GetEncodedSubpackets(
SignatureSubpacket[] ps)
{
MemoryStream sOut = new MemoryStream();
foreach (SignatureSubpacket p in ps)
{
p.Encode(sOut);
}
return sOut.ToArray();
}
private void setCreationTime()
{
foreach (SignatureSubpacket p in hashedData)
{
if (p is SignatureCreationTime)
{
creationTime = DateTimeUtilities.DateTimeToUnixMs(
((SignatureCreationTime)p).GetTime());
break;
}
}
}
public static SignaturePacket FromByteArray(byte[] data)
{
BcpgInputStream input = BcpgInputStream.Wrap(new MemoryStream(data));
return new SignaturePacket(input);
}
}
}