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/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
|
data/lib/gpgme/error.rb
ADDED
@@ -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
|
data/lib/gpgme/key.rb
ADDED
@@ -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
|