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 }