github.com/nats-io/nats-server/v2@v2.11.0-preview.2/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  }