RFC 4253
The Secure Shell (SSH) Transport Layer Protocol
1. Introduction โจ
okay so the SSH transport layer is a secure, low level transport protocol ๐ it gives you strong encryption, cryptographic host authentication, and integrity protection. it's the foundation under everything else, so it's giving infrastructure ๐
authentication at this layer is host-based. this protocol does not do user authentication ๐ that's a job for a higher level protocol that can be built on top of this one, and honestly that split is so iconic.
the protocol is built to be simple and flexible. it lets the two sides negotiate parameters and it's designed to minimize round-trips ๐โโ๏ธ the key exchange method, public key algorithm, symmetric encryption algorithm, message authentication algorithm, and hash algorithm are all negotiated. in most environments only 2 round-trips are expected for the full key exchange, server authentication, service request, and acceptance notification of the service request. the worst case is 3 round-trips. not bad at all ๐ฆ
2. Contributors ๐ซถ
the major original contributors to this set of documents are Tatu Ylonen, Tero Kivinen, Timo J. Rinne, and Sami Lehtinen (all of SSH Communications Security Corp), plus Markku-Juhani O. Saarinen (University of Jyvaskyla). Darren Moffat was the original editor of this set of documents and also made very substantial contributions ๐
so many people helped shape this over the years. the people who should be acknowledged include Mats Andersson, Ben Harris, Bill Sommerfeld, Brent McClure, Niels Moller, Damien Miller, Derek Fawcus, Frank Cusack, Heikki Nousiainen, Jakob Schlyter, Jeff Van Dyke, Jeffrey Altman, Jeffrey Hutzelman, Jon Bright, Joseph Galbraith, Ken Hornstein, Markus Friedl, Martin Forssen, Nicolas Williams, Niels Provos, Perry Metzger, Peter Gutmann, Simon Josefsson, Simon Tatham, Wei Dai, Denis Bider, der Mouse, and Tadayoshi Kohno ๐ listing them does not mean they endorse this document, only that they contributed to it. no cap, that's a real legend list.
3. Conventions Used in This Document ๐
all documents related to the SSH protocols shall use the keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" to describe requirements. these keywords are to be interpreted as described in [RFC2119] ๐
the keywords "PRIVATE USE", "HIERARCHICAL ALLOCATION", "FIRST COME FIRST SERVED", "EXPERT REVIEW", "SPECIFICATION REQUIRED", "IESG APPROVAL", "IETF CONSENSUS", and "STANDARDS ACTION" that appear in this document when used to describe namespace allocation are to be interpreted as described in [RFC2434] ๐๏ธ
protocol fields and the possible values to fill them are defined in this set of documents. the fields get defined inside the message definitions. for example, SSH_MSG_CHANNEL_DATA is defined like this ๐
byte SSH_MSG_CHANNEL_DATA
uint32 recipient channel
string data
when fields are referenced in these documents they show up in single quotes, and when values for those fields are referenced they show up in double quotes. using the example above, possible values for 'data' are "foo" and "bar" ๐
4. Connection Setup ๐
SSH works over any 8-bit clean, binary-transparent transport. the underlying transport SHOULD protect against transmission errors, because those errors cause the SSH connection to terminate ๐
the client is the one who initiates the connection ๐
4.1. Use over TCP/IP ๐
when SSH is used over TCP/IP, the server normally listens for connections on port 22. this port number has been registered with the IANA and officially assigned for SSH ๐ฆ
4.2. Protocol Version Exchange ๐ง
once the connection is established, both sides MUST send an identification string. the identification string MUST look like this:
SSH-protoversion-softwareversion SP comments CR LF
since the protocol defined in this set of documents is version 2.0, the 'protoversion' MUST be "2.0". the 'comments' string is OPTIONAL. if you include the 'comments' string, a space character (SP, ASCII 32) MUST separate the 'softwareversion' and 'comments' strings. the identification MUST be terminated by a single Carriage Return (CR) and a single Line Feed (LF) character (ASCII 13 and 10, respectively) โจ
implementers who want to stay compatible with older, undocumented versions of this protocol may want to process the identification string without expecting the carriage return character. the reasons for that are in Section 5 of this document, so go check it out ๐ also, the null character MUST NOT be sent. the maximum length of the string is 255 characters, including the Carriage Return and Line Feed. strict, but it's giving clean ๐
the part of the identification string before the Carriage Return and Line Feed is used in the Diffie-Hellman key exchange (see Section 8) ๐
the server MAY send other lines of data before it sends the version string. each of those lines SHOULD be terminated by a Carriage Return and Line Feed. those lines MUST NOT begin with "SSH-", and they SHOULD be encoded in ISO-10646 UTF-8 [RFC3629] (the language is not specified). clients MUST be able to process those lines. the lines MAY be silently ignored, or MAY be displayed to the client user. if they do get displayed, control character filtering, as discussed in [SSH-ARCH], SHOULD be used. the main reason for this feature is letting TCP-wrappers show an error message before disconnecting ๐
both the 'protoversion' and 'softwareversion' strings MUST consist of printable US-ASCII characters, with the exception of whitespace characters and the minus sign (-). the 'softwareversion' string is mainly used to trigger compatibility extensions and to show what an implementation can do. the 'comments' string SHOULD contain extra information that could help solve user problems. here's an example of a valid identification string ๐
SSH-2.0-billsSSH_3.6.3q3<CR><LF>
this one doesn't have the optional 'comments' string, so it gets terminated by a CR and LF right after the 'softwareversion' string ๐ธ
key exchange begins immediately after you send this identifier. all packets that follow the identification string SHALL use the binary packet protocol, which is described in Section 6 ๐
5. Compatibility With Old SSH Versions ๐ฐ๏ธ
like we said earlier, the 'protoversion' for this protocol is "2.0". earlier versions of this protocol were never formally documented, but everyone knows they use a 'protoversion' of "1.x" (for example "1.5" or "1.3") ๐ผ at the time of writing, lots of SSH implementations are on protocol version 2.0, but devices still running the previous versions definitely exist. during the transition period it matters a lot to work in a way that stays compatible with the installed SSH clients and servers that use the older version of the protocol ๐ซ
the info in this section only matters for implementations that support compatibility with SSH versions 1.x. for anyone who's curious, the only known documentation of the 1.x protocol lives in README files shipped with the source code [ssh-1.2.30] ๐
to be continued! part 2 is coming next, bestie ๐ง๐
5.1. Old Client, New Server
Okay bestie, so server implementations MAY support a configurable compatibility flag ๐ that lets them talk to old versions. When that flag is on, the server SHOULD identify its 'protoversion' as "1.99" โจ. Clients using protocol 2.0 MUST be able to read that as identical to "2.0" ๐ซถ. In this mode, the server SHOULD NOT send the Carriage Return character (ASCII 13) after the identification string ๐ธ.
In compatibility mode, the server SHOULD NOT send any further data after its identification string until it has received an identification string from the client ๐ฆ. That way the server can figure out whether the client is using an old protocol, and it can switch back to the old protocol if it needs to ๐. Also, in compatibility mode the server MUST NOT send additional data before the identification string ๐.
When you don't need to support old clients, the server MAY send its initial key exchange data right after the identification string ๐.
5.2. New Client, Old Server
The new client MAY immediately send extra data after its identification string, before it has received the server's identification string ๐. So the old protocol might already be corrupt by the time the client finds out the server is old ๐ญ. When that happens, the client SHOULD close the connection to the server and reconnect using the old protocol ๐ง.
5.3. Packet Size and Overhead
Some readers will worry about packets getting bigger because of the new headers, padding, and the Message Authentication Code (MAC) ๐ฅบ. The minimum packet size is in the order of 28 bytes (depending on negotiated algorithms) ๐. The increase is negligible for large packets, but it's very significant for one-byte packets, like telnet-type sessions ๐. There are, however, several factors that make this a non-issue in almost all cases ๐:
- The minimum size of a TCP/IP header is 32 bytes. Thus, the increase is actually from 33 to 51 bytes (roughly) โจ.
- The minimum size of the data field of an Ethernet packet is 46 bytes [RFC0894]. Thus, the increase is no more than 5 bytes. When Ethernet headers are considered, the increase is less than 10 percent ๐ฅ.
- The total fraction of telnet-type data in the Internet is negligible, even with increased packet sizes ๐ .
The only environment where the packet size increase is likely to have a significant effect is PPP [RFC1661] over slow modem lines (PPP compresses the TCP/IP headers, which makes the increase in packet size stand out more) ๐ข. But with modern modems, the transfer time is in the order of 2 milliseconds, which is a lot faster than people can type โก.
There are also issues related to the maximum packet size ๐ฆ. To keep screen updates from lagging, you don't want excessively large packets for interactive sessions ๐. The maximum packet size is negotiated separately for each channel ๐ซง.
6. Binary Packet Protocol โจ
Okay so every packet looks like this ๐:
uint32 packet_length
byte padding_length
byte[n1] payload; n1 = packet_length - padding_length - 1
byte[n2] random padding; n2 = padding_length
byte[m] mac (Message Authentication Code - MAC); m = mac_length
- packet_length: the length of the packet in bytes, not counting 'mac' or the 'packet_length' field itself ๐.
- padding_length: the length of 'random padding' in bytes ๐ง.
- payload: the useful stuff in the packet ๐. If compression has been negotiated, this field gets compressed. At the start, compression MUST be "none" ๐ โโ๏ธ.
- random padding: arbitrary-length padding, so that the total length of (packet_length || padding_length || payload || random padding) is a multiple of the cipher block size or 8, whichever is larger ๐ธ. There MUST be at least four bytes of padding ๐. The padding SHOULD consist of random bytes ๐ฒ. The maximum amount of padding is 255 bytes ๐ซถ.
- mac: the Message Authentication Code ๐. If message authentication has been negotiated, this field holds the MAC bytes. At the start, the MAC algorithm MUST be "none" ๐.
Note that the length of the concatenation of 'packet_length', 'padding_length', 'payload', and 'random padding' MUST be a multiple of the cipher block size or 8, whichever is larger ๐งฉ. This constraint MUST be enforced, even when using stream ciphers ๐จ. The 'packet_length' field is also encrypted, so handling it takes some special care when sending or receiving packets ๐ง. Also, adding variable amounts of 'random padding' may help thwart traffic analysis ๐ต๏ธโโ๏ธ.
The minimum size of a packet is 16 bytes (or the cipher block size, whichever is larger), plus 'mac' ๐ฆ. Implementations SHOULD decrypt the length after receiving the first 8 bytes (or the cipher block size, whichever is larger) of a packet ๐.
6.1. Maximum Packet Length
All implementations MUST be able to process packets with an uncompressed payload length of 32768 bytes or less and a total packet size of 35000 bytes or less (including 'packet_length', 'padding_length', 'payload', 'random padding', and 'mac') ๐. The 35000-byte maximum is an arbitrarily chosen value that's bigger than the uncompressed length above ๐. Implementations SHOULD support longer packets where they might be needed ๐. For example, if an implementation wants to send a huge number of certificates, larger packets MAY be sent if the identification string shows the other party can handle them ๐. However, implementations SHOULD check that the packet length is reasonable, so they can avoid denial of service and/or buffer overflow attacks ๐จ๐ญ.
6.2. Compression
If compression has been negotiated, only the 'payload' field gets compressed, using the negotiated algorithm ๐๏ธ. The 'packet_length' field and 'mac' are computed from the compressed payload ๐งฎ. Encryption happens after compression ๐.
Compression MAY be stateful, depending on the method ๐. Compression MUST be independent for each direction, and implementations MUST allow independent choosing of the algorithm for each direction ๐ . In practice, though, it is RECOMMENDED that the compression method be the same in both directions ๐ซถ.
The following compression methods are currently defined โจ:
none REQUIRED no compression
zlib OPTIONAL ZLIB (LZ77) compression
The "zlib" compression is described in [RFC1950] and [RFC1951] ๐. The compression context is initialized after each key exchange and gets passed from one packet to the next, with only a partial flush at the end of each packet ๐ง . A partial flush means the current compressed block ends and all data gets output ๐จ. If the current block is not a stored block, one or more empty blocks are added after it so there are at least 8 bits, counting from the start of the end-of-block code of the current block to the end of the packet payload ๐งฉ.
Additional methods may be defined as specified in [SSH-ARCH] and [SSH-NUMBERS] ๐ธ.
6.3. Encryption โจ
okay so, encryption gets negotiated during the key exchange, along with the actual key ๐ once encryption is switched on, the packet length, padding length, payload, and padding fields of every packet MUST be encrypted with the chosen algorithm. no skipping fields, bestie ๐
all the encrypted data going one way SHOULD be treated as one continuous stream ๐ so for example, initialization vectors SHOULD carry over from the end of one packet into the start of the next. and all ciphers SHOULD use keys with an effective key length of 128 bits or more ๐
the two directions run completely separately ๐ฆ each direction MUST run independently, and if local policy allows more than one algorithm, implementations MUST let each direction pick its own. that said, it is RECOMMENDED that both directions use the same algorithm, because that's what happens in practice ๐ซถ
here's the list of ciphers defined so far ๐
3des-cbc REQUIRED three-key 3DES in CBC mode
blowfish-cbc OPTIONAL Blowfish in CBC mode
twofish256-cbc OPTIONAL Twofish in CBC mode,
with a 256-bit key
twofish-cbc OPTIONAL alias for "twofish256-cbc"
(this is being retained
for historical reasons)
twofish192-cbc OPTIONAL Twofish with a 192-bit key
twofish128-cbc OPTIONAL Twofish with a 128-bit key
aes256-cbc OPTIONAL AES in CBC mode,
with a 256-bit key
aes192-cbc OPTIONAL AES with a 192-bit key
aes128-cbc RECOMMENDED AES with a 128-bit key
serpent256-cbc OPTIONAL Serpent in CBC mode, with
a 256-bit key
serpent192-cbc OPTIONAL Serpent with a 192-bit key
serpent128-cbc OPTIONAL Serpent with a 128-bit key
arcfour OPTIONAL the ARCFOUR stream cipher
with a 128-bit key
idea-cbc OPTIONAL IDEA in CBC mode
cast128-cbc OPTIONAL CAST-128 in CBC mode
none OPTIONAL no encryption; NOT RECOMMENDED
the "3des-cbc" cipher is three-key triple-DES, run as encrypt, decrypt, then encrypt ๐ the first 8 bytes of the key do the first encryption, the next 8 bytes do the decryption, and the last 8 bytes do the final encryption. that means 24 bytes of key data, though only 168 bits of it are actually used. for CBC mode, outer chaining MUST be used, so there's only ONE initialization vector. it's a block cipher with 8-byte blocks, and it's defined in [FIPS-46-3]. ngl, this one is a little bit of a drama queen ๐ it only has an effective key length of 112 bits ([SCHNEIER]), so it doesn't meet the rule that SSH encryption should use keys of 128 bits or more. BUT it's still REQUIRED for historical reasons! basically every implementation around when this was written supported it, and it's the fundamental interoperable algorithm everyone falls back on. someday another, stronger algorithm will be so prevalent and ubiquitous that "3des-cbc" gets deprecated by another STANDARDS ACTION ๐
the "blowfish-cbc" cipher is Blowfish in CBC mode with 128-bit keys [SCHNEIER] ๐ธ it's a block cipher with 8-byte blocks.
the "twofish-cbc" or "twofish256-cbc" cipher is Twofish in CBC mode with 256-bit keys, as described in [TWOFISH] โจ it's a block cipher with 16-byte blocks. "twofish192-cbc" is the same thing with a 192-bit key, and "twofish128-cbc" is the same with a 128-bit key ๐ฆ
the "aes256-cbc" cipher is AES (Advanced Encryption Standard) from [FIPS-197], in CBC mode, using a 256-bit key ๐ "aes192-cbc" is the same with a 192-bit key, and "aes128-cbc" is the same with a 128-bit key. iconic consistency ๐
the "serpent256-cbc" cipher is Serpent in CBC mode with a 256-bit key, as described in the Serpent AES submission ๐ง "serpent192-cbc" is the same with a 192-bit key, and "serpent128-cbc" is the same with a 128-bit key.
the "arcfour" cipher is the Arcfour stream cipher with 128-bit keys ๐ it's believed to be compatible with RC4 [SCHNEIER]. but ARCFOUR (and RC4) has problems with weak keys, so it should be used with caution ๐
the "idea-cbc" cipher is IDEA in CBC mode [SCHNEIER] โญ and "cast128-cbc" is CAST-128 in CBC mode with a 128-bit key [RFC2144] ๐
and then there's "none" ๐ it means no encryption at all, so it gives NO confidentiality protection, and it is NOT RECOMMENDED. if you pick this one, some stuff like password authentication might get turned off for security reasons. not me going "absolutely not" ๐ญ
additional methods may be defined as specified in [SSH-ARCH] and [SSH-NUMBERS] ๐ง
6.4. Data Integrity ๐
okay, data integrity is protected by attaching a MAC to each packet ๐ซถ the MAC is computed from a shared secret, the packet sequence number, and the contents of the packet. no cap, it's giving bodyguard energy ๐ช
the MAC algorithm and key get negotiated during the key exchange. at the very start no MAC is in effect, and its length MUST be zero ๐ธ once key exchange is done, the 'mac' for the selected algorithm gets computed before encryption, from the concatenation of the packet data:
mac = MAC(key, sequence_number || unencrypted_packet)
here, unencrypted_packet is the entire packet minus the 'mac' (so the length fields, 'payload', and 'random padding'), and sequence_number is an implicit packet sequence number stored as a uint32 ๐ข it starts at zero for the first packet and goes up by one after EVERY packet, whether or not encryption or a MAC is on. it's never reset, not even if keys or algorithms get renegotiated later ๐ it wraps back to zero after every 2^32 packets. and the sequence_number itself never goes over the wire, so it's a secret little sidekick ๐คซ
the MAC algorithms for each direction MUST run independently, and implementations MUST allow picking the algorithm separately for both directions. again, in practice it is RECOMMENDED that both directions use the same algorithm ๐ฆ
the value of 'mac' MUST be sent unencrypted as the LAST part of the packet ๐ how many 'mac' bytes you get depends on the algorithm you chose.
the MAC algorithms currently defined are ๐
hmac-sha1 REQUIRED HMAC-SHA1 (digest length = key
length = 20)
hmac-sha1-96 RECOMMENDED first 96 bits of HMAC-SHA1 (digest
length = 12, key length = 20)
hmac-md5 OPTIONAL HMAC-MD5 (digest length = key
length = 16)
hmac-md5-96 OPTIONAL first 96 bits of HMAC-MD5 (digest
length = 12, key length = 16)
none OPTIONAL no MAC; NOT RECOMMENDED
the "hmac-" algorithms are described in [RFC2104] โจ and the "-n" MACs just use the first n bits of the result. SHA-1 is described in [FIPS-180-2], and MD5 is described in [RFC1321] ๐
additional methods may be defined, as specified in [SSH-ARCH] and [SSH-NUMBERS] ๐ง
6.5. Key Exchange Methods ๐
the key exchange method is how the one-time session keys get generated for encryption and authentication, and how server authentication happens ๐
two key exchange methods are REQUIRED, and they're the must-haves ๐
diffie-hellman-group1-sha1 REQUIRED
diffie-hellman-group14-sha1 REQUIRED
both of these are described in Section 8 ๐ธ
additional methods may be defined as specified in [SSH-NUMBERS] ๐ง the name "diffie-hellman-group1-sha1" is used for a key exchange method using an Oakley group, as defined in [RFC2409]. SSH keeps its own group identifier space, which is logically separate from Oakley [RFC2412] and IKE. but for one extra group, the Working Group adopted the number assigned by [RFC3526], and that group got the name diffie-hellman-group14-sha1 ๐ซถ implementations should treat these names as opaque identifiers, and they should not assume any relationship between the groups used by SSH and the groups defined for IKE. no peeking, bestie ๐
6.6. Public Key Algorithms ๐โจ
okay bestie, this protocol is built to work with almost any public key format, encoding, and algorithm (signing and/or encryption) ๐ฆ it's giving flexible!!
there are a few things that define a public key type:
- Key format ๐: how the key is encoded and how certificates are represented. The key blobs in this protocol MAY contain certificates in addition to keys.
- Signature and/or encryption algorithms ๐ : some key types can't do both signing and encryption. Policy statements (like the ones in certificates) can also restrict how a key gets used. In that case, different key types SHOULD be defined for the different policy alternatives.
- Encoding of signatures and/or encrypted data ๐ธ: this includes (but isn't limited to) padding, byte order, and data formats.
these public key and/or certificate formats are defined right now ๐
ssh-dss REQUIRED sign Raw DSS Key
ssh-rsa RECOMMENDED sign Raw RSA Key
pgp-sign-rsa OPTIONAL sign OpenPGP certificates (RSA key)
pgp-sign-dss OPTIONAL sign OpenPGP certificates (DSS key)
more key types can be defined too, as specified in [SSH-ARCH] and in [SSH-NUMBERS] ๐
the key type MUST always be explicitly known (from algorithm negotiation or some other source) ๐ it's normally not included in the key blob, so don't go looking for it there!
certificates and public keys are encoded like this ๐ง
string certificate or public key format identifier
byte[n] key/certificate data
the certificate part can be a zero length string, but a public key is required. That public key is the one used for authentication ๐ the certificate sequence inside the certificate blob can be used to provide authorization โจ
public key/certificate formats that don't explicitly specify a signature format identifier MUST use the public key/certificate format identifier as the signature identifier ๐
signatures are encoded like this ๐
string signature format identifier (as specified by the
public key/certificate format)
byte[n] signature blob in format specific encoding.
the "ssh-dss" key format has this specific encoding ๐
string "ssh-dss"
mpint p
mpint q
mpint g
mpint y
here, the 'p', 'q', 'g', and 'y' parameters form the signature key blob ๐ซถ
signing and verifying with this key format is done according to the Digital Signature Standard [FIPS-186-2], using the SHA-1 hash [FIPS-180-2] ๐ฅ
the resulting signature is encoded like this ๐ท
string "ssh-dss"
string dss_signature_blob
the value for 'dss_signature_blob' is encoded as a string containing r, followed by s. These are 160-bit integers, with no lengths or padding, unsigned, and in network byte order ๐งฎ
the "ssh-rsa" key format has this specific encoding ๐
string "ssh-rsa"
mpint e
mpint n
here the 'e' and 'n' parameters form the signature key blob ๐ฆ
signing and verifying with this key format is done according to the RSASSA-PKCS1-v1_5 scheme in [RFC3447], using the SHA-1 hash ๐
the resulting signature is encoded like this ๐ธ
string "ssh-rsa"
string rsa_signature_blob
the value for 'rsa_signature_blob' is encoded as a string containing s. This is an integer, with no lengths or padding, unsigned, and in network byte order ๐
the "pgp-sign-rsa" method says the certificates, the public key, and the signature are all in OpenPGP compatible binary format ([RFC2440]) ๐ it also tells you the key is an RSA key โญ
the "pgp-sign-dss" method works the same way, but tells you the key is a DSS key ๐
7. Key Exchange โจ
okay so key exchange (kex) kicks off with each side sending name-lists of the algorithms it supports ๐ each side has a preferred algorithm in every category, and it's assumed that most implementations will be using the same preferred algorithm at any given time ๐ซถ each side MAY guess which algorithm the other side is using, and MAY send an initial key exchange packet that matches that algorithm, if that fits the preferred method ๐ฎ
the guess is considered wrong if:
- the kex algorithm and/or the host key algorithm was guessed wrong (the server and client have different preferred algorithms), or
- any of the other algorithms can't be agreed upon (the procedure is defined below in Section 7.1) ๐ญ
otherwise, the guess is considered right, and the optimistically sent packet MUST be handled as the first key exchange packet ๐
but if the guess was wrong, and one or both parties optimistically sent a packet, those packets MUST be ignored (even if the error in the guess wouldn't change the contents of the initial packet(s)) ๐ โโ๏ธ and the appropriate side MUST send the correct initial packet ๐ซ
a key exchange method uses explicit server authentication if the key exchange messages include a signature or other proof of the server's authenticity ๐ชช a key exchange method uses implicit server authentication if, to prove its authenticity, the server also has to prove that it knows the shared secret, K, by sending a message and a corresponding MAC that the client can verify ๐ต๏ธโโ๏ธ
the key exchange method defined by this document uses explicit server authentication ๐ however, key exchange methods with implicit server authentication MAY be used with this protocol ๐ after a key exchange with implicit server authentication, the client MUST wait for a response to its service request message before sending any further data ๐ no skipping ahead, bestie!!
7.1. Algorithm Negotiation
okay so key exchange starts with each side sending this packet ๐
byte SSH_MSG_KEXINIT
byte[16] cookie (random bytes)
name-list kex_algorithms
name-list server_host_key_algorithms
name-list encryption_algorithms_client_to_server
name-list encryption_algorithms_server_to_client
name-list mac_algorithms_client_to_server
name-list mac_algorithms_server_to_client
name-list compression_algorithms_client_to_server
name-list compression_algorithms_server_to_client
name-list languages_client_to_server
name-list languages_server_to_client
boolean first_kex_packet_follows
uint32 0 (reserved for future extension)
Every algorithm name-list MUST be a comma-separated list of algorithm names ๐ (check out Algorithm Naming in [SSH-ARCH] and the extra info in [SSH-NUMBERS]). Each supported (allowed) algorithm MUST be listed in order of preference, from most to least. Iconic behavior, honestly ๐
The first algorithm in each name-list MUST be the preferred (guessed) algorithm, and each name-list MUST contain at least one algorithm name. No empty lists, bestie ๐
cookie
The 'cookie' MUST be a random value generated by the sender. Its whole purpose is to make it impossible for either side to fully determine the keys and the session identifier ๐โจ
kex_algorithms
Key exchange algorithms were defined above. The first algorithm MUST be the preferred (and guessed) algorithm. If both sides make the same guess, that algorithm MUST be used. Otherwise, the following algorithm MUST be used to choose a key exchange method: iterate over the client's kex algorithms, one at a time, and choose the first algorithm that satisfies these conditions:
- the server also supports the algorithm,
- if the algorithm requires an encryption-capable host key, there is an encryption-capable algorithm on the server's server_host_key_algorithms that is also supported by the client, and
- if the algorithm requires a signature-capable host key, there is a signature-capable algorithm on the server's server_host_key_algorithms that is also supported by the client.
If no algorithm satisfying all these conditions can be found, the connection fails, and both sides MUST disconnect. Rip, no vibes ๐
server_host_key_algorithms
This is a name-list of the algorithms supported for the server host key. The server lists the algorithms it has host keys for, and the client lists the algorithms it's willing to accept. There MAY be multiple host keys for a host, possibly with different algorithms ๐๏ธ
Some host keys may not support both signatures and encryption (you can tell from the algorithm), so not every host key works for every key exchange method ๐
Algorithm selection depends on whether the chosen key exchange algorithm needs a signature or an encryption-capable host key. It MUST be possible to determine this from the public key algorithm name. The first algorithm on the client's name-list that meets the requirements and is also supported by the server MUST be chosen. If there is no such algorithm, both sides MUST disconnect.
encryption_algorithms
This is a name-list of acceptable symmetric encryption algorithms (also known as ciphers), in order of preference. The chosen encryption algorithm for each direction MUST be the first algorithm on the client's name-list that is also on the server's name-list. If there is no such algorithm, both sides MUST disconnect.
Note that "none" must be explicitly listed if it is to be acceptable. The defined algorithm names are listed in Section 6.3. ๐
mac_algorithms
This is a name-list of acceptable MAC algorithms in order of preference. The chosen MAC algorithm MUST be the first algorithm on the client's name-list that is also on the server's name-list. If there is no such algorithm, both sides MUST disconnect.
Note that "none" must be explicitly listed if it is to be acceptable. The MAC algorithm names are listed in Section 6.4. ๐
compression_algorithms
This is a name-list of acceptable compression algorithms in order of preference. The chosen compression algorithm MUST be the first algorithm on the client's name-list that is also on the server's name-list. If there is no such algorithm, both sides MUST disconnect.
Note that "none" must be explicitly listed if it is to be acceptable. The compression algorithm names are listed in Section 6.2. ๐๏ธ
languages
This is a name-list of language tags in order of preference [RFC3066]. Both parties MAY ignore this name-list. If there are no language preferences, this name-list SHOULD be empty, as defined in Section 5 of [SSH-ARCH]. Language tags SHOULD NOT be present unless the sending party knows they're needed ๐
first_kex_packet_follows
This indicates whether a guessed key exchange packet follows. If a guessed packet will be sent, this MUST be TRUE. If no guessed packet will be sent, this MUST be FALSE.
After receiving the SSH_MSG_KEXINIT packet from the other side, each party will know whether their guess was right. If the other party's guess was wrong and this field was TRUE, the next packet MUST be silently ignored, and both sides MUST then act as determined by the negotiated key exchange method. If the guess was right, key exchange MUST continue using the guessed packet. Main character plot twist energy ๐ญ
After the SSH_MSG_KEXINIT message exchange, the key exchange algorithm runs. It may involve several packet exchanges, as specified by the key exchange method.
Once a party has sent an SSH_MSG_KEXINIT message for key exchange or re-exchange, it MUST NOT send any messages other than the following until it has sent an SSH_MSG_NEWKEYS message (Section 7.3). โ ๏ธ
- Transport layer generic messages (1 to 19) (but SSH_MSG_SERVICE_REQUEST and SSH_MSG_SERVICE_ACCEPT MUST NOT be sent);
- Algorithm negotiation messages (20 to 29) (but further SSH_MSG_KEXINIT messages MUST NOT be sent);
- Specific key exchange method messages (30 to 49).
The provisions of Section 11 apply to unrecognized messages.
Note, however, that during a key re-exchange, after sending an SSH_MSG_KEXINIT message, each party MUST be prepared to process an arbitrary number of messages that may be in-flight before receiving an SSH_MSG_KEXINIT message from the other party. Chaos but make it structured ๐ช๏ธ๐
7.2. Output from Key Exchange
The key exchange produces two values: a shared secret K, and an exchange hash H. Encryption and authentication keys get derived from these ๐. The exchange hash H from the first key exchange is also used as the session identifier, which is a unique identifier for this connection. Authentication methods use it as part of the data that gets signed as proof of possession of a private key. Once computed, the session identifier does not change, even if keys are later re-exchanged. Iconic permanence ๐
Each key exchange method specifies a hash function that gets used in the key exchange. The same hash algorithm MUST be used in key derivation. Here, we'll call it HASH. ๐งฎ
Encryption keys MUST be computed as HASH, of a known value and K, as follows:
- Initial IV client to server:
HASH(K || H || "A" || session_id)(Here K is encoded as mpint and "A" as byte and session_id as raw data. "A" means the single character A, ASCII 65.) - Initial IV server to client:
HASH(K || H || "B" || session_id) - Encryption key client to server:
HASH(K || H || "C" || session_id) - Encryption key server to client:
HASH(K || H || "D" || session_id) - Integrity key client to server:
HASH(K || H || "E" || session_id) - Integrity key server to client:
HASH(K || H || "F" || session_id)
Key data MUST be taken from the beginning of the hash output. As many bytes as needed are taken from the beginning of the hash value. If the key length needed is longer than the output of HASH, the key is extended by computing HASH of the concatenation of K, H, and the entire key so far, then appending the resulting bytes (as many as HASH generates) to the key. This repeats until there's enough key material, and the key is taken from the beginning of this value. In other words:
K1 = HASH(K || H || X || session_id) (X is e.g., "A")
K2 = HASH(K || H || K1)
K3 = HASH(K || H || K1 || K2)
...
key = K1 || K2 || K3 || ...
This process will lose entropy if the amount of entropy in K is larger than the internal state size of HASH. ๐ฌ
7.3. Taking Keys Into Use
Key exchange ends when each side sends an SSH_MSG_NEWKEYS message. This message is sent with the old keys and algorithms. All messages sent after it MUST use the new keys and algorithms. ๐
When this message is received, the new keys and algorithms MUST be used for receiving.
The purpose of this message is to make sure a party can still respond with an SSH_MSG_DISCONNECT message that the other party understands if something goes wrong with the key exchange. Safety net, bestie ๐ซถ
byte SSH_MSG_NEWKEYS
8. Diffie-Hellman Key Exchange โจ
okay so the Diffie-Hellman (DH) key exchange gives you a shared secret that neither party could figure out alone ๐ and it's combined with a signature made with the host key, so you get host authentication too. this method gives you explicit server authentication, as defined in Section 7. iconic โจ
here's the setup. C is the client, S is the server, p is a large safe prime, g is a generator for a subgroup of GF(p), and q is the order of that subgroup. V_S is S's identification string and V_C is C's identification string. K_S is S's public host key. I_C is C's SSH_MSG_KEXINIT message and I_S is S's SSH_MSG_KEXINIT message, and both have already been exchanged before this part begins ๐ฆ
- C picks a random number x (
1 < x < q) and computese = g^x mod p. then C sends e to S. - S picks a random number y (
0 < y < q) and computesf = g^y mod p. S receives e, then computesK = e^y mod pandH = hash(V_C || V_S || I_C || I_S || K_S || e || f || K)(these elements are encoded according to their types, see below). S also makes a signature s on H with its private host key, and sends (K_S || f || s) to C. the signing operation may involve a second hashing operation ๐ - C checks that K_S really is the host key for S (for example, with certificates or a local database). C is also allowed to accept the key without verifying it, but doing that will make the protocol insecure against active attacks. it might still be desirable for practical reasons in the short term in many environments, so sometimes people do it ๐ then C computes
K = f^x mod pandH = hash(V_C || V_S || I_C || I_S || K_S || e || f || K), and verifies the signature s on H.
values of 'e' or 'f' that are not in the range [1, p-1] MUST NOT be sent or accepted by either side. if that rule gets broken, the key exchange fails. no vibes allowed ๐
this is implemented with the following messages. the hash algorithm used to compute the exchange hash is defined by the method name, and it's called HASH. the public key algorithm for signing is negotiated in the SSH_MSG_KEXINIT messages ๐ธ
first, the client sends this:
byte SSH_MSG_KEXDH_INIT
mpint e
then the server responds with this:
byte SSH_MSG_KEXDH_REPLY
string server public host key and certificates (K_S)
mpint f
string signature of H
the hash H is the HASH hash of the concatenation of the following:
string V_C, the client's identification string (CR and LF
excluded)
string V_S, the server's identification string (CR and LF
excluded)
string I_C, the payload of the client's SSH_MSG_KEXINIT
string I_S, the payload of the server's SSH_MSG_KEXINIT
string K_S, the host key
mpint e, exchange value sent by the client
mpint f, exchange value sent by the server
mpint K, the shared secret
this value is called the exchange hash, and it's what authenticates the key exchange. the exchange hash SHOULD be kept secret ๐คซ
the signature algorithm MUST be applied over H, not the original data. most signature algorithms already include hashing and some extra padding (e.g., "ssh-dss" specifies SHA-1 hashing). so in that case the data is first hashed with HASH to compute H, and then H gets hashed with SHA-1 as part of the signing operation. it's giving layers ๐ง
8.1. diffie-hellman-group1-sha1
the "diffie-hellman-group1-sha1" method is DH key exchange with SHA-1 as HASH, using Oakley Group 2 [RFC2409] (the 1024-bit MODP Group). this method MUST be supported for interoperability, because all of the known implementations currently support it. ngl the name is kind of a plot twist: it says "group1" even though it actually uses Oakley Group 2 ๐
8.2. diffie-hellman-group14-sha1
the "diffie-hellman-group14-sha1" method is a Diffie-Hellman key exchange with SHA-1 as HASH and Oakley Group 14 [RFC3526] (the 2048-bit MODP Group). it MUST also be supported ๐
9. Key Re-Exchange
key re-exchange starts when someone sends an SSH_MSG_KEXINIT packet while they aren't already doing a key exchange (as described in Section 7.1). when that message is received, a party MUST respond with its own SSH_MSG_KEXINIT message, unless the SSH_MSG_KEXINIT it got was already a reply. either party MAY start the re-exchange, but roles MUST NOT be changed. the server stays the server and the client stays the client, no role swaps ๐
the re-exchange uses whatever encryption was in effect when the exchange started. encryption, compression, and MAC methods don't change until a new SSH_MSG_NEWKEYS is sent after the key exchange (just like the initial one). the re-exchange gets processed exactly like the initial key exchange, except the session identifier stays the same. it's totally allowed to change some or all of the algorithms during the re-exchange, and host keys can change too ๐ฆ all keys and initialization vectors get recomputed after the exchange, and the compression and encryption contexts get reset.
it is RECOMMENDED that the keys be changed after each gigabyte of transmitted data or after each hour of connection time, whichever comes sooner. BUT the re-exchange is a public key operation, so it takes a fair amount of processing power and should not be performed too often. main character energy means not overdoing it ๐ง
more application data can be sent after the SSH_MSG_NEWKEYS packet has been sent. the key exchange doesn't affect the protocols that sit above the SSH transport layer, so your stuff keeps flowing โจ
10. Service Request
after the key exchange, the client requests a service. each service is identified by a name. the format of names, and the procedures for defining new names, are defined in [SSH-ARCH] and [SSH-NUMBERS] ๐
right now, these names are reserved:
ssh-userauth
ssh-connection
service names follow a similar local naming policy to the one used for algorithm names. a local service should use the PRIVATE USE syntax of "servicename@domain" ๐
byte SSH_MSG_SERVICE_REQUEST
string service name
if the server rejects the service request, it SHOULD send an appropriate SSH_MSG_DISCONNECT message, and it MUST disconnect. ๐
when the service starts, it may have access to the session identifier that was generated during the key exchange ๐
if the server supports the service (and lets the client use it), it MUST respond with this:
byte SSH_MSG_SERVICE_ACCEPT
string service name
message numbers used by services should be in the area reserved for them (see [SSH-ARCH] and [SSH-NUMBERS]). the transport level keeps processing its own messages, so they don't get lost in the shuffle ๐ง
note that after a key exchange with implicit server authentication, the client MUST wait for a response to its service request message before sending any further data. patience is a virtue, bestie ๐ซถ
11. Additional Messages
either party may send any of the following messages at any time ๐
11.1. Disconnection Message
byte SSH_MSG_DISCONNECT
uint32 reason code
string description in ISO-10646 UTF-8 encoding [RFC3629]
string language tag [RFC3066]
this message causes an immediate termination of the connection. all implementations MUST be able to process this message, and they SHOULD be able to send it. ๐จ
the sender MUST NOT send or receive any data after this message, and the recipient MUST NOT accept any data after receiving it. the Disconnection Message 'description' string gives a more specific explanation in human-readable form. the Disconnection Message 'reason code' gives the reason in a more machine-readable format (suitable for localization). it can have the values shown in the table below. the decimal representation is shown in the table for readability, but the values are actually uint32 values ๐ข
Symbolic name reason code
------------- -----------
SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1
SSH_DISCONNECT_PROTOCOL_ERROR 2
SSH_DISCONNECT_KEY_EXCHANGE_FAILED 3
SSH_DISCONNECT_RESERVED 4
SSH_DISCONNECT_MAC_ERROR 5
SSH_DISCONNECT_COMPRESSION_ERROR 6
SSH_DISCONNECT_SERVICE_NOT_AVAILABLE 7
SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8
SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE 9
SSH_DISCONNECT_CONNECTION_LOST 10
SSH_DISCONNECT_BY_APPLICATION 11
SSH_DISCONNECT_TOO_MANY_CONNECTIONS 12
SSH_DISCONNECT_AUTH_CANCELLED_BY_USER 13
SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14
SSH_DISCONNECT_ILLEGAL_USER_NAME 15
if the 'description' string gets displayed, the control character filtering discussed in [SSH-ARCH] should be used to avoid attacks that send terminal control characters. this is a real safety thing, so don't skip it ๐
requests for assignments of new Disconnection Message 'reason code' values (and the associated 'description' text) in the range of 0x00000010 to 0xFDFFFFFF MUST be done through the IETF CONSENSUS method, as described in [RFC2434]. the Disconnection Message 'reason code' values in the range of 0xFE000000 through 0xFFFFFFFF are reserved for PRIVATE USE. as noted, the actual instructions to the IANA are in [SSH-NUMBERS] ๐
11.2. Ignored Data Message
byte SSH_MSG_IGNORE
string data
all implementations MUST understand (and ignore) this message at any time after receiving the identification string. no implementation is required to send them. this message can be used as an extra protection measure against advanced traffic analysis techniques ๐ต๏ธโโ๏ธโจ
11.3. Debug Message
byte SSH_MSG_DEBUG
boolean always_display
string message in ISO-10646 UTF-8 encoding [RFC3629]
string language tag [RFC3066]
all implementations MUST understand this message, but they're allowed to ignore it. it's used to send information that may help with debugging ๐ if 'always_display' is TRUE, the message SHOULD be displayed. otherwise, it SHOULD NOT be displayed unless the user has explicitly asked for debugging information ๐ค
the 'message' doesn't need to contain a newline. it is, however, allowed to be made of multiple lines separated by CRLF (Carriage Return - Line Feed) pairs ๐
if the 'message' string gets displayed, the terminal control character filtering discussed in [SSH-ARCH] should be used to avoid attacks that send terminal control characters. same energy as before, bestie, don't skip it ๐
11.4. Reserved Messages
an implementation MUST respond to all unrecognized messages with an SSH_MSG_UNIMPLEMENTED message, in the order the messages were received. those messages MUST be otherwise ignored. later protocol versions may define other meanings for these message types ๐ฎ
byte SSH_MSG_UNIMPLEMENTED
uint32 packet sequence number of rejected message
12. Summary of Message Numbers ๐ขโจ
okay so this is the cheat sheet bestie ๐ซถ every message and its number, all in one place:
SSH_MSG_DISCONNECT 1
SSH_MSG_IGNORE 2
SSH_MSG_UNIMPLEMENTED 3
SSH_MSG_DEBUG 4
SSH_MSG_SERVICE_REQUEST 5
SSH_MSG_SERVICE_ACCEPT 6
SSH_MSG_KEXINIT 20
SSH_MSG_NEWKEYS 21
ngl the gap between 21 and 30 is giving "saving room for later" ๐ธ and numbers 30-49 are reserved for kex packets ๐ different kex methods are allowed to reuse those same numbers, so the number alone doesn't tell you which method is in use ๐
13. IANA Considerations ๐๐
this one's a little plot twist ๐ง this document is part of a set, so the IANA stuff for the whole SSH protocol is spread out across [SSH-ARCH], [SSH-USERAUTH], [SSH-CONNECT], and this document, and all the details live in [SSH-NUMBERS] ๐
14. Security Considerations ๐๐ญ
okay this section is serious business, no cap ๐ this protocol gives you a secure encrypted channel over a network that is NOT secure (the internet is kinda chaotic lol). it does server host authentication, key exchange, encryption, and integrity protection all in one go ๐ก๏ธ it also comes up with a unique session ID that higher-level protocols can use ๐โจ
the full security considerations are in [SSH-ARCH], so go read that one too, periodt ๐๐
15. References ๐๐
15.1. Normative References ๐
- [SSH-ARCH] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH) Protocol Architecture", RFC 4251, January 2006.
- [SSH-USERAUTH] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH) Authentication Protocol", RFC 4252, January 2006.
- [SSH-CONNECT] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH) Connection Protocol", RFC 4254, January 2006.
- [SSH-NUMBERS] Lehtinen, S. and C. Lonvick, Ed., "The Secure Shell (SSH) Protocol Assigned Numbers", RFC 4250, January 2006.
- [RFC1321] Rivest, R., "The MD5 Message-Digest Algorithm ", RFC 1321, April 1992.
- [RFC1950] Deutsch, P. and J-L. Gailly, "ZLIB Compressed Data Format Specification version 3.3", RFC 1950, May 1996.
- [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification version 1.3", RFC 1951, May 1996.
- [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-Hashing for Message Authentication", RFC 2104, February 1997.
- [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
- [RFC2144] Adams, C., "The CAST-128 Encryption Algorithm", RFC 2144, May 1997.
- [RFC2409] Harkins, D. and D. Carrel, "The Internet Key Exchange (IKE)", RFC 2409, November 1998.
- [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
- [RFC2440] Callas, J., Donnerhacke, L., Finney, H., and R. Thayer, "OpenPGP Message Format", RFC 2440, November 1998.
- [RFC3066] Alvestrand, H., "Tags for the Identification of Languages", BCP 47, RFC 3066, January 2001.
- [RFC3447] Jonsson, J. and B. Kaliski, "Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1", RFC 3447, February 2003.
- [RFC3526] Kivinen, T. and M. Kojo, "More Modular Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)", RFC 3526, May 2003.
- [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003.
- [FIPS-180-2] US National Institute of Standards and Technology, "Secure Hash Standard (SHS)", Federal Information Processing Standards Publication 180-2, August 2002.
- [FIPS-186-2] US National Institute of Standards and Technology, "Digital Signature Standard (DSS)", Federal Information Processing Standards Publication 186-2, January 2000.
- [FIPS-197] US National Institute of Standards and Technology, "Advanced Encryption Standard (AES)", Federal Information Processing Standards Publication 197, November 2001.
- [FIPS-46-3] US National Institute of Standards and Technology, "Data Encryption Standard (DES)", Federal Information Processing Standards Publication 46-3, October 1999.
- [SCHNEIER] Schneier, B., "Applied Cryptography Second Edition: protocols algorithms and source in code in C", John Wiley and Sons, New York, NY, 1996.
- [TWOFISH] Schneier, B., "The Twofish Encryptions Algorithm: A 128-Bit Block Cipher, 1st Edition", March 1999.
15.2. Informative References ๐ง
- [RFC0894] Hornig, C., "Standard for the transmission of IP datagrams over Ethernet networks", STD 41, RFC 894, April 1984.
- [RFC1661] Simpson, W., "The Point-to-Point Protocol (PPP)", STD 51, RFC 1661, July 1994.
- [RFC2412] Orman, H., "The OAKLEY Key Determination Protocol", RFC 2412, November 1998.
- [ssh-1.2.30] Ylonen, T., "ssh-1.2.30/RFC", File within compressed tarball ftp://ftp.funet.fi/pub/unix/security/login/ssh/ssh-1.2.30.tar.gz, November 1995.
Authors' Addresses ๐ก๐
Tatu Ylonen
SSH Communications Security Corp
Valimotie 17
00380 Helsinki
Finland
EMail: ylo@ssh.com
Chris Lonvick (editor)
Cisco Systems, Inc.
12515 Research Blvd.
Austin 78759
USA
EMail: clonvick@cisco.com
Trademark Notice ๐ท๏ธ
"ssh" is a registered trademark in the United States and/or other countries. โข๏ธ bestie knows whose name is on it ๐
Full Copyright Statement โ๏ธ
Copyright (C) The Internet Society (2006).
This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.
This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Intellectual Property ๐ฆ
The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.
Acknowledgement ๐ท
Funding for the RFC Editor function is provided by the IETF Administrative Support Activity (IASA). ๐ซถ๐
๐ฆ where this page comes from
- ietf: rfc4253 2006-01
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms