RFC 2704

The KeyNote Trust-Management System Version 2

πŸ“… published September 1999, from ietf

RFC 2704: The KeyNote Trust-Management System Version 2 πŸŽ€

Okay bestie, this one is from the Network Working Group, published as RFC 2704 in September 1999 πŸ“…. The authors are M. Blaze, J. Feigenbaum, J. Ioannidis (AT&T Labs - Research), and A. Keromytis (U. of Pennsylvania). It's Informational πŸ¦‹, so it's giving "here's useful info," not "you must do this." The title block doesn't list anything it obsoletes or updates, so it's standing on its own. πŸ’…

Status of This Memo πŸ“

This memo provides information for the Internet community. It does not specify an Internet standard of any kind. Distribution of this memo is unlimited. 🌸

Copyright (C) The Internet Society (1999). All Rights Reserved. πŸ’œ

Abstract ✨

This memo describes version 2 of the KeyNote trust-management system. It lays out the syntax and semantics of KeyNote "assertions", explains "action attribute" processing, and sketches the application architecture where a KeyNote implementation can fit. Honestly the KeyNote architecture and language are iconic building blocks for the trust management side of a bunch of Internet protocols and services. πŸ”₯

1. Introduction πŸ¦‹

Okay so trust management was introduced in the PolicyMaker system [BFL96]. It's a unified way to specify and interpret security policies, credentials, and relationships, and it lets you directly authorize security-critical actions. A trust-management system gives you standard, general-purpose mechanisms for writing application security policies and credentials. Trust-management credentials describe a specific delegation of trust and basically take over the job of public key certificates. The difference is that regular certificates bind keys to names, while credentials can bind keys directly to permission to do specific tasks. Main character energy πŸ‘‘

A trust-management system has five basic components:

  • A language for describing "actions", which are operations with security consequences that the system is supposed to control.
  • A mechanism for identifying "principals", which are entities that can be authorized to perform actions.
  • A language for specifying application "policies", which govern the actions principals are authorized to perform.
  • A language for specifying "credentials", which let principals delegate authorization to other principals.
  • A "compliance checker", which gives applications a service for figuring out how an action requested by principals should be handled, given a policy and a set of credentials.

The trust-management approach has a bunch of advantages over other ways of specifying and controlling authorization, especially when security policy is distributed over a network or is otherwise decentralized. 🌈

Trust management unifies the ideas of security policy, credentials, access control, and authorization. An application using a trust-management system can simply ask the compliance checker whether a requested action should be allowed. Plus, policies and credentials are written in standard languages shared by all trust-managed applications, so the security configuration mechanism for one application has exactly the same syntactic and semantic structure as another's, even when the applications themselves work totally differently. Iconic consistency 🫢

Trust-management policies are easy to distribute across networks. That helps you avoid needing application-specific distributed policy configuration mechanisms, access control lists, and certificate parsers and interpreters. Less mess, more slay ✨

For a general discussion of trust management in distributed system security, see [Bla99]. πŸ“š

KeyNote is a simple and flexible trust-management system designed to work well for a variety of large- and small-scale Internet-based applications. It gives you a single, unified language for both local policies and credentials. KeyNote policies and credentials, called "assertions", contain predicates that describe the trusted actions permitted by the holders of specific public keys. KeyNote assertions are basically small, highly-structured programs πŸ’– A signed assertion, which can be sent over an untrusted network, is also called a "credential assertion". Credential assertions also serve as certificates, and they have the same syntax as policy assertions but are also signed by the principal delegating the trust.

In KeyNote:

  • Actions are specified as a collection of name-value pairs.
  • Principal names can be any convenient string and can directly represent cryptographic public keys.
  • The same language is used for both policies and credentials.
  • The policy and credential language is concise, highly expressive, human readable and writable, and compatible with a variety of storage and transmission media, including electronic mail.
  • The compliance checker returns an application-configured "policy compliance value" that describes how a request should be handled by the application. Policy compliance values are always positively derived from policy and credentials, which makes KeyNote-based systems easier to analyze. 🧚
  • Compliance checking is efficient enough for high-performance and real-time applications. πŸ”₯

This document describes the KeyNote policy and credential assertion language, the structure of KeyNote action descriptions, and the KeyNote model of computation. πŸ“

We assume that applications talk to a locally trusted KeyNote compliance checker through a "function call" style interface. The application sends a collection of KeyNote policy and credential assertions plus an action description as input, and gets the resulting policy compliance value back as output. However, the requirements of different applications, hosts, and environments may lead to a variety of different interfaces to KeyNote compliance checkers. This document doesn't aim to specify a complete compliance checker API, so no spoilers there πŸ™ˆ

2. KeyNote Concepts 🌸

In KeyNote, the authority to perform trusted actions is tied to one or more "principals". A principal can be a physical entity, a process in an operating system, a public key, or any other convenient abstraction. KeyNote principals are identified by a string called a "Principal Identifier". Sometimes a Principal Identifier contains a cryptographic key that KeyNote interprets (for example, for credential signature verification). Other times, Principal Identifiers have a structure that is opaque to KeyNote. πŸ’œ

Principals do two things KeyNote cares about: they request "actions" and they issue "assertions". Actions are any trusted operations that an application puts under KeyNote control. Assertions delegate the authorization to perform actions to other principals. 🫢

Actions are described to the KeyNote compliance checker as a collection of name-value pairs called an "action attribute set". The invoking application creates the action attribute set, and its structure and format are described in detail in Section 3 of this document. πŸ“¦

KeyNote gives applications advice about how to interpret policy for specific requested actions. Applications invoke the KeyNote compliance checker by issuing a "query" that contains a proposed action attribute set and identifies the principal(s) requesting it. The KeyNote system figures out and returns an appropriate "policy compliance value" from an ordered set of possible responses. πŸ”₯

The policy compliance value returned from a KeyNote query tells the application how to process the requested action. In the simplest case the compliance value is Boolean (for example, "reject" or "approve"). Assertions can also be written to pick from a range of possible compliance values when that makes sense for the application (for example, "no access", "restricted access", "full access"). Applications can configure the relative ordering of compliance values, from "weakest" to "strongest", at query time. πŸ‘‘

Assertions are the basic programming unit for specifying policy and delegating authority. They describe the conditions under which a principal authorizes actions requested by other principals. An assertion identifies the principal that made it, which other principals are being authorized, and the conditions under which the authorization applies. The syntax of assertions is given in Section 4. πŸ“

A special principal, whose identifier is "POLICY", provides the root of trust in KeyNote. So "POLICY" is considered to be authorized to perform any action. πŸ‘‘

Assertions issued by the "POLICY" principal are called "policy assertions" and are used to delegate authority to otherwise untrusted principals. The KeyNote security policy of an application is made up of a collection of policy assertions. 🌈

When a principal is identified by a public key, it can digitally sign assertions and distribute them over untrusted networks for use by other KeyNote compliance checkers. These signed assertions are also called "credentials", and they serve a role similar to traditional public key certificates. Policies and credentials share the same syntax and are evaluated under the same semantics, so a principal can turn its policy assertions into credentials just by digitally signing them. ✨

KeyNote is designed to encourage human-readable policies and credentials that work for transmission and storage over a variety of media. Its assertion syntax is inspired by the format of RFC822-style message headers [Cro82]. A KeyNote assertion contains a sequence of sections, called "fields", and each one specifies one aspect of the assertion's semantics. Fields start with an identifier at the beginning of a line and keep going until the next field shows up. Here's an example:

KeyNote-Version: 2
Comment: A simple, if contrived, email certificate for user mab
Local-Constants:  ATT_CA_key = "RSA:acdfa1df1011bbac"
                  mab_key = "DSA:deadbeefcafe001a"
Authorizer: ATT_CA_key
Licensees: mab_key
Conditions: ((app_domain == "email")  # valid for email only
          && (address == "mab@research.att.com"));
