tem_ruby 0.9.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 +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
|
+
|