Class: Reyes::PgpWrapper

Inherits:
Object
  • Object
show all
Includes:
Chalk::Log
Defined in:
lib/reyes/pgp_wrapper.rb

Defined Under Namespace

Classes: VerificationFailed

Constant Summary collapse

PATTERN =

Pattern for parsing gpg –status-fd signature output

/^\[GNUPG:\] VALIDSIG ([A-F0-9]{40}) .+ ([A-F0-9]{40})$/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ PgpWrapper

Create a PgpVerifier

Parameters:

  • key_id (String)

    40 digit key fingerprint



13
14
15
16
17
# File 'lib/reyes/pgp_wrapper.rb', line 13

def initialize(config)
  @config = config
  @key_id = @config.reyes_config.fetch("signing_key").upcase
  @keyring_directory = @config.reyes_config.fetch("keyring_directory")
end

Instance Attribute Details

#key_idObject (readonly)

Returns the value of attribute key_id.



8
9
10
# File 'lib/reyes/pgp_wrapper.rb', line 8

def key_id
  @key_id
end

#keyring_directoryObject (readonly)

Returns the value of attribute keyring_directory.



8
9
10
# File 'lib/reyes/pgp_wrapper.rb', line 8

def keyring_directory
  @keyring_directory
end

Instance Method Details

#clearsign(data) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
# File 'lib/reyes/pgp_wrapper.rb', line 69

def clearsign(data)
  log.info("Signing #{data.length} bytes with key #{key_id}")

  gpg_cmd = %W{gpg --batch --clearsign -u #{key_id}} + keyring_args + ['-']
  Subprocess.check_call(gpg_cmd,
                        :stdin => Subprocess::PIPE,
                        :stdout => Subprocess::PIPE) do |child|
    out, _ = child.communicate(data)
    return out
  end
end

#verify!(data, retries = 1) ⇒ String

Verifies data against @keyid

Parameters:

  • data (String)

    the data to verify

Returns:

  • (String)

    the stripped cleartext data

Raises:



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/reyes/pgp_wrapper.rb', line 30

def verify!(data, retries=1)
  log.info("Verifying #{data.length} bytes against key #{key_id}")

  gpg_cmd = %w{gpg --batch --decrypt --status-fd 2} + keyring_args + ['-']
  Subprocess.check_call(gpg_cmd,
                        :stdin => Subprocess::PIPE,
                        :stdout => Subprocess::PIPE,
                        :stderr => Subprocess::PIPE) do |child|
    out, err = child.communicate(data)

    begin
      if err =~ PATTERN
        raise VerificationFailed.new("Bad key match") unless $1 == $2
        raise VerificationFailed.new("Bad Key ID") unless $1 == key_id
      else
        # There's a weird and very rare race condition where the stderr
        # output returned by Subprocess can be truncated.
        # Retry in this case.
        if err.split("\n").length < 4 && retries > 0
          log.error("Looks like we hit hit GPG/Subprocess race condition")
          log.error("GPG stderr:")
          log.error(err.inspect)
          log.error("Retrying...")
          return verify!(data, retries - 1)
        else
          raise VerificationFailed.new("Pattern does not match")
        end
      end
    rescue VerificationFailed => exc
      log.error("GPG verification failed: #{exc.message}")
      log_error_output(out, err, data)
      raise
    end

    # Sig looks ok
   return out
  end
end