2 * Copyright (c) 2004 Apple Computer, 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, err
);
66 const char *Error::what() const throw ()
68 return pcsc_stringify_error(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
= known
;
108 void ReaderState::lastKnown(unsigned long s
)
110 // clear out CHANGED and UNAVAILABLE
111 dwCurrentState
= 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
);
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
)
227 return; // no readers, no foul
228 check(::SCardGetStatusChange(mContext
, timeout
, readers
, nReaders
));
236 : mConnectedState(kInitial
)
244 void Card::setIOType(unsigned long activeProtocol
)
246 switch (activeProtocol
)
248 case SCARD_PROTOCOL_T0
:
249 mIOType
= SCARD_PCI_T0
;
251 case SCARD_PROTOCOL_T1
:
252 mIOType
= SCARD_PCI_T1
;
255 mIOType
= SCARD_PCI_RAW
;
260 void Card::connect(Session
&session
, const char *reader
,
261 unsigned long share
, unsigned long protocols
)
263 uint32_t activeProtocol
;
264 Error::check(::SCardConnect(session
.mContext
,
265 reader
, share
, protocols
, &mHandle
, &activeProtocol
));
266 setIOType(activeProtocol
);
267 mConnectedState
= kConnected
;
270 void Card::reconnect(unsigned long share
, unsigned long protocols
, unsigned long initialization
)
272 assert(mConnectedState
!= kInitial
);
274 DWORD activeProtocol
;
275 Error::check(::SCardReconnect(mHandle
, share
, protocols
,
276 initialization
, &activeProtocol
));
277 setIOType(activeProtocol
);
278 mConnectedState
= kConnected
;
281 void Card::disconnect(unsigned long disposition
)
283 if (mConnectedState
== kConnected
)
285 if (mTransactionNestLevel
> 0)
287 secdebug("pcsc", "%p: disconnect, dropping: %d transactions", this, mTransactionNestLevel
);
288 mTransactionNestLevel
= 0;
291 checkReset(::SCardDisconnect(mHandle
, disposition
));
293 mConnectedState
= kInitial
;
298 Card::checkReset(unsigned int rv
)
300 if (rv
== SCARD_W_RESET_CARD
)
302 secdebug("pcsc", "%p: card reset during pcsc call, we're disconnected", this);
309 Card::didDisconnect()
311 mConnectedState
= kDisconnected
;
312 mTransactionNestLevel
= 0;
321 Card::transmit(const unsigned char *pbSendBuffer
, size_t cbSendLength
,
322 unsigned char *pbRecvBuffer
, size_t &pcbRecvLength
)
324 if (mConnectedState
== kDisconnected
)
326 secdebug("pcsc", "%p: transmit after disconnect, reconnecting", this);
330 IFDUMPING("pcsc", dump("->", pbSendBuffer
, cbSendLength
));
332 uint32_t tmpRecvLength
= pcbRecvLength
;
333 checkReset(::SCardTransmit(mHandle
, mIOType
, pbSendBuffer
, cbSendLength
,
334 NULL
, pbRecvBuffer
, &tmpRecvLength
));
335 pcbRecvLength
= tmpRecvLength
;
337 IFDUMPING("pcsc", dump("<-", pbRecvBuffer
, pcbRecvLength
));
342 // Only the first transaction started is sent to PCSC
343 if (mTransactionNestLevel
== 0)
345 if (mConnectedState
== kDisconnected
)
347 secdebug("pcsc", "%p: begin transaction after disconnect, reconnecting", this);
351 checkReset(::SCardBeginTransaction(mHandle
));
353 mTransactionNestLevel
++;
354 secdebug("pcsc", "%p begin transaction: %d", this, mTransactionNestLevel
);
357 void Card::end(unsigned long disposition
)
359 // Only the last transaction ended is sent to PCSC
360 secdebug("pcsc", "%p end transaction: %d", this, mTransactionNestLevel
);
361 if (disposition
== SCARD_RESET_CARD
)
363 if (mConnectedState
== kDisconnected
)
365 secdebug("pcsc", "%p: end transaction after disconnect, reconnecting to reset card", this);
369 checkReset(::SCardEndTransaction(mHandle
, disposition
));
372 else if (mTransactionNestLevel
> 0)
374 --mTransactionNestLevel
;
375 if (mTransactionNestLevel
== 0)
377 if (mConnectedState
== kDisconnected
)
378 secdebug("pcsc", "%p: end transaction while disconnected ignored", this);
381 checkReset(::SCardEndTransaction(mHandle
, disposition
));
390 end(/*SCARD_RESET_CARD*/);
393 #if defined(DEBUGDUMP)
396 Card::dump(const char *direction
, const unsigned char *buffer
, size_t length
)
398 Debug::dump("[%02lu]%s:", length
, direction
);
400 for (size_t ix
= 0; ix
< length
; ++ix
)
401 Debug::dump(" %02x", buffer
[ix
]);
409 void Transaction::commitAction()
411 mCarrier
.end(mDisposition
);
416 } // namespace Security