Signature: "RSA-SHA1:f00f2244"

The meanings of the various sections are described in Sections 4 and 5 of this document. πŸŽ€

KeyNote semantics sort out the relationship between an application's policy and actions requested by other principals, as backed up by credentials. The KeyNote compliance checker processes the assertions against the action attribute set to figure out the policy compliance value of a requested action. Those semantics are defined in Section 5. πŸ’–

An important principle in KeyNote's design is "assertion monotonicity". The policy compliance value of an action is always positively derived from assertions made by trusted principals. Removing an assertion never makes the compliance value returned by KeyNote for a given query go up. This property can simplify the design and analysis of complex network-based security protocols, since network failures that stop credentials from being transmitted can never lead to spurious authorization of dangerous actions. A detailed discussion of monotonicity and safety in trust management can be found in [BFL96] and [BFS98]. πŸ”₯😭

3. Action Attributes ✨

okay so, trusted actions that KeyNote evaluates are described by a bundle of name-value pairs called the action attribute set. πŸŽ€ Action attributes are how applications send their requests to KeyNote, and they're the main things KeyNote assertions work on. bestie, the whole attribute set gets handed to the KeyNote compliance checker with every query πŸ’…

each action attribute is a name plus a value. KeyNote doesn't interpret what the names and values mean πŸ™ˆ that's up to each application. they change from app to app, and the people writing the applications have to agree on them with the people writing the policies and credentials that the apps will use. no cap, that agreement is the whole game.

action attribute names and values are arbitrary-length strings. KeyNote guarantees support for names and values up to 2048 characters long πŸ’– longer ones aren't specified and depend on the KeyNote implementation, so applications and assertions should avoid relying on attributes with names or values longer than 2048 characters. the length of a value is tracked by whatever mechanism the implementation uses (for example NUL-terminated strings, or an explicit length field).

attribute values are untyped and are character strings by default πŸ¦‹ they can contain any non-NUL ASCII character. numbers get turned into ASCII text by the calling application first, so for example the value 1234.5 would be the string "1234.5". ✨

attribute names look like this:

<AttributeID>:: {Any string starting with a-z, A-Z, or the
                 underscore character, followed by any number of
                 a-z, A-Z, 0-9, or underscore characters} ;

so an <AttributeID> starts with a letter or an underscore and can be followed by any number of letters, digits and underscores. attribute names are case-sensitive, so capitalization matters πŸ‘‘

the exact way the action attribute set gets passed to the compliance checker is up to the KeyNote implementation. an implementation might let you pass the whole attribute set in as an explicit parameter of the query, or it might use a callback that fires each time an attribute gets dereferenced (like for looking up kernel variables, for example) 🌈

if an action attribute isn't defined, its value is treated as the empty string 🧚

attribute names that begin with "_" are reserved for the KeyNote runtime environment, and applications aren't allowed to pass them in as part of a query 🚫 these are the special names:

    Name                        Purpose
    ------------------------    ------------------------------------
    _MIN_TRUST                  Lowest-order (minimum) compliance
                                value in query; see Section 5.1.

    _MAX_TRUST                  Highest-order (maximum) compliance
                                value in query; see Section 5.1.

    _VALUES                     Linearly ordered set of compliance
                                values in query; see Section 5.1.
                                Comma separated.

    _ACTION_AUTHORIZERS         Names of principals directly
                                authorizing action in query.
                                Comma separated.

also, attributes named like "_<N>", where <N> is an ASCII-encoded integer, are used by the regular expression matching mechanism in Section 5 πŸ“ and the meaning of any other "_"-prefixed attribute names is unspecified and implementation-dependent πŸ€·β€β™€οΈ

everything else in the attribute set isn't specified by KeyNote. those names just have to be agreed on by the people writing any policies and credentials that are supposed to work together in one specific query evaluation 🫢

by convention, the application domain that the attributes should be read under goes in an attribute called app_domain. the IANA (or some other suitable authority) will keep a registry of reserved app_domain names, and that registry will list each application's attributes along with what they mean πŸ“š

the app_domain thing is what keeps credentials being read the way they were meant to be. the same attribute name can show up in lots of app domains and mean something totally different in each one 😭 a global registry isn't always necessary for small, closed applications though. the only real requirement is that the policies and credentials given to the KeyNote compliance checker read attributes using the same meanings the creating application assumed. ✨

for example, an email app might reserve the app_domain "RFC822-EMAIL" and use the attributes "address" (the sender's email address), "name" (the sender's human name), and "organization" for any organization headers that are there (the organization name). those values would come from the email headers in the obvious way πŸ’Œ and the public key of the message's signer would go in the "_ACTION_AUTHORIZERS" attribute. πŸ”‘

and yes, "RFC822-EMAIL" is just a hypothetical example, so it might or might not be in the real registry with these attributes (or different ones). also, we totally recognize that email security is way messier than this example makes it sound πŸ™ˆπŸ’€


4. KeyNote Assertion Syntax πŸŽ€

in the next bits, the notation [X]* means zero or more repetitions of the character string X. [X]+ means one or more repetitions of X. <X>* means zero or more repetitions of the non-terminal <X>. <X>+ means one or more repetitions of X, and <X>? means zero or one repetitions of X. non-terminal grammar symbols go in angle brackets, and quoted strings in the grammar productions are terminals πŸ’œ

4.1. Basic Structure

here's the overall shape of an assertion:

<Assertion>:: <VersionField>? <AuthField> <LicenseesField>?
              <LocalConstantsField>? <ConditionsField>?
              <CommentField>? <SignatureField>? ;

all KeyNote assertions are encoded in ASCII πŸ”€

assertions get split up into sections called fields, and each one does a different semantic job. every field starts with an identifying label at the beginning of a line, then a ":" and the field's contents. there can be at most one field per line πŸ“

a field can keep going across more than one line if you indent the following lines with at least one ASCII SPACE or TAB character 🌸 whitespace (SPACE, TAB, or NEWLINE) separates tokens, but otherwise it's ignored outside of quoted strings. comments that start with an octothorp (see Section 4.2) can begin in any column ✨

one field is mandatory in every assertion, and that's:

  • Authorizer

six optional fields can also show up:

  • Comment
  • Conditions
  • KeyNote-Version
  • Licensees
  • Local-Constants
  • Signature

field names are case-insensitive πŸ™Œ if KeyNote-Version is there, it comes first, and if Signature is there, it comes last. otherwise fields can go in any order. each field can show up at most once in any assertion πŸš«πŸ”

blank lines aren't allowed inside assertions πŸ™…β€β™€οΈ but that's actually useful, because if you store several assertions in one file (like in application policy configurations), you can split them apart cleanly just by putting a blank line between each one 🧁

4.2. Comments

<Comment>:: "#" {ASCII characters} ;

the octothorp character ("#", ASCII 35 decimal) can start a comment πŸ’¬ outside of quoted strings (see Section 4.3), everything from the "#" through the end of that line gets ignored. BUT, the commented text still gets included when the assertion signatures are computed (see Section 4.6.7), so don't think you can sneak stuff past the signature πŸ‘€

4.3. Strings

a string is a lexical object made of a sequence of characters 🧡 strings can contain any non-NUL characters, including newlines and nonprintable characters. they can be written as literals, computed from complex expressions, or dereferenced from attribute names πŸ’…

4.3.1. String Literals

<StringLiteral>:: "\"" {see description below} "\"" ;

a string literal directly gives you the value of a string 🍭 string literals have to be wrapped in double quotes (ASCII 34 decimal) at both ends.

a printable character can be escaped inside a quoted string literal by putting a backslash (ASCII 92 decimal) in front of it, like "like \"this\".". that's how you get double quotes and backslashes inside a string literal πŸ™Œ

