Class: IOStreams::Pgp::Writer

Inherits:
Writer
  • Object
show all
Defined in:
lib/io_streams/pgp/writer.rb

Class Attribute Summary collapse

Attributes inherited from Writer

#output_stream

Class Method Summary collapse

Methods inherited from Writer

#initialize, open, stream

Constructor Details

This class inherits a constructor from IOStreams::Writer

Class Attribute Details

.audit_recipientObject

Encrypt all pgp output files with this recipient for audit purposes. Allows the generated pgp files to be decrypted with this email address. Useful for audit or problem resolution purposes.



18
19
20
# File 'lib/io_streams/pgp/writer.rb', line 18

def audit_recipient
  @audit_recipient
end

.default_signer=(value) ⇒ Object

Sign all encrypted files with this users key. Default: Do not sign encrypted files.



9
10
11
# File 'lib/io_streams/pgp/writer.rb', line 9

def default_signer=(value)
  @default_signer = value
end

.default_signer_passphrase=(value) ⇒ Object

Passphrase to use to open the private key when signing the file. Default: None.



13
14
15
# File 'lib/io_streams/pgp/writer.rb', line 13

def default_signer_passphrase=(value)
  @default_signer_passphrase = value
end

Class Method Details

.file(file_name, encrypt: true, recipient: nil, import_and_trust_key: nil, import_and_trust_level: 5, signer: default_signer, signer_passphrase: default_signer_passphrase, compress: :zip, compress_level: 6) ⇒ Object

Write to a PGP / GPG file, encrypting and/or signing the contents as it is written.

file_name: [String]

Name of file to write to.

encrypt: [true|false]

Whether to encrypt the file for the supplied recipient(s).
When set to false the file is signed but not encrypted, in which case a
:signer must be supplied and :recipient / :import_and_trust_key are ignored.
Default: true

recipient: [String|Array<String>]

One or more emails of users for which to encrypt the file.
Ignored when encrypt is false.

import_and_trust_key: [String|Array<String>]

One or more pgp keys to import and then use to encrypt the file.
Note: Ascii Keys can contain multiple keys, only the last one in the file is used.

import_and_trust_level: [Integer]

The owner-trust level to assign to keys supplied via :import_and_trust_key.
  1 : Undefined  (no opinion)
  2 : Never      (do not trust)
  3 : Marginal
  4 : Full
  5 : Ultimate
Default: 5 : Ultimate

SECURITY WARNING:
  Only import and trust keys received from a verified, trusted source.
  The default trust level is `5` (Ultimate), which tells GPG to treat the imported key
  as if it were one of your own keys. An ultimately trusted key is implicitly valid and
  can in turn confer validity on other keys it has signed. Importing an attacker supplied
  key at this level allows that attacker to impersonate other recipients.
  When the key cannot be fully verified, supply a lower `import_and_trust_level`.

signer: [String]

Name of user with which to sign the encypted file.
Default: default_signer or do not sign.

signer_passphrase: [String]

Passphrase to use to open the private key when signing the file.
Default: default_signer_passphrase

compress: [:none|:zip|:zlib|:bzip2]

Note: Standard PGP only supports :zip.
:zlib is better than zip.
:bzip2 is best, but uses a lot of memory and is much slower.
Default: :zip

compress_level: [Integer]

Compression level
Default: 6

Note: There is intentionally no option here to disable MDC (Modification Detection Code) integrity protection on the files we produce. The reader exposes ‘ignore_mdc_error:` so we can consume legacy files that lack MDC (see Reader), but we never want to generate them: MDC is what protects the encrypted contents against tampering, and modern GnuPG mandates it for current ciphers anyway (`–disable-mdc` is a no-op unless an obsolete cipher is forced). Omitting MDC on output would only weaken files we create, with no upside for this library.



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/io_streams/pgp/writer.rb', line 90

def self.file(file_name,
              encrypt: true,
              recipient: nil,
              import_and_trust_key: nil,
              import_and_trust_level: 5,
              signer: default_signer,
              signer_passphrase: default_signer_passphrase,
              compress: :zip,
              compress_level: 6)
  if encrypt
    raise(ArgumentError, "Requires either :recipient or :import_and_trust_key") unless recipient || import_and_trust_key
  elsif !signer
    raise(ArgumentError, "Requires a :signer when encrypt is false")
  end

  compress_level = 0 if compress == :none

  recipients =
    if encrypt
      collect_recipients(recipient, import_and_trust_key, import_and_trust_level)
    else
      []
    end

  # Write to stdin, with the encrypted and/or signed contents being written to the file
  args = build_args(
    file_name:         file_name,
    encrypt:           encrypt,
    signer:            signer,
    signer_passphrase: signer_passphrase,
    compress:          compress,
    compress_level:    compress_level,
    recipients:        recipients
  )
  command = IOStreams::Pgp.gpg_command(*args)

  # Do not log the command, it may contain the signer passphrase.
  action = encrypt ? "encrypt" : "sign"
  IOStreams.logger&.debug { "IOStreams::Pgp::Writer.open: #{action} -o #{file_name}" }

  result = nil
  Open3.popen2e(*command) do |stdin, out, waith_thr|
    begin
      stdin.binmode
      result = yield(stdin)
      stdin.close
    rescue Errno::EPIPE
      # Ignore broken pipe because gpg terminates early due to an error
      ::FileUtils.rm_f(file_name)
      raise(Pgp::Failure, "GPG Failed writing to encrypted file: #{file_name}: #{out.read.chomp}")
    end
    unless waith_thr.value.success?
      ::FileUtils.rm_f(file_name)
      raise(Pgp::Failure, "GPG Failed to create encrypted file: #{file_name}: #{out.read.chomp}")
    end
  end
  result
end