github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/repository/key.go (about)

     1  package repository
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"os"
     9  	"os/user"
    10  	"time"
    11  
    12  	"github.com/restic/restic/internal/errors"
    13  	"github.com/restic/restic/internal/restic"
    14  
    15  	"github.com/restic/restic/internal/backend"
    16  	"github.com/restic/restic/internal/crypto"
    17  	"github.com/restic/restic/internal/debug"
    18  )
    19  
    20  var (
    21  	// ErrNoKeyFound is returned when no key for the repository could be decrypted.
    22  	ErrNoKeyFound = errors.Fatal("wrong password or no key found")
    23  
    24  	// ErrMaxKeysReached is returned when the maximum number of keys was checked and no key could be found.
    25  	ErrMaxKeysReached = errors.Fatal("maximum number of keys reached")
    26  )
    27  
    28  // Key represents an encrypted master key for a repository.
    29  type Key struct {
    30  	Created  time.Time `json:"created"`
    31  	Username string    `json:"username"`
    32  	Hostname string    `json:"hostname"`
    33  
    34  	KDF  string `json:"kdf"`
    35  	N    int    `json:"N"`
    36  	R    int    `json:"r"`
    37  	P    int    `json:"p"`
    38  	Salt []byte `json:"salt"`
    39  	Data []byte `json:"data"`
    40  
    41  	user   *crypto.Key
    42  	master *crypto.Key
    43  
    44  	name string
    45  }
    46  
    47  // Params tracks the parameters used for the KDF. If not set, it will be
    48  // calibrated on the first run of AddKey().
    49  var Params *crypto.Params
    50  
    51  var (
    52  	// KDFTimeout specifies the maximum runtime for the KDF.
    53  	KDFTimeout = 500 * time.Millisecond
    54  
    55  	// KDFMemory limits the memory the KDF is allowed to use.
    56  	KDFMemory = 60
    57  )
    58  
    59  // createMasterKey creates a new master key in the given backend and encrypts
    60  // it with the password.
    61  func createMasterKey(s *Repository, password string) (*Key, error) {
    62  	return AddKey(context.TODO(), s, password, nil)
    63  }
    64  
    65  // OpenKey tries do decrypt the key specified by name with the given password.
    66  func OpenKey(ctx context.Context, s *Repository, name string, password string) (*Key, error) {
    67  	k, err := LoadKey(ctx, s, name)
    68  	if err != nil {
    69  		debug.Log("LoadKey(%v) returned error %v", name, err)
    70  		return nil, err
    71  	}
    72  
    73  	// check KDF
    74  	if k.KDF != "scrypt" {
    75  		return nil, errors.New("only supported KDF is scrypt()")
    76  	}
    77  
    78  	// derive user key
    79  	params := crypto.Params{
    80  		N: k.N,
    81  		R: k.R,
    82  		P: k.P,
    83  	}
    84  	k.user, err = crypto.KDF(params, k.Salt, password)
    85  	if err != nil {
    86  		return nil, errors.Wrap(err, "crypto.KDF")
    87  	}
    88  
    89  	// decrypt master keys
    90  	nonce, ciphertext := k.Data[:k.user.NonceSize()], k.Data[k.user.NonceSize():]
    91  	buf, err := k.user.Open(nil, nonce, ciphertext, nil)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	// restore json
    97  	k.master = &crypto.Key{}
    98  	err = json.Unmarshal(buf, k.master)
    99  	if err != nil {
   100  		debug.Log("Unmarshal() returned error %v", err)
   101  		return nil, errors.Wrap(err, "Unmarshal")
   102  	}
   103  	k.name = name
   104  
   105  	if !k.Valid() {
   106  		return nil, errors.New("Invalid key for repository")
   107  	}
   108  
   109  	return k, nil
   110  }
   111  
   112  // SearchKey tries to decrypt at most maxKeys keys in the backend with the
   113  // given password. If none could be found, ErrNoKeyFound is returned. When
   114  // maxKeys is reached, ErrMaxKeysReached is returned. When setting maxKeys to
   115  // zero, all keys in the repo are checked.
   116  func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int) (*Key, error) {
   117  	checked := 0
   118  
   119  	// try at most maxKeysForSearch keys in repo
   120  	for name := range s.Backend().List(ctx, restic.KeyFile) {
   121  		if maxKeys > 0 && checked > maxKeys {
   122  			return nil, ErrMaxKeysReached
   123  		}
   124  
   125  		debug.Log("trying key %q", name)
   126  		key, err := OpenKey(ctx, s, name, password)
   127  		if err != nil {
   128  			debug.Log("key %v returned error %v", name, err)
   129  
   130  			// ErrUnauthenticated means the password is wrong, try the next key
   131  			if errors.Cause(err) == crypto.ErrUnauthenticated {
   132  				continue
   133  			}
   134  
   135  			if err != nil {
   136  				debug.Log("unable to open key %v: %v\n", err)
   137  				continue
   138  			}
   139  		}
   140  
   141  		debug.Log("successfully opened key %v", name)
   142  		return key, nil
   143  	}
   144  
   145  	return nil, ErrNoKeyFound
   146  }
   147  
   148  // LoadKey loads a key from the backend.
   149  func LoadKey(ctx context.Context, s *Repository, name string) (k *Key, err error) {
   150  	h := restic.Handle{Type: restic.KeyFile, Name: name}
   151  	data, err := backend.LoadAll(ctx, s.be, h)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	k = &Key{}
   157  	err = json.Unmarshal(data, k)
   158  	if err != nil {
   159  		return nil, errors.Wrap(err, "Unmarshal")
   160  	}
   161  
   162  	return k, nil
   163  }
   164  
   165  // AddKey adds a new key to an already existing repository.
   166  func AddKey(ctx context.Context, s *Repository, password string, template *crypto.Key) (*Key, error) {
   167  	// make sure we have valid KDF parameters
   168  	if Params == nil {
   169  		p, err := crypto.Calibrate(KDFTimeout, KDFMemory)
   170  		if err != nil {
   171  			return nil, errors.Wrap(err, "Calibrate")
   172  		}
   173  
   174  		Params = &p
   175  		debug.Log("calibrated KDF parameters are %v", p)
   176  	}
   177  
   178  	// fill meta data about key
   179  	newkey := &Key{
   180  		Created: time.Now(),
   181  		KDF:     "scrypt",
   182  		N:       Params.N,
   183  		R:       Params.R,
   184  		P:       Params.P,
   185  	}
   186  
   187  	hn, err := os.Hostname()
   188  	if err == nil {
   189  		newkey.Hostname = hn
   190  	}
   191  
   192  	usr, err := user.Current()
   193  	if err == nil {
   194  		newkey.Username = usr.Username
   195  	}
   196  
   197  	// generate random salt
   198  	newkey.Salt, err = crypto.NewSalt()
   199  	if err != nil {
   200  		panic("unable to read enough random bytes for salt: " + err.Error())
   201  	}
   202  
   203  	// call KDF to derive user key
   204  	newkey.user, err = crypto.KDF(*Params, newkey.Salt, password)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	if template == nil {
   210  		// generate new random master keys
   211  		newkey.master = crypto.NewRandomKey()
   212  	} else {
   213  		// copy master keys from old key
   214  		newkey.master = template
   215  	}
   216  
   217  	// encrypt master keys (as json) with user key
   218  	buf, err := json.Marshal(newkey.master)
   219  	if err != nil {
   220  		return nil, errors.Wrap(err, "Marshal")
   221  	}
   222  
   223  	nonce := crypto.NewRandomNonce()
   224  	ciphertext := make([]byte, 0, len(buf)+newkey.user.Overhead()+newkey.user.NonceSize())
   225  	ciphertext = append(ciphertext, nonce...)
   226  	ciphertext = newkey.user.Seal(ciphertext, nonce, buf, nil)
   227  	newkey.Data = ciphertext
   228  
   229  	// dump as json
   230  	buf, err = json.Marshal(newkey)
   231  	if err != nil {
   232  		return nil, errors.Wrap(err, "Marshal")
   233  	}
   234  
   235  	// store in repository and return
   236  	h := restic.Handle{
   237  		Type: restic.KeyFile,
   238  		Name: restic.Hash(buf).String(),
   239  	}
   240  
   241  	err = s.be.Save(ctx, h, bytes.NewReader(buf))
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	newkey.name = h.Name
   247  
   248  	return newkey, nil
   249  }
   250  
   251  func (k *Key) String() string {
   252  	if k == nil {
   253  		return "<Key nil>"
   254  	}
   255  	return fmt.Sprintf("<Key of %s@%s, created on %s>", k.Username, k.Hostname, k.Created)
   256  }
   257  
   258  // Name returns an identifier for the key.
   259  func (k Key) Name() string {
   260  	return k.name
   261  }
   262  
   263  // Valid tests whether the mac and encryption keys are valid (i.e. not zero)
   264  func (k *Key) Valid() bool {
   265  	return k.user.Valid() && k.master.Valid()
   266  }