
/*
 * msoft.c
 *
 * Rewritten by Archie Cobbs <archie@freebsd.org>
 * Copyright (c) 1998-1999 Whistle Communications, Inc. All rights reserved.
 * See ``COPYRIGHT.whistle''
 */

#include "ppp.h"
#include "msoft.h"
#ifdef ENCRYPTION_MPPE
#include <openssl/sha.h>
#endif
#include <openssl/md4.h>
#include <openssl/des.h>

/*
 * This stuff is described in:
 *
 * MS-CHAPv1
 *   http://www.es.net/pub/rfcs/rfc2433.txt
 *
 * MS-CHAPv2
 *   http://www.es.net/pub/rfcs/rfc2759.txt
 *
 * Deriving MPPE keys from MS-CHAPv1 and MS-CHAPv2
 *   http://www.es.net/pub/rfcs/rfc3079.txt
 */

/* Magic constants */
#define MS_MAGIC_1	"This is the MPPE Master Key"
#define MS_MAGIC_2	"On the client side, this is the send key;" \
			" on the server side, it is the receive key."
#define MS_MAGIC_3	"On the client side, this is the receive key;" \
			" on the server side, it is the send key."
#define MS_AR_MAGIC_1	"Magic server to client signing constant"
#define MS_AR_MAGIC_2	"Pad to make it do more than one iteration"

/*
 * INTERNAL FUNCTIONS
 */

  static void	ChallengeResponse(const u_char *chal,
			const char *pwHash, u_char *hash);
  static void	DesEncrypt(const u_char *clear, u_char *key0, u_char *cypher);
  static void	ChallengeHash(const u_char *peerchal, const u_char *authchal,
			const char *username, u_char *hash);

/*
 * LMPasswordHash()
 *
 * password	ASCII password
 * hash		16 byte output LanManager hash
 */

void
LMPasswordHash(const char *password, u_char *hash)
{
  const char	*const clear = "KGS!@#$%%";
  u_char	up[14];		/* upper case password */
  int		k;

  memset(&up, 0, sizeof(up));
  for (k = 0; k < sizeof(up) && password[k]; k++)
    up[k] = toupper(password[k]);

  DesEncrypt(clear, &up[0], &hash[0]);
  DesEncrypt(clear, &up[7], &hash[8]);
}

/*
 * NTPasswordHash()
 *
 * password	ASCII (NOT Unicode) password
 * hash		16 byte output NT hash
 */

void
NTPasswordHash(const char *password, u_char *hash)
{
  u_int16_t	unipw[128];
  int		unipwLen;
  MD4_CTX	md4ctx;
  const char	*s;

/* Convert password to Unicode */

  for (unipwLen = 0, s = password; unipwLen < sizeof(unipw) / 2 && *s; s++)
    unipw[unipwLen++] = htons(*s << 8);

/* Compute MD4 of Unicode password */

  MD4_Init(&md4ctx);
  MD4_Update(&md4ctx, (u_char *) unipw, unipwLen * sizeof(*unipw));
  MD4_Final(hash, &md4ctx);
}

/*
 * NTChallengeResponse()
 *
 * chal		8 byte challenge
 * password	ASCII (NOT Unicode) password
 * hash		24 byte response
 */

void
NTChallengeResponse(const u_char *chal, const char *password, u_char *hash)
{
  u_char	pwHash[16];

  NTPasswordHash(password, pwHash);
  ChallengeResponse(chal, pwHash, hash);
}

/*
 * ChallengeResponse()
 *
 * chal		8 byte challenge
 * pwHash	16 byte password hash
 * hash		24 byte response
 */

static void
ChallengeResponse(const u_char *chal, const char *pwHash, u_char *hash)
{
  u_char	buf[21];
  int		k;

  memset(&buf, 0, sizeof(buf));
  memcpy(buf, pwHash, 16);

/* Use DES to hash the hash */

  for (k = 0; k < 3; k++)
  {
    u_char	*const key = &buf[k * 7];
    u_char	*const output = &hash[k * 8];

    DesEncrypt(chal, key, output);
  }
}

/*
 * DesEncrypt()
 *
 * clear	8 byte cleartext
 * key		7 byte key
 * cypher	8 byte cyphertext
 */

static void
DesEncrypt(const u_char *clear, u_char *key0, u_char *cypher)
{
  des_key_schedule	ks;
  u_char		key[8];

/* Create DES key */

  key[0] = key0[0] & 0xfe;
  key[1] = (key0[0] << 7) | (key0[1] >> 1);
  key[2] = (key0[1] << 6) | (key0[2] >> 2);
  key[3] = (key0[2] << 5) | (key0[3] >> 3);
  key[4] = (key0[3] << 4) | (key0[4] >> 4);
  key[5] = (key0[4] << 3) | (key0[5] >> 5);
  key[6] = (key0[5] << 2) | (key0[6] >> 6);
  key[7] = key0[6] << 1;
  des_set_key((des_cblock *) key, ks);

/* Encrypt using key */

  des_ecb_encrypt((des_cblock *) clear, (des_cblock *) cypher, ks, 1);
}

