Added general-purpose API for AEAD algorithms, and GCM implementation.
authorThomas Pornin <pornin@bolet.org>
Mon, 17 Jul 2017 15:22:46 +0000 (17:22 +0200)
committerThomas Pornin <pornin@bolet.org>
Mon, 17 Jul 2017 15:22:46 +0000 (17:22 +0200)
inc/bearssl.h
inc/bearssl_aead.h [new file with mode: 0644]
mk/Rules.mk
mk/mkrules.sh
src/aead/gcm.c [new file with mode: 0644]
test/test_crypto.c

index 56df3ec..3436789 100644 (file)
 #include "bearssl_rand.h"
 #include "bearssl_prf.h"
 #include "bearssl_block.h"
+#include "bearssl_aead.h"
 #include "bearssl_rsa.h"
 #include "bearssl_ec.h"
 #include "bearssl_ssl.h"
diff --git a/inc/bearssl_aead.h b/inc/bearssl_aead.h
new file mode 100644 (file)
index 0000000..09cb9e8
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * 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.
+ */
+
+#ifndef BR_BEARSSL_AEAD_H__
+#define BR_BEARSSL_AEAD_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "bearssl_block.h"
+#include "bearssl_hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file bearssl_aead.h
+ *
+ * # Authenticated Encryption with Additional Data
+ *
+ * This file documents the API for AEAD encryption.
+ *
+ *
+ * ## Procedural API
+ *
+ * An AEAD algorithm processes messages and provides confidentiality
+ * (encryption) and checked integrity (MAC). It uses the following
+ * parameters:
+ *
+ *   - A symmetric key. Exact size depends on the AEAD algorithm.
+ *
+ *   - A nonce (IV). Size depends on the AEAD algorithm; for most
+ *     algorithms, it is crucial for security that any given nonce
+ *     value is never used twice for the same key and distinct
+ *     messages.
+ *
+ *   - Data to encrypt and protect.
+ *
+ *   - Additional authenticated data, which is covered by the MAC but
+ *     otherwise left untouched (i.e. not encrypted).
+ *
+ * The AEAD algorithm encrypts the data, and produces an authentication
+ * tag. It is assumed that the encrypted data, the tag, the additional
+ * authenticated data and the nonce are sent to the receiver; the
+ * additional data and the nonce may be implicit (e.g. using elements of
+ * the underlying transport protocol, such as record sequence numbers).
+ * The receiver will recompute the tag value and compare it with the one
+ * received; if they match, then the data is correct, and can be
+ * decrypted and used; otherwise, at least one of the elements was
+ * altered in transit, normally leading to wholesale rejection of the
+ * complete message.
+ *
+ * For each AEAD algorithm, identified by a symbolic name (hereafter
+ * denoted as "`xxx`"), the following functions are defined:
+ *
+ *   - `br_xxx_init()`
+ *
+ *     Initialise the AEAD algorithm, on a provided context structure.
+ *     Exact parameters depend on the algorithm, and may include
+ *     pointers to extra implementations and context structures. The
+ *     secret key is provided at this point, either directly or
+ *     indirectly.
+ *
+ *   - `br_xxx_reset()`
+ *
+ *     Start a new AEAD computation. The nonce value is provided as
+ *     parameter to this function.
+ *
+ *   - `br_xxx_aad_inject()`
+ *
+ *     Inject some additional authenticated data. Additional data may
+ *     be provided in several chunks of arbitrary length.
+ *
+ *   - `br_xxx_flip()`
+ *
+ *     This function MUST be called after injecting all additional
+ *     authenticated data, and before beginning to encrypt the plaintext
+ *     (or decrypt the ciphertext).
+ *
+ *   - `br_xxx_run()`
+ *
+ *     Process some plaintext (to encrypt) or ciphertext (to decrypt).
+ *     Encryption/decryption is done in place. Data may be provided in
+ *     several chunks of arbitrary length.
+ *
+ *   - `br_xxx_get_tag()`
+ *
+ *     Compute the authentication tag. All message data (encrypted or
+ *     decrypted) must have been injected at that point. Also, this
+ *     call may modify internal context elements, so it may be called
+ *     only once for a given AEAD computation.
+ *
+ *   - `br_xxx_check_tag()`
+ *
+ *     An alternative to `br_xxx_get_tag()`, meant to be used by the
+ *     receiver: the authentication tag is internally recomputed, and
+ *     compared with the one provided as parameter.
+ *
+ * This API makes the following assumptions on the AEAD algorithm:
+ *
+ *   - Encryption does not expand the size of the ciphertext; there is
+ *     no padding. This is true of most modern AEAD modes such as GCM.
+ *
+ *   - The additional authenticated data must be processed first,
+ *     before the encrypted/decrypted data.
+ *
+ *   - 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.
+ *
+ * Each AEAD algorithm has its own requirements and limits on the sizes
+ * of additional data and plaintext. This API does not provide any
+ * way to report invalid usage; it is up to the caller to ensure that
+ * the provided key, nonce, and data elements all fit the algorithm's
+ * requirements.
+ *
+ *
+ * ## Object-Oriented API
+ *
+ * Each context structure begins with a field (called `vtable`) that
+ * points to an instance of a structure that references the relevant
+ * functions through pointers. Each such structure contains the
+ * following:
+ *
+ *   - `reset`
+ *
+ *     Pointer to the reset function, that allows starting a new
+ *     computation.
+ *
+ *   - `aad_inject`
+ *
+ *     Pointer to the additional authenticated data injection function.
+ *
+ *   - `flip`
+ *
+ *     Pointer to the function that transitions from additional data
+ *     to main message data processing.
+ *
+ *   - `get_tag`
+ *
+ *     Pointer to the function that computes and returns the tag.
+ *
+ *   - `check_tag`
+ *
+ *     Pointer to the function that computes and verifies the tag against
+ *     a received value.
+ *
+ * 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.
+ */
+
+/**
+ * \brief Class type of an AEAD algorithm.
+ */
+typedef struct br_aead_class_ br_aead_class;
+struct br_aead_class_ {
+
+       /**
+        * \brief Size (in bytes) of authentication tags created by
+        * this AEAD algorithm.
+        */
+       size_t tag_size;
+
+       /**
+        * \brief Reset an AEAD context.
+        *
+        * This function resets an already initialised AEAD context for
+        * a new computation run. Implementations and keys are
+        * conserved. This function can be called at any time; it
+        * cancels any ongoing AEAD computation that uses the provided
+        * context structure.
+
+        * The provided IV is a _nonce_. Each AEAD algorithm has its
+        * own requirements on IV size and contents; for most of them,
+        * it is crucial to security that each nonce value is used
+        * only once for a given secret key.
+        *
+        * \param cc    AEAD context structure.
+        * \param iv    AEAD nonce to use.
+        * \param len   AEAD nonce length (in bytes).
+        */
+       void (*reset)(const br_aead_class **cc, const void *iv, size_t len);
+
+       /**
+        * \brief Inject additional authenticated data.
+        *
+        * The provided data is injected into a running AEAD
+        * computation. Additional data must be injected _before_ the
+        * call to `flip()`. Additional data can be injected in several
+        * chunks of arbitrary length.
+        *
+        * \param cc     AEAD context structure.
+        * \param data   pointer to additional authenticated data.
+        * \param len    length of additiona authenticated data (in bytes).
+        */
+       void (*aad_inject)(const br_aead_class **cc,
+               const void *data, size_t len);
+
+       /**
+        * \brief Finish injection of additional authenticated data.
+        *
+        * This function MUST be called before beginning the actual
+        * encryption or decryption (with `run()`), even if no
+        * additional authenticated data was injected. No additional
+        * authenticated data may be injected after this function call.
+        *
+        * \param cc   AEAD context structure.
+        */
+       void (*flip)(const br_aead_class **cc);
+
+       /**
+        * \brief Encrypt or decrypt some data.
+        *
+        * Data encryption or decryption can be done after `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 cc        AEAD 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 (*run)(const br_aead_class **cc, int encrypt,
+               void *data, size_t len);
+
+       /**
+        * \brief Compute authentication tag.
+        *
+        * Compute the AEAD authentication tag. The tag length depends
+        * on the AEAD algorithm; it is written in the provided `tag`
+        * buffer. This call terminates the AEAD run: no data may be
+        * processed with that AEAD context afterwards, until `reset()`
+        * is called to initiate a new AEAD 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
+        * `check_tag()` function may be used to compute and check the
+        * tag value.
+        *
+        * \param cc    AEAD context structure.
+        * \param tag   destination buffer for the tag.
+        */
+       void (*get_tag)(const br_aead_class **cc, void *tag);
+
+       /**
+        * \brief Compute and check authentication tag.
+        *
+        * This function is an alternative to `get_tag()`, and is
+        * normally used on the receiving end (i.e. when decrypting
+        * messages). 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 cc    AEAD context structure.
+        * \param tag   tag value to compare with (16 bytes).
+        * \return  1 on success (exact match of tag value), 0 otherwise.
+        */
+       uint32_t (*check_tag)(const br_aead_class **cc, const void *tag);
+};
+
+/**
+ * \brief Context structure for GCM.
+ *
+ * GCM is an AEAD mode that combines a block cipher in CTR mode with a
+ * MAC based on GHASH, to provide authenticated encryption:
+ *
+ *   - Any block cipher with 16-byte blocks can be used with GCM.
+ *
+ *   - The nonce can have any length, from 0 up to 2^64-1 bits; however,
+ *     96-bit nonces (12 bytes) are recommended (nonces with a length
+ *     distinct from 12 bytes are internally hashed, which risks reusing
+ *     nonce value with a small but not always negligible probability).
+ *
+ *   - Additional authenticated data may have length up to 2^64-1 bits.
+ *
+ *   - Message length may range up to 2^39-256 bits at most.
+ *
+ *   - The authentication tag has length 16 bytes.
+ *
+ * The GCM 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
+ * GCM 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_ctr_class **bctx;
+       br_ghash gh;
+       unsigned char h[16];
+       unsigned char j0_1[12];
+       unsigned char buf[16];
+       unsigned char y[16];
+       uint32_t j0_2, jc;
+       uint64_t count_aad, count_ctr;
+#endif
+} br_gcm_context;
+
+/**
+ * \brief Initialize a GCM context.
+ *
+ * A block cipher implementation, with its initialised context structure,
+ * is provided. The block cipher MUST use 16-byte blocks in CTR mode,
+ * and its secret key MUST have been already set in the provided context.
+ * A GHASH implementation must also be provided. The parameters are linked
+ * in the GCM context.
+ *
+ * After this function has been called, the `br_gcm_reset()` function must
+ * be called, to provide the IV for GCM computation.
+ *
+ * \param ctx    GCM context structure.
+ * \param bctx   block cipher context (already initialised with secret key).
+ * \param gh     GHASH implementation.
+ */
+void br_gcm_init(br_gcm_context *ctx,
+       const br_block_ctr_class **bctx, br_ghash gh);
+
+/**
+ * \brief Reset a GCM context.
+ *
+ * This function resets an already initialised GCM context for a new
+ * computation run. Implementations and keys are conserved. This function
+ * can be called at any time; it cancels any ongoing GCM computation that
+ * uses the provided context structure.
+ *
+ * The provided IV is a _nonce_. It is critical to GCM security that IV
+ * values are not repeated for the same encryption key. IV can have
+ * arbitrary length (up to 2^64-1 bits), but the "normal" length is
+ * 96 bits (12 bytes).
+ *
+ * \param ctx   GCM context structure.
+ * \param iv    GCM nonce to use.
+ * \param len   GCM nonce length (in bytes).
+ */
+void br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len);
+
+/**
+ * \brief Inject additional authenticated data into GCM.
+ *
+ * The provided data is injected into a running GCM computation. Additional
+ * data must be injected _before_ the call to `br_gcm_flip()`.
+ * Additional data can be injected in several chunks of arbitrary length;
+ * the maximum total size of additional authenticated data is 2^64-1
+ * bits.
+ *
+ * \param ctx    GCM context structure.
+ * \param data   pointer to additional authenticated data.
+ * \param len    length of additiona authenticated data (in bytes).
+ */
+void br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len);
+
+/**
+ * \brief Finish injection of additional authenticated data into GCM.
+ *
+ * This function MUST be called before beginning the actual encryption
+ * or decryption (with `br_gcm_run()`), even if no additional authenticated
+ * data was injected. No additional authenticated data may be injected
+ * after this function call.
+ *
+ * \param ctx   GCM context structure.
+ */
+void br_gcm_flip(br_gcm_context *ctx);
+
+/**
+ * \brief Encrypt or decrypt some data with GCM.
+ *
+ * Data encryption or decryption can be done after `br_gcm_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. The maximum
+ * total length for data is 2^39-256 bits, i.e. about 65 gigabytes.
+ *
+ * \param ctx       GCM 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_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len);
+
+/**
+ * \brief Compute GCM authentication tag.
+ *
+ * Compute the GCM authentication tag. The tag is a 16-byte value which
+ * 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()` function can be used to
+ * compute and check the tag value.
+ *
+ * \param ctx   GCM context structure.
+ * \param tag   destination buffer for the tag (16 bytes).
+ */
+void br_gcm_get_tag(br_gcm_context *ctx, void *tag);
+
+/**
+ * \brief Compute and check GCM authentication tag.
+ *
+ * This function is an alternative to `br_gcm_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   GCM 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_gcm_check_tag(br_gcm_context *ctx, const void *tag);
+
+/**
+ * \brief Class instance for GCM.
+ */
+extern const br_aead_class br_gcm_vtable;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 5a50049..fdedc6e 100644 (file)
@@ -1,11 +1,11 @@
 # Automatically generated rules. Use 'mkrules.sh' to modify/regenerate.
 
