tem_ruby 0.9.2 → 0.10.0
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/CHANGELOG +2 -0
- data/Manifest +47 -36
- data/Rakefile +23 -0
- data/bin/tem_bench +0 -0
- data/bin/tem_ca +0 -0
- data/bin/tem_irb +1 -8
- data/bin/tem_proxy +65 -0
- data/bin/tem_stat +8 -12
- data/dev_ca/config.yml +2 -0
- data/lib/tem/auto_conf.rb +25 -0
- data/lib/tem/buffers.rb +21 -34
- data/lib/tem/crypto_abi.rb +78 -30
- data/lib/tem/keys.rb +21 -22
- data/lib/tem/lifecycle.rb +2 -2
- data/lib/tem/seclosures.rb +9 -13
- data/lib/tem/tag.rb +19 -14
- data/lib/tem/tem.rb +9 -25
- data/lib/tem/transport/auto_configurator.rb +87 -0
- data/lib/tem/transport/java_card_mixin.rb +99 -0
- data/lib/tem/transport/jcop_remote_protocol.rb +51 -0
- data/lib/tem/transport/jcop_remote_server.rb +171 -0
- data/lib/tem/transport/jcop_remote_transport.rb +65 -0
- data/lib/tem/transport/pcsc_transport.rb +87 -0
- data/lib/tem/transport/transport.rb +10 -0
- data/lib/tem_ruby.rb +12 -4
- data/tem_ruby.gemspec +24 -40
- data/test/_test_cert.rb +2 -13
- data/test/tem_test_case.rb +26 -0
- data/test/test_driver.rb +3 -22
- data/test/test_exceptions.rb +3 -22
- data/test/test_tem.rb +2 -21
- data/test/transport/test_auto_configurator.rb +114 -0
- data/test/transport/test_java_card_mixin.rb +90 -0
- data/test/transport/test_jcop_remote.rb +82 -0
- data/timings/timings.rb +2 -9
- metadata +94 -62
- data/lib/scard/java_card.rb +0 -31
- data/lib/scard/jcop_remote_terminal.rb +0 -52
- data/lib/scard/pcsc_terminal.rb +0 -83
data/CHANGELOG
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
v0.10.0. New transport code, allowing for multiple readers and TEM proxying.
|
2
|
+
|
1
3
|
v0.9.2. Changed exec-SECpack calling sequence for fw 1.9.1(fire, the released version).
|
2
4
|
|
3
5
|
v0.9.1. Cleaner names for the pstore data types and opcode arguments. "Bound" instead of "sealed" SECpack.
|
data/Manifest
CHANGED
@@ -1,45 +1,56 @@
|
|
1
|
-
bin/
|
1
|
+
bin/tem_bench
|
2
2
|
bin/tem_ca
|
3
3
|
bin/tem_irb
|
4
|
-
bin/
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
timings/vm_perf.rb
|
12
|
-
timings/devchip_decrypt.rb
|
13
|
-
timings/simple_apdu.rb
|
14
|
-
timings/post_buffer.rb
|
15
|
-
timings/blank_bound_secpack.rb
|
16
|
-
timings/vm_perf_bound.rb
|
17
|
-
timings/timings.rb
|
18
|
-
timings/blank_sec.rb
|
19
|
-
lib/scard/java_card.rb
|
20
|
-
lib/scard/jcop_remote_terminal.rb
|
21
|
-
lib/scard/pcsc_terminal.rb
|
22
|
-
lib/tem_ruby.rb
|
23
|
-
lib/tem/tag.rb
|
24
|
-
lib/tem/keys.rb
|
25
|
-
lib/tem/sec_opcodes.rb
|
4
|
+
bin/tem_proxy
|
5
|
+
bin/tem_stat
|
6
|
+
CHANGELOG
|
7
|
+
dev_ca/ca_cert.cer
|
8
|
+
dev_ca/ca_cert.pem
|
9
|
+
dev_ca/ca_key.pem
|
10
|
+
dev_ca/config.yml
|
26
11
|
lib/tem/_cert.rb
|
27
|
-
lib/tem/buffers.rb
|
28
|
-
lib/tem/toolkit.rb
|
29
|
-
lib/tem/tem.rb
|
30
12
|
lib/tem/abi.rb
|
31
|
-
lib/tem/
|
13
|
+
lib/tem/auto_conf.rb
|
14
|
+
lib/tem/buffers.rb
|
32
15
|
lib/tem/ca.rb
|
33
|
-
lib/tem/
|
34
|
-
lib/tem/sec_exec_error.rb
|
35
|
-
lib/tem/sec_assembler.rb
|
36
|
-
lib/tem/lifecycle.rb
|
16
|
+
lib/tem/crypto_abi.rb
|
37
17
|
lib/tem/ecert.rb
|
38
18
|
lib/tem/hive.rb
|
19
|
+
lib/tem/keys.rb
|
20
|
+
lib/tem/lifecycle.rb
|
21
|
+
lib/tem/sec_assembler.rb
|
22
|
+
lib/tem/sec_exec_error.rb
|
23
|
+
lib/tem/sec_opcodes.rb
|
39
24
|
lib/tem/seclosures.rb
|
25
|
+
lib/tem/secpack.rb
|
26
|
+
lib/tem/tag.rb
|
27
|
+
lib/tem/tem.rb
|
28
|
+
lib/tem/toolkit.rb
|
29
|
+
lib/tem/transport/auto_configurator.rb
|
30
|
+
lib/tem/transport/java_card_mixin.rb
|
31
|
+
lib/tem/transport/jcop_remote_protocol.rb
|
32
|
+
lib/tem/transport/jcop_remote_server.rb
|
33
|
+
lib/tem/transport/jcop_remote_transport.rb
|
34
|
+
lib/tem/transport/pcsc_transport.rb
|
35
|
+
lib/tem/transport/transport.rb
|
36
|
+
lib/tem_ruby.rb
|
37
|
+
LICENSE
|
38
|
+
Manifest
|
39
|
+
Rakefile
|
40
40
|
README
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
41
|
+
test/_test_cert.rb
|
42
|
+
test/tem_test_case.rb
|
43
|
+
test/test_driver.rb
|
44
|
+
test/test_exceptions.rb
|
45
|
+
test/test_tem.rb
|
46
|
+
test/transport/test_auto_configurator.rb
|
47
|
+
test/transport/test_java_card_mixin.rb
|
48
|
+
test/transport/test_jcop_remote.rb
|
49
|
+
timings/blank_bound_secpack.rb
|
50
|
+
timings/blank_sec.rb
|
51
|
+
timings/devchip_decrypt.rb
|
52
|
+
timings/post_buffer.rb
|
53
|
+
timings/simple_apdu.rb
|
54
|
+
timings/timings.rb
|
55
|
+
timings/vm_perf.rb
|
56
|
+
timings/vm_perf_bound.rb
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'echoe'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('tem_ruby') do |p|
|
6
|
+
p.project = 'tem' # rubyforge project
|
7
|
+
p.docs_host = "costan@rubyforge.org:/var/www/gforge-projects/tem/rdoc/"
|
8
|
+
|
9
|
+
p.author = 'Victor Costan'
|
10
|
+
p.email = 'victor@costan.us'
|
11
|
+
p.summary = 'TEM (Trusted Execution Module) driver, written in and for ruby.'
|
12
|
+
p.url = 'http://tem.rubyforge.org'
|
13
|
+
p.dependencies = ['smartcard >=0.3.0']
|
14
|
+
|
15
|
+
p.need_tar_gz = !Platform.windows?
|
16
|
+
p.need_zip = !Platform.windows?
|
17
|
+
p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
18
|
+
end
|
19
|
+
|
20
|
+
if $0 == __FILE__
|
21
|
+
Rake.application = Rake::Application.new
|
22
|
+
Rake.application.run
|
23
|
+
end
|
data/bin/tem_bench
CHANGED
File without changes
|
data/bin/tem_ca
CHANGED
File without changes
|
data/bin/tem_irb
CHANGED
@@ -6,13 +6,6 @@ require 'tem_ruby'
|
|
6
6
|
|
7
7
|
require 'irb'
|
8
8
|
|
9
|
-
|
10
|
-
unless $terminal.connect
|
11
|
-
$terminal.disconnect
|
12
|
-
$terminal = Tem::SCard::PCSCTerminal.new
|
13
|
-
$terminal.connect
|
14
|
-
end
|
15
|
-
$javacard = Tem::SCard::JavaCard.new($terminal)
|
16
|
-
$tem = Tem::Session.new($javacard)
|
9
|
+
Tem.auto_conf
|
17
10
|
|
18
11
|
IRB.start __FILE__
|
data/bin/tem_proxy
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# TEM transport-level proxy.
|
4
|
+
# Serves a TCP connection
|
5
|
+
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'tem_ruby'
|
10
|
+
|
11
|
+
# JCOP remote serving logic implementing a proxy to another transport.
|
12
|
+
class ServingLogic
|
13
|
+
include Tem::Transport::JcopRemoteServingStubs
|
14
|
+
def initialize(serving_transport, logging = false)
|
15
|
+
@serving = serving_transport
|
16
|
+
@logger = Logger.new STDERR
|
17
|
+
@logger.level = logging ? Logger::DEBUG : Logger::FATAL
|
18
|
+
@connected = true
|
19
|
+
end
|
20
|
+
def connection_start
|
21
|
+
@logger.info "Connection start"
|
22
|
+
unless @connected
|
23
|
+
@serving.connect
|
24
|
+
@connected = true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def connection_end
|
28
|
+
@logger.info "Connection end"
|
29
|
+
@serving.disconnect if @connected
|
30
|
+
@connected = false
|
31
|
+
end
|
32
|
+
def exchange_apdu(apdu)
|
33
|
+
@logger.info "APDU request: #{apdu.map { |n| '%02x' % n }.join(' ')}"
|
34
|
+
response = @serving.exchange_apdu apdu
|
35
|
+
@logger.info "APDU response: #{response.map { |n| '%02x' % n }.join(' ')}"
|
36
|
+
response
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Indefinitely runs a JCOP remove serving loop that proxies to another TEM
|
41
|
+
# transport.
|
42
|
+
#
|
43
|
+
# The TEM transport is automatically configured based on environment information
|
44
|
+
# and defaults.
|
45
|
+
#
|
46
|
+
def serve(options)
|
47
|
+
@logger = Logger.new STDERR
|
48
|
+
@logger.level = options[:logging] ? Logger::DEBUG : Logger::FATAL
|
49
|
+
|
50
|
+
serving_transport = Tem::Transport.auto_transport
|
51
|
+
@logger.info "Proxying to #{serving_transport.inspect}\n"
|
52
|
+
@logger.info "Serving with #{options.inspect}\n"
|
53
|
+
serving_logic = ServingLogic.new serving_transport, options[:logging]
|
54
|
+
Tem::Transport::JcopRemoteServer.new(options, serving_logic).run
|
55
|
+
end
|
56
|
+
|
57
|
+
# Parses the commmand-line arguments into an options hash suitable for #serve.
|
58
|
+
def parse_args
|
59
|
+
{ :ip => ARGV[1] || '0.0.0.0', :port => (ARGV[0] || '9000').to_i,
|
60
|
+
:logging => !(ENV['DEBUG'] &&
|
61
|
+
['0', 'no', 'false'].include?(ENV['DEBUG'].downcase)) }
|
62
|
+
end
|
63
|
+
|
64
|
+
options = parse_args
|
65
|
+
serve options
|
data/bin/tem_stat
CHANGED
@@ -5,35 +5,31 @@ require 'rubygems'
|
|
5
5
|
require 'tem_ruby'
|
6
6
|
require 'pp'
|
7
7
|
|
8
|
-
|
9
|
-
unless $terminal.connect
|
10
|
-
$terminal.disconnect
|
11
|
-
$terminal = Tem::SCard::PCSCTerminal.new
|
12
|
-
$terminal.connect
|
13
|
-
end
|
14
|
-
$javacard = Tem::SCard::JavaCard.new($terminal)
|
15
|
-
$tem = Tem::Session.new($javacard)
|
8
|
+
Tem.auto_conf
|
16
9
|
|
17
|
-
print "Connected to TEM using #{$
|
10
|
+
print "Connected to TEM using #{$tem.transport.inspect}\n"
|
18
11
|
begin
|
19
12
|
fw_ver = $tem.tk_firmware_ver
|
20
13
|
print "TEM firmware version: #{fw_ver[:major]}.#{fw_ver[:minor]}\n"
|
21
|
-
rescue
|
14
|
+
rescue Exception => e
|
22
15
|
print "Could not read TEM firmware version. Is the TEM emitted?\n"
|
16
|
+
print "#{e.class.name}: #{e}\n#{e.backtrace.join("\n")}\n"
|
23
17
|
end
|
24
18
|
|
25
19
|
begin
|
26
20
|
b_stat = $tem.stat_buffers
|
27
21
|
print "TEM memory stat:\n"
|
28
22
|
pp b_stat
|
29
|
-
rescue
|
23
|
+
rescue Exception => e
|
30
24
|
print "Could not retrieve TEM memory stat. Is the TEM activated?\n"
|
25
|
+
print "#{e.class.name}: #{e}\n#{e.backtrace.join("\n")}\n"
|
31
26
|
end
|
32
27
|
|
33
28
|
begin
|
34
29
|
k_stat = $tem.stat_keys
|
35
30
|
print "TEM crypto stat:\n"
|
36
31
|
pp k_stat
|
37
|
-
rescue
|
32
|
+
rescue Exception => e
|
38
33
|
print "Could not retrieve TEM crypto stat. Is the TEM activated?\n"
|
34
|
+
print "#{e.class.name}: #{e}\n#{e.backtrace.join("\n")}\n"
|
39
35
|
end
|
data/dev_ca/config.yml
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
---
|
2
|
+
# the development CA is valid for 10 years
|
2
3
|
:ca_validity_days: 3652
|
3
4
|
:issuer:
|
4
5
|
CN: Trusted Execution Module Development CA
|
@@ -7,6 +8,7 @@
|
|
7
8
|
C: US
|
8
9
|
O: Massachusetts Insitute of Technology
|
9
10
|
OU: Computer Science and Artificial Intelligence Laboratory
|
11
|
+
# a TEM is valid for two days
|
10
12
|
:ecert_validity_days: 730
|
11
13
|
:subject:
|
12
14
|
CN: Trusted Execution Module DevChip
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tem
|
2
|
+
# Automatically configures a TEM.
|
3
|
+
#
|
4
|
+
# In case of success, the $tem global variable is set to a Tem::Session that
|
5
|
+
# can be used to talk to some TEM. An exception will be raised if the session
|
6
|
+
# creation fails.
|
7
|
+
#
|
8
|
+
# It is safe to call auto_conf multiple times. A single session will be open.
|
9
|
+
def self.auto_conf
|
10
|
+
return $tem if $tem
|
11
|
+
$tem = auto_tem
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a new session to a TEM, using an automatically-configured transport.
|
15
|
+
# :call-seq:
|
16
|
+
# Tem.auto_tem -> Tem::Session
|
17
|
+
#
|
18
|
+
# In case of success, returns a Tem::Session that can be used to talk to some
|
19
|
+
# TEM. An exception will be raised if the session creation fails.
|
20
|
+
def self.auto_tem
|
21
|
+
transport = Tem::Transport.auto_transport
|
22
|
+
raise 'No suitable TEM was found' unless transport
|
23
|
+
Tem::Session.new transport
|
24
|
+
end
|
25
|
+
end
|
data/lib/tem/buffers.rb
CHANGED
@@ -1,77 +1,64 @@
|
|
1
1
|
module Tem::Buffers
|
2
2
|
def alloc_buffer(length)
|
3
|
-
|
4
|
-
|
5
|
-
tem_error(response) if failure_code(response)
|
3
|
+
response = @transport.applet_apdu! :ins => 0x20,
|
4
|
+
:p12 => to_tem_short(length)
|
6
5
|
return read_tem_byte(response, 0)
|
7
6
|
end
|
8
7
|
|
9
8
|
def release_buffer(buffer_id)
|
10
|
-
|
11
|
-
response = issue_apdu apdu
|
12
|
-
tem_error(response) if failure_code(response)
|
13
|
-
return true
|
9
|
+
@transport.applet_apdu! :ins => 0x21, :p1 => buffer_id
|
14
10
|
end
|
15
11
|
|
16
12
|
def flush_buffers
|
17
|
-
|
18
|
-
response = issue_apdu apdu
|
19
|
-
tem_error(response) if failure_code(response)
|
20
|
-
return true
|
13
|
+
@transport.applet_apdu! :ins => 0x26
|
21
14
|
end
|
22
15
|
|
23
16
|
def get_buffer_length(buffer_id)
|
24
|
-
|
25
|
-
response = issue_apdu apdu
|
26
|
-
tem_error(response) if failure_code(response)
|
17
|
+
response = @transport.applet_apdu! :ins => 0x22, :p1 => buffer_id
|
27
18
|
return read_tem_short(response, 0)
|
28
19
|
end
|
29
20
|
|
30
21
|
def read_buffer(buffer_id)
|
31
|
-
|
22
|
+
guess_buffer_chunk_size
|
32
23
|
|
33
24
|
buffer = []
|
34
25
|
chunk_id = 0
|
35
26
|
while true do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
break if response.length != @buffer_chunk_size + 2
|
27
|
+
response = @transport.applet_apdu! :ins => 0x23, :p1 => buffer_id,
|
28
|
+
:p2 => chunk_id
|
29
|
+
buffer += response
|
30
|
+
break if response.length != @buffer_chunk_size
|
41
31
|
chunk_id += 1
|
42
32
|
end
|
43
33
|
return buffer
|
44
34
|
end
|
45
35
|
|
46
36
|
def write_buffer(buffer_id, data)
|
47
|
-
|
37
|
+
guess_buffer_chunk_size
|
48
38
|
|
49
39
|
chunk_id, offset = 0, 0
|
50
40
|
while offset < data.length do
|
51
41
|
write_size = (data.length - offset < @buffer_chunk_size) ?
|
52
42
|
data.length - offset : @buffer_chunk_size
|
53
|
-
|
54
|
-
|
55
|
-
response = issue_apdu apdu
|
56
|
-
tem_error(response) if failure_code(response)
|
57
|
-
|
43
|
+
@transport.applet_apdu! :ins => 0x24, :p1 => buffer_id, :p2 => chunk_id,
|
44
|
+
:data => data[offset, write_size]
|
58
45
|
chunk_id += 1
|
59
46
|
offset += write_size
|
60
47
|
end
|
61
48
|
end
|
62
49
|
|
63
50
|
def guess_buffer_chunk_size
|
64
|
-
|
65
|
-
|
66
|
-
|
51
|
+
@buffer_chunk_size ||= guess_buffer_chunk_size!
|
52
|
+
end
|
53
|
+
|
54
|
+
def guess_buffer_chunk_size!
|
55
|
+
response = @transport.applet_apdu! :ins => 0x25
|
67
56
|
return read_tem_short(response, 0)
|
68
57
|
end
|
69
58
|
|
70
59
|
def stat_buffers
|
71
|
-
|
72
|
-
|
73
|
-
tem_error(response) if failure_code(response)
|
74
|
-
response = reply_data(response)
|
60
|
+
response = @transport.applet_apdu! :ins => 0x27
|
61
|
+
|
75
62
|
memory_types = [:persistent, :clear_on_reset, :clear_on_deselect]
|
76
63
|
stat = {:free => {}, :buffers => []}
|
77
64
|
memory_types.each_with_index { |mt, i| stat[:free][mt] = read_tem_short(response, i * 2) }
|
@@ -91,7 +78,7 @@ module Tem::Buffers
|
|
91
78
|
end
|
92
79
|
|
93
80
|
def post_buffer(data)
|
94
|
-
buffer_id = alloc_buffer
|
81
|
+
buffer_id = alloc_buffer data.length
|
95
82
|
write_buffer buffer_id, data
|
96
83
|
return buffer_id
|
97
84
|
end
|
data/lib/tem/crypto_abi.rb
CHANGED
@@ -5,12 +5,16 @@ require 'yaml'
|
|
5
5
|
module Tem::CryptoAbi
|
6
6
|
include Tem::Abi
|
7
7
|
|
8
|
-
#
|
8
|
+
# The methods that will be mixed into the TEM module
|
9
9
|
module MixedMethods
|
10
|
+
# Reads a TEM-encoded big number.
|
10
11
|
def read_tem_bignum(buffer, offset, length)
|
11
|
-
return buffer[offset...(offset+length)].inject(0)
|
12
|
+
return buffer[offset...(offset+length)].inject(0) do |num, digit|
|
13
|
+
num = (num << 8) | digit
|
14
|
+
end
|
12
15
|
end
|
13
|
-
|
16
|
+
|
17
|
+
# Returns the TEM encoding for a big number.
|
14
18
|
def to_tem_bignum(n)
|
15
19
|
if n.kind_of? OpenSSL::BN
|
16
20
|
len = n.num_bytes
|
@@ -33,19 +37,30 @@ module Tem::CryptoAbi
|
|
33
37
|
return q.reverse
|
34
38
|
end
|
35
39
|
end
|
36
|
-
|
40
|
+
|
37
41
|
def load_tem_key_material(key, syms, buffer, offset)
|
38
|
-
lengths = (0...syms.length).map
|
42
|
+
lengths = (0...syms.length).map do |i|
|
43
|
+
read_tem_short buffer, offset + i * 2
|
44
|
+
end
|
39
45
|
offsets = [offset + syms.length * 2]
|
40
|
-
|
41
|
-
|
42
|
-
key.send((syms[i].to_s + '=').to_sym,
|
43
|
-
|
46
|
+
syms.each_index { |i| offsets[i + 1] = offsets[i] + lengths[i] }
|
47
|
+
syms.each_index do |i|
|
48
|
+
key.send((syms[i].to_s + '=').to_sym,
|
49
|
+
read_tem_bignum(buffer, offsets[i], lengths[i]))
|
50
|
+
end
|
44
51
|
end
|
45
|
-
|
52
|
+
|
53
|
+
# The length of a TEM symmetric key.
|
54
|
+
def tem_symmetric_key_length
|
55
|
+
16 # 128 bits
|
56
|
+
end
|
57
|
+
|
46
58
|
def read_tem_key(buffer, offset)
|
47
59
|
key_type = read_tem_ubyte buffer, offset
|
48
|
-
|
60
|
+
case key_type
|
61
|
+
when 0x99
|
62
|
+
key = buffer[offset, tem_symmetric_key_length]
|
63
|
+
when 0xAA, 0x55
|
49
64
|
key = OpenSSL::PKey::RSA.new
|
50
65
|
syms = (key_type == 0xAA) ? [:e, :n] : [:p, :q, :dmp1, :dmq1, :iqmp]
|
51
66
|
load_tem_key_material key, syms, buffer, offset + 1
|
@@ -61,10 +76,10 @@ module Tem::CryptoAbi
|
|
61
76
|
key.e = (emp1 < emq1) ? emp1 : emq1
|
62
77
|
key.d = key.e.mod_inverse(p1q1)
|
63
78
|
end
|
64
|
-
return new_key_from_ssl(key, (key_type == 0xAA))
|
65
79
|
else
|
66
80
|
raise "Invalid key type #{'%02x' % key_type}"
|
67
81
|
end
|
82
|
+
return new_key_from_ssl(key, (key_type == 0xAA))
|
68
83
|
end
|
69
84
|
|
70
85
|
def to_tem_key(ssl_key, type)
|
@@ -78,18 +93,19 @@ module Tem::CryptoAbi
|
|
78
93
|
end
|
79
94
|
end
|
80
95
|
|
96
|
+
# Creates a new TEM key wrapper from a SSL key
|
81
97
|
def new_key_from_ssl(ssl_key, is_public)
|
82
|
-
|
98
|
+
if ssl_key.kind_of? OpenSSL::PKey::RSA
|
99
|
+
AsymmetricKey.new ssl_key, is_public, :pkcs1
|
100
|
+
else
|
101
|
+
SymmetricKey.new ssl_key
|
102
|
+
end
|
83
103
|
end
|
84
104
|
|
105
|
+
# Compute a cryptographic hash in the same way that the TEM does.
|
85
106
|
def hash_for_tem(data)
|
86
|
-
|
87
|
-
|
88
|
-
else
|
89
|
-
data_string = data.pack('C*')
|
90
|
-
end
|
91
|
-
digest_string = Digest::SHA1.digest(data_string)
|
92
|
-
return digest_string.unpack('C*')
|
107
|
+
data = data.pack 'C*' unless data.kind_of? String
|
108
|
+
Digest::SHA1.digest(data).unpack 'C*'
|
93
109
|
end
|
94
110
|
end
|
95
111
|
|
@@ -104,13 +120,19 @@ module Tem::CryptoAbi
|
|
104
120
|
end
|
105
121
|
|
106
122
|
def self.load_ssl(ssl_key)
|
107
|
-
return {:pubkey => AsymmetricKey.new(ssl_key, true, :pkcs1),
|
123
|
+
return { :pubkey => AsymmetricKey.new(ssl_key, true, :pkcs1),
|
124
|
+
:privkey => AsymmetricKey.new(ssl_key, false, :pkcs1) }
|
108
125
|
end
|
109
126
|
|
110
127
|
def self.generate_ssl_kp
|
111
128
|
return Tem::CryptoAbi::AsymmetricKey.generate_ssl_kp
|
112
129
|
end
|
113
130
|
|
131
|
+
def self.generate_ssl_sk
|
132
|
+
return Tem::CryptoAbi::SymmetricKey.generate_ssl_sk
|
133
|
+
end
|
134
|
+
|
135
|
+
# Wraps a TEM asymmetric key.
|
114
136
|
class AsymmetricKey
|
115
137
|
attr_reader :ssl_key
|
116
138
|
|
@@ -131,9 +153,11 @@ module Tem::CryptoAbi
|
|
131
153
|
self.to_array.to_yaml.to_s
|
132
154
|
end
|
133
155
|
|
156
|
+
# Generate an asymmetric OpenSSL key pair
|
134
157
|
def self.generate_ssl_kp
|
135
158
|
return OpenSSL::PKey::RSA.generate(2048, 65537)
|
136
159
|
end
|
160
|
+
|
137
161
|
def initialize(ssl_key, is_public, padding_type)
|
138
162
|
@ssl_key = ssl_key
|
139
163
|
@is_public = is_public ? true : false
|
@@ -188,29 +212,53 @@ module Tem::CryptoAbi
|
|
188
212
|
end
|
189
213
|
|
190
214
|
def encrypt(data)
|
191
|
-
encrypt_decrypt
|
215
|
+
encrypt_decrypt data, @size - @padding_bytes,
|
216
|
+
@is_public ? :public_encrypt : :private_encrypt
|
192
217
|
end
|
193
218
|
|
194
219
|
def decrypt(data)
|
195
|
-
encrypt_decrypt
|
220
|
+
encrypt_decrypt data, @size,
|
221
|
+
@is_public ? :public_decrypt : :private_decrypt
|
196
222
|
end
|
197
223
|
|
198
224
|
def sign(data)
|
199
|
-
|
225
|
+
data = data.pack 'C*' if data.respond_to? :pack
|
200
226
|
# PKCS1-padding is forced in by openssl... sigh!
|
201
|
-
out_data = @ssl_key.sign OpenSSL::Digest::SHA1.new,
|
202
|
-
|
227
|
+
out_data = @ssl_key.sign OpenSSL::Digest::SHA1.new, data
|
228
|
+
data.respond_to?(:pack) ? out_data : out_data.unpack('C*')
|
203
229
|
end
|
204
230
|
|
205
231
|
def verify(data, signature)
|
206
|
-
|
207
|
-
|
232
|
+
data = data.pack 'C*' if data.respond_to? :pack
|
233
|
+
signature = signature.pack 'C*' if signature.respond_to? :pack
|
208
234
|
# PKCS1-padding is forced in by openssl... sigh!
|
209
|
-
@ssl_key.verify OpenSSL::Digest::SHA1.new,
|
235
|
+
@ssl_key.verify OpenSSL::Digest::SHA1.new, signature, data
|
210
236
|
end
|
211
237
|
|
212
238
|
def is_public?
|
213
239
|
@is_public
|
214
240
|
end
|
215
|
-
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Wraps a TEM symmetric key
|
244
|
+
def SymmetricKey
|
245
|
+
def initialize(ssl_key)
|
246
|
+
@key = ssl_key
|
247
|
+
@cipher = OpenSSL::Cipher::Cipher.new 'aes-128-ecb'
|
248
|
+
@cipher.key = @key
|
249
|
+
@cipher.iv = "\0" * 16
|
250
|
+
end
|
251
|
+
|
252
|
+
def encrypt(data)
|
253
|
+
end
|
254
|
+
|
255
|
+
def decrypt(data)
|
256
|
+
end
|
257
|
+
|
258
|
+
def sign(data)
|
259
|
+
end
|
260
|
+
|
261
|
+
def verify(data)
|
262
|
+
end
|
263
|
+
end
|
216
264
|
end
|
data/lib/tem/keys.rb
CHANGED
@@ -1,21 +1,19 @@
|
|
1
1
|
module Tem::Keys
|
2
2
|
def devchip_generate_key_pair
|
3
|
-
response =
|
4
|
-
|
5
|
-
|
3
|
+
response = @transport.applet_apdu! :ins => 0x40
|
4
|
+
return { :privkey_id => read_tem_byte(response, 0),
|
5
|
+
:pubkey_id => read_tem_byte(response, 1) }
|
6
6
|
end
|
7
7
|
|
8
8
|
def devchip_release_key(key_id)
|
9
|
-
|
10
|
-
tem_error(response) if failure_code(response)
|
9
|
+
@transport.applet_apdu! :ins => 0x41, :p1 => key_id
|
11
10
|
return true
|
12
11
|
end
|
13
12
|
|
14
13
|
def devchip_save_key(key_id)
|
15
|
-
response =
|
16
|
-
|
17
|
-
|
18
|
-
buffer_id, buffer_length = read_tem_byte(response, 0), read_tem_short(response, 1)
|
14
|
+
response = @transport.applet_apdu! :ins => 0x43, :p1 => key_id
|
15
|
+
buffer_id = read_tem_byte response, 0
|
16
|
+
buffer_length = read_tem_short response, 1
|
19
17
|
key_buffer = read_buffer buffer_id
|
20
18
|
release_buffer buffer_id
|
21
19
|
|
@@ -24,35 +22,36 @@ module Tem::Keys
|
|
24
22
|
|
25
23
|
def devchip_encrypt_decrypt(data, key_id, opcode)
|
26
24
|
buffer_id = post_buffer data
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
begin
|
26
|
+
response = @transport.applet_apdu! :ins => opcode, :p1 => key_id,
|
27
|
+
:p2 => buffer_id
|
28
|
+
ensure
|
29
|
+
release_buffer buffer_id
|
30
|
+
end
|
30
31
|
|
31
|
-
buffer_id
|
32
|
+
buffer_id = read_tem_byte response, 0
|
33
|
+
buffer_length = read_tem_short response, 1
|
32
34
|
data_buffer = read_buffer buffer_id
|
33
35
|
release_buffer buffer_id
|
34
36
|
|
35
37
|
return data_buffer[0...buffer_length]
|
36
38
|
end
|
37
39
|
def devchip_encrypt(data, key_id)
|
38
|
-
devchip_encrypt_decrypt
|
40
|
+
devchip_encrypt_decrypt data, key_id, 0x44
|
39
41
|
end
|
40
42
|
def devchip_decrypt(data, key_id)
|
41
|
-
devchip_encrypt_decrypt
|
43
|
+
devchip_encrypt_decrypt data, key_id, 0x45
|
42
44
|
end
|
43
45
|
|
44
46
|
def stat_keys
|
45
|
-
|
46
|
-
|
47
|
-
tem_error(response) if failure_code(response)
|
48
|
-
response = reply_data(response)
|
49
|
-
key_types = {0x99 => :symmetric, 0x55 => :private, 0xAA => :public}
|
47
|
+
response = @transport.applet_apdu! :ins => 0x27, :p1 => 0x01
|
48
|
+
key_types = { 0x99 => :symmetric, 0x55 => :private, 0xAA => :public }
|
50
49
|
stat = {:keys => {}}
|
51
50
|
offset = 0
|
52
51
|
while offset < response.length do
|
53
52
|
stat[:keys][read_tem_ubyte(response, offset)] =
|
54
|
-
{:type => key_types[read_tem_ubyte(response, offset + 1)],
|
55
|
-
|
53
|
+
{ :type => key_types[read_tem_ubyte(response, offset + 1)],
|
54
|
+
:bits => read_tem_ushort(response, offset + 2) }
|
56
55
|
offset += 4
|
57
56
|
end
|
58
57
|
return stat
|