Hi,

This is a first diff of a series that brings support for the
Galois/Counter Mode for AES in OpenBSD.

Intro
=====

For those unfamiliar with GCM, I'm going to quote Wikipedia[1]:

  GCM mode (Galois/Counter Mode) is a mode of operation for symmetri
  key cryptographic block ciphers. It is an authenticated encryption
  algorithm designed to provide both authentication and secrecy.
  GCM mode is defined for block ciphers with a block size of 128 bits.
  GMAC is an authentication-only variant of the GCM.

So GCM is an emerging standard for IPsec that is approved by NIST
in the NIST Special Publication 800-38D[2].  It's unencumbered by
patents.

GCM is cryptographically secure as long as underlying cipher is
secure; it heavily relies on uniqueness and non-repeating of the
Initialization Vector.  Also use of IKE is mandatory for GCM.
Quoting RFC 4106[3]:

  Because reusing an nonce/key combination destroys the security
  guarantees of AES-GCM mode, it can be difficult to use this mode
  securely when using statically configured keys.  For safety's sake,
  implementations MUST use an automated key management system, such as
  the Internet Key Exchange (IKE) [RFC2409], to ensure that this
  requirement is met.

"Authentication weaknesses in GCM" by Niels Ferguson[4] describes
some of the potentian issues with GCM in detail.

Implementation details
======================

As this is the first time a combined authentication-encryption
transformation hits OpenBSD kernel, I chose to split to split
it into two parts: encryption transformation and authentication
hash, AES-GCM and AES-GMAC respectively.

AES-GCM part is fairly simple and it reuses AES-CTR code to do
key generation, encryption and decryption.  IV handling is almost
the same, except that GCM spec requires to start counter with 1.

Here I have to note that AES-GCM is a stream cipher hence it has a
block size of "1".

Now GMAC is a bit different to HMAC-style functions.  Highlights:
 - uses one round of AES (GCTR) hence requires the same key an IV
   as AES-GCM;

 - requires input data to be continuous, i.e.:
     given block C of 16 bytes which is a concatenation of blocks
     A an B (C = A || B): GMAC(C) =/= GMAC(B, GMAC(A, Z)), where
     GMAC(X, Y) denotes GMAC application to the block X and initial
     (or state) block Y.

 - can be efficiently accelerated by using table method or CPU
   instructions (CLMUL instructions on newer Intel CPUs).

 - defines two types of input data: Additional Authentication Data
   (AAD) and plaintext;

 - requires the last invocation of the hash routine to be done on
   the so-called length block that includes lengths of AAD and
   plaintext inputs.

RFC 4106 (use of AES-GCM in ESP) specifies three key sizes (128,
192 and 256) and three types of "Type 1" (encryption) transforms:

  AES-GCM with an Integrity Check Value (ICV) of 8 bytes (as MAY)
  AES-GCM with an ICV of 12 bytes (as MAY)
  AES-GCM with an ICV of 16 bytes (as MUST)

This implementation provides support for AES-GCM-16 only.

RFC 4543[5] (use of AES-GMAC in ESP and AH) specifies GMAC to have
ICV values of 16 bytes only.

OCF integration
===============

OCF integration required extension of the auth_hash structure to
include two new function pointers (Setkey and Reinit).  That was
done to keep AES_GMAC_Update stateless.  AES_GMAC_Update relies
on the callee to composing the last lengths block.

GMAC code is written in a way that supporting other block cipher
won't require cipher-independent changes.

Software crypto implementation gets a separate routine to handle
combined modes that is also written in a way that will support
future enchancements.  Please note that it's not an overhead,
in this case it actually simplifies the integration.

Test vectors
============

Yes, they do exist!  The problem here is that we can't (sort of)
test GMAC through the /dev/crypto as we don't provide access to
the auth algorithms.

It is possible to include a regress program with test vectors
for the encryption part though.