-OBJ = $(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)$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)$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)$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)$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)$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)$Pkeys$O $(OBJDIR)$Pnames$O $(OBJDIR)$Pserver$O $(OBJDIR)$Pskey$O $(OBJDIR)$Psslio$O $(OBJDIR)$Pta$O $(OBJDIR)$Pvector$O $(OBJDIR)$Pverify$O $(OBJDIR)$Pxmem$O
 OBJTESTCRYPTO = $(OBJDIR)$Ptest_crypto$O
 OBJTESTSPEED = $(OBJDIR)$Ptest_speed$O
 OBJTESTX509 = $(OBJDIR)$Ptest_x509$O
-HEADERSPUB = inc$Pbearssl.h inc$Pbearssl_block.h inc$Pbearssl_ec.h inc$Pbearssl_hash.h inc$Pbearssl_hmac.h inc$Pbearssl_pem.h inc$Pbearssl_prf.h inc$Pbearssl_rand.h inc$Pbearssl_rsa.h inc$Pbearssl_ssl.h inc$Pbearssl_x509.h
+HEADERSPUB = inc$Pbearssl.h inc$Pbearssl_aead.h inc$Pbearssl_block.h inc$Pbearssl_ec.h inc$Pbearssl_hash.h inc$Pbearssl_hmac.h inc$Pbearssl_pem.h inc$Pbearssl_prf.h inc$Pbearssl_rand.h inc$Pbearssl_rsa.h inc$Pbearssl_ssl.h inc$Pbearssl_x509.h
 HEADERSPRIV = $(HEADERSPUB) src$Pconfig.h src$Pinner.h
 HEADERSTOOLS = $(HEADERSPUB) tools$Pbrssl.h
 T0SRC = T0$PBlobWriter.cs T0$PCPU.cs T0$PCodeElement.cs T0$PCodeElementJump.cs T0$PCodeElementUInt.cs T0$PCodeElementUIntExpr.cs T0$PCodeElementUIntInt.cs T0$PCodeElementUIntUInt.cs T0$PConstData.cs T0$POpcode.cs T0$POpcodeCall.cs T0$POpcodeConst.cs T0$POpcodeGetLocal.cs T0$POpcodeJump.cs T0$POpcodeJumpIf.cs T0$POpcodeJumpIfNot.cs T0$POpcodeJumpUncond.cs T0$POpcodePutLocal.cs T0$POpcodeRet.cs T0$PSType.cs T0$PT0Comp.cs T0$PTPointerBase.cs T0$PTPointerBlob.cs T0$PTPointerExpr.cs T0$PTPointerNull.cs T0$PTPointerXT.cs T0$PTValue.cs T0$PWord.cs T0$PWordBuilder.cs T0$PWordData.cs T0$PWordInterpreted.cs T0$PWordNative.cs
