source_test.go (3422B)
1 package sshkey 2 3 import ( 4 "context" 5 "crypto/ed25519" 6 "crypto/rand" 7 "crypto/x509" 8 "encoding/pem" 9 "net" 10 "os" 11 "path/filepath" 12 "strings" 13 "testing" 14 15 "golang.org/x/crypto/ssh" 16 "golang.org/x/crypto/ssh/agent" 17 ) 18 19 func TestLocalSourceDiscoversAndSignsWithSelectedEd25519Key(t *testing.T) { 20 directory := t.TempDir() 21 publicRaw, privateKey, err := ed25519.GenerateKey(rand.Reader) 22 if err != nil { 23 t.Fatal(err) 24 } 25 publicKey, err := ssh.NewPublicKey(publicRaw) 26 if err != nil { 27 t.Fatal(err) 28 } 29 encodedPrivate, err := x509.MarshalPKCS8PrivateKey(privateKey) 30 if err != nil { 31 t.Fatal(err) 32 } 33 privatePath := filepath.Join(directory, "id_example") 34 if err := os.WriteFile(privatePath, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: encodedPrivate}), 0o600); err != nil { 35 t.Fatal(err) 36 } 37 publicText := strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey))) + " test@example\n" 38 if err := os.WriteFile(privatePath+".pub", []byte(publicText), 0o644); err != nil { 39 t.Fatal(err) 40 } 41 // Discovery ignores public keys using unsupported algorithms. 42 if err := os.WriteFile(filepath.Join(directory, "id_rsa.pub"), []byte("ssh-rsa invalid"), 0o644); err != nil { 43 t.Fatal(err) 44 } 45 46 source := NewLocalSource(directory, "") 47 keys, err := source.Keys(context.Background()) 48 if err != nil { 49 t.Fatal(err) 50 } 51 if len(keys) != 1 || keys[0].Path != privatePath+".pub" || keys[0].Fingerprint != ssh.FingerprintSHA256(publicKey) || keys[0].PublicKey != strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey))) { 52 t.Fatalf("keys = %#v", keys) 53 } 54 55 message := []byte("gotextlog-auth-v1\ntest") 56 encodedSignature, err := source.Sign(context.Background(), keys[0], message) 57 if err != nil { 58 t.Fatal(err) 59 } 60 var signature ssh.Signature 61 if err := ssh.Unmarshal(encodedSignature, &signature); err != nil { 62 t.Fatal(err) 63 } 64 if err := publicKey.Verify(message, &signature); err != nil { 65 t.Fatalf("signature verification: %v", err) 66 } 67 } 68 func TestLocalSourceSignsWithAgentWhenPrivateFileIsUnavailable(t *testing.T) { 69 directory := t.TempDir() 70 publicRaw, privateKey, err := ed25519.GenerateKey(rand.Reader) 71 if err != nil { 72 t.Fatal(err) 73 } 74 publicKey, err := ssh.NewPublicKey(publicRaw) 75 if err != nil { 76 t.Fatal(err) 77 } 78 publicPath := filepath.Join(directory, "agent-only.pub") 79 if err := os.WriteFile(publicPath, ssh.MarshalAuthorizedKey(publicKey), 0o644); err != nil { 80 t.Fatal(err) 81 } 82 83 keyring := agent.NewKeyring() 84 if err := keyring.Add(agent.AddedKey{PrivateKey: privateKey}); err != nil { 85 t.Fatal(err) 86 } 87 socket := filepath.Join(t.TempDir(), "agent.sock") 88 listener, err := net.Listen("unix", socket) 89 if err != nil { 90 t.Fatal(err) 91 } 92 defer listener.Close() 93 go func() { 94 connection, acceptErr := listener.Accept() 95 if acceptErr == nil { 96 defer connection.Close() 97 _ = agent.ServeAgent(keyring, connection) 98 } 99 }() 100 101 source := NewLocalSource(directory, socket) 102 keys, err := source.Keys(context.Background()) 103 if err != nil { 104 t.Fatal(err) 105 } 106 if len(keys) != 1 { 107 t.Fatalf("keys = %#v", keys) 108 } 109 message := []byte("agent challenge") 110 encodedSignature, err := source.Sign(context.Background(), keys[0], message) 111 if err != nil { 112 t.Fatal(err) 113 } 114 var signature ssh.Signature 115 if err := ssh.Unmarshal(encodedSignature, &signature); err != nil { 116 t.Fatal(err) 117 } 118 if err := publicKey.Verify(message, &signature); err != nil { 119 t.Fatalf("signature verification: %v", err) 120 } 121 }