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 */
23
24/**
25 * ASN.1 Object Id implementation.
26 *
27 * @package asn1
28 */
29class ASN1ObjectId extends ASN1Object {
30
31    protected static $ALLOWED_CHARACTERS = array(
32        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'
33    );
34
35    protected $value;
36
37    /**
38     * Constructs a new ASN.1 Object Id.
39     *
40     * @param  string $value value as string
41     * @return void
42     */
43    public function __construct($value = null) {
44
45        if (!is_null($value)) {
46
47            if (strlen($value) == 0) {
48                throw new GTException("Invalid OID: 0 length");
49            }
50
51            foreach (GTUtil::toArray($value) as $char) {
52
53                if (!in_array($char, self::$ALLOWED_CHARACTERS, true)) {
54                    throw new GTException("Invalid OID: character {$char} not allowed");
55                }
56
57            }
58
59            $tokens = explode('.', $value);
60
61            if (count($tokens) < 2) {
62                throw new GTException("Invaid OID: must contain at least 2 '.' separated tokens");
63            }
64
65            $this->value = $value;
66        }
67
68    }
69
70    /**
71     * Gets the value of this object id.
72     *
73     * @return string value
74     */
75    public function getValue() {
76        return $this->value;
77    }
78
79    /**
80     * Encodes the contents of this ASN1 Object Id as DER.
81     *
82     * @return array DER encoding of this object Id
83     */
84    public function encodeDER() {
85
86        $bytes = array();
87
88        $tokens = explode('.', $this->value);
89
90        $b1 = (int) array_shift($tokens);
91        $b2 = (int) array_shift($tokens);
92
93        $this->append($bytes, ($b1 * 40 + $b2));
94
95        $zero = new GTBigInteger(0);
96
97        $mask1 = new GTBigInteger(0x80);
98        $mask2 = new GTBigInteger(0x7F);
99
100        foreach ($tokens as $token) {
101
102            $integer = new GTBigInteger($token);
103
104            $size = 1;
105            $buff = $integer;
106
107            while (true) {
108
109                $buff = $buff->shiftRight(7);
110
111                if ($buff->comp($zero) == 0) {
112                    break;
113                }
114
115                $size++;
116            }
117
118            for ($i = ($size - 1) * 7; $i >= 0; $i -= 7) {
119
120                if ($i == 0) {
121                    $this->append($bytes, (int) $integer->bitAnd($mask2)->getValue());
122
123                } else {
124                    $this->append($bytes, (int) $integer->shiftRight($i)->bitAnd($mask2)->bitOr($mask1)->getValue());
125                }
126
127            }
128        }
129
130        $this->prepend($bytes, ASN1DER::encodeLength(count($bytes)));
131        $this->prepend($bytes, ASN1DER::encodeType(ASN1_TAG_OBJECT_ID));
132
133        return $bytes;
134
135    }
136
137    /**
138     * Decodes an ASN1ObjectId from the given byte stream.
139     *
140     * @param  $bytes V bytes from the encoding of ASN1ObjectId TLV.
141     * @return void
142     */
143    public function decodeDER($bytes) {
144
145        $this->value = '';
146
147        $current = null;
148
149        for ($i = 0; $i < count($bytes); $i++) {
150
151            if ($current == null) {
152                $current = new GTBigInteger(0);
153            }
154
155            $byte = $bytes[$i];
156
157            $current = $current->shiftLeft(7);
158            $current = $current->bitOr(new GTBigInteger($byte & 0x7F));
159
160            if (($byte & 0x80) != 0) {
161                continue;
162            }
163
164            if ($i == 0) {
165
166                // int is always big enough to hold the first chunk
167
168                $current = (int) $current->getValue();
169
170                if ($current < 40) {
171                    $byte1 = 0;
172
173                } else if ($current < 80) {
174                    $byte1 = 1;
175
176                } else {
177                    $byte1 = 2;
178                }
179
180                $byte2 = $current - ($byte1 * 40);
181
182                $this->value .= $byte1;
183                $this->value .= '.';
184                $this->value .= $byte2;
185                $this->value .= '.';
186
187                $current = null;
188
189            } else {
190
191                $this->value .= $current->getValue();
192
193                if ($i != count($bytes) - 1) {
194                    $this->value .= '.';
195                }
196
197                $current = null;
198
199            }
200
201        }
202
203    }
204}
205
206?>
207