@@ -23,7 +23,9 @@ tools: $(BRSSL)
 
 tests: $(TESTCRYPTO) $(TESTSPEED) $(TESTX509)
 
-T0: $(T0COMP) src$Pssl$Pssl_hs_common.t0 src$Pssl$Pssl_hs_client.t0 src$Pssl$Pssl_hs_server.t0 src$Px509$Pasn1.t0 src$Px509$Pskey_decoder.t0 src$Px509$Px509_decoder.t0 src$Px509$Px509_minimal.t0
+T0: kT0
+
+kT0: $(T0COMP) src$Pssl$Pssl_hs_common.t0 src$Pssl$Pssl_hs_client.t0 src$Pssl$Pssl_hs_server.t0 src$Px509$Pasn1.t0 src$Px509$Pskey_decoder.t0 src$Px509$Px509_decoder.t0 src$Px509$Px509_minimal.t0
        $(RUNT0COMP) -o src$Pcodec$Ppemdec -r br_pem_decoder src$Pcodec$Ppemdec.t0
        $(RUNT0COMP) -o src$Pssl$Pssl_hs_client -r br_ssl_hs_client src$Pssl$Pssl_hs_common.t0 src$Pssl$Pssl_hs_client.t0
        $(RUNT0COMP) -o src$Pssl$Pssl_hs_server -r br_ssl_hs_server src$Pssl$Pssl_hs_common.t0 src$Pssl$Pssl_hs_server.t0
