atrhandler.c   [plain text]


/*
 * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved.
 * The contents of this file constitute Original Code as defined in and are
 * subject to the Apple Public Source License Version 1.2 (the 'License').
 * You may not use this file except in compliance with the License. Please
 * obtain a copy of the License at http://www.apple.com/publicsource and
 * read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please
 * see the License for the specific language governing rights and
 * limitations under the License.
 */

/******************************************************************
 
        MUSCLE SmartCard Development ( http://www.linuxnet.com )
            Title  : atrhandler.c
            Author : David Corcoran
            Date   : 7/27/99
            License: Copyright (C) 1999 David Corcoran
                     <corcoran@linuxnet.com> 
            Purpose: This keeps track of smartcard protocols,
                     timing issues, and atr handling.
 
********************************************************************/

/**
 * @file
 *
 * @brief This keeps track of smartcard protocols, timing issues
 * and ATR (Answer-to-Reset) handling.
 *
 * @note use ./configure --enable-debugatr to enable debug messages
 * to be logged.
 */

#include <syslog.h>
#include <string.h>

#include "config.h"
#include "wintypes.h"
#include "pcsclite.h"
#include "debuglog.h"
#include "atrhandler.h"

/**
 * Uncomment the following for ATR debugging 
 * or use ./configure --enable-debugatr
 */
/* #define ATR_DEBUG */

/**
 * @brief parse an ATR
 *
 * @param[out] psExtension
 * @param[in] pucAtr ATR
 * @param[in] dwLength ATR length
 * @return
 */
