github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/golang.org/x/crypto/ssh/agent/client.go (about) 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 12 package agent // import "golang.org/x/crypto/ssh/agent" 13 14 import ( 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. 33 type 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. 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. 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. 79 const ( 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. 103 const 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 110 const agentFailure = 5 111 112 type failureAgentMsg struct{} 113 114 const agentSuccess = 6 115 116 type successAgentMsg struct{} 117 118 // See [PROTOCOL.agent], section 2.5.2. 119 const agentRequestIdentities = 11 120 121 type requestIdentitiesAgentMsg struct{} 122 123 // See [PROTOCOL.agent], section 2.5.2. 124 const agentIdentitiesAnswer = 12 125 126 type identitiesAnswerAgentMsg struct { 127 NumKeys uint32 `sshtype:"12"` 128 Keys []byte `ssh:"rest"` 129 } 130 131 // See [PROTOCOL.agent], section 2.6.2. 132 const agentSignRequest = 13 133 134 type 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 143 const agentSignResponse = 14 144 145 type signResponseAgentMsg struct { 146 SigBlob []byte `sshtype:"14"` 147 } 148 149 type 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. 156 type Key struct { 157 Format string 158 Blob []byte 159 Comment string 160 } 161 162 func 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. 168 func (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. 179 func (k *Key) Type() string { 180 return k.Format 181 } 182 183 // Marshal returns key blob to satisfy the ssh.PublicKey interface. 184 func (k *Key) Marshal() []byte { 185 return k.Blob 186 } 187 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) 191 if err != nil { 192 return fmt.Errorf("agent: bad public key: %v", err) 193 } 194 return pubKey.Verify(data, sig) 195 } 196 197 type wireKey struct { 198 Format string 199 Rest []byte `ssh:"rest"` 200 } 201 202 func 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. 226 type 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. 235 func 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. 242 func (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 273 func (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 284 func (c *client) RemoveAll() error { 285 return c.simpleCall([]byte{agentRemoveAllIdentities}) 286 } 287 288 func (c *client) Remove(key ssh.PublicKey) error { 289 req := ssh.Marshal(&agentRemoveIdentityMsg{ 290 KeyBlob: key.Marshal(), 291 }) 292 return c.simpleCall(req) 293 } 294 295 func (c *client) Lock(passphrase []byte) error { 296 req := ssh.Marshal(&agentLockMsg{ 297 Passphrase: passphrase, 298 }) 299 return c.simpleCall(req) 300 } 301 302 func (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. 310 func (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. 343 func (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. 370 func 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 395 type 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 407 type 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 418 type 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 427 type 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. 436 func (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 503 type 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 514 type 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 522 type 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 530 type 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 // 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 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 563 func (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. 634 func (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 647 type agentKeyringSigner struct { 648 agent *client 649 pub ssh.PublicKey 650 } 651 652 func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { 653 return s.pub 654 } 655 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) 659 }