Split up GCM into a start/update/finish cycle

This commit is contained in:
Paul Bakker 2013-06-26 15:07:16 +02:00
parent 534f82c77a
commit b9d3cfa114
11 changed files with 312 additions and 49 deletions

View File

@ -16,6 +16,7 @@ Changes
dynamically set (Better support for hardware acceleration)
* Moved all OID functionality to a separate module. RSA function
prototypes for the RSA sign and verify functions changed as a result
* Split up the GCM module into a starts/update/finish cycle
Bugfix
* Fix for MPI assembly for ARM

View File

@ -49,6 +49,12 @@ typedef struct {
aes_context aes_ctx; /*!< AES context used */
uint64_t HL[16]; /*!< Precalculated HTable */
uint64_t HH[16]; /*!< Precalculated HTable */
uint64_t len; /*!< Total data length */
uint64_t add_len; /*!< Total add length */
unsigned char base_ectr[16];/*!< First ECTR for tag */
unsigned char y[16]; /*!< Y working value */
unsigned char buf[16]; /*!< buf working value */
int mode; /*!< Encrypt or Decrypt */
}
gcm_context;
@ -133,6 +139,62 @@ int gcm_auth_decrypt( gcm_context *ctx,
const unsigned char *input,
unsigned char *output );
/**
* \brief Generic GCM stream start function
*
* \param ctx GCM context
* \param mode GCM_ENCRYPT or GCM_DECRYPT
* \param iv initialization vector
* \param iv_len length of IV
* \param add additional data
* \param add_len length of additional data
*
* \return 0 if successful
*/
int gcm_starts( gcm_context *ctx,
int mode,
const unsigned char *iv,
size_t iv_len,
const unsigned char *add,
size_t add_len );
/**
* \brief Generic GCM update function. Encrypts/decrypts using the
* given GCM context. Expects input to be a multiple of 16
* bytes! Only the last call before gcm_finish() can be less
* than 16 bytes!
*
* \note On decryption, the output buffer cannot be the same as input buffer.
* If buffers overlap, the output buffer must trail at least 8 bytes
* behind the input buffer.
*
* \param ctx GCM context
* \param length length of the input data
* \param input buffer holding the input data
* \param output buffer for holding the output data
*
* \return 0 if successful or POLARSSL_ERR_GCM_BAD_INPUT
*/
int gcm_update( gcm_context *ctx,
size_t length,
const unsigned char *input,
unsigned char *output );
/**
* \brief Generic GCM finalisation function. Wraps up the GCM stream
* and generated the tag. The tag can have a maximum length of
* 16 bytes.
*
* \param ctx GCM context
* \param tag buffer for holding the tag
* \param tag_len length of the tag to generate
*
* \return 0 if successful or POLARSSL_ERR_GCM_BAD_INPUT
*/
int gcm_finish( gcm_context *ctx,
unsigned char *tag,
size_t tag_len );
/**
* \brief Checkup routine
*

View File

@ -169,41 +169,24 @@ static void gcm_mult( gcm_context *ctx, const unsigned char x[16],
PUT_UINT32_BE( zl, output, 12 );
}
int gcm_crypt_and_tag( gcm_context *ctx,
int mode,
size_t length,
const unsigned char *iv,
size_t iv_len,
const unsigned char *add,
size_t add_len,
const unsigned char *input,
unsigned char *output,
size_t tag_len,
unsigned char *tag )
int gcm_starts( gcm_context *ctx,
int mode,
const unsigned char *iv,
size_t iv_len,
const unsigned char *add,
size_t add_len )
{
unsigned char y[16];
unsigned char ectr[16];
unsigned char buf[16];
unsigned char work_buf[16];
size_t i;
const unsigned char *p;
unsigned char *out_p = output;
size_t use_len;
uint64_t orig_len = length * 8;
uint64_t orig_add_len = add_len * 8;
memset( y, 0x00, 16 );
memset( work_buf, 0x00, 16 );
memset( tag, 0x00, tag_len );
memset( buf, 0x00, 16 );
if( output > input && (size_t) ( output - input ) < length )
return( POLARSSL_ERR_GCM_BAD_INPUT );
ctx->mode = mode;
if( iv_len == 12 )
{
memcpy( y, iv, iv_len );
y[15] = 1;
memcpy( ctx->y, iv, iv_len );
ctx->y[15] = 1;
}
else
{
@ -216,64 +199,100 @@ int gcm_crypt_and_tag( gcm_context *ctx,
use_len = ( iv_len < 16 ) ? iv_len : 16;
for( i = 0; i < use_len; i++ )
y[i] ^= p[i];
ctx->y[i] ^= p[i];
gcm_mult( ctx, y, y );
gcm_mult( ctx, ctx->y, ctx->y );
iv_len -= use_len;
p += use_len;
}
for( i = 0; i < 16; i++ )
y[i] ^= work_buf[i];
ctx->y[i] ^= work_buf[i];
gcm_mult( ctx, y, y );
gcm_mult( ctx, ctx->y, ctx->y );
}
aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, y, ectr );
memcpy( tag, ectr, tag_len );
aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, ctx->y, ctx->base_ectr );
ctx->add_len = add_len;
p = add;
while( add_len > 0 )
{
use_len = ( add_len < 16 ) ? add_len : 16;
for( i = 0; i < use_len; i++ )
buf[i] ^= p[i];
ctx->buf[i] ^= p[i];
gcm_mult( ctx, buf, buf );
gcm_mult( ctx, ctx->buf, ctx->buf );
add_len -= use_len;
p += use_len;
}
return( 0 );
}
int gcm_update( gcm_context *ctx,
size_t length,
const unsigned char *input,
unsigned char *output )
{
unsigned char ectr[16];
size_t i;
const unsigned char *p;
unsigned char *out_p = output;
size_t use_len;
if( output > input && (size_t) ( output - input ) < length )
return( POLARSSL_ERR_GCM_BAD_INPUT );
ctx->len += length;
p = input;
while( length > 0 )
{
use_len = ( length < 16 ) ? length : 16;
for( i = 16; i > 12; i-- )
if( ++y[i - 1] != 0 )
if( ++ctx->y[i - 1] != 0 )
break;
aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, y, ectr );
aes_crypt_ecb( &ctx->aes_ctx, AES_ENCRYPT, ctx->y, ectr );
for( i = 0; i < use_len; i++ )
{
if( mode == GCM_DECRYPT )
buf[i] ^= p[i];
if( ctx->mode == GCM_DECRYPT )
ctx->buf[i] ^= p[i];
out_p[i] = ectr[i] ^ p[i];
if( mode == GCM_ENCRYPT )
buf[i] ^= out_p[i];
if( ctx->mode == GCM_ENCRYPT )
ctx->buf[i] ^= out_p[i];
}
gcm_mult( ctx, buf, buf );
gcm_mult( ctx, ctx->buf, ctx->buf );
length -= use_len;
p += use_len;
out_p += use_len;
}
return( 0 );
}
int gcm_finish( gcm_context *ctx,
unsigned char *tag,
size_t tag_len )
{
unsigned char work_buf[16];
size_t i;
uint64_t orig_len = ctx->len * 8;
uint64_t orig_add_len = ctx->add_len * 8;
memcpy( tag, ctx->base_ectr, tag_len );
if( tag_len > 16 )
return( POLARSSL_ERR_GCM_BAD_INPUT );
if( orig_len || orig_add_len )
{
memset( work_buf, 0x00, 16 );
@ -284,17 +303,43 @@ int gcm_crypt_and_tag( gcm_context *ctx,
PUT_UINT32_BE( ( orig_len ), work_buf, 12 );
for( i = 0; i < 16; i++ )
buf[i] ^= work_buf[i];
ctx->buf[i] ^= work_buf[i];
gcm_mult( ctx, buf, buf );
gcm_mult( ctx, ctx->buf, ctx->buf );
for( i = 0; i < tag_len; i++ )
tag[i] ^= buf[i];
tag[i] ^= ctx->buf[i];
}
return( 0 );
}
int gcm_crypt_and_tag( gcm_context *ctx,
int mode,
size_t length,
const unsigned char *iv,
size_t iv_len,
const unsigned char *add,
size_t add_len,
const unsigned char *input,
unsigned char *output,
size_t tag_len,
unsigned char *tag )
{
int ret;
if( ( ret = gcm_starts( ctx, mode, iv, iv_len, add, add_len ) ) != 0 )
return( ret );
if( ( ret = gcm_update( ctx, length, input, output ) ) != 0 )
return( ret );
if( ( ret = gcm_finish( ctx, tag, tag_len ) ) != 0 )
return( ret );
return( 0 );
}
int gcm_auth_decrypt( gcm_context *ctx,
size_t length,
const unsigned char *iv,
@ -562,7 +607,9 @@ int gcm_self_test( int verbose )
for( i = 0; i < MAX_TESTS; i++ )
{
printf( " AES-GCM-%3d #%d (%s): ", key_len, i, "enc" );
if( verbose != 0 )
printf( " AES-GCM-%3d #%d (%s): ", key_len, i, "enc" );
gcm_init( &ctx, key[key_index[i]], key_len );
ret = gcm_crypt_and_tag( &ctx, GCM_ENCRYPT,
@ -584,7 +631,9 @@ int gcm_self_test( int verbose )
if( verbose != 0 )
printf( "passed\n" );
printf( " AES-GCM-%3d #%d (%s): ", key_len, i, "dec" );
if( verbose != 0 )
printf( " AES-GCM-%3d #%d (%s): ", key_len, i, "dec" );
gcm_init( &ctx, key[key_index[i]], key_len );
ret = gcm_crypt_and_tag( &ctx, GCM_DECRYPT,
@ -605,10 +654,138 @@ int gcm_self_test( int verbose )
if( verbose != 0 )
printf( "passed\n" );
if( verbose != 0 )
printf( " AES-GCM-%3d #%d split (%s): ", key_len, i, "enc" );
gcm_init( &ctx, key[key_index[i]], key_len );
ret = gcm_starts( &ctx, GCM_ENCRYPT,
iv[iv_index[i]], iv_len[i],
additional[add_index[i]], add_len[i] );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
if( pt_len[i] > 32 )
{
size_t rest_len = pt_len[i] - 32;
ret = gcm_update( &ctx, 32, pt[pt_index[i]], buf );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
ret = gcm_update( &ctx, rest_len, pt[pt_index[i]] + 32, buf + 32 );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
}
else
{
ret = gcm_update( &ctx, pt_len[i], pt[pt_index[i]], buf );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
}
ret = gcm_finish( &ctx, tag_buf, 16 );
if( ret != 0 ||
memcmp( buf, ct[j * 6 + i], pt_len[i] ) != 0 ||
memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
if( verbose != 0 )
printf( "passed\n" );
if( verbose != 0 )
printf( " AES-GCM-%3d #%d split (%s): ", key_len, i, "dec" );
gcm_init( &ctx, key[key_index[i]], key_len );
ret = gcm_starts( &ctx, GCM_DECRYPT,
iv[iv_index[i]], iv_len[i],
additional[add_index[i]], add_len[i] );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
if( pt_len[i] > 32 )
{
size_t rest_len = pt_len[i] - 32;
ret = gcm_update( &ctx, 32, ct[j * 6 + i], buf );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
ret = gcm_update( &ctx, rest_len, ct[j * 6 + i] + 32, buf + 32 );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
}
else
{
ret = gcm_update( &ctx, pt_len[i], ct[j * 6 + i], buf );
if( ret != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
}
ret = gcm_finish( &ctx, tag_buf, 16 );
if( ret != 0 ||
memcmp( buf, pt[pt_index[i]], pt_len[i] ) != 0 ||
memcmp( tag_buf, tag[j * 6 + i], 16 ) != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
if( verbose != 0 )
printf( "passed\n" );
}
}
printf( "\n" );
if( verbose != 0 )
printf( "\n" );
return( 0 );
}

View File

@ -91,4 +91,8 @@ while (my $line = <TEST_DATA>)
print("\n\n");
}
}
print("GCM Selftest\n");
print("gcm_selftest:\n\n");
close(TEST_DATA);

View File

@ -74,4 +74,8 @@ while (my $line = <TEST_DATA>)
print("\n\n");
}
}
print("GCM Selftest\n");
print("gcm_selftest:\n\n");
close(TEST_DATA);

View File

@ -502,3 +502,5 @@ gcm_decrypt_and_verify:"db68a96e216b0dd9945f14b878487e03":"5634196a32d4cbfa7a2f8
GCM NIST Validation (AES-128,128,1024,1024,32) #2
gcm_decrypt_and_verify:"659b9e729d12f68b73fdc2f7260ab114":"fd0732a38224c3f16f58de3a7f333da2ecdb6eec92b469544a891966dd4f8fb64a711a793f1ef6a90e49765eacaccdd8cc438c2b57c51902d27a82ee4f24925a864a9513a74e734ddbf77204a99a3c0060fcfbaccae48fe509bc95c3d6e1b1592889c489801265715e6e4355a45357ce467c1caa2f1c3071bd3a9168a7d223e3":"459df18e2dfbd66d6ad04978432a6d97":"ee0b0b52a729c45b899cc924f46eb1908e55aaaeeaa0c4cdaacf57948a7993a6debd7b6cd7aa426dc3b3b6f56522ba3d5700a820b1697b8170bad9ca7caf1050f13d54fb1ddeb111086cb650e1c5f4a14b6a927205a83bf49f357576fd0f884a83b068154352076a6e36a5369436d2c8351f3e6bfec65b4816e3eb3f144ed7f9":32:"8e5a6a79":"FAIL":0
GCM Selftest
gcm_selftest:

View File

@ -501,3 +501,6 @@ gcm_decrypt_and_verify:"3fc76d627c775de2f789279dc7b67979a9f1cc23c8dcabc9":"92a60
GCM NIST Validation (AES-192,128,1024,1024,32) #2
gcm_decrypt_and_verify:"b10979797fb8f418a126120d45106e1779b4538751a19bf6":"e3dc64e3c02731fe6e6ec0e899183018da347bf8bd476aa7746d7a7729d83a95f64bb732ba987468d0cede154e28169f7bafa36559200795037ee38279e0e4ca40f9cfa85aa0c8035df9649345c8fdffd1c31528b485dfe443c1923180cc8fae5196d16f822be4ad07e3f1234e1d218e7c8fb37a0e4480dc6717c9c09ff5c45f":"ca362e615024a1fe11286668646cc1de":"237d95d86a5ad46035870f576a1757eded636c7234d5ed0f8039f6f59f1333cc31cb893170d1baa98bd4e79576de920120ead0fdecfb343edbc2fcc556540a91607388a05d43bdb8b55f1327552feed3b620614dfcccb2b342083896cbc81dc9670b761add998913ca813163708a45974e6d7b56dfd0511a72eb879f239d6a6d":32:"28d730ea":"dafde27aa8b3076bfa16ab1d89207d339c4997f8a756cc3eb62c0b023976de808ab640ba4467f2b2ea83d238861229c73387594cd43770386512ea595a70888b4c38863472279e06b923e7cf32438199b3e054ac4bc21baa8df39ddaa207ebb17fa4cad6e83ea58c3a92ec74e6e01b0a8979af145dd31d5df29750bb91b42d45":0
GCM Selftest
gcm_selftest:

View File

@ -502,3 +502,5 @@ gcm_decrypt_and_verify:"9bb36fe25e966a075ae2c3bb43b5877679ebc379d5123c8eda3fa0e3
GCM NIST Validation (AES-256,128,1024,1024,32) #2
gcm_decrypt_and_verify:"ca264e7caecad56ee31c8bf8dde9592f753a6299e76c60ac1e93cff3b3de8ce9":"8d03cf6fac31182ad3e6f32e4c823e3b421aef786d5651afafbf70ef14c00524ab814bc421b1d4181b4d3d82d6ae4e8032e43a6c4e0691184425b37320798f865c88b9b306466311d79e3e42076837474c37c9f6336ed777f05f70b0c7d72bd4348a4cd754d0f0c3e4587f9a18313ea2d2bace502a24ea417d3041b709a0471f":"4763a4e37b806a5f4510f69fd8c63571":"07daeba37a66ebe15f3d6451d1176f3a7107a302da6966680c425377e621fd71610d1fc9c95122da5bf85f83b24c4b783b1dcd6b508d41e22c09b5c43693d072869601fc7e3f5a51dbd3bc6508e8d095b9130fb6a7f2a043f3a432e7ce68b7de06c1379e6bab5a1a48823b76762051b4e707ddc3201eb36456e3862425cb011a":32:"3105dddb":"FAIL":0
GCM Selftest
gcm_selftest:

View File

@ -501,3 +501,6 @@ gcm_encrypt_and_tag:"00d4bf20509a61bc76430ffa5f013589":"036a191a388cf3c57c9e6f0e
GCM NIST Validation (AES-128,128,1024,1024,32) #2
gcm_encrypt_and_tag:"fe481476fce76efcfc78ed144b0756f1":"246e1f2babab8da98b17cc928bd49504d7d87ea2cc174f9ffb7dbafe5969ff824a0bcb52f35441d22f3edcd10fab0ec04c0bde5abd3624ca25cbb4541b5d62a3deb52c00b75d68aaf0504d51f95b8dcbebdd8433f4966c584ac7f8c19407ca927a79fa4ead2688c4a7baafb4c31ef83c05e8848ec2b4f657aab84c109c91c277":"1a2c18c6bf13b3b2785610c71ccd98ca":"b0ab3cb5256575774b8242b89badfbe0dfdfd04f5dd75a8e5f218b28d3f6bc085a013defa5f5b15dfb46132db58ed7a9ddb812d28ee2f962796ad988561a381c02d1cf37dca5fd33e081d61cc7b3ab0b477947524a4ca4cb48c36f48b302c440be6f5777518a60585a8a16cea510dbfc5580b0daac49a2b1242ff55e91a8eae8":"5587620bbb77f70afdf3cdb7ae390edd0473286d86d3f862ad70902d90ff1d315947c959f016257a8fe1f52cc22a54f21de8cb60b74808ac7b22ea7a15945371e18b77c9571aad631aa080c60c1e472019fa85625fc80ed32a51d05e397a8987c8fece197a566689d24d05361b6f3a75616c89db6123bf5902960b21a18bc03a":32:"bd4265a8":0
GCM Selftest
gcm_selftest:

View File

@ -501,3 +501,6 @@ gcm_encrypt_and_tag:"8a7d8197d9ceebd8e3f6b3bfb74877ccf649ac91d7057af5":"37b01df3
GCM NIST Validation (AES-192,128,1024,1024,32) #2
gcm_encrypt_and_tag:"713358e746dd84ab27b8adb3b17ea59cd75fa6cb0c13d1a8":"35b8b655efdf2d09f5ed0233c9eeb0b6f85e513834848cd594dba3c6e64f78e7af4a7a6d53bba7b43764334d6373360ae3b73b1e765978dffa7dbd805fda7825b8e317e8d3f1314aa97f877be815439c5da845028d1686283735aefac79cdb9e02ec3590091cb507089b9174cd9a6111f446feead91f19b80fd222fc6299fd1c":"26ed909f5851961dd57fa950b437e17c":"c9469ad408764cb7d417f800d3d84f03080cee9bbd53f652763accde5fba13a53a12d990094d587345da2cdc99357b9afd63945ca07b760a2c2d4948dbadb1312670ccde87655a6a68edb5982d2fcf733bb4101d38cdb1a4942a5d410f4c45f5ddf00889bc1fe5ec69b40ae8aaee60ee97bea096eeef0ea71736efdb0d8a5ec9":"cc3f9983e1d673ec2c86ae4c1e1b04e30f9f395f67c36838e15ce825b05d37e9cd40041470224da345aa2da5dfb3e0c561dd05ba7984a1332541d58e8f9160e7e8457e717bab203de3161a72b7aedfa53616b16ca77fd28d566fbf7431be559caa1a129b2f29b9c5bbf3eaba594d6650c62907eb28e176f27c3be7a3aa24cef6":32:"5be7611b":0
GCM Selftest
gcm_selftest:

View File

@ -502,3 +502,5 @@ gcm_encrypt_and_tag:"b55ac909e73989e310ae37d13c54bbd5a126f419a3b01a2ad8961d89bd2
GCM NIST Validation (AES-256,128,1024,1024,32) #2
gcm_encrypt_and_tag:"1477e189fb3546efac5cc144f25e132ffd0081be76e912e25cbce7ad63f1c2c4":"7bd3ea956f4b938ebe83ef9a75ddbda16717e924dd4e45202560bf5f0cffbffcdd23be3ae08ff30503d698ed08568ff6b3f6b9fdc9ea79c8e53a838cc8566a8b52ce7c21b2b067e778925a066c970a6c37b8a6cfc53145f24bf698c352078a7f0409b53196e00c619237454c190b970842bb6629c0def7f166d19565127cbce0":"c109f35893aff139db8ed51c85fee237":"8f7f9f71a4b2bb0aaf55fced4eb43c57415526162070919b5f8c08904942181820d5847dfd54d9ba707c5e893a888d5a38d0130f7f52c1f638b0119cf7bc5f2b68f51ff5168802e561dff2cf9c5310011c809eba002b2fa348718e8a5cb732056273cc7d01cce5f5837ab0b09b6c4c5321a7f30a3a3cd21f29da79fce3f3728b":"7841e3d78746f07e5614233df7175931e3c257e09ebd7b78545fae484d835ffe3db3825d3aa1e5cc1541fe6cac90769dc5aaeded0c148b5b4f397990eb34b39ee7881804e5a66ccc8d4afe907948780c4e646cc26479e1da874394cb3537a8f303e0aa13bd3cc36f6cc40438bcd41ef8b6a1cdee425175dcd17ee62611d09b02":32:"cb13ce59":0
GCM Selftest
gcm_selftest: