gpgme 1.0.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|