Click here to Skip to main content
15,896,348 members
Articles / Programming Languages / C++

An UMDF Driver for a Virtual Smart Card Reader

Rate me:
Please Sign up or sign in to vote.
4.89/5 (70 votes)
30 Jan 2014CPOL23 min read 594.7K   10.5K   104  
A simple implementation of a driver for a virtual smart card reader, based on UMDF
#include "internal.h"
#include "VirtualSCReader.h"
#include "reader.h"
#include "device.h"
#include <winscard.h>
#include "memory.h"
#include <Sddl.h>
#include "SectionLocker.h"

void Reader::init(wchar_t *section) {
	section;
	state=SCARD_ABSENT;
}

void Reader::IoSmartCardIsPresent(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	
	OutputDebugString(L"[BixVReader][IPRE]IOCTL_SMARTCARD_IS_PRESENT");
	if (CheckATR()) {
		// there's a smart card present, so complete the request
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
	}
	else {
		// there's no smart card present, so leave the request pending; it will be completed later
		SectionLocker lock(device->m_RequestLock);
		waitInsertIpr=pRequest;
		IRequestCallbackCancel *callback;
		device->QueryInterface(__uuidof(IRequestCallbackCancel),(void**)&callback);
		pRequest->MarkCancelable(callback);
		callback->Release();
	}
}

void Reader::IoSmartCardGetState(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	OutputDebugString(L"[BixVReader][GSTA]IOCTL_SMARTCARD_GET_STATE");
	wchar_t log[300];
	swprintf(log,L"[BixVReader]STATE:%08X",state);
	OutputDebugString(log);
	setInt(device,pRequest,state);
}
void Reader::IoSmartCardIsAbsent(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	OutputDebugString(L"[BixVReader][IABS]IOCTL_SMARTCARD_IS_ABSENT");
	if (!CheckATR()) {
		// there's no smart card present, so complete the request
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
	}
	else {
		SectionLocker lock(device->m_RequestLock);
		// there's a smart card present, so leave the request pending; it will be completed later
		waitRemoveIpr=pRequest;
		IRequestCallbackCancel *callback;

		device->QueryInterface(__uuidof(IRequestCallbackCancel),(void**)&callback);
		pRequest->MarkCancelable(callback);
		callback->Release();
	}
}

void Reader::IoSmartCardPower(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	OutputDebugString(L"[BixVReader][POWR]IOCTL_SMARTCARD_POWER");
	DWORD code=getInt(pRequest);
	if (code==SCARD_COLD_RESET) {
		OutputDebugString(L"[BixVReader][POWR]SCARD_COLD_RESET");
		protocol=0;
		powered=1;
		state=SCARD_NEGOTIABLE;
	}
	else if (code==SCARD_WARM_RESET) {
		OutputDebugString(L"[BixVReader][POWR]SCARD_WARM_RESET");
		protocol=0;
		powered=1;
		state=SCARD_NEGOTIABLE;
	}
	else if (code==SCARD_POWER_DOWN) {
		OutputDebugString(L"[BixVReader][POWR]SCARD_POWER_DOWN");
		protocol=0;
		powered=0;
		state=SCARD_SWALLOWED;
	}
	if (code==SCARD_COLD_RESET || code==SCARD_WARM_RESET) {
		BYTE ATR[100];
		DWORD ATRsize;
		if (!QueryATR(ATR,&ATRsize,true))
		{
			pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);					
			return;
		}
		setBuffer(device,pRequest,ATR,ATRsize);
	}
	else {
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
	}
	
}

