github.com/creativeprojects/go-selfupdate@v1.2.0/update/apply_test.go (about) 1 package update 2 3 import ( 4 "bytes" 5 "crypto" 6 "crypto/rand" 7 "crypto/sha256" 8 "crypto/x509" 9 "encoding/pem" 10 "fmt" 11 "os" 12 "testing" 13 ) 14 15 var ( 16 oldFile = []byte{0xDE, 0xAD, 0xBE, 0xEF} 17 newFile = []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06} 18 newFileChecksum = sha256.Sum256(newFile) 19 ) 20 21 func cleanup(path string) { 22 os.Remove(path) 23 os.Remove(fmt.Sprintf(".%s.new", path)) 24 } 25 26 // we write with a separate name for each test so that we can run them in parallel 27 func writeOldFile(path string, t *testing.T) { 28 if err := os.WriteFile(path, oldFile, 0777); err != nil { 29 t.Fatalf("Failed to write file for testing preparation: %v", err) 30 } 31 } 32 33 func validateUpdate(path string, err error, t *testing.T) { 34 if err != nil { 35 t.Fatalf("Failed to update: %v", err) 36 } 37 38 buf, err := os.ReadFile(path) 39 if err != nil { 40 t.Fatalf("Failed to read file post-update: %v", err) 41 } 42 43 if !bytes.Equal(buf, newFile) { 44 t.Fatalf("File was not updated! Bytes read: %v, Bytes expected: %v", buf, newFile) 45 } 46 } 47 48 func TestApplySimple(t *testing.T) { 49 fName := "TestApplySimple" 50 defer cleanup(fName) 51 writeOldFile(fName, t) 52 53 err := Apply(bytes.NewReader(newFile), Options{ 54 TargetPath: fName, 55 }) 56 validateUpdate(fName, err, t) 57 } 58 59 func TestApplyOldSavePath(t *testing.T) { 60 fName := "TestApplyOldSavePath" 61 defer cleanup(fName) 62 writeOldFile(fName, t) 63 64 oldfName := "OldSavePath" 65 66 err := Apply(bytes.NewReader(newFile), Options{ 67 TargetPath: fName, 68 OldSavePath: oldfName, 69 }) 70 validateUpdate(fName, err, t) 71 72 if _, err := os.Stat(oldfName); os.IsNotExist(err) { 73 t.Fatalf("Failed to find the old file: %v", err) 74 } 75 76 cleanup(oldfName) 77 } 78 79 func TestVerifyChecksum(t *testing.T) { 80 fName := "TestVerifyChecksum" 81 defer cleanup(fName) 82 writeOldFile(fName, t) 83 84 err := Apply(bytes.NewReader(newFile), Options{ 85 TargetPath: fName, 86 Checksum: newFileChecksum[:], 87 }) 88 validateUpdate(fName, err, t) 89 } 90 91 func TestVerifyChecksumNegative(t *testing.T) { 92 fName := "TestVerifyChecksumNegative" 93 defer cleanup(fName) 94 writeOldFile(fName, t) 95 96 badChecksum := []byte{0x0A, 0x0B, 0x0C, 0xFF} 97 err := Apply(bytes.NewReader(newFile), Options{ 98 TargetPath: fName, 99 Checksum: badChecksum, 100 }) 101 if err == nil { 102 t.Fatalf("Failed to detect bad checksum!") 103 } 104 } 105 106 const ecdsaPublicKey = ` 107 -----BEGIN PUBLIC KEY----- 108 MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEL8ThbSyEucsCxnd4dCZR2hIy5nea54ko 109 O+jUUfIjkvwhCWzASm0lpCVdVpXKZXIe+NZ+44RQRv3+OqJkCCGzUgJkPNI3lxdG 110 9zu8rbrnxISV06VQ8No7Ei9wiTpqmTBB 111 -----END PUBLIC KEY----- 112 ` 113 114 const ecdsaPrivateKey = ` 115 -----BEGIN EC PRIVATE KEY----- 116 MIGkAgEBBDBttCB/1NOY4T+WrG4FSV49Ayn3gK1DNzfGaJ01JUXeiNFCWQM2pqpU 117 om8ATPP/dkegBwYFK4EEACKhZANiAAQvxOFtLIS5ywLGd3h0JlHaEjLmd5rniSg7 118 6NRR8iOS/CEJbMBKbSWkJV1Wlcplch741n7jhFBG/f46omQIIbNSAmQ80jeXF0b3 119 O7ytuufEhJXTpVDw2jsSL3CJOmqZMEE= 120 -----END EC PRIVATE KEY----- 121 ` 122 123 const rsaPublicKey = ` 124 -----BEGIN PUBLIC KEY----- 125 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxSWmu7trWKAwDFjiCN2D 126 Tk2jj2sgcr/CMlI4cSSiIOHrXCFxP1I8i9PvQkd4hasXQrLbT5WXKrRGv1HKUKab 127 b9ead+kD0kxk7i2bFYvKX43oq66IW0mOLTQBO7I9UyT4L7svcMD+HUQ2BqHoaQe4 128 y20C59dPr9Dpcz8DZkdLsBV6YKF6Ieb3iGk8oRLMWNaUqPa8f1BGgxAkvPHcqDjT 129 x4xRnjgTRRRlZvRtALHMUkIChgxDOhoEzKpGiqnX7HtMJfrhV6h0PAXNA4h9Kjv5 130 5fhJ08Rz7mmZmtH5JxTK5XTquo59sihSajR4bSjZbbkQ1uLkeFlY3eli3xdQ7Nrf 131 fQIDAQAB 132 -----END PUBLIC KEY-----` 133 134 const rsaPrivateKey = ` 135 -----BEGIN RSA PRIVATE KEY----- 136 MIIEogIBAAKCAQEAxSWmu7trWKAwDFjiCN2DTk2jj2sgcr/CMlI4cSSiIOHrXCFx 137 P1I8i9PvQkd4hasXQrLbT5WXKrRGv1HKUKabb9ead+kD0kxk7i2bFYvKX43oq66I 138 W0mOLTQBO7I9UyT4L7svcMD+HUQ2BqHoaQe4y20C59dPr9Dpcz8DZkdLsBV6YKF6 139 Ieb3iGk8oRLMWNaUqPa8f1BGgxAkvPHcqDjTx4xRnjgTRRRlZvRtALHMUkIChgxD 140 OhoEzKpGiqnX7HtMJfrhV6h0PAXNA4h9Kjv55fhJ08Rz7mmZmtH5JxTK5XTquo59 141 sihSajR4bSjZbbkQ1uLkeFlY3eli3xdQ7NrffQIDAQABAoIBAAkN+6RvrTR61voa 142 Mvd5RQiZpEN4Bht/Fyo8gH8h0Zh1B9xJZOwlmMZLS5fdtHlfLEhR8qSrGDBL61vq 143 I8KkhEsUufF78EL+YzxVN+Q7cWYGHIOWFokqza7hzpSxUQO6lPOMQ1eIZaNueJTB 144 Zu07/47ISPPg/bXzgGVcpYlTCPTjUwKjtfyMqvX9AD7fIyYRm6zfE7EHj1J2sBFt 145 Yz1OGELg6HfJwXfpnPfBvftD0hWGzJ78Bp71fPJe6n5gnqmSqRvrcXNWFnH/yqkN 146 d6vPIxD6Z3LjvyZpkA7JillLva2L/zcIFhg4HZvQnWd8/PpDnUDonu36hcj4SC5j 147 W4aVPLkCgYEA4XzNKWxqYcajzFGZeSxlRHupSAl2MT7Cc5085MmE7dd31wK2T8O4 148 n7N4bkm/rjTbX85NsfWdKtWb6mpp8W3VlLP0rp4a/12OicVOkg4pv9LZDmY0sRlE 149 YuDJk1FeCZ50UrwTZI3rZ9IhZHhkgVA6uWAs7tYndONkxNHG0pjqs4sCgYEA39MZ 150 JwMqo3qsPntpgP940cCLflEsjS9hYNO3+Sv8Dq3P0HLVhBYajJnotf8VuU0fsQZG 151 grmtVn1yThFbMq7X1oY4F0XBA+paSiU18c4YyUnwax2u4sw9U/Q9tmQUZad5+ueT 152 qriMBwGv+ewO+nQxqvAsMUmemrVzrfwA5Oct+hcCgYAfiyXoNZJsOy2O15twqBVC 153 j0oPGcO+/9iT89sg5lACNbI+EdMPNYIOVTzzsL1v0VUfAe08h++Enn1BPcG0VHkc 154 ZFBGXTfJoXzfKQrkw7ZzbzuOGB4m6DH44xlP0oIlNlVvfX/5ASF9VJf3RiBJNsAA 155 TsP6ZVr/rw/ZuL7nlxy+IQKBgDhL/HOXlE3yOQiuOec8WsNHTs7C1BXe6PtVxVxi 156 988pYK/pclL6zEq5G5NLSceF4obAMVQIJ9UtUGbabrncyGUo9UrFPLsjYvprSZo8 157 YHegpVwL50UcYgCP2kXZ/ldjPIcjYDz8lhvdDMor2cidGTEJn9P11HLNWP9V91Ob 158 4jCZAoGAPNRSC5cC8iP/9j+s2/kdkfWJiNaolPYAUrmrkL6H39PYYZM5tnhaIYJV 159 Oh9AgABamU0eb3p3vXTISClVgV7ifq1HyZ7BSUhMfaY2Jk/s3sUHCWFxPZe9sgEG 160 KinIY/373KIkIV/5g4h2v1w330IWcfptxKcY/Er3DJr38f695GE= 161 -----END RSA PRIVATE KEY-----` 162 163 func signec(privatePEM string, source []byte, t *testing.T) []byte { 164 parseFn := func(p []byte) (crypto.Signer, error) { return x509.ParseECPrivateKey(p) } 165 return sign(parseFn, privatePEM, source, t) 166 } 167 168 func signrsa(privatePEM string, source []byte, t *testing.T) []byte { 169 parseFn := func(p []byte) (crypto.Signer, error) { return x509.ParsePKCS1PrivateKey(p) } 170 return sign(parseFn, privatePEM, source, t) 171 } 172 173 func sign(parsePrivKey func([]byte) (crypto.Signer, error), privatePEM string, source []byte, t *testing.T) []byte { 174 block, _ := pem.Decode([]byte(privatePEM)) 175 if block == nil { 176 t.Fatalf("Failed to parse private key PEM") 177 } 178 179 priv, err := parsePrivKey(block.Bytes) 180 if err != nil { 181 t.Fatalf("Failed to parse private key DER: %v", err) 182 } 183 184 checksum := sha256.Sum256(source) 185 sig, err := priv.Sign(rand.Reader, checksum[:], crypto.SHA256) 186 if err != nil { 187 t.Fatalf("Failed to sign: %v", sig) 188 } 189 190 return sig 191 } 192 193 func TestVerifyECSignature(t *testing.T) { 194 fName := "TestVerifyECSignature" 195 defer cleanup(fName) 196 writeOldFile(fName, t) 197 198 opts := Options{TargetPath: fName} 199 err := opts.SetPublicKeyPEM([]byte(ecdsaPublicKey)) 200 if err != nil { 201 t.Fatalf("Could not parse public key: %v", err) 202 } 203 204 opts.Signature = signec(ecdsaPrivateKey, newFile, t) 205 err = Apply(bytes.NewReader(newFile), opts) 206 validateUpdate(fName, err, t) 207 } 208 209 func TestVerifyRSASignature(t *testing.T) { 210 fName := "TestVerifyRSASignature" 211 defer cleanup(fName) 212 writeOldFile(fName, t) 213 214 opts := Options{ 215 TargetPath: fName, 216 Verifier: NewRSAVerifier(), 217 } 218 err := opts.SetPublicKeyPEM([]byte(rsaPublicKey)) 219 if err != nil { 220 t.Fatalf("Could not parse public key: %v", err) 221 } 222 223 opts.Signature = signrsa(rsaPrivateKey, newFile, t) 224 err = Apply(bytes.NewReader(newFile), opts) 225 validateUpdate(fName, err, t) 226 } 227 228 func TestVerifyFailBadSignature(t *testing.T) { 229 fName := "TestVerifyFailBadSignature" 230 defer cleanup(fName) 231 writeOldFile(fName, t) 232 233 opts := Options{ 234 TargetPath: fName, 235 Signature: []byte{0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA}, 236 } 237 err := opts.SetPublicKeyPEM([]byte(ecdsaPublicKey)) 238 if err != nil { 239 t.Fatalf("Could not parse public key: %v", err) 240 } 241 242 err = Apply(bytes.NewReader(newFile), opts) 243 if err == nil { 244 t.Fatalf("Did not fail with bad signature") 245 } 246 } 247 248 func TestVerifyFailNoSignature(t *testing.T) { 249 fName := "TestVerifySignatureWithPEM" 250 defer cleanup(fName) 251 writeOldFile(fName, t) 252 253 opts := Options{TargetPath: fName} 254 err := opts.SetPublicKeyPEM([]byte(ecdsaPublicKey)) 255 if err != nil { 256 t.Fatalf("Could not parse public key: %v", err) 257 } 258 259 err = Apply(bytes.NewReader(newFile), opts) 260 if err == nil { 261 t.Fatalf("Did not fail with empty signature") 262 } 263 } 264 265 const wrongKey = ` 266 -----BEGIN EC PRIVATE KEY----- 267 MIGkAgEBBDBzqYp6N2s8YWYifBjS03/fFfmGeIPcxQEi+bbFeekIYt8NIKIkhD+r 268 hpaIwSmot+qgBwYFK4EEACKhZANiAAR0EC8Usbkc4k30frfEB2ECmsIghu9DJSqE 269 RbH7jfq2ULNv8tN/clRjxf2YXgp+iP3SQF1R1EYERKpWr8I57pgfIZtoZXjwpbQC 270 VBbP/Ff+05HOqwPC7rJMy1VAJLKg7Cw= 271 -----END EC PRIVATE KEY----- 272 ` 273 274 func TestVerifyFailWrongSignature(t *testing.T) { 275 fName := "TestVerifyFailWrongSignature" 276 defer cleanup(fName) 277 writeOldFile(fName, t) 278 279 opts := Options{TargetPath: fName} 280 err := opts.SetPublicKeyPEM([]byte(ecdsaPublicKey)) 281 if err != nil { 282 t.Fatalf("Could not parse public key: %v", err) 283 } 284 285 opts.Signature = signec(wrongKey, newFile, t) 286 err = Apply(bytes.NewReader(newFile), opts) 287 if err == nil { 288 t.Fatalf("Verified an update that was signed by an untrusted key!") 289 } 290 } 291 292 func TestSignatureButNoPublicKey(t *testing.T) { 293 fName := "TestSignatureButNoPublicKey" 294 defer cleanup(fName) 295 writeOldFile(fName, t) 296 297 err := Apply(bytes.NewReader(newFile), Options{ 298 TargetPath: fName, 299 Signature: signec(ecdsaPrivateKey, newFile, t), 300 }) 301 if err == nil { 302 t.Fatalf("Allowed an update with a signature verification when no public key was specified!") 303 } 304 } 305 306 func TestPublicKeyButNoSignature(t *testing.T) { 307 fName := "TestPublicKeyButNoSignature" 308 defer cleanup(fName) 309 writeOldFile(fName, t) 310 311 opts := Options{TargetPath: fName} 312 if err := opts.SetPublicKeyPEM([]byte(ecdsaPublicKey)); err != nil { 313 t.Fatalf("Could not parse public key: %v", err) 314 } 315 err := Apply(bytes.NewReader(newFile), opts) 316 if err == nil { 317 t.Fatalf("Allowed an update with no signature when a public key was specified!") 318 } 319 } 320 321 func TestWriteError(t *testing.T) { 322 fName := "TestWriteError" 323 defer cleanup(fName) 324 writeOldFile(fName, t) 325 326 openFile = func(name string, flags int, perm os.FileMode) (*os.File, error) { 327 f, err := os.OpenFile(name, flags, perm) 328 329 // simulate Write() error by closing the file prematurely 330 f.Close() 331 332 return f, err 333 } 334 defer func() { 335 openFile = os.OpenFile 336 }() 337 338 err := Apply(bytes.NewReader(newFile), Options{TargetPath: fName}) 339 if err == nil { 340 t.Fatalf("Allowed an update to an empty file") 341 } 342 }