1<?php
2/*
3 * Copyright 2008-2010 GuardTime AS
4 *
5 * This file is part of the GuardTime PHP SDK.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20/**
21 * @package asn1
22 * @subpackage cms
23 */
24
25/**
26 * CMS SignerInfo implementation.
27 *
28 * <pre>
29 * SignerInfo ::= SEQUENCE {
30 *       version                CMSVersion,
31 *       sid                    SignerIdentifier,
32 *       digestAlgorithm        DigestAlgorithmIdentifier,
33 *       signedAttrs        [0] IMPLICIT SignedAttributes OPTIONAL,
34 *       signatureAlgorithm     SignatureAlgorithmIdentifier,
35 *       signature              SignatureValue,
36 *       unsignedAttrs      [1] IMPLICIT UnsignedAttributes OPTIONAL
37 * }
38 * </pre>
39 *
40 * @package asn1
41 * @subpackage cms
42 *
43 * @see http://tools.ietf.org/html/rfc3852#section-5.3 RFC 3852
44 */
45class CMSSignerInfo {
46
47    private $version;
48    private $sid;
49    private $digestAlgorithm;
50    private $signedAttrs;
51    private $signatureAlgorithm;
52    private $signature;
53    private $unsignedAttrs;
54
55    /**
56     * Constructs a new instance of CMSSignerInfo.
57     */
58    public function __construct() {
59    }
60
61    /**
62     * Decodes the given ASN1Sequence as CMSSignerInfo.
63     *
64     * @throws GTException
65     * @param  ASN1Sequence $object CMSSignerInfo encoded as ASN1Sequence
66     * @return void
67     */
68    public function decode($object) {
69
70        if (!$object instanceof ASN1Sequence) {
71            throw new GTException("Expecting an ASN1Sequence");
72        }
73
74        $size = $object->getObjectCount();
75
76        if ($size < 5 || $size > 7) {
77            throw new GTException("Invalid sequence size: {$size}");
78        }
79
80        $version = $object->getObjectAt(0);
81
82        if (!$version instanceof ASN1Integer) {
83            throw new GTException("Expecting an ASN1Integer for version");
84        }
85
86        $this->version = (int) $version->getValue();
87
88        if ($this->version != 1) {
89            throw new GTException("Invalid version: {$this->version}");
90        }
91
92        $sid = new CMSSignerIdentifier();
93        $sid->decode($object->getObjectAt(1));
94
95        $this->sid = $sid;
96
97        $digestAlgorithm = new X509AlgorithmIdentifier();
98        $digestAlgorithm->decode($object->getObjectAt(2));
99
100        $this->digestAlgorithm = $digestAlgorithm;
101
102        for ($i = 3; $i < $object->getObjectCount(); $i++) {
103
104            $item = $object->getObjectAt($i);
105
106            if ($item instanceof ASN1Tag) {
107
108                switch ($item->getTagValue()) {
109
110                    case 0:
111
112                        $this->signedAttrs = array();
113
114                        $items = $item->getObjectAs(ASN1_TAG_SET);
115
116                        foreach ($items->getObjects() as $a) {
117
118                            $attribute = new CMSAttribute();
119                            $attribute->decode($a);
120
121                            array_push($this->signedAttrs, $attribute);
122                        }
123
124                        break;
125
126                    case 1:
127
128                        $this->unsignedAttrs = array();
129
130                        $items = $item->getObjectAs(ASN1_TAG_SET);
131
132                        foreach ($items->getObjects() as $a) {
133
134                            $attribute = new CMSAttribute();
135                            $attribute->decode($a);
136
137                            array_push($this->unsignedAttrs, $attribute);
138                        }
139
140                        break;
141
142                    default:
143
144                        throw new GTException("Unsupported TAG value: " . $item->getTagValue());
145
146                }
147
148            } else if ($item instanceof ASN1Sequence) {
149
150                // signature algorithm
151                $signatureAlgorithm = new X509AlgorithmIdentifier();
152                $signatureAlgorithm->decode($item);
153                $this->signatureAlgorithm = $signatureAlgorithm;
154
155            } else if ($item instanceof ASN1OctetString) {
156
157                // signature
158                $this->signature = $item->getValue();
159
160            } else {
161
162                throw new GTException("Unexpected ASN.1 TYPE: " . get_class($item));
163            }
164
165        }
166
167        if ($this->signature === null) {
168            throw new GTException("Signature missing");
169        }
170
171        if ($this->signatureAlgorithm === null) {
172            throw new GTException("Signature algorithm missing");
173        }
174
175    }
176
177    /**
178     * Encodes this CMSSignerInfo using DER.
179     *
180     * @return array byte array that contains the DER encoding of this CMSSignerInfo
181     */
182    public function encodeDER() {
183
184        $sequence = new ASN1Sequence();
185
186        $sequence->add(new ASN1Integer((int) $this->version));
187        $sequence->add($this->sid);
188        $sequence->add($this->digestAlgorithm);
189
190        if (!empty($this->signedAttrs)) {
191
192            $signed = new ASN1Set();
193
194            foreach ($this->signedAttrs as $attr) {
195                $signed->add($attr);
196            }
197
198            $tag = new ASN1Tag();
199            $tag->setExplicit(false);
200            $tag->setTagType(ASN1_TAG_CONSTRUCTED);
201            $tag->setTagClass(ASN1_TAG_CONTEXT);
202            $tag->setTagValue(0);
203            $tag->setObject($signed);
204
205            $sequence->add($tag);
206
207        }
208
209        $sequence->add($this->signatureAlgorithm);
210        $sequence->add(new ASN1OctetString($this->signature));
211
212        if (!empty($this->unsignedAttrs)) {
213
214            $unsigned = new ASN1Set();
215
216            foreach ($this->unsignedAttrs as $attr) {
217                $unsigned->add($attr);
218            }
219
220            $tag = new ASN1Tag();
221            $tag->setExplicit(false);
222            $tag->setTagType(ASN1_TAG_CONSTRUCTED);
223            $tag->setTagClass(ASN1_TAG_CONTEXT);
224            $tag->setTagValue(1);
225            $tag->setObject($unsigned);
226
227            $sequence->add($tag);
228
229        }
230
231        return $sequence->encodeDER();
232
233    }
234
235    /**
236     * Gets the sid.
237     *
238     * @return CMSSignerIdentifier sid
239     */
240    public function getSid() {
241        return $this->sid;
242    }
243
244    /**
245     * Gets the signature.
246     *
247     * @return array byte array containing the signature
248     */
249    public function getSignature() {
250        return $this->signature;
251    }
252
253    /**
254     * Sets the signature.
255     *
256     * @param  $signature byte array containing the signature
257     * @return void
258     */
259    public function setSignature($signature) {
260
261        $this->signature = $signature;
262    }
263
264    /**
265     * Gets the signature algorithm.
266     *
267     * @return X509AlgorithmIdentifier signature algorithm
268     */
269    public function getSignatureAlgorithm() {
270        return $this->signatureAlgorithm;
271    }
272
273    /**
274     * Gets the digest algorithm.
275     *
276     * @return X509AlgorithmIdentifier digest algorithm
277     */
278    public function getDigestAlgorithm() {
279        return $this->digestAlgorithm;
280    }
281
282    /**
283     * Gets the signet attributes.
284     *
285     * @return array array containing CMSAttribute instances
286     */
287    public function getSignedAttrs() {
288        return $this->signedAttrs;
289    }
290
291    /**
292     * Gets the unsigned attributes.
293     *
294     * @return array array containing CMSAttribute instances
295     */
296    public function getUnsignedAttrs() {
297        return $this->unsignedAttrs;
298    }
299}
300
301?>
302