Decoding RFC 4251 binary data in Common Lisp
RFC 4251 describes the Secure Shell (SSH) Protocol Architecture, which also contains a section of the various Data Type Representations Used in the SSH Protocols.
Section 5 of the RFC document defines the data types, which are also used to describe an OpenSSH certificate key, and my goal is to be able to decode an OpenSSH certificate using Common Lisp by following the specification defined in the RFC document.
In this post I will show you how to decode different types of data from a binary stream, which have been encoded using the RFC 4251, section 5 specification.
In order to parse RFC 4251 encoded data we will use the cl-rfc4251 system.
CL-USER> (ql:quickload :cl-rfc4251)
The table below summarizes the supported data types, that can be
decoded by the
cl-rfc4251 system. The
RFC 4251 and
type columns specify the mapping between the RFC defined data type
name and the keywords used to decode a given value in Common Lisp.
|RFC 4251||cl-rfc4251 type||Description|
||An arbitrary 8-bit value (octet)|
||A boolean value, either
||Unsigned 16-bit integer in big-endian byte order|
||Unsigned 32-bit integer in big-endian byte order|
||Unsigned 64-bit integer in big-endian byte order|
||Arbitrary length string|
||Multiple precision integer|
||A list of string names|
In addition to the above data types, the following ones are also supported, which are not directly specified in RFC 4251, but are also useful on their own, depending on your use case.
||Read a sequence of raw bytes up to a given length|
||Unsigned 16-bit integer in little-endian byte order|
||Unsigned 32-bit integer in little-endian byte order|
||Unsigned 64-bit integer in little-endian byte order|
cl-rfc4251 system exports the generic function
DECODE via the
CL-RFC4251 (also available via its nickname
RFC4251:DECODE function takes a type and a binary stream
from which to decode. Some types also take additional keyword
:raw-bytes), which allow to specify the number of
bytes to be decoded.
In all of the examples that follow below
s represents a binary
stream. You can also use the
function to create a binary stream, which uses a vector for the
Decode raw bytes with a given length from the binary stream
CL-USER> (rfc4251:decode :raw-bytes s :length 2)
Decode a 16-bit unsigned integer represented in big-endian byte
order from a given binary
CL-USER> (rfc4251:decode :uint16 s)
Decode a multiple precision integer represented in two’s
complement format from the
CL-USER> (rfc4251:decode :mpint s)
You can find other examples in the included test suite.