kiro-ruby-sasl 0.0.4.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.
@@ -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