RFC 8181

A Publication Protocol for the Resource Public Key Infrastructure (RPKI)

πŸ“… published July 2017, from ietf

A Publication Protocol for the Resource Public Key Infrastructure (RPKI) πŸŽ€

Okay bestie, here's the header rundown ✨

  • Published by: the Internet Engineering Task Force (IETF) 🌸
  • RFC number: 8181
  • Authors: S. Weiler (W3C / MIT), A. Sonalker (STEER Tech), and R. Austein (Dragon Research Labs) πŸ‘©β€πŸ’»
  • Category: Standards Track
  • ISSN: 2070-1721
  • Date: July 2017
  • Obsoletes / updates: the header doesn't list any RFCs that this one obsoletes or updates πŸ™ˆ

Abstract ✨

okay so this doc defines a protocol for publishing RPKI objects πŸ“¦. the RPKI is gonna have tons of participants issuing certificates and making other objects, so it's operationally useful to consolidate where those objects get published πŸ’–. even when a certificate issuer runs its own publication repository, it can be super useful to run the certificate engine on a different machine from that repository 🧚. this doc defines a protocol that handles all of these needs πŸ’…

Status of This Memo πŸ“

This is an Internet Standards Track document, no cap πŸ”₯

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community, it got public review, and it was approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check out Section 2 of RFC 7841 πŸŽ€

For info on the current status of this document, any errata, and how to give feedback on it, go to http://www.rfc-editor.org/info/rfc8181 πŸ¦‹

Copyright (c) 2017 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, because they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Simplified BSD License 🫢

Table of Contents πŸ—‚οΈ

   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   3
     1.1.  Historical Note . . . . . . . . . . . . . . . . . . . . .   4
     1.2.  Terminology . . . . . . . . . . . . . . . . . . . . . . .   5
   2.  Protocol Specification  . . . . . . . . . . . . . . . . . . .   5
     2.1.  Common XML Message Format . . . . . . . . . . . . . . . .   6
     2.2.  Publication and Withdrawal  . . . . . . . . . . . . . . .   7
     2.3.  Listing the Repository  . . . . . . . . . . . . . . . . .   8
     2.4.  Error Handling  . . . . . . . . . . . . . . . . . . . . .   8
     2.5.  Error Codes . . . . . . . . . . . . . . . . . . . . . . .   9
     2.6.  XML Schema  . . . . . . . . . . . . . . . . . . . . . . .  10
   3.  Examples  . . . . . . . . . . . . . . . . . . . . . . . . . .  12
     3.1.  <publish/> Query, No Existing Object  . . . . . . . . . .  12
     3.2.  <publish/> Query, Overwriting Existing Object . . . . . .  12
     3.3.  <withdraw/> Query . . . . . . . . . . . . . . . . . . . .  13
     3.4.  <success/> Reply  . . . . . . . . . . . . . . . . . . . .  13
     3.5.  <report_error/> with Optional Elements  . . . . . . . . .  13
     3.6.  <report_error/> without Optional Elements . . . . . . . .  14
     3.7.  Error Handling with Multi-Element Queries . . . . . . . .  14
       3.7.1.  Multi-Element Query . . . . . . . . . . . . . . . . .  14
       3.7.2.  Successful Multi-Element Response . . . . . . . . . .  15
       3.7.3.  Failure Multi-Element Response, First Error Only  . .  15
       3.7.4.  Failure Multi-Element Response, All Errors  . . . . .  16
     3.8.  <list/> Query . . . . . . . . . . . . . . . . . . . . . .  16
     3.9.  <list/> Reply . . . . . . . . . . . . . . . . . . . . . .  17
   4.  IANA Considerations . . . . . . . . . . . . . . . . . . . . .  17
   5.  Security Considerations . . . . . . . . . . . . . . . . . . .  18
   6.  References  . . . . . . . . . . . . . . . . . . . . . . . . .  19
     6.1.  Normative References  . . . . . . . . . . . . . . . . . .  19
     6.2.  Informative References  . . . . . . . . . . . . . . . . .  20
   Acknowledgements  . . . . . . . . . . . . . . . . . . . . . . . .  21
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  21

1. Introduction πŸ’–

This doc assumes you already know the basics of the Resource Public Key Infrastructure (RPKI), which exists to support better routing security on the Internet πŸ”. For an overview of the RPKI, see [RFC6480].