void Reader::IoSmartCardSetProtocol(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	UNREFERENCED_PARAMETER(instance);

	DWORD requestedProtocol=getInt(pRequest);	
	wchar_t log[300];
	swprintf(log,L"[BixVReader][SPRT]IOCTL_SMARTCARD_SET_PROTOCOL:%08X",requestedProtocol);
	OutputDebugString(log);

	BYTE ATR[100];
	DWORD ATRsize;
	state=SCARD_SPECIFIC;
	if (!QueryATR(ATR,&ATRsize,true))
	{
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);					
		return;
	}
		
	if (((requestedProtocol & SCARD_PROTOCOL_T1) != 0) &&
		((availableProtocol & SCARD_PROTOCOL_T1) != 0)) {
		protocol = SCARD_PROTOCOL_T1;
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
		OutputDebugString(L"[BixVReader]PROTOCOL SET: T1");
		return;
	}	

	if (((requestedProtocol & SCARD_PROTOCOL_T0) != 0) &&
		((availableProtocol & SCARD_PROTOCOL_T0) != 0)) {
		protocol = SCARD_PROTOCOL_T0;
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
		OutputDebugString(L"[BixVReader]PROTOCOL SET: T0");
		return;
	}	

	if (((requestedProtocol & (SCARD_PROTOCOL_DEFAULT)) != 0) &&
		((availableProtocol & SCARD_PROTOCOL_T1) != 0)) {
		protocol = SCARD_PROTOCOL_T1;
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
		OutputDebugString(L"[BixVReader]PROTOCOL SET: T1");
		return;
	}	

	if (((requestedProtocol & (SCARD_PROTOCOL_DEFAULT)) != 0) &&
		((availableProtocol & SCARD_PROTOCOL_T0) != 0)) {
		protocol = SCARD_PROTOCOL_T0;
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
		OutputDebugString(L"[BixVReader]PROTOCOL SET: T0");
		return;
	}	
	{
		state=SCARD_NEGOTIABLE;
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
	}
}

	void Reader::IoSmartCardSetAttribute(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	UNREFERENCED_PARAMETER(instance);
	OutputDebugString(L"[BixVReader][SATT]IOCTL_SMARTCARD_SET_ATTRIBUTE");
	
	IWDFMemory *inmem=NULL;
	pRequest->GetInputMemory(&inmem);
	
	SIZE_T size;
	BYTE *data=(BYTE *)inmem->GetDataBuffer(&size);

	DWORD minCode=*(DWORD*)(data);
	bool handled=false;
	if (minCode==SCARD_ATTR_DEVICE_IN_USE) {
		SectionLocker lock(device->m_RequestLock);
		OutputDebugString(L"[BixVReader][SATT]SCARD_ATTR_DEVICE_IN_USE");
		pRequest->CompleteWithInformation(STATUS_SUCCESS, 0);
		handled=true;
	}
	inmem->Release();
	if (!handled) {
		SectionLocker lock(device->m_RequestLock);
		wchar_t log[300];
		swprintf(log,L"[BixVReader][SATT]ERROR_NOT_SUPPORTED:%08X",minCode);
		OutputDebugString(log);
		pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
	}
}

void Reader::IoSmartCardTransmit(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	UNREFERENCED_PARAMETER(outBufSize);
	OutputDebugString(L"[BixVReader][TRSM]IOCTL_SMARTCARD_TRANSMIT");
	BYTE APDU[1000];
	int APDUSize;
	getBuffer(pRequest,APDU,&APDUSize);
	if (((SCARD_IO_REQUEST*)APDU)->dwProtocol!=protocol) {
		SectionLocker lock(device->m_RequestLock);
        pRequest->CompleteWithInformation(STATUS_INVALID_DEVICE_STATE, 0);
		return;
	}
	BYTE Resp[1000];
	int RespSize;
	if (!QueryTransmit(APDU+sizeof(SCARD_IO_REQUEST),APDUSize-sizeof(SCARD_IO_REQUEST),Resp+sizeof(SCARD_IO_REQUEST),&RespSize))
	{
		SectionLocker lock(device->m_RequestLock);
		pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);					
		return;
	}
	((SCARD_IO_REQUEST*)Resp)->cbPciLength=sizeof(SCARD_IO_REQUEST);
	((SCARD_IO_REQUEST*)Resp)->dwProtocol=protocol;
	setBuffer(device,pRequest,Resp,RespSize+sizeof(SCARD_IO_REQUEST));
}

