RFC 3687

Lightweight Directory Access Protocol (LDAP) and X.500 Component Matching Rules

πŸ“… published February 2004, from ietf

Lightweight Directory Access Protocol (LDAP) and X.500 Component Matching Rules πŸŽ€

Okay bestie, this is RFC 3687 πŸ’– It was written by S. Legg of Adacel Technologies, lives in the Network Working Group, and is a Standards Track document dated February 2004. The title block doesn't list any RFCs it obsoletes or updates, so it's a standalone spec πŸ¦‹

The official status and copyright text comes next. Here it is word for word, because the legal stuff has to be complete ✨

Status of this Memo

This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited.

Copyright (C) The Internet Society (2004). All Rights Reserved.

Abstract

Okay so attribute syntaxes in LDAP or X.500 directories go from super simple stuff (text strings, integers, booleans) all the way up to really complex structured types, like the syntaxes of the directory schema operational attributes πŸ’… Matching rules for the complex ones usually only give you the most immediately useful matching, which is kinda sad 😭 This document defines generic matching rules that can match any component parts you pick out of an attribute value, no matter how complicated the attribute syntax is πŸ”₯

Table of Contents

   1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  3
   2.  Conventions. . . . . . . . . . . . . . . . . . . . . . . . . .  4
   3.  ComponentAssertion . . . . . . . . . . . . . . . . . . . . . .  5
       3.1.  Component Reference. . . . . . . . . . . . . . . . . . .  6
             3.1.1.  Component Type Substitutions . . . . . . . . . .  7
             3.1.2.  Referencing SET, SEQUENCE and CHOICE Components.  8
             3.1.3.  Referencing SET OF and SEQUENCE OF Components. .  9
             3.1.4.  Referencing Components of Parameterized Types. . 10
             3.1.5.  Component Referencing Example. . . . . . . . . . 10
             3.1.6.  Referencing Components of Open Types . . . . . . 12
                     3.1.6.1. Open Type Referencing Example . . . . . 12
             3.1.7.  Referencing Contained Types. . . . . . . . . . . 14
                     3.1.7.1. Contained Type Referencing Example. . . 14
       3.2.  Matching of Components . . . . . . . . . . . . . . . . . 15
             3.2.1.  Applicability of Existing Matching Rules . . . . 17
                     3.2.1.1. String Matching . . . . . . . . . . . . 17
                     3.2.1.2. Telephone Number Matching . . . . . . . 17
                     3.2.1.3. Distinguished Name Matching . . . . . . 18
             3.2.2.  Additional Useful Matching Rules . . . . . . . . 18
                     3.2.2.1. The rdnMatch Matching Rule. . . . . . . 18
                     3.2.2.2. The presentMatch Matching Rule. . . . . 19
             3.2.3.  Summary of Useful Matching Rules . . . . . . . . 20
   4.  ComponentFilter. . . . . . . . . . . . . . . . . . . . . . . . 21
   5.  The componentFilterMatch Matching Rule . . . . . . . . . . . . 22
   6.  Equality Matching of Complex Components. . . . . . . . . . . . 24
       6.1.  The OpenAssertionType Syntax . . . . . . . . . . . . . . 24
       6.2.  The allComponentsMatch Matching Rule . . . . . . . . . . 25
       6.3.  Deriving Component Equality Matching Rules . . . . . . . 27
       6.4.  The directoryComponentsMatch Matching Rule . . . . . . . 28
   7.  Component Matching Examples. . . . . . . . . . . . . . . . . . 30
   8.  Security Considerations. . . . . . . . . . . . . . . . . . . . 37
   9.  Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 37
   10. IANA Considerations. . . . . . . . . . . . . . . . . . . . . . 37
   11. References . . . . . . . . . . . . . . . . . . . . . . . . . . 38
       11.1.  Normative References. . . . . . . . . . . . . . . . . . 38
       11.2.  Informative References. . . . . . . . . . . . . . . . . 40
   12. Intellectual Property Statement. . . . . . . . . . . . . . . . 40
   13. Author's Address . . . . . . . . . . . . . . . . . . . . . . . 41
   14. Full Copyright Statement . . . . . . . . . . . . . . . . . . . 42

1. Introduction ✨

Attribute syntax is basically the blueprint for what kind of data lives in an attribute of an LDAP [7] or X.500 [19] directory 🧚 Those blueprints range from simple stuff like text strings, integers, and booleans to complex types, like the syntaxes of the directory schema operational attributes πŸ’œ

In X.500, attribute syntaxes are spelled out in Abstract Syntax Notation One (ASN.1) [13] type definitions. ASN.1 has simple types (like PrintableString, INTEGER, and BOOLEAN) and combining types (SET, SEQUENCE, SET OF, SEQUENCE OF, and CHOICE) that you can use to build seriously complicated types out of simpler component types 🌈 In LDAP, syntaxes are usually written in Augmented Backus-Naur Form (ABNF) [2], but there's an implied link between LDAP attribute syntaxes and the X.500 ASN.1 types. Honestly, most of the data types for attribute values in LDAP or X.500 directories are described by ASN.1 types 🫢 That formal description can be used to find the component parts of an attribute value for all sorts of purposes. This document is all about attribute value matching πŸ”₯

Here's the thing: with any complex attribute syntax, you usually want to partially match a value by only matching the parts you care about πŸ™ˆ Usually there are rules specific to that syntax that handle this, but those are normally only super useful in the most immediate way. Some complex syntaxes don't even have an equality matching rule, let alone extra rules for partial matching πŸ’€ This document gives you a generic way to match user-selected components in an attribute value of any arbitrarily complex syntax, as long as that syntax is described with ASN.1 type notation. Every type notation defined in X.680 [13] is supported πŸ’–

Section 3 describes the ComponentAssertion, which is a testable assertion about the value of a component in an attribute value of any complex syntax πŸ¦‹

Section 4 introduces the ComponentFilter assertion, which is an expression built out of ComponentAssertions. It makes filter matching of components in an attribute value way more powerful πŸ’…

Section 5 defines the componentFilterMatch matching rule, which lets you evaluate a ComponentFilter against attribute values 🌸

Section 6 defines matching rules for component-wise equality matching of attribute values for any syntax described by an ASN.1 type definition πŸ‘‘

Examples showing how to use componentFilterMatch are in Section 7 πŸŽ€

For a brand new attribute syntax, the Generic String Encoding Rules [9] plus the specs in sections 3 to 6 of this document let you fully and precisely define the LDAP-specific encoding, the LDAP and X.500 binary encoding (and possibly other ASN.1 encodings later on), a suitable equality matching rule, and a whole collection of component matching capabilities. All you have to do is write down an ASN.1 type definition for the syntax πŸ”₯ These implicit definitions get extended automatically if the ASN.1 type gets extended later, which is so iconic. The relationship between the ASN.1 type definition, the various encodings, and the component matching behaviour means directory server support for the component matching rules can be automatically generated from ASN.1 type definitions πŸ€–βœ¨

Schema designers get to choose: store related data items as one attribute value of a complex syntax in a single entry, or as a subordinate entry where each related item is its own attribute value with a simpler syntax πŸ“ People have argued for the subordinate entries approach because you can't search inside the parts of a complex syntax. The component matching rules give you the same kind of matching on a complex attribute value that a search filter gives you on a subordinate entry 🌈

Most LDAP syntaxes have matching ASN.1 type definitions, even though those definitions usually aren't reproduced or referenced next to the formal LDAP syntax definition πŸ‘€ Syntaxes defined with only a character string encoding (so no explicit or implied ASN.1 type definition) can't use the component matching capabilities in this document, unless and until someone defines a semantically equivalent ASN.1 type for them 😭

2. Conventions 🌸

Throughout this document, "type" means an ASN.1 type unless it's explicitly qualified as an attribute type, and "value" means an ASN.1 value unless it's explicitly qualified as an attribute value πŸ’œ

Quick note: "ASN.1 value" does not mean a Basic Encoding Rules (BER) [17] encoded value 🧠 The ASN.1 value is an abstract concept that doesn't depend on any particular encoding. BER is just one possible encoding of an ASN.1 value. The component matching rules work at that abstract level, without caring how a value gets encoded πŸŽ€

Attribute type and matching rule definitions in this document come in both the X.500 [10] and LDAP [4] description formats πŸ¦‹ The LDAP descriptions have extra white-space and line breaks added so they're easier to read ✨

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED" and "MAY" in this document are to be interpreted as described in BCP 14, RFC 2119 [1]. The key word "OPTIONAL" is used exclusively with its ASN.1 meaning 🫢

3. ComponentAssertion πŸ’–

A ComponentAssertion is an assertion about whether components are present in an ASN.1 value (an instance of an ASN.1 type), or about what values those components have πŸ”₯ Usually the ASN.1 value is an attribute value, and the ASN.1 type is the syntax of that attribute. But a ComponentAssertion can also be applied to a component part of an attribute value 🧚 For each ASN.1 value it tests, the assertion evaluates to TRUE, FALSE, or Undefined πŸ’…

A ComponentAssertion is described by the following ASN.1 type (assumed to be defined with "EXPLICIT TAGS" in force):

   ComponentAssertion ::= SEQUENCE {
       component         ComponentReference (SIZE(1..MAX)) OPTIONAL,
       useDefaultValues  BOOLEAN DEFAULT TRUE,
       rule              MATCHING-RULE.&id,
       value             MATCHING-RULE.&AssertionType }

   ComponentReference ::= UTF8String

