Blame crypt-sha512.c

Packit 13e0ca
/* One way encryption based on SHA512 sum.
Packit 13e0ca
Packit 13e0ca
   Copyright (C) 2007-2017 Free Software Foundation, Inc.
Packit 13e0ca
Packit 13e0ca
   This library is free software; you can redistribute it and/or
Packit 13e0ca
   modify it under the terms of the GNU Lesser General Public License
Packit 13e0ca
   as published by the Free Software Foundation; either version 2.1 of
Packit 13e0ca
   the License, or (at your option) any later version.
Packit 13e0ca
Packit 13e0ca
   This library is distributed in the hope that it will be useful,
Packit 13e0ca
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 13e0ca
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 13e0ca
   GNU Lesser General Public License for more details.
Packit 13e0ca
Packit 13e0ca
   You should have received a copy of the GNU Lesser General Public
Packit 13e0ca
   License along with this library; if not, see
Packit 13e0ca
   <https://www.gnu.org/licenses/>.  */
Packit 13e0ca
Packit 13e0ca
#include "crypt-port.h"
Packit 13e0ca
#include "crypt-private.h"
Packit 13e0ca
#include "alg-sha512.h"
Packit 13e0ca
Packit 13e0ca
#include <errno.h>
Packit 13e0ca
#include <stdio.h>
Packit 13e0ca
#include <stdlib.h>
Packit 13e0ca
Packit 13e0ca
#if INCLUDE_sha512
Packit 13e0ca
Packit 13e0ca
/* Define our magic string to mark salt for SHA512 "encryption"
Packit 13e0ca
   replacement.  */
Packit 13e0ca
static const char sha512_salt_prefix[] = "$6$";
Packit 13e0ca
Packit 13e0ca
/* Prefix for optional rounds specification.  */
Packit 13e0ca
static const char sha512_rounds_prefix[] = "rounds=";
Packit 13e0ca
Packit 13e0ca
/* Maximum salt string length.  */
Packit 13e0ca
#define SALT_LEN_MAX 16
Packit 13e0ca
/* Default number of rounds if not explicitly specified.  */
Packit 13e0ca
#define ROUNDS_DEFAULT 5000
Packit 13e0ca
/* Minimum number of rounds.  */
Packit 13e0ca
#define ROUNDS_MIN 1000
Packit 13e0ca
/* Maximum number of rounds.  */
Packit 13e0ca
#define ROUNDS_MAX 999999999
Packit 13e0ca
Packit 13e0ca
/* The maximum possible length of a SHA512-hashed password string,
Packit 13e0ca
   including the terminating NUL character.  Prefix (including its NUL)
Packit 13e0ca
   + rounds tag ("rounds=$" = "rounds=\0") + strlen(ROUNDS_MAX)
Packit 13e0ca
   + salt (up to SALT_LEN_MAX chars) + '$' + hash (86 chars).  */
Packit 13e0ca
Packit 13e0ca
#define LENGTH_OF_NUMBER(n) (sizeof #n - 1)
Packit 13e0ca
Packit 13e0ca
#define SHA512_HASH_LENGTH \
Packit 13e0ca
  (sizeof (sha512_salt_prefix) + sizeof (sha512_rounds_prefix) + \
Packit 13e0ca
   LENGTH_OF_NUMBER (ROUNDS_MAX) + SALT_LEN_MAX + 1 + 86)
Packit 13e0ca
Packit 13e0ca
static_assert (SHA512_HASH_LENGTH <= CRYPT_OUTPUT_SIZE,
Packit 13e0ca
               "CRYPT_OUTPUT_SIZE is too small for SHA512");
Packit 13e0ca
Packit 13e0ca
/* A sha512_buffer holds all of the sensitive intermediate data.  */
Packit 13e0ca
struct sha512_buffer
Packit 13e0ca
{
Packit 13e0ca
  struct sha512_ctx ctx;
Packit 13e0ca
  uint8_t result[64];
Packit 13e0ca
  uint8_t p_bytes[64];
Packit 13e0ca
  uint8_t s_bytes[64];
Packit 13e0ca
};
Packit 13e0ca
Packit 13e0ca
static_assert (sizeof (struct sha512_buffer) <= ALG_SPECIFIC_SIZE,
Packit 13e0ca
               "ALG_SPECIFIC_SIZE is too small for SHA512");
Packit 13e0ca
Packit 13e0ca
Packit 13e0ca
/* Table with characters for base64 transformation.  */
Packit 13e0ca
static const char b64t[] =
Packit 13e0ca
  "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
Packit 13e0ca
Packit 13e0ca
/* Subroutine of _xcrypt_crypt_sha512_rn: Feed CTX with LEN bytes of a
Packit 13e0ca
   virtual byte sequence consisting of BLOCK repeated over and over
Packit 13e0ca
   indefinitely.  */
