gpgme 1.0.8 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/genkey.rb +1 -1
- data/examples/keylist.rb +2 -1
- data/examples/roundtrip.rb +7 -4
- data/examples/sign.rb +5 -3
- data/examples/verify.rb +4 -2
- data/ext/gpgme/extconf.rb +58 -0
- data/ext/gpgme/gpgme-1.3.1.tar.bz2 +0 -0
- data/{gpgme_n.c → ext/gpgme/gpgme_n.c} +8 -8
- data/ext/gpgme/libassuan-2.0.2.tar.bz2 +0 -0
- data/ext/gpgme/libgpg-error-1.10.tar.bz2 +0 -0
- data/lib/gpgme.rb +88 -1541
- data/lib/gpgme/compat.rb +2 -0
- data/lib/gpgme/constants.rb +23 -0
- data/lib/gpgme/crypto.rb +357 -0
- data/lib/gpgme/ctx.rb +462 -0
- data/lib/gpgme/data.rb +177 -0
- data/lib/gpgme/engine.rb +76 -0
- data/lib/gpgme/error.rb +66 -0
- data/lib/gpgme/io_callbacks.rb +21 -0
- data/lib/gpgme/key.rb +242 -0
- data/lib/gpgme/key_common.rb +43 -0
- data/lib/gpgme/key_sig.rb +35 -0
- data/lib/gpgme/misc.rb +66 -0
- data/lib/gpgme/signature.rb +85 -0
- data/lib/gpgme/sub_key.rb +58 -0
- data/lib/gpgme/user_id.rb +20 -0
- data/lib/gpgme/version.rb +3 -0
- data/test/crypto_test.rb +242 -0
- data/test/ctx_test.rb +426 -0
- data/test/data_test.rb +116 -0
- data/test/files/testkey_pub.gpg +52 -0
- data/test/files/testkey_sec.gpg +54 -0
- data/test/gpgme_test.rb +12 -0
- data/test/key_test.rb +201 -0
- data/test/signature_test.rb +48 -0
- data/test/sub_key_test.rb +45 -0
- data/test/support/resources.rb +516 -0
- data/test/test_helper.rb +83 -0
- metadata +144 -65
- data.tar.gz.sig +0 -3
- data/COPYING +0 -340
- data/COPYING.LESSER +0 -510
- data/Makefile +0 -172
- data/Manifest.txt +0 -18
- data/README +0 -86
- data/Rakefile +0 -17
- data/THANKS +0 -15
- data/extconf.rb +0 -26
- metadata.gz.sig +0 -0
data/lib/gpgme/compat.rb
CHANGED
data/lib/gpgme/constants.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module GPGME
|
2
|
+
|
2
3
|
ATTR_ALGO = GPGME_ATTR_ALGO
|
3
4
|
ATTR_CAN_CERTIFY = GPGME_ATTR_CAN_CERTIFY
|
4
5
|
ATTR_CAN_ENCRYPT = GPGME_ATTR_CAN_ENCRYPT
|
@@ -161,4 +162,26 @@ module GPGME
|
|
161
162
|
VALIDITY_ULTIMATE = GPGME_VALIDITY_ULTIMATE
|
162
163
|
VALIDITY_UNDEFINED = GPGME_VALIDITY_UNDEFINED
|
163
164
|
VALIDITY_UNKNOWN = GPGME_VALIDITY_UNKNOWN
|
165
|
+
|
166
|
+
PROTOCOL_NAMES = {
|
167
|
+
PROTOCOL_OpenPGP => :OpenPGP,
|
168
|
+
PROTOCOL_CMS => :CMS
|
169
|
+
}
|
170
|
+
|
171
|
+
KEYLIST_MODE_NAMES = {
|
172
|
+
KEYLIST_MODE_LOCAL => :local,
|
173
|
+
KEYLIST_MODE_EXTERN => :extern,
|
174
|
+
KEYLIST_MODE_SIGS => :sigs,
|
175
|
+
KEYLIST_MODE_VALIDATE => :validate
|
176
|
+
}
|
177
|
+
|
178
|
+
VALIDITY_NAMES = {
|
179
|
+
VALIDITY_UNKNOWN => :unknown,
|
180
|
+
VALIDITY_UNDEFINED => :undefined,
|
181
|
+
VALIDITY_NEVER => :never,
|
182
|
+
VALIDITY_MARGINAL => :marginal,
|
183
|
+
VALIDITY_FULL => :full,
|
184
|
+
VALIDITY_ULTIMATE => :ultimate
|
185
|
+
}
|
186
|
+
|
164
187
|
end
|
data/lib/gpgme/crypto.rb
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
module GPGME
|
2
|
+
|
3
|
+
##
|
4
|
+
# Different, independent methods providing the simplest possible API to
|
5
|
+
# execute crypto operations via GPG. All methods accept as options the same
|
6
|
+
# common options as {GPGME::Ctx.new}. Read the documentation for that class to
|
7
|
+
# know how to customize things further (like output stuff in ASCII armored
|
8
|
+
# format, for example).
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# crypto = GPGME::Crypto.new :armor => true
|
12
|
+
# encrypted = crypto.encrypt 'Plain text'
|
13
|
+
#
|
14
|
+
class Crypto
|
15
|
+
|
16
|
+
attr_reader :default_options
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@default_options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Encrypts an element
|
24
|
+
#
|
25
|
+
# crypto.encrypt something, options
|
26
|
+
#
|
27
|
+
# Will return a {GPGME::Data} element which can then be read.
|
28
|
+
#
|
29
|
+
# Must have some key imported, look for {GPGME::Key.import} to know how
|
30
|
+
# to import one, or the gpg documentation to know how to create one
|
31
|
+
#
|
32
|
+
# @param plain
|
33
|
+
# Must be something that can be converted into a {GPGME::Data} object, or
|
34
|
+
# a {GPGME::Data} object itself.
|
35
|
+
#
|
36
|
+
# @param [Hash] options
|
37
|
+
# The optional parameters are as follows:
|
38
|
+
# * +:recipients+ for which recipient do you want to encrypt this file. It
|
39
|
+
# will pick the first one available if none specified. Can be an array of
|
40
|
+
# identifiers or just one (a string).
|
41
|
+
# * +:symmetric+ if set to true, will ignore +:recipients+, and will perform
|
42
|
+
# a symmetric encryption. Must provide a password via the +:password+
|
43
|
+
# option.
|
44
|
+
# * +:always_trust+ if set to true specifies all the recipients to be
|
45
|
+
# trusted, thus not requiring confirmation.
|
46
|
+
# * +:sign+ if set to true, performs a combined sign and encrypt operation.
|
47
|
+
# * +:signers+ if +:sign+ specified to true, a list of additional possible
|
48
|
+
# signers. Must be an array of sign identifiers.
|
49
|
+
# * +:output+ if specified, it will write the output into it. It will be
|
50
|
+
# converted to a {GPGME::Data} object, so it could be a file for example.
|
51
|
+
# * Any other option accepted by {GPGME::Ctx.new}
|
52
|
+
#
|
53
|
+
# @return [GPGME::Data] a {GPGME::Data} object that can be read.
|
54
|
+
#
|
55
|
+
# @example returns a {GPGME::Data} that can be later encrypted
|
56
|
+
# encrypted = crypto.encrypt "Hello world!"
|
57
|
+
# encrypted.read # => Encrypted stuff
|
58
|
+
#
|
59
|
+
# @example to be decrypted by someone@example.com.
|
60
|
+
# crypto.encrypt "Hello", :recipients => "someone@example.com"
|
61
|
+
#
|
62
|
+
# @example If I didn't trust any of my keys by default
|
63
|
+
# crypto.encrypt "Hello" # => GPGME::Error::General
|
64
|
+
# crypto.encrypt "Hello", :always_trust => true # => Will work fine
|
65
|
+
#
|
66
|
+
# @example encrypted string that can be decrypted and/or *verified*
|
67
|
+
# crypto.encrypt "Hello", :sign => true
|
68
|
+
#
|
69
|
+
# @example multiple signers
|
70
|
+
# crypto.encrypt "Hello", :sign => true, :signers => "extra@example.com"
|
71
|
+
#
|
72
|
+
# @example writing to a file instead
|
73
|
+
# file = File.open("signed.sec","w+")
|
74
|
+
# crypto.encrypt "Hello", :output => file # output written to signed.sec
|
75
|
+
#
|
76
|
+
# @raise [GPGME::Error::General] when trying to encrypt with a key that is
|
77
|
+
# not trusted, and +:always_trust+ wasn't specified
|
78
|
+
#
|
79
|
+
def encrypt(plain, options = {})
|
80
|
+
options = @default_options.merge options
|
81
|
+
|
82
|
+
plain_data = Data.new(plain)
|
83
|
+
cipher_data = Data.new(options[:output])
|
84
|
+
keys = Key.find(:public, options[:recipients])
|
85
|
+
keys = nil if options[:symmetric]
|
86
|
+
|
87
|
+
flags = 0
|
88
|
+
flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust]
|
89
|
+
|
90
|
+
GPGME::Ctx.new(options) do |ctx|
|
91
|
+
begin
|
92
|
+
if options[:sign]
|
93
|
+
if options[:signers]
|
94
|
+
signers = Key.find(:public, options[:signers], :sign)
|
95
|
+
ctx.add_signer(*signers)
|
96
|
+
end
|
97
|
+
ctx.encrypt_sign(keys, plain_data, cipher_data, flags)
|
98
|
+
else
|
99
|
+
ctx.encrypt(keys, plain_data, cipher_data, flags)
|
100
|
+
end
|
101
|
+
rescue GPGME::Error::UnusablePublicKey => exc
|
102
|
+
exc.keys = ctx.encrypt_result.invalid_recipients
|
103
|
+
raise exc
|
104
|
+
rescue GPGME::Error::UnusableSecretKey => exc
|
105
|
+
exc.keys = ctx.sign_result.invalid_signers
|
106
|
+
raise exc
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
cipher_data.seek(0)
|
111
|
+
cipher_data
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Decrypts a previously encrypted element
|
116
|
+
#
|
117
|
+
# crypto.decrypt cipher, options, &block
|
118
|
+
#
|
119
|
+
# Must have the appropiate key to be able to decrypt, of course. Returns
|
120
|
+
# a {GPGME::Data} object which can then be read.
|
121
|
+
#
|
122
|
+
# @param cipher
|
123
|
+
# Must be something that can be converted into a {GPGME::Data} object,
|
124
|
+
# or a {GPGME::Data} object itself. It is the element that will be
|
125
|
+
# decrypted.
|
126
|
+
#
|
127
|
+
# @param [Hash] options
|
128
|
+
# The optional parameters:
|
129
|
+
# * +:output+ if specified, it will write the output into it. It will
|
130
|
+
# me converted to a {GPGME::Data} object, so it can also be a file,
|
131
|
+
# for example.
|
132
|
+
# * If the file was encrypted with symmentric encryption, must provide
|
133
|
+
# a :password option.
|
134
|
+
# * Any other option accepted by {GPGME::Ctx.new}
|
135
|
+
#
|
136
|
+
# @param &block
|
137
|
+
# In the block all the signatures are yielded, so one could verify them.
|
138
|
+
# See examples.
|
139
|
+
#
|
140
|
+
# @return [GPGME::Data] a {GPGME::Data} that can be read.
|
141
|
+
#
|
142
|
+
# @example Simple decrypt
|
143
|
+
# crypto.decrypt encrypted_data
|
144
|
+
#
|
145
|
+
# @example symmetric encryption, or passwored key
|
146
|
+
# crypto.decrypt encrypted_data, :password => "gpgme"
|
147
|
+
#
|
148
|
+
# @example Output to file
|
149
|
+
# file = File.open("decrypted.txt", "w+")
|
150
|
+
# crypto.decrypt encrypted_data, :output => file
|
151
|
+
#
|
152
|
+
# @example Verifying signatures
|
153
|
+
# crypto.decrypt encrypted_data do |signature|
|
154
|
+
# raise "Signature could not be verified" unless signature.valid?
|
155
|
+
# end
|
156
|
+
#
|
157
|
+
# @raise [GPGME::Error::UnsupportedAlgorithm] when the cipher was encrypted
|
158
|
+
# using an algorithm that's not supported currently.
|
159
|
+
#
|
160
|
+
# @raise [GPGME::Error::WrongKeyUsage] TODO Don't know when
|
161
|
+
#
|
162
|
+
# @raise [GPGME::Error::DecryptFailed] when the cipher was encrypted
|
163
|
+
# for a key that's not available currently.
|
164
|
+
def decrypt(cipher, options = {})
|
165
|
+
options = @default_options.merge options
|
166
|
+
|
167
|
+
plain_data = Data.new(options[:output])
|
168
|
+
cipher_data = Data.new(cipher)
|
169
|
+
|
170
|
+
GPGME::Ctx.new(options) do |ctx|
|
171
|
+
begin
|
172
|
+
ctx.decrypt_verify(cipher_data, plain_data)
|
173
|
+
rescue GPGME::Error::UnsupportedAlgorithm => exc
|
174
|
+
exc.algorithm = ctx.decrypt_result.unsupported_algorithm
|
175
|
+
raise exc
|
176
|
+
rescue GPGME::Error::WrongKeyUsage => exc
|
177
|
+
exc.key_usage = ctx.decrypt_result.wrong_key_usage
|
178
|
+
raise exc
|
179
|
+
end
|
180
|
+
|
181
|
+
verify_result = ctx.verify_result
|
182
|
+
if verify_result && block_given?
|
183
|
+
verify_result.signatures.each do |signature|
|
184
|
+
yield signature
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
plain_data.seek(0)
|
191
|
+
plain_data
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# Creates a signature of a text
|
196
|
+
#
|
197
|
+
# crypto.sign text, options
|
198
|
+
#
|
199
|
+
# Must have the appropiate key to be able to decrypt, of course. Returns
|
200
|
+
# a {GPGME::Data} object which can then be read.
|
201
|
+
#
|
202
|
+
# @param text
|
203
|
+
# The object that will be signed. Must be something that can be converted
|
204
|
+
# to {GPGME::Data}.
|
205
|
+
#
|
206
|
+
# @param [Hash] options
|
207
|
+
# Optional parameters.
|
208
|
+
# * +:signer+ sign identifier to sign the text with. Will use the first
|
209
|
+
# key it finds if none specified.
|
210
|
+
# * +:output+ if specified, it will write the output into it. It will be
|
211
|
+
# converted to a {GPGME::Data} object, so it could be a file for example.
|
212
|
+
# * +:mode+ Desired type of signature. Options are:
|
213
|
+
# - +GPGME::SIG_MODE_NORMAL+ for a normal signature. The default one if
|
214
|
+
# not specified.
|
215
|
+
# - +GPGME::SIG_MODE_DETACH+ for a detached signature
|
216
|
+
# - +GPGME::SIG_MODE_CLEAR+ for a cleartext signature
|
217
|
+
# * Any other option accepted by {GPGME::Ctx.new}
|
218
|
+
#
|
219
|
+
# @return [GPGME::Data] a {GPGME::Data} that can be read.
|
220
|
+
#
|
221
|
+
# @example normal sign
|
222
|
+
# crypto.sign "Hi there"
|
223
|
+
#
|
224
|
+
# @example outputing to a file
|
225
|
+
# file = File.open("text.sign", "w+")
|
226
|
+
# crypto.sign "Hi there", :options => file
|
227
|
+
#
|
228
|
+
# @example doing a detached signature
|
229
|
+
# crypto.sign "Hi there", :mode => GPGME::SIG_MODE_DETACH
|
230
|
+
#
|
231
|
+
# @example specifying the signer
|
232
|
+
# crypto.sign "Hi there", :signer => "mrsimo@example.com"
|
233
|
+
#
|
234
|
+
# @raise [GPGME::Error::UnusableSecretKey] TODO don't know when
|
235
|
+
def sign(text, options = {})
|
236
|
+
options = @default_options.merge options
|
237
|
+
|
238
|
+
plain = Data.new(text)
|
239
|
+
output = Data.new(options[:output])
|
240
|
+
mode = options[:mode] || GPGME::SIG_MODE_NORMAL
|
241
|
+
|
242
|
+
GPGME::Ctx.new(options) do |ctx|
|
243
|
+
if options[:signer]
|
244
|
+
signers = Key.find(:secret, options[:signer], :sign)
|
245
|
+
ctx.add_signer(*signers)
|
246
|
+
end
|
247
|
+
|
248
|
+
begin
|
249
|
+
ctx.sign(plain, output, mode)
|
250
|
+
rescue GPGME::Error::UnusableSecretKey => exc
|
251
|
+
exc.keys = ctx.sign_result.invalid_signers
|
252
|
+
raise exc
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
output.seek(0)
|
257
|
+
output
|
258
|
+
end
|
259
|
+
|
260
|
+
# Verifies a previously signed element
|
261
|
+
#
|
262
|
+
# crypto.verify sig, options, &block
|
263
|
+
#
|
264
|
+
# Must have the proper keys available.
|
265
|
+
#
|
266
|
+
# @param sig
|
267
|
+
# The signature itself. Must be possible to convert into a {GPGME::Data}
|
268
|
+
# object, so can be a file.
|
269
|
+
#
|
270
|
+
# @param [Hash] options
|
271
|
+
# * +:signed_text+ if the sign is detached, then must be the plain text
|
272
|
+
# for which the signature was created.
|
273
|
+
# * +:output+ where to store the result of the signature. Will be
|
274
|
+
# converted to a {GPGME::Data} object.
|
275
|
+
# * Any other option accepted by {GPGME::Ctx.new}
|
276
|
+
#
|
277
|
+
# @param &block
|
278
|
+
# In the block all the signatures are yielded, so one could verify them.
|
279
|
+
# See examples.
|
280
|
+
#
|
281
|
+
# @return [GPGME::Data] unless the sign is detached, the {GPGME::Data}
|
282
|
+
# object with the plain text. If the sign is detached, will return nil.
|
283
|
+
#
|
284
|
+
# @example simple verification
|
285
|
+
# sign = crypto.sign("Hi there")
|
286
|
+
# data = crypto.verify(sign) { |signature| signature.valid? }
|
287
|
+
# data.read # => "Hi there"
|
288
|
+
#
|
289
|
+
# @example saving output to file
|
290
|
+
# sign = crypto.sign("Hi there")
|
291
|
+
# out = File.open("test.asc", "w+")
|
292
|
+
# crypto.verify(sign, :output => out) {|signature| signature.valid?}
|
293
|
+
# out.read # => "Hi there"
|
294
|
+
#
|
295
|
+
# @example verifying a detached signature
|
296
|
+
# sign = crypto.detach_sign("Hi there")
|
297
|
+
# # Will fail
|
298
|
+
# crypto.verify(sign) { |signature| signature.valid? }
|
299
|
+
# # Will succeed
|
300
|
+
# crypto.verify(sign, :signed_text => "hi there") do |signature|
|
301
|
+
# signature.valid?
|
302
|
+
# end
|
303
|
+
#
|
304
|
+
def verify(sig, options = {})
|
305
|
+
options = @default_options.merge options
|
306
|
+
|
307
|
+
sig = Data.new(sig)
|
308
|
+
signed_text = Data.new(options[:signed_text])
|
309
|
+
output = Data.new(options[:output]) unless options[:signed_text]
|
310
|
+
|
311
|
+
GPGME::Ctx.new(options) do |ctx|
|
312
|
+
ctx.verify(sig, signed_text, output)
|
313
|
+
ctx.verify_result.signatures.each do |signature|
|
314
|
+
yield signature
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
if output
|
319
|
+
output.seek(0)
|
320
|
+
output
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# Clearsigns an element
|
325
|
+
#
|
326
|
+
# crypto.clearsign text, options
|
327
|
+
#
|
328
|
+
# Same functionality of {.sign} only doing clearsigns by default.
|
329
|
+
#
|
330
|
+
def clearsign(text, options = {})
|
331
|
+
sign text, options.merge(:mode => GPGME::SIG_MODE_CLEAR)
|
332
|
+
end
|
333
|
+
|
334
|
+
# Creates a detached signature of an element
|
335
|
+
#
|
336
|
+
# crypto.detach_sign text, options
|
337
|
+
#
|
338
|
+
# Same functionality of {.sign} only doing detached signs by default.
|
339
|
+
#
|
340
|
+
def detach_sign(text, options = {})
|
341
|
+
sign text, options.merge(:mode => GPGME::SIG_MODE_DETACH)
|
342
|
+
end
|
343
|
+
|
344
|
+
##
|
345
|
+
# Allows calling of methods directly in the module without the need to
|
346
|
+
# create a new instance.
|
347
|
+
def self.method_missing(method, *args, &block)
|
348
|
+
if GPGME::Crypto.instance_methods(false).include?(method)
|
349
|
+
crypto = GPGME::Crypto.new
|
350
|
+
crypto.send method, *args, &block
|
351
|
+
else
|
352
|
+
super
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
end # module Crypto
|
357
|
+
end # module GPGME
|
data/lib/gpgme/ctx.rb
ADDED
@@ -0,0 +1,462 @@
|
|
1
|
+
module GPGME
|
2
|
+
|
3
|
+
##
|
4
|
+
# A context within which all cryptographic operations are performed.
|
5
|
+
#
|
6
|
+
# More operations can be done which are not available in the higher level
|
7
|
+
# API. Note how to create a new instance of this class in {GPGME::Ctx.new}.
|
8
|
+
#
|
9
|
+
class Ctx
|
10
|
+
|
11
|
+
##
|
12
|
+
# Create a new instance from the given +options+. Must be released either
|
13
|
+
# executing the operations inside a block, or executing {GPGME::Ctx#release}
|
14
|
+
# afterwards.
|
15
|
+
#
|
16
|
+
# @param [Hash] options
|
17
|
+
# The optional parameters are as follows:
|
18
|
+
# * +:protocol+ Either +PROTOCOL_OpenPGP+ or +PROTOCOL_CMS+.
|
19
|
+
# * +:armor+ will return ASCII armored outputs if specified true.
|
20
|
+
# * +:textmode+ if +true+, inform the recipient that the input is text.
|
21
|
+
# * +:keylist_mode+ One of: +KEYLIST_MODE_LOCAL+, +KEYLIST_MODE_EXTERN+,
|
22
|
+
# +KEYLIST_MODE_SIGS+ or +KEYLIST_MODE_VALIDATE+.
|
23
|
+
# * +:password+ password of the passphrased password being used.
|
24
|
+
# * +:passphrase_callback+ A callback function. See {#set_passphrase_callback}.
|
25
|
+
# * +:passphrase_callback_value+ An object passed to passphrase_callback.
|
26
|
+
# * +:progress_callback+ A callback function. See {#set_progress_callback}.
|
27
|
+
# * +:progress_callback_value+ An object passed to progress_callback.
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# ctx = GPGME::Ctx.new
|
31
|
+
# # operate on ctx
|
32
|
+
# ctx.release
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# GPGME::Ctx.new do |ctx|
|
36
|
+
# # operate on ctx
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
def self.new(options = {})
|
40
|
+
rctx = []
|
41
|
+
err = GPGME::gpgme_new(rctx)
|
42
|
+
exc = GPGME::error_to_exception(err)
|
43
|
+
raise exc if exc
|
44
|
+
ctx = rctx[0]
|
45
|
+
|
46
|
+
ctx.protocol = options[:protocol] if options[:protocol]
|
47
|
+
ctx.armor = options[:armor] if options[:armor]
|
48
|
+
ctx.textmode = options[:textmode] if options[:textmode]
|
49
|
+
ctx.keylist_mode = options[:keylist_mode] if options[:keylist_mode]
|
50
|
+
|
51
|
+
if options[:password]
|
52
|
+
ctx.set_passphrase_callback GPGME::Ctx.method(:pass_function),
|
53
|
+
options[:password]
|
54
|
+
else
|
55
|
+
if options[:passphrase_callback]
|
56
|
+
ctx.set_passphrase_callback options[:passphrase_callback],
|
57
|
+
options[:passphrase_callback_value]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
if options[:progress_callback]
|
61
|
+
ctx.set_progress_callback options[:progress_callback],
|
62
|
+
options[:progress_callback_value]
|
63
|
+
end
|
64
|
+
|
65
|
+
if block_given?
|
66
|
+
begin
|
67
|
+
yield ctx
|
68
|
+
ensure
|
69
|
+
GPGME::gpgme_release(ctx)
|
70
|
+
end
|
71
|
+
else
|
72
|
+
ctx
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
##
|
78
|
+
# Releases the Ctx instance. Must be called if it was initialized without
|
79
|
+
# a block.
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# ctx = GPGME::Ctx.new
|
83
|
+
# # operate on ctx
|
84
|
+
# ctx.release
|
85
|
+
#
|
86
|
+
def release
|
87
|
+
GPGME::gpgme_release(self)
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Getters and setters
|
92
|
+
##
|
93
|
+
|
94
|
+
# Set the +protocol+ used within this context. See {GPGME::Ctx.new} for
|
95
|
+
# possible values.
|
96
|
+
def protocol=(proto)
|
97
|
+
err = GPGME::gpgme_set_protocol(self, proto)
|
98
|
+
exc = GPGME::error_to_exception(err)
|
99
|
+
raise exc if exc
|
100
|
+
proto
|
101
|
+
end
|
102
|
+
|
103
|
+
# Return the +protocol+ used within this context.
|
104
|
+
def protocol
|
105
|
+
GPGME::gpgme_get_protocol(self)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Tell whether the output should be ASCII armored.
|
109
|
+
def armor=(yes)
|
110
|
+
GPGME::gpgme_set_armor(self, yes ? 1 : 0)
|
111
|
+
yes
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return true if the output is ASCII armored.
|
115
|
+
def armor
|
116
|
+
GPGME::gpgme_get_armor(self) == 1 ? true : false
|
117
|
+
end
|
118
|
+
|
119
|
+
# Tell whether canonical text mode should be used.
|
120
|
+
def textmode=(yes)
|
121
|
+
GPGME::gpgme_set_textmode(self, yes ? 1 : 0)
|
122
|
+
yes
|
123
|
+
end
|
124
|
+
|
125
|
+
# Return true if canonical text mode is enabled.
|
126
|
+
def textmode
|
127
|
+
GPGME::gpgme_get_textmode(self) == 1 ? true : false
|
128
|
+
end
|
129
|
+
|
130
|
+
# Change the default behaviour of the key listing functions.
|
131
|
+
def keylist_mode=(mode)
|
132
|
+
GPGME::gpgme_set_keylist_mode(self, mode)
|
133
|
+
mode
|
134
|
+
end
|
135
|
+
|
136
|
+
# Return the current key listing mode.
|
137
|
+
def keylist_mode
|
138
|
+
GPGME::gpgme_get_keylist_mode(self)
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Passphrase and progress callbacks
|
143
|
+
##
|
144
|
+
|
145
|
+
# Set the passphrase callback with given hook value.
|
146
|
+
# +passfunc+ should respond to +call+ with 5 arguments.
|
147
|
+
#
|
148
|
+
# * +obj+ the parameter +:passphrase_callback_value+ passed when creating
|
149
|
+
# the {GPGME::Ctx} object.
|
150
|
+
# * +uid_hint+ hint as to what key are we asking the password for. Ex:
|
151
|
+
#
|
152
|
+
# +CFB3294A50C2CFD7 Albert Llop <mrsimo@example.com>+
|
153
|
+
#
|
154
|
+
# * +passphrase_info+
|
155
|
+
# * +prev_was_bad+ 0 if it's the first time the password is being asked,
|
156
|
+
# 1 otherwise.
|
157
|
+
# * +fd+ file descriptor where the password must be written too.
|
158
|
+
#
|
159
|
+
# Expects a Method object which can be obtained by the +method+ method
|
160
|
+
# (really..).
|
161
|
+
#
|
162
|
+
# ctx.set_passphrase_callback(MyModule.method(:passfunc))
|
163
|
+
#
|
164
|
+
# @example this method will simply return +maria+ as password.
|
165
|
+
# def pass_function(obj, uid_hint, passphrase_info, prev_was_bad, fd)
|
166
|
+
# io = IO.for_fd(fd, 'w')
|
167
|
+
# io.puts "maria"
|
168
|
+
# io.flush
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# @example this will interactively ask for the password
|
172
|
+
# def passfunc(obj, uid_hint, passphrase_info, prev_was_bad, fd)
|
173
|
+
# $stderr.write("Passphrase for #{uid_hint}: ")
|
174
|
+
# $stderr.flush
|
175
|
+
# begin
|
176
|
+
# system('stty -echo')
|
177
|
+
# io = IO.for_fd(fd, 'w')
|
178
|
+
# io.puts(gets)
|
179
|
+
# io.flush
|
180
|
+
# ensure
|
181
|
+
# (0 ... $_.length).each do |i| $_[i] = ?0 end if $_
|
182
|
+
# system('stty echo')
|
183
|
+
# end
|
184
|
+
# $stderr.puts
|
185
|
+
# end
|
186
|
+
#
|
187
|
+
def set_passphrase_callback(passfunc, hook_value = nil)
|
188
|
+
GPGME::gpgme_set_passphrase_cb(self, passfunc, hook_value)
|
189
|
+
end
|
190
|
+
alias set_passphrase_cb set_passphrase_callback
|
191
|
+
|
192
|
+
# Set the progress callback with given hook value.
|
193
|
+
# <i>progfunc</i> should respond to <code>call</code> with 5 arguments.
|
194
|
+
#
|
195
|
+
# def progfunc(hook, what, type, current, total)
|
196
|
+
# $stderr.write("#{what}: #{current}/#{total}\r")
|
197
|
+
# $stderr.flush
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# ctx.set_progress_callback(method(:progfunc))
|
201
|
+
#
|
202
|
+
def set_progress_callback(progfunc, hook_value = nil)
|
203
|
+
GPGME::gpgme_set_progress_cb(self, progfunc, hook_value)
|
204
|
+
end
|
205
|
+
alias set_progress_cb set_progress_callback
|
206
|
+
|
207
|
+
##
|
208
|
+
# Searching and iterating through keys. Used by {GPGME::Key.find}
|
209
|
+
##
|
210
|
+
|
211
|
+
# Initiate a key listing operation for given pattern. If +pattern+ is
|
212
|
+
# +nil+, all available keys are returned. If +secret_only<+ is +true+,
|
213
|
+
# only secret keys are returned.
|
214
|
+
#
|
215
|
+
# Used by {GPGME::Ctx#each_key}
|
216
|
+
def keylist_start(pattern = nil, secret_only = false)
|
217
|
+
err = GPGME::gpgme_op_keylist_start(self, pattern, secret_only ? 1 : 0)
|
218
|
+
exc = GPGME::error_to_exception(err)
|
219
|
+
raise exc if exc
|
220
|
+
end
|
221
|
+
|
222
|
+
# Advance to the next key in the key listing operation.
|
223
|
+
#
|
224
|
+
# Used by {GPGME::Ctx#each_key}
|
225
|
+
def keylist_next
|
226
|
+
rkey = []
|
227
|
+
err = GPGME::gpgme_op_keylist_next(self, rkey)
|
228
|
+
exc = GPGME::error_to_exception(err)
|
229
|
+
raise exc if exc
|
230
|
+
rkey[0]
|
231
|
+
end
|
232
|
+
|
233
|
+
# End a pending key list operation.
|
234
|
+
#
|
235
|
+
# Used by {GPGME::Ctx#each_key}
|
236
|
+
def keylist_end
|
237
|
+
err = GPGME::gpgme_op_keylist_end(self)
|
238
|
+
exc = GPGME::error_to_exception(err)
|
239
|
+
raise exc if exc
|
240
|
+
end
|
241
|
+
|
242
|
+
# Convenient method to iterate over keys.
|
243
|
+
#
|
244
|
+
# If +pattern+ is +nil+, all available keys are returned. If +secret_only+
|
245
|
+
# is +true+, only secret keys are returned.
|
246
|
+
#
|
247
|
+
# See {GPGME::Key.find} for an example of how to use, or for an easier way
|
248
|
+
# to use.
|
249
|
+
def each_key(pattern = nil, secret_only = false, &block)
|
250
|
+
keylist_start(pattern, secret_only)
|
251
|
+
begin
|
252
|
+
loop { yield keylist_next }
|
253
|
+
rescue EOFError
|
254
|
+
# The last key in the list has already been returned.
|
255
|
+
ensure
|
256
|
+
keylist_end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
alias each_keys each_key
|
260
|
+
|
261
|
+
# Returns the keys that match the +pattern+, or all if +pattern+ is nil.
|
262
|
+
# Returns only secret keys if +secret_only+ is true.
|
263
|
+
def keys(pattern = nil, secret_only = nil)
|
264
|
+
keys = []
|
265
|
+
each_key(pattern, secret_only) do |key|
|
266
|
+
keys << key
|
267
|
+
end
|
268
|
+
keys
|
269
|
+
end
|
270
|
+
|
271
|
+
# Get the key with the +fingerprint+.
|
272
|
+
# If +secret+ is +true+, secret key is returned.
|
273
|
+
def get_key(fingerprint, secret = false)
|
274
|
+
rkey = []
|
275
|
+
err = GPGME::gpgme_get_key(self, fingerprint, rkey, secret ? 1 : 0)
|
276
|
+
exc = GPGME::error_to_exception(err)
|
277
|
+
raise exc if exc
|
278
|
+
rkey[0]
|
279
|
+
end
|
280
|
+
|
281
|
+
##
|
282
|
+
# Import/export and generation/deletion of keys
|
283
|
+
##
|
284
|
+
|
285
|
+
# Generate a new key pair.
|
286
|
+
# +parms+ is a string which looks like
|
287
|
+
#
|
288
|
+
# <GnupgKeyParms format="internal">
|
289
|
+
# Key-Type: DSA
|
290
|
+
# Key-Length: 1024
|
291
|
+
# Subkey-Type: ELG-E
|
292
|
+
# Subkey-Length: 1024
|
293
|
+
# Name-Real: Joe Tester
|
294
|
+
# Name-Comment: with stupid passphrase
|
295
|
+
# Name-Email: joe@foo.bar
|
296
|
+
# Expire-Date: 0
|
297
|
+
# Passphrase: abc
|
298
|
+
# </GnupgKeyParms>
|
299
|
+
#
|
300
|
+
# If +pubkey+ and +seckey+ are both set to +nil+, it stores the generated
|
301
|
+
# key pair into your key ring.
|
302
|
+
def generate_key(parms, pubkey = nil, seckey = nil)
|
303
|
+
err = GPGME::gpgme_op_genkey(self, parms, pubkey, seckey)
|
304
|
+
exc = GPGME::error_to_exception(err)
|
305
|
+
raise exc if exc
|
306
|
+
end
|
307
|
+
alias genkey generate_key
|
308
|
+
|
309
|
+
# Extract the public keys that match the +recipients+. Returns a
|
310
|
+
# {GPGME::Data} object which is not rewinded (should do +seek(0)+
|
311
|
+
# before reading).
|
312
|
+
#
|
313
|
+
# Private keys cannot be exported due to GPGME restrictions.
|
314
|
+
#
|
315
|
+
# If passed, the key will be exported to +keydata+, which must be
|
316
|
+
# a {GPGME::Data} object.
|
317
|
+
def export_keys(recipients, keydata = Data.new)
|
318
|
+
err = GPGME::gpgme_op_export(self, recipients, 0, keydata)
|
319
|
+
exc = GPGME::error_to_exception(err)
|
320
|
+
raise exc if exc
|
321
|
+
keydata
|
322
|
+
end
|
323
|
+
alias export export_keys
|
324
|
+
|
325
|
+
# Add the keys in the data buffer to the key ring.
|
326
|
+
def import_keys(keydata)
|
327
|
+
err = GPGME::gpgme_op_import(self, keydata)
|
328
|
+
exc = GPGME::error_to_exception(err)
|
329
|
+
raise exc if exc
|
330
|
+
end
|
331
|
+
alias import import_keys
|
332
|
+
|
333
|
+
def import_result
|
334
|
+
GPGME::gpgme_op_import_result(self)
|
335
|
+
end
|
336
|
+
|
337
|
+
# Delete the key from the key ring.
|
338
|
+
# If allow_secret is false, only public keys are deleted,
|
339
|
+
# otherwise secret keys are deleted as well.
|
340
|
+
def delete_key(key, allow_secret = false)
|
341
|
+
err = GPGME::gpgme_op_delete(self, key, allow_secret ? 1 : 0)
|
342
|
+
exc = GPGME::error_to_exception(err)
|
343
|
+
raise exc if exc
|
344
|
+
end
|
345
|
+
alias delete delete_key
|
346
|
+
|
347
|
+
# Edit attributes of the key in the local key ring.
|
348
|
+
def edit_key(key, editfunc, hook_value = nil, out = Data.new)
|
349
|
+
err = GPGME::gpgme_op_edit(self, key, editfunc, hook_value, out)
|
350
|
+
exc = GPGME::error_to_exception(err)
|
351
|
+
raise exc if exc
|
352
|
+
end
|
353
|
+
alias edit edit_key
|
354
|
+
|
355
|
+
# Edit attributes of the key on the card.
|
356
|
+
def edit_card_key(key, editfunc, hook_value = nil, out = Data.new)
|
357
|
+
err = GPGME::gpgme_op_card_edit(self, key, editfunc, hook_value, out)
|
358
|
+
exc = GPGME::error_to_exception(err)
|
359
|
+
raise exc if exc
|
360
|
+
end
|
361
|
+
alias edit_card edit_card_key
|
362
|
+
alias card_edit edit_card_key
|
363
|
+
|
364
|
+
##
|
365
|
+
# Crypto operations
|
366
|
+
##
|
367
|
+
|
368
|
+
# Decrypt the ciphertext and return the plaintext.
|
369
|
+
def decrypt(cipher, plain = Data.new)
|
370
|
+
err = GPGME::gpgme_op_decrypt(self, cipher, plain)
|
371
|
+
exc = GPGME::error_to_exception(err)
|
372
|
+
raise exc if exc
|
373
|
+
plain
|
374
|
+
end
|
375
|
+
|
376
|
+
def decrypt_verify(cipher, plain = Data.new)
|
377
|
+
err = GPGME::gpgme_op_decrypt_verify(self, cipher, plain)
|
378
|
+
exc = GPGME::error_to_exception(err)
|
379
|
+
raise exc if exc
|
380
|
+
plain
|
381
|
+
end
|
382
|
+
|
383
|
+
def decrypt_result
|
384
|
+
GPGME::gpgme_op_decrypt_result(self)
|
385
|
+
end
|
386
|
+
|
387
|
+
# Verify that the signature in the data object is a valid signature.
|
388
|
+
def verify(sig, signed_text = nil, plain = Data.new)
|
389
|
+
err = GPGME::gpgme_op_verify(self, sig, signed_text, plain)
|
390
|
+
exc = GPGME::error_to_exception(err)
|
391
|
+
raise exc if exc
|
392
|
+
plain
|
393
|
+
end
|
394
|
+
|
395
|
+
def verify_result
|
396
|
+
GPGME::gpgme_op_verify_result(self)
|
397
|
+
end
|
398
|
+
|
399
|
+
# Remove the list of signers from this object.
|
400
|
+
def clear_signers
|
401
|
+
GPGME::gpgme_signers_clear(self)
|
402
|
+
end
|
403
|
+
|
404
|
+
# Add _keys_ to the list of signers.
|
405
|
+
def add_signer(*keys)
|
406
|
+
keys.each do |key|
|
407
|
+
err = GPGME::gpgme_signers_add(self, key)
|
408
|
+
exc = GPGME::error_to_exception(err)
|
409
|
+
raise exc if exc
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Create a signature for the text.
|
414
|
+
# +plain+ is a data object which contains the text.
|
415
|
+
# +sig+ is a data object where the generated signature is stored.
|
416
|
+
def sign(plain, sig = Data.new, mode = GPGME::SIG_MODE_NORMAL)
|
417
|
+
err = GPGME::gpgme_op_sign(self, plain, sig, mode)
|
418
|
+
exc = GPGME::error_to_exception(err)
|
419
|
+
raise exc if exc
|
420
|
+
sig
|
421
|
+
end
|
422
|
+
|
423
|
+
def sign_result
|
424
|
+
GPGME::gpgme_op_sign_result(self)
|
425
|
+
end
|
426
|
+
|
427
|
+
# Encrypt the plaintext in the data object for the recipients and
|
428
|
+
# return the ciphertext.
|
429
|
+
def encrypt(recp, plain, cipher = Data.new, flags = 0)
|
430
|
+
err = GPGME::gpgme_op_encrypt(self, recp, flags, plain, cipher)
|
431
|
+
exc = GPGME::error_to_exception(err)
|
432
|
+
raise exc if exc
|
433
|
+
cipher
|
434
|
+
end
|
435
|
+
|
436
|
+
def encrypt_result
|
437
|
+
GPGME::gpgme_op_encrypt_result(self)
|
438
|
+
end
|
439
|
+
|
440
|
+
def encrypt_sign(recp, plain, cipher = Data.new, flags = 0)
|
441
|
+
err = GPGME::gpgme_op_encrypt_sign(self, recp, flags, plain, cipher)
|
442
|
+
exc = GPGME::error_to_exception(err)
|
443
|
+
raise exc if exc
|
444
|
+
cipher
|
445
|
+
end
|
446
|
+
|
447
|
+
def inspect
|
448
|
+
"#<#{self.class} protocol=#{PROTOCOL_NAMES[protocol] || protocol}, \
|
449
|
+
armor=#{armor}, textmode=#{textmode}, \
|
450
|
+
keylist_mode=#{KEYLIST_MODE_NAMES[keylist_mode]}>"
|
451
|
+
end
|
452
|
+
|
453
|
+
private
|
454
|
+
|
455
|
+
def self.pass_function(pass, uid_hint, passphrase_info, prev_was_bad, fd)
|
456
|
+
io = IO.for_fd(fd, 'w')
|
457
|
+
io.puts pass
|
458
|
+
io.flush
|
459
|
+
end
|
460
|
+
|
461
|
+
end
|
462
|
+
end
|