summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpObjectFactory.cs
blob: f7bf89507bafa8304683981203b3e6a81af9c97f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using System;
using System.Collections.Generic;
using System.IO;

namespace Org.BouncyCastle.Bcpg.OpenPgp
{
	/// <remarks>
    /// General class for reading a PGP object stream.
    /// <p>
    /// 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.</p>
	/// </remarks>
	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))
        {
        }

		/// <summary>Return the next object in the stream, or null if the end is reached.</summary>
		/// <exception cref="IOException">On a parse error</exception>
        public PgpObject NextPgpObject()
        {
            PacketTag tag = bcpgIn.NextPacketTag();

            if ((int) tag == -1)
                return null;

            switch (tag)
            {
            case PacketTag.Signature:
            {
                var l = new List<PgpSignature>();

                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);
			// TODO Make PgpPublicKey a PgpObject or return a PgpPublicKeyRing
			//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<PgpOnePassSignature>();

                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());
        }

		/// <summary>
		/// Return all available objects in a list.
		/// </summary>
		/// <returns>An <c>IList</c> containing all objects from this factory, in order.</returns>
		public IList<PgpObject> AllPgpObjects()
		{
            var result = new List<PgpObject>();
			PgpObject pgpObject;
			while ((pgpObject = NextPgpObject()) != null)
			{
				result.Add(pgpObject);
			}
			return result;
		}

        /// <summary>
        /// Read all available objects, returning only those that are assignable to the specified type.
        /// </summary>
        /// <returns>An <see cref="IList{T}"/> containing the filtered objects from this factory, in order.</returns>
        public IList<T> FilterPgpObjects<T>()
            where T : PgpObject
        {
            var result = new List<T>();
            PgpObject pgpObject;
            while ((pgpObject = NextPgpObject()) != null)
            {
                if (pgpObject is T t)
                {
                    result.Add(t);
                }
            }
            return result;
        }
    }
}