MATCHING-RULE.&id is the OBJECT IDENTIFIER of a matching rule. MATCHING-RULE.&AssertionType is an open type (formerly known as the ANY type) 🌈

Here's how the fields work. The component field says which component part of a value of some ASN.1 type gets tested. The useDefaultValues field says whether DEFAULT values should be substituted for absent component values. The rule field says how the component gets tested, and the value field is the asserted ASN.1 value the component is tested against. The ASN.1 type of the asserted value depends on which rule you pick πŸŽ€

The fields of a ComponentAssertion get described in detail in the following sections πŸ‘‘

3.1. Component Reference πŸŽ€

okay so the component field in a ComponentAssertion is a UTF-8 character string [6], and its text is a component reference 🧚 That's the thing that points at a component part of some ASN.1 type or value. A component reference follows this ABNF [2], which extends the notation from Clause 14 of X.680 [13]:

   component-reference = ComponentId *( "." ComponentId )
   ComponentId         = identifier /
                         from-beginning /
                         count /
                         from-end /       ; extends Clause 14
                         content /        ; extends Clause 14
                         select /         ; extends Clause 14
                         all

   identifier          = lowercase *alphanumeric
                            *(hyphen 1*alphanumeric)
   alphanumeric        = uppercase / lowercase / decimal-digit
   uppercase           = %x41-5A  ; "A" to "Z"
   lowercase           = %x61-7A  ; "a" to "z"
   hyphen              = "-"

   from-beginning      = positive-number
   count               = "0"
   from-end            = "-" positive-number
   content             = %x63.6F.6E.74.65.6E.74 ; "content"
   select              = "(" Value *( "," Value ) ")"
   all                 = "*"


   positive-number     = non-zero-digit *decimal-digit

   decimal-digit       = %x30-39  ; "0" to "9"
   non-zero-digit      = %x31-39  ; "1" to "9"

An <identifier> matches the ASN.1 identifier definition (Clause 11.3 of X.680 [13]) ✨ It starts with a lowercase letter, then has zero or more letters, digits, and hyphens. A hyphen can't be the last character, and it can't be followed by another hyphen either. Not allowed, no cap πŸ™…β€β™€οΈ

The <Value> rule is described by the Generic String Encoding Rules (GSER) [9].

