pax_global_header00006660000000000000000000000064147221466220014520gustar00rootroot0000000000000052 comment=2f98026978b11613e906c91cf70b3fc5656d9124 golang-github-minio-selfupdate-0.6.0+ds/000077500000000000000000000000001472214662200201175ustar00rootroot00000000000000golang-github-minio-selfupdate-0.6.0+ds/.github/000077500000000000000000000000001472214662200214575ustar00rootroot00000000000000golang-github-minio-selfupdate-0.6.0+ds/.github/workflows/000077500000000000000000000000001472214662200235145ustar00rootroot00000000000000golang-github-minio-selfupdate-0.6.0+ds/.github/workflows/vulncheck.yml000066400000000000000000000012011472214662200262130ustar00rootroot00000000000000name: VulnCheck on: pull_request: branches: - master - main push: branches: - master - main jobs: vulncheck: name: Analysis runs-on: ubuntu-latest strategy: matrix: go-version: [ 1.19 ] steps: - name: Check out code into the Go module directory uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: go-version: ${{ matrix.go-version }} check-latest: true - name: Get govulncheck run: go install golang.org/x/vuln/cmd/govulncheck@latest shell: bash - name: Run govulncheck run: govulncheck ./... shell: bash golang-github-minio-selfupdate-0.6.0+ds/.gitignore000066400000000000000000000000071472214662200221040ustar00rootroot00000000000000*.test golang-github-minio-selfupdate-0.6.0+ds/LICENSE000066400000000000000000000261361472214662200211340ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-minio-selfupdate-0.6.0+ds/LICENSE.minisig000066400000000000000000000004561472214662200225670ustar00rootroot00000000000000untrusted comment: signature from minisign secret key RUQhjNB8gjlNDZN66rN1aESIzZK6jG17OXx2wki+TYYuhwlW9cOq0qIHtTEt4b776mziUbtITtm1+UrwfODM32VR3jG2eqn/NwA= trusted comment: timestamp:1639597543 file:LICENSE hashed rbQFZEBnFNdFMLj+6bhp2ADasgXnPEkpDbpytMKcxbCa+wm0UFUB1nputqIANfpc6GTRq4JPa0N97y/uzrRuBQ== golang-github-minio-selfupdate-0.6.0+ds/NOTICE000066400000000000000000000011331472214662200210210ustar00rootroot00000000000000Copyright 2020 MinIO,Inc rewrites and modifications Copyright 2015 Alan Shreve Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-minio-selfupdate-0.6.0+ds/README.md000066400000000000000000000031031472214662200213730ustar00rootroot00000000000000[![API Reference](https://img.shields.io/badge/api-reference-blue.svg)](https://pkg.go.dev/github.com/minio/selfupdate?tab=doc) [![Apache V2 License](https://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/minio/selfupdate/blob/master/LICENSE) # selfupdate: Build self-updating Go programs > NOTE: Original work at github.com/inconshreveable/go-update, modified for the needs within MinIO project Package update provides functionality to implement secure, self-updating Go programs (or other single-file targets) A program can update itself by replacing its executable file with a new version. It provides the flexibility to implement different updating user experiences like auto-updating, or manual user-initiated updates. It also boasts advanced features like binary patching and code signing verification. Example of updating from a URL: ```go import ( "fmt" "net/http" "github.com/minio/selfupdate" ) func doUpdate(url string) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() err := selfupdate.Apply(resp.Body, update.Options{}) if err != nil { // error handling } return err } ``` ## Features - Cross platform support (Windows too!) - Binary patch application - Checksum verification - Code signing verification - Support for updating arbitrary files ## License This SDK is distributed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0), see LICENSE for more information. Original work was also distributed under the same license. golang-github-minio-selfupdate-0.6.0+ds/apply.go000066400000000000000000000214151472214662200215760ustar00rootroot00000000000000package selfupdate import ( "bytes" "crypto" "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "github.com/minio/selfupdate/internal/osext" ) // Apply performs an update of the current executable or opts.TargetFile, with // the contents of the given io.Reader. When the update fails, it is unlikely // that old executable is corrupted, but still, applications need to check the // returned error with RollbackError() and notify the user of the bad news and // ask them to recover manually. func Apply(update io.Reader, opts Options) error { err := PrepareAndCheckBinary(update, opts) if err != nil { return err } return CommitBinary(opts) } // PrepareAndCheckBinary reads the new binary content from io.Reader and performs the following actions: // 1. If configured, applies the contents of the update io.Reader as a binary patch. // 2. If configured, computes the checksum of the executable and verifies it matches. // 3. If configured, verifies the signature with a public key. // 4. Creates a new file, /path/to/.target.new with the TargetMode with the contents of the updated file func PrepareAndCheckBinary(update io.Reader, opts Options) error { // get target path targetPath, err := opts.getPath() if err != nil { return err } var newBytes []byte if opts.Patcher != nil { if newBytes, err = opts.applyPatch(update, targetPath); err != nil { return err } } else { // no patch to apply, go on through if newBytes, err = ioutil.ReadAll(update); err != nil { return err } } // verify checksum if requested if opts.Checksum != nil { if err = opts.verifyChecksum(newBytes); err != nil { return err } } if opts.Verifier != nil { if err = opts.Verifier.Verify(newBytes); err != nil { return err } } // get the directory the executable exists in updateDir := filepath.Dir(targetPath) filename := filepath.Base(targetPath) // Copy the contents of newbinary to a new executable file newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename)) fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, opts.getMode()) if err != nil { return err } defer fp.Close() _, err = io.Copy(fp, bytes.NewReader(newBytes)) if err != nil { return err } // if we don't call fp.Close(), windows won't let us move the new executable // because the file will still be "in use" fp.Close() return nil } // CommitBinary moves the new executable to the location of the current executable or opts.TargetPath // if specified. It performs the following operations: // 1. Renames /path/to/target to /path/to/.target.old // 2. Renames /path/to/.target.new to /path/to/target // 3. If the final rename is successful, deletes /path/to/.target.old, returns no error. On Windows, // the removal of /path/to/target.old always fails, so instead Apply hides the old file instead. // 4. If the final rename fails, attempts to roll back by renaming /path/to/.target.old // back to /path/to/target. // // If the roll back operation fails, the file system is left in an inconsistent state where there is // no new executable file and the old executable file could not be be moved to its original location. // In this case you should notify the user of the bad news and ask them to recover manually. Applications // can determine whether the rollback failed by calling RollbackError, see the documentation on that function // for additional detail. func CommitBinary(opts Options) error { // get the directory the file exists in targetPath, err := opts.getPath() if err != nil { return err } updateDir := filepath.Dir(targetPath) filename := filepath.Base(targetPath) newPath := filepath.Join(updateDir, fmt.Sprintf(".%s.new", filename)) // this is where we'll move the executable to so that we can swap in the updated replacement oldPath := opts.OldSavePath removeOld := opts.OldSavePath == "" if removeOld { oldPath = filepath.Join(updateDir, fmt.Sprintf(".%s.old", filename)) } // delete any existing old exec file - this is necessary on Windows for two reasons: // 1. after a successful update, Windows can't remove the .old file because the process is still running // 2. windows rename operations fail if the destination file already exists _ = os.Remove(oldPath) // move the existing executable to a new file in the same directory err = os.Rename(targetPath, oldPath) if err != nil { return err } // move the new exectuable in to become the new program err = os.Rename(newPath, targetPath) if err != nil { // move unsuccessful // // The filesystem is now in a bad state. We have successfully // moved the existing binary to a new location, but we couldn't move the new // binary to take its place. That means there is no file where the current executable binary // used to be! // Try to rollback by restoring the old binary to its original path. rerr := os.Rename(oldPath, targetPath) if rerr != nil { return &rollbackErr{err, rerr} } return err } // move successful, remove the old binary if needed if removeOld { errRemove := os.Remove(oldPath) // windows has trouble with removing old binaries, so hide it instead if errRemove != nil { _ = hideFile(oldPath) } } return nil } // RollbackError takes an error value returned by Apply and returns the error, if any, // that occurred when attempting to roll back from a failed update. Applications should // always call this function on any non-nil errors returned by Apply. // // If no rollback was needed or if the rollback was successful, RollbackError returns nil, // otherwise it returns the error encountered when trying to roll back. func RollbackError(err error) error { if err == nil { return nil } if rerr, ok := err.(*rollbackErr); ok { return rerr.rollbackErr } return nil } type rollbackErr struct { error // original error rollbackErr error // error encountered while rolling back } type Options struct { // TargetPath defines the path to the file to update. // The emptry string means 'the executable file of the running program'. TargetPath string // Create TargetPath replacement with this file mode. If zero, defaults to 0755. TargetMode os.FileMode // Checksum of the new binary to verify against. If nil, no checksum or signature verification is done. Checksum []byte // Verifier for signature verification. If nil, no signature verification is done. Verifier *Verifier // Use this hash function to generate the checksum. If not set, SHA256 is used. Hash crypto.Hash // If nil, treat the update as a complete replacement for the contents of the file at TargetPath. // If non-nil, treat the update contents as a patch and use this object to apply the patch. Patcher Patcher // Store the old executable file at this path after a successful update. // The empty string means the old executable file will be removed after the update. OldSavePath string } // CheckPermissions determines whether the process has the correct permissions to // perform the requested update. If the update can proceed, it returns nil, otherwise // it returns the error that would occur if an update were attempted. func (o *Options) CheckPermissions() error { // get the directory the file exists in path, err := o.getPath() if err != nil { return err } fileDir := filepath.Dir(path) fileName := filepath.Base(path) // attempt to open a file in the file's directory newPath := filepath.Join(fileDir, fmt.Sprintf(".%s.check-perm", fileName)) fp, err := os.OpenFile(newPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, o.getMode()) if err != nil { return err } fp.Close() _ = os.Remove(newPath) return nil } func (o *Options) getPath() (string, error) { if o.TargetPath == "" { return osext.Executable() } else { return o.TargetPath, nil } } func (o *Options) getMode() os.FileMode { if o.TargetMode == 0 { return 0755 } return o.TargetMode } func (o *Options) getHash() crypto.Hash { if o.Hash == 0 { o.Hash = crypto.SHA256 } return o.Hash } func (o *Options) applyPatch(patch io.Reader, targetPath string) ([]byte, error) { // open the file to patch old, err := os.Open(targetPath) if err != nil { return nil, err } defer old.Close() // apply the patch var applied bytes.Buffer if err = o.Patcher.Patch(old, &applied, patch); err != nil { return nil, err } return applied.Bytes(), nil } func (o *Options) verifyChecksum(updated []byte) error { checksum, err := checksumFor(o.getHash(), updated) if err != nil { return err } if !bytes.Equal(o.Checksum, checksum) { return fmt.Errorf("Updated file has wrong checksum. Expected: %x, got: %x", o.Checksum, checksum) } return nil } func checksumFor(h crypto.Hash, payload []byte) ([]byte, error) { if !h.Available() { return nil, errors.New("requested hash function not available") } hash := h.New() hash.Write(payload) // guaranteed not to error return hash.Sum([]byte{}), nil } golang-github-minio-selfupdate-0.6.0+ds/apply_test.go000066400000000000000000000166551472214662200226470ustar00rootroot00000000000000package selfupdate import ( "bytes" "crypto" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" "os" "testing" "github.com/minio/selfupdate/internal/binarydist" ) var ( oldFile = []byte{0xDE, 0xAD, 0xBE, 0xEF} newFile = []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06} newFileChecksum = sha256.Sum256(newFile) ) func cleanup(path string) { os.Remove(path) os.Remove(fmt.Sprintf(".%s.new", path)) } // we write with a separate name for each test so that we can run them in parallel func writeOldFile(path string, t *testing.T) { if err := ioutil.WriteFile(path, oldFile, 0777); err != nil { t.Fatalf("Failed to write file for testing preparation: %v", err) } } func validateUpdate(path string, err error, t *testing.T) { if err != nil { t.Fatalf("Failed to update: %v", err) } buf, err := ioutil.ReadFile(path) if err != nil { t.Fatalf("Failed to read file post-update: %v", err) } if !bytes.Equal(buf, newFile) { t.Fatalf("File was not updated! Bytes read: %v, Bytes expected: %v", buf, newFile) } } func TestApplySimple(t *testing.T) { fName := "TestApplySimple" defer cleanup(fName) writeOldFile(fName, t) err := Apply(bytes.NewReader(newFile), Options{ TargetPath: fName, }) validateUpdate(fName, err, t) } func TestApplyOldSavePath(t *testing.T) { fName := "TestApplyOldSavePath" defer cleanup(fName) writeOldFile(fName, t) oldfName := "OldSavePath" err := Apply(bytes.NewReader(newFile), Options{ TargetPath: fName, OldSavePath: oldfName, }) validateUpdate(fName, err, t) if _, err := os.Stat(oldfName); os.IsNotExist(err) { t.Fatalf("Failed to find the old file: %v", err) } cleanup(oldfName) } func TestVerifyChecksum(t *testing.T) { fName := "TestVerifyChecksum" defer cleanup(fName) writeOldFile(fName, t) err := Apply(bytes.NewReader(newFile), Options{ TargetPath: fName, Checksum: newFileChecksum[:], }) validateUpdate(fName, err, t) } func TestVerifyChecksumNegative(t *testing.T) { fName := "TestVerifyChecksumNegative" defer cleanup(fName) writeOldFile(fName, t) badChecksum := []byte{0x0A, 0x0B, 0x0C, 0xFF} err := Apply(bytes.NewReader(newFile), Options{ TargetPath: fName, Checksum: badChecksum, }) if err == nil { t.Fatalf("Failed to detect bad checksum!") } } func TestApplyPatch(t *testing.T) { fName := "TestApplyPatch" defer cleanup(fName) writeOldFile(fName, t) patch := new(bytes.Buffer) err := binarydist.Diff(bytes.NewReader(oldFile), bytes.NewReader(newFile), patch) if err != nil { t.Fatalf("Failed to create patch: %v", err) } err = Apply(patch, Options{ TargetPath: fName, Patcher: NewBSDiffPatcher(), }) validateUpdate(fName, err, t) } func TestCorruptPatch(t *testing.T) { fName := "TestCorruptPatch" defer cleanup(fName) writeOldFile(fName, t) badPatch := []byte{0x44, 0x38, 0x86, 0x3c, 0x4f, 0x8d, 0x26, 0x54, 0xb, 0x11, 0xce, 0xfe, 0xc1, 0xc0, 0xf8, 0x31, 0x38, 0xa0, 0x12, 0x1a, 0xa2, 0x57, 0x2a, 0xe1, 0x3a, 0x48, 0x62, 0x40, 0x2b, 0x81, 0x12, 0xb1, 0x21, 0xa5, 0x16, 0xed, 0x73, 0xd6, 0x54, 0x84, 0x29, 0xa6, 0xd6, 0xb2, 0x1b, 0xfb, 0xe6, 0xbe, 0x7b, 0x70} err := Apply(bytes.NewReader(badPatch), Options{ TargetPath: fName, Patcher: NewBSDiffPatcher(), }) if err == nil { t.Fatalf("Failed to detect corrupt patch!") } } func TestVerifyChecksumPatchNegative(t *testing.T) { fName := "TestVerifyChecksumPatchNegative" defer cleanup(fName) writeOldFile(fName, t) patch := new(bytes.Buffer) anotherFile := []byte{0x77, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66} err := binarydist.Diff(bytes.NewReader(oldFile), bytes.NewReader(anotherFile), patch) if err != nil { t.Fatalf("Failed to create patch: %v", err) } err = Apply(patch, Options{ TargetPath: fName, Checksum: newFileChecksum[:], Patcher: NewBSDiffPatcher(), }) if err == nil { t.Fatalf("Failed to detect patch to wrong file!") } } const ecdsaPublicKey = ` -----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEL8ThbSyEucsCxnd4dCZR2hIy5nea54ko O+jUUfIjkvwhCWzASm0lpCVdVpXKZXIe+NZ+44RQRv3+OqJkCCGzUgJkPNI3lxdG 9zu8rbrnxISV06VQ8No7Ei9wiTpqmTBB -----END PUBLIC KEY----- ` const ecdsaPrivateKey = ` -----BEGIN EC PRIVATE KEY----- MIGkAgEBBDBttCB/1NOY4T+WrG4FSV49Ayn3gK1DNzfGaJ01JUXeiNFCWQM2pqpU om8ATPP/dkegBwYFK4EEACKhZANiAAQvxOFtLIS5ywLGd3h0JlHaEjLmd5rniSg7 6NRR8iOS/CEJbMBKbSWkJV1Wlcplch741n7jhFBG/f46omQIIbNSAmQ80jeXF0b3 O7ytuufEhJXTpVDw2jsSL3CJOmqZMEE= -----END EC PRIVATE KEY----- ` const rsaPublicKey = ` -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxSWmu7trWKAwDFjiCN2D Tk2jj2sgcr/CMlI4cSSiIOHrXCFxP1I8i9PvQkd4hasXQrLbT5WXKrRGv1HKUKab b9ead+kD0kxk7i2bFYvKX43oq66IW0mOLTQBO7I9UyT4L7svcMD+HUQ2BqHoaQe4 y20C59dPr9Dpcz8DZkdLsBV6YKF6Ieb3iGk8oRLMWNaUqPa8f1BGgxAkvPHcqDjT x4xRnjgTRRRlZvRtALHMUkIChgxDOhoEzKpGiqnX7HtMJfrhV6h0PAXNA4h9Kjv5 5fhJ08Rz7mmZmtH5JxTK5XTquo59sihSajR4bSjZbbkQ1uLkeFlY3eli3xdQ7Nrf fQIDAQAB -----END PUBLIC KEY-----` const rsaPrivateKey = ` -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAxSWmu7trWKAwDFjiCN2DTk2jj2sgcr/CMlI4cSSiIOHrXCFx P1I8i9PvQkd4hasXQrLbT5WXKrRGv1HKUKabb9ead+kD0kxk7i2bFYvKX43oq66I W0mOLTQBO7I9UyT4L7svcMD+HUQ2BqHoaQe4y20C59dPr9Dpcz8DZkdLsBV6YKF6 Ieb3iGk8oRLMWNaUqPa8f1BGgxAkvPHcqDjTx4xRnjgTRRRlZvRtALHMUkIChgxD OhoEzKpGiqnX7HtMJfrhV6h0PAXNA4h9Kjv55fhJ08Rz7mmZmtH5JxTK5XTquo59 sihSajR4bSjZbbkQ1uLkeFlY3eli3xdQ7NrffQIDAQABAoIBAAkN+6RvrTR61voa Mvd5RQiZpEN4Bht/Fyo8gH8h0Zh1B9xJZOwlmMZLS5fdtHlfLEhR8qSrGDBL61vq I8KkhEsUufF78EL+YzxVN+Q7cWYGHIOWFokqza7hzpSxUQO6lPOMQ1eIZaNueJTB Zu07/47ISPPg/bXzgGVcpYlTCPTjUwKjtfyMqvX9AD7fIyYRm6zfE7EHj1J2sBFt Yz1OGELg6HfJwXfpnPfBvftD0hWGzJ78Bp71fPJe6n5gnqmSqRvrcXNWFnH/yqkN d6vPIxD6Z3LjvyZpkA7JillLva2L/zcIFhg4HZvQnWd8/PpDnUDonu36hcj4SC5j W4aVPLkCgYEA4XzNKWxqYcajzFGZeSxlRHupSAl2MT7Cc5085MmE7dd31wK2T8O4 n7N4bkm/rjTbX85NsfWdKtWb6mpp8W3VlLP0rp4a/12OicVOkg4pv9LZDmY0sRlE YuDJk1FeCZ50UrwTZI3rZ9IhZHhkgVA6uWAs7tYndONkxNHG0pjqs4sCgYEA39MZ JwMqo3qsPntpgP940cCLflEsjS9hYNO3+Sv8Dq3P0HLVhBYajJnotf8VuU0fsQZG grmtVn1yThFbMq7X1oY4F0XBA+paSiU18c4YyUnwax2u4sw9U/Q9tmQUZad5+ueT qriMBwGv+ewO+nQxqvAsMUmemrVzrfwA5Oct+hcCgYAfiyXoNZJsOy2O15twqBVC j0oPGcO+/9iT89sg5lACNbI+EdMPNYIOVTzzsL1v0VUfAe08h++Enn1BPcG0VHkc ZFBGXTfJoXzfKQrkw7ZzbzuOGB4m6DH44xlP0oIlNlVvfX/5ASF9VJf3RiBJNsAA TsP6ZVr/rw/ZuL7nlxy+IQKBgDhL/HOXlE3yOQiuOec8WsNHTs7C1BXe6PtVxVxi 988pYK/pclL6zEq5G5NLSceF4obAMVQIJ9UtUGbabrncyGUo9UrFPLsjYvprSZo8 YHegpVwL50UcYgCP2kXZ/ldjPIcjYDz8lhvdDMor2cidGTEJn9P11HLNWP9V91Ob 4jCZAoGAPNRSC5cC8iP/9j+s2/kdkfWJiNaolPYAUrmrkL6H39PYYZM5tnhaIYJV Oh9AgABamU0eb3p3vXTISClVgV7ifq1HyZ7BSUhMfaY2Jk/s3sUHCWFxPZe9sgEG KinIY/373KIkIV/5g4h2v1w330IWcfptxKcY/Er3DJr38f695GE= -----END RSA PRIVATE KEY-----` func signec(privatePEM string, source []byte, t *testing.T) []byte { parseFn := func(p []byte) (crypto.Signer, error) { return x509.ParseECPrivateKey(p) } return sign(parseFn, privatePEM, source, t) } func signrsa(privatePEM string, source []byte, t *testing.T) []byte { parseFn := func(p []byte) (crypto.Signer, error) { return x509.ParsePKCS1PrivateKey(p) } return sign(parseFn, privatePEM, source, t) } func sign(parsePrivKey func([]byte) (crypto.Signer, error), privatePEM string, source []byte, t *testing.T) []byte { block, _ := pem.Decode([]byte(privatePEM)) if block == nil { t.Fatalf("Failed to parse private key PEM") } priv, err := parsePrivKey(block.Bytes) if err != nil { t.Fatalf("Failed to parse private key DER: %v", err) } checksum := sha256.Sum256(source) sig, err := priv.Sign(rand.Reader, checksum[:], crypto.SHA256) if err != nil { t.Fatalf("Failed to sign: %v", sig) } return sig } golang-github-minio-selfupdate-0.6.0+ds/doc.go000066400000000000000000000112161472214662200212140ustar00rootroot00000000000000/* Package update provides functionality to implement secure, self-updating Go programs (or other single-file targets). For complete updating solutions please see Equinox (https://equinox.io) and go-tuf (https://github.com/flynn/go-tuf). # Basic Example This example shows how to update a program remotely from a URL. import ( "fmt" "net/http" "github.com/minio/selfupdate" ) func doUpdate(url string) error { // request the new file resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() err := selfupdate.Apply(resp.Body, selfupdate.Options{}) if err != nil { if rerr := selfupdate.RollbackError(err); rerr != nil { fmt.Println("Failed to rollback from bad update: %v", rerr) } } return err } # Binary Patching Go binaries can often be large. It can be advantageous to only ship a binary patch to a client instead of the complete program text of a new version. This example shows how to update a program with a bsdiff binary patch. Other patch formats may be applied by implementing the Patcher interface. import ( "encoding/hex" "io" "github.com/minio/selfupdate" ) func updateWithPatch(patch io.Reader) error { err := selfupdate.Apply(patch, selfupdate.Options{ Patcher: selfupdate.NewBSDiffPatcher() }) if err != nil { // error handling } return err } # Checksum Verification Updating executable code on a computer can be a dangerous operation unless you take the appropriate steps to guarantee the authenticity of the new code. While checksum verification is important, it should always be combined with signature verification (next section) to guarantee that the code came from a trusted party. selfupdate validates SHA256 checksums by default, but this is pluggable via the Hash property on the Options struct. This example shows how to guarantee that the newly-updated binary is verified to have an appropriate checksum (that was otherwise retrieved via a secure channel) specified as a hex string. import ( "crypto" _ "crypto/sha256" "encoding/hex" "io" "github.com/minio/selfupdate" ) func updateWithChecksum(binary io.Reader, hexChecksum string) error { checksum, err := hex.DecodeString(hexChecksum) if err != nil { return err } err = selfupdate.Apply(binary, selfupdate.Options{ Hash: crypto.SHA256, // this is the default, you don't need to specify it Checksum: checksum, }) if err != nil { // error handling } return err } # Cryptographic Signature Verification Cryptographic verification of new code from an update is an extremely important way to guarantee the security and integrity of your updates. Verification is performed by validating the signature of a hash of the new file. This means nothing changes if you apply your update with a patch. This example shows how to add signature verification to your updates. To make all of this work an application distributor must first create a public/private key pair and embed the public key into their application. When they issue a new release, the issuer must sign the new executable file with the private key and distribute the signature along with the selfupdate. import ( "crypto" _ "crypto/sha256" "encoding/hex" "io" "github.com/minio/selfupdate" ) func verifiedUpdate(binary io.Reader, hexChecksum string) { checksum, err := hex.DecodeString(hexChecksum) if err != nil { return err } opts := selfupdate.Options{ Checksum: checksum, Hash: crypto.SHA256, // this is the default, you don't need to specify it } err = selfupdate.Apply(binary, opts) if err != nil { // error handling } return err } # Building Single-File Go Binaries In order to update a Go application with selfupdate, you must distribute it as a single executable. This is often easy, but some applications require static assets (like HTML and CSS asset files or TLS certificates). In order to update applications like these, you'll want to make sure to embed those asset files into the distributed binary with a tool like go-bindata (my favorite): https://github.com/jteeuwen/go-bindata # Non-Goals Mechanisms and protocols for determining whether an update should be applied and, if so, which one are out of scope for this package. Please consult go-tuf (https://github.com/flynn/go-tuf) or Equinox (https://equinox.io) for more complete solutions. selfupdate only works for self-updating applications that are distributed as a single binary, i.e. applications that do not have additional assets or dependency files. Updating application that are distributed as multiple on-disk files is out of scope, although this may change in future versions of this library. */ package selfupdate golang-github-minio-selfupdate-0.6.0+ds/go.mod000066400000000000000000000002271472214662200212260ustar00rootroot00000000000000module github.com/minio/selfupdate go 1.14 require ( aead.dev/minisign v0.2.0 golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b // indirect ) golang-github-minio-selfupdate-0.6.0+ds/go.sum000066400000000000000000000037531472214662200212620ustar00rootroot00000000000000aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk= aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b h1:QAqMVf3pSa6eeTsuklijukjXBlj7Es2QQplab+/RbQ4= golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang-github-minio-selfupdate-0.6.0+ds/hide_noop.go000066400000000000000000000001311472214662200224050ustar00rootroot00000000000000// +build !windows package selfupdate func hideFile(path string) error { return nil } golang-github-minio-selfupdate-0.6.0+ds/hide_windows.go000066400000000000000000000005401472214662200231300ustar00rootroot00000000000000package selfupdate import ( "syscall" "unsafe" ) func hideFile(path string) error { kernel32 := syscall.NewLazyDLL("kernel32.dll") setFileAttributes := kernel32.NewProc("SetFileAttributesW") r1, _, err := setFileAttributes.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))), 2) if r1 == 0 { return err } else { return nil } } golang-github-minio-selfupdate-0.6.0+ds/minisign.go000066400000000000000000000031651472214662200222700ustar00rootroot00000000000000package selfupdate import ( "errors" "io" "net/http" "aead.dev/minisign" ) type Verifier struct { publicKey minisign.PublicKey signature minisign.Signature } func (v *Verifier) LoadFromURL(signatureURL string, passphrase string, transport http.RoundTripper) error { var publicKey minisign.PublicKey if err := publicKey.UnmarshalText([]byte(passphrase)); err != nil { return err } client := &http.Client{Transport: transport} req, err := http.NewRequest(http.MethodGet, signatureURL, nil) if err != nil { return err } resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return errors.New(resp.Status) } const MaxSize = 1 << 20 b, err := io.ReadAll(io.LimitReader(resp.Body, MaxSize)) if err != nil { return err } var signature minisign.Signature if err = signature.UnmarshalText(b); err != nil { return err } v.publicKey, v.signature = publicKey, signature return nil } func (v *Verifier) LoadFromFile(signaturePath string, passphrase string) error { var publicKey minisign.PublicKey if err := publicKey.UnmarshalText([]byte(passphrase)); err != nil { return err } signature, err := minisign.SignatureFromFile(signaturePath) if err != nil { return err } v.publicKey, v.signature = publicKey, signature return nil } func NewVerifier() *Verifier { return &Verifier{} } func (v *Verifier) Verify(bin []byte) error { signature, err := v.signature.MarshalText() if err != nil { return err } if !minisign.Verify(v.publicKey, bin, signature) { return errors.New("selfupdate: signature verification failed") } return nil } golang-github-minio-selfupdate-0.6.0+ds/minisign_test.go000066400000000000000000000005761472214662200233320ustar00rootroot00000000000000package selfupdate import ( "io/ioutil" "testing" ) func TestMinisign(t *testing.T) { v := NewVerifier() err := v.LoadFromFile("LICENSE.minisig", "RWQhjNB8gjlNDQYRsRiGEzKTtGwzkcFLRMiSEy+texbTAVMvsgFLLfSr") if err != nil { t.Fatal(err) } buf, err := ioutil.ReadFile("LICENSE") if err != nil { t.Fatal(err) } if err = v.Verify(buf); err != nil { t.Fatal(err) } } golang-github-minio-selfupdate-0.6.0+ds/patcher.go000066400000000000000000000012211472214662200220700ustar00rootroot00000000000000package selfupdate import ( "io" "github.com/minio/selfupdate/internal/binarydist" ) // Patcher defines an interface for applying binary patches to an old item to get an updated item. type Patcher interface { Patch(old io.Reader, new io.Writer, patch io.Reader) error } type patchFn func(io.Reader, io.Writer, io.Reader) error func (fn patchFn) Patch(old io.Reader, new io.Writer, patch io.Reader) error { return fn(old, new, patch) } // NewBSDifferPatcher returns a new Patcher that applies binary patches using // the bsdiff algorithm. See http://www.daemonology.net/bsdiff/ func NewBSDiffPatcher() Patcher { return patchFn(binarydist.Patch) } golang-github-minio-selfupdate-0.6.0+ds/test.key000066400000000000000000000004061472214662200216100ustar00rootroot00000000000000untrusted comment: minisign encrypted secret key RWRTY0IyG1x0dmDUEt6W4pVJdOc68t4Qw5WGYE2YvmQKDVOhYHEAAAACAAAAAAAAAEAAAAAAriScLG3bvMtySsBgoKpDAFZwjsEk7mftUovvG1i31XD8W+9a+lOBUPaC8CSUma9AmGuKFWCNsYRo1acAvvtW/zMXd9DamLaRhfvyJ+h7aUYpZsaVTBzczwATrvsIJkXclKmGe4eIXV0= golang-github-minio-selfupdate-0.6.0+ds/test.pub000066400000000000000000000001601472214662200216030ustar00rootroot00000000000000untrusted comment: minisign public key D4D39827CD08C21 RWQhjNB8gjlNDQYRsRiGEzKTtGwzkcFLRMiSEy+texbTAVMvsgFLLfSr