@@ -59,6 +61,9 @@ $(TESTSPEED): $(BEARSSLLIB) $(OBJTESTSPEED)
 $(TESTX509): $(BEARSSLLIB) $(OBJTESTX509)
        $(LD) $(LDFLAGS) $(LDOUT)$(TESTX509) $(OBJTESTX509) $(BEARSSLLIB)
 
+$(OBJDIR)$Pgcm$O: src$Paead$Pgcm.c $(HEADERSPRIV)
+       $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pgcm$O src$Paead$Pgcm.c
+
 $(OBJDIR)$Pccopy$O: src$Pcodec$Pccopy.c $(HEADERSPRIV)
        $(CC) $(CFLAGS) $(INCFLAGS) $(CCOUT)$(OBJDIR)$Pccopy$O src$Pcodec$Pccopy.c
 
index 1cc9c88..4929294 100755 (executable)
@@ -49,6 +49,7 @@ set -e
 
 # Source files. Please keep in alphabetical order.
 coresrc=" \
+       src/aead/gcm.c \
        src/codec/ccopy.c \
        src/codec/dec16be.c \
        src/codec/dec16le.c \
@@ -307,6 +308,7 @@ testx509src=" \
 # Public header files.
 headerspub=" \
        inc/bearssl.h \
+       inc/bearssl_aead.h \
        inc/bearssl_block.h \
        inc/bearssl_ec.h \
        inc/bearssl_hash.h \
@@ -432,7 +434,9 @@ tools: \$(BRSSL)
 
 tests: \$(TESTCRYPTO) \$(TESTSPEED) \$(TESTX509)
 
-T0: \$(T0COMP) src\$Pssl\$Pssl_hs_common.t0 src\$Pssl\$Pssl_hs_client.t0 src\$Pssl\$Pssl_hs_server.t0 src\$Px509\$Pasn1.t0 src\$Px509\$Pskey_decoder.t0 src\$Px509\$Px509_decoder.t0 src\$Px509\$Px509_minimal.t0
+T0: kT0
+
+kT0: \$(T0COMP) src\$Pssl\$Pssl_hs_common.t0 src\$Pssl\$Pssl_hs_client.t0 src\$Pssl\$Pssl_hs_server.t0 src\$Px509\$Pasn1.t0 src\$Px509\$Pskey_decoder.t0 src\$Px509\$Px509_decoder.t0 src\$Px509\$Px509_minimal.t0
        \$(RUNT0COMP) -o src\$Pcodec\$Ppemdec -r br_pem_decoder src\$Pcodec\$Ppemdec.t0
        \$(RUNT0COMP) -o src\$Pssl\$Pssl_hs_client -r br_ssl_hs_client src\$Pssl\$Pssl_hs_common.t0 src\$Pssl\$Pssl_hs_client.t0
        \$(RUNT0COMP) -o src\$Pssl\$Pssl_hs_server -r br_ssl_hs_server src\$Pssl\$Pssl_hs_common.t0 src\$Pssl\$Pssl_hs_server.t0
