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@
25 // pcsc++ - PCSC client interface layer in C++
31 #include <security_utilities/debugging.h>
32 #include <PCSC/pcsclite.h>
33 #include <PCSC/wintypes.h>
34 #include <Security/cssmapple.h>
43 static void decode(vector
<string
> &names
, const char *buffer
, size_t size
)
46 for (size_t pos
= 0; pos
< size
- 1; ) {
47 size_t len
= strlen(buffer
+ pos
);
48 names
.push_back(string(buffer
+ pos
, len
));
53 inline void decode(vector
<string
> &names
, const vector
<char> &buffer
, size_t size
)
55 decode(names
, &buffer
[0], size
);
62 Error::Error(unsigned long err
) : error(err
)
64 SECURITY_EXCEPTION_THROW_PCSC(this, (unsigned int)err
);
65 secnotice("security_exception", "pcsc: %d", (unsigned int) err
);
69 const char *Error::what() const _NOEXCEPT
71 return pcsc_stringify_error((int32_t)error
);
75 void Error::throwMe(unsigned long err
)
81 OSStatus
Error::osStatus() const
85 // @@@ more errors should be mapped here
86 case SCARD_W_RESET_CARD
:
87 return CSSMERR_CSP_DEVICE_RESET
;
89 return CSSMERR_CSP_INTERNAL_ERROR
;
93 int Error::unixError() const
95 return EINVAL
; //@@@ preliminary
102 void ReaderState::set(const char *name
, unsigned long known
)
107 dwCurrentState
= (uint32_t)known
;
111 void ReaderState::lastKnown(unsigned long s
)
113 // clear out CHANGED and UNAVAILABLE
114 dwCurrentState
= (uint32_t)s
& ~(SCARD_STATE_CHANGED
| SCARD_STATE_UNAVAILABLE
);
118 void ReaderState::setATR(const void *atr
, size_t size
)
120 if (size
> sizeof(rgbAtr
))
121 Error::throwMe(SCARD_E_INVALID_ATR
);
122 memcpy(rgbAtr
, atr
, size
);
123 cbAtr
= (uint32_t)size
;
127 #if defined(DEBUGDUMP)
129 void ReaderState::dump()
131 Debug::dump("reader(%s) state=0x%x events=0x%x",
132 szReader
? szReader
: "(null)", dwCurrentState
, dwEventState
);
133 Debug::dumpData(" ATR", rgbAtr
, cbAtr
);
155 // (Re)establish PCSC context.
156 // Don't fail on SCARD_F_INTERNAL_ERROR (pcscd not running - urgh).
162 Error::check(::SCardEstablishContext(SCARD_SCOPE_SYSTEM
, NULL
, NULL
, &mContext
));
164 secinfo("pcsc", "context opened");
165 } catch (const Error
&err
) {
166 if (err
.error
== SCARD_F_INTERNAL_ERROR
)
168 secinfo("pcsc", "got internal error; assuming pcscd absent; context not ready");
175 void Session::close()
181 Error::check(SCardReleaseContext(mContext
));
182 secinfo("pcsc", "context closed");
183 } catch (const Error
&err
) {
184 if (err
.error
== SCARD_F_INTERNAL_ERROR
)
186 secinfo("pcsc", "got internal error; assuming pcscd absent; context not ready");
193 bool Session::check(long rc
)
195 switch ((unsigned long) rc
) {
196 case SCARD_S_SUCCESS
:
197 return true; // got reader(s), call succeeded
198 case SCARD_E_READER_UNAVAILABLE
:
199 return false; // no readers, but don't treat as error
202 return false; // placebo
207 void Session::listReaders(vector
<string
> &readers
, const char *groups
)
209 uint32_t size
= uint32_t(mReaderBuffer
.size());
212 int32_t rc
= ::SCardListReaders(mContext
, groups
, &mReaderBuffer
[0], &size
);
214 case SCARD_S_SUCCESS
:
215 if (size
<= mReaderBuffer
.size()) {
216 decode(readers
, mReaderBuffer
, size
);
219 case (int32_t) SCARD_E_INSUFFICIENT_BUFFER
:
220 mReaderBuffer
.resize(size
);
230 // Reader status check
232 void Session::statusChange(ReaderState
*readers
, unsigned int nReaders
, long timeout
)
234 check(::SCardGetStatusChange(mContext
, (uint32_t)timeout
, readers
, nReaders
));
242 : mConnectedState(kInitial
)
250 void Card::setIOType(unsigned long activeProtocol
)
252 switch (activeProtocol
)
254 case SCARD_PROTOCOL_T0
:
255 mIOType
= SCARD_PCI_T0
;
257 case SCARD_PROTOCOL_T1
:
258 mIOType
= SCARD_PCI_T1
;
261 mIOType
= SCARD_PCI_RAW
;
266 void Card::connect(Session
&session
, const char *reader
,
267 unsigned long share
, unsigned long protocols
)
269 uint32_t activeProtocol
;
270 Error::check(::SCardConnect(session
.mContext
,
271 reader
, (uint32_t)share
, (uint32_t)protocols
, &mHandle
, &activeProtocol
));
272 setIOType(activeProtocol
);
273 mConnectedState
= kConnected
;
276 void Card::reconnect(unsigned long share
, unsigned long protocols
, unsigned long initialization
)
278 assert(mConnectedState
!= kInitial
);
280 uint32_t activeProtocol
;
281 Error::check(::SCardReconnect(mHandle
, (uint32_t)share
, (uint32_t)protocols
,
282 (uint32_t)initialization
, &activeProtocol
));
283 setIOType(activeProtocol
);
284 mConnectedState
= kConnected
;
287 void Card::disconnect(unsigned long disposition
)
289 if (mConnectedState
== kConnected
)
291 if (mTransactionNestLevel
> 0)
293 secinfo("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel
);
294 mTransactionNestLevel
= 0;
297 checkReset(::SCardDisconnect(mHandle
, (uint32_t)disposition
));
299 mConnectedState
= kInitial
;
304 Card::checkReset(unsigned int rv
)
306 if (rv
== SCARD_W_RESET_CARD
)
308 secinfo("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
315 Card::didDisconnect()
317 mConnectedState
= kDisconnected
;
318 mTransactionNestLevel
= 0;
327 Card::transmit(const unsigned char *pbSendBuffer
, size_t cbSendLength
,
328 unsigned char *pbRecvBuffer
, size_t &pcbRecvLength
)
330 if (mConnectedState
== kDisconnected
)
332 secinfo("pcsc", "%p: transmit after disconnect, reconnecting", this);
336 IFDUMPING("pcsc", dump("->", pbSendBuffer
, cbSendLength
));
338 uint32_t tmpRecvLength
= (uint32_t)pcbRecvLength
;
339 checkReset(::SCardTransmit(mHandle
, mIOType
, pbSendBuffer
, (uint32_t)cbSendLength
,
340 NULL
, pbRecvBuffer
, &tmpRecvLength
));
341 pcbRecvLength
= tmpRecvLength
;
343 IFDUMPING("pcsc", dump("<-", pbRecvBuffer
, pcbRecvLength
));
348 // Only the first transaction started is sent to PCSC
349 if (mTransactionNestLevel
== 0)
351 if (mConnectedState
== kDisconnected
)
353 secinfo("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
357 checkReset(::SCardBeginTransaction(mHandle
));
359 mTransactionNestLevel
++;
360 secinfo("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel
);
363 void Card::end(unsigned long disposition
)
365 // Only the last transaction ended is sent to PCSC
366 secinfo("pcsc", "%p end transaction: %d", this, mTransactionNestLevel
);
367 if (disposition
== SCARD_RESET_CARD
)
369 if (mConnectedState
== kDisconnected
)
371 secinfo("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
375 checkReset(::SCardEndTransaction(mHandle
, (uint32_t)disposition
));
378 else if (mTransactionNestLevel
> 0)
380 --mTransactionNestLevel
;
381 if (mTransactionNestLevel
== 0)
383 if (mConnectedState
== kDisconnected
)
384 secinfo("pcsc", "%p: end transaction while disconnected ignored", this);
387 checkReset(::SCardEndTransaction(mHandle
, (uint32_t)disposition
));
396 end(/*SCARD_RESET_CARD*/);
399 #if defined(DEBUGDUMP)
402 Card::dump(const char *direction
, const unsigned char *buffer
, size_t length
)
404 Debug::dump("[%02lu]%s:", length
, direction
);
406 for (size_t ix
= 0; ix
< length
; ++ix
)
407 Debug::dump(" %02x", buffer
[ix
]);
415 void Transaction::commitAction()
417 mCarrier
.end(mDisposition
);
422 } // namespace Security
424 #endif // TARGET_OS_OSX