aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaiki Ueno2013-10-03 16:11:27 +0900
committerDaiki Ueno2013-10-03 16:11:27 +0900
commitacbadd0046cb1643eeaf8595ede1a69cc25d3158 (patch)
treef1b7ffc2d37226d1f20d53b879e008b400f545ae
parent0a858ebfc57a072ae8ab65f509d8a4901a2ec073 (diff)
downloademacs-acbadd0046cb1643eeaf8595ede1a69cc25d3158.tar.gz
emacs-acbadd0046cb1643eeaf8595ede1a69cc25d3158.zip
Add support for package signature checking.
* lisp/emacs-lisp/package.el (url-http-file-exists-p) (epg-make-context, epg-context-set-home-directory) (epg-verify-string, epg-context-result-for) (epg-signature-status, epg-signature-to-string) (epg-check-configuration, epg-configuration) (epg-import-keys-from-file): Declare. (package-check-signature): New user option. (package-unsigned-archives): New user option. (package-desc): Add `signed' field. (package-load-descriptor): Set `signed' field if .signed file exists. (package--archive-file-exists-p): New function. (package--check-signature): New function. (package-install-from-archive): Check package signature. (package--download-one-archive): Check archive signature. (package-delete): Remove .signed file. (package-import-keyring): New command. (package-refresh-contents): Import default keyring. (package-desc-status): Add "unsigned" status. (describe-package-1, package-menu--print-info) (package-menu-mark-delete, package-menu--find-upgrades) (package-menu--status-predicate): Support "unsigned" status. * test/automated/data/package/signed/archive-contents: * test/automated/data/package/signed/archive-contents.sig: * test/automated/data/package/signed/signed-good-1.0.el: * test/automated/data/package/signed/signed-good-1.0.el.sig: * test/automated/data/package/signed/signed-bad-1.0.el: * test/automated/data/package/signed/signed-bad-1.0.el.sig: * test/automated/data/package/key.pub: * test/automated/data/package/key.sec: New files. * test/automated/package-test.el (package-test-update-listing) (package-test-update-archives, package-test-describe-package): Adjust to package.el change. (package-test-signed): New test.
-rw-r--r--lisp/ChangeLog25
-rw-r--r--lisp/emacs-lisp/package.el196
-rw-r--r--test/ChangeLog16
-rw-r--r--test/automated/data/package/key.pub18
-rw-r--r--test/automated/data/package/key.sec33
-rw-r--r--test/automated/data/package/signed/archive-contents7
-rw-r--r--test/automated/data/package/signed/archive-contents.sigbin0 -> 287 bytes
-rw-r--r--test/automated/data/package/signed/signed-bad-1.0.el33
-rw-r--r--test/automated/data/package/signed/signed-bad-1.0.el.sigbin0 -> 287 bytes
-rw-r--r--test/automated/data/package/signed/signed-good-1.0.el33
-rw-r--r--test/automated/data/package/signed/signed-good-1.0.el.sigbin0 -> 287 bytes
-rw-r--r--test/automated/package-test.el39
12 files changed, 376 insertions, 24 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 90158b85b4d..936f2b1f8e5 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,28 @@
12013-10-03 Daiki Ueno <ueno@gnu.org>
2
3 Add support for package signature checking.
4 * emacs-lisp/package.el (url-http-file-exists-p)
5 (epg-make-context, epg-context-set-home-directory)
6 (epg-verify-string, epg-context-result-for)
7 (epg-signature-status, epg-signature-to-string)
8 (epg-check-configuration, epg-configuration)
9 (epg-import-keys-from-file): Declare.
10 (package-check-signature): New user option.
11 (package-unsigned-archives): New user option.
12 (package-desc): Add `signed' field.
13 (package-load-descriptor): Set `signed' field if .signed file exists.
14 (package--archive-file-exists-p): New function.
15 (package--check-signature): New function.
16 (package-install-from-archive): Check package signature.
17 (package--download-one-archive): Check archive signature.
18 (package-delete): Remove .signed file.
19 (package-import-keyring): New command.
20 (package-refresh-contents): Import default keyring.
21 (package-desc-status): Add "unsigned" status.
22 (describe-package-1, package-menu--print-info)
23 (package-menu-mark-delete, package-menu--find-upgrades)
24 (package-menu--status-predicate): Support "unsigned" status.
25
12013-10-03 Stefan Monnier <monnier@iro.umontreal.ca> 262013-10-03 Stefan Monnier <monnier@iro.umontreal.ca>
2 27
3 * emacs-lisp/cconv.el (cconv-convert, cconv-analyse-form): Adjust for 28 * emacs-lisp/cconv.el (cconv-convert, cconv-analyse-form): Adjust for
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index ec01d16329f..cdf210498ce 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -206,6 +206,7 @@ If VERSION is nil, the package is not loaded (it is \"disabled\")."
206(defvar Info-directory-list) 206(defvar Info-directory-list)
207(declare-function info-initialize "info" ()) 207(declare-function info-initialize "info" ())
208(declare-function url-http-parse-response "url-http" ()) 208(declare-function url-http-parse-response "url-http" ())
209(declare-function url-http-file-exists-p "url-http" (url))
209(declare-function lm-header "lisp-mnt" (header)) 210(declare-function lm-header "lisp-mnt" (header))
210(declare-function lm-commentary "lisp-mnt" (&optional file)) 211(declare-function lm-commentary "lisp-mnt" (&optional file))
211(defvar url-http-end-of-headers) 212(defvar url-http-end-of-headers)
@@ -285,6 +286,22 @@ contrast, `package-user-dir' contains packages for personal use."
285 :group 'package 286 :group 'package
286 :version "24.1") 287 :version "24.1")
287 288
289(defcustom package-check-signature 'allow-unsigned
290 "Whether to check package signatures when installing."
291 :type '(choice (const nil :tag "Never")
292 (const allow-unsigned :tag "Allow unsigned")
293 (const t :tag "Check always"))
294 :risky t
295 :group 'package
296 :version "24.1")
297
298(defcustom package-unsigned-archives nil
299 "A list of archives which do not use package signature."
300 :type '(repeat (string :tag "Archive name"))
301 :risky t
302 :group 'package
303 :version "24.1")
304
288(defvar package--default-summary "No description available.") 305(defvar package--default-summary "No description available.")
289 306
290(cl-defstruct (package-desc 307(cl-defstruct (package-desc
@@ -340,7 +357,9 @@ Slots:
340`dir' The directory where the package is installed (if installed), 357`dir' The directory where the package is installed (if installed),
341 `builtin' if it is built-in, or nil otherwise. 358 `builtin' if it is built-in, or nil otherwise.
342 359
343`extras' Optional alist of additional keyword-value pairs." 360`extras' Optional alist of additional keyword-value pairs.
361
362`signed' Flag to indicate that the package is signed by provider."
344 name 363 name
345 version 364 version
346 (summary package--default-summary) 365 (summary package--default-summary)
@@ -348,7 +367,8 @@ Slots:
348 kind 367 kind
349 archive 368 archive
350 dir 369 dir
351 extras) 370 extras
371 signed)
352 372
353;; Pseudo fields. 373;; Pseudo fields.
354(defun package-desc-full-name (pkg-desc) 374(defun package-desc-full-name (pkg-desc)
@@ -428,7 +448,8 @@ This is, approximately, the inverse of `version-to-list'.
428(defun package-load-descriptor (pkg-dir) 448(defun package-load-descriptor (pkg-dir)
429 "Load the description file in directory PKG-DIR." 449 "Load the description file in directory PKG-DIR."
430 (let ((pkg-file (expand-file-name (package--description-file pkg-dir) 450 (let ((pkg-file (expand-file-name (package--description-file pkg-dir)
431 pkg-dir))) 451 pkg-dir))
452 (signed-file (concat pkg-dir ".signed")))
432 (when (file-exists-p pkg-file) 453 (when (file-exists-p pkg-file)
433 (with-temp-buffer 454 (with-temp-buffer
434 (insert-file-contents pkg-file) 455 (insert-file-contents pkg-file)
@@ -436,6 +457,8 @@ This is, approximately, the inverse of `version-to-list'.
436 (let ((pkg-desc (package-process-define-package 457 (let ((pkg-desc (package-process-define-package
437 (read (current-buffer)) pkg-file))) 458 (read (current-buffer)) pkg-file)))
438 (setf (package-desc-dir pkg-desc) pkg-dir) 459 (setf (package-desc-dir pkg-desc) pkg-dir)
460 (if (file-exists-p signed-file)
461 (setf (package-desc-signed pkg-desc) t))
439 pkg-desc))))) 462 pkg-desc)))))
440 463
441(defun package-load-all-descriptors () 464(defun package-load-all-descriptors ()
@@ -766,13 +789,87 @@ It will move point to somewhere in the headers."
766 (error "Error during download request:%s" 789 (error "Error during download request:%s"
767 (buffer-substring-no-properties (point) (line-end-position)))))) 790 (buffer-substring-no-properties (point) (line-end-position))))))
768 791
792(defun package--archive-file-exists-p (location file)
793 (let ((http (string-match "\\`https?:" location)))
794 (if http
795 (progn
796 (require 'url-http)
797 (url-http-file-exists-p (concat location file)))
798 (file-exists-p (expand-file-name file location)))))
799
800(declare-function epg-make-context "epg"
801 (&optional protocol armor textmode include-certs
802 cipher-algorithm
803 digest-algorithm
804 compress-algorithm))
805(declare-function epg-context-set-home-directory "epg" (context directory))
806(declare-function epg-verify-string "epg" (context signature
807 &optional signed-text))
808(declare-function epg-context-result-for "epg" (context name))
809(declare-function epg-signature-status "epg" (signature))
810(declare-function epg-signature-to-string "epg" (signature))
811
812(defun package--check-signature (location file)
813 "Check signature of the current buffer.
814GnuPG keyring is located under \"gnupg\" in `package-user-dir'."
815 (let ((context (epg-make-context 'OpenPGP))
816 (homedir (expand-file-name "gnupg" package-user-dir))
817 (sig-file (concat file ".sig"))
818 sig-content
819 good-signatures)
820 (condition-case-unless-debug error
821 (setq sig-content (package--with-work-buffer location sig-file
822 (buffer-string)))
823 (error "Failed to download %s: %S" sig-file (cdr error)))
824 (epg-context-set-home-directory context homedir)
825 (epg-verify-string context sig-content (buffer-string))
826 ;; The .sig file may contain multiple signatures. Success if one
827 ;; of the signatures is good.
828 (setq good-signatures
829 (delq nil (mapcar (lambda (sig)
830 (if (eq (epg-signature-status sig) 'good)
831 sig))
832 (epg-context-result-for context 'verify))))
833 (if (null good-signatures)
834 (error "Failed to verify signature %s: %S"
835 sig-file
836 (mapcar #'epg-signature-to-string
837 (epg-context-result-for context 'verify)))
838 good-signatures)))
839
769(defun package-install-from-archive (pkg-desc) 840(defun package-install-from-archive (pkg-desc)
770 "Download and install a tar package." 841 "Download and install a tar package."
771 (let ((location (package-archive-base pkg-desc)) 842 (let* ((location (package-archive-base pkg-desc))
772 (file (concat (package-desc-full-name pkg-desc) 843 (file (concat (package-desc-full-name pkg-desc)
773 (package-desc-suffix pkg-desc)))) 844 (package-desc-suffix pkg-desc)))
845 (sig-file (concat file ".sig"))
846 good-signatures pkg-descs)
774 (package--with-work-buffer location file 847 (package--with-work-buffer location file
775 (package-unpack pkg-desc)))) 848 (if (and package-check-signature
849 (not (member (package-desc-archive pkg-desc)
850 package-unsigned-archives)))
851 (if (package--archive-file-exists-p location sig-file)
852 (setq good-signatures (package--check-signature location file))
853 (unless (eq package-check-signature 'allow-unsigned)
854 (error "Unsigned package: `%s'"
855 (package-desc-name pkg-desc)))))
856 (package-unpack pkg-desc))
857 ;; Here the package has been installed successfully, mark it as
858 ;; signed if appropriate.
859 (when good-signatures
860 ;; Write out good signatures into NAME-VERSION.signed file.
861 (write-region (mapconcat #'epg-signature-to-string good-signatures "\n")
862 nil
863 (expand-file-name
864 (concat (package-desc-full-name pkg-desc)
865 ".signed")
866 package-user-dir))
867 ;; Update the old pkg-desc which will be shown on the description buffer.
868 (setf (package-desc-signed pkg-desc) t)
869 ;; Update the new (activated) pkg-desc as well.
870 (setq pkg-descs (cdr (assq (package-desc-name pkg-desc) package-alist)))
871 (if pkg-descs
872 (setf (package-desc-signed (car pkg-descs)) t)))))
776 873
777(defvar package--initialized nil) 874(defvar package--initialized nil)
778 875
@@ -1104,6 +1201,10 @@ The file can either be a tar file or an Emacs Lisp file."
1104 (error "Package `%s' is a system package, not deleting" 1201 (error "Package `%s' is a system package, not deleting"
1105 (package-desc-full-name pkg-desc)) 1202 (package-desc-full-name pkg-desc))
1106 (delete-directory dir t t) 1203 (delete-directory dir t t)
1204 ;; Remove NAME-VERSION.signed file.
1205 (let ((signed-file (concat dir ".signed")))
1206 (if (file-exists-p signed-file)
1207 (delete-file signed-file)))
1107 ;; Update package-alist. 1208 ;; Update package-alist.
1108 (let* ((name (package-desc-name pkg-desc))) 1209 (let* ((name (package-desc-name pkg-desc)))
1109 (delete pkg-desc (assq name package-alist))) 1210 (delete pkg-desc (assq name package-alist)))
@@ -1118,16 +1219,50 @@ The file can either be a tar file or an Emacs Lisp file."
1118ARCHIVE should be a cons cell of the form (NAME . LOCATION), 1219ARCHIVE should be a cons cell of the form (NAME . LOCATION),
1119similar to an entry in `package-alist'. Save the cached copy to 1220similar to an entry in `package-alist'. Save the cached copy to
1120\"archives/NAME/archive-contents\" in `package-user-dir'." 1221\"archives/NAME/archive-contents\" in `package-user-dir'."
1121 (let* ((dir (expand-file-name (format "archives/%s" (car archive)) 1222 (let ((dir (expand-file-name (format "archives/%s" (car archive))
1122 package-user-dir))) 1223 package-user-dir))
1224 (sig-file (concat file ".sig"))
1225 good-signatures)
1123 (package--with-work-buffer (cdr archive) file 1226 (package--with-work-buffer (cdr archive) file
1227 ;; Check signature of archive-contents, if desired.
1228 (if (and package-check-signature
1229 (not (member archive package-unsigned-archives)))
1230 (if (package--archive-file-exists-p (cdr archive) sig-file)
1231 (setq good-signatures (package--check-signature (cdr archive)
1232 file))
1233 (unless (eq package-check-signature 'allow-unsigned)
1234 (error "Unsigned archive `%s'"
1235 (car archive)))))
1124 ;; Read the retrieved buffer to make sure it is valid (e.g. it 1236 ;; Read the retrieved buffer to make sure it is valid (e.g. it
1125 ;; may fetch a URL redirect page). 1237 ;; may fetch a URL redirect page).
1126 (when (listp (read buffer)) 1238 (when (listp (read buffer))
1127 (make-directory dir t) 1239 (make-directory dir t)
1128 (setq buffer-file-name (expand-file-name file dir)) 1240 (setq buffer-file-name (expand-file-name file dir))
1129 (let ((version-control 'never)) 1241 (let ((version-control 'never))
1130 (save-buffer)))))) 1242 (save-buffer))))
1243 (when good-signatures
1244 ;; Write out good signatures into archive-contents.signed file.
1245 (write-region (mapconcat #'epg-signature-to-string good-signatures "\n")
1246 nil
1247 (expand-file-name (concat file ".signed") dir)))))
1248
1249(declare-function epg-check-configuration "epg-config"
1250 (config &optional minimum-version))
1251(declare-function epg-configuration "epg-config" ())
1252(declare-function epg-import-keys-from-file "epg" (context keys))
1253
1254;;;###autoload
1255(defun package-import-keyring (&optional file)
1256 "Import keys from FILE."
1257 (interactive "fFile: ")
1258 (setq file (expand-file-name file))
1259 (let ((context (epg-make-context 'OpenPGP))
1260 (homedir (expand-file-name "gnupg" package-user-dir)))
1261 (make-directory homedir t)
1262 (epg-context-set-home-directory context homedir)
1263 (message "Importing %s..." (file-name-nondirectory file))
1264 (epg-import-keys-from-file context file)
1265 (message "Importing %s...done" (file-name-nondirectory file))))
1131 1266
1132;;;###autoload 1267;;;###autoload
1133(defun package-refresh-contents () 1268(defun package-refresh-contents ()
@@ -1138,6 +1273,14 @@ makes them available for download."
1138 ;; FIXME: Do it asynchronously. 1273 ;; FIXME: Do it asynchronously.
1139 (unless (file-exists-p package-user-dir) 1274 (unless (file-exists-p package-user-dir)
1140 (make-directory package-user-dir t)) 1275 (make-directory package-user-dir t))
1276 (let ((default-keyring (expand-file-name "package-keyring.gpg"
1277 data-directory)))
1278 (if (file-exists-p default-keyring)
1279 (condition-case-unless-debug error
1280 (progn
1281 (epg-check-configuration (epg-configuration))
1282 (package-import-keyring default-keyring))
1283 (error (message "Cannot import default keyring: %S" (cdr error))))))
1141 (dolist (archive package-archives) 1284 (dolist (archive package-archives)
1142 (condition-case-unless-debug nil 1285 (condition-case-unless-debug nil
1143 (package--download-one-archive archive "archive-contents") 1286 (package--download-one-archive archive "archive-contents")
@@ -1209,7 +1352,8 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages."
1209 (homepage (if desc (cdr (assoc :url (package-desc-extras desc))))) 1352 (homepage (if desc (cdr (assoc :url (package-desc-extras desc)))))
1210 (built-in (eq pkg-dir 'builtin)) 1353 (built-in (eq pkg-dir 'builtin))
1211 (installable (and archive (not built-in))) 1354 (installable (and archive (not built-in)))
1212 (status (if desc (package-desc-status desc) "orphan"))) 1355 (status (if desc (package-desc-status desc) "orphan"))
1356 (signed (if desc (package-desc-signed desc))))
1213 (prin1 name) 1357 (prin1 name)
1214 (princ " is ") 1358 (princ " is ")
1215 (princ (if (memq (aref status 0) '(?a ?e ?i ?o ?u)) "an " "a ")) 1359 (princ (if (memq (aref status 0) '(?a ?e ?i ?o ?u)) "an " "a "))
@@ -1222,7 +1366,9 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages."
1222 'font-lock-face 'font-lock-builtin-face) 1366 'font-lock-face 'font-lock-builtin-face)
1223 ".")) 1367 "."))
1224 (pkg-dir 1368 (pkg-dir
1225 (insert (propertize (capitalize status) ;FIXME: Why comment-face? 1369 (insert (propertize (if (equal status "unsigned")
1370 "Installed"
1371 (capitalize status)) ;FIXME: Why comment-face?
1226 'font-lock-face 'font-lock-comment-face)) 1372 'font-lock-face 'font-lock-comment-face))
1227 (insert " in `") 1373 (insert " in `")
1228 ;; Todo: Add button for uninstalling. 1374 ;; Todo: Add button for uninstalling.
@@ -1233,9 +1379,11 @@ If optional arg NO-ACTIVATE is non-nil, don't activate packages."
1233 (not (package-built-in-p name version))) 1379 (not (package-built-in-p name version)))
1234 (insert "',\n shadowing a " 1380 (insert "',\n shadowing a "
1235 (propertize "built-in package" 1381 (propertize "built-in package"
1236 'font-lock-face 'font-lock-builtin-face) 1382 'font-lock-face 'font-lock-builtin-face))
1237 ".") 1383 (insert "'"))
1238 (insert "'."))) 1384 (if signed
1385 (insert ".")
1386 (insert " (unsigned).")))
1239 (installable 1387 (installable
1240 (insert (capitalize status)) 1388 (insert (capitalize status))
1241 (insert " from " (format "%s" archive)) 1389 (insert " from " (format "%s" archive))
@@ -1449,7 +1597,8 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC."
1449 (dir (package-desc-dir pkg-desc)) 1597 (dir (package-desc-dir pkg-desc))
1450 (lle (assq name package-load-list)) 1598 (lle (assq name package-load-list))
1451 (held (cadr lle)) 1599 (held (cadr lle))
1452 (version (package-desc-version pkg-desc))) 1600 (version (package-desc-version pkg-desc))
1601 (signed (package-desc-signed pkg-desc)))
1453 (cond 1602 (cond
1454 ((eq dir 'builtin) "built-in") 1603 ((eq dir 'builtin) "built-in")
1455 ((and lle (null held)) "disabled") 1604 ((and lle (null held)) "disabled")
@@ -1463,7 +1612,9 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC."
1463 (dir ;One of the installed packages. 1612 (dir ;One of the installed packages.
1464 (cond 1613 (cond
1465 ((not (file-exists-p (package-desc-dir pkg-desc))) "deleted") 1614 ((not (file-exists-p (package-desc-dir pkg-desc))) "deleted")
1466 ((eq pkg-desc (cadr (assq name package-alist))) "installed") 1615 ((eq pkg-desc (cadr (assq name package-alist))) (if signed
1616 "installed"
1617 "unsigned"))
1467 (t "obsolete"))) 1618 (t "obsolete")))
1468 (t 1619 (t
1469 (let* ((ins (cadr (assq name package-alist))) 1620 (let* ((ins (cadr (assq name package-alist)))
@@ -1473,7 +1624,9 @@ package PKG-DESC, add one. The alist is keyed with PKG-DESC."
1473 (if (memq name package-menu--new-package-list) 1624 (if (memq name package-menu--new-package-list)
1474 "new" "available")) 1625 "new" "available"))
1475 ((version-list-< version ins-v) "obsolete") 1626 ((version-list-< version ins-v) "obsolete")
1476 ((version-list-= version ins-v) "installed"))))))) 1627 ((version-list-= version ins-v) (if signed
1628 "installed"
1629 "unsigned"))))))))
1477 1630
1478(defun package-menu--refresh (&optional packages) 1631(defun package-menu--refresh (&optional packages)
1479 "Re-populate the `tabulated-list-entries'. 1632 "Re-populate the `tabulated-list-entries'.
@@ -1532,6 +1685,7 @@ Return (PKG-DESC [NAME VERSION STATUS DOC])."
1532 (`"held" 'font-lock-constant-face) 1685 (`"held" 'font-lock-constant-face)
1533 (`"disabled" 'font-lock-warning-face) 1686 (`"disabled" 'font-lock-warning-face)
1534 (`"installed" 'font-lock-comment-face) 1687 (`"installed" 'font-lock-comment-face)
1688 (`"unsigned" 'font-lock-warning-face)
1535 (_ 'font-lock-warning-face)))) ; obsolete. 1689 (_ 'font-lock-warning-face)))) ; obsolete.
1536 (list pkg-desc 1690 (list pkg-desc
1537 (vector (list (symbol-name (package-desc-name pkg-desc)) 1691 (vector (list (symbol-name (package-desc-name pkg-desc))
@@ -1570,7 +1724,7 @@ If optional arg BUTTON is non-nil, describe its associated package."
1570(defun package-menu-mark-delete (&optional _num) 1724(defun package-menu-mark-delete (&optional _num)
1571 "Mark a package for deletion and move to the next line." 1725 "Mark a package for deletion and move to the next line."
1572 (interactive "p") 1726 (interactive "p")
1573 (if (member (package-menu-get-status) '("installed" "obsolete")) 1727 (if (member (package-menu-get-status) '("installed" "obsolete" "unsigned"))
1574 (tabulated-list-put-tag "D" t) 1728 (tabulated-list-put-tag "D" t)
1575 (forward-line))) 1729 (forward-line)))
1576 1730
@@ -1624,7 +1778,7 @@ If optional arg BUTTON is non-nil, describe its associated package."
1624 ;; ENTRY is (PKG-DESC [NAME VERSION STATUS DOC]) 1778 ;; ENTRY is (PKG-DESC [NAME VERSION STATUS DOC])
1625 (let ((pkg-desc (car entry)) 1779 (let ((pkg-desc (car entry))
1626 (status (aref (cadr entry) 2))) 1780 (status (aref (cadr entry) 2)))
1627 (cond ((equal status "installed") 1781 (cond ((member status '("installed" "unsigned"))
1628 (push pkg-desc installed)) 1782 (push pkg-desc installed))
1629 ((member status '("available" "new")) 1783 ((member status '("available" "new"))
1630 (push (cons (package-desc-name pkg-desc) pkg-desc) 1784 (push (cons (package-desc-name pkg-desc) pkg-desc)
@@ -1738,6 +1892,8 @@ Optional argument NOQUERY non-nil means do not ask the user to confirm."
1738 ((string= sB "available") nil) 1892 ((string= sB "available") nil)
1739 ((string= sA "installed") t) 1893 ((string= sA "installed") t)
1740 ((string= sB "installed") nil) 1894 ((string= sB "installed") nil)
1895 ((string= sA "unsigned") t)
1896 ((string= sB "unsigned") nil)
1741 ((string= sA "held") t) 1897 ((string= sA "held") t)
1742 ((string= sB "held") nil) 1898 ((string= sB "held") nil)
1743 ((string= sA "built-in") t) 1899 ((string= sA "built-in") t)
diff --git a/test/ChangeLog b/test/ChangeLog
index bf8ecbcb9eb..00a49eea936 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,19 @@
12013-10-03 Daiki Ueno <ueno@gnu.org>
2
3 * automated/data/package/signed/archive-contents:
4 * automated/data/package/signed/archive-contents.sig:
5 * automated/data/package/signed/signed-good-1.0.el:
6 * automated/data/package/signed/signed-good-1.0.el.sig:
7 * automated/data/package/signed/signed-bad-1.0.el:
8 * automated/data/package/signed/signed-bad-1.0.el.sig:
9 * automated/data/package/key.pub:
10 * automated/data/package/key.sec: New files.
11
12 * automated/package-test.el (package-test-update-listing)
13 (package-test-update-archives, package-test-describe-package):
14 Adjust to package.el change.
15 (package-test-signed): New test.
16
12013-10-01 Dmitry Gutov <dgutov@yandex.ru> 172013-10-01 Dmitry Gutov <dgutov@yandex.ru>
2 18
3 * automated/package-test.el: Update all cases to use :url instead 19 * automated/package-test.el: Update all cases to use :url instead
diff --git a/test/automated/data/package/key.pub b/test/automated/data/package/key.pub
new file mode 100644
index 00000000000..a326d34e54f
--- /dev/null
+++ b/test/automated/data/package/key.pub
@@ -0,0 +1,18 @@
1-----BEGIN PGP PUBLIC KEY BLOCK-----
2Version: GnuPG v1.4.14 (GNU/Linux)
3
4mQENBFJNB8gBCACfbtpvYrM8V1HM0KFlIwatcEJugHqwOHpr/Z9mrCW0fxyQAW/d
52L+3QVNsN9Tz/K9lLcBUgeR7rhVEzHNqhmhNj/HnikwGqXbIofhp+QbZmBKnAlCz
6d77kg8K9lozHtfTkm1gX/7DdPzQKmgi7WOzzi2395wGubeqJLvYaEcqVbI0Eob+E
73CzRjNy/e/Tf3TJRW5etTcdZN6LVuIY7tNCHqlQZTwyycON/hfLTX6cLCnzDsqm/
8NxCuwn9aqP9aGRGfIu7Y+If3zTymvrXEPUN98OEID814bOKdx0uVTZRiSMbvuTGI
98uMa/kpGX/78rqI61gbZV51RFoU7pT2tzwY/ABEBAAG0HkouIFIuIEhhY2tlciA8
10anJoQGV4YW1wbGUuY29tPokBOAQTAQIAIgUCUk0HyAIbAwYLCQgHAwIGFQgCCQoL
11BBYCAwECHgECF4AACgkQtpVAhgkYletuhQf+JAyHYhTZNxjq0UYlikuLX8EtYbXX
12PB+03J0B73SMzEai5XsiTU2ADxqxwr7pveVK1INf+IGLiiXBlQq+4DSOvQY4xLfp
1358jTOYRV1ECvlXK/JtvVOwufXREADaydf9l/MUxA5G2PPBWIuQknh3ysPSsx68OJ
14SzNHFwklLn0DKc4WloE/GLDpTzimnCg7QGzuUo3Iilpjdy8EvTdI5d3jx/mGJIwI
15goB+YZgyxSPM+GjDwh5DEwD7OexNqqa7RynnmU0epmlYyi9UufCHLwgiiEIzjpWi
166+iF+CQ45ZAKncovByenIUv73J3ImOudrsskeAHBmahljv1he6uV9Egj2Q==
17=b5Kg
18-----END PGP PUBLIC KEY BLOCK-----
diff --git a/test/automated/data/package/key.sec b/test/automated/data/package/key.sec
new file mode 100644
index 00000000000..d21e6ae9a45
--- /dev/null
+++ b/test/automated/data/package/key.sec
@@ -0,0 +1,33 @@
1-----BEGIN PGP PRIVATE KEY BLOCK-----
2Version: GnuPG v1.4.14 (GNU/Linux)
3
4lQO+BFJNB8gBCACfbtpvYrM8V1HM0KFlIwatcEJugHqwOHpr/Z9mrCW0fxyQAW/d
52L+3QVNsN9Tz/K9lLcBUgeR7rhVEzHNqhmhNj/HnikwGqXbIofhp+QbZmBKnAlCz
6d77kg8K9lozHtfTkm1gX/7DdPzQKmgi7WOzzi2395wGubeqJLvYaEcqVbI0Eob+E
73CzRjNy/e/Tf3TJRW5etTcdZN6LVuIY7tNCHqlQZTwyycON/hfLTX6cLCnzDsqm/
8NxCuwn9aqP9aGRGfIu7Y+If3zTymvrXEPUN98OEID814bOKdx0uVTZRiSMbvuTGI
98uMa/kpGX/78rqI61gbZV51RFoU7pT2tzwY/ABEBAAH+AwMCKCCpPNXkXuVgF7cz
10eByuvgIO7wImDYGOdJqsASSzV4q0u1acnGtlxg7WphKDF9RnC5+1ZZ1ZcrBcv2uJ
11xZm2jHdjqM3FmgQTN70GVzO1nKEur2wxlKotG4Q+8BtaRDwHdKpQFk+QW9aInH3C
12BkNWTK97iFwZaoUGxKuRJb35qjMe3SsDE7kdbtOqO+tOeppRVeOOZCn7F33ir/6i
13j2gmIME6LFDzvBi6YAyMBSh90Ak70HJINt0QfXlZf5MtX1NaxaEcnsRmwwcNqxh9
14JvcC9q4WrR92NhHCHI+lOsAe7hbwo/VkwRjSSx0HdKkx6kvdcNj/9LeX/jykzLvg
15kEqvAqT4Jmk57W2seqvpNcAO+eUVrJ5D1OR6khsUtikPp2pQH5MDXJDGcie+ZAFb
16w6BwoWBDBjooKtfuP0LKqrdtJG2JLe6yhBhWvfqHPBlUU1SsA7a5aTCLo8FiqgEI
17Kyy60zMx/2Mi48oN1a/mAoV1MTWLhOVUWJlIHM7nVLj1OaX0316LcLX/uTLTq40p
18apHKwERanzY7f8ROiv/Fa/J+9cCsfOLKfjFAjpBVUVoOb39HsyS/vvkGMY4kgaD6
19K6r9JPdsaoYvsLkxk5HyHF7Mk2uS1z1EIArD2/3lRiX6ag+IU1Nl3XDkgfZj06K3
20juS84dGF8CmN49uOEjzAJAQZH9jTs5OKzUuZhGJF+gt0L78vLOoKRr8bu1N1GPqU
21wnS908HWruXzjJl1CAhnuCa8FnDaU+tmEKjYpWuelx85kolpMW7LT5gOFZr84MIj
22Kq3Rt2hU6qQ7Cdy1ep531YKkmyh9Y4l/Tgir1OtnQQqtNuwHI497l7qAUnKZBBHZ
23guApjS9BoHsRXkw2mgDssZ+khOwj/xJm876nFSiQeCD0aIbU/4zJ9e2HUOJAZI1r
24d7QeSi4gUi4gSGFja2VyIDxqcmhAZXhhbXBsZS5jb20+iQE4BBMBAgAiBQJSTQfI
25AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRC2lUCGCRiV626FB/4kDIdi
26FNk3GOrRRiWKS4tfwS1htdc8H7TcnQHvdIzMRqLleyJNTYAPGrHCvum95UrUg1/4
27gYuKJcGVCr7gNI69BjjEt+nnyNM5hFXUQK+Vcr8m29U7C59dEQANrJ1/2X8xTEDk
28bY88FYi5CSeHfKw9KzHrw4lLM0cXCSUufQMpzhaWgT8YsOlPOKacKDtAbO5SjciK
29WmN3LwS9N0jl3ePH+YYkjAiCgH5hmDLFI8z4aMPCHkMTAPs57E2qprtHKeeZTR6m
30aVjKL1S58IcvCCKIQjOOlaLr6IX4JDjlkAqdyi8HJ6chS/vcnciY652uyyR4AcGZ
31qGWO/WF7q5X0SCPZ
32=5FZK
33-----END PGP PRIVATE KEY BLOCK-----
diff --git a/test/automated/data/package/signed/archive-contents b/test/automated/data/package/signed/archive-contents
new file mode 100644
index 00000000000..2a773ecba6a
--- /dev/null
+++ b/test/automated/data/package/signed/archive-contents
@@ -0,0 +1,7 @@
1(1
2 (signed-good .
3 [(1 0)
4 nil "A package with good signature" single])
5 (signed-bad .
6 [(1 0)
7 nil "A package with bad signature" single]))
diff --git a/test/automated/data/package/signed/archive-contents.sig b/test/automated/data/package/signed/archive-contents.sig
new file mode 100644
index 00000000000..658edd3f60e
--- /dev/null
+++ b/test/automated/data/package/signed/archive-contents.sig
Binary files differ
diff --git a/test/automated/data/package/signed/signed-bad-1.0.el b/test/automated/data/package/signed/signed-bad-1.0.el
new file mode 100644
index 00000000000..3734823876e
--- /dev/null
+++ b/test/automated/data/package/signed/signed-bad-1.0.el
@@ -0,0 +1,33 @@
1;;; signed-bad.el --- A single-file package with bad signature
2
3;; Author: J. R. Hacker <jrh@example.com>
4;; Version: 1.0
5;; Keywords: frobnicate
6;; URL: http://doodles.au
7
8;;; Commentary:
9
10;; This package provides a minor mode to frobnicate and/or bifurcate
11;; any flanges you desire. To activate it, type "C-M-r M-3 butterfly"
12;; and all your dreams will come true.
13
14;;; Code:
15
16(defgroup signed-bad nil "Simply a file"
17 :group 'lisp)
18
19(defcustom signed-bad-super-sunday t
20 "How great is this?"
21 :type 'boolean
22 :group 'signed-bad)
23
24(defvar signed-bad-sudo-sandwich nil
25 "Make a sandwich?")
26
27;;;###autoload
28(define-minor-mode signed-bad-mode
29 "It does good things to stuff")
30
31(provide 'signed-bad)
32
33;;; signed-bad.el ends here
diff --git a/test/automated/data/package/signed/signed-bad-1.0.el.sig b/test/automated/data/package/signed/signed-bad-1.0.el.sig
new file mode 100644
index 00000000000..747918794ca
--- /dev/null
+++ b/test/automated/data/package/signed/signed-bad-1.0.el.sig
Binary files differ
diff --git a/test/automated/data/package/signed/signed-good-1.0.el b/test/automated/data/package/signed/signed-good-1.0.el
new file mode 100644
index 00000000000..22718df2763
--- /dev/null
+++ b/test/automated/data/package/signed/signed-good-1.0.el
@@ -0,0 +1,33 @@
1;;; signed-good.el --- A single-file package with good signature
2
3;; Author: J. R. Hacker <jrh@example.com>
4;; Version: 1.0
5;; Keywords: frobnicate
6;; URL: http://doodles.au
7
8;;; Commentary:
9
10;; This package provides a minor mode to frobnicate and/or bifurcate
11;; any flanges you desire. To activate it, type "C-M-r M-3 butterfly"
12;; and all your dreams will come true.
13
14;;; Code:
15
16(defgroup signed-good nil "Simply a file"
17 :group 'lisp)
18
19(defcustom signed-good-super-sunday t
20 "How great is this?"
21 :type 'boolean
22 :group 'signed-good)
23
24(defvar signed-good-sudo-sandwich nil
25 "Make a sandwich?")
26
27;;;###autoload
28(define-minor-mode signed-good-mode
29 "It does good things to stuff")
30
31(provide 'signed-good)
32
33;;; signed-good.el ends here
diff --git a/test/automated/data/package/signed/signed-good-1.0.el.sig b/test/automated/data/package/signed/signed-good-1.0.el.sig
new file mode 100644
index 00000000000..747918794ca
--- /dev/null
+++ b/test/automated/data/package/signed/signed-good-1.0.el.sig
Binary files differ
diff --git a/test/automated/package-test.el b/test/automated/package-test.el
index 84f520df9bc..ec85432b637 100644
--- a/test/automated/package-test.el
+++ b/test/automated/package-test.el
@@ -254,7 +254,7 @@ Must called from within a `tar-mode' buffer."
254 (should (package-installed-p 'simple-single)) 254 (should (package-installed-p 'simple-single))
255 (switch-to-buffer "*Packages*") 255 (switch-to-buffer "*Packages*")
256 (goto-char (point-min)) 256 (goto-char (point-min))
257 (should (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+installed" nil t)) 257 (should (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+unsigned" nil t))
258 (goto-char (point-min)) 258 (goto-char (point-min))
259 (should-not (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+\\(available\\|new\\)" nil t)) 259 (should-not (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+\\(available\\|new\\)" nil t))
260 (kill-buffer buf)))) 260 (kill-buffer buf))))
@@ -276,7 +276,7 @@ Must called from within a `tar-mode' buffer."
276 ;; New version should be available and old version should be installed 276 ;; New version should be available and old version should be installed
277 (goto-char (point-min)) 277 (goto-char (point-min))
278 (should (re-search-forward "^\\s-+simple-single\\s-+1.4\\s-+new" nil t)) 278 (should (re-search-forward "^\\s-+simple-single\\s-+1.4\\s-+new" nil t))
279 (should (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+installed" nil t)) 279 (should (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+unsigned" nil t))
280 280
281 (goto-char (point-min)) 281 (goto-char (point-min))
282 (should (re-search-forward "^\\s-+new-pkg\\s-+1.0\\s-+\\(available\\|new\\)" nil t)) 282 (should (re-search-forward "^\\s-+new-pkg\\s-+1.0\\s-+\\(available\\|new\\)" nil t))
@@ -307,9 +307,9 @@ Must called from within a `tar-mode' buffer."
307 (with-fake-help-buffer 307 (with-fake-help-buffer
308 (describe-package 'simple-single) 308 (describe-package 'simple-single)
309 (goto-char (point-min)) 309 (goto-char (point-min))
310 (should (search-forward "simple-single is an installed package." nil t)) 310 (should (search-forward "simple-single is an unsigned package." nil t))
311 (should (search-forward 311 (should (search-forward
312 (format "Status: Installed in `%s/'." 312 (format "Status: Installed in `%s/' (unsigned)."
313 (expand-file-name "simple-single-1.3" package-user-dir)) 313 (expand-file-name "simple-single-1.3" package-user-dir))
314 nil t)) 314 nil t))
315 (should (search-forward "Version: 1.3" nil t)) 315 (should (search-forward "Version: 1.3" nil t))
@@ -347,6 +347,37 @@ Must called from within a `tar-mode' buffer."
347 (should (search-forward "This is a bare-bones readme file for the multi-file" 347 (should (search-forward "This is a bare-bones readme file for the multi-file"
348 nil t))))) 348 nil t)))))
349 349
350(ert-deftest package-test-signed ()
351 "Test verifying package signature."
352 :expected-result (condition-case nil
353 (progn
354 (epg-check-configuration (epg-configuration))
355 :passed)
356 (error :failed))
357 (let* ((keyring (expand-file-name "key.pub" package-test-data-dir))
358 (package-test-data-dir
359 (expand-file-name "data/package/signed" package-test-file-dir)))
360 (with-package-test ()
361 (package-initialize)
362 (package-import-keyring keyring)
363 (package-refresh-contents)
364 (should (package-install 'signed-good))
365 (should-error (package-install 'signed-bad))
366 ;; Check if the installed package status is updated.
367 (let ((buf (package-list-packages)))
368 (package-menu-refresh)
369 (should (re-search-forward "^\\s-+signed-good\\s-+1\\.0\\s-+installed"
370 nil t)))
371 ;; Check if the package description is updated.
372 (with-fake-help-buffer
373 (describe-package 'signed-good)
374 (goto-char (point-min))
375 (should (search-forward "signed-good is an installed package." nil t))
376 (should (search-forward
377 (format "Status: Installed in `%s/'."
378 (expand-file-name "signed-good-1.0" package-user-dir))
379 nil t))))))
380
350(provide 'package-test) 381(provide 'package-test)
351 382
352;;; package-test.el ends here 383;;; package-test.el ends here