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 * Class implementing DER helper methods for encoding and decoding. 26 * 27 * @package asn1 28 */ 29class ASN1DER { 30 31 /** 32 * Encodes object as DER. 33 * 34 * @static 35 * @throws GTException 36 * @param ASN1DEREncodable $object 37 * @return array der encoding bytes 38 */ 39 public static function encode($object) { 40 41 if (!($object instanceof ASN1DEREncodable)) { 42 throw new GTException("Unable to encode object that is not DEREncodable"); 43 } 44 45 return $object->encodeDER(); 46 47 } 48 49 /** 50 * Encodes the length using DER (L in TLV). 51 * 52 * @static 53 * @param int $length the length to encode 54 * @return array der encoding bytes 55 */ 56 public static function encodeLength($length) { 57 58 $bytes = array(); 59 60 if ($length > 127) { 61 62 $size = 1; 63 $value = $length; 64 while (($value >>= 8) != 0) { 65 $size++; 66 } 67 68 array_push($bytes, $size | 0x80); 69 70 for ($i = ($size - 1) * 8; $i >= 0; $i -= 8) { 71 72 $byte = $length >> $i; 73 $byte = $byte & 0xFF; 74 75 array_push($bytes, $byte); 76 } 77 78 } else { 79 80 array_push($bytes, $length); 81 82 } 83 84 return $bytes; 85 86 } 87 88 /** 89 * Encodes the type using DER (T in TLV). 90 * 91 * @static 92 * @param int $type the type to encode 93 * @return array der encoding bytes 94 */ 95 public static function encodeType($type) { 96 97 if ($type == ASN1_TAG_SET || $type == ASN1_TAG_SEQUENCE) { 98 $type = $type | 0x20; 99 } 100 101 return array($type); 102 } 103 104 /** 105 * Decodes an ASN1Object from the given byte stream. 106 * 107 * @static 108 * @throws GTException 109 * @param array $bytes the byte stream 110 * @return ASN1Object decoded object 111 */ 112 public static function decode($bytes) { 113 114 if (empty($bytes)) { 115 throw new GTException("parameter bytes is required"); 116 } 117 118 $object = ASN1DER::decodeType($bytes); 119 $length = ASN1DER::decodeLength($bytes); 120 121 if (count($bytes) < $length) { 122 throw new GTException("Invalid DER stream, not enough bytes"); 123 } 124 125 if (count($bytes) > $length) { 126 throw new GTException("Invalid DER stream, too many trailing bytes"); 127 } 128 129 $object->decodeDER($bytes); 130 131 return $object; 132 } 133 134 /** 135 * Decodes the length from the given byte stream (L in TLV). 136 * 137 * @static 138 * @throws GTException 139 * @param arrayref &$bytes the byte stream 140 * @return int decoded length 141 */ 142 public static function decodeLength(&$bytes) { 143 144 if (!is_array($bytes)) { 145 throw new GTException("parameter bytes must be an array of bytes"); 146 } 147 148 if (count($bytes) == 0) { 149 throw new GTException("Invalid DER stream, not enough bytes to decode length"); 150 } 151 152 $length = array_shift($bytes); 153 154 if ($length == 128) { 155 throw new GTException("indefinite length encoding not allowed for DER"); 156 } 157 158 if ($length > 127) { 159 160 $size = $length & 0x7F; 161 162 if ($size > 4) { 163 throw new GTException("size > 4"); 164 } 165 166 $length = 0; 167 168 for ($i = 0; $i < $size; $i++) { 169 if (count($bytes) == 0) { 170 throw new GTException("Invalid DER stream, not enough bytes to decode length"); 171 } 172 173 $length = ($length << 8) + array_shift($bytes); 174 } 175 176 } 177 178 return $length; 179 180 } 181 182 /** 183 * Decodes type from the given byte stream (T in TLV). 184 * 185 * @static 186 * @throws GTException 187 * @param arrayref &$bytes the byte stream 188 * @return ASN1Object a concrete subclass instance 189 */ 190 public static function decodeType(&$bytes) { 191 192 if (!is_array($bytes)) { 193 throw new GTException("parameter bytes must be an array of bytes"); 194 } 195 196 if (count($bytes) < 1) { 197 throw new GTException("unexpected EOF"); 198 } 199 200 $byte = array_shift($bytes); 201 202 $bit8 = ($byte >> 7) & 0x1; 203 $bit7 = ($byte >> 6) & 0x1; 204 $bit6 = ($byte >> 5) & 0x1; 205 206 $tagClass = ""; 207 208 if ($bit8 == 0 && $bit7 == 0) { 209 210 // 00xx xxxx UNIVERSAL 211 $tagClass = ASN1_TAG_UNIVERSAL; 212 213 } else if ($bit8 == 0 && $bit7 == 1) { 214 215 // 01xx xxxx APPLICATION 216 $tagClass = ASN1_TAG_APPLICATION; 217 218 } else if ($bit8 == 1 && $bit7 == 0) { 219 220 // 10xx xxxx CONTEXT 221 $tagClass = ASN1_TAG_CONTEXT; 222 223 } else if ($bit7 == 1 && $bit8 == 1) { 224 225 // 11xx xxxx PRIVATE 226 $tagClass = ASN1_TAG_PRIVATE; 227 228 } 229 230 if ($bit6) { 231 232 // xx1x xxxx CONSTRUCTED 233 $tagType = ASN1_TAG_CONSTRUCTED; 234 235 } else { 236 237 // xx0x xxxx PRIMITIVE 238 $tagType = ASN1_TAG_PRIMITIVE; 239 240 } 241 242 $byte = $byte & 0x1F; // clear tags: 000x xxxx 243 244 if ($byte == 0x1F) { 245 throw new GTException("Multibyte tags not yet supported!"); 246 } 247 248 $object = null; 249 250 if ($tagClass == ASN1_TAG_UNIVERSAL) { 251 252 switch ($byte) { 253 254 case ASN1_TAG_BOOLEAN: 255 256 $object = new ASN1Boolean(); 257 break; 258 259 case ASN1_TAG_INTEGER: 260 261 $object = new ASN1Integer(); 262 break; 263 264 case ASN1_TAG_BIT_STRING: 265 266 $object = new ASN1BitString(); 267 break; 268 269 case ASN1_TAG_OCTET_STRING: 270 271 $object = new ASN1OctetString(); 272 break; 273 274 case ASN1_TAG_NULL: 275 276 $object = new ASN1Null(); 277 break; 278 279 case ASN1_TAG_OBJECT_ID: 280 281 $object = new ASN1ObjectId(); 282 break; 283 284 case ASN1_TAG_UTF8_STRING: 285 286 $object = new ASN1UTF8String(); 287 break; 288 289 case ASN1_TAG_PRINTABLE_STRING: 290 291 $object = new ASN1PrintableString(); 292 break; 293 294 case ASN1_TAG_T61_STRING: 295 296 $object = new ASN1T61String(); 297 break; 298 299 case ASN1_TAG_IA5_STRING: 300 301 $object = new ASN1IA5String(); 302 break; 303 304 case ASN1_TAG_BMP_STRING: 305 306 $object = new ASN1BMPString(); 307 break; 308 309 case ASN1_TAG_UTC_TIME: 310 311 $object = new ASN1UTCTime(); 312 break; 313 314 case ASN1_TAG_GENERALIZED_TIME: 315 316 $object = new ASN1GeneralizedTime(); 317 break; 318 319 case ASN1_TAG_SEQUENCE: 320 321 $object = new ASN1Sequence(); 322 break; 323 324 case ASN1_TAG_SET: 325 326 $object = new ASN1Set(); 327 break; 328 329 default: 330 331 throw new GTException("Unsupported ASN.1 UNIVERSAL type: {$byte}"); 332 } 333 334 } else { 335 336 $object = new ASN1Tag(); 337 } 338 339 $object->setTagClass($tagClass); 340 $object->setTagType($tagType); 341 $object->setTagValue($byte); 342 343 return $object; 344 345 } 346 347} 348 349?> 350