A component reference is a sequence of one or more ComponentIds. Each one picks either an inner component one level deeper inside an ASN.1 combining type (that's SET, SEQUENCE, SET OF, SEQUENCE OF, or CHOICE), or a specific type inside an ASN.1 open type πŸ’…

A component reference is always read in the context of a particular complex ASN.1 type πŸ’– Applied to the ASN.1 type, it identifies a specific component type. Applied to a value of that ASN.1 type, it identifies zero, one, or more component values of that component type. The component values might sit in a DEFAULT value if useDefaultValues is TRUE. The specific component type that the reference identifies decides which matching rules can be used to match the component values. It's giving precision πŸ”₯

The component field in a ComponentAssertion can also be absent. In that case the identified component type is the ASN.1 type that the ComponentAssertion is applied to, and the identified component value is the whole ASN.1 value 🌸

A valid component reference for a particular complex ASN.1 type gets built by starting at the outermost combining type and repeatedly picking one of the allowed forms of ComponentId, so you dig into successively deeper nested components πŸ§šβ€β™€οΈ A component reference MAY identify a component with a complex ASN.1 type. In other words, the component type a reference identifies doesn't have to be a simple ASN.1 type. Iconic flexibility πŸ‘‘


3.1.1. Component Type Substitutions πŸ¦‹

ASN.1 type notation has a bunch of constructs for referencing other defined types, plus constructs that don't matter for matching. None of these show up in a component reference, so the component type gets substituted to strip them out before we think about anything else. These substitutions happen automatically before each ComponentId, whether you're building or reading a component reference. They do NOT happen after the last ComponentId, except as Section 3.2 allows. Lowkey important 🫢

If the ASN.1 type is an ASN.1 type reference, the component type is the actual definition on the right-hand side of the type assignment for the referenced type.

If the ASN.1 type is a tagged type, the component type is the type without the tag.

If the ASN.1 type is a constrained type (see X.680 [13] and X.682 [15] for the details of ASN.1 constraint notation), the component type is the type without the constraint.

If the ASN.1 type is an ObjectClassFieldType (Clause 14 of X.681 [14]) that denotes a specific ASN.1 type (for example, MATCHING-RULE.&id denotes the OBJECT IDENTIFIER type), the component type is the denoted type. Section 3.1.6 covers the case where the ObjectClassFieldType denotes an open type.

If the ASN.1 type is a selection type that isn't used in the list of components for a SET or SEQUENCE type, the component type is the selected alternative type from the named CHOICE.

If the ASN.1 type is a TypeFromObject (Clause 15 of X.681 [14]), the component type is the denoted type.

If the ASN.1 type is a ValueSetFromObjects (Clause 15 of X.681 [14]), the component type is the governing type of the denoted values.


3.1.2. Referencing SET, SEQUENCE and CHOICE Components πŸ’œ

If the ASN.1 type is a SET or SEQUENCE type, the <identifier> form of ComponentId can be used to identify the component type inside that SET or SEQUENCE that has that identifier. If <identifier> points at an OPTIONAL component type and that component isn't present in a particular value, there are no corresponding component values. If <identifier> points at a DEFAULT component type, useDefaultValues is TRUE (the default setting for useDefaultValues), and the component isn't present in a particular value, the component value is taken to be the default value. If <identifier> points at a DEFAULT component type, useDefaultValues is FALSE, and the component isn't present in a particular value, there are no corresponding component values. Phew, that's a lot of branches πŸ’€

If the ASN.1 type is a CHOICE type, the <identifier> form of ComponentId can be used to identify the alternative type inside that CHOICE that has that identifier. If <identifier> points at an alternative other than the one used in a particular value, there are no corresponding component values.

The COMPONENTS OF notation in Clause 24 of X.680 [13] adds to the defined list of components in a SET or SEQUENCE type by including all the components of another defined SET or SEQUENCE type, respectively. Those included components get referenced directly by identifier, as though they were defined in-line in the SET or SEQUENCE type that has the COMPONENTS OF notation πŸ“

The SelectionType (Clause 29 of X.680 [13]), when it's used in the list of components for a SET or SEQUENCE type, includes a single component from a defined CHOICE type. That included component gets referenced directly by identifier, as though it were defined in-line in the SET or SEQUENCE type πŸŽ€

The REAL type is treated as though it's the SEQUENCE type defined in Clause 20.5 of X.680 [13].

The EMBEDDED PDV type is treated as though it's the SEQUENCE type defined in Clause 33.5 of X.680 [13].

The EXTERNAL type is treated as though it's the SEQUENCE type defined in Clause 8.18.1 of X.690 [17].

The unrestricted CHARACTER STRING type is treated as though it's the SEQUENCE type defined in Clause 40.5 of X.680 [13].

The INSTANCE OF type is treated as though it's the SEQUENCE type defined in Annex C of X.681 [14].

The <identifier> form MUST NOT be used on any other ASN.1 type. This one is a hard no πŸ™…β€β™€οΈπŸš«


3.1.3. Referencing SET OF and SEQUENCE OF Components 🌈

If the ASN.1 type is a SET OF or SEQUENCE OF type, the <from-beginning>, <from-end>, <count> and <all> forms of ComponentId can be used.

The <from-beginning> form of ComponentId can be used to identify one instance (that is, one value) of the component type of the SET OF or SEQUENCE OF type. For example, if Foo ::= SET OF Bar, then Bar is the component type. The instances are numbered from one upwards. If <from-beginning> points at a higher-numbered instance than the last instance in a particular value of the SET OF or SEQUENCE OF type, there's no corresponding component value.

The <from-end> form of ComponentId can be used to identify one instance of the component type of the SET OF or SEQUENCE OF type. Here "-1" is the last instance, "-2" is the second-last instance, and so on. If <from-end> points at a lower-numbered instance than the first instance in a particular value of the SET OF or SEQUENCE OF type, there's no corresponding component value.

The <count> form of ComponentId identifies a notional count of the number of instances of the component type in a value of the SET OF or SEQUENCE OF type. This count isn't explicitly represented, but for matching purposes it has an assumed ASN.1 type of INTEGER (0..MAX). A ComponentId of the <count> form, if used, MUST be the last ComponentId in a component reference. Last one in the chain, bestie 🧚

The <all> form of ComponentId can be used to identify all instances of the component type of the SET OF or SEQUENCE OF type at the same time. This is the only way a component reference can identify more than one component value ✨ That said, if a particular value of the SET OF or SEQUENCE OF type is an empty list, there are no corresponding component values.

When multiple component values are identified, any remaining ComponentIds in the component reference can identify zero, one, or more subcomponent values for each of the higher-level component values πŸ’–

The corresponding ASN.1 type for the <from-beginning>, <from-end> and <all> forms of ComponentId is the component type of the SET OF or SEQUENCE OF type.

The <from-beginning>, <count>, <from-end> and <all> forms MUST NOT be used on ASN.1 types other than SET OF or SEQUENCE OF. Not on my watch πŸ™…β€β™€οΈ


3.1.4. Referencing Components of Parameterized Types πŸ¦‹

A component reference can't be formed for a parameterized type unless the type has been used with actual parameters. When it has, the type is treated as though the DummyReferences [16] have been substituted with those actual parameters 🌸

3.1.5. Component Referencing Example ✨

okay bestie, time for an example!! πŸ’– Here are some ASN.1 type definitions to look at:

ExampleType ::= SEQUENCE {
    part1       [0] INTEGER,
    part2       [1] ExampleSet,
    part3       [2] SET OF OBJECT IDENTIFIER,
    part4       [3] ExampleChoice }

ExampleSet ::= SET {
    option      PrintableString,
    setting     BOOLEAN }

ExampleChoice ::= CHOICE {
    eeny-meeny  BIT STRING,
    miney-mo    OCTET STRING }

Now let's build some component references against ExampleType 🧚

The component reference part1 picks out a component of a value of ExampleType that has the ASN.1 tagged type [0] INTEGER. Iconic. 🌸

The component reference part2 picks out a component of a value of ExampleType that has the ASN.1 type of [1] ExampleSet.

The component reference part2.option picks out a component of a value of ExampleType that has the ASN.1 type of PrintableString. ngl this is where it gets fun, because a ComponentAssertion could also be applied to a value of ASN.1 type ExampleSet directly. In that case, the component reference option would identify the same kind of information. πŸ¦‹

The component reference part3 picks out a component of a value of ExampleType that has the ASN.1 type of [2] SET OF OBJECT IDENTIFIER. πŸ’…

The component reference part3.2 picks out the second instance of the part3 SET OF. That instance has the ASN.1 type of OBJECT IDENTIFIER. ✨

The component reference part3.0 picks out the count of how many instances are in the part3 SET OF. The count has the corresponding ASN.1 type of INTEGER (0..MAX). Counting is literally a whole vibe πŸ”’πŸ’€

The component reference part3.* picks out all the instances in the part3 SET OF at once. Each instance has the ASN.1 type of OBJECT IDENTIFIER. main character energy πŸ‘‘

The component reference part4 picks out a component of a value of ExampleType that has the ASN.1 type of [3] ExampleChoice.

The component reference part4.miney-mo picks out a component of a value of ExampleType that has the ASN.1 type of OCTET STRING. πŸ“

3.1.6. Referencing Components of Open Types 🌈

okay so this part is a little spicy 🌢️ If a sequence of ComponentIds identifies an ObjectClassFieldType that denotes an open type (for example, ATTRIBUTE.&Type denotes an open type), then the ASN.1 type of the component varies. Not me going "wait, it changes??" 😭

The good news is that an open type is usually constrained by some other component or components in an outer enclosing type. That constraint can be formal, through a component relation constraint [15], or informal, through the accompanying text. Because of that, the actual ASN.1 type of a value of the open type will generally be known. The constraint also limits the range of types that are allowed. 🫢

To pick out one of those allowed types inside an open type, the <select> form of ComponentId may be used. Once you've picked the type, you can identify its subcomponents with further ComponentIds. Easy peasy πŸŽ€

The other components that constrain the open type are called the referenced components [15]. The <select> form holds a list of one or more values. Those values take the place of the value or values of the referenced component or components, and together they uniquely identify one of the allowed types of the open type. πŸ”₯

When the open type is constrained by a component relation constraint, there is one <Value> in the <select> form for each referenced component in that constraint. They show up in the same order. The ASN.1 type of each of these values is the same as the ASN.1 type of the matching referenced component. A referenced component can be any ASN.1 type at all, but it's typically an OBJECT IDENTIFIER or an INTEGER. So the <Value> in the <select> form will nearly always be an <ObjectIdentifierValue> or an <IntegerValue> [9]. Also, component relation constraints typically have only one referenced component. ✨

When the open type is not constrained by a component relation constraint, the specification that introduces the syntax containing the open type should explicitly name the referenced components and their order. That way the <select> form can still be used. πŸ“

If an instance of <select> contains a value that is different from the value of the referenced component used in a particular value of the outer enclosing type, then there are no corresponding component values for the open type. Not a match, no tea πŸ«–πŸ’…

3.1.6.1. Open Type Referencing Example 🧁

The ASN.1 type AttributeTypeAndValue [10] describes a single attribute value of a named attribute type.

AttributeTypeAndValue ::= SEQUENCE {
    type    ATTRIBUTE.&id ({SupportedAttributes}),
    value   ATTRIBUTE.&Type ({SupportedAttributes}{@type}) }

ATTRIBUTE.&id denotes an OBJECT IDENTIFIER, and ({SupportedAttributes}) constrains that OBJECT IDENTIFIER to be a supported attribute type. πŸ™ˆ

ATTRIBUTE.&Type denotes an open type. In this case that open type is an attribute value. ({SupportedAttributes}{@type}) is a component relation constraint, and it constrains the open type to be the attribute syntax for the attribute type. The constraint only references the type component, which has the ASN.1 type of OBJECT IDENTIFIER. So if you use the <select> form to identify attribute values of specific attribute types, it will contain a single OBJECT IDENTIFIER value. πŸ’œ

The component reference value on AttributeTypeAndValue refers to the open type.

One of the X.500 standard attributes is facsimileTelephoneNumber [12]. It is identified with the OBJECT IDENTIFIER 2.5.4.23, and it is defined to have the following syntax.

FacsimileTelephoneNumber ::= SEQUENCE {
    telephoneNumber PrintableString(SIZE(1..ub-telephone-number)),
    parameters      G3FacsimileNonBasicParameters OPTIONAL }

The component reference value.(2.5.4.23) on AttributeTypeAndValue specifies an attribute value with the FacsimileTelephoneNumber syntax. πŸ“ž

The component reference value.(2.5.4.23).telephoneNumber on AttributeTypeAndValue identifies the telephoneNumber component of a facsimileTelephoneNumber attribute value. The component reference value.(facsimileTelephoneNumber) is equivalent to value.(2.5.4.23). Two ways to say the same thing, iykyk πŸ¦‹

If the AttributeTypeAndValue ASN.1 value contains an attribute type other than facsimileTelephoneNumber, then there are no corresponding component values for the component references value.(2.5.4.23) and value.(2.5.4.23).telephoneNumber. πŸ™…β€β™€οΈ

3.1.7. Referencing Contained Types πŸŽ€

sometimes the contents of a BIT STRING or OCTET STRING value have to be encodings of other ASN.1 values of specific types. For example, the extnValue component of the Extension type component in the Certificate type [11] is an OCTET STRING that has to contain a Distinguished Encoding Rules (DER) [17] encoding of a certificate extension value. It's really useful to be able to refer to that embedded encoded value and its components. We call the embedded encoded value a contained value, and its associated type the contained type. πŸ§šβ€β™€οΈ

If the ASN.1 type is a BIT STRING or OCTET STRING type that contains encodings of other ASN.1 values, then the <content> form of ComponentId may be used to identify the contained type. Subcomponents of that type can then be identified with further ComponentIds. πŸ“

The contained type may be (effectively) an open type, constrained by some other component in an outer enclosing type. For example, in a certificate Extension, extnValue is constrained by the chosen extnId. In these cases, the next ComponentId, if any, MUST be of the <select> form. Non-negotiable bestie 🚫

For building component references, the content of the extnValue OCTET STRING in the Extension type is assumed to be an open type. That open type has a notional component relation constraint, with the extnId component as the single referenced component. In other words:

EXTENSION.&ExtnType ({ExtensionSet}{@extnId})

The data-value component of the associated types for the EMBEDDED PDV and CHARACTER STRING types is an OCTET STRING. It contains the encoding of a data value that is described by the identification component. For building component references, the content of that data-value OCTET STRING is assumed to be an open type. It has a notional component relation constraint with the identification component as the single referenced component. 🌸

3.1.7.1. Contained Type Referencing Example πŸ’–

The Extension ASN.1 type [11] describes a single certificate extension value of a named extension type.

Extension ::= SEQUENCE {
    extnId     EXTENSION.&id ({ExtensionSet}),
    critical   BOOLEAN DEFAULT FALSE,
    extnValue  OCTET STRING
        -- contains a DER encoding of a value of type &ExtnType
        -- for the extension object identified by extnId -- }

EXTENSION.&id denotes an OBJECT IDENTIFIER, and ({ExtensionSet}) constrains that OBJECT IDENTIFIER to be the identifier of a supported certificate extension. 🫢

The component reference extnValue on Extension refers to a component type of OCTET STRING, so the corresponding component values will be OCTET STRING values. The component reference extnValue.content on Extension refers to the type of the contained type, which in this case is an open type. πŸ”₯

One of the X.509 [11] standard extensions is basicConstraints. It is identified with the OBJECT IDENTIFIER 2.5.29.19, and it is defined to have the following syntax.

BasicConstraintsSyntax ::= SEQUENCE {
    cA                 BOOLEAN DEFAULT FALSE,
    pathLenConstraint  INTEGER (0..MAX) OPTIONAL }

The component reference extnValue.content.(2.5.29.19) on Extension specifies a BasicConstraintsSyntax extension value. The component reference extnValue.content.(2.5.29.19).cA identifies the cA component of a BasicConstraintsSyntax extension value. βœ¨πŸ‘‘

3.2. Matching of Components πŸŽ€

okay so the rule in a ComponentAssertion is the bit that decides how the component values (zero, one, or more of them) pulled out by the component reference get tested. πŸ’… Attribute matching rules define what that test actually means.

Each matching rule has a notional set of attribute syntaxes (usually just one) written as ASN.1 types, and it can be applied to those. Inside a ComponentAssertion, the rule works on the same ASN.1 types, except the ASN.1 values aren't necessarily complete attribute values. πŸ¦‹

Here's a bestie-level heads up: the referenced component type might be a tagged and/or constrained version of the attribute syntax the rule expects. For example, you could have [0] INTEGER when integerMatch expects plain INTEGER. It could also be an open type. 🧚 When that happens, the additional type substitutions from Section 3.1.1 get applied as needed to shrink the component type down to the same type as the attribute syntax the matching rule expects.

If a matching rule works with more than one attribute syntax (objectIdentifierFirstComponentMatch [12] is one), the minimum number of substitutions needed to fit any one of those syntaxes gets done. ✨ If a matching rule can apply to any attribute syntax at all (like allComponentsMatch from Section 6.2), then the referenced component type gets used exactly as it is, with no extra substitutions. Very chill.

The value in a ComponentAssertion has to be in the assertion syntax (the ASN.1 type) that the chosen matching rule requires. Heads up, though: a matching rule's assertion syntax isn't always the same as the attribute syntax(es) it can be applied to. 🌸

Some matching rules don't have a fixed assertion syntax (allComponentsMatch being one). For those, the assertion syntax gets worked out every time the rule is used, based on the syntax of the attribute type it's applied to. In that case, the ASN.1 type of the referenced component stands in for the attribute syntax when deciding what the assertion syntax should be. 🧠

A ComponentAssertion is Undefined if any of these happen:

a) the matching rule in the ComponentAssertion isn't known to the evaluating procedure,