diff --git a/src/aead/gcm.c b/src/aead/gcm.c
new file mode 100644 (file)
index 0000000..9cf0f38
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * 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
+ * ====================
+ *
+ * Since CTR and GHASH implementations can handle only full blocks, a
+ * 16-byte buffer (buf[]) is maintained in the context:
+ *
+ *  - When processing AAD, buf[] contains the 0-15 unprocessed bytes.
+ *
+ *  - When doing CTR encryption / decryption, buf[] contains the AES output
+ *    for the last partial block, to be used with the next few bytes of
+ *    data, as well as the already encrypted bytes. For instance, if the
+ *    processed data length so far is 21 bytes, then buf[0..4] contains
+ *    the five last encrypted bytes, and buf[5..15] contains the next 11
+ *    AES output bytes to be XORed with the next 11 bytes of input.
+ *
+ *    The recorded AES output bytes are used to complete the block when
+ *    the corresponding bytes are obtained. Note that buf[] always
+ *    contains the _encrypted_ bytes, whether we apply encryption or
+ *    decryption: these bytes are used as input to GHASH when the block
+ *    is complete.
+ *
+ * In both cases, the low bits of the data length counters (count_aad,
+ * count_ctr) are used to work out the current situation.
+ */
+
+/* see bearssl_aead.h */
+void
+br_gcm_init(br_gcm_context *ctx, const br_block_ctr_class **bctx, br_ghash gh)
+{
+       unsigned char iv[12];
+
+       ctx->bctx = bctx;
+       ctx->gh = gh;
+
+       /*
+        * The GHASH key h[] is the raw encryption of the all-zero
+        * block. Since we only have a CTR implementation, we use it
+        * with an all-zero IV and a zero counter, to CTR-encrypt an
+        * all-zero block.
+        */
+       memset(ctx->h, 0, sizeof ctx->h);
+       memset(iv, 0, sizeof iv);
+       (*bctx)->run(bctx, iv, 0, ctx->h, sizeof ctx->h);
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len)
+{
+       /*
+        * If the provided nonce is 12 bytes, then this is the initial
+        * IV for CTR mode; it will be used with a counter that starts
+        * at 2 (value 1 is for encrypting the GHASH output into the tag).
+        *
+        * If the provided nonce has any other length, then it is hashed
+        * (with GHASH) into a 16-byte value that will be the IV for CTR
+        * (both 12-byte IV and 32-bit counter).
+        */
+       if (len == 12) {
+               memcpy(ctx->j0_1, iv, 12);
+               ctx->j0_2 = 1;
+       } else {
+               unsigned char ty[16], tmp[16];
+
+               memset(ty, 0, sizeof ty);
+               ctx->gh(ty, ctx->h, iv, len);
+               memset(tmp, 0, 8);
+               br_enc64be(tmp + 8, (uint64_t)len << 3);
+               ctx->gh(ty, ctx->h, tmp, 16);
+               memcpy(ctx->j0_1, ty, 12);
+               ctx->j0_2 = br_dec32be(ty + 12);
+       }
+       ctx->jc = ctx->j0_2 + 1;
+       memset(ctx->y, 0, sizeof ctx->y);
+       ctx->count_aad = 0;
+       ctx->count_ctr = 0;
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len)
+{
+       size_t ptr, dlen;
+
+       ptr = (size_t)ctx->count_aad & (size_t)15;
+       if (ptr != 0) {
+               /*
+                * If there is a partial block, then we first try to
+                * complete it.
+                */
+               size_t clen;
+
+               clen = 16 - ptr;
+               if (len < clen) {
+                       memcpy(ctx->buf + ptr, data, len);
+                       ctx->count_aad += (uint64_t)len;
+                       return;
+               }
+               memcpy(ctx->buf + ptr, data, clen);
+               ctx->gh(ctx->y, ctx->h, ctx->buf, 16);
+               data = (const unsigned char *)data + clen;
+               len -= clen;
+               ctx->count_aad += (uint64_t)clen;
+       }
+
+       /*
+        * Now AAD is aligned on a 16-byte block (with regards to GHASH).
+        * We process all complete blocks, and save the last partial
+        * block.
+        */
+       dlen = len & ~(size_t)15;
+       ctx->gh(ctx->y, ctx->h, data, dlen);
+       memcpy(ctx->buf, (const unsigned char *)data + dlen, len - dlen);
+       ctx->count_aad += (uint64_t)len;
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_flip(br_gcm_context *ctx)
+{
+       /*
+        * We complete the GHASH computation if there is a partial block.
+        * The GHASH implementation automatically applies padding with
+        * zeros.
+        */
+       size_t ptr;
+
+       ptr = (size_t)ctx->count_aad & (size_t)15;
+       if (ptr != 0) {
+               ctx->gh(ctx->y, ctx->h, ctx->buf, ptr);
+       }
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len)
+{
+       unsigned char *buf;
+       size_t ptr, dlen;
+
+       buf = data;
+       ptr = (size_t)ctx->count_ctr & (size_t)15;
+       if (ptr != 0) {
+               /*
+                * If we have a partial block, then we try to complete it.
+                */
+               size_t u, clen;
+
+               clen = 16 - ptr;
+               if (len < clen) {
+                       clen = len;
+               }
+               for (u = 0; u < clen; u ++) {
+                       unsigned x, y;
+
+                       x = buf[u];
+                       y = x ^ ctx->buf[ptr + u];
+                       ctx->buf[ptr + u] = encrypt ? y : x;
+                       buf[u] = y;
+               }
+               ctx->count_ctr += (uint64_t)clen;
+               buf += clen;
+               len -= clen;
+               if (ptr + clen < 16) {
+                       return;
+               }
+               ctx->gh(ctx->y, ctx->h, ctx->buf, 16);
+       }
+
+       /*
+        * Process full blocks.
+        */
+       dlen = len & ~(size_t)15;
+       if (!encrypt) {
+               ctx->gh(ctx->y, ctx->h, buf, dlen);
+       }
+       ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->jc, buf, dlen);
+       if (encrypt) {
+               ctx->gh(ctx->y, ctx->h, buf, dlen);
+       }
+       buf += dlen;
+       len -= dlen;
+       ctx->count_ctr += (uint64_t)dlen;
+
+       if (len > 0) {
+               /*
+                * There is a partial block.
+                */
+               size_t u;
+
+               memset(ctx->buf, 0, sizeof ctx->buf);
+               ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1,
+                       ctx->jc, ctx->buf, 16);
+               for (u = 0; u < len; u ++) {
+                       unsigned x, y;
+
+                       x = buf[u];
+                       y = x ^ ctx->buf[u];
+                       ctx->buf[u] = encrypt ? y : x;
+                       buf[u] = y;
+               }
+               ctx->count_ctr += (uint64_t)len;
+       }
+}
+
+/* see bearssl_aead.h */
+void
+br_gcm_get_tag(br_gcm_context *ctx, void *tag)
+{
+       size_t ptr;
+       unsigned char tmp[16];
+
+       ptr = (size_t)ctx->count_ctr & (size_t)15;
+       if (ptr > 0) {
+               /*
+                * There is a partial block: encrypted/decrypted data has
+                * been produced, but the encrypted bytes must still be
+                * processed by GHASH.
+                */
+               ctx->gh(ctx->y, ctx->h, ctx->buf, ptr);
+       }
+
+       /*
+        * Final block for GHASH: the AAD and plaintext lengths (in bits).
+        */
+       br_enc64be(tmp, ctx->count_aad << 3);
+       br_enc64be(tmp + 8, ctx->count_ctr << 3);
+       ctx->gh(ctx->y, ctx->h, tmp, 16);
+
+       /*
+        * Tag is the GHASH output XORed with the encryption of the
+        * nonce with the initial counter value.
+        */
+       memcpy(tag, ctx->y, 16);
+       (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->j0_2, tag, 16);
+}
+
+/* see bearssl_aead.h */
+uint32_t
+br_gcm_check_tag(br_gcm_context *ctx, const void *tag)
+{
+       unsigned char tmp[16];
+       size_t u;
+       int x;
+
+       br_gcm_get_tag(ctx, tmp);
+       x = 0;
+       for (u = 0; u < sizeof tmp; u ++) {
+               x |= tmp[u] ^ ((const unsigned char *)tag)[u];
+       }
+       return EQ0(x);
+}
+
+/* see bearssl_aead.h */
+const br_aead_class br_gcm_vtable = {
+       16,
+       (void (*)(const br_aead_class **, const void *, size_t))
+               &br_gcm_reset,
+       (void (*)(const br_aead_class **, const void *, size_t))
+               &br_gcm_aad_inject,
+       (void (*)(const br_aead_class **))
+               &br_gcm_flip,
+       (void (*)(const br_aead_class **, int, void *, size_t))
+               &br_gcm_run,
+       (void (*)(const br_aead_class **, void *))
+               &br_gcm_get_tag,
+       (uint32_t (*)(const br_aead_class **, const void *))
+               &br_gcm_check_tag
+};
index 7e282ab..9e68cd9 100644 (file)
@@ -4892,6 +4892,258 @@ test_GHASH_pwr8(void)
        }
 }
 
