From a91d840acf316a65f5dae7045a194b75631bb2ff Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Thu, 2 Feb 2017 17:18:07 +0100 Subject: [PATCH] Fix GPG verification on Windows and add GPG2 1. Use IO.popen to avoid issues with missing escaping in the paths. 2. Remove unused method normalize_path. 3. Try both gpg2 and gpg for signature verification. 4. Use relative path for keyring and delete the key afterwards. To point 4: The reason for use relative paths is that there are 3 different flavors of gpg are used on Windows, which differ in the way how absolute paths are specified: MINGW: C:\path\to\keyring.gpg MSYS2: /c/path/to/keyring.gpg Cygwin: /cygdrive/c/path/to/keyring.gpg Since there is no reliable way to find out which flavor of gpg is actually running, it's best to use relative paths only. The downside is, that we can not reliably say where the file is located in the end, which is why only the imported key is deleted afterwards, not the whole keyring file. --- lib/mini_portile2/mini_portile.rb | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/mini_portile2/mini_portile.rb b/lib/mini_portile2/mini_portile.rb index 53a70c3..636fd23 100644 --- a/lib/mini_portile2/mini_portile.rb +++ b/lib/mini_portile2/mini_portile.rb @@ -251,10 +251,7 @@ def files_hashs end end - def normalize_path path - path - # path.gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR) - end + KEYRING_NAME = "mini_portile_keyring.gpg" def verify_file(file) if file.has_key?(:gpg) @@ -262,23 +259,28 @@ def verify_file(file) signature_url = gpg[:signature_url] || "#{file[:url]}.asc" signature_file = file[:local_path] + ".asc" + # download the signature file download_file(signature_url, signature_file) - key = Tempfile.new('armored_key') - key.write(gpg[:key]) - key.close - key_path = normalize_path(key.path) + gpg_exe = which('gpg2') || which('gpg') || raise("Neither GPG nor GPG2 is installed") - keyring = Tempfile.new('keyring') - keyring.close - keyring_path = normalize_path(keyring.path) + # import the key into our own keyring + gpg_status = IO.popen([gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--import"], "w+") do |io| + io.write gpg[:key] + io.close_write + io.read + end + raise "invalid gpg key provided" unless /\[GNUPG:\] IMPORT_OK \d+ (?[0-9a-f]+)/i =~ gpg_status - gpg_status = `gpg --status-fd 1 --no-default-keyring --keyring #{keyring_path} --import #{key_path}` + # verify the signature against our keyring + gpg_status = IO.popen([gpg_exe, "--status-fd", "1", "--no-default-keyring", "--keyring", KEYRING_NAME, "--verify", signature_file, file[:local_path]], &:read) - raise "invalid gpg key provided" unless gpg_status.match(/\[GNUPG:\] IMPORT_OK/) + # remove the key from our keyring + IO.popen([gpg_exe, "--batch", "--yes", "--no-default-keyring", "--keyring", KEYRING_NAME, "--delete-keys", key_id], &:read) - gpg_status = `gpg --status-fd 1 --no-default-keyring --keyring #{keyring_path} --verify #{signature_file} #{file[:local_path]} 2>&1` + raise "unable to delete the imported key" unless $?.exitstatus==0 raise "signature mismatch" unless gpg_status.match(/^\[GNUPG:\] VALIDSIG/) + else digest = case when exp=file[:sha256] then Digest::SHA256