Skip to content

Commit

Permalink
x/crypto/ssh: Add Fingerprint method to PublicKey
Browse files Browse the repository at this point in the history
Implement a standards-compliant fingerprint format method (RFC 4716 section 4)

Fixes golang/go#12292

Change-Id: I96562197a96c491ccb0b7b6f20435b2a9be30c20
  • Loading branch information
dragon3 committed Oct 15, 2015
1 parent c8b9e63 commit c0e91ee
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 0 deletions.
8 changes: 8 additions & 0 deletions ssh/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package ssh

import (
"bytes"
"crypto/md5"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -470,6 +471,13 @@ func (c *Certificate) Verify(data []byte, sig *Signature) error {
return c.Key.Verify(data, sig)
}

// Fingerprint returns the key's fingerprint. It is part of the
// PublicKey interface.
func (c *Certificate) Fingerprint() string {
md5sum := md5.Sum(c.Key.Marshal())
return rfc4716hex(md5sum[:])
}

func parseSignatureBody(in []byte) (out *Signature, rest []byte, ok bool) {
format, in, ok := parseString(in)
if !ok {
Expand Down
30 changes: 30 additions & 0 deletions ssh/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
Expand Down Expand Up @@ -200,6 +201,10 @@ type PublicKey interface {
// Verify that sig is a signature on the given data using this
// key. This function will hash the data appropriately first.
Verify(data []byte, sig *Signature) error

// Fingerprint returns the user presentation of the key's
// fingerprint as described by RFC 4716 section 4.
Fingerprint() string
}

// A Signer can create signatures that verify against a public key.
Expand All @@ -217,6 +222,10 @@ type rsaPublicKey rsa.PublicKey
func (r *rsaPublicKey) Type() string {
return "ssh-rsa"
}
func (r *rsaPublicKey) Fingerprint() string {
md5sum := md5.Sum(r.Marshal())
return rfc4716hex(md5sum[:])
}

// parseRSA parses an RSA key according to RFC 4253, section 6.6.
func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
Expand Down Expand Up @@ -273,6 +282,11 @@ func (r *dsaPublicKey) Type() string {
return "ssh-dss"
}

func (r *dsaPublicKey) Fingerprint() string {
md5sum := md5.Sum(r.Marshal())
return rfc4716hex(md5sum[:])
}

// parseDSA parses an DSA key according to RFC 4253, section 6.6.
func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
var w struct {
Expand Down Expand Up @@ -381,6 +395,11 @@ func (key *ecdsaPublicKey) nistID() string {
panic("ssh: unsupported ecdsa key size")
}

func (r *ecdsaPublicKey) Fingerprint() string {
md5sum := md5.Sum(r.Marshal())
return rfc4716hex(md5sum[:])
}

func supportedEllipticCurve(curve elliptic.Curve) bool {
return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
}
Expand Down Expand Up @@ -644,3 +663,14 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
X: k.Pub,
}, nil
}

func rfc4716hex(data []byte) string {
var fingerprint string
for i := 0; i < len(data); i++ {
fingerprint = fmt.Sprintf("%s%0.2x", fingerprint, data[i])
if i != len(data)-1 {
fingerprint = fingerprint + ":"
}
}
return fingerprint
}
12 changes: 12 additions & 0 deletions ssh/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,15 @@ func TestInvalidEntry(t *testing.T) {
t.Errorf("got valid entry for %q", authInvalid)
}
}

func TestFingerprint(t *testing.T) {
dsa := testPublicKeys["dsa"]
if dsa.Fingerprint() != "c9:67:63:e7:b5:34:5c:72:e3:7d:41:1b:cc:cd:89:28" {
t.Errorf("got invalid fingerprint %s", dsa.Fingerprint())
}

rsa := testPublicKeys["rsa"]
if rsa.Fingerprint() != "5c:00:08:96:4e:02:63:6c:ab:15:1c:b6:ea:6c:59:37" {
t.Errorf("got invalid fingerprint %s", rsa.Fingerprint())
}
}

0 comments on commit c0e91ee

Please sign in to comment.