summary refs log tree commit diff
path: root/crypto/src/asn1/tsp/ArchiveTimeStamp.cs
blob: e1be303b728b7e0be8cdea6980c401c572d0391a (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
using System;

using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;

namespace Org.BouncyCastle.Asn1.Tsp
{
    /**
     * Implementation of the Archive Timestamp type defined in RFC4998.
     * @see <a href="https://tools.ietf.org/html/rfc4998">RFC 4998</a>
     * <p/>
     * ASN.1 Archive Timestamp
     * <p/>
     * ArchiveTimeStamp ::= SEQUENCE {
     * digestAlgorithm [Ø] AlgorithmIdentifier OPTIONAL,
     * attributes      [1] Attributes OPTIONAL,
     * reducedHashtree [2] SEQUENCE OF PartialHashtree OPTIONAL,
     * timeStamp       ContentInfo}
     * <p/>
     * PartialHashtree ::= SEQUENCE OF OCTET STRING
     * <p/>
     * Attributes ::= SET SIZE (1..MAX) OF Attribute
     */
    public class ArchiveTimeStamp
        : Asn1Encodable
    {
        /**
         * Return an ArchiveTimestamp from the given object.
         *
         * @param obj the object we want converted.
         * @return an ArchiveTimestamp instance, or null.
         * @throws IllegalArgumentException if the object cannot be converted.
         */
        public static ArchiveTimeStamp GetInstance(object obj)
        {
            if (obj == null)
                return null;
            if (obj is ArchiveTimeStamp archiveTimeStamp)
                return archiveTimeStamp;
            return new ArchiveTimeStamp(Asn1Sequence.GetInstance(obj));
        }

        public static ArchiveTimeStamp GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
        {
            return new ArchiveTimeStamp(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
        }

        private readonly AlgorithmIdentifier m_digestAlgorithm;
        private readonly Attributes m_attributes;
        private readonly Asn1Sequence m_reducedHashTree;
        private readonly ContentInfo m_timeStamp;

        public ArchiveTimeStamp(AlgorithmIdentifier digestAlgorithm, PartialHashtree[] reducedHashTree,
            ContentInfo timeStamp)
            : this(digestAlgorithm, null, reducedHashTree, timeStamp)
        {
        }

        public ArchiveTimeStamp(ContentInfo timeStamp)
            : this(null, null, null, timeStamp)
        {
        }

        public ArchiveTimeStamp(AlgorithmIdentifier digestAlgorithm, Attributes attributes,
            PartialHashtree[] reducedHashTree, ContentInfo timeStamp)
        {
            m_digestAlgorithm = digestAlgorithm;
            m_attributes = attributes;
            if (reducedHashTree != null)
            {
                m_reducedHashTree = new DerSequence(reducedHashTree);
            }
            else
            {
                m_reducedHashTree = null;
            }
            m_timeStamp = timeStamp;
        }

        private ArchiveTimeStamp(Asn1Sequence sequence)
        {
            if (sequence.Count < 1 || sequence.Count > 4)
                throw new ArgumentException("wrong sequence size in constructor: " + sequence.Count, nameof(sequence));

            AlgorithmIdentifier digAlg = null;
            Attributes attrs = null;
            Asn1Sequence rHashTree = null;
            for (int i = 0; i < sequence.Count - 1; i++)
            {
                Asn1Encodable obj = sequence[i];

                if (obj is Asn1TaggedObject taggedObject)
                {
                    switch (taggedObject.TagNo)
                    {
                    case 0:
                        digAlg = AlgorithmIdentifier.GetInstance(taggedObject, false);
                        break;
                    case 1:
                        attrs = Attributes.GetInstance(taggedObject, false);
                        break;
                    case 2:
                        rHashTree = Asn1Sequence.GetInstance(taggedObject, false);
                        break;
                    default:
                        throw new ArgumentException("invalid tag no in constructor: " + taggedObject.TagNo);
                    }
                }
            }

            m_digestAlgorithm = digAlg;
            m_attributes = attrs;
            m_reducedHashTree = rHashTree;
            m_timeStamp = ContentInfo.GetInstance(sequence[sequence.Count - 1]);
        }

        public virtual AlgorithmIdentifier GetDigestAlgorithmIdentifier() => m_digestAlgorithm
            ?? GetTimeStampInfo().MessageImprint.HashAlgorithm;

        public virtual byte[] GetTimeStampDigestValue() => GetTimeStampInfo().MessageImprint.GetHashedMessage();

        private TstInfo GetTimeStampInfo()
        {
            if (!CmsObjectIdentifiers.SignedData.Equals(m_timeStamp.ContentType))
                throw new InvalidOperationException("cannot identify algorithm identifier for digest");

            SignedData tsData = SignedData.GetInstance(m_timeStamp.Content);
            var contentInfo = tsData.EncapContentInfo;

            if (!Asn1.Pkcs.PkcsObjectIdentifiers.IdCTTstInfo.Equals(contentInfo.ContentType))
                throw new InvalidOperationException("cannot parse time stamp");

            return TstInfo.GetInstance(Asn1OctetString.GetInstance(contentInfo.Content).GetOctets());
        }

        /**
         * Return the contents of the digestAlgorithm field - null if not set.
         *
         * @return the contents of the digestAlgorithm field, or null if not set.
         */
        public virtual AlgorithmIdentifier DigestAlgorithm() => m_digestAlgorithm;

        /**
         * Return the first node in the reduced hash tree which contains the leaf node.
         *
         * @return the node containing the data hashes, null if no reduced hash tree is present.
         */
        public virtual PartialHashtree GetHashTreeLeaf()
        {
            if (m_reducedHashTree == null)
                return null;

            return PartialHashtree.GetInstance(m_reducedHashTree[0]);
        }

        public virtual PartialHashtree[] GetReducedHashTree() =>
            m_reducedHashTree?.MapElements(PartialHashtree.GetInstance);

        public virtual ContentInfo TimeStamp => m_timeStamp;

        public override Asn1Object ToAsn1Object()
        {
            Asn1EncodableVector v = new Asn1EncodableVector(4);
            v.AddOptionalTagged(false, 0, m_digestAlgorithm);
            v.AddOptionalTagged(false, 1, m_attributes);
            v.AddOptionalTagged(false, 2, m_reducedHashTree);
            v.Add(m_timeStamp);
            return new DerSequence(v);
        }
    }
}