gpgme 1.0.8 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/examples/genkey.rb +1 -1
  2. data/examples/keylist.rb +2 -1
  3. data/examples/roundtrip.rb +7 -4
  4. data/examples/sign.rb +5 -3
  5. data/examples/verify.rb +4 -2
  6. data/ext/gpgme/extconf.rb +58 -0
  7. data/ext/gpgme/gpgme-1.3.1.tar.bz2 +0 -0
  8. data/{gpgme_n.c → ext/gpgme/gpgme_n.c} +8 -8
  9. data/ext/gpgme/libassuan-2.0.2.tar.bz2 +0 -0
  10. data/ext/gpgme/libgpg-error-1.10.tar.bz2 +0 -0
  11. data/lib/gpgme.rb +88 -1541
  12. data/lib/gpgme/compat.rb +2 -0
  13. data/lib/gpgme/constants.rb +23 -0
  14. data/lib/gpgme/crypto.rb +357 -0
  15. data/lib/gpgme/ctx.rb +462 -0
  16. data/lib/gpgme/data.rb +177 -0
  17. data/lib/gpgme/engine.rb +76 -0
  18. data/lib/gpgme/error.rb +66 -0
  19. data/lib/gpgme/io_callbacks.rb +21 -0
  20. data/lib/gpgme/key.rb +242 -0
  21. data/lib/gpgme/key_common.rb +43 -0
  22. data/lib/gpgme/key_sig.rb +35 -0
  23. data/lib/gpgme/misc.rb +66 -0
  24. data/lib/gpgme/signature.rb +85 -0
  25. data/lib/gpgme/sub_key.rb +58 -0
  26. data/lib/gpgme/user_id.rb +20 -0
  27. data/lib/gpgme/version.rb +3 -0
  28. data/test/crypto_test.rb +242 -0
  29. data/test/ctx_test.rb +426 -0
  30. data/test/data_test.rb +116 -0
  31. data/test/files/testkey_pub.gpg +52 -0
  32. data/test/files/testkey_sec.gpg +54 -0
  33. data/test/gpgme_test.rb +12 -0
  34. data/test/key_test.rb +201 -0
  35. data/test/signature_test.rb +48 -0
  36. data/test/sub_key_test.rb +45 -0
  37. data/test/support/resources.rb +516 -0
  38. data/test/test_helper.rb +83 -0
  39. metadata +144 -65
  40. data.tar.gz.sig +0 -3
  41. data/COPYING +0 -340
  42. data/COPYING.LESSER +0 -510
  43. data/Makefile +0 -172
  44. data/Manifest.txt +0 -18
  45. data/README +0 -86
  46. data/Rakefile +0 -17
  47. data/THANKS +0 -15
  48. data/extconf.rb +0 -26
  49. metadata.gz.sig +0 -0
