benburkert-gpgme 0.1.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/.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.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
$:.push File.expand_path("../..", __FILE__) # C extension is in the root
|
2
|
+
|
3
|
+
require 'gpgme_n'
|
4
|
+
|
5
|
+
# TODO without this call one can't GPGME::Ctx.new, find out why
|
6
|
+
GPGME::gpgme_check_version(nil)
|
7
|
+
|
8
|
+
require 'gpgme/constants'
|
9
|
+
require 'gpgme/ctx'
|
10
|
+
require 'gpgme/data'
|
11
|
+
require 'gpgme/error'
|
12
|
+
require 'gpgme/io_callbacks'
|
13
|
+
require 'gpgme/key_common'
|
14
|
+
require 'gpgme/key'
|
15
|
+
require 'gpgme/sub_key'
|
16
|
+
require 'gpgme/key_sig'
|
17
|
+
require 'gpgme/misc'
|
18
|
+
require 'gpgme/signature'
|
19
|
+
require 'gpgme/user_id'
|
20
|
+
require 'gpgme/engine'
|
21
|
+
require 'gpgme/crypto'
|
22
|
+
|
23
|
+
module GPGME
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# From the c extension
|
27
|
+
alias pubkey_algo_name gpgme_pubkey_algo_name
|
28
|
+
alias hash_algo_name gpgme_hash_algo_name
|
29
|
+
|
30
|
+
##
|
31
|
+
# Auxiliary method used by all the library to generate exceptions
|
32
|
+
# from error codes returned by the C extension.
|
33
|
+
def error_to_exception(err)
|
34
|
+
case GPGME::gpgme_err_code(err)
|
35
|
+
when GPG_ERR_EOF
|
36
|
+
EOFError.new
|
37
|
+
when GPG_ERR_NO_ERROR
|
38
|
+
nil
|
39
|
+
when GPG_ERR_GENERAL
|
40
|
+
Error::General.new(err)
|
41
|
+
when GPG_ERR_ENOMEM
|
42
|
+
Errno::ENOMEM.new
|
43
|
+
when GPG_ERR_INV_VALUE
|
44
|
+
Error::InvalidValue.new(err)
|
45
|
+
when GPG_ERR_UNUSABLE_PUBKEY
|
46
|
+
Error::UnusablePublicKey.new(err)
|
47
|
+
when GPG_ERR_UNUSABLE_SECKEY
|
48
|
+
Error::UnusableSecretKey.new(err)
|
49
|
+
when GPG_ERR_NO_DATA
|
50
|
+
Error::NoData.new(err)
|
51
|
+
when GPG_ERR_CONFLICT
|
52
|
+
Error::Conflict.new(err)
|
53
|
+
when GPG_ERR_NOT_IMPLEMENTED
|
54
|
+
Error::NotImplemented.new(err)
|
55
|
+
when GPG_ERR_DECRYPT_FAILED
|
56
|
+
Error::DecryptFailed.new(err)
|
57
|
+
when GPG_ERR_BAD_PASSPHRASE
|
58
|
+
Error::BadPassphrase.new(err)
|
59
|
+
when GPG_ERR_CANCELED
|
60
|
+
Error::Canceled.new(err)
|
61
|
+
when GPG_ERR_INV_ENGINE
|
62
|
+
Error::InvalidEngine.new(err)
|
63
|
+
when GPG_ERR_AMBIGUOUS_NAME
|
64
|
+
Error::AmbiguousName.new(err)
|
65
|
+
when GPG_ERR_WRONG_KEY_USAGE
|
66
|
+
Error::WrongKeyUsage.new(err)
|
67
|
+
when GPG_ERR_CERT_REVOKED
|
68
|
+
Error::CertificateRevoked.new(err)
|
69
|
+
when GPG_ERR_CERT_EXPIRED
|
70
|
+
Error::CertificateExpired.new(err)
|
71
|
+
when GPG_ERR_NO_CRL_KNOWN
|
72
|
+
Error::NoCRLKnown.new(err)
|
73
|
+
when GPG_ERR_NO_POLICY_MATCH
|
74
|
+
Error::NoPolicyMatch.new(err)
|
75
|
+
when GPG_ERR_NO_SECKEY
|
76
|
+
Error::NoSecretKey.new(err)
|
77
|
+
when GPG_ERR_MISSING_CERT
|
78
|
+
Error::MissingCertificate.new(err)
|
79
|
+
when GPG_ERR_BAD_CERT_CHAIN
|
80
|
+
Error::BadCertificateChain.new(err)
|
81
|
+
when GPG_ERR_UNSUPPORTED_ALGORITHM
|
82
|
+
Error::UnsupportedAlgorithm.new(err)
|
83
|
+
when GPG_ERR_BAD_SIGNATURE
|
84
|
+
Error::BadSignature.new(err)
|
85
|
+
when GPG_ERR_NO_PUBKEY
|
86
|
+
Error::NoPublicKey.new(err)
|
87
|
+
else
|
88
|
+
Error.new(err)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# TODO find out what it does, can't seem to find a proper parameter that
|
94
|
+
# returns something other than nil.
|
95
|
+
def check_version(options = nil)
|
96
|
+
version = nil
|
97
|
+
if options.kind_of?(String)
|
98
|
+
version = options
|
99
|
+
elsif options.include?(:version)
|
100
|
+
version = options[:version]
|
101
|
+
end
|
102
|
+
unless GPGME::gpgme_check_version(version)
|
103
|
+
raise Error::InvalidVersion.new
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
data/test/crypto_test.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'test_helper'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe GPGME::Crypto do
|
6
|
+
describe "default options functionality" do
|
7
|
+
it "allows operation from instances normally" do
|
8
|
+
crypto = GPGME::Crypto.new
|
9
|
+
encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :recipients => KEYS.first[:sha]
|
10
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can set default options when using the instance way" do
|
14
|
+
crypto = GPGME::Crypto.new :always_trust => true
|
15
|
+
encrypted = crypto.encrypt TEXT[:plain], :recipients => KEYS.first[:sha]
|
16
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
17
|
+
end
|
18
|
+
|
19
|
+
it "but they can still be overwritten" do
|
20
|
+
crypto = GPGME::Crypto.new :always_trust => false
|
21
|
+
encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :recipients => KEYS.first[:sha]
|
22
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "roundtrip encryption/decryption" do
|
27
|
+
it "does the roundtrip encrypting" do
|
28
|
+
crypto = GPGME::Crypto.new
|
29
|
+
encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :recipients => KEYS.first[:sha]
|
30
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
31
|
+
end
|
32
|
+
|
33
|
+
it "does so even with armored encrypted stuff" do
|
34
|
+
crypto = GPGME::Crypto.new
|
35
|
+
encrypted = crypto.encrypt TEXT[:plain], :always_trust => true, :armor => true
|
36
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe :encrypt do
|
41
|
+
it "should raise an error if the recipients aren't trusted" do
|
42
|
+
assert_raises GPGME::Error::General do
|
43
|
+
GPGME::Crypto.new.encrypt TEXT[:plain]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "doesn't raise an error and returns something when encrypting nothing" do
|
48
|
+
data = GPGME::Crypto.new.encrypt nil, :always_trust => true
|
49
|
+
refute_empty data.read
|
50
|
+
data = GPGME::Crypto.new.encrypt "", :always_trust => true
|
51
|
+
refute_empty data.read
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can specify which key(s) to use for encrypting with a string" do
|
55
|
+
crypto = GPGME::Crypto.new :always_trust => true
|
56
|
+
key = KEYS.last
|
57
|
+
encrypted = crypto.encrypt TEXT[:plain], :recipients => key[:sha]
|
58
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
59
|
+
|
60
|
+
remove_key key
|
61
|
+
encrypted.seek 0
|
62
|
+
assert_raises GPGME::Error::DecryptFailed do
|
63
|
+
crypto.decrypt(encrypted)
|
64
|
+
end
|
65
|
+
import_key key
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can specify which key to use for encrypting with a Key object" do
|
69
|
+
crypto = GPGME::Crypto.new :always_trust => true
|
70
|
+
key = KEYS.last
|
71
|
+
real_key = GPGME::Key.find(:public, key[:sha]).first
|
72
|
+
|
73
|
+
encrypted = crypto.encrypt TEXT[:plain], :recipients => real_key
|
74
|
+
assert_equal TEXT[:plain], crypto.decrypt(encrypted).read
|
75
|
+
|
76
|
+
remove_key key
|
77
|
+
encrypted.seek 0
|
78
|
+
assert_raises GPGME::Error::DecryptFailed do
|
79
|
+
crypto.decrypt(encrypted)
|
80
|
+
end
|
81
|
+
import_key key
|
82
|
+
end
|
83
|
+
|
84
|
+
it "can also sign at the same time" do
|
85
|
+
crypto = GPGME::Crypto.new :always_trust => true
|
86
|
+
encrypted = crypto.encrypt TEXT[:plain], :sign => true
|
87
|
+
signatures = 0
|
88
|
+
|
89
|
+
crypto.verify(encrypted) do |signature|
|
90
|
+
assert_instance_of GPGME::Signature, signature
|
91
|
+
signatures += 1
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal 1, signatures
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can be signed by more than one person" do
|
98
|
+
crypto = GPGME::Crypto.new :always_trust => true
|
99
|
+
encrypted = crypto.encrypt TEXT[:plain], :sign => true, :signers => KEYS.map{|k| k[:sha]}
|
100
|
+
signatures = 0
|
101
|
+
|
102
|
+
crypto.verify(encrypted) do |signature|
|
103
|
+
assert_instance_of GPGME::Signature, signature
|
104
|
+
signatures += 1
|
105
|
+
end
|
106
|
+
|
107
|
+
assert_equal 4, signatures
|
108
|
+
end
|
109
|
+
|
110
|
+
it "outputs to a file if specified" do
|
111
|
+
crypto = GPGME::Crypto.new :always_trust => true
|
112
|
+
file = Tempfile.new "test"
|
113
|
+
crypto.encrypt TEXT[:plain], :output => file
|
114
|
+
file_contents = file.read
|
115
|
+
file.seek 0
|
116
|
+
|
117
|
+
refute_empty file_contents
|
118
|
+
assert_equal TEXT[:plain], crypto.decrypt(file).read
|
119
|
+
end
|
120
|
+
|
121
|
+
# TODO find how to test
|
122
|
+
# it "raises GPGME::Error::UnusablePublicKey"
|
123
|
+
# it "raises GPGME::Error::UnusableSecretKey"
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "symmetric encryption/decryption" do
|
127
|
+
it "requires a password to encrypt" do
|
128
|
+
assert_raises GPGME::Error::BadPassphrase do
|
129
|
+
GPGME::Crypto.new.encrypt TEXT[:plain], :symmetric => true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "requires a password to decrypt" do
|
134
|
+
crypto = GPGME::Crypto.new
|
135
|
+
encrypted_data = crypto.encrypt TEXT[:plain],
|
136
|
+
:symmetric => true, :password => "gpgme"
|
137
|
+
|
138
|
+
assert_raises GPGME::Error::BadPassphrase do
|
139
|
+
crypto.decrypt encrypted_data
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it "can encrypt and decrypt with the same password" do
|
144
|
+
crypto = GPGME::Crypto.new :symmetric => true, :password => "gpgme"
|
145
|
+
encrypted_data = crypto.encrypt TEXT[:plain]
|
146
|
+
plain = crypto.decrypt encrypted_data
|
147
|
+
|
148
|
+
assert_equal "Hi there", plain.read
|
149
|
+
end
|
150
|
+
|
151
|
+
it "but breaks with different ones" do
|
152
|
+
crypto = GPGME::Crypto.new
|
153
|
+
encrypted_data = crypto.encrypt TEXT[:plain],
|
154
|
+
:symmetric => true, :password => "gpgme"
|
155
|
+
|
156
|
+
assert_raises GPGME::Error::DecryptFailed do
|
157
|
+
crypto.decrypt encrypted_data, :password => "wrong one"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe :decrypt do
|
163
|
+
it "decrypts encrypted stuff" do
|
164
|
+
assert_equal TEXT[:plain], GPGME::Crypto.new.decrypt(TEXT[:encrypted]).read
|
165
|
+
end
|
166
|
+
|
167
|
+
it "will not get into the signatures block if there's none" do
|
168
|
+
GPGME::Crypto.new.decrypt(TEXT[:encrypted]) do |signature|
|
169
|
+
flunk "If I'm here means there was some signature"
|
170
|
+
end
|
171
|
+
pass
|
172
|
+
end
|
173
|
+
|
174
|
+
it "will get signature elements if the encrypted thing was signed" do
|
175
|
+
signatures = 0
|
176
|
+
GPGME::Crypto.new.decrypt(TEXT[:signed]) do |signature|
|
177
|
+
assert_instance_of GPGME::Signature, signature
|
178
|
+
signatures += 1
|
179
|
+
end
|
180
|
+
assert_equal 1, signatures
|
181
|
+
end
|
182
|
+
|
183
|
+
it "writes to the output if passed" do
|
184
|
+
buffer = GPGME::Data.new
|
185
|
+
GPGME::Crypto.new.decrypt(TEXT[:encrypted], :output => buffer)
|
186
|
+
assert_equal TEXT[:plain], buffer.read
|
187
|
+
end
|
188
|
+
|
189
|
+
# TODO find ways to test this
|
190
|
+
# it "raises UnsupportedAlgorithm"
|
191
|
+
# it "raises WrongKeyUsage"
|
192
|
+
|
193
|
+
it "raises DecryptFailed when the decrypting key isn't available" do
|
194
|
+
assert_raises GPGME::Error::DecryptFailed do
|
195
|
+
GPGME::Crypto.new.decrypt(TEXT[:unavailable])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe :sign do
|
201
|
+
it "signs normal strings" do
|
202
|
+
crypto = GPGME::Crypto.new
|
203
|
+
signatures = 0
|
204
|
+
sign = crypto.sign "Hi there"
|
205
|
+
|
206
|
+
crypto.verify(sign) do |signature|
|
207
|
+
assert_instance_of GPGME::Signature, signature
|
208
|
+
assert signature.valid?
|
209
|
+
signatures += 1
|
210
|
+
end
|
211
|
+
|
212
|
+
assert_equal 1, signatures
|
213
|
+
end
|
214
|
+
|
215
|
+
# TODO Find how to import an expired public key
|
216
|
+
# it "raises an error if trying to sign with an expired key" do
|
217
|
+
# with_key EXPIRED_KEY do
|
218
|
+
# crypto = GPGME::Crypto.new
|
219
|
+
# assert_raises GPGME::Error::General do
|
220
|
+
# sign = crypto.sign "Hi there", :signer => EXPIRED_KEY[:sha]
|
221
|
+
# end
|
222
|
+
# end
|
223
|
+
# end
|
224
|
+
|
225
|
+
it "selects who to sign for" do
|
226
|
+
crypto = GPGME::Crypto.new
|
227
|
+
sign = crypto.sign "Hi there", :signer => KEYS.last[:sha]
|
228
|
+
key = GPGME::Key.get(KEYS.last[:sha])
|
229
|
+
|
230
|
+
signatures = 0
|
231
|
+
|
232
|
+
crypto.verify(sign) do |signature|
|
233
|
+
assert_instance_of GPGME::Signature, signature
|
234
|
+
assert_equal key, signature.key
|
235
|
+
signatures += 1
|
236
|
+
end
|
237
|
+
|
238
|
+
assert_equal 1, signatures
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
end
|