1
2
3
4
5
6
7
8
9
10
11
12
13 package agent
14
15 import (
16 "bytes"
17 "crypto/dsa"
18 "crypto/ecdsa"
19 "crypto/ed25519"
20 "crypto/elliptic"
21 "crypto/rsa"
22 "encoding/base64"
23 "encoding/binary"
24 "errors"
25 "fmt"
26 "io"
27 "math/big"
28 "sync"
29
30 "golang.org/x/crypto/ssh"
31 )
32
33
34
35 type SignatureFlags uint32
36
37
38 const (
39 SignatureFlagReserved SignatureFlags = 1 << iota
40 SignatureFlagRsaSha256
41 SignatureFlagRsaSha512
42 )
43
44
45 type Agent interface {
46
47 List() ([]*Key, error)
48
49
50
51 Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
52
53
54 Add(key AddedKey) error
55
56
57 Remove(key ssh.PublicKey) error
58
59
60 RemoveAll() error
61
62
63 Lock(passphrase []byte) error
64
65
66 Unlock(passphrase []byte) error
67
68
69 Signers() ([]ssh.Signer, error)
70 }
71
72 type ExtendedAgent interface {
73 Agent
74
75
76 SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error)
77
78
79
80
81
82
83
84
85
86
87
88
89 Extension(extensionType string, contents []byte) ([]byte, error)
90 }
91
92
93 type ConstraintExtension struct {
94
95
96
97 ExtensionName string
98
99
100 ExtensionDetails []byte
101 }
102
103
104 type AddedKey struct {
105
106
107
108 PrivateKey interface{}
109
110
111 Certificate *ssh.Certificate
112
113 Comment string
114
115
116 LifetimeSecs uint32
117
118
119 ConfirmBeforeUse bool
120
121
122 ConstraintExtensions []ConstraintExtension
123 }
124
125
126 const (
127 agentRequestV1Identities = 1
128 agentRemoveAllV1Identities = 9
129
130
131 agentAddIdentity = 17
132 agentRemoveIdentity = 18
133 agentRemoveAllIdentities = 19
134 agentAddIDConstrained = 25
135
136
137 agentAddSmartcardKey = 20
138 agentRemoveSmartcardKey = 21
139 agentLock = 22
140 agentUnlock = 23
141 agentAddSmartcardKeyConstrained = 26
142
143
144 agentConstrainLifetime = 1
145 agentConstrainConfirm = 2
146
147
148
149 agentConstrainExtensionV00 = 3
150
151 agentConstrainExtension = 255
152 )
153
154
155
156 const maxAgentResponseBytes = 16 << 20
157
158
159
160
161
162
163 const agentFailure = 5
164
165 type failureAgentMsg struct{}
166
167 const agentSuccess = 6
168
169 type successAgentMsg struct{}
170
171
172 const agentRequestIdentities = 11
173
174 type requestIdentitiesAgentMsg struct{}
175
176
177 const agentIdentitiesAnswer = 12
178
179 type identitiesAnswerAgentMsg struct {
180 NumKeys uint32 `sshtype:"12"`
181 Keys []byte `ssh:"rest"`
182 }
183
184
185 const agentSignRequest = 13
186
187 type signRequestAgentMsg struct {
188 KeyBlob []byte `sshtype:"13"`
189 Data []byte
190 Flags uint32
191 }
192
193
194
195
196 const agentSignResponse = 14
197
198 type signResponseAgentMsg struct {
199 SigBlob []byte `sshtype:"14"`
200 }
201
202 type publicKey struct {
203 Format string
204 Rest []byte `ssh:"rest"`
205 }
206
207
208 type constrainLifetimeAgentMsg struct {
209 LifetimeSecs uint32 `sshtype:"1"`
210 }
211
212 type constrainExtensionAgentMsg struct {
213 ExtensionName string `sshtype:"255|3"`
214 ExtensionDetails []byte
215
216
217 Rest []byte `ssh:"rest"`
218 }
219
220
221 const agentExtension = 27
222 const agentExtensionFailure = 28
223
224
225
226
227
228
229
230 var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
231
232 type extensionAgentMsg struct {
233 ExtensionType string `sshtype:"27"`
234
235
236 Contents []byte `ssh:"rest"`
237 }
238
239
240
241 type Key struct {
242 Format string
243 Blob []byte
244 Comment string
245 }
246
247 func clientErr(err error) error {
248 return fmt.Errorf("agent: client error: %v", err)
249 }
250
251
252
253 func (k *Key) String() string {
254 s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
255
256 if k.Comment != "" {
257 s += " " + k.Comment
258 }
259
260 return s
261 }
262
263
264 func (k *Key) Type() string {
265 return k.Format
266 }
267
268
269 func (k *Key) Marshal() []byte {
270 return k.Blob
271 }
272
273
274 func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
275 pubKey, err := ssh.ParsePublicKey(k.Blob)
276 if err != nil {
277 return fmt.Errorf("agent: bad public key: %v", err)
278 }
279 return pubKey.Verify(data, sig)
280 }
281
282 type wireKey struct {
283 Format string
284 Rest []byte `ssh:"rest"`
285 }
286
287 func parseKey(in []byte) (out *Key, rest []byte, err error) {
288 var record struct {
289 Blob []byte
290 Comment string
291 Rest []byte `ssh:"rest"`
292 }
293
294 if err := ssh.Unmarshal(in, &record); err != nil {
295 return nil, nil, err
296 }
297
298 var wk wireKey
299 if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
300 return nil, nil, err
301 }
302
303 return &Key{
304 Format: wk.Format,
305 Blob: record.Blob,
306 Comment: record.Comment,
307 }, record.Rest, nil
308 }
309
310
311 type client struct {
312
313 conn io.ReadWriter
314
315 mu sync.Mutex
316 }
317
318
319
320 func NewClient(rw io.ReadWriter) ExtendedAgent {
321 return &client{conn: rw}
322 }
323
324
325
326
327 func (c *client) call(req []byte) (reply interface{}, err error) {
328 buf, err := c.callRaw(req)
329 if err != nil {
330 return nil, err
331 }
332 reply, err = unmarshal(buf)
333 if err != nil {
334 return nil, clientErr(err)
335 }
336 return reply, nil
337 }
338
339
340
341
342 func (c *client) callRaw(req []byte) (reply []byte, err error) {
343 c.mu.Lock()
344 defer c.mu.Unlock()
345
346 msg := make([]byte, 4+len(req))
347 binary.BigEndian.PutUint32(msg, uint32(len(req)))
348 copy(msg[4:], req)
349 if _, err = c.conn.Write(msg); err != nil {
350 return nil, clientErr(err)
351 }
352
353 var respSizeBuf [4]byte
354 if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
355 return nil, clientErr(err)
356 }
357 respSize := binary.BigEndian.Uint32(respSizeBuf[:])
358 if respSize > maxAgentResponseBytes {
359 return nil, clientErr(errors.New("response too large"))
360 }
361
362 buf := make([]byte, respSize)
363 if _, err = io.ReadFull(c.conn, buf); err != nil {
364 return nil, clientErr(err)
365 }
366 return buf, nil
367 }
368
369 func (c *client) simpleCall(req []byte) error {
370 resp, err := c.call(req)
371 if err != nil {
372 return err
373 }
374 if _, ok := resp.(*successAgentMsg); ok {
375 return nil
376 }
377 return errors.New("agent: failure")
378 }
379
380 func (c *client) RemoveAll() error {
381 return c.simpleCall([]byte{agentRemoveAllIdentities})
382 }
383
384 func (c *client) Remove(key ssh.PublicKey) error {
385 req := ssh.Marshal(&agentRemoveIdentityMsg{
386 KeyBlob: key.Marshal(),
387 })
388 return c.simpleCall(req)
389 }
390
391 func (c *client) Lock(passphrase []byte) error {
392 req := ssh.Marshal(&agentLockMsg{
393 Passphrase: passphrase,
394 })
395 return c.simpleCall(req)
396 }
397
398 func (c *client) Unlock(passphrase []byte) error {
399 req := ssh.Marshal(&agentUnlockMsg{
400 Passphrase: passphrase,
401 })
402 return c.simpleCall(req)
403 }
404
405
406 func (c *client) List() ([]*Key, error) {
407
408 req := []byte{agentRequestIdentities}
409
410 msg, err := c.call(req)
411 if err != nil {
412 return nil, err
413 }
414
415 switch msg := msg.(type) {
416 case *identitiesAnswerAgentMsg:
417 if msg.NumKeys > maxAgentResponseBytes/8 {
418 return nil, errors.New("agent: too many keys in agent reply")
419 }
420 keys := make([]*Key, msg.NumKeys)
421 data := msg.Keys
422 for i := uint32(0); i < msg.NumKeys; i++ {
423 var key *Key
424 var err error
425 if key, data, err = parseKey(data); err != nil {
426 return nil, err
427 }
428 keys[i] = key
429 }
430 return keys, nil
431 case *failureAgentMsg:
432 return nil, errors.New("agent: failed to list keys")
433 }
434 panic("unreachable")
435 }
436
437
438
439 func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
440 return c.SignWithFlags(key, data, 0)
441 }
442
443 func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) {
444 req := ssh.Marshal(signRequestAgentMsg{
445 KeyBlob: key.Marshal(),
446 Data: data,
447 Flags: uint32(flags),
448 })
449
450 msg, err := c.call(req)
451 if err != nil {
452 return nil, err
453 }
454
455 switch msg := msg.(type) {
456 case *signResponseAgentMsg:
457 var sig ssh.Signature
458 if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
459 return nil, err
460 }
461
462 return &sig, nil
463 case *failureAgentMsg:
464 return nil, errors.New("agent: failed to sign challenge")
465 }
466 panic("unreachable")
467 }
468
469
470
471 func unmarshal(packet []byte) (interface{}, error) {
472 if len(packet) < 1 {
473 return nil, errors.New("agent: empty packet")
474 }
475 var msg interface{}
476 switch packet[0] {
477 case agentFailure:
478 return new(failureAgentMsg), nil
479 case agentSuccess:
480 return new(successAgentMsg), nil
481 case agentIdentitiesAnswer:
482 msg = new(identitiesAnswerAgentMsg)
483 case agentSignResponse:
484 msg = new(signResponseAgentMsg)
485 case agentV1IdentitiesAnswer:
486 msg = new(agentV1IdentityMsg)
487 default:
488 return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
489 }
490 if err := ssh.Unmarshal(packet, msg); err != nil {
491 return nil, err
492 }
493 return msg, nil
494 }
495
496 type rsaKeyMsg struct {
497 Type string `sshtype:"17|25"`
498 N *big.Int
499 E *big.Int
500 D *big.Int
501 Iqmp *big.Int
502 P *big.Int
503 Q *big.Int
504 Comments string
505 Constraints []byte `ssh:"rest"`
506 }
507
508 type dsaKeyMsg struct {
509 Type string `sshtype:"17|25"`
510 P *big.Int
511 Q *big.Int
512 G *big.Int
513 Y *big.Int
514 X *big.Int
515 Comments string
516 Constraints []byte `ssh:"rest"`
517 }
518
519 type ecdsaKeyMsg struct {
520 Type string `sshtype:"17|25"`
521 Curve string
522 KeyBytes []byte
523 D *big.Int
524 Comments string
525 Constraints []byte `ssh:"rest"`
526 }
527
528 type ed25519KeyMsg struct {
529 Type string `sshtype:"17|25"`
530 Pub []byte
531 Priv []byte
532 Comments string
533 Constraints []byte `ssh:"rest"`
534 }
535
536
537 func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
538 var req []byte
539 switch k := s.(type) {
540 case *rsa.PrivateKey:
541 if len(k.Primes) != 2 {
542 return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
543 }
544 k.Precompute()
545 req = ssh.Marshal(rsaKeyMsg{
546 Type: ssh.KeyAlgoRSA,
547 N: k.N,
548 E: big.NewInt(int64(k.E)),
549 D: k.D,
550 Iqmp: k.Precomputed.Qinv,
551 P: k.Primes[0],
552 Q: k.Primes[1],
553 Comments: comment,
554 Constraints: constraints,
555 })
556 case *dsa.PrivateKey:
557 req = ssh.Marshal(dsaKeyMsg{
558 Type: ssh.KeyAlgoDSA,
559 P: k.P,
560 Q: k.Q,
561 G: k.G,
562 Y: k.Y,
563 X: k.X,
564 Comments: comment,
565 Constraints: constraints,
566 })
567 case *ecdsa.PrivateKey:
568 nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
569 req = ssh.Marshal(ecdsaKeyMsg{
570 Type: "ecdsa-sha2-" + nistID,
571 Curve: nistID,
572 KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
573 D: k.D,
574 Comments: comment,
575 Constraints: constraints,
576 })
577 case ed25519.PrivateKey:
578 req = ssh.Marshal(ed25519KeyMsg{
579 Type: ssh.KeyAlgoED25519,
580 Pub: []byte(k)[32:],
581 Priv: []byte(k),
582 Comments: comment,
583 Constraints: constraints,
584 })
585
586
587
588 case *ed25519.PrivateKey:
589 req = ssh.Marshal(ed25519KeyMsg{
590 Type: ssh.KeyAlgoED25519,
591 Pub: []byte(*k)[32:],
592 Priv: []byte(*k),
593 Comments: comment,
594 Constraints: constraints,
595 })
596 default:
597 return fmt.Errorf("agent: unsupported key type %T", s)
598 }
599
600
601 if len(constraints) != 0 {
602 req[0] = agentAddIDConstrained
603 }
604
605 resp, err := c.call(req)
606 if err != nil {
607 return err
608 }
609 if _, ok := resp.(*successAgentMsg); ok {
610 return nil
611 }
612 return errors.New("agent: failure")
613 }
614
615 type rsaCertMsg struct {
616 Type string `sshtype:"17|25"`
617 CertBytes []byte
618 D *big.Int
619 Iqmp *big.Int
620 P *big.Int
621 Q *big.Int
622 Comments string
623 Constraints []byte `ssh:"rest"`
624 }
625
626 type dsaCertMsg struct {
627 Type string `sshtype:"17|25"`
628 CertBytes []byte
629 X *big.Int
630 Comments string
631 Constraints []byte `ssh:"rest"`
632 }
633
634 type ecdsaCertMsg struct {
635 Type string `sshtype:"17|25"`
636 CertBytes []byte
637 D *big.Int
638 Comments string
639 Constraints []byte `ssh:"rest"`
640 }
641
642 type ed25519CertMsg struct {
643 Type string `sshtype:"17|25"`
644 CertBytes []byte
645 Pub []byte
646 Priv []byte
647 Comments string
648 Constraints []byte `ssh:"rest"`
649 }
650
651
652
653 func (c *client) Add(key AddedKey) error {
654 var constraints []byte
655
656 if secs := key.LifetimeSecs; secs != 0 {
657 constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
658 }
659
660 if key.ConfirmBeforeUse {
661 constraints = append(constraints, agentConstrainConfirm)
662 }
663
664 cert := key.Certificate
665 if cert == nil {
666 return c.insertKey(key.PrivateKey, key.Comment, constraints)
667 }
668 return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
669 }
670
671 func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
672 var req []byte
673 switch k := s.(type) {
674 case *rsa.PrivateKey:
675 if len(k.Primes) != 2 {
676 return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
677 }
678 k.Precompute()
679 req = ssh.Marshal(rsaCertMsg{
680 Type: cert.Type(),
681 CertBytes: cert.Marshal(),
682 D: k.D,
683 Iqmp: k.Precomputed.Qinv,
684 P: k.Primes[0],
685 Q: k.Primes[1],
686 Comments: comment,
687 Constraints: constraints,
688 })
689 case *dsa.PrivateKey:
690 req = ssh.Marshal(dsaCertMsg{
691 Type: cert.Type(),
692 CertBytes: cert.Marshal(),
693 X: k.X,
694 Comments: comment,
695 Constraints: constraints,
696 })
697 case *ecdsa.PrivateKey:
698 req = ssh.Marshal(ecdsaCertMsg{
699 Type: cert.Type(),
700 CertBytes: cert.Marshal(),
701 D: k.D,
702 Comments: comment,
703 Constraints: constraints,
704 })
705 case ed25519.PrivateKey:
706 req = ssh.Marshal(ed25519CertMsg{
707 Type: cert.Type(),
708 CertBytes: cert.Marshal(),
709 Pub: []byte(k)[32:],
710 Priv: []byte(k),
711 Comments: comment,
712 Constraints: constraints,
713 })
714
715
716
717 case *ed25519.PrivateKey:
718 req = ssh.Marshal(ed25519CertMsg{
719 Type: cert.Type(),
720 CertBytes: cert.Marshal(),
721 Pub: []byte(*k)[32:],
722 Priv: []byte(*k),
723 Comments: comment,
724 Constraints: constraints,
725 })
726 default:
727 return fmt.Errorf("agent: unsupported key type %T", s)
728 }
729
730
731 if len(constraints) != 0 {
732 req[0] = agentAddIDConstrained
733 }
734
735 signer, err := ssh.NewSignerFromKey(s)
736 if err != nil {
737 return err
738 }
739 if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
740 return errors.New("agent: signer and cert have different public key")
741 }
742
743 resp, err := c.call(req)
744 if err != nil {
745 return err
746 }
747 if _, ok := resp.(*successAgentMsg); ok {
748 return nil
749 }
750 return errors.New("agent: failure")
751 }
752
753
754 func (c *client) Signers() ([]ssh.Signer, error) {
755 keys, err := c.List()
756 if err != nil {
757 return nil, err
758 }
759
760 var result []ssh.Signer
761 for _, k := range keys {
762 result = append(result, &agentKeyringSigner{c, k})
763 }
764 return result, nil
765 }
766
767 type agentKeyringSigner struct {
768 agent *client
769 pub ssh.PublicKey
770 }
771
772 func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
773 return s.pub
774 }
775
776 func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
777
778 return s.agent.Sign(s.pub, data)
779 }
780
781 func (s *agentKeyringSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) {
782 if algorithm == "" || algorithm == underlyingAlgo(s.pub.Type()) {
783 return s.Sign(rand, data)
784 }
785
786 var flags SignatureFlags
787 switch algorithm {
788 case ssh.KeyAlgoRSASHA256:
789 flags = SignatureFlagRsaSha256
790 case ssh.KeyAlgoRSASHA512:
791 flags = SignatureFlagRsaSha512
792 default:
793 return nil, fmt.Errorf("agent: unsupported algorithm %q", algorithm)
794 }
795
796 return s.agent.SignWithFlags(s.pub, data, flags)
797 }
798
799 var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
800
801
802
803
804
805 var certKeyAlgoNames = map[string]string{
806 ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
807 ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
808 ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
809 ssh.CertAlgoDSAv01: ssh.KeyAlgoDSA,
810 ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
811 ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
812 ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
813 ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
814 ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
815 ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
816 }
817
818
819
820
821 func underlyingAlgo(algo string) string {
822 if a, ok := certKeyAlgoNames[algo]; ok {
823 return a
824 }
825 return algo
826 }
827
828
829
830
831
832 func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) {
833 req := ssh.Marshal(extensionAgentMsg{
834 ExtensionType: extensionType,
835 Contents: contents,
836 })
837 buf, err := c.callRaw(req)
838 if err != nil {
839 return nil, err
840 }
841 if len(buf) == 0 {
842 return nil, errors.New("agent: failure; empty response")
843 }
844
845
846 if buf[0] == agentFailure {
847 return nil, ErrExtensionUnsupported
848 }
849 if buf[0] == agentExtensionFailure {
850 return nil, errors.New("agent: generic extension failure")
851 }
852
853 return buf, nil
854 }
855
View as plain text