emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

scratch/package-security 7f29c9e 1/3: Support package checksum verificat


From: Stefan Kangas
Subject: scratch/package-security 7f29c9e 1/3: Support package checksum verification
Date: Tue, 8 Sep 2020 12:51:57 -0400 (EDT)

branch: scratch/package-security
commit 7f29c9e37177fe4fb47c420678c35a6b6705cc3d
Author: Stefan Kangas <stefankangas@gmail.com>
Commit: Stefan Kangas <stefan@marxist.se>

    Support package checksum verification
    
    This is the first step towards protecting users of package.el against
    metadata replay attacks.  (Bug#19479)
    
    * lisp/emacs-lisp/package.el (package-verify-checksums): New
    defcustom.
    (package-desc, package--ac-desc)
    (package--add-to-archive-contents, package-install-from-archive): New
    fields 'size' and 'checksums'.
    (package-desc-filename): New function.
    
    (bad-checksum, bad-size): New error types.
    (package-insecure-hash-algorithms): New constant.
    (package--verify-package-checksum)
    (package--verify-package-size): New function to verify that the
    checksum and size of a package corresponds to the checksum and size
    data in the "archive-contents" file on the package archive.
    (package--show-verify-checksum-error): New function to show
    details of an error on checksum verification.
    
    * lisp/emacs-lisp/package-x.el (package-upload-buffer-internal):
    Update to use above new fields 'size' and 'checksums'.
    
    * test/lisp/emacs-lisp/package-tests.el (package-test-refresh-contents)
    (package-test-install-single-from-archive)
    (package-test-list-filter-by-archive)
    (package-test-list-filter-by-status): Update tests.
    (with-install-using-checksum): New macro.
    (package-test-install-wrong-size-single)
    (package-test-install-wrong-size-tar): New tests for size checking.
    (package-test-install-with-checksum/single-valid)
    (package-test-install-with-checksum/single-invalid)
    (package-test-install-with-checksum/tar-valid)
    (package-test-install-with-checksum/tar-invalid): New tests for
    installing packages with checksums.
    (package-test-verification-text)
    (package-tests-valid-md5-checksum)
    (package-tests-valid-sha256-checksum)
    (package-tests-valid-sha512-checksum): New variables.
    (package-tests--run-verify-checksums-test): New macro.
    (package-test-verify-package-checksums-nil/ignore-invalid)
    (package-test-verify-package-checksums-allow-missing)
    (package-test-verify-package-checksums-allow-missing/missing)
    (package-test-verify-package-checksums-allow-missing/ignore-unsupported)
    (package-test-verify-package-checksums-t)
    (package-test-verify-package-checksums-t/invalid-fails)
    (package-test-verify-package-checksums-t/missing-fails)
    (package-test-verify-package-checksums-all)
    (package-test-verify-package-checksums-all/invalid-fails)
    (package-test-verify-package-checksums-all/missing-fails)
    (package-test-verify-package-checksums-all/no-supported-hash-fails)
    (package-test-verify-package-checksums-all/ignore-unsupported)
    (package-test-verify-package-size): New tests for the checksum
    support.
    
    * test/lisp/emacs-lisp/package-resources/archive-contents:
    * test/lisp/emacs-lisp/package-resources/checksum-invalid-1.0.el:
    * test/lisp/emacs-lisp/package-resources/checksum-valid-123.el:
    * test/lisp/emacs-lisp/package-resources/checksum-valid-tar-0.99.tar:
    * test/lisp/emacs-lisp/package-resources/checksum-valid-tar-0.99.tar:
    New test data files.
    
    * doc/emacs/package.texi (Package Installation): Document package
    checksum checking.
    * etc/NEWS: Announce it.
---
 doc/emacs/package.texi                             |  14 +-
 etc/NEWS                                           |  15 ++
 lisp/emacs-lisp/package-x.el                       |   4 +-
 lisp/emacs-lisp/package.el                         | 162 ++++++++++++++--
 .../emacs-lisp/package-resources/archive-contents  |  36 +++-
 .../package-resources/checksum-invalid-1.0.el      |  17 ++
 .../package-resources/checksum-invalid-tar-0.1.tar | Bin 0 -> 10240 bytes
 .../package-resources/checksum-valid-123.el        |  17 ++
 .../package-resources/checksum-valid-tar-0.99.tar  | Bin 0 -> 10240 bytes
 .../package-resources/wrong-size-single-1.0.el     |   1 +
 .../package-resources/wrong-size-tar-1.0.tar       | Bin 0 -> 10240 bytes
 test/lisp/emacs-lisp/package-tests.el              | 203 ++++++++++++++++++++-
 12 files changed, 448 insertions(+), 21 deletions(-)

diff --git a/doc/emacs/package.texi b/doc/emacs/package.texi
index 453d9eb..49ef20b 100644
--- a/doc/emacs/package.texi
+++ b/doc/emacs/package.texi
@@ -316,8 +316,20 @@ name of the package archive directory.  You can alter this 
list if you
 wish to use third party package archives---but do so at your own risk,
 and use only third parties that you think you can trust!
 
-@anchor{Package Signing}
+@anchor{Package Checksums}
 @cindex package security
+@cindex package checksums
+  The maintainers of package archives can add package checksums to
+their packages.  When downloading a package that has a checksum, it
+is used to verify that the correct version of that file has been
+downloaded.  By default, Emacs will verify these checksums
+automatically when available, and ignore it when not.  You can control
+this with the user option @code{package-verify-checksums}.  If you
+want to install only packages with a valid checksum, you can set this
+option to @code{t}.  This improves security, but requires that all
+package archives you use distribute checksums.
+
+@anchor{Package Signing}
 @cindex package signing
   The maintainers of package archives can increase the trust that you
 can have in their packages by @dfn{signing} them.  They generate a
diff --git a/etc/NEWS b/etc/NEWS
index fd6cdbe..4e2719d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -567,6 +567,21 @@ See the new user options 'package-name-column-width',
 'package-version-column-width', 'package-status-column-width', and
 'package-archive-column-width'.
 
+*** The package system now supports package checksums.
+This improves the security of the Emacs package system.  If the
+package archives you use implements package checksums, you will
+automatically benefit from this by default.
+
+The user option 'package-verify-checksums' controls how and when the
+package system will use checksums.  The default is 'allow-missing',
+which will check them when they are available yet allow installation
+if they are missing.
+
+For improved security, you might want to set this to 't' or
+'all'.  Make sure that the package archives you use support checksums
+before setting these values, or you will be unable to install
+packages.
+
 ** gdb-mi
 
 +++
diff --git a/lisp/emacs-lisp/package-x.el b/lisp/emacs-lisp/package-x.el
index c01b6ef..964cf07 100644
--- a/lisp/emacs-lisp/package-x.el
+++ b/lisp/emacs-lisp/package-x.el
@@ -219,7 +219,9 @@ if it exists."
          (let ((contents (or (package--archive-contents-from-url archive-url)
                              (package--archive-contents-from-file)))
                (new-desc (package-make-ac-desc
-                           split-version requires desc file-type extras)))
+                           split-version requires desc file-type extras
+                           ;; FIXME: Use better values than nil nil.
+                           nil nil)))
            (if (> (car contents) package-archive-version)
                (error "Unrecognized archive version %d" (car contents)))
            (let ((elt (assq pkg-name (cdr contents))))
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index a173fc0..79256b7 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -335,6 +335,31 @@ default directory."
   :risky t
   :version "26.1")
 