there's a similar escape thing for nonprintable characters too. "\n" is newline (ASCII 10 decimal), "\r" is carriage return (ASCII 13 decimal), "\t" is tab (ASCII 9 decimal), and "\f" is form-feed (ASCII 12 decimal). if a backslash comes right before a newline, it swallows all the whitespace after it (including that newline) up to the next non-whitespace character, which is how you can continue a long string across lines 🧢 and un-escaped newline and return characters are illegal inside string literals ❌

the constructs "\0o", "\0oo", and "\ooo" (where o is any octal digit) can stand in for any non-NUL ASCII character using its octal value. so "\012" is the same as "\n", "\101" is "A", and "\377" is ASCII 255 decimal πŸ”’ the NUL character can't be written this way though: "\0", "\00", and "\000" turn into the strings "0", "00", and "000" respectively 😭 and all the other escaped characters just lose their leading backslash (so "\a" becomes "a", and "\\" becomes "\"). these four strings are all equivalent:

"this string contains a newline\n followed by one space."
"this string contains a newline\n \
followed by one space."
"this str\
   ing contains a \
     newline\n followed by one space."

"this string contains a newline\012\040followed by one space."

4.3.2. String Expressions

anywhere a quoted string literal is allowed, you can use a string expression instead 🧩 a string expression builds a string out of string constants, dereferenced attributes (described in Section 4.4), and a string concatenation operator. string expressions can be wrapped in parentheses too πŸŽ€

<StrEx>:: <StrEx> "." <StrEx>    /* String concatenation */
        | <StringLiteral>        /* Quoted string */
        | "(" <StrEx> ")"
        | <DerefAttribute>       /* See Section 4.4 */
        | "$" <StrEx> ;          /* See Section 4.4 */

the "$" operator has higher precedence than the "." operator ⭐

4.4. Dereferenced Attributes ✨

Okay so action attributes are the main way apps slide info into assertions, bestie πŸ’– Attribute names are strings from a limited character set (<AttributeID>, from Section 3), and their values are stored internally as strings. To dereference one, you just use its name. Generally, KeyNote lets you use an attribute anywhere a string literal is allowed πŸŽ€

Attributes are dereferenced as strings by default πŸ¦‹ When you need a number, you can convert with the type conversion operators "@" and "&". So an attribute named "foo" with the value "1.2" can be read as the string "1.2" (foo), the integer 1 (@foo), or the float 1.2 (&foo). Iconic πŸ‘‘

Converted integers and floats follow the ANSI C long and float types. Integers go from -2147483648 to 2147483647, and floats go from 1.17549435E-38F to 3.40282347E+38F πŸ”₯

An uninitialized attribute is the empty string when dereferenced as a string, and zero when dereferenced as an integer or float πŸ’…

Attribute names can be literal, or calculated from string expressions, and they can be dereferenced recursively. The simplest version is just using the name outside of quotes, so the string value of "foo" is foo (outside of quotes). The "$<StrEx>" construct dereferences whatever attribute name the string expression <StrEx> gives you. Say the attribute "foo" holds "bar", the attribute "bar" holds "xyz", and the attribute "xyz" holds "qua". Then all of these string comparisons are true 🧚

foo == "bar"
$("foo") == "bar"
$foo == "xyz"
$(foo) == "xyz"
$$foo == "qua"

