costan-tem_ruby 0.10.2

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.
Files changed (76) hide show
  1. data/CHANGELOG +45 -0
  2. data/LICENSE +21 -0
  3. data/Manifest +75 -0
  4. data/README +8 -0
  5. data/Rakefile +23 -0
  6. data/bin/tem_bench +9 -0
  7. data/bin/tem_ca +13 -0
  8. data/bin/tem_irb +11 -0
  9. data/bin/tem_proxy +65 -0
  10. data/bin/tem_stat +35 -0
  11. data/dev_ca/ca_cert.cer +0 -0
  12. data/dev_ca/ca_cert.pem +32 -0
  13. data/dev_ca/ca_key.pem +27 -0
  14. data/dev_ca/config.yml +14 -0
  15. data/lib/tem/_cert.rb +158 -0
  16. data/lib/tem/apdus/buffers.rb +89 -0
  17. data/lib/tem/apdus/keys.rb +64 -0
  18. data/lib/tem/apdus/lifecycle.rb +13 -0
  19. data/lib/tem/apdus/tag.rb +38 -0
  20. data/lib/tem/auto_conf.rb +25 -0
  21. data/lib/tem/builders/abi.rb +482 -0
  22. data/lib/tem/builders/assembler.rb +314 -0
  23. data/lib/tem/builders/crypto.rb +124 -0
  24. data/lib/tem/builders/isa.rb +120 -0
  25. data/lib/tem/ca.rb +114 -0
  26. data/lib/tem/definitions/abi.rb +65 -0
  27. data/lib/tem/definitions/assembler.rb +23 -0
  28. data/lib/tem/definitions/isa.rb +188 -0
  29. data/lib/tem/ecert.rb +77 -0
  30. data/lib/tem/hive.rb +18 -0
  31. data/lib/tem/keys/asymmetric.rb +116 -0
  32. data/lib/tem/keys/key.rb +48 -0
  33. data/lib/tem/keys/symmetric.rb +47 -0
  34. data/lib/tem/sec_exec_error.rb +63 -0
  35. data/lib/tem/seclosures.rb +81 -0
  36. data/lib/tem/secpack.rb +107 -0
  37. data/lib/tem/tem.rb +31 -0
  38. data/lib/tem/toolkit.rb +101 -0
  39. data/lib/tem/transport/auto_configurator.rb +87 -0
  40. data/lib/tem/transport/java_card_mixin.rb +99 -0
  41. data/lib/tem/transport/jcop_remote_protocol.rb +59 -0
  42. data/lib/tem/transport/jcop_remote_server.rb +171 -0
  43. data/lib/tem/transport/jcop_remote_transport.rb +65 -0
  44. data/lib/tem/transport/pcsc_transport.rb +87 -0
  45. data/lib/tem/transport/transport.rb +10 -0
  46. data/lib/tem_ruby.rb +47 -0
  47. data/tem_ruby.gemspec +35 -0
  48. data/test/_test_cert.rb +70 -0
  49. data/test/builders/test_abi_builder.rb +298 -0
  50. data/test/tem_test_case.rb +26 -0
  51. data/test/tem_unit/test_tem_alu.rb +33 -0
  52. data/test/tem_unit/test_tem_bound_secpack.rb +51 -0
  53. data/test/tem_unit/test_tem_branching.rb +56 -0
  54. data/test/tem_unit/test_tem_crypto_asymmetric.rb +123 -0
  55. data/test/tem_unit/test_tem_crypto_hash.rb +35 -0
  56. data/test/tem_unit/test_tem_crypto_pstore.rb +53 -0
  57. data/test/tem_unit/test_tem_crypto_random.rb +25 -0
  58. data/test/tem_unit/test_tem_emit.rb +23 -0
  59. data/test/tem_unit/test_tem_memory.rb +48 -0
  60. data/test/tem_unit/test_tem_memory_compare.rb +65 -0
  61. data/test/tem_unit/test_tem_output.rb +32 -0
  62. data/test/tem_unit/test_tem_yaml_secpack.rb +47 -0
  63. data/test/test_driver.rb +108 -0
  64. data/test/test_exceptions.rb +35 -0
  65. data/test/transport/test_auto_configurator.rb +114 -0
  66. data/test/transport/test_java_card_mixin.rb +90 -0
  67. data/test/transport/test_jcop_remote.rb +82 -0
  68. data/timings/blank_bound_secpack.rb +18 -0
  69. data/timings/blank_sec.rb +14 -0
  70. data/timings/devchip_decrypt.rb +9 -0
  71. data/timings/post_buffer.rb +10 -0
  72. data/timings/simple_apdu.rb +5 -0
  73. data/timings/timings.rb +64 -0
  74. data/timings/vm_perf.rb +140 -0
  75. data/timings/vm_perf_bound.rb +141 -0
  76. metadata +201 -0
