iostreams 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 79c7b3bec464587bc14ad4cb1c951f6b08f8ffd2
4
- data.tar.gz: 106e0fc38ffc1396ee5714ef19f9a14ea38113ae
3
+ metadata.gz: d4dcddf28ba98342cec548ede58e83e08f6d4948
4
+ data.tar.gz: 0fd6ff53b81dc4fdd8847f5c646d875aa03f7c4e
5
5
  SHA512:
6
- metadata.gz: 3f61f7b6a5a07f0419ac4f322fa2b2e986adf0f104d8b65c7f20539e8adbdb3c629f24af3ab8376202d1fb4f18f8376cd6720a83ba654be6b8d784b36c22c1ad
7
- data.tar.gz: bf2604ec77e4fd5923aaed1a478f66464e7f9a73c695574b3420b078723752be9b1eaed2b0f930da714b2958d649435711619c1528802ff4d60ce1e23d779d61
6
+ metadata.gz: '09453fc367c6d133311676971c31e9135549559995f2ffbcb9960bfd6054121344c6c6b5d7eb85bfb993b1531a8d70a7299659bceb0cffb12805b9ce823ac035'
7
+ data.tar.gz: afb3e83e545230f98a6e2acf936e937d20fdc3a753607aae039788daae426cd0bbd03d5018272e0c179512b7e4b55e6a9fcc8af2ad4b83b43aa05996630de446
@@ -62,8 +62,8 @@ module IOStreams
62
62
  # Select highest level: 5
63
63
  #
64
64
  # Delete test keys:
65
- # IOStreams::Pgp.delete_keys(email: 'sender@example.org', secret: true)
66
- # IOStreams::Pgp.delete_keys(email: 'receiver@example.org', secret: true)
65
+ # IOStreams::Pgp.delete_keys(email: 'sender@example.org', private: true)
66
+ # IOStreams::Pgp.delete_keys(email: 'receiver@example.org', private: true)
67
67
  #
68
68
  # Limitations
69
69
  # - Designed for processing larger files since a process is spawned for each file processed.
@@ -84,9 +84,13 @@ module IOStreams
84
84
  class Failure < StandardError
85
85
  end
86
86
 
87
- # Generate a new ultimate trusted local public and private key
88
- # Returns [String] the key id for the generated key
89
- # Raises an exception if it fails to generate the key
87
+ class UnsupportedVersion < Failure
88
+ end
89
+
90
+ # Generate a new ultimate trusted local public and private key.
91
+ #
92
+ # Returns [String] the key id for the generated key.
93
+ # Raises an exception if it fails to generate the key.
90
94
  #
91
95
  # name: [String]
92
96
  # Name of who owns the key, such as organization
@@ -105,126 +109,197 @@ module IOStreams
105
109
  #
106
110
  # See `man gpg` for the remaining options
107
111
  def self.generate_key(name:, email:, comment: nil, passphrase: nil, key_type: 'RSA', key_length: 4096, subkey_type: 'RSA', subkey_length: key_length, expire_date: nil)
108
- Open3.popen2e('gpg --batch --gen-key') do |stdin, out, waith_thr|
109
- stdin.puts "Key-Type: #{key_type}" if key_type
110
- stdin.puts "Key-Length: #{key_length}" if key_length
111
- stdin.puts "Subkey-Type: #{subkey_type}" if subkey_type
112
- stdin.puts "Subkey-Length: #{subkey_length}" if subkey_length
113
- stdin.puts "Name-Real: #{name}" if name
114
- stdin.puts "Name-Comment: #{comment}" if comment
115
- stdin.puts "Name-Email: #{email}" if email
116
- stdin.puts "Expire-Date: #{expire_date}" if expire_date
117
- stdin.puts "Passphrase: #{passphrase}" if passphrase
118
- stdin.puts '%commit'
119
- stdin.close
120
- if waith_thr.value.success?
121
- key_id = nil
122
- out.each_line do |line|
123
- if (line = line.chomp) =~ /^gpg: key ([0-9A-F]+) marked as ultimately trusted/
124
- key_id = $1.to_i(16)
125
- end
126
- end
127
- key_id
128
- else
129
- raise(Pgp::Failure, "GPG Failed to generate key: #{out.read.chomp}")
112
+ version_check
113
+ params = ''
114
+ params << "Key-Type: #{key_type}\n" if key_type
115
+ params << "Key-Length: #{key_length}\n" if key_length
116
+ params << "Subkey-Type: #{subkey_type}\n" if subkey_type
117
+ params << "Subkey-Length: #{subkey_length}\n" if subkey_length
118
+ params << "Name-Real: #{name}\n" if name
119
+ params << "Name-Comment: #{comment}\n" if comment
120
+ params << "Name-Email: #{email}\n" if email
121
+ params << "Expire-Date: #{expire_date}\n" if expire_date
122
+ params << "Passphrase: #{passphrase}\n" if passphrase
123
+ params << '%commit'
124
+ out, err, status = Open3.capture3('gpg --batch --gen-key', binmode: true, stdin_data: params)
125
+ logger.debug { "IOStreams::Pgp.generate_key output:\n#{out}#{err}" } if logger
126
+ if status.success?
127
+ if match = err.match(/gpg: key ([0-9A-F]+)\s+/)
128
+ return match[1]
130
129
  end
