5 cryptorand "crypto/rand"
19func base64Decode(s string) []byte {
20 buf, err := base64.StdEncoding.DecodeString(s)
27func tcheck(t *testing.T, err error, msg string) {
30 t.Fatalf("%s: %s", msg, err)
34func TestSCRAMSHA1Server(t *testing.T) {
36 salt := base64Decode("QSXCR+Q6sek8bf92")
37 saltedPassword, err := SaltPassword(sha1.New, "pencil", salt, 4096)
38 tcheck(t, err, "saltpassword")
40 server, err := NewServer(sha1.New, []byte("n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL"), nil, false)
41 server.serverNonceOverride = "3rfcNHYJY1ZVvWVs7j"
42 tcheck(t, err, "newserver")
43 resp, err := server.ServerFirst(4096, salt)
44 tcheck(t, err, "server first")
45 if resp != "r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096" {
46 t.Fatalf("bad server first")
48 serverFinal, err := server.Finish([]byte("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="), saltedPassword)
49 tcheck(t, err, "finish")
50 if serverFinal != "v=rmF9pqV8S7suAoZWja4dJRkFsKQ=" {
51 t.Fatalf("bad server final")
55func TestSCRAMSHA256Server(t *testing.T) {
57 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
58 saltedPassword, err := SaltPassword(sha256.New, "pencil", salt, 4096)
59 tcheck(t, err, "saltpassword")
61 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"), nil, false)
62 server.serverNonceOverride = "%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0"
63 tcheck(t, err, "newserver")
64 resp, err := server.ServerFirst(4096, salt)
65 tcheck(t, err, "server first")
66 if resp != "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096" {
67 t.Fatalf("bad server first")
69 serverFinal, err := server.Finish([]byte("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
70 tcheck(t, err, "finish")
71 if serverFinal != "v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=" {
72 t.Fatalf("bad server final")
76// Bad attempt with wrong password.
77func TestScramServerBadPassword(t *testing.T) {
78 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
79 saltedPassword, err := SaltPassword(sha256.New, "marker", salt, 4096)
80 tcheck(t, err, "saltpassword")
82 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"), nil, false)
83 server.serverNonceOverride = "%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0"
84 tcheck(t, err, "newserver")
85 _, err = server.ServerFirst(4096, salt)
86 tcheck(t, err, "server first")
87 _, err = server.Finish([]byte("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
88 if !errors.Is(err, ErrInvalidProof) {
89 t.Fatalf("got %v, expected ErrInvalidProof", err)
93// Bad attempt with different number of rounds.
94func TestScramServerBadIterations(t *testing.T) {
95 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
96 saltedPassword, err := SaltPassword(sha256.New, "pencil", salt, 2048)
97 tcheck(t, err, "saltpassword")
99 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"), nil, false)
100 server.serverNonceOverride = "%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0"
101 tcheck(t, err, "newserver")
102 _, err = server.ServerFirst(4096, salt)
103 tcheck(t, err, "server first")
104 _, err = server.Finish([]byte("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
105 if !errors.Is(err, ErrInvalidProof) {
106 t.Fatalf("got %v, expected ErrInvalidProof", err)
110// Another attempt but with a randomly different nonce.
111func TestScramServerBad(t *testing.T) {
112 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
113 saltedPassword, err := SaltPassword(sha256.New, "pencil", salt, 4096)
114 tcheck(t, err, "saltpassword")
116 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"), nil, false)
117 tcheck(t, err, "newserver")
118 _, err = server.ServerFirst(4096, salt)
119 tcheck(t, err, "server first")
120 _, err = server.Finish([]byte("c=biws,r="+server.nonce+",p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
121 if !errors.Is(err, ErrInvalidProof) {
122 t.Fatalf("got %v, expected ErrInvalidProof", err)
126// Test with multiple extensions.
127func TestScramExtensions(t *testing.T) {
128 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
129 saltedPassword, err := SaltPassword(sha256.New, "pencil", salt, 4096)
130 tcheck(t, err, "saltpassword")
132 // Client-first with two trailing extensions (a=1,b=2).
133 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO,a=1,b=2"), nil, false)
134 tcheck(t, err, "newserver with client-first extensions")
135 if server.Authentication != "user" {
136 t.Fatalf("got username %q, expected %q", server.Authentication, "user")
138 if server.clientNonce != "rOprNGfwEbeRWgbNEkqO" {
139 t.Fatalf("got client nonce %q, expected %q", server.clientNonce, "rOprNGfwEbeRWgbNEkqO")
142 _, err = server.ServerFirst(4096, salt)
143 tcheck(t, err, "server first")
145 // Client-final with two extensions (a=1,b=2).
146 _, err = server.Finish([]byte("c=biws,r="+server.nonce+",a=1,b=2,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
147 if !errors.Is(err, ErrInvalidProof) {
148 t.Fatalf("got %v, expected ErrInvalidProof", err)
152func TestScramClient(t *testing.T) {
153 c := NewClient(sha256.New, "user", "", false, nil)
154 c.clientNonce = "rOprNGfwEbeRWgbNEkqO"
155 clientFirst, err := c.ClientFirst()
156 tcheck(t, err, "ClientFirst")
157 if clientFirst != "n,,n=user,r=rOprNGfwEbeRWgbNEkqO" {
158 t.Fatalf("bad clientFirst")
160 clientFinal, err := c.ServerFirst([]byte("r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096"), "pencil")
161 tcheck(t, err, "ServerFirst")
162 if clientFinal != "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=" {
163 t.Fatalf("bad clientFinal")
165 err = c.ServerFinal([]byte("v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4="))
166 tcheck(t, err, "ServerFinal")
169func TestScram(t *testing.T) {
170 runHash := func(h func() hash.Hash, expErr error, username, authzid, password string, iterations int, clientNonce, serverNonce string, noServerPlus bool, clientcs, servercs *tls.ConnectionState) {
175 if x == nil || x == "" {
181 // check err is either nil or the expected error. if the expected error, panic to abort the authentication session.
182 xerr := func(err error, msg string) {
184 if err != nil && !errors.Is(err, expErr) {
185 t.Fatalf("%s: got %v, expected %v", msg, err, expErr)
188 panic("") // Abort test.
193 saltedPassword, err := SaltPassword(h, password, salt, iterations)
194 tcheck(t, err, "saltpassword")
196 client := NewClient(h, username, "", noServerPlus, clientcs)
197 client.clientNonce = clientNonce
198 clientFirst, err := client.ClientFirst()
199 xerr(err, "client.ClientFirst")
201 server, err := NewServer(h, []byte(clientFirst), servercs, servercs != nil)
202 xerr(err, "NewServer")
203 server.serverNonceOverride = serverNonce
205 serverFirst, err := server.ServerFirst(iterations, salt)
206 xerr(err, "server.ServerFirst")
208 clientFinal, err := client.ServerFirst([]byte(serverFirst), password)
209 xerr(err, "client.ServerFirst")
211 serverFinal, err := server.Finish([]byte(clientFinal), saltedPassword)
212 xerr(err, "server.Finish")
214 err = client.ServerFinal([]byte(serverFinal))
215 xerr(err, "client.ServerFinal")
218 t.Fatalf("got no error, expected %v", expErr)
222 makeState := func(maxTLSVersion uint16) (tls.ConnectionState, tls.ConnectionState) {
223 client, server := net.Pipe()
226 tlsClient := tls.Client(client, &tls.Config{
227 InsecureSkipVerify: true,
228 MaxVersion: maxTLSVersion,
230 tlsServer := tls.Server(server, &tls.Config{
231 Certificates: []tls.Certificate{fakeCert(t, "mox.example", false)},
232 MaxVersion: maxTLSVersion,
234 errc := make(chan error, 1)
236 errc <- tlsServer.Handshake()
238 err := tlsClient.Handshake()
239 tcheck(t, err, "tls handshake")
241 tcheck(t, err, "server tls handshake")
242 clientcs := tlsClient.ConnectionState()
243 servercs := tlsServer.ConnectionState()
245 return clientcs, servercs
248 runPlus := func(maxTLSVersion uint16, expErr error, username, authzid, password string, iterations int, clientNonce, serverNonce string) {
252 clientcs, servercs := makeState(maxTLSVersion)
253 runHash(sha1.New, expErr, username, authzid, password, iterations, clientNonce, serverNonce, false, &clientcs, &servercs)
254 runHash(sha256.New, expErr, username, authzid, password, iterations, clientNonce, serverNonce, false, &clientcs, &servercs)
257 run := func(expErr error, username, authzid, password string, iterations int, clientNonce, serverNonce string) {
261 runHash(sha1.New, expErr, username, authzid, password, iterations, clientNonce, serverNonce, false, nil, nil)
262 runHash(sha256.New, expErr, username, authzid, password, iterations, clientNonce, serverNonce, false, nil, nil)
264 // Check with both TLS 1.2 for "tls-unique", and latest TLS for "tls-exporter".
265 runPlus(tls.VersionTLS12, expErr, username, authzid, password, iterations, clientNonce, serverNonce)
266 runPlus(0, expErr, username, authzid, password, iterations, clientNonce, serverNonce)
269 run(nil, "user", "", "pencil", 4096, "", "")
270 run(nil, "mjl@mox.example", "", "testtest", 4096, "", "")
271 run(nil, "mjl@mox.example", "", "short", 4096, "", "")
272 run(nil, "mjl@mox.example", "", "short", 2048, "", "")
273 run(nil, "mjl@mox.example", "mjl@mox.example", "testtest", 4096, "", "")
274 run(nil, "mjl@mox.example", "other@mox.example", "testtest", 4096, "", "")
275 run(nil, "mjl@mox.example", "other@mox.example", "testtest", 4096, "", "")
276 run(ErrUnsafe, "user", "", "pencil", 1, "", "") // Few iterations.
277 run(ErrUnsafe, "user", "", "pencil", 2048, "short", "") // Short client nonce.
278 run(ErrUnsafe, "user", "", "pencil", 2048, "test1234", "test") // Server added too few random data.
280 // Test mechanism downgrade attacks are detected.
281 runHash(sha1.New, ErrServerDoesSupportChannelBinding, "user", "", "pencil", 4096, "", "", true, nil, nil)
282 runHash(sha256.New, ErrServerDoesSupportChannelBinding, "user", "", "pencil", 4096, "", "", true, nil, nil)
284 // Test channel binding, detecting MitM attacks.
285 runChannelBind := func(maxTLSVersion uint16) {
288 clientcs0, _ := makeState(maxTLSVersion)
289 _, servercs1 := makeState(maxTLSVersion)
290 runHash(sha1.New, ErrChannelBindingsDontMatch, "user", "", "pencil", 4096, "", "", false, &clientcs0, &servercs1)
291 runHash(sha256.New, ErrChannelBindingsDontMatch, "user", "", "pencil", 4096, "", "", false, &clientcs0, &servercs1)
293 // Client thinks it is on a TLS connection and server is not.
294 runHash(sha1.New, ErrChannelBindingsDontMatch, "user", "", "pencil", 4096, "", "", false, &clientcs0, nil)
295 runHash(sha256.New, ErrChannelBindingsDontMatch, "user", "", "pencil", 4096, "", "", false, &clientcs0, nil)
299 runChannelBind(tls.VersionTLS12)
302// Just a cert that appears valid.
303func fakeCert(t *testing.T, name string, expired bool) tls.Certificate {
304 notAfter := time.Now()
306 notAfter = notAfter.Add(-time.Hour)
308 notAfter = notAfter.Add(time.Hour)
311 privKey := ed25519.NewKeyFromSeed(make([]byte, ed25519.SeedSize)) // Fake key, don't use this for real!
312 template := &x509.Certificate{
313 SerialNumber: big.NewInt(1), // Required field...
314 DNSNames: []string{name},
315 NotBefore: time.Now().Add(-time.Hour),
318 localCertBuf, err := x509.CreateCertificate(cryptorand.Reader, template, template, privKey.Public(), privKey)
320 t.Fatalf("making certificate: %s", err)
322 cert, err := x509.ParseCertificate(localCertBuf)
324 t.Fatalf("parsing generated certificate: %s", err)
326 c := tls.Certificate{
327 Certificate: [][]byte{localCertBuf},