@@ -0,0 +1,65 @@
1
+ require 'socket'
2
+
3
+ # :nodoc: namespace
4
+ module Tem::Transport
5
+
6
+ # Implements the transport layer for a JCOP simulator instance.
7
+ class JcopRemoteTransport
8
+ include JavaCardMixin
9
+ include JcopRemoteProtocol
10
+
11
+ # Creates a new unconnected transport for a JCOP simulator serving TCP/IP.
12
+ #
13
+ # The options parameter must have the following keys:
14
+ # host:: the DNS name or IP of the host running the JCOP simulator
15
+ # port:: the TCP/IP port of the JCOP simulator server
16
+ def initialize(options)
17
+ @host, @port = options[:host], options[:port]
18
+ @socket = nil
19
+ end
20
+
21
+ #
22
+ def exchange_apdu(apdu)
23
+ send_message @socket, :type => 1, :node => 0, :data => apdu
24
+ recv_message(@socket)[:data]
25
+ end
26
+
27
+ # Makes a transport-level connection to the TEM.
28
+ def connect
29
+ begin
30
+ Socket.getaddrinfo(@host, @port, Socket::AF_INET,
31
+ Socket::SOCK_STREAM).each do |addr_info|
32
+ begin
33
+ @socket = Socket.new(addr_info[4], addr_info[5], addr_info[6])
34
+ @socket.connect Socket.pack_sockaddr_in(addr_info[1], addr_info[3])
35
+ break
36
+ rescue
37
+ @socket = nil
38
+ end
39
+ end
40
+ raise 'Connection refused' unless @socket
41
+
42
+ # Wait for the card to be inserted.
43
+ send_message @socket, :type => 0, :node => 0, :data => [0, 1, 0, 0]
44
+ recv_message @socket # ATR should come here, but who cares
45
+ rescue Exception
46
+ @socket = nil
47
+ raise
48
+ end
49
+ end
50
+
51
+ # Breaks down the transport-level connection to the TEM.
52
+ def disconnect
53
+ if @socket
54
+ @socket.close
55
+ @socket = nil
56
+ end
57
+ end
58
+
59
+ def to_s
60
+ "#<JCOP Remote Terminal: disconnected>" if @socket.nil?
61
+ "#<JCOP Remote Terminal: #{@host}:#{@port}>"
62
+ end
63
+ end # class JcopRemoteTransport
64
+
65
+ end # module Tem::Transport
@@ -0,0 +1,87 @@
1
+ require 'rubygems'
2
+ require 'smartcard'
3
+
4
+ # :nodoc: namespace
5
+ module Tem::Transport
6
+
7
+ class PcscTransport
8
+ include JavaCardMixin
9
+ PCSC = Smartcard::PCSC
10
+
11
+ def initialize(options)
12
+ @options = options
13
+ @context = nil
14
+ @card = nil
15
+ end
16
+
17
+ def exchange_apdu(apdu)
18
+ xmit_apdu_string = apdu.pack('C*')
19
+ result_string = @card.transmit xmit_apdu_string, @xmit_ioreq, @recv_ioreq
20
+ return result_string.unpack('C*')
21
+ end
22
+
23
+ def connect
24
+ @context = PCSC::Context.new(PCSC::SCOPE_SYSTEM) if @context.nil?
25
+
26
+ if @options[:reader_name]
27
+ @reader_name = reader_name
28
+ else
29
+ # get the first reader
30
+ readers = @context.list_readers nil
31
+ @reader_name = readers[@options[:reader_index] || 0]
32
+ end
33
+
34
+ # get the reader's status
35
+ reader_states = PCSC::ReaderStates.new(1)
36
+ reader_states.set_reader_name_of!(0, @reader_name)
37
+ reader_states.set_current_state_of!(0, PCSC::STATE_UNKNOWN)
38
+ @context.get_status_change reader_states, 100
39
+ reader_states.acknowledge_events!
40
+
41
+ # prompt for card insertion unless that already happened
42
+ if (reader_states.current_state_of(0) & PCSC::STATE_PRESENT) == 0
43
+ puts "Please insert TEM card in reader #{@reader_name}\n"
44
+ while (reader_states.current_state_of(0) & PCSC::STATE_PRESENT) == 0 do
45
+ @context.get_status_change reader_states, PCSC::INFINITE_TIMEOUT
46
+ reader_states.acknowledge_events!
47
+ end
48
+ puts "Card detected\n"
49
+ end
50
+
51
+ # connect to card
52
+ @card = PCSC::Card.new @context, @reader_name, PCSC::SHARE_EXCLUSIVE,
53
+ PCSC::PROTOCOL_ANY
54
+
55
+ # build the transmit / receive IoRequests
56
+ status = @card.status
57
+ @xmit_ioreq = @@xmit_iorequest[status[:protocol]]
58
+ if RUBY_PLATFORM =~ /win/ and (not RUBY_PLATFORM =~ /darwin/)
59
+ @recv_ioreq = nil
60
+ else
61
+ @recv_ioreq = PCSC::IoRequest.new
62
+ end
63
+ end
64
+
65
+ def disconnect
66
+ unless @card.nil?
67
+ @card.disconnect PCSC::DISPOSITION_LEAVE
68
+ @card = nil
69
+ end
70
+ unless @context.nil?
71
+ @context.release
72
+ @context = nil
73
+ end
74
+ end
75
+
76
+ def to_s
77
+ "#<PC/SC Terminal: disconnected>" if @card.nil?
78
+ "#<PC/SC Terminal: #{@reader_name}>"
79
+ end
80
+
81
+ @@xmit_iorequest = {
82
+ Smartcard::PCSC::PROTOCOL_T0 => Smartcard::PCSC::IOREQUEST_T0,
83
+ Smartcard::PCSC::PROTOCOL_T1 => Smartcard::PCSC::IOREQUEST_T1,
84
+ }
85
+ end # class PcscTransport
86
+
87
+ end # module Tem::Transport
@@ -0,0 +1,10 @@
1
+ # The transport module contains classes responsible for transferring low-level
2
+ # commands issed by the high-level TEM methods to actual TEMs, which can be
3
+ # connected to the system in various ways.
4
+ module Tem::Transport
5
+
6
+ # Shortcut for Tem::Transport::AutoConfigurator#auto_transport
7
+ def self.auto_transport
8
+ Tem::Transport::AutoConfigurator.auto_transport
9
+ end
10
+ end
data/lib/tem_ruby.rb ADDED
@@ -0,0 +1,47 @@
1
+ # gems
2
+ require 'rubygems'
3
+ require 'smartcard'
4
+
5
+ # :nodoc:
6
+ module Tem
7
+ end
8
+
9
+ # :nodoc:
10
+ module Tem::Transport
11
+ end
12
+
13
+ require 'tem/transport/transport.rb'
14
+ require 'tem/transport/java_card_mixin.rb'
15
+ require 'tem/transport/pcsc_transport.rb'
16
+ require 'tem/transport/jcop_remote_protocol.rb'
17
+ require 'tem/transport/jcop_remote_transport.rb'
18
+ require 'tem/transport/jcop_remote_server.rb'
19
+ require 'tem/transport/auto_configurator.rb'
20
+
21
+ require 'tem/keys/key.rb'
22
+ require 'tem/keys/asymmetric.rb'
23
+ require 'tem/keys/symmetric.rb'
24
+
25
+ require 'tem/builders/abi.rb'
26
+ require 'tem/builders/assembler.rb'
27
+ require 'tem/builders/crypto.rb'
28
+ require 'tem/builders/isa.rb'
29
+
30
+ require 'tem/definitions/abi.rb'
31
+ require 'tem/definitions/isa.rb'
32
+ require 'tem/definitions/assembler.rb'
33
+
34
+ require 'tem/auto_conf.rb'
35
+ require 'tem/apdus/buffers.rb'
36
+ require 'tem/apdus/keys.rb'
37
+ require 'tem/apdus/lifecycle.rb'
38
+ require 'tem/apdus/tag.rb'
39
+
40
+ require 'tem/ca.rb'
41
+ require 'tem/ecert.rb'
42
+ require 'tem/hive.rb'
43
+ require 'tem/sec_exec_error.rb'
44
+ require 'tem/seclosures.rb'
45
+ require 'tem/secpack.rb'
46
+ require 'tem/toolkit.rb'
47
+ require 'tem/tem.rb'
data/tem_ruby.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{tem_ruby}
5
+ s.version = "0.10.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Victor Costan"]
9
+ s.date = %q{2009-05-31}
10
+ s.description = %q{TEM (Trusted Execution Module) driver, written in and for ruby.}
11
+ s.email = %q{victor@costan.us}
12
+ s.executables = ["tem_bench", "tem_ca", "tem_irb", "tem_proxy", "tem_stat"]
13
+ s.extra_rdoc_files = ["bin/tem_bench", "bin/tem_ca", "bin/tem_irb", "bin/tem_proxy", "bin/tem_stat", "CHANGELOG", "lib/tem/_cert.rb", "lib/tem/apdus/buffers.rb", "lib/tem/apdus/keys.rb", "lib/tem/apdus/lifecycle.rb", "lib/tem/apdus/tag.rb", "lib/tem/auto_conf.rb", "lib/tem/builders/abi.rb", "lib/tem/builders/assembler.rb", "lib/tem/builders/crypto.rb", "lib/tem/builders/isa.rb", "lib/tem/ca.rb", "lib/tem/definitions/abi.rb", "lib/tem/definitions/assembler.rb", "lib/tem/definitions/isa.rb", "lib/tem/ecert.rb", "lib/tem/hive.rb", "lib/tem/keys/asymmetric.rb", "lib/tem/keys/key.rb", "lib/tem/keys/symmetric.rb", "lib/tem/sec_exec_error.rb", "lib/tem/seclosures.rb", "lib/tem/secpack.rb", "lib/tem/tem.rb", "lib/tem/toolkit.rb", "lib/tem/transport/auto_configurator.rb", "lib/tem/transport/java_card_mixin.rb", "lib/tem/transport/jcop_remote_protocol.rb", "lib/tem/transport/jcop_remote_server.rb", "lib/tem/transport/jcop_remote_transport.rb", "lib/tem/transport/pcsc_transport.rb", "lib/tem/transport/transport.rb", "lib/tem_ruby.rb", "LICENSE", "README"]
14
+ s.files = ["bin/tem_bench", "bin/tem_ca", "bin/tem_irb", "bin/tem_proxy", "bin/tem_stat", "CHANGELOG", "dev_ca/ca_cert.cer", "dev_ca/ca_cert.pem", "dev_ca/ca_key.pem", "dev_ca/config.yml", "lib/tem/_cert.rb", "lib/tem/apdus/buffers.rb", "lib/tem/apdus/keys.rb", "lib/tem/apdus/lifecycle.rb", "lib/tem/apdus/tag.rb", "lib/tem/auto_conf.rb", "lib/tem/builders/abi.rb", "lib/tem/builders/assembler.rb", "lib/tem/builders/crypto.rb", "lib/tem/builders/isa.rb", "lib/tem/ca.rb", "lib/tem/definitions/abi.rb", "lib/tem/definitions/assembler.rb", "lib/tem/definitions/isa.rb", "lib/tem/ecert.rb", "lib/tem/hive.rb", "lib/tem/keys/asymmetric.rb", "lib/tem/keys/key.rb", "lib/tem/keys/symmetric.rb", "lib/tem/sec_exec_error.rb", "lib/tem/seclosures.rb", "lib/tem/secpack.rb", "lib/tem/tem.rb", "lib/tem/toolkit.rb", "lib/tem/transport/auto_configurator.rb", "lib/tem/transport/java_card_mixin.rb", "lib/tem/transport/jcop_remote_protocol.rb", "lib/tem/transport/jcop_remote_server.rb", "lib/tem/transport/jcop_remote_transport.rb", "lib/tem/transport/pcsc_transport.rb", "lib/tem/transport/transport.rb", "lib/tem_ruby.rb", "LICENSE", "Manifest", "Rakefile", "README", "tem_ruby.gemspec", "test/_test_cert.rb", "test/builders/test_abi_builder.rb", "test/tem_test_case.rb", "test/tem_unit/test_tem_alu.rb", "test/tem_unit/test_tem_bound_secpack.rb", "test/tem_unit/test_tem_branching.rb", "test/tem_unit/test_tem_crypto_asymmetric.rb", "test/tem_unit/test_tem_crypto_hash.rb", "test/tem_unit/test_tem_crypto_pstore.rb", "test/tem_unit/test_tem_crypto_random.rb", "test/tem_unit/test_tem_emit.rb", "test/tem_unit/test_tem_memory.rb", "test/tem_unit/test_tem_memory_compare.rb", "test/tem_unit/test_tem_output.rb", "test/tem_unit/test_tem_yaml_secpack.rb", "test/test_driver.rb", "test/test_exceptions.rb", "test/transport/test_auto_configurator.rb", "test/transport/test_java_card_mixin.rb", "test/transport/test_jcop_remote.rb", "timings/blank_bound_secpack.rb", "timings/blank_sec.rb", "timings/devchip_decrypt.rb", "timings/post_buffer.rb", "timings/simple_apdu.rb", "timings/timings.rb", "timings/vm_perf.rb", "timings/vm_perf_bound.rb"]
15
+ s.homepage = %q{http://tem.rubyforge.org}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Tem_ruby", "--main", "README"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{tem}
19
+ s.rubygems_version = %q{1.3.3}
20
+ s.summary = %q{TEM (Trusted Execution Module) driver, written in and for ruby.}
21
+ s.test_files = ["test/builders/test_abi_builder.rb", "test/tem_unit/test_tem_alu.rb", "test/tem_unit/test_tem_bound_secpack.rb", "test/tem_unit/test_tem_branching.rb", "test/tem_unit/test_tem_crypto_asymmetric.rb", "test/tem_unit/test_tem_crypto_hash.rb", "test/tem_unit/test_tem_crypto_pstore.rb", "test/tem_unit/test_tem_crypto_random.rb", "test/tem_unit/test_tem_emit.rb", "test/tem_unit/test_tem_memory.rb", "test/tem_unit/test_tem_memory_compare.rb", "test/tem_unit/test_tem_output.rb", "test/tem_unit/test_tem_yaml_secpack.rb", "test/test_driver.rb", "test/test_exceptions.rb", "test/transport/test_auto_configurator.rb", "test/transport/test_java_card_mixin.rb", "test/transport/test_jcop_remote.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<smartcard>, [">= 0.3.0"])
29
+ else
30
+ s.add_dependency(%q<smartcard>, [">= 0.3.0"])
31
+ end
32
+ else
33
+ s.add_dependency(%q<smartcard>, [">= 0.3.0"])
34
+ end
35
+ end
@@ -0,0 +1,70 @@
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
+ @tem = Tem.auto_tem
30
+
31
+ @tem.kill
32
+ @tem.activate
33
+ end
34
+
35
+ def teardown
36
+ @tem.disconnect unless @tem.nil?
37
+ end
38
+
39
+ def test_cert
40
+ #Create issuer's (manufacturer's) certificate
41
+ issuer_key = OpenSSL::PKey::RSA.new 2048, 0x10001
42
+ issuer_cert = Tem::Cert.create_issuer_cert(issuer_key)
43
+
44
+ #Create subject's (TEM's) certificate
45
+ subject_key = OpenSSL::PKey::RSA.new 2048, 0x10001
46
+ subject_cert = Tem::Cert.create_subject_cert(subject_key, issuer_key, issuer_cert)
47
+
48
+ #Create the tag that will go on the TEM from it's certificate
49
+ written_tag = Tem::Cert.create_tag_from_cert(subject_cert)
50
+
51
+ #Set the tag on the TEM, assert that tag read = tag written
52
+ @tem.set_tag(written_tag)
53
+ read_tag = @tem.get_tag[2..-1] #chop off first two bytes, TEM puts firmware version on front of written tag
54
+ assert_equal written_tag, read_tag, 'error in posted tag data'
55
+
56
+ #Now reconstruct original certificate from tag data
57
+ read_cert = Tem::Cert.create_cert_from_tag(read_tag, issuer_cert)
58
+ read_cert.sign issuer_key, OpenSSL::Digest::SHA1.new
59
+
60
+ assert_equal Tem::Cert.extract_sig_from_cert(subject_cert), Tem::Cert.extract_sig_from_cert(read_cert), 'signatures do not match'
61
+ #If the signature of the original certificate matches the signature of the reconstructed certificate,
62
+ #we can be pretty much certain that the certificates are identical
63
+
64
+ #TODO: PROBLEM:
65
+ #There is no way to set the signature to a known value.
66
+ #The only way to set the signature is to sign the certificate, and only the issuer (manufacturer) can do this.
67
+ #This means that the manufacturer has to be contacted every time the user wants to verify the TEM's certificate,
68
+ #and this may not be practical.
69
+ end
70
+ end
@@ -0,0 +1,298 @@
1
+ require 'openssl'
2
+ require 'test/unit'
3
+
4
+ require 'tem_ruby'
5
+
6
+ class AbiBuilderTest < Test::Unit::TestCase
7
+ class Wrapped
8
+ attr_accessor :p, :q, :n
9
+ attr_accessor :d # Derived value.
10
+ attr_accessor :c # Constructor value.
11
+
12
+ def initialize(ctor_value = 'ctor default')
13
+ self.c = ctor_value
14
+ end
15
+ end
16
+
17
+ class Multi
18
+ attr_accessor :p, :q, :n
19
+ attr_accessor :a, :b, :c
20
+ attr_accessor :str, :const
21
+ end
22
+
23
+ module Abi
24
+ Tem::Builders::Abi.define_abi self do |abi|
25
+ abi.fixed_length_number :byte, 1, :signed => true
26
+ abi.fixed_length_number :ubyte, 1, :signed => false
27
+
28
+ abi.fixed_length_number :word, 2, :signed => true, :big_endian => false
29
+ abi.fixed_length_number :netword, 2, :signed => true, :big_endian => true
30
+
31
+ abi.fixed_length_number :dword, 4, :signed => true, :big_endian => true
32
+ abi.fixed_length_number :udword, 4, :signed => false, :big_endian => false
33
+
34
+ abi.variable_length_number :vln, :word, :signed => false,
35
+ :big_endian => false
36
+ abi.variable_length_number :net_vln, :netword, :signed => false,
37
+ :big_endian => true
38
+ abi.packed_variable_length_numbers :packed, :word, [:p, :q, :n],
39
+ :signed => false,
40
+ :big_endian => false
41
+ abi.packed_variable_length_numbers :net_packed, :netword,
42
+ [:x, :y, :z, :a],
43
+ :signed => false,
44
+ :big_endian => true
45
+ abi.fixed_length_string :mac_id, 6
46
+ abi.object_wrapper :wrapped_raw, Wrapped, [:packed, nil]
47
+ abi.object_wrapper :wrapped, Wrapped, [:packed, nil],
48
+ :to => lambda { |o| w = Wrapped.new
49
+ w.p, w.q, w.n = o.p, o.q, o.n * 100
50
+ w },
51
+ :read => lambda { |o| w = Wrapped.new(o.c); w.d = o.p * o.q; w },
52
+ :new => lambda { |klass| klass.new('hook-new') }
53
+ abi.object_wrapper :multi, Multi,
54
+ [:packed, nil,:packed, { :p => :a, :q => :b, :n => :c},
55
+ :mac_id, :str, 'constant string', :const]
56
+
57
+ abi.conditional_wrapper :conditional, 2,
58
+ [{:tag => [0x59, 0xAF], :class => String, :type => :mac_id},
59
+ {:tag => [0x59, 0xAC], :class => Integer, :type => :net_vln,
60
+ :predicate => lambda { |n| n % 2 == 1 } },
61
+ {:tag => [0x59, 0xAD], :type => :dword,
62
+ :predicate => lambda { |n| n % 3 == 1 } }]
63
+ end
64
+ end
65
+
66
+ def setup
67
+ @garbage = [0xFD, 0xFC, 0xFD, 0xFC, 0xFD] * 5
68
+ end
69
+
70
+ def test_fixed_and_variable_length_number_encoding
71
+ [
72
+ [:byte, 0, [0]], [:byte, 127, [127]],
73
+ [:byte, -1, [255]], [:byte, -127, [129]], [:byte, -128, [128]],
74
+ [:byte, 128, nil], [:byte, -129, nil],
75
+
76
+ [:word, 0, [0, 0]], [:word, 127, [127, 0]], [:word, 128, [128, 0]],
77
+ [:word, 256, [0, 1]], [:word, 32767, [255, 127]],
78
+ [:word, -1, [255, 255]], [:word, -127, [129, 255]],
79
+ [:word, -128, [128, 255]], [:word, -256, [0, 255]],
80
+ [:word, -32767, [1, 128], [:word, -32768, [0, 128]]],
81
+ [:word, 32768, nil], [:byte, -32769, nil],
82
+
83
+ [:netword, 0, [0, 0]], [:netword, 127, [0, 127]],
84
+ [:netword, 128, [0, 128]],
85
+ [:netword, 256, [1, 0]], [:netword, 32767, [127, 255]],
86
+ [:netword, -1, [255, 255]], [:netword, -127, [255, 129]],
87
+ [:netword, -128, [255, 128]], [:netword, -256, [255, 0]],
88
+ [:netword, -32767, [128, 1], [:netword, -32768, [128, 0]]],
89
+ [:netword, 32768, nil], [:netword, -32769, nil],
90
+
91
+ [:dword, 0x12345678, [0x12, 0x34, 0x56, 0x78]],
92
+ [:udword, 0x12345678, [0x78, 0x56, 0x34, 0x12]],
93
+ [:udword, 0xFFFFFFFF, [255, 255, 255, 255]],
94
+ [:udword, 0xFFFFFFFE, [254, 255, 255, 255]],
95
+
96
+ [:vln, 0, [0x01, 0x00, 0x00]], [:vln, 1, [0x01, 0x00, 0x01]],
97
+ [:vln, 256, [0x02, 0x00, 0x00, 0x01]],
98
+ [:vln, 65537, [0x03, 0x00, 0x01, 0x00, 0x01]],
99
+ [:vln, 0x12345678, [0x04, 0x00, 0x78, 0x56, 0x34, 0x12]],
100
+ [:vln, 0xFFFFFFFF, [0x04, 0x00, 255, 255, 255, 255]],
101
+ [:vln, 0xFFFFFFFE, [0x04, 0x00, 254, 255, 255, 255]],
102
+
103
+ [:net_vln, 0, [0x00, 0x01, 0x00]], [:net_vln, 1, [0x00, 0x01, 0x01]],
104
+ [:net_vln, 256, [0x00, 0x02, 0x01, 0x00]],
105
+ [:net_vln, 65537, [0x00, 0x03, 0x01, 0x00, 0x01]],
106
+ [:net_vln, 0x12345678, [0x00, 0x04, 0x12, 0x34, 0x56, 0x78]],
107
+ [:net_vln, 0xFFFFFFFF, [0x00, 0x04, 255, 255, 255, 255]],
108
+ [:net_vln, 0xFFFFFFFE, [0x00, 0x04, 255, 255, 255, 254]],
109
+ ].each do |test_line|
110
+ type, number, array = *test_line
111
+ if array
112
+ assert_equal array, Abi.send(:"to_#{type}", number),
113
+ "#{type} failed on Ruby number -> array"
114
+ assert_equal array, Abi.send(:"to_#{type}",
115
+ OpenSSL::BN.new(number.to_s)),
116
+ "#{type} failed on OpenSSL number -> array"
117
+ assert_equal number, Abi.send(:"read_#{type}", @garbage + array,
118
+ @garbage.length)
119
+ if Abi.respond_to? :"#{type}_length"
120
+ assert_equal array.length, Abi.send(:"#{type}_length"),
121
+ "#{type} failed on length"
122
+ elsif Abi.respond_to? :"read_#{type}_length"
123
+ assert_equal array.length,
124
+ Abi.send(:"read_#{type}_length", @garbage + array,
125
+ @garbage.length),
126
+ "#{type} failed on read_#{type}_length"
127
+ else
128
+ flunk "#{type} does not provide _length or read_#{type}_length"
129
+ end
130
+ else
131
+ assert_raise RuntimeError do
132
+ assert_equal array, Abi.send(:"to_#{type}", number)
133
+ end
134
+ assert_raise RuntimeError do
135
+ assert_equal array, Abi.send(:"to_#{type}",
136
+ OpenSSL::BN.new(number.to_s))
137
+ end
138
+ end
139
+ end
140
+
141
+ assert_equal [255, 255, 255, 255], Abi.signed_to_udword(-1),
142
+ 'Failed on signed_to_udword'
143
+ end
144
+
145
+ def test_packed_number_encoding
146
+ packed = { :p => 0x123, :q => 0xABCDEF, :n => 5 }
147
+ gold_packed = [0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x23, 0x01, 0xEF, 0xCD,
148
+ 0xAB, 0x05]
149
+ assert_equal gold_packed, Abi.to_packed(packed), 'packed'
150
+ assert_equal packed, Abi.read_packed(@garbage + gold_packed,
151
+ @garbage.length), 'packed'
152
+ assert_equal gold_packed.length,
153
+ Abi.read_packed_length(@garbage + gold_packed,
154
+ @garbage.length),
155
+ 'read_packed_length'
156
+
157
+ net_packed = { :x => 0x271, :y => 0x314159, :z => 0, :a => 0x5AA5 }
158
+ gold_net_packed = [0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02,
159
+ 0x02, 0x71, 0x31, 0x41, 0x59, 0x00, 0x5A, 0xA5 ]
160
+ assert_equal gold_net_packed, Abi.to_net_packed(net_packed), 'net-packed'
161
+ assert_equal net_packed, Abi.read_net_packed(@garbage + gold_net_packed,
162
+ @garbage.length),
163
+ 'net_packed'
164
+ assert_equal gold_net_packed.length,
165
+ Abi.read_net_packed_length(@garbage + gold_net_packed,
166
+ @garbage.length),
167
+ 'read_net_packed_length'
168
+ components = Abi.net_packed_components
169
+ assert_equal [:x, :y, :z, :a], components,
170
+ 'incorrect result from _components'
171
+ assert_raise TypeError, '_components result is mutable' do
172
+ components[0] = :w
173
+ end
174
+ end
175
+
176
+ def test_fixed_length_string_encoding
177
+ [
178
+ [:mac_id, "abcdef", nil, [?a, ?b, ?c, ?d, ?e, ?f]],
179
+ [:mac_id, "abc", "abc\0\0\0", [?a, ?b, ?c, 0, 0, 0]],
180
+ [:mac_id, "", "\0\0\0\0\0\0", [0, 0, 0, 0, 0, 0]],
181
+ [:mac_id, "abcdefg", nil, nil],
182
+ [:mac_id, [?a, ?b, ?c, ?d, ?e, ?f], "abcdef", [?a, ?b, ?c, ?d, ?e, ?f]],
183
+ [:mac_id, [?a, ?b, ?c], "abc\0\0\0", [?a, ?b, ?c, 0, 0, 0]],
184
+ [:mac_id, [], "\0\0\0\0\0\0", [0, 0, 0, 0, 0, 0]],
185
+ [:mac_id, [?a, ?b, ?c, ?d, ?e, ?f, ?g], nil, nil],
186
+ ].each do |line|
187
+ type, source, string, array = *line
188
+ string ||= source
189
+ if array
190
+ assert_equal array, Abi.send(:"to_#{type}", source),
191
+ "#{type} failed on string -> array"
192
+ assert_equal string, Abi.send(:"read_#{type}", @garbage + array,
193
+ @garbage.length)
194
+ else
195
+ assert_raise RuntimeError do
196
+ assert_equal array, Abi.send(:"to_#{type}", source)
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ def test_object_wrapper_directs
203
+ packed = { :p => 2301, :q => 4141, :n => 60 }
204
+ gold_packed = Abi.to_packed packed
205
+ wrapped = Abi.read_wrapped_raw @garbage + gold_packed, @garbage.length
206
+ assert_equal Wrapped, wrapped.class,
207
+ 'Reading wrapped object instantiated wrong class'
208
+ assert_equal [packed[:p], packed[:q], packed[:n], nil, 'ctor default'],
209
+ [wrapped.p, wrapped.q, wrapped.n, wrapped.d, wrapped.c],
210
+ 'Reading wrapped object gave wrong attributes'
211
+ assert_equal gold_packed.length,
212
+ Abi.read_wrapped_raw_length(@garbage + gold_packed,
213
+ @garbage.length),
214
+ 'Reading wrapped object length'
215
+ assert_equal gold_packed, Abi.to_wrapped_raw(wrapped),
216
+ 'Wrapped object -> array'
217
+ end
218
+
219
+ def test_object_wrapper_schema
220
+ packed = { :p => 2301, :q => 4141, :n => 60 }
221
+ xpacked = { :p => 6996, :q => 1331, :n => 22 }
222
+ gold_multi = Abi.to_packed(packed) + Abi.to_packed(xpacked) +
223
+ Abi.to_mac_id("abc")
224
+ multi = Abi.read_multi @garbage + gold_multi, @garbage.length
225
+ assert_equal Multi, multi.class,
226
+ 'Reading wrapped object instantiated wrong class'
227
+ assert_equal [packed[:p], packed[:q], packed[:n],
228
+ xpacked[:p], xpacked[:q], xpacked[:n], "abc\0\0\0",
229
+ "constant string"],
230
+ [multi.p, multi.q, multi.n, multi.a, multi.b, multi.c,
231
+ multi.str, multi.const],
232
+ 'Reading wrapped object gave wrong attributes'
233
+ assert_equal gold_multi, Abi.to_multi(multi),
234
+ 'Wrapped object -> array'
235
+ assert_equal gold_multi.length,
236
+ Abi.read_multi_length(@garbage + gold_multi, @garbage.length),
237
+ 'Reading wrapped object length'
238
+ end
239
+
240
+ def test_object_wrapper_hooks
241
+ packed = { :p => 2301, :q => 4141, :n => 60 }
242
+ gold_packed = Abi.to_packed packed
243
+ wrapped = Abi.read_wrapped @garbage + gold_packed, @garbage.length
244
+ assert_equal Wrapped, wrapped.class,
245
+ 'Reading wrapped object instantiated wrong class'
246
+ assert_equal [nil, nil, nil, packed[:p] * packed[:q], 'hook-new'],
247
+ [wrapped.p, wrapped.q, wrapped.n, wrapped.d, wrapped.c],
248
+ 'Reading wrapped object with hook gave wrong attributes'
249
+
250
+ wrapped = Abi.read_wrapped_raw gold_packed, 0
251
+ packed[:n] *= 100
252
+ gold_packed = Abi.to_packed packed
253
+ assert_equal gold_packed, Abi.to_wrapped(wrapped),
254
+ 'Wrapped object -> array (with hook)'
255
+
256
+ assert_equal gold_packed.length,
257
+ Abi.read_packed_length(@garbage + gold_packed,
258
+ @garbage.length),
259
+ 'Reading wrapped object length'
260
+ end
261
+
262
+ def test_conditional_wrapper
263
+ [
264
+ [:conditional, "abcdef", [0x59, 0xAF, ?a, ?b, ?c, ?d, ?e, ?f]],
265
+ [:conditional, 3, [0x59, 0xAC, 0x00, 0x01, 0x03]],
266
+ [:conditional, 4, [0x59, 0xAD, 0x00, 0x00, 0x00, 0x04]],
267
+ [:conditional, OpenSSL::BN.new('7'), [0x59, 0xAD, 0x00, 0x00, 0x00, 0x07]],
268
+ [:conditional, 6, nil]
269
+ ].each do |test_line|
270
+ type, object, array = *test_line
271
+ if array
272
+ assert_equal array, Abi.send(:"to_#{type}", object),
273
+ "Object #{object.inspect} -> array"
274
+ assert_equal object, Abi.send(:"read_#{type}", @garbage + array,
275
+ @garbage.length)
276
+ assert_equal array.length,
277
+ Abi.send(:"read_#{type}_length", @garbage + array,
278
+ @garbage.length),
279
+ "#{type} failed on read_#{type}_length"
280
+ else
281
+ assert_raise RuntimeError do
282
+ assert_equal array, Abi.send(:"to_#{type}", object)
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ def test_length
289
+ [[:byte, 1], [:ubyte, 1],
290
+ [:word, 2], [:netword, 2],
291
+ [:dword, 4], [:udword, 4],
292
+ [:mac_id, 6]
293
+ ].each do |test_line|
294
+ assert_equal test_line.last, Abi.send(:"#{test_line.first}_length"),
295
+ "length failed for #{test_line.first}"
296
+ end
297
+ end
298
+ end