b) the matching rule doesn't apply to the referenced component type, even after the additional type substitutions,

c) the value in the ComponentAssertion doesn't conform to the assertion syntax defined for the matching rule,

d) some part of the component reference identifies an open type in the tested value that can't be decoded, or

e) the implementation doesn't support that particular combo of component reference and matching rule. πŸ’€

If the ComponentAssertion isn't Undefined, then it evaluates to TRUE when there's at least one component value where the matching rule, applied to that value, returns TRUE. Otherwise it evaluates to FALSE, and that includes the case where there are no component values at all. πŸ‘‘

3.2.1. Applicability of Existing Matching Rules

3.2.1.1. String Matching

ASN.1 has a bunch of built-in restricted character string types, each with its own character set and/or encoding. πŸ“ Honestly, a directory user usually doesn't care which character set or encoding is used to store a string component value, and some directory server implementations don't even tell the string types apart internally. So instead of writing string matching rules for every single restricted character string type, the existing caseIgnoreMatch-family and caseExactMatch-family rules are extended to apply to component values of any restricted character string type and any ChoiceOfStrings type [9], on top of the DirectoryString type they already cover. This extension is only for the component matching this document describes. πŸ¦‹

The relevant string matching rules are: caseIgnoreMatch, caseIgnoreOrderingMatch, caseIgnoreSubstringsMatch, caseExactMatch, caseExactOrderingMatch and caseExactSubstringsMatch. The relevant restricted character string types are: NumericString, PrintableString, VisibleString, IA5String, UTF8String, BMPString, UniversalString, TeletexString, VideotexString, GraphicString and GeneralString. A ChoiceOfStrings type is a purely syntactic CHOICE of these ASN.1 string types. Also, GSER [9] declares every use of the DirectoryString{} parameterized type to be a ChoiceOfStrings type. 🌈

Even with all that, the assertion syntax for the string matching rules is still DirectoryString, no matter what string syntax the component being matched uses. So an implementation will end up comparing a DirectoryString value against a value of one of the restricted character string types, or against a ChoiceOfStrings type. Just like comparing two DirectoryStrings whose chosen alternatives are different string types, the comparison goes ahead as long as the matching characters can be represented in both character sets. Otherwise, the matching returns FALSE. πŸ’…

3.2.1.2. Telephone Number Matching

Early editions of X.520 [12] defined the telephoneNumber attribute syntax as a constrained PrintableString. The fourth edition of X.520 makes the ASN.1 type name TelephoneNumber equal to that constrained PrintableString, and uses TelephoneNumber as both the attribute and assertion syntax. πŸ“ž For component matching, telephoneNumberMatch and telephoneNumberSubstringsMatch are allowed to be applied to any PrintableString value, not just TelephoneNumber values. ✨

3.2.1.3. Distinguished Name Matching

The DistinguishedName type is defined by assignment to be the same as the RDNSequence type. But RDNSequence is sometimes used directly in other type definitions, so for component matching, distinguishedNameMatch is also allowed to be applied to RDNSequence values. 🌸

3.2.2. Additional Useful Matching Rules

This section defines some extra matching rules that could come in handy in ComponentAssertions. These rules can also be used in extensibleMatch search filters [3]. 🧚

3.2.2.1. The rdnMatch Matching Rule

The distinguishedNameMatch rule can match whole distinguished names, but sometimes you only want to match specific Relative Distinguished Names (RDNs) inside a Distinguished Name (DN) and ignore the other RDNs. That's where rdnMatch comes in: it lets component RDNs of a DN get tested. πŸ”₯

The LDAP-style definitions for rdnMatch and its assertion syntax are:

( 1.2.36.79672281.1.13.3 NAME 'rdnMatch'
    SYNTAX 1.2.36.79672281.1.5.0 )

( 1.2.36.79672281.1.5.0 DESC 'RDN' )

The LDAP-specific encoding for a value of the RDN syntax is given by the <RelativeDistinguishedNameValue> rule [9].

The X.500-style definition for rdnMatch is:

rdnMatch MATCHING-RULE ::= {
    SYNTAX  RelativeDistinguishedName
    ID      { 1 2 36 79672281 1 13 3 } }

The rdnMatch rule evaluates to true if the component value and the assertion value are the same RDN, using the same RDN comparison method as distinguishedNameMatch. πŸ¦‹

Now, when you use rdnMatch to match components of DNs, watch out for this: the LDAP-specific encoding of a DN [5] reverses the order of the RDNs. πŸ™ˆ So for the DN that LDAP represents as "cn=Steven Legg,o=Adacel,c=AU", the RDN "cn=Steven Legg" corresponds to the component reference "3", or alternatively, "-1". πŸ’–

3.2.2.2. The presentMatch Matching Rule

Sometimes you don't care whether a specific value of a particular component is present. You just want to know whether any value of that component is there at all. ✨ The presentMatch rule lets you test whether a particular component value is present.

The LDAP-style definitions for presentMatch and its assertion syntax are:

( 1.2.36.79672281.1.13.5 NAME 'presentMatch'
    SYNTAX 1.2.36.79672281.1.5.1 )

( 1.2.36.79672281.1.5.1 DESC 'NULL' )

The LDAP-specific encoding for a value of the NULL syntax is given by the <NullValue> rule [9].

The X.500-style definition for presentMatch is:

presentMatch MATCHING-RULE ::= {
    SYNTAX  NULL
    ID      { 1 2 36 79672281 1 13 5 } }

When presentMatch is used in an extensible match filter item, it behaves like the "present" case of a regular search filter. 🌈 In a ComponentAssertion, presentMatch evaluates to TRUE if and only if the component reference identifies one or more component values, no matter what those values actually contain. Heads up: if useDefaultValues is TRUE, the identified component values might be (part of) a DEFAULT value. πŸ™ˆ

The notional count referenced by the <count> form of ComponentId is considered present if the SET OF value is present, and absent otherwise. 🧠 In ASN.1 notation, an absent SET OF value is totally different from a SET OF value that is present but empty. Whether that distinction matters is up to the specification that uses the ASN.1 notation. Often, an empty SET OF component and an absent SET OF component get treated as semantically equivalent. πŸ’… But if a SET OF value is present and empty, a presentMatch on the SET OF component SHALL return TRUE, and the notional count SHALL be regarded as present and equal to zero. πŸ‘‘

3.2.3. Summary of Useful Matching Rules ✨

okay so here's the cheat sheet πŸ“‹ it's a non-exhaustive list (so there are more, bestie), and it covers which matching rules work on which ASN.1 types, taking into account everything from Section 3.2.1 plus the new ones from Section 3.2.2. the table is giving organized πŸ’…