void Reader::IoSmartCardGetAttribute(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T outBufSize) {
	UNREFERENCED_PARAMETER(inBufSize);
	
	wchar_t log[300]=L"";
	char temp[300];

	DWORD code=getInt(pRequest);
	swprintf(log,L"[BixVReader][GATT]  - code %0X",code);
	OutputDebugString(log);

	switch(code) {
		case SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA009):
			// custom attribute; RPC_TYPE
			OutputDebugString(L"[BixVReader][GATT]RPC_TYPE");
			setInt(device,pRequest,rpcType);
			return;
		case SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA00a):
			// custom attribute; PipeName
			if (rpcType==0) {	
				PipeReader *pipe=(PipeReader *)this;
				OutputDebugString(L"[BixVReader][GATT]PIPE_NAME");
				sprintf(temp,"%S",pipe->pipeName);
				setString(device,pRequest,(char*)temp,(int)outBufSize);
			}
			else {
				SectionLocker lock(device->m_RequestLock);
				pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
			}
			return;
		case SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA00b):
			// custom attribute; EventPipeName
			if (rpcType==0) {	
				PipeReader *pipe=(PipeReader *)this;
				OutputDebugString(L"[BixVReader][GATT]EVENT_PIPE_NAME");
				sprintf(temp,"%S",pipe->pipeEventName);
				setString(device,pRequest,(char*)temp,(int)outBufSize);
			}
			else {
				SectionLocker lock(device->m_RequestLock);
				pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
			}
			return;
		case SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA00c):
			// custom attribute; TCP port
			if (rpcType==1) {	
				TcpIpReader *tcpIp=(TcpIpReader *)this;
				OutputDebugString(L"[BixVReader][GATT]PORT");
				setInt(device,pRequest,tcpIp->port);
			}
			else {
				SectionLocker lock(device->m_RequestLock);
				pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
			}
			return;
		case SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA00d):
			// custom attribute; TCP event port
			if (rpcType==1) {	
				TcpIpReader *tcpIp=(TcpIpReader *)this;
				OutputDebugString(L"[BixVReader][GATT]EVENT_PORT");
				setInt(device,pRequest,tcpIp->eventPort);
			}
			else {
				SectionLocker lock(device->m_RequestLock);
				pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
			}
			return;
		case SCARD_ATTR_VALUE(SCARD_CLASS_VENDOR_DEFINED, 0xA00e):
			// custom attribute; TCP base port
			if (rpcType==1) {	
				TcpIpReader *tcpIp=(TcpIpReader *)this;
				OutputDebugString(L"[BixVReader][GATT]BASE_PORT");
				setInt(device,pRequest,tcpIp->portBase);
				tcpIp;
			}
			else {
				SectionLocker lock(device->m_RequestLock);
				pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
			}
			return;
		case SCARD_ATTR_CHARACTERISTICS:
			// 0x00000000 No special characteristics
			OutputDebugString(L"[BixVReader][GATT]SCARD_ATTR_CHARACTERISTICS");
			setInt(device,pRequest,0);
			return;
		case SCARD_ATTR_VENDOR_NAME:
			OutputDebugString(L"[BixVReader][GATT]SCARD_ATTR_VENDOR_NAME");
			setString(device,pRequest,vendorName,(int)outBufSize);
			return;
		case SCARD_ATTR_VENDOR_IFD_TYPE:
			OutputDebugString(L"[BixVReader][GATT]SCARD_ATTR_VENDOR_IFD_TYPE");
			setString(device,pRequest,vendorIfdType,(int)outBufSize);
			return;
		case SCARD_ATTR_DEVICE_UNIT:
			OutputDebugString(L"[BixVReader][GATT]SCARD_ATTR_DEVICE_UNIT");
			setInt(device,pRequest,deviceUnit);
			return;
		case SCARD_ATTR_ATR_STRING:
			OutputDebugString(L"[BixVReader][GATT]SCARD_ATTR_ATR_STRING");
			BYTE ATR[100];
			DWORD ATRsize;
			if (!QueryATR(ATR,&ATRsize))
			{
				SectionLocker lock(device->m_RequestLock);
				pRequest->CompleteWithInformation(STATUS_NO_MEDIA, 0);					
				return;
			}
			setBuffer(device,pRequest,ATR,ATRsize);
			return;
		case SCARD_ATTR_CURRENT_PROTOCOL_TYPE:
			OutputDebugString(L"[BixVReader][GATT]SCARD_ATTR_CURRENT_PROTOCOL_TYPE");
			setInt(device,pRequest,protocol); // T=0 or T=1
			return;
		default: {
			swprintf(log,L"[BixVReader][GATT]ERROR_NOT_SUPPORTED:%08X",code);
			OutputDebugString(log);
			SectionLocker lock(device->m_RequestLock);
	        pRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED), 0);
		}
	}
}

bool Reader::CheckATR() {
	return false;
}

bool Reader::QueryTransmit(BYTE *APDU,int APDUlen,BYTE *Resp,int *Resplen) {
	APDU;
	APDUlen;
	Resp;
	Resplen;
	return false;
}

bool Reader::QueryATR(BYTE *ATR,DWORD *ATRsize,bool reset) {
	ATR;
	ATRsize;
	reset;
	return false;
}

bool Reader::initProtocols() {
	// ask ATR to determine available protocols
	BYTE ATR[100];
	DWORD ATRsize=100;
	availableProtocol=0;
	if (QueryATR(ATR,&ATRsize,true))
	{
		availableProtocol=0;
		int iNumHistBytes=0;
		int iTotHistBytes=0;
		int y=0;
		int block=1;
		bool isHist=false;
		char let='A';
		for (unsigned int i=0;i<ATRsize && ! isHist;i++) {
			if (i==0) {
				// we are in TS
			}
			else if (i==1) {
				// we are in T0; read number of historical bytes
				let='A';
				y=ATR[i] >> 4;			
				iTotHistBytes=ATR[i] & 0xF;
			}
			else {
				while ((y & 1)==0) {
					let++;
					y>>=1;
					if (y==0) {
						isHist=true;
						iNumHistBytes=1;
						i--;
						goto end;
					}
				}
				// we are in T(let)(block)
				y=y & 0xE;
				if (let=='D') {
					// check available protocol(s)
					int protocol=(ATR[i] & 0x0f);
					availableProtocol|=1 << protocol;
					// prepare for next block
					let='A';
					y=ATR[i] >> 4;
					block++;
				}
			}
end:
			;
		}
		//support at least T=0
		if (availableProtocol==0)
			availableProtocol=1;
		return true;
	}
	return false;
}

DWORD Reader::startServer() {
	return 0;
}

void Reader::shutdown() {
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions