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/test/_test_cert.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Victor Costan:
|
2
|
+
# dropped because it wasn't hooked up to the rest of the code
|
3
|
+
# preserved to move all the features into the new ca.rb / ecert.rb
|
4
|
+
|
5
|
+
require 'tem_ruby'
|
6
|
+
require 'test/unit'
|
7
|
+
require 'openssl'
|
8
|
+
|
9
|
+
# Integration work by Victor Costan
|
10
|
+
|
11
|
+
|
12
|
+
#Author: Jorge de la Garza (MIT '08), mongoose08@alum.mit.edu
|
13
|
+
#This unit test does the following:
|
14
|
+
#1. Makes issuer's (manufacturer's) X.509 certificate, which is self-signed.
|
15
|
+
#2. Makes subject's (TEM's) X.509 certificate, which is signed with the issuers private key.
|
16
|
+
#3. Constructs the TEMTag from the subject's:
|
17
|
+
# -Serial number (4 bytes)
|
18
|
+
# -Not before date (4 bytes)
|
19
|
+
# -Not after date (4 bytes)
|
20
|
+
# -Modulus (256 bytes)
|
21
|
+
# -Public key exp (3 bytes)
|
22
|
+
# -Signature (256 bytes)
|
23
|
+
#4. Sets the TEMTag on the TEM
|
24
|
+
#5. Reads back the TEMTag
|
25
|
+
#6. Constructs a new X.509 certificate from the TEMTag and asserts that this is equal to the original certificate
|
26
|
+
|
27
|
+
class CertTest < Test::Unit::TestCase
|
28
|
+
def setup
|
29
|
+
@terminal = Tem::SCard::JCOPRemoteTerminal.new
|
30
|
+
unless @terminal.connect
|
31
|
+
@terminal.disconnect
|
32
|
+
@terminal = Tem::SCard::PCSCTerminal.new
|
33
|
+
@terminal.connect
|
34
|
+
end
|
35
|
+
@javacard = Tem::SCard::JavaCard.new(@terminal)
|
36
|
+
@tem = Tem::Session.new(@javacard)
|
37
|
+
|
38
|
+
@tem.kill
|
39
|
+
@tem.activate
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def teardown
|
45
|
+
@terminal.disconnect unless @terminal.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_cert
|
49
|
+
#Create issuer's (manufacturer's) certificate
|
50
|
+
issuer_key = OpenSSL::PKey::RSA.new 2048, 0x10001
|
51
|
+
issuer_cert = Tem::Cert.create_issuer_cert(issuer_key)
|
52
|
+
|
53
|
+
#Create subject's (TEM's) certificate
|
54
|
+
subject_key = OpenSSL::PKey::RSA.new 2048, 0x10001
|
55
|
+
subject_cert = Tem::Cert.create_subject_cert(subject_key, issuer_key, issuer_cert)
|
56
|
+
|
57
|
+
#Create the tag that will go on the TEM from it's certificate
|
58
|
+
written_tag = Tem::Cert.create_tag_from_cert(subject_cert)
|
59
|
+
|
60
|
+
#Set the tag on the TEM, assert that tag read = tag written
|
61
|
+
@tem.set_tag(written_tag)
|
62
|
+
read_tag = @tem.get_tag[2..-1] #chop off first two bytes, TEM puts firmware version on front of written tag
|
63
|
+
assert_equal written_tag, read_tag, 'error in posted tag data'
|
64
|
+
|
65
|
+
#Now reconstruct original certificate from tag data
|
66
|
+
read_cert = Tem::Cert.create_cert_from_tag(read_tag, issuer_cert)
|
67
|
+
read_cert.sign issuer_key, OpenSSL::Digest::SHA1.new
|
68
|
+
|
69
|
+
assert_equal Tem::Cert.extract_sig_from_cert(subject_cert), Tem::Cert.extract_sig_from_cert(read_cert), 'signatures do not match'
|
70
|
+
#If the signature of the original certificate matches the signature of the reconstructed certificate,
|
71
|
+
#we can be pretty much certain that the certificates are identical
|
72
|
+
|
73
|
+
#TODO: PROBLEM:
|
74
|
+
#There is no way to set the signature to a known value.
|
75
|
+
#The only way to set the signature is to sign the certificate, and only the issuer (manufacturer) can do this.
|
76
|
+
#This means that the manufacturer has to be contacted every time the user wants to verify the TEM's certificate,
|
77
|
+
#and this may not be practical.
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/test/test_driver.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'tem_ruby'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class DriverTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@terminal = Tem::SCard::JCOPRemoteTerminal.new
|
7
|
+
unless @terminal.connect
|
8
|
+
@terminal.disconnect
|
9
|
+
@terminal = Tem::SCard::PCSCTerminal.new
|
10
|
+
@terminal.connect
|
11
|
+
end
|
12
|
+
@javacard = Tem::SCard::JavaCard.new(@terminal)
|
13
|
+
@tem = Tem::Session.new(@javacard)
|
14
|
+
|
15
|
+
@tem.kill
|
16
|
+
@tem.activate
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
@terminal.disconnect unless @terminal.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_buffers_io
|
24
|
+
garbage = (1...569).map { |i| (i * i * 217 + i * 661 + 393) % 256 }
|
25
|
+
|
26
|
+
bid = @tem.post_buffer garbage
|
27
|
+
assert_equal garbage.length, @tem.get_buffer_length(bid), 'error in posted buffer length'
|
28
|
+
assert_equal garbage, @tem.read_buffer(bid), 'error in posted buffer data'
|
29
|
+
|
30
|
+
garbage.reverse!
|
31
|
+
@tem.write_buffer bid, garbage
|
32
|
+
assert_equal garbage, @tem.read_buffer(bid), 'error in (reverted) posted buffer data'
|
33
|
+
@tem.release_buffer bid
|
34
|
+
|
35
|
+
@tem.post_buffer [1]
|
36
|
+
@tem.post_buffer [2]
|
37
|
+
@tem.flush_buffers
|
38
|
+
assert_equal 0, @tem.stat_buffers[:buffers].reject { |b| b[:free] }.length, 'flush_buffers left allocated buffers'
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_buffers_alloc
|
42
|
+
b_lengths = [569, 231, 455, 18, 499, 332, 47]
|
43
|
+
b_ids = b_lengths.map { |len| @tem.alloc_buffer(len) }
|
44
|
+
bstat = @tem.stat_buffers
|
45
|
+
|
46
|
+
assert bstat[:free], 'buffer stat does not contain free memory information'
|
47
|
+
assert bstat[:free][:persistent].kind_of?(Numeric), 'buffer stat does not show free persistent memory'
|
48
|
+
assert bstat[:free][:persistent] >= 0, 'buffer stat shows negative free persistent memory'
|
49
|
+
assert bstat[:free][:clear_on_reset].kind_of?(Numeric), 'buffer stat does not show free clear_on_reset memory'
|
50
|
+
assert bstat[:free][:clear_on_reset] >= 0, 'buffer stat shows negative free clear_on_reset memory'
|
51
|
+
assert bstat[:free][:clear_on_deselect].kind_of?(Numeric), 'buffer stat does not show free clear_on_deselect memory'
|
52
|
+
assert bstat[:free][:clear_on_deselect] >= 0, 'buffer stat shows negative free clear_on_deselect memory'
|
53
|
+
|
54
|
+
b_lengths.each_index do |i|
|
55
|
+
assert bstat[:buffers][b_ids[i]], "buffer stat does not show an entry for a #{b_lengths[i]}-bytes buffer"
|
56
|
+
assert bstat[:buffers][b_ids[i]][:type].kind_of?(Symbol), "buffer stat does not show the memory type for a #{b_lengths[i]}-bytes buffer"
|
57
|
+
assert_equal b_lengths[i], bstat[:buffers][b_ids[i]][:length], "bad length in buffer stat entry for a #{b_lengths[i]}-bytes buffer"
|
58
|
+
assert_equal false, bstat[:buffers][b_ids[i]][:pinned], "bad pinned flag in buffer stat entry for a #{b_lengths[i]}-bytes buffer"
|
59
|
+
end
|
60
|
+
|
61
|
+
b_ids.each { |bid| @tem.release_buffer(bid) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_tag
|
65
|
+
garbage = (1...569).map { |i| (i * i * 217 + i * 661 + 393) % 256 }
|
66
|
+
|
67
|
+
assert_raise(RuntimeError, 'tag returned before being set') { @tem.get_tag }
|
68
|
+
|
69
|
+
@tem.set_tag(garbage)
|
70
|
+
assert_equal garbage, @tem.get_tag[2..-1], 'error in posted tag data'
|
71
|
+
|
72
|
+
fwver = @tem.tk_firmware_ver
|
73
|
+
assert fwver[:major].kind_of?(Numeric) && fwver[:minor].kind_of?(Numeric), 'error in tag-backed firmware version'
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_crypto
|
77
|
+
garbage = (1...415).map { |i| (i * i * 217 + i * 661 + 393) % 256 }
|
78
|
+
key_pair = @tem.devchip_generate_key_pair
|
79
|
+
pubkey = @tem.devchip_save_key key_pair[:pubkey_id]
|
80
|
+
|
81
|
+
encrypted_garbage = @tem.devchip_encrypt garbage, key_pair[:privkey_id]
|
82
|
+
decrypted_garbage = pubkey.decrypt encrypted_garbage
|
83
|
+
assert_equal garbage, decrypted_garbage, 'priv-encryption+pub-decryption messed up the data'
|
84
|
+
|
85
|
+
encrypted_garbage = pubkey.encrypt garbage
|
86
|
+
decrypted_garbage = @tem.devchip_decrypt encrypted_garbage, key_pair[:privkey_id]
|
87
|
+
assert_equal garbage, decrypted_garbage, 'pub-encryption+priv-decryption messed up the data'
|
88
|
+
|
89
|
+
key_stat = @tem.stat_keys
|
90
|
+
assert key_stat[:keys], 'key stat does not contain key information'
|
91
|
+
assert_equal :public, key_stat[:keys][key_pair[:pubkey_id]][:type], 'key stat reports wrong type for public key'
|
92
|
+
assert_equal :private, key_stat[:keys][key_pair[:privkey_id]][:type], 'key stat reports wrong type for private key'
|
93
|
+
assert_in_delta 2, 2048, key_stat[:keys][key_pair[:pubkey_id]][:bits], 'key stat reports wrong size for public key'
|
94
|
+
assert_in_delta 2, 2048, key_stat[:keys][key_pair[:privkey_id]][:bits], 'key stat reports wrong size for private key'
|
95
|
+
|
96
|
+
[:pubkey_id, :privkey_id].each { |ki| @tem.devchip_release_key key_pair[ki] }
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_crypto_abi
|
100
|
+
ekey = OpenSSL::PKey::RSA.generate(2048, 65537)
|
101
|
+
pubk = @tem.new_key_from_ssl ekey, true
|
102
|
+
privk = @tem.new_key_from_ssl ekey, false
|
103
|
+
|
104
|
+
# array and string encryption/decryption
|
105
|
+
garbage = (1...569).map { |i| (i * i * 217 + i * 661 + 393) % 256 }
|
106
|
+
[garbage, garbage.pack('C*')].each do |g|
|
107
|
+
encrypted_garbage = pubk.encrypt g
|
108
|
+
decrypted_garbage = privk.decrypt encrypted_garbage
|
109
|
+
assert_equal g, decrypted_garbage, 'pub-encryption+priv-decryption messed up the data'
|
110
|
+
encrypted_garbage = privk.encrypt g
|
111
|
+
decrypted_garbage = pubk.decrypt encrypted_garbage
|
112
|
+
assert_equal g, decrypted_garbage, 'priv-encryption+pub-decryption messed up the data'
|
113
|
+
end
|
114
|
+
|
115
|
+
# test key serialization/deserialization through encryption/decryption
|
116
|
+
pubk_ys = pubk.to_yaml_str
|
117
|
+
pubk2 = Tem::CryptoAbi::AsymmetricKey.new_from_yaml_str(pubk_ys)
|
118
|
+
privk_ys = privk.to_yaml_str
|
119
|
+
privk2 = Tem::CryptoAbi::AsymmetricKey.new_from_yaml_str(privk_ys)
|
120
|
+
encrypted_garbage = pubk.encrypt garbage
|
121
|
+
decrypted_garbage = privk2.decrypt encrypted_garbage
|
122
|
+
assert_equal garbage, decrypted_garbage, 'pub-encryption+priv-decryption messed up the data'
|
123
|
+
encrypted_garbage = privk.encrypt garbage
|
124
|
+
decrypted_garbage = pubk2.decrypt encrypted_garbage
|
125
|
+
assert_equal garbage, decrypted_garbage, 'priv-encryption+pub-decryption messed up the data'
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'tem_ruby'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class ExceptionsTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@terminal = Tem::SCard::JCOPRemoteTerminal.new
|
7
|
+
unless @terminal.connect
|
8
|
+
@terminal.disconnect
|
9
|
+
@terminal = Tem::SCard::PCSCTerminal.new
|
10
|
+
@terminal.connect
|
11
|
+
end
|
12
|
+
@javacard = Tem::SCard::JavaCard.new(@terminal)
|
13
|
+
@tem = Tem::Session.new(@javacard)
|
14
|
+
|
15
|
+
@tem.kill
|
16
|
+
@tem.activate
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
@terminal.disconnect unless @terminal.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_trace
|
24
|
+
# test the exception handling mechanism
|
25
|
+
bad_sec = @tem.assemble { |s|
|
26
|
+
s.ldbc 2
|
27
|
+
s.outnew
|
28
|
+
s.ldbc 6
|
29
|
+
s.outw
|
30
|
+
# this exceeds the address space, so it should make the TEM die
|
31
|
+
s.ldwc 0x7fff
|
32
|
+
s.ldbv
|
33
|
+
s.label :bad_code
|
34
|
+
s.halt
|
35
|
+
s.label :stack
|
36
|
+
s.stack
|
37
|
+
s.extra 10
|
38
|
+
}
|
39
|
+
assert_raise(Tem::SecExecError) { @tem.execute bad_sec }
|
40
|
+
|
41
|
+
caught = false
|
42
|
+
begin
|
43
|
+
@tem.execute bad_sec
|
44
|
+
rescue Tem::SecExecError => e
|
45
|
+
caught = true
|
46
|
+
assert_equal Hash, e.trace.class, "TEM exception does not have a TEM trace"
|
47
|
+
assert_equal 2, e.trace[:out], "Bad output buffer position in TEM trace"
|
48
|
+
assert_equal bad_sec.label_address(:bad_code), e.trace[:ip], "Bad instruction pointer in TEM trace"
|
49
|
+
assert_equal bad_sec.label_address(:stack), e.trace[:sp], "Bad instruction pointer in TEM trace"
|
50
|
+
assert_equal Hash, e.buffer_state.class, "TEM exception does not have buffer state information"
|
51
|
+
assert_equal Hash, e.key_state.class, "TEM exception does not have key state information"
|
52
|
+
end
|
53
|
+
assert caught, "Executing a bad SECpack did not raise a SecExecError"
|
54
|
+
end
|
55
|
+
end
|