tem_ruby 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.10.1. Internal refactorings.
2
+
1
3
  v0.10.0. New transport code, allowing for multiple readers and TEM proxying.
2
4
 
3
5
  v0.9.2. Changed exec-SECpack calling sequence for fw 1.9.1(fire, the released version).
data/Manifest CHANGED
@@ -9,21 +9,25 @@ dev_ca/ca_cert.pem
9
9
  dev_ca/ca_key.pem
10
10
  dev_ca/config.yml
11
11
  lib/tem/_cert.rb
12
- lib/tem/abi.rb
12
+ lib/tem/apdus/buffers.rb
13
+ lib/tem/apdus/keys.rb
14
+ lib/tem/apdus/lifecycle.rb
15
+ lib/tem/apdus/tag.rb
13
16
  lib/tem/auto_conf.rb
14
- lib/tem/buffers.rb
17
+ lib/tem/builders/abi.rb
18
+ lib/tem/builders/crypto.rb
15
19
  lib/tem/ca.rb
16
- lib/tem/crypto_abi.rb
20
+ lib/tem/definitions/abi.rb
17
21
  lib/tem/ecert.rb
18
22
  lib/tem/hive.rb
19
- lib/tem/keys.rb
20
- lib/tem/lifecycle.rb
23
+ lib/tem/keys/asymmetric.rb
24
+ lib/tem/keys/key.rb
25
+ lib/tem/keys/symmetric.rb
21
26
  lib/tem/sec_assembler.rb
22
27
  lib/tem/sec_exec_error.rb
23
28
  lib/tem/sec_opcodes.rb
24
29
  lib/tem/seclosures.rb
25
30
  lib/tem/secpack.rb
26
- lib/tem/tag.rb
27
31
  lib/tem/tem.rb
28
32
  lib/tem/toolkit.rb
29
33
  lib/tem/transport/auto_configurator.rb
@@ -39,6 +43,7 @@ Manifest
39
43
  Rakefile
40
44
  README
41
45
  test/_test_cert.rb
46
+ test/builders/test_abi_builder.rb
42
47
  test/tem_test_case.rb
43
48
  test/test_driver.rb
44
49
  test/test_exceptions.rb
@@ -1,4 +1,7 @@
1
- module Tem::Buffers
1
+ # :nodoc: namespace
2
+ module Tem::Apdus
3
+
4
+ module Buffers
2
5
  def alloc_buffer(length)
3
6
  response = @transport.applet_apdu! :ins => 0x20,
4
7
  :p12 => to_tem_short(length)
@@ -83,3 +86,5 @@ module Tem::Buffers
83
86
  return buffer_id
84
87
  end
85
88
  end
89
+
90
+ end # namespace Tem::Apdus
@@ -1,4 +1,7 @@
1
- module Tem::Keys
1
+ # :nodoc: namespace
2
+ module Tem::Apdus
3
+
4
+ module Keys
2
5
  def devchip_generate_key_pair
3
6
  response = @transport.applet_apdu! :ins => 0x40
