crypto/ssh: Support turning a crypto.Signer into an ssh.Signer
This adds a NewSignerFromSigner to crypto/ssh which takes a crypto.Signer and turns it into an ssh.Signer, helpful if, e.g., your crypto.Signer is backed by some sort of hardware device. The interfaces are very similar - the biggest differences are that a crypto.Signer accepts hashed data, while an ssh.Signer does not, and some differences in encoding for DSA and ECDSA signatures. This also adjusts NewSignerFromKey to use NewSignerFromSigner where possible, dropping the rsaPrivateKey and ecdsaPrivateKey types in favor of wrappedSigner. (However, because *dsa.PrivateKey is not a crypto.Signer, we still have to keep dsaPrivateKey) Change-Id: Ia2e20ece9c9d3844b4e5a64c1a7d997178ec8781 Reviewed-on: https://go-review.googlesource.com/10953 Reviewed-by: Adam Langley <agl@golang.org>
This commit is contained in:
committed by
Adam Langley
parent
aedad9a179
commit
e74b0352e5
+88
-70
@@ -267,28 +267,6 @@ func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
|
||||
}
|
||||
|
||||
type rsaPrivateKey struct {
|
||||
*rsa.PrivateKey
|
||||
}
|
||||
|
||||
func (r *rsaPrivateKey) PublicKey() PublicKey {
|
||||
return (*rsaPublicKey)(&r.PrivateKey.PublicKey)
|
||||
}
|
||||
|
||||
func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
h := crypto.SHA1.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
blob, err := rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Signature{
|
||||
Format: r.PublicKey().Type(),
|
||||
Blob: blob,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type dsaPublicKey dsa.PublicKey
|
||||
|
||||
func (r *dsaPublicKey) Type() string {
|
||||
@@ -496,72 +474,112 @@ func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||
return errors.New("ssh: signature did not verify")
|
||||
}
|
||||
|
||||
type ecdsaPrivateKey struct {
|
||||
*ecdsa.PrivateKey
|
||||
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
|
||||
// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding
|
||||
// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
||||
func NewSignerFromKey(key interface{}) (Signer, error) {
|
||||
switch key := key.(type) {
|
||||
case crypto.Signer:
|
||||
return NewSignerFromSigner(key)
|
||||
case *dsa.PrivateKey:
|
||||
return &dsaPrivateKey{key}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) PublicKey() PublicKey {
|
||||
return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey)
|
||||
type wrappedSigner struct {
|
||||
signer crypto.Signer
|
||||
pubKey PublicKey
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
h := ecHash(k.PrivateKey.PublicKey.Curve).New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
r, s, err := ecdsa.Sign(rand, k.PrivateKey, digest)
|
||||
// NewSignerFromSigner takes any crypto.Signer implementation and
|
||||
// returns a corresponding Signer interface. This can be used, for
|
||||
// example, with keys kept in hardware modules.
|
||||
func NewSignerFromSigner(signer crypto.Signer) (Signer, error) {
|
||||
pubKey, err := NewPublicKey(signer.Public())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := make([]byte, intLength(r)+intLength(s))
|
||||
rest := marshalInt(sig, r)
|
||||
marshalInt(rest, s)
|
||||
return &wrappedSigner{signer, pubKey}, nil
|
||||
}
|
||||
|
||||
func (s *wrappedSigner) PublicKey() PublicKey {
|
||||
return s.pubKey
|
||||
}
|
||||
|
||||
func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
var hashFunc crypto.Hash
|
||||
|
||||
switch key := s.pubKey.(type) {
|
||||
case *rsaPublicKey, *dsaPublicKey:
|
||||
hashFunc = crypto.SHA1
|
||||
case *ecdsaPublicKey:
|
||||
hashFunc = ecHash(key.Curve)
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
|
||||
h := hashFunc.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
|
||||
signature, err := s.signer.Sign(rand, digest, hashFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// crypto.Signer.Sign is expected to return an ASN.1-encoded signature
|
||||
// for ECDSA and DSA, but that's not the encoding expected by SSH, so
|
||||
// re-encode.
|
||||
switch s.pubKey.(type) {
|
||||
case *ecdsaPublicKey, *dsaPublicKey:
|
||||
type asn1Signature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
asn1Sig := new(asn1Signature)
|
||||
_, err := asn1.Unmarshal(signature, asn1Sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch s.pubKey.(type) {
|
||||
case *ecdsaPublicKey:
|
||||
signature = Marshal(asn1Sig)
|
||||
|
||||
case *dsaPublicKey:
|
||||
signature = make([]byte, 40)
|
||||
r := asn1Sig.R.Bytes()
|
||||
s := asn1Sig.S.Bytes()
|
||||
copy(signature[20-len(r):20], r)
|
||||
copy(signature[40-len(s):40], s)
|
||||
}
|
||||
}
|
||||
|
||||
return &Signature{
|
||||
Format: k.PublicKey().Type(),
|
||||
Blob: sig,
|
||||
Format: s.pubKey.Type(),
|
||||
Blob: signature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSignerFromKey takes a pointer to rsa, dsa or ecdsa PrivateKey
|
||||
// returns a corresponding Signer instance. EC keys should use P256,
|
||||
// P384 or P521.
|
||||
func NewSignerFromKey(k interface{}) (Signer, error) {
|
||||
var sshKey Signer
|
||||
switch t := k.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
sshKey = &rsaPrivateKey{t}
|
||||
case *dsa.PrivateKey:
|
||||
sshKey = &dsaPrivateKey{t}
|
||||
case *ecdsa.PrivateKey:
|
||||
if !supportedEllipticCurve(t.Curve) {
|
||||
return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.")
|
||||
}
|
||||
|
||||
sshKey = &ecdsaPrivateKey{t}
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
|
||||
}
|
||||
return sshKey, nil
|
||||
}
|
||||
|
||||
// NewPublicKey takes a pointer to rsa, dsa or ecdsa PublicKey
|
||||
// and returns a corresponding ssh PublicKey instance. EC keys should use P256, P384 or P521.
|
||||
func NewPublicKey(k interface{}) (PublicKey, error) {
|
||||
var sshKey PublicKey
|
||||
switch t := k.(type) {
|
||||
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey or
|
||||
// any other crypto.Signer and returns a corresponding Signer instance. ECDSA
|
||||
// keys must use P-256, P-384 or P-521.
|
||||
func NewPublicKey(key interface{}) (PublicKey, error) {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
sshKey = (*rsaPublicKey)(t)
|
||||
return (*rsaPublicKey)(key), nil
|
||||
case *ecdsa.PublicKey:
|
||||
if !supportedEllipticCurve(t.Curve) {
|
||||
return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.")
|
||||
if !supportedEllipticCurve(key.Curve) {
|
||||
return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.")
|
||||
}
|
||||
sshKey = (*ecdsaPublicKey)(t)
|
||||
return (*ecdsaPublicKey)(key), nil
|
||||
case *dsa.PublicKey:
|
||||
sshKey = (*dsaPublicKey)(t)
|
||||
return (*dsaPublicKey)(key), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
return sshKey, nil
|
||||
}
|
||||
|
||||
// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports
|
||||
|
||||
+4
-4
@@ -57,12 +57,12 @@ func TestUnsupportedCurves(t *testing.T) {
|
||||
t.Fatalf("GenerateKey: %v", err)
|
||||
}
|
||||
|
||||
if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") {
|
||||
t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err)
|
||||
if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
|
||||
t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
|
||||
}
|
||||
|
||||
if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") {
|
||||
t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err)
|
||||
if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
|
||||
t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user