+(defcustom package-verify-checksums 'allow-missing
+  "Non-nil means to verify the checksum of a package before installing it.
+
+This can be one of:
+- t                Require a valid checksum; refuse to install
+                   package if the checksum is missing or invalid.
+                   Verify only one checksum.
+- `all'            Same as t, but verify all available (and supported)
+                   checksums.
+- `allow-missing'  Same as t if a checksum exists, but install a
+                   package even if there is no checksum.
+- nil              Ignore checksums.
+
+The package checksums are automatically fetched from package
+archives with the package data on `package-refresh-contents'.
+
+Note that setting this to nil is intended for debugging, and
+should normally not be used since it will decrease security."
+  :type '(choice (const nil :tag "Never")
+                 (const allow-missing :tag "Allow missing")
+                 (const t :tag "Require valid checksum")
+                 (const t :tag "Require valid checksum, and check all"))
+  :risky t
+  :version "28.1")
+
 (defcustom package-check-signature 'allow-unsigned
   "Non-nil means to check package signatures when installing.
 More specifically the value can be:
@@ -449,6 +474,8 @@ synchronously."
                                  requirements)))
                  (kind (plist-get rest-plist :kind))
                  (archive (plist-get rest-plist :archive))
+                 (checksums (plist-get rest-plist :checksums))
+                 (size (plist-get rest-plist :size))
                  (extras (let (alist)
                            (while rest-plist
                              (unless (memq (car rest-plist) '(:kind :archive))
@@ -486,6 +513,13 @@ Slots:
 
 `extras' Optional alist of additional keyword-value pairs.
 
+`size'  Size of the package in bytes.
+
+`checksums' Checksums for the package file.  Alist of ((ALGORITHM
+        . CHECKSUM)) where ALGORITHM is a symbol specifying a
+        `secure-hash' algorithm, and CHECKSUM is a string
+        containing the checksum.
+
 `signed' Flag to indicate that the package is signed by provider."
   name
   version
