(1) Given a location, e.g. "pub.gale@ofb.net", construct the list of candidate key names for this location: pub.gale@ofb.net pub.*@ofb.net *@ofb.net There will always be N+1 candidate key names for a location with N local parts (e.g., "pub" and "gale"). (2) In parallel, look up each of these candidate key names. Each candidate key will either return a positive reply, a negative reply, or no reply (timeout, treated equivalently to negative reply). If no positive replies are received, the location does not exist and an error should be reported. If more than one positive reply is received, one may be picked at random (alternatively an error may be reported). (3) In this positive key reply, look for the key.redirect field. If it exists, its value must be merged with the key name and the original location to produce a new location. First, count the number of components in the local part of the key name, excluding any trailing '*'. Remove that many components from the beginning of the local part of the original location. Also remove the domain from the original location. You will be left with zero or more components from the end of the local part of the original location. Append this string to the local part of the location stored in the key.redirect fragment. The location thus produced is the merged location. Go to step (1) and repeat the process with the merged location. Use the merged location rather than the original location for all further purposes when dealing with this message (such as category generation, UI presentation, etc). (4) If there is no key.redirect, then the key MUST contain either one or more key.member fields; or the three fields rsa.modulus, rsa.bits, and rsa.exponent; or both key.member and the rsa fields. If there is no key.member field present, then encrypt the puff using this public key. If both types of fields are present, the puff must be encrypted to this public key as well as the keys specified in the key.member field(s). If key.member is present but not the rsa fields, then encrypt only to the keys listed in the key.member fragments. (5) Each key.member field has a value whose contents are another key name, or the empty string. If the empty string is found, this means that the puff should not be encrypted; processing may terminate here. Otherwise, construct the set of keys named in the key.member fields. Look up each of these keys in parallel, starting from step (2). Each request must terminate with either a valid public key, or the empty string. When the transitive closure is complete, the result will be either the empty string or a set of public keys. If it is the empty string, do not encrypt the puff; otherwise, encrypt the puff to this list of keys. This is a graph traversal; there may be cycles and shared structures. If a key contains a key.redirect fragment, the fragment is to be ignored during this graph traversal. Key fragments: message/recipient - deprecated; used to set a friendly name for the recipient of the message question.receipt - supercedes question/receipt; must be set to the location to which receipts should be sent answer.receipt - supercedes answer/receipt; set to Gale ID of user sending receipt; used by clients to determine whether a puff is a return receipt or not question.key - used in an AKD request; supercedes question/key; the local part of the key name must not be reversed question/key - used in an AKD request; obsolete; the local part of the key name must be reversed answer/key - response to old-style or new-style AKD request answer.key - some key responses use this instead of answer/key answer/key/error - error result for AKD request (old or new) Special locations: _gale.key.keyname - the location on which to answer AKD requests _gale.query.keyname - the location in which to make AKD requests _gale.notice.keyname - the location for presence notices for this user Look up a public key named top.sub.bar@dom in the disk cache in this order: ~/auth/trusted/top.sub.bar@dom.gpub ~/auth/local/top.sub.bar@dom.gpub ~/auth/cache/top.sub.bar@dom.gpub GALE_SYS_DIR/auth/trusted/top.sub.bar@dom.gpub GALE_SYS_DIR/auth/local/top.sub.bar@dom.gpub GALE_SYS_DIR/auth/cache/top.sub.bar@dom.gpub ~/auth/trusted/bar.sub.top@dom ~/auth/local/bar.sub.top@dom ~/auth/cache/bar.sub.top@dom GALE_SYS_DIR/auth/trusted/bar.sub.top@dom GALE_SYS_DIR/auth/local/bar.sub.top@dom GALE_SYS_DIR/auth/cache/bar.sub.top@dom Look up a private key top.sub@dom in this order: ~/auth/private/top.sub.bar@dom.gpri GALE_SYS_DIR/auth/private/top.sub.bar@dom.gpri ~/auth/private/bar.sub.top@dom GALE_SYS_DIR/auth/private/bar.sub.top@dom RLE encoding: RLE data is a sequence of "chunks". Each chunk is a single "control byte" followed by one or more data bytes. If the control byte has the high bit set (0x80), then it is followed by N+1 data bytes, where N is the value of the control byte with the high bit stripped. Those N+1 data bytes should be copied literally. If the control byte does not have the high bit set, then it is followed by a single data byte. That data byte should be repeated N+1 times in the output, where N is the value of the control byte. UNAUTHORIZED LOCATIONS: Resolve a location to a set of keys in the standard way. If the set is empty, then the location in INVALID. If the set contains the empty string (i.e. at least one key.member is "") or the user has access to at least one of the private keys present in the set, then the location is VALID. Otherwise, the user does not have one of the private keys listed in the set, and the location is UNAUTHORIZED.