summary refs log tree commit diff
path: root/crypto/src/bcpg/SignatureSubpacketsReader.cs
blob: 35831fa9ce9c7a1f8925550a1b6ae6ed740e490b (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
150
using System;
using System.IO;

using Org.BouncyCastle.Bcpg.Sig;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.IO;

namespace Org.BouncyCastle.Bcpg
{
	/**
	* reader for signature sub-packets
	*/
	public class SignatureSubpacketsParser
	{
		private readonly Stream input;

		public SignatureSubpacketsParser(
			Stream input)
		{
			this.input = input;
		}

		public SignatureSubpacket ReadPacket()
		{
			int l = input.ReadByte();
			if (l < 0)
				return null;

			int bodyLen = 0;
            bool isLongLength = false;

            if (l < 192)
			{
				bodyLen = l;
			}
			else if (l <= 223)
			{
				bodyLen = ((l - 192) << 8) + (input.ReadByte()) + 192;
			}
			else if (l == 255)
			{
                isLongLength = true;
				bodyLen = (input.ReadByte() << 24) | (input.ReadByte() << 16)
					|  (input.ReadByte() << 8)  | input.ReadByte();
			}
			else
			{
                throw new IOException("unexpected length header");
			}

            int tag = input.ReadByte();
			if (tag < 0)
				throw new EndOfStreamException("unexpected EOF reading signature sub packet");

            if (bodyLen <= 0)
                throw new EndOfStreamException("out of range data found in signature sub packet");

            byte[] data = new byte[bodyLen - 1];

            //
            // this may seem a bit strange but it turns out some applications miscode the length
            // in fixed length fields, so we check the length we do get, only throwing an exception if
            // we really cannot continue
            //
            int bytesRead = Streams.ReadFully(input, data);

            bool isCritical = ((tag & 0x80) != 0);
            SignatureSubpacketTag type = (SignatureSubpacketTag)(tag & 0x7f);

            if (bytesRead != data.Length)
            {
                switch (type)
                {
                case SignatureSubpacketTag.CreationTime:
                    data = CheckData(data, 4, bytesRead, "Signature Creation Time");
                    break;
                case SignatureSubpacketTag.IssuerKeyId:
                    data = CheckData(data, 8, bytesRead, "Issuer");
                    break;
                case SignatureSubpacketTag.KeyExpireTime:
                    data = CheckData(data, 4, bytesRead, "Signature Key Expiration Time");
                    break;
                case SignatureSubpacketTag.ExpireTime:
                    data = CheckData(data, 4, bytesRead, "Signature Expiration Time");
                    break;
                default:
                    throw new EndOfStreamException("truncated subpacket data.");
                }
            }

            switch (type)
			{
			case SignatureSubpacketTag.CreationTime:
				return new SignatureCreationTime(isCritical, isLongLength, data);
            case SignatureSubpacketTag.EmbeddedSignature:
                return new EmbeddedSignature(isCritical, isLongLength, data);
			case SignatureSubpacketTag.KeyExpireTime:
                return new KeyExpirationTime(isCritical, isLongLength, data);
			case SignatureSubpacketTag.ExpireTime:
                return new SignatureExpirationTime(isCritical, isLongLength, data);
			case SignatureSubpacketTag.Revocable:
                return new Revocable(isCritical, isLongLength, data);
			case SignatureSubpacketTag.Exportable:
                return new Exportable(isCritical, isLongLength, data);
			case SignatureSubpacketTag.Features:
                return new Features(isCritical, isLongLength, data);
			case SignatureSubpacketTag.IssuerKeyId:
                return new IssuerKeyId(isCritical, isLongLength, data);
			case SignatureSubpacketTag.TrustSig:
                return new TrustSignature(isCritical, isLongLength, data);
			case SignatureSubpacketTag.PreferredCompressionAlgorithms:
			case SignatureSubpacketTag.PreferredHashAlgorithms:
			case SignatureSubpacketTag.PreferredSymmetricAlgorithms:
			case SignatureSubpacketTag.PreferredAeadAlgorithms:
                return new PreferredAlgorithms(type, isCritical, isLongLength, data);
			case SignatureSubpacketTag.KeyFlags:
                return new KeyFlags(isCritical, isLongLength, data);
            case SignatureSubpacketTag.PolicyUrl:
                return new PolicyUrl(isCritical, isLongLength, data);
			case SignatureSubpacketTag.PrimaryUserId:
                return new PrimaryUserId(isCritical, isLongLength, data);
			case SignatureSubpacketTag.SignerUserId:
                return new SignerUserId(isCritical, isLongLength, data);
			case SignatureSubpacketTag.NotationData:
                return new NotationData(isCritical, isLongLength, data);
			case SignatureSubpacketTag.RegExp:
                return new RegularExpression(isCritical, isLongLength, data);
            case SignatureSubpacketTag.RevocationReason:
                return new RevocationReason(isCritical, isLongLength, data);
            case SignatureSubpacketTag.RevocationKey:
                return new RevocationKey(isCritical, isLongLength, data);
            case SignatureSubpacketTag.SignatureTarget:
                return new SignatureTarget(isCritical, isLongLength, data);
            case SignatureSubpacketTag.IssuerFingerprint:
                return new IssuerFingerprint(isCritical, isLongLength, data);
            case SignatureSubpacketTag.IntendedRecipientFingerprint:
                return new IntendedRecipientFingerprint(isCritical, isLongLength, data);
            }
            return new SignatureSubpacket(type, isCritical, isLongLength, data);
		}

        private byte[] CheckData(byte[] data, int expected, int bytesRead, string name)
        {
            if (bytesRead != expected)
                throw new EndOfStreamException("truncated " + name + " subpacket data.");

            return Arrays.CopyOfRange(data, 0, expected);
        }
	}
}