#ifdef ENCRYPTION_MPPE

/*
 * MsoftGetStartKey()
 */

void
MsoftGetStartKey(u_char *chal, u_char *h)
{
  SHA_CTX	c;
  u_char	hash[20];

  SHA1_Init(&c);
  SHA1_Update(&c, h, 16);
  SHA1_Update(&c, h, 16);
  SHA1_Update(&c, chal, 8);
  SHA1_Final(hash, &c);
  memcpy(h, hash, 16);
}

#endif

/*
 * GenerateNTResponse()
 *
 * authchal	16 byte authenticator challenge
 * peerchal	16 byte peer challenge
 * username	ASCII username
 * password	ASCII (NOT Unicode) password
 * hash		24 byte response
 */

void
GenerateNTResponse(const u_char *authchal, const u_char *peerchal,
  const char *username, const char *password, u_char *hash)
{
  u_char	chal[8];
  u_char	pwHash[16];

  ChallengeHash(peerchal, authchal, username, chal);
  NTPasswordHash(password, pwHash);
  ChallengeResponse(chal, pwHash, hash);
}

/*
 * ChallengeHash()
 *
 * peerchal	16 byte peer challenge
 * authchal	16 byte authenticator challenge
 * username	ASCII username
 * hash		8 byte response
 */

static void
ChallengeHash(const u_char *peerchal, const u_char *authchal,
  const char *username, u_char *hash)
{
  SHA_CTX	c;
  u_char	digest[20];

  SHA1_Init(&c);
  SHA1_Update(&c, peerchal, 16);
  SHA1_Update(&c, authchal, 16);
  SHA1_Update(&c, username, strlen(username));
  SHA1_Final(digest, &c);
  memcpy(hash, digest, 8);
}

/*
 * Generate response to MS-CHAPv2 piggy-backed challenge.
 *
 * "authresp" must point to a 20 byte buffer.
 */
void
GenerateAuthenticatorResponse(const char *password,
  const u_char *ntresp, const u_char *peerchal,
  const u_char *authchal, const char *username, u_char *authresp)
{
  u_char hash[16];
  u_char digest[SHA_DIGEST_LENGTH];
  u_char chal[8];
  MD4_CTX md4ctx;
  SHA_CTX shactx;

  NTPasswordHash(password, hash);

  MD4_Init(&md4ctx);
  MD4_Update(&md4ctx, hash, 16);
  MD4_Final(hash, &md4ctx);

  SHA1_Init(&shactx);
  SHA1_Update(&shactx, hash, 16);
  SHA1_Update(&shactx, ntresp, 24);
  SHA1_Update(&shactx, MS_AR_MAGIC_1, 39);
  SHA1_Final(digest, &shactx);

  ChallengeHash(peerchal, authchal, username, chal);

  SHA1_Init(&shactx);
  SHA1_Update(&shactx, digest, sizeof(digest));
  SHA1_Update(&shactx, chal, 8);
  SHA1_Update(&shactx, MS_AR_MAGIC_2, 41);
  SHA1_Final(authresp, &shactx);
}

#ifdef ENCRYPTION_MPPE

/*
 * MsoftGetMasterKey()
 */

void
MsoftGetMasterKey(u_char *resp, u_char *h)
{
  SHA_CTX	c;
  u_char	hash[20];

  SHA1_Init(&c);
  SHA1_Update(&c, h, 16);
  SHA1_Update(&c, resp, 24);
  SHA1_Update(&c, MS_MAGIC_1, 27);
  SHA1_Final(hash, &c);
  memcpy(h, hash, 16);
}

/*
 * MsoftGetAsymetricStartKey()
 */

void
MsoftGetAsymetricStartKey(u_char *h, int server_recv)
{
  SHA_CTX		c;
  u_char		pad[40];
  u_char		hash[20];

  SHA1_Init(&c);
  SHA1_Update(&c, h, 16);
  memset(pad, 0x00, sizeof(pad));
  SHA1_Update(&c, pad, sizeof(pad));
  SHA1_Update(&c, server_recv ? MS_MAGIC_2 : MS_MAGIC_3, 84);
  memset(pad, 0xf2, sizeof(pad));
  SHA1_Update(&c, pad, sizeof(pad));
  SHA1_Final(hash, &c);
  memcpy(h, hash, 16);
}

#endif
