1package xmpp
2
3import (
4	"encoding/xml"
5
6	"github.com/coyim/coyim/xmpp/data"
7	"github.com/coyim/coyim/xmpp/errors"
8
9	. "gopkg.in/check.v1"
10)
11
12type SaslXMPPSuite struct{}
13
14var _ = Suite(&SaslXMPPSuite{})
15
16func (s *SaslXMPPSuite) Test_authenticate_failsIfPlainIsNotAnOption(c *C) {
17	conn := conn{}
18
19	err := conn.Authenticate("", "")
20	c.Assert(err, Equals, errUnsupportedSASLMechanism)
21}
22
23func (s *SaslXMPPSuite) Test_authenticate_authenticatesWithUsernameAndPassword(c *C) {
24	out := &mockConnIOReaderWriter{}
25	mockIn := &mockConnIOReaderWriter{read: []byte("<sasl:success xmlns:sasl='urn:ietf:params:xml:ns:xmpp-sasl'></sasl:success>")}
26	conn := conn{
27		rawOut: out,
28		in:     xml.NewDecoder(mockIn),
29		features: data.StreamFeatures{
30			Mechanisms: data.SaslMechanisms{
31				Mechanism: []string{"FOO", "PLAIN"},
32			},
33		},
34	}
35
36	e := conn.Authenticate("foo", "bar")
37	c.Assert(e, IsNil)
38	c.Assert(string(out.write), Equals, "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AGZvbwBiYXI=</auth>\n")
39}
40
41func (s *SaslXMPPSuite) Test_authenticate_handlesFailure(c *C) {
42	out := &mockConnIOReaderWriter{}
43	mockIn := &mockConnIOReaderWriter{read: []byte("<sasl:failure xmlns:sasl='urn:ietf:params:xml:ns:xmpp-sasl'><foobar></foobar></sasl:failure>")}
44	conn := conn{
45		rawOut: out,
46		in:     xml.NewDecoder(mockIn),
47		features: data.StreamFeatures{
48			Mechanisms: data.SaslMechanisms{
49				Mechanism: []string{"FOO", "PLAIN"},
50			},
51		},
52	}
53
54	e := conn.Authenticate("foo", "bar")
55	c.Assert(e.Error(), Equals, "xmpp: authentication failure: foobar")
56}
57
58func (s *SaslXMPPSuite) Test_authenticate_handlesWrongResponses(c *C) {
59	out := &mockConnIOReaderWriter{}
60	mockIn := &mockConnIOReaderWriter{read: []byte("<sasl:something xmlns:sasl='urn:ietf:params:xml:ns:xmpp-sasl'></sasl:something>")}
61	conn := conn{
62		rawOut: out,
63		in:     xml.NewDecoder(mockIn),
64		features: data.StreamFeatures{
65			Mechanisms: data.SaslMechanisms{
66				Mechanism: []string{"FOO", "PLAIN"},
67			},
68		},
69	}
70
71	e := conn.Authenticate("foo", "bar")
72	c.Assert(e, Equals, errors.ErrAuthenticationFailed)
73}
74
75func (s *SaslXMPPSuite) Test_digestMD5_authenticatesWithUsernameAndPassword(c *C) {
76	out := &mockConnIOReaderWriter{}
77	mockIn := &mockConnIOReaderWriter{read: []byte(
78		"<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09ImNveS5pbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz</challenge>\n" +
79			"<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA==</challenge>\n" +
80			"<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n",
81	)}
82
83	mockRand := &mockConnIOReaderWriter{read: []byte{
84		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
85	}}
86
87	conn := conn{
88		rawOut: out,
89		in:     xml.NewDecoder(mockIn),
90		rand:   mockRand,
91		features: data.StreamFeatures{
92			Mechanisms: data.SaslMechanisms{
93				Mechanism: []string{"DIGEST-MD5"},
94			},
95		},
96	}
97
98	expectedOut := "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'></auth>\n" +
99		"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>Y2hhcnNldD11dGYtOCx1c2VybmFtZT0iZm9vIixyZWFsbT0iY295LmltIixub25jZT0iT0E2TUc5dEVRR20yaGgiLG5jPTAwMDAwMDAxLGNub25jZT0iMDEwMjAzMDQwNTA2MDciLGRpZ2VzdC11cmk9InhtcHAvY295LmltIixyZXNwb25zZT00ZGVlODYyNjkxOTZiNmUxNGI5Zjc2OWZhYmQ5OTdiZCxxb3A9YXV0aA==</response>\n" +
100		"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></response>\n"
101
102	e := conn.Authenticate("foo", "bar")
103	c.Assert(e, IsNil)
104	c.Assert(string(out.write), Equals, expectedOut)
105}
106
107func (s *SaslXMPPSuite) Test_digestMD5_serverFailsToVerifyChallenge(c *C) {
108	out := &mockConnIOReaderWriter{}
109	mockIn := &mockConnIOReaderWriter{read: []byte(
110		"<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09ImNveS5pbSIsbm9uY2U9Ik9BNk1HOXRFUUdtMmhoIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz</challenge>\n" +
111			"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>",
112	)}
113
114	mockRand := &mockConnIOReaderWriter{read: []byte{
115		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
116	}}
117
118	conn := conn{
119		rawOut: out,
120		in:     xml.NewDecoder(mockIn),
121		rand:   mockRand,
122		features: data.StreamFeatures{
123			Mechanisms: data.SaslMechanisms{
124				Mechanism: []string{"DIGEST-MD5"},
125			},
126		},
127	}
128
129	expectedOut := "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'></auth>\n" +
130		"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>Y2hhcnNldD11dGYtOCx1c2VybmFtZT0iZm9vIixyZWFsbT0iY295LmltIixub25jZT0iT0E2TUc5dEVRR20yaGgiLG5jPTAwMDAwMDAxLGNub25jZT0iMDEwMjAzMDQwNTA2MDciLGRpZ2VzdC11cmk9InhtcHAvY295LmltIixyZXNwb25zZT00ZGVlODYyNjkxOTZiNmUxNGI5Zjc2OWZhYmQ5OTdiZCxxb3A9YXV0aA==</response>\n"
131
132	e := conn.Authenticate("foo", "bar")
133	c.Assert(e.Error(), Equals, "xmpp: unexpected <response> in urn:ietf:params:xml:ns:xmpp-sasl")
134	c.Assert(string(out.write), Equals, expectedOut)
135}
136
137func (s *SaslXMPPSuite) Test_scramSHA1Auth_authenticatesWithUsernameAndPassword(c *C) {
138	out := &mockConnIOReaderWriter{}
139	mockIn := &mockConnIOReaderWriter{read: []byte(
140		"<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cj03ZjI5MjhmOWRkYTU2ZDNyZmNOSFlKWTFaVnZXVnM3aixzPVFTWENSK1E2c2VrOGJmOTIsaT00MDk2</challenge>\n" +
141			"<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>dj1FL1E5T3BnUWhNd1hjWEhtNGU3N3Q4b2lIT0E9</success>\n",
142	)}
143
144	mockRand := &mockConnIOReaderWriter{read: []byte{
145		0x7f, 0x29, 0x28, 0xf9, 0xdd, 0xa5, 0x6d, 0xb1,
146		0x60, 0x38, 0xd4, 0x6f, 0xf6, 0xa9, 0x31, 0x75,
147		0xac, 0xb,
148	}}
149
150	conn := conn{
151		rawOut: out,
152		in:     xml.NewDecoder(mockIn),
153		rand:   mockRand,
154		features: data.StreamFeatures{
155			Mechanisms: data.SaslMechanisms{
156				Mechanism: []string{"SCRAM-SHA-1"},
157			},
158		},
159	}
160
161	expectedOut := "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='SCRAM-SHA-1'>biwsbj11c2VyLHI9N2YyOTI4ZjlkZGE1NmQ=</auth>\n" +
162		"<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>Yz1iaXdzLHI9N2YyOTI4ZjlkZGE1NmQzcmZjTkhZSlkxWlZ2V1ZzN2oscD1KbWYrcWVpSG5jTXRaSjZ3YnJ5ZFdOQ2N4V1E9</response>\n"
163
164	e := conn.Authenticate("user", "pencil")
165	c.Assert(e, IsNil)
166	c.Assert(string(out.write), Equals, expectedOut)
167}
168