From 4c5163234b002ba1225fae3f48acc7d490d5f7e2 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Thu, 18 Oct 2012 12:38:43 +0200 Subject: [PATCH] ASN1: Fix default value optimization upon encoding. Also encode SETs as SEQUENCEs since order is not important. --- phpseclib/File/ASN1.php | 95 +++++++++++------------------------------ 1 file changed, 25 insertions(+), 70 deletions(-) diff --git a/phpseclib/File/ASN1.php b/phpseclib/File/ASN1.php index dc536655..df3a7e44 100644 --- a/phpseclib/File/ASN1.php +++ b/phpseclib/File/ASN1.php @@ -728,8 +728,8 @@ class File_ASN1 { return $source->element; } - // do not encode optional fields with value set to default - if (!empty($mapping['optional']) && isset($mapping['default']) && $source === $mapping['default']) { + // do not encode (implicitly optional) fields with value set to default + if (isset($mapping['default']) && $source === $mapping['default']) { return ''; } @@ -740,6 +740,7 @@ class File_ASN1 { $tag = $mapping['type']; switch ($tag) { + case FILE_ASN1_TYPE_SET: // Children order is not important, thus process in sequence. case FILE_ASN1_TYPE_SEQUENCE: $tag|= 0x20; // set the constructed bit $value = ''; @@ -766,13 +767,19 @@ class File_ASN1 { continue; } + $temp = $this->_encode_der($source[$key], $child, $key); + if ($temp === false) { + return false; + } + + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { - $temp = $this->_encode_der($source[$key], $child, $key); - if ($temp === false) { - return false; - } - /* From X.680-0207.pdf#page=58 (30.6): @@ -789,65 +796,10 @@ class File_ASN1 { $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } - } else { - $temp = $this->_encode_der($source[$key], $child, $key); - if ($temp === false) { - return false; - } } $value.= $temp; } break; - // the main diff between sets and sequences is the encapsulation of the foreach in another for loop - case FILE_ASN1_TYPE_SET: - $tag|= 0x20; - $value = ''; - - // ignore the min and max - if (isset($mapping['min']) && isset($mapping['max'])) { - $child = $mapping['children']; - foreach ($source as $content) { - $temp = $this->_encode_der($content, $child); - if ($temp === false) { - return false; - } - $value.= $temp; - } - break; - } - - for ($i = 0; $i < count($source[$key]); $i++) { - foreach ($mapping['children'] as $key => $child) { - if (!isset($source[$key])) { - if (!isset($child['optional'])) { - return false; - } - continue; - } - - // if isset($child['constant']) is true then isset($child['optional']) should be true as well - if (isset($child['constant'])) { - $temp = $this->_encode_der($source[$key][$i], $child, $key); - if ($temp === false) { - return false; - } - if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) { - $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); - $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; - } else { - $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); - $temp = $subtag . substr($temp, 1); - } - } else { - $temp = $this->_encode_der($source[$key][$i], $child, $key); - if ($temp === false) { - return false; - } - } - $value.= $temp; - } - } - break; case FILE_ASN1_TYPE_CHOICE: $temp = false; @@ -857,13 +809,20 @@ class File_ASN1 { } $temp = $this->_encode_der($source[$key], $child, $key); + if ($temp === false) { + return false; + } + + // An empty child encoding means it has been optimized out. + // Else we should have at least one tag byte. + if ($temp === '') { + continue; + } + $tag = ord($temp[0]); // if isset($child['constant']) is true then isset($child['optional']) should be true as well if (isset($child['constant'])) { - if ($temp === false) { - return false; - } if (isset($child['explicit']) || $child['type'] == FILE_ASN1_TYPE_CHOICE) { $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | 0x20 | $child['constant']); $temp = $subtag . $this->_encodeLength(strlen($temp)) . $temp; @@ -871,10 +830,6 @@ class File_ASN1 { $subtag = chr((FILE_ASN1_CLASS_CONTEXT_SPECIFIC << 6) | (ord($temp[0]) & 0x20) | $child['constant']); $temp = $subtag . substr($temp, 1); } - } else { - if ($temp === false) { - return false; - } } } @@ -882,7 +837,7 @@ class File_ASN1 { array_pop($this->location); } - if (isset($mapping['cast'])) { + if ($temp && isset($mapping['cast'])) { $temp[0] = chr(($mapping['class'] << 6) | ($tag & 0x20) | $mapping['cast']); }