benburkert-gpgme 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/COPYING +340 -0
- data/COPYING.LESSER +510 -0
- data/Gemfile +6 -0
- data/History.txt +15 -0
- data/Manifest.txt +18 -0
- data/README.rdoc +162 -0
- data/Rakefile +37 -0
- data/THANKS +15 -0
- data/benburkert-gpgme.gemspec +30 -0
- data/examples/edit.rb +77 -0
- data/examples/genkey.rb +55 -0
- data/examples/keylist.rb +6 -0
- data/examples/roundtrip.rb +39 -0
- data/examples/sign.rb +29 -0
- data/examples/verify.rb +6 -0
- data/ext/gpgme/extconf.rb +26 -0
- data/ext/gpgme/gpgme_n.c +2622 -0
- data/lib/gpgme/compat.rb +48 -0
- data/lib/gpgme/constants.rb +187 -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/lib/gpgme.rb +108 -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 +79 -0
- metadata +196 -0
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
|
data/lib/gpgme/data.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
module GPGME
|
2
|
+
|
3
|
+
##
|
4
|
+
# A class whose purpose is to unify the way we work with the data (both input
|
5
|
+
# and output). Most of the calls expect instances of this class, or will try
|
6
|
+
# to create one from your parameters.
|
7
|
+
#
|
8
|
+
# Read the {#read}, {#write} and {#seek} methods for the most commonly used
|
9
|
+
# methods.
|
10
|
+
class Data
|
11
|
+
|
12
|
+
BLOCK_SIZE = 4096
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
##
|
17
|
+
# We implement +self.new+ instead of initialize because objects are actually
|
18
|
+
# instantiated through the C API with stuff like +gpgme_data_new+.
|
19
|
+
#
|
20
|
+
# We try to create a {GPGME::Data} smartly depending on the object passed, and if
|
21
|
+
# another {GPGME::Data} object is passed, it just returns it, so when in
|
22
|
+
# doubt, you can always pass a {GPGME::Data} object.
|
23
|
+
#
|
24
|
+
# @example empty
|
25
|
+
# data = GPGME::Data.new
|
26
|
+
# data.write("stuff")
|
27
|
+
#
|
28
|
+
# @example from a string
|
29
|
+
# data = GPGME::Data.new("From a string")
|
30
|
+
#
|
31
|
+
# @example from a file
|
32
|
+
# data = GPGME::Data.new(File.open("secure.pass"))
|
33
|
+
#
|
34
|
+
# @example from a file descriptor
|
35
|
+
# data = GPGME::Data.new(0) # Standard input
|
36
|
+
# data = GPGME::Data.new(1) # Standard output
|
37
|
+
#
|
38
|
+
# file = File.open("secure.pass")
|
39
|
+
# data = GPGME::Data.new(file.fileno) # file descriptor
|
40
|
+
#
|
41
|
+
def new(object = nil)
|
42
|
+
if object.nil?
|
43
|
+
empty!
|
44
|
+
elsif object.is_a?(Data)
|
45
|
+
object
|
46
|
+
elsif object.is_a?(Integer)
|
47
|
+
from_fd(object)
|
48
|
+
elsif object.respond_to? :to_str
|
49
|
+
from_str(object.to_str)
|
50
|
+
elsif object.respond_to? :to_io
|
51
|
+
from_io(object.to_io)
|
52
|
+
elsif object.respond_to? :open
|
53
|
+
from_io(object.open)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a new instance with an empty buffer.
|
58
|
+
def empty!
|
59
|
+
rdh = []
|
60
|
+
err = GPGME::gpgme_data_new(rdh)
|
61
|
+
exc = GPGME::error_to_exception(err)
|
62
|
+
raise exc if exc
|
63
|
+
rdh.first
|
64
|
+
end
|
65
|
+
|
66
|
+
# Create a new instance with internal buffer.
|
67
|
+
def from_str(string)
|
68
|
+
rdh = []
|
69
|
+
err = GPGME::gpgme_data_new_from_mem(rdh, string, string.length)
|
70
|
+
exc = GPGME::error_to_exception(err)
|
71
|
+
raise exc if exc
|
72
|
+
rdh.first
|
73
|
+
end
|
74
|
+
|
75
|
+
# Create a new instance associated with a given IO.
|
76
|
+
def from_io(io)
|
77
|
+
from_callbacks(IOCallbacks.new(io))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Create a new instance from the specified file descriptor.
|
81
|
+
def from_fd(fd)
|
82
|
+
rdh = []
|
83
|
+
err = GPGME::gpgme_data_new_from_fd(rdh, fd)
|
84
|
+
exc = GPGME::error_to_exception(err)
|
85
|
+
raise exc if exc
|
86
|
+
rdh.first
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a new instance from the specified callbacks.
|
90
|
+
def from_callbacks(callbacks, hook_value = nil)
|
91
|
+
rdh = []
|
92
|
+
err = GPGME::gpgme_data_new_from_cbs(rdh, callbacks, hook_value)
|
93
|
+
exc = GPGME::error_to_exception(err)
|
94
|
+
raise exc if exc
|
95
|
+
rdh.first
|
96
|
+
end
|
97
|
+
end # class << self
|
98
|
+
|
99
|
+
# Read at most +length+ bytes from the data object, or to the end
|
100
|
+
# of file if +length+ is omitted or is +nil+.
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# data = GPGME::Data.new("From a string")
|
104
|
+
# data.read # => "From a string"
|
105
|
+
#
|
106
|
+
# @example
|
107
|
+
# data = GPGME::Data.new("From a string")
|
108
|
+
# data.read(4) # => "From"
|
109
|
+
#
|
110
|
+
def read(length = nil)
|
111
|
+
if length
|
112
|
+
GPGME::gpgme_data_read(self, length)
|
113
|
+
else
|
114
|
+
buf = String.new
|
115
|
+
loop do
|
116
|
+
s = GPGME::gpgme_data_read(self, BLOCK_SIZE)
|
117
|
+
break unless s
|
118
|
+
buf << s
|
119
|
+
end
|
120
|
+
buf
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Seek to a given +offset+ in the data object according to the
|
126
|
+
# value of +whence+.
|
127
|
+
#
|
128
|
+
# @example going to the beginning of the buffer after writing something
|
129
|
+
# data = GPGME::Data.new("Some data")
|
130
|
+
# data.read # => "Some data"
|
131
|
+
# data.read # => ""
|
132
|
+
# data.seek 0
|
133
|
+
# data.read # => "Some data"
|
134
|
+
#
|
135
|
+
def seek(offset, whence = IO::SEEK_SET)
|
136
|
+
GPGME::gpgme_data_seek(self, offset, IO::SEEK_SET)
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Writes +length+ bytes from +buffer+ into the data object.
|
141
|
+
# Writes the full buffer if no length passed.
|
142
|
+
#
|
143
|
+
# @example
|
144
|
+
# data = GPGME::Data.new
|
145
|
+
# data.write "hola"
|
146
|
+
# data.seek 0
|
147
|
+
# data.read # => "hola"
|
148
|
+
#
|
149
|
+
# @example
|
150
|
+
# data = GPGME::Data.new
|
151
|
+
# data.write "hola", 2
|
152
|
+
# data.seek 0
|
153
|
+
# data.read # => "ho"
|
154
|
+
#
|
155
|
+
def write(buffer, length = buffer.length)
|
156
|
+
GPGME::gpgme_data_write(self, buffer, length)
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Return the encoding of the underlying data.
|
161
|
+
def encoding
|
162
|
+
GPGME::gpgme_data_get_encoding(self)
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Sets the encoding for this buffer. Accepts only values in one of the
|
167
|
+
# DATA_ENCODING_* constants.
|
168
|
+
#
|
169
|
+
# @raise [GPGME::Error::InvalidValue] if the value isn't accepted.
|
170
|
+
def encoding=(encoding)
|
171
|
+
err = GPGME::gpgme_data_set_encoding(self, encoding)
|
172
|
+
exc = GPGME::error_to_exception(err)
|
173
|
+
raise exc if exc
|
174
|
+
encoding
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
data/lib/gpgme/engine.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module GPGME
|
2
|
+
|
3
|
+
##
|
4
|
+
# Convenience methods to check different aspects of the gpg system
|
5
|
+
# installation.
|
6
|
+
module Engine
|
7
|
+
class << self
|
8
|
+
|
9
|
+
##
|
10
|
+
# Verify that the engine implementing the protocol +proto+ is installed in
|
11
|
+
# the system. Can be one of +PROTOCOL_OpenPGP+ or +PROTOCOL_CMS+.
|
12
|
+
#
|
13
|
+
# @return [Boolean] true if the engine is installed.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# GPGME::Engine.check_version(GPGME::PROTOCOL_OpenPGP) # => true
|
17
|
+
#
|
18
|
+
def check_version(proto)
|
19
|
+
err = GPGME::gpgme_engine_check_version(proto)
|
20
|
+
exc = GPGME::error_to_exception(err)
|
21
|
+
!exc
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Return an array of {GPGME::EngineInfo} structures of enabled engines.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# GPGME::Engine.engine_info.first
|
29
|
+
# # => #<GPGME::EngineInfo:0x00000100d4fbd8
|
30
|
+
# @file_name="/usr/local/bin/gpg",
|
31
|
+
# @protocol=0,
|
32
|
+
# @req_version="1.3.0",
|
33
|
+
# @version="1.4.11">
|
34
|
+
#
|
35
|
+
def info
|
36
|
+
rinfo = []
|
37
|
+
GPGME::gpgme_get_engine_info(rinfo)
|
38
|
+
rinfo
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Change the default configuration of the crypto engine implementing
|
43
|
+
# protocol +proto+.
|
44
|
+
#
|
45
|
+
# @param proto
|
46
|
+
# Can be one of +PROTOCOL_OpenPGP+ or +PROTOCOL_CMS+.
|
47
|
+
#
|
48
|
+
# @param file_name
|
49
|
+
# The file name of the executable program implementing the protocol.
|
50
|
+
#
|
51
|
+
# @param home_dir
|
52
|
+
# The directory name of the configuration directory.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# GPGME::Engine.set
|
56
|
+
#
|
57
|
+
def set_info(proto, file_name, home_dir)
|
58
|
+
err = GPGME::gpgme_set_engine_info(proto, file_name, home_dir)
|
59
|
+
exc = GPGME::error_to_exception(err)
|
60
|
+
raise exc if exc
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Sets the home dir for the configuration options. This way one could,
|
65
|
+
# for example, load the keys from a customized keychain.
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# GPGME::Engine.home_dir = '/tmp'
|
69
|
+
#
|
70
|
+
def home_dir=(home_dir)
|
71
|
+
current = info.first
|
72
|
+
set_info current.protocol, current.file_name, home_dir
|
73
|
+
end
|
74
|
+
end # class << self
|
75
|
+
end # class Engine
|
76
|
+
end # module GPGME
|