mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 20:47:44 +00:00
437 lines
11 KiB
HTML
437 lines
11 KiB
HTML
<html>
|
|
<title>
|
|
PyASN1 subtype constraints
|
|
</title>
|
|
<head>
|
|
</head>
|
|
<body>
|
|
<center>
|
|
<table width=60%>
|
|
<tr>
|
|
<td>
|
|
|
|
<h4>
|
|
1.4 PyASN1 subtype constraints
|
|
</h4>
|
|
|
|
<p>
|
|
Most ASN.1 types can correspond to an infinite set of values. To adapt to
|
|
particular application's data model and needs, ASN.1 provides a mechanism
|
|
for limiting the infinite set to values, that make sense in particular case.
|
|
</p>
|
|
|
|
<p>
|
|
Imposing value constraints on an ASN.1 type can also be seen as creating
|
|
a subtype from its base type.
|
|
</p>
|
|
|
|
<p>
|
|
In pyasn1, constraints take shape of immutable objects capable
|
|
of evaluating given value against constraint-specific requirements.
|
|
Constraint object is a property of pyasn1 type. Like TagSet property,
|
|
associated with every pyasn1 type, constraints can never be modified
|
|
in place. The only way to modify pyasn1 type constraint is to associate
|
|
new constraint object to a new pyasn1 type object.
|
|
</p>
|
|
|
|
<p>
|
|
A handful of different flavors of <i>constraints</i> are defined in ASN.1.
|
|
We will discuss them one by one in the following chapters and also explain
|
|
how to combine and apply them to types.
|
|
</p>
|
|
|
|
<a name="1.4.1"></a>
|
|
<h4>
|
|
1.4.1 Single value constraint
|
|
</h4>
|
|
|
|
<p>
|
|
This kind of constraint allows for limiting type to a finite, specified set
|
|
of values.
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
DialButton ::= OCTET STRING (
|
|
"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
|
)
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Its pyasn1 implementation would look like:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import constraint
|
|
>>> c = constraint.SingleValueConstraint(
|
|
'0','1','2','3','4','5','6','7','8','9'
|
|
)
|
|
>>> c
|
|
SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
|
>>> c('0')
|
|
>>> c('A')
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
As can be seen in the snippet above, if a value violates the constraint, an
|
|
exception will be thrown. A constrainted pyasn1 type object holds a
|
|
reference to a constraint object (or their combination, as will be explained
|
|
later) and calls it for value verification.
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import univ, constraint
|
|
>>> class DialButton(univ.OctetString):
|
|
... subtypeSpec = constraint.SingleValueConstraint(
|
|
... '0','1','2','3','4','5','6','7','8','9'
|
|
... )
|
|
>>> DialButton('0')
|
|
DialButton(b'0')
|
|
>>> DialButton('A')
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
SingleValueConstraint(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) failed at: A
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Constrained pyasn1 value object can never hold a violating value.
|
|
</p>
|
|
|
|
<a name="1.4.2"></a>
|
|
<h4>
|
|
1.4.2 Value range constraint
|
|
</h4>
|
|
|
|
<p>
|
|
A pair of values, compliant to a type to be constrained, denote low and upper
|
|
bounds of allowed range of values of a type.
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
Teenagers ::= INTEGER (13..19)
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
And in pyasn1 terms:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import univ, constraint
|
|
>>> class Teenagers(univ.Integer):
|
|
... subtypeSpec = constraint.ValueRangeConstraint(13, 19)
|
|
>>> Teenagers(14)
|
|
Teenagers(14)
|
|
>>> Teenagers(20)
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
ValueRangeConstraint(13, 19) failed at: 20
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Value range constraint usually applies numeric types.
|
|
</p>
|
|
|
|
<a name="1.4.3"></a>
|
|
<h4>
|
|
1.4.3 Size constraint
|
|
</h4>
|
|
|
|
<p>
|
|
It is sometimes convenient to set or limit the allowed size of a data item
|
|
to be sent from one application to another to manage bandwidth and memory
|
|
consumption issues. Size constraint specifies the lower and upper bounds
|
|
of the size of a valid value.
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
TwoBits ::= BIT STRING (SIZE (2))
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Express the same grammar in pyasn1:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import univ, constraint
|
|
>>> class TwoBits(univ.BitString):
|
|
... subtypeSpec = constraint.ValueSizeConstraint(2, 2)
|
|
>>> TwoBits((1,1))
|
|
TwoBits("'11'B")
|
|
>>> TwoBits((1,1,0))
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
ValueSizeConstraint(2, 2) failed at: (1, 1, 0)
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Size constraint can be applied to potentially massive values - bit or octet
|
|
strings, SEQUENCE OF/SET OF values.
|
|
</p>
|
|
|
|
<a name="1.4.4"></a>
|
|
<h4>
|
|
1.4.4 Alphabet constraint
|
|
</h4>
|
|
|
|
<p>
|
|
The permitted alphabet constraint is similar to Single value constraint
|
|
but constraint applies to individual characters of a value.
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
MorseCode ::= PrintableString (FROM ("."|"-"|" "))
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
And in pyasn1:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import char, constraint
|
|
>>> class MorseCode(char.PrintableString):
|
|
... subtypeSpec = constraint.PermittedAlphabetConstraint(".", "-", " ")
|
|
>>> MorseCode("...---...")
|
|
MorseCode('...---...')
|
|
>>> MorseCode("?")
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
PermittedAlphabetConstraint(".", "-", " ") failed at: "?"
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Current implementation does not handle ranges of characters in constraint
|
|
(FROM "A".."Z" syntax), one has to list the whole set in a range.
|
|
</p>
|
|
|
|
<a name="1.4.5"></a>
|
|
<h4>
|
|
1.4.5 Constraint combinations
|
|
</h4>
|
|
|
|
<p>
|
|
Up to this moment, we used a single constraint per ASN.1 type. The standard,
|
|
however, allows for combining multiple individual constraints into
|
|
intersections, unions and exclusions.
|
|
</p>
|
|
|
|
<p>
|
|
In pyasn1 data model, all of these methods of constraint combinations are
|
|
implemented as constraint-like objects holding individual constraint (or
|
|
combination) objects. Like terminal constraint objects, combination objects
|
|
are capable to perform value verification at its set of enclosed constraints
|
|
according to the logic of particular combination.
|
|
</p>
|
|
|
|
<p>
|
|
Constraints intersection verification succeeds only if a value is
|
|
compliant to each constraint in a set. To begin with, the following
|
|
specification will constitute a valid telephone number:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
PhoneNumber ::= NumericString (FROM ("0".."9")) (SIZE 11)
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Constraint intersection object serves the logic above:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import char, constraint
|
|
>>> class PhoneNumber(char.NumericString):
|
|
... subtypeSpec = constraint.ConstraintsIntersection(
|
|
... constraint.PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
|
|
... constraint.ValueSizeConstraint(11, 11)
|
|
... )
|
|
>>> PhoneNumber('79039343212')
|
|
PhoneNumber('79039343212')
|
|
>>> PhoneNumber('?9039343212')
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
ConstraintsIntersection(
|
|
PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
|
|
ValueSizeConstraint(11, 11)) failed at:
|
|
PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9') failed at: "?039343212"
|
|
>>> PhoneNumber('9343212')
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
ConstraintsIntersection(
|
|
PermittedAlphabetConstraint('0','1','2','3','4','5','6','7','8','9'),
|
|
ValueSizeConstraint(11, 11)) failed at:
|
|
ValueSizeConstraint(10, 10) failed at: "9343212"
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Union of constraints works by making sure that a value is compliant
|
|
to any of the constraint in a set. For instance:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
CapitalOrSmall ::= IA5String (FROM ('A','B','C') | FROM ('a','b','c'))
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
It's important to note, that a value must fully comply to any single
|
|
constraint in a set. In the specification above, a value of all small or
|
|
all capital letters is compliant, but a mix of small&capitals is not.
|
|
Here's its pyasn1 analogue:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import char, constraint
|
|
>>> class CapitalOrSmall(char.IA5String):
|
|
... subtypeSpec = constraint.ConstraintsUnion(
|
|
... constraint.PermittedAlphabetConstraint('A','B','C'),
|
|
... constraint.PermittedAlphabetConstraint('a','b','c')
|
|
... )
|
|
>>> CapitalOrSmall('ABBA')
|
|
CapitalOrSmall('ABBA')
|
|
>>> CapitalOrSmall('abba')
|
|
CapitalOrSmall('abba')
|
|
>>> CapitalOrSmall('Abba')
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
ConstraintsUnion(PermittedAlphabetConstraint('A', 'B', 'C'),
|
|
PermittedAlphabetConstraint('a', 'b', 'c')) failed at: failed for "Abba"
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
Finally, the exclusion constraint simply negates the logic of value
|
|
verification at a constraint. In the following example, any integer value
|
|
is allowed in a type but not zero.
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
NoZero ::= INTEGER (ALL EXCEPT 0)
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
In pyasn1 the above definition would read:
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import univ, constraint
|
|
>>> class NoZero(univ.Integer):
|
|
... subtypeSpec = constraint.ConstraintsExclusion(
|
|
... constraint.SingleValueConstraint(0)
|
|
... )
|
|
>>> NoZero(1)
|
|
NoZero(1)
|
|
>>> NoZero(0)
|
|
Traceback (most recent call last):
|
|
...
|
|
pyasn1.type.error.ValueConstraintError:
|
|
ConstraintsExclusion(SingleValueConstraint(0)) failed at: 0
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
The depth of such a constraints tree, built with constraint combination objects
|
|
at its nodes, has not explicit limit. Value verification is performed in a
|
|
recursive manner till a definite solution is found.
|
|
</p>
|
|
|
|
<a name="1.5"></a>
|
|
<h4>
|
|
1.5 Types relationships
|
|
</h4>
|
|
|
|
<p>
|
|
In the course of data processing in an application, it is sometimes
|
|
convenient to figure out the type relationships between pyasn1 type or
|
|
value objects. Formally, two things influence pyasn1 types relationship:
|
|
<i>tag set</i> and <i>subtype constraints</i>. One pyasn1 type is considered
|
|
to be a derivative of another if their TagSet and Constraint objects are
|
|
a derivation of one another.
|
|
</p>
|
|
|
|
<p>
|
|
The following example illustrates the concept (we use the same tagset but
|
|
different constraints for simplicity):
|
|
</p>
|
|
|
|
<table bgcolor="lightgray" border=0 width=100%><TR><TD>
|
|
<pre>
|
|
>>> from pyasn1.type import univ, constraint
|
|
>>> i1 = univ.Integer(subtypeSpec=constraint.ValueRangeConstraint(3,8))
|
|
>>> i2 = univ.Integer(subtypeSpec=constraint.ConstraintsIntersection(
|
|
... constraint.ValueRangeConstraint(3,8),
|
|
... constraint.ValueRangeConstraint(4,7)
|
|
... ) )
|
|
>>> i1.isSameTypeWith(i2)
|
|
False
|
|
>>> i1.isSuperTypeOf(i2)
|
|
True
|
|
>>> i1.isSuperTypeOf(i1)
|
|
True
|
|
>>> i2.isSuperTypeOf(i1)
|
|
False
|
|
>>>
|
|
</pre>
|
|
</td></tr></table>
|
|
|
|
<p>
|
|
As can be seen in the above code snippet, there are two methods of any pyasn1
|
|
type/value object that test types for their relationship:
|
|
<b>isSameTypeWith</b>() and <b>isSuperTypeOf</b>(). The former is
|
|
self-descriptive while the latter yields true if the argument appears
|
|
to be a pyasn1 object which has tagset and constraints derived from those
|
|
of the object being called.
|
|
</p>
|
|
|
|
<hr>
|
|
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
</body>
|
|
</html>
|