tem_ruby 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +35 -0
- data/LICENSE +21 -0
- data/Manifest +45 -0
- data/README +6 -0
- data/bin/tem_bench +9 -0
- data/bin/tem_ca +13 -0
- data/bin/tem_irb +18 -0
- data/bin/tem_stat +39 -0
- data/dev_ca/ca_cert.cer +0 -0
- data/dev_ca/ca_cert.pem +32 -0
- data/dev_ca/ca_key.pem +27 -0
- data/dev_ca/config.yml +12 -0
- data/lib/scard/java_card.rb +31 -0
- data/lib/scard/jcop_remote_terminal.rb +52 -0
- data/lib/scard/pcsc_terminal.rb +83 -0
- data/lib/tem/_cert.rb +158 -0
- data/lib/tem/abi.rb +55 -0
- data/lib/tem/buffers.rb +98 -0
- data/lib/tem/ca.rb +114 -0
- data/lib/tem/crypto_abi.rb +216 -0
- data/lib/tem/ecert.rb +78 -0
- data/lib/tem/hive.rb +18 -0
- data/lib/tem/keys.rb +60 -0
- data/lib/tem/lifecycle.rb +8 -0
- data/lib/tem/sec_assembler.rb +91 -0
- data/lib/tem/sec_exec_error.rb +45 -0
- data/lib/tem/sec_opcodes.rb +154 -0
- data/lib/tem/seclosures.rb +82 -0
- data/lib/tem/secpack.rb +86 -0
- data/lib/tem/tag.rb +28 -0
- data/lib/tem/tem.rb +47 -0
- data/lib/tem/toolkit.rb +104 -0
- data/lib/tem_ruby.rb +29 -0
- data/tem_ruby.gemspec +53 -0
- data/test/_test_cert.rb +81 -0
- data/test/test_driver.rb +127 -0
- data/test/test_exceptions.rb +55 -0
- data/test/test_tem.rb +542 -0
- data/timings/blank_bound_secpack.rb +20 -0
- data/timings/blank_sec.rb +15 -0
- data/timings/devchip_decrypt.rb +9 -0
- data/timings/post_buffer.rb +10 -0
- data/timings/simple_apdu.rb +5 -0
- data/timings/timings.rb +66 -0
- data/timings/vm_perf.rb +141 -0
- data/timings/vm_perf_bound.rb +142 -0
- metadata +143 -0
data/lib/tem/buffers.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module Tem::Buffers
|
2
|
+
def alloc_buffer(length)
|
3
|
+
apdu = [0x00, 0x20, to_tem_short(length), 0x00].flatten
|
4
|
+
response = issue_apdu apdu
|
5
|
+
tem_error(response) if failure_code(response)
|
6
|
+
return read_tem_byte(response, 0)
|
7
|
+
end
|
8
|
+
|
9
|
+
def release_buffer(buffer_id)
|
10
|
+
apdu = [0x00, 0x21, to_tem_byte(buffer_id), 0x00, 0x00].flatten
|
11
|
+
response = issue_apdu apdu
|
12
|
+
tem_error(response) if failure_code(response)
|
13
|
+
return true
|
14
|
+
end
|
15
|
+
|
16
|
+
def flush_buffers
|
17
|
+
apdu = [0x00, 0x26, 0x00, 0x00, 0x00].flatten
|
18
|
+
response = issue_apdu apdu
|
19
|
+
tem_error(response) if failure_code(response)
|
20
|
+
return true
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_buffer_length(buffer_id)
|
24
|
+
apdu = [0x00, 0x22, to_tem_byte(buffer_id), 0x00, 0x00].flatten
|
25
|
+
response = issue_apdu apdu
|
26
|
+
tem_error(response) if failure_code(response)
|
27
|
+
return read_tem_short(response, 0)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_buffer(buffer_id)
|
31
|
+
@buffer_chunk_size = guess_buffer_chunk_size unless defined? @buffer_chunk_size
|
32
|
+
|
33
|
+
buffer = []
|
34
|
+
chunk_id = 0
|
35
|
+
while true do
|
36
|
+
apdu = [0x00, 0x23, to_tem_byte(buffer_id), to_tem_byte(chunk_id), 0x00].flatten
|
37
|
+
response = issue_apdu apdu
|
38
|
+
tem_error(response) if failure_code(response)
|
39
|
+
buffer += response[0...(response.length - 2)]
|
40
|
+
break if response.length != @buffer_chunk_size + 2
|
41
|
+
chunk_id += 1
|
42
|
+
end
|
43
|
+
return buffer
|
44
|
+
end
|
45
|
+
|
46
|
+
def write_buffer(buffer_id, data)
|
47
|
+
@buffer_chunk_size = guess_buffer_chunk_size unless defined? @buffer_chunk_size
|
48
|
+
|
49
|
+
chunk_id, offset = 0, 0
|
50
|
+
while offset < data.length do
|
51
|
+
write_size = (data.length - offset < @buffer_chunk_size) ?
|
52
|
+
data.length - offset : @buffer_chunk_size
|
53
|
+
apdu = [0x00, 0x24, to_tem_byte(buffer_id), to_tem_byte(chunk_id), to_tem_ubyte(write_size),
|
54
|
+
data.values_at(offset...(offset+write_size))].flatten
|
55
|
+
response = issue_apdu apdu
|
56
|
+
tem_error(response) if failure_code(response)
|
57
|
+
|
58
|
+
chunk_id += 1
|
59
|
+
offset += write_size
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def guess_buffer_chunk_size
|
64
|
+
apdu = [0x00, 0x25, 0x00, 0x00, 0x00].flatten
|
65
|
+
response = issue_apdu apdu
|
66
|
+
tem_error(response) if failure_code(response)
|
67
|
+
return read_tem_short(response, 0)
|
68
|
+
end
|
69
|
+
|
70
|
+
def stat_buffers
|
71
|
+
apdu = [0x00, 0x27, 0x00, 0x00, 0x00].flatten
|
72
|
+
response = issue_apdu apdu
|
73
|
+
tem_error(response) if failure_code(response)
|
74
|
+
response = reply_data(response)
|
75
|
+
memory_types = [:persistent, :clear_on_reset, :clear_on_deselect]
|
76
|
+
stat = {:free => {}, :buffers => []}
|
77
|
+
memory_types.each_with_index { |mt, i| stat[:free][mt] = read_tem_short(response, i * 2) }
|
78
|
+
offset = 6
|
79
|
+
i = 0
|
80
|
+
while offset < response.length do
|
81
|
+
stat[:buffers][i] =
|
82
|
+
{:type => memory_types[read_tem_ubyte(response, offset) & 0x3f],
|
83
|
+
:pinned => (read_tem_ubyte(response, offset) & 0x80) != 0,
|
84
|
+
:free => (read_tem_ubyte(response, offset) & 0x40) == 0,
|
85
|
+
:length => read_tem_ushort(response, offset + 1),
|
86
|
+
:xlength => read_tem_ushort(response, offset + 3)}
|
87
|
+
offset += 5
|
88
|
+
i += 1
|
89
|
+
end
|
90
|
+
return stat
|
91
|
+
end
|
92
|
+
|
93
|
+
def post_buffer(data)
|
94
|
+
buffer_id = alloc_buffer(data.length)
|
95
|
+
write_buffer buffer_id, data
|
96
|
+
return buffer_id
|
97
|
+
end
|
98
|
+
end
|
data/lib/tem/ca.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# Certificate Authority (CA) functionality for TEM manufacturers
|
5
|
+
module Tem::CA
|
6
|
+
# creates an Endorsement Certificate for a TEM's Public Endorsement Key
|
7
|
+
def new_ecert(pubek)
|
8
|
+
ca_cert = Tem::CA.ca_cert
|
9
|
+
ca_key = Tem::CA.ca_key
|
10
|
+
conf = Tem::CA.config
|
11
|
+
|
12
|
+
dn = OpenSSL::X509::Name.new conf[:issuer].merge(conf[:subject]).to_a
|
13
|
+
now = Time.now
|
14
|
+
ecert = OpenSSL::X509::Certificate.new
|
15
|
+
ecert.issuer = ca_cert.subject
|
16
|
+
ecert.subject = dn
|
17
|
+
ecert.not_before = now;
|
18
|
+
ecert.not_after = now + conf[:ecert_validity_days] * 60 * 60 * 24;
|
19
|
+
ecert.public_key = pubek
|
20
|
+
ecert.version = 2
|
21
|
+
cf = OpenSSL::X509::ExtensionFactory.new
|
22
|
+
cf.subject_certificate = ecert
|
23
|
+
cf.issuer_certificate = ca_cert
|
24
|
+
ecert.add_extension cf.create_extension("basicConstraints", "CA:true", true)
|
25
|
+
ecert.add_extension cf.create_extension("authorityKeyIdentifier", "keyid,issuer")
|
26
|
+
ecert.add_extension cf.create_extension("keyUsage", "digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign")
|
27
|
+
ecert.add_extension cf.create_extension("extendedKeyUsage", "serverAuth,clientAuth,codeSigning,emailProtection,timeStamping,msCodeInd,msCodeCom,msCTLSign,msSGC,msEFS,nsSGC")
|
28
|
+
ecert.add_extension cf.create_extension("nsCertType", "client,server,email,objsign,sslCA,emailCA,objCA")
|
29
|
+
ecert.add_extension cf.create_extension("subjectKeyIdentifier", "hash")
|
30
|
+
ecert.sign ca_key, OpenSSL::Digest::SHA1.new
|
31
|
+
|
32
|
+
return ecert
|
33
|
+
end
|
34
|
+
|
35
|
+
@@dev_dir = File.join(File.dirname(__FILE__), "..", "..", "dev_ca")
|
36
|
+
|
37
|
+
# retrieves the TEM CA configuration
|
38
|
+
def self.config
|
39
|
+
cpath = Tem::Hive.path_to 'ca/config.yml'
|
40
|
+
cpath = File.join(@@dev_dir, 'config.yml') unless File.exists? cpath
|
41
|
+
|
42
|
+
# try to open it in the base folder
|
43
|
+
scaffold_config unless File.exists? cpath
|
44
|
+
return File.open(cpath, 'r') { |f| YAML.load f }
|
45
|
+
end
|
46
|
+
|
47
|
+
# retrieves the TEM CA certificate
|
48
|
+
def self.ca_cert
|
49
|
+
cpath = Tem::Hive.path_to 'ca/ca_cert.pem'
|
50
|
+
cpath = File.join(@@dev_dir, 'ca_cert.pem') unless File.exists? cpath
|
51
|
+
return OpenSSL::X509::Certificate.new(File.open(cpath, 'r') { |f| f.read })
|
52
|
+
end
|
53
|
+
|
54
|
+
# retrieves the TEM CA key pair (needed for signing)
|
55
|
+
def self.ca_key
|
56
|
+
cpath = Tem::Hive.path_to 'ca/ca_key.pem'
|
57
|
+
cpath = File.join(@@dev_dir, 'ca_key.pem') unless File.exists? cpath
|
58
|
+
return OpenSSL::PKey::RSA.new(File.open(cpath, 'r') { |f| f.read })
|
59
|
+
end
|
60
|
+
|
61
|
+
# scaffolds the structures needed for a TEM CA
|
62
|
+
def self.scaffold_ca
|
63
|
+
conf = config
|
64
|
+
|
65
|
+
# generate and write key
|
66
|
+
ca_key = Tem::CryptoAbi.generate_ssl_kp
|
67
|
+
key_path = Tem::Hive.create 'ca/ca_key.pem'
|
68
|
+
File.open(key_path, 'w') { |f| f.write ca_key.to_pem }
|
69
|
+
|
70
|
+
# create the CA certificate
|
71
|
+
dn = OpenSSL::X509::Name.new conf[:issuer].to_a
|
72
|
+
now = Time.now
|
73
|
+
cert = OpenSSL::X509::Certificate.new
|
74
|
+
cert.subject = cert.issuer = dn
|
75
|
+
cert.not_before = now;
|
76
|
+
cert.not_after = now + conf[:ca_validity_days] * 60 * 60 * 24;
|
77
|
+
cert.public_key = ca_key.public_key
|
78
|
+
cert.version = 2
|
79
|
+
cf = OpenSSL::X509::ExtensionFactory.new
|
80
|
+
cf.subject_certificate = cf.issuer_certificate = cert
|
81
|
+
cert.add_extension cf.create_extension("basicConstraints", "CA:true", true)
|
82
|
+
cert.add_extension cf.create_extension("authorityKeyIdentifier", "keyid,issuer")
|
83
|
+
cert.add_extension cf.create_extension("keyUsage", "cRLSign,keyCertSign")
|
84
|
+
cert.add_extension cf.create_extension("nsCertType", "emailCA,sslCA")
|
85
|
+
cert.add_extension cf.create_extension("subjectKeyIdentifier", "hash")
|
86
|
+
cert.sign ca_key, OpenSSL::Digest::SHA1.new
|
87
|
+
|
88
|
+
# write the CA certificate
|
89
|
+
cert_path = Tem::Hive.create 'ca/ca_cert.pem'
|
90
|
+
File.open(cert_path, 'w') { |f| f.write cert.to_pem }
|
91
|
+
cert_path = Tem::Hive.create 'ca/ca_cert.cer'
|
92
|
+
File.open(cert_path, 'wb') { |f| f.write cert.to_der }
|
93
|
+
end
|
94
|
+
|
95
|
+
# scaffolds a TEM CA configuration
|
96
|
+
def self.scaffold_config
|
97
|
+
def_config = {
|
98
|
+
:issuer => {
|
99
|
+
'C' => 'US', 'ST' => 'Massachusetts', 'L' => 'Cambridge',
|
100
|
+
'O' => 'Massachusetts Insitute of Technology',
|
101
|
+
'OU' => 'Computer Science and Artificial Intelligence Laboratory',
|
102
|
+
'CN' => 'Trusted Execution Module Development CA'
|
103
|
+
},
|
104
|
+
:subject => {
|
105
|
+
'CN' => 'Trusted Execution Module DevChip'
|
106
|
+
},
|
107
|
+
:ca_validity_days => 3652,
|
108
|
+
:ecert_validity_days => 365 * 2,
|
109
|
+
}
|
110
|
+
|
111
|
+
cpath = Tem::Hive.create 'ca/config.yml'
|
112
|
+
File.open(cpath, 'w') { |f| YAML.dump def_config, f }
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'digest'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Tem::CryptoAbi
|
6
|
+
include Tem::Abi
|
7
|
+
|
8
|
+
# contains the methods
|
9
|
+
module MixedMethods
|
10
|
+
def read_tem_bignum(buffer, offset, length)
|
11
|
+
return buffer[offset...(offset+length)].inject(0) { |num, digit| num = (num << 8) | digit }
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_tem_bignum(n)
|
15
|
+
if n.kind_of? OpenSSL::BN
|
16
|
+
len = n.num_bytes
|
17
|
+
bytes = (0...len).map do |i|
|
18
|
+
bit_i = (len - i) * 8
|
19
|
+
v = 0
|
20
|
+
1.upto(8) do
|
21
|
+
bit_i -= 1
|
22
|
+
v = (v << 1) | (n.bit_set?(bit_i) ? 1 : 0)
|
23
|
+
end
|
24
|
+
v
|
25
|
+
end
|
26
|
+
return bytes
|
27
|
+
else
|
28
|
+
q = 0
|
29
|
+
until n == 0 do
|
30
|
+
q << (n & 0xFF)
|
31
|
+
n >>= 8
|
32
|
+
end
|
33
|
+
return q.reverse
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_tem_key_material(key, syms, buffer, offset)
|
38
|
+
lengths = (0...syms.length).map { |i| read_tem_short(buffer, offset + i * 2)}
|
39
|
+
offsets = [offset + syms.length * 2]
|
40
|
+
1.upto(syms.length - 1) { |i| offsets[i] = offsets[i - 1] + lengths[i - 1] }
|
41
|
+
0.upto(syms.length - 1) do |i|
|
42
|
+
key.send((syms[i].to_s + '=').to_sym, read_tem_bignum(buffer, offsets[i], lengths[i]))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_tem_key(buffer, offset)
|
47
|
+
key_type = read_tem_ubyte buffer, offset
|
48
|
+
if key_type == 0xAA || key_type == 0x55
|
49
|
+
key = OpenSSL::PKey::RSA.new
|
50
|
+
syms = (key_type == 0xAA) ? [:e, :n] : [:p, :q, :dmp1, :dmq1, :iqmp]
|
51
|
+
load_tem_key_material key, syms, buffer, offset + 1
|
52
|
+
if key_type == 0x55
|
53
|
+
# a bit of math to rebuild the public key
|
54
|
+
key.n = key.p * key.q
|
55
|
+
p1, q1 = key.p - 1, key.q - 1
|
56
|
+
p1q1 = p1 * q1
|
57
|
+
# HACK: I haven't figured out how to restore d from dmp1 and dmq1, so
|
58
|
+
# I'm betting on the fact that e must be a small prime
|
59
|
+
emp1 = key.dmp1.mod_inverse(p1)
|
60
|
+
emq1 = key.dmq1.mod_inverse(q1)
|
61
|
+
key.e = (emp1 < emq1) ? emp1 : emq1
|
62
|
+
key.d = key.e.mod_inverse(p1q1)
|
63
|
+
end
|
64
|
+
return new_key_from_ssl(key, (key_type == 0xAA))
|
65
|
+
else
|
66
|
+
raise "Invalid key type #{'%02x' % key_type}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_tem_key(ssl_key, type)
|
71
|
+
if [:private, :public].include? type
|
72
|
+
# asymmetric key
|
73
|
+
syms = (type == :public) ? [:e, :n] : [:p, :q, :dmp1, :dmq1, :iqmp]
|
74
|
+
numbers = syms.map { |s| to_tem_bignum ssl_key.send(s) }
|
75
|
+
return [(type == :public) ? 0xAA : 0x55, numbers.map { |n| to_tem_ushort(n.length) }, numbers].flatten
|
76
|
+
else
|
77
|
+
# symmetric key
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def new_key_from_ssl(ssl_key, is_public)
|
82
|
+
AsymmetricKey.new(ssl_key, is_public, :pkcs1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def hash_for_tem(data)
|
86
|
+
if data.kind_of? String
|
87
|
+
data_string = data
|
88
|
+
else
|
89
|
+
data_string = data.pack('C*')
|
90
|
+
end
|
91
|
+
digest_string = Digest::SHA1.digest(data_string)
|
92
|
+
return digest_string.unpack('C*')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
self.extend MixedMethods
|
97
|
+
include MixedMethods
|
98
|
+
def self.included(klass)
|
99
|
+
klass.extend MixedMethods
|
100
|
+
end
|
101
|
+
|
102
|
+
def hash_for_tem(data)
|
103
|
+
Tem::CryptoAbi.hash_for_tem data
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.load_ssl(ssl_key)
|
107
|
+
return {:pubkey => AsymmetricKey.new(ssl_key, true, :pkcs1), :privkey => AsymmetricKey.new(ssl_key, false, :pkcs1) }
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.generate_ssl_kp
|
111
|
+
return Tem::CryptoAbi::AsymmetricKey.generate_ssl_kp
|
112
|
+
end
|
113
|
+
|
114
|
+
class AsymmetricKey
|
115
|
+
attr_reader :ssl_key
|
116
|
+
|
117
|
+
def self.new_from_array(array)
|
118
|
+
AsymmetricKey.new(OpenSSL::PKey::RSA.new(array[0]), *array[1..-1])
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.new_from_yaml_str(yaml_str)
|
122
|
+
array = YAML.load yaml_str
|
123
|
+
new_from_array array
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_array
|
127
|
+
[@ssl_key.to_pem, @is_public, @padding_type]
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_yaml_str
|
131
|
+
self.to_array.to_yaml.to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.generate_ssl_kp
|
135
|
+
return OpenSSL::PKey::RSA.generate(2048, 65537)
|
136
|
+
end
|
137
|
+
def initialize(ssl_key, is_public, padding_type)
|
138
|
+
@ssl_key = ssl_key
|
139
|
+
@is_public = is_public ? true : false
|
140
|
+
@padding_type = padding_type
|
141
|
+
|
142
|
+
case padding_type
|
143
|
+
when :oaep
|
144
|
+
@padding_id = OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
|
145
|
+
@padding_bytes = 42
|
146
|
+
when :pkcs1
|
147
|
+
@padding_id = OpenSSL::PKey::RSA::PKCS1_PADDING
|
148
|
+
@padding_bytes = 11
|
149
|
+
else
|
150
|
+
raise "Unknown padding type #{padding_type}\n"
|
151
|
+
end
|
152
|
+
|
153
|
+
@size = 0
|
154
|
+
n = is_public ? @ssl_key.n : (@ssl_key.p * @ssl_key.q)
|
155
|
+
while n != 0 do
|
156
|
+
@size += 1
|
157
|
+
n >>= 8
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_tem_key
|
162
|
+
Tem::CryptoAbi.to_tem_key @ssl_key, (@is_public ? :public : :private)
|
163
|
+
end
|
164
|
+
|
165
|
+
def chug_data(data, in_size, &chug_block)
|
166
|
+
output = data.class.new
|
167
|
+
i = 0
|
168
|
+
while i < data.length do
|
169
|
+
block_size = (data.length - i < in_size) ? data.length - i : in_size
|
170
|
+
if data.kind_of? String
|
171
|
+
block = data[i...(i+block_size)]
|
172
|
+
else
|
173
|
+
block = data[i...(i+block_size)].pack('C*')
|
174
|
+
end
|
175
|
+
o_block = yield block
|
176
|
+
if data.kind_of? String
|
177
|
+
output += o_block
|
178
|
+
else
|
179
|
+
output += o_block.unpack('C*')
|
180
|
+
end
|
181
|
+
i += block_size
|
182
|
+
end
|
183
|
+
return output
|
184
|
+
end
|
185
|
+
|
186
|
+
def encrypt_decrypt(data, in_size, op)
|
187
|
+
chug_data(data, in_size) { |block| @ssl_key.send op, block, @padding_id }
|
188
|
+
end
|
189
|
+
|
190
|
+
def encrypt(data)
|
191
|
+
encrypt_decrypt(data, @size - @padding_bytes, @is_public ? :public_encrypt : :private_encrypt)
|
192
|
+
end
|
193
|
+
|
194
|
+
def decrypt(data)
|
195
|
+
encrypt_decrypt(data, @size, @is_public ? :public_decrypt : :private_decrypt)
|
196
|
+
end
|
197
|
+
|
198
|
+
def sign(data)
|
199
|
+
in_data = if data.kind_of? String then data else data.pack('C*') end
|
200
|
+
# PKCS1-padding is forced in by openssl... sigh!
|
201
|
+
out_data = @ssl_key.sign OpenSSL::Digest::SHA1.new, in_data
|
202
|
+
if data.kind_of? String then out_data else out_data.unpack('C*') end
|
203
|
+
end
|
204
|
+
|
205
|
+
def verify(data, signature)
|
206
|
+
in_data = if data.kind_of? String then data else data.pack('C*') end
|
207
|
+
in_signature = if signature.kind_of? String then signature else signature.pack('C*') end
|
208
|
+
# PKCS1-padding is forced in by openssl... sigh!
|
209
|
+
@ssl_key.verify OpenSSL::Digest::SHA1.new, in_signature, in_data
|
210
|
+
end
|
211
|
+
|
212
|
+
def is_public?
|
213
|
+
@is_public
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
data/lib/tem/ecert.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Tem::ECert
|
4
|
+
# writes an Endorsement Certificate to the TEM's tag
|
5
|
+
def set_ecert(ecert)
|
6
|
+
set_tag ecert.to_der.unpack('C*')
|
7
|
+
end
|
8
|
+
|
9
|
+
# retrieves the TEM's Endorsement Certificate
|
10
|
+
def endorsement_cert
|
11
|
+
OpenSSL::X509::Certificate.new get_tag[2..-1].pack('C*')
|
12
|
+
end
|
13
|
+
|
14
|
+
# retrieves the certificate of the TEM's Manfacturer (CA)
|
15
|
+
def manufacturer_cert
|
16
|
+
Tem::CA.ca_cert
|
17
|
+
end
|
18
|
+
|
19
|
+
# retrieves the TEM's Public Endorsement Key
|
20
|
+
def pubek
|
21
|
+
new_key_from_ssl endorsement_cert.public_key, true
|
22
|
+
end
|
23
|
+
|
24
|
+
# emits a TEM
|
25
|
+
def emit
|
26
|
+
emit_proc = assemble do |p|
|
27
|
+
# generate EK, compare with (0, 1)
|
28
|
+
p.genkp :type => 0
|
29
|
+
p.ldbc 1
|
30
|
+
p.sub
|
31
|
+
p.jne :to => :not_ok
|
32
|
+
p.ldbc 0
|
33
|
+
p.sub
|
34
|
+
p.jne :to => :not_ok
|
35
|
+
|
36
|
+
# generate and output random authorization for PrivEK
|
37
|
+
p.ldbc 20
|
38
|
+
p.dupn :n => 1
|
39
|
+
p.outnew
|
40
|
+
p.ldwc :privek_auth
|
41
|
+
p.dupn :n => 2
|
42
|
+
p.rnd
|
43
|
+
p.outvb
|
44
|
+
# set authorizations for PrivEK and PubkEK
|
45
|
+
p.ldbc 0
|
46
|
+
p.authk :auth => :privek_auth
|
47
|
+
p.ldbc 1 # PubEK always has its initial authorization be all zeroes
|
48
|
+
p.authk :auth => :pubek_auth
|
49
|
+
p.halt
|
50
|
+
|
51
|
+
# emitting didn't go well, return nothing and leave
|
52
|
+
p.label :not_ok
|
53
|
+
p.ldbc 0
|
54
|
+
p.outnew
|
55
|
+
p.halt
|
56
|
+
|
57
|
+
p.label :privek_auth
|
58
|
+
p.filler :ubyte, 20
|
59
|
+
p.label :pubek_auth
|
60
|
+
p.filler :ubyte, 20
|
61
|
+
p.stack
|
62
|
+
p.extra 8
|
63
|
+
end
|
64
|
+
|
65
|
+
r = execute emit_proc
|
66
|
+
if r.length == 0
|
67
|
+
return nil
|
68
|
+
else
|
69
|
+
privk_auth = r[0...20]
|
70
|
+
pubek_auth = (0...20).map {|i| 0}
|
71
|
+
pubek = tk_read_key 1, pubek_auth
|
72
|
+
tk_delete_key 1, pubek_auth
|
73
|
+
ecert = new_ecert pubek.ssl_key
|
74
|
+
set_ecert ecert
|
75
|
+
return { :privek_auth => privk_auth }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/tem/hive.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# The TEM's configuation hive
|
5
|
+
module Tem::Hive
|
6
|
+
@@hive_dir = File.join(Gem.user_home, ".tem")
|
7
|
+
|
8
|
+
def self.path_to(*hive_entry)
|
9
|
+
File.join(@@hive_dir, *hive_entry)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.create(*hive_entry)
|
13
|
+
path = File.join(@@hive_dir, *hive_entry)
|
14
|
+
FileUtils.mkdir_p File.dirname(path)
|
15
|
+
File.open(path, "w") { |f| }
|
16
|
+
return path
|
17
|
+
end
|
18
|
+
end
|
data/lib/tem/keys.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Tem::Keys
|
2
|
+
def devchip_generate_key_pair
|
3
|
+
response = issue_apdu [0x00, 0x40, 0x00, 0x00, 0x00].flatten
|
4
|
+
tem_error(response) if failure_code(response)
|
5
|
+
return { :privkey_id => read_tem_byte(response, 0), :pubkey_id => read_tem_byte(response, 1) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def devchip_release_key(key_id)
|
9
|
+
response = issue_apdu [0x00, 0x41, to_tem_byte(key_id), 0x00, 0x00].flatten
|
10
|
+
tem_error(response) if failure_code(response)
|
11
|
+
return true
|
12
|
+
end
|
13
|
+
|
14
|
+
def devchip_save_key(key_id)
|
15
|
+
response = issue_apdu [0x00, 0x43, to_tem_byte(key_id), 0x00, 0x00].flatten
|
16
|
+
tem_error(response) if failure_code(response)
|
17
|
+
|
18
|
+
buffer_id, buffer_length = read_tem_byte(response, 0), read_tem_short(response, 1)
|
19
|
+
key_buffer = read_buffer buffer_id
|
20
|
+
release_buffer buffer_id
|
21
|
+
|
22
|
+
read_tem_key key_buffer[0...buffer_length], 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def devchip_encrypt_decrypt(data, key_id, opcode)
|
26
|
+
buffer_id = post_buffer data
|
27
|
+
response = issue_apdu [0x00, opcode, to_tem_byte(key_id), to_tem_byte(buffer_id), 0x00].flatten
|
28
|
+
release_buffer buffer_id
|
29
|
+
tem_error(response) if failure_code(response)
|
30
|
+
|
31
|
+
buffer_id, buffer_length = read_tem_byte(response, 0), read_tem_short(response, 1)
|
32
|
+
data_buffer = read_buffer buffer_id
|
33
|
+
release_buffer buffer_id
|
34
|
+
|
35
|
+
return data_buffer[0...buffer_length]
|
36
|
+
end
|
37
|
+
def devchip_encrypt(data, key_id)
|
38
|
+
devchip_encrypt_decrypt(data, key_id, 0x44)
|
39
|
+
end
|
40
|
+
def devchip_decrypt(data, key_id)
|
41
|
+
devchip_encrypt_decrypt(data, key_id, 0x45)
|
42
|
+
end
|
43
|
+
|
44
|
+
def stat_keys
|
45
|
+
apdu = [0x00, 0x27, 0x01, 0x00, 0x00].flatten
|
46
|
+
response = issue_apdu apdu
|
47
|
+
tem_error(response) if failure_code(response)
|
48
|
+
response = reply_data(response)
|
49
|
+
key_types = {0x99 => :symmetric, 0x55 => :private, 0xAA => :public}
|
50
|
+
stat = {:keys => {}}
|
51
|
+
offset = 0
|
52
|
+
while offset < response.length do
|
53
|
+
stat[:keys][read_tem_ubyte(response, offset)] =
|
54
|
+
{:type => key_types[read_tem_ubyte(response, offset + 1)],
|
55
|
+
:bits => read_tem_ushort(response, offset + 2)}
|
56
|
+
offset += 4
|
57
|
+
end
|
58
|
+
return stat
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Tem::SecAssembler
|
2
|
+
def initialize(tem_klass)
|
3
|
+
@tem_klass = tem_klass
|
4
|
+
@body = []
|
5
|
+
@labels = {}
|
6
|
+
@lines = {}
|
7
|
+
@sp, @ep, @extra_bytes = nil, nil, nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.opcode(name, value, *params)
|
11
|
+
p_hash = {}
|
12
|
+
params.each_index { |i| p_hash[params[i][:name]] = i unless params[i][:name].nil? }
|
13
|
+
|
14
|
+
define_method(name.to_sym) do |*m_params|
|
15
|
+
# linearize the parameters
|
16
|
+
param_idx = 0
|
17
|
+
s_params = []
|
18
|
+
m_params.each_index do |i|
|
19
|
+
if m_params[i].instance_of? Hash
|
20
|
+
raise "no embedded hashes please! (check parameter #{param_idx})" unless i == m_params.length - 1
|
21
|
+
m_params[i].each do |k, v|
|
22
|
+
raise "no parameter with name #{k} for opcode #{name}" if p_hash[k].nil?
|
23
|
+
raise "parameter #{k} was already assigned a value" unless (param_idx <= p_hash[k] and s_params[p_hash[k]].nil?)
|
24
|
+
s_params[p_hash[k]] = v
|
25
|
+
end
|
26
|
+
else
|
27
|
+
s_params[param_idx] = m_params[i]
|
28
|
+
param_idx += 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# check for missing parameters
|
33
|
+
raise "opcode #{name} requires more parameters" unless s_params.length == params.length and s_params.all? { |v| !v.nil? }
|
34
|
+
|
35
|
+
# encode parameters
|
36
|
+
@lines[@body.length] = Kernel.caller(0)
|
37
|
+
@body += @tem_klass.to_tem_ubyte(value)
|
38
|
+
s_params.each_index do |i|
|
39
|
+
if (s_params[i].kind_of? Numeric) && !params[i][:relative]
|
40
|
+
@body += @tem_klass.send "to_tem_#{params[i][:type]}".to_sym, s_params[i]
|
41
|
+
else
|
42
|
+
@body << { :type => params[i][:type], :relative => params[i][:reladdr] ? params[i][:reladdr] : false }.merge!(
|
43
|
+
(s_params[i].kind_of? Numeric) ? { :address => s_params[i].to_i } : { :label => s_params[i].to_sym })
|
44
|
+
@body += (@tem_klass.send "to_tem_#{params[i][:type]}".to_sym, 0)[1..-1]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def assemble(&proc_block)
|
51
|
+
# call the block to build the proc
|
52
|
+
yield self
|
53
|
+
|
54
|
+
# link in label addresses
|
55
|
+
@body.each_index do |i|
|
56
|
+
if @body[i].kind_of? Hash
|
57
|
+
raise "label #{@body[i][:label]} undefined" if (!@body[i][:label].nil? and @labels[@body[i][:label]].nil?)
|
58
|
+
addr = @body[i][:label].nil? ? @body[i][:address] : @labels[@body[i][:label]]
|
59
|
+
q = @body[i][:relative] ? (@tem_klass.send "to_tem_#{@body[i][:type]}_reladdr".to_sym, addr - i - @body[i][:relative]) :
|
60
|
+
(@tem_klass.send "to_tem_#{@body[i][:type]}".to_sym, addr)
|
61
|
+
@body[i, q.length] = *q
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
return Tem::SecPack.new(:tem_class => @tem_klass, :body => @body, :labels => @labels,
|
66
|
+
:ep => @ep || 0, :sp => @sp || @body.length, :extra_bytes => @extra_bytes || 0, :lines => @lines)
|
67
|
+
end
|
68
|
+
|
69
|
+
def label(name)
|
70
|
+
raise "label #{name} already defined" unless @labels[name.to_sym].nil?
|
71
|
+
@labels[name.to_sym] = @body.length
|
72
|
+
end
|
73
|
+
def filler(type_name, count = 1)
|
74
|
+
bytes = count * @tem_klass.send("tem_#{type_name}_length".to_sym)
|
75
|
+
@body += Array.new(bytes, 0)
|
76
|
+
end
|
77
|
+
def immed(type_name, values)
|
78
|
+
values = [values] unless values.instance_of? Array
|
79
|
+
@body += values.map { |v| @tem_klass.send "to_tem_#{type_name}".to_sym, v }.flatten
|
80
|
+
end
|
81
|
+
def entry
|
82
|
+
@ep = @body.length
|
83
|
+
end
|
84
|
+
def stack
|
85
|
+
@sp = @body.length
|
86
|
+
end
|
87
|
+
def extra(extra_bytes)
|
88
|
+
@extra_bytes = extra_bytes
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|