Added generic EAX and CCM implementations.
authorThomas Pornin <pornin@bolet.org>
Mon, 23 Oct 2017 21:27:28 +0000 (23:27 +0200)
committerThomas Pornin <pornin@bolet.org>
Mon, 23 Oct 2017 21:27:28 +0000 (23:27 +0200)
14 files changed:
inc/bearssl_aead.h
inc/bearssl_block.h
mk/Rules.mk
mk/mkrules.sh
src/aead/ccm.c [new file with mode: 0644]
src/aead/eax.c [new file with mode: 0644]
src/aead/gcm.c
src/symcipher/aes_big_ctrcbc.c [new file with mode: 0644]
src/symcipher/aes_ct64_ctrcbc.c [new file with mode: 0644]
src/symcipher/aes_ct_ctrcbc.c [new file with mode: 0644]
src/symcipher/aes_small_ctrcbc.c [new file with mode: 0644]
src/symcipher/aes_x86ni_ctrcbc.c [new file with mode: 0644]
test/test_crypto.c
test/test_speed.c

index 09cb9e8..b1e52a3 100644 (file)
@@ -127,7 +127,7 @@ extern "C" {
  *
  *   - Nonce, plaintext and additional authenticated data all consist
  *     in an integral number of bytes. There is no provision to use
- *     elements whose lengh in bits is not a multiple of 8.
+ *     elements whose length in bits is not a multiple of 8.
  *
  * Each AEAD algorithm has its own requirements and limits on the sizes
  * of additional data and plaintext. This API does not provide any
@@ -169,6 +169,9 @@ extern "C" {
  * Note that there is no OOP method for context initialisation: the
  * various AEAD algorithms have different requirements that would not
  * map well to a single initialisation API.
+ *
+ * The OOP API is not provided for CCM, due to its specific requirements
+ * (length of plaintext must be known in advance).
  */
 
 /**
@@ -213,7 +216,7 @@ struct br_aead_class_ {
         *
         * \param cc     AEAD context structure.
         * \param data   pointer to additional authenticated data.
-        * \param len    length of additiona authenticated data (in bytes).
+        * \param len    length of additional authenticated data (in bytes).
         */
        void (*aad_inject)(const br_aead_class **cc,
                const void *data, size_t len);
@@ -266,6 +269,8 @@ struct br_aead_class_ {
         * `check_tag()` function may be used to compute and check the
         * tag value.
         *
+        * Tag length depends on the AEAD algorithm.
+        *
         * \param cc    AEAD context structure.
         * \param tag   destination buffer for the tag.
         */
@@ -282,11 +287,44 @@ struct br_aead_class_ {
         * data or the tag was altered in transit, normally leading to
         * wholesale rejection of the complete message.
         *
+        * Tag length depends on the AEAD algorithm.
+        *
         * \param cc    AEAD context structure.
-        * \param tag   tag value to compare with (16 bytes).
+        * \param tag   tag value to compare with.
         * \return  1 on success (exact match of tag value), 0 otherwise.
         */
        uint32_t (*check_tag)(const br_aead_class **cc, const void *tag);
+
+       /**
+        * \brief Compute authentication tag (with truncation).
+        *
+        * This function is similar to `get_tag()`, except that the tag
+        * length is provided. Some AEAD algorithms allow several tag
+        * lengths, usually by truncating the normal tag. Shorter tags
+        * mechanically increase success probability of forgeries.
+        * The range of allowed tag lengths depends on the algorithm.
+        *
+        * \param cc    AEAD context structure.
+        * \param tag   destination buffer for the tag.
+        * \param len   tag length (in bytes).
+        */
+       void (*get_tag_trunc)(const br_aead_class **cc, void *tag, size_t len);
+
+       /**
+        * \brief Compute and check authentication tag (with truncation).
+        *
+        * This function is similar to `check_tag()` except that it
+        * works over an explicit tag length. See `get_tag()` for a
+        * discussion of explicit tag lengths; the range of allowed tag
+        * lengths depends on the algorithm.
+        *
+        * \param cc    AEAD context structure.
+        * \param tag   tag value to compare with.
+        * \param len   tag length (in bytes).
+        * \return  1 on success (exact match of tag value), 0 otherwise.
+        */
+       uint32_t (*check_tag_trunc)(const br_aead_class **cc,
+               const void *tag, size_t len);
 };
 
 /**
@@ -379,7 +417,7 @@ void br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len);
  *
  * \param ctx    GCM context structure.
  * \param data   pointer to additional authenticated data.
- * \param len    length of additiona authenticated data (in bytes).
+ * \param len    length of additional authenticated data (in bytes).
  */
 void br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len);
 
@@ -449,11 +487,478 @@ void br_gcm_get_tag(br_gcm_context *ctx, void *tag);
  */
 uint32_t br_gcm_check_tag(br_gcm_context *ctx, const void *tag);
 
+/**
+ * \brief Compute GCM authentication tag (with truncation).
+ *
+ * This function is similar to `br_gcm_get_tag()`, except that it allows
+ * the tag to be truncated to a smaller length. The intended tag length
+ * is provided as `len` (in bytes); it MUST be no more than 16, but
+ * it may be smaller. Note that decreasing tag length mechanically makes
+ * forgeries easier; NIST SP 800-38D specifies that the tag length shall
+ * lie between 12 and 16 bytes (inclusive), but may be truncated down to
+ * 4 or 8 bytes, for specific applications that can tolerate it. It must
+ * also be noted that successful forgeries leak information on the
+ * authentication key, making subsequent forgeries easier. Therefore,
+ * tag truncation, and in particular truncation to sizes lower than 12
+ * bytes, shall be envisioned only with great care.
+ *
+ * The tag is written in the provided `tag` buffer. This call terminates
+ * the GCM run: no data may be processed with that GCM context
+ * afterwards, until `br_gcm_reset()` is called to initiate a new GCM
+ * run.
+ *
+ * The tag value must normally be sent along with the encrypted data.
+ * When decrypting, the tag value must be recomputed and compared with
+ * the received tag: if the two tag values differ, then either the tag
+ * or the encrypted data was altered in transit. As an alternative to
+ * this function, the `br_gcm_check_tag_trunc()` function can be used to
+ * compute and check the tag value.
+ *
+ * \param ctx   GCM context structure.
+ * \param tag   destination buffer for the tag.
+ * \param len   tag length (16 bytes or less).
+ */
+void br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len);
+
+/**
+ * \brief Compute and check GCM authentication tag (with truncation).
+ *
+ * This function is an alternative to `br_gcm_get_tag_trunc()`, normally used
+ * on the receiving end (i.e. when decrypting value). The tag value is
+ * recomputed and compared with the provided tag value. If they match, 1
+ * is returned; on mismatch, 0 is returned. A returned value of 0 means
+ * that the data or the tag was altered in transit, normally leading to
+ * wholesale rejection of the complete message.
+ *
+ * Tag length MUST be 16 bytes or less. The normal GCM tag length is 16
+ * bytes. See `br_check_tag_trunc()` for some discussion on the potential
+ * perils of truncating authentication tags.
+ *
+ * \param ctx   GCM context structure.
+ * \param tag   tag value to compare with.
+ * \param len   tag length (in bytes).
+ * \return  1 on success (exact match of tag value), 0 otherwise.
+ */
+uint32_t br_gcm_check_tag_trunc(br_gcm_context *ctx,
+       const void *tag, size_t len);
+
 /**
  * \brief Class instance for GCM.
  */
 extern const br_aead_class br_gcm_vtable;
 
+/**
+ * \brief Context structure for EAX.
+ *
+ * EAX is an AEAD mode that combines a block cipher in CTR mode with
+ * CBC-MAC using the same block cipher and the same key, to provide
+ * authenticated encryption:
+ *
+ *   - Any block cipher with 16-byte blocks can be used with EAX
+ *     (technically, other block sizes are defined as well, but this
+ *     is not implemented by these functions; shorter blocks also
+ *     imply numerous security issues).
+ *
+ *   - The nonce can have any length, as long as nonce values are
+ *     not reused (thus, if nonces are randomly selected, the nonce
+ *     size should be such that reuse probability is negligible).
+ *
+ *   - Additional authenticated data length is unlimited.
+ *
+ *   - Message length is unlimited.
+ *
+ *   - The authentication tag has length 16 bytes.
+ *
+ * The EAX initialisation function receives as parameter an
+ * _initialised_ block cipher implementation context, with the secret
+ * key already set. A pointer to that context will be kept within the
+ * EAX context structure. It is up to the caller to allocate and
+ * initialise that block cipher context.
+ */
+typedef struct {
+       /** \brief Pointer to vtable for this context. */
+       const br_aead_class *vtable;
+
+#ifndef BR_DOXYGEN_IGNORE
+       const br_block_ctrcbc_class **bctx;
+       unsigned char L2[16];
+       unsigned char L4[16];
+       unsigned char nonce[16];
+       unsigned char head[16];
+       unsigned char ctr[16];
+       unsigned char cbcmac[16];
+       unsigned char buf[16];
+       size_t ptr;
+#endif
+} br_eax_context;
+
+/**
+ * \brief Initialize an EAX context.
+ *
+ * A block cipher implementation, with its initialised context
+ * structure, is provided. The block cipher MUST use 16-byte blocks in
+ * CTR + CBC-MAC mode, and its secret key MUST have been already set in
+ * the provided context. The parameters are linked in the EAX context.
+ *
+ * After this function has been called, the `br_eax_reset()` function must
+ * be called, to provide the nonce for EAX computation.
+ *
+ * \param ctx    EAX context structure.
+ * \param bctx   block cipher context (already initialised with secret key).
+ */
+void br_eax_init(br_eax_context *ctx, const br_block_ctrcbc_class **bctx);
+
+/**
+ * \brief Reset an EAX context.
+ *
+ * This function resets an already initialised EAX context for a new
+ * computation run. Implementations and keys are conserved. This function
+ * can be called at any time; it cancels any ongoing EAX computation that
+ * uses the provided context structure.
+ *
+ * It is critical to EAX security that nonce values are not repeated for
+ * the same encryption key. Nonces can have arbitrary length. If nonces
+ * are randomly generated, then a nonce length of at least 128 bits (16
+ * bytes) is recommended, to make nonce reuse probability sufficiently
+ * low.
+ *
+ * \param ctx     EAX context structure.
+ * \param nonce   EAX nonce to use.
+ * \param len     EAX nonce length (in bytes).
+ */
+void br_eax_reset(br_eax_context *ctx, const void *nonce, size_t len);
+
+/**
+ * \brief Inject additional authenticated data into EAX.
+ *
+ * The provided data is injected into a running EAX computation. Additional
+ * data must be injected _before_ the call to `br_eax_flip()`.
+ * Additional data can be injected in several chunks of arbitrary length;
+ * the total amount of additional authenticated data is unlimited.
+ *
+ * \param ctx    EAX context structure.
+ * \param data   pointer to additional authenticated data.
+ * \param len    length of additional authenticated data (in bytes).
+ */
+void br_eax_aad_inject(br_eax_context *ctx, const void *data, size_t len);
+
+/**
+ * \brief Finish injection of additional authenticated data into EAX.
+ *
+ * This function MUST be called before beginning the actual encryption
+ * or decryption (with `br_eax_run()`), even if no additional authenticated
+ * data was injected. No additional authenticated data may be injected
+ * after this function call.
+ *
+ * \param ctx   EAX context structure.
+ */
+void br_eax_flip(br_eax_context *ctx);
+
+/**
+ * \brief Encrypt or decrypt some data with EAX.
+ *
+ * Data encryption or decryption can be done after `br_eax_flip()`
+ * has been called on the context. If `encrypt` is non-zero, then the
+ * provided data shall be plaintext, and it is encrypted in place.
+ * Otherwise, the data shall be ciphertext, and it is decrypted in place.
+ *
+ * Data may be provided in several chunks of arbitrary length.
+ *
+ * \param ctx       EAX context structure.
+ * \param encrypt   non-zero for encryption, zero for decryption.
+ * \param data      data to encrypt or decrypt.
+ * \param len       data length (in bytes).
+ */
+void br_eax_run(br_eax_context *ctx, int encrypt, void *data, size_t len);
+
+/**
+ * \brief Compute EAX authentication tag.
+ *
+ * Compute the EAX authentication tag. The tag is a 16-byte value which
+ * is written in the provided `tag` buffer. This call terminates the
+ * EAX run: no data may be processed with that EAX context afterwards,
+ * until `br_eax_reset()` is called to initiate a new EAX run.
+ *
+ * The tag value must normally be sent along with the encrypted data.
+ * When decrypting, the tag value must be recomputed and compared with
+ * the received tag: if the two tag values differ, then either the tag
+ * or the encrypted data was altered in transit. As an alternative to
+ * this function, the `br_eax_check_tag()` function can be used to
+ * compute and check the tag value.
+ *
+ * \param ctx   EAX context structure.
+ * \param tag   destination buffer for the tag (16 bytes).
+ */
+void br_eax_get_tag(br_eax_context *ctx, void *tag);
+
+/**
+ * \brief Compute and check EAX authentication tag.
+ *
+ * This function is an alternative to `br_eax_get_tag()`, normally used
+ * on the receiving end (i.e. when decrypting value). The tag value is
+ * recomputed and compared with the provided tag value. If they match, 1
+ * is returned; on mismatch, 0 is returned. A returned value of 0 means
+ * that the data or the tag was altered in transit, normally leading to
+ * wholesale rejection of the complete message.
+ *
+ * \param ctx   EAX context structure.
+ * \param tag   tag value to compare with (16 bytes).
+ * \return  1 on success (exact match of tag value), 0 otherwise.
+ */
+uint32_t br_eax_check_tag(br_eax_context *ctx, const void *tag);
+
+/**
+ * \brief Compute EAX authentication tag (with truncation).
+ *
+ * This function is similar to `br_eax_get_tag()`, except that it allows
+ * the tag to be truncated to a smaller length. The intended tag length
+ * is provided as `len` (in bytes); it MUST be no more than 16, but
+ * it may be smaller. Note that decreasing tag length mechanically makes
+ * forgeries easier; NIST SP 800-38D specifies that the tag length shall
+ * lie between 12 and 16 bytes (inclusive), but may be truncated down to
+ * 4 or 8 bytes, for specific applications that can tolerate it. It must
+ * also be noted that successful forgeries leak information on the
+ * authentication key, making subsequent forgeries easier. Therefore,
+ * tag truncation, and in particular truncation to sizes lower than 12
+ * bytes, shall be envisioned only with great care.
+ *
+ * The tag is written in the provided `tag` buffer. This call terminates
+ * the EAX run: no data may be processed with that EAX context
+ * afterwards, until `br_eax_reset()` is called to initiate a new EAX
+ * run.
+ *
+ * The tag value must normally be sent along with the encrypted data.
+ * When decrypting, the tag value must be recomputed and compared with
+ * the received tag: if the two tag values differ, then either the tag
+ * or the encrypted data was altered in transit. As an alternative to
+ * this function, the `br_eax_check_tag_trunc()` function can be used to
+ * compute and check the tag value.
+ *
+ * \param ctx   EAX context structure.
+ * \param tag   destination buffer for the tag.
+ * \param len   tag length (16 bytes or less).
+ */
+void br_eax_get_tag_trunc(br_eax_context *ctx, void *tag, size_t len);
+
+/**
+ * \brief Compute and check EAX authentication tag (with truncation).
+ *
+ * This function is an alternative to `br_eax_get_tag_trunc()`, normally used
+ * on the receiving end (i.e. when decrypting value). The tag value is
+ * recomputed and compared with the provided tag value. If they match, 1
+ * is returned; on mismatch, 0 is returned. A returned value of 0 means
+ * that the data or the tag was altered in transit, normally leading to
+ * wholesale rejection of the complete message.
+ *
+ * Tag length MUST be 16 bytes or less. The normal EAX tag length is 16
+ * bytes. See `br_check_tag_trunc()` for some discussion on the potential
+ * perils of truncating authentication tags.
+ *
+ * \param ctx   EAX context structure.
+ * \param tag   tag value to compare with.
+ * \param len   tag length (in bytes).
+ * \return  1 on success (exact match of tag value), 0 otherwise.
+ */
+uint32_t br_eax_check_tag_trunc(br_eax_context *ctx,
+       const void *tag, size_t len);
+
+/**
+ * \brief Class instance for EAX.
+ */
+extern const br_aead_class br_eax_vtable;
+
+/**
+ * \brief Context structure for CCM.
+ *
+ * CCM is an AEAD mode that combines a block cipher in CTR mode with
+ * CBC-MAC using the same block cipher and the same key, to provide
+ * authenticated encryption:
+ *
+ *   - Any block cipher with 16-byte blocks can be used with CCM
+ *     (technically, other block sizes are defined as well, but this
+ *     is not implemented by these functions; shorter blocks also
+ *     imply numerous security issues).
+ *
+ *   - The authentication tag length, and plaintext length, MUST be
+ *     known when starting processing data. Plaintext and ciphertext
+ *     can still be provided by chunks, but the total size must match
+ *     the value provided upon initialisation.
+ *
+ *   - The nonce length is constrained betwen 7 and 13 bytes (inclusive).
+ *     Furthermore, the plaintext length, when encoded, must fit over
+ *     15-nonceLen bytes; thus, if the nonce has length 13 bytes, then
+ *     the plaintext length cannot exceed 65535 bytes.
+ *
+ *   - Additional authenticated data length is practically unlimited
+ *     (formal limit is at 2^64 bytes).
+ *
+ *   - The authentication tag has length 4 to 16 bytes (even values only).
+ *
+ * The CCM initialisation function receives as parameter an
+ * _initialised_ block cipher implementation context, with the secret
+ * key already set. A pointer to that context will be kept within the
+ * CCM context structure. It is up to the caller to allocate and
+ * initialise that block cipher context.
+ */
+typedef struct {
+#ifndef BR_DOXYGEN_IGNORE
+       const br_block_ctrcbc_class **bctx;
+       unsigned char ctr[16];
+       unsigned char cbcmac[16];
+       unsigned char tagmask[16];
+       unsigned char buf[16];
+       size_t ptr;
+       size_t tag_len;
+#endif
+} br_ccm_context;
+
+/**
+ * \brief Initialize a CCM context.
+ *
+ * A block cipher implementation, with its initialised context
+ * structure, is provided. The block cipher MUST use 16-byte blocks in
+ * CTR + CBC-MAC mode, and its secret key MUST have been already set in
+ * the provided context. The parameters are linked in the CCM context.
+ *
+ * After this function has been called, the `br_ccm_reset()` function must
+ * be called, to provide the nonce for CCM computation.
+ *
+ * \param ctx    CCM context structure.
+ * \param bctx   block cipher context (already initialised with secret key).
+ */
+void br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx);
+
+/**
+ * \brief Reset a CCM context.
+ *
+ * This function resets an already initialised CCM context for a new
+ * computation run. Implementations and keys are conserved. This function
+ * can be called at any time; it cancels any ongoing CCM computation that
+ * uses the provided context structure.
+ *
+ * The `aad_len` parameter contains the total length, in bytes, of the
+ * additional authenticated data. It may be zero. That length MUST be
+ * exact.
+ *
+ * The `data_len` parameter contains the total length, in bytes, of the
+ * data that will be injected (plaintext or ciphertext). That length MUST
+ * be exact. Moreover, that length MUST be less than 2^(8*(15-nonce_len)).
+ *
+ * The nonce length (`nonce_len`), in bytes, must be in the 7..13 range
+ * (inclusive).
+ *
+ * The tag length (`tag_len`), in bytes, must be in the 4..16 range, and
+ * be an even integer. Short tags mechanically allow for higher forgery
+ * probabilities; hence, tag sizes smaller than 12 bytes shall be used only
+ * with care.
+ *
+ * It is critical to CCM security that nonce values are not repeated for
+ * the same encryption key. Random generation of nonces is not generally
+ * recommended, due to the relatively small maximum nonce value.
+ *
+ * Returned value is 1 on success, 0 on error. An error is reported if
+ * the tag or nonce length is out of range, or if the
+ * plaintext/ciphertext length cannot be encoded with the specified
+ * nonce length.
+ *
+ * \param ctx         CCM context structure.
+ * \param nonce       CCM nonce to use.
+ * \param nonce_len   CCM nonce length (in bytes, 7 to 13).
+ * \param aad_len     additional authenticated data length (in bytes).
+ * \param data_len    plaintext/ciphertext length (in bytes).
+ * \param tag_len     tag length (in bytes).
+ * \return  1 on success, 0 on error.
+ */
+int br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len,
+       uint64_t aad_len, uint64_t data_len, size_t tag_len);
+
+/**
+ * \brief Inject additional authenticated data into CCM.
+ *
+ * The provided data is injected into a running CCM computation. Additional
+ * data must be injected _before_ the call to `br_ccm_flip()`.
+ * Additional data can be injected in several chunks of arbitrary length,
+ * but the total amount MUST exactly match the value which was provided
+ * to `br_ccm_reset()`.
+ *
+ * \param ctx    CCM context structure.
+ * \param data   pointer to additional authenticated data.
+ * \param len    length of additional authenticated data (in bytes).
+ */
+void br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len);
+
+/**
+ * \brief Finish injection of additional authenticated data into CCM.
+ *
+ * This function MUST be called before beginning the actual encryption
+ * or decryption (with `br_ccm_run()`), even if no additional authenticated
+ * data was injected. No additional authenticated data may be injected
+ * after this function call.
+ *
+ * \param ctx   CCM context structure.
+ */
+void br_ccm_flip(br_ccm_context *ctx);
+
+/**
+ * \brief Encrypt or decrypt some data with CCM.
+ *
+ * Data encryption or decryption can be done after `br_ccm_flip()`
+ * has been called on the context. If `encrypt` is non-zero, then the
+ * provided data shall be plaintext, and it is encrypted in place.
+ * Otherwise, the data shall be ciphertext, and it is decrypted in place.
+ *
+ * Data may be provided in several chunks of arbitrary length, provided
+ * that the total length exactly matches the length provided to the
+ * `br_ccm_reset()` call.
+ *
+ * \param ctx       CCM context structure.
+ * \param encrypt   non-zero for encryption, zero for decryption.
+ * \param data      data to encrypt or decrypt.
+ * \param len       data length (in bytes).
+ */
+void br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len);
+
+/**
+ * \brief Compute CCM authentication tag.
+ *
+ * Compute the CCM authentication tag. This call terminates the CCM
+ * run: all data must have been injected with `br_ccm_run()` (in zero,
+ * one or more successive calls). After this function has been called,
+ * no more data can br processed; a `br_ccm_reset()` call is required
+ * to start a new message.
+ *
+ * The tag length was provided upon context initialisation (last call
+ * to `br_ccm_reset()`); it is returned by this function.
+ *
+ * The tag value must normally be sent along with the encrypted data.
+ * When decrypting, the tag value must be recomputed and compared with
+ * the received tag: if the two tag values differ, then either the tag
+ * or the encrypted data was altered in transit. As an alternative to
+ * this function, the `br_ccm_check_tag()` function can be used to
+ * compute and check the tag value.
+ *
+ * \param ctx   CCM context structure.
+ * \param tag   destination buffer for the tag (up to 16 bytes).
+ * \return  the tag length (in bytes).
+ */
+size_t br_ccm_get_tag(br_ccm_context *ctx, void *tag);
+
+/**
+ * \brief Compute and check CCM authentication tag.
+ *
+ * This function is an alternative to `br_ccm_get_tag()`, normally used
+ * on the receiving end (i.e. when decrypting value). The tag value is
+ * recomputed and compared with the provided tag value. If they match, 1
+ * is returned; on mismatch, 0 is returned. A returned value of 0 means
+ * that the data or the tag was altered in transit, normally leading to
+ * wholesale rejection of the complete message.
+ *
+ * \param ctx   CCM context structure.
+ * \param tag   tag value to compare with (up to 16 bytes).
+ * \return  1 on success (exact match of tag value), 0 otherwise.
+ */
+uint32_t br_ccm_check_tag(br_ccm_context *ctx, const void *tag);
+
 #ifdef __cplusplus
 }
 #endif
