ARPA2 Common Libraries  2.3.1
Modules | Data Structures | Macros | Typedefs | Enumerations | Functions
ARPA2 Identity and Selector functions
Collaboration diagram for ARPA2 Identity and Selector functions:

Modules

 Iteration of ARPA2 Selectors
 
 Signatures on ARPA2 Identities
 

Data Structures

struct  a2sel_t
 ARPA2 Selector. More...
 

Macros

#define A2ID_MAXLEN   ( 64 + 1 + 4*255 )
 
#define A2ID_BUFSIZE   ( 64 + 1 + 4*255 + 1 )
 
#define A2SEL_MAXLEN   ( 64 + 1 + 4*255 )
 
#define A2SEL_BUFSIZE   ( 64 + 1 + 4*255 + 1 )
 
#define a2id_equal(a, b)   (0 == strcmp ((a)->txt, (b)->txt))
 Test if two ARPA2 Identity values are the same. More...
 
#define a2sel_equal(a, b)   (0 == strcmp ((a)->txt, (b)->txt))
 Test if two ARPA2 Selector values are the same. More...
 
#define a2id_match(id, sel)   (a2sel_subseteq ((id),(sel)))
 Test if an ARPA2 Identity matches an ARPA2 Selector. More...
 
#define a2id_member(id, sel)   (a2id_match ((id),(sel)))
 
#define a2sel_subseteq(s, g)   a2sel_special ((s), (g))
 

Typedefs

typedef a2sel_t a2id_t
 ARPA2 Identity. More...
 
typedef a2id_t a2act_t
 ARPA2 Actor Identity. More...
 

Enumerations

enum  a2id_offset_t {
  A2ID_OFS_PLUS_SERVICE , A2ID_OFS_SERVICE , A2ID_OFS_PLUS_SVCARGS , A2ID_OFS_SVCARGS ,
  A2ID_OFS_USERID , A2ID_OFS_PLUS_ALIASES , A2ID_OFS_ALIASES , A2ID_OFS_OPEN_END ,
  A2ID_OFS_PLUS_SIG , A2ID_OFS_SIGFLAGS , A2ID_OFS_SIGEXPIRE , A2ID_OFS_SIGVALUE ,
  A2ID_OFS_SIGPLUS , A2ID_OFS_AT_DOMAIN , A2ID_OFS_DOT_DOMAIN , A2ID_OFS_DOMAIN ,
  A2ID_OFS_END , A2ID_OFS_BUFSIZE , A2ID_OFS_COUNT
}
 Offsets into parsed identity strings. More...
 

Functions

void a2id_init (void)
 Initialise the ARPA2 Identity system. More...
 
void a2id_fini (void)
 Finalise the ARPA2 Identity system. More...
 
bool a2id_parse (a2id_t *out, const char *in, unsigned inlen)
 Parse and normalise a string into an ARPA2 Identity. More...
 
bool a2id_parse_remote (a2id_t *out, const char *in, unsigned inlen)
 Parse and normalise a string into a remote ARPA2 Identity. More...
 
bool a2sel_parse (a2sel_t *out, const char *in, unsigned inlen)
 Parse and normalise a string into an ARPA2 Selector. More...
 
bool a2act_parse (a2act_t *out, const char *in, unsigned inlen, unsigned nact)
 Parse and normalise a string into an ARPA2 Actor Identity. More...
 
static bool a2act_isempty (const a2act_t *tested)
 Test if an a2act_t is an empty Actor Identity. More...
 
static bool a2act_isstatic (const a2act_t *tested)
 Test if an a2act_t is a Static Actor Identity. More...
 
static bool a2act_isdynamic (const a2act_t *tested)
 Test if an a2act_t is a Dynamic Actor Identity. More...
 
static bool a2act_isdynamicrecipe (const a2act_t *tested)
 Test if an a2act_t is a recipe for a Dynamic Actor Identity. More...
 
bool a2sel_special (const a2sel_t *special, const a2sel_t *general)
 Test if the left ARPA2 Selector is a specialised form of the right ARPA2 Selector. More...
 
bool a2sel_textshift (a2id_t *shifty, a2id_offset_t ofsidx, uint16_t newofs, bool before)
 Shift text in an ARPA2 Selector. More...
 