@@ -495,7 +529,9 @@ Slots:
   archive
   dir
   extras
-  signed)
+  signed
+  size
+  checksums)
 
 (defun package--from-builtin (bi-desc)
   "Create a `package-desc' object from BI-DESC.
@@ -558,6 +594,13 @@ Signal an error if the kind is none of the above."
     ('dir "")
     (kind (error "Unknown package kind: %s" kind))))
 
+(defun package-desc-filename (pkg-desc)
+  "Return file-name of package-desc object PKG-DESC.
+This is the concatenation of `package-desc-full-name' and
+`package-desc-suffix'."
+  (concat (package-desc-full-name pkg-desc)
+          (package-desc-suffix pkg-desc)))
+
 (defun package-desc--keywords (pkg-desc)
   "Return keywords of package-desc object PKG-DESC.
 These keywords come from the foo-pkg.el file, and in general
@@ -1334,6 +1377,93 @@ errors signaled by ERROR-FORM or by BODY).
                    url))
           (insert-file-contents-literally url)))))
 
+(define-error 'bad-checksum "Failed to verify checksum")
+
+(defun package--show-verify-checksum-error (pkg-desc details)
+  "Show error on failed checksum verification of PKG-DESC with DETAILS.
+Error is displayed in a new buffer named \"*Error*\"."
+  (with-output-to-temp-buffer "*Error*"
+    (with-current-buffer standard-output
+      (insert (format "Failed to verify checksum of package `%s':\n\n"
+                      (package-desc-name pkg-desc)))
+      (insert details))))
+
+(defconst package-insecure-hash-algorithms '(md5 sha1)
+  "List of hash algorithms that are not considered secure.")
+
+(defun package--verify-package-checksum (pkg-desc)
+  "Verify checksums of `package-desc' object PKG-DESC.
+This assumes that the we are in a buffer containing package.
+
+The value of `package-verify-checksums' decides what this
+function does:
+- t                Verify that there is at least one valid checksum.
+- `all'            Like t, but check all supported checksums.
+- `allow-missing'  Verify checksum if it exists, otherwise do
+                   nothing.
+- nil              Do nothing.
+
+Signal an error of type `bad-checksum' if the verification."
+  (cl-flet*
+      ((supported-hashes
+        (lambda ()
+          (or (seq-filter
+               (lambda (h)
+                 (and (memql (car h) (secure-hash-algorithms))
+                      (not (memql (car h) package-insecure-hash-algorithms))))
+               (package-desc-checksums pkg-desc))
+              ;; Failed; signal error.
+              (package--show-verify-checksum-error
+               pkg-desc
+               (concat
+                (if (package-desc-checksums pkg-desc)
+                    (concat
+                     "No supported checksums found\n\n"
+                     (format-message "Package archive had: %s\n"
+                                     (package-desc-checksums pkg-desc))
+                     (format-message "Emacs supports: %s\n"
+                                     (secure-hash-algorithms)))
+                  "Package archive had no checksums for this package\n")))
+              (signal 'bad-checksum "no supported checksums found"))))
+       (do-check
+        (lambda (&optional all)
+          (dolist (hash (seq-take (supported-hashes)
+                                  (if all most-positive-fixnum 1)))
+            (let* ((algorithm (car hash))
+                   (expected (cdr hash))
+                   (actual (secure-hash algorithm (current-buffer))))
+              (if (equal expected actual) t
+                ;; Failed; signal error.
+                (package--show-verify-checksum-error
+                 pkg-desc
+                 (concat
+                  (format-message "\nChecksum mismatch (%s)\n\n" algorithm)
+                  (format-message "Expected: %s\n" expected)
+                  (format-message "Result: %s\n" actual)))
+                (signal 'bad-checksum (list "checksum mismatch" expected 
actual))))))))
+    (pcase package-verify-checksums
+      ('nil nil)
+      ('allow-missing (when (package-desc-checksums pkg-desc) (do-check)))
+      ('t (do-check))
+      ('all (do-check 'all))
+      (_ (user-error "Value of `package-verify-checksums' is invalid: `%s'"
+                     package-verify-checksums)))))
+
+(define-error 'bad-size "Package size mismatch")
+
+(defun package--verify-package-size (pkg-desc)
+  "Verify package size of `package-desc' object PKG-DESC.
+This assumes that the we are in a buffer containing package."
+  (when-let ((expected (package-desc-size pkg-desc))
+             (actual (string-bytes (buffer-string))))
+    (unless (equal expected actual)
+      (with-output-to-temp-buffer "*Error*"
+        (with-current-buffer standard-output
+          (insert (format "Mismatch in package size for `%s':\n"
+                          (package-desc-name pkg-desc)))
+          (insert (format "Expected %s bytes, but received %s" expected 
actual))))
+      (signal 'bad-size (list "size mismatch" expected actual)))))
+
 (define-error 'bad-signature "Failed to verify signature")
 
 (defun package--check-signature-content (content string &optional sig-file)
@@ -1461,14 +1591,19 @@ the table."
                 (version-list-< table-version version))
         (puthash name version package--compatibility-table)))))
 
-;; Package descriptor objects used inside the "archive-contents" file.
-;; Changing this defstruct implies changing the format of the
-;; "archive-contents" files.
 (cl-defstruct (package--ac-desc
-               (:constructor package-make-ac-desc (version reqs summary kind 
extras))
+               (:constructor
+                package-make-ac-desc (version reqs summary kind extras size 
checksums))
                (:copier nil)
                (:type vector))
-  version reqs summary kind extras)
+  "Package descriptor object used inside the \"archive-contents\" file.
+Changing this defstruct implies changing the format of the
+\"archive-contents\" files.
+
+This is mainly used in `package--add-to-archive-contents' to make
+the code that parses the \"archive-contents\" file more
+readable."
+  version reqs summary kind extras size checksums)
 
 (defun package--append-to-alist (pkg-desc alist)
   "Append an entry for PKG-DESC to the start of ALIST and return it.
@@ -1506,10 +1641,14 @@ Also, add the originating archive to the `package-desc' 
structure."
            :summary (package--ac-desc-summary (cdr package))
            :kind (package--ac-desc-kind (cdr package))
            :archive archive
+           ;; Older "archive-contents" files might not have the
+           ;; below elements.
            :extras (and (> (length (cdr package)) 4)
-                        ;; Older archive-contents files have only 4
-                        ;; elements here.
-                        (package--ac-desc-extras (cdr package)))))
+                        (package--ac-desc-extras (cdr package)))
+           :size (and (> (length (cdr package)) 5)
+                      (package--ac-desc-size (cdr package)))
+           :checksums (and (> (length (cdr package)) 6)
+                           (package--ac-desc-checksums (cdr package)))))
          (pinned-to-archive (assoc name package-pinned-packages)))
     ;; Skip entirely if pinned to another archive.
     (when (not (and pinned-to-archive
@@ -1979,9 +2118,10 @@ if all the in-between dependencies are also in 
PACKAGE-LIST."
   (when (eq (package-desc-kind pkg-desc) 'dir)
     (error "Can't install directory package from archive"))
   (let* ((location (package-archive-base pkg-desc))
-         (file (concat (package-desc-full-name pkg-desc)
-                       (package-desc-suffix pkg-desc))))
+         (file (package-desc-filename pkg-desc)))
     (package--with-response-buffer location :file file
+      (package--verify-package-size pkg-desc)
+      (package--verify-package-checksum pkg-desc)
       (if (or (not (package-check-signature))
               (member (package-desc-archive pkg-desc)
                       package-unsigned-archives))
diff --git a/test/lisp/emacs-lisp/package-resources/archive-contents 
b/test/lisp/emacs-lisp/package-resources/archive-contents
index e2f9230..724972d 100644
--- a/test/lisp/emacs-lisp/package-resources/archive-contents
+++ b/test/lisp/emacs-lisp/package-resources/archive-contents
@@ -14,4 +14,38 @@
  (multi-file .
              [(0 2 3)
               nil "Example of a multi-file tar package" tar
-              ((:url . "http://puddles.li";))]))
+              ((:url . "http://puddles.li";))])
+ (checksum-valid  .
+                  [(123)
+                    nil "A single-file package with a valid checksum." single
+                    nil
+                    343
+                    ((sha512 . 
"a889917427569cc6817db5db08a88390d44ec010acdf6810c2dfaba04b9a03f00315378c3f03d5f4d531833028ad61db54c4c56106662585da6a0dde602f5c0d"))])
+ (checksum-valid-tar .
+                     [(0 99)
+                     nil "A multi-file package with a valid checksum." tar
+                     nil
+                     10240
+                     ((sha512 . 
"2be7c37a16db32a2b08fc917ed5f4241814e2665bda1bd15328c2e5a842e45b81f6f31274697248ffaabf8010796685acb3342c5920af53ddd1e75d7fd764bd1"))])
+ (checksum-invalid .
+                   [(1 0)
+                    nil "A single-file package with an invalid checksum." 
single
+                    nil
+                    365
+                    ((sha512 . "not-a-valid-checksum"))])
+ (checksum-invalid-tar .
+                       [(0 1)
+                        nil "A multi-file package with an invalid checksum." 
tar
+                        nil
+                        10240
+                        ((sha512 . "not-a-valid-checksum"))])
+ (wrong-size-single .
+                    [(1 0)
+                     nil "A single-file package with an invalid size." single
+                     nil
+                     1])
+ (wrong-size-tar .
+                 [(1 0)
+                  nil "A multi-file package with an invalid size." tar
+                  nil
+                  1]))
diff --git a/test/lisp/emacs-lisp/package-resources/checksum-invalid-1.0.el 
b/test/lisp/emacs-lisp/package-resources/checksum-invalid-1.0.el
new file mode 100644
index 0000000..c3d4790
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/checksum-invalid-1.0.el
@@ -0,0 +1,17 @@
+;;; checksum-invalid.el --- A package with an invalid checksum in 
archive-contents
+
+;; Version: 1.0
+
+;;; Commentary:
+
+;; This package has an invalid checksum in archive-contents and is
+;; just used to verify that package.el refuses to install.
+
+;;; Code:
+
+(defun p-equal-to-np-p ()
+  (error "FIXME"))
+
+(provide 'checksum-invalid)
+
+;;; checksum-invalid.el ends here
diff --git 
a/test/lisp/emacs-lisp/package-resources/checksum-invalid-tar-0.1.tar 
b/test/lisp/emacs-lisp/package-resources/checksum-invalid-tar-0.1.tar
new file mode 100644
index 0000000..8adc7f5
Binary files /dev/null and 
b/test/lisp/emacs-lisp/package-resources/checksum-invalid-tar-0.1.tar differ
diff --git a/test/lisp/emacs-lisp/package-resources/checksum-valid-123.el 
b/test/lisp/emacs-lisp/package-resources/checksum-valid-123.el
new file mode 100644
index 0000000..8cfc387
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/checksum-valid-123.el
@@ -0,0 +1,17 @@
+;;; checksum-valid.el --- A package with an valid checksum in archive-contents
+
+;; Version: 123
+
+;;; Commentary:
+
+;; This package has an valid checksum in archive-contents and is
+;; used to verify that package.el installs it.
+
+;;; Code:
+
+(defun p-equal-to-np-p ()
+  (error "FIXME"))
+
+(provide 'checksum-valid)
+
+;;; checksum-valid.el ends here
diff --git a/test/lisp/emacs-lisp/package-resources/checksum-valid-tar-0.99.tar 
b/test/lisp/emacs-lisp/package-resources/checksum-valid-tar-0.99.tar
new file mode 100644
index 0000000..e468754
Binary files /dev/null and 
b/test/lisp/emacs-lisp/package-resources/checksum-valid-tar-0.99.tar differ
diff --git a/test/lisp/emacs-lisp/package-resources/wrong-size-single-1.0.el 
b/test/lisp/emacs-lisp/package-resources/wrong-size-single-1.0.el
new file mode 100644
index 0000000..a4e3daf
--- /dev/null
+++ b/test/lisp/emacs-lisp/package-resources/wrong-size-single-1.0.el
@@ -0,0 +1 @@
+;; This file just has the wrong size (i.e. not 1 as specified).
diff --git a/test/lisp/emacs-lisp/package-resources/wrong-size-tar-1.0.tar 
b/test/lisp/emacs-lisp/package-resources/wrong-size-tar-1.0.tar
new file mode 100644
index 0000000..61d47c6
Binary files /dev/null and 
b/test/lisp/emacs-lisp/package-resources/wrong-size-tar-1.0.tar differ
diff --git a/test/lisp/emacs-lisp/package-tests.el 
b/test/lisp/emacs-lisp/package-tests.el
index dd8ae39..f4b7528 100644
--- a/test/lisp/emacs-lisp/package-tests.el
+++ b/test/lisp/emacs-lisp/package-tests.el
@@ -43,6 +43,9 @@
 
 (setq package-menu-async nil)
 
+;; Silence byte-compiler.
+(defvar epg-config--program-alist)
+
 (defvar package-test-user-dir nil
   "Directory to use for installing packages during testing.")
 
@@ -298,14 +301,33 @@ Must called from within a `tar-mode' buffer."
   (with-package-test ()
     (package-initialize)
     (package-refresh-contents)
-    (should (eq 4 (length package-archive-contents)))))
+    (should (eq 10 (length package-archive-contents)))))
 
 (ert-deftest package-test-install-single-from-archive ()
   "Install a single package from a package archive."
   (with-package-test ()
     (package-initialize)
     (package-refresh-contents)
-    (package-install 'simple-single)))
+    (package-install 'simple-single)
+    (should (package-installed-p 'simple-single))))
+
+(ert-deftest package-test-install-wrong-size-single ()
+  "Install a tar package with invalid size."
+  (should-error
+   (with-package-test ()
+     (package-initialize)
+     (package-refresh-contents)
+     (package-install 'wrong-size-single))
+   :type 'bad-size))
+
+(ert-deftest package-test-install-wrong-size-tar ()
+  "Install a tar package with invalid size."
+  (should-error
+   (with-package-test ()
+     (package-initialize)
+     (package-refresh-contents)
+     (package-install 'wrong-size-tar))
+   :type 'bad-size))
 
 (ert-deftest package-test-install-prioritized ()
   "Install a lower version from a higher-prioritized archive."
@@ -384,8 +406,8 @@ Must called from within a `tar-mode' buffer."
     ;;       the testing environment currently only has one.
     (package-menu-filter-by-archive "gnu")
     (goto-char (point-min))
-    (should (looking-at "^\\s-+multi-file"))
-    (should (= (count-lines (point-min) (point-max)) 4))
+    (should (looking-at "^\\s-+checksum-invalid"))
+    (should (= (count-lines (point-min) (point-max)) 10))
     (should-error (package-menu-filter-by-archive "non-existent archive"))))
 
 (ert-deftest package-test-list-filter-by-keyword ()
@@ -411,7 +433,7 @@ Must called from within a `tar-mode' buffer."
     (package-menu-filter-by-status "available")
     (goto-char (point-min))
     (should (re-search-forward "^\\s-+multi-file" nil t))
-    (should (= (count-lines (point-min) (point-max)) 4))
+    (should (= (count-lines (point-min) (point-max)) 10))
     ;; No installed packages in default environment.
     (should-error (package-menu-filter-by-status "installed"))))
 
@@ -668,6 +690,169 @@ Must called from within a `tar-mode' buffer."
                "Status: Installed in ['`‘]signed-good-1.0/['’]."
                nil t))))))
 
