1package scram
2
3import (
4 "crypto/sha1"
5 "crypto/sha256"
6 "encoding/base64"
7 "errors"
8 "testing"
9)
10
11func base64Decode(s string) []byte {
12 buf, err := base64.StdEncoding.DecodeString(s)
13 if err != nil {
14 panic("bad base64")
15 }
16 return buf
17}
18
19func tcheck(t *testing.T, err error, msg string) {
20 t.Helper()
21 if err != nil {
22 t.Fatalf("%s: %s", msg, err)
23 }
24}
25
26func TestSCRAMSHA1Server(t *testing.T) {
27 // Test vector from ../rfc/5802:496
28 salt := base64Decode("QSXCR+Q6sek8bf92")
29 saltedPassword := SaltPassword(sha1.New, "pencil", salt, 4096)
30
31 server, err := NewServer(sha1.New, []byte("n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL"))
32 server.serverNonceOverride = "3rfcNHYJY1ZVvWVs7j"
33 tcheck(t, err, "newserver")
34 resp, err := server.ServerFirst(4096, salt)
35 tcheck(t, err, "server first")
36 if resp != "r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096" {
37 t.Fatalf("bad server first")
38 }
39 serverFinal, err := server.Finish([]byte("c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts="), saltedPassword)
40 tcheck(t, err, "finish")
41 if serverFinal != "v=rmF9pqV8S7suAoZWja4dJRkFsKQ=" {
42 t.Fatalf("bad server final")
43 }
44}
45
46func TestSCRAMSHA256Server(t *testing.T) {
47 // Test vector from ../rfc/7677:122
48 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
49 saltedPassword := SaltPassword(sha256.New, "pencil", salt, 4096)
50
51 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"))
52 server.serverNonceOverride = "%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0"
53 tcheck(t, err, "newserver")
54 resp, err := server.ServerFirst(4096, salt)
55 tcheck(t, err, "server first")
56 if resp != "r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096" {
57 t.Fatalf("bad server first")
58 }
59 serverFinal, err := server.Finish([]byte("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
60 tcheck(t, err, "finish")
61 if serverFinal != "v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4=" {
62 t.Fatalf("bad server final")
63 }
64}
65
66// Bad attempt with wrong password.
67func TestScramServerBadPassword(t *testing.T) {
68 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
69 saltedPassword := SaltPassword(sha256.New, "marker", salt, 4096)
70
71 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"))
72 server.serverNonceOverride = "%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0"
73 tcheck(t, err, "newserver")
74 _, err = server.ServerFirst(4096, salt)
75 tcheck(t, err, "server first")
76 _, err = server.Finish([]byte("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
77 if !errors.Is(err, ErrInvalidProof) {
78 t.Fatalf("got %v, expected ErrInvalidProof", err)
79 }
80}
81
82// Bad attempt with different number of rounds.
83func TestScramServerBadIterations(t *testing.T) {
84 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
85 saltedPassword := SaltPassword(sha256.New, "pencil", salt, 2048)
86
87 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"))
88 server.serverNonceOverride = "%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0"
89 tcheck(t, err, "newserver")
90 _, err = server.ServerFirst(4096, salt)
91 tcheck(t, err, "server first")
92 _, err = server.Finish([]byte("c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
93 if !errors.Is(err, ErrInvalidProof) {
94 t.Fatalf("got %v, expected ErrInvalidProof", err)
95 }
96}
97
98// Another attempt but with a randomly different nonce.
99func TestScramServerBad(t *testing.T) {
100 salt := base64Decode("W22ZaJ0SNY7soEsUEjb6gQ==")
101 saltedPassword := SaltPassword(sha256.New, "pencil", salt, 4096)
102
103 server, err := NewServer(sha256.New, []byte("n,,n=user,r=rOprNGfwEbeRWgbNEkqO"))
104 tcheck(t, err, "newserver")
105 _, err = server.ServerFirst(4096, salt)
106 tcheck(t, err, "server first")
107 _, err = server.Finish([]byte("c=biws,r="+server.nonce+",p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ="), saltedPassword)
108 if !errors.Is(err, ErrInvalidProof) {
109 t.Fatalf("got %v, expected ErrInvalidProof", err)
110 }
111}
112
113func TestScramClient(t *testing.T) {
114 c := NewClient(sha256.New, "user", "")
115 c.clientNonce = "rOprNGfwEbeRWgbNEkqO"
116 clientFirst, err := c.ClientFirst()
117 tcheck(t, err, "ClientFirst")
118 if clientFirst != "n,,n=user,r=rOprNGfwEbeRWgbNEkqO" {
119 t.Fatalf("bad clientFirst")
120 }
121 clientFinal, err := c.ServerFirst([]byte("r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096"), "pencil")
122 tcheck(t, err, "ServerFirst")
123 if clientFinal != "c=biws,r=rOprNGfwEbeRWgbNEkqO%hvYDpWUa2RaTCAfuxFIlj)hNlF$k0,p=dHzbZapWIk4jUhN+Ute9ytag9zjfMHgsqmmiz7AndVQ=" {
124 t.Fatalf("bad clientFinal")
125 }
126 err = c.ServerFinal([]byte("v=6rriTRBi23WpRR/wtup+mMhUZUn/dB5nLTJRsjl95G4="))
127 tcheck(t, err, "ServerFinal")
128}
129
130func TestScram(t *testing.T) {
131 run := func(expErr error, username, authzid, password string, iterations int, clientNonce, serverNonce string) {
132 t.Helper()
133
134 defer func() {
135 x := recover()
136 if x == nil || x == "" {
137 return
138 }
139 panic(x)
140 }()
141
142 // check err is either nil or the expected error. if the expected error, panic to abort the authentication session.
143 xerr := func(err error, msg string) {
144 t.Helper()
145 if err != nil && !errors.Is(err, expErr) {
146 t.Fatalf("%s: got %v, expected %v", msg, err, expErr)
147 }
148 if err != nil {
149 panic("") // Abort test.
150 }
151 }
152
153 salt := MakeRandom()
154 saltedPassword := SaltPassword(sha256.New, password, salt, iterations)
155
156 client := NewClient(sha256.New, username, "")
157 client.clientNonce = clientNonce
158 clientFirst, err := client.ClientFirst()
159 xerr(err, "client.ClientFirst")
160
161 server, err := NewServer(sha256.New, []byte(clientFirst))
162 xerr(err, "NewServer")
163 server.serverNonceOverride = serverNonce
164
165 serverFirst, err := server.ServerFirst(iterations, salt)
166 xerr(err, "server.ServerFirst")
167
168 clientFinal, err := client.ServerFirst([]byte(serverFirst), password)
169 xerr(err, "client.ServerFirst")
170
171 serverFinal, err := server.Finish([]byte(clientFinal), saltedPassword)
172 xerr(err, "server.Finish")
173
174 err = client.ServerFinal([]byte(serverFinal))
175 xerr(err, "client.ServerFinal")
176
177 if expErr != nil {
178 t.Fatalf("got no error, expected %v", expErr)
179 }
180 }
181
182 run(nil, "user", "", "pencil", 4096, "", "")
183 run(nil, "mjl@mox.example", "", "testtest", 4096, "", "")
184 run(nil, "mjl@mox.example", "", "short", 4096, "", "")
185 run(nil, "mjl@mox.example", "", "short", 2048, "", "")
186 run(nil, "mjl@mox.example", "mjl@mox.example", "testtest", 4096, "", "")
187 run(nil, "mjl@mox.example", "other@mox.example", "testtest", 4096, "", "")
188 run(ErrUnsafe, "user", "", "pencil", 1, "", "") // Few iterations.
189 run(ErrUnsafe, "user", "", "pencil", 2048, "short", "") // Short client nonce.
190 run(ErrUnsafe, "user", "", "pencil", 2048, "test1234", "test") // Server added too few random data.
191}
192