github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/crypto_test.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package engine
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"runtime/debug"
    10  	"testing"
    11  
    12  	"golang.org/x/crypto/nacl/box"
    13  	"golang.org/x/net/context"
    14  
    15  	"github.com/keybase/client/go/kbcrypto"
    16  	"github.com/keybase/client/go/libkb"
    17  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    18  )
    19  
    20  // Test that SignED25519() signs the given message with the device
    21  // signing key, and that the signature is verifiable by the returned
    22  // public key.
    23  //
    24  // (For general tests that valid signatures are accepted and invalid
    25  // signatures are rejected, see naclwrap_test.go.)
    26  func TestCryptoSignED25519(t *testing.T) {
    27  	tc := SetupEngineTest(t, "crypto")
    28  	defer tc.Cleanup()
    29  
    30  	CreateAndSignupFakeUser(tc, "fu")
    31  
    32  	msg := []byte("test message")
    33  	ret, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
    34  		Msg: msg,
    35  	})
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  
    40  	publicKey := kbcrypto.NaclSigningKeyPublic(ret.PublicKey)
    41  	if !publicKey.Verify(msg, kbcrypto.NaclSignature(ret.Sig)) {
    42  		t.Error(kbcrypto.VerificationError{})
    43  	}
    44  }
    45  
    46  // Test that SignToString() signs the given message with the device
    47  // signing key and that the signature is verifiable and contains the message.
    48  func TestCryptoSignToString(t *testing.T) {
    49  	tc := SetupEngineTest(t, "crypto")
    50  	defer tc.Cleanup()
    51  
    52  	CreateAndSignupFakeUser(tc, "fu")
    53  
    54  	msg := []byte("test message")
    55  	signature, err := SignToString(context.TODO(), tc.G, keybase1.SignToStringArg{
    56  		Msg: msg,
    57  	})
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	_, msg2, _, err := kbcrypto.NaclVerifyAndExtract(signature)
    63  	if err != nil {
    64  		t.Fatal(err)
    65  	}
    66  	if !bytes.Equal(msg, msg2) {
    67  		t.Fatal(fmt.Errorf("message mismatch, expected: %s, got: %s",
    68  			string(msg), string(msg2)))
    69  	}
    70  }
    71  
    72  // Test that CryptoHandler.SignED25519() propagates any error
    73  // encountered when getting the device signing key.
    74  func TestCryptoSignED25519NoSigningKey(t *testing.T) {
    75  	tc := SetupEngineTest(t, "crypto")
    76  	defer tc.Cleanup()
    77  
    78  	_, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
    79  		Msg: []byte("test message"),
    80  	})
    81  
    82  	if _, ok := err.(libkb.LoginRequiredError); !ok {
    83  		t.Errorf("expected LoginRequiredError, got %v", err)
    84  	}
    85  }
    86  
    87  func BenchmarkCryptoSignED25519(b *testing.B) {
    88  	tc := SetupEngineTest(b, "crypto")
    89  	defer tc.Cleanup()
    90  
    91  	CreateAndSignupFakeUser(tc, "fu")
    92  
    93  	b.ResetTimer()
    94  	for i := 0; i < b.N; i++ {
    95  		msg := []byte("test message")
    96  		_, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
    97  			Msg: msg,
    98  		})
    99  		if err != nil {
   100  			b.Fatal(err)
   101  		}
   102  	}
   103  }
   104  
   105  // Test that CryptoHandler.UnboxBytes32() decrypts a boxed 32-byte
   106  // array correctly.
   107  func TestCryptoUnboxBytes32(t *testing.T) {
   108  	tc := SetupEngineTest(t, "crypto")
   109  	defer tc.Cleanup()
   110  
   111  	u := CreateAndSignupFakeUser(tc, "fu")
   112  	f := func() libkb.SecretUI {
   113  		return &libkb.TestSecretUI{Passphrase: u.Passphrase}
   114  	}
   115  
   116  	key, err := GetMySecretKey(
   117  		context.TODO(),
   118  		tc.G, libkb.DeviceEncryptionKeyType, "test")
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	kp, ok := key.(libkb.NaclDHKeyPair)
   123  	if !ok || kp.Private == nil {
   124  		t.Fatalf("unexpected key %v", key)
   125  	}
   126  
   127  	peerKp, err := libkb.GenerateNaclDHKeyPair()
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	expectedBytes32 := keybase1.Bytes32{0, 1, 2, 3, 4, 5}
   133  	nonce := [24]byte{6, 7, 8, 9, 10}
   134  	peersPublicKey := keybase1.BoxPublicKey(peerKp.Public)
   135  
   136  	encryptedData := box.Seal(nil, expectedBytes32[:], &nonce, (*[32]byte)(&kp.Public), (*[32]byte)(peerKp.Private))
   137  
   138  	var encryptedBytes32 keybase1.EncryptedBytes32
   139  	if len(encryptedBytes32) != len(encryptedData) {
   140  		t.Fatalf("Expected %d bytes, got %d", len(encryptedBytes32), len(encryptedData))
   141  	}
   142  
   143  	copy(encryptedBytes32[:], encryptedData)
   144  
   145  	bytes32, err := UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{
   146  		EncryptedBytes32: encryptedBytes32,
   147  		Nonce:            nonce,
   148  		PeersPublicKey:   peersPublicKey,
   149  	})
   150  
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	if bytes32 != expectedBytes32 {
   156  		t.Errorf("expected %s, got %s", expectedBytes32, bytes32)
   157  	}
   158  
   159  	// also test UnboxBytes32Any:
   160  	arg := keybase1.UnboxBytes32AnyArg{
   161  		Bundles: []keybase1.CiphertextBundle{
   162  			{Kid: kp.GetKID(), Ciphertext: encryptedBytes32, Nonce: nonce, PublicKey: peersPublicKey},
   163  		},
   164  	}
   165  	res, err := UnboxBytes32Any(NewMetaContextForTest(tc), f, arg)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	if res.Plaintext != expectedBytes32 {
   170  		t.Errorf("UnboxBytes32Any plaintext: %x, expected %x", res.Plaintext, expectedBytes32)
   171  	}
   172  	if res.Kid.IsNil() {
   173  		t.Errorf("UnboxBytes32Any kid is nil")
   174  	}
   175  }
   176  
   177  // Test that CryptoHandler.UnboxBytes32() propagates any decryption
   178  // errors correctly.
   179  //
   180  // For now, we're assuming that nacl/box works correctly (i.e., we're
   181  // not testing the ways in which decryption can fail).
   182  func TestCryptoUnboxBytes32DecryptionError(t *testing.T) {
   183  	tc := SetupEngineTest(t, "crypto")
   184  	defer tc.Cleanup()
   185  
   186  	CreateAndSignupFakeUser(tc, "fu")
   187  
   188  	_, err := UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{})
   189  	if _, ok := err.(libkb.DecryptionError); !ok {
   190  		t.Errorf("expected libkb.DecryptionError, got %T", err)
   191  	}
   192  }
   193  
   194  // Test that CryptoHandler.UnboxBytes32() propagates any error
   195  // encountered when getting the device encryption key.
   196  func TestCryptoUnboxBytes32NoEncryptionKey(t *testing.T) {
   197  	tc := SetupEngineTest(t, "crypto")
   198  	defer tc.Cleanup()
   199  
   200  	_, err := UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{})
   201  
   202  	if _, ok := err.(libkb.LoginRequiredError); !ok {
   203  		t.Errorf("expected LoginRequiredError, got %v", err)
   204  	}
   205  }
   206  
   207  func cachedSecretKey(tc libkb.TestContext, ktype libkb.SecretKeyType) (key libkb.GenericKey, err error) {
   208  	return tc.G.ActiveDevice.KeyByType(ktype)
   209  }
   210  
   211  func assertCachedSecretKey(tc libkb.TestContext, ktype libkb.SecretKeyType) {
   212  	skey, err := cachedSecretKey(tc, ktype)
   213  	if err != nil {
   214  		debug.PrintStack()
   215  		tc.T.Fatalf("error getting cached secret key: %s", err)
   216  	}
   217  	if skey == nil {
   218  		tc.T.Fatalf("expected cached key, got nil")
   219  	}
   220  }
   221  
   222  func assertNotCachedSecretKey(tc libkb.TestContext, ktype libkb.SecretKeyType) {
   223  	skey, err := cachedSecretKey(tc, ktype)
   224  	if err == nil {
   225  		tc.T.Fatal("expected err getting cached secret key, got nil")
   226  	}
   227  	if _, notFound := err.(libkb.NotFoundError); !notFound {
   228  		tc.T.Fatalf("expected not found error, got %s (%T)", err, err)
   229  	}
   230  	if skey != nil {
   231  		tc.T.Fatalf("expected nil cached key, got %v", skey)
   232  	}
   233  }
   234  
   235  // TestCachedSecretKey tests that secret device keys are cached
   236  // properly.
   237  func TestCachedSecretKey(t *testing.T) {
   238  	tc := SetupEngineTest(t, "login")
   239  	defer tc.Cleanup()
   240  
   241  	u := CreateAndSignupFakeUser(tc, "login")
   242  
   243  	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
   244  	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
   245  
   246  	Logout(tc)
   247  
   248  	assertNotCachedSecretKey(tc, libkb.DeviceSigningKeyType)
   249  	assertNotCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
   250  
   251  	u.LoginOrBust(tc)
   252  
   253  	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
   254  	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
   255  
   256  	msg := []byte("test message")
   257  	_, err := SignED25519(context.TODO(), tc.G, keybase1.SignED25519Arg{
   258  		Msg: msg,
   259  	})
   260  	if err != nil {
   261  		t.Fatal(err)
   262  	}
   263  
   264  	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
   265  	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
   266  
   267  	Logout(tc)
   268  
   269  	assertNotCachedSecretKey(tc, libkb.DeviceSigningKeyType)
   270  	assertNotCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
   271  
   272  	u.LoginOrBust(tc)
   273  
   274  	assertCachedSecretKey(tc, libkb.DeviceSigningKeyType)
   275  	assertCachedSecretKey(tc, libkb.DeviceEncryptionKeyType)
   276  }
   277  
   278  func TestCryptoUnboxBytes32AnyPaper(t *testing.T) {
   279  	tc := SetupEngineTest(t, "crypto")
   280  	defer tc.Cleanup()
   281  
   282  	u := CreateAndSignupFakeUser(tc, "fu")
   283  
   284  	// create a paper key and cache it
   285  	uis := libkb.UIs{
   286  		LogUI:    tc.G.UI.GetLogUI(),
   287  		LoginUI:  &libkb.TestLoginUI{},
   288  		SecretUI: u.NewSecretUI(),
   289  	}
   290  	peng := NewPaperKey(tc.G)
   291  	m := NewMetaContextForTest(tc).WithUIs(uis)
   292  	if err := RunEngine2(m, peng); err != nil {
   293  		t.Fatal(err)
   294  	}
   295  
   296  	m.ActiveDevice().CacheProvisioningKey(m, libkb.NewDeviceWithKeysOnly(peng.SigKey(), peng.EncKey(), libkb.KeychainModeNone))
   297  
   298  	key := peng.EncKey()
   299  	kp, ok := key.(libkb.NaclDHKeyPair)
   300  	if !ok {
   301  		t.Fatalf("paper enc key type: %T, expected libkb.NaclDHKeyPair", key)
   302  	}
   303  	if kp.Private == nil {
   304  		t.Fatalf("paper enc key has nil private key")
   305  	}
   306  
   307  	peerKp, err := libkb.GenerateNaclDHKeyPair()
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  
   312  	expectedBytes32 := keybase1.Bytes32{0, 1, 2, 3, 4, 5}
   313  	nonce := [24]byte{6, 7, 8, 9, 10}
   314  	peersPublicKey := keybase1.BoxPublicKey(peerKp.Public)
   315  
   316  	encryptedData := box.Seal(nil, expectedBytes32[:], &nonce, (*[32]byte)(&kp.Public), (*[32]byte)(peerKp.Private))
   317  
   318  	var encryptedBytes32 keybase1.EncryptedBytes32
   319  	if len(encryptedBytes32) != len(encryptedData) {
   320  		t.Fatalf("Expected %d bytes, got %d", len(encryptedBytes32), len(encryptedData))
   321  	}
   322  
   323  	copy(encryptedBytes32[:], encryptedData)
   324  
   325  	f := func() libkb.SecretUI {
   326  		return u.NewSecretUI()
   327  	}
   328  
   329  	_, err = UnboxBytes32(context.TODO(), tc.G, keybase1.UnboxBytes32Arg{
   330  		EncryptedBytes32: encryptedBytes32,
   331  		Nonce:            nonce,
   332  		PeersPublicKey:   peersPublicKey,
   333  	})
   334  
   335  	// this should fail
   336  	if err == nil {
   337  		t.Fatal("UnboxBytes32 worked with paper key encrypted data")
   338  	}
   339  	if _, ok := err.(libkb.DecryptionError); !ok {
   340  		t.Fatalf("error %T, expected libkb.DecryptionError", err)
   341  	}
   342  
   343  	// this should work
   344  	arg := keybase1.UnboxBytes32AnyArg{
   345  		Bundles: []keybase1.CiphertextBundle{
   346  			{Kid: kp.GetKID(), Ciphertext: encryptedBytes32, Nonce: nonce, PublicKey: peersPublicKey},
   347  		},
   348  		PromptPaper: true,
   349  	}
   350  	res, err := UnboxBytes32Any(NewMetaContextForTest(tc), f, arg)
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  	if res.Plaintext != expectedBytes32 {
   355  		t.Errorf("UnboxBytes32Any plaintext: %x, expected %x", res.Plaintext, expectedBytes32)
   356  	}
   357  	if res.Kid.IsNil() {
   358  		t.Errorf("UnboxBytes32Any kid is nil")
   359  	}
   360  
   361  	// clear the paper key cache to test getting a paper key via UI
   362  	clearCaches(tc.G)
   363  	if err != nil {
   364  		t.Fatal(err)
   365  	}
   366  
   367  	f = func() libkb.SecretUI {
   368  		// set the passphrase in the secretUI to the paper key
   369  		secretUI := u.NewSecretUI()
   370  		secretUI.Passphrase = peng.Passphrase()
   371  		return secretUI
   372  	}
   373  
   374  	res, err = UnboxBytes32Any(NewMetaContextForTest(tc), f, arg)
   375  	if err != nil {
   376  		t.Fatal(err)
   377  	}
   378  	if res.Plaintext != expectedBytes32 {
   379  		t.Errorf("UnboxBytes32Any plaintext: %x, expected %x", res.Plaintext, expectedBytes32)
   380  	}
   381  	if res.Kid.IsNil() {
   382  		t.Errorf("UnboxBytes32Any kid is nil")
   383  	}
   384  }