To make participating in the RPKI easier, it helps to have a few consolidated repositories for RPKI objects. That way, every participant doesn't have to pay the cost of running a brand new service πŸ’Έ. Relying parties that use RPKI objects also find it faster and more reliable to grab the set they need from a smaller number of repositories πŸƒβ€β™€οΈ

These consolidated RPKI object repositories will often sit outside the administrative scope of the organization that issues a given RPKI object. In some cases, outsourcing the repository is the whole point: some resource holders really want to control their own RPKI private keys, but they might not have the resources to run a 24x7 repository, or they just might not want to 😩

The operator of an RPKI publication repository might be an Internet registry that issues certificates to its customers, but it doesn't have to be. Conceptually, running a publication repository is separate from running an RPKI Certification Authority (CA) 🧁

Even when a resource holder runs both a certificate engine and a publication repository, it can be useful to separate the two functions πŸ”’. They have somewhat different operational and security requirements, so keeping them apart makes sense.

This doc defines an RPKI publication protocol that allows publication either within or across organizational boundaries. It also keeps demands on both the CA engine and the publication service fairly minimal ✨

The authentication and message integrity architecture of the publication protocol is basically the same as the architecture used in [RFC6492]. That's because the participants in this protocol are the same CA engines as in RFC 6492, which lets you reuse the same "Business PKI" (BPKI) (see Section 1.2) infrastructure that already supports RFC 6492 πŸ’…. As in RFC 6492, authorization is a matter of external configuration. We assume that any given publication repository has some kind of policy controlling which certificate engines are allowed to publish, modify, or withdraw particular RPKI objects, most likely following the recommendation in [RFC6480], Section 4.4. The details of that policy are a private matter between the operator of a certificate engine and the operator of the chosen publication repository 🀫

The following diagram tries to show where this publication protocol fits into the overall data flow between certificate issuers and relying parties πŸ—ΊοΈ

          +------+    +------+    +------+
          |  CA  |    |  CA  |    |  CA  |
          +------+    +------+    +------+
              |           |           |    Publication protocol
              |           |           |    business relationship
              +-------+   |  +--------+      perhaps set up by
                      |   |  |                    RFC 8183
                 +----v---v--v-----+
                 |                 |
                 |   Publication   |
                 |   Repository    |
                 |                 |
                 +-----------------+     Distribution protocols
                          |                   rsync or RRDP
           +--------------+----------------+
           |              |                |
   +-------v-----+ +------v------+  +------v------+
   |   Relying   | |   Relying   |  |   Relying   |
   |    Party    | |    Party    |  |    Party    |
   +-------------+ +-------------+  +-------------+

The publication protocol itself is not visible to relying parties. They only see the public interface of the publication server, which is an rsync or RPKI Repository Delta Protocol (RRDP) [RFC8182] server πŸ‘€

Operators of certificate engines and publication repositories may find [RFC8183] a useful tool for setting up the pairwise relationships between these servers. They aren't required to use it, though πŸŽ€

1.1. Historical Note πŸ•°οΈ

This protocol started out as an informal collaboration between several of the early RPKI implementers πŸ§‘β€πŸ’». The designers always intended for the resulting protocol to end up on the IETF Standards Track, but it took a few years to get there because standardizing other pieces of the overall RPKI protocol space was more urgent. The Standards Track version keeps the original XML namespace and protocol version scheme, so it stays backwards compatible with running code built against older versions of the specification πŸ’œ

1.2. Terminology πŸ“š

Here's the key vocab, and it matters because these words carry real meaning 🧐

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

"Publication engine" and "publication server" mean the same thing 🀝. Both refer to the server that provides the service described in this document.

"Business Public Key Infrastructure" ("Business PKI" or "BPKI") refers to a PKI, separate from the RPKI, that's used to authenticate clients to the publication engine. We use the term "Business PKI" here because an Internet registry might already have a PKI for authenticating its clients, and it might want to reuse that PKI for this protocol. There is, however, no requirement to reuse such a PKI 🫢

2. Protocol Specification ✨

The publication protocol uses XML [XML] messages wrapped in signed Cryptographic Message Syntax (CMS) messages, carried over HTTP transport [RFC7230]. The CMS encapsulation is identical to the one used in Section 3.1 (and subsections) of RFC 6492 [RFC6492] πŸ’…

The publication protocol uses a simple request/response interaction. The client passes a request to the server, and the server generates a corresponding response πŸ¦‹

