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/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
|