ldap-ber 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +6 -0
- data/.rubocop.yml +20 -0
- data/.rubocop_todo.yml +44 -0
- data/Gemfile +17 -0
- data/LICENSE +25 -0
- data/README.md +9 -0
- data/Rakefile +16 -0
- data/bin/console +8 -0
- data/bin/setup +5 -0
- data/ldap-ber.gemspec +30 -0
- data/lib/ber.rb +76 -0
- data/lib/ber.yaml +199 -0
- data/lib/ber/function.rb +104 -0
- data/lib/ber/identified.rb +6 -0
- data/lib/ber/identified/array.rb +14 -0
- data/lib/ber/identified/null.rb +13 -0
- data/lib/ber/identified/oid.rb +27 -0
- data/lib/ber/identified/string.rb +20 -0
- data/lib/ber/refinements.rb +10 -0
- data/lib/ber/refinements/array.rb +59 -0
- data/lib/ber/refinements/false_class.rb +11 -0
- data/lib/ber/refinements/integer.rb +59 -0
- data/lib/ber/refinements/io.rb +19 -0
- data/lib/ber/refinements/ssl_socket.rb +21 -0
- data/lib/ber/refinements/string.rb +74 -0
- data/lib/ber/refinements/string_io.rb +27 -0
- data/lib/ber/refinements/true_class.rb +14 -0
- data/lib/ber/version.rb +5 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/unit/ber_spec.rb +50 -0
- data/spec/unit/identified/identified_string_spec.rb +46 -0
- data/spec/unit/refinements/array_spec.rb +32 -0
- data/spec/unit/refinements/boolean_spec.rb +16 -0
- data/spec/unit/refinements/integer_spec.rb +73 -0
- data/spec/unit/refinements/string_spec.rb +33 -0
- data/spec/unit/request_spec.rb +38 -0
- metadata +159 -0
    
        data/lib/ber/function.rb
    ADDED
    
    | @@ -0,0 +1,104 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'ber/identified'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module BER
         | 
| 6 | 
            +
              # @see BER.function
         | 
| 7 | 
            +
              #
         | 
| 8 | 
            +
              # Used within refinements to identify...
         | 
| 9 | 
            +
              #
         | 
| 10 | 
            +
              class Function
         | 
| 11 | 
            +
                def parse_ber_object(syntax, id, data)
         | 
| 12 | 
            +
                  object_type = (syntax && syntax[id]) || IDENTIFIED[id]
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  case object_type
         | 
| 15 | 
            +
                  when :string
         | 
| 16 | 
            +
                    s = BerIdentifiedString.new(data || EMPTY_STRING)
         | 
| 17 | 
            +
                    s.ber_identifier = id
         | 
| 18 | 
            +
                    s
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  when :integer
         | 
| 21 | 
            +
                    neg = !(data.unpack1('C') & 0x80).zero?
         | 
| 22 | 
            +
                    int = 0
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    data.each_byte do |b|
         | 
| 25 | 
            +
                      int = (int << 8) + (neg ? 255 - b : b)
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    if neg
         | 
| 29 | 
            +
                      (int + 1) * -1
         | 
| 30 | 
            +
                    else
         | 
| 31 | 
            +
                      int
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  when :oid
         | 
| 35 | 
            +
                    oid = data.unpack('w*')
         | 
| 36 | 
            +
                    f   = oid.shift
         | 
| 37 | 
            +
                    g   = if f < 40
         | 
| 38 | 
            +
                            [0, f]
         | 
| 39 | 
            +
                          elsif f < 80
         | 
| 40 | 
            +
                            [1, f - 40]
         | 
| 41 | 
            +
                          else
         | 
| 42 | 
            +
                            [2, f - 80]
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
                    oid.unshift g.last
         | 
| 45 | 
            +
                    oid.unshift g.first
         | 
| 46 | 
            +
                    BerIdentifiedOid.new(oid)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  when :array
         | 
| 49 | 
            +
                    seq = BerIdentifiedArray.new
         | 
| 50 | 
            +
                    seq.ber_identifier = id
         | 
| 51 | 
            +
                    sio = StringIO.new(data || EMPTY_STRING)
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    until (e = read_ber(sio, syntax)).nil?
         | 
| 54 | 
            +
                      seq << e
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                    seq
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  when :boolean
         | 
| 59 | 
            +
                    data != "\000"
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  when :null
         | 
| 62 | 
            +
                    n = BerIdentifiedNull.new
         | 
| 63 | 
            +
                    n.ber_identifier = id
         | 
| 64 | 
            +
                    n
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  else
         | 
| 67 | 
            +
                    raise Error, "Unsupported object type: id=#{id}"
         | 
| 68 | 
            +
                  end
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def read_ber_length(object)
         | 
| 72 | 
            +
                  n = object.getbyte
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  if n <= 0x7f
         | 
| 75 | 
            +
                    n
         | 
| 76 | 
            +
                  elsif n == 0x80
         | 
| 77 | 
            +
                    -1
         | 
| 78 | 
            +
                  elsif n == 0xff
         | 
| 79 | 
            +
                    raise Error, 'Invalid BER length 0xFF detected.'
         | 