A message exchange starts with the client initiating an HTTP POST with a content type of application/rpki-publication, with the message object as the body. The server's response will similarly be the body of the response, with a content type of application/rpki-publication πŸ“¨

The content of the POST and the server's response will be a well-formed CMS [RFC5652] object with OID = 1.2.840.113549.1.7.2, as described in Section 3.1 of [RFC6492] πŸŽ€

The CMS signatures are used to protect the integrity of the protocol messages and to authenticate the client and server to each other πŸ”. Authorization to perform particular operations is a local matter. It might be determined by contractual agreements between the operators of any particular client-server pair, but in any case it's beyond the scope of this specification 🌈

2.1. Common XML Message Format ✨

okay so the XML schema for this protocol is down in Section 2.6 πŸ’… but first, here's the basic message shape, which is honestly so simple πŸŽ€

<msg
    type="query"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <!-- Zero or more PDUs -->
</msg>

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <!-- Zero or more PDUs -->
</msg>

As we mentioned earlier, the outermost XML element gets wrapped in a signed CMS message πŸ”. Queries are signed by the client, and replies are signed by the server. Very fair, very balanced 🫢

Common attributes:

  • version: the version of this protocol. This document describes version 4 🌸
  • type: the only possible values are "reply" and "query" πŸ’–

A query PDU can be one of three types: <publish/>, <withdraw/>, or <list/> ✨

A reply PDU can be one of three types: <success/>, <list/>, or <report_error/> πŸ¦‹

The <publish/> and <withdraw/> PDUs have a "tag" attribute, which is there to make bulk operations easier 🧚 When a CA engine is doing a bunch of these at once, it'll probably love giving each <publish/> or <withdraw/> PDU its own tag value, so it's super easy to match an error with the exact PDU that caused it. The tag attribute is mandatory, just so parsing stays simple. Still, a CA engine that has no real use for tagging MAY use any syntactically legal value, even the empty string for every tag field. No pressure, lol πŸ™ˆ

This document describes version 4 of the protocol. An implementation that only understands this version MUST reject messages with a different protocol version attribute, and it signals that error the way Section 2.4 describes 🚫 Since "4" is currently the only value the schema allows for the version attribute (Section 2.6), a wrong protocol version can get caught either by checking the version attribute directly or as a schema validation error. Any future update that is either syntactically or semantically incompatible with the current version will need to bump the protocol version number. That's the rule, bestie πŸ‘‘


2.2. Publication and Withdrawal πŸ¦‹

The publication protocol uses one common message format to request publication of any RPKI object. This format was picked on purpose, so the protocol can handle brand-new types of RPKI objects without needing any changes to the protocol itself. Iconic design, honestly 😭✨

Both the <publish/> and <withdraw/> PDUs carry a payload made of a tag and an rsync URI [RFC3986] [RFC5781]. The <publish/> query also contains the DER object to be published, encoded in Base64 ([RFC4648], Section 4, with line breaks inside the Base64 text permitted but not required) πŸ“

Both the <publish/> and <withdraw/> PDUs also have a "hash" attribute. This carries a hash of the existing object at the specified repository URI, written as a hexadecimal string πŸ”₯ Now the rules, and they're strict:

  • For <withdraw/> PDUs, the hash MUST be present. Withdrawing something that doesn't exist makes no sense, so there has to be an existing object to withdraw πŸ’€
  • For <publish/> PDUs, the hash MUST be present if the publication operation is overwriting an existing object. It MUST NOT be present if the operation is writing to a brand-new URI where no prior object exists πŸ™…β€β™€οΈ
  • Presence of an object when no "hash" attribute was specified is an error. Absence of an object, or an incorrect hash value when a "hash" attribute was specified, is also an error. Any of these errors MUST be reported using the <report_error/> PDU 🚨

The hash algorithm is SHA-256 [SHS], which keeps publication protocol hashes easy to compare with RPKI manifest hashes. Very much a "make your life easier" move πŸ’…

The whole point of the "hash" attribute is to let the client and server spot any disagreement about what a <publish/> or <withdraw/> PDU is going to do to the repository πŸ•΅οΈβ€β™€οΈ

Heads up, though: every publish and withdraw action requires a new manifest. That means every single publish or withdraw action involves at least two objects. Yes, it's a lot, but it's the way it works 🌈