130
+ else
131
+ raise(Pgp::Failure, "GPG Failed to generate key: #{out}#{err}")
131
132
  end
132
133
  end
133
134
 
134
- # Delete a secret and public keys using its email
135
- # Returns false if no key was found
136
- # Raises an exception if it fails to delete the key
135
+ # Delete all private and public keys for a particular email.
137
136
  #
138
- # email: [String] Email address for the key
137
+ # Returns false if no key was found.
138
+ # Raises an exception if it fails to delete the key.
139
+ #
140
+ # email: [String] Email address for the key.
139
141
  #
140
142
  # public: [true|false]
141
143
  # Whether to delete the public key
142
144
  # Default: true
143
145
  #
144
- # secret: [true|false]
145
- # Whether to delete the secret key
146
+ # private: [true|false]
147
+ # Whether to delete the private key
146
148
  # Default: false
147
- def self.delete_keys(email:, public: true, secret: false)
149
+ def self.delete_keys(email:, public: true, private: false)
150
+ version_check
148
151
  cmd = "for i in `gpg --with-colons --fingerprint #{email} | grep \"^fpr\" | cut -d: -f10`; do\n"
149
- cmd << "gpg --batch --delete-secret-keys \"$i\" ;\n" if secret
152
+ cmd << "gpg --batch --delete-secret-keys \"$i\" ;\n" if private
150
153
  cmd << "gpg --batch --delete-keys \"$i\" ;\n" if public
151
154
  cmd << 'done'
152
- Open3.popen2e(cmd) do |stdin, out, waith_thr|
153
- output = out.read.chomp
154
- if waith_thr.value.success?
155
- return false if output =~ /(public key not found|No public key)/i
156
- raise(Pgp::Failure, "GPG Failed to delete keys for #{email}: #{output}") if output.include?('error')
157
- true
158
- else
159
- raise(Pgp::Failure, "GPG Failed calling gpg to delete secret keys for #{email}: #{output}")
160
- end
155
+
156
+ out, err, status = Open3.capture3(cmd, binmode: true)
157
+ logger.debug { "IOStreams::Pgp.delete_keys output:\n#{err}#{out}" } if logger
158
+
159
+ if status.success?
160
+ return false if err =~ /(not found|No public key)/i
161
+ raise(Pgp::Failure, "GPG Failed to delete keys for #{email}:#{err}#{out}") if out.include?('error')
162
+ true
163
+ else
164
+ raise(Pgp::Failure, "GPG Failed calling gpg to delete private keys for #{email}: #{err}#{out}")
161
165
  end
162
166
  end
163
167
 