+================================+==============================+
| Matching Rule                  | ASN.1 Type                   |
+================================+==============================+
| bitStringMatch                 | BIT STRING                   |
+--------------------------------+------------------------------+
| booleanMatch                   | BOOLEAN                      |
+--------------------------------+------------------------------+
| caseIgnoreMatch                | NumericString                |
| caseIgnoreOrderingMatch        | PrintableString              |
| caseIgnoreSubstringsMatch      | VisibleString (ISO646String) |
| caseExactMatch                 | IA5String                    |
| caseExactOrderingMatch         | UTF8String                   |
| caseExactSubstringsMatch       | BMPString (UCS-2, UNICODE)   |
|                                | UniversalString (UCS-4)      |
|                                | TeletexString (T61String)    |
|                                | VideotexString               |
|                                | GraphicString                |
|                                | GeneralString                |
|                                | any ChoiceOfStrings type     |
+--------------------------------+------------------------------+
| caseIgnoreIA5Match             | IA5String                    |
| caseExactIA5Match              |                              |
+--------------------------------+------------------------------+
| distinguishedNameMatch         | DistinguishedName            |
|                                | RDNSequence                  |
+--------------------------------+------------------------------+
| generalizedTimeMatch           | GeneralizedTime              |
| generalizedTimeOrderingMatch   |                              |
+--------------------------------+------------------------------+
| integerMatch                   | INTEGER                      |
| integerOrderingMatch           |                              |
+--------------------------------+------------------------------+
| numericStringMatch             | NumericString                |
| numericStringOrderingMatch     |                              |
| numericStringSubstringsMatch   |                              |
+--------------------------------+------------------------------+
| objectIdentifierMatch          | OBJECT IDENTIFIER            |
+--------------------------------+------------------------------+
| octetStringMatch               | OCTET STRING                 |
| octetStringOrderingMatch       |                              |
| octetStringSubstringsMatch     |                              |
+--------------------------------+------------------------------+
| presentMatch                   | any ASN.1 type               |
+--------------------------------+------------------------------+
| rdnMatch                       | RelativeDistinguishedName    |
+--------------------------------+------------------------------+
| telephoneNumberMatch           | PrintableString              |
| telephoneNumberSubstringsMatch | TelephoneNumber              |
+--------------------------------+------------------------------+
| uTCTimeMatch                   | UTCTime                      |
| uTCTimeOrderingMatch           |                              |
+--------------------------------+------------------------------+

ngl the big bonus here πŸŽ€ is that allComponentsMatch (from Section 6.2) can do equality matching on ENUMERATED, NULL, REAL and RELATIVE-OID values, among other things. so it's not just a one-trick pony πŸ¦‹


4. ComponentFilter πŸ’œ

okay the ComponentAssertion lets you match the value(s) of one component type inside a complex ASN.1 type. but bestie, we usually want to match more than one component at once πŸ‘€ that's where a ComponentFilter comes in! it's an assertion about whether multiple components are present, and what their values are 🫢

a ComponentFilter is an expression built out of ComponentAssertions, and for every ASN.1 value you test it, it comes back as TRUE, FALSE or Undefined ✨

here's the ASN.1 type (it assumes "EXPLICIT TAGS" is in force) 🧚

ComponentFilter ::= CHOICE {
    item  [0] ComponentAssertion,
    and   [1] SEQUENCE OF ComponentFilter,
    or    [2] SEQUENCE OF ComponentFilter,
    not   [3] ComponentFilter }

Note: even though "and" and "or" use SEQUENCE OF instead of SET OF, the order of the component filters doesn't matter πŸ™ˆ it's giving "order-free vibes" lol

so here's how it all evaluates, iykyk πŸ’…

  • a ComponentFilter that's just a ComponentAssertion is TRUE if the ComponentAssertion is TRUE, FALSE if it's FALSE, and Undefined otherwise.
  • the "and" of a sequence of component filters is TRUE if the sequence is empty or if every component filter is TRUE. it's FALSE if at least one component filter is FALSE, and Undefined otherwise.
  • the "or" of a sequence of component filters is FALSE if the sequence is empty or if every component filter is FALSE. it's TRUE if at least one component filter is TRUE, and Undefined otherwise.
  • the "not" of a component filter is TRUE if the component filter is FALSE, FALSE if the component filter is TRUE, and Undefined otherwise.

5. The componentFilterMatch Matching Rule πŸŽ€

the componentFilterMatch matching rule lets you apply a ComponentFilter to an attribute value. the result of the matching rule is just the result of that ComponentFilter evaluated on the attribute value. simple, clean, slay ✨

here are the LDAP-style definitions for componentFilterMatch and its assertion syntax 🌸

( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch'
    SYNTAX 1.2.36.79672281.1.5.2 )

( 1.2.36.79672281.1.5.2 DESC 'ComponentFilter' )

the LDAP-specific encoding for the ComponentFilter assertion syntax is specified by GSER [9].

for implementors (aka the people building this stuff, hi bestie 🫢), there's also an ABNF description of the GSER encoding for ComponentFilter. just know that if this ABNF ever disagrees with what GSER produces, GSER is the one to trust πŸ‘‘ here's the ABNF:

ComponentFilter = filter-item /
                  and-filter /
                  or-filter /
                  not-filter

filter-item     = item-chosen ComponentAssertion
and-filter      = and-chosen  SequenceOfComponentFilter
or-filter       = or-chosen   SequenceOfComponentFilter
not-filter      = not-chosen  ComponentFilter
item-chosen     = %x69.74.65.6D.3A  ; "item:"
and-chosen      = %x61.6E.64.3A     ; "and:"
or-chosen       = %x6F.72.3A        ; "or:"
not-chosen      = %x6E.6F.74.3A     ; "not:"

SequenceOfComponentFilter = "{" [ sp ComponentFilter
                                 *( "," sp ComponentFilter) ] sp "}"

ComponentAssertion = "{" [ sp component "," ]
                         [ sp useDefaultValues "," ]
                           sp rule ","
                           sp assertion-value sp "}"
component          = component-label msp StringValue
useDefaultValues   = use-defaults-label msp BooleanValue
rule               = rule-label msp ObjectIdentifierValue
assertion-value    = value-label msp Value

component-label    = %x63.6F.6D.70.6F.6E.65.6E.74  ; "component"
use-defaults-label = %x75.73.65.44.65.66.61.75.6C.74.56.61.6C.75
                     %x65.73                  ; "useDefaultValues"
rule-label         = %x72.75.6C.65            ; "rule"
value-label        = %x76.61.6C.75.65         ; "value"

sp                 =  *%x20  ; zero, one or more space characters
msp                = 1*%x20  ; one or more space characters

the ABNF for <Value>, <StringValue>, <ObjectIdentifierValue> and <BooleanValue> is defined by GSER [9] 🧁

real talk: the ABNF descriptions of LDAP-specific encodings for attribute syntaxes usually don't clearly or consistently spell out the component parts of an attribute value 😭 so we need a regular, uniform character string encoding for any component data type, to encode the assertion value in a ComponentAssertion. the <Value> rule from GSER gives you a human-readable text encoding for a component value of any arbitrary ASN.1 type. it's so good, bestie 🌈

and here's the X.500-style definition [10] for componentFilterMatch πŸ’…

componentFilterMatch MATCHING-RULE ::= {
    SYNTAX  ComponentFilter
    ID      { 1 2 36 79672281 1 13 2 } }

one more iconic thing: a ComponentAssertion can use any matching rule at all, componentFilterMatch included! so componentFilterMatch can be nested πŸͺ† when that happens, the component references inside a nested componentFilterMatch are relative to the component of the containing ComponentAssertion. Section 7 has an example search on the seeAlso attribute that shows this off πŸ¦‹


6. Equality Matching of Complex Components πŸ‘‘

okay so it's possible to check whether an attribute value of a complex ASN.1 syntax is the same as some assertion value, by writing a complicated ComponentFilter that checks if the corresponding components match. ✨ but honestly? that's a lot of work 😩 it would be way more convenient to hand the whole assertion value to a matching rule that compares it component-by-component against the attribute value, for any attribute syntax. and similarly, a straightforward equality check on a component whose own type is complex would also be super handy πŸ“

ngl though, it's really hard to make ONE matching rule that fits every idea of what equality should mean πŸ™ˆ like sometimes you want a case-sensitive comparison of string components, and sometimes you don't. so the basic equality matching rule, allComponentsMatch, is defined in Section 6.2, and Section 6.3 explains how to derive new matching rules from it with slightly different equality semantics 🌸

the directoryComponentsMatch defined in Section 6.4 is a derivation of allComponentsMatch that suits typical directory uses πŸ’– and other specifications are free to derive new rules from allComponentsMatch or directoryComponentsMatch to suit how they use the directory 🫢

last bit: the allComponentsMatch rule, the directoryComponentsMatch rule, and any matching rules derived from them are collectively called component equality matching rules. 🌈

6.1. The OpenAssertionType Syntax

okay so the component equality matching rules are kinda shapeshifters πŸ¦‹ their assertion syntax is variable, which in X.500 is shown by leaving out the optional SYNTAX field in the MATCHING-RULE information object. no SYNTAX field means the assertion syntax defaults to whatever the target attribute's syntax is in actual use, unless the matching rule's description says otherwise ✨ LDAP doesn't allow that gap, though. the SYNTAX field in the LDAP-specific encoding of a MatchingRuleDescription is mandatory πŸ™ˆ so the OpenAssertionType syntax was created to fill that exact spot. it's semantically the same as an omitted SYNTAX field in an X.500 MATCHING-RULE information object. ngl that's so clever πŸ’…

OpenAssertionType MUST NOT be used as the attribute syntax in an attribute type definition. 🚫 it's a matching-only vibe, not a real attribute type, bestie 🫢

Unless a particular matching rule's description explicitly changes things, here's how the LDAP encoding works 🌸 if an OpenAssertionType assertion value shows up inside a ComponentAssertion, its LDAP-specific encoding is the one described by the <Value> rule in GSER [9]. otherwise, its encoding is the one defined for the syntax of the attribute type that the matching rule (with the OpenAssertionType assertion syntax) is applied to.

The LDAP definition for the OpenAssertionType syntax is:

( 1.2.36.79672281.1.5.3 DESC 'OpenAssertionType' )

6.2. The allComponentsMatch Matching Rule