Processing of a query message is atomic. It's all or nothing: either the entire query succeeds, or none of it does 🫧 When a query has multiple PDUs and one of them fails, the server may have to roll back the actions that earlier PDUs already triggered. Dramatic, but fair 😭

When a query containing <publish/> or <withdraw/> PDUs succeeds, the server returns a single <success/> reply. Clean and simple πŸ’–

When a query fails, the server returns one or more <report_error/> reply PDUs. Usually, the server only generates one <report_error/>, corresponding to the first query PDU that failed. Servers MAY return multiple <report_error/> PDUs if the implementer wants to, though 🧚


2.3. Listing the Repository πŸ“œ

The <list/> operation lets the client ask the server for a complete listing of the objects the server believes the client has published. This is mainly there so the client can recover after noticing (probably through the "hash" attribute; see Section 2.2) that it has somehow lost synchronization πŸ”„ Lost sync is the worst, so this is a lifesaver 🫢

The <list/> query is made of a single PDU. A <list/> query MUST be the only PDU in the query. It can't be mixed with any <publish/> or <withdraw/> queries, no exceptions 🚫

The <list/> reply has zero or more PDUs, one per object this client has published in this repository. Each PDU shares the URI and hash of one published object πŸŽ€


2.4. Error Handling ⚠️

Errors get handled at two levels, so let's break it down πŸ¦‹

Errors that make it impossible to decode a query or encode a response are handled at the HTTP layer. 4xx and 5xx HTTP response codes are how that layer says something bad happened 😬

In every other case, errors result in an XML <report_error/> PDU. Like the rest of this protocol, <report_error/> PDUs are CMS-signed XML messages, so they can be archived to make an audit trail πŸ“βœ¨

<report_error/> PDUs only ever appear in replies, never in queries. Never. πŸ™…β€β™€οΈ

The "tag" attribute of a <report_error/> PDU that's associated with a <publish/> or <withdraw/> PDU MUST be set to the same value as the "tag" attribute in the PDU that caused the error. That's how a client figures out which PDU made an update fail πŸ•΅οΈβ€β™€οΈ

The error itself is conveyed in the "error_code" attribute. Its value is a token that names the specific error that happened πŸ’…

The body of the <report_error/> element has two sub-elements:

  1. An optional text element, <error_text/>. If it's there, it holds a text string with debugging information meant for humans to read 🧠
  2. An optional element, <failed_pdu/>. If it's there, it holds a verbatim copy of the query PDU whose failure triggered the <report_error/> PDU. The quoted element must be syntactically valid πŸŽ€

See Section 3.7 for examples of a multi-element query and its responses 🌸


2.5. Error Codes 🚨

