Blame nss/lib/freebl/gcm.c

Packit 40b132
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit 40b132
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit 40b132
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit 40b132
Packit 40b132
#ifdef FREEBL_NO_DEPEND
Packit 40b132
#include "stubs.h"
Packit 40b132
#endif
Packit 40b132
#include "blapii.h"
Packit 40b132
#include "blapit.h"
Packit 40b132
#include "gcm.h"
Packit 40b132
#include "ctr.h"
Packit 40b132
#include "secerr.h"
Packit 40b132
#include "prtypes.h"
Packit 40b132
#include "pkcs11t.h"
Packit 40b132
Packit 40b132
#include <limits.h>
Packit 40b132
Packit 40b132
/**************************************************************************
Packit 40b132
 *          First implement the Galois hash function of GCM (gcmHash)     *
Packit 40b132
 **************************************************************************/
Packit 40b132
#define GCM_HASH_LEN_LEN 8 /* gcm hash defines lengths to be 64 bits */
Packit 40b132
Packit 40b132
typedef struct gcmHashContextStr gcmHashContext;
Packit 40b132
Packit 40b132
static SECStatus gcmHash_InitContext(gcmHashContext *hash,
Packit 40b132
				     const unsigned char *H,
Packit 40b132
				     unsigned int blocksize);
Packit 40b132
static void gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit);
Packit 40b132
static SECStatus gcmHash_Update(gcmHashContext *ghash,
Packit 40b132
				const unsigned char *buf, unsigned int len,
Packit 40b132
				unsigned int blocksize);
Packit 40b132
static SECStatus gcmHash_Sync(gcmHashContext *ghash, unsigned int blocksize);
Packit 40b132
static SECStatus gcmHash_Final(gcmHashContext *gcm, unsigned char *outbuf,
Packit 40b132
			       unsigned int *outlen, unsigned int maxout,
Packit 40b132
			       unsigned int blocksize);
Packit 40b132
static SECStatus gcmHash_Reset(gcmHashContext *ghash,
Packit 40b132
			       const unsigned char *inbuf,
Packit 40b132
			       unsigned int inbufLen, unsigned int blocksize);
Packit 40b132
Packit 40b132
/* compile time defines to select how the GF2 multiply is calculated.
Packit 40b132
 * There are currently 2 algorithms implemented here: MPI and ALGORITHM_1.
Packit 40b132
 *
Packit 40b132
 * MPI uses the GF2m implemented in mpi to support GF2 ECC.
Packit 40b132
 * ALGORITHM_1 is the Algorithm 1 in both NIST SP 800-38D and
Packit 40b132
 * "The Galois/Counter Mode of Operation (GCM)", McGrew & Viega.
Packit 40b132
 */
Packit 40b132
#if !defined(GCM_USE_ALGORITHM_1) && !defined(GCM_USE_MPI)
Packit 40b132
#define GCM_USE_MPI 1 /* MPI is about 5x faster with the
Packit 40b132
		       * same or less complexity. It's possible to use
Packit 40b132
		       * tables to speed things up even more */
Packit 40b132
#endif
Packit 40b132
Packit 40b132
/* GCM defines the bit string to be LSB first, which is exactly
Packit 40b132
 * opposite everyone else, including hardware. build array
Packit 40b132
 * to reverse everything. */
Packit 40b132
static const unsigned char gcm_byte_rev[256] = {
Packit 40b132
    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
Packit 40b132
    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
Packit 40b132
    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
Packit 40b132
    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
Packit 40b132
    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
Packit 40b132
    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
Packit 40b132
    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
Packit 40b132
    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
Packit 40b132
    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
Packit 40b132
    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
Packit 40b132
    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
Packit 40b132
    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
Packit 40b132
    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
Packit 40b132
    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
Packit 40b132
    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
Packit 40b132
    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
Packit 40b132
    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
Packit 40b132
    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
Packit 40b132
    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
Packit 40b132
    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
Packit 40b132
    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
Packit 40b132
    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
Packit 40b132
    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
Packit 40b132
    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
Packit 40b132
    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
Packit 40b132
    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
Packit 40b132
    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
Packit 40b132
    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
Packit 40b132
    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
Packit 40b132
    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
Packit 40b132
    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
Packit 40b132
    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
Packit 40b132
};
Packit 40b132
Packit 40b132
Packit 40b132
#ifdef GCM_TRACE
Packit 40b132
#include <stdio.h>
Packit 40b132
Packit 40b132
#define GCM_TRACE_X(ghash,label) { \
Packit 40b132
	unsigned char _X[MAX_BLOCK_SIZE]; int i; \
Packit 40b132
	gcm_getX(ghash, _X, blocksize); \
Packit 40b132
	printf(label,(ghash)->m); \
Packit 40b132
	for (i=0; i < blocksize; i++) printf("%02x",_X[i]); \
Packit 40b132
	printf("\n"); }
Packit 40b132
#define GCM_TRACE_BLOCK(label,buf,blocksize) {\
Packit 40b132
	printf(label); \
Packit 40b132
	for (i=0; i < blocksize; i++) printf("%02x",buf[i]); \
Packit 40b132
	printf("\n"); }
Packit 40b132
#else
Packit 40b132
#define GCM_TRACE_X(ghash,label)
Packit 40b132
#define GCM_TRACE_BLOCK(label,buf,blocksize)
Packit 40b132
#endif
Packit 40b132
Packit 40b132
#ifdef GCM_USE_MPI
Packit 40b132
Packit 40b132
#ifdef GCM_USE_ALGORITHM_1
Packit 40b132
#error "Only define one of GCM_USE_MPI, GCM_USE_ALGORITHM_1"
Packit 40b132
#endif
Packit 40b132
/* use the MPI functions to calculate Xn = (Xn-1^C_i)*H mod poly */
Packit 40b132
#include "mpi.h"
Packit 40b132
#include "secmpi.h"
Packit 40b132
#include "mplogic.h"
Packit 40b132
#include "mp_gf2m.h"
Packit 40b132
Packit 40b132
/* state needed to handle GCM Hash function */
Packit 40b132
struct gcmHashContextStr {
Packit 40b132
     mp_int H;
Packit 40b132
     mp_int X;
Packit 40b132
     mp_int C_i;
Packit 40b132
     const unsigned int *poly;
Packit 40b132
     unsigned char buffer[MAX_BLOCK_SIZE];
Packit 40b132
     unsigned int bufLen;
Packit 40b132
     int m; /* XXX what is m? */
Packit 40b132
     unsigned char counterBuf[2*GCM_HASH_LEN_LEN];
Packit 40b132
     PRUint64 cLen;
Packit 40b132
};
Packit 40b132
Packit 40b132
/* f = x^128 + x^7 + x^2 + x + 1 */
Packit 40b132
static const unsigned int poly_128[] = { 128, 7, 2, 1, 0 };
Packit 40b132
Packit 40b132
/* sigh, GCM defines the bit strings exactly backwards from everything else */
Packit 40b132
static void
Packit 40b132
gcm_reverse(unsigned char *target, const unsigned char *src,
Packit 40b132
							unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    unsigned int i;
Packit 40b132
    for (i=0; i < blocksize; i++) {
Packit 40b132
	target[blocksize-i-1] = gcm_byte_rev[src[i]];
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Initialize a gcmHashContext */
Packit 40b132
static SECStatus
Packit 40b132
gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H,
Packit 40b132
		    unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    mp_err err = MP_OKAY;
Packit 40b132
    unsigned char H_rev[MAX_BLOCK_SIZE];
Packit 40b132
Packit 40b132
    MP_DIGITS(&ghash->H) = 0;
Packit 40b132
    MP_DIGITS(&ghash->X) = 0;
Packit 40b132
    MP_DIGITS(&ghash->C_i) = 0;
Packit 40b132
    CHECK_MPI_OK( mp_init(&ghash->H) );
Packit 40b132
    CHECK_MPI_OK( mp_init(&ghash->X) );
Packit 40b132
    CHECK_MPI_OK( mp_init(&ghash->C_i) );
Packit 40b132
Packit 40b132
    mp_zero(&ghash->X);
Packit 40b132
    gcm_reverse(H_rev, H, blocksize);
Packit 40b132
    CHECK_MPI_OK( mp_read_unsigned_octets(&ghash->H, H_rev, blocksize) );
Packit 40b132
Packit 40b132
    /* set the irreducible polynomial. Each blocksize has its own polynomial.
Packit 40b132
     * for now only blocksize 16 (=128 bits) is defined */
Packit 40b132
    switch (blocksize) {
Packit 40b132
    case 16: /* 128 bits */
Packit 40b132
	ghash->poly = poly_128;
Packit 40b132
	break;
Packit 40b132
    default:
Packit 40b132
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
Packit 40b132
	goto cleanup;
Packit 40b132
    }
Packit 40b132
    ghash->cLen = 0;
Packit 40b132
    ghash->bufLen = 0;
Packit 40b132
    ghash->m = 0;
Packit 40b132
    PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf));
Packit 40b132
    return SECSuccess;
Packit 40b132
cleanup:
Packit 40b132
    gcmHash_DestroyContext(ghash, PR_FALSE);
Packit 40b132
    return SECFailure;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Destroy a HashContext (Note we zero the digits so this function
Packit 40b132
 * is idempotent if called with freeit == PR_FALSE */
Packit 40b132
static void
Packit 40b132
gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit)
Packit 40b132
{
Packit 40b132
    mp_clear(&ghash->H);
Packit 40b132
    mp_clear(&ghash->X);
Packit 40b132
    mp_clear(&ghash->C_i);
Packit 40b132
    MP_DIGITS(&ghash->H) = 0;
Packit 40b132
    MP_DIGITS(&ghash->X) = 0;
Packit 40b132
    MP_DIGITS(&ghash->C_i) = 0;
Packit 40b132
    if (freeit) {
Packit 40b132
	PORT_Free(ghash);
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
gcm_getX(gcmHashContext *ghash, unsigned char *T, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    int len;
Packit 40b132
    mp_err err;
Packit 40b132
    unsigned char tmp_buf[MAX_BLOCK_SIZE];
Packit 40b132
    unsigned char *X;
Packit 40b132
Packit 40b132
    len = mp_unsigned_octet_size(&ghash->X);
Packit 40b132
    if (len <= 0) {
Packit 40b132
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    X = tmp_buf;
Packit 40b132
    PORT_Assert((unsigned int)len <= blocksize);
Packit 40b132
    if ((unsigned int)len > blocksize) {
Packit 40b132
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    /* zero pad the result */
Packit 40b132
    if (len != blocksize) {
Packit 40b132
	PORT_Memset(X,0,blocksize-len);
Packit 40b132
	X += blocksize-len;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    err = mp_to_unsigned_octets(&ghash->X, X, len);
Packit 40b132
    if (err < 0) {
Packit 40b132
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    gcm_reverse(T, tmp_buf, blocksize);
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
gcm_HashMult(gcmHashContext *ghash, const unsigned char *buf,
Packit 40b132
		unsigned int count, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    SECStatus rv = SECFailure;
Packit 40b132
    mp_err err = MP_OKAY;
Packit 40b132
    unsigned char tmp_buf[MAX_BLOCK_SIZE];
Packit 40b132
    unsigned int i;
Packit 40b132
Packit 40b132
    for (i=0; i < count; i++, buf += blocksize) {
Packit 40b132
	ghash->m++;
Packit 40b132
	gcm_reverse(tmp_buf, buf, blocksize);
Packit 40b132
	CHECK_MPI_OK(mp_read_unsigned_octets(&ghash->C_i, tmp_buf, blocksize));
Packit 40b132
	CHECK_MPI_OK(mp_badd(&ghash->X, &ghash->C_i, &ghash->C_i));
Packit 40b132
	/*
Packit 40b132
	 * Looking to speed up GCM, this the the place to do it.
Packit 40b132
	 * There are two areas that can be exploited to speed up this code.
Packit 40b132
	 *
Packit 40b132
	 * 1) H is a constant in this multiply. We can precompute H * (0 - 255)
Packit 40b132
	 * at init time and this becomes an blockize xors of our table lookup.
Packit 40b132
	 *
Packit 40b132
	 * 2) poly is a constant for each blocksize. We can calculate the
Packit 40b132
	 * modulo reduction by a series of adds and shifts.
Packit 40b132
	 *
Packit 40b132
	 * For now we are after functionality, so we will go ahead and use
Packit 40b132
	 * the builtin bmulmod from mpi
Packit 40b132
	 */
Packit 40b132
        CHECK_MPI_OK(mp_bmulmod(&ghash->C_i, &ghash->H,
Packit 40b132
					ghash->poly, &ghash->X));
Packit 40b132
	GCM_TRACE_X(ghash, "X%d = ")
Packit 40b132
    }
Packit 40b132
    rv = SECSuccess;
Packit 40b132
cleanup:
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	MP_TO_SEC_ERROR(err);
Packit 40b132
    }
Packit 40b132
    return rv;
Packit 40b132
}
Packit 40b132
Packit 40b132
static void
Packit 40b132
gcm_zeroX(gcmHashContext *ghash)
Packit 40b132
{
Packit 40b132
    mp_zero(&ghash->X);
Packit 40b132
    ghash->m = 0;
Packit 40b132
}
Packit 40b132
Packit 40b132
#endif
Packit 40b132
Packit 40b132
#ifdef GCM_USE_ALGORITHM_1
Packit 40b132
/* use algorithm 1 of McGrew & Viega "The Galois/Counter Mode of Operation" */
Packit 40b132
Packit 40b132
#define GCM_ARRAY_SIZE (MAX_BLOCK_SIZE/sizeof(unsigned long))
Packit 40b132
Packit 40b132
struct gcmHashContextStr {
Packit 40b132
     unsigned long H[GCM_ARRAY_SIZE];
Packit 40b132
     unsigned long X[GCM_ARRAY_SIZE];
Packit 40b132
     unsigned long R;
Packit 40b132
     unsigned char buffer[MAX_BLOCK_SIZE];
Packit 40b132
     unsigned int bufLen;
Packit 40b132
     int m;
Packit 40b132
     unsigned char counterBuf[2*GCM_HASH_LEN_LEN];
Packit 40b132
     PRUint64 cLen;
Packit 40b132
};
Packit 40b132
Packit 40b132
static void
Packit 40b132
gcm_bytes_to_longs(unsigned long *l, const unsigned char *c, unsigned int len)
Packit 40b132
{
Packit 40b132
    int i,j;
Packit 40b132
    int array_size = len/sizeof(unsigned long);
Packit 40b132
Packit 40b132
    PORT_Assert(len % sizeof(unsigned long) == 0);
Packit 40b132
    for (i=0; i < array_size; i++) {
Packit 40b132
	unsigned long tmp = 0;
Packit 40b132
	int byte_offset = i * sizeof(unsigned long);
Packit 40b132
	for (j=sizeof(unsigned long)-1; j >= 0; j--) {
Packit 40b132
	    tmp = (tmp << PR_BITS_PER_BYTE) | gcm_byte_rev[c[byte_offset+j]];
Packit 40b132
	}
Packit 40b132
	l[i] = tmp;
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
static void
Packit 40b132
gcm_longs_to_bytes(const unsigned long *l, unsigned char *c, unsigned int len)
Packit 40b132
{
Packit 40b132
    int i,j;
Packit 40b132
    int array_size = len/sizeof(unsigned long);
Packit 40b132
Packit 40b132
    PORT_Assert(len % sizeof(unsigned long) == 0);
Packit 40b132
    for (i=0; i < array_size; i++) {
Packit 40b132
	unsigned long tmp = l[i];
Packit 40b132
	int byte_offset = i * sizeof(unsigned long);
Packit 40b132
	for (j=0; j < sizeof(unsigned long); j++) {
Packit 40b132
	    c[byte_offset+j] = gcm_byte_rev[tmp & 0xff];
Packit 40b132
	    tmp = (tmp >> PR_BITS_PER_BYTE);
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/* Initialize a gcmHashContext */
Packit 40b132
static SECStatus
Packit 40b132
gcmHash_InitContext(gcmHashContext *ghash, const unsigned char *H,
Packit 40b132
		    unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    PORT_Memset(ghash->X, 0, sizeof(ghash->X));
Packit 40b132
    PORT_Memset(ghash->H, 0, sizeof(ghash->H));
Packit 40b132
    gcm_bytes_to_longs(ghash->H, H, blocksize);
Packit 40b132
Packit 40b132
    /* set the irreducible polynomial. Each blocksize has its own polynommial
Packit 40b132
     * for now only blocksize 16 (=128 bits) is defined */
Packit 40b132
    switch (blocksize) {
Packit 40b132
    case 16: /* 128 bits */
Packit 40b132
	ghash->R = (unsigned long) 0x87; /* x^7 + x^2 + x +1 */
Packit 40b132
	break;
Packit 40b132
    default:
Packit 40b132
	PORT_SetError(SEC_ERROR_INVALID_ARGS);
Packit 40b132
	goto cleanup;
Packit 40b132
    }
Packit 40b132
    ghash->cLen = 0;
Packit 40b132
    ghash->bufLen = 0;
Packit 40b132
    ghash->m = 0;
Packit 40b132
    PORT_Memset(ghash->counterBuf, 0, sizeof(ghash->counterBuf));
Packit 40b132
    return SECSuccess;
Packit 40b132
cleanup:
Packit 40b132
    return SECFailure;
Packit 40b132
}
Packit 40b132
Packit 40b132
/* Destroy a HashContext (Note we zero the digits so this function
Packit 40b132
 * is idempotent if called with freeit == PR_FALSE */
Packit 40b132
static void
Packit 40b132
gcmHash_DestroyContext(gcmHashContext *ghash, PRBool freeit)
Packit 40b132
{
Packit 40b132
    if (freeit) {
Packit 40b132
	PORT_Free(ghash);
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
static unsigned long
Packit 40b132
gcm_shift_one(unsigned long *t, unsigned int count)
Packit 40b132
{
Packit 40b132
    unsigned long carry = 0;
Packit 40b132
    unsigned long nextcarry = 0;
Packit 40b132
    unsigned int i;
Packit 40b132
    for (i=0; i < count; i++) {
Packit 40b132
	nextcarry = t[i] >> ((sizeof(unsigned long)*PR_BITS_PER_BYTE)-1);
Packit 40b132
	t[i] = (t[i] << 1) | carry;
Packit 40b132
	carry = nextcarry;
Packit 40b132
    }
Packit 40b132
    return carry;
Packit 40b132
}
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
gcm_getX(gcmHashContext *ghash, unsigned char *T, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    gcm_longs_to_bytes(ghash->X, T, blocksize);
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
#define GCM_XOR(t, s, len) \
Packit 40b132
	for (l=0; l < len; l++) t[l] ^= s[l]
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
gcm_HashMult(gcmHashContext *ghash, const unsigned char *buf,
Packit 40b132
		unsigned int count, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    unsigned long C_i[GCM_ARRAY_SIZE];
Packit 40b132
    unsigned int arraysize = blocksize/sizeof(unsigned long);
Packit 40b132
    unsigned int i, j, k, l;
Packit 40b132
Packit 40b132
    for (i=0; i < count; i++, buf += blocksize) {
Packit 40b132
	ghash->m++;
Packit 40b132
	gcm_bytes_to_longs(C_i, buf, blocksize);
Packit 40b132
	GCM_XOR(C_i, ghash->X, arraysize);
Packit 40b132
	/* multiply X = C_i * H */
Packit 40b132
	PORT_Memset(ghash->X, 0, sizeof(ghash->X));
Packit 40b132
	for (j=0; j < arraysize; j++) {
Packit 40b132
	    unsigned long H = ghash->H[j];
Packit 40b132
	    for (k=0; k < sizeof(unsigned long)*PR_BITS_PER_BYTE; k++) {
Packit 40b132
		if (H & 1) {
Packit 40b132
		    GCM_XOR(ghash->X, C_i, arraysize);
Packit 40b132
		}
Packit 40b132
		if (gcm_shift_one(C_i, arraysize)) {
Packit 40b132
		    C_i[0] = C_i[0] ^ ghash->R;
Packit 40b132
		}
Packit 40b132
		H = H >> 1;
Packit 40b132
	    }
Packit 40b132
	}
Packit 40b132
	GCM_TRACE_X(ghash, "X%d = ")
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
static void
Packit 40b132
gcm_zeroX(gcmHashContext *ghash)
Packit 40b132
{
Packit 40b132
    PORT_Memset(ghash->X, 0, sizeof(ghash->X));
Packit 40b132
    ghash->m = 0;
Packit 40b132
}
Packit 40b132
#endif
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * implement GCM GHASH using the freebl GHASH function. The gcm_HashMult
Packit 40b132
 * function always takes blocksize lengths of data. gcmHash_Update will
Packit 40b132
 * format the data properly.
Packit 40b132
 */
Packit 40b132
static SECStatus
Packit 40b132
gcmHash_Update(gcmHashContext *ghash, const unsigned char *buf,
Packit 40b132
	       unsigned int len, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    unsigned int blocks;
Packit 40b132
    SECStatus rv;
Packit 40b132
Packit 40b132
    ghash->cLen += (len*PR_BITS_PER_BYTE);
Packit 40b132
Packit 40b132
    /* first deal with the current buffer of data. Try to fill it out so
Packit 40b132
     * we can hash it */
Packit 40b132
    if (ghash->bufLen) {
Packit 40b132
	unsigned int needed = PR_MIN(len, blocksize - ghash->bufLen);
Packit 40b132
	if (needed != 0) {
Packit 40b132
	    PORT_Memcpy(ghash->buffer+ghash->bufLen, buf, needed);
Packit 40b132
	}
Packit 40b132
	buf += needed;
Packit 40b132
	len -= needed;
Packit 40b132
	ghash->bufLen += needed;
Packit 40b132
	if (len == 0) {
Packit 40b132
	    /* didn't add enough to hash the data, nothing more do do */
Packit 40b132
	    return SECSuccess;
Packit 40b132
	}
Packit 40b132
	PORT_Assert(ghash->bufLen == blocksize);
Packit 40b132
	/* hash the buffer and clear it */
Packit 40b132
	rv = gcm_HashMult(ghash, ghash->buffer, 1, blocksize);
Packit 40b132
	PORT_Memset(ghash->buffer, 0, blocksize);
Packit 40b132
	ghash->bufLen = 0;
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    /* now hash any full blocks remaining in the data stream */
Packit 40b132
    blocks = len/blocksize;
Packit 40b132
    if (blocks) {
Packit 40b132
	rv = gcm_HashMult(ghash, buf, blocks, blocksize);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
	buf += blocks*blocksize;
Packit 40b132
	len -= blocks*blocksize;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* save any remainder in the buffer to be hashed with the next call */
Packit 40b132
    if (len != 0) {
Packit 40b132
	PORT_Memcpy(ghash->buffer, buf, len);
Packit 40b132
	ghash->bufLen = len;
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * write out any partial blocks zero padded through the GHASH engine,
Packit 40b132
 * save the lengths for the final completion of the hash
Packit 40b132
 */
Packit 40b132
static SECStatus
Packit 40b132
gcmHash_Sync(gcmHashContext *ghash, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    int i;
Packit 40b132
    SECStatus rv;
Packit 40b132
Packit 40b132
    /* copy the previous counter to the upper block */
Packit 40b132
    PORT_Memcpy(ghash->counterBuf, &ghash->counterBuf[GCM_HASH_LEN_LEN],
Packit 40b132
							GCM_HASH_LEN_LEN);
Packit 40b132
    /* copy the current counter in the lower block */
Packit 40b132
    for (i=0; i < GCM_HASH_LEN_LEN; i++) {
Packit 40b132
	ghash->counterBuf[GCM_HASH_LEN_LEN+i] =
Packit 40b132
	    (ghash->cLen >> ((GCM_HASH_LEN_LEN-1-i)*PR_BITS_PER_BYTE)) & 0xff;
Packit 40b132
    }
Packit 40b132
    ghash->cLen = 0;
Packit 40b132
Packit 40b132
    /* now zero fill the buffer and hash the last block */
Packit 40b132
    if (ghash->bufLen) {
Packit 40b132
	PORT_Memset(ghash->buffer+ghash->bufLen, 0, blocksize - ghash->bufLen);
Packit 40b132
	rv = gcm_HashMult(ghash, ghash->buffer, 1, blocksize);
Packit 40b132
	PORT_Memset(ghash->buffer, 0, blocksize);
Packit 40b132
	ghash->bufLen = 0;
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * This does the final sync, hashes the lengths, then returns
Packit 40b132
 * "T", the hashed output.
Packit 40b132
 */
Packit 40b132
static SECStatus
Packit 40b132
gcmHash_Final(gcmHashContext *ghash, unsigned char *outbuf,
Packit 40b132
		unsigned int *outlen, unsigned int maxout,
Packit 40b132
		unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    unsigned char T[MAX_BLOCK_SIZE];
Packit 40b132
    SECStatus rv;
Packit 40b132
Packit 40b132
    rv = gcmHash_Sync(ghash, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    rv = gcm_HashMult(ghash, ghash->counterBuf, (GCM_HASH_LEN_LEN*2)/blocksize,
Packit 40b132
								blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    GCM_TRACE_X(ghash, "GHASH(H,A,C) = ")
Packit 40b132
Packit 40b132
    rv = gcm_getX(ghash, T, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (maxout > blocksize) maxout = blocksize;
Packit 40b132
    PORT_Memcpy(outbuf, T, maxout);
Packit 40b132
    *outlen = maxout;
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
SECStatus
Packit 40b132
gcmHash_Reset(gcmHashContext *ghash, const unsigned char *AAD,
Packit 40b132
	      unsigned int AADLen, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    SECStatus rv;
Packit 40b132
Packit 40b132
    ghash->cLen = 0;
Packit 40b132
    PORT_Memset(ghash->counterBuf, 0, GCM_HASH_LEN_LEN*2);
Packit 40b132
    ghash->bufLen = 0;
Packit 40b132
    gcm_zeroX(ghash);
Packit 40b132
Packit 40b132
    /* now kick things off by hashing the Additional Authenticated Data */
Packit 40b132
    if (AADLen != 0) {
Packit 40b132
	rv = gcmHash_Update(ghash, AAD, AADLen, blocksize);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
	rv = gcmHash_Sync(ghash, blocksize);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    return SECFailure;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/**************************************************************************
Packit 40b132
 *           Now implement the GCM using gcmHash and CTR                  *
Packit 40b132
 **************************************************************************/
Packit 40b132
Packit 40b132
/* state to handle the full GCM operation (hash and counter) */
Packit 40b132
struct GCMContextStr {
Packit 40b132
    gcmHashContext ghash_context;
Packit 40b132
    CTRContext ctr_context;
Packit 40b132
    unsigned long tagBits;
Packit 40b132
    unsigned char tagKey[MAX_BLOCK_SIZE];
Packit 40b132
};
Packit 40b132
Packit 40b132
GCMContext *
Packit 40b132
GCM_CreateContext(void *context, freeblCipherFunc cipher,
Packit 40b132
		  const unsigned char *params, unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    GCMContext *gcm = NULL;
Packit 40b132
    gcmHashContext *ghash;
Packit 40b132
    unsigned char H[MAX_BLOCK_SIZE];
Packit 40b132
    unsigned int tmp;
Packit 40b132
    PRBool freeCtr = PR_FALSE;
Packit 40b132
    PRBool freeHash = PR_FALSE;
Packit 40b132
    const CK_GCM_PARAMS *gcmParams = (const CK_GCM_PARAMS *)params;
Packit 40b132
    CK_AES_CTR_PARAMS ctrParams;
Packit 40b132
    SECStatus rv;
Packit 40b132
Packit 40b132
    if (blocksize > MAX_BLOCK_SIZE || blocksize > sizeof(ctrParams.cb)) {
Packit 40b132
	PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
    gcm = PORT_ZNew(GCMContext);
Packit 40b132
    if (gcm == NULL) {
Packit 40b132
	return NULL;
Packit 40b132
    }
Packit 40b132
    /* first fill in the ghash context */
Packit 40b132
    ghash = &gcm->ghash_context;
Packit 40b132
    PORT_Memset(H, 0, blocksize);
Packit 40b132
    rv = (*cipher)(context, H, &tmp, blocksize, H, blocksize, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    rv = gcmHash_InitContext(ghash, H, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    freeHash = PR_TRUE;
Packit 40b132
Packit 40b132
    /* fill in the Counter context */
Packit 40b132
    ctrParams.ulCounterBits = 32;
Packit 40b132
    PORT_Memset(ctrParams.cb, 0, sizeof(ctrParams.cb));
Packit 40b132
    if ((blocksize == 16) && (gcmParams->ulIvLen == 12)) {
Packit 40b132
	PORT_Memcpy(ctrParams.cb, gcmParams->pIv, gcmParams->ulIvLen);
Packit 40b132
	ctrParams.cb[blocksize-1] = 1;
Packit 40b132
    } else {
Packit 40b132
	rv = gcmHash_Update(ghash, gcmParams->pIv, gcmParams->ulIvLen,
Packit 40b132
			    blocksize);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
	rv = gcmHash_Final(ghash, ctrParams.cb, &tmp, blocksize, blocksize);
Packit 40b132
	if (rv != SECSuccess) {
Packit 40b132
	    goto loser;
Packit 40b132
	}
Packit 40b132
    }
Packit 40b132
    rv = CTR_InitContext(&gcm->ctr_context, context, cipher,
Packit 40b132
				(unsigned char *)&ctrParams, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
    freeCtr = PR_TRUE;
Packit 40b132
Packit 40b132
    /* fill in the gcm structure */
Packit 40b132
    gcm->tagBits = gcmParams->ulTagBits; /* save for final step */
Packit 40b132
    /* calculate the final tag key. NOTE: gcm->tagKey is zero to start with.
Packit 40b132
     * if this assumption changes, we would need to explicitly clear it here */
Packit 40b132
    rv = CTR_Update(&gcm->ctr_context, gcm->tagKey, &tmp, blocksize,
Packit 40b132
		    gcm->tagKey, blocksize, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    /* finally mix in the AAD data */
Packit 40b132
    rv = gcmHash_Reset(ghash, gcmParams->pAAD, gcmParams->ulAADLen, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	goto loser;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    return gcm;
Packit 40b132
Packit 40b132
loser:
Packit 40b132
    if (freeCtr) {
Packit 40b132
	CTR_DestroyContext(&gcm->ctr_context, PR_FALSE);
Packit 40b132
    }
Packit 40b132
    if (freeHash) {
Packit 40b132
	gcmHash_DestroyContext(&gcm->ghash_context, PR_FALSE);
Packit 40b132
    }
Packit 40b132
    if (gcm) {
Packit 40b132
	PORT_Free(gcm);
Packit 40b132
    }
Packit 40b132
    return NULL;
Packit 40b132
}
Packit 40b132
Packit 40b132
void
Packit 40b132
GCM_DestroyContext(GCMContext *gcm, PRBool freeit)
Packit 40b132
{
Packit 40b132
    /* these two are statically allocated and will be freed when we free
Packit 40b132
     * gcm. call their destroy functions to free up any locally
Packit 40b132
     * allocated data (like mp_int's) */
Packit 40b132
    CTR_DestroyContext(&gcm->ctr_context, PR_FALSE);
Packit 40b132
    gcmHash_DestroyContext(&gcm->ghash_context, PR_FALSE);
Packit 40b132
    if (freeit) {
Packit 40b132
	PORT_Free(gcm);
Packit 40b132
    }
Packit 40b132
}
Packit 40b132
Packit 40b132
static SECStatus
Packit 40b132
gcm_GetTag(GCMContext *gcm, unsigned char *outbuf,
Packit 40b132
	unsigned int *outlen, unsigned int maxout,
Packit 40b132
	unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    unsigned int tagBytes;
Packit 40b132
    unsigned int extra;
Packit 40b132
    unsigned int i;
Packit 40b132
    SECStatus rv;
Packit 40b132
Packit 40b132
    tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE;
Packit 40b132
    extra = tagBytes*PR_BITS_PER_BYTE - gcm->tagBits;
Packit 40b132
Packit 40b132
    if (outbuf == NULL) {
Packit 40b132
	*outlen = tagBytes;
Packit 40b132
	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    if (maxout < tagBytes) {
Packit 40b132
	*outlen = tagBytes;
Packit 40b132
	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    maxout = tagBytes;
Packit 40b132
    rv = gcmHash_Final(&gcm->ghash_context, outbuf, outlen, maxout, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    GCM_TRACE_BLOCK("GHASH=", outbuf, blocksize);
Packit 40b132
    GCM_TRACE_BLOCK("Y0=", gcm->tagKey, blocksize);
Packit 40b132
    for (i=0; i < *outlen; i++) {
Packit 40b132
	outbuf[i] ^= gcm->tagKey[i];
Packit 40b132
    }
Packit 40b132
    GCM_TRACE_BLOCK("Y0=", gcm->tagKey, blocksize);
Packit 40b132
    GCM_TRACE_BLOCK("T=", outbuf, blocksize);
Packit 40b132
    /* mask off any extra bits we got */
Packit 40b132
    if (extra) {
Packit 40b132
	outbuf[tagBytes-1] &= ~((1 << extra)-1);
Packit 40b132
    }
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * See The Galois/Counter Mode of Operation, McGrew and Viega.
Packit 40b132
 *  GCM is basically counter mode with a specific initialization and
Packit 40b132
 *  built in macing operation.
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
GCM_EncryptUpdate(GCMContext *gcm, unsigned char *outbuf,
Packit 40b132
		unsigned int *outlen, unsigned int maxout,
Packit 40b132
		const unsigned char *inbuf, unsigned int inlen,
Packit 40b132
		unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    SECStatus rv;
Packit 40b132
    unsigned int tagBytes;
Packit 40b132
    unsigned int len;
Packit 40b132
Packit 40b132
    tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE;
Packit 40b132
    if (UINT_MAX - inlen < tagBytes) {
Packit 40b132
	PORT_SetError(SEC_ERROR_INPUT_LEN);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    if (maxout < inlen + tagBytes) {
Packit 40b132
	*outlen = inlen + tagBytes;
Packit 40b132
	PORT_SetError(SEC_ERROR_OUTPUT_LEN);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    rv = CTR_Update(&gcm->ctr_context, outbuf, outlen, maxout,
Packit 40b132
			inbuf, inlen, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    rv = gcmHash_Update(&gcm->ghash_context, outbuf, *outlen, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	PORT_Memset(outbuf, 0, *outlen); /* clear the output buffer */
Packit 40b132
	*outlen = 0;
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    rv = gcm_GetTag(gcm, outbuf + *outlen, &len, maxout - *outlen, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	PORT_Memset(outbuf, 0, *outlen); /* clear the output buffer */
Packit 40b132
	*outlen = 0;
Packit 40b132
	return SECFailure;
Packit 40b132
    };
Packit 40b132
    *outlen += len;
Packit 40b132
    return SECSuccess;
Packit 40b132
}
Packit 40b132
Packit 40b132
/*
Packit 40b132
 * See The Galois/Counter Mode of Operation, McGrew and Viega.
Packit 40b132
 *  GCM is basically counter mode with a specific initialization and
Packit 40b132
 *  built in macing operation. NOTE: the only difference between Encrypt
Packit 40b132
 *  and Decrypt is when we calculate the mac. That is because the mac must
Packit 40b132
 *  always be calculated on the cipher text, not the plain text, so for
Packit 40b132
 *  encrypt, we do the CTR update first and for decrypt we do the mac first.
Packit 40b132
 */
Packit 40b132
SECStatus
Packit 40b132
GCM_DecryptUpdate(GCMContext *gcm, unsigned char *outbuf,
Packit 40b132
		unsigned int *outlen, unsigned  int maxout,
Packit 40b132
		const unsigned char *inbuf, unsigned int inlen,
Packit 40b132
		unsigned int blocksize)
Packit 40b132
{
Packit 40b132
    SECStatus rv;
Packit 40b132
    unsigned int tagBytes;
Packit 40b132
    unsigned char tag[MAX_BLOCK_SIZE];
Packit 40b132
    const unsigned char *intag;
Packit 40b132
    unsigned int len;
Packit 40b132
Packit 40b132
    tagBytes = (gcm->tagBits + (PR_BITS_PER_BYTE-1)) / PR_BITS_PER_BYTE;
Packit 40b132
Packit 40b132
    /* get the authentication block */
Packit 40b132
    if (inlen < tagBytes) {
Packit 40b132
	PORT_SetError(SEC_ERROR_INPUT_LEN);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
Packit 40b132
    inlen -= tagBytes;
Packit 40b132
    intag = inbuf + inlen;
Packit 40b132
Packit 40b132
    /* verify the block */
Packit 40b132
    rv = gcmHash_Update(&gcm->ghash_context, inbuf, inlen, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    rv = gcm_GetTag(gcm, tag, &len, blocksize, blocksize);
Packit 40b132
    if (rv != SECSuccess) {
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    /* Don't decrypt if we can't authenticate the encrypted data!
Packit 40b132
     * This assumes that if tagBits is not a multiple of 8, intag will
Packit 40b132
     * preserve the masked off missing bits.  */
Packit 40b132
    if (NSS_SecureMemcmp(tag, intag, tagBytes) != 0) {
Packit 40b132
	/* force a CKR_ENCRYPTED_DATA_INVALID error at in softoken */
Packit 40b132
	PORT_SetError(SEC_ERROR_BAD_DATA);
Packit 40b132
	return SECFailure;
Packit 40b132
    }
Packit 40b132
    /* finish the decryption */
Packit 40b132
    return CTR_Update(&gcm->ctr_context, outbuf, outlen, maxout,
Packit 40b132
			  inbuf, inlen, blocksize);
Packit 40b132
}