Repository: lima-vm/lima
Pull request #387 (2021-11-05 16:09:08)

read ecdsa public keys to ssh-authorized-keys
I use [Secretive](https://github.com/maxgoedjen/secretive) to manage SSH keys, and it defaults to `ecdsa-sha2-nistp256` algorithm. 
After following the instruction about [putting public key in your ~/.ssh/ directory](https://github.com/maxgoedjen/secretive/blob/main/FAQ.md#how-do-i-tell-ssh-to-use-a-specific-key), `limactl start` give these output
```
WARN[0000] [hostagent] public key "/Users/enihsyou/.ssh/id_rsa.pub" doesn't seem to be in ssh format 
WARN[0000] [hostagent] public key "/Users/enihsyou/.ssh/secretive.pub" doesn't seem to be in ssh format 
```

Those OpenSSH public key format should be picked up, so I learned from what [golang.org/x/crypto/ssh.ParsePublicKey](https://github.com/golang/crypto/blob/089bfa5675191fd96a44247682f76ebca03d7916/ssh/keys.go#L265) do to make a soft public key check.


Patch:
diff --git a/pkg/sshutil/sshutil.go b/pkg/sshutil/sshutil.go
index 4c644a9c..499af463 100644
--- a/pkg/sshutil/sshutil.go
+++ b/pkg/sshutil/sshutil.go
@@ -2,6 +2,8 @@ package sshutil
 
 import (
 	"bytes"
+	"encoding/base64"
+	"encoding/binary"
 	"errors"
 	"fmt"
 	"io/fs"
@@ -94,7 +96,7 @@ func DefaultPubKeys(loadDotSSH bool) ([]PubKey, error) {
 		}
 		entry, err := readPublicKey(f)
 		if err == nil {
-			if strings.ContainsRune(entry.Content, '\n') || !strings.HasPrefix(entry.Content, "ssh-") {
+			if !detectValidPublicKey(entry.Content) {
 				logrus.Warnf("public key %q doesn't seem to be in ssh format", entry.Filename)
 			} else {
 				res = append(res, entry)
@@ -261,3 +263,28 @@ func detectOpenSSHVersion() semver.Version {
 	}
 	return v
 }
+
+// detectValidPublicKey returns whether content represent a public key.
+// OpenSSH public key format have the structure of '<algorithm> <key> <comment>'.
+// By checking 'algorithm' with signature format identifier in 'key' part,
+// this function may report false positive but provide better compatibility.
+func detectValidPublicKey(content string) bool {
+	if strings.ContainsRune(content, '\n') {
+		return false
+	}
+	var spaced = strings.SplitN(content, " ", 3)
+	if len(spaced) < 2 {
+		return false
+	}
+	var algo, base64Key = spaced[0], spaced[1]
+	var decodedKey, err = base64.StdEncoding.DecodeString(base64Key)
+	if err != nil || len(decodedKey) < 4 {
+		return false
+	}
+	var sigLength = binary.BigEndian.Uint32(decodedKey)
+	if uint32(len(decodedKey)) < sigLength {
+		return false
+	}
+	var sigFormat = string(decodedKey[4 : 4+sigLength])
+	return algo == sigFormat
+}


Test patch:
diff --git a/pkg/sshutil/sshutil_test.go b/pkg/sshutil/sshutil_test.go
index ba4f8b7c..ef2b754d 100644
--- a/pkg/sshutil/sshutil_test.go
+++ b/pkg/sshutil/sshutil_test.go
@@ -27,3 +27,16 @@ func TestParseOpenSSHVersion(t *testing.T) {
 	// OpenBSD 5.8
 	assert.Check(t, ParseOpenSSHVersion([]byte("OpenSSH_7.0, LibreSSL")).Equal(*semver.New("7.0.0")))
 }
+
+func Test_detectValidPublicKey(t *testing.T) {
+	assert.Check(t, detectValidPublicKey("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAACQDf2IooTVPDBw== 64bit"))
+	assert.Check(t, detectValidPublicKey("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAACQDf2IooTVPDBw=="))
+	assert.Check(t, detectValidPublicKey("ssh-dss AAAAB3NzaC1kc3MAAACBAP/yAytaYzqXq01uTd5+1RC=" /* truncate */))
+	assert.Check(t, detectValidPublicKey("ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTY=" /* truncate */))
+	assert.Check(t, detectValidPublicKey("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICs1tSO/jx8oc4O=" /* truncate */))
+
+	assert.Check(t, !detectValidPublicKey("wrong-algo AAAAB3NzaC1kc3MAAACBAP/yAytaYzqXq01uTd5+1RC="))
+	assert.Check(t, !detectValidPublicKey("huge-length AAAD6A=="))
+	assert.Check(t, !detectValidPublicKey("arbitrary content"))
+	assert.Check(t, !detectValidPublicKey(""))
+}