From da777a4193ba2d033f3b9c1300ef056f2eabff50 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 28 Mar 2023 22:32:58 +0200 Subject: [PATCH 1/5] Fix SSH certificate fingerprint encoding Before this commit, the fingerprint for an SSH certificate would include the bytes that encode the certificate. An SSH fingerprint should only be based on its public key. This commit checks if the SSH key is actually an SSH certificate and will fingerprint just the SSH certificate public key instead of the entire certificate contents. --- sshutil/fingerprint.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sshutil/fingerprint.go b/sshutil/fingerprint.go index 00175845..ff268ae8 100644 --- a/sshutil/fingerprint.go +++ b/sshutil/fingerprint.go @@ -76,7 +76,15 @@ func FormatFingerprint(in []byte, encoding FingerprintEncoding) (string, error) return "", fmt.Errorf("error determining key type and size: %w", err) } - fp := EncodedFingerprint(key, encoding) + // if the SSH key is actually an SSH certificate, get its + // public key and encode just that, instead of encoding + // the entire key blob including certificate bytes. + publicKey := key + if c, ok := key.(*ssh.Certificate); ok { + publicKey = c.Key + } + + fp := EncodedFingerprint(publicKey, encoding) if fp == "" { return "", fmt.Errorf("unsupported encoding format %v", encoding) } From 6920d089991dd5c2d0b56965903170fb058c40a7 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 28 Mar 2023 22:49:50 +0200 Subject: [PATCH 2/5] Fix test for SSH certificate fingerprint --- sshutil/fingerprint_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sshutil/fingerprint_test.go b/sshutil/fingerprint_test.go index f10394b5..5570e61b 100644 --- a/sshutil/fingerprint_test.go +++ b/sshutil/fingerprint_test.go @@ -114,6 +114,7 @@ func TestFormatFingerprint(t *testing.T) { skECKey := generateFakeSKKey(t, ecKey) skEDKey := generateFakeSKKey(t, edKey) sshCert := generateCertificate(t) + sshCertPublicKey := sshCert.(*ssh.Certificate).Key dsaKey := new(dsa.PrivateKey) if err := dsa.GenerateParameters(&dsaKey.Parameters, rand.Reader, dsa.L1024N160); err != nil { @@ -164,7 +165,7 @@ func TestFormatFingerprint(t *testing.T) { {"RSA", args{marshal(sshRSAKey, "jane@example.com"), 0}, "2048 " + ssh.FingerprintSHA256(sshRSAKey) + " jane@example.com (RSA)", false}, {"SK-ECDSA", args{marshal(skECKey, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(skECKey) + " jane@example.com (SK-ECDSA)", false}, {"SK-ED25519", args{marshal(skEDKey, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(skEDKey) + " jane@example.com (SK-ED25519)", false}, - {"ED25519-CERT", args{marshal(sshCert, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(sshCert) + " jane@example.com (ED25519-CERT)", false}, + {"ED25519-CERT", args{marshal(sshCert, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(sshCertPublicKey) + " jane@example.com (ED25519-CERT)", false}, {"DSA", args{marshal(sshDSAKey, "jane@example.com"), 0}, "1024 " + ssh.FingerprintSHA256(sshDSAKey) + " jane@example.com (DSA)", false}, {"Base64RawFingerprint", args{marshal(sshECKey, ""), Base64RawFingerprint}, "256 " + ssh.FingerprintSHA256(sshECKey) + " no comment (ECDSA)", false}, {"Base64RawURLFingerprint", args{marshal(sshECKey, ""), Base64RawURLFingerprint}, "256 SHA256:" + base64.RawURLEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false}, From d6a6377ba749737d787af4153d693adadcb54f1f Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 28 Mar 2023 23:03:52 +0200 Subject: [PATCH 3/5] Add SSH certificate fixture fingerprint test --- sshutil/fingerprint_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sshutil/fingerprint_test.go b/sshutil/fingerprint_test.go index 5570e61b..c4d8a3c9 100644 --- a/sshutil/fingerprint_test.go +++ b/sshutil/fingerprint_test.go @@ -166,6 +166,7 @@ func TestFormatFingerprint(t *testing.T) { {"SK-ECDSA", args{marshal(skECKey, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(skECKey) + " jane@example.com (SK-ECDSA)", false}, {"SK-ED25519", args{marshal(skEDKey, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(skEDKey) + " jane@example.com (SK-ED25519)", false}, {"ED25519-CERT", args{marshal(sshCert, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(sshCertPublicKey) + " jane@example.com (ED25519-CERT)", false}, + {"ED25519-CERT (fixture)", args{[]byte(fixtureED25519Certificate), DefaultFingerprint}, "256 SHA256:RvkDPGwl/G9d7LUFm1kmWhvOD9I/moPq4yxcb0STwr0 herman (ECDSA-CERT)", false}, {"DSA", args{marshal(sshDSAKey, "jane@example.com"), 0}, "1024 " + ssh.FingerprintSHA256(sshDSAKey) + " jane@example.com (DSA)", false}, {"Base64RawFingerprint", args{marshal(sshECKey, ""), Base64RawFingerprint}, "256 " + ssh.FingerprintSHA256(sshECKey) + " no comment (ECDSA)", false}, {"Base64RawURLFingerprint", args{marshal(sshECKey, ""), Base64RawURLFingerprint}, "256 SHA256:" + base64.RawURLEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false}, @@ -190,6 +191,10 @@ func TestFormatFingerprint(t *testing.T) { } } +const ( + fixtureED25519Certificate = `ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgLnkvSk4odlo3b1R+RDw+LmorL3RkN354IilCIVFVen4AAAAIbmlzdHAyNTYAAABBBHjKHss8WM2ffMYlavisoLXR0I6UEIU+cidV1ogEH1U6+/SYaFPrlzQo0tGLM5CNkMbhInbyasQsrHzn8F1Rt7nHg5/tcSf9qwAAAAEAAAAGaGVybWFuAAAACgAAAAZoZXJtYW4AAAAAY8kvJwAAAABjyhBjAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEE/ayqpPrZZF5uA1UlDt4FreTf15agztQIzpxnWq/XoxAHzagRSkFGkdgFpjgsfiRpP8URHH3BZScqc0ZDCTxhoQAAAGQAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAEkAAAAhAJuP1wCVwoyrKrEtHGfFXrVbRHySDjvXtS1tVTdHyqymAAAAIBa/CSSzfZb4D2NLP+eEmOOMJwSjYOiNM8fiOoAaqglI herman` +) + type fakeKey struct { typ string bytes []byte From 4728af153b15b986110da29c5b3e8435892ce054 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 28 Mar 2023 23:10:30 +0200 Subject: [PATCH 4/5] Fix name for test from ED25519 to ECDSA --- sshutil/fingerprint_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sshutil/fingerprint_test.go b/sshutil/fingerprint_test.go index c4d8a3c9..ac51bdac 100644 --- a/sshutil/fingerprint_test.go +++ b/sshutil/fingerprint_test.go @@ -166,7 +166,7 @@ func TestFormatFingerprint(t *testing.T) { {"SK-ECDSA", args{marshal(skECKey, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(skECKey) + " jane@example.com (SK-ECDSA)", false}, {"SK-ED25519", args{marshal(skEDKey, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(skEDKey) + " jane@example.com (SK-ED25519)", false}, {"ED25519-CERT", args{marshal(sshCert, "jane@example.com"), 0}, "256 " + ssh.FingerprintSHA256(sshCertPublicKey) + " jane@example.com (ED25519-CERT)", false}, - {"ED25519-CERT (fixture)", args{[]byte(fixtureED25519Certificate), DefaultFingerprint}, "256 SHA256:RvkDPGwl/G9d7LUFm1kmWhvOD9I/moPq4yxcb0STwr0 herman (ECDSA-CERT)", false}, + {"ECDSA-CERT (fixture)", args{[]byte(fixtureECDSACertificate), DefaultFingerprint}, "256 SHA256:RvkDPGwl/G9d7LUFm1kmWhvOD9I/moPq4yxcb0STwr0 herman (ECDSA-CERT)", false}, {"DSA", args{marshal(sshDSAKey, "jane@example.com"), 0}, "1024 " + ssh.FingerprintSHA256(sshDSAKey) + " jane@example.com (DSA)", false}, {"Base64RawFingerprint", args{marshal(sshECKey, ""), Base64RawFingerprint}, "256 " + ssh.FingerprintSHA256(sshECKey) + " no comment (ECDSA)", false}, {"Base64RawURLFingerprint", args{marshal(sshECKey, ""), Base64RawURLFingerprint}, "256 SHA256:" + base64.RawURLEncoding.EncodeToString(ec256Bytes) + " no comment (ECDSA)", false}, @@ -192,7 +192,7 @@ func TestFormatFingerprint(t *testing.T) { } const ( - fixtureED25519Certificate = `ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgLnkvSk4odlo3b1R+RDw+LmorL3RkN354IilCIVFVen4AAAAIbmlzdHAyNTYAAABBBHjKHss8WM2ffMYlavisoLXR0I6UEIU+cidV1ogEH1U6+/SYaFPrlzQo0tGLM5CNkMbhInbyasQsrHzn8F1Rt7nHg5/tcSf9qwAAAAEAAAAGaGVybWFuAAAACgAAAAZoZXJtYW4AAAAAY8kvJwAAAABjyhBjAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEE/ayqpPrZZF5uA1UlDt4FreTf15agztQIzpxnWq/XoxAHzagRSkFGkdgFpjgsfiRpP8URHH3BZScqc0ZDCTxhoQAAAGQAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAEkAAAAhAJuP1wCVwoyrKrEtHGfFXrVbRHySDjvXtS1tVTdHyqymAAAAIBa/CSSzfZb4D2NLP+eEmOOMJwSjYOiNM8fiOoAaqglI herman` + fixtureECDSACertificate = `ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgLnkvSk4odlo3b1R+RDw+LmorL3RkN354IilCIVFVen4AAAAIbmlzdHAyNTYAAABBBHjKHss8WM2ffMYlavisoLXR0I6UEIU+cidV1ogEH1U6+/SYaFPrlzQo0tGLM5CNkMbhInbyasQsrHzn8F1Rt7nHg5/tcSf9qwAAAAEAAAAGaGVybWFuAAAACgAAAAZoZXJtYW4AAAAAY8kvJwAAAABjyhBjAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAEEE/ayqpPrZZF5uA1UlDt4FreTf15agztQIzpxnWq/XoxAHzagRSkFGkdgFpjgsfiRpP8URHH3BZScqc0ZDCTxhoQAAAGQAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAEkAAAAhAJuP1wCVwoyrKrEtHGfFXrVbRHySDjvXtS1tVTdHyqymAAAAIBa/CSSzfZb4D2NLP+eEmOOMJwSjYOiNM8fiOoAaqglI herman` ) type fakeKey struct { From a917ed948023ff426c83961b60a08d37d13befb6 Mon Sep 17 00:00:00 2001 From: Herman Slatman Date: Tue, 11 Apr 2023 16:03:25 +0200 Subject: [PATCH 5/5] Add `FormatCertificateFingerprint` function The `FormatCertificateFingerprint` allows fingerprints for SSH certificates to be calculated. As opposed to the `FormatFingerprint` function, the new function will always include all certificate bytes as opposed to just the marshaled public key when calculating the fingerprint. The default behavior for `FormatFingerprint` now behaves like other systems, such as `ssh-add -l`. --- sshutil/fingerprint.go | 43 ++++++++++++-- sshutil/fingerprint_test.go | 109 ++++++++++++++++++++++++------------ 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/sshutil/fingerprint.go b/sshutil/fingerprint.go index ff268ae8..ffe983c5 100644 --- a/sshutil/fingerprint.go +++ b/sshutil/fingerprint.go @@ -62,11 +62,42 @@ func EncodedFingerprint(pub ssh.PublicKey, encoding FingerprintEncoding) string // OpenSSH and returns a public key fingerprint in the following format: // // SHA256: ( SHA256: ( SHA256: (