Obviously I've done tests in userland and with IPsec against
Linux to verify correctness.  (I didn't test on !AMD64 though -
it's on my TODO list).

AES-GMAC in ESP and AH
======================

This section is here just to clarify presense of the AES-GMAC
encryption transformation with all function pointers set to NIL.
Well, blame the committee.  They've specified [in RFC 4543] a
ESP encryption transformation that only does authentication:

  It is called ENCR_NULL_AUTH_AES_GMAC to highlight the fact that
  it performs no encryption and provides no confidentiality.

C'est la vie.

Outro
=====

For now this is provided for reference and discussion purposes only.
(Not asking for okays, but will if my account will be reenabled)

PS.
I'm deeply sorry for my Engrish, mistakes and bad phrase composition.

Have fun.

References:

[1] http://en.wikipedia.org/wiki/Galois/Counter_Mode
[2] http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
[3] http://tools.ietf.org/html/rfc4106
[4] 
http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/comments/CWC-GCM/Ferguson2.pdf
[5] http://tools.ietf.org/html/rfc4543

Index: conf/files
===================================================================
RCS file: /home/cvs/src/sys/conf/files,v
retrieving revision 1.499
diff -u -p -r1.499 files
--- conf/files  31 Jul 2010 08:33:19 -0000      1.499
+++ conf/files  3 Aug 2010 17:42:09 -0000
@@ -897,6 +897,7 @@ file crypto/arc4.c                  
 file crypto/michael.c                  wlan
 file crypto/cmac.c                     wlan
 file crypto/hmac.c                     wlan | (softraid & crypto)
+file crypto/gmac.c                     (inet & ipsec) | crypto
 file crypto/key_wrap.c                 wlan
 file crypto/idgen.c                    inet6 | nfsclient | nfsserver
 file netatalk/aarp.c                   netatalk
Index: crypto/cryptodev.c
===================================================================
RCS file: /home/cvs/src/sys/crypto/cryptodev.c,v
retrieving revision 1.73
diff -u -p -r1.73 cryptodev.c
--- crypto/cryptodev.c  21 Jul 2010 18:44:01 -0000      1.73
+++ crypto/cryptodev.c  26 Jul 2010 11:26:07 -0000
@@ -178,6 +178,9 @@ cryptof_ioctl(struct file *fp, u_long cm
                case CRYPTO_AES_XTS:
                        txform = &enc_xform_aes_xts;
                        break;
+               case CRYPTO_AES_GCM_16:
+                       txform = &enc_xform_aes_gcm;
+                       break;
                case CRYPTO_ARC4:
                        txform = &enc_xform_arc4;
                        break;
Index: crypto/cryptodev.h
===================================================================
RCS file: /home/cvs/src/sys/crypto/cryptodev.h,v
retrieving revision 1.51
diff -u -p -r1.51 cryptodev.h
--- crypto/cryptodev.h  23 Jun 2010 09:26:32 -0000      1.51
+++ crypto/cryptodev.h  28 Jul 2010 16:13:56 -0000
@@ -105,7 +105,12 @@
 #define CRYPTO_SHA2_512_HMAC   20
 #define CRYPTO_AES_CTR         21
 #define CRYPTO_AES_XTS         22
-#define CRYPTO_ALGORITHM_MAX   22 /* Keep updated */
+#define CRYPTO_AES_GCM_16      23
+#define CRYPTO_AES_128_GMAC    24
+#define CRYPTO_AES_192_GMAC    25
+#define CRYPTO_AES_256_GMAC    26
+#define CRYPTO_AES_GMAC                27
+#define CRYPTO_ALGORITHM_MAX   28 /* Keep updated */
 
 /* Algorithm flags */
 #define        CRYPTO_ALG_FLAG_SUPPORTED       0x01 /* Algorithm is supported 
*/
Index: crypto/cryptosoft.c
===================================================================
RCS file: /home/cvs/src/sys/crypto/cryptosoft.c,v
retrieving revision 1.54
diff -u -p -r1.54 cryptosoft.c
--- crypto/cryptosoft.c 2 Jul 2010 02:40:15 -0000       1.54
+++ crypto/cryptosoft.c 20 Aug 2010 15:38:50 -0000
@@ -492,6 +492,147 @@ swcr_authcompute(struct cryptop *crp, st
 }
 
 /*
+ * Apply a combined encryption-authentication transformation
+ */
+int
+swcr_combined(struct cryptop *crp)
+{
+       unsigned char iv[EALG_MAX_BLOCK_LEN], blk[EALG_MAX_BLOCK_LEN];
+       unsigned char aalg[AALG_MAX_RESULT_LEN];
+       union authctx ctx;
+       struct cryptodesc *crd, *crda = NULL, *crde = NULL;
+       struct swcr_data *sw, *swa, *swe;
+       struct auth_hash *axf = NULL;
+       struct enc_xform *exf = NULL;
+       struct mbuf *m = NULL;
+       struct uio *uio = NULL;
+       caddr_t buf = (caddr_t)crp->crp_buf;
+       uint32_t *blkp;
+       int i, blksz, ivlen, outtype, len;
+
+       for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
+               for (sw = swcr_sessions[crp->crp_sid & 0xffffffff];
+                    sw && sw->sw_alg != crd->crd_alg;
+                    sw = sw->sw_next)
+                       ;
+               if (sw == NULL)
+                       return (EINVAL);
+
+               switch (sw->sw_alg) {
+               case CRYPTO_AES_GCM_16:
+               case CRYPTO_AES_GMAC:
+                       swe = sw;
+                       crde = crd;
+                       exf = swe->sw_exf;
+                       ivlen = exf->ivsize;
+                       break;
+               case CRYPTO_AES_128_GMAC:
+               case CRYPTO_AES_192_GMAC:
+               case CRYPTO_AES_256_GMAC:
+                       swa = sw;
+                       crda = crd;
+                       axf = swa->sw_axf;
+                       if (swa->sw_ictx == 0)
+                               return (EINVAL);
+                       bcopy(swa->sw_ictx, &ctx, axf->ctxsize);
+                       blksz = axf->blocksize;
+                       break;
+               default:
+                       return (EINVAL);
+               }
+       }
+       if (crde == NULL || crda == NULL)
+               return (EINVAL);
+
+       if (crp->crp_flags & CRYPTO_F_IMBUF) {
+               outtype = CRYPTO_BUF_MBUF;
+               m = (struct mbuf *)buf;
+       } else {
+               outtype = CRYPTO_BUF_IOV;
+               uio = (struct uio *)buf;
+       }
+
+       /* Initialize the IV */
+       if (crde->crd_flags & CRD_F_ENCRYPT) {
+               /* IV explicitly provided ? */
+               if (crde->crd_flags & CRD_F_IV_EXPLICIT)
+                       bcopy(crde->crd_iv, iv, ivlen);
+               else
+                       arc4random_buf(iv, ivlen);
+
+               /* Do we need to write the IV */
+               if (!(crde->crd_flags & CRD_F_IV_PRESENT)) {
+                       COPYBACK(outtype, buf, crde->crd_inject, ivlen, iv);
+               }
+
+       } else {        /* Decryption */
+                       /* IV explicitly provided ? */
+               if (crde->crd_flags & CRD_F_IV_EXPLICIT)
+                       bcopy(crde->crd_iv, iv, ivlen);
+               else {
+                       /* Get IV off buf */
+                       COPYDATA(outtype, buf, crde->crd_inject, ivlen, iv);
+               }
+       }
+
+       /* Supply MAC with IV */
+       if (axf->Reinit)
+               axf->Reinit(&ctx, iv, ivlen);
+
+       /* Supply MAC with AAD */
+       for (i = 0; i < crda->crd_len; i += blksz) {
+               len = MIN(crda->crd_len - i, blksz);
+               COPYDATA(outtype, buf, crda->crd_skip + i, len, blk);
+               axf->Update(&ctx, blk, len);
+       }
+
+       if (exf->reinit)
+               exf->reinit(swe->sw_kschedule, iv);
+
+       /* Do encryption/decryption with MAC */
+       for (i = 0; i < crde->crd_len; i += blksz) {
+               len = MIN(crde->crd_len - i, blksz);
+               if (len < blksz)
+                       bzero(blk, blksz);
+               COPYDATA(outtype, buf, crde->crd_skip + i, len, blk);
+               if (crde->crd_flags & CRD_F_ENCRYPT) {
+                       exf->encrypt(swe->sw_kschedule, blk);
+                       axf->Update(&ctx, blk, len);
+               } else {
+                       axf->Update(&ctx, blk, len);
+                       exf->decrypt(swe->sw_kschedule, blk);
+               }
+               COPYBACK(outtype, buf, crde->crd_skip + i, len, blk);
+       }
+
+       /* Do any required special finalization */
+       switch (crda->crd_alg) {
+               case CRYPTO_AES_128_GMAC:
+               case CRYPTO_AES_192_GMAC:
+               case CRYPTO_AES_256_GMAC:
+                       /* length block */
+                       bzero(blk, blksz);
+                       blkp = (uint32_t *)blk + 1;
+                       *blkp = htobe32(crda->crd_len * 8);
+                       blkp = (uint32_t *)blk + 3;
+                       *blkp = htobe32(crde->crd_len * 8);
+                       axf->Update(&ctx, blk, blksz);
+                       break;
+       }
+
+       /* Finalize MAC */
+       axf->Final(aalg, &ctx);
+
+       /* Inject the authentication data */
+       if (outtype == CRYPTO_BUF_MBUF)
+               COPYBACK(outtype, buf, crda->crd_inject, axf->authsize, aalg);
+       else
+               bcopy(aalg, crp->crp_mac, axf->authsize);
+
+       return (0);
+}
+
+/*
  * Apply a compression/decompression algorithm
  */
 int
@@ -653,6 +794,13 @@ swcr_newsession(u_int32_t *sid, struct c
                case CRYPTO_AES_XTS:
                        txf = &enc_xform_aes_xts;
                        goto enccommon;
+               case CRYPTO_AES_GCM_16:
+                       txf = &enc_xform_aes_gcm;
+                       goto enccommon;
+               case CRYPTO_AES_GMAC:
+                       txf = &enc_xform_aes_gmac;
+                       (*swd)->sw_exf = txf;
+                       break;
                case CRYPTO_NULL:
                        txf = &enc_xform_null;
                        goto enccommon;
@@ -769,6 +917,29 @@ swcr_newsession(u_int32_t *sid, struct c
                        (*swd)->sw_axf = axf;
                        break;
 
+               case CRYPTO_AES_128_GMAC:
+                       axf = &auth_hash_gmac_aes_128;
+                       goto auth4common;
+
+               case CRYPTO_AES_192_GMAC:
+                       axf = &auth_hash_gmac_aes_192;
+                       goto auth4common;
+
+               case CRYPTO_AES_256_GMAC:
+                       axf = &auth_hash_gmac_aes_256;
+               auth4common:
+                       (*swd)->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
+                           M_NOWAIT);
+                       if ((*swd)->sw_ictx == NULL) {
+                               swcr_freesession(i);
+                               return ENOBUFS;
+                       }
+                       axf->Init((*swd)->sw_ictx);
+                       axf->Setkey((*swd)->sw_ictx, cri->cri_key,
+                           cri->cri_klen / 8);
+                       (*swd)->sw_axf = axf;
+                       break;
+
                case CRYPTO_DEFLATE_COMP:
                        cxf = &comp_algo_deflate;
                        (*swd)->sw_cxf = cxf;
@@ -855,6 +1026,9 @@ swcr_freesession(u_int64_t tid)
                        }
                        break;
 
+               case CRYPTO_AES_128_GMAC:
+               case CRYPTO_AES_192_GMAC:
+               case CRYPTO_AES_256_GMAC:
                case CRYPTO_MD5:
                case CRYPTO_SHA1:
                        axf = swd->sw_axf;
@@ -953,6 +1127,13 @@ swcr_process(struct cryptop *crp)
                                goto done;
                        break;
 
+               case CRYPTO_AES_GCM_16:
+               case CRYPTO_AES_128_GMAC:
+               case CRYPTO_AES_192_GMAC:
+               case CRYPTO_AES_256_GMAC:
+                       crp->crp_etype = swcr_combined(crp);
+                       goto done;
+
                case CRYPTO_DEFLATE_COMP:
                        if ((crp->crp_etype = swcr_compdec(crd, sw,
                            crp->crp_buf, type)) != 0)
@@ -1006,11 +1187,15 @@ swcr_init(void)
        algs[CRYPTO_RIJNDAEL128_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_AES_CTR] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_AES_XTS] = CRYPTO_ALG_FLAG_SUPPORTED;
