ASN1: Improve input SEQUENCE and SET mapping, better syntax error detection.

This commit is contained in:
Patrick Monnerat 2012-11-07 15:23:54 +01:00
parent bf2107eaa8
commit d9ab2d7f10

View File

@ -515,6 +515,12 @@ class File_ASN1 {
case isset($option['constant']) && $option['constant'] == $decoded['constant']: case isset($option['constant']) && $option['constant'] == $decoded['constant']:
case !isset($option['constant']) && $option['type'] == $decoded['type']: case !isset($option['constant']) && $option['type'] == $decoded['type']:
$value = $this->asn1map($decoded, $option); $value = $this->asn1map($decoded, $option);
break;
case !isset($option['constant']) && $option['type'] == FILE_ASN1_TYPE_CHOICE:
$v = $this->asn1map($decoded, $option);
if (isset($v)) {
$value = $v;
}
} }
if (isset($value)) { if (isset($value)) {
return array($key => $value); return array($key => $value);
@ -537,92 +543,72 @@ class File_ASN1 {
case FILE_ASN1_TYPE_SEQUENCE: case FILE_ASN1_TYPE_SEQUENCE:
$map = array(); $map = array();
if (empty($decoded['content'])) {
return $map;
}
// ignore the min and max // ignore the min and max
if (isset($mapping['min']) && isset($mapping['max'])) { if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children']; $child = $mapping['children'];
foreach ($decoded['content'] as $content) { foreach ($decoded['content'] as $content) {
$map[] = $this->asn1map($content, $child); if (($map[] = $this->asn1map($content, $child)) === NULL) {
return NULL;
}
} }
return $map; return $map;
} }
$temp = $decoded['content'][$i = 0]; $n = count($decoded['content']);
$i = 0;
foreach ($mapping['children'] as $key => $child) { foreach ($mapping['children'] as $key => $child) {
if (!isset($child['optional']) && $child['type'] == FILE_ASN1_TYPE_CHOICE) { $maymatch = $i < $n; // Match only existing input.
$map[$key] = $this->asn1map($temp, $child); if ($maymatch) {
$i++;
if (count($decoded['content']) == $i) {
break;
}
$temp = $decoded['content'][$i]; $temp = $decoded['content'][$i];
continue;
}
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
$constant = NULL; // Get the mapping and input class & constant.
if (isset($temp['constant'])) { $childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
$tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; $constant = NULL;
} if (isset($temp['constant'])) {
if (isset($child['class'])) { $tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$childClass = $child['class'];
$constant = $child['cast'];
}
elseif (isset($child['constant'])) {
$childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($child['optional'])) {
if (isset($constant) && isset($temp['constant'])) {
if (($constant == $temp['constant']) && ($childClass == $tempClass)) {
$map[$key] = $this->asn1map($temp, $child);
$i++;
if (count($decoded['content']) == $i) {
break;
}
$temp = $decoded['content'][$i];
} }
} elseif (!isset($child['constant'])) { if (isset($child['class'])) {
// we could do this, as well: $childClass = $child['class'];
// $buffer = $this->asn1map($temp, $child); if (isset($buffer)) { $map[$key] = $buffer; } $constant = $child['cast'];
if ($child['type'] == $temp['type'] || $child['type'] == FILE_ASN1_TYPE_ANY) { }
$map[$key] = $this->asn1map($temp, $child); elseif (isset($child['constant'])) {
$i++; $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
if (count($decoded['content']) == $i) { $constant = $child['constant'];
break; }
}
$temp = $decoded['content'][$i]; if (isset($constant) && isset($temp['constant'])) {
} elseif ($child['type'] == FILE_ASN1_TYPE_CHOICE) { // Can only match if constants and class match.
$candidate = $this->asn1map($temp, $child); $maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
if (!empty($candidate)) { } else {
$map[$key] = $candidate; // Can only match if no constant expected and type matches or is generic.
$i++; $maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false;
if (count($decoded['content']) == $i) {
break;
}
$temp = $decoded['content'][$i];
}
} }
} }
}
if (!isset($map[$key]) && isset($child['default'])) { if ($maymatch) {
$map[$key] = $child['default']; // Attempt submapping.
} $candidate = $this->asn1map($temp, $child);
} else { $maymatch = $candidate !== NULL;
$map[$key] = $this->asn1map($temp, $child); }
if ($maymatch) {
// Got the match: use it.
$map[$key] = $candidate;
$i++; $i++;
if (count($decoded['content']) == $i) { } elseif (isset($child['default'])) {
break; $map[$key] = $child['default']; // Use default.
} } elseif (!isset($child['optional'])) {
$temp = $decoded['content'][$i]; return NULL; // Syntax error.
} }
} }
return $map; // Fail mapping if all input items have not been consumed.
return $i < $n? NULL: $map;
// the main diff between sets and sequences is the encapsulation of the foreach in another for loop // the main diff between sets and sequences is the encapsulation of the foreach in another for loop
case FILE_ASN1_TYPE_SET: case FILE_ASN1_TYPE_SET:
$map = array(); $map = array();
@ -631,52 +617,70 @@ class File_ASN1 {
if (isset($mapping['min']) && isset($mapping['max'])) { if (isset($mapping['min']) && isset($mapping['max'])) {
$child = $mapping['children']; $child = $mapping['children'];
foreach ($decoded['content'] as $content) { foreach ($decoded['content'] as $content) {
$map[] = $this->asn1map($content, $child); if (($map[] = $this->asn1map($content, $child)) === NULL) {
return NULL;
}
} }
return $map; return $map;
} }
for ($i = 0; $i < count($decoded['content']); $i++) { for ($i = 0; $i < count($decoded['content']); $i++) {
foreach ($mapping['children'] as $key => $child) { $temp = $decoded['content'][$i];
$temp = $decoded['content'][$i]; $tempClass = FILE_ASN1_CLASS_UNIVERSAL;
if (isset($temp['constant'])) {
$tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
}
if (!isset($child['optional']) && $child['type'] == FILE_ASN1_TYPE_CHOICE) { foreach ($mapping['children'] as $key => $child) {
$map[$key] = $this->asn1map($temp, $child); if (isset($map[$key])) {
continue; continue;
} }
$maymatch = true;
$childClass = $tempClass = FILE_ASN1_CLASS_UNIVERSAL; if ($child['type'] != FILE_ASN1_TYPE_CHOICE) {
$constant = NULL; $childClass = FILE_ASN1_CLASS_UNIVERSAL;
if (isset($temp['constant'])) { $constant = NULL;
$tempClass = isset($temp['class']) ? $temp['class'] : FILE_ASN1_CLASS_CONTEXT_SPECIFIC; if (isset($child['class'])) {
} $childClass = $child['class'];
if (isset($child['class'])) { $constant = $child['cast'];
$childClass = $child['class'];
$constant = $child['cast'];
}
elseif (isset($child['constant'])) {
$childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
$constant = $child['constant'];
}
if (isset($constant) && isset($temp['constant'])) {
if (($constant == $temp['constant']) && ($childClass == $tempClass)) {
$map[$key] = $this->asn1map($temp['content'], $child);
} }
} elseif (!isset($child['constant'])) { elseif (isset($child['constant'])) {
// we could do this, as well: $childClass = FILE_ASN1_CLASS_CONTEXT_SPECIFIC;
// $buffer = $this->asn1map($temp['content'], $child); if (isset($buffer)) { $map[$key] = $buffer; } $constant = $child['constant'];
if ($child['type'] == $temp['type']) { }
$map[$key] = $this->asn1map($temp, $child);
if (isset($constant) && isset($temp['constant'])) {
// Can only match if constants and class match.
$maymatch = $constant == $temp['constant'] && $childClass == $tempClass;
} else {
// Can only match if no constant expected and type matches or is generic.
$maymatch = !isset($child['constant']) && array_search($child['type'], array($temp['type'], FILE_ASN1_TYPE_ANY, FILE_ASN1_TYPE_CHOICE)) !== false;
} }
} }
if ($maymatch) {
// Attempt submapping.
$candidate = $this->asn1map($temp, $child);
$maymatch = $candidate !== NULL;
}
if (!$maymatch) {
break;
}
// Got the match: use it.
$map[$key] = $candidate;
break;
} }
} }
foreach ($mapping['children'] as $key => $child) { foreach ($mapping['children'] as $key => $child) {
if (!isset($map[$key]) && isset($child['default'])) { if (!isset($map[$key])) {
$map[$key] = $child['default']; if (isset($child['default'])) {
$map[$key] = $child['default'];
} elseif (!isset($child['optional'])) {
return NULL;
}
} }
} }
return $map; return $map;