kiro-ruby-sasl 0.0.4.0 → 0.0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 10e07b40466156392cccf8216fdb8ba1fe3a1a3a
4
- data.tar.gz: 4c112a5fb128343ff32cfce1f6896762d7abfcad
3
+ metadata.gz: d6120e3e4c0ff7622a3c9a1fc44b40c15a4383df
4
+ data.tar.gz: 1cd8c81fadca22b24db8c0ecbdbe4f9556864c42
5
5
  SHA512:
6
- metadata.gz: 3579aa92c4f44e391aa21613c1d6398f1548b495e7784da8f9972b2803e0a3836844b02c342359662e86a30437b5004e2f9a2b9aba062fad223bb0822cf6c2d4
7
- data.tar.gz: 6a7e9657313c56e2e5ce2166df38767d3bdfb800dfc3f601e459e3703d7b4234eacf43ea7c3b7ee511df9f6cc1bf46db1961420ab8eff761e98c927a25c42c01
6
+ metadata.gz: 33f868fd65da70183308305655ad62c0f5f35fc8c0c9493019043b2cf043f744557ed8d05564c82be104d7b27d5afc3e91e8c6d07277185ba31b3b4910fd05e8
7
+ data.tar.gz: 6eb02464ab684c0442e1c20ee325f63f78a63e56bafbd619c1764b73aa2a00718a630a8de3d231d47c50eb3c3e33769d1846b20ee010dcc9d3e6c7c26bf56e4e
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kiro-ruby-sasl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4.0
4
+ version: 0.0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephan Maka
@@ -18,21 +18,8 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - MIT-LICENSE
21
22
  - README.markdown
22
- - lib/sasl.rb
23
- - lib/sasl/anonymous.rb
24
- - lib/sasl/base.rb
25
- - lib/sasl/base64.rb
26
- - lib/sasl/digest_md5.rb
27
- - lib/sasl/gssapi.rb
28
- - lib/sasl/gssspnego.rb
29
- - lib/sasl/plain.rb
30
- - lib/sasl/socket.rb
31
- - spec/anonymous_spec.rb
32
- - spec/digest_md5_spec.rb
33
- - spec/mechanism_spec.rb
34
- - spec/plain_spec.rb
35
- - spec/socket_spec.rb
36
23
  homepage: http://github.com/luizluca/ruby-sasl/
37
24
  licenses: []
38
25
  metadata: {}
@@ -56,9 +43,4 @@ rubygems_version: 2.2.2
56
43
  signing_key:
57
44
  specification_version: 4
58
45
  summary: SASL client library
