tem_ruby 0.9.2 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|