If <StrEx> ends up as an invalid or uninitialized attribute name, its value is the empty string (or zero if it's used as a number) πŸ’€

The <DerefAttribute> token is defined as:

<DerefAttribute>:: <AttributeID> ;

4.5. Principal Identifiers 🫢

Principals are written as ASCII strings called Principal Identifiers 🌸 They can be arbitrary labels that KeyNote doesn't try to interpret, or they can encode cryptographic keys that KeyNote uses to verify credential signatures. Both kinds are covered by this grammar:

<PrincipalIdentifier>:: <OpaqueID>
                      | <KeyID> ;

4.5.1. Opaque Principal Identifiers 🧸

Principal Identifiers that KeyNote only uses as labels are called opaque. They're encoded in assertions as strings (see Section 4.3):

<OpaqueID>:: <StrEx> ;

Opaque identifier strings should not contain the ":" character.

4.5.2. Cryptographic Principal Identifiers πŸ”

Principal Identifiers that KeyNote uses as keys, for example to verify credential signatures, are called cryptographic. They're also encoded lexically as strings:

<KeyID>:: <StrEx> ;

Cryptographic identifier strings are a bit more special, though πŸ¦‹ For KeyNote to interpret one (for signature verification), the identifier string should look like this:

<IDString>:: <ALGORITHM>":"<ENCODEDBITS> ;

"ALGORITHM" is an ASCII substring that says which algorithms to use for reading the key's bits. It names the major cryptographic algorithm (for example RSA [RSA78], DSA [DSA94], and so on), the structured format (for example PKCS1 [PKCS1]), and the key bit encoding (for example HEX or BASE64). By convention, the ALGORITHM substring starts with a letter and can contain letters, digits, underscores, or dashes. In other words, it should match the regular expression "[a-zA-Z][a-zA-Z0-9_-]*". The IANA (or some other appropriate authority) will provide a registry of reserved algorithm identifiers πŸ“š

"ENCODEDBITS" is a substring of characters that represents the key's bits. Its encoding and format depend on the ALGORITHM 🌈 By convention, hex-encoded keys use lower-case ASCII characters.

Cryptographic Principal Identifiers get converted to a normalized canonical form for any internal comparisons between them. See Section 5.2 for that πŸ’œ

Note that the keys used in examples throughout this document are fictitious, and they're generally much shorter than what you'd need for security in practice πŸ™ˆ


4.6. KeyNote Fields πŸ“

4.6.1. The KeyNote-Version Field ✨

The KeyNote-Version field tells you which version of the KeyNote assertion language the assertion was written in. It looks like this:

<VersionField>:: "KeyNote-Version:" <VersionString> ;
<VersionString>:: <StringLiteral>
                | <IntegerLiteral> ;

<VersionString> is an ASCII-encoded string. Assertions in production versions of KeyNote use decimal digits in the version to represent the version number of the KeyNote language they should be read under. Assertions written to follow this document should be identified with the version string "2" (or the integer 2). If it's included, the KeyNote-Version field should appear first πŸŽ€

4.6.2. The Local-Constants Field 🌸

This field adds or overrides action attributes, but only in the current assertion. It lets you use short names for (usually very long) cryptographic principal identifiers, which makes the Licensees field way easier to read 🫢 The Local-Constants field looks like this:

<LocalConstantsField>:: "Local-Constants:" <Assignments> ;
<Assignments>:: /* can be empty */
              | <AttributeID> "=" <StringLiteral> <Assignments> ;

<AttributeID> is an attribute name from the action attribute namespace, as defined in Section 3. The name can be used as an attribute in any later field. If the Local-Constants field defines more than one identifier, it can take up more than one line and be indented. <StringLiteral> is a string literal as described in Section 4.3. Attributes defined in the Local-Constants field override any attributes with the same name that were passed in with the action attribute set πŸ”₯

An attribute may be initialized at most once in the Local-Constants field. If an attribute is initialized more than once in an assertion, the entire assertion is considered invalid, and the KeyNote compliance checker does not consider it when evaluating queries. πŸ™…β€β™€οΈ

4.6.3. The Authorizer Field πŸ‘‘

The Authorizer is the Principal who issues the assertion. This field looks like this:

<AuthField>:: "Authorizer:" <AuthID> ;
<AuthID>:: <PrincipalIdentifier>
         | <DerefAttribute> ;

The Principal Identifier can be given directly, or by reference to the attribute namespace (as defined in Section 4.4) 🧚

4.6.4. The Licensees Field πŸ’–

The Licensees field says which principals the assertion authorizes. More than one principal can be authorized, and authorization can be spread across several principals using and and threshold constructs. The field looks like this:

<LicenseesField>:: "Licensees:" <LicenseesExpr> ;

<LicenseesExpr>::      /* can be empty */
                  | <PrincExpr> ;

<PrincExpr>:: "(" <PrincExpr> ")"
              | <PrincExpr> "&&" <PrincExpr>
              | <PrincExpr> "||" <PrincExpr>
              | <K>"-of(" <PrincList> ")"        /* Threshold */
              | <PrincipalIdentifier>
              | <DerefAttribute> ;

<PrincList>:: <PrincipalIdentifier>
            | <DerefAttribute>
            | <PrincList> "," <PrincList> ;

<K>:: {Decimal number starting with a digit from 1 to 9} ;

The "&&" operator has higher precedence than the "||" operator πŸ’… <K> is an ASCII-encoded positive decimal integer. If a <PrincList> contains fewer than <K> principals, the entire assertion is left out of processing πŸ’€

4.6.5. The Conditions Field πŸ’…

okay bestie, this field is where the Authorizer spells out the conditions under which the Licensees are allowed to do something 🫢 Conditions are predicates (basically yes-or-no checks) that run on the action attribute set. The Conditions field looks like this:

<ConditionsField>:: "Conditions:" <ConditionsProgram> ;

<ConditionsProgram>:: /* Can be empty */
                      | <Clause> ";" <ConditionsProgram> ;

<Clause>:: <Test> "->" "{" <ConditionsProgram> "}"
         | <Test> "->" <Value>
         | <Test> ;

<Value>:: <StrEx> ;
<Test>:: <RelExpr> ;

<RelExpr>:: "(" <RelExpr> ")"        /* Parentheses */
          | <RelExpr> "&&" <RelExpr> /* Logical AND */
          | <RelExpr> "||" <RelExpr> /* Logical OR */
          | "!" <RelExpr>         /* Logical NOT */
          | <IntRelExpr>
          | <FloatRelExpr>
          | <StringRelExpr>
          | "true"        /* case insensitive */
          | "false" ;     /* case insensitive */

<IntRelExpr>:: <IntEx> "==" <IntEx>
             | <IntEx> "!=" <IntEx>
             | <IntEx> "<" <IntEx>
             | <IntEx> ">" <IntEx>
             | <IntEx> "<=" <IntEx>
             | <IntEx> ">=" <IntEx> ;

<FloatRelExpr>:: <FloatEx> "<" <FloatEx>
               | <FloatEx> ">" <FloatEx>
               | <FloatEx> "<=" <FloatEx>
               | <FloatEx> ">=" <FloatEx> ;

<StringRelExpr>:: <StrEx> "==" <StrEx>  /* String equality */
                | <StrEx> "!=" <StrEx>  /* String inequality */
                | <StrEx> "<" <StrEx>   /* Alphanum. comparisons */
                | <StrEx> ">" <StrEx>
                | <StrEx> "<=" <StrEx>
                | <StrEx> ">=" <StrEx>
                | <StrEx> "~=" <RegExpr> ; /* Reg. expr. matching */

<IntEx>:: <IntEx> "+" <IntEx>        /* Integer */
        | <IntEx> "-" <IntEx>
        | <IntEx> "*" <IntEx>
        | <IntEx> "/" <IntEx>
        | <IntEx> "%" <IntEx>
        | <IntEx> "^" <IntEx>        /* Exponentiation */
        | "-" <IntEx>
        | "(" <IntEx> ")"
        | <IntegerLiteral>
        | "@" <StrEx> ;

<FloatEx>:: <FloatEx> "+" <FloatEx>  /* Floating point */
          | <FloatEx> "-" <FloatEx>
          | <FloatEx> "*" <FloatEx>
          | <FloatEx> "/" <FloatEx>
          | <FloatEx> "^" <FloatEx> /* Exponentiation */
          | "-" <FloatEx>
          | "(" <FloatEx> ")"
          | <FloatLiteral>
          | "&" <StrEx> ;

<IntegerLiteral>:: {Decimal number of at least one digit} ;
<FloatLiteral>:: <IntegerLiteral>"."<IntegerLiteral> ;

<StringLiteral> is a quoted string as defined in Section 4.3
<AttributeID> is defined in Section 3.

The operation precedence classes go from highest to lowest like this:

{ (, ) }
{unary -, @, &, $}
{^}
{*, /, %}
{+, -, .}

Operators in the same precedence class get evaluated left-to-right ➑️

Heads up πŸ™ˆ you can't test floating point values for equality. Most floating point implementations (hardware or otherwise) don't guarantee accurate equality testing, so don't even try it, it's giving unreliable πŸ’€

Also, integer and floating point expressions can only be used within clauses of condition fields. They are not allowed in any other KeyNote field 🌸

The keywords "true" and "false" are not reserved, so they can be used as attribute or principal identifier names. Tbh though, this practice makes assertions really hard to understand, so it's discouraged πŸ™ƒ

<RegExpr> is a standard regular expression, following the POSIX 1003.2 regular expression syntax and semantics πŸ”₯

Any string expression (or attribute) that contains the ASCII representation of a numeric value can be converted to an integer or float using the "@" and "&" operators, respectively. If an attribute is dereferenced as an integer, any fractional component gets rounded down ✨ And if an attribute dereferenced as a number can't be properly converted (say it has invalid characters or is empty), its value is treated as zero. No cap, that's a whole mood 🧚


4.6.6. The Comment Field πŸŽ€

The Comment field lets assertions be annotated with info about their purpose. It looks like this:

<CommentField>:: "Comment:" <text> ;

KeyNote doesn't interpret the contents of this field at all 🫢 Also, this is only one of two ways to add comments to KeyNote assertions. You can also drop comments anywhere in an assertion's body by starting them with the "#" character, as long as you're not inside a string literal πŸ’–

4.6.7. The Signature Field πŸ”

The Signature field identifies a signed assertion and holds the encoded digital signature of the principal named in the Authorizer field. It's formatted like this:

<SignatureField>:: "Signature:" <Signature> ;

<Signature>:: <StrEx> ;

The <Signature> string should be shaped like this:

<IDString>:: <ALGORITHM>":"<ENCODEDBITS> ;

The formats of the "ALGORITHM" and "ENCODEDBITS" substrings are the same as what's described for Cryptographic Principal Identifiers in Section 4.4.2. The algorithm name should match the one used by the principal in the Authorizer field. The IANA (or some other suitable authority) will keep a registry of reserved names πŸ“š And the encodings of the signature and the authorizer key don't have to match, so no stress about that πŸ¦‹

If the Signature field is included, then the principal named in the Authorizer field must be a Cryptographic Principal Identifier, the algorithm must be known to the KeyNote implementation, and the signature must be correct for the assertion body and authorizer key. Lots of rules, but they're all important πŸ‘‘

The signature is computed over the assertion text, starting from the first field (field identifier string included) and going up to, but not including, the Signature field identifier. The newline right before the signature field identifier is the last character included in the signature calculation πŸ“ The signature is always the last field in a KeyNote assertion, and any text after it is not considered part of the assertion πŸ›‘

The algorithms for computing and verifying signatures must be configured into each KeyNote implementation. They're defined and documented separately, so that's a different tea β˜•

Note that every signature in this document's examples is fictitious, and they're generally way shorter than what you'd need for real security in practice. Don't copy them and call it a day 😭

5. Query Evaluation Semantics ✨

The KeyNote compliance checker finds and returns the Policy Compliance Value of queries, as defined in Section 5.3 below. This is the main event, bestie πŸ‘‘

5.1. Query Parameters 🧚

A KeyNote query has four parameters:

  • The identifier of the principal(s) requesting the action.
  • The action attribute set describing the action.
  • The set of compliance values of interest to the application, ordered from _MIN_TRUST to _MAX_TRUST.
  • The policy and credential assertions that should be included in the evaluation.

How these parameters get passed to the KeyNote evaluator depends on the application 🌈 An evaluator might take some parameters explicitly, look up others externally (like credentials pulled from a network-based distribution system), or ask the application for others as needed through a callback mechanism. That last one is handy for attribute values that come from a really huge namespace πŸ’…

5.1.1. Action Requester πŸ™‹β€β™€οΈ

At least one Principal must be identified in each query as the requester of the action. Actions can be requested by several principals, and each one is considered to have individually requested it. This lets policies require multiple authorizations, like two person control πŸ” The set of authorizing principals is made available in the special attribute "_ACTION_AUTHORIZERS", and if several principals are authorizers, their identifiers are separated with commas.

5.1.2. Ordered Compliance Value Set πŸ“Š

The invoking application decides which compliance values it cares about, plus how they rank against each other, and then passes that set to the KeyNote evaluator as a parameter of the query ✨ A lot of apps will be Boolean, like the ordered sets {FALSE, TRUE} or {REJECT, APPROVE}. Others need a range of values, like {No_Access, Limited_Access, Full_Access}. Just make sure the set only includes compliance value names that the assertions actually return 🫢

The lowest-order and highest-order compliance value strings given in the query are available in the special attributes "_MIN_TRUST" and "_MAX_TRUST", respectively. The full set of query compliance values is available in ascending order (from _MIN_TRUST to _MAX_TRUST) in the special attribute "_VALUES". The values are separated with commas, so if your assertions use the _VALUES attribute, avoid compliance value strings that contain commas themselves. That's a real trap, so watch out πŸ™ˆ

5.2. Principal Identifier Normalization 🧼

When you compare Cryptographic Principal Identifiers (the ones that represent keys) in the Authorizer and Licensees fields, or in an action's direct authorizers, you normalize them first by converting them to a canonical form ✨

Every cryptographic algorithm used in KeyNote defines how to convert keys into their canonical form, and it also says how to check two keys for equality. If KeyNote doesn't recognize the algorithm named in the identifier, the identifier gets treated as opaque πŸ¦‹

Opaque identifiers are compared as case-sensitive strings, so Abc and abc are not the same person, bestie πŸ’€

Also, using opaque identifiers in the Authorizer field means the assertion's integrity has to be locally trusted. The compliance checker can't verify it cryptographically, so you're kind of relying on trust instead of math here πŸ”₯

5.3. Policy Compliance Value Calculation πŸ‘‘

The Policy Compliance Value of a query is the Principal Compliance Value of the principal named "POLICY". Here's how that's worked out πŸ’–

5.3.1. Principal Compliance Value πŸ’Ž

The Compliance Value of a principal <X> is the highest order (maximum) of:

  • the Direct Authorization Value of principal <X>; and
  • the Assertion Compliance Values of all assertions identifying <X> in the Authorizer field.

5.3.2. Direct Authorization Value 🌸

The Direct Authorization Value of a principal <X> is _MAX_TRUST if <X> is listed in the query as an authorizer of the action. Otherwise, it's _MIN_TRUST πŸ™Œ

5.3.3. Assertion Compliance Value πŸŽ€

The Assertion Compliance Value of an assertion is the lowest order (minimum) of the assertion's Conditions Compliance Value and its Licensee Compliance Value. So the weakest link decides, periodt πŸ’…

5.3.4 Conditions Compliance Value ✨

okay so the Conditions Compliance Value of an assertion is the highest-order (maximum) value among all the successful clauses in its conditions section 🫢

if no clause's test succeeds, or the Conditions field is empty, the assertion's Conditions Compliance Value is treated as the _MIN_TRUST value, as defined in Section 5.1 πŸ’€

if an assertion's Conditions field is missing entirely, its Conditions Compliance Value is treated as the _MAX_TRUST value, as defined in Section 5.1 πŸ‘‘

here's how the set of successful test clause values gets calculated 🧚

remember from the grammar in section 4.6.5 that each clause in the conditions section has two logical parts: a test and an optional value. the value part, if it's there, is separated from the test by the -> token. the test is a predicate that either succeeds (evaluates to logical true) or fails (evaluates to logical false). the value is a string expression that evaluates to one value from the ordered set of compliance values given with the query πŸ’… if the value is missing, it's considered _MAX_TRUST. so this clause:

foo=="bar";

is the same as this one:

foo=="bar" -> _MAX_TRUST;

when the value is present, the simplest case is a string expression that represents a possible compliance value. for example, check out this assertion with the following Conditions field:

Conditions:
   @user_id == 0 -> "full_access";             # clause (1)
   @user_id < 1000 -> "user_access";           # clause (2)
   @user_id < 10000 -> "guest_access";         # clause (3)
   user_name == "root" -> "full_access";       # clause (4)

say the user_id attribute is "1073" and the user_name attribute is "root". then the possible compliance value set would contain "guest_access" (from clause (3)) and "full_access" (from clause (4)) πŸ”₯ if the ordered set of compliance values in the query (in ascending order) is {"no_access", "guest_access", "user_access", "full_access"}, the Conditions Compliance Value of the assertion would be "full_access", because "full_access" has a higher-order value than "guest_access" πŸŽ€

now if user_id is "19283" and user_name is "nobody", no clause succeeds. so the Conditions Compliance Value ends up as "no_access", which is the lowest-order possible value (_MIN_TRUST) πŸ™ˆ

if a clause lists an explicit value, that value string has to be named in the query's ordered compliance value set. any value not named in the query's set is considered equivalent to _MIN_TRUST ✨

the value part of a clause can also hold recursively-nested clauses. these nested ones only get evaluated if their parent test is true 🌸 so this:

a=="b" ->  { b=="c" -> "value1";
             d=="e"  -> "value2";
             true -> "value3"; } ;

is the same as this:

(a=="b") && (b=="c") -> "value1";
(a=="b") && (d=="e") -> "value2";
(a=="b") -> "value3";

string comparisons are case-sensitive πŸ’œ

a regular expression comparison (~=) counts as true if the left-hand-side string expression matches the right-hand-side regular expression. if the POSIX regular expression group matching scheme is used, the number of groups matched goes into the temporary meta-attribute _0 (dereferenced as _0), and each match goes in sequence into the temporary attributes (_1, _2, ..., _N). those match-attribute values are only valid within subsequent references made in the same clause πŸ¦‹ and regular expression evaluation is case-sensitive too 🫢

a runtime error during the evaluation of a test, like division by zero or an invalid regular expression, makes that test count as false 😭

for example:

foo == "bar" -> {
                  @a == 1/0 -> "oneval";    # subclause 1
                  @a == 2 -> "anotherval";  # subclause 2
                };

here, subclause 1 triggers a runtime error. so subclause 1 is false (and has the value _MIN_TRUST). subclause 2, though, gets evaluated normally 🧚

an invalid <RegExpr> also counts as a runtime error, and that makes the test it's in false πŸ’…

5.3.5 Licensee Compliance Value 🌈

the Licensee Compliance Value of an assertion is worked out by evaluating the expression in the Licensees field, based on the Principal Compliance Value of the principals named there 🫢

if an assertion's Licensees field is empty, its Licensee Compliance Value is treated as _MIN_TRUST. if the Licensees field is missing altogether, its Licensee Compliance Value is treated as _MAX_TRUST πŸ‘‘

for each principal named in the Licensees field, that principal's Principal Compliance Value gets swapped in for its name. if no Principal Compliance Value can be found for a named principal, its name gets replaced with the _MIN_TRUST value πŸ’€

the licensees expression (as defined in Section 4.6.4) is evaluated like this:

  • a (...) expression has the value of the subexpression inside it πŸŽ€
  • a && expression has the lower-order (minimum) of its two subexpression values πŸ”₯
  • a || expression has the higher-order (maximum) of its two subexpression values ✨
  • a <K>-of(<List>) expression has the K-th highest order compliance value listed in <List>. values that show up more than once get counted with multiplicity. for example, if K = 3 and the orders of the listed compliance values are (0, 1, 2, 2, 3), the value of the expression is the compliance value of order 2 🌸

for example, take this Licensees field:

Licensees: ("alice" && "bob") || "eve"

say the Principal Compliance Value is "yes" for principal "alice", "no" for principal "bob", and "no" for principal "eve", and "yes" is higher order than "no" in the query's Compliance Value Set. then the resulting Licensee Compliance Value is "no" πŸ’…

and here's the big brain part 🧠 if there are exactly two possible compliance values (like "false" and "true"), the rules of Licensee Compliance Value resolution reduce exactly to standard Boolean logic. iconic, honestly πŸ¦‹

5.4 Assertion Management πŸŽ€

assertions can be either signed or unsigned. only signed assertions should be used as credentials, or transmitted or stored on untrusted media πŸ” unsigned assertions should only be used to specify policy, and for assertions whose integrity has already been verified as conforming to local policy by some mechanism outside the KeyNote system itself (for example, X.509 certificates converted into KeyNote assertions by a trusted conversion program) 🫢

implementations that let signed credentials get verified by the KeyNote compliance checker usually offer two channels for applications to make assertions available πŸ’– unsigned, locally-trusted assertions come in over a trusted interface, and signed credentials come in over an untrusted interface. the KeyNote compliance checker verifies the correct signatures for every assertion submitted over the untrusted interface. KeyNote evaluation's integrity depends on only assertions trusted as reflecting local policy getting submitted through the trusted interface πŸ”₯

note that apps that use KeyNote purely as a local policy specification mechanism only need trusted assertions. other apps might only need a small number of rarely changed trusted assertions to bootstrap a policy whose details live in signed credentials issued by others and submitted over the untrusted interface 🌈

5.5 Implementation Issues πŸ“

informally, KeyNote evaluation semantics can be thought of as building a directed graph of KeyNote assertions, rooted at a POLICY assertion, that connects with at least one of the principals who requested the action 🧚

delegating some authorization from principal <A> to a set of principals <B> is expressed as an assertion with principal <A> in the Authorizer field, principal set <B> in the Licensees field, and the authorization being delegated encoded in the Conditions field ✨ how the expression digraph gets built is implementation-dependent, and implementations may use different algorithms to optimize the graph's construction. some might use a "bottom up" traversal starting at the principals that requested the action, others might follow a "top down" approach starting at the POLICY assertions, and still others might use totally different heuristics πŸ’…

implementations are encouraged to have mechanisms for recording exceptions (like division by zero or syntax error) and reporting them to the invoking application if requested. those mechanisms are outside the scope of this document 🫢

6. Examples ✨

okay bestie, this section is all about hypothetical apps and what KeyNote assertions would look like in them πŸŽ€ These are mainly here to show off the assertion syntax and semantics. They are NOT claiming to be the best way to wire KeyNote into an app πŸ’…

We're using way shorter keys than you'd see in real life, just for readability πŸ¦‹ And the Signature fields are placeholders, not real signature calculations, so don't go trying to verify anything πŸ™ˆ

1. TRADITIONAL CA / EMAIL πŸ’Œ

A. Here's a policy that unconditionally authorizes RSA key abc123 for all actions. Basically it hands the whole job of writing policy over to whoever holds the secret key for abc123 πŸ‘‘

  Authorizer: "POLICY"
  Licensees: "RSA:abc123"

B. Now this credential assertion says RSA key abc123 trusts either RSA key 4401ff92 (called Alice') or DSA key d1234f (called Bob') to perform actions where "app_domain" is "RFC822-EMAIL" and the "address" matches the regular expression "^.*@keynote.research.att.com$". So in plain English, abc123 trusts Alice and Bob as certification authorities for the keynote.research.att.com domain 🫢

  KeyNote-Version: 2
  Local-Constants: Alice="DSA:4401ff92"  # Alice's key
                   Bob="RSA:d1234f"      # Bob's key
  Authorizer: "RSA:abc123"
  Licensees: Alice || Bob
  Conditions: (app_domain == "RFC822-EMAIL") &&
              (address ~=   # only applies to one domain
                "^.*@keynote\\.research\\.att\\.com$");
  Signature: "RSA-SHA1:213354f9"

C. This is a certificate credential for one specific user, mab@keynote.research.att.com. Her name, if it's present, has to be "M. Blaze". It was issued by the `Alice' authority, the one whose key got certified back in Example B 🌸

  KeyNote-Version: 2
  Authorizer: "DSA:4401ff92"  # the Alice CA
  Licensees: "DSA:12340987"   # mab's key
  Conditions: ((app_domain == "RFC822-EMAIL") &&
               (name == "M. Blaze" || name == "") &&
               (address == "mab@keynote.research.att.com"));
  Signature: "DSA-SHA1:ab23487"

D. Another certificate credential for a specific user, also issued by the `Alice' authority. This one lets three different keys sign as jf@keynote.research.att.com, each using a different cryptographic algorithm. It's basically three credentials stuffed into one 🧚

  KeyNote-Version: "2"
  Authorizer: "DSA:4401ff92"   # the Alice CA
  Licensees: "DSA:abc991" ||   # jf's DSA key
             "RSA:cde773" ||   # jf's RSA key
             "BFIK:fd091a"     # jf's BFIK key
  Conditions: ((app_domain == "RFC822-EMAIL") &&
               (name == "J. Feigenbaum" || name == "") &&
               (address == "jf@keynote.research.att.com"));
  Signature: "DSA-SHA1:8912aa"

Okay now the fun part: the vibe check πŸ”₯ Under policy A and credentials B, C and D, these action attribute sets get accepted (they return _MAX_TRUST) ✨

  _ACTION_AUTHORIZERS = "dsa:12340987"
  app_domain = "RFC822-EMAIL"
  address = "mab@keynote.research.att.com"

and

  _ACTION_AUTHORIZERS = "dsa:12340987"
  app_domain = "RFC822-EMAIL"
  address = "mab@keynote.research.att.com"
  name = "M. Blaze"

But these ones are NOT accepted (they return _MIN_TRUST) πŸ’€

  _ACTION_AUTHORIZERS = "dsa:12340987"
  app_domain = "RFC822-EMAIL"
  address = "angelos@dsl.cis.upenn.edu"

and

  _ACTION_AUTHORIZERS = "dsa:abc991"
  app_domain = "RFC822-EMAIL"
  address = "mab@keynote.research.att.com"
  name = "M. Blaze"

and

  _ACTION_AUTHORIZERS = "dsa:12340987"
  app_domain = "RFC822-EMAIL"
  address = "mab@keynote.research.att.com"
  name = "J. Feigenbaum"

2. WORKFLOW/ELECTRONIC COMMERCE πŸ’Έ

E. This policy delegates authority for the "SPEND" application domain to RSA key dab212, but only when the amount in the "dollars" attribute is less than 10000 πŸ’…

  Authorizer: "POLICY"
  Licensees: "RSA:dab212"  # the CFO's key
  Conditions: (app_domain=="SPEND") && (@dollars < 10000);

F. Here RSA key dab212 delegates authorization to any two signers from a list, and one of those signers MUST be DSA key feed1234 for the "SPEND" application when @dollars < 7500. If the amount in @dollars is 2500 or greater, the request gets approved but logged πŸ“

  KeyNote-Version: 2
  Comment: This credential specifies a spending policy
  Authorizer: "RSA:dab212"        # the CFO
  Licensees: "DSA:feed1234" &&    # The vice president
                 ("RSA:abc123" || # middle manager #1
                  "DSA:bcd987" || # middle manager #2
                  "DSA:cde333" || # middle manager #3
                  "DSA:def975" || # middle manager #4
                  "DSA:978add")   # middle manager #5
  Conditions: (app_domain=="SPEND")  # note nested clauses
                -> { (@(dollars) < 2500)
                       -> _MAX_TRUST;
                     (@(dollars) < 7500)
                       -> "ApproveAndLog";
                   };
  Signature: "RSA-SHA1:9867a1"

G. According to this policy, any two signers from the list of managers will do if @(dollars) < 1000 πŸ™Œ

  KeyNote-Version: 2
  Authorizer: "POLICY"
  Licensees: 2-of("DSA:feed1234", # The VP
                  "RSA:abc123",   # Middle management clones
                  "DSA:bcd987",
                  "DSA:cde333",
                  "DSA:def975",
                  "DSA:978add")
  Conditions: (app_domain=="SPEND") &&
              (@(dollars) < 1000);

H. This is a credential from dab212 with a similar policy, except only one signer is needed if @(dollars) < 500. A log entry gets made if the amount is at least 100 🧾

  KeyNote-Version: 2
  Comment: This one credential is equivalent to six separate
           credentials, one for each VP and middle manager.
           Individually, they can spend up to $500, but if
           it's $100 or more, we log it.
  Authorizer: "RSA:dab212"      # From the CFO
  Licensees: "DSA:feed1234" ||  # The VP
             "RSA:abc123" ||    # The middle management clones
             "DSA:bcd987" ||
             "DSA:cde333" ||
             "DSA:def975" ||
             "DSA:978add"
  Conditions: (app_domain="SPEND")  # nested clauses
                -> { (@(dollars) < 100) -> _MAX_TRUST;
                     (@(dollars) < 500) -> "ApproveAndLog";
                   };
  Signature: "RSA-SHA1:186123"

Let's set the scene 🎬 Assume a query where the ordered set of Compliance Values is {"Reject", "ApproveAndLog", "Approve"}. Under policies E and G and credentials F and H, the Policy Compliance Value is "Approve" (_MAX_TRUST) when:

  _ACTION_AUTHORIZERS = "DSA:978add"
  app_domain = "SPEND"
  dollars = "45"
  unmentioned_attribute = "whatever"

and

  _ACTION_AUTHORIZERS = "RSA:abc123,DSA:cde333"
  app_domain = "SPEND"
  dollars = "550"

These ones return "ApproveAndLog" ✨

  _ACTION_AUTHORIZERS = "DSA:feed1234,DSA:cde333"
  app_domain = "SPEND"
  dollars = "5500"

and

  _ACTION_AUTHORIZERS = "DSA:cde333"
  app_domain = "SPEND"
  dollars = "150"

And finally, these ones return "Reject" (_MIN_TRUST) 😭

  _ACTION_AUTHORIZERS = "DSA:def975"
  app_domain = "SPEND"
  dollars = "550"

and

  _ACTION_AUTHORIZERS = "DSA:cde333,DSA:978add"
  app_domain = "SPEND"
  dollars = "5500"

7. Trust-Management Architecture ✨

okay so KeyNote's whole vibe is different from traditional certification systems πŸ’… Instead of resolving names, the security model binds keys to predicates, meaning statements about what a key is authorized by policy to do. So the infrastructure and architecture around it are pretty different from what you'd need for a name-based scheme. The KeyNote trust-management architecture is based on the one used by PolicyMaker [BFL96,BFS98] πŸŽ€

It's super important to know who handles what 🫢 A KeyNote compliance checker looks at policy and credential assertions and decides whether a proposed action is allowed by policy. But whether KeyNote's output actually enforces anything depends on a few things:

  • The action attributes and their values have to accurately match the security requirements of the application. Picking which attributes go in the action attribute set is probably the biggest job when you're plugging KeyNote into a new app πŸ”₯
  • The application's policy has to be correct and well-formed. Trust should only ever be handed to principals the application actually should trust πŸ‘‘
  • The application itself has to be trustworthy πŸ₯Ί KeyNote doesn't enforce policy on its own. It just gives advice to the apps that call it. So KeyNote assumes the app is trusted and the policy assertions are correct. Nothing stops an app from feeding KeyNote misleading or wrong assertions, or from ignoring KeyNote entirely πŸ’€

It's also on the application (or some service outside KeyNote) to pick the right credentials and policy assertions for each query πŸ’– Here's the good part though: even if the wrong credentials get passed in, that can't get an illegal action approved, as long as the policy assertions are correct and the action attribute set is passed to KeyNote correctly ✨

KeyNote is monotonic, which is a fancy way of saying adding an assertion to a query can never give that query a lower compliance value than it would've had without it πŸ¦‹ Leaving credentials out can definitely cause legal actions to get disallowed, though πŸ™ˆ Picking the right credentials (say, from a distributed database or a key server) is outside the KeyNote language's scope. That job can be handled by a remote client making the request, by the local application receiving it, or by a network-based service, depending on the app 🌸

Also, KeyNote doesn't provide credential revocation services by itself πŸ’… You can make credentials expire after a certain date by putting a date test in the predicate though 🌈 Apps that need revocation can use KeyNote to help specify and implement their revocation policies. A future document is supposed to cover expiration and revocation services in KeyNote πŸ‘€

Since KeyNote is built to support lots of different apps, there are a few ways to interface with a KeyNote implementation 🧚 In the simplest form, a KeyNote compliance checker is a stand-alone application that other apps call whenever they need it. KeyNote could also be a library that apps link against. And finally, a KeyNote implementation could run as a local trusted service, with local apps sending their queries over some interprocess communication mechanism πŸ’œ


8. Security Considerations

okay this section is short but LOUD 😭 Trust management is itself a security service. So bugs in a KeyNote compliance checker implementation, or using one incorrectly, could have security implications for any application that uses it πŸš¨πŸ’€


9. IANA Considerations

this document has three identifier lists that IANA maintains πŸ“ This section explains the criteria IANA should use when assigning new identifiers to each of them πŸŽ€

9.1. app_domain Identifiers

for these, IANA really just needs to make sure each one is a unique string πŸ’… These strings are case-sensitive for KeyNote purposes. That said, it's strongly recommended that IANA only gives different capitalizations of the same string to the same organization 🫢

9.2. Public Key Format Identifiers

these strings uniquely identify a public key algorithm as it's used in KeyNote for representing keys πŸ”‘ If you want a new one, the request has to come with an RFC-style document describing the details of the encoding πŸ“„ Examples are rsa-hex: and dsa-base64: ✨ These strings are case-insensitive 🌸

9.3. Signature Algorithm Identifiers

these strings uniquely identify a public key algorithm as it's used in KeyNote for representing public key signatures ✍️ Same deal as before: new requests need an RFC-style document describing the encoding details πŸ’– Example strings are sig-rsa-md5-hex: and sig-dsa-sha1-base64: πŸ¦‹ Note that every one of these strings must begin with the prefix sig- πŸ‘‘ And yep, they're case-insensitive too 🌈


A. Acknowledgments

big thank yous to Lorrie Faith Cranor (AT&T Labs - Research) and Jonathan M. Smith (University of Pennsylvania) for their suggestions and comments on earlier versions of this document πŸ«ΆπŸ’œ


B. Full BNF (alphabetical order)

the whole grammar, A to Z, so bookmark this one πŸ”–πŸ’…

<ALGORITHM>:: {see section 4.4.2} ;

<Assertion>:: <VersionField>? <AuthField> <LicenseesField>?
              <LocalConstantsField>? <ConditionsField>?
              <CommentField>? <SignatureField>? ;

<Assignments>:: "" | <AttributeID> "=" <StringLiteral> <Assignments>
;

<AttributeID>:: {Any string starting with a-z, A-Z, or the
                 underscore character, followed by any number of
                 a-z, A-Z, 0-9, or underscore characters} ;

<AuthField>:: "Authorizer:" <AuthID> ;

<AuthID>:: <PrincipalIdentifier> | <DerefAttribute> ;

<Clause>:: <Test> "->" "{" <ConditionsProgram> "}"
         | <Test> "->" <Value> | <Test> ;

<Comment>:: "#" {ASCII characters} ;

<CommentField>:: "Comment:" {Free-form text} ;

<ConditionsField>:: "Conditions:" <ConditionsProgram> ;

<ConditionsProgram>:: "" | <Clause> ";" <ConditionsProgram> ;

<DerefAttribute>:: <AttributeID> ;

<ENCODEDBITS>:: {see section 4.4.2} ;

<FloatEx>:: <FloatEx> "+" <FloatEx> | <FloatEx> "-" <FloatEx>
          | <FloatEx> "*" <FloatEx> | <FloatEx> "/" <FloatEx>
          | <FloatEx> "^" <FloatEx> | "-" <FloatEx>
          | "(" <FloatEx> ")" | <FloatLiteral> | "&" <StrEx> ;

<FloatRelExpr>:: <FloatEx> "<" <FloatEx> | <FloatEx> ">" <FloatEx>
               | <FloatEx> "<=" <FloatEx>
               | <FloatEx> ">=" <FloatEx> ;

<FloatLiteral>:: <IntegerLiteral>"."<IntegerLiteral> ;

<IDString>:: <ALGORITHM>":"<ENCODEDBITS> ;

<IntegerLiteral>:: {Decimal number of at least one digit} ;

<IntEx>:: <IntEx> "+" <IntEx> | <IntEx> "-" <IntEx>
        | <IntEx> "*" <IntEx> | <IntEx> "/" <IntEx>
        | <IntEx> "%" <IntEx> | <IntEx> "^" <IntEx>
        | "-" <IntEx> | "(" <IntEx> ")" | <IntegerLiteral>
        | "@" <StrEx> ;

<IntRelExpr>:: <IntEx> "==" <IntEx> | <IntEx> "!=" <IntEx>
             | <IntEx> "<" <IntEx>  | <IntEx> ">" <IntEx>
             | <IntEx> "<=" <IntEx> | <IntEx> ">=" <IntEx> ;

<K>:: {Decimal number starting with a digit from 1 to 9} ;

<KeyID>:: <StrEx> ;

<LicenseesExpr>:: "" | <PrincExpr> ;

<LicenseesField>:: "Licensees:" <LicenseesExpr> ;

<LocalConstantsField>:: "Local-Constants:" <Assignments> ;

<OpaqueID>:: <StrEx> ;

<PrincExpr>:: "(" <PrincExpr> ")" | <PrincExpr> "&&" <PrincExpr>
            | <PrincExpr> "||" <PrincExpr>
            | <K>"-of(" <PrincList> ")" | <PrincipalIdentifier>
            | <DerefAttribute> ;

<PrincipalIdentifier>:: <OpaqueID> | <KeyID> ;

<PrincList>:: <PrincipalIdentifier> | <DerefAttribute>
            | <PrincList> "," <PrincList> ;

<RegExpr>:: {POSIX 1003.2 Regular Expression}

<RelExpr>:: "(" <RelExpr> ")" | <RelExpr> "&&" <RelExpr>
          | <RelExpr> "||" <RelExpr> | "!" <RelExpr>
          | <IntRelExpr> | <FloatRelExpr> | <StringRelExpr>
          | "true" | "false" ;

<Signature>:: <StrEx> ;

<SignatureField>:: "Signature:" <Signature> ;

<StrEx>:: <StrEx> "." <StrEx> | <StringLiteral> | "(" <StrEx> ")"
        | <DerefAttribute> | "$" <StrEx> ;

<StringLiteral>:: {see section 4.3.1} ;

<StringRelExpr>:: <StrEx> "==" <StrEx> | <StrEx> "!=" <StrEx>
                | <StrEx> "<" <StrEx> | <StrEx> ">" <StrEx>
                | <StrEx> "<=" <StrEx> | <StrEx> ">=" <StrEx>
                | <StrEx> "~=" <RegExpr> ;

<Test>:: <RelExpr> ;

<Value>:: <StrEx> ;

<VersionField>:: "KeyNote-Version:" <VersionString> ;

<VersionString>:: <StringLiteral> | <IntegerLiteral> ;

References

here's the reading list, complete with every source πŸ“šπŸŒ·

  • [BFL96] M. Blaze, J. Feigenbaum, J. Lacy. Decentralized Trust Management. Proceedings of the 17th IEEE Symp. on Security and Privacy. pp 164-173. IEEE Computer Society, 1996. Available at ftp://ftp.research.att.com/dist/mab/policymaker.ps
  • [BFS98] M. Blaze, J. Feigenbaum, M. Strauss. Compliance-Checking in the PolicyMaker Trust-Management System. Proc. 2nd Financial Crypto Conference. Anguilla 1998. LNCS #1465, pp 251-265, Springer-Verlag, 1998. Available at ftp://ftp.research.att.com/dist/mab/pmcomply.ps
  • [Bla99] M. Blaze, J. Feigenbaum, J. Ioannidis, A. Keromytis. The Role of Trust Management in Distributed System Security. Chapter in Secure Internet Programming: Security Issues for Mobile and Distributed Objects (Vitek and Jensen, eds.). Springer-Verlag, 1999. Available at ftp://ftp.research.att.com/dist/mab/trustmgt.ps.
  • [Cro82] Crocker, D., "Standard for the Format of ARPA Internet Text Messages", STD 11, RFC 822, August 1982.
  • [DSA94] Digital Signature Standard. FIPS-186. National Institute of Standards, U.S. Department of Commerce. May 1994.
  • [PKCS1] PKCS #1: RSA Encryption Standard, Version 1.5. RSA Laboratories. November 1993.
  • [RSA78] R. L. Rivest, A. Shamir, L. M. Adleman. A Method for Obtaining Digital Signatures and Public-Key Cryptosystems. Communications of the ACM, v21n2. pp 120-126. February 1978.

Authors' Addresses πŸ’Œ

okay bestie, comments about this doc should go to the keynote-users mailing list hosted at nsa.research.att.com 🫢 to join, send an email with just this one line in the body to <majordomo@nsa.research.att.com>:

subscribe keynote-users

questions can also go to the authors as a group at keynote@research.att.com, or to each author individually πŸ’œ

Matt Blaze
AT&T Labs - Research
180 Park Avenue
Florham Park, New Jersey 07932-0971

EMail: mab@research.att.com
Joan Feigenbaum
AT&T Labs - Research
180 Park Avenue
Florham Park, New Jersey 07932-0971

EMail: jf@research.att.com
John Ioannidis
AT&T Labs - Research
180 Park Avenue
Florham Park, New Jersey 07932-0971

EMail: ji@research.att.com
Angelos D. Keromytis
Distributed Systems Lab
CIS Department, University of Pennsylvania
200 S. 33rd Street
Philadelphia, Pennsylvania  19104-6389

EMail: angelos@dsl.cis.upenn.edu

the copyright notice reads exactly like this πŸ¦‹

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

so the deal is ✨ anyone can copy and share this doc, and make derivative works that comment on it, explain it, or help people implement it. you can publish and distribute those copies and works, in whole or in part, with no real restrictions, as long as the copyright notice above and this paragraph are included on every copy and derivative work πŸ’…

but the doc itself may not be modified in any way 🚫 that includes removing the copyright notice or the references to the Internet Society or other Internet organizations. the only exceptions are: (a) doing it to develop Internet standards, in which case the copyright procedures in the Internet Standards process have to be followed, or (b) translating it into languages other than English 🌈

these permissions are perpetual, and the Internet Society or its successors or assigns will never revoke them 🧚

and the fine print, which is serious and in all caps, so read it πŸ™ˆ this document and the info in it 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 🌸

big thank you to the people funding the RFC Editor function 🫢 right now, that funding comes from the Internet Society ✨

πŸ“¦ where this page comes from