static void a2sel_detail (char *descr, const a2sel_t *sel)
 Print a parsed ARPA2 Identity structure on the detail debugging output. More...
 

Detailed Description

Our blog discusses much background in the Identity series.

These functions provide precodition tests for the format of ARPA2 Identity and ARPA2 Selector strings. Comparisons may additionally be applied, for instance to check rights in comparison to application-specific access control configurations.

The design of this API is light-weight, raher than an opaque structure with function calls to operate on it. You are expected to unerstand the data structures, but are saved from having to understand a pile of functions. Some functions are general in nature, but have a simpler counterparts defined as macro or inline function.

There is good support for stack allocation to reduce risk of mistakes. Also, functions return a boolean when they succeed, to enable a programming style like

 bool ok = true;
 ok = ok && a2id_something (...);
 bool got_resource = ok;
 ok = ok && a2id_someother (...);
 if (got_resource) {
    a2id_cleanup (...);
 }
 return ok;

SPDX-FileCopyrightText: Copyright 2020 Rick van Rein rick@.nosp@m.open.nosp@m.fortr.nosp@m.ess..nosp@m.nl SPDX-License-Identifier: BSD-2-Clause

Macro Definition Documentation

◆ a2id_equal

#define a2id_equal (   a,
 
)    (0 == strcmp ((a)->txt, (b)->txt))

Test if two ARPA2 Identity values are the same.

Return true on yes, false on no.

This quick test on equality can help to weed out special cases that might arise during use of the more advanced comparisons.

◆ a2id_match

#define a2id_match (   id,
  sel 
)    (a2sel_subseteq ((id),(sel)))

Test if an ARPA2 Identity matches an ARPA2 Selector.

One might think of an ARPA2 Identity as being a member of the set permissible under an ARPA2 Selector. In this line of thought, this function is a "member-of" test.

Note that every ARPA2 Identity is also a valid ARPA2 Selector, and so the function is a special use of a subset-or-equals test between Selectors.

◆ a2sel_equal

#define a2sel_equal (   a,
 
)    (0 == strcmp ((a)->txt, (b)->txt))

Test if two ARPA2 Selector values are the same.

Return true on yes, false on no.

This quick test on equality can help to weed out special cases that might arise during use of the more advanced comparisons.

Typedef Documentation

◆ a2act_t

typedef a2id_t a2act_t

ARPA2 Actor Identity.

This type is like a normal identity, except that knowledge about the cut-off between group name and members is incorporated into the separation between the userid and aliases. See the =g<actor>+<group> attribute in Communication Access for the normal way in which this separation is learnt.

This means that the userid field can hold '+' symbols when the group does – though that would be highly confusing.

The form may mention no aliases to address the entire group, one alias for an individual member, or a subset of members as aliases separated by the customary '+' symbols, and even the '+-+' separator may occur in this form to remove members who would otherwise be addressed by default.

In most common use cases, this falls together with a single userid with one optional alias, but in support of the possible variations it is helpful to clearly set aside a separate type and process it accordingly.

◆ a2id_t

typedef a2sel_t a2id_t

ARPA2 Identity.

This type represents a parsed and normalised ARPA2 Identity. Note that this is a special/constrained form of ARPA2 Selector. It has been validated and normalised whie parsing the structure into offsets. It is stored with a NUL terminator, so the txt field can actually be printed as a normal C-string, also after editing operations are performed on it.

Enumeration Type Documentation

◆ a2id_offset_t

Offsets into parsed identity strings.

After parsing, a number of offsets into the string buffer are derived. They are stored in an array, in their order of appearance. The first is 0 and the last is the string length.

Identity manipulation involves looking at ranges between offsets, be they subsequent or not, but at least used in order of appearance. Absense of a part of the ARPA2 Identity or ARPA2 Selector is marked as an empty string between two subsequent offsets, rathen than as a NULL pointer, so a range spans 0 characters if its field is absent.

Several of the markers are included, such as _PLUS_SIG before _SIG. When these successive values are equal, then there would not be a separator and, as a result, no signature. In this particular case, the additional use of _SIGPLUS is the plus sign after the _SIG, the actual marker being parsed to detect a signature, but it is also shown by marking the preceding _PLUS_SIG as such. Note that the value doubles as the end marker for aliases, or of the userid.

