/* mppe.c - MPPE key implementation
 *
 * Copyright (c) 2020 Eivind Naess. All rights reserved.
 * Copyright (c) 2008-2024 Paul Mackerras. 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, 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.
 *
 * 3. The name(s) of the authors of this software must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission.
 *
 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>


#include "pppd-private.h"
#include "fsm.h"
#include "ccp.h"
#include "chap_ms.h"
#include "mppe.h"
#include "crypto.h"

u_char mppe_send_key[MPPE_MAX_KEY_SIZE];
u_char mppe_recv_key[MPPE_MAX_KEY_SIZE];
int mppe_keys_set = 0;

void
mppe_set_keys(u_char *send_key, u_char *recv_key, int keylen)
{
	int length = keylen;
	if (length > MPPE_MAX_KEY_SIZE)
		length = MPPE_MAX_KEY_SIZE;
	
	if (send_key) {
	    BCOPY(send_key, mppe_send_key, length);
	    BZERO(send_key, keylen);
	}

	if (recv_key) {
	    BCOPY(recv_key, mppe_recv_key, length);
	    BZERO(recv_key, keylen);
	}

	mppe_keys_set = length;
}

bool
mppe_keys_isset()
{
	return !!mppe_keys_set;
}

int
mppe_get_recv_key(u_char *recv_key, int length)
{
	if (mppe_keys_isset()) {
		if (length > mppe_keys_set)
			length = mppe_keys_set;
		BCOPY(mppe_recv_key, recv_key, length);
		return length;
	}
	return 0;
}

int
mppe_get_send_key(u_char *send_key, int length)
{
	if (mppe_keys_isset()) {
		if (length > mppe_keys_set)
			length = mppe_keys_set;
		BCOPY(mppe_send_key, send_key, length);
		return length;
	}
	return 0;
}

void
mppe_clear_keys(void)
{
	mppe_keys_set = 0;
	BZERO(mppe_send_key, sizeof(mppe_send_key));
	BZERO(mppe_recv_key, sizeof(mppe_recv_key));
}

/*
 * Set mppe_xxxx_key from the NTPasswordHashHash.
 * RFC 2548 (RADIUS support) requires us to export this function (ugh).
 */
void
mppe_set_chapv1(unsigned char *rchallenge, unsigned char *PasswordHashHash)
{
    PPP_MD_CTX *ctx;
    u_char Digest[SHA_DIGEST_LENGTH];
    unsigned int DigestLen;

    ctx = PPP_MD_CTX_new();
    if (ctx != NULL) {

        if (PPP_DigestInit(ctx, PPP_sha1())) {

            if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) {

                if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) {

                    if (PPP_DigestUpdate(ctx, rchallenge, 8)) {
                        
                        DigestLen = SHA_DIGEST_LENGTH;
                        PPP_DigestFinal(ctx, Digest, &DigestLen);
                    }
                }
            }
        }
        
        PPP_MD_CTX_free(ctx);
    }


    /* Same key in both directions. */
    mppe_set_keys(Digest, Digest, sizeof(Digest));
}

/*
 * Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
 *
 * This helper function used in the Winbind module, which gets the
 * NTHashHash from the server.
 */
