mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-11-04 09:14:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			279 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* kdf.c  - Key Derivation Functions
 | 
						|
 * Copyright (C) 1998, 2011 Free Software Foundation, Inc.
 | 
						|
 *
 | 
						|
 * This file is part of Libgcrypt.
 | 
						|
 *
 | 
						|
 * Libgcrypt is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU Lesser general Public License as
 | 
						|
 * published by the Free Software Foundation; either version 2.1 of
 | 
						|
 * the License, or (at your option) any later version.
 | 
						|
 *
 | 
						|
 * Libgcrypt is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU Lesser General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Lesser General Public
 | 
						|
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include <config.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
#include "g10lib.h"
 | 
						|
#include "cipher.h"
 | 
						|
#include "ath.h"
 | 
						|
 | 
						|
 | 
						|
/* Transform a passphrase into a suitable key of length KEYSIZE and
 | 
						|
   store this key in the caller provided buffer KEYBUFFER.  The caller
 | 
						|
   must provide an HASHALGO, a valid ALGO and depending on that algo a
 | 
						|
   SALT of 8 bytes and the number of ITERATIONS.  Code taken from
 | 
						|
   gnupg/agent/protect.c:hash_passphrase.  */
 | 
						|
gpg_err_code_t
 | 
						|
openpgp_s2k (const void *passphrase, size_t passphraselen,
 | 
						|
             int algo, int hashalgo,
 | 
						|
             const void *salt, size_t saltlen,
 | 
						|
             unsigned long iterations,
 | 
						|
             size_t keysize, void *keybuffer)
 | 
						|
{
 | 
						|
  gpg_err_code_t ec;
 | 
						|
  gcry_md_hd_t md;
 | 
						|
  char *key = keybuffer;
 | 
						|
  int pass, i;
 | 
						|
  int used = 0;
 | 
						|
  int secmode;
 | 
						|
 | 
						|
  if ((algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K)
 | 
						|
      && (!salt || saltlen != 8))
 | 
						|
    return GPG_ERR_INV_VALUE;
 | 
						|
 | 
						|
  secmode = gcry_is_secure (passphrase) || gcry_is_secure (keybuffer);
 | 
						|
 | 
						|
  ec = gpg_err_code (gcry_md_open (&md, hashalgo,
 | 
						|
                                   secmode? GCRY_MD_FLAG_SECURE : 0));
 | 
						|
  if (ec)
 | 
						|
    return ec;
 | 
						|
 | 
						|
  for (pass=0; used < keysize; pass++)
 | 
						|
    {
 | 
						|
      if (pass)
 | 
						|
        {
 | 
						|
          gcry_md_reset (md);
 | 
						|
          for (i=0; i < pass; i++) /* Preset the hash context.  */
 | 
						|
            gcry_md_putc (md, 0);
 | 
						|
	}
 | 
						|
 | 
						|
      if (algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K)
 | 
						|
        {
 | 
						|
          int len2 = passphraselen + 8;
 | 
						|
          unsigned long count = len2;
 | 
						|
 | 
						|
          if (algo == GCRY_KDF_ITERSALTED_S2K)
 | 
						|
            {
 | 
						|
              count = iterations;
 | 
						|
              if (count < len2)
 | 
						|
                count = len2;
 | 
						|
            }
 | 
						|
 | 
						|
          while (count > len2)
 | 
						|
            {
 | 
						|
              gcry_md_write (md, salt, saltlen);
 | 
						|
              gcry_md_write (md, passphrase, passphraselen);
 | 
						|
              count -= len2;
 | 
						|
            }
 | 
						|
          if (count < saltlen)
 | 
						|
            gcry_md_write (md, salt, count);
 | 
						|
          else
 | 
						|
            {
 | 
						|
              gcry_md_write (md, salt, saltlen);
 | 
						|
              count -= saltlen;
 | 
						|
              gcry_md_write (md, passphrase, count);
 | 
						|
            }
 | 
						|
        }
 | 
						|
      else
 | 
						|
        gcry_md_write (md, passphrase, passphraselen);
 | 
						|
 | 
						|
      gcry_md_final (md);
 | 
						|
      i = gcry_md_get_algo_dlen (hashalgo);
 | 
						|
      if (i > keysize - used)
 | 
						|
        i = keysize - used;
 | 
						|
      memcpy (key+used, gcry_md_read (md, hashalgo), i);
 | 
						|
      used += i;
 | 
						|
    }
 | 
						|
  gcry_md_close (md);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Transform a passphrase into a suitable key of length KEYSIZE and
 | 
						|
   store this key in the caller provided buffer KEYBUFFER.  The caller
 | 
						|
   must provide PRFALGO which indicates the pseudorandom function to
 | 
						|
   use: This shall be the algorithms id of a hash algorithm; it is
 | 
						|
   used in HMAC mode.  SALT is a salt of length SALTLEN and ITERATIONS
 | 
						|
   gives the number of iterations.  */
 | 
						|
gpg_err_code_t
 | 
						|
pkdf2 (const void *passphrase, size_t passphraselen,
 | 
						|
       int hashalgo,
 | 
						|
       const void *salt, size_t saltlen,
 | 
						|
       unsigned long iterations,
 | 
						|
       size_t keysize, void *keybuffer)
 | 
						|
{
 | 
						|
  gpg_err_code_t ec;
 | 
						|
  gcry_md_hd_t md;
 | 
						|
  int secmode;
 | 
						|
  unsigned int dklen = keysize;
 | 
						|
  char *dk = keybuffer;
 | 
						|
  unsigned int hlen;   /* Output length of the digest function.  */
 | 
						|
  unsigned int l;      /* Rounded up number of blocks.  */
 | 
						|
  unsigned int r;      /* Number of octets in the last block.  */
 | 
						|
  char *sbuf;          /* Malloced buffer to concatenate salt and iter
 | 
						|
                          as well as space to hold TBUF and UBUF.  */
 | 
						|
  char *tbuf;          /* Buffer for T; ptr into SBUF, size is HLEN. */
 | 
						|
  char *ubuf;          /* Buffer for U; ptr into SBUF, size is HLEN. */
 | 
						|
  unsigned int lidx;   /* Current block number.  */
 | 
						|
  unsigned long iter;  /* Current iteration number.  */
 | 
						|
  unsigned int i;
 | 
						|
 | 
						|
  if (!salt || !saltlen || !iterations || !dklen)
 | 
						|
    return GPG_ERR_INV_VALUE;
 | 
						|
 | 
						|
  hlen = gcry_md_get_algo_dlen (hashalgo);
 | 
						|
  if (!hlen)
 | 
						|
    return GPG_ERR_DIGEST_ALGO;
 | 
						|
 | 
						|
  secmode = gcry_is_secure (passphrase) || gcry_is_secure (keybuffer);
 | 
						|
 | 
						|
  /* We ignore step 1 from pksc5v2.1 which demands a check that dklen
 | 
						|
     is not larger that 0xffffffff * hlen.  */
 | 
						|
 | 
						|
  /* Step 2 */
 | 
						|
  l = ((dklen - 1)/ hlen) + 1;
 | 
						|
  r = dklen - (l - 1) * hlen;
 | 
						|
 | 
						|
  /* Setup buffers and prepare a hash context.  */
 | 
						|
  sbuf = (secmode
 | 
						|
          ? gcry_malloc_secure (saltlen + 4 + hlen + hlen)
 | 
						|
          : gcry_malloc (saltlen + 4 + hlen + hlen));
 | 
						|
  if (!sbuf)
 | 
						|
    return gpg_err_code_from_syserror ();
 | 
						|
  tbuf = sbuf + saltlen + 4;
 | 
						|
  ubuf = tbuf + hlen;
 | 
						|
 | 
						|
  ec = gpg_err_code (gcry_md_open (&md, hashalgo,
 | 
						|
                                   (GCRY_MD_FLAG_HMAC
 | 
						|
                                    | (secmode?GCRY_MD_FLAG_SECURE:0))));
 | 
						|
  if (ec)
 | 
						|
    {
 | 
						|
      gcry_free (sbuf);
 | 
						|
      return ec;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Step 3 and 4. */
 | 
						|
  memcpy (sbuf, salt, saltlen);
 | 
						|
  for (lidx = 1; lidx <= l; lidx++)
 | 
						|
    {
 | 
						|
      for (iter = 0; iter < iterations; iter++)
 | 
						|
        {
 | 
						|
          ec = gpg_err_code (gcry_md_setkey (md, passphrase, passphraselen));
 | 
						|
          if (ec)
 | 
						|
            {
 | 
						|
              gcry_md_close (md);
 | 
						|
              gcry_free (sbuf);
 | 
						|
              return ec;
 | 
						|
            }
 | 
						|
          if (!iter) /* Compute U_1:  */
 | 
						|
            {
 | 
						|
              sbuf[saltlen]     = (lidx >> 24);
 | 
						|
              sbuf[saltlen + 1] = (lidx >> 16);
 | 
						|
              sbuf[saltlen + 2] = (lidx >> 8);
 | 
						|
              sbuf[saltlen + 3] = lidx;
 | 
						|
              gcry_md_write (md, sbuf, saltlen + 4);
 | 
						|
              memcpy (ubuf, gcry_md_read (md, 0), hlen);
 | 
						|
              memcpy (tbuf, ubuf, hlen);
 | 
						|
            }
 | 
						|
          else /* Compute U_(2..c):  */
 | 
						|
            {
 | 
						|
              gcry_md_write (md, ubuf, hlen);
 | 
						|
              memcpy (ubuf, gcry_md_read (md, 0), hlen);
 | 
						|
              for (i=0; i < hlen; i++)
 | 
						|
                tbuf[i] ^= ubuf[i];
 | 
						|
            }
 | 
						|
        }
 | 
						|
      if (lidx == l)  /* Last block.  */
 | 
						|
        memcpy (dk, tbuf, r);
 | 
						|
      else
 | 
						|
        {
 | 
						|
          memcpy (dk, tbuf, hlen);
 | 
						|
          dk += hlen;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
  gcry_md_close (md);
 | 
						|
  gcry_free (sbuf);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Derive a key from a passphrase.  KEYSIZE gives the requested size
 | 
						|
   of the keys in octets.  KEYBUFFER is a caller provided buffer
 | 
						|
   filled on success with the derived key.  The input passphrase is
 | 
						|
   taken from (PASSPHRASE,PASSPHRASELEN) which is an arbitrary memory
 | 
						|
   buffer.  ALGO specifies the KDF algorithm to use; these are the
 | 
						|
   constants GCRY_KDF_*.  SUBALGO specifies an algorithm used
 | 
						|
   internally by the KDF algorithms; this is usually a hash algorithm
 | 
						|
   but certain KDF algorithm may use it differently.  {SALT,SALTLEN}
 | 
						|
   is a salt as needed by most KDF algorithms.  ITERATIONS is a
 | 
						|
   positive integer parameter to most KDFs.  0 is returned on success,
 | 
						|
   or an error code on failure.  */
 | 
						|
gpg_error_t
 | 
						|
gcry_kdf_derive (const void *passphrase, size_t passphraselen,
 | 
						|
                 int algo, int subalgo,
 | 
						|
                 const void *salt, size_t saltlen,
 | 
						|
                 unsigned long iterations,
 | 
						|
                 size_t keysize, void *keybuffer)
 | 
						|
{
 | 
						|
  gpg_err_code_t ec;
 | 
						|
 | 
						|
  if (!passphrase || (!passphraselen && algo != GCRY_KDF_PBKDF2))
 | 
						|
    {
 | 
						|
      ec = GPG_ERR_INV_DATA;
 | 
						|
      goto leave;
 | 
						|
    }
 | 
						|
  if (!keybuffer || !keysize)
 | 
						|
    {
 | 
						|
      ec = GPG_ERR_INV_VALUE;
 | 
						|
      goto leave;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
  switch (algo)
 | 
						|
    {
 | 
						|
    case GCRY_KDF_SIMPLE_S2K:
 | 
						|
    case GCRY_KDF_SALTED_S2K:
 | 
						|
    case GCRY_KDF_ITERSALTED_S2K:
 | 
						|
      ec = openpgp_s2k (passphrase, passphraselen, algo, subalgo,
 | 
						|
                        salt, saltlen, iterations, keysize, keybuffer);
 | 
						|
      break;
 | 
						|
 | 
						|
    case GCRY_KDF_PBKDF1:
 | 
						|
      ec = GPG_ERR_UNSUPPORTED_ALGORITHM;
 | 
						|
      break;
 | 
						|
 | 
						|
    case GCRY_KDF_PBKDF2:
 | 
						|
      ec = pkdf2 (passphrase, passphraselen, subalgo,
 | 
						|
                  salt, saltlen, iterations, keysize, keybuffer);
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      ec = GPG_ERR_UNKNOWN_ALGORITHM;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
 leave:
 | 
						|
  return gpg_error (ec);
 | 
						|
}
 |