You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

keyring.go 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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. package agent
  5. import (
  6. "bytes"
  7. "crypto/rand"
  8. "crypto/subtle"
  9. "errors"
  10. "fmt"
  11. "sync"
  12. "time"
  13. "golang.org/x/crypto/ssh"
  14. )
  15. type privKey struct {
  16. signer ssh.Signer
  17. comment string
  18. expire *time.Time
  19. }
  20. type keyring struct {
  21. mu sync.Mutex
  22. keys []privKey
  23. locked bool
  24. passphrase []byte
  25. }
  26. var errLocked = errors.New("agent: locked")
  27. // NewKeyring returns an Agent that holds keys in memory. It is safe
  28. // for concurrent use by multiple goroutines.
  29. func NewKeyring() Agent {
  30. return &keyring{}
  31. }
  32. // RemoveAll removes all identities.
  33. func (r *keyring) RemoveAll() error {
  34. r.mu.Lock()
  35. defer r.mu.Unlock()
  36. if r.locked {
  37. return errLocked
  38. }
  39. r.keys = nil
  40. return nil
  41. }
  42. // removeLocked does the actual key removal. The caller must already be holding the
  43. // keyring mutex.
  44. func (r *keyring) removeLocked(want []byte) error {
  45. found := false
  46. for i := 0; i < len(r.keys); {
  47. if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
  48. found = true
  49. r.keys[i] = r.keys[len(r.keys)-1]
  50. r.keys = r.keys[:len(r.keys)-1]
  51. continue
  52. } else {
  53. i++
  54. }
  55. }
  56. if !found {
  57. return errors.New("agent: key not found")
  58. }
  59. return nil
  60. }
  61. // Remove removes all identities with the given public key.
  62. func (r *keyring) Remove(key ssh.PublicKey) error {
  63. r.mu.Lock()
  64. defer r.mu.Unlock()
  65. if r.locked {
  66. return errLocked
  67. }
  68. return r.removeLocked(key.Marshal())
  69. }
  70. // Lock locks the agent. Sign and Remove will fail, and List will return an empty list.
  71. func (r *keyring) Lock(passphrase []byte) error {
  72. r.mu.Lock()
  73. defer r.mu.Unlock()
  74. if r.locked {
  75. return errLocked
  76. }
  77. r.locked = true
  78. r.passphrase = passphrase
  79. return nil
  80. }
  81. // Unlock undoes the effect of Lock
  82. func (r *keyring) Unlock(passphrase []byte) error {
  83. r.mu.Lock()
  84. defer r.mu.Unlock()
  85. if !r.locked {
  86. return errors.New("agent: not locked")
  87. }
  88. if 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) {
  89. return fmt.Errorf("agent: incorrect passphrase")
  90. }
  91. r.locked = false
  92. r.passphrase = nil
  93. return nil
  94. }
  95. // expireKeysLocked removes expired keys from the keyring. If a key was added
  96. // with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
  97. // ellapsed, it is removed. The caller *must* be holding the keyring mutex.
  98. func (r *keyring) expireKeysLocked() {
  99. for _, k := range r.keys {
  100. if k.expire != nil && time.Now().After(*k.expire) {
  101. r.removeLocked(k.signer.PublicKey().Marshal())
  102. }
  103. }
  104. }
  105. // List returns the identities known to the agent.
  106. func (r *keyring) List() ([]*Key, error) {
  107. r.mu.Lock()
  108. defer r.mu.Unlock()
  109. if r.locked {
  110. // section 2.7: locked agents return empty.
  111. return nil, nil
  112. }
  113. r.expireKeysLocked()
  114. var ids []*Key
  115. for _, k := range r.keys {
  116. pub := k.signer.PublicKey()
  117. ids = append(ids, &Key{
  118. Format: pub.Type(),
  119. Blob: pub.Marshal(),
  120. Comment: k.comment})
  121. }
  122. return ids, nil
  123. }
  124. // Insert adds a private key to the keyring. If a certificate
  125. // is given, that certificate is added as public key. Note that
  126. // any constraints given are ignored.
  127. func (r *keyring) Add(key AddedKey) error {
  128. r.mu.Lock()
  129. defer r.mu.Unlock()
  130. if r.locked {
  131. return errLocked
  132. }
  133. signer, err := ssh.NewSignerFromKey(key.PrivateKey)
  134. if err != nil {
  135. return err
  136. }
  137. if cert := key.Certificate; cert != nil {
  138. signer, err = ssh.NewCertSigner(cert, signer)
  139. if err != nil {
  140. return err
  141. }
  142. }
  143. p := privKey{
  144. signer: signer,
  145. comment: key.Comment,
  146. }
  147. if key.LifetimeSecs > 0 {
  148. t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second)
  149. p.expire = &t
  150. }
  151. r.keys = append(r.keys, p)
  152. return nil
  153. }
  154. // Sign returns a signature for the data.
  155. func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
  156. return r.SignWithFlags(key, data, 0)
  157. }
  158. func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
  159. r.mu.Lock()
  160. defer r.mu.Unlock()
  161. if r.locked {
  162. return nil, errLocked
  163. }
  164. r.expireKeysLocked()
  165. wanted := key.Marshal()
  166. for _, k := range r.keys {
  167. if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) {
  168. if flags == 0 {
  169. return k.signer.Sign(rand.Reader, data)
  170. } else {
  171. if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok {
  172. return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer)
  173. } else {
  174. var algorithm string
  175. switch flags {
  176. case SignatureFlagRsaSha256:
  177. algorithm = ssh.SigAlgoRSASHA2256
  178. case SignatureFlagRsaSha512:
  179. algorithm = ssh.SigAlgoRSASHA2512
  180. default:
  181. return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
  182. }
  183. return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm)
  184. }
  185. }
  186. }
  187. }
  188. return nil, errors.New("not found")
  189. }
  190. // Signers returns signers for all the known keys.
  191. func (r *keyring) Signers() ([]ssh.Signer, error) {
  192. r.mu.Lock()
  193. defer r.mu.Unlock()
  194. if r.locked {
  195. return nil, errLocked
  196. }
  197. r.expireKeysLocked()
  198. s := make([]ssh.Signer, 0, len(r.keys))
  199. for _, k := range r.keys {
  200. s = append(s, k.signer)
  201. }
  202. return s, nil
  203. }
  204. // The keyring does not support any extensions
  205. func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) {
  206. return nil, ErrExtensionUnsupported
  207. }