/*
 * Copyright (c) 2000
 *      Traakan, Inc., Los Altos, CA
 *      All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Project:  NDMJOB
 * Ident:    $Id: $
 *
 * Description:
 *
 * MD5 authentication support
 ****************************************************************
 * Both sides share a secret in the form of a clear-text
 * password. One side generates a challenge (64-bytes)
 * and conveys it to the other. The other side then
 * uses the challenge, the clear-text password, and
 * the NDMP rules for MD5, and generates a digest.
 * The digest is returned as proof that both sides
 * share the same secret clear-text password.
 *
 * The NDMP rules for MD5 are implemented in ndmmd5_digest().
 * It amounts to positioning the clear-text password and challenge
 * into a "message" buffer, then applying the MD5 algorithm.
 *
 * ndmmd5_generate_challenge() generates a challenge[]
 * using conventional random number routines.
 *
 * ndmmd5_ok_digest() takes a locally known challenge[]
 * and clear-text password, a remotely generated
 * digest[], and determines if everything is correct.
 *
 * Using MD5 prevents clear-text passwords from being conveyed
 * over the network. However, it compels both sides to maintain
 * clear-text passwords in a secure fashion, which is difficult
 * to say the least. Because the NDMP MD5 rules must be followed
 * to digest() the password, it's impractical to consider
 * an external authentication authority.
 *
 * Credits to Rajiv of NetApp for helping with MD5 stuff.
 */


#include "ndmlib.h"
#include "md5.h"


int ndmmd5_generate_challenge(char challenge[NDMP_MD5_CHALLENGE_LENGTH])
{
  int i;

  NDMOS_MACRO_SRAND();

  for (i = 0; i < NDMP_MD5_CHALLENGE_LENGTH; i++) {
    challenge[i] = NDMOS_MACRO_RAND() >> (i & 7);
  }

  return 0;
}


int ndmmd5_ok_digest(char challenge[NDMP_MD5_CHALLENGE_LENGTH],
                     char* clear_text_password,
                     char digest[NDMP_MD5_DIGEST_LENGTH])
{
  char my_digest[16];
  int i;

  ndmmd5_digest(challenge, clear_text_password, my_digest);

  for (i = 0; i < NDMP_MD5_DIGEST_LENGTH; i++)
    if (digest[i] != my_digest[i]) return 0; /* Invalid */

  return 1; /* OK */
}


int ndmmd5_digest(char challenge[NDMP_MD5_CHALLENGE_LENGTH],
                  char* clear_text_password,
                  char digest[NDMP_MD5_DIGEST_LENGTH])
{
  int pwlength = strlen(clear_text_password);
  struct MD5Context mdContext;
  unsigned char message[128];

  /*
   * The spec describes the construction of the 128 byte
   * "message" (probably MD5-speak). It is described as:
   *
   *    PASSWORD PADDING CHALLENGE PADDING PASSWORD
   *
   * Each PADDING is defined as zeros of length 64 minus pwlen.
   *
   * A pwlen of over 32 would result in not all fields
   * fitting. This begs a question of the order elements
   * are inserted into the message[]. You get a different
   * message[] if you insert the PASSWORD(s) before
   * the CHALLENGE than you get the other way around.
   *
   * A pwlen of over 64 would result in PADDING of negative
   * length, which could cause crash boom bang.
   *
   * The resolution of this vaguery implemented here is to
   * only use the first 32 bytes of the password. All
   * fields fit. Order dependencies are avoided.
   *
   * Final resolution is pending.
   */
  if (pwlength > 32) pwlength = 32;

  // Compose the 128-byte buffer according to NDMP rules
  NDMOS_API_BZERO(message, sizeof message);
  NDMOS_API_BCOPY(clear_text_password, &message[0], pwlength);
  NDMOS_API_BCOPY(clear_text_password, &message[128 - pwlength], pwlength);
  NDMOS_API_BCOPY(challenge, &message[64 - pwlength], 64);

  // Grind it up, ala MD5
  MD5Init(&mdContext);
  MD5Update(&mdContext, message, 128);
  MD5Final((unsigned char*)digest, &mdContext);

  // ding! done
  return 0;
}