this is the main event, periodt πŸ‘‘ here's the LDAP-style definition for allComponentsMatch:

( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch'
    SYNTAX 1.2.36.79672281.1.5.3 )

and the X.500-style definition:

allComponentsMatch MATCHING-RULE ::= {
    ID      { 1 2 36 79672281 1 13 6 } }

when allComponentsMatch is used inside a ComponentAssertion, the assertion syntax is the same as the ASN.1 type of the identified component πŸŽ€ otherwise, its assertion syntax matches the attribute syntax of whatever attribute the matching rule is applied to.

the big picture is this: it evaluates to true if and only if the corresponding components of the assertion value and the attribute or component value are the same πŸ¦‹ the detailed version is the "recursive" checklist below, where each case gets applied again to any nested values, so pay attention, iykyk 🧚

a) Two values of a SET or SEQUENCE type are the same if and only if, for each component type, the corresponding component values are either:

  1. both absent,
  2. both present and the same, or
  3. absent or the same as the DEFAULT value for the component, if a DEFAULT value is defined.

Values of an EMBEDDED PDV, EXTERNAL, unrestricted CHARACTER STRING, or INSTANCE OF type are compared according to their respective associated SEQUENCE type (see Section 3.1.2).

b) Two values of a SEQUENCE OF type are the same if and only if, the values have the same number of (possibly duplicated) instances and corresponding instances are the same.

c) Two values of a SET OF type are the same if and only if, the values have the same number of instances and each distinct instance occurs in both values the same number of times, i.e., both values have the same instances, including duplicates, but in any order. πŸ“

d) Two values of a CHOICE type are the same if and only if, both values are of the same chosen alternative and the component values are the same.

e) Two BIT STRING values are the same if and only if the values have the same number of bits and corresponding bits are the same. If the BIT STRING type is defined with a named bit list then trailing zero bits in the values are treated as absent for the purposes of this comparison.

f) Two BOOLEAN values are the same if and only if both are TRUE or both are FALSE. ✨

g) Two values of a string type are the same if and only if the values have the same number of characters and corresponding characters are the same. Letter case is significant πŸ™ˆ for the purposes of allComponentsMatch, the string types are NumericString, PrintableString, TeletexString (T61String), VideotexString, IA5String, GraphicString, VisibleString (ISO646String), GeneralString, UniversalString, BMPString, UTF8String, GeneralizedTime, UTCTime and ObjectDescriptor.

h) Two INTEGER values are the same if and only if the integers are equal. πŸ”₯

i) Two ENUMERATED values are the same if and only if the enumeration item identifiers are the same (equivalently, if the integer values associated with the identifiers are equal). πŸ’œ

j) Two NULL values are always the same, unconditionally. πŸ’€

k) Two OBJECT IDENTIFIER values are the same if and only if the values have the same number of arcs and corresponding arcs are the same. 🌈

l) Two OCTET STRING values are the same if and only if the values have the same number of octets and corresponding octets are the same.

m) Two REAL values are the same if and only if they are both the same special value, or neither is a special value and they have the same base and represent the same real number. The special values for REAL are zero, PLUS-INFINITY and MINUS-INFINITY. ⭐

n) Two RELATIVE-OID values are the same if and only if the values have the same number of arcs and corresponding arcs are the same. The respective starting nodes for the RELATIVE-OID values are disregarded in the comparison, i.e., they are assumed to be the same. πŸ¦‹

o) Two values of an open type are the same if and only if both are of the same ASN.1 type and are the same according to that type. If the actual ASN.1 type of the values is unknown then the allComponentsMatch rule evaluates to Undefined. 😭

Tags and constraints, being part of the type definition and not part of the abstract values, are ignored for matching purposes. 🌸

The allComponentsMatch rule may be used as the defined equality matching rule for an attribute. πŸ‘‘

6.3. Deriving Component Equality Matching Rules

so you can make a brand new component equality matching rule with fancier matching semantics πŸ’… just derive it from allComponentsMatch, or from any other component equality matching rule, using the convention in this section. it's giving custom-made ✨

the derived rule's behaviour is set by nominating, for one or more identified components, a commutative equality matching rule to match values of that component. that overrides whatever matching the base rule would otherwise do for that component. these overrides are easiest to picture as rows in a table like this πŸŽ€

Component   |  Matching Rule
============+===============
            |
            |

usually, every component value of a particular ASN.1 type gets matched the same way 🧚 so an ASN.1 type reference (like DistinguishedName) or an ASN.1 built-in type name (like INTEGER) in the Component column means the nominated rule applies to all values of that type, no matter where they show up.

if you add a component reference after the type reference (separated by a "."), the nominated rule only applies to those identified components of values of that type. other component values that happen to be the same ASN.1 type are not selected, so they don't get swept up πŸ™ˆ

Additional type substitutions described in Section 3.2 are assumed to be performed to line up the component type with the matching rule assertion syntax. πŸ¦‹

conceptually, the rows in the base rule's table get added onto the rows in the derived rule's table when you decide how the derived rule matches things 🌈 notionally, allComponentsMatch has an empty table.

now the priority drama πŸ‘‘ a row for an outer containing type (like DistinguishedName) beats a row for an inner component type (like RelativeDistinguishedName), no matter what order they're listed in. adding a row for an inner type is only worth it if that type can also show up on its own, or as a component of a different outer type. for example, if there's a row for DistinguishedName, then a row for RelativeDistinguishedName can only ever apply to RelativeDistinguishedName component values that aren't part of a DistinguishedName. and a row for an outer type in the base rule's table beats a row for an inner type in the derived rule's table ✨

if more than one row applies to a particular component value, the earlier row wins over the later one. so rows in the derived rule's table always beat any rows for the same component in the base rule's table πŸ’–

6.4. The directoryComponentsMatch Matching Rule

okay bestie, directoryComponentsMatch is basically the glow-up of allComponentsMatch πŸ’…βœ¨ It's derived from it, so it's giving family resemblance.

The LDAP-style definition looks like this:

( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch'
    SYNTAX 1.2.36.79672281.1.5.3 )

And the X.500-style definition looks like this:

directoryComponentsMatch MATCHING-RULE ::= {
    ID      { 1 2 36 79672281 1 13 7 } }

The matching semantics are laid out in the table below, using the convention from Section 6.3. Yes, it's a big table, but it's a very organized big table πŸŽ€

ASN.1 Type                               | Matching Rule
=========================================+========================
RDNSequence                              | distinguishedNameMatch
RelativeDistinguishedName                | rdnMatch
TelephoneNumber                          | telephoneNumberMatch
FacsimileTelephoneNumber.telephoneNumber | telephoneNumberMatch
NumericString                            | numericStringMatch
GeneralizedTime                          | generalizedTimeMatch
UTCTime                                  | uTCTimeMatch
DirectoryString{}                        | caseIgnoreMatch
BMPString                                | caseIgnoreMatch
GeneralString                            | caseIgnoreMatch
GraphicString                            | caseIgnoreMatch
IA5String                                | caseIgnoreMatch
PrintableString                          | caseIgnoreMatch
TeletexString                            | caseIgnoreMatch
UniversalString                          | caseIgnoreMatch
UTF8String                               | caseIgnoreMatch
VideotexString                           | caseIgnoreMatch
VisibleString                            | caseIgnoreMatch

Some notes, because the details matter here πŸ™ˆ

  1. The DistinguishedName type is assigned to be the same as the RDNSequence type. Some types, like Name and LocalName, reference RDNSequence directly instead of DistinguishedName. Specifying RDNSequence captures all of these DN-like types, which is so clean 🧚
  2. A RelativeDistinguishedName value is only matched by rdnMatch if it is not part of an RDNSequence value. So context is everything, babe.
  3. The telephone number component of the FacsimileTelephoneNumber ASN.1 type [12] is defined as a constrained PrintableString. PrintableString component values that belong to a FacsimileTelephoneNumber value can be identified separately from other PrintableString components, using the specifier FacsimileTelephoneNumber.telephoneNumber, so that telephoneNumberMatch can be applied selectively. The fourth edition of X.520 defines the telephoneNumber component of FacsimileTelephoneNumber as the type TelephoneNumber, which makes the FacsimileTelephoneNumber.telephoneNumber row redundant. Iconic history moment πŸ“ 

The directoryComponentsMatch rule may be used as the defined equality matching rule for an attribute. Main character energy for sure πŸ‘‘


7. Component Matching Examples

Okay, this section is the fun part, where we actually write search filters using componentFilterMatch πŸ¦‹ The filters are written in the string representation of LDAP search filters [18]. Heads up: that representation needs asterisks escaped inside assertion values. In these examples the assertion values are all <ComponentAssertion> encodings, and the asterisks are NOT escaped here, purely so it's easier to read and so we don't mix up the protocol representation, where that escaping doesn't apply. Line breaks and indenting were added only to help you read it, so don't panic if your copy looks different πŸ’€

The example filters all use componentFilterMatch as a single extensible match filter item. There's no reason componentFilterMatch can't be used in more complicated filters though, so don't think it's limited πŸ’–

The first examples search the objectClasses schema operational attribute. Its attribute syntax is the ASN.1 type ObjectClassDescription [10], and it holds the definitions of every object class a directory server knows about. Here's ObjectClassDescription:

