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  }