view libtomcrypt/src/misc/pkcs5/pkcs_5_1.c @ 1672:3a97f14c0235

Add Chacha20-Poly1305, AES128-GCM and AES256-GCM support (#93) * Add Chacha20-Poly1305 authenticated encryption * Add general AEAD approach. * Add [email protected] algo using LibTomCrypt chacha and poly1305 routines. Chacha20-Poly1305 is generally faster than AES256 on CPU w/o dedicated AES instructions, having the same key size. Compiling in will add ~5,5kB to binary size on x86-64. function old new delta chacha_crypt - 1397 +1397 _poly1305_block - 608 +608 poly1305_done - 595 +595 dropbear_chachapoly_crypt - 457 +457 .rodata 26976 27392 +416 poly1305_process - 290 +290 poly1305_init - 221 +221 chacha_setup - 218 +218 encrypt_packet 1068 1270 +202 dropbear_chachapoly_getlength - 147 +147 decrypt_packet 756 897 +141 chacha_ivctr64 - 137 +137 read_packet 543 637 +94 dropbear_chachapoly_start - 94 +94 read_kex_algos 792 880 +88 chacha_keystream - 69 +69 dropbear_mode_chachapoly - 48 +48 sshciphers 280 320 +40 dropbear_mode_none 24 48 +24 dropbear_mode_ctr 24 48 +24 dropbear_mode_cbc 24 48 +24 dropbear_chachapoly_mac - 24 +24 dropbear_chachapoly - 24 +24 gen_new_keys 848 854 +6 ------------------------------------------------------------------------------ (add/remove: 14/0 grow/shrink: 10/0 up/down: 5388/0) Total: 5388 bytes * Add AES128-GCM and AES256-GCM authenticated encryption * Add general AES-GCM mode. * Add [email protected] and [email protected] algo using LibTomCrypt gcm routines. AES-GCM is combination of AES CTR mode and GHASH, slower than AES-CTR on CPU w/o dedicated AES/GHASH instructions therefore disabled by default. Compiling in will add ~6kB to binary size on x86-64. function old new delta gcm_process - 1060 +1060 .rodata 26976 27808 +832 gcm_gf_mult - 820 +820 gcm_add_aad - 660 +660 gcm_shift_table - 512 +512 gcm_done - 471 +471 gcm_add_iv - 384 +384 gcm_init - 347 +347 dropbear_gcm_crypt - 309 +309 encrypt_packet 1068 1270 +202 decrypt_packet 756 897 +141 gcm_reset - 118 +118 read_packet 543 637 +94 read_kex_algos 792 880 +88 sshciphers 280 360 +80 gcm_mult_h - 80 +80 dropbear_gcm_start - 62 +62 dropbear_mode_gcm - 48 +48 dropbear_mode_none 24 48 +24 dropbear_mode_ctr 24 48 +24 dropbear_mode_cbc 24 48 +24 dropbear_ghash - 24 +24 dropbear_gcm_getlength - 24 +24 gen_new_keys 848 854 +6 ------------------------------------------------------------------------------ (add/remove: 14/0 grow/shrink: 10/0 up/down: 6434/0) Total: 6434 bytes
author Vladislav Grishenko <themiron@users.noreply.github.com>
date Mon, 25 May 2020 20:50:25 +0500
parents 6dba84798cd5
children
line wrap: on
line source

/* LibTomCrypt, modular cryptographic library -- Tom St Denis
 *
 * LibTomCrypt is a library that provides various cryptographic
 * algorithms in a highly modular and flexible manner.
 *
 * The library is free for all purposes without any express
 * guarantee it works.
 */
#include "tomcrypt.h"

/**
   @file pkcs_5_1.c
   PKCS #5, Algorithm #1, Tom St Denis
*/
#ifdef LTC_PKCS_5
/**
   Execute PKCS #5 v1 in strict or OpenSSL EVP_BytesToKey()-compat mode.

   PKCS#5 v1 specifies that the output key length can be no larger than
   the hash output length.  OpenSSL unilaterally extended that by repeating
   the hash process on a block-by-block basis for as long as needed to make
   bigger keys.  If you want to be compatible with KDF for e.g. "openssl enc",
   you'll want that.

   If you want strict PKCS behavior, turn openssl_compat off.  Or (more
   likely), use one of the convenience functions below.

   @param password         The password (or key)
   @param password_len     The length of the password (octet)
   @param salt             The salt (or nonce) which is 8 octets long
   @param iteration_count  The PKCS #5 v1 iteration count
   @param hash_idx         The index of the hash desired
   @param out              [out] The destination for this algorithm
   @param outlen           [in/out] The max size and resulting size of the algorithm output
   @param openssl_compat   [in] Whether or not to grow the key to the buffer size ala OpenSSL
   @return CRYPT_OK if successful
*/
static int _pkcs_5_alg1_common(const unsigned char *password,
                       unsigned long password_len,
                       const unsigned char *salt,
                       int iteration_count,  int hash_idx,
                       unsigned char *out,   unsigned long *outlen,
                       int openssl_compat)
{
   int err;
   unsigned long x;
   hash_state    *md;
   unsigned char *buf;
   /* Storage vars in case we need to support > hashsize (OpenSSL compat) */
   unsigned long block = 0, iter;
   /* How many bytes to put in the outbut buffer (convenience calc) */
   unsigned long outidx = 0, nb = 0;

   LTC_ARGCHK(password != NULL);
   LTC_ARGCHK(salt     != NULL);
   LTC_ARGCHK(out      != NULL);
   LTC_ARGCHK(outlen   != NULL);

   /* test hash IDX */
   if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
      return err;
   }

   /* allocate memory */
   md  = XMALLOC(sizeof(hash_state));
   buf = XMALLOC(MAXBLOCKSIZE);
   if (md == NULL || buf == NULL) {
      if (md != NULL) {
         XFREE(md);
      }
      if (buf != NULL) {
         XFREE(buf);
      }
      return CRYPT_MEM;
   }

   while(block * hash_descriptor[hash_idx].hashsize < *outlen) {

      /* hash initial (maybe previous hash) + password + salt */
      if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
          goto LBL_ERR;
      }
      /* in OpenSSL mode, we first hash the previous result for blocks 2-n */
      if (openssl_compat && block) {
          if ((err = hash_descriptor[hash_idx].process(md, buf, hash_descriptor[hash_idx].hashsize)) != CRYPT_OK) {
              goto LBL_ERR;
          }
      }
      if ((err = hash_descriptor[hash_idx].process(md, password, password_len)) != CRYPT_OK) {
          goto LBL_ERR;
      }
      if ((err = hash_descriptor[hash_idx].process(md, salt, 8)) != CRYPT_OK) {
          goto LBL_ERR;
      }
      if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) {
          goto LBL_ERR;
      }

      iter = iteration_count;
      while (--iter) {
         /* code goes here. */
         x = MAXBLOCKSIZE;
         if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
            goto LBL_ERR;
         }
      }

      /* limit the size of the copy to however many bytes we have left in
         the output buffer (and how many bytes we have to copy) */
      outidx = block*hash_descriptor[hash_idx].hashsize;
      nb = hash_descriptor[hash_idx].hashsize;
      if(outidx+nb > *outlen)
          nb = *outlen - outidx;
      if(nb > 0)
          XMEMCPY(out+outidx, buf, nb);

      block++;
      if (!openssl_compat)
          break;
   }
   /* In strict mode, we always return the hashsize, in compat we filled it
      as much as was requested, so we leave it alone. */
   if(!openssl_compat)
      *outlen = hash_descriptor[hash_idx].hashsize;

   err = CRYPT_OK;
