1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Boris Kuznetsov
20* @version $Revision$
21*/
22package org.apache.harmony.security.pkcs7;
23
24import java.io.IOException;
25import java.util.Arrays;
26import org.apache.harmony.security.asn1.ASN1Any;
27import org.apache.harmony.security.asn1.ASN1Explicit;
28import org.apache.harmony.security.asn1.ASN1OctetString;
29import org.apache.harmony.security.asn1.ASN1Oid;
30import org.apache.harmony.security.asn1.ASN1Sequence;
31import org.apache.harmony.security.asn1.ASN1Type;
32import org.apache.harmony.security.asn1.BerInputStream;
33
34/**
35 * As defined in PKCS #7: Cryptographic Message Syntax Standard
36 * (http://www.ietf.org/rfc/rfc2315.txt)
37 *
38 * ContentInfo ::= SEQUENCE {
39 *       contentType  ContentType,
40 *       content      [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
41 *     }
42 */
43public final class ContentInfo {
44
45    // OIDs
46    public static final int[] DATA = new int[] {1, 2, 840, 113549, 1, 7, 1};
47    public static final int[] SIGNED_DATA = new int[] {1, 2, 840, 113549, 1, 7, 2};
48    public static final int[] ENVELOPED_DATA = new int[] {1, 2, 840, 113549, 1, 7, 3};
49    public static final int[] SIGNED_AND_ENVELOPED_DATA = new int[] {1, 2, 840, 113549, 1, 7, 4};
50    public static final int[] DIGESTED_DATA = new int[] {1, 2, 840, 113549, 1, 7, 5};
51    public static final int[] ENCRYPTED_DATA = new int[] {1, 2, 840, 113549, 1, 7, 6};
52
53    private final int[] oid;
54    private final Object content;
55    private byte[] encoding;
56
57    private ContentInfo(int[] oid, Object content, byte[] encoding) {
58        this.oid = oid;
59        this.content = content;
60        this.encoding = encoding;
61    }
62
63    public SignedData getSignedData() {
64        if (Arrays.equals(oid, SIGNED_DATA)) {
65            return (SignedData)content;
66        }
67        return null;
68    }
69
70    public Object getContent() {
71        return content;
72    }
73
74    public int[] getContentType() {
75        return oid;
76    }
77
78    public byte[] getEncoded() {
79        if (encoding == null) {
80            encoding = ASN1.encode(this);
81        }
82        // Note: this is internal object and can not be accessible from
83        // public API, so encoding is not copied. The classes which use
84        // this class should copy encoding before passing it out.
85        return encoding;
86    }
87
88    @Override public String toString() {
89        StringBuilder res = new StringBuilder();
90        res.append("==== ContentInfo:");
91        res.append("\n== ContentType (OID): ");
92        for (int i : oid) {
93            res.append(i);
94            res.append(' ');
95        }
96        res.append("\n== Content: ");
97        if (content != null) {
98            res.append("\n");
99            res.append(content.toString());
100        }
101        res.append("\n== Content End");
102        res.append("\n==== ContentInfo End\n");
103        return res.toString();
104    }
105
106    public static final ASN1Sequence ASN1 =
107        new ASN1Sequence(new ASN1Type[] {
108                ASN1Oid.getInstance(),
109                new ASN1Explicit(0, ASN1Any.getInstance())
110                })  {
111        {
112            setOptional(1); // content is optional
113        }
114
115        @Override protected void getValues(Object object, Object[] values) {
116            ContentInfo ci = (ContentInfo) object;
117            values[0] = ci.oid;
118            if (ci.content != null) {
119                if (Arrays.equals(ci.oid, DATA)) {
120                    if (ci.content != null) {
121                        values[1] =
122                            ASN1OctetString.getInstance().encode(ci.content);
123                    }
124                } else if (ci.content instanceof SignedData) {
125                    values[1] = SignedData.ASN1.encode(ci.content);
126                } else {
127                    values[1] = ci.content;
128                }
129            }
130        }
131
132        @Override protected Object getDecodedObject(BerInputStream in) throws IOException {
133            Object[] values = (Object[]) in.content;
134            int[] oid = (int[]) values[0];
135            if (Arrays.equals(oid, DATA)) {
136                if (values[1] != null) {
137                    return new ContentInfo(oid,
138                            ASN1OctetString.getInstance().decode((byte[])values[1]),
139                            in.getEncoded());
140                }  else {
141                    return new ContentInfo((int[])values[0], null,
142                            in.getEncoded());
143                }
144            }
145            if (Arrays.equals(oid, SIGNED_DATA)) {
146                return new ContentInfo((int[])values[0],
147                        SignedData.ASN1.decode((byte[])values[1]),
148                        in.getEncoded());
149            }
150            return new ContentInfo((int[]) values[0], values[1], in.getEncoded());
151        }
152   };
153}
154