1 // Copyright 2012 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.
5 // Package agent implements the ssh-agent protocol, and provides both
6 // a client and a server. The client can talk to a standard ssh-agent
7 // that uses UNIX sockets, and one could implement an alternative
8 // ssh-agent process using the sample server.
11 // [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
12 package agent // import "golang.org/x/crypto/ssh/agent"
28 "golang.org/x/crypto/ed25519"
29 "golang.org/x/crypto/ssh"
32 // Agent represents the capabilities of an ssh-agent.
33 type Agent interface {
34 // List returns the identities known to the agent.
35 List() ([]*Key, error)
37 // Sign has the agent sign the data using a protocol 2 key as defined
38 // in [PROTOCOL.agent] section 2.6.2.
39 Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
41 // Add adds a private key to the agent.
42 Add(key AddedKey) error
44 // Remove removes all identities with the given public key.
45 Remove(key ssh.PublicKey) error
47 // RemoveAll removes all identities.
50 // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
51 Lock(passphrase []byte) error
53 // Unlock undoes the effect of Lock
54 Unlock(passphrase []byte) error
56 // Signers returns signers for all the known keys.
57 Signers() ([]ssh.Signer, error)
60 // AddedKey describes an SSH key to be added to an Agent.
61 type AddedKey struct {
62 // PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
63 // *ecdsa.PrivateKey, which will be inserted into the agent.
64 PrivateKey interface{}
65 // Certificate, if not nil, is communicated to the agent and will be
66 // stored with the key.
67 Certificate *ssh.Certificate
68 // Comment is an optional, free-form string.
70 // LifetimeSecs, if not zero, is the number of seconds that the
71 // agent will store the key for.
73 // ConfirmBeforeUse, if true, requests that the agent confirm with the
74 // user before each use of this key.
78 // See [PROTOCOL.agent], section 3.
80 agentRequestV1Identities = 1
81 agentRemoveAllV1Identities = 9
83 // 3.2 Requests from client to agent for protocol 2 key operations
85 agentRemoveIdentity = 18
86 agentRemoveAllIdentities = 19
87 agentAddIdConstrained = 25
89 // 3.3 Key-type independent requests from client to agent
90 agentAddSmartcardKey = 20
91 agentRemoveSmartcardKey = 21
94 agentAddSmartcardKeyConstrained = 26
96 // 3.7 Key constraint identifiers
97 agentConstrainLifetime = 1
98 agentConstrainConfirm = 2
101 // maxAgentResponseBytes is the maximum agent reply size that is accepted. This
102 // is a sanity check, not a limit in the spec.
103 const maxAgentResponseBytes = 16 << 20
106 // These structures mirror the wire format of the corresponding ssh agent
107 // messages found in [PROTOCOL.agent].
109 // 3.4 Generic replies from agent to client
110 const agentFailure = 5
112 type failureAgentMsg struct{}
114 const agentSuccess = 6
116 type successAgentMsg struct{}
118 // See [PROTOCOL.agent], section 2.5.2.
119 const agentRequestIdentities = 11
121 type requestIdentitiesAgentMsg struct{}
123 // See [PROTOCOL.agent], section 2.5.2.
124 const agentIdentitiesAnswer = 12
126 type identitiesAnswerAgentMsg struct {
127 NumKeys uint32 `sshtype:"12"`
128 Keys []byte `ssh:"rest"`
131 // See [PROTOCOL.agent], section 2.6.2.
132 const agentSignRequest = 13
134 type signRequestAgentMsg struct {
135 KeyBlob []byte `sshtype:"13"`
140 // See [PROTOCOL.agent], section 2.6.2.
142 // 3.6 Replies from agent to client for protocol 2 key operations
143 const agentSignResponse = 14
145 type signResponseAgentMsg struct {
146 SigBlob []byte `sshtype:"14"`
149 type publicKey struct {
151 Rest []byte `ssh:"rest"`
154 // Key represents a protocol 2 public key as defined in
155 // [PROTOCOL.agent], section 2.5.2.
162 func clientErr(err error) error {
163 return fmt.Errorf("agent: client error: %v", err)
166 // String returns the storage form of an agent key with the format, base64
167 // encoded serialized key, and the comment if it is not empty.
168 func (k *Key) String() string {
169 s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
178 // Type returns the public key type.
179 func (k *Key) Type() string {
183 // Marshal returns key blob to satisfy the ssh.PublicKey interface.
184 func (k *Key) Marshal() []byte {
188 // Verify satisfies the ssh.PublicKey interface.
189 func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
190 pubKey, err := ssh.ParsePublicKey(k.Blob)
192 return fmt.Errorf("agent: bad public key: %v", err)
194 return pubKey.Verify(data, sig)
197 type wireKey struct {
199 Rest []byte `ssh:"rest"`
202 func parseKey(in []byte) (out *Key, rest []byte, err error) {
206 Rest []byte `ssh:"rest"`
209 if err := ssh.Unmarshal(in, &record); err != nil {
214 if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
221 Comment: record.Comment,
225 // client is a client for an ssh-agent process.
227 // conn is typically a *net.UnixConn
229 // mu is used to prevent concurrent access to the agent
233 // NewClient returns an Agent that talks to an ssh-agent process over
234 // the given connection.
235 func NewClient(rw io.ReadWriter) Agent {
236 return &client{conn: rw}
239 // call sends an RPC to the agent. On success, the reply is
240 // unmarshaled into reply and replyType is set to the first byte of
241 // the reply, which contains the type of the message.
242 func (c *client) call(req []byte) (reply interface{}, err error) {
246 msg := make([]byte, 4+len(req))
247 binary.BigEndian.PutUint32(msg, uint32(len(req)))
249 if _, err = c.conn.Write(msg); err != nil {
250 return nil, clientErr(err)
253 var respSizeBuf [4]byte
254 if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
255 return nil, clientErr(err)
257 respSize := binary.BigEndian.Uint32(respSizeBuf[:])
258 if respSize > maxAgentResponseBytes {
259 return nil, clientErr(err)
262 buf := make([]byte, respSize)
263 if _, err = io.ReadFull(c.conn, buf); err != nil {
264 return nil, clientErr(err)
266 reply, err = unmarshal(buf)
268 return nil, clientErr(err)
273 func (c *client) simpleCall(req []byte) error {
274 resp, err := c.call(req)
278 if _, ok := resp.(*successAgentMsg); ok {
281 return errors.New("agent: failure")
284 func (c *client) RemoveAll() error {
285 return c.simpleCall([]byte{agentRemoveAllIdentities})
288 func (c *client) Remove(key ssh.PublicKey) error {
289 req := ssh.Marshal(&agentRemoveIdentityMsg{
290 KeyBlob: key.Marshal(),
292 return c.simpleCall(req)
295 func (c *client) Lock(passphrase []byte) error {
296 req := ssh.Marshal(&agentLockMsg{
297 Passphrase: passphrase,
299 return c.simpleCall(req)
302 func (c *client) Unlock(passphrase []byte) error {
303 req := ssh.Marshal(&agentUnlockMsg{
304 Passphrase: passphrase,
306 return c.simpleCall(req)
309 // List returns the identities known to the agent.
310 func (c *client) List() ([]*Key, error) {
311 // see [PROTOCOL.agent] section 2.5.2.
312 req := []byte{agentRequestIdentities}
314 msg, err := c.call(req)
319 switch msg := msg.(type) {
320 case *identitiesAnswerAgentMsg:
321 if msg.NumKeys > maxAgentResponseBytes/8 {
322 return nil, errors.New("agent: too many keys in agent reply")
324 keys := make([]*Key, msg.NumKeys)
326 for i := uint32(0); i < msg.NumKeys; i++ {
329 if key, data, err = parseKey(data); err != nil {
335 case *failureAgentMsg:
336 return nil, errors.New("agent: failed to list keys")
341 // Sign has the agent sign the data using a protocol 2 key as defined
342 // in [PROTOCOL.agent] section 2.6.2.
343 func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
344 req := ssh.Marshal(signRequestAgentMsg{
345 KeyBlob: key.Marshal(),
349 msg, err := c.call(req)
354 switch msg := msg.(type) {
355 case *signResponseAgentMsg:
356 var sig ssh.Signature
357 if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
362 case *failureAgentMsg:
363 return nil, errors.New("agent: failed to sign challenge")
368 // unmarshal parses an agent message in packet, returning the parsed
369 // form and the message type of packet.
370 func unmarshal(packet []byte) (interface{}, error) {
372 return nil, errors.New("agent: empty packet")
377 return new(failureAgentMsg), nil
379 return new(successAgentMsg), nil
380 case agentIdentitiesAnswer:
381 msg = new(identitiesAnswerAgentMsg)
382 case agentSignResponse:
383 msg = new(signResponseAgentMsg)
384 case agentV1IdentitiesAnswer:
385 msg = new(agentV1IdentityMsg)
387 return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
389 if err := ssh.Unmarshal(packet, msg); err != nil {
395 type rsaKeyMsg struct {
396 Type string `sshtype:"17|25"`
400 Iqmp *big.Int // IQMP = Inverse Q Mod P
404 Constraints []byte `ssh:"rest"`
407 type dsaKeyMsg struct {
408 Type string `sshtype:"17|25"`
415 Constraints []byte `ssh:"rest"`
418 type ecdsaKeyMsg struct {
419 Type string `sshtype:"17|25"`
424 Constraints []byte `ssh:"rest"`
427 type ed25519KeyMsg struct {
428 Type string `sshtype:"17|25"`
432 Constraints []byte `ssh:"rest"`
435 // Insert adds a private key to the agent.
436 func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
438 switch k := s.(type) {
439 case *rsa.PrivateKey:
440 if len(k.Primes) != 2 {
441 return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
444 req = ssh.Marshal(rsaKeyMsg{
445 Type: ssh.KeyAlgoRSA,
447 E: big.NewInt(int64(k.E)),
449 Iqmp: k.Precomputed.Qinv,
453 Constraints: constraints,
455 case *dsa.PrivateKey:
456 req = ssh.Marshal(dsaKeyMsg{
457 Type: ssh.KeyAlgoDSA,
464 Constraints: constraints,
466 case *ecdsa.PrivateKey:
467 nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
468 req = ssh.Marshal(ecdsaKeyMsg{
469 Type: "ecdsa-sha2-" + nistID,
471 KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
474 Constraints: constraints,
476 case *ed25519.PrivateKey:
477 req = ssh.Marshal(ed25519KeyMsg{
478 Type: ssh.KeyAlgoED25519,
479 Pub: []byte(*k)[32:],
482 Constraints: constraints,
485 return fmt.Errorf("agent: unsupported key type %T", s)
488 // if constraints are present then the message type needs to be changed.
489 if len(constraints) != 0 {
490 req[0] = agentAddIdConstrained
493 resp, err := c.call(req)
497 if _, ok := resp.(*successAgentMsg); ok {
500 return errors.New("agent: failure")
503 type rsaCertMsg struct {
504 Type string `sshtype:"17|25"`
507 Iqmp *big.Int // IQMP = Inverse Q Mod P
511 Constraints []byte `ssh:"rest"`
514 type dsaCertMsg struct {
515 Type string `sshtype:"17|25"`
519 Constraints []byte `ssh:"rest"`
522 type ecdsaCertMsg struct {
523 Type string `sshtype:"17|25"`
527 Constraints []byte `ssh:"rest"`
530 type ed25519CertMsg struct {
531 Type string `sshtype:"17|25"`
536 Constraints []byte `ssh:"rest"`
539 // Add adds a private key to the agent. If a certificate is given,
540 // that certificate is added instead as public key.
541 func (c *client) Add(key AddedKey) error {
542 var constraints []byte
544 if secs := key.LifetimeSecs; secs != 0 {
545 constraints = append(constraints, agentConstrainLifetime)
547 var secsBytes [4]byte
548 binary.BigEndian.PutUint32(secsBytes[:], secs)
549 constraints = append(constraints, secsBytes[:]...)
552 if key.ConfirmBeforeUse {
553 constraints = append(constraints, agentConstrainConfirm)
556 if cert := key.Certificate; cert == nil {
557 return c.insertKey(key.PrivateKey, key.Comment, constraints)
559 return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
563 func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
565 switch k := s.(type) {
566 case *rsa.PrivateKey:
567 if len(k.Primes) != 2 {
568 return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
571 req = ssh.Marshal(rsaCertMsg{
573 CertBytes: cert.Marshal(),
575 Iqmp: k.Precomputed.Qinv,
579 Constraints: constraints,
581 case *dsa.PrivateKey:
582 req = ssh.Marshal(dsaCertMsg{
584 CertBytes: cert.Marshal(),
587 Constraints: constraints,
589 case *ecdsa.PrivateKey:
590 req = ssh.Marshal(ecdsaCertMsg{
592 CertBytes: cert.Marshal(),
595 Constraints: constraints,
597 case *ed25519.PrivateKey:
598 req = ssh.Marshal(ed25519CertMsg{
600 CertBytes: cert.Marshal(),
601 Pub: []byte(*k)[32:],
604 Constraints: constraints,
607 return fmt.Errorf("agent: unsupported key type %T", s)
610 // if constraints are present then the message type needs to be changed.
611 if len(constraints) != 0 {
612 req[0] = agentAddIdConstrained
615 signer, err := ssh.NewSignerFromKey(s)
619 if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
620 return errors.New("agent: signer and cert have different public key")
623 resp, err := c.call(req)
627 if _, ok := resp.(*successAgentMsg); ok {
630 return errors.New("agent: failure")
633 // Signers provides a callback for client authentication.
634 func (c *client) Signers() ([]ssh.Signer, error) {
635 keys, err := c.List()
640 var result []ssh.Signer
641 for _, k := range keys {
642 result = append(result, &agentKeyringSigner{c, k})
647 type agentKeyringSigner struct {
652 func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
656 func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
657 // The agent has its own entropy source, so the rand argument is ignored.
658 return s.agent.Sign(s.pub, data)