aboutsummaryrefslogtreecommitdiff
path: root/ssh/keys.go
diff options
context:
space:
mode:
Diffstat (limited to 'ssh/keys.go')
-rw-r--r--ssh/keys.go88
1 files changed, 82 insertions, 6 deletions
diff --git a/ssh/keys.go b/ssh/keys.go
index 1bf28d8..bcaae50 100644
--- a/ssh/keys.go
+++ b/ssh/keys.go
@@ -335,7 +335,7 @@ type CryptoPublicKey interface {
// A Signer can create signatures that verify against a public key.
//
-// Some Signers provided by this package also implement AlgorithmSigner.
+// Some Signers provided by this package also implement MultiAlgorithmSigner.
type Signer interface {
// PublicKey returns the associated PublicKey.
PublicKey() PublicKey
@@ -350,9 +350,9 @@ type Signer interface {
// An AlgorithmSigner is a Signer that also supports specifying an algorithm to
// use for signing.
//
-// An AlgorithmSigner can't advertise the algorithms it supports, so it should
-// be prepared to be invoked with every algorithm supported by the public key
-// format.
+// An AlgorithmSigner can't advertise the algorithms it supports, unless it also
+// implements MultiAlgorithmSigner, so it should be prepared to be invoked with
+// every algorithm supported by the public key format.
type AlgorithmSigner interface {
Signer
@@ -363,6 +363,75 @@ type AlgorithmSigner interface {
SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error)
}
+// MultiAlgorithmSigner is an AlgorithmSigner that also reports the algorithms
+// supported by that signer.
+type MultiAlgorithmSigner interface {
+ AlgorithmSigner
+
+ // Algorithms returns the available algorithms in preference order. The list
+ // must not be empty, and it must not include certificate types.
+ Algorithms() []string
+}
+
+// NewSignerWithAlgorithms returns a signer restricted to the specified
+// algorithms. The algorithms must be set in preference order. The list must not
+// be empty, and it must not include certificate types. An error is returned if
+// the specified algorithms are incompatible with the public key type.
+func NewSignerWithAlgorithms(signer AlgorithmSigner, algorithms []string) (MultiAlgorithmSigner, error) {
+ if len(algorithms) == 0 {
+ return nil, errors.New("ssh: please specify at least one valid signing algorithm")
+ }
+ var signerAlgos []string
+ supportedAlgos := algorithmsForKeyFormat(underlyingAlgo(signer.PublicKey().Type()))
+ if s, ok := signer.(*multiAlgorithmSigner); ok {
+ signerAlgos = s.Algorithms()
+ } else {
+ signerAlgos = supportedAlgos
+ }
+
+ for _, algo := range algorithms {
+ if !contains(supportedAlgos, algo) {
+ return nil, fmt.Errorf("ssh: algorithm %q is not supported for key type %q",
+ algo, signer.PublicKey().Type())
+ }
+ if !contains(signerAlgos, algo) {
+ return nil, fmt.Errorf("ssh: algorithm %q is restricted for the provided signer", algo)
+ }
+ }
+ return &multiAlgorithmSigner{
+ AlgorithmSigner: signer,
+ supportedAlgorithms: algorithms,
+ }, nil
+}
+
+type multiAlgorithmSigner struct {
+ AlgorithmSigner
+ supportedAlgorithms []string
+}
+
+func (s *multiAlgorithmSigner) Algorithms() []string {
+ return s.supportedAlgorithms
+}
+
+func (s *multiAlgorithmSigner) isAlgorithmSupported(algorithm string) bool {
+ if algorithm == "" {
+ algorithm = underlyingAlgo(s.PublicKey().Type())
+ }
+ for _, algo := range s.supportedAlgorithms {
+ if algorithm == algo {
+ return true
+ }
+ }
+ return false
+}
+
+func (s *multiAlgorithmSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
+ if !s.isAlgorithmSupported(algorithm) {
+ return nil, fmt.Errorf("ssh: algorithm %q is not supported: %v", algorithm, s.supportedAlgorithms)
+ }
+ return s.AlgorithmSigner.SignWithAlgorithm(rand, data, algorithm)
+}
+
type rsaPublicKey rsa.PublicKey
func (r *rsaPublicKey) Type() string {
@@ -526,6 +595,10 @@ func (k *dsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
return k.SignWithAlgorithm(rand, data, k.PublicKey().Type())
}
+func (k *dsaPrivateKey) Algorithms() []string {
+ return []string{k.PublicKey().Type()}
+}
+
func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
if algorithm != "" && algorithm != k.PublicKey().Type() {
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
@@ -975,13 +1048,16 @@ func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
return s.SignWithAlgorithm(rand, data, s.pubKey.Type())
}
+func (s *wrappedSigner) Algorithms() []string {
+ return algorithmsForKeyFormat(s.pubKey.Type())
+}
+
func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*Signature, error) {
if algorithm == "" {
algorithm = s.pubKey.Type()
}
- supportedAlgos := algorithmsForKeyFormat(s.pubKey.Type())
- if !contains(supportedAlgos, algorithm) {
+ if !contains(s.Algorithms(), algorithm) {
return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type())
}