| 80 | 
            +
                  else
         | 
| 81 | 
            +
                    v = 0
         | 
| 82 | 
            +
                    object.read(n & 0x7f).each_byte do |b|
         | 
| 83 | 
            +
                      v = (v << 8) + b
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    v
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def read_ber(object, syntax)
         | 
| 91 | 
            +
                  return unless (id = object.getbyte)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  content_length = read_ber_length(object)
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  yield id, content_length if block_given?
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  raise Error, 'Indeterminite BER content length not implemented.' if content_length == -1
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  data = object.read(content_length)
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  parse_ber_object(syntax, id, data)
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module BER
         | 
| 4 | 
            +
              class BerIdentifiedOid
         | 
| 5 | 
            +
                attr_accessor :ber_identifier
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(oid)
         | 
| 8 | 
            +
                  @value = oid.split(/\./).map(&:to_i) if oid.is_a?(String)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def to_ber
         | 
| 12 | 
            +
                  to_ber_oid
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def to_ber_oid
         | 
| 16 | 
            +
                  @value.to_ber_oid
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def to_s
         | 
| 20 | 
            +
                  @value.join('.')
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def to_arr
         | 
| 24 | 
            +
                  @value.dup
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module BER
         | 
| 4 | 
            +
              # Wrapper around a String
         | 
| 5 | 
            +
              #
         | 
| 6 | 
            +
              # @see BER::Function.parse_ber_object
         | 
| 7 | 
            +
              class BerIdentifiedString < String
         | 
| 8 | 
            +
                attr_accessor :ber_identifier
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(args)
         | 
| 11 | 
            +
                  super
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  return unless encoding == Encoding::BINARY
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  current_encoding = encoding
         | 
| 16 | 
            +
                  force_encoding('UTF-8')
         | 
| 17 | 
            +
                  force_encoding(current_encoding) unless valid_encoding?
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,10 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'ber/refinements/array'
         | 
| 4 | 
            +
            require 'ber/refinements/false_class'
         | 
| 5 | 
            +
            require 'ber/refinements/integer'
         | 
| 6 | 
            +
            require 'ber/refinements/io'
         | 
| 7 | 
            +
            require 'ber/refinements/ssl_socket'
         | 
| 8 | 
            +
            require 'ber/refinements/string'
         | 
| 9 | 
            +
            require 'ber/refinements/string_io'
         | 
| 10 | 
            +
            require 'ber/refinements/true_class'
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Refine Array
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            module BER
         | 
| 6 | 
            +
              refine ::Array do
         | 
| 7 | 
            +
                # 48
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                def to_ber(id = 0)
         | 
| 10 | 
            +
                  to_ber_seq_internal(0x30 + id)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                alias_method :to_ber_sequence, :to_ber
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                # 49
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                def to_ber_set(id = 0)
         | 
| 18 | 
            +
                  to_ber_seq_internal(0x31 + id)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                # 96
         | 
| 22 | 
            +
                #
         | 
| 23 | 
            +
                def to_ber_appsequence(id = 0)
         | 
| 24 | 
            +
                  to_ber_seq_internal(0x60 + id)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                # 160
         | 
| 28 | 
            +
                #
         | 
| 29 | 
            +
                def to_ber_contextspecific(id = 0)
         | 
| 30 | 
            +
                  to_ber_seq_internal(0xa0 + id)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def to_ber_oid
         | 
| 34 | 
            +
                  ary   = dup
         | 
| 35 | 
            +
                  first = ary.shift
         | 
| 36 | 
            +
                  raise BER::Error, 'Invalid OID' unless [0, 1, 2].include?(first)
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  first = first * 40 + ary.shift
         | 
| 39 | 
            +
                  ary.unshift first
         | 
| 40 | 
            +
                  oid = ary.pack('w*')
         | 
| 41 | 
            +
                  [6, oid.length].pack('CC') + oid
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def to_ber_control
         | 
| 45 | 
            +
                  ary = self[0].is_a?(Array) ? self : [self]
         | 
| 46 | 
            +
                  ary = ary.collect do |control_sequence|
         | 
| 47 | 
            +
                    control_sequence.collect(&:to_ber).to_ber_sequence.reject_empty_ber_arrays
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                  ary.to_ber_sequence.reject_empty_ber_arrays
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                private
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def to_ber_seq_internal(code)
         | 
| 55 | 
            +
                  s = join
         | 
| 56 | 
            +
                  [code].pack('C') + s.length.to_ber_length_encoding + s
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Refine Integer
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            module BER
         | 
| 6 | 
            +
              refine ::Integer do
         | 
| 7 | 
            +
                # @return [String]
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # @api public
         | 
| 10 | 
            +
                def to_ber
         | 
| 11 | 
            +
                  "\002#{to_ber_internal}"
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # @return [String]
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @api public
         | 
| 17 | 
            +
                def to_ber_enumerated
         | 
| 18 | 
            +
                  "\012#{to_ber_internal}"
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def to_ber_length_encoding
         | 
| 22 | 
            +
                  if self <= 127
         | 
| 23 | 
            +
                    [self].pack('C')
         | 