Packit 13e0ca
static void
Packit 13e0ca
sha512_process_recycled_bytes (unsigned char block[64], size_t len,
Packit 13e0ca
                               struct sha512_ctx *ctx)
Packit 13e0ca
{
Packit 13e0ca
  size_t cnt;
Packit 13e0ca
  for (cnt = len; cnt >= 64; cnt -= 64)
Packit 13e0ca
    sha512_process_bytes (block, 64, ctx);
Packit 13e0ca
  sha512_process_bytes (block, cnt, ctx);
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
void
Packit 13e0ca
crypt_sha512_rn (const char *phrase, size_t phr_size,
Packit 13e0ca
                 const char *setting, size_t ARG_UNUSED (set_size),
Packit 13e0ca
                 uint8_t *output, size_t out_size,
Packit 13e0ca
                 void *scratch, size_t scr_size)
Packit 13e0ca
{
Packit 13e0ca
  /* This shouldn't ever happen, but...  */
Packit 13e0ca
  if (out_size < SHA512_HASH_LENGTH
Packit 13e0ca
      || scr_size < sizeof (struct sha512_buffer))
Packit 13e0ca
    {
Packit 13e0ca
      errno = ERANGE;
Packit 13e0ca
      return;
Packit 13e0ca
    }
Packit 13e0ca
Packit 13e0ca
  struct sha512_buffer *buf = scratch;
Packit 13e0ca
  struct sha512_ctx *ctx = &buf->ctx;
Packit 13e0ca
  uint8_t *result = buf->result;
Packit 13e0ca
  uint8_t *p_bytes = buf->p_bytes;
Packit 13e0ca
  uint8_t *s_bytes = buf->s_bytes;
Packit 13e0ca
  char *cp = (char *)output;
Packit 13e0ca
  const char *salt = setting;
Packit 13e0ca
Packit 13e0ca
  size_t salt_size;
Packit 13e0ca
  size_t cnt;
Packit 13e0ca
  /* Default number of rounds.  */
Packit 13e0ca
  size_t rounds = ROUNDS_DEFAULT;
Packit 13e0ca
  bool rounds_custom = false;
Packit 13e0ca
Packit 13e0ca
  /* Find beginning of salt string.  The prefix should normally always
Packit 13e0ca
     be present.  Just in case it is not.  */
Packit 13e0ca
  if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0)
Packit 13e0ca
    /* Skip salt prefix.  */
Packit 13e0ca
    salt += sizeof (sha512_salt_prefix) - 1;
Packit 13e0ca
Packit 13e0ca
  if (strncmp (salt, sha512_rounds_prefix, sizeof (sha512_rounds_prefix) - 1)
Packit 13e0ca
      == 0)
Packit 13e0ca
    {
Packit 13e0ca
      const char *num = salt + sizeof (sha512_rounds_prefix) - 1;
Packit 13e0ca
      /* Do not allow an explicit setting of zero rounds, nor of the
Packit 13e0ca
         default number of rounds, nor leading zeroes on the rounds.  */
Packit 13e0ca
      if (!(*num >= '1' && *num <= '9'))
Packit 13e0ca
        {
Packit 13e0ca
          errno = EINVAL;
Packit 13e0ca
          return;
Packit 13e0ca
        }
Packit 13e0ca
Packit 13e0ca
      errno = 0;
Packit 13e0ca
      char *endp;
Packit 13e0ca
      rounds = strtoul (num, &endp, 10);
Packit 13e0ca
      if (endp == num || *endp != '$'
Packit 13e0ca
          || rounds < ROUNDS_MIN
Packit 13e0ca
          || rounds > ROUNDS_MAX
Packit 13e0ca
          || errno)
Packit 13e0ca
        {
Packit 13e0ca
          errno = EINVAL;
Packit 13e0ca
          return;
Packit 13e0ca
        }
Packit 13e0ca
      salt = endp + 1;
Packit 13e0ca
      rounds_custom = true;
Packit 13e0ca
    }
Packit 13e0ca
Packit 13e0ca
  salt_size = strspn (salt, b64t);
Packit 13e0ca
  if (salt[salt_size] && salt[salt_size] != '$')
Packit 13e0ca
    {
Packit 13e0ca
      errno = EINVAL;
Packit 13e0ca
      return;
Packit 13e0ca
    }
Packit 13e0ca
  if (salt_size > SALT_LEN_MAX)
Packit 13e0ca
    salt_size = SALT_LEN_MAX;
Packit 13e0ca
  phr_size = strlen (phrase);
Packit 13e0ca
Packit 13e0ca
  /* Compute alternate SHA512 sum with input PHRASE, SALT, and PHRASE.  The
Packit 13e0ca
     final result will be added to the first context.  */
Packit 13e0ca
  sha512_init_ctx (ctx);
Packit 13e0ca
Packit 13e0ca
  /* Add phrase.  */
Packit 13e0ca
  sha512_process_bytes (phrase, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Add salt.  */
Packit 13e0ca
  sha512_process_bytes (salt, salt_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Add phrase again.  */
Packit 13e0ca
  sha512_process_bytes (phrase, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Now get result of this (64 bytes) and add it to the other
Packit 13e0ca
     context.  */
Packit 13e0ca
  sha512_finish_ctx (ctx, result);
Packit 13e0ca
Packit 13e0ca
  /* Prepare for the real work.  */
Packit 13e0ca
  sha512_init_ctx (ctx);
Packit 13e0ca
Packit 13e0ca
  /* Add the phrase string.  */
Packit 13e0ca
  sha512_process_bytes (phrase, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* The last part is the salt string.  This must be at most 8
Packit 13e0ca
     characters and it ends at the first `$' character (for
Packit 13e0ca
     compatibility with existing implementations).  */
Packit 13e0ca
  sha512_process_bytes (salt, salt_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Add for any character in the phrase one byte of the alternate sum.  */
Packit 13e0ca
  for (cnt = phr_size; cnt > 64; cnt -= 64)
Packit 13e0ca
    sha512_process_bytes (result, 64, ctx);
Packit 13e0ca
  sha512_process_bytes (result, cnt, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Take the binary representation of the length of the phrase and for every
Packit 13e0ca
     1 add the alternate sum, for every 0 the phrase.  */
Packit 13e0ca
  for (cnt = phr_size; cnt > 0; cnt >>= 1)
Packit 13e0ca
    if ((cnt & 1) != 0)
Packit 13e0ca
      sha512_process_bytes (result, 64, ctx);
Packit 13e0ca
    else
Packit 13e0ca
      sha512_process_bytes (phrase, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Create intermediate result.  */
Packit 13e0ca
  sha512_finish_ctx (ctx, result);
Packit 13e0ca
Packit 13e0ca
  /* Start computation of P byte sequence.  */
Packit 13e0ca
  sha512_init_ctx (ctx);
Packit 13e0ca
Packit 13e0ca
  /* For every character in the password add the entire password.  */
Packit 13e0ca
  for (cnt = 0; cnt < phr_size; ++cnt)
Packit 13e0ca
    sha512_process_bytes (phrase, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Finish the digest.  */
Packit 13e0ca
  sha512_finish_ctx (ctx, p_bytes);
Packit 13e0ca
Packit 13e0ca
  /* Start computation of S byte sequence.  */
Packit 13e0ca
  sha512_init_ctx (ctx);
Packit 13e0ca
Packit 13e0ca
  /* For every character in the password add the entire password.  */
Packit 13e0ca
  for (cnt = 0; cnt < (size_t) 16 + (size_t) result[0]; ++cnt)
Packit 13e0ca
    sha512_process_bytes (salt, salt_size, ctx);
Packit 13e0ca
Packit 13e0ca
  /* Finish the digest.  */
Packit 13e0ca
  sha512_finish_ctx (ctx, s_bytes);
Packit 13e0ca
Packit 13e0ca
  /* Repeatedly run the collected hash value through SHA512 to burn
Packit 13e0ca
     CPU cycles.  */
Packit 13e0ca
  for (cnt = 0; cnt < rounds; ++cnt)
Packit 13e0ca
    {
Packit 13e0ca
      /* New context.  */
Packit 13e0ca
      sha512_init_ctx (ctx);
Packit 13e0ca
Packit 13e0ca
      /* Add phrase or last result.  */
Packit 13e0ca
      if ((cnt & 1) != 0)
Packit 13e0ca
        sha512_process_recycled_bytes (p_bytes, phr_size, ctx);
Packit 13e0ca
      else
Packit 13e0ca
        sha512_process_bytes (result, 64, ctx);
Packit 13e0ca
Packit 13e0ca
      /* Add salt for numbers not divisible by 3.  */
Packit 13e0ca
      if (cnt % 3 != 0)
Packit 13e0ca
        sha512_process_recycled_bytes (s_bytes, salt_size, ctx);
Packit 13e0ca
Packit 13e0ca
      /* Add phrase for numbers not divisible by 7.  */
Packit 13e0ca
      if (cnt % 7 != 0)
Packit 13e0ca
        sha512_process_recycled_bytes (p_bytes, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
      /* Add phrase or last result.  */
Packit 13e0ca
      if ((cnt & 1) != 0)
Packit 13e0ca
        sha512_process_bytes (result, 64, ctx);
Packit 13e0ca
      else
Packit 13e0ca
        sha512_process_recycled_bytes (p_bytes, phr_size, ctx);
Packit 13e0ca
Packit 13e0ca
      /* Create intermediate result.  */
Packit 13e0ca
      sha512_finish_ctx (ctx, result);
Packit 13e0ca
    }
Packit 13e0ca
Packit 13e0ca
  /* Now we can construct the result string.  It consists of four
Packit 13e0ca
     parts, one of which is optional.  We already know that buflen is
Packit 13e0ca
     at least sha512_hash_length, therefore none of the string bashing
Packit 13e0ca
     below can overflow the buffer. */
Packit 13e0ca
Packit 13e0ca
  memcpy (cp, sha512_salt_prefix, sizeof (sha512_salt_prefix) - 1);
Packit 13e0ca
  cp += sizeof (sha512_salt_prefix) - 1;
Packit 13e0ca
Packit 13e0ca
  if (rounds_custom)
Packit 13e0ca
    {
Packit 13e0ca
      int n = snprintf (cp,
Packit 13e0ca
                        SHA512_HASH_LENGTH - (sizeof (sha512_salt_prefix) - 1),
Packit 13e0ca
                        "%s%zu$", sha512_rounds_prefix, rounds);
Packit 13e0ca
      cp += n;
Packit 13e0ca
    }
Packit 13e0ca
Packit 13e0ca
  memcpy (cp, salt, salt_size);
Packit 13e0ca
  cp += salt_size;
Packit 13e0ca
  *cp++ = '$';
Packit 13e0ca
Packit 13e0ca
#define b64_from_24bit(B2, B1, B0, N)                   \
Packit 13e0ca
  do {                                                  \
Packit 13e0ca
    unsigned int w = ((((unsigned int)(B2)) << 16) |    \
Packit 13e0ca
                      (((unsigned int)(B1)) << 8) |     \
Packit 13e0ca
                      ((unsigned int)(B0)));            \
Packit 13e0ca
    int n = (N);                                        \
Packit 13e0ca
    while (n-- > 0)                                     \
Packit 13e0ca
      {                                                 \
Packit 13e0ca
        *cp++ = b64t[w & 0x3f];                         \
Packit 13e0ca
        w >>= 6;                                        \
Packit 13e0ca
      }                                                 \
Packit 13e0ca
  } while (0)
Packit 13e0ca
Packit 13e0ca
  b64_from_24bit (result[0], result[21], result[42], 4);
Packit 13e0ca
  b64_from_24bit (result[22], result[43], result[1], 4);
Packit 13e0ca
  b64_from_24bit (result[44], result[2], result[23], 4);
Packit 13e0ca
  b64_from_24bit (result[3], result[24], result[45], 4);
Packit 13e0ca
  b64_from_24bit (result[25], result[46], result[4], 4);
Packit 13e0ca
  b64_from_24bit (result[47], result[5], result[26], 4);
Packit 13e0ca
  b64_from_24bit (result[6], result[27], result[48], 4);
Packit 13e0ca
  b64_from_24bit (result[28], result[49], result[7], 4);
Packit 13e0ca
  b64_from_24bit (result[50], result[8], result[29], 4);
Packit 13e0ca
  b64_from_24bit (result[9], result[30], result[51], 4);
Packit 13e0ca
  b64_from_24bit (result[31], result[52], result[10], 4);
Packit 13e0ca
  b64_from_24bit (result[53], result[11], result[32], 4);
Packit 13e0ca
  b64_from_24bit (result[12], result[33], result[54], 4);
Packit 13e0ca
  b64_from_24bit (result[34], result[55], result[13], 4);
Packit 13e0ca
  b64_from_24bit (result[56], result[14], result[35], 4);
Packit 13e0ca
  b64_from_24bit (result[15], result[36], result[57], 4);
Packit 13e0ca
  b64_from_24bit (result[37], result[58], result[16], 4);
Packit 13e0ca
  b64_from_24bit (result[59], result[17], result[38], 4);
Packit 13e0ca
  b64_from_24bit (result[18], result[39], result[60], 4);
Packit 13e0ca
  b64_from_24bit (result[40], result[61], result[19], 4);
Packit 13e0ca
  b64_from_24bit (result[62], result[20], result[41], 4);
Packit 13e0ca
  b64_from_24bit (0, 0, result[63], 2);
Packit 13e0ca
Packit 13e0ca
  *cp = '\0';
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
void
Packit 13e0ca
gensalt_sha512_rn (unsigned long count,
Packit 13e0ca
                   const uint8_t *rbytes, size_t nrbytes,
Packit 13e0ca
                   uint8_t *output, size_t output_size)
Packit 13e0ca
{
Packit 13e0ca
  gensalt_sha_rn ('6', SALT_LEN_MAX, ROUNDS_DEFAULT, ROUNDS_MIN, ROUNDS_MAX,
Packit 13e0ca
                  count, rbytes, nrbytes, output, output_size);
Packit 13e0ca
}
Packit 13e0ca
Packit 13e0ca
#endif