void
mppe_set_chapv2(unsigned char *PasswordHashHash, unsigned char *NTResponse,
        int IsServer)
{
    PPP_MD_CTX *ctx;
    
    u_char	MasterKey[SHA_DIGEST_LENGTH];
    u_char	SendKey[SHA_DIGEST_LENGTH];
    u_char	RecvKey[SHA_DIGEST_LENGTH];
    unsigned int KeyLen;

    u_char SHApad1[40] =
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    u_char SHApad2[40] =
	{ 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
	  0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 };

    /* "This is the MPPE Master Key" */
    u_char Magic1[27] =
	{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
	  0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
	  0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
    /* "On the client side, this is the send key; "
       "on the server side, it is the receive key." */
    u_char Magic2[84] =
	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
	  0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
	  0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
	  0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
	  0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
	  0x6b, 0x65, 0x79, 0x2e };
    /* "On the client side, this is the receive key; "
       "on the server side, it is the send key." */
    u_char Magic3[84] =
	{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
	  0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
	  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
	  0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
	  0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
	  0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
	  0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
	  0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
	  0x6b, 0x65, 0x79, 0x2e };
    u_char *s;

    ctx = PPP_MD_CTX_new();
    if (ctx != NULL) {

        if (PPP_DigestInit(ctx, PPP_sha1())) {

            if (PPP_DigestUpdate(ctx, PasswordHashHash, MD4_DIGEST_LENGTH)) {

                if (PPP_DigestUpdate(ctx, NTResponse, 24)) {

                    if (PPP_DigestUpdate(ctx, Magic1, sizeof(Magic1))) {
                        
                        KeyLen = SHA_DIGEST_LENGTH;
                        PPP_DigestFinal(ctx, MasterKey, &KeyLen);
                    }
                }
            }
        }
        
        PPP_MD_CTX_free(ctx);
    }

    /*
     * generate send key
     */
    if (IsServer)
	s = Magic3;
    else
	s = Magic2;

    ctx = PPP_MD_CTX_new();
    if (ctx != NULL) {

        if (PPP_DigestInit(ctx, PPP_sha1())) {

            if (PPP_DigestUpdate(ctx, MasterKey, 16)) {

                if (PPP_DigestUpdate(ctx, SHApad1, sizeof(SHApad1))) {

                    if (PPP_DigestUpdate(ctx, s, 84)) {

                        if (PPP_DigestUpdate(ctx, SHApad2, sizeof(SHApad2))) {
                        
                            KeyLen = SHA_DIGEST_LENGTH;
                            PPP_DigestFinal(ctx, SendKey, &KeyLen);
                        }
                    }
                }
            }
        }
        
        PPP_MD_CTX_free(ctx);
    }


    /*
     * generate recv key
     */
    if (IsServer)
	s = Magic2;
    else
	s = Magic3;

    ctx = PPP_MD_CTX_new();
    if (ctx != NULL) {

        if (PPP_DigestInit(ctx, PPP_sha1())) {

            if (PPP_DigestUpdate(ctx, MasterKey, 16)) {

                if (PPP_DigestUpdate(ctx, SHApad1, sizeof(SHApad1))) {

                    if (PPP_DigestUpdate(ctx, s, 84)) {

                        if (PPP_DigestUpdate(ctx, SHApad2, sizeof(SHApad2))) {
                        
                            KeyLen = SHA_DIGEST_LENGTH;
                            PPP_DigestFinal(ctx, RecvKey, &KeyLen);
                        }
                    }
                }
            }
        }
        
        PPP_MD_CTX_free(ctx);
    }

    mppe_set_keys(SendKey, RecvKey, SHA_DIGEST_LENGTH);
}

#ifndef UNIT_TEST

/*
 * Set MPPE options from plugins.
 */
void
mppe_set_enc_types(int policy, int types)
{
    /* Early exit for unknown policies. */
    if (policy != MPPE_ENC_POL_ENC_ALLOWED &&
	policy != MPPE_ENC_POL_ENC_REQUIRED)
	return;

    /* Don't modify MPPE if it's optional and wasn't already configured. */
    if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
	return;

    /*
     * Disable undesirable encryption types.  Note that we don't ENABLE
     * any encryption types, to avoid overriding manual configuration.
     */
    switch(types) {
	case MPPE_ENC_TYPES_RC4_40:
	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_128;	/* disable 128-bit */
	    break;
	case MPPE_ENC_TYPES_RC4_128:
	    ccp_wantoptions[0].mppe &= ~MPPE_OPT_40;	/* disable 40-bit */
	    break;
	default:
	    break;
    }
}

#endif