59
- test_files:
60
- - spec/mechanism_spec.rb
61
- - spec/anonymous_spec.rb
62
- - spec/plain_spec.rb
63
- - spec/digest_md5_spec.rb
64
- - spec/socket_spec.rb
46
+ test_files: []
data/lib/sasl.rb DELETED
@@ -1,5 +0,0 @@
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
@@ -1,14 +0,0 @@
1
- module SASL
2
- ##
3
- # SASL ANONYMOUS where you only send a username that may not get
4
- # evaluated by the server.
5
- #
6
- # RFC 4505:
7
- # http://tools.ietf.org/html/rfc4505
8
- class Anonymous < Mechanism
9
- def start
10
- @state = nil
11
- ['auth', preferences.username.to_s]
12
- end
13
- end
14
- end
data/lib/sasl/base.rb DELETED
@@ -1,126 +0,0 @@
1
- ##
2
- # RFC 4422:
3
- # http://tools.ietf.org/html/rfc4422
4
- module SASL
5
- ##
6
- # You must derive from class Preferences and overwrite methods you
7
- # want to implement.
8
- class Preferences
9
- attr_reader :config
10
- # key in config hash
11
- # authzid: Authorization identitiy ('username@domain' in XMPP)
12
- # realm: Realm ('domain' in XMPP)
13
- # digest-uri: : serv-type/serv-name | serv-type/host/serv-name ('xmpp/domain' in XMPP)
14
- # username
15
- # has_password?
16
- # allow_plaintext?
17
- # password
18
- # want_anonymous?
19
-
20
- def initialize (config)
21
- @config = {:has_password? => false, :allow_plaintext? => false, :want_anonymous? => false}.merge(config.dup)
22
- end
23
- def method_missing(sym, *args, &block)
24
- @config.send "[]", sym, &block
25
- end
26
- end
27
-
28
- ##
29
- # Will be raised by SASL.new_mechanism if mechanism passed to the
30
- # constructor is not known.
31
- class UnknownMechanism < RuntimeError
32
- def initialize(mechanism)
33
- @mechanism = mechanism
34
- end
35
-
36
- def to_s
37
- "Unknown mechanism: #{@mechanism.inspect}"
38
- end
39
- end
40
-
41
- def SASL.new(mechanisms, preferences)
42
- best_mechanism = if preferences.want_anonymous? && mechanisms.include?('ANONYMOUS')
43
- 'ANONYMOUS'
44
- elsif preferences.has_password?
45
- if mechanisms.include?('DIGEST-MD5')
46
- 'DIGEST-MD5'
47
- elsif preferences.allow_plaintext?
48
- 'PLAIN'
49
- else
50
- raise UnknownMechanism.new(mechanisms)
51
- end
52
- else
53
- raise UnknownMechanism.new(mechanisms)
54
- end
55
- new_mechanism(best_mechanism, preferences)
56
- end
57
-
58
- ##
59
- # Create a SASL Mechanism for the named mechanism
60
- #
61
- # mechanism:: [String] mechanism name
62
- def SASL.new_mechanism(mechanism, preferences)
63
- mechanism_class = case mechanism
64
- when 'DIGEST-MD5'
65
- DigestMD5
66
- when 'PLAIN'
67
- Plain
68
- when 'ANONYMOUS'
69
- Anonymous
70
- when 'GSS-SPNEGO'
71
- GssSpnego
72
- when 'GSSAPI'
73
- GssApi
74
- else
75
- raise UnknownMechanism.new(mechanism)
76
- end
77
- mechanism_class.new(mechanism, preferences)
78
- end
79
-
80
-
81
- class AbstractMethod < Exception # :nodoc:
82
- def to_s
83
- "Abstract method is not implemented"
84
- end
85
- end
86
-
87
- ##
88
- # Common functions for mechanisms
89
- #
90
- # Mechanisms implement handling of methods start and receive. They
91
- # return: [message_name, content] or nil where message_name is
92
- # either 'auth' or 'response' and content is either a string which
93
- # may transmitted encoded as Base64 or nil.
94
- class Mechanism
95
- attr_reader :mechanism
96
- attr_reader :preferences
97
-
98
- def initialize(mechanism, preferences)
99
- @mechanism = mechanism
100
- @preferences = preferences
101
- @state = nil
102
- end
103
-
104
- def success?
105
- @state == :success
106
- end
107
- def failure?
108
- @state == :failure
109
- end
110
-
111
- def start
112
- raise AbstractMethod
113
- end
114
-
115
-
116
- def receive(message_name, content)
117
- case message_name
118
- when 'success'
119
- @state = :success
120
- when 'failure'
121
- @state = :failure
122
- end
123
- nil
124
- end
125
- end
126
- end
data/lib/sasl/base64.rb DELETED
@@ -1,32 +0,0 @@
1
- # =XMPP4R - XMPP Library for Ruby
2
- # License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
3
- # Website::http://home.gna.org/xmpp4r/
4
-
5
- begin
6
- require 'base64'
7
- rescue LoadError
8
- ##
9
- # Ruby 1.9 has dropped the Base64 module,
10
- # this is a replacement
11
- #
12
- # We could replace all call by Array#pack('m')
13
- # and String#unpack('m'), but this module
14
- # improves readability.
15
- module Base64
16
- ##
17
- # Encode a String
18
- # data:: [String] Binary
19
- # result:: [String] Binary in Base64
20
- def self.encode64(data)
21
- [data].pack('m')
22
- end
23
-
24
- ##
25
- # Decode a Base64-encoded String
26
- # data64:: [String] Binary in Base64
27
- # result:: [String] Binary
28
- def self.decode64(data64)
29
- data64.unpack('m').first
30
- end
31
- end
32
- end
@@ -1,432 +0,0 @@
1
- require 'digest/md5'
2
-
3
- module SASL
4
- ##
5
- # RFC 2831:
6
- # http://tools.ietf.org/html/rfc2831
7
- class DigestMD5 < Mechanism
8
- begin
9
- require 'openssl'
10
- ##
11
- # Set to +true+ if OpenSSL is available and LDAPS is supported.
12
- HasOpenSSL = true
13
- rescue LoadError
14
- # :stopdoc:
15
- HasOpenSSL = false
16
- # :startdoc:
17
- end
18
-
19
- attr_writer :cnonce
20
-
21
- def initialize(*a)
22
- super
23
- @nonce_count = 0
24
- preferences.config[:secure_layer]=false if preferences.config[:secure_layer]==nil
25
- preferences.config[:confidentiality]=preferences.config[:secure_layer] if preferences.config[:confidentiality]==nil
26
- preferences.config[:cipher]="rc4" if preferences.config[:confidentiality] and not preferences.config[:cipher]
27
-
28
- if preferences.secure_layer and not HasOpenSSL
29
- raise ":secure_layer in #{self.class} depends on Openssl"
30
- end
31
- end
32
-
33
- def start
34
- @state = nil
35
- unless defined? @nonce
36
- ['auth', nil]
37
- else
38
- # reauthentication
39
- receive('challenge', '')
40
- end
41
- end
42
-
43
- def receive(message_name, content)
44
- case message_name
45
- when 'challenge'
46
- c = decode_challenge(content)
47
-
48
- unless c['rspauth']
49
- response = {}
50
- if defined?(@nonce) && response['nonce'].nil?
51
- # Could be reauth
52
- else
53
- # No reauth:
54
- @nonce_count = 0
55
- end
56
- @nonce ||= c['nonce']
57
- response['username'] = preferences.username
58
- response['realm'] = c['realm'] || preferences.realm
59
- response['nonce'] = @nonce
60
- @cnonce = generate_nonce unless defined? @cnonce
61
- response['cnonce'] = @cnonce
62
- @nc = next_nc
63
- response['nc'] = @nc
64
- @qop="auth"
65
- if c['qop']
66
- c_qop = c['qop'].split(",")
67
- else
68
- c_qop = []
69
- end
70
- if preferences.secure_layer and preferences.confidentiality and c_qop.include?("auth-conf")
71
- response['qop'] = "auth-conf"
72
- response['cipher'] = preferences.config[:cipher]
73
- elsif preferences.secure_layer and not preferences.confidentiality and c_qop.include?("auth-int")
74
- response['qop'] = "auth-int"
75
- else
76
- response['qop'] = 'auth'
77
- end
78
- @cipher=response['cipher']
79
- @qop=response['qop']
80
- response['digest-uri'] = preferences.digest_uri
81
- response['charset'] = 'utf-8'
82
- @algorithm = c['algorithm'] || "md5"
83
- response['response'] = response_value(@algorithm, response['nonce'], response['nc'], response['cnonce'], response['qop'], response['realm'])
84
- result=['response', encode_response(response)]
85
- else
86
- rspauth_expected = response_value(@algorithm, @nonce, @nc, @cnonce, @qop, '')
87
- #p :rspauth_received=>c['rspauth'], :rspauth_expected=>rspauth_expected
88
- if c['rspauth'] == rspauth_expected
89
- result=['response', nil]
90
- else
91
- # Bogus server?
92
- @state = :failure
93
- result=['failure', nil]
94
- end
95
- end
96
- when 'success'
97
- result=super
98
- if preferences.secure_layer
99
- securelayer_wrapper = proc {|io| DigestMD5SecureLayer.new(io, @ha1, @qop=="auth-conf", @cipher) }
100
- result=['securelayer_wrapper', securelayer_wrapper]
101
- end
102
- else
103
- # No challenge? Might be success or failure
104
- result=super
105
- end
106
- result
107
- end
108
-
109
- private
110
-
111
- def decode_challenge(text)
112
- challenge = {}
113
-
114
- state = :key
115
- key = ''
116
- value = ''
117
-
118
- text.scan(/./) do |ch|
119
- if state == :key
120
- if ch == '='
121
- state = :value
122
- elsif ch =~ /\S/
123
- key += ch
124
- end
125
-
126
- elsif state == :value
127
- if ch == ','
128
- challenge[key] = value
129
- key = ''
130
- value = ''
131
- state = :key
132
- elsif ch == '"' and value == ''
133
- state = :quote
134
- else
135
- value += ch
136
- end
137
-
138
- elsif state == :quote
139
- if ch == '"'
140
- state = :value
141
- else
142
- value += ch
143
- end
144
- end
145
- end
146
- challenge[key] = value unless key == ''
147
-
148
- #p :decode_challenge => challenge
149
- challenge
150
- end
151
-
152
- def encode_response(response)
153
- #p :encode_response => response
154
- response.collect do |k,v|
155
- if ['username', 'cnonce', 'nonce', 'digest-uri', 'authzid','realm','qop'].include? k
156
- v.sub!('\\', '\\\\')
157
- v.sub!('"', '\\"')
158
- "#{k}=\"#{v}\""
159
- else
160
- "#{k}=#{v}"
161
- end
162
- end.join(',')
163
- end
164
-
165
- def generate_nonce
166
- nonce = ''
167
- while nonce.length < 32
168
- c = rand(128).chr
169
- nonce += c if c =~ /^[a-zA-Z0-9]$/
170
- end
171
- nonce
172
- end
173
-
174
- ##
175
- # Function from RFC2831
176
- def self.h(s); Digest::MD5.digest(s); end
177
- def h(s) self.class.h(s); end
178
- ##
179
- # Function from RFC2831
180
- def self.hh(s); Digest::MD5.hexdigest(s); end
181
- def hh(s) self.class.hh(s); end
182
-
183
- ##
184
- # Calculate the value for the response field
185
- def response_value(algorithm, nonce, nc, cnonce, qop, realm, a2_prefix='AUTHENTICATE')
186
- #p :response_value => {:nonce=>nonce,
187
- # :cnonce=>cnonce,
188
- # :qop=>qop,
189
- # :username=>preferences.username,
190
- # :realm=>preferences.realm,
191
- # :password=>preferences.password,
192
- # :authzid=>preferences.authzid}
193
- a1 = "#{preferences.username}:#{realm}:#{preferences.password}"
194
- if algorithm.downcase == "md5-sess"
195
- a1 = "#{h(a1)}:#{nonce}:#{cnonce}"
196
- end
197
-
198
- if preferences.authzid
199
- a1 += ":#{preferences.authzid}"
200
- end
201
- @ha1=h(a1)
202
-
203
- a2="#{a2_prefix}:#{preferences.digest_uri}"
204
-
205
- qop = "missing" if not qop
206
-
207
- case qop.downcase
208
- when "auth-int", "auth-conf"
209
- a2 = "#{a2}:00000000000000000000000000000000"
210
- end
211
-
212
- case qop.downcase
213
- when "auth", "auth-int", "auth-conf"
214
- hh("#{hh(a1)}:#{nonce}:#{nc}:#{cnonce}:#{qop}:#{hh(a2)}")
215
- when "missing"
216
- hh("#{hh(a1)}:#{nonce}:#{hh(a2)}")
217
- else
218
- raise "Unknown qop=#{qop}"
219
- end
220
- end
221
-
222
- def next_nc
223
- @nonce_count += 1
224
- s = @nonce_count.to_s
225
- s = "0#{s}" while s.length < 8
226
- s
227
- end
228
- end
229
-
230
- class DigestMD5SecureLayer < SecureLayer
231
- class DigestMD5SecureLayerError < StandardError; end
232
-
233
- DIGEST_SESSKEY_MAGIC_CONS_C2S = "Digest session key to client-to-server signing key magic constant"
234
- DIGEST_SESSKEY_MAGIC_CONS_S2C = "Digest session key to server-to-client signing key magic constant"
235
- DIGEST_HA1_MAGIC_CONS_C2S = "Digest H(A1) to client-to-server sealing key magic constant"
236
- DIGEST_HA1_MAGIC_CONS_S2C = "Digest H(A1) to server-to-client sealing key magic constant"
237
- ONE = [1].pack("n")
238
-
239
- # DES does not use the last bit
240
- def self.des_key(key)
241
- key=key.bytes.to_a
242
- (0..(key.size)).map {|i|
243
- left = (i>=1 ? ((key[i-1]<<(8-i))%256) : 0)
244
- right = (i<key.size ? (key[i]>>i) : 0)
245
- (left | right).chr
246
- }.join
247
- end
248
-
249
- def initialize(io, ha1, confidentiality, cipher, is_server=false)
250
- super(io)
251
- @localseq=0
252
- @remoteseq=0
253
-
254
- @confidentiality=confidentiality
255
-
256
- if is_server
257
- @ki_send=self.class.kis(ha1)
258
- @ki_recv=self.class.kic(ha1)
259
- else
260
- @ki_send=self.class.kic(ha1)
261
- @ki_recv=self.class.kis(ha1)
262
- end
263
-
264
- if @confidentiality
265
- cipher.downcase!
266
-
267
- # adapt openssl 3des name
268
- ssl_cipher=cipher
269
- key_len=nil
270
- case cipher
271
- when "des"
272
- ssl_cipher="des-cbc"
273
- when "3des"
274
- ssl_cipher="des-ede-cbc"
275
- when /rc4-[0-9]*/
276
- key_bits=cipher.split("-").last.to_i
277
- raise "Non 8-bit multiple for key size: #{key_bits}" if not key_bits%8 == 0
278
- key_len=key_bits/8
279
- ssl_cipher="rc4"
280
- end
281
-
282
- @enc=OpenSSL::Cipher.new(ssl_cipher).encrypt
283
- @dec=OpenSSL::Cipher.new(ssl_cipher).decrypt
284
-
285
- # Force keylen size for rc4-* that is not rc-40 or rc4. Does it work?
286
- [@enc,@dec].each {|cp| cp.key_len = key_len } if key_len
287
-
288
- case cipher
289
- # For cipher "rc4-40" n is 5;
290
- when "rc4-40"
291
- n=5
292
- # for "rc4-56" n is 7;
293
- when "rc4-56"
294
- n=7
295
- # for the rest n is 16
296
- else
297
- n=16
298
- end
299
-
300
- if is_server
301
- @kc_send=self.class.kcs(ha1, n)
302
- @kc_recv=self.class.kcc(ha1, n)
303
- else
304
- @kc_send=self.class.kcc(ha1, n)
305
- @kc_recv=self.class.kcs(ha1, n)
306
- end
307
-
308
- # The key for the "rc-*" ciphers is all 16 bytes of Kcc or Kcs
309
- case cipher
310
- when /rc.*/
311
- key_len=16
312
- iv_len=0
313
- # the key for "des" is the first 7 bytes
314
- when "des"
315
- key_len=7
316
- iv_len=8
317
- when "3des"
318
- key_len=14
319
- iv_len=8
320
- end
321
-
322
- kc_send=@kc_send[0,key_len]
323
- kc_recv=@kc_recv[0,key_len]
324
-
325
- case cipher
326
- when "des"
327
- # (8 bit * 7 bytes) key must be expanded to (7-bit * 8 bytes),
328
- # skipping last bit
329
- kc_send=self.class.des_key(kc_send)
330
- kc_recv=self.class.des_key(kc_recv)
331
- key_len = 8
332
- # DES does not use padding here
333
- [@enc,@dec].each {|cp| cp.padding=0 }
334
- when "3des"
335
- # (8 bit * 7 bytes) key must be expanded to (7-bit * 8 bytes),
336
- # skipping last bit
337
- kc_send=self.class.des_key(kc_send[0,7])+self.class.des_key(kc_send[7,7])
338
- kc_recv=self.class.des_key(kc_recv[0,7])+self.class.des_key(kc_recv[7,7])
339
- key_len = 16
340
- # 3DES does not use padding here
341
- [@enc,@dec].each {|cp| cp.padding=0 }
342
- end
343
-
344
- [@enc,@dec].each {|cp| cp.key_len = key_len } if key_len
345
-
346
- @enc.key=kc_send
347
- @enc.iv=@kc_send[-iv_len,iv_len] if iv_len >0
348
- @dec.key=kc_recv
349
- @dec.iv=@kc_recv[-iv_len,iv_len] if iv_len >0
350
- end
351
- end
352
-
353
- def hm(ki, msg)
354
- OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('md5'), ki, msg)
355
- end
356
-
357
- def mac(ki, seqnum, msg)
358
- hm(ki, (seqnum + msg))[0..9]# + ONE + seqnum
359
- end
360
-
361
- def wrap(msg)
362
- seqnum=[@localseq].pack("N")
363
- if @confidentiality
364
- # SEAL(Ki, Kc, SeqNum, msg) = {CIPHER(Kc, {msg, pad, HMAC(Ki, {SeqNum, msg})[0..9])}), 0x0001, SeqNum}
365
- if @enc.block_size==1
366
- pad=""
367
- else
368
- pad_len = @enc.block_size - ((msg.size + 10) % @enc.block_size)
369
- pad=pad_len.chr*pad_len
370
- end
371
- buf=@enc.update(msg + pad + mac(@ki_send, seqnum, msg)) + ONE + seqnum
372
- else
373
- #MAC(Ki, SeqNum, msg) = (HMAC(Ki, {SeqNum, msg})[0..9], 0x0001, SeqNum)
374
- buf=msg + mac(@ki_send, seqnum, msg) + ONE + seqnum
375
- end
376
- @localseq+=1
377
- buf
378
- end
379
-
380
- def unwrap(buf)
381
- msg_seqnum=buf[-4..-1]
382
- # rfc2831 does not ask to check this
383
- #exp_seqnum=[@remoteseq].pack("N")
384
- #raise DigestMD5SecureLayerError, "Invalid remote sequence field! expected:#{@remoteseq}, got:#{msg_seqnum.unpack("N").first}" if not msg_seqnum == exp_seqnum
385
-
386
- msg_one=buf[-6..-5]
387
- raise DigestMD5SecureLayerError, "Invalid one field!" if not msg_one == ONE
388
-
389
- if @confidentiality
390
- msg_pad_mac=@dec.update(buf[0..-7])
391
- msg_mac=msg_pad_mac[-10..-1]
392
-
393
- if @enc.block_size==1
394
- msg=msg_pad_mac[0..-11]
395
- else
396
- pad_len=msg_pad_mac[-11].ord
397
- raise DigestMD5SecureLayerError, "Invalid pad size. Invalid crypto? key?" if not ((1..8).include?(pad_len))
398
- msg=msg_pad_mac[0..(-11-(pad_len))]
399
- end
400
- else
401
- msg=buf[0..-17]
402
- msg_mac=buf[-16..-7]
403
- end
404
- exp_mac=mac(@ki_recv, msg_seqnum, msg)
405
- raise DigestMD5SecureLayerError, "Invalid mac field!" if not msg_mac == exp_mac
406
-
407
- @remoteseq+=1
408
- msg
409
- end
410
-
411
- # Kic = MD5({H(A1), "Digest session key to client-to-server signing key magic constant"})
412
- def self.kic(ha1)
413
- DigestMD5.h(ha1 + DIGEST_SESSKEY_MAGIC_CONS_C2S)
414
- end
415
- def self.kis(ha1)
416
- DigestMD5.h(ha1 + DIGEST_SESSKEY_MAGIC_CONS_S2C)
417
- end
418
-
419
- # Kcs = MD5({H(A1)[0..n], "Digest H(A1) to server-to-client sealing key magic constant"})
420
- # FYI: Specs do not specify what 0..n means. According to cyrus sasl code, n is length and not position.
421
- # More like [0,n] for ruby
422
- def self.kcc(ha1,n=16)
423
- DigestMD5.h(ha1[0,n] + DIGEST_HA1_MAGIC_CONS_C2S)
424
- end
425
- def self.kcs(ha1,n=16)
426
- DigestMD5.h(ha1[0,n] + DIGEST_HA1_MAGIC_CONS_S2C)
427
- end
428
-
429
- end
430
- end
431
-
432
-
data/lib/sasl/gssapi.rb DELETED
@@ -1,92 +0,0 @@
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
-
@@ -1,38 +0,0 @@
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 DELETED
@@ -1,14 +0,0 @@
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
data/lib/sasl/socket.rb DELETED
@@ -1,88 +0,0 @@
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
@@ -1,19 +0,0 @@
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
@@ -1,244 +0,0 @@
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
@@ -1,65 +0,0 @@
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
data/spec/plain_spec.rb DELETED
@@ -1,39 +0,0 @@
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
data/spec/socket_spec.rb DELETED
@@ -1,49 +0,0 @@
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