164
- def self.has_key?(email:)
165
- Open3.popen2e("gpg --list-keys --with-colons #{email}") do |stdin, out, waith_thr|
166
- output = out.read.chomp
167
- if waith_thr.value.success?
168
- output.each_line do |line|
169
- return true if line.include?(email)
170
- end
171
- false
172
- else
173
- return false if output =~ /(public key not found|No public key)/i
174
- raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email}: #{output}")
175
- end
168
+ # Returns [true|false] whether their is a key for the supplied email or key_id
169
+ def self.has_key?(email: nil, key_id: nil, private: false)
170
+ raise(ArgumentError, 'Either :email, or :key_id must be supplied') if email.nil? && key_id.nil?
171
+
172
+ !list_keys(email: email, key_id: key_id, private: private).empty?
173
+ end
174
+
175
+ # Returns [Array<Hash>] the list of keys.
176
+ # Each Hash consists of:
177
+ # key_length: [Integer]
178
+ # key_type: [String]
179
+ # key_id: [String]
180
+ # date: [String]
181
+ # name: [String]
182
+ # email: [String]
183
+ # Returns [] if no keys were found.
184
+ def self.list_keys(email: nil, key_id: nil, private: false)
185
+ version_check
186
+ cmd = private ? '--list-secret-keys' : '--list-keys'
187
+ out, err, status = Open3.capture3("gpg #{cmd} #{email || key_id}", binmode: true)
188
+ logger.debug { "IOStreams::Pgp.list_keys output:\n#{err}#{out}" } if logger
189
+ if status.success? && out.length > 0
190
+ # Sample output
191
+ # pub 4096R/3A5456F5 2017-06-07
192
+ # uid [ unknown] Joe Bloggs <j@bloggs.net>
193
+ # sub 4096R/2C9B240B 2017-06-07
194
+ parse_list_output(out)
195
+ else
196
+ return [] if err =~ /(key not found|No (public|secret) key)/i
197
+ raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email || key_id}: #{err}#{out}")
176
198
  end
177
199
  end
178
200
 
179
- # Returns [String] the first fingerprint for the supplied email
180
- # Returns nil if no fingerprint was found
181
- def self.fingerprint(email:)
182
- Open3.popen2e("gpg --list-keys --fingerprint --with-colons #{email}") do |stdin, out, waith_thr|
183
- output = out.read.chomp
184
- if waith_thr.value.success?
185
- output.each_line do |line|
186
- if match = line.match(/\Afpr.*::([^\:]*):\Z/)
187
- return match[1]
188
- end
189
- end
190
- nil
191
- else
192
- return if output =~ /(public key not found|No public key)/i
193
- raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email}: #{output}")
194
- end
201
+ # Extract information from the supplied key.
202
+ #
203
+ # Useful for confirming encryption keys before importing them.
204
+ #
205
+ # Returns [Array<Hash>] the list of primary keys.
206
+ # Each Hash consists of:
207
+ # key_length: [Integer]
208
+ # key_type: [String]
209
+ # key_id: [String]
210
+ # date: [String]
211
+ # name: [String]
212
+ # email: [String]
213
+ def self.key_info(key:)
214
+ version_check
215
+ out, err, status = Open3.capture3('gpg', binmode: true, stdin_data: key)
216
+ logger.debug { "IOStreams::Pgp.key_info output:\n#{err}#{out}" } if logger
217
+ if status.success? && out.length > 0
218
+ # Sample Output:
219
+ #
220
+ # pub 4096R/3A5456F5 2017-06-07
221
+ # uid Joe Bloggs <j@bloggs.net>
222
+ # sub 4096R/2C9B240B 2017-06-07
223
+ parse_list_output(out)
224
+ else
225
+ raise(Pgp::Failure, "GPG Failed extracting key details: #{err} #{out}")
195
226
  end
196
227
  end
197
228
 
198
- # Returns [String] the key for the supplied email address
229
+ # Returns [String] containing all the keys for the supplied email address.
199
230
  #
200
- # email: [String] Email address for requested key
231
+ # email: [String] Email address for requested key.
201
232
  #
202
233
  # ascii: [true|false]
203
234
  # Whether to export as ASCII text instead of binary format
204
235
  # Default: true
205
236
  #
206
- # secret: [true|false]
237
+ # private: [true|false]
207
238
  # Whether to export the private key
208
239
  # Default: false
209
- def self.export(email:, ascii: true, secret: false)
240
+ def self.export(email:, ascii: true, private: false)
241
+ version_check
210
242
  armor = ascii ? ' --armor' : nil
211
- cmd = secret ? '--export-secret-keys' : '--export'
243
+ cmd = private ? '--export-secret-keys' : '--export'
212
244
  out, err, status = Open3.capture3("gpg#{armor} #{cmd} #{email}", binmode: true)
245
+ logger.debug { "IOStreams::Pgp.export output:\n#{err}" } if logger
213
246
  if status.success? && out.length > 0
214
247
  out
215
248
  else
