summaryrefslogtreecommitdiff
path: root/cipher/cipher.c
diff options
context:
space:
mode:
Diffstat (limited to 'cipher/cipher.c')
-rw-r--r--cipher/cipher.c322
1 files changed, 232 insertions, 90 deletions
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 66470ee6..e3284355 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -1,6 +1,6 @@
/* cipher.c - cipher dispatcher
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- * 2005, 2007 Free Software Foundation, Inc.
+ * 2005, 2007, 2008 Free Software Foundation, Inc.
*
* This file is part of Libgcrypt.
*
@@ -38,51 +38,76 @@
#define NEED_16BYTE_ALIGNED_CONTEXT 1
#endif
+/* A dummy extraspec so that we do not need to tests the extraspec
+ field from the module specification against NULL and instead
+ directly test the respective fields of extraspecs. */
+static cipher_extra_spec_t dummy_extra_spec;
+
/* This is the list of the default ciphers, which are included in
libgcrypt. */
static struct cipher_table_entry
{
gcry_cipher_spec_t *cipher;
+ cipher_extra_spec_t *extraspec;
unsigned int algorithm;
+ int fips_allowed;
} cipher_table[] =
{
#if USE_BLOWFISH
- { &_gcry_cipher_spec_blowfish, GCRY_CIPHER_BLOWFISH },
+ { &_gcry_cipher_spec_blowfish,
+ &dummy_extra_spec, GCRY_CIPHER_BLOWFISH },
#endif
#if USE_DES
- { &_gcry_cipher_spec_des, GCRY_CIPHER_DES },
- { &_gcry_cipher_spec_tripledes, GCRY_CIPHER_3DES },
+ { &_gcry_cipher_spec_des,
+ &dummy_extra_spec, GCRY_CIPHER_DES },
+ { &_gcry_cipher_spec_tripledes,
+ &_gcry_cipher_extraspec_tripledes, GCRY_CIPHER_3DES, 1 },
#endif
#if USE_ARCFOUR
- { &_gcry_cipher_spec_arcfour, GCRY_CIPHER_ARCFOUR },
+ { &_gcry_cipher_spec_arcfour,
+ &dummy_extra_spec, GCRY_CIPHER_ARCFOUR },
#endif
#if USE_CAST5
- { &_gcry_cipher_spec_cast5, GCRY_CIPHER_CAST5 },
+ { &_gcry_cipher_spec_cast5,
+ &dummy_extra_spec, GCRY_CIPHER_CAST5 },
#endif
#if USE_AES
- { &_gcry_cipher_spec_aes, GCRY_CIPHER_AES},
- { &_gcry_cipher_spec_aes192, GCRY_CIPHER_AES192},
- { &_gcry_cipher_spec_aes256, GCRY_CIPHER_AES256},
+ { &_gcry_cipher_spec_aes,
+ &_gcry_cipher_extraspec_aes, GCRY_CIPHER_AES, 1 },
+ { &_gcry_cipher_spec_aes192,
+ &_gcry_cipher_extraspec_aes192, GCRY_CIPHER_AES192, 1 },
+ { &_gcry_cipher_spec_aes256,
+ &_gcry_cipher_extraspec_aes256, GCRY_CIPHER_AES256, 1 },
#endif
#if USE_TWOFISH
- { &_gcry_cipher_spec_twofish, GCRY_CIPHER_TWOFISH },
- { &_gcry_cipher_spec_twofish128, GCRY_CIPHER_TWOFISH128 },
+ { &_gcry_cipher_spec_twofish,
+ &dummy_extra_spec, GCRY_CIPHER_TWOFISH },
+ { &_gcry_cipher_spec_twofish128,
+ &dummy_extra_spec, GCRY_CIPHER_TWOFISH128 },
#endif
#if USE_SERPENT
- { &_gcry_cipher_spec_serpent128, GCRY_CIPHER_SERPENT128 },
- { &_gcry_cipher_spec_serpent192, GCRY_CIPHER_SERPENT192 },
- { &_gcry_cipher_spec_serpent256, GCRY_CIPHER_SERPENT256 },
+ { &_gcry_cipher_spec_serpent128,
+ &dummy_extra_spec, GCRY_CIPHER_SERPENT128 },
+ { &_gcry_cipher_spec_serpent192,
+ &dummy_extra_spec, GCRY_CIPHER_SERPENT192 },
+ { &_gcry_cipher_spec_serpent256,
+ &dummy_extra_spec, GCRY_CIPHER_SERPENT256 },
#endif
#if USE_RFC2268
- { &_gcry_cipher_spec_rfc2268_40, GCRY_CIPHER_RFC2268_40 },
+ { &_gcry_cipher_spec_rfc2268_40,
+ &dummy_extra_spec, GCRY_CIPHER_RFC2268_40 },
#endif
#if USE_SEED
- { &_gcry_cipher_spec_seed, GCRY_CIPHER_SEED },
+ { &_gcry_cipher_spec_seed,
+ &dummy_extra_spec, GCRY_CIPHER_SEED },
#endif
#if USE_CAMELLIA
- { &_gcry_cipher_spec_camellia128, GCRY_CIPHER_CAMELLIA128 },
- { &_gcry_cipher_spec_camellia192, GCRY_CIPHER_CAMELLIA192 },
- { &_gcry_cipher_spec_camellia256, GCRY_CIPHER_CAMELLIA256 },
+ { &_gcry_cipher_spec_camellia128,
+ &dummy_extra_spec, GCRY_CIPHER_CAMELLIA128 },
+ { &_gcry_cipher_spec_camellia192,
+ &dummy_extra_spec, GCRY_CIPHER_CAMELLIA192 },
+ { &_gcry_cipher_spec_camellia256,
+ &dummy_extra_spec, GCRY_CIPHER_CAMELLIA256 },
#endif
{ NULL }
};
@@ -104,7 +129,7 @@ static int default_ciphers_registered;
ath_mutex_lock (&ciphers_registered_lock); \
if (! default_ciphers_registered) \
{ \
- gcry_cipher_register_default (); \
+ cipher_register_default (); \
default_ciphers_registered = 1; \
} \
ath_mutex_unlock (&ciphers_registered_lock); \
@@ -251,7 +276,7 @@ dummy_decrypt_stream (void *c,
CIPHER_TABLE. Note, that this function gets only used by the macro
REGISTER_DEFAULT_CIPHERS which protects it using a mutex. */
static void
-gcry_cipher_register_default (void)
+cipher_register_default (void)
{
gcry_err_code_t err = GPG_ERR_NO_ERROR;
int i;
@@ -269,9 +294,13 @@ gcry_cipher_register_default (void)
if (! cipher_table[i].cipher->stdecrypt)
cipher_table[i].cipher->stdecrypt = dummy_decrypt_stream;
+ if ( fips_mode () && !cipher_table[i].fips_allowed )
+ continue;
+
err = _gcry_module_add (&ciphers_registered,
cipher_table[i].algorithm,
(void *) cipher_table[i].cipher,
+ (void *) cipher_table[i].extraspec,
NULL);
}
@@ -340,16 +369,23 @@ gcry_cipher_lookup_oid (const char *oid)
CIPHER. On success, a new algorithm ID is stored in ALGORITHM_ID
and a pointer representhing this module is stored in MODULE. */
gcry_error_t
-gcry_cipher_register (gcry_cipher_spec_t *cipher,
- int *algorithm_id,
- gcry_module_t *module)
+_gcry_cipher_register (gcry_cipher_spec_t *cipher,
+ cipher_extra_spec_t *extraspec,
+ int *algorithm_id,
+ gcry_module_t *module)
{
gcry_err_code_t err = 0;
gcry_module_t mod;
+ /* We do not support module loading in fips mode. */
+ if (fips_mode ())
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
ath_mutex_lock (&ciphers_registered_lock);
err = _gcry_module_add (&ciphers_registered, 0,
- (void *) cipher, &mod);
+ (void *)cipher,
+ (void *)(extraspec? extraspec : &dummy_extra_spec),
+ &mod);
ath_mutex_unlock (&ciphers_registered_lock);
if (! err)
@@ -529,8 +565,9 @@ disable_cipher_algo (int algorithm)
}
-/* Return 0 if the cipher algorithm with indentifier ALGORITHM is
- available. Returns a basic error code value if it is not available. */
+/* Return 0 if the cipher algorithm with identifier ALGORITHM is
+ available. Returns a basic error code value if it is not
+ available. */
static gcry_err_code_t
check_cipher_algo (int algorithm)
{
@@ -572,7 +609,7 @@ cipher_get_keylen (int algorithm)
if (cipher)
{
len = ((gcry_cipher_spec_t *) cipher->spec)->keylen;
- if (! len)
+ if (!len)
log_bug ("cipher %d w/o key length\n", algorithm);
_gcry_module_release (cipher);
}
@@ -793,7 +830,7 @@ gcry_cipher_close (gcry_cipher_hd_t h)
{
size_t off;
- if (! h)
+ if (!h)
return;
if ((h->magic != CTX_MAGIC_SECURE)
@@ -824,16 +861,18 @@ gcry_cipher_close (gcry_cipher_hd_t h)
/* Set the key to be used for the encryption context C to KEY with
length KEYLEN. The length should match the required length. */
static gcry_error_t
-cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned keylen)
+cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen)
{
gcry_err_code_t ret;
ret = (*c->cipher->setkey) (&c->context.c, key, keylen);
- if (! ret)
- /* Duplicate initial context. */
- memcpy ((void *) ((char *) &c->context.c + c->cipher->contextsize),
- (void *) &c->context.c,
- c->cipher->contextsize);
+ if (!ret)
+ {
+ /* Duplicate initial context. */
+ memcpy ((void *) ((char *) &c->context.c + c->cipher->contextsize),
+ (void *) &c->context.c,
+ c->cipher->contextsize);
+ }
return gcry_error (ret);
}
@@ -848,8 +887,11 @@ cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen )
if (iv)
{
if (ivlen != c->cipher->blocksize)
- log_info ("WARNING: cipher_setiv: ivlen=%u blklen=%u\n",
- ivlen, (unsigned int)c->cipher->blocksize);
+ {
+ log_info ("WARNING: cipher_setiv: ivlen=%u blklen=%u\n",
+ ivlen, (unsigned int)c->cipher->blocksize);
+ fips_signal_error ("IV length does not match blocklength");
+ }
if (ivlen > c->cipher->blocksize)
ivlen = c->cipher->blocksize;
memcpy (c->u_iv.iv, iv, ivlen);
@@ -876,12 +918,13 @@ static void
do_ecb_encrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
unsigned int nblocks )
{
- unsigned int n;
-
- for(n=0; n < nblocks; n++ ) {
- c->cipher->encrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
- inbuf += c->cipher->blocksize;
- outbuf += c->cipher->blocksize;
+ unsigned int n;
+
+ for (n=0; n < nblocks; n++ )
+ {
+ c->cipher->encrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
+ inbuf += c->cipher->blocksize;
+ outbuf += c->cipher->blocksize;
}
}
@@ -889,12 +932,13 @@ static void
do_ecb_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
unsigned int nblocks )
{
- unsigned n;
+ unsigned int n;
- for(n=0; n < nblocks; n++ ) {
- c->cipher->decrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
- inbuf += c->cipher->blocksize;
- outbuf += c->cipher->blocksize;
+ for (n=0; n < nblocks; n++ )
+ {
+ c->cipher->decrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
+ inbuf += c->cipher->blocksize;
+ outbuf += c->cipher->blocksize;
}
}
@@ -994,7 +1038,7 @@ do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
for (n=0; n < nblocks; n++ )
{
/* Because outbuf and inbuf might be the same, we have to
- * save the original ciphertext block. We use lastiv for
+ * save the original ciphertext block. We use LASTIV for
* this here because it is not used otherwise. */
memcpy (c->lastiv, inbuf, blocksize);
c->cipher->decrypt ( &c->context.c, outbuf, inbuf );
@@ -1400,10 +1444,12 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
gcry_err_code_t err;
if (!in)
- /* Caller requested in-place encryption. */
- /* Actually cipher_encrypt() does not need to know about it, but
- * we may change it in the future to get better performance. */
- err = cipher_encrypt (h, out, out, outsize);
+ {
+ /* Caller requested in-place encryption. */
+ /* Actually cipher_encrypt() does not need to know about it, but
+ * we may change it in the future to get better performance. */
+ err = cipher_encrypt (h, out, out, outsize);
+ }
else if (outsize < ((h->flags & GCRY_CIPHER_CBC_MAC) ?
h->cipher->blocksize : inlen))
err = GPG_ERR_TOO_SHORT;
@@ -1485,10 +1531,12 @@ gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
gcry_err_code_t err = 0;
if (!in)
- /* Caller requested in-place encryption. */
- /* Actually cipher_encrypt() does not need to know about it, but
- * we may change it in the future to get better performance. */
- err = cipher_decrypt (h, out, out, outsize);
+ {
+ /* Caller requested in-place encryption. */
+ /* Actually cipher_encrypt() does not need to know about it, but
+ * we may change it in the future to get better performance. */
+ err = cipher_decrypt (h, out, out, outsize);
+ }
else if (outsize < inlen)
err = GPG_ERR_TOO_SHORT;
else if (((h->mode == GCRY_CIPHER_MODE_ECB)
@@ -1510,7 +1558,7 @@ gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
* the corresponding flag is set.
*/
static void
-cipher_sync( gcry_cipher_hd_t c )
+cipher_sync (gcry_cipher_hd_t c)
{
if ((c->flags & GCRY_CIPHER_ENABLE_SYNC) && c->unused)
{
@@ -1524,24 +1572,58 @@ cipher_sync( gcry_cipher_hd_t c )
gcry_error_t
+_gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *key, size_t keylen)
+{
+ return cipher_setkey (hd, (void*)key, keylen);
+}
+
+
+gcry_error_t
+_gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
+{
+ cipher_setiv (hd, iv, ivlen);
+ return 0;
+}
+
+/* Set counter for CTR mode. (CTR,CTRLEN) must denote a buffer of
+ block size length, or (NULL,0) to set the CTR to the all-zero
+ block. */
+gpg_error_t
+_gcry_cipher_setctr (gcry_cipher_hd_t hd, const void *ctr, size_t ctrlen)
+{
+ if (ctr && ctrlen == hd->cipher->blocksize)
+ memcpy (hd->ctr, ctr, hd->cipher->blocksize);
+ else if (!ctr || !ctrlen)
+ memset (hd->ctr, 0, hd->cipher->blocksize);
+ else
+ return gpg_error (GPG_ERR_INV_ARG);
+ return 0;
+}
+
+
+gcry_error_t
gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
{
gcry_err_code_t rc = GPG_ERR_NO_ERROR;
switch (cmd)
{
- case GCRYCTL_SET_KEY:
+ case GCRYCTL_SET_KEY: /* Deprecated; use gcry_cipher_setkey. */
rc = cipher_setkey( h, buffer, buflen );
break;
- case GCRYCTL_SET_IV:
+
+ case GCRYCTL_SET_IV: /* Deprecated; use gcry_cipher_setiv. */
cipher_setiv( h, buffer, buflen );
break;
+
case GCRYCTL_RESET:
cipher_reset (h);
break;
+
case GCRYCTL_CFB_SYNC:
cipher_sync( h );
break;
+
case GCRYCTL_SET_CBC_CTS:
if (buflen)
if (h->flags & GCRY_CIPHER_CBC_MAC)
@@ -1551,6 +1633,7 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
else
h->flags &= ~GCRY_CIPHER_CBC_CTS;
break;
+
case GCRYCTL_SET_CBC_MAC:
if (buflen)
if (h->flags & GCRY_CIPHER_CBC_CTS)
@@ -1560,15 +1643,16 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
else
h->flags &= ~GCRY_CIPHER_CBC_MAC;
break;
+
case GCRYCTL_DISABLE_ALGO:
- /* this one expects a NULL handle and buffer pointing to an
- * integer with the algo number.
- */
+ /* This command expects NULL for H and BUFFER to point to an
+ integer with the algo number. */
if( h || !buffer || buflen != sizeof(int) )
return gcry_error (GPG_ERR_CIPHER_ALGO);
disable_cipher_algo( *(int*)buffer );
break;
- case GCRYCTL_SET_CTR:
+
+ case GCRYCTL_SET_CTR: /* Deprecated; use gcry_cipher_setctr. */
if (buffer && buflen == h->cipher->blocksize)
memcpy (h->ctr, buffer, h->cipher->blocksize);
else if (buffer == NULL || buflen == 0)
@@ -1585,11 +1669,16 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
}
-/****************
- * Return information about the cipher handle.
+/* Return information about the cipher handle H. CMD is the kind of
+ information requested. BUFFER and NBYTES are reserved for now.
+
+ There are no values for CMD yet defined.
+
+ The fucntion always returns GPG_ERR_INV_OP.
+
*/
gcry_error_t
-gcry_cipher_info( gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
+gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
{
gcry_err_code_t err = GPG_ERR_NO_ERROR;
@@ -1606,27 +1695,29 @@ gcry_cipher_info( gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
return gcry_error (err);
}
-/****************
- * Return information about the given cipher algorithm
- * WHAT select the kind of information returned:
- * GCRYCTL_GET_KEYLEN:
- * Return the length of the key, if the algorithm
- * supports multiple key length, the maximum supported value
- * is returnd. The length is return as number of octets.
- * buffer and nbytes must be zero.
- * The keylength is returned in _bytes_.
- * GCRYCTL_GET_BLKLEN:
- * Return the blocklength of the algorithm counted in octets.
- * buffer and nbytes must be zero.
- * GCRYCTL_TEST_ALGO:
- * Returns 0 when the specified algorithm is available for use.
- * buffer and nbytes must be zero.
- *
- * Note: Because this function is in most cases used to return an
- * integer value, we can make it easier for the caller to just look at
- * the return value. The caller will in all cases consult the value
- * and thereby detecting whether a error occured or not (i.e. while checking
- * the block size)
+/* Return information about the given cipher algorithm ALGO.
+
+ WHAT select the kind of information returned:
+
+ GCRYCTL_GET_KEYLEN:
+ Return the length of the key. If the algorithm ALGO
+ supports multiple key lengths, the maximum supported key length
+ is returned. The key length is returned as number of octets.
+ BUFFER and NBYTES must be zero.
+
+ GCRYCTL_GET_BLKLEN:
+ Return the blocklength of the algorithm ALGO counted in octets.
+ BUFFER and NBYTES must be zero.
+
+ GCRYCTL_TEST_ALGO:
+ Returns 0 if the specified algorithm ALGO is available for use.
+ BUFFER and NBYTES must be zero.
+
+ Note: Because this function is in most cases used to return an
+ integer value, we can make it easier for the caller to just look at
+ the return value. The caller will in all cases consult the value
+ and thereby detecting whether a error occured or not (i.e. while
+ checking the block size)
*/
gcry_error_t
gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
@@ -1681,17 +1772,30 @@ gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
}
+/* This function returns length of the key for algorithm ALGO. If the
+ algorithm supports multiple key lengths, the maximum supported key
+ length is returned. On error 0 is returned. The key length is
+ returned as number of octets.
+
+ This is a convenience functions which should be preferred over
+ gcry_cipher_algo_info because it allows for proper type
+ checking. */
size_t
gcry_cipher_get_algo_keylen (int algo)
{
size_t n;
- if (gcry_cipher_algo_info( algo, GCRYCTL_GET_KEYLEN, NULL, &n))
+ if (gcry_cipher_algo_info (algo, GCRYCTL_GET_KEYLEN, NULL, &n))
n = 0;
return n;
}
+/* This functions returns the blocklength of the algorithm ALGO
+ counted in octets. On error 0 is returned.
+ This is a convenience functions which should be preferred over
+ gcry_cipher_algo_info because it allows for proper type
+ checking. */
size_t
gcry_cipher_get_algo_blklen (int algo)
{
@@ -1702,7 +1806,7 @@ gcry_cipher_get_algo_blklen (int algo)
return n;
}
-
+/* Explicitly initialize this module. */
gcry_err_code_t
_gcry_cipher_init (void)
{
@@ -1730,3 +1834,41 @@ gcry_cipher_list (int *list, int *list_length)
return err;
}
+
+
+/* Run the selftests for cipher algorithm ALGO with optional reporting
+ function REPORT. */
+gpg_error_t
+_gcry_cipher_selftest (int algo, selftest_report_func_t report)
+{
+ gcry_module_t module = NULL;
+ cipher_extra_spec_t *extraspec = NULL;
+ gcry_err_code_t ec = 0;
+
+ REGISTER_DEFAULT_CIPHERS;
+
+ ath_mutex_lock (&ciphers_registered_lock);
+ module = _gcry_module_lookup_id (ciphers_registered, algo);
+ if (module && !(module->flags & FLAG_MODULE_DISABLED))
+ extraspec = module->extraspec;
+ ath_mutex_unlock (&ciphers_registered_lock);
+ if (extraspec && extraspec->selftest)
+ ec = extraspec->selftest (algo, report);
+ else
+ {
+ ec = GPG_ERR_CIPHER_ALGO;
+ if (report)
+ report ("cipher", algo, "module",
+ module && !(module->flags & FLAG_MODULE_DISABLED)?
+ "no selftest available" :
+ module? "algorithm disabled" : "algorithm not found");
+ }
+
+ if (module)
+ {
+ ath_mutex_lock (&ciphers_registered_lock);
+ _gcry_module_release (module);
+ ath_mutex_unlock (&ciphers_registered_lock);
+ }
+ return gpg_error (ec);
+}