short ATRDecodeAtr(PSMARTCARD_EXTENSION psExtension,
	const unsigned char *pucAtr, DWORD dwLength)
{
	USHORT p;
	UCHAR K, TCK;				/* MSN of T0/Check Sum */
	UCHAR Y1i, T;				/* MSN/LSN of TDi */
	int i = 1;					/* value of the index in TAi, TBi, etc. */

	/*
	 * Zero out everything 
	 */
	p = K = TCK = Y1i = T = 0;

#ifdef ATR_DEBUG
	if (dwLength > 0)
		LogXxd(PCSC_LOG_DEBUG, "ATR: ", pucAtr, dwLength);
#endif

	if (dwLength < 2)
		return 0;	/** @retval 0 Atr must have TS and T0 */

	/*
	 * Zero out the bitmasks 
	 */
	psExtension->CardCapabilities.AvailableProtocols = SCARD_PROTOCOL_UNDEFINED;
	psExtension->CardCapabilities.CurrentProtocol = SCARD_PROTOCOL_UNDEFINED;

	/*
	 * Decode the TS byte 
	 */
	if (pucAtr[0] == 0x3F)
	{	/* Inverse convention used */
		psExtension->CardCapabilities.Convention = SCARD_CONVENTION_INVERSE;
	}
	else
		if (pucAtr[0] == 0x3B)
	{	/* Direct convention used */
		psExtension->CardCapabilities.Convention = SCARD_CONVENTION_DIRECT;
		}
		else
	{
		memset(psExtension, 0x00, sizeof(SMARTCARD_EXTENSION));
			return 0;	/** @retval 0 Unable to decode TS byte */
	}

	/*
	 * Here comes the platform dependant stuff 
	 */

	/*
	 * Decode the T0 byte 
	 */
	Y1i = pucAtr[1] >> 4;	/* Get the MSN in Y1 */
	K = pucAtr[1] & 0x0F;	/* Get the LSN in K */

	p = 2;

#ifdef ATR_DEBUG
	Log4(PCSC_LOG_DEBUG, "Conv: %02X, Y1: %02X, K: %02X",
		psExtension->CardCapabilities.Convention, Y1i, K);
#endif

	/*
	 * Examine Y1 
	 */
	do
	{
		short TAi, TBi, TCi, TDi;	/* Interface characters */

		TAi = (Y1i & 0x01) ? pucAtr[p++] : -1;
		TBi = (Y1i & 0x02) ? pucAtr[p++] : -1;
		TCi = (Y1i & 0x04) ? pucAtr[p++] : -1;
		TDi = (Y1i & 0x08) ? pucAtr[p++] : -1;

#ifdef ATR_DEBUG
		Log9(PCSC_LOG_DEBUG,
			"TA%d: %02X, TB%d: %02X, TC%d: %02X, TD%d: %02X",
			i, TAi, i, TBi, i, TCi, i, TDi);
#endif

		/*
		 * Examine TDi to determine protocol and more 
		 */
		if (TDi >= 0)
		{
			Y1i = TDi >> 4;	/* Get the MSN in Y1 */
			T = TDi & 0x0F;	/* Get the LSN in K */

			/*
			 * Set the current protocol TD1 (first TD only)
			 */
			if (psExtension->CardCapabilities.CurrentProtocol == SCARD_PROTOCOL_UNDEFINED)
			{
				switch (T)
				{
				case 0:
					psExtension->CardCapabilities.CurrentProtocol =
						SCARD_PROTOCOL_T0;
					break;
				case 1:
					psExtension->CardCapabilities.CurrentProtocol =
						SCARD_PROTOCOL_T1;
					break;
				default:
						return 0; /** @retval 0 Unable to decode LNS */
				}
			}

#ifdef ATR_DEBUG
			Log2(PCSC_LOG_DEBUG, "T=%d Protocol Found", T);
#endif
			if (0 == T)
			{
				psExtension->CardCapabilities.AvailableProtocols |=
					SCARD_PROTOCOL_T0;
			}
			else
				if (1 == T)
			{
				psExtension->CardCapabilities.AvailableProtocols |=
					SCARD_PROTOCOL_T1;
				}
				else
					if (15 == T)
			{
						psExtension->CardCapabilities.AvailableProtocols |=
							SCARD_PROTOCOL_T15;
					}
					else
					{
				/*
				 * Do nothing for now since other protocols are not
				 * supported at this time 
				 */
			}
		} else
			Y1i = 0;

		/* test presence of TA2 */
		if ((2 == i) && (TAi >= 0))
		{
			T = TAi & 0x0F;
#ifdef ATR_DEBUG
			Log2(PCSC_LOG_DEBUG, "Specific mode: T=%d", T);
#endif
			switch (T)
			{
				case 0:
					psExtension->CardCapabilities.CurrentProtocol =
						psExtension->CardCapabilities.AvailableProtocols =
						SCARD_PROTOCOL_T0;
					break;

				case 1:
					psExtension->CardCapabilities.CurrentProtocol =
						psExtension->CardCapabilities.AvailableProtocols =
						SCARD_PROTOCOL_T1;
					break;

				default:
					return 0; /** @retval 0 Unable do decode T protocol */
		}
		}

		if (p > MAX_ATR_SIZE)
		{
			memset(psExtension, 0x00, sizeof(SMARTCARD_EXTENSION));
			return 0;	/** @retval 0 Maximum attribute size */
		}

		/* next interface characters index */
		i++;
	}
	while (Y1i != 0);

	/*
	 * If TDx is not set then the current must be T0 
	 */
	if (psExtension->CardCapabilities.CurrentProtocol == SCARD_PROTOCOL_UNDEFINED)
	{
		psExtension->CardCapabilities.CurrentProtocol = SCARD_PROTOCOL_T0;
		psExtension->CardCapabilities.AvailableProtocols |= SCARD_PROTOCOL_T0;
	}

	/*
	 * Take care of the historical characters 
	 */
	psExtension->ATR.HistoryLength = K;
	memcpy(psExtension->ATR.HistoryValue, &pucAtr[p], K);

	p = p + K;

	/*
	 * Check to see if TCK character is included It will be included if
	 * more than T=0 is supported 
	 */
	if (psExtension->CardCapabilities.AvailableProtocols & SCARD_PROTOCOL_T1)
		TCK = pucAtr[p++];

	memcpy(psExtension->ATR.Value, pucAtr, p);
	psExtension->ATR.Length = p;	/* modified from p-1 */

#ifdef ATR_DEBUG
	Log3(PCSC_LOG_DEBUG, "CurrentProtocol: %d, AvailableProtocols: %d",
		psExtension->CardCapabilities.CurrentProtocol,
		psExtension->CardCapabilities.AvailableProtocols);
#endif

	return 1; /** @retval 1 Success */
}