ObjectClassDescription ::= SEQUENCE {
    identifier       OBJECT-CLASS.&id,
    name             SET OF DirectoryString {ub-schema} OPTIONAL,
    description      DirectoryString {ub-schema} OPTIONAL,
    obsolete         BOOLEAN DEFAULT FALSE,
    information  [0] ObjectClassInformation }

ObjectClassInformation ::= SEQUENCE {
    subclassOf       SET OF OBJECT-CLASS.&id OPTIONAL,
    kind             ObjectClassKind DEFAULT structural,
    mandatories  [3] SET OF ATTRIBUTE.&id OPTIONAL,
    optionals    [4] SET OF ATTRIBUTE.&id OPTIONAL }

ObjectClassKind ::= ENUMERATED {
    abstract     (0),
    structural   (1),
    auxiliary    (2) }

OBJECT-CLASS.&id and ATTRIBUTE.&id are equivalent to the OBJECT IDENTIFIER ASN.1 type. A value of OBJECT-CLASS.&id is an OBJECT IDENTIFIER for an object class, and a value of ATTRIBUTE.&id is an OBJECT IDENTIFIER for an attribute type. Fundamentals, but they matter πŸŽ€

This filter finds the object class definition for the object class identified by the OBJECT IDENTIFIER 2.5.6.18:

(objectClasses:componentFilterMatch:=
     item:{ component "identifier",
            rule objectIdentifierMatch, value 2.5.6.18 })

A match on the "identifier" component of objectClasses values is equivalent to the objectIdentifierFirstComponentMatch matching rule applied to objectClasses attribute values. So componentFilterMatch subsumes the functionality of objectIdentifierFirstComponentMatch, integerFirstComponentMatch and directoryStringFirstComponentMatch. It's literally the one-rule-to-rule-them-all era πŸ‘‘

This filter finds the object class definition for the object class called foobar:

(objectClasses:componentFilterMatch:=
    item:{ component "name.*",
           rule caseIgnoreMatch, value "foobar" })

An object class definition can have multiple names, and the filter above matches an objectClasses value if any one of those names is "foobar". Multiple names, one match, very reasonable 🌸

The component reference "name.0" stands for the notional count of names in an object class definition. This filter finds object class definitions with exactly one name:

(objectClasses:componentFilterMatch:=
    item:{ component "name.0", rule integerMatch, value 1 })

The "description" component of an ObjectClassDescription is an OPTIONAL DirectoryString. This filter finds object class definitions that have descriptions, no matter what the description string says:

(objectClasses:componentFilterMatch:=
    item:{ component "description",
           rule presentMatch, value NULL })

presentMatch returns TRUE if the description component is present, and FALSE otherwise. Simple but powerful ✨

This filter finds object class definitions that don't have descriptions:

(objectClasses:componentFilterMatch:=
    not:item:{ component "description",
               rule presentMatch, value NULL })

This filter finds object class definitions with the word "bogus" in the description:

(objectClasses:componentFilterMatch:=
    item:{ component "description",
           rule caseIgnoreSubstringsMatch,
           value { any:"bogus" } })

The assertion value uses the SubstringAssertion syntax, which looks like this:

SubstringAssertion ::= SEQUENCE OF CHOICE {
    initial      [0] DirectoryString {ub-match},
    any          [1] DirectoryString {ub-match},
    final        [2] DirectoryString {ub-match} }

The "obsolete" component of an ObjectClassDescription is defined as DEFAULT FALSE. An object class is obsolete if the "obsolete" component is present and set to TRUE. This filter finds all obsolete object classes:

(objectClasses:componentFilterMatch:=
    item:{ component "obsolete", rule booleanMatch, value TRUE })

An object class is not obsolete if the "obsolete" component is absent, in which case it defaults to FALSE, or if it's present and explicitly set to FALSE. This filter finds all non-obsolete object classes:

(objectClasses:componentFilterMatch:=
    item:{ component "obsolete", rule booleanMatch, value FALSE })

The useDefaultValues flag in the ComponentAssertion defaults to TRUE, so componentFilterMatch treats an absent "obsolete" component as present and set to FALSE. This filter finds only object class definitions where "obsolete" has been explicitly set to FALSE, rather than quietly defaulting to FALSE:

(objectClasses:componentFilterMatch:=
    item:{ component "obsolete", useDefaultValues FALSE,
           rule booleanMatch, value FALSE })

With useDefaultValues set to FALSE, if the "obsolete" component is absent, the component reference identifies no component value and the matching rule returns FALSE. The matching rule can only return TRUE when the component is present and set to FALSE. Be precise, bestie, it matters πŸ™ˆ

The "information.kind" component of ObjectClassDescription is an ENUMERATED type. The allComponentsMatch matching rule can be used to match values of an ENUMERATED type. This filter finds object class definitions for auxiliary object classes:

(objectClasses:componentFilterMatch:=
    item:{ component "information.kind",
           rule allComponentsMatch, value auxiliary })

This filter finds auxiliary object classes that have commonName (cn or 2.5.4.3) as a mandatory attribute:

(objectClasses:componentFilterMatch:=and:{
    item:{ component "information.kind",
           rule allComponentsMatch, value auxiliary },
    item:{ component "information.mandatories.*",
           rule objectIdentifierMatch, value cn } })

This filter finds auxiliary object classes that have commonName as a mandatory or optional attribute:

(objectClasses:componentFilterMatch:=and:{
    item:{ component "information.kind",
           rule allComponentsMatch, value auxiliary },
    or:{
        item:{ component "information.mandatories.*",
               rule objectIdentifierMatch, value cn },
        item:{ component "information.optionals.*",
               rule objectIdentifierMatch, value cn } } })
Extra care is required when matching optional SEQUENCE OF or SET OF components, because there's a real difference between an absent list of instances and a present but empty list of instances. This filter finds object class definitions with fewer than three names, including ones with a present but empty list of names, but it does NOT find object class definitions with an absent list of names:
(objectClasses:componentFilterMatch:=
    item:{ component "name.0",
           rule integerOrderingMatch, value 3 })

If the "name" component is absent, the "name.0" component is also considered absent, so the ComponentAssertion evaluates to FALSE. If the "name" component is present but empty, the "name.0" component is present and equal to zero, so the ComponentAssertion evaluates to TRUE. To also find the object class definitions with an absent list of names, the following search filter would be used:

      (objectClasses:componentFilterMatch:=or:{
          not:item:{ component "name", rule presentMatch, value NULL },
          item:{ component "name.0",
                 rule integerOrderingMatch, value 3 } })

Okay bestie, DNs hiding inside other syntaxes? Still totally matchable with componentFilterMatch πŸ’…

uniqueMember has its own vibe πŸŽ€ Its syntax is the ASN.1 type NameAndOptionalUID:

      NameAndOptionalUID ::= SEQUENCE {
          dn        DistinguishedName,
          uid       UniqueIdentifier OPTIONAL }

Here's a search filter that finds uniqueMember values holding the author's DN πŸ¦‹

      (uniqueMember:componentFilterMatch:=
          item:{ component "dn",
                 rule distinguishedNameMatch,
                 value "cn=Steven Legg,o=Adacel,c=AU" })