216
- raise(Pgp::Failure, "GPG Failed reading key: #{email}: #{err} #{out}")
249
+ raise(Pgp::Failure, "GPG Failed reading key: #{email}: #{err}")
217
250
  end
218
251
  end
219
252
 
220
253
  # Imports the supplied public/private key
221
- # Returns [String] the output returned from the import command
222
- def self.import(key)
254
+ #
255
+ # Returns [Array<Hash>] keys that were successfully imported.
256
+ # Each Hash consists of:
257
+ # key_id: [String]
258
+ # type: [String]
259
+ # name: [String]
260
+ # email: [String]
261
+ # Returns [] if the same key was previously imported.
262
+ #
263
+ # Raises Pgp::Failure if there was an issue importing any of the keys.
264
+ #
265
+ # Notes:
266
+ # * Importing a new key for the same email address does not remove the prior key if any.
267
+ # * Invalidated keys must be removed manually.
268
+ def self.import(key:)
269
+ version_check
223
270
  out, err, status = Open3.capture3('gpg --import', binmode: true, stdin_data: key)
224
- if status.success? && out.length > 0
225
- out
271
+ logger.debug { "IOStreams::Pgp.import output:\n#{err}#{out}" } if logger
272
+ if status.success? && err.length > 0
273
+ # Sample output
274
+ #
275
+ # gpg: key C16500E3: secret key imported\n"
276
+ # gpg: key C16500E3: public key "Joe Bloggs <pgp_test@iostreams.net>" imported
277
+ # gpg: Total number processed: 1
278
+ # gpg: imported: 1 (RSA: 1)
279
+ # gpg: secret keys read: 1
280
+ # gpg: secret keys imported: 1
281
+ #
282
+ # Ignores unchanged:
283
+ # gpg: key 9615D46D: \"Joe Bloggs <j@bloggs.net>\" not changed\n
284
+ results = []
285
+ secret = false
286
+ err.each_line do |line|
287
+ if line =~ /secret key imported/
288
+ secret = true
289
+ elsif match = line.match(/key\s+(\w+):\s+(\w+).+\"(.*)<(.*)>\"/)
290
+ results << {
291
+ key_id: match[1].to_s.strip,
292
+ private: secret,
293
+ name: match[3].to_s.strip,
294
+ email: match[4].to_s.strip
295
+ }
296
+ secret = false
297
+ end
298
+ end
299
+ results
226
300
  else
227
- raise(Pgp::Failure, "GPG Failed importing key: #{err} #{out}")
301
+ return [] if err =~ /already in secret keyring/
302
+ raise(Pgp::Failure, "GPG Failed importing key: #{err}#{out}")
228
303
  end
229
304
  end
230
305
 
@@ -236,11 +311,13 @@ module IOStreams
236
311
  # After importing keys, they are not trusted and the relevant trust level must be set.
237
312
  # Default: 5 : Ultimate
238
313
  def self.set_trust(email:, level: 5)
314
+ version_check
239
315
  fingerprint = fingerprint(email: email)
240
316
  return unless fingerprint
241
317
 
242
318
  trust = "#{fingerprint}:#{level + 1}:\n"
243
319
  out, err, status = Open3.capture3('gpg --import-ownertrust', stdin_data: trust)
320
+ logger.debug { "IOStreams::Pgp.set_trust output:\n#{err}#{out}" } if logger
244
321
  if status.success?
245
322
  err
246
323
  else
@@ -248,5 +325,100 @@ module IOStreams
248
325
  end
249
326
  end
250
327
 
328
+ # DEPRECATED - Use key_ids instead of fingerprints
329
+ def self.fingerprint(email:)
330
+ version_check
331
+ Open3.popen2e("gpg --list-keys --fingerprint --with-colons #{email}") do |stdin, out, waith_thr|
332
+ output = out.read.chomp
333
+ if waith_thr.value.success?
334
+ output.each_line do |line|
335
+ if match = line.match(/\Afpr.*::([^\:]*):\Z/)
336
+ return match[1]
337
+ end
338
+ end
339
+ nil
340
+ else
341
+ return if output =~ /(public key not found|No public key)/i
342
+ raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email}: #{output}")
343
+ end
344
+ end
345
+ end
346
+
347
+ def self.logger=(logger)
348
+ @logger = logger
349
+ end
350
+
351
+ # Returns [String] the version of pgp currently installed
352
+ def self.pgp_version
353
+ @pgp_version ||= begin
354
+ out, err, status = Open3.capture3("gpg --version")
355
+ logger.debug { "IOStreams::Pgp.version output:\n#{err}#{out}" } if logger
356
+ if status.success?
357
+ # Sample output
358
+ # gpg (GnuPG) 2.0.30
359
+ # libgcrypt 1.7.6
360
+ # Copyright (C) 2015 Free Software Foundation, Inc.
361
+ # License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
362
+ # This is free software: you are free to change and redistribute it.
363
+ # There is NO WARRANTY, to the extent permitted by law.
364
+ #
365
+ # Home: ~/.gnupg
366
+ # Supported algorithms:
367
+ # Pubkey: RSA, RSA, RSA, ELG, DSA
368
+ # Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
369
+ # CAMELLIA128, CAMELLIA192, CAMELLIA256
370
+ # Hash: MD5, SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
371
+ # Compression: Uncompressed, ZIP, ZLIB, BZIP2
372
+ if match = out.lines.first.match(/(\d+\.\d+.\d+)/)
373
+ match[1]
374
+ end
375
+ else
376
+ return [] if err =~ /(key not found|No (public|secret) key)/i
377
+ raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email || key_id}: #{err}#{out}")
378
+ end
379
+ end
380
+ end
381
+
382
+ private
383
+
384
+ @logger = nil
385
+
386
+ def self.logger
387
+ @logger
388
+ end
389
+
390
+ def self.version_check
391
+ raise(Pgp::UnsupportedVersion, "Version #{pgp_version} of gpg is not yet supported. You are welcome to submit a Pull Request.") if pgp_version.to_f >= 2.1
392
+ end
393
+
394
+ def self.parse_list_output(out)
395
+ results = []
396
+ hash = {}
397
+ out.each_line do |line|
398
+ if match = line.match(/(pub|sec)\s+(\d+)(.*)\/(\w+)\s+(\S+)/)
399
+ hash = {
400
+ private: match[1] == 'sec',
401
+ key_length: match[2].to_s.to_i,
402
+ key_type: match[3],
403
+ key_id: match[4],
404
+ date: (Date.parse(match[5].to_s) rescue match[5])
405
+ }
406
+ elsif match = line.match(/uid\s+(.+)<(.+)>/)
407
+ name = match[1].strip
408
+ hash[:email] = match[2].strip
409
+ if match = name.match(/(\[(.+)\])?(.+)/)
410
+ trust = match[2].to_s.strip
411
+ hash[:trust] = trust unless trust.empty?
412
+ hash[:name] = match[3].to_s.strip
413
+ else
414
+ hash[:name] = name
415
+ end
416
+ results << hash
417
+ hash = {}
418
+ end
419
+ end
420
+ results
421
+ end
422
+
251
423
  end
