encodeDER(); } /** * Encodes the length using DER (L in TLV). * * @static * @param int $length the length to encode * @return array der encoding bytes */ public static function encodeLength($length) { $bytes = array(); if ($length > 127) { $size = 1; $value = $length; while (($value >>= 8) != 0) { $size++; } array_push($bytes, $size | 0x80); for ($i = ($size - 1) * 8; $i >= 0; $i -= 8) { $byte = $length >> $i; $byte = $byte & 0xFF; array_push($bytes, $byte); } } else { array_push($bytes, $length); } return $bytes; } /** * Encodes the type using DER (T in TLV). * * @static * @param int $type the type to encode * @return array der encoding bytes */ public static function encodeType($type) { if ($type == ASN1_TAG_SET || $type == ASN1_TAG_SEQUENCE) { $type = $type | 0x20; } return array($type); } /** * Decodes an ASN1Object from the given byte stream. * * @static * @throws GTException * @param array $bytes the byte stream * @return ASN1Object decoded object */ public static function decode($bytes) { if (empty($bytes)) { throw new GTException("parameter bytes is required"); } $object = ASN1DER::decodeType($bytes); $length = ASN1DER::decodeLength($bytes); if (count($bytes) < $length) { throw new GTException("Invalid DER stream, not enough bytes"); } if (count($bytes) > $length) { throw new GTException("Invalid DER stream, too many trailing bytes"); } $object->decodeDER($bytes); return $object; } /** * Decodes the length from the given byte stream (L in TLV). * * @static * @throws GTException * @param arrayref &$bytes the byte stream * @return int decoded length */ public static function decodeLength(&$bytes) { if (!is_array($bytes)) { throw new GTException("parameter bytes must be an array of bytes"); } if (count($bytes) == 0) { throw new GTException("Invalid DER stream, not enough bytes to decode length"); } $length = array_shift($bytes); if ($length == 128) { throw new GTException("indefinite length encoding not allowed for DER"); } if ($length > 127) { $size = $length & 0x7F; if ($size > 4) { throw new GTException("size > 4"); } $length = 0; for ($i = 0; $i < $size; $i++) { if (count($bytes) == 0) { throw new GTException("Invalid DER stream, not enough bytes to decode length"); } $length = ($length << 8) + array_shift($bytes); } } return $length; } /** * Decodes type from the given byte stream (T in TLV). * * @static * @throws GTException * @param arrayref &$bytes the byte stream * @return ASN1Object a concrete subclass instance */ public static function decodeType(&$bytes) { if (!is_array($bytes)) { throw new GTException("parameter bytes must be an array of bytes"); } if (count($bytes) < 1) { throw new GTException("unexpected EOF"); } $byte = array_shift($bytes); $bit8 = ($byte >> 7) & 0x1; $bit7 = ($byte >> 6) & 0x1; $bit6 = ($byte >> 5) & 0x1; $tagClass = ""; if ($bit8 == 0 && $bit7 == 0) { // 00xx xxxx UNIVERSAL $tagClass = ASN1_TAG_UNIVERSAL; } else if ($bit8 == 0 && $bit7 == 1) { // 01xx xxxx APPLICATION $tagClass = ASN1_TAG_APPLICATION; } else if ($bit8 == 1 && $bit7 == 0) { // 10xx xxxx CONTEXT $tagClass = ASN1_TAG_CONTEXT; } else if ($bit7 == 1 && $bit8 == 1) { // 11xx xxxx PRIVATE $tagClass = ASN1_TAG_PRIVATE; } if ($bit6) { // xx1x xxxx CONSTRUCTED $tagType = ASN1_TAG_CONSTRUCTED; } else { // xx0x xxxx PRIMITIVE $tagType = ASN1_TAG_PRIMITIVE; } $byte = $byte & 0x1F; // clear tags: 000x xxxx if ($byte == 0x1F) { throw new GTException("Multibyte tags not yet supported!"); } $object = null; if ($tagClass == ASN1_TAG_UNIVERSAL) { switch ($byte) { case ASN1_TAG_BOOLEAN: $object = new ASN1Boolean(); break; case ASN1_TAG_INTEGER: $object = new ASN1Integer(); break; case ASN1_TAG_BIT_STRING: $object = new ASN1BitString(); break; case ASN1_TAG_OCTET_STRING: $object = new ASN1OctetString(); break; case ASN1_TAG_NULL: $object = new ASN1Null(); break; case ASN1_TAG_OBJECT_ID: $object = new ASN1ObjectId(); break; case ASN1_TAG_UTF8_STRING: $object = new ASN1UTF8String(); break; case ASN1_TAG_PRINTABLE_STRING: $object = new ASN1PrintableString(); break; case ASN1_TAG_T61_STRING: $object = new ASN1T61String(); break; case ASN1_TAG_IA5_STRING: $object = new ASN1IA5String(); break; case ASN1_TAG_BMP_STRING: $object = new ASN1BMPString(); break; case ASN1_TAG_UTC_TIME: $object = new ASN1UTCTime(); break; case ASN1_TAG_GENERALIZED_TIME: $object = new ASN1GeneralizedTime(); break; case ASN1_TAG_SEQUENCE: $object = new ASN1Sequence(); break; case ASN1_TAG_SET: $object = new ASN1Set(); break; default: throw new GTException("Unsupported ASN.1 UNIVERSAL type: {$byte}"); } } else { $object = new ASN1Tag(); } $object->setTagClass($tagClass); $object->setTagType($tagType); $object->setTagValue($byte); return $object; } } ?>