4
7
  return { :privkey_id => read_tem_byte(response, 0),
@@ -11,13 +14,13 @@ module Tem::Keys
11
14
  end
12
15
 
13
16
  def devchip_save_key(key_id)
14
- response = @transport.applet_apdu! :ins => 0x43, :p1 => key_id
17
+ response = @transport.applet_apdu! :ins => 0x43, :p1 => key_id
15
18
  buffer_id = read_tem_byte response, 0
16
19
  buffer_length = read_tem_short response, 1
17
20
  key_buffer = read_buffer buffer_id
18
21
  release_buffer buffer_id
19
22
 
20
- read_tem_key key_buffer[0...buffer_length], 0
23
+ read_tem_key key_buffer[0, buffer_length], 0
21
24
  end
22
25
 
23
26
  def devchip_encrypt_decrypt(data, key_id, opcode)
@@ -34,7 +37,7 @@ module Tem::Keys
34
37
  data_buffer = read_buffer buffer_id
35
38
  release_buffer buffer_id
36
39
 
37
- return data_buffer[0...buffer_length]
40
+ return data_buffer[0, buffer_length]
38
41
  end
39
42
  def devchip_encrypt(data, key_id)
40
43
  devchip_encrypt_decrypt data, key_id, 0x44
@@ -57,3 +60,5 @@ module Tem::Keys
57
60
  return stat
58
61
  end
59
62
  end
63
+
64
+ end # namespace Tem::Apdus
@@ -1,4 +1,7 @@
1
- module Tem::Lifecycle
1
+ # :nodoc: namespace
2
+ module Tem::Apdus
3
+
4
+ module Lifecycle
2
5
  def activate
3
6
  @transport.applet_apdu(:ins => 0x10)[:status] == 0x9000
4
7
  end
@@ -6,3 +9,5 @@ module Tem::Lifecycle
6
9
  @transport.applet_apdu(:ins => 0x11)[:status] == 0x9000
7
10
  end
8
11
  end
12
+
13
+ end # namespace Tem::Apdus
@@ -1,4 +1,7 @@
1
- module Tem::Tag
1
+ # :nodoc: namespace
2
+ module Tem::Apdus
3
+
4
+ module Tag
2
5
  def set_tag(tag_data)
3
6
  buffer_id = post_buffer tag_data
4
7
  begin
@@ -31,3 +34,5 @@ module Tem::Tag
31
34
  get_tag_data 0, tag_length
32
35
  end
33
36
  end
37
+
38
+ end # namespace Tem::Apdus
@@ -0,0 +1,482 @@
1
+ require 'openssl'
2
+
3
+
4
+ # :nodoc: namespace
5
+ module Tem::Builders
6
+
7
+ # Builder class and namespace for the ABI builder.
8
+ class Abi
9
+ # Creates a builder targeting a module / class.
10
+ #
11
+ # The given parameter should be a class or module
12
+ def self.define_abi(class_or_module) # :yields: abi
13
+ yield new(class_or_module)
14
+ end
15
+
16
+ # Defines the methods for handling a fixed-length number type in an ABI.
17
+ #
18
+ # The |options| hash supports the following keys:
19
+ # signed:: if false, the value type cannot hold negative numbers;
20
+ # signed values are stored using 2s-complement; defaults to true
21
+ # big_endian:: if true, bytes are sent over the wire in big-endian order
22
+ #
23
+ # The following methods are defined for a type named 'name':
24
+ # * read_name(array, offset) -> number
25
+ # * to_name(number) -> array
26
+ # * signed_to_name(number) -> array # takes signed inputs on unsigned types
27
+ # * name_length -> number # the number of bytes in the number type
28
+ def fixed_length_number(name, bytes, options = {})
29
+ impl = Tem::Builders::Abi::Impl
30
+ signed = options.fetch :signed, true
31
+ big_endian = options.fetch :big_endian, true
32
+
33
+ defines = Proc.new do
34
+ define_method :"read_#{name}" do |array, offset|
35
+ impl.number_from_array array, offset, bytes, signed, big_endian
36
+ end
37
+ define_method :"to_#{name}" do |n|
38
+ impl.check_number_range n, bytes, signed
39
+ impl.number_to_array n, bytes, signed, big_endian
40
+ end
41
+ define_method :"signed_to_#{name}" do |n|
42
+ impl.number_to_array n, bytes, signed, big_endian
43
+ end
44
+ define_method(:"#{name}_length") { bytes }
45
+ end
46
+
47
+ @target.class_eval &defines
48
+ (class << @target; self; end).module_eval &defines
49
+ end
50
+
51
+ # Defines the methods for handling a variable-length number type in an ABI.
52
+ #
53
+ # The length_type argument holds the name of a fixed-length number type that
54
+ # will be used to store the length of hte variable-length number.
55
+ #
56
+ # The |options| hash supports the following keys:
57
+ # signed:: if false, the value type cannot hold negative numbers;
58
+ # signed values are stored using 2s-complement; defaults to true
59
+ # big_endian:: if true, bytes are sent over the wire in big-endian order
60
+ #
61
+ # The following methods are defined for a type named 'name':
62
+ # * read_name(array, offset) -> number
63
+ # * read_name_length(array, offset) -> number
64
+ # * to_name(number) -> array
65
+ def variable_length_number(name, length_type, options = {})
66
+ impl = Tem::Builders::Abi::Impl
67
+ signed = options.fetch :signed, true
68
+ big_endian = options.fetch :big_endian, true
69
+ length_bytes = @target.send :"#{length_type}_length"
70
+ read_length_msg = :"read_#{length_type}"
71
+ write_length_msg = :"to_#{length_type}"
72
+
73
+ defines = Proc.new do
74
+ define_method :"read_#{name}" do |array, offset|
75
+ length = self.send read_length_msg, array, offset
76
+ impl.number_from_array array, offset + length_bytes, length, signed,
77
+ big_endian
78
+ end
79
+ define_method :"to_#{name}" do |n|
80
+ number_data = impl.number_to_array n, nil, signed, big_endian
81
+ length_data = self.send write_length_msg, number_data.length
82
+ length_data + number_data
83
+ end
84
+ define_method :"read_#{name}_length" do |array, offset|
85
+ length_bytes + self.send(read_length_msg, array, offset)
86
+ end
87
+ end
88
+
89
+ @target.class_eval &defines
90
+ (class << @target; self; end).module_eval &defines
91
+ end
92
+
93
+ # Defines the methods for handling a group of packed variable-length numbers
94
+ # in the ABI.
95
+ #
96
+ # When serializing a group of variable-length numbers, it's desirable to have
97
+ # the lengths of all the numbers grouped before the number data. This makes
98
+ # reading and writing easier & faster for embedded code. The optimization is
99
+ # important enough that it's made its way into the API.
100
+ #
101
+ # All the numbers' lengths are assumed to be represented by the same
102
+ # fixed-length type, whose name is given as the length_type parameter.
103
+ #
104
+ # The numbers are de-serialized into a hash, where each number is associated
105
+ # with a key. The components argument specifies the names of the keys, in the
106
+ # order that the numbers are serialized in.
107
+ #
108
+ # The |options| hash supports the following keys:
109
+ # signed:: if false, the value type cannot hold negative numbers;
110
+ # signed values are stored using 2s-complement; defaults to true
111
+ # big_endian:: if true, bytes are sent over the wire in big-endian order
112
+ #
113
+ # The following methods are defined for a type named 'name':
114
+ # * read_name(array, offset) -> hash
115
+ # * read_name_length(array, offset) -> number
116
+ # * to_name(hash) -> array
117
+ # * name_components -> array
118
+ def packed_variable_length_numbers(name, length_type, components,
119
+ options = {})
120
+ impl = Tem::Builders::Abi::Impl
121
+ sub_names = components.freeze
122
+ signed = options.fetch :signed, true
123
+ big_endian = options.fetch :big_endian, true
124
+ length_bytes = @target.send :"#{length_type}_length"
125
+ read_length_msg = :"read_#{length_type}"
126
+ write_length_msg = :"to_#{length_type}"
127
+
128
+ defines = Proc.new do
129
+ define_method :"read_#{name}" do |array, offset|
130
+ response = {}
131
+ data_offset = offset + length_bytes * sub_names.length
132
+ sub_names.each_with_index do |sub_name, i|
133
+ length = self.send read_length_msg, array, offset + i * length_bytes
134
+ response[sub_name] =
135
+ impl.number_from_array array, data_offset, length, signed,
136
+ big_endian
137
+ data_offset += length
138
+ end
139
+ response
140
+ end
141
+ define_method :"to_#{name}" do |numbers|
142
+ number_data = sub_names.map do |sub_name|
143
+ impl.number_to_array numbers[sub_name], nil, signed, big_endian
144
+ end
145
+ length_data = number_data.map do |number|
146
+ self.send write_length_msg, number.length
147
+ end
148
+ # Concatenate all the arrays without using flatten.
149
+ lengths = length_data.inject([]) { |acc, i| acc += i }
150
+ number_data.inject(lengths) { |acc, i| acc += i }
151
+ end
152
+ define_method :"read_#{name}_length" do |array, offset|
153
+ response = sub_names.length * length_bytes
154
+ 0.upto(sub_names.length - 1) do |i|
155
+ response += self.send read_length_msg, array,
156
+ offset + i * length_bytes
157
+ end
158
+ response
159
+ end
160
+ define_method(:"#{name}_components") { sub_names }
161
+ end
162
+
163
+ @target.class_eval &defines
164
+ (class << @target; self; end).module_eval &defines
165
+ end
166
+
167
+ # Defines the methods for handling a fixed-length string type in an ABI.
168
+ #
169
+ # The |options| hash supports the following keys:
170
+ # signed:: if false, the value type cannot hold negative numbers;
171
+ # signed values are stored using 2s-complement; defaults to true
172
+ # big_endian:: if true, bytes are sent over the wire in big-endian order
173
+ #
174
+ # The following methods are defined for a type named 'name':
175
+ # * read_name(array, offset) -> string
176
+ # * to_name(string or array) -> array
177
+ # * name_length -> number # the number of bytes in the string type
178
+ def fixed_length_string(name, bytes, options = {})
179
+ impl = Tem::Builders::Abi::Impl
180
+ signed = options.fetch :signed, true
181
+ big_endian = options.fetch :big_endian, true
182
+
183
+ defines = Proc.new do
184
+ define_method :"read_#{name}" do |array, offset|
185
+ impl.string_from_array array, offset, bytes
186
+ end
187
+ define_method :"to_#{name}" do |n|
188
+ impl.string_to_array n, bytes
189
+ end
190
+ define_method(:"#{name}_length") { bytes }
191
+ end
192
+
193
+ @target.class_eval &defines
194
+ (class << @target; self; end).module_eval &defines
195
+ end
196
+
197
+ # Defines methods for handling a complex ABI structure wrapped into an object.
198
+ #
199
+ # Objects are assumed to be of the object_class type. The objects are
200
+ # serialized according to a schema, which is an array of 2-element directives.
201
+ # The first element in a directive indicates the lower-level ABI type to be
202
+ # serialized, and the 2nd element indicates the mapping between the object and
203
+ # the higher level ABI. The mapping can be:
204
+ # * a symbol - the lower level ABI output is assigned to an object property
205
+ # * a hash - the keys in the lower level ABI output are assigned to the
206
+ # object properties indicated by the values
207
+ # * nil - the components of the lower level ABI type (which should act like
208
+ # packed_variable_length_numbers types) are mapped to identically
209
+ # named object properties
210
+ #
211
+ # The following methods are defined for a type named 'name':
212
+ # * read_name(array, offset) -> object
213
+ # * read_name_length(array, offset) -> number
214
+ # * to_name(object) -> array
215
+ # * name_class -> Class
216
+ #
217
+ # The following hooks (Procs in the hooks argument) are supported:
218
+ # new(object_class) -> object:: called to instantiate a new object in read_;
219
+ # if the hook is not present, object_class.new is used instead
220
+ # read(object) -> object:: called after the object is de-serialized using
221
+ # the lower-level ABI; if the hook is present, its value is returned
222
+ # from the read_ method
223
+ # to(object) -> object:: called before the object is serialized using the
224
+ # lower-level ABI; if the hook is present, its value is used for
225
+ # serialization
226
+ def object_wrapper(name, object_class, schema, hooks = {})
227
+ if hooks[:new]
228
+ read_body = "r = #{name}_newhook(#{name}_class);"
229
+ else
230
+ read_body = "r = #{name}_class.new;"
231
+ end
232
+
233
+ to_body = "r = [];"
234
+ to_body << "value = #{name}_tohook(value);" if hooks[:to]
235
+
236
+ readlen_body = "old_offset = offset;"
237
+
238
+ 0.upto schema.length / 2 - 1 do |i|
239
+ abi_type = schema[i * 2]
240
+ type_mapping = schema[i * 2 + 1]
241
+
242
+ # Set up the translation table.
243
+ if type_mapping.nil?
244
+ type_mapping = {}
245
+ components = @target.send :"#{abi_type}_components"
246
+ components.each { |c| type_mapping[c] = c }
247
+ end
248
+
249
+ # Set up the read_ and read_name_length methods.
250
+ if abi_type.kind_of? Symbol
251
+ read_body << "v = read_#{abi_type}(array,offset);"
252
+ else
253
+ read_body << "v = #{abi_type.inspect};"
254
+ end
255
+ case type_mapping
256
+ when Symbol
257
+ read_body << "r.#{type_mapping} = v;"
258
+ when Hash, nil
259
+ type_mapping.each do |k, v|
260
+ read_body << "r.#{v} = v[:#{k}];"
261
+ end
262
+ end
263
+ if abi_type.kind_of? Symbol
264
+ if @target.respond_to? :"#{abi_type}_length"
265
+ read_body << "offset += #{@target.send :"#{abi_type}_length"};"
266
+ readlen_body << "offset += #{@target.send :"#{abi_type}_length"};"
267
+ elsif @target.respond_to? :"read_#{abi_type}_length"
268
+ read_body << "offset += read_#{abi_type}_length(array,offset);"
269
+ readlen_body << "offset += read_#{abi_type}_length(array,offset);"
270
+ else
271
+ raise "#{abi_type} doesn't support _length or read_#{abi_type}_length"
272
+ end
273
+ end
274
+
275
+ # Set up the to_ method.
276
+ next unless abi_type.kind_of? Symbol
277
+ to_body << "r += to_#{abi_type}("
278
+ case type_mapping
279
+ when Symbol
280
+ to_body << "value.#{type_mapping}"
281
+ when Hash
282
+ to_body << type_mapping.map { |k, v| ":#{k} => value.#{v}" }.join(', ')
283
+ end
284
+ to_body << ");"
285
+ end
286
+ read_body << "r = self.#{name}_readhook(r);" if hooks[:read]
287
+ read_body << "r;"
288
+ to_body << "r;"
289
+ readlen_body << "offset - old_offset;"
290
+
291
+ define_str = "def read_#{name}(array,offset);#{read_body}end;"
292
+ define_str << "def read_#{name}_length(array,offset);#{readlen_body}end;"
293
+ define_str << "def to_#{name}(value);#{to_body}end;"
294
+
295
+ defines = Proc.new do
296
+ define_method(:"#{name}_class") { object_class }
297
+ define_method(:"#{name}_newhook", &hooks[:new]) if hooks[:new]
298
+ define_method(:"#{name}_readhook", &hooks[:read]) if hooks[:read]
299
+ define_method(:"#{name}_tohook", &hooks[:to]) if hooks[:to]
300
+ end
301
+
302
+ @target.class_eval &defines
303
+ @target.class_eval define_str
304
+ (class << @target; self; end).module_eval &defines
305
+ (class << @target; self; end).module_eval define_str
306
+ end
307
+
308
+ # Defines methods for handling an 'enum'-like ABI type whose type is
309
+ # determined by a fixed-length tag that is prefixed to the data.
310
+ #
311
+ # tag_length indicates the tag's length, in bytes. The mapping between tags
312
+ # and lower-level ABI types is expressed as an array of rules. Each rule is a
313
+ # hash, and the following attributes are supported.
314
+ # tag:: an array of numbers; the tag must match this array (de-serializing)
315
+ # type:: the lower-level ABI type used for serialization/de-serialization
316
+ # class:: a ruby Class; if present, the value must be a kind of the given
317
+ # class to match the rule (serializing)
318
+ # predicate:: a Proc; if present, the Proc is given the value, and must
319
+ # return a true value for the value to match the rule (serializing)
320
+ def conditional_wrapper(name, tag_length, rules)
321
+ impl = Tem::Builders::Abi::Impl
322
+
323
+ defines = Proc.new do
324
+ define_method :"read_#{name}" do |array, offset|
325
+ tag = array[offset, tag_length]
326
+ matching_rule = rules.find { |rule| rule[:tag] == tag }
327
+ raise "Rules don't cover tag #{tag.inspect}" unless matching_rule
328
+ self.send :"read_#{matching_rule[:type]}", array, offset + tag_length
329
+ end
330
+ define_method :"read_#{name}_length" do |array, offset|
331
+ tag = array[offset, tag_length]
332
+ matching_rule = rules.find { |rule| rule[:tag] == tag }
333
+ raise "Rules don't cover tag #{tag.inspect}" unless matching_rule
334
+ if self.respond_to? :"#{matching_rule[:type]}_length"
335
+ tag_length + self.send(:"#{matching_rule[:type]}_length")
336
+ else
337
+ tag_length + self.send(:"read_#{matching_rule[:type]}_length", array,
338
+ offset + tag_length)
339
+ end
340
+ end
341
+ define_method :"to_#{name}" do |value|
342
+ matching_rule = rules.find do |rule|
343
+ next false if rule[:class] && !value.kind_of?(rule[:class])
344
+ next false if rule[:predicate] && !rule[:predicate].call(value)
345
+ true
346
+ end
347
+
348
+ raise "Rules don't cover #{value.inspect}" unless matching_rule
349
+ matching_rule[:tag] + self.send(:"to_#{matching_rule[:type]}", value)
350
+ end
351
+ define_method(:"#{name}_length") { bytes }
352
+ end
353
+
354
+ @target.class_eval &defines
355
+ (class << @target; self; end).module_eval &defines
356
+ end
357
+
358
+ # The module / class impacted by the builder.
359
+ attr_reader :target
360
+
361
+ # Creates a builder targeting a module / class.
362
+ def initialize(target)
363
+ @target = target
364
+ end
365
+ private_class_method :new
366
+ end # class Abi
367
+
368
+
369
+ # Implementation code for the ABI methods.
370
+ module Abi::Impl
371
+ # Reads a variable-length number serialized to an array.
372
+ #
373
+ # The source is indicated by the array and offset parameters. The number's
374
+ # length is given in bytes.
375
+ def self.number_from_array(array, offset, length, is_signed, is_big_endian)
376
+ # Read the raw number from the array.
377
+ number = 0
378
+ if is_big_endian
379
+ 0.upto length - 1 do |i|
380
+ number = (number << 8) | array[offset + i]
381
+ end
382
+ else
383
+ (length - 1).downto 0 do |i|
384
+ number = (number << 8) | array[offset + i]
385
+ end
386
+ end
387
+
388
+ if is_signed # Add the sign if necessary.
389
+ range = 1 << (8 * length)
390
+ max_value = (range >> 1) - 1
391
+ number -= range if number > max_value
392
+ end
393
+
394
+ number
395
+ end
396
+
397
+ # Writes a fixed or variable-length number to an array.
398
+ #
399
+ # The source is indicated by the array and offset parameters. The number's
400
+ # length is given in bytes.
401
+ def self.number_to_array(number, length, is_signed, is_big_endian)
402
+ bytes = []
403
+ if number.kind_of? OpenSSL::BN # OpenSSL key material
404
+ if is_signed and number < 0 # normalize number
405
+ range = OpenSSL::BN.new("1") << (8 * length)
406
+ number += range
407
+ end
408
+
409
+ length ||= [number.num_bytes, 1].max
410
+ v = 0
411
+ (length * 8 - 1).downto 0 do |i|
412
+ v = (v << 1) | (number.bit_set?(i) ? 1 : 0)
413
+ if i & 7 == 0
414
+ bytes << v
415
+ v = 0
416
+ end
417
+ end
418
+ bytes.reverse! unless is_big_endian
419
+ else # Ruby number.
420
+ if length
421
+ length.times do
422
+ bytes << (number & 0xFF)
423
+ number >>= 8
424
+ end
425
+ else
426
+ loop do
427
+ bytes << (number & 0xFF)
428
+ number >>= 8
429
+ break if number == 0
430
+ end
431
+ end
432
+ bytes.reverse! if is_big_endian
433
+ end
434
+ bytes
435
+ end
436
+
437
+ # Checks the range of a number for fixed-length encoding.
438
+ def self.check_number_range(number, length, is_signed)
439
+ range = 1 << (8 * length)
440
+ if is_signed
441
+ min_value, max_value = -(range >> 1), (range >> 1) - 1
442
+ else
443
+ min_value, max_value = 0, range - 1
444
+ end
445
+
446
+ exception_string = "Number #{number} exceeds #{min_value}-#{max_value}"
447
+ raise exception_string if number < min_value or number > max_value
448
+ end
449
+
450
+ # Reads a variable-length string serialized to an array.
451
+ #
452
+ # The source is indicated by the array and offset parameters. The number's
453
+ # length is given in bytes.
454
+ def self.string_from_array(array, offset, length)
455
+ array[offset, length].pack('C*')
456
+ end
457
+
458
+ # Writes a fixed or variable-length string to an array.
459
+ #
460
+ # The source is indicated by the array and offset parameters. The number's
461
+ # length is given in bytes.
462
+ def self.string_to_array(array_or_string, length)
463
+ if array_or_string.respond_to? :to_str
464
+ # array_or_string is String-like
465
+ string = array_or_string.to_str
466
+ array = string.unpack('C*')
467
+ else
468
+ # array_or_string is Array-like
469
+ array = array_or_string
470
+ end
471
+
472
+ if length and array.length > length
473
+ raise "Cannot fit #{array_or_string.inspect} into a #{length}-byte string"
474
+ end
475
+ # Pad the array with zeros up to the fixed length.
476
+ length ||= 0
477
+ array << 0 while array.length < length
478
+ array
479
+ end
480
+ end
481
+
482
+ end # namespace Tem::Builders