LBL_ERR:
#ifdef LTC_CLEAN_STACK
   zeromem(buf, MAXBLOCKSIZE);
   zeromem(md, sizeof(hash_state));
#endif

   XFREE(buf);
   XFREE(md);

   return err;
}

/**
   Execute PKCS #5 v1 - Strict mode (no OpenSSL-compatible extension)
   @param password         The password (or key)
   @param password_len     The length of the password (octet)
   @param salt             The salt (or nonce) which is 8 octets long
   @param iteration_count  The PKCS #5 v1 iteration count
   @param hash_idx         The index of the hash desired
   @param out              [out] The destination for this algorithm
   @param outlen           [in/out] The max size and resulting size of the algorithm output
   @return CRYPT_OK if successful
*/
int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
                const unsigned char *salt,
                int iteration_count,  int hash_idx,
                unsigned char *out,   unsigned long *outlen)
{
   return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
                             hash_idx, out, outlen, 0);
}

/**
   Execute PKCS #5 v1 - OpenSSL-extension-compatible mode

   Use this one if you need to derive keys as "openssl enc" does by default.
   OpenSSL (for better or worse), uses MD5 as the hash and iteration_count=1.
   @param password         The password (or key)
   @param password_len     The length of the password (octet)
   @param salt             The salt (or nonce) which is 8 octets long
   @param iteration_count  The PKCS #5 v1 iteration count
   @param hash_idx         The index of the hash desired
   @param out              [out] The destination for this algorithm
   @param outlen           [in/out] The max size and resulting size of the algorithm output
   @return CRYPT_OK if successful
*/
int pkcs_5_alg1_openssl(const unsigned char *password,
                        unsigned long password_len,
                        const unsigned char *salt,
                        int iteration_count,  int hash_idx,
                        unsigned char *out,   unsigned long *outlen)
{
   return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
                             hash_idx, out, outlen, 1);
}

#endif

/* ref:         $Format:%D$ */
/* git commit:  $Format:%H$ */
/* commit time: $Format:%ai$ */