| <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> |