openpgp 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,117 @@
1
+ require 'openssl'
2
+
3
+ module OpenPGP
4
+ ##
5
+ # OpenPGP cipher algorithm.
6
+ class Cipher
7
+ autoload :IDEA, 'openpgp/cipher/idea'
8
+ autoload :TripleDES, 'openpgp/cipher/3des'
9
+ autoload :CAST5, 'openpgp/cipher/cast5'
10
+ autoload :Blowfish, 'openpgp/cipher/blowfish'
11
+ autoload :AES128, 'openpgp/cipher/aes'
12
+ autoload :AES192, 'openpgp/cipher/aes'
13
+ autoload :AES256, 'openpgp/cipher/aes'
14
+ autoload :Twofish, 'openpgp/cipher/twofish'
15
+
16
+ DEFAULT = AES128
17
+
18
+ ##
19
+ # @see http://tools.ietf.org/html/rfc4880#section-9.2
20
+ def self.for(identifier)
21
+ case identifier
22
+ when Symbol then const_get(identifier.to_s.upcase)
23
+ when String then const_get(identifier.upcase.to_sym)
24
+ when 1 then IDEA
25
+ when 2 then TripleDES
26
+ when 3 then CAST5
27
+ when 4 then Blowfish
28
+ when 7 then AES128
29
+ when 8 then AES192
30
+ when 9 then AES256
31
+ when 10 then Twofish
32
+ end
33
+ end
34
+
35
+ attr_accessor :key, :options
36
+ attr_accessor :engine
37
+
38
+ def initialize(key, options = {})
39
+ @key = case key
40
+ when S2K then key.to_key(key_size)
41
+ else S2K::Simple.new(key).to_key(key_size)
42
+ end
43
+ @options = options
44
+ end
45
+
46
+ def self.to_i() identifier end
47
+
48
+ def self.identifier
49
+ const_get(:IDENTIFIER)
50
+ end
51
+
52
+ def identifier()
53
+ self.class.identifier
54
+ end
55
+
56
+ def key_size
57
+ @key_size ||= engine.key_len
58
+ end
59
+
60
+ def block_size
61
+ @block_size ||= engine.block_size
62
+ end
63
+
64
+ def engine
65
+ @engine ||= Engine::OpenSSL.use do
66
+ OpenSSL::Cipher.new(self.class.const_get(:ENGINE))
67
+ end
68
+ end
69
+
70
+ ##
71
+ # @see http://tools.ietf.org/html/rfc4880#section-13.9
72
+ def encrypt(plaintext)
73
+ ciphertext = String.new
74
+
75
+ engine.reset
76
+ engine.encrypt
77
+
78
+ # IV
79
+ rblock = Random.bytes(block_size)
80
+ iblock = encrypt_block("\0" * block_size)
81
+ block_size.times do |i|
82
+ ciphertext << (iblock[i] ^= rblock[i]).chr
83
+ end
84
+
85
+ # Checksum
86
+ iblock = encrypt_block(iblock)
87
+ ciphertext << (iblock[0] ^ rblock[block_size - 2]).chr
88
+ ciphertext << (iblock[1] ^ rblock[block_size - 1]).chr
89
+
90
+ # Resync
91
+ iblock = ciphertext[2..-1]
92
+
93
+ # Encrypt
94
+ plaintext.size.times do |n|
95
+ if (i = n % block_size) == 0
96
+ iblock = encrypt_block(iblock)
97
+ end
98
+ ciphertext << (iblock[i] ^= plaintext[n]).chr
99
+ end
100
+
101
+ ciphertext
102
+ end
103
+
104
+ def decrypt(ciphertext)
105
+ # TODO
106
+ engine.reset
107
+ engine.decrypt
108
+ end
109
+
110
+ def encrypt_block(block)
111
+ engine.encrypt
112
+ engine.key = @key
113
+ engine.iv = (@iv ||= "\0" * engine.iv_len)
114
+ engine.update(block) << engine.final
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class TripleDES < Cipher
5
+ IDENTIFIER = 2
6
+ ENGINE = 'DES-EDE3'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class AES < Cipher
5
+ ENGINE = 'AES-128-ECB'
6
+ end
7
+
8
+ ##
9
+ class AES128 < AES
10
+ IDENTIFIER = 7
11
+ ENGINE = 'AES-128-ECB'
12
+ end
13
+
14
+ ##
15
+ class AES192 < AES
16
+ IDENTIFIER = 8
17
+ ENGINE = 'AES-192-ECB'
18
+ end
19
+
20
+ ##
21
+ class AES256 < AES
22
+ IDENTIFIER = 9
23
+ ENGINE = 'AES-256-ECB'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class Blowfish < Cipher
5
+ IDENTIFIER = 4
6
+ ENGINE = 'BF-ECB'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class CAST5 < Cipher
5
+ IDENTIFIER = 3
6
+ ENGINE = 'CAST5-ECB'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class IDEA < Cipher
5
+ IDENTIFIER = 1
6
+ ENGINE = 'IDEA-ECB'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class Twofish < Cipher
5
+ IDENTIFIER = 10
6
+ ENGINE = nil # N/A
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,583 @@
1
+ module OpenPGP module Client
2
+ ##
3
+ # GNU Privacy Guard (GnuPG) implementation.
4
+ #
5
+ # @see http://www.gnupg.org/
6
+ class GnuPG
7
+ VERSION = OpenPGP::VERSION
8
+
9
+ attr_accessor :options
10
+
11
+ def initialize(options = {})
12
+ @options = {
13
+ :homedir => ENV['GNUPGHOME'] || '~/.gnupg',
14
+ :version => false,
15
+ }
16
+ @options.merge!(options)
17
+
18
+ if options.has_key?(:options)
19
+ parse_options_file(options[:options])
20
+ end
21
+ end
22
+
23
+ # Commands not specific to the function
24
+
25
+ ##
26
+ # Prints the program version and licensing information.
27
+ def version
28
+ puts "gpg.rb (GnuPG compatible) #{VERSION}"
29
+ puts
30
+ puts "Home: #{options[:homedir]}"
31
+ puts "Supported algorithms:"
32
+ puts "Pubkey: " # TODO
33
+ puts "Cipher: #{cipher_algorithms.keys.map(&:to_s).sort.join(', ')}"
34
+ puts "Hash: #{digest_algorithms.join(', ')}"
35
+ puts "Compression: #{compress_algorithms.keys.map(&:to_s).sort.join(', ')}"
36
+ end
37
+
38
+ ##
39
+ # Prints a usage message summarizing the most useful command-line options.
40
+ def help() end
41
+
42
+ ##
43
+ # Prints warranty information.
44
+ def warranty
45
+ raise NotImplementedError
46
+ end
47
+
48
+ ##
49
+ # Prints a list of all available options and commands.
50
+ def dump_options
51
+ self.class.public_instance_methods(false).each do |command|
52
+ if command =~ /^[\w\d_]+$/
53
+ puts "--#{command.to_s.gsub('_', '-')}"
54
+ end
55
+ end
56
+ # TODO: list available options, too.
57
+ end
58
+
59
+ # Commands to select the type of operation
60
+
61
+ ##
62
+ # Makes a signature.
63
+ def sign
64
+ raise NotImplementedError # TODO
65
+ end
66
+
67
+ ##
68
+ # Makes a clear text signature.
69
+ def clearsign
70
+ raise NotImplementedError # TODO
71
+ end
72
+
73
+ ##
74
+ # Makes a detached signature.
75
+ def detach_sign
76
+ raise NotImplementedError # TODO
77
+ end
78
+
79
+ ##
80
+ # Encrypts data.
81
+ def encrypt
82
+ raise NotImplementedError # TODO
83
+ end
84
+
85
+ ##
86
+ # Encrypts with a symmetric cipher using a passphrase.
87
+ def symmetric(file)
88
+ print OpenPGP.encrypt(File.read(file), {
89
+ :symmetric => true,
90
+ :passphrase => read_passphrase,
91
+ :cipher => cipher_algorithm,
92
+ :digest => digest_algorithm,
93
+ :compress => compress_algorithm,
94
+ })
95
+ end
96
+
97
+ ##
98
+ # Stores only (make a simple RFC1991 literal data packet).
99
+ def store(file)
100
+ Message.write(stdout) do |msg|
101
+ msg << Packet::LiteralData.new({
102
+ :format => :b,
103
+ :filename => File.basename(file),
104
+ :timestamp => File.mtime(file),
105
+ :data => File.read(file),
106
+ })
107
+ end
108
+ end
109
+
110
+ ##
111
+ # Decrypts data.
112
+ def decrypt(file)
113
+ raise NotImplementedError # TODO
114
+ end
115
+
116
+ ##
117
+ # Verifies data.
118
+ def verify(file)
119
+ raise NotImplementedError # TODO
120
+ end
121
+
122
+ ##
123
+ # Identical to --multifile --verify.
124
+ def verify_files(*files)
125
+ options[:multifile] = true
126
+ files.each { |file| verify(file) }
127
+ end
128
+
129
+ ##
130
+ # Identical to --multifile --encrypt.
131
+ def encrypt_files(*files)
132
+ options[:multifile] = true
133
+ files.each { |file| encrypt(file) }
134
+ end
135
+
136
+ ##
137
+ # Identical to --multifile --decrypt.
138
+ def decrypt_files(*files)
139
+ options[:multifile] = true
140
+ files.each { |file| decrypt(file) }
141
+ end
142
+
143
+ ##
144
+ # Lists keys from the public keyrings.
145
+ def list_keys(*keys)
146
+ list_public_keys(*keys)
147
+ end
148
+
149
+ ##
150
+ # Lists keys from the public keyrings.
151
+ def list_public_keys(*keys)
152
+ public_keyrings.each do |keyring_filename, keyring|
153
+ puts (keyring_filename = File.expand_path(keyring_filename))
154
+ print '-' * keyring_filename.size
155
+
156
+ keyring.each do |packet|
157
+ case packet
158
+ when Packet::PublicSubkey
159
+ print_key_listing(packet, :sub)
160
+ when Packet::PublicKey
161
+ print_key_listing(packet, :pub)
162
+ when Packet::UserID
163
+ print_uid_listing(packet)
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ ##
170
+ # Lists keys from the secret keyrings.
171
+ def list_secret_keys(*keys)
172
+ secret_keyrings.each do |keyring_filename, keyring|
173
+ puts (keyring_filename = File.expand_path(keyring_filename))
174
+ print '-' * keyring_filename.size
175
+
176
+ keyring.each do |packet|
177
+ case packet
178
+ when Packet::SecretSubkey
179
+ print_key_listing(packet, :ssb)
180
+ when Packet::SecretKey
181
+ print_key_listing(packet, :sec)
182
+ when Packet::UserID
183
+ print_uid_listing(packet)
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ ##
190
+ # Same as +list_keys+, but the signatures are listed too.
191
+ def list_sigs
192
+ raise NotImplementedError # TODO
193
+ end
194
+
195
+ ##
196
+ # Same as +list_sigs+, but the signatures are verified.
197
+ def check_sigs
198
+ raise NotImplementedError # TODO
199
+ end
200
+
201
+ ##
202
+ # Lists all keys (or the specified ones) along with their fingerprints.
203
+ def fingerprint(*keys)
204
+ options[:fingerprint] = true
205
+ list_keys(*keys)
206
+ end
207
+
208
+ ##
209
+ # Lists only the sequence of packets.
210
+ def list_packets
211
+ raise NotImplementedError # TODO
212
+ end
213
+
214
+ ##
215
+ # Presents a menu to work with a smartcard.
216
+ def card_edit
217
+ raise NotImplementedError # TODO
218
+ end
219
+
220
+ ##
221
+ # Shows the content of the smart card.
222
+ def card_status
223
+ raise NotImplementedError # TODO
224
+ end
225
+
226
+ ##
227
+ # Presents a menu to allow changing the PIN of a smartcard.
228
+ def change_pin
229
+ raise NotImplementedError # TODO
230
+ end
231
+
232
+ ##
233
+ # Removes key from the public keyring.
234
+ def delete_key(name)
235
+ raise NotImplementedError # TODO
236
+ end
237
+
238
+ ##
239
+ # Removes key from the secret and public keyring.
240
+ def delete_secret_key(name)
241
+ raise NotImplementedError # TODO
242
+ end
243
+
244
+ ##
245
+ # Removes key from the secret and public keyring. If a secret key exists, it will be removed first.
246
+ def delete_secret_and_public_key(name)
247
+ raise NotImplementedError # TODO
248
+ end
249
+
250
+ ##
251
+ # Exports keys from the public keyring.
252
+ def export(*keys)
253
+ raise NotImplementedError # TODO
254
+ end
255
+
256
+ ##
257
+ # Sends keys to a keyserver.
258
+ def send_keys(*keys)
259
+ raise NotImplementedError # TODO
260
+ end
261
+
262
+ ##
263
+ # Exports the secret keys.
264
+ def export_secret_keys
265
+ raise NotImplementedError # TODO
266
+ end
267
+
268
+ ##
269
+ # Exports the secret subkeys.
270
+ def export_secret_subkeys
271
+ raise NotImplementedError # TODO
272
+ end
273
+
274
+ ##
275
+ # Imports/merges keys, adding the given keys to the keyring.
276
+ def import(*keys)
277
+ raise NotImplementedError # TODO
278
+ end
279
+
280
+ ##
281
+ # Alias for +import+.
282
+ def fast_import(*keys)
283
+ import(*keys)
284
+ end
285
+
286
+ ##
287
+ # Imports the keys with the given key IDs from a keyserver.
288
+ def recv_keys(*keys)
289
+ raise NotImplementedError # TODO
290
+ end
291
+
292
+ ##
293
+ # Requests updates from a keyserver for keys that already exist on the local keyring.
294
+ def refresh_keys(*keys)
295
+ raise NotImplementedError # TODO
296
+ end
297
+
298
+ ##
299
+ # Searches the keyserver for the given names.
300
+ def search_keys(*names)
301
+ raise NotImplementedError # TODO
302
+ end
303
+
304
+ ##
305
+ # Retrieves keys located at the specified URIs.
306
+ def fetch_keys(*uris)
307
+ require 'open-uri'
308
+ raise NotImplementedError # TODO
309
+ end
310
+
311
+ ##
312
+ # Does trust database maintenance.
313
+ def update_trustdb
314
+ raise NotImplementedError # TODO
315
+ end
316
+
317
+ ##
318
+ # Does trust database maintenance without user interaction.
319
+ def check_trustdb
320
+ raise NotImplementedError # TODO
321
+ end
322
+
323
+ ##
324
+ # Sends the ownertrust values to stdout.
325
+ def export_ownertrust
326
+ raise NotImplementedError # TODO
327
+ end
328
+
329
+ ##
330
+ # Updates the trustdb with the ownertrust values stored in +files+ or stdin.
331
+ def import_ownertrust(*files)
332
+ raise NotImplementedError # TODO
333
+ end
334
+
335
+ ##
336
+ # Creates signature caches in the keyring.
337
+ def rebuild_keydb_caches
338
+ raise NotImplementedError # TODO
339
+ end
340
+
341
+ ##
342
+ # Prints message digest of algorithm +algo+ for all given files or stdin.
343
+ def print_md(algo, *files)
344
+ unless digest_algorithms.include?(algorithm = algo.to_s.upcase.to_sym)
345
+ abort "gpg: invalid hash algorithm `#{algo}'"
346
+ else
347
+ digest = Digest.for(algorithm)
348
+ end
349
+
350
+ files.each do |file|
351
+ puts (prefix = "#{file}: ") << format_fingerprint(digest.file(file).hexdigest, prefix.size)
352
+ end
353
+ end
354
+
355
+ ##
356
+ # Prints message digests of all available algorithms for all given files or stdin.
357
+ def print_mds(*files)
358
+ files.each do |file|
359
+ digest_algorithms.each do |algorithm|
360
+ algorithm = :RMD160 if algorithm == :RIPEMD160
361
+ digest = Digest.for(algorithm)
362
+
363
+ puts (prefix = "#{file}: #{algorithm.to_s.rjust(6)} = ") << format_fingerprint(digest.file(file).hexdigest, prefix.size)
364
+ end
365
+ end
366
+ end
367
+
368
+ ##
369
+ # Emits +count+ random bytes of the given quality level.
370
+ def gen_random(level = 0, count = nil)
371
+ wrong_args "--gen-random 0|1|2 [count]" unless (0..2).include?(level)
372
+
373
+ require 'openssl'
374
+ count = count.to_i if count
375
+ endless = count.nil?
376
+ while endless || count > 0
377
+ n = !endless && count < 99 ? count : 99
378
+ p = Random.bytes(n)
379
+ print options[:armor] ? [p].pack('m').delete("\n") : p
380
+ count -= n unless endless
381
+ end
382
+ puts if options[:armor]
383
+ end
384
+
385
+ ##
386
+ # Generates a prime number.
387
+ def gen_prime(mode, bits, qbits = nil)
388
+ case mode.to_i
389
+ when 1..4
390
+ raise NotImplementedError # TODO
391
+ else
392
+ wrong_args "--gen-prime mode bits [qbits]"
393
+ end
394
+ end
395
+
396
+ ##
397
+ # Packs an arbitrary input into an OpenPGP ASCII armor.
398
+ def enarmor(file)
399
+ text = OpenPGP.enarmor(File.read(file), :armored_file, :comment => 'Use "gpg --dearmor" for unpacking', :line_length => 64)
400
+ puts text # FIXME
401
+ end
402
+
403
+ ##
404
+ # Unpacks an arbitrary input from an OpenPGP ASCII armor.
405
+ def dearmor(file)
406
+ data = OpenPGP.dearmor(File.read(file))
407
+ puts data # FIXME
408
+ end
409
+
410
+ # Commands for key management
411
+
412
+ ##
413
+ # Generates a new key pair.
414
+ def gen_key
415
+ raise NotImplementedError # TODO
416
+ end
417
+
418
+ ##
419
+ # Generates a revocation certificate for the complete key.
420
+ def gen_revoke(name)
421
+ raise NotImplementedError # TODO
422
+ end
423
+
424
+ ##
425
+ # Generates a designated revocation certificate for a key.
426
+ def desig_revoke(name)
427
+ raise NotImplementedError # TODO
428
+ end
429
+
430
+ ##
431
+ # Present a menu which enables you to do most of the key management related tasks.
432
+ def edit_key(key)
433
+ raise NotImplementedError # TODO
434
+ end
435
+
436
+ ##
437
+ # Signs a public key with your secret key.
438
+ def sign_key(name)
439
+ raise NotImplementedError # TODO
440
+ end
441
+
442
+ ##
443
+ # Signs a public key with your secret key but marks it as non-exportable.
444
+ def lsign_key(name)
445
+ raise NotImplementedError # TODO
446
+ end
447
+
448
+ protected
449
+
450
+ def stdin() $stdin end
451
+ def stdout() $stdout end
452
+ def stderr() $stdout end
453
+
454
+ def read_passphrase
455
+ if options[:passphrase]
456
+ options[:passphrase]
457
+ else
458
+ # TODO
459
+ end
460
+ end
461
+
462
+ def public_keyrings
463
+ {public_keyring_file => keyring(public_keyring_file)} # FIXME
464
+ end
465
+
466
+ def secret_keyrings
467
+ {secret_keyring_file => keyring(secret_keyring_file)} # FIXME
468
+ end
469
+
470
+ def keyring(file)
471
+ OpenPGP::Message.parse(File.read(File.expand_path(file)))
472
+ end
473
+
474
+ def public_keyring_file
475
+ File.join(options[:homedir], 'pubring.gpg')
476
+ end
477
+
478
+ def secret_keyring_file
479
+ File.join(options[:homedir], 'secring.gpg')
480
+ end
481
+
482
+ def trustdb_file
483
+ File.join(options[:homedir], 'trustdb.gpg')
484
+ end
485
+
486
+ def print_key_listing(packet, type)
487
+ puts unless (is_sub_key = [:sub, :ssb].include?(type))
488
+ puts "#{type} #{format_keyspec(packet)} #{Time.at(packet.timestamp).strftime('%Y-%m-%d')}"
489
+ if options[:fingerprint] && !is_sub_key
490
+ puts " Key fingerprint = #{format_fingerprint(packet.fingerprint)}"
491
+ end
492
+ end
493
+
494
+ def print_uid_listing(packet)
495
+ puts "uid" + (' ' * 18) + packet.to_s
496
+ end
497
+
498
+ def format_keyspec(key)
499
+ "____?/#{key.key_id}" # TODO
500
+ end
501
+
502
+ def format_fingerprint(input, column = 0)
503
+ group_size = case input.size
504
+ when 32 then 2 # MD5
505
+ when 40 then 4 # SHA1, RIPEMD160
506
+ else 8 # SHA2*
507
+ end
508
+
509
+ lines, line, pos = [], '', 0
510
+ input.upcase!
511
+ input.each_byte do |c|
512
+ line << c
513
+ if (pos += 1) % group_size == 0
514
+ if (line.size + column) >= (80 - group_size)
515
+ lines << line
516
+ line, pos = '', 0
517
+ else
518
+ line << ' '
519
+ end
520
+ end
521
+ end
522
+ lines << line.strip unless line.empty?
523
+
524
+ output = lines.join($/ + (' ' * column))
525
+ output = output.insert(output.size / 2, ' ') if group_size < 8
526
+ return output
527
+ end
528
+
529
+ def parse_options_file(file)
530
+ # TODO
531
+ end
532
+
533
+ def cipher_algorithm
534
+ unless options[:cipher_algo]
535
+ Cipher::CAST5 # this is the default cipher
536
+ else
537
+ algorithm = options[:cipher_algo].to_s.upcase.to_sym
538
+ unless cipher_algorithms.has_key?(algorithm)
539
+ abort "gpg: selected cipher algorithm is invalid"
540
+ end
541
+ cipher_algorithms[algorithm]
542
+ end
543
+ end
544
+
545
+ def digest_algorithm
546
+ options[:digest_algo]
547
+ end
548
+
549
+ def compress_algorithm
550
+ options[:compress_algo]
551
+ end
552
+
553
+ def cipher_algorithms
554
+ {
555
+ :"3DES" => Cipher::TripleDES,
556
+ :CAST5 => Cipher::CAST5,
557
+ :BLOWFISH => Cipher::Blowfish,
558
+ :AES => Cipher::AES128,
559
+ :AES192 => Cipher::AES192,
560
+ :AES256 => Cipher::AES256,
561
+ #:TWOFISH => Cipher::Twofish, # N/A
562
+ }
563
+ end
564
+
565
+ def digest_algorithms
566
+ [:MD5, :SHA1, :RIPEMD160, :SHA256, :SHA384, :SHA512]
567
+ end
568
+
569
+ def compress_algorithms
570
+ {
571
+ :none => nil,
572
+ :ZIP => nil, # TODO
573
+ :ZLIB => nil, # TODO
574
+ :BZIP2 => nil, # TODO
575
+ }
576
+ end
577
+
578
+ def wrong_args(usage)
579
+ abort "usage: gpg.rb [options] #{usage}"
580
+ end
581
+ end
582
+
583
+ end end