smartcard 0.3.1-x86-mswin32-60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/BUILD ADDED
@@ -0,0 +1,65 @@
1
+ = Builds are hard
2
+ +smartcard+ needs to talk to hardware (card readers), so it's bound to use a Ruby extension.
3
+ This means C, and platform-specific building nightmares. Read below for details.
4
+
5
+ = The build system
6
+ +smartcard+ uses {echoe}[http://blog.evanweaver.com/files/doc/fauna/echoe/] for builds. This
7
+ automates most of the building and packaging work, and can even push the gem to rubyforge,
8
+ with a bit of luck. Most used commands:
9
+ rake manifest # builds the manifest (do this after checking out)
10
+ rake package # builds the gem for your platform
11
+ rake mswin package # cross-builds the gem for Windows
12
+ rake test # builds the gem and runs the tests
13
+ rake docs # runs Rdoc to produce the docs
14
+
15
+ On the other hand, you have to <tt>gem install echoe</tt> to be able to build anything.
16
+
17
+ = Platform-specific information
18
+
19
+ == OSX
20
+
21
+ You need to install the Developer Tools to get gcc.
22
+
23
+ Leopard includes a working PC/SC provider, as well as a good driver for CCID readers.
24
+ Tiger's PC/SC implementation is broken and incomplete, so the Ruby extension code in +smartcard+
25
+ has a few hacks to work around that (look for the <tt>RB_SMARTCARD_OSX_TIGER_HACK</tt> define.
26
+ The following commands are broken / don't work:
27
+ * Smartcard::PCSC::Context#is_valid (always returs +true+)
28
+ * Smartcard::PCSC::Card#get_attribute (throws exception because it's not implemented)
29
+ * Smartcard::PCSC::Card#set_attribute (throws exception because it's not implemented)
30
+ * Smartcard::PCSC::Card#control (Tiger's API is broken, so the call will probably not work)
31
+
32
+ The developer team doesn't support or test against ports of +gcc+ or +pcsclite+,
33
+ but we success notifications are welcome.
34
+
35
+ == Windows
36
+
37
+ A lot of effort has been spent to make Windows builds as easy as possible.
38
+ +smartcard+ is currently built using a full edition of
39
+ {Visual Studio 2005}[http://msdn.microsoft.com/vstudio/], but all sources
40
+ indicate that {Visual C++ Express 2005}[http://www.microsoft.com/express/download/] works,
41
+ as long as you also install a Windows SDK (you're on your own for that). Visual Studio 2008
42
+ might work, but it hasn't been tested yet.
43
+
44
+ A summary of the hacks that have been done to get Windows builds working:
45
+ * removing the extension files from the gemspec at the "right time" so that +echoe+ compiles the extension, and the gem doesn't require re-compilation (see the +Rakefile+)
46
+ * adding the compiled extension to the file list in the gemspec
47
+ * adding code to <tt>extconf.rb</tt> to patch the +Makefile+ so a manifest is embedded in the extension dll (and ruby doesn't crash if it uses a different version of the C runtime)
48
+
49
+ == Linux
50
+
51
+ +smartcard+ is developed (and tested) against the
52
+ {MUSCLE project}[http://www.linuxnet.com/software.html]. If you go this route, you need to
53
+ get at least the {pcsclite library}[http://pcsclite.alioth.debian.org/] and a driver.
54
+ +smartcard+ developers use the {CCID driver}[http://pcsclite.alioth.debian.org/], which
55
+ works on most (new) readers.
56
+
57
+ === Ubuntu
58
+
59
+ Installing the following packages (and their dependencies) gets you going on Ubuntu (tested on 7.10):
60
+ * buildessentials
61
+ * libccid
62
+ * libpcsclite
63
+ * libpcsclite-dev
64
+ * pcscd
65
+ * pcsc-tools
data/CHANGELOG ADDED
@@ -0,0 +1,29 @@
1
+ v0.3.1. Fixed documentation for the new PcscException class.
2
+
3
+ v0.3.0. Added PcscException class used to wrap PC/SC exceptions.
4
+
5
+ v0.2.3. Fixed minor bug in error string for PCSC::Card.transmit
6
+
7
+ v0.2.2. Fixed APDU exchange bugs on Windows
8
+ Restructured PCSC::IoRequest to allow PCI_ consts
9
+ Added PCI_ consts
10
+ Changed test suite to reflect proper way to do transmits
11
+
12
+ v0.2.1. Added OSX Tiger support
13
+ Many workarounds for Tiger's buggy/incomplete PC/SC.
14
+ Small bugfixes for PC/SC status constants.
15
+
16
+ v0.2.0. Added automatic builds
17
+ Rakefile for auto builds using echoe
18
+ extconf.rb: hack to fix Windows makefiles
19
+
20
+ v0.1.2. Added Windows compatibility
21
+ *.c: restructured code so VC2005 likes it
22
+ (variable declarations before function body)
23
+ docs: license, readme, and what the gem is
24
+
25
+ v0.1.1. Added support for Ubuntu 7.10
26
+ ext/smartcard_pcsc/extconf.rb: better header
27
+ detection
28
+
29
+ v0.1.0. Initial release for OSX Leopard
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2007 Massachusetts Institute of Technology
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,23 @@
1
+ BUILD
2
+ CHANGELOG
3
+ ext/smartcard_pcsc/extconf.rb
4
+ ext/smartcard_pcsc/pcsc.h
5
+ ext/smartcard_pcsc/pcsc_card.c
6
+ ext/smartcard_pcsc/pcsc_constants.c
7
+ ext/smartcard_pcsc/pcsc_context.c
8
+ ext/smartcard_pcsc/pcsc_exception.c
9
+ ext/smartcard_pcsc/pcsc_io_request.c
10
+ ext/smartcard_pcsc/pcsc_main.c
11
+ ext/smartcard_pcsc/pcsc_multi_strings.c
12
+ ext/smartcard_pcsc/pcsc_namespace.c
13
+ ext/smartcard_pcsc/pcsc_reader_states.c
14
+ ext/smartcard_pcsc/pcsc_surrogate_reader.h
15
+ ext/smartcard_pcsc/pcsc_surrogate_wintypes.h
16
+ lib/smartcard/pcsc_exception.rb
17
+ lib/smartcard.rb
18
+ LICENSE
19
+ Manifest
20
+ README
21
+ test/test_containers.rb
22
+ test/test_smoke.rb
23
+ tests/ts_pcsc_ext.rb
data/README ADDED
@@ -0,0 +1,32 @@
1
+ +smartcard+ aims to become the standard support library for smart-card development on ruby.
2
+ Right now, the project offers a PC/SC binding that is working on ruby 1.8, under
3
+ Linux, OSX, and Windows. Future plans include a high level abstraction for the PC/SC binding
4
+ (plus any other bindings people need), and an interface for Java cards.
5
+
6
+ = Installation
7
+
8
+ gem install smartcard
9
+ Windows:: Select the <tt>win32</tt> gem.
10
+ OSX:: Install the Developer Tools to get +gcc+. Requires Tiger or Leopard.
11
+ UNIX:: You need a PC/SC provider. See the {BUILD file}[link://files/BUILD.html] for details. The file also contains package lists for popular distributions.
12
+
13
+ = Documentation
14
+
15
+ The documentation you see was generated with RDoc. If you install the +smartcard+ gem, you should be
16
+ able to use ri to see the documentation. If you're using the SVN version, you can <tt>rake doc</tt> to build
17
+ the HTML documentation yourself.
18
+
19
+ = License
20
+
21
+ +smartcard+ is released under the MIT license. This means you're free to do whatever you want
22
+ with it. However, it'd be nice to let the developers know if you plan to include this in a
23
+ distribution (bragging rights are always good). The {LICENSE file}[link://files/LICENSE.html]
24
+ has the license in legalese.
25
+
26
+ = Acknowledgements
27
+
28
+ +smartcard+ is developed by Victor Costan while working as a Research Assistant for MIT, under
29
+ {Prof. Srini Devadas}[http://people.csail.mit.edu/devadas/], in the
30
+ {Trusted Computing group}[http://projects.csail.mit.edu/tc/].
31
+ The work is funded by a grant from {Quanta Computer Inc}[http://www.quanta.com.tw], under the
32
+ {T-Party Project}[http://projects.csail.mit.edu/tparty/].
@@ -0,0 +1,70 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS ||= ''
4
+ $LDFLAGS ||= ''
5
+
6
+ pcsc_defines = []
7
+
8
+ if RUBY_PLATFORM =~ /darwin/
9
+ $LDFLAGS += ' -framework PCSC'
10
+ darwin_version = `uname -r`
11
+ if darwin_version =~ /^8./
12
+ pcsc_defines.push 'RB_SMARTCARD_OSX_TIGER_HACK'
13
+ end
14
+ elsif RUBY_PLATFORM =~ /win/
15
+
16
+ have_library('winscard')
17
+ pcsc_defines << 'PCSC_SURROGATE_SCARD_IS_VALID_CONTEXT' unless have_library('winscard', 'SCardIsValidContext')
18
+ else
19
+ # pcsclite is retarded and uses stuff like '#include <wintypes.h>'
20
+ $CFLAGS += ' -I /usr/include/PCSC -I /usr/local/include/PCSC'
21
+ have_library('pcsclite')
22
+ end
23
+
24
+ pcsc_headers = []
25
+ ['wintypes.h', 'reader.h', 'winscard.h', 'pcsclite.h'].each do |header|
26
+ ['', 'PCSC/', './pcsc_surrogate_'].each do |path_prefix|
27
+ if have_header(path_prefix + header)
28
+ pcsc_headers.push((path_prefix[0,1] == '.') ? "\"#{path_prefix + header}\"" : "<#{path_prefix + header}>")
29
+ break
30
+ end
31
+ end
32
+ end
33
+
34
+ File.open('pcsc_autogen.h', 'w') do |f|
35
+ pcsc_defines.each { |d| f.write "\#define #{d}\n" }
36
+ pcsc_headers.each { |h| f.write "\#include #{h}\n" }
37
+ end
38
+
39
+ create_makefile('smartcard/pcsc')
40
+
41
+ def win32_hack(mf_name)
42
+ # get the version of MSVC
43
+ Kernel.system `cl 2> msvc_version.txt`
44
+ msvc_logo = File.open('msvc_version.txt') { |f| f.read }
45
+ print msvc_logo
46
+ File.delete 'msvc_version.txt'
47
+ if msvc_logo =~ /Optimizing Compiler Version (\d+)/
48
+ msvc_ver = $1.to_i
49
+ # for MSVC 6.0, no manifest BS is needed -- straight-up compilation is good
50
+ return if msvc_ver == 12
51
+ end
52
+
53
+ # evil, evil, evil -- hack the makefile to embed the manifest in the extension dll
54
+ make_contents = File.open(mf_name, 'r') { |f| f.read }
55
+ make_rules = make_contents.split(/(\n|\r)(\n|\r)+/)
56
+ new_make_rules = make_rules.map do |rule|
57
+ if rule =~ /^\$\(DLLIB\)\:/
58
+ rule + "\n\tmt.exe -manifest $(@).manifest -outputresource:$(@);2"
59
+ else
60
+ rule
61
+ end
62
+ end
63
+ File.open(mf_name, 'w') { |f| f.write new_make_rules.join("\n\n")}
64
+ end
65
+
66
+ case RUBY_PLATFORM
67
+ when /darwin/
68
+ when /win/
69
+ win32_hack 'Makefile'
70
+ end
@@ -0,0 +1,52 @@
1
+ /* Ruby extension API. */
2
+ #include <ruby.h>
3
+ /* Generated by 'extconf.rb' to point to the PC/SC header. */
4
+ #include "pcsc_autogen.h"
5
+
6
+ /* Entrypoint into the Ruby extension. */
7
+ void Init_pcsc();
8
+
9
+ /* Namespace structure. */
10
+ extern VALUE mSmartcard; /* Smartcard module / namespace */
11
+ extern VALUE mPcsc; /* Smartcard::PCSC module / namespace */
12
+
13
+ /* Class Smartcard::PCSC::ReaderStates */
14
+ extern VALUE cPcscReaderStates;
15
+ void Init_PCSC_ReaderStates();
16
+ int _PCSC_ReaderStates_lowlevel_get(VALUE rbReaderStates, SCARD_READERSTATE **reader_states, size_t *reader_states_count);
17
+
18
+ /* Class Smartcard::PCSC::IoRequest */
19
+ extern VALUE cPcscIoRequest;
20
+ void Init_PCSC_IoRequest();
21
+ int _PCSC_IoRequest_lowlevel_get(VALUE rbIoRequest, SCARD_IO_REQUEST **io_request);
22
+ VALUE _PCSC_IoRequest_lowlevel_new(const SCARD_IO_REQUEST *io_request);
23
+
24
+ /* Class Smartcard::PCSC::Context */
25
+ extern VALUE cPcscContext;
26
+ void Init_PCSC_Context();
27
+ int _PCSC_Context_lowlevel_get(VALUE rbContext, SCARDCONTEXT *pcsc_context);
28
+
29
+ /* Class Smartcard::PCSC::Card */
30
+ extern VALUE cPcscCard;
31
+ void Init_PCSC_Card();
32
+ int _PCSC_Card_lowlevel_get(VALUE rbCard, SCARDHANDLE *card_handle);
33
+
34
+ /* Class Smartcard::PCSC::Exception */
35
+ extern VALUE ePcscException;
36
+ void Init_PCSC_Exception();
37
+ void _PCSC_Exception_raise(DWORD pcsc_error, char *pcsc_function);
38
+
39
+ /* Constants in Smartcard::PCSC */
40
+ void Init_PCSC_Consts();
41
+
42
+ /* Multi-string (win32 abomination) tools. */
43
+ VALUE PCSC_Internal_multistring_to_ruby_array(char *mstr, size_t mstr_len);
44
+ int PCSC_Internal_ruby_strings_to_multistring(VALUE rbStrings, char **strings);
45
+
46
+ /* Messing up with constants that aren't uniformly defined. */
47
+ #if !defined(MAX_ATR_SIZE)
48
+ #define MAX_ATR_SIZE 32
49
+ #endif
50
+ #if !defined(SCARD_PROTOCOL_ANY)
51
+ #define SCARD_PROTOCOL_ANY (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
52
+ #endif
@@ -0,0 +1,492 @@
1
+ #include "pcsc.h"
2
+
3
+ VALUE cPcscCard;
4
+
5
+ /* Wraps a SCARDHANDLE, tracking whether it was released or not, together with the last error that occured on it. */
6
+ struct SCardHandleEx {
7
+ SCARDHANDLE card_handle;
8
+ DWORD pcsc_error;
9
+ int released;
10
+ };
11
+
12
+ /* Custom free for Smartcard::PCSC::Card. Releases the card handle via a disconnect if that was not already done. */
13
+ static void PCSC_Card_free(struct SCardHandleEx *_card) {
14
+ if(_card != NULL) {
15
+ if(!_card->released)
16
+ SCardDisconnect(_card->card_handle, SCARD_LEAVE_CARD);
17
+ xfree(_card);
18
+ }
19
+ }
20
+
21
+ /* Custom allocation for Smartcard::PCSC::Card. Wraps a SCardHandleEx. */
22
+ static VALUE PCSC_Card_alloc(VALUE klass) {
23
+ struct SCardHandleEx *card;
24
+
25
+ VALUE rbCard = Data_Make_Struct(klass, struct SCardHandleEx, NULL, PCSC_Card_free, card);
26
+ card->pcsc_error = SCARD_S_SUCCESS;
27
+ card->released = 1;
28
+ return rbCard;
29
+ }
30
+
31
+ /* :Document-method: new
32
+ * call-seq:
33
+ * new(context, reader_name, share_mode, preferred_protocols) --> card
34
+ *
35
+ * Establishes a connection to the card in the reader whose friendly name is +reader_name+.
36
+ * The first connection will power up and perform a reset on the card.
37
+ * Wraps _SCardConnect_ in PC/SC.
38
+ *
39
+ * +context+:: the Smartcard::PCSC::Context to use to connect to the PC/SC resource manager
40
+ * +reader_name+:: friendly name of the reader to connect to; get using Smartcard::PCSC::Context#list_readers
41
+ * +share_mode+:: whether a shared or exclusive lock will be requested on the reader; use one of the Smartcard::PCSC::SHARE_ constants
42
+ * +preferred_protocols+:: desired protocol; use one of the Smartcard::PCSC::PROTOCOL_ constants
43
+ */
44
+ static VALUE PCSC_Card_initialize(VALUE self, VALUE rbContext, VALUE rbReaderName, VALUE rbShareMode, VALUE rbPreferredProtocols) {
45
+ struct SCardHandleEx *card;
46
+ SCARDCONTEXT context;
47
+ VALUE rbFinalReaderName;
48
+ DWORD share_mode, preferred_protocols, active_protocol;
49
+
50
+ Data_Get_Struct(self, struct SCardHandleEx, card);
51
+
52
+ if(_PCSC_Context_lowlevel_get(rbContext, &context) == 0) {
53
+ rb_raise(rb_eArgError, "first argument is not a Context instance");
54
+ return self;
55
+ }
56
+
57
+ rbFinalReaderName = rb_check_string_type(rbReaderName);
58
+ if(NIL_P(rbFinalReaderName)) {
59
+ rb_raise(rb_eArgError, "second argument (should be reader name) does not convert to a String");
60
+ return self;
61
+ }
62
+
63
+ share_mode = NUM2UINT(rbShareMode);
64
+ preferred_protocols = NUM2UINT(rbPreferredProtocols);
65
+
66
+ card->pcsc_error = SCardConnect(context, RSTRING(rbFinalReaderName)->ptr, share_mode, preferred_protocols, &card->card_handle, &active_protocol);
67
+ if(card->pcsc_error != SCARD_S_SUCCESS)
68
+ _PCSC_Exception_raise(card->pcsc_error, "SCardConnect");
69
+ else
70
+ card->released = 0;
71
+ return self;
72
+ }
73
+
74
+ /* :Document-method: reconnect
75
+ * call-seq:
76
+ * card.reconnect(share_mode, preferred_protocols, initialization) --> self
77
+ *
78
+ * Reestablishes a connection to a reader that was previously connected to using Card#new.
79
+ * Wraps _SCardReconnect_ in PC/SC.
80
+ *
81
+ * +share_mode+:: whether a shared or exclusive lock will be requested on the reader; use one of the Smartcard::PCSC::SHARE_ constants
82
+ * +preferred_protocols+:: desired protocol; use one of the Smartcard::PCSC::PROTOCOL_ constants
83
+ * +initialization+:: action to be taken on the card inside the reader; use one of the Smartcard::PCSC::INITIALIZE_ constants
84
+ */
85
+ static VALUE PCSC_Card_reconnect(VALUE self, VALUE rbShareMode, VALUE rbPreferredProtocols, VALUE rbInitialization) {
86
+ struct SCardHandleEx *card;
87
+ DWORD share_mode, preferred_protocols, initialization, active_protocol;
88
+
89
+ Data_Get_Struct(self, struct SCardHandleEx, card);
90
+ if(card == NULL) return self;
91
+
92
+ share_mode = NUM2UINT(rbShareMode);
93
+ preferred_protocols = NUM2UINT(rbPreferredProtocols);
94
+ initialization = NUM2UINT(rbInitialization);
95
+
96
+ card->pcsc_error = SCardReconnect(card->card_handle, share_mode, preferred_protocols, initialization, &active_protocol);
97
+ if(card->pcsc_error != SCARD_S_SUCCESS)
98
+ _PCSC_Exception_raise(card->pcsc_error, "SCardReconnect");
99
+
100
+ return self;
101
+ }
102
+
103
+ /* :Document-method: disconnect
104
+ * call-seq:
105
+ * context.disconnect(disposition) --> self
106
+ *
107
+ * Terminates the connection made using Card#new. The Card object is invalid afterwards.
108
+ * Wraps _SCardDisconnect_ in PC/SC.
109
+ *
110
+ * +disposition+:: action to be taken on the card inside the reader; use one of the Smartcard::PCSC::DISPOSITION_ constants
111
+ */
112
+ static VALUE PCSC_Card_disconnect(VALUE self, VALUE rbDisposition) {
113
+ struct SCardHandleEx *card;
114
+ DWORD disposition;
115
+
116
+ Data_Get_Struct(self, struct SCardHandleEx, card);
117
+ if(card == NULL) return self;
118
+
119
+ disposition = NUM2UINT(rbDisposition);
120
+ if(!card->released) {
121
+ card->pcsc_error = SCardDisconnect(card->card_handle, disposition);
122
+ card->released = 1;
123
+ if(card->pcsc_error != SCARD_S_SUCCESS)
124
+ _PCSC_Exception_raise(card->pcsc_error, "SCardDisconnect");
125
+ }
126
+ return self;
127
+ }
128
+
129
+ /* :Document-method: begin_transaction
130
+ * call-seq:
131
+ * card.begin_transaction() --> self
132
+ *
133
+ * Establishes a temporary exclusive access mode for doing a series of commands or transaction.
134
+ * Wraps _SCardBeginTransaction_ in PC/SC.
135
+ */
136
+ static VALUE PCSC_Card_begin_transaction(VALUE self) {
137
+ struct SCardHandleEx *card;
138
+
139
+ Data_Get_Struct(self, struct SCardHandleEx, card);
140
+ if(card == NULL) return self;
141
+
142
+ card->pcsc_error = SCardBeginTransaction(card->card_handle);
143
+ if(card->pcsc_error != SCARD_S_SUCCESS)
144
+ _PCSC_Exception_raise(card->pcsc_error, "SCardBeginTransaction");
145
+ return self;
146
+ }
147
+
148
+ /* :Document-method: end_transaction
149
+ * call-seq:
150
+ * context.end_transaction(disposition) --> attribute_value
151
+ *
152
+ * Ends a previously begun transaction. The calling application must be the owner of the previously begun transaction or an error will occur.
153
+ * Wraps _SCardEndTransaction_ in PC/SC.
154
+ *
155
+ * +disposition+:: action to be taken on the card inside the reader; use one of the Smartcard::PCSC::DISPOSITION_ constants
156
+ */
157
+ static VALUE PCSC_Card_end_transaction(VALUE self, VALUE rbDisposition) {
158
+ struct SCardHandleEx *card;
159
+ DWORD disposition;
160
+
161
+ Data_Get_Struct(self, struct SCardHandleEx, card);
162
+ if(card == NULL) return self;
163
+
164
+ disposition = NUM2UINT(rbDisposition);
165
+ card->pcsc_error = SCardEndTransaction(card->card_handle, disposition);
166
+ if(card->pcsc_error != SCARD_S_SUCCESS)
167
+ _PCSC_Exception_raise(card->pcsc_error, "SCardEndTransaction");
168
+ return self;
169
+ }
170
+
171
+ /* :Document-method: get_attribute
172
+ * call-seq:
173
+ * card.get_attribute(attribute_id) --> attribute_value
174
+ *
175
+ * Reads the value of an attribute from the interface driver.
176
+ * Remember that the IFD may not implement some of the attributes specified in Smartcard::PCSC, and it may implement some attributes that
177
+ * are not included in Smartcard::PCSC.
178
+ * Wraps _SCardGetAttrib_ in PC/SC.
179
+ *
180
+ * The returned value has the bytes in the attribute, wrapped in a string. (don't complain, it's a low-level API)
181
+ *
182
+ * +attribute_id+:: identifies the attribute to be read; use one of the Smartcard::PCSC::ATTR_ constants
183
+ */
184
+ static VALUE PCSC_Card_get_attribute(VALUE self, VALUE rbAttributeId) {
185
+ struct SCardHandleEx *card;
186
+ char *attribute_buffer;
187
+ DWORD attribute_id, attribute_length;
188
+ VALUE rbAttribute;
189
+
190
+ Data_Get_Struct(self, struct SCardHandleEx, card);
191
+ if(card == NULL) return Qnil;
192
+
193
+ attribute_id = NUM2UINT(rbAttributeId);
194
+ attribute_length;
195
+ #if defined(RB_SMARTCARD_OSX_TIGER_HACK)
196
+ card->pcsc_error = SCARD_F_INTERNAL_ERROR;
197
+ rb_raise(rb_eRuntimeError, "SCardSetAttrib: not implemented in OSX Tiger");
198
+ #else
199
+ card->pcsc_error = SCardGetAttrib(card->card_handle, attribute_id, NULL, &attribute_length);
200
+ if(card->pcsc_error == SCARD_S_SUCCESS) {
201
+ attribute_buffer = ALLOC_N(char, attribute_length);
202
+ if(attribute_buffer != NULL) {
203
+ card->pcsc_error = SCardGetAttrib(card->card_handle, attribute_id, (LPSTR)attribute_buffer, &attribute_length);
204
+ if(card->pcsc_error == SCARD_S_SUCCESS) {
205
+ rbAttribute = rb_str_new(attribute_buffer, attribute_length);
206
+ xfree(attribute_buffer);
207
+ return rbAttribute;
208
+ }
209
+ }
210
+ }
211
+ if(card->pcsc_error != SCARD_S_SUCCESS)
212
+ _PCSC_Exception_raise(card->pcsc_error, "SCardGetAttrib");
213
+ #endif
214
+ return Qnil;
215
+ }
216
+
217
+ /* :Document-method: set_attribute
218
+ * call-seq:
219
+ * context.set_attribute(attribute_id, attribute_value) --> self
220
+ *
221
+ * Sets the value of an attribute in the interface driver.
222
+ * Remember that the IFD may not implement some of the attributes specified in Smartcard::PCSC, and it may implement some attributes that
223
+ * are not included in Smartcard::PCSC.
224
+ * Wraps _SCardSetAttrib_ in PC/SC.
225
+ *
226
+ * +attribute_id+:: identifies the attribute to be set; use one of the Smartcard::PCSC::ATTR_ constants
227
+ * +attribute_value+:: the value to be assigned to the attribute; wrap the bytes in a string-like object (low-level API, remember?)
228
+ */
229
+ static VALUE PCSC_Card_set_attribute(VALUE self, VALUE rbAttributeId, VALUE rbAttributeValue) {
230
+ struct SCardHandleEx *card;
231
+ DWORD attribute_id;
232
+ VALUE rbFinalAttributeValue;
233
+
234
+ Data_Get_Struct(self, struct SCardHandleEx, card);
235
+ if(card == NULL) return self;
236
+
237
+ attribute_id = NUM2UINT(rbAttributeId);
238
+
239
+ rbFinalAttributeValue = rb_check_string_type(rbAttributeValue);
240
+ if(NIL_P(rbFinalAttributeValue)) {
241
+ rb_raise(rb_eArgError, "second argument (attribute buffer) does not convert to a String");
242
+ return self;
243
+ }
244
+
245
+ #if defined(RB_SMARTCARD_OSX_TIGER_HACK)
246
+ card->pcsc_error = SCARD_F_INTERNAL_ERROR;
247
+ rb_raise(rb_eRuntimeError, "SCardSetAttrib: not implemented in OSX Tiger");
248
+ #else
249
+ card->pcsc_error = SCardSetAttrib(card->card_handle, attribute_id, (LPSTR)RSTRING(rbFinalAttributeValue)->ptr, RSTRING(rbFinalAttributeValue)->len);
250
+ if(card->pcsc_error != SCARD_S_SUCCESS)
251
+ _PCSC_Exception_raise(card->pcsc_error, "SCardSetAttrib");
252
+ #endif
253
+ return self;
254
+ }
255
+
256
+ /* :Document-method: transmit
257
+ * call-seq:
258
+ * card.transmit(send_data, send_io_request, recv_io_request) --> recv_data
259
+ *
260
+ * Sends an APDU to the smart card, and returns the card's response to the APDU.
261
+ * Wraps _SCardTransmit_ in PC/SC.
262
+ *
263
+ * The bytes in the card's response are returned wrapped in a string. (don't complain, it's a low-level API)
264
+ *
265
+ * +send_data+:: the APDU to be send to the card; wrap the bytes in a string-like object (low-level API, remember?)
266
+ * +send_io_request+:: Smartcard::PCSC::IoRequest instance indicating the send protocol; you should use one of the Smartcard::PCSC::IOREQUEST_ constants
267
+ * +recv_io_request+:: Smartcard::PCSC::IoRequest instance receving information about the recv protocol; you can use the result of Smartcard::PCSC::IoRequest#new
268
+ */
269
+ static VALUE PCSC_Card_transmit(VALUE self, VALUE rbSendData, VALUE rbSendIoRequest, VALUE rbRecvIoRequest) {
270
+ struct SCardHandleEx *card;
271
+ VALUE rbFinalSendData, rbRecvData;
272
+ SCARD_IO_REQUEST *send_io_request, *recv_io_request;
273
+ char *recv_buffer;
274
+ DWORD recv_length;
275
+
276
+ Data_Get_Struct(self, struct SCardHandleEx, card);
277
+ if(card == NULL) return Qnil;
278
+
279
+ rbFinalSendData = rb_check_string_type(rbSendData);
280
+ if(NIL_P(rbFinalSendData)) {
281
+ rb_raise(rb_eArgError, "first argument (send buffer) does not convert to a String");
282
+ return Qnil;
283
+ }
284
+
285
+ if(_PCSC_IoRequest_lowlevel_get(rbSendIoRequest, &send_io_request) == 0) {
286
+ rb_raise(rb_eArgError, "second argument (send io request) is not an IoRequest instance");
287
+ return Qnil;
288
+ }
289
+ if(_PCSC_IoRequest_lowlevel_get(rbRecvIoRequest, &recv_io_request) == 0) {
290
+ rb_raise(rb_eArgError, "third argument (recv io request) is not an IoRequest instance");
291
+ return Qnil;
292
+ }
293
+
294
+ #if defined(PCSCLITE_MAX_MESSAGE_SIZE)
295
+ recv_length = PCSCLITE_MAX_MESSAGE_SIZE;
296
+ #elif defined(MAX_BUFFER_SIZE_EXTENDED)
297
+ recv_length = MAX_BUFFER_SIZE_EXTENDED;
298
+ #else
299
+ recv_length = 65536;
300
+ #endif
301
+ recv_buffer = ALLOC_N(char, recv_length);
302
+ if(recv_buffer == NULL) return Qnil;
303
+
304
+ card->pcsc_error = SCardTransmit(card->card_handle, send_io_request,
305
+ (LPSTR)RSTRING(rbFinalSendData)->ptr, RSTRING(rbFinalSendData)->len,
306
+ recv_io_request, (LPSTR)recv_buffer, &recv_length);
307
+ if(card->pcsc_error != SCARD_S_SUCCESS) {
308
+ xfree(recv_buffer);
309
+ _PCSC_Exception_raise(card->pcsc_error, "SCardTransmit");
310
+ return Qnil;
311
+ }
312
+
313
+ rbRecvData = rb_str_new(recv_buffer, recv_length);
314
+ xfree(recv_buffer);
315
+ return rbRecvData;
316
+ }
317
+
318
+ /* :Document-method: control
319
+ * call-seq:
320
+ * card.control(control_code, send_data, max_recv_bytes) --> recv_data
321
+ *
322
+ * Sends a command directly to the interface driver to be processed by the reader.
323
+ * Useful for creating client side reader drivers for functions like PIN pads, biometrics, or other smart card reader
324
+ * extensions that are not normally handled by PC/SC.
325
+ * Wraps _SCardControl_ in PC/SC.
326
+ *
327
+ * The bytes in the response are returned wrapped in a string. (don't complain, it's a low-level API)
328
+ *
329
+ * +control_code+:: control code for the operation; it's an integer, and it's IFD-specific
330
+ * +send_data+:: the data bytes to be send to the driver; wrap the bytes in a string-like object (low-level API, remember?)
331
+ * +max_recv_bytes+:: the maximum number of bytes that can be received
332
+ *
333
+ * In general, I tried to avoid having you specify receive buffer sizes. This is the only case where that is impossible to achieve,
334
+ * because there is no well-known maximum buffer size, and the _SCardControl_ call is not guaranteed to be idempotent, so it's not OK to
335
+ * re-issue it until the buffer size works out.
336
+ */
337
+ static VALUE PCSC_Card_control(VALUE self, VALUE rbControlCode, VALUE rbSendData, VALUE rbMaxRecvBytes) {
338
+ struct SCardHandleEx *card;
339
+ VALUE rbFinalSendData, rbRecvData;
340
+ char *recv_buffer;
341
+ DWORD control_code, recv_length;
342
+
343
+ Data_Get_Struct(self, struct SCardHandleEx, card);
344
+ if(card == NULL) return Qnil;
345
+
346
+ rbFinalSendData = rb_check_string_type(rbSendData);
347
+ if(NIL_P(rbFinalSendData)) {
348
+ rb_raise(rb_eArgError, "second argument (send buffer) does not convert to a String");
349
+ return Qnil;
350
+ }
351
+
352
+ control_code = NUM2UINT(rbControlCode);
353
+ recv_length = NUM2UINT(rbMaxRecvBytes);
354
+ recv_buffer = ALLOC_N(char, recv_length);
355
+ if(recv_buffer == NULL) return Qnil;
356
+
357
+ #if defined(RB_SMARTCARD_OSX_TIGER_HACK)
358
+ /* TODO: this will compile and run, but it won't do anything useful */
359
+ card->pcsc_error = SCardControl(card->card_handle,
360
+ (LPSTR)RSTRING(rbFinalSendData)->ptr, RSTRING(rbFinalSendData)->len,
361
+ recv_buffer, &recv_length);
362
+ #else
363
+ card->pcsc_error = SCardControl(card->card_handle, control_code,
364
+ (LPSTR)RSTRING(rbFinalSendData)->ptr, RSTRING(rbFinalSendData)->len,
365
+ recv_buffer, recv_length, &recv_length);
366
+ #endif
367
+ if(card->pcsc_error != SCARD_S_SUCCESS) {
368
+ xfree(recv_buffer);
369
+ _PCSC_Exception_raise(card->pcsc_error, "SCardControl");
370
+ return Qnil;
371
+ }
372
+
373
+ rbRecvData = rb_str_new(recv_buffer, recv_length);
374
+ xfree(recv_buffer);
375
+ return rbRecvData;
376
+ }
377
+
378
+ static VALUE _rbStateKey, _rbProtocolKey, _rbAtrKey, _rbReaderNamesKey;
379
+
380
+ /* :Document-method: status
381
+ * call-seq:
382
+ * card.status() --> card_status
383
+ *
384
+ * Retrieves the current status of the smartcard, and packages it up in a nice hash for you.
385
+ * Wraps _SCardStatus_ in PC/SC.
386
+ *
387
+ * The response hash contains the following keys:
388
+ * <tt>:state</tt> :: reader/card status; bitfield, with bits defined as Smartcard::PCSC::STATUS_ constants
389
+ * <tt>:protocol</tt> :: the protocol established with the card; check against Smartcard::PCSC::PROTOCOL_ constants
390
+ * <tt>:atr</tt> :: the card's ATR bytes, wrapped in a string
391
+ * <tt>:reader_names</tt> :: array of strings containing all the names of the reader containing the smartcard
392
+ */
393
+ static VALUE PCSC_Card_status(VALUE self) {
394
+ struct SCardHandleEx *card;
395
+ char *atr_buffer, *reader_names_buffer;
396
+ DWORD atr_length, reader_names_length, state, protocol;
397
+ VALUE rbStateVal, rbProtocolVal, rbAtrVal, rbReaderNamesVal, rbReturnHash;
398
+
399
+ Data_Get_Struct(self, struct SCardHandleEx, card);
400
+ if(card == NULL) return Qnil;
401
+
402
+ atr_length = MAX_ATR_SIZE;
403
+ atr_buffer = ALLOC_N(char, atr_length);
404
+ reader_names_length = 4096;
405
+ reader_names_buffer = ALLOC_N(char, reader_names_length);
406
+ if(atr_buffer == NULL || reader_names_buffer == NULL) {
407
+ if(reader_names_buffer != NULL) xfree(reader_names_buffer);
408
+ if(atr_buffer != NULL) xfree(atr_buffer);
409
+ return Qnil;
410
+ }
411
+
412
+ card->pcsc_error = SCardStatus(card->card_handle, reader_names_buffer, &reader_names_length, &state, &protocol, (LPSTR)atr_buffer, &atr_length);
413
+ if(card->pcsc_error != SCARD_S_SUCCESS) {
414
+ xfree(reader_names_buffer); xfree(atr_buffer);
415
+ _PCSC_Exception_raise(card->pcsc_error, "SCardStatus");
416
+ return Qnil;
417
+ }
418
+
419
+ rbStateVal = UINT2NUM(state);
420
+ rbProtocolVal = UINT2NUM(protocol);
421
+ rbAtrVal = rb_str_new(atr_buffer, atr_length);
422
+ rbReaderNamesVal = PCSC_Internal_multistring_to_ruby_array(reader_names_buffer, reader_names_length);
423
+
424
+ rbReturnHash = rb_hash_new();
425
+ rb_hash_aset(rbReturnHash, _rbStateKey, rbStateVal);
426
+ rb_hash_aset(rbReturnHash, _rbProtocolKey, rbProtocolVal);
427
+ rb_hash_aset(rbReturnHash, _rbAtrKey, rbAtrVal);
428
+ rb_hash_aset(rbReturnHash, _rbReaderNamesKey, rbReaderNamesVal);
429
+ return rbReturnHash;
430
+ }
431
+
432
+ /* :Document-method: last_error
433
+ * call-seq:
434
+ * card.last_error() --> last_error
435
+ *
436
+ * The error code returned by the last PC/SC call. Useful for recovering from exceptions.
437
+ *
438
+ * The returned code is a number, and should be one of the Smartcard::PCSC::SCARD_ constants.
439
+ * The code indicating correct operation is Smartcard::PCSC::SCARD_S_SUCCESS.
440
+ */
441
+ static VALUE PCSC_Card_last_error(VALUE self) {
442
+ struct SCardHandleEx *card;
443
+
444
+ Data_Get_Struct(self, struct SCardHandleEx, card);
445
+ if(card == NULL) return Qnil;
446
+
447
+ return UINT2NUM(card->pcsc_error);
448
+ }
449
+
450
+ #ifdef MAKE_RDOC_HAPPY
451
+ mSmartcard = rb_define_module("Smartcard");
452
+ mPcsc = rb_define_module_under(mSmartcard, "PCSC");
453
+ #endif
454
+
455
+ /* :Document-class: Smartcard::PCSC::Card
456
+ * Connects a smart-card in a PC/SC reader to the Ruby world.
457
+ * Wraps a _SCARDHANDLE_ structure.
458
+ */
459
+ void Init_PCSC_Card() {
460
+ ID state_id, protocol_id, atr_id, reader_names_id;
461
+
462
+ state_id = rb_intern("status"); _rbStateKey = ID2SYM(state_id);
463
+ protocol_id = rb_intern("protocol"); _rbProtocolKey = ID2SYM(protocol_id);
464
+ atr_id = rb_intern("atr"); _rbAtrKey = ID2SYM(atr_id);
465
+ reader_names_id = rb_intern("reader_names"); _rbReaderNamesKey = ID2SYM(reader_names_id);
466
+
467
+ cPcscCard = rb_define_class_under(mPcsc, "Card", rb_cObject);
468
+ rb_define_alloc_func(cPcscCard, PCSC_Card_alloc);
469
+ rb_define_method(cPcscCard, "initialize", PCSC_Card_initialize, 4);
470
+ rb_define_method(cPcscCard, "disconnect", PCSC_Card_disconnect, 1);
471
+ rb_define_method(cPcscCard, "reconnect", PCSC_Card_reconnect, 3);
472
+ rb_define_method(cPcscCard, "begin_transaction", PCSC_Card_begin_transaction, 0);
473
+ rb_define_method(cPcscCard, "end_transaction", PCSC_Card_end_transaction, 1);
474
+ rb_define_method(cPcscCard, "get_attribute", PCSC_Card_get_attribute, 1);
475
+ rb_define_method(cPcscCard, "set_attribute", PCSC_Card_set_attribute, 2);
476
+ rb_define_method(cPcscCard, "transmit", PCSC_Card_transmit, 3);
477
+ rb_define_method(cPcscCard, "control", PCSC_Card_control, 3);
478
+ rb_define_method(cPcscCard, "status", PCSC_Card_status, 0);
479
+ rb_define_method(cPcscCard, "last_error", PCSC_Card_last_error, 0);
480
+ }
481
+
482
+ /* Retrieves the SCARDHANDLE wrapped into a Smartcard::PCSC::Card instance. */
483
+ int _PCSC_Card_lowlevel_get(VALUE rbCard, SCARDHANDLE *card_handle) {
484
+ struct SCardHandleEx *card;
485
+
486
+ if(TYPE(rbCard) != T_DATA || RDATA(rbCard)->dfree != (void (*)(void *))PCSC_Card_free)
487
+ return 0;
488
+
489
+ Data_Get_Struct(rbCard, struct SCardHandleEx, card);
490
+ *card_handle = card->card_handle;
491
+ return 1;
492
+ }