Emacsのパッケージをインストールする時には署名の検査が行われますが、署名の検査を行わないように設定に設定できます。今回はその際に設定する変数と使われ方について調べました。

TL;DR

  • パッケージの署名の検査はpackage-check-signatureとpackage-unsigned-archivesの設定で検査するかどうかが決定する

  • package-check-signatureに設定できる値は nil 'allow-signed t 'all のいずれかを設定する

  • package-unsigned-archivesは署名の検査をしないパッケージアーカイブの名前のリストを設定する

package-check-signatureに設定できる値と意味

意味
nil 署名のチェックをしない
'allow-signed 署名があり鍵がある時だけ署名の確認を行う
t 少なくとも1つの検証済み署名が必要
'all 全ての検証済み署名が必要

署名ファイルがないのにpackage-refresh-contentsできる?

自分用にパッケージアーカイブを作成しました。しかし署名ファイルはまだ準備していません。それにもかかわらずなぜかpackage-refresh-contentsできてしまいました。Emacsのパッケージがインストールされるのを眺めるで確認した時にはM-x package-refresh-contentsを実行してインデックス情報であるarchive-contentsをダウンロードした時にも、パッケージをインストールする時にも署名ファイル(.sig)のダウンロードとその検査を行なっていました。今回はこの挙動の違いの理由を調べます。

package-check-signature - 署名を確認するかどうかを制御する

package-check-signatureはインストール時に署名を確認するかどうかを制御するための変数です。

(defcustom package-check-signature
  (if (and (require 'epg-config)
           (epg-find-configuration 'OpenPGP))
      'allow-unsigned)
  "Non-nil means to check package signatures when installing.
More specifically the value can be:
- nil: package signatures are ignored.
- `allow-unsigned': install a package even if it is unsigned,
  but if it is signed and we have the key for it, verify the signature.
- t: accept a package only if it comes with at least one verified signature.
- `all': same as t, except when the package has several signatures,
  in which case we verify all the signatures.

This also applies to the \"archive-contents\" file that lists the
contents of the archive."
  :type '(choice (const nil :tag "Never")
                 (const allow-unsigned :tag "Allow unsigned")
                 (const t :tag "Check always")
                 (const all :tag "Check all signatures"))
  :risky t
  :version "27.1")

現在の値を確認します。

package-check-signature
allow-unsigned

現在の値はallow-unsignedになっていました。これは署名があれば検証するが、なければそのままインストールしてしまう設定です。このため署名を作ってない自分用のパッケージアーカイブからもインストールができる状態になっていました。

package-check-signatureに設定できる値

package-check-signatureには nil 'allow-signed t 'all が設定できます。

意味
nil 署名のチェックをしない
'allow-signed 署名があり鍵がある時だけ署名の確認を行う
t 少なくとも1つの検証済み署名が必要
'all 全ての検証済み署名が必要

使われている所

package-refresh-contents

package-refresh-contentsはpackage-archivesに設定されているパッケージアーカイブからパッケージの情報をダウンロードし読み込みます。

抜粋::

    (when (and package-check-signature (file-exists-p default-keyring))
      (condition-case-unless-debug error
          (package-import-keyring default-keyring)
        (error (message "Cannot import default keyring: %S" (cdr error))))))

package-check-signatureが設定されていればGnuPGの鍵を読み込みます。この鍵は署名の検証に使われます。なおdefault-keyringはデフォルトは以下で設定されている。

"/Applications/Emacs.app/Contents/Resources/etc/package-keyring.gpg"

package–download-one-archive

package–download-one-archiveはアーカイブからファイルをダウンロードしてキャッシュします。 アーカイブは(NAME . LOCATION)という形式のコンスセルです。 ダウンロードしたファイルはpackage-user-dir配下のpackages/NAME/FILEに保存されます。

抜粋::

        (if (or (not package-check-signature)
                (member name package-unsigned-archives))
            ;; If we don't care about the signature, save the file and
            ;; we're done.
            (progn (let ((coding-system-for-write 'utf-8))
                     (write-region content nil local-file nil 'silent))
                   (package--update-downloads-in-progress archive))
          ;; If we care, check it (perhaps async) and *then* write the file.
          (package--check-signature
           location file content async
           ;; This function will be called after signature checking.
           (lambda (&optional good-sigs)
             (let ((coding-system-for-write 'utf-8))
               (write-region content nil local-file nil 'silent))
             ;; Write out good signatures into archive-contents.signed file.
             (when good-sigs
               (write-region (mapconcat #'epg-signature-to-string good-sigs "\n")
                             nil (concat local-file ".signed") nil 'silent)))
           (lambda () (package--update-downloads-in-progress archive))))))))

package-check-signatureとpackage-unsigned-archivesの状態をみてpackage–check-signatureされるかどうかが分岐しています。package–check-signatureは署名ファイルをダウンロードし検証します。

package-unsigned-archives - 署名を確認しないパッケージアーカイブのリスト

package-unsigned-archivesに名前が設定されたパッケージアーカイブは署名のチェックを行いません。

(defcustom package-unsigned-archives nil
  "List of archives where we do not check for package signatures."
  :type '(repeat (string :tag "Archive name"))
  :risky t
  :version "24.4")

現在の値を確認します。

package-unsigned-archives
nil

こちらは設定していませんでした。

使われているところ

(member name package-unsigned-archives) のようにこのpackage-unsigned-archivesにパッケージ名が含まれているかどうかをチェックしています。

package–download-one-archive

抜粋::

        (if (or (not package-check-signature)
                (member name package-unsigned-archives))
            ;; If we don't care about the signature, save the file and
            ;; we're done.
            (progn (let ((coding-system-for-write 'utf-8))
                     (write-region content nil local-file nil 'silent))
                   (package--update-downloads-in-progress archive))
          ;; If we care, check it (perhaps async) and *then* write the file.
          (package--check-signature

package-install-from-archive

抜粋::

    (package--with-response-buffer location :file file
      (if (or (not package-check-signature)
              (member (package-desc-archive pkg-desc)
                       package-unsigned-archives))
          ;; If we don't care about the signature, unpack and we're
          ;; done.
          (let ((save-silently t))
            (package-unpack pkg-desc))

まとめ

署名の検査をする(もしくはしない)ための設定はpackage-check-signatureとpackage-unsigned-archivesの設定によって制御されていました。package-check-signatureは署名の検査を(どの程度)するか、package-unsigned-archivesはそのパッケージアーカイブが署名の検証が不要かを設定する変数でした。