+/*
+ * From: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
+ *
+ * Order: key, plaintext, AAD, IV, ciphertext, tag
+ */
+static const char *const KAT_GCM[] = {
+       "00000000000000000000000000000000",
+       "",
+       "",
+       "000000000000000000000000",
+       "",
+       "58e2fccefa7e3061367f1d57a4e7455a",
+
+       "00000000000000000000000000000000",
+       "00000000000000000000000000000000",
+       "",
+       "000000000000000000000000",
+       "0388dace60b6a392f328c2b971b2fe78",
+       "ab6e47d42cec13bdf53a67b21257bddf",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+       "",
+       "cafebabefacedbaddecaf888",
+       "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+       "4d5c2af327cd64a62cf35abd2ba6fab4",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbaddecaf888",
+       "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+       "5bc94fbc3221a5db94fae95ae7121a47",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbad",
+       "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+       "3612d2e79e3b0785561be14aaca2fccb",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+       "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+       "619cc5aefffe0bfa462af43c1699d050",
+
+       "000000000000000000000000000000000000000000000000",
+       "",
+       "",
+       "000000000000000000000000",
+       "",
+       "cd33b28ac773f74ba00ed1f312572435",
+
+       "000000000000000000000000000000000000000000000000",
+       "00000000000000000000000000000000",
+       "",
+       "000000000000000000000000",
+       "98e7247c07f0fe411c267e4384b0f600",
+       "2ff58d80033927ab8ef4d4587514f0fb",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+       "",
+       "cafebabefacedbaddecaf888",
+       "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+       "9924a7c8587336bfb118024db8674a14",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbaddecaf888",
+       "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+       "2519498e80f1478f37ba55bd6d27618c",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbad",
+       "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+       "65dcc57fcf623a24094fcca40d3533f8",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+       "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+       "dcf566ff291c25bbb8568fc3d376a6d9",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "",
+       "",
+       "000000000000000000000000",
+       "",
+       "530f8afbc74536b9a963b4f1c4cb738b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "00000000000000000000000000000000",
+       "",
+       "000000000000000000000000",
+       "cea7403d4d606b6e074ec5d3baf39d18",
+       "d0d1c8a799996bf0265b98b5d48ab919",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+       "",
+       "cafebabefacedbaddecaf888",
+       "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+       "b094dac5d93471bdec1a502270e3cc6c",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbaddecaf888",
+       "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+       "76fc6ece0f4e1768cddf8853bb2d551b",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbad",
+       "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+       "3a337dbf46a792c45e454913fe2ea8f2",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+       "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+       "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+
+       NULL
+};
+
+static void
+test_GCM(void)
+{
+       size_t u;
+
+       printf("Test GCM: ");
+       fflush(stdout);
+
+       for (u = 0; KAT_GCM[u]; u += 6) {
+               unsigned char key[32];
+               unsigned char plain[100];
+               unsigned char aad[100];
+               unsigned char iv[100];
+               unsigned char cipher[100];
+               unsigned char tag[100];
+               size_t key_len, plain_len, aad_len, iv_len;
+               br_aes_ct_ctr_keys bc;
+               br_gcm_context gc;
+               unsigned char tmp[100], out[16];
+               size_t v;
+
+               key_len = hextobin(key, KAT_GCM[u]);
+               plain_len = hextobin(plain, KAT_GCM[u + 1]);
+               aad_len = hextobin(aad, KAT_GCM[u + 2]);
+               iv_len = hextobin(iv, KAT_GCM[u + 3]);
+               hextobin(cipher, KAT_GCM[u + 4]);
+               hextobin(tag, KAT_GCM[u + 5]);
+
+               br_aes_ct_ctr_init(&bc, key, key_len);
+               br_gcm_init(&gc, &bc.vtable, br_ghash_ctmul32);
+
+               memset(tmp, 0x54, sizeof tmp);
+
+               /*
+                * Basic operation.
+                */
+               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(&gc, out);
+               check_equals("KAT GCM 1", tmp, cipher, plain_len);
+               check_equals("KAT GCM 2", out, tag, 16);
+
+               br_gcm_reset(&gc, iv, iv_len);
+               br_gcm_aad_inject(&gc, aad, aad_len);
+               br_gcm_flip(&gc);
+               br_gcm_run(&gc, 0, tmp, plain_len);
+               check_equals("KAT GCM 3", tmp, plain, plain_len);
+               if (!br_gcm_check_tag(&gc, 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_gcm_reset(&gc, iv, iv_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_gcm_aad_inject(&gc, aad + v, 1);
+               }
+               br_gcm_flip(&gc);
+               for (v = 0; v < plain_len; v ++) {
+                       br_gcm_run(&gc, 1, tmp + v, 1);
+               }
+               check_equals("KAT GCM 4", tmp, cipher, plain_len);
+               if (!br_gcm_check_tag(&gc, tag)) {
+                       fprintf(stderr, "Tag not verified (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               br_gcm_reset(&gc, iv, iv_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_gcm_aad_inject(&gc, aad + v, 1);
+               }
+               br_gcm_flip(&gc);
+               for (v = 0; v < plain_len; v ++) {
+                       br_gcm_run(&gc, 0, tmp + v, 1);
+               }
+               br_gcm_get_tag(&gc, out);
+               check_equals("KAT GCM 5", tmp, plain, plain_len);
+               check_equals("KAT GCM 6", out, tag, 16);
+
+               /*
+                * Check that alterations are detected.
+                */
+               for (v = 0; v < aad_len; v ++) {
+                       memcpy(tmp, cipher, plain_len);
+                       br_gcm_reset(&gc, iv, iv_len);
+                       aad[v] ^= 0x04;
+                       br_gcm_aad_inject(&gc, aad, aad_len);
+                       aad[v] ^= 0x04;
+                       br_gcm_flip(&gc);
+                       br_gcm_run(&gc, 0, tmp, plain_len);
+                       check_equals("KAT GCM 7", tmp, plain, plain_len);
+                       if (br_gcm_check_tag(&gc, tag)) {
+                               fprintf(stderr, "Tag should have changed\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
 static void
 test_EC_inner(const char *sk, const char *sU,
        const br_ec_impl *impl, int curve)
@@ -5934,6 +6186,7 @@ static const struct {
        STU(GHASH_ctmul64),
        STU(GHASH_pclmul),
        STU(GHASH_pwr8),
+       STU(GCM),
        STU(EC_prime_i15),
        STU(EC_prime_i31),
        STU(EC_p256_m15),