costan-tem_ruby 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
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