/*
apdu.c
ISO 7816-4 Application Layer PDU's handling
This file is part of the Unix driver for Towitoko smartcard readers
Copyright (C) 2000 2001 Carlos Prados <cprados@yahoo.com>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "defines.h"
#include "apdu.h"
#include <stdlib.h>
#include <string.h>
/*
* Not exported constants definiton
*/
#define APDU_MIN_CMD_SIZE 4 /* Min command size */
#define APDU_MIN_RSP_SIZE 2 /* Min response size */
#define APDU_CMD_HEADER_SIZE 4 /* Size of the header */
/*
* Exported functions definition
*/
APDU_Cmd *
APDU_Cmd_New (BYTE * data, unsigned long length)
{
APDU_Cmd *apdu;
if ((length > APDU_MAX_CMD_SIZE))
return NULL;
apdu = (APDU_Cmd *) malloc (sizeof (APDU_Cmd));
if (apdu != NULL)
{
apdu->length = MAX (APDU_MIN_CMD_SIZE, length);
apdu->command = (BYTE *) calloc (apdu->length, sizeof (BYTE));
if (apdu->command != NULL)
{
memcpy (apdu->command, data, length);
if (length < apdu->length)
memset (apdu->command + length, 0, apdu->length - length);
}
else
{
free (apdu);
apdu = NULL;
}
}
return apdu;
}
void
APDU_Cmd_Delete (APDU_Cmd * apdu)
{
free (apdu->command);
free (apdu);
}
int
APDU_Cmd_Case (APDU_Cmd * apdu)
{
BYTE B1;
unsigned short B2B3;
unsigned long L;
int res;
/* Calculate length of body */
L = MAX(apdu->length - 4, 0);
/* Case 1 */
if (L == 0)
res = APDU_CASE_1;
else
{
/* Get first byte of body */
B1 = apdu->command[4];
if ((B1 != 0) && (L == B1 + 1))
res = APDU_CASE_2S;
else if (L == 1)
res = APDU_CASE_3S;
else if ((B1 != 0) && (L == B1 + 2))
res = APDU_CASE_4S;
else if ((B1 == 0) && (L>2))
{
/* Get second and third byte of body */
B2B3 = (((unsigned short)(apdu->command[5]) << 8) | apdu->command[6]);
if ((B2B3 != 0) && (L == B2B3 + 3))
res = APDU_CASE_2E;
else if (L == 3)
res = APDU_CASE_3E;
else if ((B2B3 != 0) && (L == B2B3 + 5))
res = APDU_CASE_4E;
else
res = APDU_MALFORMED;
}
else
res = APDU_MALFORMED;
}
return res;
}
BYTE
APDU_Cmd_Cla (APDU_Cmd * apdu)
{
return apdu->command[0];
}
BYTE
APDU_Cmd_Ins (APDU_Cmd * apdu)
{
return apdu->command[1];
}
BYTE
APDU_Cmd_P1 (APDU_Cmd * apdu)
{
return apdu->command[2];
}
BYTE
APDU_Cmd_P2 (APDU_Cmd * apdu)
{
return apdu->command[3];
}
unsigned long
APDU_Cmd_Lc (APDU_Cmd * apdu)
{
int c;
unsigned long res;
c = APDU_Cmd_Case (apdu);
if ((c == APDU_CASE_1) || (c == APDU_CASE_3S) || (c == APDU_CASE_3E))
res = 0;
else if ((c == APDU_CASE_2S) || (c == APDU_CASE_4S))
res = apdu->command[4];
else if ((c == APDU_CASE_2E) || (c == APDU_CASE_4E))
res = (((unsigned long)(apdu->command[5]) << 8) | apdu->command[6]);
else
res = 0;
return res;
}
unsigned long
APDU_Cmd_Le (APDU_Cmd * apdu)
{
int c;
unsigned long res;
c = APDU_Cmd_Case (apdu);
if ((c == APDU_CASE_1) || (c == APDU_CASE_2S) || (c == APDU_CASE_2E))
res = 0;
else if (c == APDU_CASE_3S)
res = ((apdu->command[4] == 0) ? 256: apdu->command[4]);
else if (c == APDU_CASE_4S)
res = ((apdu->command[apdu->length - 1] == 0) ? 256: apdu->command[apdu->length - 1]);
else if (c == APDU_CASE_3E)
res = ((((unsigned long)(apdu->command[5]) << 8) | apdu->command[6]) == 0 ? 65536:
(((unsigned long)(apdu->command[5]) << 8) | apdu->command[6]));
else if (c == APDU_CASE_4E)
res = ((((unsigned long)(apdu->command[apdu->length - 2]) << 8) | apdu->command[apdu->length - 1]) == 0 ? 65536:
(((unsigned long)(apdu->command[apdu->length - 2]) << 8) | apdu->command[apdu->length - 1]));
else
res = 0;
return res;
}
bool
APDU_Cmd_Le_Available (APDU_Cmd * apdu)
{
int c;
bool res;
c = APDU_Cmd_Case (apdu);
if (c == APDU_CASE_3S)
res = (apdu->command[4] == 0);
else if (c == APDU_CASE_4S)
res = (apdu->command[apdu->length - 1] == 0);
else if (c == APDU_CASE_3E)
res = ((((unsigned long)(apdu->command[5]) << 8) | apdu->command[6]) == 0);
else if (c == APDU_CASE_4E)
res = ((((unsigned long)(apdu->command[apdu->length - 2]) << 8) | apdu->command[apdu->length - 1]) == 0);
else
res = FALSE;
return res;
}
BYTE *
APDU_Cmd_Header (APDU_Cmd * apdu)
{
return apdu->command;
}
BYTE *
APDU_Cmd_Data (APDU_Cmd * apdu)
{
int c;
BYTE * res;
c = APDU_Cmd_Case (apdu);
if ((c == APDU_CASE_1) || (c == APDU_CASE_3S) || (c == APDU_CASE_3E))
res = NULL;
else if ((c == APDU_CASE_2S) || (c == APDU_CASE_4S))
res = apdu->command + 5;
else if ((c == APDU_CASE_2E) || (c == APDU_CASE_4E))
res = apdu->command + 7;
else
res = NULL;
return res;
}
BYTE *
APDU_Cmd_Raw (APDU_Cmd * apdu)
{
return apdu->command;
}
unsigned long
APDU_Cmd_RawLen (APDU_Cmd * apdu)
{
return apdu->length;
}
APDU_Rsp *
APDU_Rsp_New (BYTE * data, unsigned long length)
{
APDU_Rsp *apdu;
if (length < APDU_MIN_RSP_SIZE)
return NULL;
apdu = (APDU_Rsp *) malloc (sizeof (APDU_Rsp));
if (apdu != NULL)
{
apdu->length = length;
apdu->response = (BYTE *) calloc (length, sizeof (BYTE));
if (apdu->response != NULL)
memcpy (apdu->response, data, length);
else
{
free (apdu);
apdu = NULL;
}
}
return apdu;
}
void
APDU_Rsp_Delete (APDU_Rsp * apdu)
{
free (apdu->response);
free (apdu);
}
BYTE APDU_Rsp_SW1 (APDU_Rsp * apdu)
{
return (apdu->response[(apdu->length) - 2]);
}
BYTE APDU_Rsp_SW2 (APDU_Rsp * apdu)
{
return (apdu->response[(apdu->length) - 1]);
}
unsigned long
APDU_Rsp_DataLen (APDU_Rsp * apdu)
{
return (apdu->length - 2);
}
BYTE *
APDU_Rsp_Data (APDU_Rsp * apdu)
{
return apdu->response;
}
BYTE *
APDU_Rsp_Raw (APDU_Rsp * apdu)
{
return apdu->response;
}
unsigned long
APDU_Rsp_RawLen (APDU_Rsp * apdu)
{
return apdu->length;
}
void
APDU_Rsp_TruncateData (APDU_Rsp * apdu, unsigned long length)
{
if ((length > 0) && ((signed long)length < (signed long)(apdu->length - 2)))
{
apdu->response[length] = APDU_Rsp_SW1 (apdu);
apdu->response[length + 1] = APDU_Rsp_SW2 (apdu);
apdu->length = length +2;
}
}
int
APDU_Rsp_AppendData (APDU_Rsp * apdu1, APDU_Rsp * apdu2)
{
BYTE * response;
unsigned long length;
int ret;
length = APDU_Rsp_DataLen(apdu1) + APDU_Rsp_RawLen(apdu2);
if ((length > 2) && (length <= APDU_MAX_RSP_SIZE))
{
response = (BYTE *) realloc (apdu1->response, length);
if (response != NULL)
{
memcpy (response + APDU_Rsp_DataLen (apdu1),
APDU_Rsp_Raw (apdu2), APDU_Rsp_RawLen(apdu2));
apdu1->response = response;
apdu1->length = length;
ret = APDU_OK;
}
else
ret = APDU_MALFORMED;
}
else
ret = APDU_MALFORMED;
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1