The DistinguishedName and RelativeDistinguishedName types are complex too (ngl they're giving layers πŸ§…), so component matching rules can reach their inner components πŸ‘‘

      DistinguishedName   ::= RDNSequence

      RDNSequence ::= SEQUENCE OF RelativeDistinguishedName

      RelativeDistinguishedName ::= SET SIZE (1..MAX) OF
          AttributeTypeAndValue

      AttributeTypeAndValue ::= SEQUENCE {
          type        AttributeType ({SupportedAttributes}),
          value       AttributeValue ({SupportedAttributes}{@type}) }

      AttributeType ::= ATTRIBUTE.&id

      AttributeValue ::= ATTRIBUTE.&Type

ATTRIBUTE.&Type is an open type. A value of ATTRIBUTE.&Type is constrained by the type component of AttributeTypeAndValue to be of the attribute syntax of the nominated attribute type. Note: the fourth edition of X.500 extends and renames the AttributeTypeAndValue SEQUENCE type.

The seeAlso attribute has the DistinguishedName syntax, so it's giving DN energy 🌸 This filter finds seeAlso values containing the RDN, "o=Adacel", anywhere in the DN:

      (seeAlso:componentFilterMatch:=
          item:{ component "*", rule rdnMatch, value "o=Adacel" })

This one finds all seeAlso values with "cn=Steven Legg" as the RDN of the named entry (i.e., the "first" RDN in an LDAPDN or the "last" RDN in an X.500 DN) ✨

      (seeAlso:componentFilterMatch:=
          item:{ component "-1",
                 rule rdnMatch, value "cn=Steven Legg" })

Next up: all seeAlso values naming entries in the DIT subtree of "o=Adacel,c=AU" 🧚

      (seeAlso:componentFilterMatch:=and:{
          item:{ component "1", rule rdnMatch, value "c=AU" },
          item:{ component "2", rule rdnMatch, value "o=Adacel" } })

Now this one finds all seeAlso values containing the naming attribute types commonName (cn) and telephoneNumber in the same RDN πŸ’–

      (seeAlso:componentFilterMatch:=
          item:{ component "*", rule componentFilterMatch,
                 value and:{
                     item:{ component "*.type",
                            rule objectIdentifierMatch, value cn },
                     item:{ component "*.type",
                            rule objectIdentifierMatch,
                            value telephoneNumber } } })

And this one would find all seeAlso values containing the attribute types commonName and telephoneNumber, but not necessarily in the same RDN πŸ™ˆ

      (seeAlso:componentFilterMatch:=and:{
          item:{ component "*.*.type",
                 rule objectIdentifierMatch, value cn },
          item:{ component "*.*.type",
                 rule objectIdentifierMatch, value telephoneNumber } })

This filter finds all seeAlso values containing the word "Adacel" in any organizationalUnitName (ou) attribute value in any AttributeTypeAndValue of any RDN πŸ“

      (seeAlso:componentFilterMatch:=
          item:{ component "*.*.value.(2.5.4.11)",
                 rule caseIgnoreSubstringsMatch,
                 value { any:"Adacel" } })

The component reference "..value" identifies an open type, in this case an attribute value. In a particular AttributeTypeAndValue, if the attribute type is not organizationalUnitName then the ComponentAssertion evaluates to FALSE. Otherwise the substring assertion is evaluated against the attribute value.

Absent component references in ComponentAssertions can be exploited to avoid false positive matches on multi-valued attributes 🧠 For example, suppose there is a multi-valued attribute named productCodes, defined to have the Integer syntax (1.3.6.1.4.1.1466.115.121.1.27). Consider the following search filter:

      (&(!(productCodes:integerOrderingMatch:=3))
        (productCodes:integerOrderingMatch:=8))

An entry whose productCodes attribute contains only the values 1 and 10 will match the above filter 😭 The first subfilter is satisfied by the value 10 (10 is not less than 3), and the second subfilter is satisfied by the value 1 (1 is less than 8).

The following search filter can be used instead to only match entries that have a productCodes value in the range 3 to 7, because the ComponentFilter is evaluated against each productCodes value in isolation πŸ’…

      (productCodes:componentFilterMatch:= and:{
           not:item:{ rule integerOrderingMatch, value 3 },
          item:{ rule integerOrderingMatch, value 8 } })

An entry whose productCodes attribute contains only the values 1 and 10 will not match the above filter. ✨


8. Security Considerations πŸ”

Component matching rules let you write a compact spec for matching capabilities that would otherwise need a whole plethora of specific matching rules 🧚 They're super expressive and flexible, but they don't behave in a way that's uncharacteristic of other matching rules, so the security issues for component matching rules are no different than for any other matching rule.

BUT (and this is a big but 😭) because component matching rules are applicable to any attribute syntax, support for them in a directory server may allow searching of attributes that were previously unsearchable, simply because there wasn't a suitable matching rule. Such attribute types ought to be properly protected with appropriate access controls. A generic, interoperable access control mechanism has not yet been developed, though, so implementors should be aware of how that gap interacts with the increased risk of exposure described above. Be careful out there πŸ’€

9. Acknowledgements 🫢

The author would like to thank Tom Gindin for private email discussions that clarified and refined the ideas presented in this document. Major thank-you, Tom πŸ’œ

10. IANA Considerations πŸ“‹

The Internet Assigned Numbers Authority (IANA) has updated the LDAP descriptors registry [8] as indicated by the following templates:

      Subject: Request for LDAP Descriptor Registration
      Descriptor (short name): componentFilterMatch
      Object Identifier: 1.2.36.79672281.1.13.2
      Person & email address to contact for further information:
        Steven Legg <steven.legg@adacel.com.au>
      Usage: other (matching rule)
      Specification: RFC 3687
      Author/Change Controller: IESG
      Subject: Request for LDAP Descriptor Registration
      Descriptor (short name): rdnMatch
      Object Identifier: 1.2.36.79672281.1.13.3
      Person & email address to contact for further information:
        Steven Legg <steven.legg@adacel.com.au>
      Usage: other (matching rule)
      Specification: RFC 3687
      Author/Change Controller: IESG

      Subject: Request for LDAP Descriptor Registration
      Descriptor (short name): presentMatch
      Object Identifier: 1.2.36.79672281.1.13.5
      Person & email address to contact for further information:
        Steven Legg <steven.legg@adacel.com.au>
      Usage: other (matching rule)
      Specification: RFC 3687
      Author/Change Controller: IESG

      Subject: Request for LDAP Descriptor Registration
      Descriptor (short name): allComponentsMatch
      Object Identifier: 1.2.36.79672281.1.13.6
      Person & email address to contact for further information:
        Steven Legg <steven.legg@adacel.com.au>
      Usage: other (matching rule)
      Specification: RFC 3687
      Author/Change Controller: IESG

      Subject: Request for LDAP Descriptor Registration
      Descriptor (short name): directoryComponentsMatch
      Object Identifier: 1.2.36.79672281.1.13.7
      Person & email address to contact for further information:
        Steven Legg <steven.legg@adacel.com.au>
      Usage: other (matching rule)
      Specification: RFC 3687
      Author/Change Controller: IESG

The object identifiers have been assigned for use in this specification by Adacel Technologies, under an arc assigned to Adacel by Standards Australia. 🌈

11. References πŸ“š

11.1. Normative References

[1] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[2] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", RFC 2234, November 1997.

[3] Wahl, M., Howes, T. and S. Kille, "Lightweight Directory Access Protocol (v3)", RFC 2251, December 1997.

[4] Wahl, M., Coulbeck, A., Howes, T. and S. Kille, "Lightweight Directory Access Protocol (v3): Attribute Syntax Definitions", RFC 2252, December 1997.

[5] Wahl, M., Kille S. and T. Howes. "Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names", RFC 2253, December 1997.

[6] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003.

[7] Hodges, J. and R. Morgan, "Lightweight Directory Access Protocol (v3): Technical Specification", RFC 3377, September 2002.

[8] Zeilenga, K., "Internet Assigned Numbers Authority (IANA) Considerations for the Lightweight Directory Access Protocol (LDAP)", BCP 64, RFC 3383, September 2002.

[9] Legg, S., "Generic String Encoding Rules (GSER) for ASN.1 Types", RFC 3641, October 2003.

[10] ITU-T Recommendation X.501 (1993) | ISO/IEC 9594-2:1994, Information Technology - Open Systems Interconnection - The Directory: Models

[11] ITU-T Recommendation X.509 (1997) | ISO/IEC 9594-8:1998, Information Technology - Open Systems Interconnection - The Directory: Authentication Framework

[12] ITU-T Recommendation X.520 (1993) | ISO/IEC 9594-6:1994, Information technology - Open Systems Interconnection - The Directory: Selected attribute types

[13] ITU-T Recommendation X.680 (07/02) | ISO/IEC 8824-1:2002, Information technology - Abstract Syntax Notation One (ASN.1): Specification of basic notation

[14] ITU-T Recommendation X.681 (07/02) | ISO/IEC 8824-2:2002, Information technology - Abstract Syntax Notation One (ASN.1): Information object specification

[15] ITU-T Recommendation X.682 (07/02) | ISO/IEC 8824-3:2002, Information technology - Abstract Syntax Notation One (ASN.1): Constraint specification

[16] ITU-T Recommendation X.683 (07/02) | ISO/IEC 8824-4:2002, Information technology - Abstract Syntax Notation One (ASN.1): Parameterization of ASN.1 specifications

[17] ITU-T Recommendation X.690 (07/02) | ISO/IEC 8825-1, Information technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)

12.2. Informative References πŸ“š

[18] Howes, T., "The String Representation of LDAP Search Filters", RFC 2254, December 1997. πŸ’Œ

[19] ITU-T Recommendation X.500 (1993) | ISO/IEC 9594-1:1994, Information Technology - Open Systems Interconnection - The Directory: Overview of concepts, models and services πŸ—‚οΈ

12. Intellectual Property Statement πŸŽ€

Okay so, the IETF doesn't take a position on whether any intellectual property or other rights that someone might claim over the technology in this document are valid or cover what they say they cover. It also doesn't say whether any license under those rights is available, or whether anyone even tried to find those rights in the first place 🫣. If you want the details on how the IETF handles rights for standards-track and standards-related documents, check BCP-11 πŸ“–. Copies of claims of rights that were filed for publication, plus any assurances that licenses will be available, or the results of an attempt to get a general license or permission to use those proprietary rights by implementers or users of this specification, can be obtained from the IETF Secretariat πŸ’Ό.

The IETF also invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that might cover technology that may be required to practice this standard. Send that info to the IETF Executive Director πŸ“¬.

13. Author's Address πŸ’Œ

The author is Steven Legg, with Adacel Technologies Ltd. 🌸

Steven Legg
Adacel Technologies Ltd.
250 Bay Street
Brighton, Victoria 3186
AUSTRALIA

Phone: +61 3 8530 7710
Fax:   +61 3 8530 7888
EMail: steven.legg@adacel.com.au

Copyright (C) The Internet Society (2004). All Rights Reserved. 🌈

This document and translations of it may be copied and furnished to others πŸ¦‹. Derivative works that comment on or otherwise explain it, or assist in its implementation, may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind πŸ’…. The one catch is that the above copyright notice and this paragraph have to be included on all such copies and derivative works 🫢. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations πŸ”’. The exceptions are when it's needed for developing Internet standards, in which case the procedures for copyrights defined in the Internet Standards process must be followed, or when it's required to translate it into languages other than English 🌍.

The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assignees ✨.

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. πŸ™ˆ

Acknowledgement 🫢

Funding for the RFC Editor function is currently provided by the Internet Society πŸ’–.

πŸ“¦ where this page comes from