Projects/AEAD encryption API
The Microsoft SSPI provides an interface for in-place encryption of messages (see MS-KILE section 3.4.5.4ff). This interface also permits additional data to be included in the checksum generated to protect integrity. Such a facility is called authenticated encryption with additional data (AEAD).
The SSPI works at the GSS-API layer, rather than the raw Kerberos layer.
This project proposes to extend the raw Kerberos cryptographic API (krb5_c_*) in order to make it possible to implement these SSPI facilities in an extension to the GSS-API. The ultimate consumer of these applications is typically DCE-style RPC, although the facilities could be used by other applications.
Contents
Functional Requirements
- Support a scatter-gather interface. Encryption APIs take a series of regions to operate on, rather than a single krb5_data.
- Support in-place encryption of data: the same buffer is used for input and output.
- Support plaintext chunks that are signed but not encrypted.
- Coordinate with Heimdal to avoid introducing unnecessary incompatibilities with their API.
- When no additional data is included, the resulting ciphertext needs to be the same as if the existing APIs are used after concatenating the plaintext.
- When additional data is used, the ciphertext needs to be compatible with Windows.
Basic approach
A new structure is defined: krb5_crypto_iov to describe a region of text to be encrypted or decrypted.
typedef struct _krb5_crypto_iov { krb5_cryptotype flags; krb5_data data; } krb5_crypto_iov;
The flags member describes the type of the iov. The data member points to the memory that will be manipulated. All iov APIs take a pointer to the first element of an array of krb5_crypto_iovs along with the size of that array. Buffer contents are manipulated in-place; data is overwritten. Callers must allocate the right number of krb5_crypto_iov structures before calling into an iov API.
Several types of krb5_crypto_iov are supported:
#define KRB5_CRYPTO_TYPE_EMPTY 0 /* [in] ignored */ #define KRB5_CRYPTO_TYPE_HEADER 1 /* [out] header */ #define KRB5_CRYPTO_TYPE_DATA 2 /* [in, out] plaintext */ #define KRB5_CRYPTO_TYPE_SIGN_ONLY 3 /* [in] associated data */ #define KRB5_CRYPTO_TYPE_PADDING 4 /* [out] padding */ #define KRB5_CRYPTO_TYPE_TRAILER 5 /* [out] checksum for encrypt */ #define KRB5_CRYPTO_TYPE_CHECKSUM 6 /* [out] checksum for MIC */ #define KRB5_CRYPTO_TYPE_STREAM 7 /* [in, out] An entire encrypted message for decryption*/
Typically the iov array would contain the following on a call to encryption functions:
- A header buffer of the appropriate length
- One or more data buffer
- Zero or more sign_only buffers
- A padding buffer of the appropriate length
- A trailer buffer of the appropriate length
The function krb5_crypto_length can be used to determine the length for padding, trailer and header chunks.
On encryption, the data and sign-only buffers would be populated; other buffers would point to allocated but empty memory. When an encryption function returns success all buffers would be populated and filled.
On decryption, all buffers would be populated, and the data buffer would include plaintext on successful return from krb5_c_decrypt_iov. Checksum operations work similarly.
An alternate calling sequence is supported for decryption. The initial iov array may contain:
- A buffer of type KRB5_CRYPTO_TYPE_STREAM containing the ciphertext (with header and trailer)
- Zero or more buffers of type KRB5_CRYPTO_TYPE_SIGN_ONLY
- One buffer of type KRB5_CRYPTO_TYPE_DATA to hold the output
This indicates that the caller does not know the decomposition of the encrypted data into header, trailer and data. On successful return, the array is updated to replace the buffer of type KRB5_CRYPTO_TYPE_DATA to point to a subset of the stream buffer. That subset will be modified in place to contain the plaintext.
Note that on return from a krb5_c_*_iov call, the lengths in the iov structure are adjusted to reflect actual lengths used. For example, if the padding length is too large, the length will be reduced. Lengths are never increased.
Constraints
- relative ordering between sign_only and data chunks may or may not be preserved depending on the crypto system. Modern crypto systems (not currently specified for use in Kerberos) such as those described in RFC 5116 handle sign_only data in a separate pass from encrypted data. The existing Kerberos crypto systems have one pass, so an iov containing (data,sign_only,sign_only) will produce different results than one containing (sign_only,data,sign_only). Thus applications should use a consistent order on sender and receiver but must not depend on this for security.
- The header buffer must be the first buffer in the iov array.
- The header and trailer are read_only buffers on decryption
- With the exception of adjusting the length when stream buffers are used, the iov array itself is read-only on decryption.
Implementation notes
While the API supports in-place encryption the implementation does not significantly reduce the number of copies.
The implementation is parallel to the existing implementation. In particular, the existing APIs are not implemented in terms of the new APIs. New code is required (with significant copying) from the generic API layer down to the enc_provider layer, although of course raw crypto primitives are used.
Currently sanity checking buffers--for example making sure there is a trailer chunk--is left to the enc_provider implementations.
Public APIs
The following APIs are introduced by this project:
krb5_error_code KRB5_CALLCONV krb5_c_make_checksum_iov (krb5_context context, krb5_cksumtype cksumtype, const krb5_keyblock *key, krb5_keyusage usage, krb5_crypto_iov *data, size_t num_data);
Create a checksum in the KRB5_CRYPTO_TYPE_CHECKSUM element over the data and sign_only chunks. Only the KRB5_CRYPTO_TYPE_CHECKSUM region is modified.
krb5_error_code KRB5_CALLCONV krb5_c_verify_checksum_iov (krb5_context context, krb5_cksumtype cksumtype, const krb5_keyblock *key, krb5_keyusage usage, const krb5_crypto_iov *data, size_t num_data, krb5_boolean *valid);
Confirm that the checksum in the KRB5_CRYPTO_TYPE_CHECKSUM element is a valid checksum of the data and sign_only regions in the iov.
krb5_error_code KRB5_CALLCONV krb5_c_encrypt_iov (krb5_context context, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *cipher_state, krb5_crypto_iov *data, size_t num_data); krb5_error_code KRB5_CALLCONV krb5_c_decrypt_iov (krb5_context context, const krb5_keyblock *key, krb5_keyusage usage, const krb5_data *cipher_state, krb5_crypto_iov *data, size_t num_data);
Encrypt or decrypt data in place supporting AEAD. The iov needs to contain entries as described in the basic overview. Data chunks are modified in-place.
krb5_error_code KRB5_CALLCONV krb5_c_crypto_length (krb5_context context, krb5_enctype enctype, krb5_cryptotype type, unsigned int *size);
Return the length needed for some data structure associated with an encryption system.
krb5_error_code KRB5_CALLCONV krb5_c_crypto_length_iov (krb5_context context, krb5_enctype enctype, krb5_crypto_iov *data, size_t num_data);
Fill in the lengths in the iov array for header, trailer and padding. Padding is set to the actual padding required based on the provided data buffers. Typically this API is used after setting up the data buffers and sign_only buffers, but before actually allocating header, trailer and padding.
krb5_error_code KRB5_CALLCONV krb5_c_padding_length (krb5_context context, krb5_enctype enctype, size_t data_length, unsigned int *size);
Return the number of padding octets required to pad data_length octets of plaintext.
Testing plan
t_encrypt.c will be expanded to:
- decrypt data using the iov APIs to confirm that data encrypted using the traditional APIs can be read with the IOV APIs
- Encrypt data with the iov APIs and confirm that if no sign_data is used, the traditional APIs can decrypt.
- Encrypt data with a constant sign_data and decrypt to confirm that works
Review
This section documents the review of the project according to Project policy. It is divided into multiple sections. First, approvals should be listed. To list an approval type
- #~~~~
on its own line. The next section is for discussion. Use standard talk page conventions. In particular, sign comments with
- --~~~~
and indent replies.
Members of Krbcore raising Blocking objections should preface their comment with {{project-block}}. The member who raised the objection should remove this markup when their objection is handled.
Approvals
- TomYu 11:05, 15 December 2008 (EST)