using System; using System.Collections.Generic; using System.IO; namespace Org.BouncyCastle.Bcpg.OpenPgp { /// /// General class for reading a PGP object stream. ///

/// Note: if this class finds a PgpPublicKey or a PgpSecretKey it /// will create a PgpPublicKeyRing, or a PgpSecretKeyRing for each /// key found. If all you are trying to do is read a key ring file use /// either PgpPublicKeyRingBundle or PgpSecretKeyRingBundle.

///
public class PgpObjectFactory { private readonly BcpgInputStream bcpgIn; public PgpObjectFactory(Stream inputStream) { this.bcpgIn = BcpgInputStream.Wrap(inputStream); } public PgpObjectFactory(byte[] bytes) : this(new MemoryStream(bytes, false)) { } /// Return the next object in the stream, or null if the end is reached. /// On a parse error public PgpObject NextPgpObject() { PacketTag tag = bcpgIn.NextPacketTag(); if ((int) tag == -1) return null; switch (tag) { case PacketTag.Signature: { var l = new List(); while (bcpgIn.NextPacketTag() == PacketTag.Signature) { try { l.Add(new PgpSignature(bcpgIn)); } catch (UnsupportedPacketVersionException) { // Signatures of unsupported version MUST BE ignored // see: https://tests.sequoia-pgp.org/#Detached_signatures_with_unknown_packets continue; } catch (PgpException e) { throw new IOException("can't create signature object: " + e); } } return new PgpSignatureList(l.ToArray()); } case PacketTag.SecretKey: try { return new PgpSecretKeyRing(bcpgIn); } catch (PgpException e) { throw new IOException("can't create secret key object: " + e); } case PacketTag.PublicKey: return new PgpPublicKeyRing(bcpgIn); case PacketTag.PublicSubkey: return PgpPublicKeyRing.ReadSubkey(bcpgIn); case PacketTag.CompressedData: return new PgpCompressedData(bcpgIn); case PacketTag.LiteralData: return new PgpLiteralData(bcpgIn); case PacketTag.PublicKeyEncryptedSession: case PacketTag.SymmetricKeyEncryptedSessionKey: return new PgpEncryptedDataList(bcpgIn); case PacketTag.OnePassSignature: { var l = new List(); while (bcpgIn.NextPacketTag() == PacketTag.OnePassSignature) { try { l.Add(new PgpOnePassSignature(bcpgIn)); } catch (PgpException e) { throw new IOException("can't create one pass signature object: " + e); } } return new PgpOnePassSignatureList(l.ToArray()); } case PacketTag.Marker: return new PgpMarker(bcpgIn); case PacketTag.Experimental1: case PacketTag.Experimental2: case PacketTag.Experimental3: case PacketTag.Experimental4: return new PgpExperimental(bcpgIn); } throw new IOException("unknown object in stream " + bcpgIn.NextPacketTag()); } /// /// Return all available objects in a list. /// /// An IList containing all objects from this factory, in order. public IList AllPgpObjects() { var result = new List(); PgpObject pgpObject; while ((pgpObject = NextPgpObject()) != null) { result.Add(pgpObject); } return result; } /// /// Read all available objects, returning only those that are assignable to the specified type. /// /// An containing the filtered objects from this factory, in order. public IList FilterPgpObjects() where T : PgpObject { var result = new List(); PgpObject pgpObject; while ((pgpObject = NextPgpObject()) != null) { if (pgpObject is T t) { result.Add(t); } } return result; } } }