2 * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 // pcsc++ - PCSC client interface layer in C++
29 #include <security_utilities/debugging.h>
30 #include <PCSC/pcsclite.h>
31 #include <PCSC/wintypes.h>
32 #include <Security/cssmapple.h>
41 static void decode(vector
<string
> &names
, const char *buffer
, size_t size
)
44 for (size_t pos
= 0; pos
< size
- 1; ) {
45 size_t len
= strlen(buffer
+ pos
);
46 names
.push_back(string(buffer
+ pos
, len
));
51 inline void decode(vector
<string
> &names
, const vector
<char> &buffer
, size_t size
)
53 decode(names
, &buffer
[0], size
);
60 Error::Error(unsigned long err
) : error(err
)
62 SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err
);
63 secnotice("security_exception", "pcsc: %d", (unsigned int) err
);
67 const char *Error::what() const throw ()
69 return pcsc_stringify_error((int32_t)error
);
73 void Error::throwMe(unsigned long err
)
79 OSStatus
Error::osStatus() const
83 // @@@ more errors should be mapped here
84 case SCARD_W_RESET_CARD
:
85 return CSSMERR_CSP_DEVICE_RESET
;
87 return CSSMERR_CSP_INTERNAL_ERROR
;
91 int Error::unixError() const
93 return EINVAL
; //@@@ preliminary
100 void ReaderState::set(const char *name
, unsigned long known
)
105 dwCurrentState
= (uint32_t)known
;
109 void ReaderState::lastKnown(unsigned long s
)
111 // clear out CHANGED and UNAVAILABLE
112 dwCurrentState
= (uint32_t)s
& ~(SCARD_STATE_CHANGED
| SCARD_STATE_UNAVAILABLE
);
116 void ReaderState::setATR(const void *atr
, size_t size
)
118 if (size
> sizeof(rgbAtr
))
119 Error::throwMe(SCARD_E_INVALID_ATR
);
120 memcpy(rgbAtr
, atr
, size
);
121 cbAtr
= (uint32_t)size
;
125 #if defined(DEBUGDUMP)
127 void ReaderState::dump()
129 Debug::dump("reader(%s) state=0x%x events=0x%x",
130 szReader
? szReader
: "(null)", dwCurrentState
, dwEventState
);
131 Debug::dumpData(" ATR", rgbAtr
, cbAtr
);
153 // (Re)establish PCSC context.
154 // Don't fail on SCARD_F_INTERNAL_ERROR (pcscd not running - urgh).
160 Error::check(::SCardEstablishContext(SCARD_SCOPE_SYSTEM
, NULL
, NULL
, &mContext
));
162 secinfo("pcsc", "context opened");
163 } catch (const Error
&err
) {
164 if (err
.error
== SCARD_F_INTERNAL_ERROR
)
166 secinfo("pcsc", "got internal error; assuming pcscd absent; context not ready");
173 void Session::close()
179 Error::check(SCardReleaseContext(mContext
));
180 secinfo("pcsc", "context closed");
181 } catch (const Error
&err
) {
182 if (err
.error
== SCARD_F_INTERNAL_ERROR
)
184 secinfo("pcsc", "got internal error; assuming pcscd absent; context not ready");
191 bool Session::check(long rc
)
194 case SCARD_S_SUCCESS
:
195 return true; // got reader(s), call succeeded
196 case SCARD_E_READER_UNAVAILABLE
:
197 return false; // no readers, but don't treat as error
200 return false; // placebo
205 void Session::listReaders(vector
<string
> &readers
, const char *groups
)
207 uint32_t size
= uint32_t(mReaderBuffer
.size());
210 int32_t rc
= ::SCardListReaders(mContext
, groups
, &mReaderBuffer
[0], &size
);
212 case SCARD_S_SUCCESS
:
213 if (size
<= mReaderBuffer
.size()) {
214 decode(readers
, mReaderBuffer
, size
);
217 case SCARD_E_INSUFFICIENT_BUFFER
:
218 mReaderBuffer
.resize(size
);
228 // Reader status check
230 void Session::statusChange(ReaderState
*readers
, unsigned int nReaders
, long timeout
)
232 check(::SCardGetStatusChange(mContext
, (uint32_t)timeout
, readers
, nReaders
));
240 : mConnectedState(kInitial
)
248 void Card::setIOType(unsigned long activeProtocol
)
250 switch (activeProtocol
)
252 case SCARD_PROTOCOL_T0
:
253 mIOType
= SCARD_PCI_T0
;
255 case SCARD_PROTOCOL_T1
:
256 mIOType
= SCARD_PCI_T1
;
259 mIOType
= SCARD_PCI_RAW
;
264 void Card::connect(Session
&session
, const char *reader
,
265 unsigned long share
, unsigned long protocols
)
267 uint32_t activeProtocol
;
268 Error::check(::SCardConnect(session
.mContext
,
269 reader
, (uint32_t)share
, (uint32_t)protocols
, &mHandle
, &activeProtocol
));
270 setIOType(activeProtocol
);
271 mConnectedState
= kConnected
;
274 void Card::reconnect(unsigned long share
, unsigned long protocols
, unsigned long initialization
)
276 assert(mConnectedState
!= kInitial
);
278 uint32_t activeProtocol
;
279 Error::check(::SCardReconnect(mHandle
, (uint32_t)share
, (uint32_t)protocols
,
280 (uint32_t)initialization
, &activeProtocol
));
281 setIOType(activeProtocol
);
282 mConnectedState
= kConnected
;
285 void Card::disconnect(unsigned long disposition
)
287 if (mConnectedState
== kConnected
)
289 if (mTransactionNestLevel
> 0)
291 secinfo("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel
);
292 mTransactionNestLevel
= 0;
295 checkReset(::SCardDisconnect(mHandle
, (uint32_t)disposition
));
297 mConnectedState
= kInitial
;
302 Card::checkReset(unsigned int rv
)
304 if (rv
== SCARD_W_RESET_CARD
)
306 secinfo("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
313 Card::didDisconnect()
315 mConnectedState
= kDisconnected
;
316 mTransactionNestLevel
= 0;
325 Card::transmit(const unsigned char *pbSendBuffer
, size_t cbSendLength
,
326 unsigned char *pbRecvBuffer
, size_t &pcbRecvLength
)
328 if (mConnectedState
== kDisconnected
)
330 secinfo("pcsc", "%p: transmit after disconnect, reconnecting", this);
334 IFDUMPING("pcsc", dump("->", pbSendBuffer
, cbSendLength
));
336 uint32_t tmpRecvLength
= (uint32_t)pcbRecvLength
;
337 checkReset(::SCardTransmit(mHandle
, mIOType
, pbSendBuffer
, (uint32_t)cbSendLength
,
338 NULL
, pbRecvBuffer
, &tmpRecvLength
));
339 pcbRecvLength
= tmpRecvLength
;
341 IFDUMPING("pcsc", dump("<-", pbRecvBuffer
, pcbRecvLength
));
346 // Only the first transaction started is sent to PCSC
347 if (mTransactionNestLevel
== 0)
349 if (mConnectedState
== kDisconnected
)
351 secinfo("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
355 checkReset(::SCardBeginTransaction(mHandle
));
357 mTransactionNestLevel
++;
358 secinfo("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel
);
361 void Card::end(unsigned long disposition
)
363 // Only the last transaction ended is sent to PCSC
364 secinfo("pcsc", "%p end transaction: %d", this, mTransactionNestLevel
);
365 if (disposition
== SCARD_RESET_CARD
)
367 if (mConnectedState
== kDisconnected
)
369 secinfo("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
373 checkReset(::SCardEndTransaction(mHandle
, (uint32_t)disposition
));
376 else if (mTransactionNestLevel
> 0)
378 --mTransactionNestLevel
;
379 if (mTransactionNestLevel
== 0)
381 if (mConnectedState
== kDisconnected
)
382 secinfo("pcsc", "%p: end transaction while disconnected ignored", this);
385 checkReset(::SCardEndTransaction(mHandle
, (uint32_t)disposition
));
394 end(/*SCARD_RESET_CARD*/);
397 #if defined(DEBUGDUMP)
400 Card::dump(const char *direction
, const unsigned char *buffer
, size_t length
)
402 Debug::dump("[%02lu]%s:", length
, direction
);
404 for (size_t ix
= 0; ix
< length
; ++ix
)
405 Debug::dump(" %02x", buffer
[ix
]);
413 void Transaction::commitAction()
415 mCarrier
.end(mDisposition
);
420 } // namespace Security