github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/pkg/crypto/pgp/pgp_test.go (about) 1 package pgp_test 2 3 import ( 4 "errors" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 12 "github.com/kubri/kubri/internal/test" 13 "github.com/kubri/kubri/pkg/crypto" 14 "github.com/kubri/kubri/pkg/crypto/internal/cryptotest" 15 "github.com/kubri/kubri/pkg/crypto/pgp" 16 ) 17 18 func TestPGP(t *testing.T) { 19 cryptotest.Test(t, 20 cryptotest.Implementation[*pgp.PrivateKey, *pgp.PublicKey]{ 21 NewPrivateKey: func() (*pgp.PrivateKey, error) { 22 return pgp.NewPrivateKey("test", "test@example.com") 23 }, 24 MarshalPrivateKey: pgp.MarshalPrivateKey, 25 UnmarshalPrivateKey: pgp.UnmarshalPrivateKey, 26 Public: pgp.Public, 27 MarshalPublicKey: pgp.MarshalPublicKey, 28 UnmarshalPublicKey: pgp.UnmarshalPublicKey, 29 Sign: pgp.Sign, 30 Verify: pgp.Verify, 31 }, 32 cryptotest.WithCmpOptions(test.ComparePGPKeys()), 33 ) 34 35 priv, _ := pgp.NewPrivateKey("test", "test@example.com") 36 pub := pgp.Public(priv) 37 pubBytes, _ := pgp.MarshalPublicKey(pub) 38 data := []byte("foo\nbar\nbaz") 39 sig, _ := pgp.Sign(priv, data) 40 signed, _ := pgp.SignText(priv, data) 41 42 t.Run("NewPrivateKey", func(t *testing.T) { 43 tests := []struct { 44 desc string 45 name string 46 email string 47 err bool 48 }{ 49 { 50 desc: "name only", 51 name: "test", 52 }, 53 { 54 desc: "email only", 55 email: "test", 56 }, 57 { 58 desc: "missing name & email", 59 err: true, 60 }, 61 } 62 63 for _, test := range tests { 64 _, err := pgp.NewPrivateKey(test.name, test.email) 65 if (err == nil) == test.err { 66 t.Errorf("%s should return error %t got %t", test.desc, test.err, err == nil) 67 } 68 } 69 }) 70 71 t.Run("SignText", func(t *testing.T) { 72 tests := []struct { 73 name string 74 key *pgp.PrivateKey 75 data []byte 76 err error 77 }{ 78 { 79 name: "valid key", 80 key: priv, 81 data: data, 82 }, 83 { 84 name: "nil key", 85 data: data, 86 err: crypto.ErrInvalidKey, 87 }, 88 } 89 90 for _, test := range tests { 91 got, err := pgp.SignText(test.key, data) 92 if !errors.Is(err, test.err) { 93 t.Errorf("%s should return error %q got %q", test.name, test.err, err) 94 } else if test.err == nil { 95 gotData, gotSig, err := pgp.Split(got) 96 if err != nil { 97 t.Errorf("%s failed to split message: %s", test.name, err) 98 } else if diff := cmp.Diff(string(test.data), string(gotData)); diff != "" { 99 t.Error(test.name, diff) 100 } else if test.err == nil && !pgp.Verify(pub, gotData, gotSig) { 101 t.Error(test.name, "should pass verification") 102 } 103 } 104 } 105 }) 106 107 t.Run("Split", func(t *testing.T) { 108 tests := []struct { 109 name string 110 in []byte 111 want []byte 112 err error 113 }{ 114 { 115 name: "valid message", 116 in: signed, 117 want: data, 118 }, 119 { 120 name: "nil bytes", 121 err: pgp.ErrInvalidMessage, 122 }, 123 { 124 name: "unarmored data", 125 in: data, 126 err: pgp.ErrInvalidMessage, 127 }, 128 { 129 name: "missing signature", 130 in: []byte("-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: SHA512\r\n\r\ndata\r\n"), 131 err: pgp.ErrInvalidMessage, 132 }, 133 { 134 name: "missing data", 135 in: append([]byte{'\n'}, sig...), 136 err: pgp.ErrInvalidMessage, 137 }, 138 } 139 140 for _, test := range tests { 141 gotData, gotSig, err := pgp.Split(test.in) 142 if !errors.Is(err, test.err) { 143 t.Errorf("%s should return error %q got %q", test.name, test.err, err) 144 } else if diff := cmp.Diff(string(test.want), string(gotData)); diff != "" { 145 t.Error(test.name, diff) 146 } else if test.err == nil && !pgp.Verify(pub, gotData, gotSig) { 147 t.Error(test.name, "should pass verification") 148 } 149 } 150 }) 151 152 t.Run("WrongKeyType", func(t *testing.T) { 153 t.Run("MarshalPrivateKey", func(t *testing.T) { 154 if _, err := pgp.MarshalPrivateKey(pub); err == nil { 155 t.Errorf("should return error") 156 } 157 }) 158 159 t.Run("MarshalPublicKey", func(t *testing.T) { 160 if _, err := pgp.MarshalPublicKey(priv); err == nil { 161 t.Errorf("should return error") 162 } 163 }) 164 165 t.Run("Sign", func(t *testing.T) { 166 if _, err := pgp.Sign(pub, data); err == nil { 167 t.Errorf("should return error") 168 } 169 }) 170 }) 171 172 t.Run("LockedKey", func(t *testing.T) { 173 privLocked, _ := priv.Lock([]byte("passphrase")) 174 175 t.Run("Sign", func(t *testing.T) { 176 if _, err := pgp.Sign(privLocked, data); err == nil { 177 t.Errorf("should return error") 178 } 179 }) 180 181 t.Run("Verify", func(t *testing.T) { 182 if pgp.Verify(privLocked, data, sig) { 183 t.Errorf("should fail") 184 } 185 }) 186 }) 187 188 t.Run("GnuPG", func(t *testing.T) { 189 if _, err := exec.LookPath("gpg"); err != nil { 190 t.Skip("gpg not in path") 191 } 192 193 dir := t.TempDir() 194 os.WriteFile(filepath.Join(dir, "key.asc"), pubBytes, 0o600) 195 os.WriteFile(filepath.Join(dir, "data"), data, 0o600) 196 os.WriteFile(filepath.Join(dir, "data.asc"), sig, 0o600) 197 os.WriteFile(filepath.Join(dir, "signed"), signed, 0o600) 198 199 baseArgs := []string{"--no-default-keyring", "--keyring", "keyring.gpg"} 200 arguments := [][]string{ 201 {"--import", "key.asc"}, // Create keybox & import key. 202 {"--verify", "data.asc"}, // Verify detached signature. 203 {"--verify", "signed"}, // Verify signed message. 204 } 205 206 for _, a := range arguments { 207 cmd := exec.Command("gpg", append(baseArgs, a...)...) 208 cmd.Dir = dir 209 out, err := cmd.CombinedOutput() 210 if err != nil { 211 t.Fatal(a, err, string(out)) 212 } 213 t.Log(a, "\n"+string(out)) 214 } 215 }) 216 }