+
+;;; Tests for package checksum verification.
+
+(defmacro with-install-using-checksum (ok fail package)
+  "Test installing PACKAGE while setting `package-verify-checksums'."
+  (declare (indent 2))
+  `(progn
+     (dolist (opt ,ok)
+       (let ((package-verify-checksums opt))
+         (with-package-test ()
+           (package-initialize)
+           (package-refresh-contents)
+           (package-install ,package)
+           (package-installed-p ,package))))
+     (dolist (opt ,fail)
+       (let ((package-verify-checksums opt))
+         (should-error
+          (with-package-test ()
+            (package-initialize)
+            (package-refresh-contents)
+            (package-install ,package))
+          :type 'bad-checksum)))))
+
+(ert-deftest package-test-install-with-checksum/single-valid ()
+  "Install a single package with valid checksum."
+  (with-install-using-checksum '(nil allow-missing t all) '() 'checksum-valid))
+
+(ert-deftest package-test-install-with-checksum/single-invalid ()
+  "Install a tar package with invalid checksum."
+  (with-install-using-checksum '(nil) '(allow-missing t all) 
'checksum-invalid))
+
+(ert-deftest package-test-install-with-checksum/tar-valid ()
+  "Install a tar package with valid checksum."
+  (with-install-using-checksum '(nil allow-missing t all) '() 
'checksum-valid-tar))
+
+(ert-deftest package-test-install-with-checksum/tar-invalid ()
+  "Install a tar package with invalid checksum."
+  (with-install-using-checksum '(nil) '(allow-missing t all) 
'checksum-invalid-tar))
+
+(defconst package-test-verification-text
+  "Example text for testing checksum verification.")
+(defconst package-tests-valid-md5-checksum
+  ;; (secure-hash 'md5 package-test-verification-text)
+  "abe6375809e532f081b808b3aa052dfb")
+(defconst package-tests-valid-sha256-checksum
+  ;; (secure-hash 'sha256 package-test-verification-text)
+  "6875aa4523e45ddef627b4edf1296f1d7dd0c22ddd6a6584f0228215d25eefcd")
+(defconst package-tests-valid-sha512-checksum
+  ;; (secure-hash 'sha512 package-test-verification-text)
+  (concat "bdc631f9e675b1ea34570f0a4bb44568dc5cecac905eea737f5f451bc52fd0c6"
+          "81b0d8b3dc2a942b9950fbe9096ebdf517668245c9b5a7bbdea8487a8f9cdce6"))
+
+(defmacro package-tests--run-verify-checksums-test (verify-checksums checksums)
+  "Run a test for `package-verify-checksums'."
+  (declare (indent 1))
+  `(with-temp-buffer
+     (insert package-test-verification-text)
+     (let ((package-verify-checksums ,verify-checksums)
+           (pkg (package-desc-create :name 'foobar
+                              :version '(1 0)
+                              :summary "Just a package with checksum."
+                              :kind 'single
+                              :checksums ,checksums)))
+       (package--verify-package-checksum pkg))))
+
+(ert-deftest package-test-verify-package-checksums-nil/ignore-invalid ()
+  "Ignore all checksums even when invalid."
+  (package-tests--run-verify-checksums-test nil
+    '((sha512 . "invalid")
+      (invalid . "invalid"))))
+
+(ert-deftest package-test-verify-package-checksums-nil/ignore-empty ()
+  "Ignore all checksums even when empty."
+  (package-tests--run-verify-checksums-test nil
+    nil))
+
+(ert-deftest package-test-verify-package-checksums-allow-missing ()
+  "Verify checksums (allow-missing) -- verify if available."
+  (package-tests--run-verify-checksums-test 'allow-missing
+    `((sha512 . ,package-tests-valid-sha512-checksum))))
+
+(ert-deftest package-test-verify-package-checksums-allow-missing/missing ()
+  "Verify checksums (allow-missing) -- allow missing."
+  (package-tests--run-verify-checksums-test 'allow-missing
+    nil))
+
+(ert-deftest 
package-test-verify-package-checksums-allow-missing/ignore-unsupported ()
+  "Verify checksums (t) -- ignore unsupported algorithm."
+  (package-tests--run-verify-checksums-test 'allow-missing
+    `((ignore . "not supported")
+      (sha512 . ,package-tests-valid-sha512-checksum))))
+
+(ert-deftest package-test-verify-package-checksums-t ()
+  "Verify checksums (t) -- succeed when valid."
+  (package-tests--run-verify-checksums-test t
+    `((sha512 . ,package-tests-valid-sha512-checksum))))
+
+(ert-deftest package-test-verify-package-checksums-t/invalid-fails ()
+  "Verify checksums (t) -- fail on invalid."
+  (should-error
+   (package-tests--run-verify-checksums-test t
+     '((sha512 . "invalid")))
+   :type 'bad-checksum))
+
+(ert-deftest package-test-verify-package-checksums-t/missing-fails ()
+  "Verify checksums (t) -- fail on missing."
+  (should-error
+   (package-tests--run-verify-checksums-test t
+     nil)
+   :type 'bad-checksum))
+
+(ert-deftest package-test-verify-package-checksums-t/ignore-unsupported ()
+  "Verify checksums (t) -- ignore unsupported algorithm."
+  (package-tests--run-verify-checksums-test t
+    `((ignore . "not supported")
+      (sha512 . ,package-tests-valid-sha512-checksum))))
+
+(ert-deftest package-test-verify-package-checksums-all ()
+  "Verify checksums (all) -- succeed on valid."
+  (package-tests--run-verify-checksums-test 'all
+    `((md5    . ,package-tests-valid-md5-checksum)
+      (sha256 . ,package-tests-valid-sha256-checksum)
+      (sha512 . ,package-tests-valid-sha512-checksum))))
+
+(ert-deftest package-test-verify-package-checksums-all/invalid-fails ()
+  "Verify checksums (all) -- fail if one checksum is invalid."
+  (should-error
+   (package-tests--run-verify-checksums-test 'all
+     `((md5    . ,package-tests-valid-md5-checksum)
+       (sha256 . "invalid")
+       (sha512 . ,package-tests-valid-sha512-checksum)))
+   :type 'bad-checksum))
+
+(ert-deftest package-test-verify-package-checksums-all/missing-fails ()
+  "Verify checksums (all) -- fail on missing checksums."
+  (should-error
+   (package-tests--run-verify-checksums-test 'all
+     nil)
+   :type 'bad-checksum))
+
+(ert-deftest package-test-verify-package-checksums-all/no-supported-hash-fails 
()
+  "Verify checksums (all) -- fail if we have no supported hash."
+  (should-error
+   (package-tests--run-verify-checksums-test 'all
+     '((unsupported . "invalid")))
+   :type 'bad-checksum))
+
+(ert-deftest package-test-verify-package-checksums-all/ignore-unsupported ()
+  "Verify checksums (all) -- succed if one hash algorithm is unsupported.
+If the rest succeed, just ignore the unsupported one."
+  (package-tests--run-verify-checksums-test 'all
+    `((md5    . ,package-tests-valid-md5-checksum)
+      (sha256 . ,package-tests-valid-sha256-checksum)
+      (sha512 . ,package-tests-valid-sha512-checksum)
+      (ignore . "not supported"))))
+
+(ert-deftest package-test-verify-package-size ()
+  (with-temp-buffer
+    (let ((pkg-desc (package-desc-create :size 6)))
+      (insert "123456")
+      (package--verify-package-size pkg-desc)
+      (insert "7")
+      (should-error (package--verify-package-size pkg-desc)))))
 
 
 ;;; Tests for package-x features.
@@ -681,7 +866,9 @@ Must called from within a `tar-mode' buffer."
                               'single
                               '((:authors ("J. R. Hacker" . "jrh@example.com"))
                                 (:maintainer "J. R. Hacker" . 
"jrh@example.com")
-                                (:url . "http://doodles.au";))))
+                                (:url . "http://doodles.au";))
+                              nil
+                              nil))
   "Expected contents of the archive entry from the \"simple-single\" package.")
 
 (defvar package-x-test--single-archive-entry-1-4
@@ -690,7 +877,9 @@ Must called from within a `tar-mode' buffer."
                               "A single-file package with no dependencies"
                               'single
                               '((:authors ("J. R. Hacker" . "jrh@example.com"))
-                                (:maintainer "J. R. Hacker" . 
"jrh@example.com"))))
+                                (:maintainer "J. R. Hacker" . 
"jrh@example.com"))
+                              nil
+                              nil))
   "Expected contents of the archive entry from the updated \"simple-single\" 
package.")
 
 (ert-deftest package-x-test-upload-buffer ()



reply via email to

[Prev in Thread] Current Thread [Next in Thread]