Values such as _AT_DOMAIN include the marker to enable comparison of a full domain name, rather then comparison of a trailing bit, such as for a Selector .domain which would use _DOMAIN instead.

There are two variants for the local-part. An initial plus is for service names, and the "aliases" are really parameters. There can be a signature on those addresses too, signified by a trailing plus in the local-part. The two sequences parse into different offsets; A2ID_USERID, _PLUS_ALIASES and _ALIASES all point at the optional signature and @domain for services. For services, the A2ID_OFS_PLUS_SERVICE, _SERVICE, _PLUS_SVCARGS, _SVCARGS all point at the begining, so at 0, as does A2ID_OFS_USERID in that case.

The FLGEXPSIG follows a grammar of its own. Encoded in BASE32, the FLG and EXP parts signal continuation in their highest bits, so they are self-terminating. The SIG part than runs to consume the remaining characters at 5 bits each.

All this is mostly of interest for domains that are known to adhere to the ARPA2 Identity forms. Others fit in the framework with the local part, possibly not even cast to lowercase, entirely between the A2ID_OFS_USERID and A2ID_OFS_PLUS_ALIASES. Anything before it equalys A2ID_OFS_PLUS_SERVICE (normally 0) and anthing after it, up to and including the A2ID_OFS_AT_DOMAIN, are equal and point to the '@' separator. It may be a matter of taste whether to err on the unsafe side, in the interest of consistency and usability and with only mild impact on the real-life security level.

Function Documentation

◆ a2act_isdynamic()

static bool a2act_isdynamic ( const a2act_t tested)
inlinestatic

Test if an a2act_t is a Dynamic Actor Identity.

This returns true when the txt field is not an empty string, when the alias is empty and the signature is not empty.

◆ a2act_isdynamicrecipe()

static bool a2act_isdynamicrecipe ( const a2act_t tested)
inlinestatic

Test if an a2act_t is a recipe for a Dynamic Actor Identity.

This returns true when the txt field is not an empty string, when the alias is empty and the signature flags and/or expiry are not empty while the the actual signature hash part is empty.

◆ a2act_isempty()

static bool a2act_isempty ( const a2act_t tested)
inlinestatic

Test if an a2act_t is an empty Actor Identity.

The txt field may hold an empty string if no Actor Identity was found, and this test will return true for that.

◆ a2act_isstatic()

static bool a2act_isstatic ( const a2act_t tested)
inlinestatic

Test if an a2act_t is a Static Actor Identity.

This returns true when the txt field is not an empty string, when the alias is not empty and the signature is empty.

◆ a2act_parse()

bool a2act_parse ( a2act_t out,
const char *  in,
unsigned  inlen,
unsigned  nact 
)

Parse and normalise a string into an ARPA2 Actor Identity.

Return true if the input follows the correct grammar. After this, offsets are setup in the .ofs field and the normalised string, including NUL terminator, is written into the .txt field. The Actor Identity is similar to a plain Identity, but the separation between userid and alias is made differently, based on context that knows about the group name being addressed. The userid of an Actor may contain '+' symbols, although that is not an advised practice. There are no service names for this form. The alias part indicates what members to iterate, if not a single one is mentioned, to find a list of member delivery addresses.

The value in nact indicates the number of <actor> levels separated by '+' symbols. Set to 0 to parse a <scene> Identity, to 1 to parse a <scene>+<actor> or any other value for parsing a set of <actor> aliases. The aliases will contain this number of '+' separated words after this call succeeds.

Normalised identities are in lowercase, except for an optional +SIG+ signature part that will be in upper case. When we normalise such things beyond ASCII, this is where c16n mappings should go.

The labels in domain names may contain UTF-8 characters but not Punycode. As such, the domain name cannot have labels that start with "xn--",

When inlen is set to 0, its value will be computed with strlen() – if it is higher than 0 then the in string does not need NUL-termination and may be part of a larger context. The selector is copied into the out value.

◆ a2id_fini()

void a2id_fini ( void  )

Finalise the ARPA2 Identity system.

This may do cleanup operations.

◆ a2id_init()

void a2id_init ( void  )

Initialise the ARPA2 Identity system.

This has a mirror image for cleaup in a2id_fini().

◆ a2id_parse()

