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
);
66 const char *Error::what() const throw ()
68 return pcsc_stringify_error((int32_t)error
);
72 void Error::throwMe(unsigned long err
)
78 OSStatus
Error::osStatus() const
82 // @@@ more errors should be mapped here
83 case SCARD_W_RESET_CARD
:
84 return CSSMERR_CSP_DEVICE_RESET
;
86 return CSSMERR_CSP_INTERNAL_ERROR
;
90 int Error::unixError() const
92 return EINVAL
; //@@@ preliminary
99 void ReaderState::set(const char *name
, unsigned long known
)
104 dwCurrentState
= (uint32_t)known
;
108 void ReaderState::lastKnown(unsigned long s
)
110 // clear out CHANGED and UNAVAILABLE
111 dwCurrentState
= (uint32_t)s
& ~(SCARD_STATE_CHANGED
| SCARD_STATE_UNAVAILABLE
);
115 void ReaderState::setATR(const void *atr
, size_t size
)
117 if (size
> sizeof(rgbAtr
))
118 Error::throwMe(SCARD_E_INVALID_ATR
);
119 memcpy(rgbAtr
, atr
, size
);
120 cbAtr
= (uint32_t)size
;
124 #if defined(DEBUGDUMP)
126 void ReaderState::dump()
128 Debug::dump("reader(%s) state=0x%x events=0x%x",
129 szReader
? szReader
: "(null)", dwCurrentState
, dwEventState
);
130 Debug::dumpData(" ATR", rgbAtr
, cbAtr
);
152 // (Re)establish PCSC context.
153 // Don't fail on SCARD_F_INTERNAL_ERROR (pcscd not running - urgh).
159 Error::check(::SCardEstablishContext(SCARD_SCOPE_SYSTEM
, NULL
, NULL
, &mContext
));
161 secdebug("pcsc", "context opened");
162 } catch (const Error
&err
) {
163 if (err
.error
== SCARD_F_INTERNAL_ERROR
)
165 secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready");
172 void Session::close()
178 Error::check(SCardReleaseContext(mContext
));
179 secdebug("pcsc", "context closed");
180 } catch (const Error
&err
) {
181 if (err
.error
== SCARD_F_INTERNAL_ERROR
)
183 secdebug("pcsc", "got internal error; assuming pcscd absent; context not ready");
190 bool Session::check(long rc
)
193 case SCARD_S_SUCCESS
:
194 return true; // got reader(s), call succeeded
195 case SCARD_E_READER_UNAVAILABLE
:
196 return false; // no readers, but don't treat as error
199 return false; // placebo
204 void Session::listReaders(vector
<string
> &readers
, const char *groups
)
207 if (check(::SCardListReaders(mContext
, groups
, NULL
, &size
)))
209 mReaderBuffer
.resize(size
);
210 if (check(::SCardListReaders(mContext
, groups
, &mReaderBuffer
[0], &size
)))
212 decode(readers
, mReaderBuffer
, size
);
217 readers
.clear(); // treat as success (returning zero readers)
222 // Reader status check
224 void Session::statusChange(ReaderState
*readers
, unsigned int nReaders
, long timeout
)
226 check(::SCardGetStatusChange(mContext
, (uint32_t)timeout
, readers
, nReaders
));
234 : mConnectedState(kInitial
)
242 void Card::setIOType(unsigned long activeProtocol
)
244 switch (activeProtocol
)
246 case SCARD_PROTOCOL_T0
:
247 mIOType
= SCARD_PCI_T0
;
249 case SCARD_PROTOCOL_T1
:
250 mIOType
= SCARD_PCI_T1
;
253 mIOType
= SCARD_PCI_RAW
;
258 void Card::connect(Session
&session
, const char *reader
,
259 unsigned long share
, unsigned long protocols
)
261 uint32_t activeProtocol
;
262 Error::check(::SCardConnect(session
.mContext
,
263 reader
, (uint32_t)share
, (uint32_t)protocols
, &mHandle
, &activeProtocol
));
264 setIOType(activeProtocol
);
265 mConnectedState
= kConnected
;
268 void Card::reconnect(unsigned long share
, unsigned long protocols
, unsigned long initialization
)
270 assert(mConnectedState
!= kInitial
);
272 uint32_t activeProtocol
;
273 Error::check(::SCardReconnect(mHandle
, (uint32_t)share
, (uint32_t)protocols
,
274 (uint32_t)initialization
, &activeProtocol
));
275 setIOType(activeProtocol
);
276 mConnectedState
= kConnected
;
279 void Card::disconnect(unsigned long disposition
)
281 if (mConnectedState
== kConnected
)
283 if (mTransactionNestLevel
> 0)
285 secdebug("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel
);
286 mTransactionNestLevel
= 0;
289 checkReset(::SCardDisconnect(mHandle
, (uint32_t)disposition
));
291 mConnectedState
= kInitial
;
296 Card::checkReset(unsigned int rv
)
298 if (rv
== SCARD_W_RESET_CARD
)
300 secdebug("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
307 Card::didDisconnect()
309 mConnectedState
= kDisconnected
;
310 mTransactionNestLevel
= 0;
319 Card::transmit(const unsigned char *pbSendBuffer
, size_t cbSendLength
,
320 unsigned char *pbRecvBuffer
, size_t &pcbRecvLength
)
322 if (mConnectedState
== kDisconnected
)
324 secdebug("pcsc", "%p: transmit after disconnect, reconnecting", this);
328 IFDUMPING("pcsc", dump("->", pbSendBuffer
, cbSendLength
));
330 uint32_t tmpRecvLength
= (uint32_t)pcbRecvLength
;
331 checkReset(::SCardTransmit(mHandle
, mIOType
, pbSendBuffer
, (uint32_t)cbSendLength
,
332 NULL
, pbRecvBuffer
, &tmpRecvLength
));
333 pcbRecvLength
= tmpRecvLength
;
335 IFDUMPING("pcsc", dump("<-", pbRecvBuffer
, pcbRecvLength
));
340 // Only the first transaction started is sent to PCSC
341 if (mTransactionNestLevel
== 0)
343 if (mConnectedState
== kDisconnected
)
345 secdebug("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
349 checkReset(::SCardBeginTransaction(mHandle
));
351 mTransactionNestLevel
++;
352 secdebug("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel
);
355 void Card::end(unsigned long disposition
)
357 // Only the last transaction ended is sent to PCSC
358 secdebug("pcsc", "%p end transaction: %d", this, mTransactionNestLevel
);
359 if (disposition
== SCARD_RESET_CARD
)
361 if (mConnectedState
== kDisconnected
)
363 secdebug("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
367 checkReset(::SCardEndTransaction(mHandle
, (uint32_t)disposition
));
370 else if (mTransactionNestLevel
> 0)
372 --mTransactionNestLevel
;
373 if (mTransactionNestLevel
== 0)
375 if (mConnectedState
== kDisconnected
)
376 secdebug("pcsc", "%p: end transaction while disconnected ignored", this);
379 checkReset(::SCardEndTransaction(mHandle
, (uint32_t)disposition
));
388 end(/*SCARD_RESET_CARD*/);
391 #if defined(DEBUGDUMP)
394 Card::dump(const char *direction
, const unsigned char *buffer
, size_t length
)
396 Debug::dump("[%02lu]%s:", length
, direction
);
398 for (size_t ix
= 0; ix
< length
; ++ix
)
399 Debug::dump(" %02x", buffer
[ix
]);
407 void Transaction::commitAction()
409 mCarrier
.end(mDisposition
);
414 } // namespace Security