252
424
  end
@@ -1,3 +1,3 @@
1
1
  module IOStreams #:nodoc
2
- VERSION = '0.11.0'
2
+ VERSION = '0.12.0'
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require 'io_streams/version'
2
+ #@formatter:off
2
3
  module IOStreams
3
4
  module CSV
4
5
  autoload :Reader, 'io_streams/csv/reader'
@@ -0,0 +1,238 @@
1
+ require_relative 'test_helper'
2
+
3
+ #IOStreams::Pgp.logger = SemanticLogger[IOStreams::Pgp]
4
+
5
+ module Streams
6
+ class PgpTest < Minitest::Test
7
+ describe IOStreams::Pgp::Reader do
8
+ let :user_name do
9
+ 'Joe Bloggs'
10
+ end
11
+
12
+ let :email do
13
+ 'pgp_test@iostreams.net'
14
+ end
15
+
16
+ let :generated_key_id do
17
+ IOStreams::Pgp.generate_key(name: user_name, email: email, key_length: 1024)
18
+ end
19
+
20
+ let :public_key do
21
+ generated_key_id
22
+ IOStreams::Pgp.export(email: email)
23
+ end
24
+
25
+ let :private_key do
26
+ generated_key_id
27
+ IOStreams::Pgp.export(email: email, private: true)
28
+ end
29
+
30
+ before do
31
+ IOStreams::Pgp.delete_keys(email: email, public: true, private: true)
32
+ end
33
+
34
+ describe '.pgp_version' do
35
+ it 'returns pgp version' do
36
+ assert IOStreams::Pgp.pgp_version
37
+ end
38
+ end
39
+
40
+ describe '.generate_key' do
41
+ it 'returns the key id' do
42
+ assert generated_key_id
43
+ end
44
+ end
45
+
46
+ describe '.has_key?' do
47
+ before do
48
+ generated_key_id
49
+ end
50
+
51
+ it 'confirms public key' do
52
+ assert IOStreams::Pgp.has_key?(key_id: generated_key_id)
53
+ end
54
+
55
+ it 'confirms private key' do
56
+ assert IOStreams::Pgp.has_key?(key_id: generated_key_id, private: true)
57
+ end
58
+ end
59
+
60
+ describe '.delete_keys' do
61
+ it 'handles no keys' do
62
+ refute IOStreams::Pgp.delete_keys(email: 'random@iostreams.net', public: true, private: true)
63
+ end
64
+
65
+ it 'deletes existing keys' do
66
+ generated_key_id
67
+ assert IOStreams::Pgp.delete_keys(email: email, public: true, private: true)
68
+ end
69
+
70
+ it 'deletes just the private key' do
71
+ generated_key_id
72
+ assert IOStreams::Pgp.delete_keys(email: email, public: false, private: true)
73
+ refute IOStreams::Pgp.has_key?(key_id: generated_key_id, private: true)
74
+ assert IOStreams::Pgp.has_key?(key_id: generated_key_id, private: false)
75
+ end
76
+ end
77
+
78
+ describe '.export' do
79
+ before do
80
+ generated_key_id
81
+ end
82
+
83
+ it 'exports public keys by email' do
84
+ assert ascii_keys = IOStreams::Pgp.export(email: email)
85
+ assert ascii_keys =~ /BEGIN PGP PUBLIC KEY BLOCK/, ascii_keys
86
+ end
87
+
88
+ it 'exports private keys by email' do
89
+ assert ascii_keys = IOStreams::Pgp.export(email: email, private: true)
90
+ assert ascii_keys =~ /BEGIN PGP PRIVATE KEY BLOCK/, ascii_keys
91
+ end
92
+
93
+ it 'exports public keys as binary' do
94
+ assert keys = IOStreams::Pgp.export(email: email, ascii: false)
95
+ refute keys =~ /BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK/, keys
96
+ end
97
+
98
+ it 'exports private keys as binary' do
99
+ assert keys = IOStreams::Pgp.export(email: email, ascii: false, private: true)
100
+ refute keys =~ /BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK/, keys
101
+ end
102
+ end
103
+
104
+ describe '.list_keys' do
105
+ before do
106
+ generated_key_id
107
+ end
108
+
109
+ it 'lists public keys' do
110
+ assert keys = IOStreams::Pgp.list_keys(email: email)
111
+ assert_equal 1, keys.size
112
+ assert key = keys.first
113
+
114
+ assert_equal key[:date], Date.today
115
+ assert_equal email, key[:email]
116
+ assert_equal generated_key_id, key[:key_id]
117
+ assert_equal 1024, key[:key_length]
118
+ assert_equal 'R', key[:key_type]
119
+ assert_equal user_name, key[:name]
120
+ refute key[:private], key
121
+ assert_equal 'ultimate', key[:trust]
122
+ end
123
+
124
+ it 'lists private keys' do
125
+ assert keys = IOStreams::Pgp.list_keys(email: email, private: true)
126
+ assert_equal 1, keys.size
127
+ assert key = keys.first
128
+
129
+ assert_equal key[:date], Date.today
130
+ assert_equal email, key[:email]
131
+ assert_equal generated_key_id, key[:key_id]
132
+ assert_equal 1024, key[:key_length]
133
+ assert_equal 'R', key[:key_type]
134
+ assert_equal user_name, key[:name]
135
+ assert key[:private], key
136
+ refute key.key?(:trust)
137
+ end
138
+ end
139
+
140
+ describe '.key_info' do
141
+ it 'extracts public key info' do
142
+ assert keys = IOStreams::Pgp.key_info(key: public_key)
143
+ assert_equal 1, keys.size
144
+ assert key = keys.first
145
+
146
+ assert_equal key[:date], Date.today
147
+ assert_equal email, key[:email]
148
+ assert_equal generated_key_id, key[:key_id]
149
+ assert_equal 1024, key[:key_length]
150
+ assert_equal 'R', key[:key_type]
151
+ assert_equal user_name, key[:name]
152
+ refute key[:private], key
153
+ refute key.key?(:trust)
154
+ end
155
+
156
+ it 'extracts private key info' do
157
+ assert keys = IOStreams::Pgp.key_info(key: private_key)
158
+ assert_equal 1, keys.size
159
+ assert key = keys.first
160
+
161
+ assert_equal key[:date], Date.today
162
+ assert_equal email, key[:email]
163
+ assert_equal generated_key_id, key[:key_id]
164
+ assert_equal 1024, key[:key_length]
165
+ assert_equal 'R', key[:key_type]
166
+ assert_equal user_name, key[:name]
167
+ assert key[:private], key
168
+ refute key.key?(:trust)
169
+ end
170
+ end
171
+
172
+ describe '.import' do
173
+ it 'handle duplicate public key' do
174
+ generated_key_id
175
+ assert_equal [], IOStreams::Pgp.import(key: public_key)
176
+ end
177
+
178
+ it 'handle duplicate private key' do
179
+ generated_key_id
180
+ assert_equal [], IOStreams::Pgp.import(key: private_key)
181
+ end
182
+
183
+ describe 'without keys' do
184
+ before do
185
+ @public_key = public_key
186
+ @private_key = private_key
187
+ IOStreams::Pgp.delete_keys(email: email, public: true, private: true)
188
+ end
189
+
190
+ it 'imports ascii public key' do
191
+ assert keys = IOStreams::Pgp.import(key: @public_key)
192
+ assert_equal 1, keys.size
193
+ assert key = keys.first
194
+
195
+ assert_equal email, key[:email]
196
+ assert_equal generated_key_id, key[:key_id]
197
+ assert_equal user_name, key[:name]
198
+ refute key[:private], key
199
+ end
200
+
201
+ it 'imports ascii private key' do
202
+ assert keys = IOStreams::Pgp.import(key: @private_key)
203
+ assert_equal 1, keys.size
204
+ assert key = keys.first
205
+
206
+ assert_equal email, key[:email]
207
+ assert_equal generated_key_id, key[:key_id]
208
+ assert_equal user_name, key[:name]
209
+ assert key[:private], key
210
+ end
211
+
212
+ it 'imports binary public key' do
213
+ assert keys = IOStreams::Pgp.import(key: @public_key)
214
+ assert_equal 1, keys.size
215
+ assert key = keys.first
216
+
217
+ assert_equal email, key[:email]
218
+ assert_equal generated_key_id, key[:key_id]
219
+ assert_equal user_name, key[:name]
220
+ refute key[:private], key
221
+ end
222
+
223
+ it 'imports binary private key' do
224
+ assert keys = IOStreams::Pgp.import(key: @private_key)
225
+ assert_equal 1, keys.size
226
+ assert key = keys.first
227
+
228
+ assert_equal email, key[:email]
229
+ assert_equal generated_key_id, key[:key_id]
230
+ assert_equal user_name, key[:name]
231
+ assert key[:private], key
232
+ end
233
+ end
234
+ end
235
+
236
+ end
237
+ end
238
+ end
@@ -32,7 +32,7 @@ module Streams
32
32
  io = StringIO.new(io_string.string)
33
33
  result = nil
34
34
  begin
35
- zin = ::Zip::InputStream.new(io)
35
+ zin = ::Zip::InputStream.new(io)
36
36
  zin.get_next_entry
37
37
  result = zin.read
38
38
  ensure
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iostreams
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-01 00:00:00.000000000 Z
11
+ date: 2017-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -69,6 +69,7 @@ files:
69
69
  - test/gzip_writer_test.rb
70
70
  - test/io_streams_test.rb
71
71
  - test/pgp_reader_test.rb
72
+ - test/pgp_test.rb
72
73
  - test/pgp_writer_test.rb
73
74
  - test/test_helper.rb
74
75
  - test/xlsx_reader_test.rb
@@ -116,6 +117,7 @@ test_files:
116
117
  - test/gzip_writer_test.rb
117
118
  - test/io_streams_test.rb
118
119
  - test/pgp_reader_test.rb
120
+ - test/pgp_test.rb
119
121
  - test/pgp_writer_test.rb
120
122
  - test/test_helper.rb
121
123
  - test/xlsx_reader_test.rb