kiro-ruby-sasl 0.0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,92 @@
1
+ require 'sasl/socket'
2
+
3
+ module SASL
4
+
5
+ class GssApi < Mechanism
6
+
7
+ begin
8
+ # gssapi (1.1.2)
9
+ require 'gssapi'
10
+ HasGSSAPI = true
11
+ rescue LoadError
12
+ # :stopdoc:
13
+ HasGSSAPI = false
14
+ # :startdoc:
15
+ end
16
+
17
+ def initialize(*args)
18
+ raise LoadError.new("You need gssapi gem in order to use this class") if not HasGSSAPI
19
+ super(*args)
20
+ preferences.config[:gss_opts] = {} if not preferences.config.include? :gss_opts
21
+ preferences.config[:secure_layer] = false if preferences.config[:secure_layer]==nil
22
+ end
23
+
24
+ def start
25
+ @state = :authneg
26
+ (@service,@host)=preferences.digest_uri.split("/")
27
+ @cli = GSSAPI::Simple.new(@host, @service)
28
+ tok = @cli.init_context(nil, preferences.gss_opts)
29
+ ['auth', tok ]
30
+ end
31
+
32
+ def receive(message_name, content)
33
+ case message_name
34
+ when 'challenge'
35
+ case @state
36
+ when :authneg
37
+ if @cli.init_context(content, preferences.gss_opts)
38
+ if false #http
39
+ @state = :waiting_result
40
+ else
41
+ @state = :ssfcap
42
+ end
43
+ else
44
+ @state = :failure
45
+ end
46
+ response = nil
47
+ when :ssfcap
48
+ ssf = @cli.unwrap_message(content)
49
+ if not ssf.size == 4
50
+ raise "token too short or long (#{ssf.size}). Should be 4."
51
+ end
52
+
53
+ if not preferences.secure_layer
54
+ # No security wanted
55
+ response = @cli.wrap_message(0)
56
+ else
57
+ response = @cli.wrap_message(ssf)
58
+ end
59
+ @state = :waiting_result
60
+ else
61
+ raise "Invalid state #{@state}. Did you called start method?"
62
+ end
63
+ result=['response', response]
64
+ when 'success'
65
+ result=super
66
+ if preferences.secure_layer
67
+ securelayer_wrapper = proc {|io| SASL::GssSecureLayer.new(io,@cli) }
68
+ result=['securelayer_wrapper', securelayer_wrapper]
69
+ end
70
+ else
71
+ result=super
72
+ end
73
+ result
74
+ end
75
+ end
76
+
77
+ class GssSecureLayer < SecureLayer
78
+ def initialize(io,ctx)
79
+ super(io)
80
+ @ctx=ctx
81
+ end
82
+
83
+ def wrap(buf)
84
+ @ctx.wrap_message(buf)
85
+ end
86
+
87
+ def unwrap(buf)
88
+ @ctx.unwrap_message(buf)
89
+ end
90
+ end
91
+ end
92
+
@@ -0,0 +1,38 @@
1
+ module SASL
2
+
3
+ class GssSpnego < Mechanism
4
+
5
+ begin
6
+ # rubyntlm (0.3.3)
7
+ require 'net/ntlm'
8
+ HasNTLM = true
9
+ rescue LoadError
10
+ # :stopdoc:
11
+ HasNTLM = false
12
+ # :startdoc:
13
+ end
14
+
15
+ def initialize(*args)
16
+ raise LoadError.new("You need rubyntlm gem in order to use this class") if not HasNTLM
17
+ super(*args)
18
+ end
19
+
20
+ def start
21
+ @state = nil
22
+ ['auth', Net::NTLM::Message::Type1.new.serialize]
23
+ end
24
+
25
+ def receive(message_name, content)
26
+ if message_name == 'challenge'
27
+ t2_msg = Net::NTLM::Message.parse(content)
28
+ t3_msg = t2_msg.response({ :user => preferences.username, :password => preferences.password},
29
+ { :ntlmv2 => true })
30
+ p message_name
31
+ ['response', t3_msg.serialize]
32
+ else
33
+ super
34
+ end
35
+ end
36
+ end
37
+ end
38
+
data/lib/sasl/plain.rb ADDED
@@ -0,0 +1,14 @@
1
+ module SASL
2
+ ##
3
+ # RFC 4616:
4
+ # http://tools.ietf.org/html/rfc4616
5
+ class Plain < Mechanism
6
+ def start
7
+ @state = nil
8
+ message = [preferences.authzid.to_s,
9
+ preferences.username,
10
+ preferences.password].join("\000")
11
+ ['auth', message]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,88 @@
1
+ module SASL
2
+
3
+ class SecureLayer
4
+ attr_reader :io
5
+
6
+ def initialize(io)
7
+ @io=io
8
+ end
9
+
10
+ def write(buf)
11
+ wbuf = wrap(buf)
12
+ @io.syswrite([wbuf.size].pack("N"))
13
+ @io.syswrite(wbuf)
14
+ end
15
+
16
+ def read
17
+ bsize=@io.sysread(4)
18
+ raise "SASL Buffer size is nil!" if bsize==nil
19
+ size=bsize.unpack("N")
20
+ buf=@io.sysread(size.first)
21
+ unwrap(buf)
22
+ end
23
+
24
+ def wrap(buf)
25
+ raise AbstractMethod
26
+ end
27
+
28
+ def unwrap(buf)
29
+ raise AbstractMethod
30
+ end
31
+
32
+ def close
33
+ @io.close
34
+ end
35
+ end
36
+
37
+ module Buffering
38
+ begin
39
+ require 'openssl'
40
+ HasOpenSSL = true
41
+ rescue LoadError
42
+ # :stopdoc:
43
+ HasOpenSSL = false
44
+ # :startdoc:
45
+ end
46
+
47
+ # When an object is extended
48
+ def self.extended(base)
49
+ class << base
50
+ Buffering.included(self)
51
+ end
52
+ base.initialize_buffering
53
+ end
54
+
55
+ # When a class is extended
56
+ def self.included(base)
57
+ raise LoadError.new("SASL::Buffering depends on OpenSSL::Buffering") if not HasOpenSSL
58
+ base.class_eval do
59
+ alias_method :nonbuf_read, :read
60
+ alias_method :nonbuf_write, :write
61
+ alias_method :nonbuf_close, :close
62
+
63
+ # OpenSSL::Buffering replaces initialize. I should save it
64
+ alias_method :orig_initialize, :initialize
65
+ include OpenSSL::Buffering
66
+ alias_method :initialize_buffering, :initialize
67
+ public :initialize_buffering
68
+
69
+ def initialize(*args)
70
+ orig_initialize(*args)
71
+ initialize_buffering
72
+ end
73
+ end
74
+ end
75
+
76
+ def sysread(size)
77
+ nonbuf_read
78
+ end
79
+
80
+ def syswrite(buf)
81
+ nonbuf_write(buf)
82
+ end
83
+
84
+ def sysclose
85
+ nonbuf_close
86
+ end
87
+ end
88
+ end
data/lib/sasl.rb ADDED
@@ -0,0 +1,5 @@
1
+ SASL_PATH = File.dirname(__FILE__) + "/sasl"
2
+ require 'sasl/base'
3
+ Dir.foreach(SASL_PATH) do |f|
4
+ require "#{SASL_PATH}/#{f}" if f =~ /^[^\.].+\.rb$/ && f != 'base.rb'
5
+ end
@@ -0,0 +1,19 @@
1
+ require 'sasl'
2
+ require 'rspec'
3
+
4
+ describe SASL::Anonymous do
5
+ class MyAnonymousPreferences < SASL::Preferences
6
+ def username
7
+ 'bob'
8
+ end
9
+ end
10
+ preferences = MyAnonymousPreferences.new({})
11
+
12
+ it 'should authenticate anonymously' do
13
+ sasl = SASL::Anonymous.new('ANONYMOUS', preferences)
14
+ sasl.start.should == ['auth', 'bob']
15
+ sasl.success?.should == false
16
+ sasl.receive('success', nil).should == nil
17
+ sasl.success?.should == true
18
+ end
19
+ end
@@ -0,0 +1,244 @@
1
+ require 'sasl'
2
+ require 'rspec'
3
+
4
+ describe SASL::DigestMD5 do
5
+ # Preferences from http://tools.ietf.org/html/rfc2831#section-4
6
+ class MyDigestMD5Preferences < SASL::Preferences
7
+ attr_writer :serv_type
8
+ def realm
9
+ 'elwood.innosoft.com'
10
+ end
11
+ def digest_uri
12
+ "#{@serv_type}/elwood.innosoft.com"
13
+ end
14
+ def username
15
+ 'chris'
16
+ end
17
+ def has_password?
18
+ true
19
+ end
20
+ def password
21
+ 'secret'
22
+ end
23
+ end
24
+ preferences = MyDigestMD5Preferences.new({})
25
+
26
+ it 'should authenticate (1)' do
27
+ preferences.serv_type = 'imap'
28
+ sasl = SASL::DigestMD5.new('DIGEST-MD5', preferences)
29
+ sasl.start.should == ['auth', nil]
30
+ sasl.cnonce = 'OA6MHXh6VqTrRk'
31
+ response = sasl.receive('challenge',
32
+ 'realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",
33
+ algorithm=md5-sess,charset=utf-8')
34
+ response[0].should == 'response'
35
+ response[1].should =~ /charset="?utf-8"?/
36
+ response[1].should =~ /username="?chris"?/
37
+ response[1].should =~ /realm="?elwood.innosoft.com"?/
38
+ response[1].should =~ /nonce="?OA6MG9tEQGm2hh"?/
39
+ response[1].should =~ /nc="?00000001"?/
40
+ response[1].should =~ /cnonce="?OA6MHXh6VqTrRk"?/
41
+ response[1].should =~ /digest-uri="?imap\/elwood.innosoft.com"?/
42
+ response[1].should =~ /response=d388dad90d4bbd760a152321f2143af7"?/
43
+ response[1].should =~ /"?qop="?auth"?/
44
+
45
+ sasl.receive('challenge',
46
+ 'rspauth=ea40f60335c427b5527b84dbabcdfffd').should ==
47
+ ['response', nil]
48
+ sasl.receive('success', nil).should == nil
49
+ sasl.success?.should == true
50
+ end
51
+
52
+ it 'should authenticate (2)' do
53
+ preferences.serv_type = 'acap'
54
+ sasl = SASL::DigestMD5.new('DIGEST-MD5', preferences)
55
+ sasl.start.should == ['auth', nil]
56
+ sasl.cnonce = 'OA9BSuZWMSpW8m'
57
+ response = sasl.receive('challenge',
58
+ 'realm="elwood.innosoft.com",nonce="OA9BSXrbuRhWay",qop="auth",
59
+ algorithm=md5-sess,charset=utf-8')
60
+ response[0].should == 'response'
61
+ response[1].should =~ /charset="?utf-8"?/
62
+ response[1].should =~ /username="?chris"?/
63
+ response[1].should =~ /realm="?elwood.innosoft.com"?/
64
+ response[1].should =~ /nonce="?OA9BSXrbuRhWay"?/
65
+ response[1].should =~ /nc="?00000001"?/
66
+ response[1].should =~ /cnonce="?OA9BSuZWMSpW8m"?/
67
+ response[1].should =~ /digest-uri="?acap\/elwood.innosoft.com"?/
68
+ response[1].should =~ /response=6084c6db3fede7352c551284490fd0fc"?/
69
+ response[1].should =~ /"?qop="?auth"?/
70
+
71
+ sasl.receive('challenge',
72
+ 'rspauth=2f0b3d7c3c2e486600ef710726aa2eae').should ==
73
+ ['response', nil]
74
+ sasl.receive('success', nil).should == nil
75
+ sasl.success?.should == true
76
+ end
77
+
78
+ it 'should reauthenticate' do
79
+ preferences.serv_type = 'imap'
80
+ sasl = SASL::DigestMD5.new('DIGEST-MD5', preferences)
81
+ sasl.start.should == ['auth', nil]
82
+ sasl.cnonce = 'OA6MHXh6VqTrRk'
83
+ sasl.receive('challenge',
84
+ 'realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",
85
+ algorithm=md5-sess,charset=utf-8')
86
+ # reauth:
87
+ response = sasl.start
88
+ response[0].should == 'response'
89
+ response[1].should =~ /charset="?utf-8"?/
90
+ response[1].should =~ /username="?chris"?/
91
+ response[1].should =~ /realm="?elwood.innosoft.com"?/
92
+ response[1].should =~ /nonce="?OA6MG9tEQGm2hh"?/
93
+ response[1].should =~ /nc="?00000002"?/
94
+ response[1].should =~ /cnonce="?OA6MHXh6VqTrRk"?/
95
+ response[1].should =~ /digest-uri="?imap\/elwood.innosoft.com"?/
96
+ response[1].should =~ /response=b0b5d72a400655b8306e434566b10efb"?/ # my own result
97
+ response[1].should =~ /"?qop="?auth"?/
98
+ end
99
+
100
+ it 'should fail' do
101
+ sasl = SASL::DigestMD5.new('DIGEST-MD5', preferences)
102
+ sasl.start.should == ['auth', nil]
103
+ sasl.receive('failure', 'EPIC FAIL')
104
+ sasl.failure?.should == true
105
+ sasl.success?.should == false
106
+ end
107
+ end
108
+
109
+ describe SASL::DigestMD5SecureLayer do
110
+ HA1="1234567890ABCDEF" if not defined? HA1
111
+ MSG="plaintext" if not defined? MSG
112
+
113
+ it 'DigestMD5SecureLayer.kic should generate correct KIC' do
114
+ result = SASL::DigestMD5SecureLayer.kic(HA1)
115
+ expected = "\xC6 6/\xFD\xD5\x0F\xF6E7=\x82Y\x16\r\x03"
116
+ expected.bytes.to_a.should == result.bytes.to_a
117
+ end
118
+
119
+ it 'DigestMD5SecureLayer.kcc should generate correct KIS' do
120
+ result = SASL::DigestMD5SecureLayer.kis(HA1)
121
+ expected = "\x10\xBED)v\x1E#I\xA5\xF8RR\xF4\x80\t_"
122
+ expected.bytes.to_a.should == result.bytes.to_a
123
+ end
124
+
125
+ it 'DigestMD5SecureLayer.kcc should generate correct KCC (n=16)' do
126
+ result = SASL::DigestMD5SecureLayer.kcc(HA1,16)
127
+ expected = "a\xA2\xF5\x9C\xD2g\x85\xF3\eOZ\x10^\xF9\x97v"
128
+ expected.bytes.to_a.should == result.bytes.to_a
129
+ end
130
+
131
+ it 'DigestMD5SecureLayer.kcc should generate correct KCC (n=7)' do
132
+ result = SASL::DigestMD5SecureLayer.kcc(HA1,7)
133
+ expected = "E{\xC1\xE4\x14W\x12\xE4\x88d=\xA5\xFCY5M"
134
+ expected.bytes.to_a.should == result.bytes.to_a
135
+ end
136
+
137
+ it 'DigestMD5SecureLayer.kcc should generate correct KCC (n=5)' do
138
+ result = SASL::DigestMD5SecureLayer.kcc(HA1,5)
139
+ expected = "\xD3\xBCK\x86\xF4\xC1\xEF\xA9\xAE\xCC\xB9K\xA4KJ\x99"
140
+ expected.bytes.to_a.should == result.bytes.to_a
141
+ end
142
+
143
+ it 'DigestMD5SecureLayer.kcs should generate correct KCS (n=5)' do
144
+ result = SASL::DigestMD5SecureLayer.kcs(HA1,16)
145
+ expected = "\x18g\xDA\xD2\x99\xC41\xD3\x11\x0E\x8B\xA2\xAC&\x82\xA3"
146
+ expected.bytes.to_a.should == result.bytes.to_a
147
+ end
148
+
149
+ ###################
150
+ # Integrity tests
151
+ ###################
152
+ function="DigestMD5SecureLayer.wrap (integrity mode, client)"
153
+ io=StringIO.new("", "w")
154
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, false, nil, false)
155
+ dg.write(MSG)
156
+ buf=io.string
157
+
158
+ # Client mode (write)
159
+ it 'should generate correct bufsize' do
160
+ result = buf[0,4]
161
+ expected = "\x00\x00\x00\x19"
162
+ expected.bytes.to_a.should == result.bytes.to_a
163
+ end
164
+ # I should tested this twice to see if it increments
165
+ it 'should generate correct seq number' do
166
+ result = buf[-4,4]
167
+ expected = "\x00\x00\x00\x00"
168
+ expected.bytes.to_a.should == result.bytes.to_a
169
+ end
170
+ it 'should generate one number' do
171
+ result = buf[-6,2]
172
+ expected = "\x00\x01"
173
+ expected.bytes.to_a.should == result.bytes.to_a
174
+ end
175
+ it 'should generate correct MAC field ' do
176
+ result = buf[-16,10]
177
+ expected = "b\xD6\xD1#\xCF\xCE7\x97\x1D\xD4"
178
+ expected.bytes.to_a.should == result.bytes.to_a
179
+ end
180
+ it 'should preserve original msg content' do
181
+ result = buf[4,9]
182
+ expected = MSG
183
+ expected.bytes.to_a.should == result.bytes.to_a
184
+ end
185
+
186
+ # Server mode (read)
187
+ it 'should return only the original msg content' do
188
+ io=StringIO.new(buf, "r")
189
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, false, nil, true)
190
+ result = dg.read
191
+ expected = MSG
192
+ expected.bytes.to_a.should == result.bytes.to_a
193
+ end
194
+ it 'should raise exception when msgsize is changed' do
195
+ buf_def=buf.clone
196
+ buf_def[4]="\x18"
197
+ io=StringIO.new(buf_def, "r")
198
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, false, nil, true)
199
+ expect { dg.read }.to raise_error SASL::DigestMD5SecureLayer::DigestMD5SecureLayerError
200
+ end
201
+ it 'should raise exception when one field is missing' do
202
+ buf_def=buf.clone
203
+ buf_def[-5]="\x02"
204
+ io=StringIO.new(buf_def, "r")
205
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, false, nil, true)
206
+ expect { dg.read }.to raise_error SASL::DigestMD5SecureLayer::DigestMD5SecureLayerError
207
+ end
208
+ it 'should raise exception when MAC is changed' do
209
+ buf_def=buf.clone
210
+ buf_def[-16]="\x0F"
211
+ io=StringIO.new(buf_def, "r")
212
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, false, nil, true)
213
+ expect { dg.read }.to raise_error SASL::DigestMD5SecureLayer::DigestMD5SecureLayerError
214
+ end
215
+ it 'should raise exception when msg content is changed' do
216
+ buf_def=buf.clone
217
+ buf_def[4]="P"
218
+ io=StringIO.new(buf_def, "r")
219
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, false, nil, true)
220
+ expect { dg.read }.to raise_error SASL::DigestMD5SecureLayer::DigestMD5SecureLayerError
221
+ end
222
+
223
+ ###########################
224
+ # Confidentiality tests
225
+ ###########################
226
+ ["rc4","rc4-40","rc4-56","des","3des"].each do
227
+ |cipher|
228
+ it "should encrypt and decrypt messages with #{cipher} cipher" do
229
+ # client
230
+ io=StringIO.new("", "w")
231
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, true, cipher, false)
232
+ dg.write(MSG)
233
+ buf=io.string
234
+
235
+ # server
236
+ io=StringIO.new(buf, "r")
237
+ dg=SASL::DigestMD5SecureLayer.new(io, HA1, true, cipher, true)
238
+ result = dg.read
239
+ expected = MSG
240
+ expected.bytes.to_a.should == result.bytes.to_a
241
+ end
242
+ end
243
+
244
+ end
@@ -0,0 +1,65 @@
1
+ require 'sasl'
2
+ require 'rspec'
3
+
4
+ describe SASL do
5
+ it 'should know DIGEST-MD5' do
6
+ sasl = SASL.new_mechanism('DIGEST-MD5', SASL::Preferences.new({}))
7
+ sasl.should be_an_instance_of SASL::DigestMD5
8
+ end
9
+ it 'should know PLAIN' do
10
+ sasl = SASL.new_mechanism('PLAIN', SASL::Preferences.new({}))
11
+ sasl.should be_an_instance_of SASL::Plain
12
+ end
13
+ it 'should know ANONYMOUS' do
14
+ sasl = SASL.new_mechanism('ANONYMOUS', SASL::Preferences.new({}))
15
+ sasl.should be_an_instance_of SASL::Anonymous
16
+ end
17
+ it 'should know GSSAPI' do
18
+ sasl = SASL.new_mechanism('GSSAPI', SASL::Preferences.new({}))
19
+ sasl.should be_an_instance_of SASL::GssApi
20
+ end
21
+ it 'should know GSS-SPNEGO' do
22
+ sasl = SASL.new_mechanism('GSS-SPNEGO', SASL::Preferences.new({}))
23
+ sasl.should be_an_instance_of SASL::GssSpnego
24
+ end
25
+
26
+ it 'should choose ANONYMOUS' do
27
+ preferences = SASL::Preferences.new({})
28
+ class << preferences
29
+ def want_anonymous?
30
+ true
31
+ end
32
+ end
33
+ SASL.new(%w(PLAIN DIGEST-MD5 ANONYMOUS), preferences).should be_an_instance_of SASL::Anonymous
34
+ end
35
+ it 'should choose DIGEST-MD5' do
36
+ preferences = SASL::Preferences.new({})
37
+ class << preferences
38
+ def has_password?
39
+ true
40
+ end
41
+ end
42
+ SASL.new(%w(PLAIN DIGEST-MD5 ANONYMOUS), preferences).should be_an_instance_of SASL::DigestMD5
43
+ end
44
+ it 'should choose PLAIN' do
45
+ preferences = SASL::Preferences.new({})
46
+ class << preferences
47
+ def has_password?
48
+ true
49
+ end
50
+ def allow_plaintext?
51
+ true
52
+ end
53
+ end
54
+ SASL.new(%w(PLAIN ANONYMOUS), preferences).should be_an_instance_of SASL::Plain
55
+ end
56
+ it 'should disallow PLAIN by default' do
57
+ preferences = SASL::Preferences.new({})
58
+ class << preferences
59
+ def has_password?
60
+ true
61
+ end
62
+ end
63
+ lambda { SASL.new(%w(PLAIN ANONYMOUS), preferences) }.should raise_error(SASL::UnknownMechanism)
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ require 'sasl'
2
+ require 'rspec'
3
+
4
+ describe SASL::Plain do
5
+ class MyPlainPreferences < SASL::Preferences
6
+ def authzid
7
+ 'bob@example.com'
8
+ end
9
+ def username
10
+ 'bob'
11
+ end
12
+ def has_password?
13
+ true
14
+ end
15
+ def password
16
+ 's3cr3t'
17
+ end
18
+ end
19
+ preferences = MyPlainPreferences.new({})
20
+
21
+ it 'should authenticate' do
22
+ sasl = SASL::Plain.new('PLAIN', preferences)
23
+ sasl.start.should == ['auth', "bob@example.com\000bob\000s3cr3t"]
24
+ sasl.success?.should == false
25
+ sasl.receive('success', nil).should == nil
26
+ sasl.failure?.should == false
27
+ sasl.success?.should == true
28
+ end
29
+
30
+ it 'should recognize failure' do
31
+ sasl = SASL::Plain.new('PLAIN', preferences)
32
+ sasl.start.should == ['auth', "bob@example.com\000bob\000s3cr3t"]
33
+ sasl.success?.should == false
34
+ sasl.failure?.should == false
35
+ sasl.receive('failure', 'keep-idiots-out').should == nil
36
+ sasl.failure?.should == true
37
+ sasl.success?.should == false
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ require "sasl"
2
+ require 'socket'
3
+ require 'rspec'
4
+
5
+ describe SASL::SecureLayer do
6
+
7
+ class PlainSecureLayer < SASL::SecureLayer
8
+ def wrap(buf)
9
+ buf
10
+ end
11
+ def unwrap(buf)
12
+ buf
13
+ end
14
+ end
15
+
16
+ MSG="plaintext" if not defined? MSG
17
+ io=StringIO.new("","w")
18
+ sl=PlainSecureLayer.new(io)
19
+ sl.write(MSG)
20
+ buf=io.string
21
+
22
+ it 'should send msg with correct size' do
23
+ buf[0,4].unpack("N").first.should == MSG.size
24
+ end
25
+ it 'should send msg with the correct content' do
26
+ msg=buf[4..-1]
27
+ msg.bytes.to_a.should == MSG.bytes.to_a
28
+ end
29
+
30
+ it 'should recv msg with the correct content' do
31
+ io=StringIO.new(buf,"r")
32
+ sl=PlainSecureLayer.new(io)
33
+ msg=sl.read
34
+ msg.bytes.to_a.should == MSG.bytes.to_a
35
+ end
36
+
37
+ class PlainSecureLayerBuffered < PlainSecureLayer
38
+ include SASL::Buffering
39
+ end
40
+
41
+ it 'should recv msg with the correct content, even when buffering' do
42
+ io=StringIO.new(buf,"r")
43
+ sl=PlainSecureLayerBuffered.new(io)
44
+ MSG.each_char {|chr|
45
+ sl.getc.ord.should == chr.ord
46
+ }
47
+ end
48
+
49
+ end