These are the defined error codes, plus a little discussion of each one. Text that's similar to these descriptions may be sent in an <error_text/> element to help explain what went wrong πŸ’–

  • xml_error: an XML problem happened. Some XML errors may be severe enough that they need reporting at the HTTP layer instead. Implementations MAY choose to report any or all XML errors at the HTTP layer 🧾
  • permission_failure: the client doesn't have permission to update this URI πŸ”’
  • bad_cms_signature: the CMS signature is bad πŸ’€
  • object_already_present: an object is already at this URI, but no "hash" attribute was specified. A "hash" attribute must be specified when overwriting or deleting an object. Maybe client and server are out of sync? πŸ™ˆ
  • no_object_present: there's no object at this URI, but a "hash" attribute was specified. Maybe client and server are out of sync? πŸ€”
  • no_object_matching_hash: the "hash" attribute supplied doesn't match the "hash" attribute of the object at this URI. Maybe client and server are out of sync? πŸ₯Ί
  • consistency_problem: the server noticed an update that looks like it will cause a consistency problem (for example, an object was deleted, but the manifest wasn't updated). A server is not required to make these checks. Honestly, it may be unwise for a server to do so πŸ™ƒ This error code just gives the server a way to explain its (in-)action πŸ’…
  • other_error: a meteor fell on the server β˜„οΈπŸ’€

2.6. XML Schema πŸ“

The schema below is a [RELAX-NG] compact form schema describing the publication protocol 🧚

This schema is normative: if it ever disagrees with the document text above, this schema is the one that's authoritative πŸ‘‘

# RELAX NG schema for RPKI publication protocol.

default namespace =
    "http://www.hactrn.net/uris/rpki/publication-spec/"

# This is version 4 of the protocol.

version = "4"

# Top-level PDU is either a query or a reply.

start |= element msg {
  attribute version { version },
  attribute type    { "query" },
  query_elt
}

start |= element msg {
  attribute version { version },
  attribute type    { "reply" },
  reply_elt
}

# Tag attributes for bulk operations.

tag = attribute tag { xsd:token { maxLength="1024" } }

# Base64-encoded DER stuff.

base64 = xsd:base64Binary
# Publication URIs.

uri = attribute uri { xsd:anyURI { maxLength="4096" } }

# Digest of an existing object (hexadecimal).

hash = attribute hash { xsd:string { pattern = "[0-9a-fA-F]+" } }

# Error codes.

error |= "xml_error"
error |= "permission_failure"
error |= "bad_cms_signature"
error |= "object_already_present"
error |= "no_object_present"
error |= "no_object_matching_hash"
error |= "consistency_problem"
error |= "other_error"

# <publish/> and <withdraw/> query elements

query_elt |= (
  element publish  { tag, uri, hash?, base64 } |
  element withdraw { tag, uri, hash          }
)*

# <success/> reply

reply_elt |= element success { empty }

# <list/> query and reply

query_elt |= element list { empty }
reply_elt |= element list { uri, hash }*

# <report_error/> reply

reply_elt |= element report_error {
  tag?,
  attribute error_code { error },
  element   error_text { xsd:string { maxLength="512000" }}?,
  element   failed_pdu { query_elt }?
}*

3. Examples ✨

Here are examples of various queries and their matching replies for the RPKI publication protocol πŸŽ€

Heads up: the authors took some liberties with the Base64, hash, and URI text in these examples so they'd fit nicely into RFC text format πŸ™ˆ Also, these examples don't show the CMS signature wrapper around the XML, just the XML payload πŸ“¦

3.1. <publish/> Query, No Existing Object πŸ¦‹

<msg
    type="query"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <!-- body is base64(new-object) -->
  <publish
      tag=""
      uri="rsync://wombat.example/Alice/01a97a70ac477f06.cer">
      SGVsbG8sIG15IG5hbWUgaXMgQWxpY2U=
    </publish>
</msg>

3.2. <publish/> Query, Overwriting Existing Object πŸ’…

<msg
    type="query"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <!-- hash is hex(SHA-256(old-object)) -->
  <!-- body is base64(new-object) -->
  <publish
      hash="01a97a70ac477f06"
      tag="foo"
      uri="rsync://wombat.example/Alice/01a97a70ac477f06.cer">
      SGVsbG8sIG15IG5hbWUgaXMgQWxpY2U=
    </publish>
</msg>

3.3. <withdraw/> Query πŸ—‘οΈ

okay so this one is a withdraw query, and it's giving "delete this specific object, bestie" πŸ’… You send a <msg> with type="query" and version="4", then a <withdraw/> that names the object by its hash, tag, and uri. The hash is the hex of the SHA-256 of the old object, which the comment in the example spells out for you 🧚

<msg
    type="query"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <!-- hash is hex(SHA-256(old-object)) -->
  <withdraw
      hash="01a97a70ac477f06"
      tag="foo"
      uri="rsync://wombat.example/Alice/01a97a70ac477f06.cer"/>
</msg>

3.4. <success/> Reply πŸŽ‰

this is the happy ending we all want πŸ₯Ή When everything goes right, the server sends back a <msg> with type="reply" and version="4", and inside it is just a <success/> element. Short, sweet, iconic ✨

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <success/>
</msg>

3.5. <report_error/> with Optional Elements πŸ™ˆ

okay this one is a little more dramatic. Here the server says "nope, can't delete an object I don't have" πŸ’€ The <report_error/> carries an error_code of no_object_matching_hash and the same tag as the query. It also has the optional extras: an <error_text/> with a human-readable message, and a <failed_pdu/> that shows the exact <publish/> element that caused the trouble, base64 payload and all πŸ“

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <report_error
      error_code="no_object_matching_hash"
      tag="foo">
    <error_text>
      Can't delete an object I don't have
    </error_text>
    <failed_pdu>
      <publish
          hash="01a97a70ac477f06"
          tag="foo"
          uri="rsync://wombat.example/Alice/01a97a70ac477f06.cer">
      SGVsbG8sIG15IG5hbWUgaXMgQWxpY2U=
    </publish>
    </failed_pdu>
  </report_error>
</msg>

3.6. <report_error/> without Optional Elements πŸ¦‹

this is the minimalist version, and honestly I'm obsessed 😭 Same <msg> wrapper, but the <report_error/> is self-closing. It only has the error_code (object_already_present) and the tag. No extra text, no failed PDU, just the facts, bestie πŸ’œ

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <report_error
      error_code="object_already_present"
      tag="foo"/>
</msg>

3.7. Error Handling with Multi-Element Queries 🧩

okay so what happens when you send a whole pile of PDUs in one query? πŸ‘€ Lowkey the most fun part of this section, because we get to see how the server reports errors when things get busy 🌈

3.7.1. Multi-Element Query πŸ›οΈ

this is the shopping cart of queries πŸ›’ One <msg> with type="query" holds a bunch of <publish/> and <withdraw/> elements all mixed together, each with its own tag. Alice, Carol, and Eve are publishing, while Bob and Dave are withdrawing πŸ¦‹

<msg
    type="query"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <publish
      tag="Alice"
      uri="rsync://wombat.example/Alice/01a97a70ac477f06.cer">
      SGVsbG8sIG15IG5hbWUgaXMgQWxpY2U=
    </publish>
  <withdraw
      hash="f46a4198efa3070e"
      tag="Bob"
      uri="rsync://wombat.example/Bob/f46a4198efa3070e.cer"/>
  <publish
      tag="Carol"
      uri="rsync://wombat.example/Carol/32e0544eeb510ec0.cer">
      SGVsbG8sIG15IG5hbWUgaXMgQ2Fyb2w=
    </publish>
  <withdraw
      hash="421ee4ac65732d72"
      tag="Dave"
      uri="rsync://wombat.example/Dave/421ee4ac65732d72.cer"/>
  <publish
      tag="Eve"
      uri="rsync://wombat.example/Eve/9dd859b01e5c2ebd.cer">
      SGVsbG8sIG15IG5hbWUgaXMgRXZl
    </publish>
</msg>

3.7.2. Successful Multi-Element Response ✨

if every single element in that big query works out, the reply is just the same <success/> we saw before. Clean. Iconic. No notes πŸ’…

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <success/>
</msg>

3.7.3. Failure Multi-Element Response, First Error Only 🚫

now the drama begins 😭 In this version, only the first error gets reported. Dave's withdraw is the one that fails, because there's no object matching that hash, so the reply names Dave's tag and shows the failed <withdraw/> inside the <failed_pdu/>.

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <report_error
      error_code="no_object_matching_hash"
      tag="Dave">
    <failed_pdu>
      <withdraw
          hash="421ee4ac65732d72"
          tag="Dave"
          uri="rsync://wombat.example/Dave/421ee4ac65732d72.cer"/>
    </failed_pdu>
  </report_error>
</msg>

3.7.4. Failure Multi-Element Response, All Errors πŸ”₯

this is the "report everything" version, and it's giving full chaos energy πŸŒͺ️ Dave's error shows up again, but this time Eve's failure is reported too. Eve's <publish/> hit object_already_present, so the server lists it right alongside Dave's problem. Two errors, two <report_error/> elements, zero chill πŸ’€

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <report_error
      error_code="no_object_matching_hash"
      tag="Dave">
    <failed_pdu>
      <withdraw
          hash="421ee4ac65732d72"
          tag="Dave"
          uri="rsync://wombat.example/Dave/421ee4ac65732d72.cer"/>
    </failed_pdu>
  </report_error>
  <report_error
      error_code="object_already_present"
      tag="Eve">
    <failed_pdu>
      <publish
          tag="Eve"
          uri="rsync://wombat.example/Eve/9dd859b01e5c2ebd.cer">
      SGVsbG8sIG15IG5hbWUgaXMgRXZl
    </publish>
    </failed_pdu>
  </report_error>
</msg>

3.8. <list/> Query πŸ“‹

this one is the "show me what you've got" query πŸ‘€ Just a <msg> with type="query" and an empty <list/> inside. No fuss, no extra arguments, just vibes 🧚

<msg
    type="query"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <list/>
</msg>

3.9. <list/> Reply 🌸

and here's the answer! πŸ’– The reply gives back one <list/> element for each object in the repository, and every one carries its own hash and uri. In this example there are four objects: Fee, Fie, Foe, and Fum. Honestly the naming is so iconic I can't 😭

<msg
    type="reply"
    version="4"
    xmlns="http://www.hactrn.net/uris/rpki/publication-spec/">
  <list
      hash="eb719b72f0648cf4"
      uri="rsync://wombat.example/Fee/eb719b72f0648cf4.cer"/>
  <list
      hash="c7c50a68b7aa50bf"
      uri="rsync://wombat.example/Fie/c7c50a68b7aa50bf.cer"/>
  <list
      hash="f222481ded47445d"
      uri="rsync://wombat.example/Foe/f222481ded47445d.cer"/>
  <list
      hash="15b94e08713275bc"
      uri="rsync://wombat.example/Fum/15b94e08713275bc.cer"/>
</msg>

4. IANA Considerations πŸ“

okay bestie, this is the paperwork part, but make it cute πŸŽ€ IANA has registered a media type called "application/rpki-publication", and here is the whole registration, copied exactly as it was filed πŸ’…

IANA has registered the "application/rpki-publication" media type as
follows:

   Type name:  application
   Subtype name:  rpki-publication
   Required parameters:  None
   Optional parameters:  None
   Encoding considerations:  binary
   Security considerations:  Carries an RPKI publication protocol
      message, as defined in RFC 8181.
   Interoperability considerations:  None
   Published specification:  RFC 8181
   Applications which use this media type: HTTP
   Additional information:
      Magic number(s):  None
      File extension(s):  None
      Macintosh File Type Code(s):  None
   Person & email address to contact for further information:
      Rob Austein <sra@hactrn.net>
   Intended usage:  COMMON
   Author/Change controller: IETF

5. Security Considerations πŸ”

okay, this section is serious, so no giggling (well, maybe a little πŸ™ˆ) But I'm still going to explain it in a way you'll actually remember πŸ’œ

The RPKI publication protocol and the data it publishes use completely separate PKIs for authentication. The published data gets authenticated within the RPKI, and this protocol has nothing to do with that. It also doesn't require the published objects to be valid in the RPKI. The publication protocol uses its own separate BPKI to authenticate its messages πŸ¦‹

Every RPKI publication protocol message gets wrapped in a signed CMS message. That gives us message integrity protection and an auditable form of message authentication, which is so important 🫢 Because of those protections at the application layer, and because all the data being published is meant to be public information anyway, this protocol does not, strictly speaking, need HTTPS or other transport security mechanisms. There may still be situations where a particular publication operator would rather use HTTPS over HTTP, as a matter of (BPKI) CA policy. Whether it's HTTP or HTTPS is, essentially, a private decision between the repository operator and its clients 🀫 Just note that even if a client/server pair uses HTTPS for this protocol, message authentication is still based on the CMS signatures, not HTTPS.

Now the hashes in the <publish/> and <withdraw/> PDUs are cryptographically strong. The digest algorithm was picked for convenience, so these hashes can be compared with the hashes that show up in RPKI manifests 🧁 The hashes used in those PDUs aren't particularly security sensitive, since the PDUs are protected by the CMS signatures. Because of that, the most likely reason to change the digest algorithm would be to follow a matching change in the digest algorithm used in RPKI manifests. If and when that change happens, it will require incrementing the version number of this publication protocol. Still, since the most likely implementation of a publication server uses these hashes as lookup keys in a database, bumping the version number would be a pretty small part of the work of changing the algorithm 🌈

Compromise of a publication server, perhaps through mismanagement of BPKI private keys, could lead to a denial-of-service attack on the RPKI. An attacker who gets hold of BPKI private keys could use this protocol to delete (withdraw) RPKI objects, which could lead to routing changes or failures. 😱 So, as in most PKIs, good key management practices are important, bestie. Seriously. Protect those keys πŸ”‘


6. References πŸ“š

6.1. Normative References

6.2. Informative References


Acknowledgements 🫢

the authors want to thank everyone who helped out: Geoff Huston, George Michaelson, Oleg Muravskiy, Paul Wouters, Randy Bush, Rob Loomans, Robert Kisteleki, Tim Bruijnzeels, and Tom Petch. They also thank anybody else who helped along the way but whose name(s) the authors have temporarily forgotten πŸ’– Main character energy for the whole squad 🌟


Authors' Addresses πŸ’Œ

Samuel Weiler W3C / MIT Email: weiler@csail.mit.edu

Anuja Sonalker STEER Tech Email: anuja@steer-tech.com

Rob Austein Dragon Research Labs Email: sra@hactrn.net

πŸ“¦ where this page comes from