get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/nkey_test.go (about) 1 // Copyright 2018 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "bufio" 18 crand "crypto/rand" 19 "encoding/base64" 20 "encoding/json" 21 "fmt" 22 mrand "math/rand" 23 "strings" 24 "testing" 25 "time" 26 27 "github.com/nats-io/nkeys" 28 ) 29 30 // Nonce has to be a string since we used different encoding by default than json.Unmarshal. 31 type nonceInfo struct { 32 Id string `json:"server_id"` 33 CID uint64 `json:"client_id,omitempty"` 34 Nonce string `json:"nonce,omitempty"` 35 } 36 37 // This is a seed for a user. We can extract public and private keys from this for testing. 38 var seed = []byte("SUAKYRHVIOREXV7EUZTBHUHL7NUMHPMAS7QMDU3GTIUWEI5LDNOXD43IZY") 39 40 func nkeyBasicSetup() (*Server, *testAsyncClient, *bufio.Reader, string) { 41 kp, _ := nkeys.FromSeed(seed) 42 pub, _ := kp.PublicKey() 43 opts := defaultServerOptions 44 opts.Nkeys = []*NkeyUser{{Nkey: string(pub)}} 45 return rawSetup(opts) 46 } 47 48 func mixedSetup() (*Server, *testAsyncClient, *bufio.Reader, string) { 49 kp, _ := nkeys.FromSeed(seed) 50 pub, _ := kp.PublicKey() 51 opts := defaultServerOptions 52 opts.Nkeys = []*NkeyUser{{Nkey: string(pub)}} 53 opts.Users = []*User{{Username: "derek", Password: "foo"}} 54 return rawSetup(opts) 55 } 56 57 func TestServerInfoNonceAlwaysEnabled(t *testing.T) { 58 opts := defaultServerOptions 59 opts.AlwaysEnableNonce = true 60 s, c, _, l := rawSetup(opts) 61 defer s.WaitForShutdown() 62 defer s.Shutdown() 63 defer c.close() 64 65 if !strings.HasPrefix(l, "INFO ") { 66 t.Fatalf("INFO response incorrect: %s\n", l) 67 } 68 69 var info nonceInfo 70 err := json.Unmarshal([]byte(l[5:]), &info) 71 if err != nil { 72 t.Fatalf("Could not parse INFO json: %v\n", err) 73 } 74 if info.Nonce == "" { 75 t.Fatalf("Expected a non-empty nonce with AlwaysEnableNonce set") 76 } 77 } 78 79 func TestServerInfoNonce(t *testing.T) { 80 c, l := setUpClientWithResponse() 81 defer c.close() 82 if !strings.HasPrefix(l, "INFO ") { 83 t.Fatalf("INFO response incorrect: %s\n", l) 84 } 85 // Make sure payload is proper json 86 var info nonceInfo 87 err := json.Unmarshal([]byte(l[5:]), &info) 88 if err != nil { 89 t.Fatalf("Could not parse INFO json: %v\n", err) 90 } 91 if info.Nonce != "" { 92 t.Fatalf("Expected an empty nonce with no nkeys defined") 93 } 94 95 // Now setup server with auth and nkeys to trigger nonce generation 96 s, c, _, l := nkeyBasicSetup() 97 defer c.close() 98 99 if !strings.HasPrefix(l, "INFO ") { 100 t.Fatalf("INFO response incorrect: %s\n", l) 101 } 102 // Make sure payload is proper json 103 err = json.Unmarshal([]byte(l[5:]), &info) 104 if err != nil { 105 t.Fatalf("Could not parse INFO json: %v\n", err) 106 } 107 if info.Nonce == "" { 108 t.Fatalf("Expected a non-empty nonce with nkeys defined") 109 } 110 111 // Make sure new clients get new nonces 112 oldNonce := info.Nonce 113 114 c, _, l = newClientForServer(s) 115 defer c.close() 116 117 err = json.Unmarshal([]byte(l[5:]), &info) 118 if err != nil { 119 t.Fatalf("Could not parse INFO json: %v\n", err) 120 } 121 if info.Nonce == "" { 122 t.Fatalf("Expected a non-empty nonce") 123 } 124 if strings.Compare(oldNonce, info.Nonce) == 0 { 125 t.Fatalf("Expected subsequent nonces to be different\n") 126 } 127 } 128 129 func TestNkeyClientConnect(t *testing.T) { 130 s, c, cr, _ := nkeyBasicSetup() 131 defer c.close() 132 // Send CONNECT with no signature or nkey, should fail. 133 connectOp := "CONNECT {\"verbose\":true,\"pedantic\":true}\r\n" 134 c.parseAsync(connectOp) 135 l, _ := cr.ReadString('\n') 136 if !strings.HasPrefix(l, "-ERR ") { 137 t.Fatalf("Expected an error") 138 } 139 140 kp, _ := nkeys.FromSeed(seed) 141 pubKey, _ := kp.PublicKey() 142 143 // Send nkey but no signature 144 c, cr, _ = newClientForServer(s) 145 defer c.close() 146 cs := fmt.Sprintf("CONNECT {\"nkey\":%q, \"verbose\":true,\"pedantic\":true}\r\n", pubKey) 147 c.parseAsync(cs) 148 l, _ = cr.ReadString('\n') 149 if !strings.HasPrefix(l, "-ERR ") { 150 t.Fatalf("Expected an error") 151 } 152 153 // Now improperly sign etc. 154 c, cr, _ = newClientForServer(s) 155 defer c.close() 156 cs = fmt.Sprintf("CONNECT {\"nkey\":%q,\"sig\":%q,\"verbose\":true,\"pedantic\":true}\r\n", pubKey, "bad_sig") 157 c.parseAsync(cs) 158 l, _ = cr.ReadString('\n') 159 if !strings.HasPrefix(l, "-ERR ") { 160 t.Fatalf("Expected an error") 161 } 162 163 // Now properly sign the nonce 164 c, cr, l = newClientForServer(s) 165 defer c.close() 166 // Check for Nonce 167 var info nonceInfo 168 err := json.Unmarshal([]byte(l[5:]), &info) 169 if err != nil { 170 t.Fatalf("Could not parse INFO json: %v\n", err) 171 } 172 if info.Nonce == "" { 173 t.Fatalf("Expected a non-empty nonce with nkeys defined") 174 } 175 sigraw, err := kp.Sign([]byte(info.Nonce)) 176 if err != nil { 177 t.Fatalf("Failed signing nonce: %v", err) 178 } 179 sig := base64.RawURLEncoding.EncodeToString(sigraw) 180 181 // PING needed to flush the +OK to us. 182 cs = fmt.Sprintf("CONNECT {\"nkey\":%q,\"sig\":\"%s\",\"verbose\":true,\"pedantic\":true}\r\nPING\r\n", pubKey, sig) 183 c.parseAsync(cs) 184 l, _ = cr.ReadString('\n') 185 if !strings.HasPrefix(l, "+OK") { 186 t.Fatalf("Expected an OK, got: %v", l) 187 } 188 } 189 190 func TestMixedClientConnect(t *testing.T) { 191 s, c, cr, _ := mixedSetup() 192 defer c.close() 193 // Normal user/pass 194 c.parseAsync("CONNECT {\"user\":\"derek\",\"pass\":\"foo\",\"verbose\":true,\"pedantic\":true}\r\nPING\r\n") 195 l, _ := cr.ReadString('\n') 196 if !strings.HasPrefix(l, "+OK") { 197 t.Fatalf("Expected an OK, got: %v", l) 198 } 199 200 kp, _ := nkeys.FromSeed(seed) 201 pubKey, _ := kp.PublicKey() 202 203 c, cr, l = newClientForServer(s) 204 defer c.close() 205 // Check for Nonce 206 var info nonceInfo 207 err := json.Unmarshal([]byte(l[5:]), &info) 208 if err != nil { 209 t.Fatalf("Could not parse INFO json: %v\n", err) 210 } 211 if info.Nonce == "" { 212 t.Fatalf("Expected a non-empty nonce with nkeys defined") 213 } 214 sigraw, err := kp.Sign([]byte(info.Nonce)) 215 if err != nil { 216 t.Fatalf("Failed signing nonce: %v", err) 217 } 218 sig := base64.RawURLEncoding.EncodeToString(sigraw) 219 220 // PING needed to flush the +OK to us. 221 cs := fmt.Sprintf("CONNECT {\"nkey\":%q,\"sig\":\"%s\",\"verbose\":true,\"pedantic\":true}\r\nPING\r\n", pubKey, sig) 222 c.parseAsync(cs) 223 l, _ = cr.ReadString('\n') 224 if !strings.HasPrefix(l, "+OK") { 225 t.Fatalf("Expected an OK, got: %v", l) 226 } 227 } 228 229 func TestMixedClientConfig(t *testing.T) { 230 confFileName := createConfFile(t, []byte(` 231 authorization { 232 users = [ 233 {nkey: "UDKTV7HZVYJFJN64LLMYQBUR6MTNNYCDC3LAZH4VHURW3GZLL3FULBXV"} 234 {user: alice, password: foo} 235 ] 236 }`)) 237 opts, err := ProcessConfigFile(confFileName) 238 if err != nil { 239 t.Fatalf("Received an error processing config file: %v", err) 240 } 241 if len(opts.Nkeys) != 1 { 242 t.Fatalf("Expected 1 nkey, got %d", len(opts.Nkeys)) 243 } 244 if len(opts.Users) != 1 { 245 t.Fatalf("Expected 1 user, got %d", len(opts.Users)) 246 } 247 } 248 249 func BenchmarkCryptoRandGeneration(b *testing.B) { 250 data := make([]byte, 16) 251 for i := 0; i < b.N; i++ { 252 crand.Read(data) 253 } 254 } 255 256 func BenchmarkMathRandGeneration(b *testing.B) { 257 data := make([]byte, 16) 258 prng := mrand.New(mrand.NewSource(time.Now().UnixNano())) 259 for i := 0; i < b.N; i++ { 260 prng.Read(data) 261 } 262 } 263 264 func BenchmarkNonceGeneration(b *testing.B) { 265 data := make([]byte, nonceRawLen) 266 b64 := make([]byte, nonceLen) 267 prand := mrand.New(mrand.NewSource(time.Now().UnixNano())) 268 for i := 0; i < b.N; i++ { 269 prand.Read(data) 270 base64.RawURLEncoding.Encode(b64, data) 271 } 272 } 273 274 func BenchmarkPublicVerify(b *testing.B) { 275 data := make([]byte, nonceRawLen) 276 nonce := make([]byte, nonceLen) 277 crand.Read(data) 278 base64.RawURLEncoding.Encode(nonce, data) 279 280 user, err := nkeys.CreateUser() 281 if err != nil { 282 b.Fatalf("Error creating User Nkey: %v", err) 283 } 284 sig, err := user.Sign(nonce) 285 if err != nil { 286 b.Fatalf("Error sigining nonce: %v", err) 287 } 288 pk, err := user.PublicKey() 289 if err != nil { 290 b.Fatalf("Could not extract public key from user: %v", err) 291 } 292 pub, err := nkeys.FromPublicKey(pk) 293 if err != nil { 294 b.Fatalf("Could not create public key pair from public key string: %v", err) 295 } 296 297 b.ResetTimer() 298 for i := 0; i < b.N; i++ { 299 if err := pub.Verify(nonce, sig); err != nil { 300 b.Fatalf("Error verifying nonce: %v", err) 301 } 302 } 303 }