RFC 5848
Signed Syslog Messages
RFC 5848: Signed Syslog Messages ๐
Okay bestie, this is RFC 5848, called Signed Syslog Messages ๐ฆ It's a Standards Track doc from the IETF, dated May 2010, with ISSN 2070-1721. Three legends wrote it: J. Kelsey from NIST, J. Callas from PGP Corporation, and A. Clemm from Cisco Systems. Your girl is obsessed ๐ญ It doesn't list anything it obsoletes or updates.
Abstract ๐ธ
This doc describes a way to add origin authentication, message integrity, replay resistance, message sequencing, and detection of missing messages to the syslog messages that get sent. It's meant to be used together with the stuff defined in RFC 5424, "The Syslog Protocol" ๐
Status of This Memo ๐
So this is an Internet Standards Track document, no cap ๐ฅ
It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 5741 โจ
For the current status, any errata, and how to give feedback, go to http://www.rfc-editor.org/info/rfc5848 ๐ง
Copyright Notice โ๏ธ
Copyright (c) 2010 IETF Trust and the persons identified as the document authors. All rights reserved. ๐
This doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as they stand on the publication date. Please actually read these, because they spell out your rights and restrictions for this document ๐ Any Code Components pulled out of this document must include the Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License.
This doc may contain material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008. The people controlling the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. So unless you get an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process. The exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐
Table of Contents ๐ง
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Conventions Used in This Document . . . . . . . . . . . . . . 5
3. Syslog Message Format . . . . . . . . . . . . . . . . . . . . 5
4. Signature Blocks . . . . . . . . . . . . . . . . . . . . . . . 6
4.1. Syslog Messages Containing a Signature Block . . . . . . . 7
4.2. Signature Block Format and Fields . . . . . . . . . . . . 7
4.2.1. Version . . . . . . . . . . . . . . . . . . . . . . . 9
4.2.2. Reboot Session ID . . . . . . . . . . . . . . . . . . 10
4.2.3. Signature Group and Signature Priority . . . . . . . . 10
4.2.4. Global Block Counter . . . . . . . . . . . . . . . . . 13
4.2.5. First Message Number . . . . . . . . . . . . . . . . . 13
4.2.6. Count . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2.7. Hash Block . . . . . . . . . . . . . . . . . . . . . . 14
4.2.8. Signature . . . . . . . . . . . . . . . . . . . . . . 14
4.2.9. Example . . . . . . . . . . . . . . . . . . . . . . . 15
5. Payload and Certificate Blocks . . . . . . . . . . . . . . . . 15
5.1. Preliminaries: Key Management and Distribution Issues . . 15
5.2. Payload Block . . . . . . . . . . . . . . . . . . . . . . 16
5.2.1. Block Format and Fields . . . . . . . . . . . . . . . 16
5.2.2. Signer Authentication and Authorization . . . . . . . 18
5.3. Certificate Block . . . . . . . . . . . . . . . . . . . . 19
5.3.1. Syslog Messages Containing a Certificate Block . . . . 19
5.3.2. Certificate Block Format and Fields . . . . . . . . . 20
6. Redundancy and Flexibility . . . . . . . . . . . . . . . . . . 24
6.1. Configuration Parameters . . . . . . . . . . . . . . . . . 24
6.1.1. Configuration Parameters for Certificate Blocks . . . 24
6.1.2. Configuration Parameters for Signature Blocks . . . . 26
6.2. Overlapping Signature Blocks . . . . . . . . . . . . . . . 27
7. Efficient Verification of Logs . . . . . . . . . . . . . . . . 27
7.1. Offline Review of Logs . . . . . . . . . . . . . . . . . . 28
7.2. Online Review of Logs . . . . . . . . . . . . . . . . . . 29
8. Security Considerations . . . . . . . . . . . . . . . . . . . 32
8.1. Cryptographic Constraints . . . . . . . . . . . . . . . . 32
8.2. Packet Parameters . . . . . . . . . . . . . . . . . . . . 33
8.3. Message Authenticity . . . . . . . . . . . . . . . . . . . 33
8.4. Replaying . . . . . . . . . . . . . . . . . . . . . . . . 33
8.5. Reliable Delivery . . . . . . . . . . . . . . . . . . . . 34
8.6. Sequenced Delivery . . . . . . . . . . . . . . . . . . . . 34
8.7. Message Integrity . . . . . . . . . . . . . . . . . . . . 34
8.8. Message Observation . . . . . . . . . . . . . . . . . . . 34
8.9. Man-in-the-Middle Attacks . . . . . . . . . . . . . . . . 34
8.10. Denial of Service . . . . . . . . . . . . . . . . . . . . 35
8.11. Covert Channels . . . . . . . . . . . . . . . . . . . . . 35
9. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 35
9.1. Structured Data and Syslog Messages . . . . . . . . . . . 35
9.2. Version Field . . . . . . . . . . . . . . . . . . . . . . 36
9.3. SG Field . . . . . . . . . . . . . . . . . . . . . . . . . 38
9.4. Key Blob Type . . . . . . . . . . . . . . . . . . . . . . 38
10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 39
11. References . . . . . . . . . . . . . . . . . . . . . . . . . . 39
11.1. Normative References . . . . . . . . . . . . . . . . . . . 39
11.2. Informative References . . . . . . . . . . . . . . . . . . 40
1. Introduction โจ
Okay so this doc describes a mechanism, called syslog-sign in here, that adds origin authentication, message integrity, replay resistance, message sequencing, and detection of missing messages to syslog ๐ The trick is that it's accomplished by sending a special syslog message. The content of that message is called a Signature Block. Each Signature Block basically holds a detached signature on some number of previously sent messages. It's cryptographically signed and contains the hashes of previously sent syslog messages ๐ The originator of syslog-sign messages is just called a "signer". The signer can be the same originator whose messages it signs, or it can be a separate originator ๐ฆ
Most syslog setups have just one originator and one collector for each message, but we need to plan for when messages go to multiple collectors ๐ This is especially true when different messages from the same originator go to different collectors, so some messages reach some collectors but not others. The sorting is usually done by the Priority value of each message. For example, messages from any Facility with a Severity value of 3, 2, 1, or 0 may go to one collector, while all messages of Facilities 4, 10, 13, and 14 may go to another collector ๐ธ The matching syslog-sign messages must be kept with their proper syslog messages, so syslog-sign uses a Signature Group. A Signature Group is a group of messages that all get kept together for signing purposes by the signer. A Signature Block always belongs to exactly one Signature Group, and it always signs only messages belonging to that Signature Group ๐
On top of that, a signer sends Certificate Blocks to share key management info between the signer and the collector ๐ง A Certificate Block has a field that says what kind of key material it is. That could be a Public Key Infrastructure using X.509 (PKIX) certificate, an OpenPGP (Pretty Good Privacy) certificate, or even just an indication that a key was pre-distributed. When certificates are being sent, they may have to be split across multiple Certificate Blocks carried in separate messages ๐
It's possible that the same host has multiple signers, each using their own keys to sign syslog messages. In that case, each signer sends its own Certificate Block and Signature Blocks, and each signer defines its own Signature Groups ๐ Each signer on a given host needs to use a distinct combination of APP-NAME, and PROCID for its Signature Block and Certificate Block message. (This implies that the combination of HOSTNAME, APP-NAME, and PROCID uniquely distinguishes originators of syslog-sign messages across hosts, provided that the signers use a unique HOSTNAME.) ๐
The collector may check that the hash of each received message matches the signed hash in the corresponding Signature Block. It can process these Signature Blocks as they arrive and build an authenticated log file, or it can store all the log messages in the order they were received. Either way, a network operator can authenticate the log file when reviewing the logs ๐ฅ
The signing process works as long as the collector accepts the syslog messages, the Certificate Blocks, and the Signature Blocks. Once that's done, the process is complete. After that, anyone can go back, find the key material, and validate the received messages using the info in the Signature Blocks ๐ Finding the key material is super easy with Key Blob Types C, P, and K (see Section 4.2), since the public key is in the Payload Block. If Key Blob Types N or U are used, some poking around may be needed to find the key material. The only way to have a vendor-specific implementation is through N or U, but even then, the key material has to be available in some form that other vendors' implementations could use ๐
Because this spec uses the concept of STRUCTURED-DATA elements defined in [RFC5424], compliant implementations of this specification MUST also implement [RFC5424]. The concepts underlying this spec could also be used with other message-delivery mechanisms, so designers of other event notification mechanisms are encouraged to consider this specification in their designs ๐ซถ
2. Conventions Used in This Document ๐
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] ๐ธ
3. Syslog Message Format โจ
Okay so this bit is the bridge between the syslog stuff you already know and the signing magic. This spec is meant to be used together with the syslog protocol from [RFC5424], so the syslog protocol MUST be supported by implementations of this specification. No skipping it bestie ๐
The signer (the originator generating the Signature Block message, aka just "signer") signs each message in its entirety. So the messages MUST NOT be changed in transit. The syslog-sign messages MUST NOT be changed in transit either ๐ โโ๏ธ If anything alters a message along the way, including a relay messing with it, the signature becomes invalid and the whole mechanism is useless. Truncation is the same disaster. If a message gets chopped between sending and receiving, the mechanism is useless again ๐
So to keep truncation from happening, syslog signer and collector implementations implementing this spec MUST support messages of up to and including 2048 octets in length. They MAY support longer messages too, but implementers need to know that any truncation that happens renders the mechanism useless. In that case it's on the operator to make sure the syslog messages can be received properly and validated ๐ซถ
[RFC5426] recommends using the Transport Layer Security (TLS) transport and deliberately constrains the use of UDP. UDP is NOT RECOMMENDED for signed syslog, and here's why it's giving no ๐ The recommended UDP payload size is 480 octets, which is way too restrictive for syslog-sign. A 480-octet Signature Block could only sign 9 normal messages, so a significant proportion of messages would end up being Signature Block messages instead of the actual logs. The 480-octet limit is mainly for small embedded systems with serious resource constraints, which wouldn't implement syslog-sign in the first place anyway. Plus, UDP is more about troubleshooting messages, which is a totally different vibe from what syslog-sign is for ๐ฅ If you do use syslog UDP transport anyway, operators have to make sure network paths are set up so messages of sufficient length (up to and including 2048 octets) can be delivered properly.
This spec uses the syslog message format from [RFC5424]. That document describes Structured Data (SD), which is defined in terms of SD ELEMENTS (SDEs). An SDE is a name plus a set of parameter name-value pairs. The SDE name is called the SD-ID. The name-value pairs are called SD-PARAM (or SD Parameters), where the name is the SD-PARAM-NAME and the value is the SD-PARAM-VALUE ๐ฆ
The syslog messages in this document carry the data tied to Signature Blocks and Certificate Blocks as Structured Data. So this spec defines special SDEs to convey parameters related to signing syslog messages โจ The MSG part of these messages SHOULD simply be empty. The content isn't meant for humans to read. It's for apps that use the messages to build an authenticated log ๐ง
Since these messages follow the format from [RFC5424], they identify the machine that originated the message in the HOSTNAME field. That means the Signature Block and Certificate Block data don't need any extra parameter to identify the machine. Iconic efficiency ๐
Now the multi-signer tea โ Several signers MAY sign messages on a single host independently of each other, each using their own Signature Groups. In that case, each unique signer is distinguished by the combination of APP-NAME and PROCID. (And yes, the same message might get signed by multiple signers ๐ญ) Each unique signer MUST have a unique APP-NAME and PROCID on each host. Lowkey this means HOSTNAME, APP-NAME and PROCID together uniquely pin down the originator of syslog-sign messages, as long as the signers use a unique HOSTNAME. Finally, a Signature Block message MUST use the same combination of HOSTNAME, APP-NAME, and PROC-ID that was used to send the corresponding Certificate Block messages containing the Payload Block. Matching energy, always ๐
4. Signature Blocks ๐
This section is where we get into the actual Signature Block: its format, its fields, and the syslog messages that carry it. Let's go!
4.1. Syslog Messages Containing a Signature Block ๐ธ
We need a way to tell apart the Signature Block itself and the syslog message that carries it, so here's the deal. Signature Blocks MUST be encompassed within completely formed syslog messages. Syslog messages that contain a Signature Block are also called Signature Block messages.
A Signature Block message is identified by an SD ELEMENT whose SD-ID is "ssign". On top of that, a Signature Block message MUST contain valid APP-NAME, PROCID, and MSGID fields to be compliant with [RFC5424]. This spec doesn't mandate particular values for those fields. However, for consistency, a signer MUST use the same values for APP-NAME, PROCID, and MSGID for every Signature Block message it sends, whatever values were chosen. The signer MUST also use the same value for its HOSTNAME field. To allow multiple signers per host, the combination of APP-NAME and PROCID MUST be unique for each such signer on any given host ๐
If a signer daemon restarts, it MAY use a new PROCID for what is otherwise the same signer. But it MUST continue to use the same APP-NAME. If it does pick a new PROCID, it MUST send a new Payload Block using Certificate Block messages that use that same new PROCID (and the same APP-NAME). It is RECOMMENDED (but not required) to use 110 as the value for the PRI field, which corresponds to facility 13 (log audit) and severity 6 (informational) โจ The Signature Block is carried as Structured Data within the Signature Block message, per the definitions in the next section. A Signature Block message MAY carry other Structured Data besides the Structured Data of the Signature Block itself. The MSG part of a Signature Block message SHOULD be empty ๐
The syslog messages defined as part of syslog-sign itself (Signature Block messages and Certificate Block messages) MUST NOT be signed by a Signature Block. Collectors that implement syslog-sign know how to tell apart the syslog-sign messages from the ones being signed, and they process them differently. The whole point of syslog-sign is to sign a stream of syslog messages, not to alter it ๐ฆ
4.2. Signature Block Format and Fields ๐ง
The content of a Signature Block message is the Signature Block itself. The Signature Block MUST be encoded as an SD ELEMENT, as defined in [RFC5424].
The SD-ID MUST have the value of "ssign" ๐
The SDE holds the fields of the Signature Block, encoded as SD Parameters, as laid out below. The Signature Block is made up of these fields. The value of each field MUST be printable ASCII. Any binary values MUST be base64 encoded, as defined in [RFC4648] ๐
Field SD-PARAM-NAME Size in octets
----- ------------- ---- -- ------
Version VER 4
Reboot Session ID RSID 1-10
Signature Group SG 1
Signature Priority SPRI 1-3
Global Block Counter GBC 1-10
First Message Number FMN 1-10
Count CNT 1-2
Hash Block HB variable, size of hash
times the number of hashes
(base64 encoded binary)
Signature SIGN variable
(base64 encoded binary)
The fields MUST be provided in the order listed. Each SD parameter MUST occur once and only once in the Signature Block. New SD parameters MUST NOT be added unless a new Version of the protocol is defined. (If implementations want to add proprietary extensions, they'll need to define a separate SD ELEMENT ๐) So a Signature Block is encoded like this, where xxx is a placeholder for the particular values:
[ssign VER="xxx" RSID="xxx" SG="xxx" SPRI="xxx" GBC="xxx" FMN="xxx"
CNT="xxx" HB="xxx" SIGN="xxx"]
Values of the fields are SD parameter values, so they get enclosed in quotes, per [RFC5424]. The fields are separated by single spaces, and each one is described in the subsections that follow ๐
4.2.1. Version ๐ฆ
The Version field is an alphanumeric value with a length of 4 octets, and it may include leading zeroes. The first 2 octets and the last octet each contain a decimal character in the range "0" to "9". The third octet contains an alphanumeric character in the range "0" to "9", "a" to "z", or "A" to "Z". The value in this field specifies the version of the syslog-sign protocol. It's extensible, so different hash algorithms and signature schemes can be used in the future. The value of this field is the grouping of the protocol version (2 octets), the hash algorithm (1 octet), and the signature scheme (1 octet) ๐
- Protocol Version - 2 octets, with
"01"as the value for the protocol version described in this document. - Hash Algorithm - 1 octet. In conjunction with Protocol Version 01, a value of
"1"denotes SHA1 and a value of"2"denotes SHA256, as defined in [FIPS.180-2.2002]. (This is the octet that can have a value of not just "0" to "9" but also "a" to "z" and "A" to "Z" ๐) - Signature Scheme - 1 octet. In conjunction with Protocol Version 01, a value of
"1"denotes OpenPGP DSA, defined in [RFC4880] and [FIPS.186-2.2000].
The version, hash algorithm, and signature scheme defined in this document would accordingly be represented as "0111" (if SHA1 is used as Hash Algorithm) and "0121" (if SHA256 is used as Hash Algorithm), respectively (without the quotation marks) โจ
The values of the Hash Algorithm and Signature Scheme are defined relative to the Protocol Version. If the single-octet representation of those values ever became a limitation, it could be overcome by defining a new Protocol Version with additional Hash Algorithms and/or Signature Schemes, with implementations supporting both Protocol Versions concurrently. Future-proof queen ๐
As long as the sender and receiver both adhere to [RFC5424], the prerequisites are in place so that signed messages can be received by the receiver and validated with a Signature Block. To ensure immediate validation of received messages, all implementations MUST support SHA1, and SHA256 SHOULD be supported. Don't skip SHA1, bestie ๐ญ๐
4.2.2. Reboot Session ID ๐
okay so the Reboot Session ID is a decimal value that's between 1 and 10 octets long ๐ The acceptable values are between 0 and 9999999999. Leading zeroes MUST be omitted ๐
the Reboot Session ID is expected to strictly monotonically increase (aka it never repeats or goes down) every time a signer reboots ๐ฅ that's how collectors can tell messages and message signatures apart across reboots ๐ there are a few ways to make this happen. one way is that the Reboot Session ID goes up by 1, starting at 1 ๐ฆ in this case, a signer is required to keep the previous Reboot Session ID across reboots ๐ซถ another way is to use the Unix time (the number of seconds since 1 January 1970) โฐ if you go with this one, implementers need to watch out for multiple reboots happening within a single second ๐ implementers also need to be aware of the year 2038 problem, which will make the 32-bit representation of Unix time wrap in the year 2038 ๐ and then there's the option where the Simple Network Management Protocol (SNMP) engine and the signer always reboot at the same time ๐ those implementations might consider using the snmpEngineBoots value as a source for this counter, as defined in [RFC3414] โจ
in cases where a signer can't guarantee that the Reboot Session ID always goes up after a reboot, the Reboot Session ID MUST always be set to a value of 0 ๐ญ if the value can't go any higher (for example, because it reaches 9999999999), it SHOULD be reset to a value of 1 ๐ง implementations SHOULD make sure that a reset like this doesn't go undetected, for example by asking the operator to acknowledge it when a reset happens upon reboot ๐ (operator acknowledgment might not be possible in every situation, like with embedded devices, so no stress if that's you ๐)
if a reboot of a signer happens, Signature Block messages MAY use a new PROCID ๐ธ however, Signature Block messages from the same signer MUST keep using the same HOSTNAME, APP-NAME, and MSGID ๐
4.2.3. Signature Group and Signature Priority ๐
the SG parameter can take any value from 0-3 inclusive, and the SPRI parameter can take any value from 0-191 inclusive ๐ these fields work together so network admins can group syslog messages with the right Signature Blocks and Certificate Blocks ๐ซถ groupings of syslog messages that get signed together are called Signature Groups ๐ and a Signature Block only contains hashes of the messages that are part of the same Signature Group โจ
for example, admins might have originators send syslog messages from Facilities 0 through 15 to one collector and the ones from Facilities 16 through 23 to another ๐ฆ in that setup, the matching Signature Blocks should probably go to the corresponding collectors too, with each collector's messages signed separately ๐ฅ that way, each collector gets Signature Blocks for all the syslog messages it receives, and only for those ๐ being able to link different kinds of syslog messages to different Signature Groups (signed in separate Signature Blocks) gives admins a lot of flexibility here ๐
syslog-sign has four options for handling Signature Groups, linking them with PRI values so they get routed to the destination that matches the syslog messages ๐ in every case, no more than 192 distinct Signature Groups (0-191) are permitted ๐
the Signature Group that a Signature Block is about is shown by the Signature Priority (SPRI) field โญ the Signature Group (SG) field tells you how to read the Signature Priority field ๐ง (fun fact: the SG field doesn't actually indicate the Signature Group itself, even though the name kinda suggests it does ๐) the SG field can have one of these values:
- a.
"0"-- there's only one Signature Group ๐ in this case, admins want all Signature Blocks sent to one destination, and most likely all the syslog messages are headed there too ๐ซถ Signature Blocks contain signatures for all messages no matter their PRI value ๐ so basically, the Signature Block's SPRI value can be ignored ๐คทโโ๏ธ however, it is RECOMMENDED that a single SPRI value be used for all Signature Blocks โจ and furthermore, it is RECOMMENDED to set that value to the same value as the PRI field of the Signature Block message ๐ that way the PRI of the Signature Block message matches the SPRI of the Signature Block it contains ๐ - b.
"1"-- each PRI value gets its own Signature Group ๐ฆ Signature Blocks for a given Signature Group have SPRI = PRI for that Signature Group ๐ฅ so the SPRI of the Signature Block matches the PRI value of the syslog messages in the Signature Group (and that are therefore signed by the Signature Block) ๐ an SG value of 1 can be used when the admin of a signer doesn't know where any of the syslog messages will end up, but expects messages with different PRI values to be collected and processed separately ๐ having a Signature Group per PRI value gives admins a ton of flexibility in how they divide up processing of syslog messages and their signatures after they're received, while still letting Signature Blocks follow their syslog messages to the final destination ๐ค - c.
"2"-- each Signature Group contains a range of PRI values ๐ Signature Groups are assigned sequentially ๐ง a Signature Block for a given Signature Group has its own SPRI value, which is the highest PRI value of the syslog messages in that Signature Group ๐ธ the lowest PRI value in that Signature Group will be 1 larger than the SPRI value of the previous Signature Group, or"0"if there's no other Signature Group with a lower SPRI value ๐ the specific Signature Groups and the ranges they're tied to are set by a system administrator ๐ซถ - d.
"3"-- Signature Groups aren't assigned any of the relationships to PRI values above ๐ instead, another scheme is used, and that scheme is outside the scope of this specification โจ there has to be some predefined arrangement between the originator and the intended collectors about which syslog messages go in which Signature Group, and a system administrator has to configure it ๐ง this also lets admins group syslog messages by criteria that aren't tied to the PRI value ๐ but this option isn't meant for deployments that lack such an arrangement, because in those cases a collector could misread the intended meaning of the Signature Group ๐ฌ a collector that receives Signature Block messages for a Signature Group whose scheme it doesn't know SHOULD bring this to the attention of the system administrator ๐จ the mechanism for doing that is implementation-specific and outside the scope of this specification ๐
one reasonable way to set up some installations is to have only one Signature Group, marked SG=0, and have the signer send a copy of each Signature Block to each collector ๐ in that case, collectors that aren't configured to receive every syslog message will still get signatures for every message, even the ones they aren't supposed to get ๐ the collector won't be able to detect gaps in the messages (since a signature for a missing message doesn't tell it whether that message would've been its concern) ๐ฅฒ but it does let all the messages that do arrive at each collector be put into the right order and verified โ it also lets each collector detect duplicates ๐ฏโโ๏ธ likewise, configuring only one Signature Group can be a reasonable choice for installations with relay chains, where one or more interim relays may or may not relay all messages to the same destination ๐ฆ
4.2.4. Global Block Counter ๐ข
the Global Block Counter is a decimal value showing the number of Signature Blocks that syslog-sign sent before the current one, in this reboot session โจ it takes at least 1 octet and at most 10 octets, displayed as a decimal counter ๐ the acceptable values are between 0 and 9999999999, starting at 0 ๐ธ leading zeroes MUST be omitted ๐ โโ๏ธ if the Global Block Counter has reached 9999999999 and the Reboot Session ID has a value other than 0 (which means persistence of the Reboot Session ID is supported), then the Reboot Session ID MUST be incremented by 1 and the Global Block Counter resumes at 0 ๐ฅ when the Reboot Session ID is 0 (aka persistent Reboot Session IDs aren't supported) and the Global Block Counter hits its maximum value, the Global Block Counter gets reset to 0 and the Reboot Session ID MUST remain at 0 ๐ค
the Global Block Counter crosses Signature Groups ๐ it lets you roughly sync up when two messages were sent, even if they went to different collectors and belong to different Signature Groups ๐ซถ
because a reboot starts a new reboot session, the signer MUST reset the Global Block Counter to 0 after a reboot occurs ๐ applications need to take into account that a reboot might have happened when they're authenticating a log ๐ and if reboots happen a lot, you might lose the ability to verify the proper sequence the messages were sent in, which puts the integrity of the log at risk ๐ฑ
4.2.5. First Message Number 1๏ธโฃ
this is a decimal value between 1 and 10 octets, with leading zeroes omitted ๐ it contains the unique message number, within this Signature Group, of the first message whose hash appears in this block ๐ the very first message of the reboot session is numbered "1" ๐ฆ this means that when the Reboot Session ID goes up, the message number gets reset to 1 ๐
for example, if this Signature Group has processed 1000 messages so far, and message number 1001 is the first message whose hash appears in this Signature Block, then this field contains 1001 โจ the message number is relative to the Signature Group it belongs to, so a message number doesn't identify a message beyond its Signature Group ๐
should the message number reach 9999999999 within the same reboot session and Signature Group, the message number restarts at 1 ๐ณ in that situation, the Global Block Counter will be vastly different between two occurrences of the same message number ๐
4.2.6. Count ๐งฎ
the count is a 1- or 2-octet field that shows the number of message hashes to follow ๐ the valid values for this field are 1 through 99 ๐ธ the number of hashes included in the Signature Block MUST be chosen so that the length of the resulting syslog message does not exceed the maximum permissible syslog message length ๐ซถ
4.2.7. Hash Block ๐งบ
okay so the hash block is basically a basket of hashes ๐งบ, and each one gets its own separate base64 encoding ๐ Every hash is the hash of the entire syslog message it stands for, and it doesn't care what transport that message rode in on ๐. The hashes line up from left to right in the same order the syslog messages happened, and a space character separates them โจ. Note, the hash block counts as ONE single SD-PARAM, so a Signature Block message MUST include all of its hashes in a single hash block and MUST NOT spread them across several hash blocks ๐ โโ๏ธ no splitting the squad!
The "entire syslog message" means the syslog message as it's described, minus the transport parts described in [RFC5425] and [RFC5426], and minus any other parts that might get defined for future transports ๐ฎ. The hash value comes from running the hashing algorithm across the syslog message, starting at the "<" of the PRI part of the header part ๐งฎ. The hash algorithm (the one indicated by the Version field) decides how big each hash is, but the size MUST NOT be shorter than 160 bits without the use of padding. After that, it gets base64 encoded as per [RFC4648] ๐
The number of hashes in a hash block SHOULD be chosen so the resulting Signature Block message does not exceed a length of 2048 octets, which helps dodge any chance of truncation ๐ When there are more hashes to send than fit inside one Signature Block message, it is advisable to start a new Signature Block ๐ฆ
4.2.8. Signature โ๏ธ
This is a digital signature, encoded in base64 per [RFC4648] ๐ It's calculated over the completely formatted Signature Block message, starting from the first octet of PRI and running through the last octet of MSG (or STRUCTURED-DATA if MSG isn't there). This happens before the SIGN parameter gets added. The SIGN parameter is made of the SD Parameter Name, the space right before it (" SIGN"), the =, and the corresponding value.
In other words, you sign the whole message while pretending the SIGN=value portion isn't there yet ๐ซฃ For the OpenPGP DSA signature scheme, the value of the signature field holds the DSA values r and s, each encoded as a multiprecision integer (see [RFC4880], Sections 5.2.2 and 3.2). Those get concatenated, and then the whole thing is encoded in base64 [RFC4648] ๐ง
4.2.9. Example ๐ธ
Here's an example of a Signature Block message, broken into lines so it fits publication rules. There's a space at the end of each line, except the last line, which ends with "]" ๐
<110>1 2009-05-03T14:00:39.529966+02:00 host.example.org syslogd
2138 - [ssign VER="0111" RSID="1" SG="0" SPRI="0" GBC="2" FMN="1"
CNT="7" HB="K6wzcombEvKJ+UTMcn9bPryAeaU= zrkDcIeaDluypaPCY8WWzwHpPok=
zgrWOdpx16ADc7UmckyIFY53icE= XfopJ+S8/hODapiBBCgVQaLqBKg=
J67gKMFl/OauTC20ibbydwIlJC8= M5GziVgB6KPY3ERU1HXdSi2vtdw=
Wxd/lU7uG/ipEYT9xeqnsfohyH0="
SIGN="AKBbX4J7QkrwuwdbV7Taujk2lvOf8gCgC62We1QYfnrNHz7FzAvdySuMyfM="]
So what's going on here? The message is syslog-sign protocol version "01" ๐ It uses SHA1 as its hash algorithm, plus an OpenPGP DSA signature scheme. Its reboot session ID is 1. Its Signature Group is 0, which means all syslog messages go to the same destination. Its Signature Priority is 0 (which can effectively be ignored, because all syslog messages get signed no matter their PRI value) ๐ Its Global Block Counter is 2. The first message number is 1, and the message contains 7 message hashes ๐
5. Payload and Certificate Blocks ๐
Certificate Blocks and Payload Blocks handle key management for syslog-sign ๐ Their whole purpose is to support key management that uses public key cryptosystems ๐ฆ
5.1. Preliminaries: Key Management and Distribution Issues ๐๏ธ
So a Payload Block carries public-key-certificate information that needs to be conveyed to the collector ๐ฌ It gets sent at the beginning of a new reboot session, carrying the public key information in effect for that reboot session. But a Payload Block is never sent directly. It goes out in (one or more) fragments, and those fragments are called Certificate Blocks. That means signers send at least one Certificate Block at the beginning of a new reboot session โจ
There are three key points to understand about Certificate Blocks:
a. They handle a variable-sized payload, fragmenting it if necessary and transmitting the fragments as legal syslog messages ๐งฉ This payload is built (as described below) at the beginning of a reboot session and is sent in pieces, with each Certificate Block carrying one piece. There is exactly one Payload Block per reboot session.
b. The Certificate Blocks are digitally signed. The signer doesn't sign the Payload Block itself, but the signatures on the Certificate Blocks make sure its authenticity holds up ๐ก๏ธ Note that it may not even be possible to verify the signature on the Certificate Blocks without the information in the Payload Block. In that case, the Payload Block is reconstructed, the key is extracted, and then the Certificate Blocks get verified. (This is necessary even when the Payload Block carries a certificate, because some other fields of the Payload Block are not otherwise verified.) In practice, most installations keep the same public key over long periods of time, so most of the time it's easy to verify the signatures on the Certificate Blocks and use the Payload Block for other useful per-session information ๐
c. The kind of Payload Block that's expected is determined by what kind of key material is on the collector that receives it ๐ The signer and the collector (or offline log viewer) both have some key material (such as a root public key or a pre-distributed public key) and an acceptable value for the Key Blob Type in the Payload Block, described below. The collector or offline log viewer MUST NOT accept a Payload Block of the wrong type ๐ซ
5.2. Payload Block ๐ฆ
The Payload Block gets built when a new reboot session starts. There's a one-to-one correspondence between reboot sessions and Payload Blocks. A signer creates a new Payload Block after each reboot, and that Payload Block stays in use until the next reboot ๐
5.2.1. Block Format and Fields ๐งฉ
A Payload Block MUST have the following fields:
a. Full local timestamp for the signer at the time the reboot session started. This has to be in the timestamp format specified in [RFC5424] (basically timestamp format per [RFC3339], with some further restrictions) โฐ
b. Key Blob Type, a one-octet field containing one of five values:
- 'C' -- a PKIX certificate (per [RFC5280]).
- 'P' -- an OpenPGP KeyID and OpenPGP certificate (a Transferable Public Key as defined in [RFC4880], Section 11.1). The first 8 octets of the key blob field contain the OpenPGP KeyID (identifying which key or subkey inside the OpenPGP certificate is used), followed by the OpenPGP certificate itself ๐
- 'K' -- the public key whose corresponding private key is being used to sign these messages. For the OpenPGP DSA signature scheme, the key blob field contains the DSA prime p, DSA group order q, DSA group generator g, and DSA public-key value y, encoded as 4 multiprecision integers (see [RFC4880], Sections 5.5.2 and 3.2) ๐งฎ
- 'N' -- no key information sent; key is pre-distributed.
- 'U' -- installation-specific key exchange information.
c. The key blob, if there is one, base64 encoded per [RFC4648] and consisting of the raw key data ๐ซง
The fields are separated by single space characters. Because a Payload Block is never carried in a syslog message directly (only the corresponding Certificate Blocks are), it doesn't need to be encoded as an SD ELEMENT ๐โโ๏ธ The Payload Block also doesn't have a field that identifies the reboot session. Instead, the reboot session can be inferred from the Reboot Session ID parameter of the Certificate Blocks that carry the Payload Block ๐
To make sure the sender and receiver have at least one common Key Blob Type (so received messages can be validated right away), all implementations MUST support Key Blob Type "C" (PKIX certificate) ๐ค When a PKIX certificate is used ("C" Key Blob Type), it's the certificate specified in [RFC5280]. Per [RFC5425], syslog messages may be transported over the TLS protocol, even where there's no PKI. If that transport is used, the device already has a PKIX certificate, and it MAY use the private key associated with that certificate to sign messages ๐ In the case where there's no PKI, the chain of trust of a PKIX certificate must still be established to meet conventional security requirements. The methods for doing this are described in [RFC5425] ๐
5.2.2. Signer Authentication and Authorization โจ
Okay so when the collector gets a Payload Block, it needs to figure out whether the signatures are actually trustworthy ๐ต๏ธโโ๏ธ These are the methods that are in scope for this spec:
- a. X.509 certification path validation: The collector gets set up with one or more trust anchors (typically root Certification Authority (CA) certificates) ๐ณ. Those let it check that a binding between the subject name and the public key is legit. Certification path validation is done as specified in [RFC5280].
If the HOSTNAME contains a Fully-Qualified Domain Name (FQDN) or an IP address, it then gets compared against the certificate as described in [RFC5425], Section 5.2. Comparing other forms of HOSTNAMEs is beyond the scope of this spec.
Collectors SHOULD support this method. Ngl, due to message size restrictions, syslog-sign only sends the end-entity certificate in the Payload Block ๐ Depending on the PKI deployment, the collector may need to get intermediate certificates by other means (for example, from a directory) ๐๏ธ
- b. X.509 end-entity certificate matching: The collector is configured with the info it needs to identify the valid end-entity certificates of its valid peers, plus, for each peer, the HOSTNAME(s) it's authorized to use ๐
To keep things interoperable, collectors MUST support fingerprints of X.509 certificates as described below. Other methods MAY be supported.
Collectors MUST support Key Blob Type 'C', and configuring the list of valid peers using certificate fingerprints. The fingerprint is calculated and formatted as specified in [RFC5425], Section 4.2.2.
For each peer, the collector MUST support configuring a list of HOSTNAMEs that this peer is allowed to use, either as FQDNs or IP addresses. Other forms of HOSTNAMEs are beyond the scope of this spec.
If the locally configured FQDN is an internationalized domain name, conforming implementations MUST convert it to the ASCII Compatible Encoding (ACE) format for performing comparisons, as specified in Section 7 of [RFC5280]. An exact case-insensitive string match MUST be supported, but the implementation MAY also support wildcards of any type ("*", regular expressions, etc.) in locally configured names ๐ค
Signer implementations MUST provide a means to generate a key pair and self-signed certificate in the case that a key pair and certificate are not available through another mechanism, and MUST make the certificate fingerprint available through a management interface ๐
- c. OpenPGP V4 fingerprints: This is like X.509 fingerprints, except Key Blob Type 'P' is used, and the fingerprint is calculated as specified in [RFC4880], Section 12.2. When the fingerprint value is displayed or configured, each byte is represented in hexadecimal (using two uppercase ASCII characters), and a space is added after every second byte. For example:
"0830 2A52 2CD1 D712 6E76 6EEC 32A5 CAE1 03C8 4F6E"๐ง
Signers and collectors MAY support this method.
Other methods, such as "web of trust", are beyond the scope of this document ๐ โโ๏ธ
5.3. Certificate Block ๐
This section describes the format of the Certificate Block and the fields used within it, as well as the syslog messages used to carry Certificate Blocks ๐ฆ
5.3.1. Syslog Messages Containing a Certificate Block ๐
Certificate Blocks are used to get the Payload Block to the collector. Just like a Signature Block, each Certificate Block is carried in its own syslog message, called a Certificate Block message. If separate collectors are associated with different Signature Groups, Certificate Block messages need to be sent to each collector ๐ฌ
Because certificates can legitimately be much longer than 2048 octets, the Payload Block can be split up into several pieces, with each Certificate Block carrying a piece of the Payload Block ๐งฉ Note that the signer MAY make the Certificate Blocks any legal length (that is, any length that keeps the entire Certificate Block message within 2048 octets) that holds all the required fields. Software that processes Certificate Blocks MUST deal correctly with blocks of any legal length. The length of the fragment of the Payload Block that a Certificate Block carries MUST be at least one octet. The length SHOULD be chosen such that the length of the Certificate Block message does not exceed 2048 octets โ๏ธ
A Certificate Block message is identified by the presence of an SD ELEMENT with an SD-ID with the value "ssign-cert". On top of that, a Certificate Block message MUST contain valid APP-NAME, PROCID, and MSGID fields to be compliant with syslog protocol. Syslog-sign doesn't mandate particular values for these fields. However, for consistency, a signer MUST use the same value for APP-NAME, PROCID, and MSGID fields for every Certificate Block message, whichever values are chosen. It MUST also use the same value for its HOSTNAME field. To allow for the possibility of multiple signers per host, the combination of APP-NAME and PROCID MUST be unique for each such originator. If a signer daemon is restarted, it MAY use a new PROCID for what is otherwise the same signer. The combination of APP-NAME and PROCID MUST be the same that is used for Signature Block messages of the same signer; however, a different MSGID MAY be used for Signature Block and Certificate Block messages ๐ชช
It is RECOMMENDED to use 110 as the value for the PRI field, corresponding to facility 13 (log audit) and severity 6 (informational) ๐ The Certificate Block is carried as Structured Data within the Certificate Block message. A Certificate Block message MAY carry other Structured Data besides the Structured Data of the Certificate Block itself. The MSG part of a Certificate Block message SHOULD be empty ๐ซถ
5.3.2. Certificate Block Format and Fields ๐งพ
The contents of a Certificate Block message is the Certificate Block itself. Like a Signature Block, the Certificate Block is encoded as an SD ELEMENT. The SD-ID of the Certificate Block is "ssign-cert". The Certificate Block is made up of the following fields, each of which is encoded as an SD Parameter with the parameter name shown. Each field must be printable ASCII, and any binary values are base64 encoded per [RFC4648] ๐ฆ
Field SD-PARAM-NAME Size in octets
----- ------------- ---- -- ------
Version VER 4
Reboot Session ID RSID 1-10
Signature Group SG 1
Signature Priority SPRI 1-3
Total Payload Block Length TPBL 1-8
Index into Payload Block INDEX 1-8
Fragment Length FLEN 1-4
Payload Block Fragment FRAG variable
(base64 encoded binary)
Signature SIGN variable
(base64 encoded binary)
The fields MUST be provided in the order listed. New SD parameters MUST NOT be added unless a new Version of the protocol is defined. (Implementations that want to add proprietary extensions will need to define a separate SD ELEMENT.) A Certificate Block is therefore encoded as follows, where xxx is a placeholder for the particular values:
[ssign-cert VER="xxx" RSID="xxx" SG="xxx" SPRI="xxx" TPBL="xxx"
INDEX="xxx" FLEN="xxx" FRAG="xxx" SIGN="xxx"]
Values of the fields are SD parameter values and are hence enclosed in quotes, per [RFC5424]. The fields are separated by single spaces and are described below. Each SD parameter MUST occur once and only once ๐ธ
5.3.2.1. Version ๐ข
The Version field is 4 octets in length. This field is identical in format and meaning to the Version field described in Section 4.2.1 ๐
5.3.2.2. Reboot Session ID ๐
The Reboot Session ID is identical in format and meaning to the RSID field described in Section 4.2.2 ๐ท
5.3.2.3. Signature Group and Signature Priority ๐ท๏ธ
The SIG field is identical in format and meaning to the SIG field described in Section 4.2.3. The SPRI field is identical in format and meaning to the SPRI field described there.
A signer SHOULD send separate Certificate Block messages for each Signature Group. This makes sure that each collector associated with a Signature Group gets the key material it needs, in case messages of different Signature Groups go to different collectors ๐ Note that the signer needs to get the same Payload Block to each collector, since for any given signer there's a one-to-one relationship between Payload Block and Reboot Session across all Signature Groups. Deployments that want different key material (and hence different Payload Blocks) tied to different Signature Groups can use separate signers for that, each one distinguished by its own combination of HOSTNAME, APP-NAME, and PROCID ๐ง
5.3.2.4. Total Payload Block Length ๐
The Total Payload Block Length is a value representing the total length of the Payload Block in octets, expressed as a decimal with 1 to 8 octets, with leading zeroes omitted ๐ข
5.3.2.5. Index into Payload Block ๐
This is a decimal value between 1 and 8 octets, with leading zeroes omitted. It contains the number of octets into the Payload Block at which this fragment starts. The first octet of the first fragment is numbered "1". (Note, it is not numbered "0".) ๐
5.3.2.6. Fragment Length ๐
The total length of this fragment expressed as a decimal integer with 1 to 4 octets, with leading zeroes omitted. The fragment length must be at least 1 ๐
5.3.2.7. Payload Block Fragment ๐งฉ
The Payload Block Fragment contains a fragment of the payload block. Its length must match the indicated fragment length ๐
5.3.2.8. Signature โ๏ธ
This is a digital signature, encoded in base64, as per [RFC4648]. The Version field effectively specifies the original encoding of the signature. The signature is calculated over the completely formatted Certificate Block message, before the SIGN parameter is added (see Section 4.2.8). For the OpenPGP DSA signature scheme, the value of the signature field contains the DSA values r and s, encoded as 2 multiprecision integers (see [RFC4880], Sections 5.2.2 and 3.2), concatenated, and then encoded in base64 [RFC4648] ๐
5.3.2.9. Example ๐
Okay bestie, here's a full Certificate Block message, chopped into lines so it fits publication rules ๐ There are no spaces at the end of the key blob and signature lines, so don't add any โจ
<110>1 2009-05-03T14:00:39.519307+02:00 host.example.org syslogd
2138 - [ssign-cert VER="0111" RSID="1" SG="0" SPRI="0" TPBL="587"
INDEX="1" FLEN="587" FRAG="2009-05-03T14:00:39.519005+02:00 K BACsLMZ
NCV2NUAwe4RAeAnSQuvv2KS51SnHFAaWJNU2XVDYvW1LjmJgg4vKvQPo3HEOD+2hEkt1z
cXADe03u5pmHoWy5FGiyCbglYxJkUJJrQqlTSS6vID9yhsmEnh07w3pOsxmb4qYo0uWQr
AAenBweVMlBgV3ZA5IMA8xq8l+i8wCgkWJjCjfLar7s+0X3HVrRroyARv8EAIYoxofh9m
N8n821BTTuQnz5hp40d6Z3UudKePu2di5Mx3GFelwnV0Qh5mSs0YkuHJg0mcXyUAoeYry
5X6482fUxbm+gOHVmYSDtBmZEB8PTEt8Os8aedWgKEt/E4dT+Hmod4omECLteLXxtScTM
gDXyC+bSBMjRRCaeWhHrYYdYBACCWMdTc12hRLJTn8LX99kv1I7qwgieyna8GCJv/rEgC
ssS9E1qARM+h19KovIUOhl4VzBw3rK7v8Dlw/CJyYDd5kwSvCwjhO21LiReeS90VPYuZF
RC1B82Sub152zOqIcAWsgd4myCCiZbWBsuJ8P0gtarFIpleNacCc6OV3i2Rg=="
SIGN="AKAQEUiQptgpd0lKcXbuggGXH/dCdQCgdysrTBLUlbeGAQ4vwrnLOqSL7+c="]
So the message is syslog-sign protocol version "01" ๐ It uses SHA1 as its hash algorithm and an OpenPGP DSA signature scheme ๐ฅ Its reboot session ID is 1, its Signature Group is 0, and its Signature Priority is 0 โจ The Total Payload Block Length is 587 octets. The index into the payload block is 1, which means this is the first fragment ๐ง The fragment length is 587, meaning the Certificate Block message carries the entire Payload Block ๐ญ The Payload Block has the timestamp 2009-05-03T14:00:39.519005+02:00. And the Key Blob Type is 'K', which means it holds a public key whose matching private key is the one signing these messages ๐
Now, the Certificate Block's timestamp is super close to the timestamp in the Payload Block, and that's the tell that this is the first Certificate Block sent in this reboot session ๐ Later Certificate Block messages can show up with a later timestamp, and they'll still carry the same Payload Block, which still has the same timestamp ๐ฆ
6. Redundancy and Flexibility ๐
Here's the vibe. As described in Section 8.5 of [RFC5424], a transport sender is allowed to throw away syslog messages ๐ And when syslog messages go over an unreliable transport, they can get lost on the way too ๐ญ But if a collector never gets its Signature and Certificate Blocks, a bunch of messages might not be verifiable at all, which is giving major drama ๐ So the signer is allowed to send Signature and Certificate Blocks more than once. Doing that adds redundancy, so the collector or relay still gets the Signature Blocks and, most importantly, the Payload Block eventually ๐ Until those blocks show up, any online review of logs (see Section 7.2) just has to wait ๐ซถ The collector MUST ignore duplicates of Signature Blocks and Certificate Blocks that it has already received and authenticated, so no double-counting, periodt ๐ And fr, the signer can change its redundancy level for any reason, without telling the collector first โจ
A signer that is also the originator of the messages it signs doesn't need to line up other messages while it sends redundant Certificate Block and Signature Block messages ๐ธ It MAY send redundant Certificate Block messages even after Signature Block messages and regular syslog messages have already gone out. By the same token, it MAY send redundant Signature Block messages even after newer syslog messages, signed by a later Signature Block, have been sent, or even after a later Signature Block message itself ๐
Also, the signer gets to pick how many hashes go inside a Signature Block ๐ It's totally legit for an originator to send short Signature Blocks so the collector can verify messages with minimal delay ๐โโ๏ธโจ
6.1. Configuration Parameters ๐ญ
Even though the transport sender isn't boxed in on how it decides to send redundant Signature and Certificate Blocks, or even whether it sends extra copies of normal syslog messages, we define some redundancy parameters below ๐ They might help control redundant transmission from the transport sender to the transport receiver, and admins might find them handy to configure ๐ง
6.1.1. Configuration Parameters for Certificate Blocks ๐
Certificate Blocks are always sent at the beginning of a new reboot session ๐ One technique for reliable delivery (see Section 8.5) is to send multiple copies, and that's controlled by a certInitialRepeat parameter:
certInitialRepeat = number of times each Certificate Block should
be sent before the first message is sent.
It's also useful to resend Certificate Blocks every now and then for long-lived reboot sessions ๐ฆ That's handled by the certResendDelay and certResendCount parameters:
certResendDelay = maximum time delay in seconds until resending
the Certificate Block.
certResendCount = maximum number of other syslog messages to send
until resending the Certificate Block.
Sometimes you might want the transport sender configured so it never sends Certificate Blocks after the first normal syslog message has gone out ๐ That can be expressed by setting both certResendDelay and certResendCount to "0". Real talk though, configuring the transport sender to keep sending redundant Certificate Blocks even after the first message is RECOMMENDED, especially when the UDP transport [RFC5426] is in use ๐
Now here's a scenario. The receiver might get a Certificate Block, some group of syslog messages, and some matching Signature Blocks. If the receiver reboots after that, recovery looks different depending on the transport ๐ญ For UDP [RFC5426], the receiver SHOULD keep using the cached Certificate Block, but it MUST validate the RSID value to make sure it has the most current one ๐ If it can't validate that, then it MUST wait for a retransmission of the Certificate Block, which can be controlled by the certResendDelay and certResendCount parameters. It's up to operators to make sure Certificate Blocks get sent often enough to cover this situation ๐ซถ
For TLS transport [RFC5425], the sender MUST send a fresh Certificate Block when a session is established ๐ That keeps the sender and receiver synced to the most current Certificate Block โจ
Implementations that support sending syslog messages of different Signature Groups to different collectors, and that want to offer super granular controls, MAY allow the above parameters to be configured on a per Signature Group basis ๐
Picking reasonable values depends on a few things: how much delay is acceptable between getting a syslog message and getting its matching Signature Block, whether UDP or TLS transport is used, and how much management bandwidth you've got ๐ธ For a deployment where the reliability of the underlying transport and of the collector implementation is of little concern, something like this might be reasonable:
certInitialRepeat=1, certResendDelay=1800 seconds,
certResendCount=10000
And for a deployment where reliability over UDP transport could be an issue, this might be reasonable:
certInitialRepeat=2, certResendDelay=300 seconds,
certResendCount=1000
6.1.2. Configuration Parameters for Signature Blocks ๐ง
Verifying log messages involves a certain amount of delay, because of the lag between when the message itself is sent and when its matching Signature Block shows up โณ This configuration parameter can help limit how much lag gets incurred. Just note that the maximum message length can also force a Signature Block to be generated, see Sections 4.2.6 and 4.2.7:
sigMaxDelay = generate a new Signature Block if this many seconds
have elapsed since the message with the First Message Number of
the Signature Block was sent.
Retransmissions of Signature Blocks don't go out right after the original transmission, they go out a little later ๐ฆ These parameters control when those retransmissions happen:
sigNumberResends = number of times a Signature Block is resent.
(It is recommended to select a value of greater than "0" in
particular when the UDP transport [RFC5426] is used.)
sigResendDelay = send the next retransmission when this many
seconds have elapsed since the previous sending of this Signature
Block.
sigResendCount = send the next retransmission when this many other
syslog messages have been sent since the previous sending of this
Signature Block.
Again, the right values depend on a few factors: how long a delay is acceptable before a syslog message can be verified (meaning, when its Signature Block arrives), how reliable the underlying transport is, and how much management bandwidth there is ๐ For a deployment where transport and collector reliability are of little concern, and where syslog messages need to be signed within 5 minutes, this might be reasonable:
sigMaxDelay=300 seconds, sigNumberResends=2, sigResendDelay=300
seconds, sigResendCount=500
And for a deployment that needs to validate syslog messages typically within 60 seconds, but no more than 3 minutes after receipt, this would be reasonable:
sigMaxDelay=30 seconds, sigNumberResends=5, sigResendDelay=30
seconds, sigResendCount=100
6.2. Overlapping Signature Blocks ๐
Even though the signer isn't constrained on whether it sends redundant Signature Block messages, Signature Blocks SHOULD NOT overlap ๐ซถ Non-overlapping blocks make life easier for the receiving collector to process them ๐ Concretely, an originator of Signature Block messages that has already sent a first message with some First Message Number and a Count SHOULD NOT send a second message with that same First Message Number but a different Count. It also SHOULD NOT send a second message whose First Message Number is greater than the First Message Number but smaller than the First Message Number plus the Count indicated in the first message ๐
That said, overlapping Signature Blocks do give extra flexibility for redundancy ๐ They're an additional option that might be desirable in some deployments, so collectors MUST be designed to cope with overlapping Signature Blocks when they run into them ๐ช And the collector MUST ignore hashes of messages it has already received and validated โจ
7. Efficient Verification of Logs ๐
Logs secured with syslog-sign can be reviewed either online or offline ๐ฆ Online review is a bit more complicated and computationally expensive, but not prohibitively so ๐ This section outlines a method for online verification and a method for offline verification of logs, which implementations MAY choose to implement so they can verify logs efficiently ๐ง Implementations MAY also choose to implement a different method, because ultimately it's up to each implementation how it processes the messages it receives ๐
7.1. Offline Review of Logs ๐ฌ
Okay so, when the collector stores logs to be reviewed later, they can be authenticated offline right before anyone reads them. ๐ซถ Offline review is simple and relatively cheap resource-wise, so long as the reviewing machine has enough space. ๐พ
So here's the process. First, we go through the stored log file. Each message gets sorted into one of three buckets: a normal message, a Signature Block message, or a Certificate Block message. Signature Blocks and Certificate Blocks get separated by signer (identified by HOSTNAME, APP-NAME, PROCID), Reboot Session ID, and Signature Group, and each group goes into its own file. โจ Normal messages go into a keyed file, indexed on their hash values. They are not separated by signer, because their (HOSTNAME, APP-NAME, PROCID) tells you which application made the message. And that application does not have to be the same as the signer. ๐
For each signer, Reboot Session ID, and Signature Group, we then do the following:
- a. Sort the Certificate Block file by
INDEXvalue, and check whether we have a set of Certificate Blocks that can rebuild the Payload Block. If we do, we reconstruct the Payload Block, verify any key-identifying information, and then use that to verify the signatures on the Certificate Blocks we received. Once this is done, we've verified the reboot session and the key used for the rest of the process. ๐ - b. Sort the Signature Block file by First Message Number. Now we make an authenticated log file, which has some header information and then a sequence of (message number, message text) pairs. Next we go through the Signature Block file, and we set a cursor for the last message number processed to 0. For each Signature Block in the file, we do this:
- Verify the signature on the Signature Block.
- If the First Message Number of the Signature Block is less than or equal to the last message number processed, skip the first (last message number processed minus First Message Number plus 1) hashes.
- For each remaining hashed message in the Signature Block:
- a. Look up the hash value in the keyed message file.
- b. If the message is found, write (message number, message text) to the authenticated log file.
- Set the last message number processed to the value of the First Message Number plus the Count of the Signature Block minus 1.
- Skip all other Signature Blocks with the same First Message Number unless one with a larger Count is encountered.
The resulting authenticated log file contains all the messages that have been authenticated. ๐ It also implicitly shows all the gaps in the authenticated messages (specifically when all messages of the same Signature Group are sent to the same collector), because their message numbers are missing. ๐
Okay so, assuming there's enough space to build the keyed file, the whole process is linear in the number of messages (generally two seeks, one to write and one to read, per normal message received). It's O(N lg N) in the number of Signature Blocks. ๐ This estimate comes with two caveats, though. First, the Signature Blocks arrive very nearly in sorted order, so they can probably be sorted more cheaply on average than O(N lg N) steps. Second, the signature verification on each Signature Block is almost certainly more expensive than the sorting step in practice. ๐
We haven't discussed error recovery, which may be necessary for the Certificate Blocks. In practice though, a simple error-recovery strategy is probably enough: if the Payload Block isn't valid, we can just try alternate instances of each Certificate Block, if any are available, until we get the Payload Block right. ๐ฉน
It is super easy for an attacker to flood us with plausible-looking messages, Signature Blocks, and Certificate Blocks. ๐ Not me being scared... but yes, me being scared. ๐ญ
7.2. Online Review of Logs โก
Some collector implementations need to monitor log messages in close to real time. This can be done with syslog-sign, though it's a bit more complex than offline verification. Here's how it goes:
- a. We have an authenticated message file, where we write (message number, message text) pairs that have been authenticated. We'll assume we're only handling one signer, one Signature Group, and one Reboot Session ID at any given time. (For concurrent support of multiple signers, Signature Groups, and Reboot Session IDs, the same procedure is applied analogously to each one. Signature Block messages and Certificate Block messages clearly indicate their respective signer, Signature Group, and Reboot Session ID.) ๐ง
- b. We have two data structures. The first is a "Waiting for Signature" queue, where (arrival sequence, hash of message) pairs are kept in sorted order. The second is a "Waiting for Message" queue, where (message number, hash of message) pairs are kept in sorted order. On top of that, we have a hash table that stores (message text, count) pairs indexed by hash value. In the hash table, count can be any number greater than zero. When count is zero, the entry in the hash table is cleared. ๐๏ธ
Note: The "Waiting for Signature" queue gets used in the normal case, when the signature arrives after the message itself. It holds messages that have been received but whose signature hasn't arrived yet. The "Waiting for Message" queue gets used when messages are lost or misordered (either in the network or in relays). It holds signatures that have been received but whose corresponding messages haven't arrived yet. Since a single Signature Block can only cover a limited number of messages (due to size restrictions), and massive reordering or delaying is rare, both queues are expected to be relatively small. ๐ธ
- c. We must receive all the Certificate Blocks before any other processing can really happen. (That's why they're sent first!) Once that's done, any extra Certificate Block message that arrives gets discarded. Any syslog messages or Signature Block messages that arrive before all the Certificate Blocks have been received need to be buffered. Once all the Certificate Blocks are in, the buffered messages can be retrieved and processed as if they were just arriving. ๐ฆ
- d. Whenever a normal message arrives, we first check whether its hash value is in the "Waiting for Message" queue. If it is, we write the message number (from the "Waiting for Message" queue) and the message into the authenticated message file, and remove that entry from the queue. โ
Otherwise, we add (arrival sequence, hash of message) to the "Waiting for Signature" queue. If our hash table already has an entry for that message's hash value, we increment its count by one. If not, we create a new entry with Count = 1. ๐งฎ
If the "Waiting for Signature" message queue is full, we remove the oldest message from the queue. That message couldn't be validated close enough to real time. To update the hash table to match, we use that entry's hash to index the hash table. If that entry has count 1, we delete it from the hash table. Otherwise, we decrement its count. By removing the message from the "Waiting for Signature" message queue without having received its signature, we make it impossible to authenticate that message if its signature arrives later. ๐ฌ So implementers need to make sure the queues are dimensioned large enough that they don't expose the collector to Denial-of-Service (DoS) attacks that try to flood it with unsigned messages. ๐ก๏ธ
- e. Whenever a Signature Block message arrives, we check its originator (the signer) by way of
HOSTNAME,APP-NAME, andPROCID, along with its Signature Group and Reboot Session ID, to make sure it matches our Certificate Blocks. Then we check whether the First Message Number value is too old to still be of interest, or whether another Signature Block with that First Message Number and the same Count or a greater Count has already been received. If either is true, we discard the Signature Block. Next we check the signature, and we discard the Signature Block if the signature isn't valid. ๐
Otherwise, we go ahead and process the hashes in the Signature Block. A Signature Block contains a sequence of hashes, each associated with a message number, starting with the First Message Number for the first hash and incrementing by one for each subsequent hash. For each hash, we first check whether the message hash is in the hash table. If it is, that means we've received the signature for a message that arrived earlier, and we do the following:
- We check whether a message with the same message number is already in the authenticated message file. If it is, the signed hash is a duplicate, so we discard it.
- Otherwise (the signed hash isn't a duplicate), we write the (message number, message text) into the authenticated message file. We also update the hash table accordingly, using that entry's hash to index it. If that entry has Count 1, we delete it from the hash table. Otherwise, we decrement its count.
If the message hash is not in the hash table, we write the (message number, message hash) to the "Waiting for Message" queue.
If the "Waiting for Message" queue is full, we remove the oldest entry. In that case, a message that was signed by the signer couldn't be validated by the receiver, either because the message was lost or because the signature arrived way ahead of the actual message. By removing the entry from the "Waiting for Message" queue without having received the message, we make it impossible to authenticate a legitimate message if it still arrives later. ๐ฅฒ So implementers need to make sure the queues are dimensioned large enough that the chances of this actually happening are minimized. ๐ง
- f. The end result is a sequence of messages in the authenticated message file. Every message in that file has been authenticated, and the sequence is labeled with numbers showing the order in which the messages were originally transmitted. ๐
One can see that this whole process is roughly linear in the number of messages, and also in the number of Signature Blocks received. But the process is susceptible to flooding attacks, because an attacker can send enough normal messages that the messages roll off their queue before their Signature Blocks can be processed. ๐ Honestly, that's the kind of plot twist that keeps me up at night. ๐
8. Security Considerations ๐
okay bestie, this is the security section and it's giving very serious energy ๐ญ๐ซถ Normal syslog event messages are unsigned, so they only have the security stuff described in Section 8 of [RFC5424]. This doc adds Certificate Blocks and Signature Blocks, which are signed syslog messages ๐ฆ Signature Blocks carry signature info for syslog event messages that were sent earlier. Put it all together and you can authenticate syslog messages and cut down (or basically erase ๐ ) a lot of the security worries from [RFC5424].
The model for syslog-sign is a direct trust system, where the certificate that gets sent is its own trust anchor ๐ When a transport sender streams syslog messages signed with a certificate, the operator or application hands that certificate over to the transport receiver. And no, you don't need a certificate chain ๐โจ
8.1. Cryptographic Constraints
Any time crypto is involved, it's a good idea to check the current literature and see if any of the algorithms here have been found to be vulnerable ๐ฅ
This spec uses Public Key Cryptography. The right party (or parties) has to control the private key half of a public-private key pair ๐๏ธ Anyone who controls a private key can sign literally anything they want, so guard those keys like your diary ๐๐
Some operations in this spec need random numbers. An appropriate entropy source SHOULD be used to generate them ๐ฒ For more on that, check out [RFC4086] and [NIST800.90].
8.2. Packet Parameters
As a signer, it's smart to avoid message lengths over 2048 octets ๐ Things can go sideways if a signer sends messages longer than 2048 octets, because relays MAY truncate messages past that length. That would make it impossible for collectors to validate a hash of the packet ๐ฉ For better interop, the golden rule is to be conservative with what you send but liberal in what you can receive ๐ธ
Signers need to stick rigidly to the RFC 5424 format when sending messages ๐ If a collector gets a message that isn't formatted properly, it might drop it, or it might change it while receiving it. (See Appendix A.2 of [RFC5424].) If that happens, the hash of the sent message won't match the hash of the received message, and that's a whole mess ๐
Collectors must not malfunction if they get malformed syslog messages or messages with characters outside what this document specifies. Basically, they should ignore those messages and keep on working ๐ชโจ
8.3. Message Authenticity
Syslog doesn't strongly tie a message to its originator ๐ง That connection gets made by the collector when it verifies the Signature Block. A Signature Block might be received, stored, and read by a human or an automated parser before it's used to check an event message's authenticity. So it's smart not to assume a message is legit until you've validated it by checking the contents of the Signature Block ๐
With Signature Block checking in place, an attacker can only forge messages if they manage to compromise the private key of the true originator ๐ต๏ธโโ๏ธ๐
8.4. Replaying
Event messages can be recorded and replayed by an attacker ๐ Using the info in the Signature Blocks, a reviewer can tell whether the received messages are the ones the originator actually sent, and can spot the replayed ones too ๐ If you use a log verification method like the one in Section 7, a replayed message can be caught by checking, before writing a message to the authenticated log file, whether that message is already in there ๐ง
8.5. Reliable Delivery
Event messages sent over UDP can get lost in transit ๐ [RFC5425] can be used for reliable delivery of syslog messages, but it doesn't protect against loss at the application layer. For example, that happens if the TCP connection or TLS session gets closed by the transport receiver for some reason ๐ A reviewer can find any messages the originator sent but the collector never received by reviewing the Signature Block info. On top of that, the info in later Signature Blocks lets a reviewer figure out whether any Signature Block messages got lost in transit ๐ต๏ธโโ๏ธ
8.6. Sequenced Delivery
Syslog messages sent over UDP can not only get lost, they can also arrive out of order ๐ A reviewer can work out the original order of the syslog messages, and can identify which ones were delivered out of order, by looking at the Signature Block info along with any timestamp info in the message โฐ
8.7. Message Integrity
Syslog messages can get damaged in transit ๐ฉน Looking at the Signature Block info tells you whether the message you received is the one the originator meant to send. A damaged Signature Block or Certificate Block is obvious, because the collector won't be able to validate that it was signed by the signer ๐ฉ
8.8. Message Observation
Unless TLS is used as a secure transport [RFC5425], event messages, Certificate Blocks, and Signature Blocks are all sent in plaintext ๐ That means network admins can read the messages while sniffing the wire, which is handy for them. But it also means an attacker can see what's in the event messages and maybe use that info for something shady ๐ค
8.9. Man-in-the-Middle Attacks
It's conceivable that an attacker intercepts Certificate Block messages and slips in their own Certificate info ๐ฑ If that happens, the attacker can receive event messages from the real originator and then relay modified messages, insert brand-new messages, or delete messages. They could also build a Signature Block and sign it with their own private key ๐ So network admins need to verify that the key in the Payload Block is actually the key being used by the real signer ๐ If it is, this MITM attack won't work. Methods for setting up a chain of trust are also described in [RFC5425] ๐ก๏ธ
8.10. Denial of Service
An attacker might send invalid Signature Block messages to overwhelm the collector's processing power and eat up all the available resources ๐ฅต Because of that, it can make sense to just receive the Signature Block messages and only process them as time allows โณ
An attacker can also simply flood a collector with more messages than it can handle ๐ Implementers are advised to consider features that cut down on this threat, like only accepting syslog messages from known IP addresses ๐งฑ
8.11. Covert Channels
Nothing in this protocol tries to get rid of covert channels ๐ Honestly, just about every part of a syslog message can be used to carry hidden signals ๐ต๏ธ For example, a collusionist could send odd and even PRI values to stand for Morse Code dashes and dots ๐ก๐
9. IANA Considerations ๐ท
9.1. Structured Data and Syslog Messages
With regard to [RFC5424], IANA added the following values to the registry called "syslog Structured Data ID Values" ๐ Each parameter is listed as mandatory:
Structured Data ID Structured Data Parameter
------------------ -------------------------
ssign
VER
RSID
SG
SPRI
GBC
FMN
CNT
HB
SIGN
ssign-cert
VER
RSID
SG
SPRI
TPBL
INDEX
FLEN
FRAG
SIGN
On top of that, IANA controls several fields in both the Signature Block and the Certificate Block, as outlined in the following sections ๐ฆโจ
9.2. Version Field ๐
Okay so IANA has made three registries ๐๏ธ, one for each piece of the Version field in Signature Blocks and Certificate Blocks. You can read about those in Sections 4.2.1 and 5.3.2.1 respectively ๐
First up is the one called "syslog-sign Protocol Version Values" ๐ . It's for the Protocol Version subfield, which is the first two octets of the Version field. New values SHALL be assigned by the IANA using the "IETF Review" policy defined in [RFC5226]. Assigned numbers go up by 1, all the way to a max of "50" โจ. Protocol Version numbers "51" through "99" are vendor specific, so values in that range are not to be assigned by the IANA ๐
IANA has registered these Protocol Version values ๐
Value Protocol Version
----- ----------------
00 Reserved
01 Defined in RFC 5848
Next is "syslog-sign Hash Algorithm Values" ๐ธ. This one is for the Hash Algorithm subfield, which is the third octet in the Version field of Signature Blocks and Certificate Blocks. New values SHALL be assigned by the IANA using the "IETF Review" policy defined in [RFC5226]. The numbers go up one at a time, first through "9", then "a" to "z", then "A" to "Z" ๐ฆ. These are registered relative to the Protocol Version, which is kind of iconic because it means the same Hash Algorithm value can be reserved for different Protocol Versions, and it could mean a different hash algorithm each time ๐ง. This sets things up for the future, when one single octet becomes too small, since more Hash Algorithms can be supported by defining more Protocol Versions that implementations might run side by side ๐ฅ
IANA has registered these Hash Algorithm values ๐
Value Protocol Version Hash Algorithm
----- ---------------- --------------
0 01 Reserved
1 01 SHA1
2 01 SHA256
Last one in this group is "syslog-sign Signature Scheme Values" ๐. It covers the Signature Scheme subfield, the fourth octet in the Version field of Signature Blocks and Certificate Blocks. New values SHALL be assigned by the IANA using the "IETF Review" policy defined in [RFC5226]. These go up by 1, up to a max of "9" โญ. The same Signature Scheme value can be reserved for different Protocol Versions, and each time it could point to a different Signature Scheme ๐. Same story as before: it's future-proofing, because more Signature Schemes can be supported by adding Protocol Versions that implementations might use at the same time ๐ซถ
IANA has registered these Signature Scheme values ๐
Value Protocol Version Signature Scheme
----- ---------------- ----------------
0 01 Reserved
1 01 OpenPGP DSA
9.3. SG Field ๐
IANA has made a registry called "syslog-sign SG Field Values" ๐. It's for the values of the SG Field defined in Section 4.2.3. New values SHALL be assigned by the IANA using the "IETF Review" policy defined in [RFC5226]. They go up by 1, up to a max of "7". Values "8" and "9" SHALL be left as vendor specific and SHALL NOT be assigned by the IANA ๐ โโ๏ธ
IANA has registered these SG Field values ๐
Value Meaning
----- -------
0 There is only one Signature Group.
1 Each PRI value is associated with its own Signature
Group.
2 Each Signature Group contains a range of PRI
values.
3 Signature Groups are not assigned with any of the
above relationships to PRI values of the syslog
messages they sign.
9.4. Key Blob Type ๐
IANA has made a registry called "syslog-sign Key Blob Type Values" ๐๏ธ. It's where the one-character identifiers for the Key Blob Type go, per Section 5.2. New values SHALL be assigned by the IANA using the "IETF Review" policy defined in [RFC5226]. Uppercase letters MAY be assigned as values ๐. Lowercase letters are vendor specific and SHALL NOT be assigned by the IANA, so hands off ๐
IANA has registered these Key Blob Type values ๐
Value Key Blob Type
----- -------------
C a PKIX certificate
P an OpenPGP certificate
K the public key whose corresponding private key is
used to sign the messages
N no key information sent, key is pre-distributed
U installation-specific key exchange information
10. Acknowledgements ๐ซถ
The authors want to give a huge shoutout ๐ฃ to the current Chairs of the Syslog Working Group, David Harrington and Chris Lonvick ๐. They also thank the rest of the Working Group, especially Alex Brown, Chris Calabrese, Steve Chang, Pasi Eronen, Carson Gaspar, Rainer Gerhards, Drew Gross, Albert Mietus, Darrin New, Marshall Rose, Andrew Ross, Martin Schuette, Holt Sorenson, Rodney Thayer ๐ธ. And a big thank you to all the many Counterpane Internet Security engineering and operations people who commented on various versions of this proposal ๐. Y'all are literally the best โจ
11. References ๐
11.1. Normative References
These are the ones you need to read to get the spec right ๐ง
[FIPS.186-2.2000] National Institute of Standards and Technology,
"Digital Signature Standard", FIPS PUB 186-2,
January 2000, <http://csrc.nist.gov/publications/
fips/archive/fips186-2/fips186-2.pdf>.
[FIPS.180-2.2002] National Institute of Standards and Technology,
"Secure Hash Standard", FIPS PUB 180-2,
August 2002, <http://csrc.nist.gov/publications/
fips/fips180-2/fips180-2.pdf>.
[RFC2119] Bradner, S., "Key words for use in RFCs to
Indicate Requirement Levels", BCP 14, RFC 2119,
March 1997.
[RFC4648] Josefsson, S., "The Base16, Base32, and Base64
Data Encodings", RFC 4648, October 2006.
[RFC4880] Callas, J., Donnerhacke, L., Finney, H., Shaw, D.,
and R. Thayer, "OpenPGP Message Format", RFC 4880,
November 2007.
[RFC5226] Narten, T. and H. Alvestrand, "Guidelines for
Writing an IANA Considerations Section in RFCs",
BCP 26, RFC 5226, May 2008.
[RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen,
S., Housley, R., and W. Polk, "Internet X.509
Public Key Infrastructure Certificate and
Certificate Revocation List (CRL) Profile",
RFC 5280, May 2008.
[RFC5424] Gerhards, R., "The syslog Protocol", RFC 5424,
March 2009.
[RFC5425] Miao, F., Yuzhi, M., and J. Salowey, "TLS
Transport Mapping for syslog", RFC 5425,
March 2009.
[RFC5426] Okmianski, A., "Transmission of syslog Messages
over UDP", RFC 5426, March 2009.
11.2. Informative References
These ones are just extra reading if you're curious ๐ฆ
[NIST800.90] National Institute of Standards and Technology,
"NIST Special Publication 800-90: Recommendation
for Random Number Generation using Deterministic
Random Bit Generators", June 2006, <http://
csrc.nist.gov/publications/nistpubs/800-90/
SP800-90revised_March2007.pdf>.
[RFC3339] Klyne, G. and C. Newman, "Date and Time on the
Internet: Timestamps", RFC 3339, July 2002.
[RFC3414] Blumenthal, U. and B. Wijnen, "User-based Security
Model (USM) for version 3 of the Simple Network
Management Protocol (SNMPv3)", RFC 3414,
December 2002.
[RFC4086] Eastlake, D., Schiller, J., and S. Crocker,
"Randomness Recommendations for Security",
RFC 4086, June 2005.
Authors' Addresses ๐
Here's how to reach the authors ๐ง
John Kelsey
NIST
EMail: john.kelsey@nist.gov
Jon Callas
PGP Corporation
EMail: jon@callas.org
Alexander Clemm
Cisco Systems
EMail: alex@cisco.com
That's the whole spec, bestie!! ๐โจ๐
๐ฆ where this page comes from
- ietf: rfc5848 2010-05
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms