IBR-DTNSuite  0.8
ibrcommon/ibrcommon/ssl/gcm/gcm.cc
Go to the documentation of this file.
00001 /*
00002  ---------------------------------------------------------------------------
00003  Copyright (c) 1998-2006, Brian Gladman, Worcester, UK. All rights reserved.
00004 
00005  LICENSE TERMS
00006 
00007  The free distribution and use of this software in both source and binary
00008  form is allowed (with or without changes) provided that:
00009 
00010    1. distributions of this source code include the above copyright
00011       notice, this list of conditions and the following disclaimer;
00012 
00013    2. distributions in binary form include the above copyright
00014       notice, this list of conditions and the following disclaimer
00015       in the documentation and/or other associated materials;
00016 
00017    3. the copyright holder's name is not used to endorse products
00018       built using this software without specific written permission.
00019 
00020  ALTERNATIVELY, provided that this notice is retained in full, this product
00021  may be distributed under the terms of the GNU General Public License (GPL),
00022  in which case the provisions of the GPL apply INSTEAD OF those given above.
00023 
00024  DISCLAIMER
00025 
00026  This software is provided 'as is' with no explicit or implied warranties
00027  in respect of its properties, including, but not limited to, correctness
00028  and/or fitness for purpose.
00029  ---------------------------------------------------------------------------
00030  Issue Date: 13/06/2006
00031 
00032  My thanks to John Viega and David McGrew for their support in developing
00033  this code and to David for testing it on a big-endain system.
00034 */
00035 
00036 //#ifdef HAVE_CONFIG_H
00037 //#  include <dtn-config.h>
00038 //#endif
00039 #define BSP_ENABLED true
00040 
00041 #ifdef BSP_ENABLED
00042 
00043 #include "gcm.h"
00044 #include "mode_hdr.h"
00045 
00046 #if defined(__cplusplus)
00047 extern "C"
00048 {
00049 #endif
00050 
00051 #define BLOCK_SIZE      GCM_BLOCK_SIZE      /* block length                 */
00052 #define BLK_ADR_MASK    (BLOCK_SIZE - 1)    /* mask for 'in block' address  */
00053 #define CTR_POS         12
00054 
00055 #define inc_ctr(x)  \
00056     {   int i = BLOCK_SIZE; while(i-- > CTR_POS && !++(ui8_ptr(x)[i])) ; }
00057 
00058 ret_type gcm_init_and_key(                      /* initialise mode and set key  */
00059             const unsigned char key[],          /* the key value                */
00060             unsigned long key_len,              /* and its length in bytes      */
00061             gcm_ctx ctx[1])                     /* the mode context             */
00062 {
00063     memset(ctx->ghash_h, 0, sizeof(ctx->ghash_h));
00064 
00065     /* set the AES key                          */
00066     aes_encrypt_key(key, key_len, ctx->aes);
00067 
00068     /* compute E(0) (for the hash function)     */
00069     aes_encrypt(ui8_ptr(ctx->ghash_h), ui8_ptr(ctx->ghash_h), ctx->aes);
00070 
00071 #if defined( TABLES_64K )
00072     init_64k_table(ui8_ptr(ctx->ghash_h), ctx->gf_t64k);
00073 #elif defined( TABLES_8K )
00074     init_8k_table(ui8_ptr(ctx->ghash_h), ctx->gf_t8k);
00075 #elif defined( TABLES_4K )
00076     init_4k_table(ui8_ptr(ctx->ghash_h), ctx->gf_t4k);
00077 #elif defined( TABLES_256 )
00078     init_256_table(ui8_ptr(ctx->ghash_h), ctx->gf_t256);
00079 #endif
00080     return RETURN_OK;
00081 }
00082 
00083 #if defined( TABLES_64K )
00084 #define gf_mul_hh(a, ctx, scr)  gf_mul_64k(a, ctx->gf_t64k, scr)
00085 #elif defined( TABLES_8K )
00086 #define gf_mul_hh(a, ctx, scr)  gf_mul_8k(a, ctx->gf_t8k, scr)
00087 #elif defined( TABLES_4K )
00088 #define gf_mul_hh(a, ctx, scr)  gf_mul_4k(a, ctx->gf_t4k, scr)
00089 #elif defined( TABLES_256 )
00090 #define gf_mul_hh(a, ctx, scr)  gf_mul_256(a, ctx->gf_t256, scr)
00091 #else
00092 #define gf_mul_hh(a, ctx, scr)  gf_mul(a, ui8_ptr(ctx->ghash_h))
00093 #endif
00094 
00095 ret_type gcm_init_message(                      /* initialise a new message     */
00096             const unsigned char iv[],           /* the initialisation vector    */
00097             unsigned long iv_len,               /* and its length in bytes      */
00098             gcm_ctx ctx[1])                     /* the mode context             */
00099 {   uint_32t i, n_pos = 0, scratch[GF_BYTE_LEN >> 2];
00100     uint_8t *p;
00101 
00102     memset(ctx->ctr_val, 0, BLOCK_SIZE);
00103     if(iv_len == CTR_POS)
00104     {
00105         memcpy(ctx->ctr_val, iv, CTR_POS); ui8_ptr(ctx->ctr_val)[15] = 0x01;
00106     }
00107     else
00108     {   n_pos = iv_len;
00109         while(n_pos >= BLOCK_SIZE)
00110         {
00111             xor_block_aligned(ctx->ctr_val, iv);
00112             n_pos -= BLOCK_SIZE;
00113             iv += BLOCK_SIZE;
00114             gf_mul_hh(ui8_ptr(ctx->ctr_val), ctx, scratch);
00115         }
00116 
00117         if(n_pos)
00118         {
00119             p = ui8_ptr(ctx->ctr_val);
00120             while(n_pos-- > 0)
00121                 *p++ ^= *iv++;
00122             gf_mul_hh(ui8_ptr(ctx->ctr_val), ctx, scratch);
00123         }
00124         n_pos = (iv_len << 3);
00125         for(i = BLOCK_SIZE - 1; n_pos; --i, n_pos >>= 8)
00126             ui8_ptr(ctx->ctr_val)[i] ^= (unsigned char)n_pos;
00127         gf_mul_hh(ui8_ptr(ctx->ctr_val), ctx, scratch);
00128     }
00129 
00130     ctx->y0_val = *ui32_ptr(ui8_ptr(ctx->ctr_val) + CTR_POS);
00131     inc_ctr(ctx->ctr_val);
00132     memset(ctx->hdr_ghv, 0, BLOCK_SIZE);
00133     memset(ctx->txt_ghv, 0, BLOCK_SIZE);
00134     ctx->hdr_cnt = 0;
00135     ctx->txt_ccnt = ctx->txt_acnt = 0;
00136     return RETURN_OK;
00137 }
00138 
00139 ret_type gcm_auth_header(                       /* authenticate the header      */
00140             const unsigned char hdr[],          /* the header buffer            */
00141             unsigned long hdr_len,              /* and its length in bytes      */
00142             gcm_ctx ctx[1])                     /* the mode context             */
00143 {   uint_32t cnt = 0, b_pos = (uint_32t)ctx->hdr_cnt & BLK_ADR_MASK;
00144     uint_32t scratch[GF_BYTE_LEN >> 2];
00145 
00146     if(!hdr_len)
00147         return RETURN_OK;
00148 
00149     if(ctx->hdr_cnt && b_pos == 0)
00150         gf_mul_hh(ui8_ptr(ctx->hdr_ghv), ctx, scratch);
00151 
00152     while(cnt < hdr_len && (b_pos & BUF_ADRMASK))
00153         ui8_ptr(ctx->hdr_ghv)[b_pos++] ^= hdr[cnt++];
00154     
00155     if(!(b_pos & BUF_ADRMASK) && !((hdr + cnt - ui8_ptr(ctx->hdr_ghv)) & BUF_ADRMASK))
00156     {
00157         while(cnt + BUF_INC <= hdr_len && b_pos <= BLOCK_SIZE - BUF_INC)
00158         {
00159             *unit_ptr(ui8_ptr(ctx->hdr_ghv) + b_pos) ^= *unit_ptr(hdr + cnt);
00160             cnt += BUF_INC; b_pos += BUF_INC;
00161         }
00162         
00163         while(cnt + BLOCK_SIZE <= hdr_len)
00164         {
00165             gf_mul_hh(ui8_ptr(ctx->hdr_ghv), ctx, scratch);
00166             xor_block_aligned(ctx->hdr_ghv, hdr + cnt);
00167             cnt += BLOCK_SIZE;
00168         }
00169     }
00170     else
00171     {
00172         while(cnt < hdr_len && b_pos < BLOCK_SIZE)
00173             ui8_ptr(ctx->hdr_ghv)[b_pos++] ^= hdr[cnt++];
00174 
00175         while(cnt + BLOCK_SIZE <= hdr_len)
00176         {
00177             gf_mul_hh(ui8_ptr(ctx->hdr_ghv), ctx, scratch);
00178             xor_block(ctx->hdr_ghv, hdr + cnt);
00179             cnt += BLOCK_SIZE;
00180         }
00181     }
00182 
00183     while(cnt < hdr_len)
00184     {
00185         if(b_pos == BLOCK_SIZE)
00186         {
00187             gf_mul_hh(ui8_ptr(ctx->hdr_ghv), ctx, scratch);
00188             b_pos = 0;
00189         }
00190         ui8_ptr(ctx->hdr_ghv)[b_pos++] ^= hdr[cnt++];
00191     }
00192 
00193     ctx->hdr_cnt += cnt;
00194     return RETURN_OK;
00195 }
00196 
00197 ret_type gcm_auth_data(                         /* authenticate ciphertext data */
00198             const unsigned char data[],         /* the data buffer              */
00199             unsigned long data_len,             /* and its length in bytes      */
00200             gcm_ctx ctx[1])                     /* the mode context             */
00201 {   uint_32t cnt = 0, b_pos = (uint_32t)(ctx->txt_acnt & BLK_ADR_MASK);
00202     uint_32t scratch[GF_BYTE_LEN >> 2];
00203 
00204     if(!data_len)
00205         return RETURN_OK;
00206 
00207     if(ctx->txt_acnt && b_pos == 0)
00208         gf_mul_hh(ui8_ptr(ctx->txt_ghv), ctx, scratch);
00209 
00210     while(cnt < data_len && (b_pos & BUF_ADRMASK))
00211         ui8_ptr(ctx->txt_ghv)[b_pos++] ^= data[cnt++];
00212 
00213     if(!(b_pos & BUF_ADRMASK) && !((data + cnt - ui8_ptr(ctx->txt_ghv)) & BUF_ADRMASK))
00214     {
00215         while(cnt + BUF_INC <= data_len && b_pos <= BLOCK_SIZE - BUF_INC)
00216         {
00217             *unit_ptr(ui8_ptr(ctx->txt_ghv) + b_pos) ^= *unit_ptr(data + cnt);
00218             cnt += BUF_INC; b_pos += BUF_INC;
00219         }
00220 
00221         while(cnt + BLOCK_SIZE <= data_len)
00222         {
00223             gf_mul_hh(ui8_ptr(ctx->txt_ghv), ctx, scratch);
00224             xor_block_aligned(ctx->txt_ghv, data + cnt);
00225             cnt += BLOCK_SIZE;
00226         }
00227     }
00228     else
00229     {
00230         while(cnt < data_len && b_pos < BLOCK_SIZE)
00231             ui8_ptr(ctx->txt_ghv)[b_pos++] ^= data[cnt++];
00232 
00233         while(cnt + BLOCK_SIZE <= data_len)
00234         {
00235             gf_mul_hh(ui8_ptr(ctx->txt_ghv), ctx, scratch);
00236             xor_block(ctx->txt_ghv, data + cnt);
00237             cnt += BLOCK_SIZE;
00238         }
00239     }
00240 
00241     while(cnt < data_len)
00242     {
00243         if(b_pos == BLOCK_SIZE)
00244         {
00245             gf_mul_hh(ui8_ptr(ctx->txt_ghv), ctx, scratch);
00246             b_pos = 0;
00247         }
00248         ui8_ptr(ctx->txt_ghv)[b_pos++] ^= data[cnt++];
00249     }
00250 
00251     ctx->txt_acnt += cnt;
00252     return RETURN_OK;
00253 }
00254 
00255 ret_type gcm_crypt_data(                        /* encrypt or decrypt data      */
00256             unsigned char data[],               /* the data buffer              */
00257             unsigned long data_len,             /* and its length in bytes      */
00258             gcm_ctx ctx[1])                     /* the mode context             */
00259 {   uint_32t cnt = 0, b_pos = (uint_32t)(ctx->txt_ccnt & BLK_ADR_MASK);
00260 
00261     if(!data_len)
00262         return RETURN_OK;
00263 
00264     if(b_pos == 0)
00265     {
00266         aes_encrypt(ui8_ptr(ctx->ctr_val), ui8_ptr(ctx->enc_ctr), ctx->aes);
00267         inc_ctr(ctx->ctr_val);
00268     }
00269 
00270     while(cnt < data_len && (b_pos & BUF_ADRMASK))
00271         data[cnt++] ^= ui8_ptr(ctx->enc_ctr)[b_pos++];
00272 
00273     if(!(b_pos & BUF_ADRMASK) && !((data + cnt - ui8_ptr(ctx->enc_ctr)) & BUF_ADRMASK))
00274     {
00275         while(cnt + BUF_INC <= data_len && b_pos <= BLOCK_SIZE - BUF_INC)
00276         {
00277             *unit_ptr(data + cnt) ^= *unit_ptr(ui8_ptr(ctx->enc_ctr) + b_pos);
00278             cnt += BUF_INC; b_pos += BUF_INC;
00279         }
00280 
00281         while(cnt + BLOCK_SIZE <= data_len)
00282         {
00283             aes_encrypt(ui8_ptr(ctx->ctr_val), ui8_ptr(ctx->enc_ctr), ctx->aes);
00284             inc_ctr(ctx->ctr_val);
00285             xor_block_aligned(data + cnt, ctx->enc_ctr);
00286             cnt += BLOCK_SIZE;
00287         }
00288     }
00289     else
00290     {
00291         while(cnt < data_len && b_pos < BLOCK_SIZE)
00292             data[cnt++] ^= ui8_ptr(ctx->enc_ctr)[b_pos++];
00293 
00294         while(cnt + BLOCK_SIZE <= data_len)
00295         {
00296             aes_encrypt(ui8_ptr(ctx->ctr_val), ui8_ptr(ctx->enc_ctr), ctx->aes);
00297             inc_ctr(ctx->ctr_val);
00298             xor_block(data + cnt, ctx->enc_ctr);
00299             cnt += BLOCK_SIZE;
00300         }
00301     }
00302 
00303     while(cnt < data_len)
00304     {
00305         if(b_pos == BLOCK_SIZE)
00306         {
00307             aes_encrypt(ui8_ptr(ctx->ctr_val), ui8_ptr(ctx->enc_ctr), ctx->aes);
00308             inc_ctr(ctx->ctr_val);
00309             b_pos = 0;
00310         }
00311         data[cnt++] ^= ui8_ptr(ctx->enc_ctr)[b_pos++];
00312     }
00313 
00314     ctx->txt_ccnt += cnt;
00315     return RETURN_OK;
00316 }
00317 
00318 ret_type gcm_compute_tag(                       /* compute authentication tag   */
00319             unsigned char tag[],                /* the buffer for the tag       */
00320             unsigned long tag_len,              /* and its length in bytes      */
00321             gcm_ctx ctx[1])                     /* the mode context             */
00322 {   uint_32t i, ln, scratch[GF_BYTE_LEN >> 2];
00323     uint_8t tbuf[BLOCK_SIZE];
00324 
00325     if(ctx->txt_acnt != ctx->txt_ccnt && ctx->txt_ccnt > 0)
00326         return RETURN_ERROR;
00327 
00328     gf_mul_hh(ui8_ptr(ctx->hdr_ghv), ctx, scratch);
00329     gf_mul_hh(ui8_ptr(ctx->txt_ghv), ctx, scratch);
00330 
00331 #if 1   /* alternative versions of the exponentiation operation */
00332     if(ctx->hdr_cnt && (ln = (uint_32t)((ctx->txt_acnt + BLOCK_SIZE - 1) / BLOCK_SIZE)))
00333     {
00334         memcpy(tbuf, ctx->ghash_h, BLOCK_SIZE);
00335         for( ; ; )
00336         {
00337             if(ln & 1) gf_mul(ui8_ptr(ctx->hdr_ghv), tbuf);
00338             if(!(ln >>= 1)) break;
00339             gf_mul(tbuf, tbuf);
00340         }
00341     }
00342 #else   /* this one seems slower on x86 and x86_64 :-( */
00343     if(ctx->hdr_cnt && (ln = (uint_32t)((ctx->txt_acnt + BLOCK_SIZE - 1) / BLOCK_SIZE)))
00344     {
00345         i = ln | ln >> 1; i |= i >> 2; i |= i >> 4;
00346         i |= i >> 8; i |= i >> 16; i = i & ~(i >> 1);
00347         memset(tbuf, 0, BLOCK_SIZE);
00348         tbuf[0] = 0x80;
00349         while(i)
00350         {
00351             gf_mul(tbuf, tbuf);
00352             if(i & ln)
00353                 gf_mul_hh(tbuf, ctx, scratch);
00354             i >>= 1;
00355         }
00356         gf_mul(ui8_ptr(ctx->hdr_ghv), tbuf);
00357     }
00358 #endif
00359     i = BLOCK_SIZE; ln = (uint_32t)(ctx->txt_acnt << 3);
00360     while(i-- > 0)
00361     {
00362         ui8_ptr(ctx->hdr_ghv)[i] ^= ui8_ptr(ctx->txt_ghv)[i] ^ (unsigned char)ln;
00363         ln = (i == 8 ? (uint_32t)(ctx->hdr_cnt << 3) : ln >> 8);
00364     }
00365 
00366     gf_mul_hh(ui8_ptr(ctx->hdr_ghv), ctx, scratch);
00367 
00368     *ui32_ptr(ui8_ptr(ctx->ctr_val) + CTR_POS) = ctx->y0_val;
00369     aes_encrypt(ui8_ptr(ctx->ctr_val), ui8_ptr(ctx->enc_ctr), ctx->aes);
00370     for(i = 0; i < (unsigned int)tag_len; ++i)
00371         tag[i] = ui8_ptr(ctx->hdr_ghv)[i] ^ ui8_ptr(ctx->enc_ctr)[i];
00372 
00373     return (ctx->txt_ccnt == ctx->txt_acnt ? RETURN_OK : RETURN_WARN);
00374 }
00375 
00376 ret_type gcm_end(                               /* clean up and end operation   */
00377             gcm_ctx ctx[1])                     /* the mode context             */
00378 {
00379     memset(ctx, 0, sizeof(gcm_ctx));
00380     return RETURN_OK;
00381 }
00382 
00383 ret_type gcm_encrypt(                           /* encrypt & authenticate data  */
00384             unsigned char data[],               /* the data buffer              */
00385             unsigned long data_len,             /* and its length in bytes      */
00386             gcm_ctx ctx[1])                     /* the mode context             */
00387 {
00388 
00389     gcm_crypt_data(data, data_len, ctx);
00390     gcm_auth_data(data, data_len, ctx);
00391     return RETURN_OK;
00392 }
00393 
00394 ret_type gcm_decrypt(                           /* authenticate & decrypt data  */
00395             unsigned char data[],               /* the data buffer              */
00396             unsigned long data_len,             /* and its length in bytes      */
00397             gcm_ctx ctx[1])                     /* the mode context             */
00398 {
00399     gcm_auth_data(data, data_len, ctx);
00400     gcm_crypt_data(data, data_len, ctx);
00401     return RETURN_OK;
00402 }
00403 
00404 ret_type gcm_encrypt_message(                   /* encrypt an entire message    */
00405             const unsigned char iv[],           /* the initialisation vector    */
00406             unsigned long iv_len,               /* and its length in bytes      */
00407             const unsigned char hdr[],          /* the header buffer            */
00408             unsigned long hdr_len,              /* and its length in bytes      */
00409             unsigned char msg[],                /* the message buffer           */
00410             unsigned long msg_len,              /* and its length in bytes      */
00411             unsigned char tag[],                /* the buffer for the tag       */
00412             unsigned long tag_len,              /* and its length in bytes      */
00413             gcm_ctx ctx[1])                     /* the mode context             */
00414 {
00415     gcm_init_message(iv, iv_len, ctx);
00416     gcm_auth_header(hdr, hdr_len, ctx);
00417     gcm_encrypt(msg, msg_len, ctx);
00418     return gcm_compute_tag(tag, tag_len, ctx) ? RETURN_ERROR : RETURN_OK;
00419 }
00420 
00421 ret_type gcm_decrypt_message(                   /* decrypt an entire message    */
00422             const unsigned char iv[],           /* the initialisation vector    */
00423             unsigned long iv_len,               /* and its length in bytes      */
00424             const unsigned char hdr[],          /* the header buffer            */
00425             unsigned long hdr_len,              /* and its length in bytes      */
00426             unsigned char msg[],                /* the message buffer           */
00427             unsigned long msg_len,              /* and its length in bytes      */
00428             const unsigned char tag[],          /* the buffer for the tag       */
00429             unsigned long tag_len,              /* and its length in bytes      */
00430             gcm_ctx ctx[1])                     /* the mode context             */
00431 {   uint_8t local_tag[BLOCK_SIZE];
00432     ret_type rr;
00433 
00434     gcm_init_message(iv, iv_len, ctx);
00435     gcm_auth_header(hdr, hdr_len, ctx);
00436     gcm_decrypt(msg, msg_len, ctx);
00437     rr = gcm_compute_tag(local_tag, tag_len, ctx);
00438     return (rr != RETURN_OK || memcmp(tag, local_tag, tag_len)) ? RETURN_ERROR : RETURN_OK;
00439 }
00440 
00441 #if defined(__cplusplus)
00442 }
00443 #endif
00444 
00445 #endif /* BSP_ENABLED */