Skip to content

Commit 158d81d

Browse files
committed
Merge pull request #172 from ruby-ldap/ber-message-id-encoding-fix
Test ber encoding of message_id > 128
2 parents 77387bf + 012ee8a commit 158d81d

File tree

6 files changed

+116
-109
lines changed

6 files changed

+116
-109
lines changed

Diff for: lib/net/ber/ber_parser.rb

+12-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,18 @@ def parse_ber_object(syntax, id, data)
4141
s.ber_identifier = id
4242
s
4343
elsif object_type == :integer
44-
j = 0
45-
data.each_byte { |b| j = (j << 8) + b }
46-
j
44+
neg = !(data.unpack("C").first & 0x80).zero?
45+
int = 0
46+
47+
data.each_byte do |b|
48+
int = (int << 8) + (neg ? 255 - b : b)
49+
end
50+
51+
if neg
52+
(int + 1) * -1
53+
else
54+
int
55+
end
4756
elsif object_type == :oid
4857
# See X.690 pgh 8.19 for an explanation of this algorithm.
4958
# This is potentially not good enough. We may need a

Diff for: lib/net/ber/core_ext.rb

+3-10
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,10 @@ class Array
3333
end
3434
# :startdoc:
3535

36-
require 'net/ber/core_ext/bignum'
36+
require 'net/ber/core_ext/integer'
3737
# :stopdoc:
38-
class Bignum
39-
include Net::BER::Extensions::Bignum
40-
end
41-
# :startdoc:
42-
43-
require 'net/ber/core_ext/fixnum'
44-
# :stopdoc:
45-
class Fixnum
46-
include Net::BER::Extensions::Fixnum
38+
class Integer
39+
include Net::BER::Extensions::Integer
4740
end
4841
# :startdoc:
4942

Diff for: lib/net/ber/core_ext/bignum.rb

-22
This file was deleted.

Diff for: lib/net/ber/core_ext/fixnum.rb

-66
This file was deleted.

Diff for: lib/net/ber/core_ext/integer.rb

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- ruby encoding: utf-8 -*-
2+
##
3+
# BER extensions to the Integer class, affecting Fixnum and Bignum objects.
4+
module Net::BER::Extensions::Integer
5+
##
6+
# Converts the Integer to BER format.
7+
def to_ber
8+
"\002#{to_ber_internal}"
9+
end
10+
11+
##
12+
# Converts the Integer to BER enumerated format.
13+
def to_ber_enumerated
14+
"\012#{to_ber_internal}"
15+
end
16+
17+
##
18+
# Converts the Integer to BER length encoding format.
19+
def to_ber_length_encoding
20+
if self <= 127
21+
[self].pack('C')
22+
else
23+
i = [self].pack('N').sub(/^[\0]+/,"")
24+
[0x80 + i.length].pack('C') + i
25+
end
26+
end
27+
28+
##
29+
# Generate a BER-encoding for an application-defined INTEGER. Examples of
30+
# such integers are SNMP's Counter, Gauge, and TimeTick types.
31+
def to_ber_application(tag)
32+
[0x40 + tag].pack("C") + to_ber_internal
33+
end
34+
35+
##
36+
# Used to BER-encode the length and content bytes of an Integer. Callers
37+
# must prepend the tag byte for the contained value.
38+
def to_ber_internal
39+
# Compute the byte length, accounting for negative values requiring two's
40+
# complement.
41+
size = 1
42+
size += 1 until (((self < 0) ? ~self : self) >> (size * 8)).zero?
43+
44+
# Padding for positive, negative values. See section 8.5 of ITU-T X.690:
45+
# http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
46+
47+
# For positive integers, if most significant bit in an octet is set to one,
48+
# pad the result (otherwise it's decoded as a negative value).
49+
if self > 0 && (self & (0x80 << (size - 1) * 8)) > 0
50+
size += 1
51+
end
52+
53+
# And for negative integers, pad if the most significant bit in the octet
54+
# is not set to one (othwerise, it's decoded as positive value).
55+
if self < 0 && (self & (0x80 << (size - 1) * 8)) == 0
56+
size += 1
57+
end
58+
59+
# Store the size of the Integer in the result
60+
result = [size]
61+
62+
# Appends bytes to result, starting with higher orders first. Extraction
63+
# of bytes is done by right shifting the original Integer by an amount
64+
# and then masking that with 0xff.
65+
while size > 0
66+
# right shift size - 1 bytes, mask with 0xff
67+
result << ((self >> ((size - 1) * 8)) & 0xff)
68+
size -= 1
69+
end
70+
71+
result.pack('C*')
72+
end
73+
private :to_ber_internal
74+
end

Diff for: test/ber/test_ber.rb

+27-8
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,44 @@ def test_false
2626
0 => "\x02\x01\x00",
2727
1 => "\x02\x01\x01",
2828
127 => "\x02\x01\x7F",
29-
128 => "\x02\x01\x80",
30-
255 => "\x02\x01\xFF",
29+
128 => "\x02\x02\x00\x80",
30+
255 => "\x02\x02\x00\xFF",
3131
256 => "\x02\x02\x01\x00",
32-
65535 => "\x02\x02\xFF\xFF",
32+
65535 => "\x02\x03\x00\xFF\xFF",
3333
65536 => "\x02\x03\x01\x00\x00",
34-
16_777_215 => "\x02\x03\xFF\xFF\xFF",
34+
8388607 => "\x02\x03\x7F\xFF\xFF",
35+
8388608 => "\x02\x04\x00\x80\x00\x00",
36+
16_777_215 => "\x02\x04\x00\xFF\xFF\xFF",
3537
0x01000000 => "\x02\x04\x01\x00\x00\x00",
3638
0x3FFFFFFF => "\x02\x04\x3F\xFF\xFF\xFF",
3739
0x4FFFFFFF => "\x02\x04\x4F\xFF\xFF\xFF",
3840

3941
# Some odd samples...
40-
5 => "\002\001\005",
41-
500 => "\002\002\001\364",
42-
50_000 => "\x02\x02\xC3P",
43-
5_000_000_000 => "\002\005\001*\005\362\000"
42+
5 => "\x02\x01\x05",
43+
500 => "\x02\x02\x01\xf4",
44+
50_000 => "\x02\x03\x00\xC3\x50",
45+
5_000_000_000 => "\x02\x05\x01\x2a\x05\xF2\x00",
46+
47+
# negatives
48+
-1 => "\x02\x01\xFF",
49+
-127 => "\x02\x01\x81",
50+
-128 => "\x02\x01\x80",
51+
-255 => "\x02\x02\xFF\x01",
52+
-256 => "\x02\x02\xFF\x00",
53+
-65535 => "\x02\x03\xFF\x00\x01",
54+
-65536 => "\x02\x03\xFF\x00\x00",
55+
-65537 => "\x02\x03\xFE\xFF\xFF",
56+
-8388607 => "\x02\x03\x80\x00\x01",
57+
-8388608 => "\x02\x03\x80\x00\x00",
58+
-16_777_215 => "\x02\x04\xFF\x00\x00\x01",
4459
}.each do |number, expected_encoding|
4560
define_method "test_encode_#{number}" do
4661
assert_equal expected_encoding.b, number.to_ber
4762
end
63+
64+
define_method "test_decode_encoded_#{number}" do
65+
assert_equal number, expected_encoding.b.read_ber
66+
end
4867
end
4968

5069
# Round-trip encoding: This is mostly to be sure to cover Bignums well.

0 commit comments

Comments
 (0)