/*
    pps.c
    Protocol Parameters Selection

    This file is part of the Unix driver for Towitoko smartcard readers
    Copyright (C) 2000 2001 Carlos Prados <cprados@yahoo.com>
    Copyright (C) 2011 Advanced Card Systems Ltd. <info@acs.com.hk>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with this library; if not, write to the Free Software Foundation,
	Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "pps.h"
#include "atr.h"
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <ifdhandler.h>

#include "commands.h"
#include "defs.h"
#include "debug.h"

#include "ccid_ifdhandler.h"

/*
 * Not exported funtions declaration
 */

static int PPS_Match (BYTE * request, unsigned len_request, BYTE * reply, unsigned len_reply);

static unsigned PPS_GetLength (BYTE * block);

static BYTE PPS_GetPCK (BYTE * block, unsigned length);

int
PPS_Exchange (int lun, BYTE * params, unsigned *length, unsigned char *pps1)
{
  CcidDesc *ccid_slot;
  BYTE confirm[PPS_MAX_LENGTH];
  unsigned len_request, len_confirm;
  int ret;

  ccid_slot = get_ccid_slot(lun);
  len_request = PPS_GetLength (params);
  params[len_request - 1] = PPS_GetPCK(params, len_request - 1);

  DEBUG_XXD ("PPS: Sending request: ", params, len_request);

  /* Send PPS request */
  if (ccid_slot->pTransmitPPS(lun, len_request, params, isCharLevel(lun) ? 4 : 0, 0)
	!= IFD_SUCCESS)
    return PPS_ICC_ERROR;

  /* Get PPS confirm */
  len_confirm = sizeof(confirm);
  if (ccid_slot->pReceive(lun, &len_confirm, confirm, NULL) != IFD_SUCCESS)
    return PPS_ICC_ERROR;

  DEBUG_XXD ("PPS: Receiving confirm: ", confirm, len_confirm);

  if (!PPS_Match (params, len_request, confirm, len_confirm))
    ret = PPS_HANDSAKE_ERROR;
  else
    ret = PPS_OK;

  *pps1 = 0x11;	/* default TA1 */

  /* if PPS1 is echoed */
  if (PPS_HAS_PPS1 (params) && PPS_HAS_PPS1 (confirm))
      *pps1 = confirm[2];

  /* Copy PPS handsake */
  memcpy (params, confirm, len_confirm);
  (*length) = len_confirm;

  return ret;
}

static int
PPS_Match (BYTE * request, unsigned len_request, BYTE * confirm, unsigned len_confirm)
{
  /* See if the reply differs from request */
  if ((len_request == len_confirm) &&	/* same length */
      memcmp (request, confirm, len_request))	/* different contents */
	return FALSE;

  if (len_request < len_confirm)	/* confirm longer than request */
    return FALSE;

  /* See if the card specifies other than default FI and D */
  if ((PPS_HAS_PPS1 (confirm))
		&& (len_confirm > 2)
		&& (confirm[2] != request[2]))
    return FALSE;

  return TRUE;
}

static unsigned
PPS_GetLength (BYTE * block)
{
  unsigned length = 3;

  if (PPS_HAS_PPS1 (block))
    length++;

  if (PPS_HAS_PPS2 (block))
    length++;

  if (PPS_HAS_PPS3 (block))
    length++;

  return length;
}

static BYTE
PPS_GetPCK (BYTE * block, unsigned length)
{
  BYTE pck;
  unsigned i;

  pck = block[0];
  for (i = 1; i < length; i++)
    pck ^= block[i];

  return pck;
}

