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. 4 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. 9// 10// References: 11// [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD 12package agent // import "golang.org/x/crypto/ssh/agent" 13 14import ( 15 "bytes" 16 "crypto/dsa" 17 "crypto/ecdsa" 18 "crypto/elliptic" 19 "crypto/rsa" 20 "encoding/base64" 21 "encoding/binary" 22 "errors" 23 "fmt" 24 "io" 25 "math/big" 26 "sync" 27 28 "golang.org/x/crypto/ed25519" 29 "golang.org/x/crypto/ssh" 30) 31 32// Agent represents the capabilities of an ssh-agent. 33type Agent interface { 34 // List returns the identities known to the agent. 35 List() ([]*Key, error) 36 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) 40 41 // Add adds a private key to the agent. 42 Add(key AddedKey) error 43 44 // Remove removes all identities with the given public key. 45 Remove(key ssh.PublicKey) error 46 47 // RemoveAll removes all identities. 48 RemoveAll() error 49 50 // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. 51 Lock(passphrase []byte) error 52 53 // Unlock undoes the effect of Lock 54 Unlock(passphrase []byte) error 55 56 // Signers returns signers for all the known keys. 57 Signers() ([]ssh.Signer, error) 58} 59 60// AddedKey describes an SSH key to be added to an Agent. 61type 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. 69 Comment string 70 // LifetimeSecs, if not zero, is the number of seconds that the 71 // agent will store the key for. 72 LifetimeSecs uint32 73 // ConfirmBeforeUse, if true, requests that the agent confirm with the 74 // user before each use of this key. 75 ConfirmBeforeUse bool 76} 77 78// See [PROTOCOL.agent], section 3. 79const ( 80 agentRequestV1Identities = 1 81 agentRemoveAllV1Identities = 9 82 83 // 3.2 Requests from client to agent for protocol 2 key operations 84 agentAddIdentity = 17 85 agentRemoveIdentity = 18 86 agentRemoveAllIdentities = 19 87 agentAddIdConstrained = 25 88 89 // 3.3 Key-type independent requests from client to agent 90 agentAddSmartcardKey = 20 91 agentRemoveSmartcardKey = 21 92 agentLock = 22 93 agentUnlock = 23 94 agentAddSmartcardKeyConstrained = 26 95 96 // 3.7 Key constraint identifiers 97 agentConstrainLifetime = 1 98 agentConstrainConfirm = 2 99) 100 101// maxAgentResponseBytes is the maximum agent reply size that is accepted. This 102// is a sanity check, not a limit in the spec. 103const maxAgentResponseBytes = 16 << 20 104 105// Agent messages: 106// These structures mirror the wire format of the corresponding ssh agent 107// messages found in [PROTOCOL.agent]. 108 109// 3.4 Generic replies from agent to client 110const agentFailure = 5 111 112type failureAgentMsg struct{} 113 114const agentSuccess = 6 115 116type successAgentMsg struct{} 117 118// See [PROTOCOL.agent], section 2.5.2. 119const agentRequestIdentities = 11 120 121type requestIdentitiesAgentMsg struct{} 122 123// See [PROTOCOL.agent], section 2.5.2. 124const agentIdentitiesAnswer = 12 125 126type identitiesAnswerAgentMsg struct { 127 NumKeys uint32 `sshtype:"12"` 128 Keys []byte `ssh:"rest"` 129} 130 131// See [PROTOCOL.agent], section 2.6.2. 132const agentSignRequest = 13 133 134type signRequestAgentMsg struct { 135 KeyBlob []byte `sshtype:"13"` 136 Data []byte 137 Flags uint32 138} 139 140// See [PROTOCOL.agent], section 2.6.2. 141 142// 3.6 Replies from agent to client for protocol 2 key operations 143const agentSignResponse = 14 144 145type signResponseAgentMsg struct { 146 SigBlob []byte `sshtype:"14"` 147} 148 149type publicKey struct { 150 Format string 151 Rest []byte `ssh:"rest"` 152} 153 154// Key represents a protocol 2 public key as defined in 155// [PROTOCOL.agent], section 2.5.2. 156type Key struct { 157 Format string 158 Blob []byte 159 Comment string 160} 161 162func clientErr(err error) error { 163 return fmt.Errorf("agent: client error: %v", err) 164} 165 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. 168func (k *Key) String() string { 169 s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob) 170 171 if k.Comment != "" { 172 s += " " + k.Comment 173 } 174 175 return s 176} 177 178// Type returns the public key type. 179func (k *Key) Type() string { 180 return k.Format 181} 182 183// Marshal returns key blob to satisfy the ssh.PublicKey interface. 184func (k *Key) Marshal() []byte { 185 return k.Blob 186} 187 188// Verify satisfies the ssh.PublicKey interface. 189func (k *Key) Verify(data []byte, sig *ssh.Signature) error { 190 pubKey, err := ssh.ParsePublicKey(k.Blob) 191 if err != nil { 192 return fmt.Errorf("agent: bad public key: %v", err) 193 } 194 return pubKey.Verify(data, sig) 195} 196 197type wireKey struct { 198 Format string 199 Rest []byte `ssh:"rest"` 200} 201 202func parseKey(in []byte) (out *Key, rest []byte, err error) { 203 var record struct { 204 Blob []byte 205 Comment string 206 Rest []byte `ssh:"rest"` 207 } 208 209 if err := ssh.Unmarshal(in, &record); err != nil { 210 return nil, nil, err 211 } 212 213 var wk wireKey 214 if err := ssh.Unmarshal(record.Blob, &wk); err != nil { 215 return nil, nil, err 216 } 217 218 return &Key{ 219 Format: wk.Format, 220 Blob: record.Blob, 221 Comment: record.Comment, 222 }, record.Rest, nil 223} 224 225// client is a client for an ssh-agent process. 226type client struct { 227 // conn is typically a *net.UnixConn 228 conn io.ReadWriter 229 // mu is used to prevent concurrent access to the agent 230 mu sync.Mutex 231} 232 233// NewClient returns an Agent that talks to an ssh-agent process over 234// the given connection. 235func NewClient(rw io.ReadWriter) Agent { 236 return &client{conn: rw} 237} 238 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. 242func (c *client) call(req []byte) (reply interface{}, err error) { 243 c.mu.Lock() 244 defer c.mu.Unlock() 245 246 msg := make([]byte, 4+len(req)) 247 binary.BigEndian.PutUint32(msg, uint32(len(req))) 248 copy(msg[4:], req) 249 if _, err = c.conn.Write(msg); err != nil { 250 return nil, clientErr(err) 251 } 252 253 var respSizeBuf [4]byte 254 if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil { 255 return nil, clientErr(err) 256 } 257 respSize := binary.BigEndian.Uint32(respSizeBuf[:]) 258 if respSize > maxAgentResponseBytes { 259 return nil, clientErr(err) 260 } 261 262 buf := make([]byte, respSize) 263 if _, err = io.ReadFull(c.conn, buf); err != nil { 264 return nil, clientErr(err) 265 } 266 reply, err = unmarshal(buf) 267 if err != nil { 268 return nil, clientErr(err) 269 } 270 return reply, err 271} 272 273func (c *client) simpleCall(req []byte) error { 274 resp, err := c.call(req) 275 if err != nil { 276 return err 277 } 278 if _, ok := resp.(*successAgentMsg); ok { 279 return nil 280 } 281 return errors.New("agent: failure") 282} 283 284func (c *client) RemoveAll() error { 285 return c.simpleCall([]byte{agentRemoveAllIdentities}) 286} 287 288func (c *client) Remove(key ssh.PublicKey) error { 289 req := ssh.Marshal(&agentRemoveIdentityMsg{ 290 KeyBlob: key.Marshal(), 291 }) 292 return c.simpleCall(req) 293} 294 295func (c *client) Lock(passphrase []byte) error { 296 req := ssh.Marshal(&agentLockMsg{ 297 Passphrase: passphrase, 298 }) 299 return c.simpleCall(req) 300} 301 302func (c *client) Unlock(passphrase []byte) error { 303 req := ssh.Marshal(&agentUnlockMsg{ 304 Passphrase: passphrase, 305 }) 306 return c.simpleCall(req) 307} 308 309// List returns the identities known to the agent. 310func (c *client) List() ([]*Key, error) { 311 // see [PROTOCOL.agent] section 2.5.2. 312 req := []byte{agentRequestIdentities} 313 314 msg, err := c.call(req) 315 if err != nil { 316 return nil, err 317 } 318 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") 323 } 324 keys := make([]*Key, msg.NumKeys) 325 data := msg.Keys 326 for i := uint32(0); i < msg.NumKeys; i++ { 327 var key *Key 328 var err error 329 if key, data, err = parseKey(data); err != nil { 330 return nil, err 331 } 332 keys[i] = key 333 } 334 return keys, nil 335 case *failureAgentMsg: 336 return nil, errors.New("agent: failed to list keys") 337 } 338 panic("unreachable") 339} 340 341// Sign has the agent sign the data using a protocol 2 key as defined 342// in [PROTOCOL.agent] section 2.6.2. 343func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { 344 req := ssh.Marshal(signRequestAgentMsg{ 345 KeyBlob: key.Marshal(), 346 Data: data, 347 }) 348 349 msg, err := c.call(req) 350 if err != nil { 351 return nil, err 352 } 353 354 switch msg := msg.(type) { 355 case *signResponseAgentMsg: 356 var sig ssh.Signature 357 if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { 358 return nil, err 359 } 360 361 return &sig, nil 362 case *failureAgentMsg: 363 return nil, errors.New("agent: failed to sign challenge") 364 } 365 panic("unreachable") 366} 367 368// unmarshal parses an agent message in packet, returning the parsed 369// form and the message type of packet. 370func unmarshal(packet []byte) (interface{}, error) { 371 if len(packet) < 1 { 372 return nil, errors.New("agent: empty packet") 373 } 374 var msg interface{} 375 switch packet[0] { 376 case agentFailure: 377 return new(failureAgentMsg), nil 378 case agentSuccess: 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) 386 default: 387 return nil, fmt.Errorf("agent: unknown type tag %d", packet[0]) 388 } 389 if err := ssh.Unmarshal(packet, msg); err != nil { 390 return nil, err 391 } 392 return msg, nil 393} 394 395type rsaKeyMsg struct { 396 Type string `sshtype:"17|25"` 397 N *big.Int 398 E *big.Int 399 D *big.Int 400 Iqmp *big.Int // IQMP = Inverse Q Mod P 401 P *big.Int 402 Q *big.Int 403 Comments string 404 Constraints []byte `ssh:"rest"` 405} 406 407type dsaKeyMsg struct { 408 Type string `sshtype:"17|25"` 409 P *big.Int 410 Q *big.Int 411 G *big.Int 412 Y *big.Int 413 X *big.Int 414 Comments string 415 Constraints []byte `ssh:"rest"` 416} 417 418type ecdsaKeyMsg struct { 419 Type string `sshtype:"17|25"` 420 Curve string 421 KeyBytes []byte 422 D *big.Int 423 Comments string 424 Constraints []byte `ssh:"rest"` 425} 426 427type ed25519KeyMsg struct { 428 Type string `sshtype:"17|25"` 429 Pub []byte 430 Priv []byte 431 Comments string 432 Constraints []byte `ssh:"rest"` 433} 434 435// Insert adds a private key to the agent. 436func (c *client) insertKey(s interface{}, comment string, constraints []byte) error { 437 var req []byte 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)) 442 } 443 k.Precompute() 444 req = ssh.Marshal(rsaKeyMsg{ 445 Type: ssh.KeyAlgoRSA, 446 N: k.N, 447 E: big.NewInt(int64(k.E)), 448 D: k.D, 449 Iqmp: k.Precomputed.Qinv, 450 P: k.Primes[0], 451 Q: k.Primes[1], 452 Comments: comment, 453 Constraints: constraints, 454 }) 455 case *dsa.PrivateKey: 456 req = ssh.Marshal(dsaKeyMsg{ 457 Type: ssh.KeyAlgoDSA, 458 P: k.P, 459 Q: k.Q, 460 G: k.G, 461 Y: k.Y, 462 X: k.X, 463 Comments: comment, 464 Constraints: constraints, 465 }) 466 case *ecdsa.PrivateKey: 467 nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) 468 req = ssh.Marshal(ecdsaKeyMsg{ 469 Type: "ecdsa-sha2-" + nistID, 470 Curve: nistID, 471 KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), 472 D: k.D, 473 Comments: comment, 474 Constraints: constraints, 475 }) 476 case *ed25519.PrivateKey: 477 req = ssh.Marshal(ed25519KeyMsg{ 478 Type: ssh.KeyAlgoED25519, 479 Pub: []byte(*k)[32:], 480 Priv: []byte(*k), 481 Comments: comment, 482 Constraints: constraints, 483 }) 484 default: 485 return fmt.Errorf("agent: unsupported key type %T", s) 486 } 487 488 // if constraints are present then the message type needs to be changed. 489 if len(constraints) != 0 { 490 req[0] = agentAddIdConstrained 491 } 492 493 resp, err := c.call(req) 494 if err != nil { 495 return err 496 } 497 if _, ok := resp.(*successAgentMsg); ok { 498 return nil 499 } 500 return errors.New("agent: failure") 501} 502 503type rsaCertMsg struct { 504 Type string `sshtype:"17|25"` 505 CertBytes []byte 506 D *big.Int 507 Iqmp *big.Int // IQMP = Inverse Q Mod P 508 P *big.Int 509 Q *big.Int 510 Comments string 511 Constraints []byte `ssh:"rest"` 512} 513 514type dsaCertMsg struct { 515 Type string `sshtype:"17|25"` 516 CertBytes []byte 517 X *big.Int 518 Comments string 519 Constraints []byte `ssh:"rest"` 520} 521 522type ecdsaCertMsg struct { 523 Type string `sshtype:"17|25"` 524 CertBytes []byte 525 D *big.Int 526 Comments string 527 Constraints []byte `ssh:"rest"` 528} 529 530type ed25519CertMsg struct { 531 Type string `sshtype:"17|25"` 532 CertBytes []byte 533 Pub []byte 534 Priv []byte 535 Comments string 536 Constraints []byte `ssh:"rest"` 537} 538 539// Insert adds a private key to the agent. If a certificate is given, 540// that certificate is added instead as public key. 541func (c *client) Add(key AddedKey) error { 542 var constraints []byte 543 544 if secs := key.LifetimeSecs; secs != 0 { 545 constraints = append(constraints, agentConstrainLifetime) 546 547 var secsBytes [4]byte 548 binary.BigEndian.PutUint32(secsBytes[:], secs) 549 constraints = append(constraints, secsBytes[:]...) 550 } 551 552 if key.ConfirmBeforeUse { 553 constraints = append(constraints, agentConstrainConfirm) 554 } 555 556 if cert := key.Certificate; cert == nil { 557 return c.insertKey(key.PrivateKey, key.Comment, constraints) 558 } else { 559 return c.insertCert(key.PrivateKey, cert, key.Comment, constraints) 560 } 561} 562 563func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { 564 var req []byte 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)) 569 } 570 k.Precompute() 571 req = ssh.Marshal(rsaCertMsg{ 572 Type: cert.Type(), 573 CertBytes: cert.Marshal(), 574 D: k.D, 575 Iqmp: k.Precomputed.Qinv, 576 P: k.Primes[0], 577 Q: k.Primes[1], 578 Comments: comment, 579 Constraints: constraints, 580 }) 581 case *dsa.PrivateKey: 582 req = ssh.Marshal(dsaCertMsg{ 583 Type: cert.Type(), 584 CertBytes: cert.Marshal(), 585 X: k.X, 586 Comments: comment, 587 Constraints: constraints, 588 }) 589 case *ecdsa.PrivateKey: 590 req = ssh.Marshal(ecdsaCertMsg{ 591 Type: cert.Type(), 592 CertBytes: cert.Marshal(), 593 D: k.D, 594 Comments: comment, 595 Constraints: constraints, 596 }) 597 case ed25519.PrivateKey: 598 req = ssh.Marshal(ed25519CertMsg{ 599 Type: cert.Type(), 600 CertBytes: cert.Marshal(), 601 Pub: []byte(k)[32:], 602 Priv: []byte(k), 603 Comments: comment, 604 Constraints: constraints, 605 }) 606 default: 607 return fmt.Errorf("agent: unsupported key type %T", s) 608 } 609 610 // if constraints are present then the message type needs to be changed. 611 if len(constraints) != 0 { 612 req[0] = agentAddIdConstrained 613 } 614 615 signer, err := ssh.NewSignerFromKey(s) 616 if err != nil { 617 return err 618 } 619 if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { 620 return errors.New("agent: signer and cert have different public key") 621 } 622 623 resp, err := c.call(req) 624 if err != nil { 625 return err 626 } 627 if _, ok := resp.(*successAgentMsg); ok { 628 return nil 629 } 630 return errors.New("agent: failure") 631} 632 633// Signers provides a callback for client authentication. 634func (c *client) Signers() ([]ssh.Signer, error) { 635 keys, err := c.List() 636 if err != nil { 637 return nil, err 638 } 639 640 var result []ssh.Signer 641 for _, k := range keys { 642 result = append(result, &agentKeyringSigner{c, k}) 643 } 644 return result, nil 645} 646 647type agentKeyringSigner struct { 648 agent *client 649 pub ssh.PublicKey 650} 651 652func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { 653 return s.pub 654} 655 656func (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) 659} 660