index 24f09ac..4772779 100644 (file)
@@ -136,6 +136,73 @@ extern "C" {
  *     chunked processing, provided that each chunk length (except possibly
  *     the last one) is a multiple of the block size.
  *
+ *   - `br_xxx_ctrcbc_keys`
+ *
+ *     Context structure that contains the subkeys resulting from the
+ *     key expansion. These subkeys are appropriate for doing combined
+ *     CTR encryption/decryption and CBC-MAC, as used in the CCM and EAX
+ *     authenticated encryption modes. The structure first field is
+ *     called `vtable` and points to the appropriate OOP structure.
+ *
+ *   - `br_xxx_ctrcbc_init(br_xxx_ctr_keys *ctx, const void *key, size_t len)`
+ *
+ *     Perform key expansion: subkeys for combined CTR
+ *     encryption/decryption and CBC-MAC are computed and written in the
+ *     provided context structure. The key length MUST be adequate for
+ *     the implemented block cipher. This function also sets the
+ *     `vtable` field.
+ *
+ *   - `br_xxx_ctrcbc_encrypt(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *cbcmac, void *data, size_t len)`
+ *
+ *     Perform CTR encryption of some data, and CBC-MAC. Processing is
+ *     done "in place" (the output data replaces the input data). This
+ *     function applies CTR encryption on the data, using a full
+ *     block-size counter (i.e. for 128-bit blocks, the counter is
+ *     incremented as a 128-bit value). The 'ctr' array contains the
+ *     initial value for the counter (used in the first block) and it is
+ *     updated with the new value after data processing. The 'cbcmac'
+ *     value shall point to a block-sized value which is used as IV for
+ *     CBC-MAC, computed over the encrypted data (output of CTR
+ *     encryption); the resulting CBC-MAC is written over 'cbcmac' on
+ *     output.
+ *
+ *     The data length MUST be a multiple of the block size.
+ *
+ *   - `br_xxx_ctrcbc_decrypt(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *cbcmac, void *data, size_t len)`
+ *
+ *     Perform CTR decryption of some data, and CBC-MAC. Processing is
+ *     done "in place" (the output data replaces the input data). This
+ *     function applies CTR decryption on the data, using a full
+ *     block-size counter (i.e. for 128-bit blocks, the counter is
+ *     incremented as a 128-bit value). The 'ctr' array contains the
+ *     initial value for the counter (used in the first block) and it is
+ *     updated with the new value after data processing. The 'cbcmac'
+ *     value shall point to a block-sized value which is used as IV for
+ *     CBC-MAC, computed over the encrypted data (input of CTR
+ *     encryption); the resulting CBC-MAC is written over 'cbcmac' on
+ *     output.
+ *
+ *     The data length MUST be a multiple of the block size.
+ *
+ *   - `br_xxx_ctrcbc_ctr(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *data, size_t len)`
+ *
+ *     Perform CTR encryption or decryption of the provided data. The
+ *     data is processed "in place" (the output data replaces the input
+ *     data). A full block-sized counter is applied (i.e. for 128-bit
+ *     blocks, the counter is incremented as a 128-bit value). The 'ctr'
+ *     array contains the initial value for the counter (used in the
+ *     first block), and it is updated with the new value after data
+ *     processing.
+ *
+ *     The data length MUST be a multiple of the block size.
+ *
+ *   - `br_xxx_ctrcbc_mac(const br_xxx_ctrcbc_keys *ctx, void *cbcmac, const void *data, size_t len)`
+ *
+ *     Compute CBC-MAC over the provided data. The IV for CBC-MAC is
+ *     provided as 'cbcmac'; the output is written over the same array.
+ *     The data itself is untouched. The data length MUST be a multiple
+ *     of the block size.
+ *
  *
  * It shall be noted that the key expansion functions return `void`. If
  * the provided key length is not allowed, then there will be no error
@@ -176,6 +243,41 @@ extern "C" {
  *
  *     Pointer to the encryption/decryption function.
  *
+ * For combined CTR/CBC-MAC encryption, the `vtable` has a slightly
+ * different structure:
+ *
+ *   - `context_size`
+ *
+ *     The size (in bytes) of the context structure for subkeys.
+ *
+ *   - `block_size`
+ *
+ *     The cipher block size (in bytes).
+ *
+ *   - `log_block_size`
+ *
+ *     The base-2 logarithm of cipher block size (e.g. 4 for blocks
+ *     of 16 bytes).
+ *
+ *   - `init`
+ *
+ *     Pointer to the key expansion function.
+ *
+ *   - `encrypt`
+ *
+ *     Pointer to the CTR encryption + CBC-MAC function.
+ *
+ *   - `decrypt`
+ *
+ *     Pointer to the CTR decryption + CBC-MAC function.
+ *
+ *   - `ctr`
+ *
+ *     Pointer to the CTR encryption/decryption function.
+ *
+ *   - `mac`
+ *
+ *     Pointer to the CBC-MAC function.
  *
  * For block cipher "`xxx`", static, constant instances of these
  * structures are defined, under the names:
@@ -183,6 +285,7 @@ extern "C" {
  *   - `br_xxx_cbcenc_vtable`
  *   - `br_xxx_cbcdec_vtable`
  *   - `br_xxx_ctr_vtable`
+ *   - `br_xxx_ctrcbc_vtable`
  *
  *
  * ## Implemented Block Ciphers
@@ -460,6 +563,132 @@ struct br_block_ctr_class_ {
                const void *iv, uint32_t cc, void *data, size_t len);
 };
 
+/**
+ * \brief Class type for combined CTR and CBC-MAC implementations.
+ *
+ * A `br_block_ctrcbc_class` instance points to the functions implementing
+ * a specific block cipher, when used in CTR mode for encrypting or
+ * decrypting data, along with CBC-MAC.
+ */
+typedef struct br_block_ctrcbc_class_ br_block_ctrcbc_class;
+struct br_block_ctrcbc_class_ {
+       /**
+        * \brief Size (in bytes) of the context structure appropriate
+        * for containing subkeys.
+        */
+       size_t context_size;
+
+       /**
+        * \brief Size of individual blocks (in bytes).
+        */
+       unsigned block_size;
+
+       /**
+        * \brief Base-2 logarithm of the size of individual blocks,
+        * expressed in bytes.
+        */
+       unsigned log_block_size;
+
+       /**
+        * \brief Initialisation function.
+        *
+        * This function sets the `vtable` field in the context structure.
+        * The key length MUST be one of the key lengths supported by
+        * the implementation.
+        *
+        * \param ctx       context structure to initialise.
+        * \param key       secret key.
+        * \param key_len   key length (in bytes).
+        */
+       void (*init)(const br_block_ctrcbc_class **ctx,
+               const void *key, size_t key_len);
+
+       /**
+        * \brief Run the CTR encryption + CBC-MAC.
+        *
+        * The `ctr` parameter points to the counter; its length shall
+        * be equal to the block size. It is updated by this function
+        * as encryption proceeds.
+        *
+        * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC
+        * is computed over the encrypted data (output of CTR
+        * encryption). Its length shall be equal to the block size. The
+        * computed CBC-MAC value is written over the `cbcmac` array.
+        *
+        * The data to encrypt is updated "in place". Its length (`len`
+        * bytes) MUST be a multiple of the block size.
+        *
+        * \param ctx      context structure (already initialised).
+        * \param ctr      counter for CTR encryption (initial and final).
+        * \param cbcmac   IV and output buffer for CBC-MAC.
+        * \param data     data to encrypt.
+        * \param len      data length (in bytes).
+        */
+       void (*encrypt)(const br_block_ctrcbc_class *const *ctx,
+               void *ctr, void *cbcmac, void *data, size_t len);
+
+       /**
+        * \brief Run the CTR decryption + CBC-MAC.
+        *
+        * The `ctr` parameter points to the counter; its length shall
+        * be equal to the block size. It is updated by this function
+        * as decryption proceeds.
+        *
+        * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC
+        * is computed over the encrypted data (i.e. before CTR
+        * decryption). Its length shall be equal to the block size. The
+        * computed CBC-MAC value is written over the `cbcmac` array.
+        *
+        * The data to decrypt is updated "in place". Its length (`len`
+        * bytes) MUST be a multiple of the block size.
+        *
+        * \param ctx      context structure (already initialised).
+        * \param ctr      counter for CTR encryption (initial and final).
+        * \param cbcmac   IV and output buffer for CBC-MAC.
+        * \param data     data to decrypt.
+        * \param len      data length (in bytes).
+        */
+       void (*decrypt)(const br_block_ctrcbc_class *const *ctx,
+               void *ctr, void *cbcmac, void *data, size_t len);
+
+       /**
+        * \brief Run the CTR encryption/decryption only.
+        *
+        * The `ctr` parameter points to the counter; its length shall
+        * be equal to the block size. It is updated by this function
+        * as decryption proceeds.
+        *
+        * The data to decrypt is updated "in place". Its length (`len`
+        * bytes) MUST be a multiple of the block size.
+        *
+        * \param ctx      context structure (already initialised).
+        * \param ctr      counter for CTR encryption (initial and final).
+        * \param data     data to decrypt.
+        * \param len      data length (in bytes).
+        */
+       void (*ctr)(const br_block_ctrcbc_class *const *ctx,
+               void *ctr, void *data, size_t len);
+
+       /**
+        * \brief Run the CBC-MAC only.
+        *
+        * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC
+        * is computed over the encrypted data (i.e. before CTR
+        * decryption). Its length shall be equal to the block size. The
+        * computed CBC-MAC value is written over the `cbcmac` array.
+        *
+        * The data is unmodified. Its length (`len` bytes) MUST be a
+        * multiple of the block size.
+        *
+        * \param ctx      context structure (already initialised).
+        * \param cbcmac   IV and output buffer for CBC-MAC.
+        * \param data     data to decrypt.
+        * \param len      data length (in bytes).
+        */
+       void (*mac)(const br_block_ctrcbc_class *const *ctx,
+               void *cbcmac, const void *data, size_t len);
+};
+
 /*
  * Traditional, table-based AES implementation. It is fast, but uses
  * internal tables (in particular a 1 kB table for encryption, another
@@ -517,6 +746,22 @@ typedef struct {
 #endif
 } br_aes_big_ctr_keys;
 
+/**
+ * \brief Context for AES subkeys (`aes_big` implementation, CTR encryption
+ * and decryption + CBC-MAC).
+ *
+ * First field is a pointer to the vtable; it is set by the initialisation
+ * function. Other fields are not supposed to be accessed by user code.
+ */
+typedef struct {
+       /** \brief Pointer to vtable for this context. */
+       const br_block_ctrcbc_class *vtable;
+#ifndef BR_DOXYGEN_IGNORE
+       uint32_t skey[60];
+       unsigned num_rounds;
+#endif
+} br_aes_big_ctrcbc_keys;
+
 /**
  * \brief Class instance for AES CBC encryption (`aes_big` implementation).
  */
@@ -533,6 +778,12 @@ extern const br_block_cbcdec_class br_aes_big_cbcdec_vtable;
  */
 extern const br_block_ctr_class br_aes_big_ctr_vtable;
 
+/**
+ * \brief Class instance for AES CTR encryption/decryption + CBC-MAC
+ * (`aes_big` implementation).
+ */
+extern const br_block_ctrcbc_class br_aes_big_ctrcbc_vtable;
+
 /**
  * \brief Context initialisation (key schedule) for AES CBC encryption
  * (`aes_big` implementation).
@@ -566,6 +817,17 @@ void br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx,
 void br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx,
        const void *key, size_t len);
 
+/**
+ * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC
+ * (`aes_big` implementation).
+ *
+ * \param ctx   context to initialise.
+ * \param key   secret key.
+ * \param len   secret key length (in bytes).
+ */
+void br_aes_big_ctrcbc_init(br_aes_big_ctrcbc_keys *ctx,
+       const void *key, size_t len);
+
 /**
  * \brief CBC encryption with AES (`aes_big` implementation).
  *
@@ -594,13 +856,59 @@ void br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx, void *iv,
  * \param ctx    context (already initialised).
  * \param iv     IV (constant, 12 bytes).
  * \param cc     initial block counter value.
- * \param data   data to decrypt (updated).
+ * \param data   data to encrypt or decrypt (updated).
  * \param len    data length (in bytes).
  * \return  new block counter value.
  */
 uint32_t br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx,
        const void *iv, uint32_t cc, void *data, size_t len);
 
+/**
+ * \brief CTR encryption + CBC-MAC with AES (`aes_big` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to encrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_big_ctrcbc_encrypt(const br_aes_big_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR decryption + CBC-MAC with AES (`aes_big` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to decrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_big_ctrcbc_decrypt(const br_aes_big_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR encryption/decryption with AES (`aes_big` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param data     data to MAC (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_big_ctrcbc_ctr(const br_aes_big_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len);
+
+/**
+ * \brief CBC-MAC with AES (`aes_big` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to MAC (unmodified).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_big_ctrcbc_mac(const br_aes_big_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len);
+
 /*
  * AES implementation optimized for size. It is slower than the
  * traditional table-based AES implementation, but requires much less
@@ -658,6 +966,22 @@ typedef struct {
 #endif
 } br_aes_small_ctr_keys;
 
+/**
+ * \brief Context for AES subkeys (`aes_small` implementation, CTR encryption
+ * and decryption + CBC-MAC).
+ *
+ * First field is a pointer to the vtable; it is set by the initialisation
+ * function. Other fields are not supposed to be accessed by user code.
+ */
+typedef struct {
+       /** \brief Pointer to vtable for this context. */
+       const br_block_ctrcbc_class *vtable;
+#ifndef BR_DOXYGEN_IGNORE
+       uint32_t skey[60];
+       unsigned num_rounds;
+#endif
+} br_aes_small_ctrcbc_keys;
+
 /**
  * \brief Class instance for AES CBC encryption (`aes_small` implementation).
  */
@@ -674,6 +998,12 @@ extern const br_block_cbcdec_class br_aes_small_cbcdec_vtable;
  */
 extern const br_block_ctr_class br_aes_small_ctr_vtable;
 
+/**
+ * \brief Class instance for AES CTR encryption/decryption + CBC-MAC
+ * (`aes_small` implementation).
+ */
+extern const br_block_ctrcbc_class br_aes_small_ctrcbc_vtable;
+
 /**
  * \brief Context initialisation (key schedule) for AES CBC encryption
  * (`aes_small` implementation).
@@ -707,6 +1037,17 @@ void br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx,
 void br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx,
        const void *key, size_t len);
 
+/**
+ * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC
+ * (`aes_small` implementation).
+ *
+ * \param ctx   context to initialise.
+ * \param key   secret key.
+ * \param len   secret key length (in bytes).
+ */
+void br_aes_small_ctrcbc_init(br_aes_small_ctrcbc_keys *ctx,
+       const void *key, size_t len);
+
 /**
  * \brief CBC encryption with AES (`aes_small` implementation).
  *
@@ -742,6 +1083,52 @@ void br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx, void *iv,
 uint32_t br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx,
        const void *iv, uint32_t cc, void *data, size_t len);
 
+/**
+ * \brief CTR encryption + CBC-MAC with AES (`aes_small` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to encrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_small_ctrcbc_encrypt(const br_aes_small_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR decryption + CBC-MAC with AES (`aes_small` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to decrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_small_ctrcbc_decrypt(const br_aes_small_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR encryption/decryption with AES (`aes_small` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param data     data to MAC (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_small_ctrcbc_ctr(const br_aes_small_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len);
+
+/**
+ * \brief CBC-MAC with AES (`aes_small` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to MAC (unmodified).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_small_ctrcbc_mac(const br_aes_small_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len);
+
 /*
  * Constant-time AES implementation. Its size is similar to that of
  * 'aes_big', and its performance is similar to that of 'aes_small' (faster
@@ -798,6 +1185,22 @@ typedef struct {
 #endif
 } br_aes_ct_ctr_keys;
 
+/**
+ * \brief Context for AES subkeys (`aes_ct` implementation, CTR encryption
+ * and decryption + CBC-MAC).
+ *
+ * First field is a pointer to the vtable; it is set by the initialisation
+ * function. Other fields are not supposed to be accessed by user code.
+ */
+typedef struct {
+       /** \brief Pointer to vtable for this context. */
+       const br_block_ctrcbc_class *vtable;
+#ifndef BR_DOXYGEN_IGNORE
+       uint32_t skey[60];
+       unsigned num_rounds;
+#endif
+} br_aes_ct_ctrcbc_keys;
+
 /**
  * \brief Class instance for AES CBC encryption (`aes_ct` implementation).
  */
@@ -814,6 +1217,12 @@ extern const br_block_cbcdec_class br_aes_ct_cbcdec_vtable;
  */
 extern const br_block_ctr_class br_aes_ct_ctr_vtable;
 
+/**
+ * \brief Class instance for AES CTR encryption/decryption + CBC-MAC
+ * (`aes_ct` implementation).
+ */
+extern const br_block_ctrcbc_class br_aes_ct_ctrcbc_vtable;
+
 /**
  * \brief Context initialisation (key schedule) for AES CBC encryption
  * (`aes_ct` implementation).
@@ -847,6 +1256,17 @@ void br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx,
 void br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx,
        const void *key, size_t len);
 
+/**
+ * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC
+ * (`aes_ct` implementation).
+ *
+ * \param ctx   context to initialise.
+ * \param key   secret key.
+ * \param len   secret key length (in bytes).
+ */
+void br_aes_ct_ctrcbc_init(br_aes_ct_ctrcbc_keys *ctx,
+       const void *key, size_t len);
+
 /**
  * \brief CBC encryption with AES (`aes_ct` implementation).
  *
@@ -882,6 +1302,52 @@ void br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx, void *iv,
 uint32_t br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx,
        const void *iv, uint32_t cc, void *data, size_t len);
 
+/**
+ * \brief CTR encryption + CBC-MAC with AES (`aes_ct` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to encrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct_ctrcbc_encrypt(const br_aes_ct_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR decryption + CBC-MAC with AES (`aes_ct` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to decrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct_ctrcbc_decrypt(const br_aes_ct_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR encryption/decryption with AES (`aes_ct` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param data     data to MAC (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct_ctrcbc_ctr(const br_aes_ct_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len);
+
+/**
+ * \brief CBC-MAC with AES (`aes_ct` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to MAC (unmodified).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct_ctrcbc_mac(const br_aes_ct_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len);
+
 /*
  * 64-bit constant-time AES implementation. It is similar to 'aes_ct'
  * but uses 64-bit registers, making it about twice faster than 'aes_ct'
@@ -940,6 +1406,22 @@ typedef struct {
 #endif
 } br_aes_ct64_ctr_keys;
 
+/**
+ * \brief Context for AES subkeys (`aes_ct64` implementation, CTR encryption
+ * and decryption + CBC-MAC).
+ *
+ * First field is a pointer to the vtable; it is set by the initialisation
+ * function. Other fields are not supposed to be accessed by user code.
+ */
+typedef struct {
+       /** \brief Pointer to vtable for this context. */
+       const br_block_ctrcbc_class *vtable;
+#ifndef BR_DOXYGEN_IGNORE
+       uint64_t skey[30];
+       unsigned num_rounds;
+#endif
+} br_aes_ct64_ctrcbc_keys;
+
 /**
  * \brief Class instance for AES CBC encryption (`aes_ct64` implementation).
  */
@@ -956,6 +1438,12 @@ extern const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable;
  */
 extern const br_block_ctr_class br_aes_ct64_ctr_vtable;
 
+/**
+ * \brief Class instance for AES CTR encryption/decryption + CBC-MAC
+ * (`aes_ct64` implementation).
+ */
+extern const br_block_ctrcbc_class br_aes_ct64_ctrcbc_vtable;
+
 /**
  * \brief Context initialisation (key schedule) for AES CBC encryption
  * (`aes_ct64` implementation).
@@ -989,6 +1477,17 @@ void br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx,
 void br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx,
        const void *key, size_t len);
 
+/**
+ * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC
+ * (`aes_ct64` implementation).
+ *
+ * \param ctx   context to initialise.
+ * \param key   secret key.
+ * \param len   secret key length (in bytes).
+ */
+void br_aes_ct64_ctrcbc_init(br_aes_ct64_ctrcbc_keys *ctx,
+       const void *key, size_t len);
+
 /**
  * \brief CBC encryption with AES (`aes_ct64` implementation).
  *
@@ -1024,6 +1523,52 @@ void br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx, void *iv,
 uint32_t br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx,
        const void *iv, uint32_t cc, void *data, size_t len);
 
+/**
+ * \brief CTR encryption + CBC-MAC with AES (`aes_ct64` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to encrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct64_ctrcbc_encrypt(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR decryption + CBC-MAC with AES (`aes_ct64` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to decrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct64_ctrcbc_decrypt(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR encryption/decryption with AES (`aes_ct64` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param data     data to MAC (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct64_ctrcbc_ctr(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len);
+
+/**
+ * \brief CBC-MAC with AES (`aes_ct64` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to MAC (unmodified).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_ct64_ctrcbc_mac(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len);
+
 /*
  * AES implementation using AES-NI opcodes (x86 platform).
  */
@@ -1083,6 +1628,24 @@ typedef struct {
 #endif
 } br_aes_x86ni_ctr_keys;
 
+/**
+ * \brief Context for AES subkeys (`aes_x86ni` implementation, CTR encryption
+ * and decryption + CBC-MAC).
+ *
+ * First field is a pointer to the vtable; it is set by the initialisation
+ * function. Other fields are not supposed to be accessed by user code.
+ */
+typedef struct {
+       /** \brief Pointer to vtable for this context. */
+       const br_block_ctrcbc_class *vtable;
+#ifndef BR_DOXYGEN_IGNORE
+       union {
+               unsigned char skni[16 * 15];
+       } skey;
+       unsigned num_rounds;
+#endif
+} br_aes_x86ni_ctrcbc_keys;
+
 /**
  * \brief Class instance for AES CBC encryption (`aes_x86ni` implementation).
  *
@@ -1111,6 +1674,16 @@ extern const br_block_cbcdec_class br_aes_x86ni_cbcdec_vtable;
  */
 extern const br_block_ctr_class br_aes_x86ni_ctr_vtable;
 
+/**
+ * \brief Class instance for AES CTR encryption/decryption + CBC-MAC
+ * (`aes_x86ni` implementation).
+ *
+ * Since this implementation might be omitted from the library, or the
+ * AES opcode unavailable on the current CPU, a pointer to this class
+ * instance should be obtained through `br_aes_x86ni_ctrcbc_get_vtable()`.
+ */
+extern const br_block_ctrcbc_class br_aes_x86ni_ctrcbc_vtable;
+
 /**
  * \brief Context initialisation (key schedule) for AES CBC encryption
  * (`aes_x86ni` implementation).
@@ -1144,6 +1717,17 @@ void br_aes_x86ni_cbcdec_init(br_aes_x86ni_cbcdec_keys *ctx,
 void br_aes_x86ni_ctr_init(br_aes_x86ni_ctr_keys *ctx,
        const void *key, size_t len);
 
+/**
+ * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC
+ * (`aes_x86ni` implementation).
+ *
+ * \param ctx   context to initialise.
+ * \param key   secret key.
+ * \param len   secret key length (in bytes).
+ */
+void br_aes_x86ni_ctrcbc_init(br_aes_x86ni_ctrcbc_keys *ctx,
+       const void *key, size_t len);
+
 /**
  * \brief CBC encryption with AES (`aes_x86ni` implementation).
  *
@@ -1179,6 +1763,52 @@ void br_aes_x86ni_cbcdec_run(const br_aes_x86ni_cbcdec_keys *ctx, void *iv,
 uint32_t br_aes_x86ni_ctr_run(const br_aes_x86ni_ctr_keys *ctx,
        const void *iv, uint32_t cc, void *data, size_t len);
 
+/**
+ * \brief CTR encryption + CBC-MAC with AES (`aes_x86ni` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to encrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_x86ni_ctrcbc_encrypt(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR decryption + CBC-MAC with AES (`aes_x86ni` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to decrypt (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_x86ni_ctrcbc_decrypt(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len);
+
+/**
+ * \brief CTR encryption/decryption with AES (`aes_x86ni` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param ctr      counter for CTR (16 bytes, updated).
+ * \param data     data to MAC (updated).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_x86ni_ctrcbc_ctr(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len);
+
+/**
+ * \brief CBC-MAC with AES (`aes_x86ni` implementation).
+ *
+ * \param ctx      context (already initialised).
+ * \param cbcmac   IV for CBC-MAC (updated).
+ * \param data     data to MAC (unmodified).
+ * \param len      data length (in bytes, MUST be a multiple of 16).
+ */
+void br_aes_x86ni_ctrcbc_mac(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len);
+
 /**
  * \brief Obtain the `aes_x86ni` AES-CBC (encryption) implementation, if
  * available.
@@ -1188,7 +1818,7 @@ uint32_t br_aes_x86ni_ctr_run(const br_aes_x86ni_ctr_keys *ctx,
  * opcodes are available on the currently running CPU. If either of
  * these conditions is not met, then this function returns `NULL`.
  *
- * \return  the `aes_x868ni` AES-CBC (encryption) implementation, or `NULL`.
+ * \return  the `aes_x86ni` AES-CBC (encryption) implementation, or `NULL`.
  */
 const br_block_cbcenc_class *br_aes_x86ni_cbcenc_get_vtable(void);
 
@@ -1201,7 +1831,7 @@ const br_block_cbcenc_class *br_aes_x86ni_cbcenc_get_vtable(void);
  * opcodes are available on the currently running CPU. If either of
  * these conditions is not met, then this function returns `NULL`.
  *
- * \return  the `aes_x868ni` AES-CBC (decryption) implementation, or `NULL`.
+ * \return  the `aes_x86ni` AES-CBC (decryption) implementation, or `NULL`.
  */
 const br_block_cbcdec_class *br_aes_x86ni_cbcdec_get_vtable(void);
 
@@ -1213,10 +1843,23 @@ const br_block_cbcdec_class *br_aes_x86ni_cbcdec_get_vtable(void);
  * opcodes are available on the currently running CPU. If either of
  * these conditions is not met, then this function returns `NULL`.
  *
- * \return  the `aes_x868ni` AES-CTR implementation, or `NULL`.
+ * \return  the `aes_x86ni` AES-CTR implementation, or `NULL`.
  */
 const br_block_ctr_class *br_aes_x86ni_ctr_get_vtable(void);
 
+/**
+ * \brief Obtain the `aes_x86ni` AES-CTR + CBC-MAC implementation, if
+ * available.
+ *
+ * This function returns a pointer to `br_aes_x86ni_ctrcbc_vtable`, if
+ * that implementation was compiled in the library _and_ the x86 AES
+ * opcodes are available on the currently running CPU. If either of
+ * these conditions is not met, then this function returns `NULL`.
+ *
+ * \return  the `aes_x86ni` AES-CTR implementation, or `NULL`.
+ */
+const br_block_ctrcbc_class *br_aes_x86ni_ctrcbc_get_vtable(void);
+
 /*
  * AES implementation using POWER8 opcodes.
  */
@@ -1452,6 +2095,22 @@ typedef union {
        br_aes_pwr8_ctr_keys c_pwr8;
 } br_aes_gen_ctr_keys;
 
+/**
+ * \brief Aggregate structure large enough to be used as context for
+ * subkeys (CTR encryption/decryption + CBC-MAC) for all AES implementations.
+ */
+typedef union {
+       const br_block_ctrcbc_class *vtable;
+       br_aes_big_ctrcbc_keys c_big;
+       br_aes_small_ctrcbc_keys c_small;
+       br_aes_ct_ctrcbc_keys c_ct;
+       br_aes_ct64_ctrcbc_keys c_ct64;
+       /* FIXME
+       br_aes_x86ni_ctrcbc_keys c_x86ni;
+       br_aes_pwr8_ctrcbc_keys c_pwr8;
+       */
+} br_aes_gen_ctrcbc_keys;
+
 /*
  * Traditional, table-based implementation for DES/3DES. Since tables are
  * used, cache-timing attacks are conceptually possible.
index 21a58ce..2a281ff 100644 (file)
@@ -1,6 +1,6 @@
 # Automatically generated rules. Use 'mkrules.sh' to modify/regenerate.
 
-OBJ = $(OBJDIR)$Psettings$O $(OBJDIR)$Pgcm$O $(OBJDIR)$Pccopy$O $(OBJDIR)$Pdec16be$O $(OBJDIR)$Pdec16le$O $(OBJDIR)$Pdec32be$O $(OBJDIR)$Pdec32le$O $(OBJDIR)$Pdec64be$O $(OBJDIR)$Pdec64le$O $(OBJDIR)$Penc16be$O $(OBJDIR)$Penc16le$O $(OBJDIR)$Penc32be$O $(OBJDIR)$Penc32le$O $(OBJDIR)$Penc64be$O $(OBJDIR)$Penc64le$O $(OBJDIR)$Ppemdec$O $(OBJDIR)$Pec_all_m15$O $(OBJDIR)$Pec_all_m31$O $(OBJDIR)$Pec_c25519_i15$O $(OBJDIR)$Pec_c25519_i31$O $(OBJDIR)$Pec_c25519_m15$O $(OBJDIR)$Pec_c25519_m31$O $(OBJDIR)$Pec_curve25519$O $(OBJDIR)$Pec_default$O $(OBJDIR)$Pec_p256_m15$O $(OBJDIR)$Pec_p256_m31$O $(OBJDIR)$Pec_prime_i15$O $(OBJDIR)$Pec_prime_i31$O $(OBJDIR)$Pec_secp256r1$O $(OBJDIR)$Pec_secp384r1$O $(OBJDIR)$Pec_secp521r1$O $(OBJDIR)$Pecdsa_atr$O $(OBJDIR)$Pecdsa_default_sign_asn1$O $(OBJDIR)$Pecdsa_default_sign_raw$O $(OBJDIR)$Pecdsa_default_vrfy_asn1$O $(OBJDIR)$Pecdsa_default_vrfy_raw$O $(OBJDIR)$Pecdsa_i15_bits$O $(OBJDIR)$Pecdsa_i15_sign_asn1$O $(OBJDIR)$Pecdsa_i15_sign_raw$O $(OBJDIR)$Pecdsa_i15_vrfy_asn1$O $(OBJDIR)$Pecdsa_i15_vrfy_raw$O $(OBJDIR)$Pecdsa_i31_bits$O $(OBJDIR)$Pecdsa_i31_sign_asn1$O $(OBJDIR)$Pecdsa_i31_sign_raw$O $(OBJDIR)$Pecdsa_i31_vrfy_asn1$O $(OBJDIR)$Pecdsa_i31_vrfy_raw$O $(OBJDIR)$Pecdsa_rta$O $(OBJDIR)$Pdig_oid$O $(OBJDIR)$Pdig_size$O $(OBJDIR)$Pghash_ctmul$O $(OBJDIR)$Pghash_ctmul32$O $(OBJDIR)$Pghash_ctmul64$O $(OBJDIR)$Pghash_pclmul$O $(OBJDIR)$Pghash_pwr8$O $(OBJDIR)$Pmd5$O $(OBJDIR)$Pmd5sha1$O $(OBJDIR)$Pmultihash$O $(OBJDIR)$Psha1$O $(OBJDIR)$Psha2big$O $(OBJDIR)$Psha2small$O $(OBJDIR)$Pi15_add$O $(OBJDIR)$Pi15_bitlen$O $(OBJDIR)$Pi15_decmod$O $(OBJDIR)$Pi15_decode$O $(OBJDIR)$Pi15_decred$O $(OBJDIR)$Pi15_encode$O $(OBJDIR)$Pi15_fmont$O $(OBJDIR)$Pi15_iszero$O $(OBJDIR)$Pi15_modpow$O $(OBJDIR)$Pi15_modpow2$O $(OBJDIR)$Pi15_montmul$O $(OBJDIR)$Pi15_mulacc$O $(OBJDIR)$Pi15_muladd$O $(OBJDIR)$Pi15_ninv15$O $(OBJDIR)$Pi15_reduce$O $(OBJDIR)$Pi15_rshift$O $(OBJDIR)$Pi15_sub$O $(OBJDIR)$Pi15_tmont$O $(OBJDIR)$Pi31_add$O $(OBJDIR)$Pi31_bitlen$O $(OBJDIR)$Pi31_decmod$O $(OBJDIR)$Pi31_decode$O $(OBJDIR)$Pi31_decred$O $(OBJDIR)$Pi31_encode$O $(OBJDIR)$Pi31_fmont$O $(OBJDIR)$Pi31_iszero$O $(OBJDIR)$Pi31_modpow$O $(OBJDIR)$Pi31_modpow2$O $(OBJDIR)$Pi31_montmul$O $(OBJDIR)$Pi31_mulacc$O $(OBJDIR)$Pi31_muladd$O $(OBJDIR)$Pi31_ninv31$O $(OBJDIR)$Pi31_reduce$O $(OBJDIR)$Pi31_rshift$O $(OBJDIR)$Pi31_sub$O $(OBJDIR)$Pi31_tmont$O $(OBJDIR)$Pi32_add$O $(OBJDIR)$Pi32_bitlen$O $(OBJDIR)$Pi32_decmod$O $(OBJDIR)$Pi32_decode$O $(OBJDIR)$Pi32_decred$O $(OBJDIR)$Pi32_div32$O $(OBJDIR)$Pi32_encode$O $(OBJDIR)$Pi32_fmont$O $(OBJDIR)$Pi32_iszero$O $(OBJDIR)$Pi32_modpow$O $(OBJDIR)$Pi32_montmul$O $(OBJDIR)$Pi32_mulacc$O $(OBJDIR)$Pi32_muladd$O $(OBJDIR)$Pi32_ninv32$O $(OBJDIR)$Pi32_reduce$O $(OBJDIR)$Pi32_sub$O $(OBJDIR)$Pi32_tmont$O $(OBJDIR)$Pi62_modpow2$O $(OBJDIR)$Phmac$O $(OBJDIR)$Phmac_ct$O $(OBJDIR)$Phmac_drbg$O $(OBJDIR)$Psysrng$O $(OBJDIR)$Prsa_default_pkcs1_sign$O $(OBJDIR)$Prsa_default_pkcs1_vrfy$O $(OBJDIR)$Prsa_default_priv$O $(OBJDIR)$Prsa_default_pub$O $(OBJDIR)$Prsa_i15_pkcs1_sign$O $(OBJDIR)$Prsa_i15_pkcs1_vrfy$O $(OBJDIR)$Prsa_i15_priv$O $(OBJDIR)$Prsa_i15_pub$O $(OBJDIR)$Prsa_i31_pkcs1_sign$O $(OBJDIR)$Prsa_i31_pkcs1_vrfy$O $(OBJDIR)$Prsa_i31_priv$O $(OBJDIR)$Prsa_i31_pub$O $(OBJDIR)$Prsa_i32_pkcs1_sign$O $(OBJDIR)$Prsa_i32_pkcs1_vrfy$O $(OBJDIR)$Prsa_i32_priv$O $(OBJDIR)$Prsa_i32_pub$O $(OBJDIR)$Prsa_i62_pkcs1_sign$O $(OBJDIR)$Prsa_i62_pkcs1_vrfy$O $(OBJDIR)$Prsa_i62_priv$O $(OBJDIR)$Prsa_i62_pub$O $(OBJDIR)$Prsa_pkcs1_sig_pad$O $(OBJDIR)$Prsa_pkcs1_sig_unpad$O $(OBJDIR)$Prsa_ssl_decrypt$O $(OBJDIR)$Pprf$O $(OBJDIR)$Pprf_md5sha1$O $(OBJDIR)$Pprf_sha256$O $(OBJDIR)$Pprf_sha384$O $(OBJDIR)$Pssl_ccert_single_ec$O $(OBJDIR)$Pssl_ccert_single_rsa$O $(OBJDIR)$Pssl_client$O $(OBJDIR)$Pssl_client_default_rsapub$O $(OBJDIR)$Pssl_client_full$O $(OBJDIR)$Pssl_engine$O $(OBJDIR)$Pssl_engine_default_aescbc$O $(OBJDIR)$Pssl_engine_default_aesgcm$O $(OBJDIR)$Pssl_engine_default_chapol$O $(OBJDIR)$Pssl_engine_default_descbc$O $(OBJDIR)$Pssl_engine_default_ec$O $(OBJDIR)$Pssl_engine_default_ecdsa$O $(OBJDIR)$Pssl_engine_default_rsavrfy$O $(OBJDIR)$Pssl_hashes$O $(OBJDIR)$Pssl_hs_client$O $(OBJDIR)$Pssl_hs_server$O $(OBJDIR)$Pssl_io$O $(OBJDIR)$Pssl_keyexport$O $(OBJDIR)$Pssl_lru$O $(OBJDIR)$Pssl_rec_cbc$O $(OBJDIR)$Pssl_rec_chapol$O $(OBJDIR)$Pssl_rec_gcm$O $(OBJDIR)$Pssl_scert_single_ec$O $(OBJDIR)$Pssl_scert_single_rsa$O $(OBJDIR)$Pssl_server$O $(OBJDIR)$Pssl_server_full_ec$O $(OBJDIR)$Pssl_server_full_rsa$O $(OBJDIR)$Pssl_server_mine2c$O $(OBJDIR)$Pssl_server_mine2g$O $(OBJDIR)$Pssl_server_minf2c$O $(OBJDIR)$Pssl_server_minf2g$O $(OBJDIR)$Pssl_server_minr2g$O $(OBJDIR)$Pssl_server_minu2g$O $(OBJDIR)$Pssl_server_minv2g$O $(OBJDIR)$Paes_big_cbcdec$O $(OBJDIR)$Paes_big_cbcenc$O $(OBJDIR)$Paes_big_ctr$O $(OBJDIR)$Paes_big_dec$O $(OBJDIR)$Paes_big_enc$O $(OBJDIR)$Paes_common$O $(OBJDIR)$Paes_ct$O $(OBJDIR)$Paes_ct64$O $(OBJDIR)$Paes_ct64_cbcdec$O $(OBJDIR)$Paes_ct64_cbcenc$O $(OBJDIR)$Paes_ct64_ctr$O $(OBJDIR)$Paes_ct64_dec$O $(OBJDIR)$Paes_ct64_enc$O $(OBJDIR)$Paes_ct_cbcdec$O $(OBJDIR)$Paes_ct_cbcenc$O $(OBJDIR)$Paes_ct_ctr$O $(OBJDIR)$Paes_ct_dec$O $(OBJDIR)$Paes_ct_enc$O $(OBJDIR)$Paes_pwr8$O $(OBJDIR)$Paes_pwr8_cbcdec$O $(OBJDIR)$Paes_pwr8_cbcenc$O $(OBJDIR)$Paes_pwr8_ctr$O $(OBJDIR)$Paes_small_cbcdec$O $(OBJDIR)$Paes_small_cbcenc$O $(OBJDIR)$Paes_small_ctr$O $(OBJDIR)$Paes_small_dec$O $(OBJDIR)$Paes_small_enc$O $(OBJDIR)$Paes_x86ni$O $(OBJDIR)$Paes_x86ni_cbcdec$O $(OBJDIR)$Paes_x86ni_cbcenc$O $(OBJDIR)$Paes_x86ni_ctr$O $(OBJDIR)$Pchacha20_ct$O $(OBJDIR)$Pchacha20_sse2$O $(OBJDIR)$Pdes_ct$O $(OBJDIR)$Pdes_ct_cbcdec$O $(OBJDIR)$Pdes_ct_cbcenc$O $(OBJDIR)$Pdes_support$O $(OBJDIR)$Pdes_tab$O $(OBJDIR)$Pdes_tab_cbcdec$O $(OBJDIR)$Pdes_tab_cbcenc$O $(OBJDIR)$Ppoly1305_ctmul$O $(OBJDIR)$Ppoly1305_ctmul32$O $(OBJDIR)$Ppoly1305_ctmulq$O $(OBJDIR)$Ppoly1305_i15$O $(OBJDIR)$Pskey_decoder$O $(OBJDIR)$Px509_decoder$O $(OBJDIR)$Px509_knownkey$O $(OBJDIR)$Px509_minimal$O $(OBJDIR)$Px509_minimal_full$O
+OBJ = $(OBJDIR)$Psettings$O $(OBJDIR)$Pccm$O $(OBJDIR)$Peax$O $(OBJDIR)$Pgcm$O $(OBJDIR)$Pccopy$O $(OBJDIR)$Pdec16be$O $(OBJDIR)$Pdec16le$O $(OBJDIR)$Pdec32be$O $(OBJDIR)$Pdec32le$O $(OBJDIR)$Pdec64be$O $(OBJDIR)$Pdec64le$O $(OBJDIR)$Penc16be$O $(OBJDIR)$Penc16le$O $(OBJDIR)$Penc32be$O $(OBJDIR)$Penc32le$O $(OBJDIR)$Penc64be$O $(OBJDIR)$Penc64le$O $(OBJDIR)$Ppemdec$O $(OBJDIR)$Pec_all_m15$O $(OBJDIR)$Pec_all_m31$O $(OBJDIR)$Pec_c25519_i15$O $(OBJDIR)$Pec_c25519_i31$O $(OBJDIR)$Pec_c25519_m15$O $(OBJDIR)$Pec_c25519_m31$O $(OBJDIR)$Pec_curve25519$O $(OBJDIR)$Pec_default$O $(OBJDIR)$Pec_p256_m15$O $(OBJDIR)$Pec_p256_m31$O $(OBJDIR)$Pec_prime_i15$O $(OBJDIR)$Pec_prime_i31$O $(OBJDIR)$Pec_secp256r1$O $(OBJDIR)$Pec_secp384r1$O $(OBJDIR)$Pec_secp521r1$O $(OBJDIR)$Pecdsa_atr$O $(OBJDIR)$Pecdsa_default_sign_asn1$O $(OBJDIR)$Pecdsa_default_sign_raw$O $(OBJDIR)$Pecdsa_default_vrfy_asn1$O $(OBJDIR)$Pecdsa_default_vrfy_raw$O $(OBJDIR)$Pecdsa_i15_bits$O $(OBJDIR)$Pecdsa_i15_sign_asn1$O $(OBJDIR)$Pecdsa_i15_sign_raw$O $(OBJDIR)$Pecdsa_i15_vrfy_asn1$O $(OBJDIR)$Pecdsa_i15_vrfy_raw$O $(OBJDIR)$Pecdsa_i31_bits$O $(OBJDIR)$Pecdsa_i31_sign_asn1$O $(OBJDIR)$Pecdsa_i31_sign_raw$O $(OBJDIR)$Pecdsa_i31_vrfy_asn1$O $(OBJDIR)$Pecdsa_i31_vrfy_raw$O $(OBJDIR)$Pecdsa_rta$O $(OBJDIR)$Pdig_oid$O $(OBJDIR)$Pdig_size$O $(OBJDIR)$Pghash_ctmul$O $(OBJDIR)$Pghash_ctmul32$O $(OBJDIR)$Pghash_ctmul64$O $(OBJDIR)$Pghash_pclmul$O $(OBJDIR)$Pghash_pwr8$O $(OBJDIR)$Pmd5$O $(OBJDIR)$Pmd5sha1$O $(OBJDIR)$Pmultihash$O $(OBJDIR)$Psha1$O $(OBJDIR)$Psha2big$O $(OBJDIR)$Psha2small$O $(OBJDIR)$Pi15_add$O $(OBJDIR)$Pi15_bitlen$O $(OBJDIR)$Pi15_decmod$O $(OBJDIR)$Pi15_decode$O $(OBJDIR)$Pi15_decred$O $(OBJDIR)$Pi15_encode$O $(OBJDIR)$Pi15_fmont$O $(OBJDIR)$Pi15_iszero$O $(OBJDIR)$Pi15_modpow$O $(OBJDIR)$Pi15_modpow2$O $(OBJDIR)$Pi15_montmul$O $(OBJDIR)$Pi15_mulacc$O $(OBJDIR)$Pi15_muladd$O $(OBJDIR)$Pi15_ninv15$O $(OBJDIR)$Pi15_reduce$O $(OBJDIR)$Pi15_rshift$O $(OBJDIR)$Pi15_sub$O $(OBJDIR)$Pi15_tmont$O $(OBJDIR)$Pi31_add$O $(OBJDIR)$Pi31_bitlen$O $(OBJDIR)$Pi31_decmod$O $(OBJDIR)$Pi31_decode$O $(OBJDIR)$Pi31_decred$O $(OBJDIR)$Pi31_encode$O $(OBJDIR)$Pi31_fmont$O $(OBJDIR)$Pi31_iszero$O $(OBJDIR)$Pi31_modpow$O $(OBJDIR)$Pi31_modpow2$O $(OBJDIR)$Pi31_montmul$O $(OBJDIR)$Pi31_mulacc$O $(OBJDIR)$Pi31_muladd$O $(OBJDIR)$Pi31_ninv31$O $(OBJDIR)$Pi31_reduce$O $(OBJDIR)$Pi31_rshift$O $(OBJDIR)$Pi31_sub$O $(OBJDIR)$Pi31_tmont$O $(OBJDIR)$Pi32_add$O $(OBJDIR)$Pi32_bitlen$O $(OBJDIR)$Pi32_decmod$O $(OBJDIR)$Pi32_decode$O $(OBJDIR)$Pi32_decred$O $(OBJDIR)$Pi32_div32$O $(OBJDIR)$Pi32_encode$O $(OBJDIR)$Pi32_fmont$O $(OBJDIR)$Pi32_iszero$O $(OBJDIR)$Pi32_modpow$O $(OBJDIR)$Pi32_montmul$O $(OBJDIR)$Pi32_mulacc$O $(OBJDIR)$Pi32_muladd$O $(OBJDIR)$Pi32_ninv32$O $(OBJDIR)$Pi32_reduce$O $(OBJDIR)$Pi32_sub$O $(OBJDIR)$Pi32_tmont$O $(OBJDIR)$Pi62_modpow2$O $(OBJDIR)$Phmac$O $(OBJDIR)$Phmac_ct$O $(OBJDIR)$Phmac_drbg$O $(OBJDIR)$Psysrng$O $(OBJDIR)$Prsa_default_pkcs1_sign$O $(OBJDIR)$Prsa_default_pkcs1_vrfy$O $(OBJDIR)$Prsa_default_priv$O $(OBJDIR)$Prsa_default_pub$O $(OBJDIR)$Prsa_i15_pkcs1_sign$O $(OBJDIR)$Prsa_i15_pkcs1_vrfy$O $(OBJDIR)$Prsa_i15_priv$O $(OBJDIR)$Prsa_i15_pub$O $(OBJDIR)$Prsa_i31_pkcs1_sign$O $(OBJDIR)$Prsa_i31_pkcs1_vrfy$O $(OBJDIR)$Prsa_i31_priv$O $(OBJDIR)$Prsa_i31_pub$O $(OBJDIR)$Prsa_i32_pkcs1_sign$O $(OBJDIR)$Prsa_i32_pkcs1_vrfy$O $(OBJDIR)$Prsa_i32_priv$O $(OBJDIR)$Prsa_i32_pub$O $(OBJDIR)$Prsa_i62_pkcs1_sign$O $(OBJDIR)$Prsa_i62_pkcs1_vrfy$O $(OBJDIR)$Prsa_i62_priv$O $(OBJDIR)$Prsa_i62_pub$O $(OBJDIR)$Prsa_pkcs1_sig_pad$O $(OBJDIR)$Prsa_pkcs1_sig_unpad$O $(OBJDIR)$Prsa_ssl_decrypt$O $(OBJDIR)$Pprf$O $(OBJDIR)$Pprf_md5sha1$O $(OBJDIR)$Pprf_sha256$O $(OBJDIR)$Pprf_sha384$O $(OBJDIR)$Pssl_ccert_single_ec$O $(OBJDIR)$Pssl_ccert_single_rsa$O $(OBJDIR)$Pssl_client$O $(OBJDIR)$Pssl_client_default_rsapub$O $(OBJDIR)$Pssl_client_full$O $(OBJDIR)$Pssl_engine$O $(OBJDIR)$Pssl_engine_default_aescbc$O $(OBJDIR)$Pssl_engine_default_aesgcm$O $(OBJDIR)$Pssl_engine_default_chapol$O $(OBJDIR)$Pssl_engine_default_descbc$O $(OBJDIR)$Pssl_engine_default_ec$O $(OBJDIR)$Pssl_engine_default_ecdsa$O $(OBJDIR)$Pssl_engine_default_rsavrfy$O $(OBJDIR)$Pssl_hashes$O $(OBJDIR)$Pssl_hs_client$O $(OBJDIR)$Pssl_hs_server$O $(OBJDIR)$Pssl_io$O $(OBJDIR)$Pssl_keyexport$O $(OBJDIR)$Pssl_lru$O $(OBJDIR)$Pssl_rec_cbc$O $(OBJDIR)$Pssl_rec_chapol$O $(OBJDIR)$Pssl_rec_gcm$O $(OBJDIR)$Pssl_scert_single_ec$O $(OBJDIR)$Pssl_scert_single_rsa$O $(OBJDIR)$Pssl_server$O $(OBJDIR)$Pssl_server_full_ec$O $(OBJDIR)$Pssl_server_full_rsa$O $(OBJDIR)$Pssl_server_mine2c$O $(OBJDIR)$Pssl_server_mine2g$O $(OBJDIR)$Pssl_server_minf2c$O $(OBJDIR)$Pssl_server_minf2g$O $(OBJDIR)$Pssl_server_minr2g$O $(OBJDIR)$Pssl_server_minu2g$O $(OBJDIR)$Pssl_server_minv2g$O $(OBJDIR)$Paes_big_cbcdec$O $(OBJDIR)$Paes_big_cbcenc$O $(OBJDIR)$Paes_big_ctr$O $(OBJDIR)$Paes_big_ctrcbc$O $(OBJDIR)$Paes_big_dec$O $(OBJDIR)$Paes_big_enc$O $(OBJDIR)$Paes_common$O $(OBJDIR)$Paes_ct$O $(OBJDIR)$Paes_ct64$O $(OBJDIR)$Paes_ct64_cbcdec$O $(OBJDIR)$Paes_ct64_cbcenc$O $(OBJDIR)$Paes_ct64_ctr$O $(OBJDIR)$Paes_ct64_ctrcbc$O $(OBJDIR)$Paes_ct64_dec$O $(OBJDIR)$Paes_ct64_enc$O $(OBJDIR)$Paes_ct_cbcdec$O $(OBJDIR)$Paes_ct_cbcenc$O $(OBJDIR)$Paes_ct_ctr$O $(OBJDIR)$Paes_ct_ctrcbc$O $(OBJDIR)$Paes_ct_dec$O $(OBJDIR)$Paes_ct_enc$O $(OBJDIR)$Paes_pwr8$O $(OBJDIR)$Paes_pwr8_cbcdec$O $(OBJDIR)$Paes_pwr8_cbcenc$O $(OBJDIR)$Paes_pwr8_ctr$O $(OBJDIR)$Paes_small_cbcdec$O $(OBJDIR)$Paes_small_cbcenc$O $(OBJDIR)$Paes_small_ctr$O $(OBJDIR)$Paes_small_ctrcbc$O $(OBJDIR)$Paes_small_dec$O $(OBJDIR)$Paes_small_enc$O $(OBJDIR)$Paes_x86ni$O $(OBJDIR)$Paes_x86ni_cbcdec$O $(OBJDIR)$Paes_x86ni_cbcenc$O $(OBJDIR)$Paes_x86ni_ctr$O $(OBJDIR)$Paes_x86ni_ctrcbc$O $(OBJDIR)$Pchacha20_ct$O $(OBJDIR)$Pchacha20_sse2$O $(OBJDIR)$Pdes_ct$O $(OBJDIR)$Pdes_ct_cbcdec$O $(OBJDIR)$Pdes_ct_cbcenc$O $(OBJDIR)$Pdes_support$O $(OBJDIR)$Pdes_tab$O $(OBJDIR)$Pdes_tab_cbcdec$O $(OBJDIR)$Pdes_tab_cbcenc$O $(OBJDIR)$Ppoly1305_ctmul$O $(OBJDIR)$Ppoly1305_ctmul32$O $(OBJDIR)$Ppoly1305_ctmulq$O $(OBJDIR)$Ppoly1305_i15$O $(OBJDIR)$Pskey_decoder$O $(OBJDIR)$Px509_decoder$O $(OBJDIR)$Px509_knownkey$O $(OBJDIR)$Px509_minimal$O $(OBJDIR)$Px509_minimal_full$O
 OBJBRSSL = $(OBJDIR)$Pbrssl$O $(OBJDIR)$Pcerts$O $(OBJDIR)$Pchain$O $(OBJDIR)$Pclient$O $(OBJDIR)$Perrors$O $(OBJDIR)$Pfiles$O $(OBJDIR)$Pimpl$O $(OBJDIR)$Pkeys$O $(OBJDIR)$Pnames$O $(OBJDIR)$Pserver$O $(OBJDIR)$Pskey$O $(OBJDIR)$Psslio$O $(OBJDIR)$Pta$O $(OBJDIR)$Ptwrch$O $(OBJDIR)$Pvector$O $(OBJDIR)$Pverify$O $(OBJDIR)$Pxmem$O
 OBJTESTCRYPTO = $(OBJDIR)$Ptest_crypto$O
 OBJTESTSPEED = $(OBJDIR)$Ptest_speed$O
@@ -64,6 +64,12 @@ $(TESTX509): $(BEARSSLLIB) $(OBJTESTX509)
 $(OBJDIR)$Psettings$O: src$Psettings.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Psettings$O src$Psettings.c
 
+$(OBJDIR)$Pccm$O: src$Paead$Pccm.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pccm$O src$Paead$Pccm.c
+
+$(OBJDIR)$Peax$O: src$Paead$Peax.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Peax$O src$Paead$Peax.c
+
 $(OBJDIR)$Pgcm$O: src$Paead$Pgcm.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pgcm$O src$Paead$Pgcm.c
 
@@ -607,6 +613,9 @@ $(OBJDIR)$Paes_big_cbcenc$O: src$Psymcipher$Paes_big_cbcenc.c $(HEADERSPRIV)
 $(OBJDIR)$Paes_big_ctr$O: src$Psymcipher$Paes_big_ctr.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_big_ctr$O src$Psymcipher$Paes_big_ctr.c
 
+$(OBJDIR)$Paes_big_ctrcbc$O: src$Psymcipher$Paes_big_ctrcbc.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_big_ctrcbc$O src$Psymcipher$Paes_big_ctrcbc.c
+
 $(OBJDIR)$Paes_big_dec$O: src$Psymcipher$Paes_big_dec.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_big_dec$O src$Psymcipher$Paes_big_dec.c
 
@@ -631,6 +640,9 @@ $(OBJDIR)$Paes_ct64_cbcenc$O: src$Psymcipher$Paes_ct64_cbcenc.c $(HEADERSPRIV)
 $(OBJDIR)$Paes_ct64_ctr$O: src$Psymcipher$Paes_ct64_ctr.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_ct64_ctr$O src$Psymcipher$Paes_ct64_ctr.c
 
+$(OBJDIR)$Paes_ct64_ctrcbc$O: src$Psymcipher$Paes_ct64_ctrcbc.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_ct64_ctrcbc$O src$Psymcipher$Paes_ct64_ctrcbc.c
+
 $(OBJDIR)$Paes_ct64_dec$O: src$Psymcipher$Paes_ct64_dec.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_ct64_dec$O src$Psymcipher$Paes_ct64_dec.c
 
@@ -646,6 +658,9 @@ $(OBJDIR)$Paes_ct_cbcenc$O: src$Psymcipher$Paes_ct_cbcenc.c $(HEADERSPRIV)
 $(OBJDIR)$Paes_ct_ctr$O: src$Psymcipher$Paes_ct_ctr.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_ct_ctr$O src$Psymcipher$Paes_ct_ctr.c
 
+$(OBJDIR)$Paes_ct_ctrcbc$O: src$Psymcipher$Paes_ct_ctrcbc.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_ct_ctrcbc$O src$Psymcipher$Paes_ct_ctrcbc.c
+
 $(OBJDIR)$Paes_ct_dec$O: src$Psymcipher$Paes_ct_dec.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_ct_dec$O src$Psymcipher$Paes_ct_dec.c
 
@@ -673,6 +688,9 @@ $(OBJDIR)$Paes_small_cbcenc$O: src$Psymcipher$Paes_small_cbcenc.c $(HEADERSPRIV)
 $(OBJDIR)$Paes_small_ctr$O: src$Psymcipher$Paes_small_ctr.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_small_ctr$O src$Psymcipher$Paes_small_ctr.c
 
+$(OBJDIR)$Paes_small_ctrcbc$O: src$Psymcipher$Paes_small_ctrcbc.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_small_ctrcbc$O src$Psymcipher$Paes_small_ctrcbc.c
+
 $(OBJDIR)$Paes_small_dec$O: src$Psymcipher$Paes_small_dec.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_small_dec$O src$Psymcipher$Paes_small_dec.c
 
@@ -691,6 +709,9 @@ $(OBJDIR)$Paes_x86ni_cbcenc$O: src$Psymcipher$Paes_x86ni_cbcenc.c $(HEADERSPRIV)
 $(OBJDIR)$Paes_x86ni_ctr$O: src$Psymcipher$Paes_x86ni_ctr.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_x86ni_ctr$O src$Psymcipher$Paes_x86ni_ctr.c
 
+$(OBJDIR)$Paes_x86ni_ctrcbc$O: src$Psymcipher$Paes_x86ni_ctrcbc.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Paes_x86ni_ctrcbc$O src$Psymcipher$Paes_x86ni_ctrcbc.c
+
 $(OBJDIR)$Pchacha20_ct$O: src$Psymcipher$Pchacha20_ct.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pchacha20_ct$O src$Psymcipher$Pchacha20_ct.c
 
index 44f787e..2fd1f0f 100755 (executable)
@@ -50,6 +50,8 @@ set -e
 # Source files. Please keep in alphabetical order.
 coresrc=" \
        src/settings.c \
+       src/aead/ccm.c \
+       src/aead/eax.c \
        src/aead/gcm.c \
        src/codec/ccopy.c \
        src/codec/dec16be.c \
@@ -231,6 +233,7 @@ coresrc=" \
        src/symcipher/aes_big_cbcdec.c \
        src/symcipher/aes_big_cbcenc.c \
        src/symcipher/aes_big_ctr.c \
+       src/symcipher/aes_big_ctrcbc.c \
        src/symcipher/aes_big_dec.c \
        src/symcipher/aes_big_enc.c \
        src/symcipher/aes_common.c \
@@ -239,11 +242,13 @@ coresrc=" \
        src/symcipher/aes_ct64_cbcdec.c \
        src/symcipher/aes_ct64_cbcenc.c \
        src/symcipher/aes_ct64_ctr.c \
+       src/symcipher/aes_ct64_ctrcbc.c \
        src/symcipher/aes_ct64_dec.c \
        src/symcipher/aes_ct64_enc.c \
        src/symcipher/aes_ct_cbcdec.c \
        src/symcipher/aes_ct_cbcenc.c \
        src/symcipher/aes_ct_ctr.c \
+       src/symcipher/aes_ct_ctrcbc.c \
        src/symcipher/aes_ct_dec.c \
        src/symcipher/aes_ct_enc.c \
        src/symcipher/aes_pwr8.c \
@@ -253,12 +258,14 @@ coresrc=" \
        src/symcipher/aes_small_cbcdec.c \
        src/symcipher/aes_small_cbcenc.c \
        src/symcipher/aes_small_ctr.c \
+       src/symcipher/aes_small_ctrcbc.c \
        src/symcipher/aes_small_dec.c \
        src/symcipher/aes_small_enc.c \
        src/symcipher/aes_x86ni.c \
        src/symcipher/aes_x86ni_cbcdec.c \
        src/symcipher/aes_x86ni_cbcenc.c \
        src/symcipher/aes_x86ni_ctr.c \
+       src/symcipher/aes_x86ni_ctrcbc.c \
        src/symcipher/chacha20_ct.c \
        src/symcipher/chacha20_sse2.c \
        src/symcipher/des_ct.c \
diff --git a/src/aead/ccm.c b/src/aead/ccm.c
new file mode 100644 (file)
index 0000000..68cc913
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Implementation Notes
+ * ====================
+ *
+ * The combined CTR + CBC-MAC functions can only handle full blocks,
+ * so some buffering is necessary.
+ *
+ *  - 'ptr' contains a value from 0 to 15, which is the number of bytes
+ *    accumulated in buf[] that still needs to be processed with the
+ *    current CBC-MAC computation.
+ *
+ *  - When processing the message itself, CTR encryption/decryption is
+ *    also done at the same time. The first 'ptr' bytes of buf[] then
+ *    contains the plaintext bytes, while the last '16 - ptr' bytes of
+ *    buf[] are the remnants of the stream block, to be used against
+ *    the next input bytes, when available. When 'ptr' is 0, the
+ *    contents of buf[] are to be ignored.
+ *
+ *  - The current counter and running CBC-MAC values are kept in 'ctr'
+ *    and 'cbcmac', respectively.
+ */
+
+/* see bearssl_block.h */
+void
+br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx)
+{
+       ctx->bctx = bctx;
+}
+
+/* see bearssl_block.h */
+int
+br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len,
+       uint64_t aad_len, uint64_t data_len, size_t tag_len)
+{
+       unsigned char tmp[16];
+       unsigned u, q;
+
+       if (nonce_len < 7 || nonce_len > 13) {
+               return 0;
+       }
+       if (tag_len < 4 || tag_len > 16 || (tag_len & 1) != 0) {
+               return 0;
+       }
+       q = 15 - (unsigned)nonce_len;
+       ctx->tag_len = tag_len;
+
+       /*
+        * Block B0, to start CBC-MAC.
+        */
+       tmp[0] = (aad_len > 0 ? 0x40 : 0x00)
+               | (((unsigned)tag_len - 2) << 2)
+               | (q - 1);
+       memcpy(tmp + 1, nonce, nonce_len);
+       for (u = 0; u < q; u ++) {
+               tmp[15 - u] = (unsigned char)data_len;
+               data_len >>= 8;
+       }
+       if (data_len != 0) {
+               /*
+                * If the data length was not entirely consumed in the
+                * loop above, then it exceeds the maximum limit of
+                * q bytes (when encoded).
+                */
+               return 0;
+       }
+
+       /*
+        * Start CBC-MAC.
+        */
+       memset(ctx->cbcmac, 0, sizeof ctx->cbcmac);
+       (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, tmp, sizeof tmp);
+
+       /*
+        * Assemble AAD length header.
+        */
+       if ((aad_len >> 32) != 0) {
+               ctx->buf[0] = 0xFF;
+               ctx->buf[1] = 0xFF;
+               br_enc64be(ctx->buf + 2, aad_len);
+               ctx->ptr = 10;
+       } else if (aad_len >= 0xFF00) {
+               ctx->buf[0] = 0xFF;
+               ctx->buf[1] = 0xFE;
+               br_enc32be(ctx->buf + 2, (uint32_t)aad_len);
+               ctx->ptr = 6;
+       } else if (aad_len > 0) {
+               br_enc16be(ctx->buf, (unsigned)aad_len);
+               ctx->ptr = 2;
+       } else {
+               ctx->ptr = 0;
+       }
+
+       /*
+        * Make initial counter value and compute tag mask.
+        */
+       ctx->ctr[0] = q - 1;
+       memcpy(ctx->ctr + 1, nonce, nonce_len);
+       memset(ctx->ctr + 1 + nonce_len, 0, q);
+       memset(ctx->tagmask, 0, sizeof ctx->tagmask);
+       (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr,
+               ctx->tagmask, sizeof ctx->tagmask);
+
+       return 1;
+}
+
+/* see bearssl_block.h */
+void
+br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len)
+{
+       const unsigned char *dbuf;
+       size_t ptr;
+
+       dbuf = data;
+
+       /*
+        * Complete partial block, if needed.
+        */
+       ptr = ctx->ptr;
+       if (ptr != 0) {
+               size_t clen;
+
+               clen = (sizeof ctx->buf) - ptr;
+               if (clen > len) {
+                       memcpy(ctx->buf + ptr, dbuf, len);
+                       ctx->ptr = ptr + len;
+                       return;
+               }
+               memcpy(ctx->buf + ptr, dbuf, clen);
+               dbuf += clen;
+               len -= clen;
+               (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac,
+                       ctx->buf, sizeof ctx->buf);
+       }
+
+       /*
+        * Process complete blocks.
+        */
+       ptr = len & 15;
+       len -= ptr;
+       (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, dbuf, len);
+       dbuf += len;
+
+       /*
+        * Copy last partial block in the context buffer.
+        */
+       memcpy(ctx->buf, dbuf, ptr);
+       ctx->ptr = ptr;
+}
+
+/* see bearssl_block.h */
+void
+br_ccm_flip(br_ccm_context *ctx)
+{
+       size_t ptr;
+
+       /*
+        * Complete AAD partial block with zeros, if necessary.
+        */
+       ptr = ctx->ptr;
+       if (ptr != 0) {
+               memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr);
+               (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac,
+                       ctx->buf, sizeof ctx->buf);
+               ctx->ptr = 0;
+       }
+
+       /*
+        * Counter was already set by br_ccm_reset().
+        */
+}
+
+/* see bearssl_block.h */
+void
+br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len)
+{
+       unsigned char *dbuf;
+       size_t ptr;
+
+       dbuf = data;
+
+       /*
+        * Complete a partial block, if any: ctx->buf[] contains
+        * ctx->ptr plaintext bytes (already reported), and the other
+        * bytes are CTR stream output.
+        */
+       ptr = ctx->ptr;
+       if (ptr != 0) {
+               size_t clen;
+               size_t u;
+
+               clen = (sizeof ctx->buf) - ptr;
+               if (clen > len) {
+                       clen = len;
+               }
+               if (encrypt) {
+                       for (u = 0; u < clen; u ++) {
+                               unsigned w, x;
+
+                               w = ctx->buf[ptr + u];
+                               x = dbuf[u];
+                               ctx->buf[ptr + u] = x;
+                               dbuf[u] = w ^ x;
+                       }
+               } else {
+                       for (u = 0; u < clen; u ++) {
+                               unsigned w;
+
+                               w = ctx->buf[ptr + u] ^ dbuf[u];
+                               dbuf[u] = w;
+                               ctx->buf[ptr + u] = w;
+                       }
+               }
+               dbuf += clen;
+               len -= clen;
+               ptr += clen;
+               if (ptr < sizeof ctx->buf) {
+                       ctx->ptr = ptr;
+                       return;
+               }
+               (*ctx->bctx)->mac(ctx->bctx,
+                       ctx->cbcmac, ctx->buf, sizeof ctx->buf);
+       }
+
+       /*
+        * Process all complete blocks. Note that the ctrcbc API is for
+        * encrypt-then-MAC (CBC-MAC is computed over the encrypted
+        * blocks) while CCM uses MAC-and-encrypt (CBC-MAC is computed
+        * over the plaintext blocks). Therefore, we need to use the
+        * _decryption_ function for encryption, and the encryption
+        * function for decryption (this works because CTR encryption
+        * and decryption are identical, so the choice really is about
+        * computing the CBC-MAC before or after XORing with the CTR
+        * stream).
+        */
+       ptr = len & 15;
+       len -= ptr;
+       if (encrypt) {
+               (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac,
+                       dbuf, len);
+       } else {
+               (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac,
+                       dbuf, len);
+       }
+       dbuf += len;
+
+       /*
+        * If there is some remaining data, then we need to compute an
+        * extra block of CTR stream.
+        */
+       if (ptr != 0) {
+               size_t u;
+
+               memset(ctx->buf, 0, sizeof ctx->buf);
+               (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr,
+                       ctx->buf, sizeof ctx->buf);
+               if (encrypt) {
+                       for (u = 0; u < ptr; u ++) {
+                               unsigned w, x;
+
+                               w = ctx->buf[u];
+                               x = dbuf[u];
+                               ctx->buf[u] = x;
+                               dbuf[u] = w ^ x;
+                       }
+               } else {
+                       for (u = 0; u < ptr; u ++) {
+                               unsigned w;
+
+                               w = ctx->buf[u] ^ dbuf[u];
+                               dbuf[u] = w;
+                               ctx->buf[u] = w;
+                       }
+               }
+       }
+       ctx->ptr = ptr;
+}
+
+/* see bearssl_block.h */
+size_t
+br_ccm_get_tag(br_ccm_context *ctx, void *tag)
+{
+       size_t ptr;
+       size_t u;
+
+       /*
+        * If there is some buffered data, then we need to pad it with
+        * zeros and finish up CBC-MAC.
+        */
+       ptr = ctx->ptr;
+       if (ptr != 0) {
+               memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr);
+               (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac,
+                       ctx->buf, sizeof ctx->buf);
+       }
+
+       /*
+        * XOR the tag mask into the CBC-MAC output.
+        */
+       for (u = 0; u < ctx->tag_len; u ++) {
+               ctx->cbcmac[u] ^= ctx->tagmask[u];
+       }
+       memcpy(tag, ctx->cbcmac, ctx->tag_len);
+       return ctx->tag_len;
+}
+
+/* see bearssl_block.h */
+uint32_t
+br_ccm_check_tag(br_ccm_context *ctx, const void *tag)
+{
+       unsigned char tmp[16];
+       size_t u, tag_len;
+       uint32_t z;
+
+       tag_len = br_ccm_get_tag(ctx, tmp);
+       z = 0;
+       for (u = 0; u < tag_len; u ++) {
+               z |= tmp[u] ^ ((const unsigned char *)tag)[u];
+       }
+       return EQ0(z);
+}
diff --git a/src/aead/eax.c b/src/aead/eax.c
new file mode 100644 (file)
index 0000000..07b1cb9
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/*
+ * Implementation Notes
+ * ====================
+ *
+ * The combined CTR + CBC-MAC functions can only handle full blocks,
+ * so some buffering is necessary. Moreover, EAX has a special padding
+ * rule for CBC-MAC, which implies that we cannot compute the MAC over
+ * the last received full block until we know whether we are at the
+ * end of the data or not.
+ *
+ *  - 'ptr' contains a value from 1 to 16, which is the number of bytes
+ *    accumulated in buf[] that still needs to be processed with the
+ *    current OMAC computation. Beware that this can go to 16: a
+ *    complete block cannot be processed until it is known whether it
+ *    is the last block or not. However, it can never be 0, because
+ *    OMAC^t works on an input that is at least one-block long.
+ *
+ *  - When processing the message itself, CTR encryption/decryption is
+ *    also done at the same time. The first 'ptr' bytes of buf[] then
+ *    contains the encrypted bytes, while the last '16 - ptr' bytes of
+ *    buf[] are the remnants of the stream block, to be used against
+ *    the next input bytes, when available.
+ *
+ *  - The current counter and running CBC-MAC values are kept in 'ctr'
+ *    and 'cbcmac', respectively.
+ *
+ *  - The derived keys for padding are kept in L2 and L4 (double and
+ *    quadruple of Enc_K(0^n), in GF(2^128), respectively).
+ */
+
+/*
+ * Start an OMAC computation; the first block is the big-endian
+ * representation of the provided value ('val' must fit on one byte).
+ * We make it a delayed block because it may also be the last one,
+ */
+static void
+omac_start(br_eax_context *ctx, unsigned val)
+{
+       memset(ctx->cbcmac, 0, sizeof ctx->cbcmac);
+       memset(ctx->buf, 0, sizeof ctx->buf);
+       ctx->buf[15] = val;
+       ctx->ptr = 16;
+}
+
+/*
+ * Double a value in finite field GF(2^128), defined with modulus
+ * X^128+X^7+X^2+X+1.
+ */
+static void
+double_gf128(unsigned char *dst, const unsigned char *src)
+{
+       unsigned cc;
+       int i;
+
+       cc = 0x87 & -((unsigned)src[0] >> 7);
+       for (i = 15; i >= 0; i --) {
+               unsigned z;
+
+               z = (src[i] << 1) ^ cc;
+               cc = z >> 8;
+               dst[i] = (unsigned char)z;
+       }
+}
+
+/*
+ * Apply padding to the last block, currently in ctx->buf (with
+ * ctx->ptr bytes), and finalize OMAC computation.
+ */
+static void
+do_pad(br_eax_context *ctx)
+{
+       unsigned char *pad;
+       size_t ptr, u;
+
+       ptr = ctx->ptr;
+       if (ptr == 16) {
+               pad = ctx->L2;
+       } else {
+               ctx->buf[ptr ++] = 0x80;
+               memset(ctx->buf + ptr, 0x00, 16 - ptr);
+               pad = ctx->L4;
+       }
+       for (u = 0; u < sizeof ctx->buf; u ++) {
+               ctx->buf[u] ^= pad[u];
+       }
+       (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, ctx->buf, sizeof ctx->buf);
+}
+
+/*
+ * Apply CBC-MAC on the provided data, with buffering management. This
+ * function assumes that on input, ctx->buf contains a full block of
+ * unprocessed data.
+ */
+static void
+do_cbcmac_chunk(br_eax_context *ctx, const void *data, size_t len)
+{
+       size_t ptr;
+
+       if (len == 0) {
+               return;
+       }
+       ptr = len & (size_t)15;
+       if (ptr == 0) {
+               len -= 16;
+               ptr = 16;
+       } else {
+               len -= ptr;
+       }
+       (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, ctx->buf, sizeof ctx->buf);
+       (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, data, len);
+       memcpy(ctx->buf, (const unsigned char *)data + len, ptr);
+       ctx->ptr = ptr;
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_init(br_eax_context *ctx, const br_block_ctrcbc_class **bctx)
+{
+       unsigned char tmp[16], iv[16];
+
+       ctx->vtable = &br_eax_vtable;
+       ctx->bctx = bctx;
+
+       /*
+        * Encrypt a whole-zero block to compute L2 and L4.
+        */
+       memset(tmp, 0, sizeof tmp);
+       memset(iv, 0, sizeof iv);
+       (*bctx)->ctr(bctx, iv, tmp, sizeof tmp);
+       double_gf128(ctx->L2, tmp);
+       double_gf128(ctx->L4, ctx->L2);
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_reset(br_eax_context *ctx, const void *nonce, size_t len)
+{
+       /*
+        * Process nonce with OMAC^0.
+        */
+       omac_start(ctx, 0);
+       do_cbcmac_chunk(ctx, nonce, len);
+       do_pad(ctx);
+       memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac);
+
+       /*
+        * Start OMAC^1 for the AAD ("header" in the EAX specification).
+        */
+       omac_start(ctx, 1);
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_aad_inject(br_eax_context *ctx, const void *data, size_t len)
+{
+       size_t ptr;
+
+       ptr = ctx->ptr;
+
+       /*
+        * If there is a partial block, first complete it.
+        */
+       if (ptr < 16) {
+               size_t clen;
+
+               clen = 16 - ptr;
+               if (len <= clen) {
+                       memcpy(ctx->buf + ptr, data, len);
+                       ctx->ptr = ptr + len;
+                       return;
+               }
+               memcpy(ctx->buf + ptr, data, clen);
+               data = (const unsigned char *)data + clen;
+               len -= clen;
+       }
+
+       /*
+        * We now have a full block in buf[], and this is not the last
+        * block.
+        */
+       do_cbcmac_chunk(ctx, data, len);
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_flip(br_eax_context *ctx)
+{
+       /*
+        * Complete the OMAC computation on the AAD.
+        */
+       do_pad(ctx);
+       memcpy(ctx->head, ctx->cbcmac, sizeof ctx->cbcmac);
+
+       /*
+        * Start OMAC^2 for the encrypted data.
+        */
+       omac_start(ctx, 2);
+
+       /*
+        * Initial counter value for CTR is the processed nonce.
+        */
+       memcpy(ctx->ctr, ctx->nonce, sizeof ctx->nonce);
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_run(br_eax_context *ctx, int encrypt, void *data, size_t len)
+{
+       unsigned char *dbuf;
+       size_t ptr;
+
+       /*
+        * Ensure that there is actual data to process.
+        */
+       if (len == 0) {
+               return;
+       }
+
+       dbuf = data;
+       ptr = ctx->ptr;
+
+       if (ptr != 16) {
+               /*
+                * We have a partially consumed block.
+                */
+               size_t u, clen;
+
+               clen = 16 - ptr;
+               if (len <= clen) {
+                       clen = len;
+               }
+               if (encrypt) {
+                       for (u = 0; u < clen; u ++) {
+                               ctx->buf[ptr + u] ^= dbuf[u];
+                       }
+                       memcpy(dbuf, ctx->buf + ptr, clen);
+               } else {
+                       for (u = 0; u < clen; u ++) {
+                               unsigned dx, sx;
+
+                               sx = ctx->buf[ptr + u];
+                               dx = dbuf[u];
+                               ctx->buf[ptr + u] = dx;
+                               dbuf[u] = sx ^ dx;
+                       }
+               }
+
+               if (len <= clen) {
+                       ctx->ptr = ptr + clen;
+                       return;
+               }
+               dbuf += clen;
+               len -= clen;
+       }
+
+       /*
+        * We now have a complete encrypted block in buf[] that must still
+        * be processed with OMAC, and this is not the final buf.
+        */
+       (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, ctx->buf, sizeof ctx->buf);
+
+       /*
+        * Do CTR encryption or decryption and CBC-MAC for all full blocks
+        * except the last.
+        */
+       ptr = len & (size_t)15;
+       if (ptr == 0) {
+               len -= 16;
+               ptr = 16;
+       } else {
+               len -= ptr;
+       }
+       if (encrypt) {
+               (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac,
+                       dbuf, len);
+       } else {
+               (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac,
+                       dbuf, len);
+       }
+       dbuf += len;
+
+       /*
+        * Compute next block of CTR stream, and use it to finish
+        * encrypting or decrypting the data.
+        */
+       memset(ctx->buf, 0, sizeof ctx->buf);
+       (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, ctx->buf, sizeof ctx->buf);
+       if (encrypt) {
+               size_t u;
+
+               for (u = 0; u < ptr; u ++) {
+                       ctx->buf[u] ^= dbuf[u];
+               }
+               memcpy(dbuf, ctx->buf, ptr);
+       } else {
+               size_t u;
+
+               for (u = 0; u < ptr; u ++) {
+                       unsigned dx, sx;
+
+                       sx = ctx->buf[u];
+                       dx = dbuf[u];
+                       ctx->buf[u] = dx;
+                       dbuf[u] = sx ^ dx;
+               }
+       }
+       ctx->ptr = ptr;
+}
+
+/*
+ * Complete tag computation. The final tag is written in ctx->cbcmac.
+ */
+static void
+do_final(br_eax_context *ctx)
+{
+       size_t u;
+
+       do_pad(ctx);
+
+       /*
+        * Authentication tag is the XOR of the three OMAC outputs for
+        * the nonce, AAD and encrypted data.
+        */
+       for (u = 0; u < 16; u ++) {
+               ctx->cbcmac[u] ^= ctx->nonce[u] ^ ctx->head[u];
+       }
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_get_tag(br_eax_context *ctx, void *tag)
+{
+       do_final(ctx);
+       memcpy(tag, ctx->cbcmac, sizeof ctx->cbcmac);
+}
+
+/* see bearssl_aead.h */
+void
+br_eax_get_tag_trunc(br_eax_context *ctx, void *tag, size_t len)
+{
+       do_final(ctx);
+       memcpy(tag, ctx->cbcmac, len);
+}
+
+/* see bearssl_aead.h */
+uint32_t
+br_eax_check_tag_trunc(br_eax_context *ctx, const void *tag, size_t len)
+{
+       unsigned char tmp[16];
+       size_t u;
+       int x;
+
+       br_eax_get_tag(ctx, tmp);
+       x = 0;
+       for (u = 0; u < len; u ++) {
+               x |= tmp[u] ^ ((const unsigned char *)tag)[u];
+       }
+       return EQ0(x);
+}
+
+/* see bearssl_aead.h */
+uint32_t
+br_eax_check_tag(br_eax_context *ctx, const void *tag)
+{
+       return br_eax_check_tag_trunc(ctx, tag, 16);
+}
+
+/* see bearssl_aead.h */
+const br_aead_class br_eax_vtable = {
+       16,
+       (void (*)(const br_aead_class **, const void *, size_t))
+               &br_eax_reset,
+       (void (*)(const br_aead_class **, const void *, size_t))
+               &br_eax_aad_inject,
+       (void (*)(const br_aead_class **))
+               &br_eax_flip,
+       (void (*)(const br_aead_class **, int, void *, size_t))
+               &br_eax_run,
+       (void (*)(const br_aead_class **, void *))
+               &br_eax_get_tag,
+       (uint32_t (*)(const br_aead_class **, const void *))
+               &br_eax_check_tag,
+       (void (*)(const br_aead_class **, void *, size_t))
+               &br_eax_get_tag_trunc,
+       (uint32_t (*)(const br_aead_class **, const void *, size_t))
+               &br_eax_check_tag_trunc
+};
index 9cf0f38..ede5f08 100644 (file)
@@ -56,6 +56,7 @@ br_gcm_init(br_gcm_context *ctx, const br_block_ctr_class **bctx, br_ghash gh)
 {
        unsigned char iv[12];
 
+       ctx->vtable = &br_gcm_vtable;
        ctx->bctx = bctx;
        ctx->gh = gh;
 
@@ -262,9 +263,19 @@ br_gcm_get_tag(br_gcm_context *ctx, void *tag)
        (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->j0_2, tag, 16);
 }
 
+/* see bearssl_aead.h */
+void
+br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len)
+{
+       unsigned char tmp[16];
+
+       br_gcm_get_tag(ctx, tmp);
+       memcpy(tag, tmp, len);
+}
+
 /* see bearssl_aead.h */
 uint32_t
-br_gcm_check_tag(br_gcm_context *ctx, const void *tag)
+br_gcm_check_tag_trunc(br_gcm_context *ctx, const void *tag, size_t len)
 {
        unsigned char tmp[16];
        size_t u;
@@ -272,12 +283,19 @@ br_gcm_check_tag(br_gcm_context *ctx, const void *tag)
 
        br_gcm_get_tag(ctx, tmp);
        x = 0;
-       for (u = 0; u < sizeof tmp; u ++) {
+       for (u = 0; u < len; u ++) {
                x |= tmp[u] ^ ((const unsigned char *)tag)[u];
        }
        return EQ0(x);
 }
 
+/* see bearssl_aead.h */
+uint32_t
+br_gcm_check_tag(br_gcm_context *ctx, const void *tag)
+{
+       return br_gcm_check_tag_trunc(ctx, tag, 16);
+}
+
 /* see bearssl_aead.h */
 const br_aead_class br_gcm_vtable = {
        16,
@@ -292,5 +310,9 @@ const br_aead_class br_gcm_vtable = {
        (void (*)(const br_aead_class **, void *))
                &br_gcm_get_tag,
        (uint32_t (*)(const br_aead_class **, const void *))
-               &br_gcm_check_tag
+               &br_gcm_check_tag,
+       (void (*)(const br_aead_class **, void *, size_t))
+               &br_gcm_get_tag_trunc,
+       (uint32_t (*)(const br_aead_class **, const void *, size_t))
+               &br_gcm_check_tag_trunc
 };
diff --git a/src/symcipher/aes_big_ctrcbc.c b/src/symcipher/aes_big_ctrcbc.c
new file mode 100644 (file)
index 0000000..d45ca76
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_big_ctrcbc_init(br_aes_big_ctrcbc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_big_ctrcbc_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_aes_big_ctrcbc_ctr(const br_aes_big_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len)
+{
+       unsigned char *buf, *bctr;
+       uint32_t cc0, cc1, cc2, cc3;
+
+       buf = data;
+       bctr = ctr;
+       cc3 = br_dec32be(bctr +  0);
+       cc2 = br_dec32be(bctr +  4);
+       cc1 = br_dec32be(bctr +  8);
+       cc0 = br_dec32be(bctr + 12);
+       while (len > 0) {
+               unsigned char tmp[16];
+               uint32_t carry;
+
+               br_enc32be(tmp +  0, cc3);
+               br_enc32be(tmp +  4, cc2);
+               br_enc32be(tmp +  8, cc1);
+               br_enc32be(tmp + 12, cc0);
+               br_aes_big_encrypt(ctx->num_rounds, ctx->skey, tmp);
+               xorbuf(buf, tmp, 16);
+               buf += 16;
+               len -= 16;
+               cc0 ++;
+               carry = (~(cc0 | -cc0)) >> 31;
+               cc1 += carry;
+               carry &= (~(cc1 | -cc1)) >> 31;
+               cc2 += carry;
+               carry &= (~(cc2 | -cc2)) >> 31;
+               cc3 += carry;
+       }
+       br_enc32be(bctr +  0, cc3);
+       br_enc32be(bctr +  4, cc2);
+       br_enc32be(bctr +  8, cc1);
+       br_enc32be(bctr + 12, cc0);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_big_ctrcbc_mac(const br_aes_big_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len)
+{
+       const unsigned char *buf;
+
+       buf = data;
+       while (len > 0) {
+               xorbuf(cbcmac, buf, 16);
+               br_aes_big_encrypt(ctx->num_rounds, ctx->skey, cbcmac);
+               buf += 16;
+               len -= 16;
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_aes_big_ctrcbc_encrypt(const br_aes_big_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       br_aes_big_ctrcbc_ctr(ctx, ctr, data, len);
+       br_aes_big_ctrcbc_mac(ctx, cbcmac, data, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_big_ctrcbc_decrypt(const br_aes_big_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       br_aes_big_ctrcbc_mac(ctx, cbcmac, data, len);
+       br_aes_big_ctrcbc_ctr(ctx, ctr, data, len);
+}
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class br_aes_big_ctrcbc_vtable = {
+       sizeof(br_aes_big_ctrcbc_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctrcbc_class **, const void *, size_t))
+               &br_aes_big_ctrcbc_init,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_big_ctrcbc_encrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_big_ctrcbc_decrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, size_t))
+               &br_aes_big_ctrcbc_ctr,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, const void *, size_t))
+               &br_aes_big_ctrcbc_mac
+};
diff --git a/src/symcipher/aes_ct64_ctrcbc.c b/src/symcipher/aes_ct64_ctrcbc.c
new file mode 100644 (file)
index 0000000..21bb8ef
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_ctrcbc_init(br_aes_ct64_ctrcbc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct64_ctrcbc_vtable;
+       ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_ctrcbc_ctr(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint64_t sk_exp[120];
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       /*
+        * We keep the counter as four 32-bit values, with big-endian
+        * convention, because that's what is expected for purposes of
+        * incrementing the counter value.
+        */
+       ivbuf = ctr;
+       iv0 = br_dec32be(ivbuf +  0);
+       iv1 = br_dec32be(ivbuf +  4);
+       iv2 = br_dec32be(ivbuf +  8);
+       iv3 = br_dec32be(ivbuf + 12);
+
+       buf = data;
+       while (len > 0) {
+               uint64_t q[8];
+               uint32_t w[16];
+               unsigned char tmp[64];
+               int i, j;
+
+               /*
+                * The bitslice implementation expects values in
+                * little-endian convention, so we have to byteswap them.
+                */
+               j = (len >= 64) ? 16 : (int)(len >> 2);
+               for (i = 0; i < j; i += 4) {
+                       uint32_t carry;
+
+                       w[i + 0] = br_swap32(iv0);
+                       w[i + 1] = br_swap32(iv1);
+                       w[i + 2] = br_swap32(iv2);
+                       w[i + 3] = br_swap32(iv3);
+                       iv3 ++;
+                       carry = ~(iv3 | -iv3) >> 31;
+                       iv2 += carry;
+                       carry &= -(~(iv2 | -iv2) >> 31);
+                       iv1 += carry;
+                       carry &= -(~(iv1 | -iv1) >> 31);
+                       iv0 += carry;
+               }
+               memset(w + i, 0, (16 - i) * sizeof(uint32_t));
+
+               for (i = 0; i < 4; i ++) {
+                       br_aes_ct64_interleave_in(
+                               &q[i], &q[i + 4], w + (i << 2));
+               }
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               for (i = 0; i < 4; i ++) {
+                       br_aes_ct64_interleave_out(
+                               w + (i << 2), q[i], q[i + 4]);
+               }
+
+               br_range_enc32le(tmp, w, 16);
+               if (len <= 64) {
+                       xorbuf(buf, tmp, len);
+                       break;
+               }
+               xorbuf(buf, tmp, 64);
+               buf += 64;
+               len -= 64;
+       }
+       br_enc32be(ivbuf +  0, iv0);
+       br_enc32be(ivbuf +  4, iv1);
+       br_enc32be(ivbuf +  8, iv2);
+       br_enc32be(ivbuf + 12, iv3);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_ctrcbc_mac(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       uint32_t cm0, cm1, cm2, cm3;
+       uint64_t q[8];
+       uint64_t sk_exp[120];
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       cm0 = br_dec32le((unsigned char *)cbcmac +  0);
+       cm1 = br_dec32le((unsigned char *)cbcmac +  4);
+       cm2 = br_dec32le((unsigned char *)cbcmac +  8);
+       cm3 = br_dec32le((unsigned char *)cbcmac + 12);
+
+       buf = data;
+       memset(q, 0, sizeof q);
+       while (len > 0) {
+               uint32_t w[4];
+
+               w[0] = cm0 ^ br_dec32le(buf +  0);
+               w[1] = cm1 ^ br_dec32le(buf +  4);
+               w[2] = cm2 ^ br_dec32le(buf +  8);
+               w[3] = cm3 ^ br_dec32le(buf + 12);
+
+               br_aes_ct64_interleave_in(&q[0], &q[4], w);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_interleave_out(w, q[0], q[4]);
+
+               cm0 = w[0];
+               cm1 = w[1];
+               cm2 = w[2];
+               cm3 = w[3];
+               buf += 16;
+               len -= 16;
+       }
+
+       br_enc32le((unsigned char *)cbcmac +  0, cm0);
+       br_enc32le((unsigned char *)cbcmac +  4, cm1);
+       br_enc32le((unsigned char *)cbcmac +  8, cm2);
+       br_enc32le((unsigned char *)cbcmac + 12, cm3);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_ctrcbc_encrypt(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       /*
+        * When encrypting, the CBC-MAC processing must be lagging by
+        * one block, since it operates on the encrypted values, so
+        * it must wait for that encryption to complete.
+        */
+
+       unsigned char *buf;
+       unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t cm0, cm1, cm2, cm3;
+       uint64_t sk_exp[120];
+       uint64_t q[8];
+       int first_iter;
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       /*
+        * We keep the counter as four 32-bit values, with big-endian
+        * convention, because that's what is expected for purposes of
+        * incrementing the counter value.
+        */
+       ivbuf = ctr;
+       iv0 = br_dec32be(ivbuf +  0);
+       iv1 = br_dec32be(ivbuf +  4);
+       iv2 = br_dec32be(ivbuf +  8);
+       iv3 = br_dec32be(ivbuf + 12);
+
+       /*
+        * The current CBC-MAC value is kept in little-endian convention.
+        */
+       cm0 = br_dec32le((unsigned char *)cbcmac +  0);
+       cm1 = br_dec32le((unsigned char *)cbcmac +  4);
+       cm2 = br_dec32le((unsigned char *)cbcmac +  8);
+       cm3 = br_dec32le((unsigned char *)cbcmac + 12);
+
+       buf = data;
+       first_iter = 1;
+       memset(q, 0, sizeof q);
+       while (len > 0) {
+               uint32_t w[8], carry;
+
+               /*
+                * The bitslice implementation expects values in
+                * little-endian convention, so we have to byteswap them.
+                */
+               w[0] = br_swap32(iv0);
+               w[1] = br_swap32(iv1);
+               w[2] = br_swap32(iv2);
+               w[3] = br_swap32(iv3);
+               iv3 ++;
+               carry = ~(iv3 | -iv3) >> 31;
+               iv2 += carry;
+               carry &= -(~(iv2 | -iv2) >> 31);
+               iv1 += carry;
+               carry &= -(~(iv1 | -iv1) >> 31);
+               iv0 += carry;
+
+               /*
+                * The block for CBC-MAC.
+                */
+               w[4] = cm0;
+               w[5] = cm1;
+               w[6] = cm2;
+               w[7] = cm3;
+
+               br_aes_ct64_interleave_in(&q[0], &q[4], w);
+               br_aes_ct64_interleave_in(&q[1], &q[5], w + 4);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_interleave_out(w, q[0], q[4]);
+               br_aes_ct64_interleave_out(w + 4, q[1], q[5]);
+
+               /*
+                * We do the XOR with the plaintext in 32-bit registers,
+                * so that the value are available for CBC-MAC processing
+                * as well.
+                */
+               w[0] ^= br_dec32le(buf +  0);
+               w[1] ^= br_dec32le(buf +  4);
+               w[2] ^= br_dec32le(buf +  8);
+               w[3] ^= br_dec32le(buf + 12);
+               br_enc32le(buf +  0, w[0]);
+               br_enc32le(buf +  4, w[1]);
+               br_enc32le(buf +  8, w[2]);
+               br_enc32le(buf + 12, w[3]);
+
+               buf += 16;
+               len -= 16;
+
+               /*
+                * We set the cm* values to the block to encrypt in the
+                * next iteration.
+                */
+               if (first_iter) {
+                       first_iter = 0;
+                       cm0 ^= w[0];
+                       cm1 ^= w[1];
+                       cm2 ^= w[2];
+                       cm3 ^= w[3];
+               } else {
+                       cm0 = w[0] ^ w[4];
+                       cm1 = w[1] ^ w[5];
+                       cm2 = w[2] ^ w[6];
+                       cm3 = w[3] ^ w[7];
+               }
+
+               /*
+                * If this was the last iteration, then compute the
+                * extra block encryption to complete CBC-MAC.
+                */
+               if (len == 0) {
+                       w[0] = cm0;
+                       w[1] = cm1;
+                       w[2] = cm2;
+                       w[3] = cm3;
+                       br_aes_ct64_interleave_in(&q[0], &q[4], w);
+                       br_aes_ct64_ortho(q);
+                       br_aes_ct64_bitslice_encrypt(
+                               ctx->num_rounds, sk_exp, q);
+                       br_aes_ct64_ortho(q);
+                       br_aes_ct64_interleave_out(w, q[0], q[4]);
+                       cm0 = w[0];
+                       cm1 = w[1];
+                       cm2 = w[2];
+                       cm3 = w[3];
+                       break;
+               }
+       }
+
+       br_enc32be(ivbuf +  0, iv0);
+       br_enc32be(ivbuf +  4, iv1);
+       br_enc32be(ivbuf +  8, iv2);
+       br_enc32be(ivbuf + 12, iv3);
+       br_enc32le((unsigned char *)cbcmac +  0, cm0);
+       br_enc32le((unsigned char *)cbcmac +  4, cm1);
+       br_enc32le((unsigned char *)cbcmac +  8, cm2);
+       br_enc32le((unsigned char *)cbcmac + 12, cm3);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct64_ctrcbc_decrypt(const br_aes_ct64_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t cm0, cm1, cm2, cm3;
+       uint64_t sk_exp[120];
+       uint64_t q[8];
+
+       br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       /*
+        * We keep the counter as four 32-bit values, with big-endian
+        * convention, because that's what is expected for purposes of
+        * incrementing the counter value.
+        */
+       ivbuf = ctr;
+       iv0 = br_dec32be(ivbuf +  0);
+       iv1 = br_dec32be(ivbuf +  4);
+       iv2 = br_dec32be(ivbuf +  8);
+       iv3 = br_dec32be(ivbuf + 12);
+
+       /*
+        * The current CBC-MAC value is kept in little-endian convention.
+        */
+       cm0 = br_dec32le((unsigned char *)cbcmac +  0);
+       cm1 = br_dec32le((unsigned char *)cbcmac +  4);
+       cm2 = br_dec32le((unsigned char *)cbcmac +  8);
+       cm3 = br_dec32le((unsigned char *)cbcmac + 12);
+
+       buf = data;
+       memset(q, 0, sizeof q);
+       while (len > 0) {
+               uint32_t w[8], carry;
+               unsigned char tmp[16];
+
+               /*
+                * The bitslice implementation expects values in
+                * little-endian convention, so we have to byteswap them.
+                */
+               w[0] = br_swap32(iv0);
+               w[1] = br_swap32(iv1);
+               w[2] = br_swap32(iv2);
+               w[3] = br_swap32(iv3);
+               iv3 ++;
+               carry = ~(iv3 | -iv3) >> 31;
+               iv2 += carry;
+               carry &= -(~(iv2 | -iv2) >> 31);
+               iv1 += carry;
+               carry &= -(~(iv1 | -iv1) >> 31);
+               iv0 += carry;
+
+               /*
+                * The block for CBC-MAC.
+                */
+               w[4] = cm0 ^ br_dec32le(buf +  0);
+               w[5] = cm1 ^ br_dec32le(buf +  4);
+               w[6] = cm2 ^ br_dec32le(buf +  8);
+               w[7] = cm3 ^ br_dec32le(buf + 12);
+
+               br_aes_ct64_interleave_in(&q[0], &q[4], w);
+               br_aes_ct64_interleave_in(&q[1], &q[5], w + 4);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct64_ortho(q);
+               br_aes_ct64_interleave_out(w, q[0], q[4]);
+               br_aes_ct64_interleave_out(w + 4, q[1], q[5]);
+
+               br_enc32le(tmp +  0, w[0]);
+               br_enc32le(tmp +  4, w[1]);
+               br_enc32le(tmp +  8, w[2]);
+               br_enc32le(tmp + 12, w[3]);
+               xorbuf(buf, tmp, 16);
+               cm0 = w[4];
+               cm1 = w[5];
+               cm2 = w[6];
+               cm3 = w[7];
+               buf += 16;
+               len -= 16;
+       }
+
+       br_enc32be(ivbuf +  0, iv0);
+       br_enc32be(ivbuf +  4, iv1);
+       br_enc32be(ivbuf +  8, iv2);
+       br_enc32be(ivbuf + 12, iv3);
+       br_enc32le((unsigned char *)cbcmac +  0, cm0);
+       br_enc32le((unsigned char *)cbcmac +  4, cm1);
+       br_enc32le((unsigned char *)cbcmac +  8, cm2);
+       br_enc32le((unsigned char *)cbcmac + 12, cm3);
+}
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class br_aes_ct64_ctrcbc_vtable = {
+       sizeof(br_aes_ct64_ctrcbc_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctrcbc_class **, const void *, size_t))
+               &br_aes_ct64_ctrcbc_init,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_ct64_ctrcbc_encrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_ct64_ctrcbc_decrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, size_t))
+               &br_aes_ct64_ctrcbc_ctr,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, const void *, size_t))
+               &br_aes_ct64_ctrcbc_mac
+};
diff --git a/src/symcipher/aes_ct_ctrcbc.c b/src/symcipher/aes_ct_ctrcbc.c
new file mode 100644 (file)
index 0000000..8ae9fc7
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_ct_ctrcbc_init(br_aes_ct_ctrcbc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_ct_ctrcbc_vtable;
+       ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct_ctrcbc_ctr(const br_aes_ct_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t sk_exp[120];
+
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       /*
+        * We keep the counter as four 32-bit values, with big-endian
+        * convention, because that's what is expected for purposes of
+        * incrementing the counter value.
+        */
+       ivbuf = ctr;
+       iv0 = br_dec32be(ivbuf +  0);
+       iv1 = br_dec32be(ivbuf +  4);
+       iv2 = br_dec32be(ivbuf +  8);
+       iv3 = br_dec32be(ivbuf + 12);
+
+       buf = data;
+       while (len > 0) {
+               uint32_t q[8], carry;
+               unsigned char tmp[32];
+
+               /*
+                * The bitslice implementation expects values in
+                * little-endian convention, so we have to byteswap them.
+                */
+               q[0] = br_swap32(iv0);
+               q[2] = br_swap32(iv1);
+               q[4] = br_swap32(iv2);
+               q[6] = br_swap32(iv3);
+               iv3 ++;
+               carry = ~(iv3 | -iv3) >> 31;
+               iv2 += carry;
+               carry &= -(~(iv2 | -iv2) >> 31);
+               iv1 += carry;
+               carry &= -(~(iv1 | -iv1) >> 31);
+               iv0 += carry;
+               q[1] = br_swap32(iv0);
+               q[3] = br_swap32(iv1);
+               q[5] = br_swap32(iv2);
+               q[7] = br_swap32(iv3);
+               if (len > 16) {
+                       iv3 ++;
+                       carry = ~(iv3 | -iv3) >> 31;
+                       iv2 += carry;
+                       carry &= -(~(iv2 | -iv2) >> 31);
+                       iv1 += carry;
+                       carry &= -(~(iv1 | -iv1) >> 31);
+                       iv0 += carry;
+               }
+
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+
+               br_enc32le(tmp, q[0]);
+               br_enc32le(tmp + 4, q[2]);
+               br_enc32le(tmp + 8, q[4]);
+               br_enc32le(tmp + 12, q[6]);
+               br_enc32le(tmp + 16, q[1]);
+               br_enc32le(tmp + 20, q[3]);
+               br_enc32le(tmp + 24, q[5]);
+               br_enc32le(tmp + 28, q[7]);
+
+               if (len <= 32) {
+                       xorbuf(buf, tmp, len);
+                       break;
+               }
+               xorbuf(buf, tmp, 32);
+               buf += 32;
+               len -= 32;
+       }
+       br_enc32be(ivbuf +  0, iv0);
+       br_enc32be(ivbuf +  4, iv1);
+       br_enc32be(ivbuf +  8, iv2);
+       br_enc32be(ivbuf + 12, iv3);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct_ctrcbc_mac(const br_aes_ct_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       uint32_t cm0, cm1, cm2, cm3;
+       uint32_t q[8];
+       uint32_t sk_exp[120];
+
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       buf = data;
+       cm0 = br_dec32le((unsigned char *)cbcmac +  0);
+       cm1 = br_dec32le((unsigned char *)cbcmac +  4);
+       cm2 = br_dec32le((unsigned char *)cbcmac +  8);
+       cm3 = br_dec32le((unsigned char *)cbcmac + 12);
+       q[1] = 0;
+       q[3] = 0;
+       q[5] = 0;
+       q[7] = 0;
+
+       while (len > 0) {
+               q[0] = cm0 ^ br_dec32le(buf +  0);
+               q[2] = cm1 ^ br_dec32le(buf +  4);
+               q[4] = cm2 ^ br_dec32le(buf +  8);
+               q[6] = cm3 ^ br_dec32le(buf + 12);
+
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+
+               cm0 = q[0];
+               cm1 = q[2];
+               cm2 = q[4];
+               cm3 = q[6];
+               buf += 16;
+               len -= 16;
+       }
+
+       br_enc32le((unsigned char *)cbcmac +  0, cm0);
+       br_enc32le((unsigned char *)cbcmac +  4, cm1);
+       br_enc32le((unsigned char *)cbcmac +  8, cm2);
+       br_enc32le((unsigned char *)cbcmac + 12, cm3);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct_ctrcbc_encrypt(const br_aes_ct_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       /*
+        * When encrypting, the CBC-MAC processing must be lagging by
+        * one block, since it operates on the encrypted values, so
+        * it must wait for that encryption to complete.
+        */
+
+       unsigned char *buf;
+       unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t cm0, cm1, cm2, cm3;
+       uint32_t sk_exp[120];
+       int first_iter;
+
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       /*
+        * We keep the counter as four 32-bit values, with big-endian
+        * convention, because that's what is expected for purposes of
+        * incrementing the counter value.
+        */
+       ivbuf = ctr;
+       iv0 = br_dec32be(ivbuf +  0);
+       iv1 = br_dec32be(ivbuf +  4);
+       iv2 = br_dec32be(ivbuf +  8);
+       iv3 = br_dec32be(ivbuf + 12);
+
+       /*
+        * The current CBC-MAC value is kept in little-endian convention.
+        */
+       cm0 = br_dec32le((unsigned char *)cbcmac +  0);
+       cm1 = br_dec32le((unsigned char *)cbcmac +  4);
+       cm2 = br_dec32le((unsigned char *)cbcmac +  8);
+       cm3 = br_dec32le((unsigned char *)cbcmac + 12);
+
+       buf = data;
+       first_iter = 1;
+       while (len > 0) {
+               uint32_t q[8], carry;
+
+               /*
+                * The bitslice implementation expects values in
+                * little-endian convention, so we have to byteswap them.
+                */
+               q[0] = br_swap32(iv0);
+               q[2] = br_swap32(iv1);
+               q[4] = br_swap32(iv2);
+               q[6] = br_swap32(iv3);
+               iv3 ++;
+               carry = ~(iv3 | -iv3) >> 31;
+               iv2 += carry;
+               carry &= -(~(iv2 | -iv2) >> 31);
+               iv1 += carry;
+               carry &= -(~(iv1 | -iv1) >> 31);
+               iv0 += carry;
+
+               /*
+                * The odd values are used for CBC-MAC.
+                */
+               q[1] = cm0;
+               q[3] = cm1;
+               q[5] = cm2;
+               q[7] = cm3;
+
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+
+               /*
+                * We do the XOR with the plaintext in 32-bit registers,
+                * so that the value are available for CBC-MAC processing
+                * as well.
+                */
+               q[0] ^= br_dec32le(buf +  0);
+               q[2] ^= br_dec32le(buf +  4);
+               q[4] ^= br_dec32le(buf +  8);
+               q[6] ^= br_dec32le(buf + 12);
+               br_enc32le(buf +  0, q[0]);
+               br_enc32le(buf +  4, q[2]);
+               br_enc32le(buf +  8, q[4]);
+               br_enc32le(buf + 12, q[6]);
+
+               buf += 16;
+               len -= 16;
+
+               /*
+                * We set the cm* values to the block to encrypt in the
+                * next iteration.
+                */
+               if (first_iter) {
+                       first_iter = 0;
+                       cm0 ^= q[0];
+                       cm1 ^= q[2];
+                       cm2 ^= q[4];
+                       cm3 ^= q[6];
+               } else {
+                       cm0 = q[0] ^ q[1];
+                       cm1 = q[2] ^ q[3];
+                       cm2 = q[4] ^ q[5];
+                       cm3 = q[6] ^ q[7];
+               }
+
+               /*
+                * If this was the last iteration, then compute the
+                * extra block encryption to complete CBC-MAC.
+                */
+               if (len == 0) {
+                       q[0] = cm0;
+                       q[2] = cm1;
+                       q[4] = cm2;
+                       q[6] = cm3;
+                       br_aes_ct_ortho(q);
+                       br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+                       br_aes_ct_ortho(q);
+                       cm0 = q[0];
+                       cm1 = q[2];
+                       cm2 = q[4];
+                       cm3 = q[6];
+                       break;
+               }
+       }
+
+       br_enc32be(ivbuf +  0, iv0);
+       br_enc32be(ivbuf +  4, iv1);
+       br_enc32be(ivbuf +  8, iv2);
+       br_enc32be(ivbuf + 12, iv3);
+       br_enc32le((unsigned char *)cbcmac +  0, cm0);
+       br_enc32le((unsigned char *)cbcmac +  4, cm1);
+       br_enc32le((unsigned char *)cbcmac +  8, cm2);
+       br_enc32le((unsigned char *)cbcmac + 12, cm3);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_ct_ctrcbc_decrypt(const br_aes_ct_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned char *ivbuf;
+       uint32_t iv0, iv1, iv2, iv3;
+       uint32_t cm0, cm1, cm2, cm3;
+       uint32_t sk_exp[120];
+
+       br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey);
+
+       /*
+        * We keep the counter as four 32-bit values, with big-endian
+        * convention, because that's what is expected for purposes of
+        * incrementing the counter value.
+        */
+       ivbuf = ctr;
+       iv0 = br_dec32be(ivbuf +  0);
+       iv1 = br_dec32be(ivbuf +  4);
+       iv2 = br_dec32be(ivbuf +  8);
+       iv3 = br_dec32be(ivbuf + 12);
+
+       /*
+        * The current CBC-MAC value is kept in little-endian convention.
+        */
+       cm0 = br_dec32le((unsigned char *)cbcmac +  0);
+       cm1 = br_dec32le((unsigned char *)cbcmac +  4);
+       cm2 = br_dec32le((unsigned char *)cbcmac +  8);
+       cm3 = br_dec32le((unsigned char *)cbcmac + 12);
+
+       buf = data;
+       while (len > 0) {
+               uint32_t q[8], carry;
+               unsigned char tmp[16];
+
+               /*
+                * The bitslice implementation expects values in
+                * little-endian convention, so we have to byteswap them.
+                */
+               q[0] = br_swap32(iv0);
+               q[2] = br_swap32(iv1);
+               q[4] = br_swap32(iv2);
+               q[6] = br_swap32(iv3);
+               iv3 ++;
+               carry = ~(iv3 | -iv3) >> 31;
+               iv2 += carry;
+               carry &= -(~(iv2 | -iv2) >> 31);
+               iv1 += carry;
+               carry &= -(~(iv1 | -iv1) >> 31);
+               iv0 += carry;
+
+               /*
+                * The odd values are used for CBC-MAC.
+                */
+               q[1] = cm0 ^ br_dec32le(buf +  0);
+               q[3] = cm1 ^ br_dec32le(buf +  4);
+               q[5] = cm2 ^ br_dec32le(buf +  8);
+               q[7] = cm3 ^ br_dec32le(buf + 12);
+
+               br_aes_ct_ortho(q);
+               br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q);
+               br_aes_ct_ortho(q);
+
+               br_enc32le(tmp +  0, q[0]);
+               br_enc32le(tmp +  4, q[2]);
+               br_enc32le(tmp +  8, q[4]);
+               br_enc32le(tmp + 12, q[6]);
+               xorbuf(buf, tmp, 16);
+               cm0 = q[1];
+               cm1 = q[3];
+               cm2 = q[5];
+               cm3 = q[7];
+               buf += 16;
+               len -= 16;
+       }
+
+       br_enc32be(ivbuf +  0, iv0);
+       br_enc32be(ivbuf +  4, iv1);
+       br_enc32be(ivbuf +  8, iv2);
+       br_enc32be(ivbuf + 12, iv3);
+       br_enc32le((unsigned char *)cbcmac +  0, cm0);
+       br_enc32le((unsigned char *)cbcmac +  4, cm1);
+       br_enc32le((unsigned char *)cbcmac +  8, cm2);
+       br_enc32le((unsigned char *)cbcmac + 12, cm3);
+}
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class br_aes_ct_ctrcbc_vtable = {
+       sizeof(br_aes_ct_ctrcbc_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctrcbc_class **, const void *, size_t))
+               &br_aes_ct_ctrcbc_init,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_ct_ctrcbc_encrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_ct_ctrcbc_decrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, size_t))
+               &br_aes_ct_ctrcbc_ctr,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, const void *, size_t))
+               &br_aes_ct_ctrcbc_mac
+};
diff --git a/src/symcipher/aes_small_ctrcbc.c b/src/symcipher/aes_small_ctrcbc.c
new file mode 100644 (file)
index 0000000..2d6ba32
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "inner.h"
+
+/* see bearssl_block.h */
+void
+br_aes_small_ctrcbc_init(br_aes_small_ctrcbc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_small_ctrcbc_vtable;
+       ctx->num_rounds = br_aes_keysched(ctx->skey, key, len);
+}
+
+static void
+xorbuf(void *dst, const void *src, size_t len)
+{
+       unsigned char *d;
+       const unsigned char *s;
+
+       d = dst;
+       s = src;
+       while (len -- > 0) {
+               *d ++ ^= *s ++;
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_aes_small_ctrcbc_ctr(const br_aes_small_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len)
+{
+       unsigned char *buf, *bctr;
+       uint32_t cc0, cc1, cc2, cc3;
+
+       buf = data;
+       bctr = ctr;
+       cc3 = br_dec32be(bctr +  0);
+       cc2 = br_dec32be(bctr +  4);
+       cc1 = br_dec32be(bctr +  8);
+       cc0 = br_dec32be(bctr + 12);
+       while (len > 0) {
+               unsigned char tmp[16];
+               uint32_t carry;
+
+               br_enc32be(tmp +  0, cc3);
+               br_enc32be(tmp +  4, cc2);
+               br_enc32be(tmp +  8, cc1);
+               br_enc32be(tmp + 12, cc0);
+               br_aes_small_encrypt(ctx->num_rounds, ctx->skey, tmp);
+               xorbuf(buf, tmp, 16);
+               buf += 16;
+               len -= 16;
+               cc0 ++;
+               carry = (~(cc0 | -cc0)) >> 31;
+               cc1 += carry;
+               carry &= (~(cc1 | -cc1)) >> 31;
+               cc2 += carry;
+               carry &= (~(cc2 | -cc2)) >> 31;
+               cc3 += carry;
+       }
+       br_enc32be(bctr +  0, cc3);
+       br_enc32be(bctr +  4, cc2);
+       br_enc32be(bctr +  8, cc1);
+       br_enc32be(bctr + 12, cc0);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_small_ctrcbc_mac(const br_aes_small_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len)
+{
+       const unsigned char *buf;
+
+       buf = data;
+       while (len > 0) {
+               xorbuf(cbcmac, buf, 16);
+               br_aes_small_encrypt(ctx->num_rounds, ctx->skey, cbcmac);
+               buf += 16;
+               len -= 16;
+       }
+}
+
+/* see bearssl_block.h */
+void
+br_aes_small_ctrcbc_encrypt(const br_aes_small_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       br_aes_small_ctrcbc_ctr(ctx, ctr, data, len);
+       br_aes_small_ctrcbc_mac(ctx, cbcmac, data, len);
+}
+
+/* see bearssl_block.h */
+void
+br_aes_small_ctrcbc_decrypt(const br_aes_small_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       br_aes_small_ctrcbc_mac(ctx, cbcmac, data, len);
+       br_aes_small_ctrcbc_ctr(ctx, ctr, data, len);
+}
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class br_aes_small_ctrcbc_vtable = {
+       sizeof(br_aes_small_ctrcbc_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctrcbc_class **, const void *, size_t))
+               &br_aes_small_ctrcbc_init,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_small_ctrcbc_encrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_small_ctrcbc_decrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, size_t))
+               &br_aes_small_ctrcbc_ctr,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, const void *, size_t))
+               &br_aes_small_ctrcbc_mac
+};
diff --git a/src/symcipher/aes_x86ni_ctrcbc.c b/src/symcipher/aes_x86ni_ctrcbc.c
new file mode 100644 (file)
index 0000000..f57fead
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BR_ENABLE_INTRINSICS   1
+#include "inner.h"
+
+#if BR_AES_X86NI
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class *
+br_aes_x86ni_ctrcbc_get_vtable(void)
+{
+       return br_aes_x86ni_supported() ? &br_aes_x86ni_ctrcbc_vtable : NULL;
+}
+
+/* see bearssl_block.h */
+void
+br_aes_x86ni_ctrcbc_init(br_aes_x86ni_ctrcbc_keys *ctx,
+       const void *key, size_t len)
+{
+       ctx->vtable = &br_aes_x86ni_ctrcbc_vtable;
+       ctx->num_rounds = br_aes_x86ni_keysched_enc(ctx->skey.skni, key, len);
+}
+
+BR_TARGETS_X86_UP
+
+/* see bearssl_block.h */
+BR_TARGET("sse2,sse4.1,aes")
+void
+br_aes_x86ni_ctrcbc_ctr(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *ctr, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned num_rounds;
+       __m128i sk[15];
+       __m128i ivx0, ivx1, ivx2, ivx3;
+       __m128i erev, zero, one, four, notthree;
+       unsigned u;
+
+       buf = data;
+       num_rounds = ctx->num_rounds;
+       for (u = 0; u <= num_rounds; u ++) {
+               sk[u] = _mm_loadu_si128((void *)(ctx->skey.skni + (u << 4)));
+       }
+
+       /*
+        * Some SSE2 constants.
+        */
+       erev = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7,
+               8, 9, 10, 11, 12, 13, 14, 15);
+       zero = _mm_setzero_si128();
+       one = _mm_set_epi64x(0, 1);
+       four = _mm_set_epi64x(0, 4);
+       notthree = _mm_sub_epi64(zero, four);
+
+       /*
+        * Decode the counter in big-endian and pre-increment the other
+        * three counters.
+        */
+       ivx0 = _mm_shuffle_epi8(_mm_loadu_si128((void *)ctr), erev);
+       ivx1 = _mm_add_epi64(ivx0, one);
+       ivx1 = _mm_sub_epi64(ivx1,
+               _mm_slli_si128(_mm_cmpeq_epi64(ivx1, zero), 8));
+       ivx2 = _mm_add_epi64(ivx1, one);
+       ivx2 = _mm_sub_epi64(ivx2,
+               _mm_slli_si128(_mm_cmpeq_epi64(ivx2, zero), 8));
+       ivx3 = _mm_add_epi64(ivx2, one);
+       ivx3 = _mm_sub_epi64(ivx3,
+               _mm_slli_si128(_mm_cmpeq_epi64(ivx3, zero), 8));
+       while (len > 0) {
+               __m128i x0, x1, x2, x3;
+
+               /*
+                * Load counter values; we need to byteswap them because
+                * the specification says that they use big-endian.
+                */
+               x0 = _mm_shuffle_epi8(ivx0, erev);
+               x1 = _mm_shuffle_epi8(ivx1, erev);
+               x2 = _mm_shuffle_epi8(ivx2, erev);
+               x3 = _mm_shuffle_epi8(ivx3, erev);
+
+               x0 = _mm_xor_si128(x0, sk[0]);
+               x1 = _mm_xor_si128(x1, sk[0]);
+               x2 = _mm_xor_si128(x2, sk[0]);
+               x3 = _mm_xor_si128(x3, sk[0]);
+               x0 = _mm_aesenc_si128(x0, sk[1]);
+               x1 = _mm_aesenc_si128(x1, sk[1]);
+               x2 = _mm_aesenc_si128(x2, sk[1]);
+               x3 = _mm_aesenc_si128(x3, sk[1]);
+               x0 = _mm_aesenc_si128(x0, sk[2]);
+               x1 = _mm_aesenc_si128(x1, sk[2]);
+               x2 = _mm_aesenc_si128(x2, sk[2]);
+               x3 = _mm_aesenc_si128(x3, sk[2]);
+               x0 = _mm_aesenc_si128(x0, sk[3]);
+               x1 = _mm_aesenc_si128(x1, sk[3]);
+               x2 = _mm_aesenc_si128(x2, sk[3]);
+               x3 = _mm_aesenc_si128(x3, sk[3]);
+               x0 = _mm_aesenc_si128(x0, sk[4]);
+               x1 = _mm_aesenc_si128(x1, sk[4]);
+               x2 = _mm_aesenc_si128(x2, sk[4]);
+               x3 = _mm_aesenc_si128(x3, sk[4]);
+               x0 = _mm_aesenc_si128(x0, sk[5]);
+               x1 = _mm_aesenc_si128(x1, sk[5]);
+               x2 = _mm_aesenc_si128(x2, sk[5]);
+               x3 = _mm_aesenc_si128(x3, sk[5]);
+               x0 = _mm_aesenc_si128(x0, sk[6]);
+               x1 = _mm_aesenc_si128(x1, sk[6]);
+               x2 = _mm_aesenc_si128(x2, sk[6]);
+               x3 = _mm_aesenc_si128(x3, sk[6]);
+               x0 = _mm_aesenc_si128(x0, sk[7]);
+               x1 = _mm_aesenc_si128(x1, sk[7]);
+               x2 = _mm_aesenc_si128(x2, sk[7]);
+               x3 = _mm_aesenc_si128(x3, sk[7]);
+               x0 = _mm_aesenc_si128(x0, sk[8]);
+               x1 = _mm_aesenc_si128(x1, sk[8]);
+               x2 = _mm_aesenc_si128(x2, sk[8]);
+               x3 = _mm_aesenc_si128(x3, sk[8]);
+               x0 = _mm_aesenc_si128(x0, sk[9]);
+               x1 = _mm_aesenc_si128(x1, sk[9]);
+               x2 = _mm_aesenc_si128(x2, sk[9]);
+               x3 = _mm_aesenc_si128(x3, sk[9]);
+               if (num_rounds == 10) {
+                       x0 = _mm_aesenclast_si128(x0, sk[10]);
+                       x1 = _mm_aesenclast_si128(x1, sk[10]);
+                       x2 = _mm_aesenclast_si128(x2, sk[10]);
+                       x3 = _mm_aesenclast_si128(x3, sk[10]);
+               } else if (num_rounds == 12) {
+                       x0 = _mm_aesenc_si128(x0, sk[10]);
+                       x1 = _mm_aesenc_si128(x1, sk[10]);
+                       x2 = _mm_aesenc_si128(x2, sk[10]);
+                       x3 = _mm_aesenc_si128(x3, sk[10]);
+                       x0 = _mm_aesenc_si128(x0, sk[11]);
+                       x1 = _mm_aesenc_si128(x1, sk[11]);
+                       x2 = _mm_aesenc_si128(x2, sk[11]);
+                       x3 = _mm_aesenc_si128(x3, sk[11]);
+                       x0 = _mm_aesenclast_si128(x0, sk[12]);
+                       x1 = _mm_aesenclast_si128(x1, sk[12]);
+                       x2 = _mm_aesenclast_si128(x2, sk[12]);
+                       x3 = _mm_aesenclast_si128(x3, sk[12]);
+               } else {
+                       x0 = _mm_aesenc_si128(x0, sk[10]);
+                       x1 = _mm_aesenc_si128(x1, sk[10]);
+                       x2 = _mm_aesenc_si128(x2, sk[10]);
+                       x3 = _mm_aesenc_si128(x3, sk[10]);
+                       x0 = _mm_aesenc_si128(x0, sk[11]);
+                       x1 = _mm_aesenc_si128(x1, sk[11]);
+                       x2 = _mm_aesenc_si128(x2, sk[11]);
+                       x3 = _mm_aesenc_si128(x3, sk[11]);
+                       x0 = _mm_aesenc_si128(x0, sk[12]);
+                       x1 = _mm_aesenc_si128(x1, sk[12]);
+                       x2 = _mm_aesenc_si128(x2, sk[12]);
+                       x3 = _mm_aesenc_si128(x3, sk[12]);
+                       x0 = _mm_aesenc_si128(x0, sk[13]);
+                       x1 = _mm_aesenc_si128(x1, sk[13]);
+                       x2 = _mm_aesenc_si128(x2, sk[13]);
+                       x3 = _mm_aesenc_si128(x3, sk[13]);
+                       x0 = _mm_aesenclast_si128(x0, sk[14]);
+                       x1 = _mm_aesenclast_si128(x1, sk[14]);
+                       x2 = _mm_aesenclast_si128(x2, sk[14]);
+                       x3 = _mm_aesenclast_si128(x3, sk[14]);
+               }
+               if (len >= 64) {
+                       x0 = _mm_xor_si128(x0,
+                               _mm_loadu_si128((void *)(buf +  0)));
+                       x1 = _mm_xor_si128(x1,
+                               _mm_loadu_si128((void *)(buf + 16)));
+                       x2 = _mm_xor_si128(x2,
+                               _mm_loadu_si128((void *)(buf + 32)));
+                       x3 = _mm_xor_si128(x3,
+                               _mm_loadu_si128((void *)(buf + 48)));
+                       _mm_storeu_si128((void *)(buf +  0), x0);
+                       _mm_storeu_si128((void *)(buf + 16), x1);
+                       _mm_storeu_si128((void *)(buf + 32), x2);
+                       _mm_storeu_si128((void *)(buf + 48), x3);
+                       buf += 64;
+                       len -= 64;
+               } else {
+                       unsigned char tmp[64];
+
+                       _mm_storeu_si128((void *)(tmp +  0), x0);
+                       _mm_storeu_si128((void *)(tmp + 16), x1);
+                       _mm_storeu_si128((void *)(tmp + 32), x2);
+                       _mm_storeu_si128((void *)(tmp + 48), x3);
+                       for (u = 0; u < len; u ++) {
+                               buf[u] ^= tmp[u];
+                       }
+                       switch (len) {
+                       case 16:
+                               ivx0 = ivx1;
+                               break;
+                       case 32:
+                               ivx0 = ivx2;
+                               break;
+                       case 48:
+                               ivx0 = ivx3;
+                               break;
+                       }
+                       break;
+               }
+
+               /*
+                * Add 4 to each counter value. For carry propagation
+                * into the upper 64-bit words, we would need to compare
+                * the results with 4, but SSE2+ has only _signed_
+                * comparisons. Instead, we mask out the low two bits,
+                * and check whether the remaining bits are zero.
+                */
+               ivx0 = _mm_add_epi64(ivx0, four);
+               ivx1 = _mm_add_epi64(ivx1, four);
+               ivx2 = _mm_add_epi64(ivx2, four);
+               ivx3 = _mm_add_epi64(ivx3, four);
+               ivx0 = _mm_sub_epi64(ivx0,
+                       _mm_slli_si128(_mm_cmpeq_epi64(
+                               _mm_and_si128(ivx0, notthree), zero), 8));
+               ivx1 = _mm_sub_epi64(ivx1,
+                       _mm_slli_si128(_mm_cmpeq_epi64(
+                               _mm_and_si128(ivx1, notthree), zero), 8));
+               ivx2 = _mm_sub_epi64(ivx2,
+                       _mm_slli_si128(_mm_cmpeq_epi64(
+                               _mm_and_si128(ivx2, notthree), zero), 8));
+               ivx3 = _mm_sub_epi64(ivx3,
+                       _mm_slli_si128(_mm_cmpeq_epi64(
+                               _mm_and_si128(ivx3, notthree), zero), 8));
+       }
+
+       /*
+        * Write back new counter value. The loop took care to put the
+        * right counter value in ivx0.
+        */
+       _mm_storeu_si128((void *)ctr, _mm_shuffle_epi8(ivx0, erev));
+}
+
+/* see bearssl_block.h */
+BR_TARGET("sse2,sse4.1,aes")
+void
+br_aes_x86ni_ctrcbc_mac(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *cbcmac, const void *data, size_t len)
+{
+       const unsigned char *buf;
+       unsigned num_rounds;
+       __m128i sk[15], ivx;
+       unsigned u;
+
+       buf = data;
+       ivx = _mm_loadu_si128(cbcmac);
+       num_rounds = ctx->num_rounds;
+       for (u = 0; u <= num_rounds; u ++) {
+               sk[u] = _mm_loadu_si128((void *)(ctx->skey.skni + (u << 4)));
+       }
+       while (len > 0) {
+               __m128i x;
+
+               x = _mm_xor_si128(_mm_loadu_si128((void *)buf), ivx);
+               x = _mm_xor_si128(x, sk[0]);
+               x = _mm_aesenc_si128(x, sk[1]);
+               x = _mm_aesenc_si128(x, sk[2]);
+               x = _mm_aesenc_si128(x, sk[3]);
+               x = _mm_aesenc_si128(x, sk[4]);
+               x = _mm_aesenc_si128(x, sk[5]);
+               x = _mm_aesenc_si128(x, sk[6]);
+               x = _mm_aesenc_si128(x, sk[7]);
+               x = _mm_aesenc_si128(x, sk[8]);
+               x = _mm_aesenc_si128(x, sk[9]);
+               if (num_rounds == 10) {
+                       x = _mm_aesenclast_si128(x, sk[10]);
+               } else if (num_rounds == 12) {
+                       x = _mm_aesenc_si128(x, sk[10]);
+                       x = _mm_aesenc_si128(x, sk[11]);
+                       x = _mm_aesenclast_si128(x, sk[12]);
+               } else {
+                       x = _mm_aesenc_si128(x, sk[10]);
+                       x = _mm_aesenc_si128(x, sk[11]);
+                       x = _mm_aesenc_si128(x, sk[12]);
+                       x = _mm_aesenc_si128(x, sk[13]);
+                       x = _mm_aesenclast_si128(x, sk[14]);
+               }
+               ivx = x;
+               buf += 16;
+               len -= 16;
+       }
+       _mm_storeu_si128(cbcmac, ivx);
+}
+
+/* see bearssl_block.h */
+BR_TARGET("sse2,sse4.1,aes")
+void
+br_aes_x86ni_ctrcbc_encrypt(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned num_rounds;
+       __m128i sk[15];
+       __m128i ivx, cmx;
+       __m128i erev, zero, one;
+       unsigned u;
+       int first_iter;
+
+       num_rounds = ctx->num_rounds;
+       for (u = 0; u <= num_rounds; u ++) {
+               sk[u] = _mm_loadu_si128((void *)(ctx->skey.skni + (u << 4)));
+       }
+
+       /*
+        * Some SSE2 constants.
+        */
+       erev = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7,
+               8, 9, 10, 11, 12, 13, 14, 15);
+       zero = _mm_setzero_si128();
+       one = _mm_set_epi64x(0, 1);
+
+       /*
+        * Decode the counter in big-endian.
+        */
+       ivx = _mm_shuffle_epi8(_mm_loadu_si128(ctr), erev);
+       cmx = _mm_loadu_si128(cbcmac);
+
+       buf = data;
+       first_iter = 1;
+       while (len > 0) {
+               __m128i dx, x0, x1;
+
+               /*
+                * Load initial values:
+                *   dx   encrypted block of data
+                *   x0   counter (for CTR encryption)
+                *   x1   input for CBC-MAC
+                */
+               dx = _mm_loadu_si128((void *)buf);
+               x0 = _mm_shuffle_epi8(ivx, erev);
+               x1 = cmx;
+
+               x0 = _mm_xor_si128(x0, sk[0]);
+               x1 = _mm_xor_si128(x1, sk[0]);
+               x0 = _mm_aesenc_si128(x0, sk[1]);
+               x1 = _mm_aesenc_si128(x1, sk[1]);
+               x0 = _mm_aesenc_si128(x0, sk[2]);
+               x1 = _mm_aesenc_si128(x1, sk[2]);
+               x0 = _mm_aesenc_si128(x0, sk[3]);
+               x1 = _mm_aesenc_si128(x1, sk[3]);
+               x0 = _mm_aesenc_si128(x0, sk[4]);
+               x1 = _mm_aesenc_si128(x1, sk[4]);
+               x0 = _mm_aesenc_si128(x0, sk[5]);
+               x1 = _mm_aesenc_si128(x1, sk[5]);
+               x0 = _mm_aesenc_si128(x0, sk[6]);
+               x1 = _mm_aesenc_si128(x1, sk[6]);
+               x0 = _mm_aesenc_si128(x0, sk[7]);
+               x1 = _mm_aesenc_si128(x1, sk[7]);
+               x0 = _mm_aesenc_si128(x0, sk[8]);
+               x1 = _mm_aesenc_si128(x1, sk[8]);
+               x0 = _mm_aesenc_si128(x0, sk[9]);
+               x1 = _mm_aesenc_si128(x1, sk[9]);
+               if (num_rounds == 10) {
+                       x0 = _mm_aesenclast_si128(x0, sk[10]);
+                       x1 = _mm_aesenclast_si128(x1, sk[10]);
+               } else if (num_rounds == 12) {
+                       x0 = _mm_aesenc_si128(x0, sk[10]);
+                       x1 = _mm_aesenc_si128(x1, sk[10]);
+                       x0 = _mm_aesenc_si128(x0, sk[11]);
+                       x1 = _mm_aesenc_si128(x1, sk[11]);
+                       x0 = _mm_aesenclast_si128(x0, sk[12]);
+                       x1 = _mm_aesenclast_si128(x1, sk[12]);
+               } else {
+                       x0 = _mm_aesenc_si128(x0, sk[10]);
+                       x1 = _mm_aesenc_si128(x1, sk[10]);
+                       x0 = _mm_aesenc_si128(x0, sk[11]);
+                       x1 = _mm_aesenc_si128(x1, sk[11]);
+                       x0 = _mm_aesenc_si128(x0, sk[12]);
+                       x1 = _mm_aesenc_si128(x1, sk[12]);
+                       x0 = _mm_aesenc_si128(x0, sk[13]);
+                       x1 = _mm_aesenc_si128(x1, sk[13]);
+                       x0 = _mm_aesenclast_si128(x0, sk[14]);
+                       x1 = _mm_aesenclast_si128(x1, sk[14]);
+               }
+
+               x0 = _mm_xor_si128(x0, dx);
+               if (first_iter) {
+                       cmx = _mm_xor_si128(cmx, x0);
+                       first_iter = 0;
+               } else {
+                       cmx = _mm_xor_si128(x1, x0);
+               }
+               _mm_storeu_si128((void *)buf, x0);
+
+               buf += 16;
+               len -= 16;
+
+               /*
+                * Increment the counter value.
+                */
+               ivx = _mm_add_epi64(ivx, one);
+               ivx = _mm_sub_epi64(ivx,
+                       _mm_slli_si128(_mm_cmpeq_epi64(ivx, zero), 8));
+
+               /*
+                * If this was the last iteration, then compute the
+                * extra block encryption to complete CBC-MAC.
+                */
+               if (len == 0) {
+                       cmx = _mm_xor_si128(cmx, sk[0]);
+                       cmx = _mm_aesenc_si128(cmx, sk[1]);
+                       cmx = _mm_aesenc_si128(cmx, sk[2]);
+                       cmx = _mm_aesenc_si128(cmx, sk[3]);
+                       cmx = _mm_aesenc_si128(cmx, sk[4]);
+                       cmx = _mm_aesenc_si128(cmx, sk[5]);
+                       cmx = _mm_aesenc_si128(cmx, sk[6]);
+                       cmx = _mm_aesenc_si128(cmx, sk[7]);
+                       cmx = _mm_aesenc_si128(cmx, sk[8]);
+                       cmx = _mm_aesenc_si128(cmx, sk[9]);
+                       if (num_rounds == 10) {
+                               cmx = _mm_aesenclast_si128(cmx, sk[10]);
+                       } else if (num_rounds == 12) {
+                               cmx = _mm_aesenc_si128(cmx, sk[10]);
+                               cmx = _mm_aesenc_si128(cmx, sk[11]);
+                               cmx = _mm_aesenclast_si128(cmx, sk[12]);
+                       } else {
+                               cmx = _mm_aesenc_si128(cmx, sk[10]);
+                               cmx = _mm_aesenc_si128(cmx, sk[11]);
+                               cmx = _mm_aesenc_si128(cmx, sk[12]);
+                               cmx = _mm_aesenc_si128(cmx, sk[13]);
+                               cmx = _mm_aesenclast_si128(cmx, sk[14]);
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * Write back new counter value and CBC-MAC value.
+        */
+       _mm_storeu_si128(ctr, _mm_shuffle_epi8(ivx, erev));
+       _mm_storeu_si128(cbcmac, cmx);
+}
+
+/* see bearssl_block.h */
+BR_TARGET("sse2,sse4.1,aes")
+void
+br_aes_x86ni_ctrcbc_decrypt(const br_aes_x86ni_ctrcbc_keys *ctx,
+       void *ctr, void *cbcmac, void *data, size_t len)
+{
+       unsigned char *buf;
+       unsigned num_rounds;
+       __m128i sk[15];
+       __m128i ivx, cmx;
+       __m128i erev, zero, one;
+       unsigned u;
+
+       num_rounds = ctx->num_rounds;
+       for (u = 0; u <= num_rounds; u ++) {
+               sk[u] = _mm_loadu_si128((void *)(ctx->skey.skni + (u << 4)));
+       }
+
+       /*
+        * Some SSE2 constants.
+        */
+       erev = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7,
+               8, 9, 10, 11, 12, 13, 14, 15);
+       zero = _mm_setzero_si128();
+       one = _mm_set_epi64x(0, 1);
+
+       /*
+        * Decode the counter in big-endian.
+        */
+       ivx = _mm_shuffle_epi8(_mm_loadu_si128(ctr), erev);
+       cmx = _mm_loadu_si128(cbcmac);
+
+       buf = data;
+       while (len > 0) {
+               __m128i dx, x0, x1;
+
+               /*
+                * Load initial values:
+                *   dx   encrypted block of data
+                *   x0   counter (for CTR encryption)
+                *   x1   input for CBC-MAC
+                */
+               dx = _mm_loadu_si128((void *)buf);
+               x0 = _mm_shuffle_epi8(ivx, erev);
+               x1 = _mm_xor_si128(cmx, dx);
+
+               x0 = _mm_xor_si128(x0, sk[0]);
+               x1 = _mm_xor_si128(x1, sk[0]);
+               x0 = _mm_aesenc_si128(x0, sk[1]);
+               x1 = _mm_aesenc_si128(x1, sk[1]);
+               x0 = _mm_aesenc_si128(x0, sk[2]);
+               x1 = _mm_aesenc_si128(x1, sk[2]);
+               x0 = _mm_aesenc_si128(x0, sk[3]);
+               x1 = _mm_aesenc_si128(x1, sk[3]);
+               x0 = _mm_aesenc_si128(x0, sk[4]);
+               x1 = _mm_aesenc_si128(x1, sk[4]);
+               x0 = _mm_aesenc_si128(x0, sk[5]);
+               x1 = _mm_aesenc_si128(x1, sk[5]);
+               x0 = _mm_aesenc_si128(x0, sk[6]);
+               x1 = _mm_aesenc_si128(x1, sk[6]);
+               x0 = _mm_aesenc_si128(x0, sk[7]);
+               x1 = _mm_aesenc_si128(x1, sk[7]);
+               x0 = _mm_aesenc_si128(x0, sk[8]);
+               x1 = _mm_aesenc_si128(x1, sk[8]);
+               x0 = _mm_aesenc_si128(x0, sk[9]);
+               x1 = _mm_aesenc_si128(x1, sk[9]);
+               if (num_rounds == 10) {
+                       x0 = _mm_aesenclast_si128(x0, sk[10]);
+                       x1 = _mm_aesenclast_si128(x1, sk[10]);
+               } else if (num_rounds == 12) {
+                       x0 = _mm_aesenc_si128(x0, sk[10]);
+                       x1 = _mm_aesenc_si128(x1, sk[10]);
+                       x0 = _mm_aesenc_si128(x0, sk[11]);
+                       x1 = _mm_aesenc_si128(x1, sk[11]);
+                       x0 = _mm_aesenclast_si128(x0, sk[12]);
+                       x1 = _mm_aesenclast_si128(x1, sk[12]);
+               } else {
+                       x0 = _mm_aesenc_si128(x0, sk[10]);
+                       x1 = _mm_aesenc_si128(x1, sk[10]);
+                       x0 = _mm_aesenc_si128(x0, sk[11]);
+                       x1 = _mm_aesenc_si128(x1, sk[11]);
+                       x0 = _mm_aesenc_si128(x0, sk[12]);
+                       x1 = _mm_aesenc_si128(x1, sk[12]);
+                       x0 = _mm_aesenc_si128(x0, sk[13]);
+                       x1 = _mm_aesenc_si128(x1, sk[13]);
+                       x0 = _mm_aesenclast_si128(x0, sk[14]);
+                       x1 = _mm_aesenclast_si128(x1, sk[14]);
+               }
+               x0 = _mm_xor_si128(x0, dx);
+               cmx = x1;
+               _mm_storeu_si128((void *)buf, x0);
+
+               buf += 16;
+               len -= 16;
+
+               /*
+                * Increment the counter value.
+                */
+               ivx = _mm_add_epi64(ivx, one);
+               ivx = _mm_sub_epi64(ivx,
+                       _mm_slli_si128(_mm_cmpeq_epi64(ivx, zero), 8));
+       }
+
+       /*
+        * Write back new counter value and CBC-MAC value.
+        */
+       _mm_storeu_si128(ctr, _mm_shuffle_epi8(ivx, erev));
+       _mm_storeu_si128(cbcmac, cmx);
+}
+
+BR_TARGETS_X86_DOWN
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class br_aes_x86ni_ctrcbc_vtable = {
+       sizeof(br_aes_x86ni_ctrcbc_keys),
+       16,
+       4,
+       (void (*)(const br_block_ctrcbc_class **, const void *, size_t))
+               &br_aes_x86ni_ctrcbc_init,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_x86ni_ctrcbc_encrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, void *, size_t))
+               &br_aes_x86ni_ctrcbc_decrypt,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, void *, size_t))
+               &br_aes_x86ni_ctrcbc_ctr,
+       (void (*)(const br_block_ctrcbc_class *const *,
+               void *, const void *, size_t))
+               &br_aes_x86ni_ctrcbc_mac
+};
+
+#else
+
+/* see bearssl_block.h */
+const br_block_ctrcbc_class *
+br_aes_x86ni_ctrcbc_get_vtable(void)
+{
+       return NULL;
+}
+
+#endif
index bea236b..e37034c 100644 (file)
@@ -3411,6 +3411,224 @@ test_AES_pwr8(void)
        }
 }
 
