1package org.bouncycastle.asn1.cms;
2
3import java.util.Enumeration;
4
5import org.bouncycastle.asn1.ASN1EncodableVector;
6import org.bouncycastle.asn1.ASN1Integer;
7import org.bouncycastle.asn1.ASN1Object;
8import org.bouncycastle.asn1.ASN1ObjectIdentifier;
9import org.bouncycastle.asn1.ASN1Primitive;
10import org.bouncycastle.asn1.ASN1Sequence;
11import org.bouncycastle.asn1.ASN1Set;
12import org.bouncycastle.asn1.ASN1TaggedObject;
13import org.bouncycastle.asn1.BERSequence;
14import org.bouncycastle.asn1.BERSet;
15import org.bouncycastle.asn1.BERTaggedObject;
16import org.bouncycastle.asn1.DERTaggedObject;
17
18/**
19 * <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>:
20 * <p>
21 * A signed data object containing multitude of {@link SignerInfo}s.
22 * <pre>
23 * SignedData ::= SEQUENCE {
24 *     version CMSVersion,
25 *     digestAlgorithms DigestAlgorithmIdentifiers,
26 *     encapContentInfo EncapsulatedContentInfo,
27 *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
28 *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
29 *     signerInfos SignerInfos
30 *   }
31 *
32 * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
33 *
34 * SignerInfos ::= SET OF SignerInfo
35 * </pre>
36 * <p>
37 * The version calculation uses following ruleset from RFC 3852 section 5.1:
38 * <pre>
39 * IF ((certificates is present) AND
40 *    (any certificates with a type of other are present)) OR
41 *    ((crls is present) AND
42 *    (any crls with a type of other are present))
43 * THEN version MUST be 5
44 * ELSE
45 *    IF (certificates is present) AND
46 *       (any version 2 attribute certificates are present)
47 *    THEN version MUST be 4
48 *    ELSE
49 *       IF ((certificates is present) AND
50 *          (any version 1 attribute certificates are present)) OR
51 *          (any SignerInfo structures are version 3) OR
52 *          (encapContentInfo eContentType is other than id-data)
53 *       THEN version MUST be 3
54 *       ELSE version MUST be 1
55 * </pre>
56 * <p>
57 * @todo Check possible update for this to RFC 5652 level
58 */
59public class SignedData
60    extends ASN1Object
61{
62    private static final ASN1Integer VERSION_1 = new ASN1Integer(1);
63    private static final ASN1Integer VERSION_3 = new ASN1Integer(3);
64    private static final ASN1Integer VERSION_4 = new ASN1Integer(4);
65    private static final ASN1Integer VERSION_5 = new ASN1Integer(5);
66
67    private ASN1Integer version;
68    private ASN1Set     digestAlgorithms;
69    private ContentInfo contentInfo;
70    private ASN1Set     certificates;
71    private ASN1Set     crls;
72    private ASN1Set     signerInfos;
73    private boolean certsBer;
74    private boolean        crlsBer;
75
76    /**
77     * Return a SignedData object from the given object.
78     * <p>
79     * Accepted inputs:
80     * <ul>
81     * <li> null &rarr; null
82     * <li> {@link SignedData} object
83     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignedData structure inside
84     * </ul>
85     *
86     * @param o the object we want converted.
87     * @exception IllegalArgumentException if the object cannot be converted.
88     */
89    public static SignedData getInstance(
90        Object  o)
91    {
92        if (o instanceof SignedData)
93        {
94            return (SignedData)o;
95        }
96        else if (o != null)
97        {
98            return new SignedData(ASN1Sequence.getInstance(o));
99        }
100
101        return null;
102    }
103
104    public SignedData(
105        ASN1Set     digestAlgorithms,
106        ContentInfo contentInfo,
107        ASN1Set     certificates,
108        ASN1Set     crls,
109        ASN1Set     signerInfos)
110    {
111        this.version = calculateVersion(contentInfo.getContentType(), certificates, crls, signerInfos);
112        this.digestAlgorithms = digestAlgorithms;
113        this.contentInfo = contentInfo;
114        this.certificates = certificates;
115        this.crls = crls;
116        this.signerInfos = signerInfos;
117        this.crlsBer = crls instanceof BERSet;
118        this.certsBer = certificates instanceof BERSet;
119    }
120
121
122    private ASN1Integer calculateVersion(
123        ASN1ObjectIdentifier contentOid,
124        ASN1Set certs,
125        ASN1Set crls,
126        ASN1Set signerInfs)
127    {
128        boolean otherCert = false;
129        boolean otherCrl = false;
130        boolean attrCertV1Found = false;
131        boolean attrCertV2Found = false;
132
133        if (certs != null)
134        {
135            for (Enumeration en = certs.getObjects(); en.hasMoreElements();)
136            {
137                Object obj = en.nextElement();
138                if (obj instanceof ASN1TaggedObject)
139                {
140                    ASN1TaggedObject tagged = ASN1TaggedObject.getInstance(obj);
141
142                    if (tagged.getTagNo() == 1)
143                    {
144                        attrCertV1Found = true;
145                    }
146                    else if (tagged.getTagNo() == 2)
147                    {
148                        attrCertV2Found = true;
149                    }
150                    else if (tagged.getTagNo() == 3)
151                    {
152                        otherCert = true;
153                    }
154                }
155            }
156        }
157
158        if (otherCert)
159        {
160            return new ASN1Integer(5);
161        }
162
163        if (crls != null)         // no need to check if otherCert is true
164        {
165            for (Enumeration en = crls.getObjects(); en.hasMoreElements();)
166            {
167                Object obj = en.nextElement();
168                if (obj instanceof ASN1TaggedObject)
169                {
170                    otherCrl = true;
171                }
172            }
173        }
174
175        if (otherCrl)
176        {
177            return VERSION_5;
178        }
179
180        if (attrCertV2Found)
181        {
182            return VERSION_4;
183        }
184
185        if (attrCertV1Found)
186        {
187            return VERSION_3;
188        }
189
190        if (checkForVersion3(signerInfs))
191        {
192            return VERSION_3;
193        }
194
195        if (!CMSObjectIdentifiers.data.equals(contentOid))
196        {
197            return VERSION_3;
198        }
199
200        return VERSION_1;
201    }
202
203    private boolean checkForVersion3(ASN1Set signerInfs)
204    {
205        for (Enumeration e = signerInfs.getObjects(); e.hasMoreElements();)
206        {
207            SignerInfo s = SignerInfo.getInstance(e.nextElement());
208
209            if (s.getVersion().getValue().intValue() == 3)
210            {
211                return true;
212            }
213        }
214
215        return false;
216    }
217
218    private SignedData(
219        ASN1Sequence seq)
220    {
221        Enumeration     e = seq.getObjects();
222
223        version = ASN1Integer.getInstance(e.nextElement());
224        digestAlgorithms = ((ASN1Set)e.nextElement());
225        contentInfo = ContentInfo.getInstance(e.nextElement());
226
227        while (e.hasMoreElements())
228        {
229            ASN1Primitive o = (ASN1Primitive)e.nextElement();
230
231            //
232            // an interesting feature of SignedData is that there appear
233            // to be varying implementations...
234            // for the moment we ignore anything which doesn't fit.
235            //
236            if (o instanceof ASN1TaggedObject)
237            {
238                ASN1TaggedObject tagged = (ASN1TaggedObject)o;
239
240                switch (tagged.getTagNo())
241                {
242                case 0:
243                    certsBer = tagged instanceof BERTaggedObject;
244                    certificates = ASN1Set.getInstance(tagged, false);
245                    break;
246                case 1:
247                    crlsBer = tagged instanceof BERTaggedObject;
248                    crls = ASN1Set.getInstance(tagged, false);
249                    break;
250                default:
251                    throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo());
252                }
253            }
254            else
255            {
256                signerInfos = (ASN1Set)o;
257            }
258        }
259    }
260
261    public ASN1Integer getVersion()
262    {
263        return version;
264    }
265
266    public ASN1Set getDigestAlgorithms()
267    {
268        return digestAlgorithms;
269    }
270
271    public ContentInfo getEncapContentInfo()
272    {
273        return contentInfo;
274    }
275
276    public ASN1Set getCertificates()
277    {
278        return certificates;
279    }
280
281    public ASN1Set getCRLs()
282    {
283        return crls;
284    }
285
286    public ASN1Set getSignerInfos()
287    {
288        return signerInfos;
289    }
290
291    /**
292     * Produce an object suitable for an ASN1OutputStream.
293     */
294    public ASN1Primitive toASN1Primitive()
295    {
296        ASN1EncodableVector  v = new ASN1EncodableVector();
297
298        v.add(version);
299        v.add(digestAlgorithms);
300        v.add(contentInfo);
301
302        if (certificates != null)
303        {
304            if (certsBer)
305            {
306                v.add(new BERTaggedObject(false, 0, certificates));
307            }
308            else
309            {
310                v.add(new DERTaggedObject(false, 0, certificates));
311            }
312        }
313
314        if (crls != null)
315        {
316            if (crlsBer)
317            {
318                v.add(new BERTaggedObject(false, 1, crls));
319            }
320            else
321            {
322                v.add(new DERTaggedObject(false, 1, crls));
323            }
324        }
325
326        v.add(signerInfos);
327
328        return new BERSequence(v);
329    }
330}
331