/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.net.eap.test.statemachine;

import static android.net.eap.test.EapSessionConfig.EapMethodConfig.EAP_TYPE_MSCHAP_V2;

import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_CHALLENGE;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_AUTHENTICATOR_RESPONSE;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_CHALLENGE;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_MASTER_KEY;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_MSK;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_NT_RESPONSE;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_HASH;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_HASH_HASH;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_PASSWORD_UTF_BYTES;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_PEER_CHALLENGE;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_RECEIVE_START_KEY;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_SEND_START_KEY;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME;
import static com.android.internal.net.eap.test.message.EapTestMessageDefinitions.MSCHAP_V2_USERNAME_ASCII_BYTES;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.net.eap.test.EapSessionConfig.EapMsChapV2Config;

import com.android.internal.net.eap.test.statemachine.EapMsChapV2MethodStateMachine.CreatedState;
import com.android.internal.net.utils.test.Log;

import org.junit.Before;
import org.junit.Test;

import java.security.SecureRandom;

public class EapMsChapV2MethodStateMachineTest {
    private EapMsChapV2Config mEapMsChapV2Config;
    private EapMsChapV2MethodStateMachine mStateMachine;

    @Before
    public void setUp() {
        mEapMsChapV2Config = new EapMsChapV2Config(MSCHAP_V2_USERNAME, MSCHAP_V2_PASSWORD);
        mStateMachine = new EapMsChapV2MethodStateMachine(mEapMsChapV2Config, new SecureRandom());
    }

    @Test
    public void testGetEapMethod() {
        assertEquals(EAP_TYPE_MSCHAP_V2, mStateMachine.getEapMethod());
    }

    @Test
    public void testStartsOnCreatedState() {
        assertTrue(mStateMachine.getState() instanceof CreatedState);
    }

    // Tests for MS CHAPv2 authentication utils. Test vectors from RFC 2759#9.2.

    @Test
    public void testUsernameToBytes() {
        assertArrayEquals(
                MSCHAP_V2_USERNAME_ASCII_BYTES,
                EapMsChapV2MethodStateMachine.usernameToBytes(MSCHAP_V2_USERNAME));
    }

    @Test
    public void testPasswordToBytes() {
        assertArrayEquals(
                MSCHAP_V2_PASSWORD_UTF_BYTES,
                EapMsChapV2MethodStateMachine.passwordToBytes(MSCHAP_V2_PASSWORD));
    }

    @Test
    public void testGenerateNtResponse() throws Exception {
        byte[] ntResponse =
                EapMsChapV2MethodStateMachine.generateNtResponse(
                        MSCHAP_V2_AUTHENTICATOR_CHALLENGE,
                        MSCHAP_V2_PEER_CHALLENGE,
                        MSCHAP_V2_USERNAME,
                        MSCHAP_V2_PASSWORD);
        assertArrayEquals(MSCHAP_V2_NT_RESPONSE, ntResponse);
    }

    @Test
    public void testChallengeHash() throws Exception {
        byte[] challenge =
                EapMsChapV2MethodStateMachine.challengeHash(
                        MSCHAP_V2_PEER_CHALLENGE,
                        MSCHAP_V2_AUTHENTICATOR_CHALLENGE,
                        MSCHAP_V2_USERNAME);
        assertArrayEquals(MSCHAP_V2_CHALLENGE, challenge);
    }

    @Test
    public void testNtPasswordHash() {
        byte[] passwordHash = EapMsChapV2MethodStateMachine.ntPasswordHash(MSCHAP_V2_PASSWORD);
        assertArrayEquals(MSCHAP_V2_PASSWORD_HASH, passwordHash);
    }

    @Test
    public void testHashNtPasswordHash() {
        byte[] passwordHashHash =
                EapMsChapV2MethodStateMachine.hashNtPasswordHash(MSCHAP_V2_PASSWORD_HASH);
        assertArrayEquals(MSCHAP_V2_PASSWORD_HASH_HASH, passwordHashHash);
    }

    @Test
    public void testChallengeResponse() throws Exception {
        byte[] challengeResponse =
                EapMsChapV2MethodStateMachine.challengeResponse(
                        MSCHAP_V2_CHALLENGE, MSCHAP_V2_PASSWORD_HASH);
        assertArrayEquals(MSCHAP_V2_NT_RESPONSE, challengeResponse);
    }

    @Test
    public void testGenerateAuthenticatorResponse() throws Exception {
        byte[] authenticatorResponse =
                EapMsChapV2MethodStateMachine.generateAuthenticatorResponse(
                        MSCHAP_V2_PASSWORD,
                        MSCHAP_V2_NT_RESPONSE,
                        MSCHAP_V2_PEER_CHALLENGE,
                        MSCHAP_V2_AUTHENTICATOR_CHALLENGE,
                        MSCHAP_V2_USERNAME);
        assertArrayEquals(MSCHAP_V2_AUTHENTICATOR_RESPONSE, authenticatorResponse);
    }

    @Test
    public void testCheckAuthenticatorResponse() throws Exception {
        assertTrue(
                "AuthenticatorResponse didn't match computed response",
                EapMsChapV2MethodStateMachine.checkAuthenticatorResponse(
                        MSCHAP_V2_PASSWORD,
                        MSCHAP_V2_NT_RESPONSE,
                        MSCHAP_V2_PEER_CHALLENGE,
                        MSCHAP_V2_AUTHENTICATOR_CHALLENGE,
                        MSCHAP_V2_USERNAME,
                        MSCHAP_V2_AUTHENTICATOR_RESPONSE));
    }

    @Test
    public void testGetMasterKey() throws Exception {
        byte[] masterKey =
                EapMsChapV2MethodStateMachine.getMasterKey(
                        MSCHAP_V2_PASSWORD_HASH_HASH, MSCHAP_V2_NT_RESPONSE);
        assertArrayEquals(MSCHAP_V2_MASTER_KEY, masterKey);
    }

    @Test
    public void testGetAsymmetricStartKeySendKey() throws Exception {
        byte[] startKey =
                EapMsChapV2MethodStateMachine.getAsymmetricStartKey(MSCHAP_V2_MASTER_KEY, true);
        assertArrayEquals(Log.byteArrayToHexString(startKey), MSCHAP_V2_SEND_START_KEY, startKey);
    }

    @Test
    public void testGetAsymmetricStartKeyReceiveKey() throws Exception {
        byte[] receiveKey =
                EapMsChapV2MethodStateMachine.getAsymmetricStartKey(MSCHAP_V2_MASTER_KEY, false);
        assertArrayEquals(MSCHAP_V2_RECEIVE_START_KEY, receiveKey);
    }

    @Test
    public void testGenerateMsk() throws Exception {
        byte[] msk =
                EapMsChapV2MethodStateMachine.generateMsk(
                        MSCHAP_V2_PASSWORD, MSCHAP_V2_NT_RESPONSE);
        assertArrayEquals(MSCHAP_V2_MSK, msk);
    }
}