bool a2id_parse ( a2id_t out,
const char *  in,
unsigned  inlen 
)

Parse and normalise a string into an ARPA2 Identity.

Return true if the input follows the correct grammar. After this, offsets are setup in the .ofs field and the normalised string, including NUL terminator, is written into the .txt field.

Normalised identities are in lowercase, except for an optional +SIG+ signature part that will be in upper case. When we normalise such things beyond ASCII, this is where c16n mappings should go.

The labels in domain names may contain UTF-8 characters but not Punycode. As such, the domain name cannot have labels that start with "xn--",

When inlen is set to 0, its value will be computed with strlen() – if it is higher than 0 then the in string does not need NUL-termination and may be part of a larger context. The selector is copied into the out value.

◆ a2id_parse_remote()

bool a2id_parse_remote ( a2id_t out,
const char *  in,
unsigned  inlen 
)

Parse and normalise a string into a remote ARPA2 Identity.

Return true if the input follows the correct grammar, where the userid is not interpreted as for a local ARPA2 Identity. The whole part before '@' ends up in the userid string. The corresponding offsets are setup in the .ofs field and the normalised string, including NUL terminator, is written into the .txt field.

Local parts are not case-normalised and no signatures can be assumed to be part of the name. The only parts that are handled cleverly are the domain names.

The labels in domain names may contain UTF-8 characters but not Punycode. As such, the domain name cannot have labels that start with "xn--",

When inlen is set to 0, its value will be computed with strlen() – if it is higher than 0 then the in string does not need NUL-termination and may be part of a larger context. The selector is copied into the out value.

◆ a2sel_detail()

static void a2sel_detail ( char *  descr,
const a2sel_t sel 
)
inlinestatic

Print a parsed ARPA2 Identity structure on the detail debugging output.

This is normally maps to doing nothing, but it can help with deep-down-debugging when DEBUG_DETAIL is set.

The output shows the buffered identity with '|' bars to signify the offsets. Since these are monotonically rising, counting them suffices to understand how the information is split up. This is what parsing does; it defines what range of characters fit into certain forms of meaning.

◆ a2sel_parse()

bool a2sel_parse ( a2sel_t out,
const char *  in,
unsigned  inlen 
)

Parse and normalise a string into an ARPA2 Selector.

Return true if the input follows the correct grammar. After this, offsets are setup in the .ofs field and the normalised string, including NUL terminator, is written into the .txt field.

Normalised identities are in lowercase, except for an optional +SIG+ signature flags that will be in upper case. When we normalise such things beyond ASCII, this is where c16n mappings should go. As part of signature validation, the normalisation trims off anything beyond the signature flags by looking at its self-termination marker. This can be used to compare against ARPA2 Identity with a signature, to select the signature on strength.

The labels in domain names may contain UTF-8 characters but not Punycode. As such, the domain name cannot have labels that start with "xn--",

When inlen is set to 0, its value will be computed with strlen() – if it is higher than 0 then the in string does not need NUL-termination and may be part of a larger context. The selector is copied into the out value.

◆ a2sel_special()

bool a2sel_special ( const a2sel_t special,
const a2sel_t general 
)

Test if the left ARPA2 Selector is a specialised form of the right ARPA2 Selector.

One might read this as a subseteq operation placed as a testing condition between the (sets of identities that would match) the ARPA2 Selectors.

Note that ARPA2 Selectors form partially ordered subsetting relations; if the left is not more specific than the right than it is not automatically true that the right is more specific than the left. They might simply have incomplete overlap in both tests.

Also note that the overlap can be perfect in both tests when the two ARPA2 Selectors are identical.

Return true when the first is more specific than the second. Return false if this is not the case.

◆ a2sel_textshift()

bool a2sel_textshift ( a2id_t shifty,
a2id_offset_t  ofsidx,
uint16_t  newofs,
bool  before 
)

Shift text in an ARPA2 Selector.

The A2ID_OFS_xxx index into the internal offset array is given, along with the value to write to that offset. The text will be shifted forward or backward as required, so text is removed and/or a known size is available to write from an earlier starting pointing. Offsets are updated; later ones receive an offset and when earlier ones are overwritten they will be squeezed down. Use the before flag to indicate that the work should be considered to be done before the indexed offset.