aboutsummaryrefslogtreecommitdiffstats
path: root/src/pcsc_server.rb
blob: 4a802e965eb2b48062702dd9bc6e980bbe5932d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env ruby
# encoding: UTF-8
=begin
This file is part of softSIM.

softSIM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

softSIM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with sofSIM.  If not, see <http://www.gnu.org/licenses/>.

Copyright (C) 2011 Kevin "tsaitgaist" Redon kevredon@mail.tsaitgaist.info
=end
require './sap/server.rb'
require 'rubygems'
require 'smartcard'
=begin
need to install
sudo aptitude install ruby ruby-dev rubygems
sudo aptitude install libpcsclite1 libpcsclite-dev libruby
sudo gem install smartcard (http://www.rubygems.org/gems/smartcard)
=end

# SAP server using PCSC for the card
class PCSCServer < Server

  # provide the io to listen to
  def initialize(io)
    super(io)
  end
  
  # get the reader and the card
  # return if connection to card succeeded
  def select_card
  
    # get PCSC context
    begin
      @context = Smartcard::PCSC::Context.new
    rescue Smartcard::PCSC::Exception => e
      puts "PCSC not available. please start PCSC"
      return false
    end
    
    # get all readers
    begin
      readers = @context.readers
    rescue Smartcard::PCSC::Exception => e
      puts "no reader available. please connect a card reader"
      return false
    end
    
    # one reader required
    if readers.size==0 then
      puts "no reader available. connect a reader"
      return false
      # select reader
    elsif readers.size==1 then
      # use the first reader
      reader = readers.first
    elsif @reader_id
      # reader already selected
    else
      # select reader
      puts "readers:"
      readers.each_index do |i|
        puts "#{i}) #{readers[i]}"
      end
      reader = nil
      until reader do
        print "select reader [0]: "
        @reader_id = gets.chomp.to_i
        reader = readers[@reader_id]
      end
    end
    puts "using reader: #{reader}"
    
    # connect to the card
    verb = true
    begin
      @card = Smartcard::PCSC::Card.new(@context,reader,:exclusive,:t0)
    rescue Smartcard::PCSC::Exception
      puts "no card inside. insert smart card"
      return false
    end
    
    puts "connected to card"
    return true
  end

  # connect to card
  def connect

    # connect to card
    sleep 1 until select_card
    
    return true
  end
  
  # release card and context
  def disconnect
    @card.disconnect
    @context.release
  end

  # get ATR
  def atr
    raise "connect to card to get ATR" unless @card
    return @card.info[:atr].unpack("C*")
  end

  # send APDU and get response
  def apdu(request)
    raise "connect to card to send APDU" unless @card
    begin
      response = @card.transmit(request.pack('C*')).unpack("C*")
    rescue Smartcard::PCSC::Exception => e
      tries = 0 unless tries
      tries += 1
      if tries <= 3 then
        puts "PCSC bug (try #{tries})"
        sleep 5
        retry
      else
        raise e if tries>=3
      end
    end
    return response
  end

end