tem_ruby 0.10.1 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Manifest +17 -3
- data/lib/tem/apdus/buffers.rb +1 -2
- data/lib/tem/builders/abi.rb +2 -2
- data/lib/tem/builders/assembler.rb +314 -0
- data/lib/tem/builders/crypto.rb +17 -8
- data/lib/tem/builders/isa.rb +120 -0
- data/lib/tem/definitions/abi.rb +8 -10
- data/lib/tem/definitions/assembler.rb +23 -0
- data/lib/tem/definitions/isa.rb +188 -0
- data/lib/tem/ecert.rb +15 -16
- data/lib/tem/sec_exec_error.rb +21 -3
- data/lib/tem/seclosures.rb +9 -5
- data/lib/tem/secpack.rb +50 -30
- data/lib/tem/toolkit.rb +11 -15
- data/lib/tem/transport/jcop_remote_protocol.rb +10 -2
- data/lib/tem_ruby.rb +4 -2
- data/tem_ruby.gemspec +5 -5
- data/test/tem_unit/test_tem_alu.rb +33 -0
- data/test/tem_unit/test_tem_bound_secpack.rb +51 -0
- data/test/tem_unit/test_tem_branching.rb +56 -0
- data/test/tem_unit/test_tem_crypto_asymmetric.rb +123 -0
- data/test/tem_unit/test_tem_crypto_hash.rb +35 -0
- data/test/tem_unit/test_tem_crypto_pstore.rb +53 -0
- data/test/tem_unit/test_tem_crypto_random.rb +25 -0
- data/test/tem_unit/test_tem_emit.rb +23 -0
- data/test/tem_unit/test_tem_memory.rb +48 -0
- data/test/tem_unit/test_tem_memory_compare.rb +65 -0
- data/test/tem_unit/test_tem_output.rb +32 -0
- data/test/tem_unit/test_tem_yaml_secpack.rb +47 -0
- data/test/test_exceptions.rb +1 -2
- data/timings/blank_bound_secpack.rb +3 -5
- data/timings/blank_sec.rb +2 -3
- data/timings/timings.rb +7 -2
- data/timings/vm_perf.rb +9 -10
- data/timings/vm_perf_bound.rb +9 -10
- metadata +35 -9
- data/lib/tem/sec_assembler.rb +0 -90
- data/lib/tem/sec_opcodes.rb +0 -154
- data/test/test_tem.rb +0 -524
data/CHANGELOG
CHANGED
data/Manifest
CHANGED
@@ -15,17 +15,19 @@ lib/tem/apdus/lifecycle.rb
|
|
15
15
|
lib/tem/apdus/tag.rb
|
16
16
|
lib/tem/auto_conf.rb
|
17
17
|
lib/tem/builders/abi.rb
|
18
|
+
lib/tem/builders/assembler.rb
|
18
19
|
lib/tem/builders/crypto.rb
|
20
|
+
lib/tem/builders/isa.rb
|
19
21
|
lib/tem/ca.rb
|
20
22
|
lib/tem/definitions/abi.rb
|
23
|
+
lib/tem/definitions/assembler.rb
|
24
|
+
lib/tem/definitions/isa.rb
|
21
25
|
lib/tem/ecert.rb
|
22
26
|
lib/tem/hive.rb
|
23
27
|
lib/tem/keys/asymmetric.rb
|
24
28
|
lib/tem/keys/key.rb
|
25
29
|
lib/tem/keys/symmetric.rb
|
26
|
-
lib/tem/sec_assembler.rb
|
27
30
|
lib/tem/sec_exec_error.rb
|
28
|
-
lib/tem/sec_opcodes.rb
|
29
31
|
lib/tem/seclosures.rb
|
30
32
|
lib/tem/secpack.rb
|
31
33
|
lib/tem/tem.rb
|
@@ -42,12 +44,24 @@ LICENSE
|
|
42
44
|
Manifest
|
43
45
|
Rakefile
|
44
46
|
README
|
47
|
+
tem_ruby.gemspec
|
45
48
|
test/_test_cert.rb
|
46
49
|
test/builders/test_abi_builder.rb
|
47
50
|
test/tem_test_case.rb
|
51
|
+
test/tem_unit/test_tem_alu.rb
|
52
|
+
test/tem_unit/test_tem_bound_secpack.rb
|
53
|
+
test/tem_unit/test_tem_branching.rb
|
54
|
+
test/tem_unit/test_tem_crypto_asymmetric.rb
|
55
|
+
test/tem_unit/test_tem_crypto_hash.rb
|
56
|
+
test/tem_unit/test_tem_crypto_pstore.rb
|
57
|
+
test/tem_unit/test_tem_crypto_random.rb
|
58
|
+
test/tem_unit/test_tem_emit.rb
|
59
|
+
test/tem_unit/test_tem_memory.rb
|
60
|
+
test/tem_unit/test_tem_memory_compare.rb
|
61
|
+
test/tem_unit/test_tem_output.rb
|
62
|
+
test/tem_unit/test_tem_yaml_secpack.rb
|
48
63
|
test/test_driver.rb
|
49
64
|
test/test_exceptions.rb
|
50
|
-
test/test_tem.rb
|
51
65
|
test/transport/test_auto_configurator.rb
|
52
66
|
test/transport/test_java_card_mixin.rb
|
53
67
|
test/transport/test_jcop_remote.rb
|
data/lib/tem/apdus/buffers.rb
CHANGED
@@ -41,8 +41,7 @@ module Buffers
|
|
41
41
|
|
42
42
|
chunk_id, offset = 0, 0
|
43
43
|
while offset < data.length do
|
44
|
-
write_size =
|
45
|
-
data.length - offset : @buffer_chunk_size
|
44
|
+
write_size = [data.length - offset, @buffer_chunk_size].min
|
46
45
|
@transport.applet_apdu! :ins => 0x24, :p1 => buffer_id, :p2 => chunk_id,
|
47
46
|
:data => data[offset, write_size]
|
48
47
|
chunk_id += 1
|
data/lib/tem/builders/abi.rb
CHANGED
@@ -4,11 +4,11 @@ require 'openssl'
|
|
4
4
|
# :nodoc: namespace
|
5
5
|
module Tem::Builders
|
6
6
|
|
7
|
-
# Builder class
|
7
|
+
# Builder class for the ABI (Abstract Binary Interface) builder.
|
8
8
|
class Abi
|
9
9
|
# Creates a builder targeting a module / class.
|
10
10
|
#
|
11
|
-
# The given parameter should be a class or module
|
11
|
+
# The given parameter should be a class or module.
|
12
12
|
def self.define_abi(class_or_module) # :yields: abi
|
13
13
|
yield new(class_or_module)
|
14
14
|
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
# :nodoc: namespace
|
2
|
+
module Tem::Builders
|
3
|
+
|
4
|
+
# Builder class for the code assembler builder.
|
5
|
+
class Assembler
|
6
|
+
# Creates a builder targeting a module / class.
|
7
|
+
#
|
8
|
+
# The given parameter should be a class or module.
|
9
|
+
def self.define_assembler(class_or_module) # :yields: abi
|
10
|
+
yield new(class_or_module)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Defines the ISA targeted by the assembler.
|
14
|
+
#
|
15
|
+
# This method should be called early in the assembler definition. It creates
|
16
|
+
# the proxy and builder classes for the assembling process.
|
17
|
+
def target_isa(isa_module)
|
18
|
+
@isa = isa_module
|
19
|
+
target.const_set :Isa, @isa
|
20
|
+
@abi = @isa.const_get :Abi
|
21
|
+
target.const_set :Abi, @abi
|
22
|
+
|
23
|
+
define_proxy_class
|
24
|
+
define_builder_class
|
25
|
+
augment_target
|
26
|
+
end
|
27
|
+
|
28
|
+
# Defines the methods for implementing a stack directive.
|
29
|
+
#
|
30
|
+
# The following options are supported:
|
31
|
+
# label:: the label serving as the stack marker (required)
|
32
|
+
# slot_type:: the ABI type representing a stack slot
|
33
|
+
#
|
34
|
+
# The following method is defined in the proxy for a directive named 'name':
|
35
|
+
# * name(slots = 0) -> places a stack marker and allocates stack slots
|
36
|
+
def stack_directive(name, options)
|
37
|
+
unless @proxy_class
|
38
|
+
raise "target_isa must be called before other builder methods"
|
39
|
+
end
|
40
|
+
# Capture these in the closure.
|
41
|
+
stack_label = options[:label]
|
42
|
+
slot_length = @abi.send :"#{options[:slot_type]}_length"
|
43
|
+
proxy_defines = Proc.new do
|
44
|
+
define_method name.to_sym do |*args|
|
45
|
+
case args.length
|
46
|
+
when 0
|
47
|
+
slots = 0
|
48
|
+
when 1
|
49
|
+
slots = args.first
|
50
|
+
else
|
51
|
+
raise "#{name}: given #{args.length} arguments, wanted at most 1"
|
52
|
+
end
|
53
|
+
@assembler.emit_label stack_label
|
54
|
+
if slots > 0
|
55
|
+
@assembler.emit_bytes name, :emit => Array.new(slots * slot_length, 0)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
@proxy_class.class_eval &proxy_defines
|
60
|
+
(class << @proxy_class; self; end).module_eval &proxy_defines
|
61
|
+
end
|
62
|
+
|
63
|
+
# Defines the methods for implementing a labeling directive.
|
64
|
+
#
|
65
|
+
# The following method is defined in the proxy for a directive named 'name':
|
66
|
+
# * name(label_name) -> creates a label named label_name at the current byte
|
67
|
+
def label_directive(name, options = {})
|
68
|
+
unless @proxy_class
|
69
|
+
raise "target_isa must be called before other builder methods"
|
70
|
+
end
|
71
|
+
proxy_defines = Proc.new do
|
72
|
+
define_method name.to_sym do |label_name|
|
73
|
+
@assembler.emit_label label_name.to_sym
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@proxy_class.class_eval &proxy_defines
|
77
|
+
(class << @proxy_class; self; end).module_eval &proxy_defines
|
78
|
+
end
|
79
|
+
|
80
|
+
# Defines the methods for implementing a special label directive.
|
81
|
+
#
|
82
|
+
# The following method is defined in the proxy for a directive named 'name':
|
83
|
+
# * name -> creates a label named label_name at the current byte
|
84
|
+
def special_label_directive(name, label_name)
|
85
|
+
unless @proxy_class
|
86
|
+
raise "target_isa must be called before other builder methods"
|
87
|
+
end
|
88
|
+
proxy_defines = Proc.new do
|
89
|
+
define_method name.to_sym do
|
90
|
+
@assembler.emit_label label_name.to_sym
|
91
|
+
end
|
92
|
+
end
|
93
|
+
@proxy_class.class_eval &proxy_defines
|
94
|
+
(class << @proxy_class; self; end).module_eval &proxy_defines
|
95
|
+
end
|
96
|
+
|
97
|
+
# Defines the methods for implementing a zero-inserting directive.
|
98
|
+
#
|
99
|
+
# The following method is defined in the proxy for a directive named 'name':
|
100
|
+
# * name(abi_type, count = 1) -> creates count zeros of abi_type
|
101
|
+
def zeros_directive(name, options = {})
|
102
|
+
unless @proxy_class
|
103
|
+
raise "target_isa must be called before other builder methods"
|
104
|
+
end
|
105
|
+
# Capture this in the closure.
|
106
|
+
abi = @abi
|
107
|
+
proxy_defines = Proc.new do
|
108
|
+
define_method name.to_sym do |*args|
|
109
|
+
if args.length == 1 || args.length == 2
|
110
|
+
type_name, count = args[0], args[1] || 1
|
111
|
+
else
|
112
|
+
raise "#{name}: given #{args.length} arguments, wanted 1 or 2"
|
113
|
+
end
|
114
|
+
bytes = count * abi.send(:"#{type_name}_length")
|
115
|
+
@assembler.emit_bytes name, :emit => Array.new(bytes, 0)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@proxy_class.class_eval &proxy_defines
|
119
|
+
(class << @proxy_class; self; end).module_eval &proxy_defines
|
120
|
+
end
|
121
|
+
|
122
|
+
# Defines the methods for implementing a data-emitting directive.
|
123
|
+
#
|
124
|
+
# The following method is defined in the proxy for a directive named 'name':
|
125
|
+
# * name(abi_type, values = 1) -> emits the given values as abi_type
|
126
|
+
def data_directive(name, options = {})
|
127
|
+
unless @proxy_class
|
128
|
+
raise "target_isa must be called before other builder methods"
|
129
|
+
end
|
130
|
+
# Capture this in the closure.
|
131
|
+
abi = @abi
|
132
|
+
proxy_defines = Proc.new do
|
133
|
+
define_method name.to_sym do |abi_type, values|
|
134
|
+
values = [values] unless values.instance_of? Array
|
135
|
+
data = []
|
136
|
+
values.each { |value| data += abi.send :"to_#{abi_type}", value }
|
137
|
+
@assembler.emit_bytes :immed, :emit => data
|
138
|
+
end
|
139
|
+
end
|
140
|
+
@proxy_class.class_eval &proxy_defines
|
141
|
+
(class << @proxy_class; self; end).module_eval &proxy_defines
|
142
|
+
end
|
143
|
+
|
144
|
+
# (private) Defines the builder class used during assembly.
|
145
|
+
#
|
146
|
+
# Builders maintain intermediate results during the assembly process. In a
|
147
|
+
# nutshell, a builder collects the bytes, labels and linker directives, and
|
148
|
+
# puts them together at the end of the assembly process.
|
149
|
+
#
|
150
|
+
# Builder classes are synthesized automatically if they don't already exist.
|
151
|
+
# To have a builder class inherit from another class, define it before calling
|
152
|
+
# target_isa. Builder classes are saved as the Builder constant in the
|
153
|
+
# assembler class.
|
154
|
+
#
|
155
|
+
# Builder classes are injected the code in Assembler::CodeBuilderBase. Look
|
156
|
+
# there for override hooks into the assembly process.
|
157
|
+
def define_builder_class
|
158
|
+
if @target.const_defined? :Builder
|
159
|
+
@builder_class = @taget.const_get :Builder
|
160
|
+
else
|
161
|
+
@builder_class = Class.new
|
162
|
+
@target.const_set :Builder, @builder_class
|
163
|
+
end
|
164
|
+
@builder_class.send :include, Assembler::CodeBuilderBase
|
165
|
+
end
|
166
|
+
private :define_builder_class
|
167
|
+
|
168
|
+
# (private) Defines the proxy class used during assembly.
|
169
|
+
#
|
170
|
+
# The proxy class is yielded to the block given to the assemble call, which
|
171
|
+
# provides the code to be assembled. For clarity, the proxy class is
|
172
|
+
# synthesized so it only contains the methods that are useful for assembly.
|
173
|
+
#
|
174
|
+
# The proxy class is always automatically synthesized, and is available under
|
175
|
+
# the constant Proxy in the assembler class.
|
176
|
+
def define_proxy_class
|
177
|
+
@proxy_class = Class.new Assembler::ProxyBase
|
178
|
+
target.const_set :Proxy, @proxy_class
|
179
|
+
|
180
|
+
@proxy_class.const_set :Abi, @abi
|
181
|
+
@proxy_class.const_set :Isa, @isa
|
182
|
+
|
183
|
+
# Capture the ISA and ABI in the closure.
|
184
|
+
isa = @isa
|
185
|
+
proxy_defines = Proc.new do
|
186
|
+
isa.instance_methods.each do |method|
|
187
|
+
if method[0, 5] == 'emit_'
|
188
|
+
isa_method_msg = method.to_sym
|
189
|
+
proxy_method_name = method[5, method.length].to_sym
|
190
|
+
define_method proxy_method_name do |*args|
|
191
|
+
emit_data = isa.send isa_method_msg, *args
|
192
|
+
@assembler.emit_bytes proxy_method_name, emit_data
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
@proxy_class.class_eval &proxy_defines
|
198
|
+
(class << @proxy_class; self; end).module_eval &proxy_defines
|
199
|
+
end
|
200
|
+
private :define_proxy_class
|
201
|
+
|
202
|
+
# (private) Augments the target with the assemble method.
|
203
|
+
def augment_target
|
204
|
+
# Capture this data in the closure.
|
205
|
+
proxy_class = @proxy_class
|
206
|
+
builder_class = @builder_class
|
207
|
+
defines = Proc.new do
|
208
|
+
# Assembles code.
|
209
|
+
def assemble(&block)
|
210
|
+
_assemble block
|
211
|
+
end
|
212
|
+
|
213
|
+
# Internal method for assembling code.
|
214
|
+
#
|
215
|
+
# We need to use a block to define this method, so we can capture the
|
216
|
+
# outer variables (proxy_class and builder_class) in its closure. However,
|
217
|
+
# blocks can't take blocks as parameters (at least not in MRI 1.8). So
|
218
|
+
# we use a regular method definition (assemble) which wraps the block
|
219
|
+
# into a Proc received by _assemble.
|
220
|
+
define_method :_assemble do |block|
|
221
|
+
code_builder = builder_class.new
|
222
|
+
code_builder.start_assembling
|
223
|
+
proxy = proxy_class.new(code_builder)
|
224
|
+
block.call proxy
|
225
|
+
code_builder.done_assembling proxy
|
226
|
+
end
|
227
|
+
private :_assemble
|
228
|
+
end
|
229
|
+
@target.class_eval &defines
|
230
|
+
(class << @target; self; end).module_eval &defines
|
231
|
+
end
|
232
|
+
|
233
|
+
# The module / class impacted by the builder.
|
234
|
+
attr_reader :target
|
235
|
+
|
236
|
+
# Creates a builder targeting a module / class.
|
237
|
+
def initialize(target)
|
238
|
+
@target = target
|
239
|
+
@isa, @abi, @proxy = nil, nil, nil
|
240
|
+
end
|
241
|
+
private_class_method :new
|
242
|
+
end # class Assembler
|
243
|
+
|
244
|
+
|
245
|
+
# Base class for the assemblers' proxy objects.
|
246
|
+
#
|
247
|
+
# The proxy object is the object that is yielded out of an assembler's
|
248
|
+
# assemble class method.
|
249
|
+
class Assembler::ProxyBase
|
250
|
+
def initialize(assembler)
|
251
|
+
@assembler = assembler
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# Module injected into the assembler's code builder class.
|
256
|
+
module Assembler::CodeBuilderBase
|
257
|
+
# Called by assemble before its associated block receives control.
|
258
|
+
#
|
259
|
+
# This method is responsible for setting up the state needed by the emit_
|
260
|
+
# methods.
|
261
|
+
def start_assembling
|
262
|
+
@bytes = []
|
263
|
+
@link_directives = []
|
264
|
+
@line_info = []
|
265
|
+
@labels = {}
|
266
|
+
end
|
267
|
+
|
268
|
+
# Emits code or data bytes, with associated link directives.
|
269
|
+
def emit_bytes(emit_atom_name, emit_data)
|
270
|
+
(emit_data[:link_directives] || []).each do |directive|
|
271
|
+
directive[:offset] += @bytes.length
|
272
|
+
@link_directives << directive
|
273
|
+
end
|
274
|
+
|
275
|
+
emit_data[:emit] ||= []
|
276
|
+
if emit_data[:emit].length > 0
|
277
|
+
@line_info << [@bytes.length, emit_atom_name, Kernel.caller(2)]
|
278
|
+
end
|
279
|
+
@bytes += emit_data[:emit] || []
|
280
|
+
end
|
281
|
+
|
282
|
+
# Emits labels, which are symbolic names for addresses.
|
283
|
+
def emit_label(label_name)
|
284
|
+
raise "label #{label_name} already defined" if @labels[label_name]
|
285
|
+
@labels[label_name] = @bytes.length
|
286
|
+
end
|
287
|
+
|
288
|
+
# Called by assemble after its associated block returns.
|
289
|
+
#
|
290
|
+
# This method is responsible for the final assembly steps (i.e. linking) and
|
291
|
+
# returning a processed result. The method's result is returned by assemble.
|
292
|
+
def done_assembling(proxy)
|
293
|
+
# Process link directives.
|
294
|
+
abi = proxy.class.const_get :Abi
|
295
|
+
@link_directives.each do |directive|
|
296
|
+
if label = directive[:label]
|
297
|
+
raise "Label #{label} undefined" unless address = @labels[label]
|
298
|
+
else
|
299
|
+
address = directive[:address]
|
300
|
+
end
|
301
|
+
if directive[:relative]
|
302
|
+
address -= directive[:offset] + directive[:relative]
|
303
|
+
end
|
304
|
+
address_bytes = abi.send :"signed_to_#{directive[:type]}", address
|
305
|
+
@bytes[directive[:offset], address_bytes.length] = *address_bytes
|
306
|
+
end
|
307
|
+
|
308
|
+
# Wrap all the built data into a nice package and return it.
|
309
|
+
{ :bytes => @bytes, :link_directives => @link_direcives, :labels => @labels,
|
310
|
+
:line_info => @line_info }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
end # namespace Tem::Builders
|
data/lib/tem/builders/crypto.rb
CHANGED
@@ -28,18 +28,25 @@ class Crypto < Abi
|
|
28
28
|
# * public_name_class -> Class
|
29
29
|
def asymmetric_key(name, ssl_class, privkey_abi_type, pubkey_abi_type,
|
30
30
|
hooks = {})
|
31
|
-
object_wrapper "private_#{name}",
|
32
|
-
|
33
|
-
:
|
31
|
+
object_wrapper "private_#{name}", Tem::Keys::Asymmetric,
|
32
|
+
[privkey_abi_type, nil],
|
33
|
+
:read => hooks[:read_private] || hooks[:read] ||
|
34
|
+
lambda { |k| Tem::Keys::Asymmetric.new k },
|
35
|
+
:to => hooks[:to_private] || hooks[:to] ||
|
36
|
+
lambda { |k| k.ssl_key },
|
34
37
|
:new => hooks[:new_private] || hooks[:new] ||
|
35
38
|
lambda { |k| ssl_class.new }
|
36
|
-
object_wrapper "public_#{name}",
|
37
|
-
|
38
|
-
:
|
39
|
+
object_wrapper "public_#{name}", Tem::Keys::Asymmetric,
|
40
|
+
[pubkey_abi_type, nil],
|
41
|
+
:read => hooks[:read_public] || hooks[:read] ||
|
42
|
+
lambda { |k| Tem::Keys::Asymmetric.new k },
|
43
|
+
:to => hooks[:to_public] || hooks[:to] ||
|
44
|
+
lambda { |k| k.ssl_key },
|
39
45
|
:new => hooks[:new_private] || hooks[:new] ||
|
40
46
|
lambda { |k| ssl_class.new }
|
41
47
|
end
|
42
48
|
|
49
|
+
|
43
50
|
# Defines the methods for a symmetric key.
|
44
51
|
#
|
45
52
|
# cipher_class should be a class in OpenSSL::Cipher. key_abi_type should be
|
@@ -50,9 +57,11 @@ class Crypto < Abi
|
|
50
57
|
# * to_name(object) -> array
|
51
58
|
# * name_class -> Class
|
52
59
|
def symmetric_key(name, cipher_class, cipher_name, key_abi_type, hooks = {})
|
53
|
-
object_wrapper name,
|
60
|
+
object_wrapper name, Tem::Keys::Symmetric, [key_abi_type, :key],
|
61
|
+
:read => lambda { |k| Tem::Keys::Symmetric.new k },
|
62
|
+
:to => lambda { |k| k.ssl_key },
|
54
63
|
:new => lambda { |klass|
|
55
|
-
k =
|
64
|
+
k = cipher_class cipher_name
|
56
65
|
|
57
66
|
unless k.respond_to? :key
|
58
67
|
# Some ciphers don't give back the key that they receive.
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
|
4
|
+
# :nodoc: namespace
|
5
|
+
module Tem::Builders
|
6
|
+
|
7
|
+
# Builder class for the ISA (Instruction Set Architecture) builder.
|
8
|
+
class Isa
|
9
|
+
# Creates a builder targeting a module / class.
|
10
|
+
#
|
11
|
+
# class_or_module will receive the ISA method definitions. abi should be a
|
12
|
+
# class or module containing the ABI definitions that the ISA definitions
|
13
|
+
# refer to.
|
14
|
+
#
|
15
|
+
# The following options are supported:
|
16
|
+
# opcode_type:: the ABI type encoding the instructions' opcodes (required)
|
17
|
+
def self.define_isa(class_or_module, abi, options) # :yields: isa
|
18
|
+
yield new(class_or_module, abi, options[:opcode_type])
|
19
|
+
end
|
20
|
+
|
21
|
+
# Defines the methods for handling an instruction in the IA.
|
22
|
+
#
|
23
|
+
# The instruction's arguments are provided as an array of hashes. Each hash
|
24
|
+
# describes one argument, and the ordering in the array reflects the encoding
|
25
|
+
# order. The following hash keys are supported:
|
26
|
+
# name:: if defined, the argument can be provided as a named argument;
|
27
|
+
# named arguments must follow positional arguments
|
28
|
+
# type:: the ABI type encoding the argument (required)
|
29
|
+
# reladdr:: if defined, the encoded argument value is relative to the
|
30
|
+
# address of the instruction containing the argument;
|
31
|
+
#
|
32
|
+
# The result of encoding an instruction is a Hash with the following keys:
|
33
|
+
# emit:: the bytes to be emitted into the code stream
|
34
|
+
# link_directives:: an array of directives for the code linker
|
35
|
+
#
|
36
|
+
# Each linker directive refers to an address cell (location in the code
|
37
|
+
# representing an address that the linker must adjust. The following keys can
|
38
|
+
# be present:
|
39
|
+
# type:: the ABI type for the address cell (required)
|
40
|
+
# offset:: the address cell's offset in the emitted bytes (required)
|
41
|
+
# address:: the absolute address to point to (mutually exclusive with label)
|
42
|
+
# label:: the name of a label that the address must point to
|
43
|
+
# relative:: if false, the address cell holds an absolute address;
|
44
|
+
# otherwise, the cell's value is computed as follows:
|
45
|
+
# target address - cell address + value of relative;
|
46
|
+
# (optional, default value is false)
|
47
|
+
# The following methods are defined for a type named 'name':
|
48
|
+
# * encode_name(*arguments) -> Hash
|
49
|
+
def instruction(opcode, name, *iargs)
|
50
|
+
encoded_opcode = @abi.send :"to_#{@opcode_type}", opcode
|
51
|
+
abi = @abi # Capture the ABI in the method closures.
|
52
|
+
named_indexes = {}
|
53
|
+
iargs.map { |iarg| iarg[:name] }.
|
54
|
+
each_with_index { |argname, i| named_indexes[argname] = i if argname }
|
55
|
+
arg_encode_msgs = iargs.map { |iarg| :"to_#{iarg[:type]}" }
|
56
|
+
defines = Proc.new do
|
57
|
+
define_method :"emit_#{name}" do |*args|
|
58
|
+
# Flatten arguments by resolving named parameters to indexes.
|
59
|
+
arg_index = 0
|
60
|
+
fargs = []
|
61
|
+
args.each_with_index do |arg, i|
|
62
|
+
fargs[i] = arg and next unless arg.kind_of? Hash
|
63
|
+
|
64
|
+
if i != args.length - 1
|
65
|
+
raise "Named arguments must follow inline arguments! (arg #{i})"
|
66
|
+
end
|
67
|
+
arg.each do |k, v|
|
68
|
+
raise "#{name} has no #{k} argument" unless i = named_indexes[k]
|
69
|
+
raise "Argument #{k} was already assigned a value" if fargs[i]
|
70
|
+
fargs[i] = v
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
arg_count = fargs.inject(0) { |acc, v| v.nil? ? acc : acc + 1 }
|
75
|
+
if arg_count != iargs.length
|
76
|
+
raise "#{name} requires #{fargs.length} args, given #{arg_count}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Encode parameters.
|
80
|
+
# @lines[@body.length] = Kernel.caller(0)
|
81
|
+
|
82
|
+
emit = encoded_opcode
|
83
|
+
link_directives = []
|
84
|
+
fargs.each_with_index do |arg, i|
|
85
|
+
if (arg.kind_of? Numeric) && !arg[:reladdr]
|
86
|
+
emit += abi.send arg_encode_msgs[i], arg
|
87
|
+
else
|
88
|
+
link_directive = { :type => iargs[i][:type], :offset => emit.length,
|
89
|
+
:relative => iargs[i][:reladdr] || false }
|
90
|
+
if arg.kind_of? Numeric
|
91
|
+
link_directive[:address] = arg.to_i
|
92
|
+
else
|
93
|
+
link_directive[:label] = arg.to_sym
|
94
|
+
end
|
95
|
+
link_directives << link_directive
|
96
|
+
emit += abi.send arg_encode_msgs[i], 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
{ :emit => emit, :link_directives => link_directives }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
@target.class_eval &defines
|
104
|
+
(class << @target; self; end).module_eval &defines
|
105
|
+
end
|
106
|
+
|
107
|
+
# The module / class impacted by the builder.
|
108
|
+
attr_reader :target
|
109
|
+
|
110
|
+
# Creates a builder targeting a module / class.
|
111
|
+
def initialize(target, abi, opcode_type)
|
112
|
+
@target = target
|
113
|
+
@target.const_set :Abi, abi
|
114
|
+
@abi = abi
|
115
|
+
@opcode_type = opcode_type
|
116
|
+
end
|
117
|
+
private_class_method :new
|
118
|
+
end # class Isa
|
119
|
+
|
120
|
+
end # namespace Tem::Builders
|
data/lib/tem/definitions/abi.rb
CHANGED
@@ -24,12 +24,13 @@ module Tem::Abi
|
|
24
24
|
Tem::Builders::Crypto.define_crypto self do |crypto|
|
25
25
|
crypto.crypto_hash :tem_hash, Digest::SHA1
|
26
26
|
|
27
|
-
crypto.asymmetric_key :tem_rsa,
|
28
|
-
:tem_pubrsa_numbers,
|
29
|
-
:to => lambda { |k| k.ssl_key },
|
30
|
-
:read_public => lambda { |key| Tem::Keys::Asymmetric.new key },
|
27
|
+
crypto.asymmetric_key :tem_rsa, OpenSSL::PKey::RSA, :tem_privrsa_numbers,
|
28
|
+
:tem_pubrsa_numbers,
|
31
29
|
:read_private => lambda { |key|
|
32
|
-
#
|
30
|
+
# The TEM uses the Chinese Remainder Theorem form of RSA keys, while
|
31
|
+
# OpenSSL uses the straightforward form (n, e, d).
|
32
|
+
|
33
|
+
# Rebuild the straightforward form from the CRT form.
|
33
34
|
key.n = key.p * key.q
|
34
35
|
p1, q1 = key.p - 1, key.q - 1
|
35
36
|
p1q1 = p1 * q1
|
@@ -42,11 +43,8 @@ module Tem::Abi
|
|
42
43
|
Tem::Keys::Asymmetric.new key
|
43
44
|
}
|
44
45
|
|
45
|
-
crypto.symmetric_key :tem_aes_key,
|
46
|
-
:tem_aes_key_string
|
47
|
-
:new => lambda { |k| OpenSSL::Cipher::AES.new 'ECB' },
|
48
|
-
:read => lambda { |k| Tem::Keys::Symmetric.new k },
|
49
|
-
:to => lambda { |k| k.ssl_key }
|
46
|
+
crypto.symmetric_key :tem_aes_key, OpenSSL::Cipher::AES, 'ECB',
|
47
|
+
:tem_aes_key_string
|
50
48
|
|
51
49
|
crypto.conditional_wrapper :tem_key, 1,
|
52
50
|
[{:tag => [0x99], :type => :tem_key,
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Tem::Assembler
|
2
|
+
Tem::Builders::Assembler.define_assembler self do |assembler|
|
3
|
+
assembler.target_isa Tem::Isa
|
4
|
+
assembler.stack_directive :stack, :label => :__stack,
|
5
|
+
:slot_type => :tem_short
|
6
|
+
assembler.label_directive :label
|
7
|
+
assembler.special_label_directive :entry, :__entry
|
8
|
+
assembler.zeros_directive :zeros
|
9
|
+
assembler.data_directive :data
|
10
|
+
end
|
11
|
+
|
12
|
+
class Builder
|
13
|
+
def done_assembling(proxy)
|
14
|
+
assembled = super
|
15
|
+
bytes = assembled[:bytes]
|
16
|
+
labels = assembled[:labels]
|
17
|
+
Tem::SecPack.new :body => bytes,
|
18
|
+
:labels => labels, :ep => labels[:__entry] || 0,
|
19
|
+
:sp => labels[:__stack] || bytes.length,
|
20
|
+
:lines => assembled[:line_info]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|