github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/crypto_client.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/kbcrypto"
    11  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    12  	"github.com/keybase/client/go/logger"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/pkg/errors"
    15  	"golang.org/x/net/context"
    16  )
    17  
    18  // CryptoClient is a keybase1.CryptoInterface based implementation for Crypto.
    19  type CryptoClient struct {
    20  	CryptoCommon
    21  	log         logger.Logger
    22  	deferLog    logger.Logger
    23  	client      keybase1.CryptoInterface
    24  	teamsClient keybase1.TeamsInterface
    25  	shutdownFn  func()
    26  }
    27  
    28  // cryptoWarningTime says how long we should wait before logging a
    29  // message about it taking too long.
    30  const cryptoWarningTime = 2 * time.Minute
    31  
    32  var _ Crypto = (*CryptoClient)(nil)
    33  
    34  func (c *CryptoClient) logAboutTooLongUnlessCancelled(ctx context.Context,
    35  	method string) *time.Timer {
    36  	return time.AfterFunc(cryptoWarningTime, func() {
    37  		log := c.log.CloneWithAddedDepth(2)
    38  		log.CInfof(ctx, "%s call took more than %s", method,
    39  			cryptoWarningTime)
    40  	})
    41  }
    42  
    43  // Sign implements the Crypto interface for CryptoClient.
    44  func (c *CryptoClient) Sign(ctx context.Context, msg []byte) (
    45  	sigInfo kbfscrypto.SignatureInfo, err error) {
    46  	c.log.CDebugf(ctx, "Signing %d-byte message", len(msg))
    47  	defer func() {
    48  		c.deferLog.CDebugf(ctx, "Signed %d-byte message with %s: err=%+v", len(msg),
    49  			sigInfo, err)
    50  	}()
    51  
    52  	timer := c.logAboutTooLongUnlessCancelled(ctx, "SignED25519")
    53  	defer timer.Stop()
    54  	ed25519SigInfo, err := c.client.SignED25519(ctx, keybase1.SignED25519Arg{
    55  		Msg:    msg,
    56  		Reason: "to use kbfs",
    57  	})
    58  	if err != nil {
    59  		return kbfscrypto.SignatureInfo{}, errors.WithStack(err)
    60  	}
    61  
    62  	return kbfscrypto.SignatureInfo{
    63  		Version:      kbfscrypto.SigED25519,
    64  		Signature:    ed25519SigInfo.Sig[:],
    65  		VerifyingKey: kbfscrypto.MakeVerifyingKey(kbcrypto.NaclSigningKeyPublic(ed25519SigInfo.PublicKey).GetKID()),
    66  	}, nil
    67  }
    68  
    69  // SignForKBFS implements the Crypto interface for CryptoClient.
    70  func (c *CryptoClient) SignForKBFS(ctx context.Context, msg []byte) (
    71  	sigInfo kbfscrypto.SignatureInfo, err error) {
    72  	c.log.CDebugf(ctx, "Signing %d-byte message", len(msg))
    73  	defer func() {
    74  		c.deferLog.CDebugf(ctx, "Signed %d-byte message with %s: err=%+v", len(msg),
    75  			sigInfo, err)
    76  	}()
    77  
    78  	timer := c.logAboutTooLongUnlessCancelled(ctx, "SignED25519ForKBFS")
    79  	defer timer.Stop()
    80  	ed25519SigInfo, err := c.client.SignED25519ForKBFS(ctx, keybase1.SignED25519ForKBFSArg{
    81  		Msg:    msg,
    82  		Reason: "to use kbfs",
    83  	})
    84  	if err != nil {
    85  		return kbfscrypto.SignatureInfo{}, errors.WithStack(err)
    86  	}
    87  
    88  	return kbfscrypto.SignatureInfo{
    89  		Version:      kbfscrypto.SigED25519ForKBFS,
    90  		Signature:    ed25519SigInfo.Sig[:],
    91  		VerifyingKey: kbfscrypto.MakeVerifyingKey(kbcrypto.NaclSigningKeyPublic(ed25519SigInfo.PublicKey).GetKID()),
    92  	}, nil
    93  }
    94  
    95  // SignToString implements the Crypto interface for CryptoClient.
    96  func (c *CryptoClient) SignToString(ctx context.Context, msg []byte) (
    97  	signature string, err error) {
    98  	c.log.CDebugf(ctx, "Signing %d-byte message to string", len(msg))
    99  	defer func() {
   100  		c.deferLog.CDebugf(ctx, "Signed %d-byte message: err=%+v", len(msg), err)
   101  	}()
   102  
   103  	timer := c.logAboutTooLongUnlessCancelled(ctx, "SignToString")
   104  	defer timer.Stop()
   105  	signature, err = c.client.SignToString(ctx, keybase1.SignToStringArg{
   106  		Msg:    msg,
   107  		Reason: "KBFS Authentication",
   108  	})
   109  	if err != nil {
   110  		return "", errors.WithStack(err)
   111  	}
   112  	return signature, nil
   113  }
   114  
   115  func (c *CryptoClient) prepareTLFCryptKeyClientHalf(
   116  	encryptedClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf) (
   117  	encryptedData keybase1.EncryptedBytes32, nonce keybase1.BoxNonce,
   118  	err error) {
   119  	if encryptedClientHalf.Version != kbfscrypto.EncryptionSecretbox {
   120  		return keybase1.EncryptedBytes32{}, keybase1.BoxNonce{},
   121  			errors.WithStack(kbfscrypto.UnknownEncryptionVer{
   122  				Ver: encryptedClientHalf.Version})
   123  	}
   124  
   125  	if len(encryptedClientHalf.EncryptedData) != len(encryptedData) {
   126  		return keybase1.EncryptedBytes32{}, keybase1.BoxNonce{},
   127  			errors.Errorf("Expected %d bytes, got %d",
   128  				len(encryptedData),
   129  				len(encryptedClientHalf.EncryptedData))
   130  	}
   131  	copy(encryptedData[:], encryptedClientHalf.EncryptedData)
   132  
   133  	if len(encryptedClientHalf.Nonce) != len(nonce) {
   134  		return keybase1.EncryptedBytes32{}, keybase1.BoxNonce{},
   135  			errors.WithStack(kbfscrypto.InvalidNonceError{
   136  				Nonce: encryptedClientHalf.Nonce})
   137  	}
   138  	copy(nonce[:], encryptedClientHalf.Nonce)
   139  	return encryptedData, nonce, nil
   140  }
   141  
   142  // DecryptTLFCryptKeyClientHalf implements the Crypto interface for
   143  // CryptoClient.
   144  func (c *CryptoClient) DecryptTLFCryptKeyClientHalf(ctx context.Context,
   145  	publicKey kbfscrypto.TLFEphemeralPublicKey,
   146  	encryptedClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf) (
   147  	clientHalf kbfscrypto.TLFCryptKeyClientHalf, err error) {
   148  	c.log.CDebugf(ctx, "Decrypting TLF client key half")
   149  	defer func() {
   150  		c.deferLog.CDebugf(ctx, "Decrypted TLF client key half: %+v",
   151  			err)
   152  	}()
   153  	encryptedData, nonce, err := c.prepareTLFCryptKeyClientHalf(
   154  		encryptedClientHalf)
   155  	if err != nil {
   156  		return kbfscrypto.TLFCryptKeyClientHalf{}, err
   157  	}
   158  
   159  	timer := c.logAboutTooLongUnlessCancelled(ctx, "UnboxBytes32")
   160  	defer timer.Stop()
   161  	decryptedClientHalf, err := c.client.UnboxBytes32(ctx, keybase1.UnboxBytes32Arg{
   162  		EncryptedBytes32: encryptedData,
   163  		Nonce:            nonce,
   164  		PeersPublicKey:   keybase1.BoxPublicKey(publicKey.Data()),
   165  		Reason:           "to use kbfs",
   166  	})
   167  	if err != nil {
   168  		return kbfscrypto.TLFCryptKeyClientHalf{}, errors.WithStack(err)
   169  	}
   170  
   171  	return kbfscrypto.MakeTLFCryptKeyClientHalf(decryptedClientHalf), nil
   172  }
   173  
   174  // DecryptTLFCryptKeyClientHalfAny implements the Crypto interface for
   175  // CryptoClient.
   176  func (c *CryptoClient) DecryptTLFCryptKeyClientHalfAny(ctx context.Context,
   177  	keys []EncryptedTLFCryptKeyClientAndEphemeral, promptPaper bool) (
   178  	clientHalf kbfscrypto.TLFCryptKeyClientHalf, index int, err error) {
   179  	c.log.CDebugf(ctx, "Decrypting TLF client key half with any key")
   180  	defer func() {
   181  		c.deferLog.CDebugf(ctx,
   182  			"Decrypted TLF client key half with any key: %+v",
   183  			err)
   184  	}()
   185  	if len(keys) == 0 {
   186  		return kbfscrypto.TLFCryptKeyClientHalf{}, -1,
   187  			errors.WithStack(NoKeysError{})
   188  	}
   189  	bundles := make([]keybase1.CiphertextBundle, 0, len(keys))
   190  	prepErrs := make([]error, 0, len(keys))
   191  	indexLookup := make([]int, 0, len(keys))
   192  	for i, k := range keys {
   193  		encryptedData, nonce, prepErr :=
   194  			c.prepareTLFCryptKeyClientHalf(k.ClientHalf)
   195  		if err != nil {
   196  			prepErrs = append(prepErrs, prepErr)
   197  		} else {
   198  			bundles = append(bundles, keybase1.CiphertextBundle{
   199  				Kid:        k.PubKey.KID(),
   200  				Ciphertext: encryptedData,
   201  				Nonce:      nonce,
   202  				PublicKey:  keybase1.BoxPublicKey(k.EPubKey.Data()),
   203  			})
   204  			indexLookup = append(indexLookup, i)
   205  		}
   206  	}
   207  	if len(bundles) == 0 {
   208  		return kbfscrypto.TLFCryptKeyClientHalf{}, -1, prepErrs[0]
   209  	}
   210  	timer := c.logAboutTooLongUnlessCancelled(ctx, "UnboxBytes32Any")
   211  	defer timer.Stop()
   212  	res, err := c.client.UnboxBytes32Any(ctx, keybase1.UnboxBytes32AnyArg{
   213  		Bundles:     bundles,
   214  		Reason:      "to rekey for kbfs",
   215  		PromptPaper: promptPaper,
   216  	})
   217  	if err != nil {
   218  		return kbfscrypto.TLFCryptKeyClientHalf{}, -1,
   219  			errors.WithStack(err)
   220  	}
   221  	return kbfscrypto.MakeTLFCryptKeyClientHalf(res.Plaintext),
   222  		indexLookup[res.Index], nil
   223  }
   224  
   225  // DecryptTeamMerkleLeaf implements the Crypto interface for
   226  // CryptoClient.
   227  func (c *CryptoClient) DecryptTeamMerkleLeaf(
   228  	ctx context.Context, teamID keybase1.TeamID,
   229  	publicKey kbfscrypto.TLFEphemeralPublicKey,
   230  	encryptedMerkleLeaf kbfscrypto.EncryptedMerkleLeaf,
   231  	minKeyGen keybase1.PerTeamKeyGeneration) (decryptedData []byte, err error) {
   232  	c.log.CDebugf(ctx, "Decrypting team Merkle leaf")
   233  	defer func() {
   234  		c.deferLog.CDebugf(ctx, "Decrypted team Merkle leaf: %+v", err)
   235  	}()
   236  	nonce, err := kbfscrypto.PrepareMerkleLeaf(
   237  		encryptedMerkleLeaf)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	timer := c.logAboutTooLongUnlessCancelled(ctx, "DecryptTeamMerkleLeaf")
   243  	defer timer.Stop()
   244  	decryptedData, err = c.teamsClient.TryDecryptWithTeamKey(ctx,
   245  		keybase1.TryDecryptWithTeamKeyArg{
   246  			TeamID:         teamID,
   247  			EncryptedData:  encryptedMerkleLeaf.EncryptedData,
   248  			Nonce:          nonce,
   249  			PeersPublicKey: keybase1.BoxPublicKey(publicKey.Data()),
   250  			MinGeneration:  minKeyGen,
   251  		})
   252  	if err != nil {
   253  		return nil, errors.WithStack(err)
   254  	}
   255  	return decryptedData, nil
   256  }
   257  
   258  // Shutdown implements the Crypto interface for CryptoClient.
   259  func (c *CryptoClient) Shutdown() {
   260  	if c.shutdownFn != nil {
   261  		c.shutdownFn()
   262  	}
   263  }