+/*
+ * Custom CTR + CBC-MAC AES implementation. Can also do CTR-only, and
+ * CBC-MAC-only. The 'aes_big' implementation (CTR) is used. This is
+ * meant for comparisons.
+ *
+ * If 'ctr' is NULL then no encryption/decryption is done; otherwise,
+ * CTR encryption/decryption is performed (full-block counter) and the
+ * 'ctr' array is updated with the new counter value.
+ *
+ * If 'cbcmac' is NULL then no CBC-MAC is done; otherwise, CBC-MAC is
+ * applied on the encrypted data, with 'cbcmac' as IV and destination
+ * buffer for the output. If 'ctr' is not NULL and 'encrypt' is non-zero,
+ * then CBC-MAC is computed over the result of CTR processing; otherwise,
+ * CBC-MAC is computed over the input data itself.
+ */
+static void
+do_aes_ctrcbc(const void *key, size_t key_len, int encrypt,
+       void *ctr, void *cbcmac, unsigned char *data, size_t len)
+{
+       br_aes_big_ctr_keys bc;
+       int i;
+
+       br_aes_big_ctr_init(&bc, key, key_len);
+       for (i = 0; i < 2; i ++) {
+               /*
+                * CBC-MAC is computed on the encrypted data, so in
+                * first pass if decrypting, second pass if encrypting.
+                */
+               if (cbcmac != NULL
+                       && ((encrypt && i == 1) || (!encrypt && i == 0)))
+               {
+                       unsigned char zz[16];
+                       size_t u;
+
+                       memcpy(zz, cbcmac, sizeof zz);
+                       for (u = 0; u < len; u += 16) {
+                               unsigned char tmp[16];
+                               size_t v;
+
+                               for (v = 0; v < 16; v ++) {
+                                       tmp[v] = zz[v] ^ data[u + v];
+                               }
+                               memset(zz, 0, sizeof zz);
+                               br_aes_big_ctr_run(&bc,
+                                       tmp, br_dec32be(tmp + 12), zz, 16);
+                       }
+                       memcpy(cbcmac, zz, sizeof zz);
+               }
+
+               /*
+                * CTR encryption/decryption is done only in the first pass.
+                * We process data block per block, because the CTR-only
+                * class uses a 32-bit counter, while the CTR+CBC-MAC
+                * class uses a 128-bit counter.
+                */
+               if (ctr != NULL && i == 0) {
+                       unsigned char zz[16];
+                       size_t u;
+
+                       memcpy(zz, ctr, sizeof zz);
+                       for (u = 0; u < len; u += 16) {
+                               int i;
+
+                               br_aes_big_ctr_run(&bc,
+                                       zz, br_dec32be(zz + 12), data + u, 16);
+                               for (i = 15; i >= 0; i --) {
+                                       zz[i] = (zz[i] + 1) & 0xFF;
+                                       if (zz[i] != 0) {
+                                               break;
+                                       }
+                               }
+                       }
+                       memcpy(ctr, zz, sizeof zz);
+               }
+       }
+}
+
+static void
+test_AES_CTRCBC_inner(const char *name, const br_block_ctrcbc_class *vt)
+{
+       br_hmac_drbg_context rng;
+       size_t key_len;
+
+       printf("Test AES CTR/CBC-MAC %s: ", name);
+       fflush(stdout);
+
+       br_hmac_drbg_init(&rng, &br_sha256_vtable, name, strlen(name));
+       for (key_len = 16; key_len <= 32; key_len += 8) {
+               br_aes_gen_ctrcbc_keys bc;
+               unsigned char key[32];
+               size_t data_len;
+
+               br_hmac_drbg_generate(&rng, key, key_len);
+               vt->init(&bc.vtable, key, key_len);
+               for (data_len = 0; data_len <= 512; data_len += 16) {
+                       unsigned char plain[512];
+                       unsigned char data1[sizeof plain];
+                       unsigned char data2[sizeof plain];
+                       unsigned char ctr[16], cbcmac[16];
+                       unsigned char ctr1[16], cbcmac1[16];
+                       unsigned char ctr2[16], cbcmac2[16];
+                       int i;
+
+                       br_hmac_drbg_generate(&rng, plain, data_len);
+
+                       for (i = 0; i <= 16; i ++) {
+                               if (i == 0) {
+                                       br_hmac_drbg_generate(&rng, ctr, 16);
+                               } else {
+                                       memset(ctr, 0, i - 1);
+                                       memset(ctr + i - 1, 0xFF, 17 - i);
+                               }
+                               br_hmac_drbg_generate(&rng, cbcmac, 16);
+
+                               memcpy(data1, plain, data_len);
+                               memcpy(ctr1, ctr, 16);
+                               vt->ctr(&bc.vtable, ctr1, data1, data_len);
+                               memcpy(data2, plain, data_len);
+                               memcpy(ctr2, ctr, 16);
+                               do_aes_ctrcbc(key, key_len, 1,
+                                       ctr2, NULL, data2, data_len);
+                               check_equals("CTR-only data",
+                                       data1, data2, data_len);
+                               check_equals("CTR-only counter",
+                                       ctr1, ctr2, 16);
+
+                               memcpy(data1, plain, data_len);
+                               memcpy(cbcmac1, cbcmac, 16);
+                               vt->mac(&bc.vtable, cbcmac1, data1, data_len);
+                               memcpy(data2, plain, data_len);
+                               memcpy(cbcmac2, cbcmac, 16);
+                               do_aes_ctrcbc(key, key_len, 1,
+                                       NULL, cbcmac2, data2, data_len);
+                               check_equals("CBC-MAC-only",
+                                       cbcmac1, cbcmac2, 16);
+
+                               memcpy(data1, plain, data_len);
+                               memcpy(ctr1, ctr, 16);
+                               memcpy(cbcmac1, cbcmac, 16);
+                               vt->encrypt(&bc.vtable,
+                                       ctr1, cbcmac1, data1, data_len);
+                               memcpy(data2, plain, data_len);
+                               memcpy(ctr2, ctr, 16);
+                               memcpy(cbcmac2, cbcmac, 16);
+                               do_aes_ctrcbc(key, key_len, 1,
+                                       ctr2, cbcmac2, data2, data_len);
+                               check_equals("encrypt: combined data",
+                                       data1, data2, data_len);
+                               check_equals("encrypt: combined counter",
+                                       ctr1, ctr2, 16);
+                               check_equals("encrypt: combined CBC-MAC",
+                                       cbcmac1, cbcmac2, 16);
+
+                               memcpy(ctr1, ctr, 16);
+                               memcpy(cbcmac1, cbcmac, 16);
+                               vt->decrypt(&bc.vtable,
+                                       ctr1, cbcmac1, data1, data_len);
+                               memcpy(ctr2, ctr, 16);
+                               memcpy(cbcmac2, cbcmac, 16);
+                               do_aes_ctrcbc(key, key_len, 0,
+                                       ctr2, cbcmac2, data2, data_len);
+                               check_equals("decrypt: combined data",
+                                       data1, data2, data_len);
+                               check_equals("decrypt: combined counter",
+                                       ctr1, ctr2, 16);
+                               check_equals("decrypt: combined CBC-MAC",
+                                       cbcmac1, cbcmac2, 16);
+                       }
+
+                       printf(".");
+                       fflush(stdout);
+               }
+
+               printf(" ");
+               fflush(stdout);
+       }
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_AES_CTRCBC_big(void)
+{
+       test_AES_CTRCBC_inner("big", &br_aes_big_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_small(void)
+{
+       test_AES_CTRCBC_inner("small", &br_aes_small_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_ct(void)
+{
+       test_AES_CTRCBC_inner("ct", &br_aes_ct_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_ct64(void)
+{
+       test_AES_CTRCBC_inner("ct64", &br_aes_ct64_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_x86ni(void)
+{
+       const br_block_ctrcbc_class *vt;
+
+       vt = br_aes_x86ni_ctrcbc_get_vtable();
+       if (vt != NULL) {
+               test_AES_CTRCBC_inner("x86ni", vt);
+       } else {
+               printf("Test AES CTR/CBC-MAC x86ni: UNAVAILABLE\n");
+       }
+}
+
 /*
  * DES known-answer tests. Order: plaintext, key, ciphertext.
  * (mostly from NIST SP 800-20).
@@ -5077,7 +5295,7 @@ test_GCM(void)
                br_aes_ct_ctr_keys bc;
                br_gcm_context gc;
                unsigned char tmp[100], out[16];
-               size_t v;
+               size_t v, tag_len;
 
                key_len = hextobin(key, KAT_GCM[u]);
                plain_len = hextobin(plain, KAT_GCM[u + 1]);
@@ -5167,6 +5385,268 @@ test_GCM(void)
                        }
                }
 
+               /*
+                * Tag truncation.
+                */
+               for (tag_len = 1; tag_len <= 16; tag_len ++) {
+                       memset(out, 0x54, sizeof out);
+                       memcpy(tmp, plain, plain_len);
+                       br_gcm_reset(&gc, iv, iv_len);
+                       br_gcm_aad_inject(&gc, aad, aad_len);
+                       br_gcm_flip(&gc);
+                       br_gcm_run(&gc, 1, tmp, plain_len);
+                       br_gcm_get_tag_trunc(&gc, out, tag_len);
+                       check_equals("KAT GCM 8", out, tag, tag_len);
+                       for (v = tag_len; v < sizeof out; v ++) {
+                               if (out[v] != 0x54) {
+                                       fprintf(stderr, "overflow on tag\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+
+                       memcpy(tmp, plain, plain_len);
+                       br_gcm_reset(&gc, iv, iv_len);
+                       br_gcm_aad_inject(&gc, aad, aad_len);
+                       br_gcm_flip(&gc);
+                       br_gcm_run(&gc, 1, tmp, plain_len);
+                       if (!br_gcm_check_tag_trunc(&gc, out, tag_len)) {
+                               fprintf(stderr, "Tag not verified (3)\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+/*
+ * From "The EAX Mode of Operation (A Two-Pass Authenticated Encryption
+ * Scheme Optimized for Simplicity and Efficiency)" (Bellare, Rogaway,
+ * Wagner), presented at FSE 2004. Full article is available at:
+ *   http://web.cs.ucdavis.edu/~rogaway/papers/eax.html
+ *
+ * EAX specification concatenates the authentication tag at the end of
+ * the ciphertext; in our API and the vectors below, the tag is separate.
+ *
+ * Order is: plaintext, key, nonce, header, ciphertext, tag.
+ */
+static const char *const KAT_EAX[] = {
+       "",
+       "233952dee4d5ed5f9b9c6d6ff80ff478",
+       "62ec67f9c3a4a407fcb2a8c49031a8b3",
+       "6bfb914fd07eae6b",
+       "",
+       "e037830e8389f27b025a2d6527e79d01",
+
+       "f7fb",
+       "91945d3f4dcbee0bf45ef52255f095a4",
+       "becaf043b0a23d843194ba972c66debd",
+       "fa3bfd4806eb53fa",
+       "19dd",
+       "5c4c9331049d0bdab0277408f67967e5",
+
+       "1a47cb4933",
+       "01f74ad64077f2e704c0f60ada3dd523",
+       "70c3db4f0d26368400a10ed05d2bff5e",
+       "234a3463c1264ac6",
+       "d851d5bae0",
+       "3a59f238a23e39199dc9266626c40f80",
+
+       "481c9e39b1",
+       "d07cf6cbb7f313bdde66b727afd3c5e8",
+       "8408dfff3c1a2b1292dc199e46b7d617",
+       "33cce2eabff5a79d",
+       "632a9d131a",
+       "d4c168a4225d8e1ff755939974a7bede",
+
+       "40d0c07da5e4",
+       "35b6d0580005bbc12b0587124557d2c2",
+       "fdb6b06676eedc5c61d74276e1f8e816",
+       "aeb96eaebe2970e9",
+       "071dfe16c675",
+       "cb0677e536f73afe6a14b74ee49844dd",
+
+       "4de3b35c3fc039245bd1fb7d",
+       "bd8e6e11475e60b268784c38c62feb22",
+       "6eac5c93072d8e8513f750935e46da1b",
+       "d4482d1ca78dce0f",
+       "835bb4f15d743e350e728414",
+       "abb8644fd6ccb86947c5e10590210a4f",
+
+       "8b0a79306c9ce7ed99dae4f87f8dd61636",
+       "7c77d6e813bed5ac98baa417477a2e7d",
+       "1a8c98dcd73d38393b2bf1569deefc19",
+       "65d2017990d62528",
+       "02083e3979da014812f59f11d52630da30",
+       "137327d10649b0aa6e1c181db617d7f2",
+
+       "1bda122bce8a8dbaf1877d962b8592dd2d56",
+       "5fff20cafab119ca2fc73549e20f5b0d",
+       "dde59b97d722156d4d9aff2bc7559826",
+       "54b9f04e6a09189a",
+       "2ec47b2c4954a489afc7ba4897edcdae8cc3",
+       "3b60450599bd02c96382902aef7f832a",
+
+       "6cf36720872b8513f6eab1a8a44438d5ef11",
+       "a4a4782bcffd3ec5e7ef6d8c34a56123",
+       "b781fcf2f75fa5a8de97a9ca48e522ec",
+       "899a175897561d7e",
+       "0de18fd0fdd91e7af19f1d8ee8733938b1e8",
+       "e7f6d2231618102fdb7fe55ff1991700",
+
+       "ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7",
+       "8395fcf1e95bebd697bd010bc766aac3",
+       "22e7add93cfc6393c57ec0b3c17d6b44",
+       "126735fcc320d25a",
+       "cb8920f87a6c75cff39627b56e3ed197c552d295a7",
+       "cfc46afc253b4652b1af3795b124ab6e",
+
+       NULL
+};
+
+static void
+test_EAX_inner(const char *name, const br_block_ctrcbc_class *vt)
+{
+       size_t u;
+
+       printf("Test EAX %s: ", name);
+       fflush(stdout);
+
+       for (u = 0; KAT_EAX[u]; u += 6) {
+               unsigned char plain[100];
+               unsigned char key[32];
+               unsigned char nonce[100];
+               unsigned char aad[100];
+               unsigned char cipher[100];
+               unsigned char tag[100];
+               size_t plain_len, key_len, nonce_len, aad_len;
+               br_aes_gen_ctrcbc_keys bc;
+               br_eax_context ec;
+               unsigned char tmp[100], out[16];
+               size_t v, tag_len;
+
+               plain_len = hextobin(plain, KAT_EAX[u]);
+               key_len = hextobin(key, KAT_EAX[u + 1]);
+               nonce_len = hextobin(nonce, KAT_EAX[u + 2]);
+               aad_len = hextobin(aad, KAT_EAX[u + 3]);
+               hextobin(cipher, KAT_EAX[u + 4]);
+               hextobin(tag, KAT_EAX[u + 5]);
+
+               vt->init(&bc.vtable, key, key_len);
+               br_eax_init(&ec, &bc.vtable);
+
+               memset(tmp, 0x54, sizeof tmp);
+
+               /*
+                * Basic operation.
+                */
+               memcpy(tmp, plain, plain_len);
+               br_eax_reset(&ec, nonce, nonce_len);
+               br_eax_aad_inject(&ec, aad, aad_len);
+               br_eax_flip(&ec);
+               br_eax_run(&ec, 1, tmp, plain_len);
+               br_eax_get_tag(&ec, out);
+               check_equals("KAT EAX 1", tmp, cipher, plain_len);
+               check_equals("KAT EAX 2", out, tag, 16);
+
+               br_eax_reset(&ec, nonce, nonce_len);
+               br_eax_aad_inject(&ec, aad, aad_len);
+               br_eax_flip(&ec);
+               br_eax_run(&ec, 0, tmp, plain_len);
+               check_equals("KAT EAX 3", tmp, plain, plain_len);
+               if (!br_eax_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               for (v = plain_len; v < sizeof tmp; v ++) {
+                       if (tmp[v] != 0x54) {
+                               fprintf(stderr, "overflow on data\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Byte-by-byte injection.
+                */
+               br_eax_reset(&ec, nonce, nonce_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_eax_aad_inject(&ec, aad + v, 1);
+               }
+               br_eax_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_eax_run(&ec, 1, tmp + v, 1);
+               }
+               check_equals("KAT EAX 4", tmp, cipher, plain_len);
+               if (!br_eax_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               br_eax_reset(&ec, nonce, nonce_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_eax_aad_inject(&ec, aad + v, 1);
+               }
+               br_eax_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_eax_run(&ec, 0, tmp + v, 1);
+               }
+               br_eax_get_tag(&ec, out);
+               check_equals("KAT EAX 5", tmp, plain, plain_len);
+               check_equals("KAT EAX 6", out, tag, 16);
+
+               /*
+                * Check that alterations are detected.
+                */
+               for (v = 0; v < aad_len; v ++) {
+                       memcpy(tmp, cipher, plain_len);
+                       br_eax_reset(&ec, nonce, nonce_len);
+                       aad[v] ^= 0x04;
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       aad[v] ^= 0x04;
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 0, tmp, plain_len);
+                       check_equals("KAT EAX 7", tmp, plain, plain_len);
+                       if (br_eax_check_tag(&ec, tag)) {
+                               fprintf(stderr, "Tag should have changed\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Tag truncation.
+                */
+               for (tag_len = 1; tag_len <= 16; tag_len ++) {
+                       memset(out, 0x54, sizeof out);
+                       memcpy(tmp, plain, plain_len);
+                       br_eax_reset(&ec, nonce, nonce_len);
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 1, tmp, plain_len);
+                       br_eax_get_tag_trunc(&ec, out, tag_len);
+                       check_equals("KAT EAX 8", out, tag, tag_len);
+                       for (v = tag_len; v < sizeof out; v ++) {
+                               if (out[v] != 0x54) {
+                                       fprintf(stderr, "overflow on tag\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+
+                       memcpy(tmp, plain, plain_len);
+                       br_eax_reset(&ec, nonce, nonce_len);
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 1, tmp, plain_len);
+                       if (!br_eax_check_tag_trunc(&ec, out, tag_len)) {
+                               fprintf(stderr, "Tag not verified (3)\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
                printf(".");
                fflush(stdout);
        }
@@ -5175,6 +5655,236 @@ test_GCM(void)
        fflush(stdout);
 }
 
+static void
+test_EAX(void)
+{
+       const br_block_ctrcbc_class *x_ctrcbc;
+
+       test_EAX_inner("aes_big", &br_aes_big_ctrcbc_vtable);
+       test_EAX_inner("aes_small", &br_aes_small_ctrcbc_vtable);
+       test_EAX_inner("aes_ct", &br_aes_ct_ctrcbc_vtable);
+       test_EAX_inner("aes_ct64", &br_aes_ct64_ctrcbc_vtable);
+
+       x_ctrcbc = br_aes_x86ni_ctrcbc_get_vtable();
+       if (x_ctrcbc != NULL) {
+               test_EAX_inner("aes_x86ni", x_ctrcbc);
+       } else {
+               printf("Test EAX aes_x86ni: UNAVAILABLE\n");
+       }
+}
+
+/*
+ * From NIST SP 800-38C, appendix C.
+ *
+ * CCM specification concatenates the authentication tag at the end of
+ * the ciphertext; in our API and the vectors below, the tag is separate.
+ *
+ * Order is: key, nonce, aad, plaintext, ciphertext, tag.
+ */
+static const char *const KAT_CCM[] = {
+       "404142434445464748494a4b4c4d4e4f",
+       "10111213141516",
+       "0001020304050607",
+       "20212223",
+       "7162015b",
+       "4dac255d",
+
+       "404142434445464748494a4b4c4d4e4f",
+       "1011121314151617",
+       "000102030405060708090a0b0c0d0e0f",
+       "202122232425262728292a2b2c2d2e2f",
+       "d2a1f0e051ea5f62081a7792073d593d",
+       "1fc64fbfaccd",
+
+       "404142434445464748494a4b4c4d4e4f",
+       "101112131415161718191a1b",
+       "000102030405060708090a0b0c0d0e0f10111213",
+       "202122232425262728292a2b2c2d2e2f3031323334353637",
+       "e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5",
+       "484392fbc1b09951",
+
+       "404142434445464748494a4b4c4d4e4f",
+       "101112131415161718191a1b1c",
+       NULL,
+       "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+       "69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72",
+       "b4ac6bec93e8598e7f0dadbcea5b",
+
+       NULL
+};
+
+static void
+test_CCM_inner(const char *name, const br_block_ctrcbc_class *vt)
+{
+       size_t u;
+
+       printf("Test CCM %s: ", name);
+       fflush(stdout);
+
+       for (u = 0; KAT_CCM[u]; u += 6) {
+               unsigned char plain[100];
+               unsigned char key[32];
+               unsigned char nonce[100];
+               unsigned char aad_buf[100], *aad;
+               unsigned char cipher[100];
+               unsigned char tag[100];
+               size_t plain_len, key_len, nonce_len, aad_len, tag_len;
+               br_aes_gen_ctrcbc_keys bc;
+               br_ccm_context ec;
+               unsigned char tmp[100], out[16];
+               size_t v;
+
+               key_len = hextobin(key, KAT_CCM[u]);
+               nonce_len = hextobin(nonce, KAT_CCM[u + 1]);
+               if (KAT_CCM[u + 2] == NULL) {
+                       aad_len = 65536;
+                       aad = malloc(aad_len);
+                       if (aad == NULL) {
+                               fprintf(stderr, "OOM error\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       for (v = 0; v < 65536; v ++) {
+                               aad[v] = (unsigned char)v;
+                       }
+               } else {
+                       aad = aad_buf;
+                       aad_len = hextobin(aad, KAT_CCM[u + 2]);
+               }
+               plain_len = hextobin(plain, KAT_CCM[u + 3]);
+               hextobin(cipher, KAT_CCM[u + 4]);
+               tag_len = hextobin(tag, KAT_CCM[u + 5]);
+
+               vt->init(&bc.vtable, key, key_len);
+               br_ccm_init(&ec, &bc.vtable);
+
+               memset(tmp, 0x54, sizeof tmp);
+
+               /*
+                * Basic operation.
+                */
+               memcpy(tmp, plain, plain_len);
+               if (!br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len))
+               {
+                       fprintf(stderr, "CCM reset failed\n");
+                       exit(EXIT_FAILURE);
+               }
+               br_ccm_aad_inject(&ec, aad, aad_len);
+               br_ccm_flip(&ec);
+               br_ccm_run(&ec, 1, tmp, plain_len);
+               if (br_ccm_get_tag(&ec, out) != tag_len) {
+                       fprintf(stderr, "CCM returned wrong tag length\n");
+                       exit(EXIT_FAILURE);
+               }
+               check_equals("KAT CCM 1", tmp, cipher, plain_len);
+               check_equals("KAT CCM 2", out, tag, tag_len);
+
+               br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len);
+               br_ccm_aad_inject(&ec, aad, aad_len);
+               br_ccm_flip(&ec);
+               br_ccm_run(&ec, 0, tmp, plain_len);
+               check_equals("KAT CCM 3", tmp, plain, plain_len);
+               if (!br_ccm_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               for (v = plain_len; v < sizeof tmp; v ++) {
+                       if (tmp[v] != 0x54) {
+                               fprintf(stderr, "overflow on data\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Byte-by-byte injection.
+                */
+               br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_ccm_aad_inject(&ec, aad + v, 1);
+               }
+               br_ccm_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_ccm_run(&ec, 1, tmp + v, 1);
+               }
+               check_equals("KAT CCM 4", tmp, cipher, plain_len);
+               if (!br_ccm_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_ccm_aad_inject(&ec, aad + v, 1);
+               }
+               br_ccm_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_ccm_run(&ec, 0, tmp + v, 1);
+               }
+               br_ccm_get_tag(&ec, out);
+               check_equals("KAT CCM 5", tmp, plain, plain_len);
+               check_equals("KAT CCM 6", out, tag, tag_len);
+
+               /*
+                * Check that alterations are detected.
+                */
+               for (v = 0; v < aad_len; v ++) {
+                       memcpy(tmp, cipher, plain_len);
+                       br_ccm_reset(&ec, nonce, nonce_len,
+                               aad_len, plain_len, tag_len);
+                       aad[v] ^= 0x04;
+                       br_ccm_aad_inject(&ec, aad, aad_len);
+                       aad[v] ^= 0x04;
+                       br_ccm_flip(&ec);
+                       br_ccm_run(&ec, 0, tmp, plain_len);
+                       check_equals("KAT CCM 7", tmp, plain, plain_len);
+                       if (br_ccm_check_tag(&ec, tag)) {
+                               fprintf(stderr, "Tag should have changed\n");
+                               exit(EXIT_FAILURE);
+                       }
+
+                       /*
+                        * When the AAD is really big, we don't want to do
+                        * the complete quadratic operation.
+                        */
+                       if (v >= 32) {
+                               break;
+                       }
+               }
+
+               if (aad != aad_buf) {
+                       free(aad);
+               }
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+static void
+test_CCM(void)
+{
+       const br_block_ctrcbc_class *x_ctrcbc;
+
+       test_CCM_inner("aes_big", &br_aes_big_ctrcbc_vtable);
+       test_CCM_inner("aes_small", &br_aes_small_ctrcbc_vtable);
+       test_CCM_inner("aes_ct", &br_aes_ct_ctrcbc_vtable);
+       test_CCM_inner("aes_ct64", &br_aes_ct64_ctrcbc_vtable);
+
+       x_ctrcbc = br_aes_x86ni_ctrcbc_get_vtable();
+       if (x_ctrcbc != NULL) {
+               test_CCM_inner("aes_x86ni", x_ctrcbc);
+       } else {
+               printf("Test CCM aes_x86ni: UNAVAILABLE\n");
+       }
+}
+
 static void
 test_EC_inner(const char *sk, const char *sU,
        const br_ec_impl *impl, int curve)
@@ -6201,6 +6911,11 @@ static const struct {
        STU(AES_ct64),
        STU(AES_pwr8),
        STU(AES_x86ni),
+       STU(AES_CTRCBC_big),
+       STU(AES_CTRCBC_small),
+       STU(AES_CTRCBC_ct),
+       STU(AES_CTRCBC_ct64),
+       STU(AES_CTRCBC_x86ni),
        STU(DES_tab),
        STU(DES_ct),
        STU(ChaCha20_ct),
@@ -6218,6 +6933,8 @@ static const struct {
        STU(GHASH_ctmul64),
        STU(GHASH_pclmul),
        STU(GHASH_pwr8),
+       STU(CCM),
+       STU(EAX),
        STU(GCM),
        STU(EC_prime_i15),
        STU(EC_prime_i31),
index 296e914..245a840 100644 (file)
@@ -443,6 +443,82 @@ test_speed_poly1305_i15(void)
        test_speed_poly1305_inner("Poly1305 (i15)", &br_poly1305_i15_run);
 }
 
+static void
+test_speed_eax_inner(char *name,
+       const br_block_ctrcbc_class *vt, size_t key_len)
+{
+       unsigned char buf[8192], key[32], nonce[16], aad[16], tag[16];
+       int i;
+       long num;
+       br_aes_gen_ctrcbc_keys ac;
+       br_eax_context ec;
+
+       memset(key, 'K', key_len);
+       memset(nonce, 'N', sizeof nonce);
+       memset(aad, 'A', sizeof aad);
+       memset(buf, 'T', sizeof buf);
+       for (i = 0; i < 10; i ++) {
+               vt->init(&ac.vtable, key, key_len);
+               br_eax_init(&ec, &ac.vtable);
+               br_eax_reset(&ec, nonce, sizeof nonce);
+               br_eax_aad_inject(&ec, aad, sizeof aad);
+               br_eax_flip(&ec);
+               br_eax_run(&ec, 1, buf, sizeof buf);
+               br_eax_get_tag(&ec, tag);
+       }
+       num = 10;
+       for (;;) {
+               clock_t begin, end;
+               double tt;
+               long k;
+
+               begin = clock();
+               for (k = num; k > 0; k --) {
+                       vt->init(&ac.vtable, key, key_len);
+                       br_eax_init(&ec, &ac.vtable);
+                       br_eax_reset(&ec, nonce, sizeof nonce);
+                       br_eax_aad_inject(&ec, aad, sizeof aad);
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 1, buf, sizeof buf);
+                       br_eax_get_tag(&ec, tag);
+               }
+               end = clock();
+               tt = (double)(end - begin) / CLOCKS_PER_SEC;
+               if (tt >= 2.0) {
+                       printf("%-30s %8.2f MB/s\n", name,
+                               ((double)sizeof buf) * (double)num
+                               / (tt * 1000000.0));
+                       fflush(stdout);
+                       return;
+               }
+               num <<= 1;
+       }
+}
+
+#define SPEED_EAX(Algo, algo, keysize, impl) \
+static void \
+test_speed_eax_ ## algo ## keysize ## _ ## impl(void) \
+{ \
+       test_speed_eax_inner("EAX " #Algo "-" #keysize "(" #impl ")", \
+               &br_ ## algo ## _ ## impl ##  _ctrcbc_vtable, (keysize) >> 3); \
+}
+
+SPEED_EAX(AES, aes, 128, big)
+SPEED_EAX(AES, aes, 128, small)
+SPEED_EAX(AES, aes, 128, ct)
+SPEED_EAX(AES, aes, 128, ct64)
+SPEED_EAX(AES, aes, 128, x86ni)
+SPEED_EAX(AES, aes, 192, big)
+SPEED_EAX(AES, aes, 192, small)
+SPEED_EAX(AES, aes, 192, ct)
+SPEED_EAX(AES, aes, 192, ct64)
+SPEED_EAX(AES, aes, 192, x86ni)
+SPEED_EAX(AES, aes, 256, big)
+SPEED_EAX(AES, aes, 256, small)
+SPEED_EAX(AES, aes, 256, ct)
+SPEED_EAX(AES, aes, 256, ct64)
+SPEED_EAX(AES, aes, 256, x86ni)
+
 static const unsigned char RSA_N[] = {
        0xE9, 0xF2, 0x4A, 0x2F, 0x96, 0xDF, 0x0A, 0x23,
        0x01, 0x85, 0xF1, 0x2C, 0xB2, 0xA8, 0xEF, 0x23,
@@ -1300,6 +1376,22 @@ static const struct {
        STU(poly1305_ctmulq),
        STU(poly1305_i15),
 
+       STU(eax_aes128_big),
+       STU(eax_aes192_big),
+       STU(eax_aes256_big),
+       STU(eax_aes128_small),
+       STU(eax_aes192_small),
+       STU(eax_aes256_small),
+       STU(eax_aes128_ct),
+       STU(eax_aes192_ct),
+       STU(eax_aes256_ct),
+       STU(eax_aes128_ct64),
+       STU(eax_aes192_ct64),
+       STU(eax_aes256_ct64),
+       STU(eax_aes128_x86ni),
+       STU(eax_aes192_x86ni),
+       STU(eax_aes256_x86ni),
+
        STU(rsa_i15),
        STU(rsa_i31),
        STU(rsa_i32),