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