@@ -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
@@ -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
@@ -0,0 +1,66 @@
1
+ module GPGME
2
+ class Error < StandardError
3
+ def initialize(error)
4
+ @error = error
5
+ end
6
+ attr_reader :error
7
+
8
+ # Return the error code.
9
+ #
10
+ # The error code indicates the type of an error, or the reason why
11
+ # an operation failed.
12
+ def code
13
+ GPGME::gpgme_err_code(@error)
14
+ end
15
+
16
+ # Return the error source.
17
+ #
18
+ # The error source has not a precisely defined meaning. Sometimes
19
+ # it is the place where the error happened, sometimes it is the
20
+ # place where an error was encoded into an error value. Usually
21
+ # the error source will give an indication to where to look for
22
+ # the problem. This is not always true, but it is attempted to
23
+ # achieve this goal.
24
+ def source
25
+ GPGME::gpgme_err_source(@error)
26
+ end
27
+
28
+ # Return a description of the error code.
29
+ def message
30
+ GPGME::gpgme_strerror(@error)
31
+ end
32
+
33
+ class General < self; end
34
+ class InvalidValue < self; end
35
+ class UnusablePublicKey < self
36
+ attr_accessor :keys
37
+ end
38
+ class UnusableSecretKey < self
39
+ attr_accessor :keys
40
+ end
41
+ class NoData < self; end
42
+ class Conflict < self; end
43
+ class NotImplemented < self; end
44
+ class DecryptFailed < self; end
45
+ class BadPassphrase < self; end
46
+ class Canceled < self; end
47
+ class InvalidEngine < self; end
48
+ class AmbiguousName < self; end
49
+ class WrongKeyUsage < self
50
+ attr_accessor :key_usage
51
+ end
52
+ class CertificateRevoked < self; end
53
+ class CertificateExpired < self; end
54
+ class NoCRLKnown < self; end
55
+ class NoPolicyMatch < self; end
56
+ class NoSecretKey < self; end
57
+ class MissingCertificate < self; end
58
+ class BadCertificateChain < self; end
59
+ class UnsupportedAlgorithm < self
60
+ attr_accessor :algorithm
61
+ end
62
+ class BadSignature < self; end
63
+ class NoPublicKey < self; end
64
+ class InvalidVersion < self; end
65
+ end
66
+ end
@@ -0,0 +1,21 @@
1
+ module GPGME
2
+ class IOCallbacks
3
+ def initialize(io)
4
+ @io = io
5
+ end
6
+
7
+ def read(hook, length)
8
+ @io.read(length)
9
+ end
10
+
11
+ def write(hook, buffer, length)
12
+ @io.write(buffer[0 .. length])
13
+ end
14
+
15
+ def seek(hook, offset, whence)
16
+ return @io.pos if offset == 0 && whence == IO::SEEK_CUR
17
+ @io.seek(offset, whence)
18
+ @io.pos
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,242 @@
1
+ module GPGME
2
+
3
+ ##
4
+ # A ruby representation of a public or a secret key.
5
+ #
6
+ # Every key has two instances of {GPGME::SubKey}, accessible through
7
+ # {.subkeys}, and with a {.primary_subkey} where most attributes are
8
+ # derived from, like the +fingerprint+.
9
+ #
10
+ # Also, every key has at least a {GPGME::UserID}, accessible through
11
+ # {.uids}, with a {.primary_uid}, where other attributes are derived from,
12
+ # like +email+ or +name+
13
+ class Key
14
+ private_class_method :new
15
+
16
+ attr_reader :keylist_mode, :protocol, :owner_trust
17
+ attr_reader :issuer_serial, :issuer_name, :chain_id
18
+ attr_reader :subkeys, :uids
19
+
20
+ include KeyCommon
21
+
22
+ class << self
23
+
24
+ ##
25
+ # Returns an array of {GPGME::Key} objects that match the parameters.
26
+ # * +secret+ set to +:secret+ to get only secret keys, or to +:public+ to
27
+ # get only public keys.
28
+ # * +keys_or_names+ an array or an item that can be either {GPGME::Key}
29
+ # elements, or string identifiers like the email or the sha. Leave
30
+ # blank to get all.
31
+ # * +purposes+ get only keys that are usable for any of these purposes.
32
+ # See {GPGME::Key} for a list of possible key capabilities.
33
+ #
34
+ # @example
35
+ # GPGME::Key.find :secret # => first secret key found
36
+ #
37
+ # @example
38
+ # GPGME::Key.find(:public, "mrsimo@example.com")
39
+ # # => return only public keys that match mrsimo@example.com
40
+ #
41
+ # @example
42
+ # GPGME::Key.find(:public, "mrsimo@example.com", :sign)
43
+ # # => return the public keys that match mrsimo@example.com and are
44
+ # # capable of signing
45
+ def find(secret, keys_or_names = nil, purposes = [])
46
+ secret = (secret == :secret)
47
+ keys_or_names = [""] if keys_or_names.nil? || (keys_or_names.is_a?(Array) && keys_or_names.empty?)
48
+ keys_or_names = [keys_or_names].flatten
49
+ purposes = [purposes].flatten.compact.uniq
50
+
51
+ keys = []
52
+ keys_or_names.each do |key_or_name|
53
+ case key_or_name
54
+ when Key then keys << key_or_name
55
+ when String
56
+ GPGME::Ctx.new do |ctx|
57
+ keys += ctx.keys(key_or_name, secret).select do |k|
58
+ k.usable_for?(purposes)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ keys
64
+ end
65
+
66
+ def get(fingerprint)
67
+ Ctx.new do |ctx|
68
+ ctx.get_key(fingerprint)
69
+ end
70
+ end
71
+
72
+ # Exports public keys
73
+ #
74
+ # GPGME::Key.export pattern, options
75
+ #
76
+ # Private keys cannot be exported due to GPGME restrictions.
77
+ #
78
+ # @param pattern
79
+ # Identifier of the key to export.
80
+ #
81
+ # @param [Hash] options
82
+ # * +:output+ specify where to write the key to. It will be converted to
83
+ # a {GPGME::Data}, so it could be a file, for example.
84
+ # * Any other option accepted by {GPGME::Ctx.new}
85
+ #
86
+ # @return [GPGME::Data] the exported key.
87
+ #
88
+ # @example
89
+ # key = GPGME::Key.export "mrsimo@example.com"
90
+ #
91
+ # @example writing to a file
92
+ # out = File.open("my.key", "w+")
93
+ # GPGME::Key.export "mrsimo@example.com", :output => out
94
+ #
95
+ def export(pattern, options = {})
96
+ output = Data.new(options[:output])
97
+
98
+ GPGME::Ctx.new(options) do |ctx|
99
+ ctx.export_keys(pattern, output)
100
+ end
101
+
102
+ output.seek(0)
103
+ output
104
+ end
105
+
106
+ # Imports a key
107
+ #
108
+ # GPGME::Key.import keydata, options
109
+ #
110
+ # @param keydata
111
+ # The key to import. It will be converted to a {GPGME::Data} object,
112
+ # so could be a file, for example.
113
+ # @param options
114
+ # Any other option accepted by {GPGME::Ctx.new}
115
+ #
116
+ # @example
117
+ # GPGME::Key.import(File.open("my.key"))
118
+ #
119
+ def import(keydata, options = {})
120
+ GPGME::Ctx.new(options) do |ctx|
121
+ ctx.import_keys(Data.new(keydata))
122
+ ctx.import_result
123
+ end
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Exports this key. Accepts the same options as {GPGME::Ctx.new}, and
129
+ # +options[:output]+, where you can specify something that can become a
130
+ # {GPGME::Data}, where the output will go.
131
+ #
132
+ # @example
133
+ # key.export(:armor => true)
134
+ # # => GPGME::Data you can read with ASCII armored format
135
+ #
136
+ # @example
137
+ # file = File.open("key.asc", "w+")
138
+ # key.export(:output => file)
139
+ # # => the key will be written to the file.
140
+ #
141
+ def export(options = {})
142
+ Key.export self.sha, options
143
+ end
144
+
145
+ ##
146
+ # Delete this key. If it's public, and has a secret one it will fail unless
147
+ # +allow_secret+ is specified as true.
148
+ def delete!(allow_secret = false)
149
+ GPGME::Ctx.new do |ctx|
150
+ ctx.delete_key self, allow_secret
151
+ end
152
+ end
153
+
154
+ ##
155
+ # Returns the expiry date for this key
156
+ def expires
157
+ primary_subkey.expires
158
+ end
159
+
160
+ ##
161
+ # Returns true if the key is expired
162
+ def expired
163
+ subkeys.any?(&:expired)
164
+ end
165
+
166
+ def primary_subkey
167
+ @primary_subkey ||= subkeys.first
168
+ end
169
+
170
+ ##
171
+ # Short descriptive value. Can be used to identify the key.
172
+ def sha
173
+ primary_subkey.sha
174
+ end
175
+
176
+ ##
177
+ # Longer descriptive value. Can be used to identify the key.
178
+ def fingerprint
179
+ primary_subkey.fingerprint
180
+ end
181
+
182
+ ##
183
+ # Returns the main {GPGME::UserID} for this key.
184
+ def primary_uid
185
+ uids.first
186
+ end
187
+
188
+ ##
189
+ # Returns the email for this key.
190
+ def email
191
+ primary_uid.email
192
+ end
193
+
194
+ ##
195
+ # Returns the issuer name for this key.
196
+ def name
197
+ primary_uid.name
198
+ end
199
+
200
+ ##
201
+ # Returns the issuer comment for this key.
202
+ def comment
203
+ primary_uid.comment
204
+ end
205
+
206
+ def ==(another_key)
207
+ fingerprint == another_key.fingerprint
208
+ end
209
+
210
+ def inspect
211
+ sprintf("#<#{self.class} %s %4d%s/%s %s trust=%s, owner_trust=%s, \
212
+ capability=%s, subkeys=%s, uids=%s>",
213
+ primary_subkey.secret? ? 'sec' : 'pub',
214
+ primary_subkey.length,
215
+ primary_subkey.pubkey_algo_letter,
216
+ primary_subkey.fingerprint[-8 .. -1],
217
+ primary_subkey.timestamp.strftime('%Y-%m-%d'),
218
+ trust.inspect,
219
+ VALIDITY_NAMES[@owner_trust].inspect,
220
+ capability.inspect,
221
+ subkeys.inspect,
222
+ uids.inspect)
223
+ end
224
+
225
+ def to_s
226
+ primary_subkey = subkeys[0]
227
+ s = sprintf("%s %4d%s/%s %s\n",
228
+ primary_subkey.secret? ? 'sec' : 'pub',
229
+ primary_subkey.length,
230
+ primary_subkey.pubkey_algo_letter,
231
+ primary_subkey.fingerprint[-8 .. -1],
232
+ primary_subkey.timestamp.strftime('%Y-%m-%d'))
233
+ uids.each do |user_id|
234
+ s << "uid\t\t#{user_id.name} <#{user_id.email}>\n"
235
+ end
236
+ subkeys.each do |subkey|
237
+ s << subkey.to_s
238
+ end
239
+ s
240
+ end
241
+ end
242
+ end