github.com/deis/deis@v1.13.5-0.20170519182049-1d9e59fbdbfc/Godeps/_workspace/src/golang.org/x/crypto/ssh/agent/keyring.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package agent
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/rand"
    10  	"crypto/subtle"
    11  	"errors"
    12  	"fmt"
    13  	"sync"
    14  
    15  	"golang.org/x/crypto/ssh"
    16  )
    17  
    18  type privKey struct {
    19  	signer  ssh.Signer
    20  	comment string
    21  }
    22  
    23  type keyring struct {
    24  	mu   sync.Mutex
    25  	keys []privKey
    26  
    27  	locked     bool
    28  	passphrase []byte
    29  }
    30  
    31  var errLocked = errors.New("agent: locked")
    32  
    33  // NewKeyring returns an Agent that holds keys in memory.  It is safe
    34  // for concurrent use by multiple goroutines.
    35  func NewKeyring() Agent {
    36  	return &keyring{}
    37  }
    38  
    39  // RemoveAll removes all identities.
    40  func (r *keyring) RemoveAll() error {
    41  	r.mu.Lock()
    42  	defer r.mu.Unlock()
    43  	if r.locked {
    44  		return errLocked
    45  	}
    46  
    47  	r.keys = nil
    48  	return nil
    49  }
    50  
    51  // Remove removes all identities with the given public key.
    52  func (r *keyring) Remove(key ssh.PublicKey) error {
    53  	r.mu.Lock()
    54  	defer r.mu.Unlock()
    55  	if r.locked {
    56  		return errLocked
    57  	}
    58  
    59  	want := key.Marshal()
    60  	found := false
    61  	for i := 0; i < len(r.keys); {
    62  		if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
    63  			found = true
    64  			r.keys[i] = r.keys[len(r.keys)-1]
    65  			r.keys = r.keys[len(r.keys)-1:]
    66  			continue
    67  		} else {
    68  			i++
    69  		}
    70  	}
    71  
    72  	if !found {
    73  		return errors.New("agent: key not found")
    74  	}
    75  	return nil
    76  }
    77  
    78  // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
    79  func (r *keyring) Lock(passphrase []byte) error {
    80  	r.mu.Lock()
    81  	defer r.mu.Unlock()
    82  	if r.locked {
    83  		return errLocked
    84  	}
    85  
    86  	r.locked = true
    87  	r.passphrase = passphrase
    88  	return nil
    89  }
    90  
    91  // Unlock undoes the effect of Lock
    92  func (r *keyring) Unlock(passphrase []byte) error {
    93  	r.mu.Lock()
    94  	defer r.mu.Unlock()
    95  	if !r.locked {
    96  		return errors.New("agent: not locked")
    97  	}
    98  	if len(passphrase) != len(r.passphrase) || 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) {
    99  		return fmt.Errorf("agent: incorrect passphrase")
   100  	}
   101  
   102  	r.locked = false
   103  	r.passphrase = nil
   104  	return nil
   105  }
   106  
   107  // List returns the identities known to the agent.
   108  func (r *keyring) List() ([]*Key, error) {
   109  	r.mu.Lock()
   110  	defer r.mu.Unlock()
   111  	if r.locked {
   112  		// section 2.7: locked agents return empty.
   113  		return nil, nil
   114  	}
   115  
   116  	var ids []*Key
   117  	for _, k := range r.keys {
   118  		pub := k.signer.PublicKey()
   119  		ids = append(ids, &Key{
   120  			Format:  pub.Type(),
   121  			Blob:    pub.Marshal(),
   122  			Comment: k.comment})
   123  	}
   124  	return ids, nil
   125  }
   126  
   127  // Insert adds a private key to the keyring. If a certificate
   128  // is given, that certificate is added as public key.
   129  func (r *keyring) Add(priv interface{}, cert *ssh.Certificate, comment string) error {
   130  	r.mu.Lock()
   131  	defer r.mu.Unlock()
   132  	if r.locked {
   133  		return errLocked
   134  	}
   135  	signer, err := ssh.NewSignerFromKey(priv)
   136  
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	if cert != nil {
   142  		signer, err = ssh.NewCertSigner(cert, signer)
   143  		if err != nil {
   144  			return err
   145  		}
   146  	}
   147  
   148  	r.keys = append(r.keys, privKey{signer, comment})
   149  
   150  	return nil
   151  }
   152  
   153  // Sign returns a signature for the data.
   154  func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
   155  	r.mu.Lock()
   156  	defer r.mu.Unlock()
   157  	if r.locked {
   158  		return nil, errLocked
   159  	}
   160  
   161  	wanted := key.Marshal()
   162  	for _, k := range r.keys {
   163  		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
   164  			return k.signer.Sign(rand.Reader, data)
   165  		}
   166  	}
   167  	return nil, errors.New("not found")
   168  }
   169  
   170  // Signers returns signers for all the known keys.
   171  func (r *keyring) Signers() ([]ssh.Signer, error) {
   172  	r.mu.Lock()
   173  	defer r.mu.Unlock()
   174  	if r.locked {
   175  		return nil, errLocked
   176  	}
   177  
   178  	s := make([]ssh.Signer, 0, len(r.keys))
   179  	for _, k := range r.keys {
   180  		s = append(s, k.signer)
   181  	}
   182  	return s, nil
   183  }