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