github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/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. Note that
   129  // any constraints given are ignored.
   130  func (r *keyring) Add(key AddedKey) error {
   131  	r.mu.Lock()
   132  	defer r.mu.Unlock()
   133  	if r.locked {
   134  		return errLocked
   135  	}
   136  	signer, err := ssh.NewSignerFromKey(key.PrivateKey)
   137  
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	if cert := key.Certificate; cert != nil {
   143  		signer, err = ssh.NewCertSigner(cert, signer)
   144  		if err != nil {
   145  			return err
   146  		}
   147  	}
   148  
   149  	r.keys = append(r.keys, privKey{signer, key.Comment})
   150  
   151  	return nil
   152  }
   153  
   154  // Sign returns a signature for the data.
   155  func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
   156  	r.mu.Lock()
   157  	defer r.mu.Unlock()
   158  	if r.locked {
   159  		return nil, errLocked
   160  	}
   161  
   162  	wanted := key.Marshal()
   163  	for _, k := range r.keys {
   164  		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
   165  			return k.signer.Sign(rand.Reader, data)
   166  		}
   167  	}
   168  	return nil, errors.New("not found")
   169  }
   170  
   171  // Signers returns signers for all the known keys.
   172  func (r *keyring) Signers() ([]ssh.Signer, error) {
   173  	r.mu.Lock()
   174  	defer r.mu.Unlock()
   175  	if r.locked {
   176  		return nil, errLocked
   177  	}
   178  
   179  	s := make([]ssh.Signer, 0, len(r.keys))
   180  	for _, k := range r.keys {
   181  		s = append(s, k.signer)
   182  	}
   183  	return s, nil
   184  }