+       algs[CRYPTO_AES_GCM_16] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_DEFLATE_COMP] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_NULL] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_SHA2_256_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_SHA2_384_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
        algs[CRYPTO_SHA2_512_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
+       algs[CRYPTO_AES_128_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
+       algs[CRYPTO_AES_192_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
+       algs[CRYPTO_AES_256_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
 
        crypto_register(swcr_id, algs, swcr_newsession,
            swcr_freesession, swcr_process);
Index: crypto/cryptosoft.h
===================================================================
RCS file: /home/cvs/src/sys/crypto/cryptosoft.h,v
retrieving revision 1.12
diff -u -p -r1.12 cryptosoft.h
--- crypto/cryptosoft.h 10 Jan 2010 12:43:07 -0000      1.12
+++ crypto/cryptosoft.h 27 Jul 2010 13:34:13 -0000
@@ -63,6 +63,7 @@ extern const u_int8_t hmac_opad_buffer[H
 int    swcr_encdec(struct cryptodesc *, struct swcr_data *, caddr_t, int);
 int    swcr_authcompute(struct cryptop *, struct cryptodesc *, struct 
swcr_data *,
        caddr_t, int);
+int    swcr_combined(struct cryptop *);
 int    swcr_compdec(struct cryptodesc *, struct swcr_data *, caddr_t, int);
 int    swcr_process(struct cryptop *);
 int    swcr_newsession(u_int32_t *, struct cryptoini *);
Index: crypto/gmac.c
===================================================================
RCS file: crypto/gmac.c
diff -N crypto/gmac.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ crypto/gmac.c       20 Aug 2010 15:40:29 -0000
@@ -0,0 +1,158 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2010 Mike Belopuhov <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This code implements the Message Authentication part of the
+ * Galois/Counter Mode (as being described in the RFC 4543) using
+ * the AES cipher.  FIPS SP 800-38D describes the algorithm details.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <crypto/rijndael.h>
+#include <crypto/gmac.h>
+
+void   ghash_gfmul(uint32_t *, uint32_t *, uint32_t *);
+void   ghash_update(GHASH_CTX *, uint8_t *, size_t);
+
+/* Computes a block multiplication in the GF(2^128) */
+void
+ghash_gfmul(uint32_t *X, uint32_t *Y, uint32_t *product)
+{
+       uint32_t        v[4];
+       uint32_t        z[4] = { 0, 0, 0, 0};
+       uint8_t         *x = (uint8_t *)X;
+       uint32_t        mul;
+       int             i;
+
+       v[0] = betoh32(Y[0]);
+       v[1] = betoh32(Y[1]);
+       v[2] = betoh32(Y[2]);
+       v[3] = betoh32(Y[3]);
+
+       for (i = 0; i < GMAC_BLOCK_LEN * 8; i++) {
+               /* update Z */
+               if (x[i >> 3] & (1 << (~i & 7))) {
+                       z[0] ^= v[0];
+                       z[1] ^= v[1];
+                       z[2] ^= v[2];
+                       z[3] ^= v[3];
+               } /* else: we preserve old values */
+
+               /* update V */
+               mul = v[3] & 1;
+               v[3] = (v[2] << 31) | (v[3] >> 1);
+               v[2] = (v[1] << 31) | (v[2] >> 1);
+               v[1] = (v[0] << 31) | (v[1] >> 1);
+               v[0] = (v[0] >> 1) ^ (0xe1000000 * mul);
+       }
+
+       product[0] = htobe32(z[0]);
+       product[1] = htobe32(z[1]);
+       product[2] = htobe32(z[2]);
+       product[3] = htobe32(z[3]);
+}
+
+void
+ghash_update(GHASH_CTX *ctx, uint8_t *X, size_t len)
+{
+       uint32_t        *x = (uint32_t *)X;
+       uint32_t        *s = (uint32_t *)ctx->S;
+       uint32_t        *y = (uint32_t *)ctx->Z;
+       int             i;
+
+       for (i = 0; i < len / GMAC_BLOCK_LEN; i++) {
+               s[0] = y[0] ^ x[0];
+               s[1] = y[1] ^ x[1];
+               s[2] = y[2] ^ x[2];
+               s[3] = y[3] ^ x[3];
+
+               ghash_gfmul((uint32_t *)ctx->S, (uint32_t *)ctx->H,
+                   (uint32_t *)ctx->S);
+
+               y = s;
+               x += 4;
+       }
+
+       bcopy(ctx->S, ctx->Z, GMAC_BLOCK_LEN);
+}
+
+/* defines from xform.c */
+#define AES_GCM_SALTSIZE       4
+#define AES_GCM_IVSIZE         8
+
+void
+AES_GMAC_Init(AES_GMAC_CTX *ctx)
+{
+       bzero(ctx->ghash.H, GMAC_BLOCK_LEN);
+       bzero(ctx->ghash.S, GMAC_BLOCK_LEN);
+       bzero(ctx->ghash.Z, GMAC_BLOCK_LEN);
+       bzero(ctx->J, GMAC_BLOCK_LEN);
+}
+
+void
+AES_GMAC_Setkey(AES_GMAC_CTX *ctx, const uint8_t *key, uint16_t klen)
+{
+       ctx->rounds = rijndaelKeySetupEnc(ctx->K, (u_char *)key,
+           (klen - AES_GCM_SALTSIZE) * 8);
+       /* copy out salt to the counter block */
+       bcopy(key + klen - AES_GCM_SALTSIZE, ctx->J, AES_GCM_SALTSIZE);
+       /* prepare a hash subkey */
+       rijndaelEncrypt(ctx->K, ctx->rounds, ctx->ghash.H, ctx->ghash.H);
+}
+
+void
+AES_GMAC_Reinit(AES_GMAC_CTX *ctx, const uint8_t *iv, uint16_t ivlen)
+{
+       /* copy out IV to the counter block */
+       bcopy(iv, ctx->J + AES_GCM_SALTSIZE, ivlen);
+}
+
+int
+AES_GMAC_Update(AES_GMAC_CTX *ctx, const uint8_t *data, uint16_t len)
+{
+       uint32_t        blk[4] = { 0, 0, 0, 0 };
+       int             plen;
+
+       if (len > 0) {
+               plen = len % GMAC_BLOCK_LEN;
+               if (len >= GMAC_BLOCK_LEN)
+                       ghash_update(&ctx->ghash, (uint8_t *)data, len - plen);
+               if (plen) {
+                       bcopy((uint8_t *)data + (len - plen), (uint8_t *)blk,
+                           plen);
+                       ghash_update(&ctx->ghash, (uint8_t *)blk,
+                           GMAC_BLOCK_LEN);
+               }
+       }
+       return (0);
+}
+
+void
+AES_GMAC_Final(uint8_t digest[GMAC_DIGEST_LEN], AES_GMAC_CTX *ctx)
+{
+       uint8_t         keystream[GMAC_BLOCK_LEN];
+       int             i;
+
+       /* do one round of GCTR */
+       ctx->J[GMAC_BLOCK_LEN - 1] = 1;
+       rijndaelEncrypt(ctx->K, ctx->rounds, ctx->J, keystream);
+       for (i = 0; i < GMAC_DIGEST_LEN; i++)
+               digest[i] = ctx->ghash.S[i] ^ keystream[i];
+}
Index: crypto/gmac.h
===================================================================
RCS file: crypto/gmac.h
diff -N crypto/gmac.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ crypto/gmac.h       20 Aug 2010 13:04:59 -0000
@@ -0,0 +1,50 @@
+/*     $OpenBSD$       */
+
+/*
+ * Copyright (c) 2010 Mike Belopuhov <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _GMAC_H_
+#define _GMAC_H_
+
+#include <crypto/rijndael.h>
+
+#define GMAC_BLOCK_LEN         16
+#define GMAC_DIGEST_LEN                16
+
+typedef struct _GHASH_CTX {
+       uint8_t         H[GMAC_BLOCK_LEN];              /* hash subkey */
+       uint8_t         S[GMAC_BLOCK_LEN];              /* state */
+       uint8_t         Z[GMAC_BLOCK_LEN];              /* initial state */
+} GHASH_CTX;
+
+typedef struct _AES_GMAC_CTX {
+       GHASH_CTX       ghash;
+       uint32_t        K[4*(AES_MAXROUNDS + 1)];
+       uint8_t         J[GMAC_BLOCK_LEN];              /* counter block */
+       int             rounds;
+} AES_GMAC_CTX;
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+void   AES_GMAC_Init(AES_GMAC_CTX *);
+void   AES_GMAC_Setkey(AES_GMAC_CTX *, const uint8_t *, uint16_t);
+void   AES_GMAC_Reinit(AES_GMAC_CTX *, const uint8_t *, uint16_t);
+int    AES_GMAC_Update(AES_GMAC_CTX *, const uint8_t *, uint16_t);
+void   AES_GMAC_Final(uint8_t [GMAC_DIGEST_LEN], AES_GMAC_CTX *);
+__END_DECLS
+
+#endif /* _GMAC_H_ */
Index: crypto/xform.c
===================================================================
RCS file: /home/cvs/src/sys/crypto/xform.c,v
retrieving revision 1.38
diff -u -p -r1.38 xform.c
--- crypto/xform.c      20 Apr 2010 22:05:41 -0000      1.38
+++ crypto/xform.c      20 Aug 2010 17:28:54 -0000
@@ -60,6 +60,7 @@
 #include <crypto/cryptodev.h>
 #include <crypto/xform.h>
 #include <crypto/deflate.h>
+#include <crypto/gmac.h>
 
 extern void des_ecb3_encrypt(caddr_t, caddr_t, caddr_t, caddr_t, caddr_t, int);
 extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int);
@@ -107,6 +108,7 @@ void null_zerokey(u_int8_t **);
 
 void aes_ctr_reinit(caddr_t, u_int8_t *);
 void aes_xts_reinit(caddr_t, u_int8_t *);
+void aes_gcm_reinit(caddr_t, u_int8_t *);
 
 int MD5Update_int(void *, const u_int8_t *, u_int16_t);
 int SHA1Update_int(void *, const u_int8_t *, u_int16_t);
@@ -194,6 +196,26 @@ struct enc_xform enc_xform_aes_ctr = {
        aes_ctr_reinit
 };
 
+struct enc_xform enc_xform_aes_gcm = {
+       CRYPTO_AES_GCM_16, "AES-GCM",
+       1, 8, 16+4, 32+4,
+       aes_ctr_crypt,
+       aes_ctr_crypt,
+       aes_ctr_setkey,
+       aes_ctr_zerokey,
+       aes_gcm_reinit
+};
+
+struct enc_xform enc_xform_aes_gmac = {
+       CRYPTO_AES_GMAC, "AES-GMAC",
+       1, 8, 16+4, 32+4,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+};
+
 struct enc_xform enc_xform_aes_xts = {
        CRYPTO_AES_XTS, "AES-XTS",
        16, 8, 32, 64,
@@ -228,70 +250,110 @@ struct enc_xform enc_xform_null = {
 struct auth_hash auth_hash_hmac_md5_96 = {
        CRYPTO_MD5_HMAC, "HMAC-MD5",
        16, 16, 12, sizeof(MD5_CTX), HMAC_MD5_BLOCK_LEN,
-       (void (*) (void *)) MD5Init, MD5Update_int,
+       (void (*) (void *)) MD5Init, NULL, NULL,
+       MD5Update_int,
        (void (*) (u_int8_t *, void *)) MD5Final
 };
 
 struct auth_hash auth_hash_hmac_sha1_96 = {
        CRYPTO_SHA1_HMAC, "HMAC-SHA1",
        20, 20, 12, sizeof(SHA1_CTX), HMAC_SHA1_BLOCK_LEN,
-       (void (*) (void *)) SHA1Init, SHA1Update_int,
+       (void (*) (void *)) SHA1Init, NULL, NULL,
+       SHA1Update_int,
        (void (*) (u_int8_t *, void *)) SHA1Final
 };
 
 struct auth_hash auth_hash_hmac_ripemd_160_96 = {
        CRYPTO_RIPEMD160_HMAC, "HMAC-RIPEMD-160",
        20, 20, 12, sizeof(RMD160_CTX), HMAC_RIPEMD160_BLOCK_LEN,
-       (void (*)(void *)) RMD160Init, RMD160Update_int,
+       (void (*)(void *)) RMD160Init, NULL, NULL,
+       RMD160Update_int,
        (void (*)(u_int8_t *, void *)) RMD160Final
 };
 
 struct auth_hash auth_hash_hmac_sha2_256_128 = {
        CRYPTO_SHA2_256_HMAC, "HMAC-SHA2-256",
        32, 32, 16, sizeof(SHA2_CTX), HMAC_SHA2_256_BLOCK_LEN,
-       (void (*)(void *)) SHA256Init, SHA256Update_int,
+       (void (*)(void *)) SHA256Init, NULL, NULL,
+       SHA256Update_int,
        (void (*)(u_int8_t *, void *)) SHA256Final
 };
 
 struct auth_hash auth_hash_hmac_sha2_384_192 = {
        CRYPTO_SHA2_384_HMAC, "HMAC-SHA2-384",
        48, 48, 24, sizeof(SHA2_CTX), HMAC_SHA2_384_BLOCK_LEN,
-       (void (*)(void *)) SHA384Init, SHA384Update_int,
+       (void (*)(void *)) SHA384Init, NULL, NULL,
+       SHA384Update_int,
        (void (*)(u_int8_t *, void *)) SHA384Final
 };
 
 struct auth_hash auth_hash_hmac_sha2_512_256 = {
        CRYPTO_SHA2_512_HMAC, "HMAC-SHA2-512",
        64, 64, 32, sizeof(SHA2_CTX), HMAC_SHA2_512_BLOCK_LEN,
-       (void (*)(void *)) SHA512Init, SHA512Update_int,
+       (void (*)(void *)) SHA512Init, NULL, NULL,
+       SHA512Update_int,
        (void (*)(u_int8_t *, void *)) SHA512Final
 };
 
+struct auth_hash auth_hash_gmac_aes_128 = {
+       CRYPTO_AES_128_GMAC, "GMAC-AES-128",
+       16+4, 16, 16, sizeof(AES_GMAC_CTX), GMAC_BLOCK_LEN,
+       (void (*)(void *)) AES_GMAC_Init,
+       (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey,
+       (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit,
+       (int  (*)(void *, const u_int8_t *, u_int16_t))AES_GMAC_Update,
+       (void (*)(u_int8_t *, void *)) AES_GMAC_Final
+};
+
+struct auth_hash auth_hash_gmac_aes_192 = {
+       CRYPTO_AES_192_GMAC, "GMAC-AES-192",
+       24+4, 16, 16, sizeof(AES_GMAC_CTX), GMAC_BLOCK_LEN,
+       (void (*)(void *)) AES_GMAC_Init,
+       (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey,
+       (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit,
+       (int  (*)(void *, const u_int8_t *, u_int16_t))AES_GMAC_Update,
+       (void (*)(u_int8_t *, void *)) AES_GMAC_Final
+};
+
+struct auth_hash auth_hash_gmac_aes_256 = {
+       CRYPTO_AES_256_GMAC, "GMAC-AES-256",
+       32+4, 16, 16, sizeof(AES_GMAC_CTX), GMAC_BLOCK_LEN,
+       (void (*)(void *)) AES_GMAC_Init,
+       (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Setkey,
+       (void (*)(void *, const u_int8_t *, u_int16_t)) AES_GMAC_Reinit,
+       (int  (*)(void *, const u_int8_t *, u_int16_t))AES_GMAC_Update,
+       (void (*)(u_int8_t *, void *)) AES_GMAC_Final
+};
+
 struct auth_hash auth_hash_key_md5 = {
        CRYPTO_MD5_KPDK, "Keyed MD5",
        0, 16, 16, sizeof(MD5_CTX), 0,
-       (void (*)(void *)) MD5Init, MD5Update_int,
+       (void (*)(void *)) MD5Init, NULL, NULL,
+       MD5Update_int,
        (void (*)(u_int8_t *, void *)) MD5Final
 };
 
 struct auth_hash auth_hash_key_sha1 = {
        CRYPTO_SHA1_KPDK, "Keyed SHA1",
        0, 20, 20, sizeof(SHA1_CTX), 0,
-       (void (*)(void *)) SHA1Init, SHA1Update_int,
+       (void (*)(void *)) SHA1Init, NULL, NULL,
+       SHA1Update_int,
        (void (*)(u_int8_t *, void *)) SHA1Final
 };
 
 struct auth_hash auth_hash_md5 = {
        CRYPTO_MD5, "MD5",
        0, 16, 16, sizeof(MD5_CTX), 0,
-       (void (*) (void *)) MD5Init, MD5Update_int,
+       (void (*) (void *)) MD5Init, NULL, NULL,
+       MD5Update_int,
        (void (*) (u_int8_t *, void *)) MD5Final
 };
 
 struct auth_hash auth_hash_sha1 = {
        CRYPTO_SHA1, "SHA1",
        0, 20, 20, sizeof(SHA1_CTX), 0,
-       (void (*)(void *)) SHA1Init, SHA1Update_int,
+       (void (*)(void *)) SHA1Init, NULL, NULL,
+       SHA1Update_int,
        (void (*)(u_int8_t *, void *)) SHA1Final
 };
 
@@ -552,6 +614,19 @@ aes_ctr_reinit(caddr_t key, u_int8_t *iv
 }
 
 void
+aes_gcm_reinit(caddr_t key, u_int8_t *iv)
+{
+       struct aes_ctr_ctx *ctx;
+
+       ctx = (struct aes_ctr_ctx *)key;
+       bcopy(iv, ctx->ac_block + AESCTR_NONCESIZE, AESCTR_IVSIZE);
+
+       /* reset counter */
+       bzero(ctx->ac_block + AESCTR_NONCESIZE + AESCTR_IVSIZE, 4);
+       ctx->ac_block[AESCTR_BLOCKSIZE - 1] = 1; /* GCM starts with 1 */
+}
+
+void
 aes_ctr_crypt(caddr_t key, u_int8_t *data)
 {
        struct aes_ctr_ctx *ctx;
@@ -743,6 +818,7 @@ SHA512Update_int(void *ctx, const u_int8
        SHA512Update(ctx, buf, len);
        return 0;
 }
+
 
 /*
  * And compression
Index: crypto/xform.h
===================================================================
RCS file: /home/cvs/src/sys/crypto/xform.h,v
retrieving revision 1.20
diff -u -p -r1.20 xform.h
--- crypto/xform.h      10 Jan 2010 12:43:07 -0000      1.20
+++ crypto/xform.h      3 Aug 2010 16:24:48 -0000
@@ -28,6 +28,7 @@
 #include <crypto/sha1.h>
 #include <crypto/rmd160.h>
 #include <crypto/sha2.h>
+#include <crypto/gmac.h>
 
 /* Declarations */
 struct auth_hash {
@@ -39,6 +40,8 @@ struct auth_hash {
        u_int16_t ctxsize;
        u_int16_t blocksize;
        void (*Init) (void *);
+       void (*Setkey) (void *, const u_int8_t *, u_int16_t);
+       void (*Reinit) (void *, const u_int8_t *, u_int16_t);
        int  (*Update) (void *, const u_int8_t *, u_int16_t);
        void (*Final) (u_int8_t *, void *);
 };
@@ -68,6 +71,7 @@ union authctx {
        SHA1_CTX sha1ctx;
        RMD160_CTX rmd160ctx;
        SHA2_CTX sha2_ctx;
+       AES_GMAC_CTX aes_gmac_ctx;
 };
 
 extern struct enc_xform enc_xform_des;
@@ -77,6 +81,8 @@ extern struct enc_xform enc_xform_cast5;
 extern struct enc_xform enc_xform_skipjack;
 extern struct enc_xform enc_xform_rijndael128;
 extern struct enc_xform enc_xform_aes_ctr;
+extern struct enc_xform enc_xform_aes_gcm;
+extern struct enc_xform enc_xform_aes_gmac;
 extern struct enc_xform enc_xform_aes_xts;
 extern struct enc_xform enc_xform_arc4;
 extern struct enc_xform enc_xform_null;
@@ -91,6 +97,9 @@ extern struct auth_hash auth_hash_hmac_r
 extern struct auth_hash auth_hash_hmac_sha2_256_128;
 extern struct auth_hash auth_hash_hmac_sha2_384_192;
 extern struct auth_hash auth_hash_hmac_sha2_512_256;
+extern struct auth_hash auth_hash_gmac_aes_128;
+extern struct auth_hash auth_hash_gmac_aes_192;
+extern struct auth_hash auth_hash_gmac_aes_256;
 
 extern struct comp_algo comp_algo_deflate;
 extern struct comp_algo comp_algo_lzs;

Reply via email to