| 24 | 
            +
                  else
         | 
| 25 | 
            +
                    i = [self].pack('N').sub(/^[\0]+/, ::BER::EMPTY_STRING)
         | 
| 26 | 
            +
                    [0x80 + i.length].pack('C') + i
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                # @param tag [Integer]
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # @return [String]
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                # @api private
         | 
| 35 | 
            +
                def to_ber_application(tag)
         | 
| 36 | 
            +
                  [0x40 + tag].pack('C') + to_ber_internal
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                private
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def to_ber_internal
         | 
| 42 | 
            +
                  size  = 1
         | 
| 43 | 
            +
                  size += 1 until ((negative? ? ~self : self) >> (size * 8)).zero?
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  size += 1 if positive? && (self & (0x80 << (size - 1) * 8)).positive?
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  size += 1 if negative? && (self & (0x80 << (size - 1) * 8)).zero?
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  result = [size]
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  while size.positive?
         | 
| 52 | 
            +
                    result << ((self >> ((size - 1) * 8)) & 0xff)
         | 
| 53 | 
            +
                    size -= 1
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  result.pack('C*')
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Refine IO
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            module BER
         | 
| 6 | 
            +
              refine ::IO do
         | 
| 7 | 
            +
                def read_ber(syntax = ::BER::ASN_SYNTAX)
         | 
| 8 | 
            +
                  ::BER.function.read_ber(self, syntax)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def read_ber_length
         | 
| 12 | 
            +
                  ::BER.function.read_ber_length(self)
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def parse_ber_object(syntax, id, data)
         | 
| 16 | 
            +
                  ::BER.function.parse_ber_object(self, syntax, id, data)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Refine ::OpenSSL::SSL::SSLSocket
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            module BER
         | 
| 6 | 
            +
              if defined? ::OpenSSL
         | 
| 7 | 
            +
                refine ::OpenSSL::SSL::SSLSocket do
         | 
| 8 | 
            +
                  def read_ber(syntax = ::BER::ASN_SYNTAX)
         | 
| 9 | 
            +
                    ::BER.function.read_ber(self, syntax)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def read_ber_length
         | 
| 13 | 
            +
                    ::BER.function.read_ber_length(self)
         | 
| 14 | 
            +
                  end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  def parse_ber_object(syntax, id, data)
         | 
| 17 | 
            +
                    ::BER.function.parse_ber_object(self, syntax, id, data)
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'stringio'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # Refine String
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            module BER
         | 
| 8 | 
            +
              refine ::String do
         | 
| 9 | 
            +
                def read_ber(syntax = ::BER::ASN_SYNTAX)
         | 
| 10 | 
            +
                  ::StringIO.new(self).read_ber(syntax)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def read_ber!(syntax = ::BER::ASN_SYNTAX)
         | 
| 14 | 
            +
                  io     = ::StringIO.new(self)
         | 
| 15 | 
            +
                  result = io.read_ber(syntax)
         | 
| 16 | 
            +
                  slice!(0...io.pos)
         | 
| 17 | 
            +
                  result
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def read_ber_length
         | 
| 21 | 
            +
                  ::BER.function.read_ber_length(self)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def parse_ber_object(syntax, id, data)
         | 
| 25 | 
            +
                  ::BER.function.parse_ber_object(self, syntax, id, data)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                # @param code [String] (0x04)
         | 
| 29 | 
            +
                #
         | 
| 30 | 
            +
                # @return [String]
         | 
| 31 | 
            +
                #
         | 
| 32 | 
            +
                # @api public
         | 
| 33 | 
            +
                def to_ber(code = 0x04)
         | 
| 34 | 
            +
                  raw_string = ascii_encoded
         | 
| 35 | 
            +
                  [code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # @param code [String] (0x04)
         | 
| 39 | 
            +
                #
         | 
| 40 | 
            +
                # @return [String]
         | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
                # @api public
         | 
| 43 | 
            +
                def to_ber_bin(code = 0x04)
         | 
| 44 | 
            +
                  [code].pack('C') + length.to_ber_length_encoding + self
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                # @param code [String]
         | 
| 48 | 
            +
                #
         | 
| 49 | 
            +
                # @return [String]
         | 
| 50 | 
            +
                #
         | 
| 51 | 
            +
                # @api public
         | 
| 52 | 
            +
                def to_ber_application_string(code)
         | 
| 53 | 
            +
                  to_ber(0x40 + code)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def to_ber_contextspecific(code)
         | 
| 57 | 
            +
                  to_ber(0x80 + code)
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def reject_empty_ber_arrays
         | 
| 61 | 
            +
                  gsub(/0\000/n, ::BER::EMPTY_STRING)
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                private
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def ascii_encoded
         | 
| 67 | 
            +
                  encode('UTF-8').force_encoding('ASCII-8BIT')
         | 
| 68 | 
            +
                rescue Encoding::UndefinedConversionError,
         | 
| 69 | 
            +
                       Encoding::ConverterNotFoundError,
         | 
| 70 | 
            +
                       Encoding::InvalidByteSequenceError
         | 
| 71 | 
            +
                  self
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         |