ruby-gpgme 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/gpgme.rb ADDED
@@ -0,0 +1,1462 @@
1
+ =begin rdoc
2
+ = What's this?
3
+
4
+ Ruby-GPGME is a Ruby language binding of GPGME (GnuPG Made Easy).
5
+
6
+ = Requirements
7
+
8
+ - Ruby 1.8 or later
9
+ - GPGME 1.1.2 or later http://www.gnupg.org/(en)/related_software/gpgme/index.html
10
+ - gpg-agent (optional, but recommended)
11
+
12
+ = Installation
13
+
14
+ $ ruby extconf.rb
15
+ $ make
16
+ $ make install
17
+
18
+ = Examples
19
+
20
+ <tt>examples/genkey.rb</tt>:: Generate a key pair in your keyring.
21
+ <tt>examples/keylist.rb</tt>:: List your keyring like gpg --list-keys.
22
+ <tt>examples/roundtrip.rb</tt>:: Encrypt and decrypt a plain text.
23
+ <tt>examples/sign.rb</tt>:: Create a clear text signature.
24
+ <tt>examples/verify.rb</tt>:: Verify a clear text signature given from stdin.
25
+
26
+ = API
27
+
28
+ Ruby-GPGME provides 3 levels of API. The highest level API is close
29
+ to the command line interface of GnuPG. The lowest level API is close
30
+ to the C interface of GPGME.
31
+
32
+ == The highest level API
33
+
34
+ It can be written in the highest level API to create a cleartext
35
+ signature of the plaintext from stdin as follows.
36
+
37
+ $ ruby -rgpgme -e 'GPGME.clearsign($stdin, $stdout)'
38
+
39
+ == The lowest level API
40
+
41
+ On the other hand, the same example can be rewritten in the lowest
42
+ level API as follows.
43
+
44
+ $ ruby -rgpgme -e <<End
45
+ ret = Array.new
46
+ GPGME::gpgme_new(ret)
47
+ ctx = ret.shift
48
+ GPGME::gpgme_data_new_from_fd(ret, 0)
49
+ plain = ret.shift
50
+ GPGME::gpgme_data_new_from_fd(ret, 1)
51
+ sig = ret.shift
52
+ GPGME::gpgme_op_sign(ctx, plain, sig, GPGME::SIG_MODE_CLEAR)
53
+ End
54
+
55
+ As you see, it's much harder to write a program in this API than the
56
+ highest level API. However, if you are already familier with the C
57
+ interface of GPGME and/or want to control detailed behavior of GPGME,
58
+ it might be useful.
59
+
60
+ == The mid level API
61
+
62
+ There is another API which looks object-oriented. It's easier to use
63
+ than the lowest level API though, you should first consult the highest
64
+ level API.
65
+
66
+ $ ruby -rgpgme -e <<End
67
+ ctx = GPGME::Ctx.new
68
+ plain = GPGME::Data.from_io($stdin)
69
+ sig = GPGME::Data.from_io($stdout)
70
+ ctx.sign(plain, sig, GPGME::SIG_MODE_CLEAR)
71
+ End
72
+
73
+ = License
74
+
75
+ Copyright (C) 2003,2006 Daiki Ueno
76
+
77
+ This file is a part of Ruby-GPGME.
78
+
79
+ This program is free software; you can redistribute it and/or modify
80
+ it under the terms of the GNU General Public License as published by
81
+ the Free Software Foundation; either version 2, or (at your option)
82
+ any later version.
83
+
84
+ This program is distributed in the hope that it will be useful,
85
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
86
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
87
+ GNU General Public License for more details.
88
+
89
+ You should have received a copy of the GNU General Public License
90
+ along with GNU Emacs; see the file COPYING. If not, write to the
91
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
92
+ Boston, MA 02110-1301, USA.
93
+ =end
94
+ module GPGME
95
+ end
96
+
97
+ require 'gpgme_n'
98
+ require 'gpgme/constants'
99
+
100
+ # call-seq:
101
+ # GPGME.decrypt(cipher, plain=nil, options=Hash.new){|signature| ...}
102
+ #
103
+ # <code>GPGME.decrypt</code> performs decryption.
104
+ #
105
+ # The arguments should be specified as follows.
106
+ #
107
+ # - GPGME.decrypt(<i>cipher</i>, <i>plain</i>, <i>options</i>)
108
+ # - GPGME.decrypt(<i>cipher</i>, <i>options</i>) -> <i>plain</i>
109
+ #
110
+ # All arguments except <i>cipher</i> are optional. <i>cipher</i> is
111
+ # input, and <i>plain</i> is output. If the last argument is a
112
+ # Hash, options will be read from it.
113
+ #
114
+ # An input argument is specified by an IO like object (which responds
115
+ # to <code>read</code>), a string, or a GPGME::Data object.
116
+ #
117
+ # An output argument is specified by an IO like object (which responds
118
+ # to <code>write</code>) or a GPGME::Data object.
119
+ #
120
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code>.
121
+ #
122
+ def GPGME.decrypt(cipher, *args_options)
123
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 2
124
+ args, options = split_args(args_options)
125
+ plain = args[0]
126
+
127
+ ctx = GPGME::Ctx.new(options)
128
+ cipher_data = input_data(cipher)
129
+ plain_data = output_data(plain)
130
+ begin
131
+ ctx.decrypt_verify(cipher_data, plain_data)
132
+ rescue GPGME::Error::UnsupportedAlgorithm => exc
133
+ exc.algorithm = ctx.decrypt_result.unsupported_algorithm
134
+ raise exc
135
+ rescue GPGME::Error::WrongKeyUsage => exc
136
+ exc.key_usage = ctx.decrypt_result.wrong_key_usage
137
+ raise exc
138
+ end
139
+
140
+ verify_result = ctx.verify_result
141
+ if verify_result && block_given?
142
+ verify_result.signatures.each do |signature|
143
+ yield signature
144
+ end
145
+ end
146
+
147
+ unless plain
148
+ plain_data.seek(0, IO::SEEK_SET)
149
+ plain_data.read
150
+ end
151
+ end
152
+
153
+ # call-seq:
154
+ # GPGME.verify(sig, signed_text=nil, plain=nil, options=Hash.new){|signature| ...}
155
+ #
156
+ # <code>GPGME.verify</code> verifies a signature.
157
+ #
158
+ # The arguments should be specified as follows.
159
+ #
160
+ # - GPGME.verify(<i>sig</i>, <i>signed_text</i>, <i>plain</i>, <i>options</i>)
161
+ # - GPGME.verify(<i>sig</i>, <i>signed_text</i>, <i>options</i>) -> <i>plain</i>
162
+ #
163
+ # All arguments except <i>sig</i> are optional. <i>sig</i> and
164
+ # <i>signed_text</i> are input. <i>plain</i> is output. If the last
165
+ # argument is a Hash, options will be read from it.
166
+ #
167
+ # An input argument is specified by an IO like object (which responds
168
+ # to <code>read</code>), a string, or a GPGME::Data object.
169
+ #
170
+ # An output argument is specified by an IO like object (which responds
171
+ # to <code>write</code>) or a GPGME::Data object.
172
+ #
173
+ # If <i>sig</i> is a detached signature, then the signed text should
174
+ # be provided in <i>signed_text</i> and <i>plain</i> should be
175
+ # <tt>nil</tt>. Otherwise, if <i>sig</i> is a normal (or cleartext)
176
+ # signature, <i>signed_text</i> should be <tt>nil</tt>.
177
+ #
178
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code>.
179
+ #
180
+ def GPGME.verify(sig, *args_options) # :yields: signature
181
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 3
182
+ args, options = split_args(args_options)
183
+ signed_text, plain = args
184
+
185
+ ctx = GPGME::Ctx.new(options)
186
+ sig_data = input_data(sig)
187
+ if signed_text
188
+ signed_text_data = input_data(signed_text)
189
+ plain_data = nil
190
+ else
191
+ signed_text_data = nil
192
+ plain_data = output_data(plain)
193
+ end
194
+ ctx.verify(sig_data, signed_text_data, plain_data)
195
+ ctx.verify_result.signatures.each do |signature|
196
+ yield signature
197
+ end
198
+ if !signed_text && !plain
199
+ plain_data.seek(0, IO::SEEK_SET)
200
+ plain_data.read
201
+ end
202
+ end
203
+
204
+ # call-seq:
205
+ # GPGME.sign(plain, sig=nil, options=Hash.new)
206
+ #
207
+ # <code>GPGME.sign</code> creates a signature of the plaintext.
208
+ #
209
+ # The arguments should be specified as follows.
210
+ #
211
+ # - GPGME.sign(<i>plain</i>, <i>sig</i>, <i>options</i>)
212
+ # - GPGME.sign(<i>plain</i>, <i>options</i>) -> <i>sig</i>
213
+ #
214
+ # All arguments except <i>plain</i> are optional. <i>plain</i> is
215
+ # input and <i>sig</i> is output. If the last argument is a Hash,
216
+ # options will be read from it.
217
+ #
218
+ # An input argument is specified by an IO like object (which responds
219
+ # to <code>read</code>), a string, or a GPGME::Data object.
220
+ #
221
+ # An output argument is specified by an IO like object (which responds
222
+ # to <code>write</code>) or a GPGME::Data object.
223
+ #
224
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code> except for
225
+ #
226
+ # - <tt>:signers</tt> Signing keys. If specified, it is an array
227
+ # whose elements are a GPGME::Key object or a string.
228
+ # - <tt>:mode</tt> Desired type of a signature. Either
229
+ # <tt>GPGME::SIG_MODE_NORMAL</tt> for a normal signature,
230
+ # <tt>GPGME::SIG_MODE_DETACH</tt> for a detached signature, or
231
+ # <tt>GPGME::SIG_MODE_CLEAR</tt> for a cleartext signature.
232
+ #
233
+ def GPGME.sign(plain, *args_options)
234
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 2
235
+ args, options = split_args(args_options)
236
+ sig = args[0]
237
+
238
+ ctx = GPGME::Ctx.new(options)
239
+ ctx.add_signer(*resolve_keys(options[:signers], true)) if options[:signers]
240
+ mode = options[:mode] || GPGME::SIG_MODE_NORMAL
241
+ plain_data = input_data(plain)
242
+ sig_data = output_data(sig)
243
+ begin
244
+ ctx.sign(plain_data, sig_data, mode)
245
+ rescue GPGME::Error::UnusableSecretKey => exc
246
+ exc.keys = ctx.sign_result.invalid_signers
247
+ raise exc
248
+ end
249
+
250
+ unless sig
251
+ sig_data.seek(0, IO::SEEK_SET)
252
+ sig_data.read
253
+ end
254
+ end
255
+
256
+ # call-seq:
257
+ # GPGME.clearsign(plain, sig=nil, options=Hash.new)
258
+ #
259
+ # <code>GPGME.clearsign</code> creates a cleartext signature of the plaintext.
260
+ #
261
+ # The arguments should be specified as follows.
262
+ #
263
+ # - GPGME.clearsign(<i>plain</i>, <i>sig</i>, <i>options</i>)
264
+ # - GPGME.clearsign(<i>plain</i>, <i>options</i>) -> <i>sig</i>
265
+ #
266
+ # All arguments except <i>plain</i> are optional. <i>plain</i> is
267
+ # input and <i>sig</i> is output. If the last argument is a Hash,
268
+ # options will be read from it.
269
+ #
270
+ # An input argument is specified by an IO like object (which responds
271
+ # to <code>read</code>), a string, or a GPGME::Data object.
272
+ #
273
+ # An output argument is specified by an IO like object (which responds
274
+ # to <code>write</code>) or a GPGME::Data object.
275
+ #
276
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code> except for
277
+ #
278
+ # - <tt>:signers</tt> Signing keys. If specified, it is an array
279
+ # whose elements are a GPGME::Key object or a string.
280
+ #
281
+ def GPGME.clearsign(plain, *args_options)
282
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 2
283
+ args, options = split_args(args_options)
284
+ args.push(options.merge({:mode => GPGME::SIG_MODE_CLEAR}))
285
+ GPGME.sign(plain, *args)
286
+ end
287
+
288
+ # call-seq:
289
+ # GPGME.detach_sign(plain, sig=nil, options=Hash.new)
290
+ #
291
+ # <code>GPGME.detach_sign</code> creates a detached signature of the plaintext.
292
+ #
293
+ # The arguments should be specified as follows.
294
+ #
295
+ # - GPGME.detach_sign(<i>plain</i>, <i>sig</i>, <i>options</i>)
296
+ # - GPGME.detach_sign(<i>plain</i>, <i>options</i>) -> <i>sig</i>
297
+ #
298
+ # All arguments except <i>plain</i> are optional. <i>plain</i> is
299
+ # input and <i>sig</i> is output. If the last argument is a Hash,
300
+ # options will be read from it.
301
+ #
302
+ # An input argument is specified by an IO like object (which responds
303
+ # to <code>read</code>), a string, or a GPGME::Data object.
304
+ #
305
+ # An output argument is specified by an IO like object (which responds
306
+ # to <code>write</code>) or a GPGME::Data object.
307
+ #
308
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code> except for
309
+ #
310
+ # - <tt>:signers</tt> Signing keys. If specified, it is an array
311
+ # whose elements are a GPGME::Key object or a string.
312
+ #
313
+ def GPGME.detach_sign(plain, *args_options)
314
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 2
315
+ args, options = split_args(args_options)
316
+ args.push(options.merge({:mode => GPGME::SIG_MODE_DETACH}))
317
+ GPGME.sign(plain, *args)
318
+ end
319
+
320
+ # call-seq:
321
+ # GPGME.encrypt(recipients, plain, cipher=nil, options=Hash.new)
322
+ #
323
+ # <code>GPGME.encrypt</code> performs encryption.
324
+ #
325
+ # The arguments should be specified as follows.
326
+ #
327
+ # - GPGME.encrypt(<i>recipients</i>, <i>plain</i>, <i>cipher</i>, <i>options</i>)
328
+ # - GPGME.encrypt(<i>recipients</i>, <i>plain</i>, <i>options</i>) -> <i>cipher</i>
329
+ #
330
+ # All arguments except <i>recipients</i> and <i>plain</i> are
331
+ # optional. <i>plain</i> is input and <i>cipher</i> is output. If
332
+ # the last argument is a Hash, options will be read from it.
333
+ #
334
+ # The recipients are specified by an array whose elements are a string
335
+ # or a GPGME::Key object. If <i>recipients</i> is <tt>nil</tt>, it
336
+ # performs symmetric encryption.
337
+ #
338
+ # An input argument is specified by an IO like object (which responds
339
+ # to <code>read</code>), a string, or a GPGME::Data object.
340
+ #
341
+ # An output argument is specified by an IO like object (which responds
342
+ # to <code>write</code>) or a GPGME::Data object.
343
+ #
344
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code> except for
345
+ #
346
+ # - <tt>:sign</tt> If <tt>true</tt>, it performs a combined sign and
347
+ # encrypt operation.
348
+ # - <tt>:signers</tt> Signing keys. If specified, it is an array
349
+ # whose elements are a GPGME::Key object or a string.
350
+ #
351
+ def GPGME.encrypt(recipients, plain, *args_options)
352
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 3
353
+ args, options = split_args(args_options)
354
+ cipher = args[0]
355
+ recipient_keys = recipients ? resolve_keys(recipients, false) : nil
356
+
357
+ ctx = GPGME::Ctx.new(options)
358
+ plain_data = input_data(plain)
359
+ cipher_data = output_data(cipher)
360
+ begin
361
+ if options[:sign]
362
+ if options[:signers]
363
+ ctx.add_signer(*resolve_keys(options[:signers], true))
364
+ end
365
+ ctx.encrypt_sign(recipient_keys, plain_data, cipher_data)
366
+ else
367
+ ctx.encrypt(recipient_keys, plain_data, cipher_data)
368
+ end
369
+ rescue GPGME::Error::UnusablePublicKey => exc
370
+ exc.keys = ctx.encrypt_result.invalid_recipients
371
+ raise exc
372
+ rescue GPGME::Error::UnusableSecretKey => exc
373
+ exc.keys = ctx.sign_result.invalid_signers
374
+ raise exc
375
+ end
376
+
377
+ unless cipher
378
+ cipher_data.seek(0, IO::SEEK_SET)
379
+ cipher_data.read
380
+ end
381
+ end
382
+
383
+ # call-seq:
384
+ # GPGME.list_keys(pattern=nil, secret_only=false, options=Hash.new){|key| ...}
385
+ #
386
+ # <code>GPGME.list_keys</code> iterates over the key ring.
387
+ #
388
+ # The arguments should be specified as follows.
389
+ #
390
+ # - GPGME.list_keys(<i>pattern</i>, <i>secret_only</i>, <i>options</i>)
391
+ #
392
+ # All arguments are optional. If the last argument is a Hash, options
393
+ # will be read from it.
394
+ #
395
+ # <i>pattern</i> is a string or <tt>nil</tt>. If <i>pattern</i> is
396
+ # <tt>nil</tt>, all available keys are returned. If
397
+ # <i>secret_only</i> is <tt>true</tt>, the only secret keys are
398
+ # returned.
399
+ #
400
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code>.
401
+ #
402
+ def GPGME.list_keys(*args_options) # :yields: key
403
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 3
404
+ args, options = split_args(args_options)
405
+ pattern, secret_only = args
406
+ ctx = GPGME::Ctx.new
407
+ if block_given?
408
+ ctx.each_key(pattern, secret_only || false) do |key|
409
+ yield key
410
+ end
411
+ else
412
+ ctx.keys(pattern, secret_only || false)
413
+ end
414
+ end
415
+
416
+ # call-seq:
417
+ # GPGME.export(pattern)
418
+ #
419
+ # <code>GPGME.export</code> extracts public keys from the key ring.
420
+ #
421
+ # The arguments should be specified as follows.
422
+ #
423
+ # - GPGME.export(<i>pattern</i>, <i>options</i>) -> <i>keydata</i>
424
+ # - GPGME.export(<i>pattern</i>, <i>keydata</i>, <i>options</i>)
425
+ #
426
+ # All arguments are optional. If the last argument is a Hash, options
427
+ # will be read from it.
428
+ #
429
+ # <i>pattern</i> is a string or <tt>nil</tt>. If <i>pattern</i> is
430
+ # <tt>nil</tt>, all available public keys are returned.
431
+ # <i>keydata</i> is output.
432
+ #
433
+ # An output argument is specified by an IO like object (which responds
434
+ # to <code>write</code>) or a GPGME::Data object.
435
+ #
436
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code>.
437
+ #
438
+ def GPGME.export(*args_options)
439
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 2
440
+ args, options = split_args(args_options)
441
+ pattern, key = args[0]
442
+ key_data = output_data(key)
443
+ ctx = GPGME::Ctx.new(options)
444
+ ctx.export_keys(pattern, key_data)
445
+
446
+ unless key
447
+ key_data.seek(0, IO::SEEK_SET)
448
+ key_data.read
449
+ end
450
+ end
451
+
452
+ # call-seq:
453
+ # GPGME.import(keydata)
454
+ #
455
+ # <code>GPGME.import</code> adds the keys to the key ring.
456
+ #
457
+ # The arguments should be specified as follows.
458
+ #
459
+ # - GPGME.import(<i>keydata</i>, <i>options</i>)
460
+ #
461
+ # All arguments are optional. If the last argument is a Hash, options
462
+ # will be read from it.
463
+ #
464
+ # <i>keydata</i> is input.
465
+ #
466
+ # An input argument is specified by an IO like object (which responds
467
+ # to <code>read</code>), a string, or a GPGME::Data object.
468
+ #
469
+ # <i>options</i> are same as <code>GPGME::Ctx.new()</code>.
470
+ #
471
+ def GPGME.import(*args_options)
472
+ raise ArgumentError, 'wrong number of arguments' if args_options.length > 2
473
+ args, options = split_args(args_options)
474
+ key = args[0]
475
+ key_data = input_data(key)
476
+ ctx = GPGME::Ctx.new(options)
477
+ ctx.import_keys(key_data)
478
+ ctx.import_result
479
+ end
480
+
481
+ module GPGME
482
+ # :stopdoc:
483
+ private
484
+
485
+ def split_args(args_options)
486
+ if args_options.length > 0 and args_options[-1].respond_to? :to_hash
487
+ args = args_options[0 ... -1]
488
+ options = args_options[-1].to_hash
489
+ else
490
+ args = args_options
491
+ options = Hash.new
492
+ end
493
+ [args, options]
494
+ end
495
+ module_function :split_args
496
+
497
+ def resolve_keys(keys_or_names, secret_only)
498
+ ctx = GPGME::Ctx.new
499
+ keys = Array.new
500
+ keys_or_names.each do |key_or_name|
501
+ if key_or_name.kind_of? Key
502
+ keys << key_or_name
503
+ elsif key_or_name.kind_of? String
504
+ keys += ctx.keys(key_or_name, secret_only)
505
+ end
506
+ end
507
+ keys
508
+ end
509
+ module_function :resolve_keys
510
+
511
+ def input_data(input)
512
+ if input.kind_of? GPGME::Data
513
+ input
514
+ elsif input.respond_to? :to_str
515
+ GPGME::Data.from_str(input.to_str)
516
+ elsif input.respond_to? :read
517
+ GPGME::Data.from_callbacks(IOCallbacks.new(input))
518
+ else
519
+ raise ArgumentError, input.inspect
520
+ end
521
+ end
522
+ module_function :input_data
523
+
524
+ def output_data(output)
525
+ if output.kind_of? GPGME::Data
526
+ output
527
+ elsif output.respond_to? :write
528
+ GPGME::Data.from_callbacks(IOCallbacks.new(output))
529
+ elsif !output
530
+ GPGME::Data.empty
531
+ else
532
+ raise ArgumentError, output.inspect
533
+ end
534
+ end
535
+ module_function :output_data
536
+
537
+ class IOCallbacks
538
+ def initialize(io)
539
+ @io = io
540
+ end
541
+
542
+ def read(hook, length)
543
+ @io.read(length)
544
+ end
545
+
546
+ def write(hook, buffer, length)
547
+ @io.write(buffer[0 .. length])
548
+ end
549
+
550
+ def seek(hook, offset, whence)
551
+ return @io.pos if offset == 0 && whence == IO::SEEK_CUR
552
+ @io.seek(offset, whence)
553
+ @io.pos
554
+ end
555
+ end
556
+
557
+ PROTOCOL_NAMES = {
558
+ PROTOCOL_OpenPGP => :OpenPGP,
559
+ PROTOCOL_CMS => :CMS
560
+ }
561
+
562
+ KEYLIST_MODE_NAMES = {
563
+ KEYLIST_MODE_LOCAL => :local,
564
+ KEYLIST_MODE_EXTERN => :extern,
565
+ KEYLIST_MODE_SIGS => :sigs,
566
+ KEYLIST_MODE_VALIDATE => :validate
567
+ }
568
+
569
+ VALIDITY_NAMES = {
570
+ VALIDITY_UNKNOWN => :unknown,
571
+ VALIDITY_UNDEFINED => :undefined,
572
+ VALIDITY_NEVER => :never,
573
+ VALIDITY_MARGINAL => :marginal,
574
+ VALIDITY_FULL => :full,
575
+ VALIDITY_ULTIMATE => :ultimate
576
+ }
577
+ # :startdoc:
578
+ end
579
+
580
+ module GPGME
581
+ class Error < StandardError
582
+ def initialize(error)
583
+ @error = error
584
+ end
585
+ attr_reader :error
586
+
587
+ # Return the error code.
588
+ #
589
+ # The error code indicates the type of an error, or the reason why
590
+ # an operation failed.
591
+ def code
592
+ GPGME::gpgme_err_code(@error)
593
+ end
594
+
595
+ # Return the error source.
596
+ #
597
+ # The error source has not a precisely defined meaning. Sometimes
598
+ # it is the place where the error happened, sometimes it is the
599
+ # place where an error was encoded into an error value. Usually
600
+ # the error source will give an indication to where to look for
601
+ # the problem. This is not always true, but it is attempted to
602
+ # achieve this goal.
603
+ def source
604
+ GPGME::gpgme_err_source(@error)
605
+ end
606
+
607
+ # Return a description of the error code.
608
+ def message
609
+ GPGME::gpgme_strerror(@error)
610
+ end
611
+
612
+ class General < self; end
613
+ class InvalidValue < self; end
614
+ class UnusablePublicKey < self
615
+ attr_accessor :keys
616
+ end
617
+ class UnusableSecretKey < self
618
+ attr_accessor :keys
619
+ end
620
+ class NoData < self; end
621
+ class Conflict < self; end
622
+ class NotImplemented < self; end
623
+ class DecryptFailed < self; end
624
+ class BadPassphrase < self; end
625
+ class Canceled < self; end
626
+ class InvalidEngine < self; end
627
+ class AmbiguousName < self; end
628
+ class WrongKeyUsage < self
629
+ attr_accessor :key_usage
630
+ end
631
+ class CertificateRevoked < self; end
632
+ class CertificateExpired < self; end
633
+ class NoCRLKnown < self; end
634
+ class NoPolicyMatch < self; end
635
+ class NoSecretKey < self; end
636
+ class MissingCertificate < self; end
637
+ class BadCertificateChain < self; end
638
+ class UnsupportedAlgorithm < self
639
+ attr_accessor :algorithm
640
+ end
641
+ class BadSignature < self; end
642
+ class NoPublicKey < self; end
643
+ end
644
+
645
+ def error_to_exception(err) # :nodoc:
646
+ case GPGME::gpgme_err_code(err)
647
+ when GPG_ERR_EOF
648
+ EOFError.new
649
+ when GPG_ERR_NO_ERROR
650
+ nil
651
+ when GPG_ERR_GENERAL
652
+ Error::General.new(err)
653
+ when GPG_ERR_ENOMEM
654
+ Errno::ENOMEM.new
655
+ when GPG_ERR_INV_VALUE
656
+ Error::InvalidValue.new(err)
657
+ when GPG_ERR_UNUSABLE_PUBKEY
658
+ Error::UnusablePublicKey.new(err)
659
+ when GPG_ERR_UNUSABLE_SECKEY
660
+ Error::UnusableSecretKey.new(err)
661
+ when GPG_ERR_NO_DATA
662
+ Error::NoData.new(err)
663
+ when GPG_ERR_CONFLICT
664
+ Error::Conflict.new(err)
665
+ when GPG_ERR_NOT_IMPLEMENTED
666
+ Error::NotImplemented.new(err)
667
+ when GPG_ERR_DECRYPT_FAILED
668
+ Error::DecryptFailed.new(err)
669
+ when GPG_ERR_BAD_PASSPHRASE
670
+ Error::BadPassphrase.new(err)
671
+ when GPG_ERR_CANCELED
672
+ Error::Canceled.new(err)
673
+ when GPG_ERR_INV_ENGINE
674
+ Error::InvalidEngine.new(err)
675
+ when GPG_ERR_AMBIGUOUS_NAME
676
+ Error::AmbiguousName.new(err)
677
+ when GPG_ERR_WRONG_KEY_USAGE
678
+ Error::WrongKeyUsage.new(err)
679
+ when GPG_ERR_CERT_REVOKED
680
+ Error::CertificateRevoked.new(err)
681
+ when GPG_ERR_CERT_EXPIRED
682
+ Error::CertificateExpired.new(err)
683
+ when GPG_ERR_NO_CRL_KNOWN
684
+ Error::NoCRLKnown.new(err)
685
+ when GPG_ERR_NO_POLICY_MATCH
686
+ Error::NoPolicyMatch.new(err)
687
+ when GPG_ERR_NO_SECKEY
688
+ Error::NoSecretKey.new(err)
689
+ when GPG_ERR_MISSING_CERT
690
+ Error::MissingCertificate.new(err)
691
+ when GPG_ERR_BAD_CERT_CHAIN
692
+ Error::BadCertificateChain.new(err)
693
+ when GPG_ERR_UNSUPPORTED_ALGORITHM
694
+ Error::UnsupportedAlgorithm.new(err)
695
+ when GPG_ERR_BAD_SIGNATURE
696
+ Error::BadSignature.new(err)
697
+ when GPG_ERR_NO_PUBKEY
698
+ Error::NoPublicKey.new(err)
699
+ else
700
+ Error.new(err)
701
+ end
702
+ end
703
+ module_function :error_to_exception
704
+ private :error_to_exception
705
+
706
+ class << self
707
+ alias check_version gpgme_check_version
708
+ alias pubkey_algo_name gpgme_pubkey_algo_name
709
+ alias hash_algo_name gpgme_hash_algo_name
710
+ end
711
+
712
+ def engine_check_version
713
+ err = GPGME::gpgme_engine_check_version
714
+ exc = GPGME::error_to_exception(err)
715
+ raise exc if exc
716
+ end
717
+
718
+ def engine_info
719
+ rinfo = Array.new
720
+ GPGME::gpgme_get_engine_info(rinfo)
721
+ rinfo
722
+ end
723
+ module_function :engine_info
724
+
725
+ # A class for managing data buffers.
726
+ class Data
727
+ BLOCK_SIZE = 4096
728
+
729
+ # Create a new instance.
730
+ #
731
+ # The created data types depend on <i>arg</i>. If <i>arg</i> is
732
+ # <tt>nil</tt>, it creates an instance with an empty buffer.
733
+ # Otherwise, <i>arg</i> is either a string, an IO, or a Pathname.
734
+ def self.new(arg = nil, copy = false)
735
+ if arg.nil?
736
+ return empty
737
+ elsif arg.respond_to? :to_str
738
+ return from_str(arg.to_str, copy)
739
+ elsif arg.respond_to? :to_io
740
+ return from_io(arg.to_io)
741
+ elsif arg.respond_to? :open
742
+ return from_io(arg.open)
743
+ end
744
+ end
745
+
746
+ # Create a new instance with an empty buffer.
747
+ def self.empty
748
+ rdh = Array.new
749
+ err = GPGME::gpgme_data_new(rdh)
750
+ exc = GPGME::error_to_exception(err)
751
+ raise exc if exc
752
+ rdh[0]
753
+ end
754
+
755
+ # Create a new instance with internal buffer.
756
+ def self.from_str(buf, copy = true)
757
+ rdh = Array.new
758
+ err = GPGME::gpgme_data_new_from_mem(rdh, buf, buf.length)
759
+ exc = GPGME::error_to_exception(err)
760
+ raise exc if exc
761
+ rdh[0]
762
+ end
763
+
764
+ # Create a new instance associated with a given IO.
765
+ def self.from_io(io)
766
+ from_callbacks(IOCallbacks.new(arg))
767
+ end
768
+
769
+ # Create a new instance from the specified file descriptor.
770
+ def self.from_fd(fd)
771
+ rdh = Array.new
772
+ err = GPGME::gpgme_data_new_from_fd(rdh, fd)
773
+ exc = GPGME::error_to_exception(err)
774
+ raise exc if exc
775
+ rdh[0]
776
+ end
777
+
778
+ # Create a new instance from the specified callbacks.
779
+ def self.from_callbacks(callbacks, hook_value = nil)
780
+ rdh = Array.new
781
+ err = GPGME::gpgme_data_new_from_cbs(rdh, callbacks, hook_value)
782
+ exc = GPGME::error_to_exception(err)
783
+ raise exc if exc
784
+ rdh[0]
785
+ end
786
+
787
+ # Read at most <i>length</i> bytes from the data object, or to the end
788
+ # of file if <i>length</i> is omitted or is <tt>nil</tt>.
789
+ def read(length = nil)
790
+ if length
791
+ GPGME::gpgme_data_read(self, length)
792
+ else
793
+ buf = String.new
794
+ loop do
795
+ s = GPGME::gpgme_data_read(self, BLOCK_SIZE)
796
+ break unless s
797
+ buf << s
798
+ end
799
+ buf
800
+ end
801
+ end
802
+
803
+ # Seek to a given <i>offset</i> in the data object according to the
804
+ # value of <i>whence</i>.
805
+ def seek(offset, whence = IO::SEEK_SET)
806
+ GPGME::gpgme_data_seek(self, offset, IO::SEEK_SET)
807
+ end
808
+
809
+ # Write <i>length</i> bytes from <i>buffer</i> into the data object.
810
+ def write(buffer, length = buffer.length)
811
+ GPGME::gpgme_data_write(self, buffer, length)
812
+ end
813
+
814
+ # Return the encoding of the underlying data.
815
+ def encoding
816
+ GPGME::gpgme_data_get_encoding(self)
817
+ end
818
+
819
+ # Set the encoding to a given <i>encoding</i> of the underlying
820
+ # data object.
821
+ def encoding=(encoding)
822
+ err = GPGME::gpgme_data_set_encoding(self, encoding)
823
+ exc = GPGME::error_to_exception(err)
824
+ raise exc if exc
825
+ encoding
826
+ end
827
+ end
828
+
829
+ class EngineInfo
830
+ private_class_method :new
831
+
832
+ attr_reader :protocol, :file_name, :version, :req_version
833
+ alias required_version req_version
834
+ end
835
+
836
+ # A context within which all cryptographic operations are performed.
837
+ class Ctx
838
+ # Create a new instance from the given <i>options</i>.
839
+ # <i>options</i> is a Hash whose keys are
840
+ #
841
+ # * <tt>:protocol</tt> Either <tt>PROTOCOL_OpenPGP</tt> or
842
+ # <tt>PROTOCOL_CMS</tt>.
843
+ #
844
+ # * <tt>:armor</tt> If <tt>true</tt>, the output should be ASCII armored.
845
+ #
846
+ # * <tt>:textmode</tt> If <tt>true</tt>, inform the recipient that the
847
+ # input is text.
848
+ #
849
+ # * <tt>:keylist_mode</tt> Either
850
+ # <tt>KEYLIST_MODE_LOCAL</tt>,
851
+ # <tt>KEYLIST_MODE_EXTERN</tt>,
852
+ # <tt>KEYLIST_MODE_SIGS</tt>, or
853
+ # <tt>KEYLIST_MODE_VALIDATE</tt>.
854
+ # * <tt>:passphrase_callback</tt> A callback function.
855
+ # * <tt>:passphrase_callback_value</tt> An object passed to
856
+ # passphrase_callback.
857
+ # * <tt>:progress_callback</tt> A callback function.
858
+ # * <tt>:progress_callback_value</tt> An object passed to
859
+ # progress_callback.
860
+ #
861
+ def self.new(options = Hash.new)
862
+ rctx = Array.new
863
+ err = GPGME::gpgme_new(rctx)
864
+ exc = GPGME::error_to_exception(err)
865
+ raise exc if exc
866
+ ctx = rctx[0]
867
+ options.each_pair do |key, value|
868
+ case key
869
+ when :protocol
870
+ ctx.protocol = value
871
+ when :armor
872
+ ctx.armor = value
873
+ when :textmode
874
+ ctx.textmode = value
875
+ when :keylist_mode
876
+ ctx.keylist_mode = value
877
+ when :passphrase_callback
878
+ ctx.set_passphrase_callback(value,
879
+ options[:passphrase_callback_value])
880
+ when :progress_callback
881
+ ctx.set_progress_callback(value,
882
+ options[:progress_callback_value])
883
+ end
884
+ end
885
+ ctx
886
+ end
887
+
888
+ # Set the <i>protocol</i> used within this context.
889
+ def protocol=(proto)
890
+ err = GPGME::gpgme_set_protocol(self, proto)
891
+ exc = GPGME::error_to_exception(err)
892
+ raise exc if exc
893
+ proto
894
+ end
895
+
896
+ # Return the protocol used within this context.
897
+ def protocol
898
+ GPGME::gpgme_get_protocol(self)
899
+ end
900
+
901
+ # Tell whether the output should be ASCII armored.
902
+ def armor=(yes)
903
+ GPGME::gpgme_set_armor(self, yes ? 1 : 0)
904
+ yes
905
+ end
906
+
907
+ # Return true if the output is ASCII armored.
908
+ def armor
909
+ GPGME::gpgme_get_armor(self) == 1 ? true : false
910
+ end
911
+
912
+ # Tell whether canonical text mode should be used.
913
+ def textmode=(yes)
914
+ GPGME::gpgme_set_textmode(self, yes ? 1 : 0)
915
+ yes
916
+ end
917
+
918
+ # Return true if canonical text mode is enabled.
919
+ def textmode
920
+ GPGME::gpgme_get_textmode(self) == 1 ? true : false
921
+ end
922
+
923
+ # Change the default behaviour of the key listing functions.
924
+ def keylist_mode=(mode)
925
+ GPGME::gpgme_set_keylist_mode(self, mode)
926
+ mode
927
+ end
928
+
929
+ # Return the current key listing mode.
930
+ def keylist_mode
931
+ GPGME::gpgme_get_keylist_mode(self)
932
+ end
933
+
934
+ def inspect
935
+ "#<#{self.class} protocol=#{PROTOCOL_NAMES[protocol] || protocol}, \
936
+ armor=#{armor}, textmode=#{textmode}, \
937
+ keylist_mode=#{KEYLIST_MODE_NAMES[keylist_mode]}>"
938
+ end
939
+
940
+ # Set the passphrase callback with given hook value.
941
+ # <i>passfunc</i> should respond to <code>call</code> with 5 arguments.
942
+ #
943
+ # def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
944
+ # $stderr.write("Passphrase for #{uid_hint}: ")
945
+ # $stderr.flush
946
+ # begin
947
+ # system('stty -echo')
948
+ # io = IO.for_fd(fd, 'w')
949
+ # io.puts(gets)
950
+ # io.flush
951
+ # ensure
952
+ # (0 ... $_.length).each do |i| $_[i] = ?0 end if $_
953
+ # system('stty echo')
954
+ # end
955
+ # puts
956
+ # end
957
+ #
958
+ # ctx.set_passphrase_callback(method(:passfunc))
959
+ #
960
+ def set_passphrase_callback(passfunc, hook_value = nil)
961
+ GPGME::gpgme_set_passphrase_cb(self, passfunc, hook_value)
962
+ end
963
+ alias set_passphrase_cb set_passphrase_callback
964
+
965
+ # Set the progress callback with given hook value.
966
+ # <i>progfunc</i> should respond to <code>call</code> with 5 arguments.
967
+ #
968
+ # def progfunc(hook, what, type, current, total)
969
+ # $stderr.write("#{what}: #{current}/#{total}\r")
970
+ # $stderr.flush
971
+ # end
972
+ #
973
+ # ctx.set_progress_callback(method(:progfunc))
974
+ #
975
+ def set_progress_callback(progfunc, hook_value = nil)
976
+ GPGME::gpgme_set_progress_cb(self, progfunc, hook_value)
977
+ end
978
+ alias set_progress_cb set_progress_callback
979
+
980
+ # Initiate a key listing operation for given pattern.
981
+ # If <i>pattern</i> is <tt>nil</tt>, all available keys are
982
+ # returned. If <i>secret_only</i> is <tt>true</tt>, the only
983
+ # secret keys are returned.
984
+ def keylist_start(pattern = nil, secret_only = false)
985
+ err = GPGME::gpgme_op_keylist_start(self, pattern, secret_only ? 1 : 0)
986
+ exc = GPGME::error_to_exception(err)
987
+ raise exc if exc
988
+ end
989
+
990
+ # Advance to the next key in the key listing operation.
991
+ def keylist_next
992
+ rkey = Array.new
993
+ err = GPGME::gpgme_op_keylist_next(self, rkey)
994
+ exc = GPGME::error_to_exception(err)
995
+ raise exc if exc
996
+ rkey[0]
997
+ end
998
+
999
+ # End a pending key list operation.
1000
+ def keylist_end
1001
+ err = GPGME::gpgme_op_keylist_end(self)
1002
+ exc = GPGME::error_to_exception(err)
1003
+ raise exc if exc
1004
+ end
1005
+
1006
+ # Convenient method to iterate over keys.
1007
+ # If <i>pattern</i> is <tt>nil</tt>, all available keys are
1008
+ # returned. If <i>secret_only</i> is <tt>true</tt>, the only
1009
+ # secret keys are returned.
1010
+ def each_key(pattern = nil, secret_only = false, &block) # :yields: key
1011
+ keylist_start(pattern, secret_only)
1012
+ begin
1013
+ loop do
1014
+ yield keylist_next
1015
+ end
1016
+ keys
1017
+ rescue EOFError
1018
+ # The last key in the list has already been returned.
1019
+ ensure
1020
+ keylist_end
1021
+ end
1022
+ end
1023
+ alias each_keys each_key
1024
+
1025
+ def keys(pattern = nil, secret_only = nil)
1026
+ keys = Array.new
1027
+ each_key(pattern, secret_only) do |key|
1028
+ keys << key
1029
+ end
1030
+ keys
1031
+ end
1032
+
1033
+ # Get the key with the <i>fingerprint</i>.
1034
+ # If <i>secret</i> is <tt>true</tt>, secret key is returned.
1035
+ def get_key(fingerprint, secret = false)
1036
+ rkey = Array.new
1037
+ err = GPGME::gpgme_get_key(self, fingerprint, rkey, secret ? 1 : 0)
1038
+ exc = GPGME::error_to_exception(err)
1039
+ raise exc if exc
1040
+ rkey[0]
1041
+ end
1042
+
1043
+ # Generate a new key pair.
1044
+ # <i>parms</i> is a string which looks like
1045
+ #
1046
+ # <GnupgKeyParms format="internal">
1047
+ # Key-Type: DSA
1048
+ # Key-Length: 1024
1049
+ # Subkey-Type: ELG-E
1050
+ # Subkey-Length: 1024
1051
+ # Name-Real: Joe Tester
1052
+ # Name-Comment: with stupid passphrase
1053
+ # Name-Email: joe@foo.bar
1054
+ # Expire-Date: 0
1055
+ # Passphrase: abc
1056
+ # </GnupgKeyParms>
1057
+ #
1058
+ # If <i>pubkey</i> and <i>seckey</i> are both set to <tt>nil</tt>,
1059
+ # it stores the generated key pair into your key ring.
1060
+ def generate_key(parms, pubkey = Data.new, seckey = Data.new)
1061
+ err = GPGME::gpgme_op_genkey(self, parms, pubkey, seckey)
1062
+ exc = GPGME::error_to_exception(err)
1063
+ raise exc if exc
1064
+ end
1065
+ alias genkey generate_key
1066
+
1067
+ # Extract the public keys of the recipients.
1068
+ def export_keys(recipients, keydata = Data.new)
1069
+ err = GPGME::gpgme_op_export(self, recipients, 0, keydata)
1070
+ exc = GPGME::error_to_exception(err)
1071
+ raise exc if exc
1072
+ keydata
1073
+ end
1074
+ alias export export_keys
1075
+
1076
+ # Add the keys in the data buffer to the key ring.
1077
+ def import_keys(keydata)
1078
+ err = GPGME::gpgme_op_import(self, keydata)
1079
+ exc = GPGME::error_to_exception(err)
1080
+ raise exc if exc
1081
+ end
1082
+ alias import import_keys
1083
+
1084
+ def import_result
1085
+ GPGME::gpgme_op_import_result(self)
1086
+ end
1087
+
1088
+ # Delete the key from the key ring.
1089
+ # If allow_secret is false, only public keys are deleted,
1090
+ # otherwise secret keys are deleted as well.
1091
+ def delete_key(key, allow_secret = false)
1092
+ err = GPGME::gpgme_op_delete(self, key, allow_secret ? 1 : 0)
1093
+ exc = GPGME::error_to_exception(err)
1094
+ raise exc if exc
1095
+ end
1096
+ alias delete delete_key
1097
+
1098
+ # Decrypt the ciphertext and return the plaintext.
1099
+ def decrypt(cipher, plain = Data.new)
1100
+ err = GPGME::gpgme_op_decrypt(self, cipher, plain)
1101
+ exc = GPGME::error_to_exception(err)
1102
+ raise exc if exc
1103
+ plain
1104
+ end
1105
+
1106
+ def decrypt_verify(cipher, plain = Data.new)
1107
+ err = GPGME::gpgme_op_decrypt_verify(self, cipher, plain)
1108
+ exc = GPGME::error_to_exception(err)
1109
+ raise exc if exc
1110
+ plain
1111
+ end
1112
+
1113
+ def decrypt_result
1114
+ GPGME::gpgme_op_decrypt_result(self)
1115
+ end
1116
+
1117
+ # Verify that the signature in the data object is a valid signature.
1118
+ def verify(sig, signed_text = nil, plain = Data.new)
1119
+ err = GPGME::gpgme_op_verify(self, sig, signed_text, plain)
1120
+ exc = GPGME::error_to_exception(err)
1121
+ raise exc if exc
1122
+ plain
1123
+ end
1124
+
1125
+ def verify_result
1126
+ GPGME::gpgme_op_verify_result(self)
1127
+ end
1128
+
1129
+ # Decrypt the ciphertext and return the plaintext.
1130
+ def decrypt_verify(cipher, plain = Data.new)
1131
+ err = GPGME::gpgme_op_decrypt_verify(self, cipher, plain)
1132
+ exc = GPGME::error_to_exception(err)
1133
+ raise exc if exc
1134
+ plain
1135
+ end
1136
+
1137
+ # Remove the list of signers from this object.
1138
+ def clear_signers
1139
+ GPGME::gpgme_signers_clear(self)
1140
+ end
1141
+
1142
+ # Add _keys_ to the list of signers.
1143
+ def add_signer(*keys)
1144
+ keys.each do |key|
1145
+ err = GPGME::gpgme_signers_add(self, key)
1146
+ exc = GPGME::error_to_exception(err)
1147
+ raise exc if exc
1148
+ end
1149
+ end
1150
+
1151
+ # Create a signature for the text.
1152
+ # <i>plain</i> is a data object which contains the text.
1153
+ # <i>sig</i> is a data object where the generated signature is stored.
1154
+ def sign(plain, sig = Data.new, mode = GPGME::SIG_MODE_NORMAL)
1155
+ err = GPGME::gpgme_op_sign(self, plain, sig, mode)
1156
+ exc = GPGME::error_to_exception(err)
1157
+ raise exc if exc
1158
+ sig
1159
+ end
1160
+
1161
+ def sign_result
1162
+ GPGME::gpgme_sign_result(self)
1163
+ end
1164
+
1165
+ # Encrypt the plaintext in the data object for the recipients and
1166
+ # return the ciphertext.
1167
+ def encrypt(recp, plain, cipher = Data.new, flags = 0)
1168
+ err = GPGME::gpgme_op_encrypt(self, recp, flags, plain, cipher)
1169
+ exc = GPGME::error_to_exception(err)
1170
+ raise exc if exc
1171
+ cipher
1172
+ end
1173
+
1174
+ def encrypt_result
1175
+ GPGME::gpgme_op_encrypt_result(self)
1176
+ end
1177
+
1178
+ def encrypt_sign(recp, plain, cipher = Data.new, flags = 0)
1179
+ err = GPGME::gpgme_op_encrypt_sign(self, recp, flags, plain, cipher)
1180
+ exc = GPGME::error_to_exception(err)
1181
+ raise exc if exc
1182
+ cipher
1183
+ end
1184
+ end
1185
+
1186
+ # A public or secret key.
1187
+ class Key
1188
+ private_class_method :new
1189
+
1190
+ attr_reader :keylist_mode, :protocol, :owner_trust
1191
+ attr_reader :issuer_serial, :issuer_name, :chain_id
1192
+ attr_reader :subkeys, :uids
1193
+
1194
+ def trust
1195
+ return :revoked if @revoked == 1
1196
+ return :expired if @expired == 1
1197
+ return :disabled if @disabled == 1
1198
+ return :invalid if @invalid == 1
1199
+ end
1200
+
1201
+ def capability
1202
+ caps = Array.new
1203
+ caps << :encrypt if @can_encrypt
1204
+ caps << :sign if @can_sign
1205
+ caps << :certify if @can_certify
1206
+ caps << :authenticate if @can_authenticate
1207
+ caps
1208
+ end
1209
+
1210
+ def secret?
1211
+ @secret == 1
1212
+ end
1213
+
1214
+ def inspect
1215
+ primary_subkey = subkeys[0]
1216
+ sprintf("#<#{self.class} %s %4d%c/%s %s trust=%s, owner_trust=%s, \
1217
+ capability=%s, subkeys=%s, uids=%s>",
1218
+ primary_subkey.secret? ? 'sec' : 'pub',
1219
+ primary_subkey.length,
1220
+ primary_subkey.pubkey_algo_letter,
1221
+ primary_subkey.fingerprint[-8 .. -1],
1222
+ primary_subkey.timestamp.strftime('%Y-%m-%d'),
1223
+ trust.inspect,
1224
+ VALIDITY_NAMES[@owner_trust].inspect,
1225
+ capability.inspect,
1226
+ subkeys.inspect,
1227
+ uids.inspect)
1228
+ end
1229
+
1230
+ def to_s
1231
+ primary_subkey = subkeys[0]
1232
+ s = sprintf("%s %4d%c/%s %s\n",
1233
+ primary_subkey.secret? ? 'sec' : 'pub',
1234
+ primary_subkey.length,
1235
+ primary_subkey.pubkey_algo_letter,
1236
+ primary_subkey.fingerprint[-8 .. -1],
1237
+ primary_subkey.timestamp.strftime('%Y-%m-%d'))
1238
+ uids.each do |user_id|
1239
+ s << "uid\t\t#{user_id.name} <#{user_id.email}>\n"
1240
+ end
1241
+ subkeys.each do |subkey|
1242
+ s << subkey.to_s
1243
+ end
1244
+ s
1245
+ end
1246
+ end
1247
+
1248
+ class SubKey
1249
+ private_class_method :new
1250
+
1251
+ attr_reader :pubkey_algo, :length, :keyid, :fpr
1252
+ alias fingerprint fpr
1253
+
1254
+ def trust
1255
+ return :revoked if @revoked == 1
1256
+ return :expired if @expired == 1
1257
+ return :disabled if @disabled == 1
1258
+ return :invalid if @invalid == 1
1259
+ end
1260
+
1261
+ def capability
1262
+ caps = Array.new
1263
+ caps << :encrypt if @can_encrypt
1264
+ caps << :sign if @can_sign
1265
+ caps << :certify if @can_certify
1266
+ caps << :authenticate if @can_authenticate
1267
+ caps
1268
+ end
1269
+
1270
+ def secret?
1271
+ @secret == 1
1272
+ end
1273
+
1274
+ def timestamp
1275
+ Time.at(@timestamp)
1276
+ end
1277
+
1278
+ def expires
1279
+ Time.at(@expires)
1280
+ end
1281
+
1282
+ PUBKEY_ALGO_LETTERS = {
1283
+ PK_RSA => ?R,
1284
+ PK_ELG_E => ?g,
1285
+ PK_ELG => ?G,
1286
+ PK_DSA => ?D
1287
+ }
1288
+
1289
+ def pubkey_algo_letter
1290
+ PUBKEY_ALGO_LETTERS[@pubkey_algo] || ??
1291
+ end
1292
+
1293
+ def inspect
1294
+ sprintf("#<#{self.class} %s %4d%c/%s %s trust=%s, capability=%s>",
1295
+ secret? ? 'ssc' : 'sub',
1296
+ length,
1297
+ pubkey_algo_letter,
1298
+ (@fingerprint || @keyid)[-8 .. -1],
1299
+ timestamp.strftime('%Y-%m-%d'),
1300
+ trust.inspect,
1301
+ capability.inspect)
1302
+ end
1303
+
1304
+ def to_s
1305
+ sprintf("%s %4d%c/%s %s\n",
1306
+ secret? ? 'ssc' : 'sub',
1307
+ length,
1308
+ pubkey_algo_letter,
1309
+ (@fingerprint || @keyid)[-8 .. -1],
1310
+ timestamp.strftime('%Y-%m-%d'))
1311
+ end
1312
+ end
1313
+
1314
+ class UserID
1315
+ private_class_method :new
1316
+
1317
+ attr_reader :validity, :uid, :name, :comment, :email, :signatures
1318
+
1319
+ def revoked?
1320
+ @revoked == 1
1321
+ end
1322
+
1323
+ def invalid?
1324
+ @invalid == 1
1325
+ end
1326
+
1327
+ def inspect
1328
+ "#<#{self.class} #{name} <#{email}> \
1329
+ validity=#{VALIDITY_NAMES[validity]}, signatures=#{signatures.inspect}>"
1330
+ end
1331
+ end
1332
+
1333
+ class KeySig
1334
+ private_class_method :new
1335
+
1336
+ attr_reader :pubkey_algo, :keyid
1337
+
1338
+ def revoked?
1339
+ @revoked == 1
1340
+ end
1341
+
1342
+ def expired?
1343
+ @expired == 1
1344
+ end
1345
+
1346
+ def invalid?
1347
+ @invalid == 1
1348
+ end
1349
+
1350
+ def exportable?
1351
+ @exportable == 1
1352
+ end
1353
+
1354
+ def timestamp
1355
+ Time.at(@timestamp)
1356
+ end
1357
+
1358
+ def expires
1359
+ Time.at(@expires)
1360
+ end
1361
+
1362
+ def inspect
1363
+ "#<#{self.class} #{keyid} timestamp=#{timestamp}, expires=#{expires}>"
1364
+ end
1365
+ end
1366
+
1367
+ class VerifyResult
1368
+ private_class_method :new
1369
+
1370
+ attr_reader :signatures
1371
+ end
1372
+
1373
+ class Signature
1374
+ private_class_method :new
1375
+
1376
+ attr_reader :summary, :fpr, :status, :notations
1377
+ alias fingerprint fpr
1378
+
1379
+ def timestamp
1380
+ Time.at(@timestamp)
1381
+ end
1382
+
1383
+ def exp_timestamp
1384
+ Time.at(@exp_timestamp)
1385
+ end
1386
+
1387
+ def to_s
1388
+ ctx = Ctx.new
1389
+ if from_key = ctx.get_key(fingerprint)
1390
+ from = "#{from_key.subkeys[0].keyid} #{from_key.uids[0].uid}"
1391
+ else
1392
+ from = fingerprint
1393
+ end
1394
+ case GPGME::gpgme_err_code(status)
1395
+ when GPGME::GPG_ERR_NO_ERROR
1396
+ "Good signature from #{from}"
1397
+ when GPGME::GPG_ERR_SIG_EXPIRED
1398
+ "Expired signature from #{from}"
1399
+ when GPGME::GPG_ERR_KEY_EXPIRED
1400
+ "Signature made from expired key #{from}"
1401
+ when GPGME::GPG_ERR_CERT_REVOKED
1402
+ "Signature made from revoked key #{from}"
1403
+ when GPGME::GPG_ERR_BAD_SIGNATURE
1404
+ "Bad signature from #{from}"
1405
+ when GPGME::GPG_ERR_NO_ERROR
1406
+ "No public key for #{from}"
1407
+ end
1408
+ end
1409
+ end
1410
+
1411
+ class DecryptResult
1412
+ private_class_method :new
1413
+
1414
+ attr_reader :unsupported_algorithm, :wrong_key_usage
1415
+ end
1416
+
1417
+ class SignResult
1418
+ private_class_method :new
1419
+
1420
+ attr_reader :invalid_signers, :signatures
1421
+ end
1422
+
1423
+ class EncryptResult
1424
+ private_class_method :new
1425
+
1426
+ attr_reader :invalid_recipients
1427
+ end
1428
+
1429
+ class InvalidKey
1430
+ private_class_method :new
1431
+
1432
+ attr_reader :fpr, :reason
1433
+ alias fingerprint fpr
1434
+ end
1435
+
1436
+ class NewSignature
1437
+ private_class_method :new
1438
+
1439
+ attr_reader :type, :pubkey_algo, :hash_algo, :sig_class, :fpr
1440
+ alias fingerprint fpr
1441
+
1442
+ def timestamp
1443
+ Time.at(@timestamp)
1444
+ end
1445
+ end
1446
+
1447
+ class ImportStatus
1448
+ private_class_method :new
1449
+
1450
+ attr_reader :fpr, :result, :status
1451
+ alias fingerprint fpr
1452
+ end
1453
+
1454
+ class ImportResult
1455
+ private_class_method :new
1456
+
1457
+ attr_reader :considered, :no_user_id, :imported, :imported_rsa, :unchanged
1458
+ attr_reader :new_user_ids, :new_sub_keys, :new_signatures, :new_revocations
1459
+ attr_reader :secret_read, :secret_imported, :secret_unchanged
1460
+ attr_reader :not_imported, :imports
1461
+ end
1462
+ end