package net.freertr.auth;

import net.freertr.cry.cryHashMd5;
import net.freertr.pack.packHolder;
import net.freertr.util.bits;

/**
 * challenge handshake authentication protocol (rfc1994)
 *
 * @author matecsaba
 */
public class autherChap extends autherDoer {

    /**
     * ppp name
     */
    public final static String pppName = "chap";

    /**
     * ppp control type
     */
    public final static int pppCtrl = 0xc223;

    /**
     * challenge
     */
    public final static int codeChal = 1;

    /**
     * response
     */
    public final static int codeResp = 2;

    /**
     * success
     */
    public final static int codeSucc = 3;

    /**
     * failure
     */
    public final static int codeFail = 4;

    private int sentId;

    private byte[] sentCh;

    /**
     * compute auth response
     *
     * @param id id received
     * @param pass password to send
     * @param chal challenge reveived
     * @return sum calculated
     */
    public static byte[] calcAuthHash(int id, String pass, byte[] chal) {
        cryHashMd5 md = new cryHashMd5();
        md.init();
        md.update(id);
        md.update(pass.getBytes());
        md.update(chal);
        return md.finish();
    }

    /**
     * the constructor
     *
     * @param prnt parent
     */
    public autherChap(authenDown prnt) {
        parent = prnt;
    }

    /**
     * convert code to string
     *
     * @param i code
     * @return string
     */
    public static String code2str(int i) {
        switch (i) {
            case codeChal:
                return "challenge";
            case codeResp:
                return "response";
            case codeSucc:
                return "success";
            case codeFail:
                return "failure";
            default:
                return "unknown=" + i;
        }
    }

    protected void authenRecv(packHolder pck, int code, int id) {
        autherChapMsg msg = new autherChapMsg();
        msg.code = code;
        msg.id = id;
        if (msg.parsePack(pck)) {
            return;
        }
        if (!working) {
            return;
        }
        working = false;
        parent.recvAuthPack("" + msg);
        switch (code) {
            case codeChal:
                if (!isClient()) {
                    return;
                }
                working = true;
                msg.code = codeResp;
                msg.message = sentUser;
                msg.value = calcAuthHash(id, sentPass, msg.value);
                msg.createPack(pck);
                parent.sendAuthPack(pck, pppCtrl, msg.code, msg.id, "" + msg);
                break;
            case codeResp:
                if (isClient()) {
                    return;
                }
                if (sentId != msg.id) {
                    parent.recvAuthPack("got bad id");
                    working = true;
                    return;
                }
                result = authenRem.authUserChap(msg.message, sentId, sentCh, msg.value);
                if (result.result == authResult.authSuccessful) {
                    msg.code = codeSucc;
                } else {
                    msg.code = codeFail;
                }
                msg.message = code2str(msg.code);
                msg.id = sentId;
                msg.createPack(pck);
                parent.sendAuthPack(pck, pppCtrl, msg.code, msg.id, "" + msg);
                break;
            case codeSucc:
                if (!isClient()) {
                    return;
                }
                result = new authResult(authenRem, authResult.authSuccessful, sentUser, sentPass);
                break;
            case codeFail:
                if (!isClient()) {
                    return;
                }
                result = new authResult(authenRem, authResult.authBadUserPass, sentUser, sentPass);
                break;
            default:
                working = true;
                break;
        }
    }

    protected void authenSend(packHolder pck) {
        if (!working) {
            return;
        }
        if (isClient()) {
            return;
        }
        sentId = bits.randomB();
        sentCh = new byte[16];
        for (int i = 0; i < sentCh.length; i++) {
            sentCh[i] = (byte) bits.randomB();
        }
        autherChapMsg msg = new autherChapMsg();
        msg.code = codeChal;
        msg.id = sentId;
        msg.message = "";
        msg.value = sentCh;
        msg.createPack(pck);
        parent.sendAuthPack(pck, pppCtrl, msg.code, msg.id, "" + msg);
        return;
    }

}

class autherChapMsg {

    public int code;

    public int id;

    public byte[] value;

    public String message;

    public boolean needValue() {
        switch (code) {
            case autherChap.codeChal:
            case autherChap.codeResp:
                return true;
            default:
                return false;
        }
    }

    public boolean parsePack(packHolder pck) {
        if (needValue()) {
            int i = pck.getByte(0);
            pck.getSkip(1);
            value = new byte[i];
            pck.getCopy(value, 0, 0, i);
            pck.getSkip(i);
        } else {
            value = new byte[0];
        }
        message = pck.getAsciiZ(0, pck.dataSize(), 0);
        return false;
    }

    public void createPack(packHolder pck) {
        pck.clear();
        if (needValue()) {
            pck.putByte(0, value.length);
            pck.putSkip(1);
            pck.putCopy(value, 0, 0, value.length);
            pck.putSkip(value.length);
        }
        pck.putAsciiZ(0, message.length(), message, 0);
        pck.putSkip(message.length());
        pck.merge2beg();
    }

    public String toString() {
        return "cod=" + autherChap.code2str(code) + " id=" + id + " msg=" + message + " val=" + bits.byteDump(value, 0, -1);
    }

}
