gpgme 2.0.24 → 2.0.26
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.
- checksums.yaml +4 -4
- data/ext/gpgme/extconf.rb +29 -6
- data/ext/gpgme/gpgme_n.c +629 -16
- data/lib/gpgme/constants.rb +138 -31
- data/lib/gpgme/crypto.rb +56 -1
- data/lib/gpgme/ctx.rb +69 -8
- data/lib/gpgme/io_callbacks.rb +6 -1
- data/lib/gpgme/key.rb +4 -3
- data/lib/gpgme/version.rb +1 -1
- data/lib/gpgme.rb +56 -1
- data/ports/archives/gpgme-2.0.0.tar.bz2 +0 -0
- data/ports/archives/libassuan-3.0.2.tar.bz2 +0 -0
- data/ports/archives/libgpg-error-1.55.tar.bz2 +0 -0
- data/test/crypto_test.rb +2 -2
- data/test/ctx_test.rb +1 -4
- data/test/data_test.rb +2 -2
- data/test/encryption_flags_test.rb +65 -0
- data/test/io_callbacks_test.rb +169 -0
- metadata +8 -9
- data/ports/archives/gpgme-1.21.0.tar.bz2 +0 -0
- data/ports/archives/libassuan-2.5.6.tar.bz2 +0 -0
- data/ports/archives/libgpg-error-1.47.tar.bz2 +0 -0
data/lib/gpgme/constants.rb
CHANGED
|
@@ -1,36 +1,98 @@
|
|
|
1
1
|
module GPGME
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
3
|
+
if defined?(GPGME_ATTR_ALGO)
|
|
4
|
+
ATTR_ALGO = GPGME_ATTR_ALGO
|
|
5
|
+
end
|
|
6
|
+
if defined?(GPGME_ATTR_CAN_CERTIFY)
|
|
7
|
+
ATTR_CAN_CERTIFY = GPGME_ATTR_CAN_CERTIFY
|
|
8
|
+
end
|
|
9
|
+
if defined?(GPGME_ATTR_CAN_ENCRYPT)
|
|
10
|
+
ATTR_CAN_ENCRYPT = GPGME_ATTR_CAN_ENCRYPT
|
|
11
|
+
end
|
|
12
|
+
if defined?(GPGME_ATTR_CAN_SIGN)
|
|
13
|
+
ATTR_CAN_SIGN = GPGME_ATTR_CAN_SIGN
|
|
14
|
+
end
|
|
15
|
+
if defined?(GPGME_ATTR_CHAINID)
|
|
16
|
+
ATTR_CHAINID = GPGME_ATTR_CHAINID
|
|
17
|
+
end
|
|
18
|
+
if defined?(GPGME_ATTR_COMMENT)
|
|
19
|
+
ATTR_COMMENT = GPGME_ATTR_COMMENT
|
|
20
|
+
end
|
|
21
|
+
if defined?(GPGME_ATTR_CREATED)
|
|
22
|
+
ATTR_CREATED = GPGME_ATTR_CREATED
|
|
23
|
+
end
|
|
24
|
+
if defined?(GPGME_ATTR_EMAIL)
|
|
25
|
+
ATTR_EMAIL = GPGME_ATTR_EMAIL
|
|
26
|
+
end
|
|
27
|
+
if defined?(GPGME_ATTR_ERRTOK)
|
|
28
|
+
ATTR_ERRTOK = GPGME_ATTR_ERRTOK
|
|
29
|
+
end
|
|
30
|
+
if defined?(GPGME_ATTR_EXPIRE)
|
|
31
|
+
ATTR_EXPIRE = GPGME_ATTR_EXPIRE
|
|
32
|
+
end
|
|
33
|
+
if defined?(GPGME_ATTR_FPR)
|
|
34
|
+
ATTR_FPR = GPGME_ATTR_FPR
|
|
35
|
+
end
|
|
36
|
+
if defined?(GPGME_ATTR_ISSUER)
|
|
37
|
+
ATTR_ISSUER = GPGME_ATTR_ISSUER
|
|
38
|
+
end
|
|
39
|
+
if defined?(GPGME_ATTR_IS_SECRET)
|
|
40
|
+
ATTR_IS_SECRET = GPGME_ATTR_IS_SECRET
|
|
41
|
+
end
|
|
42
|
+
if defined?(GPGME_ATTR_KEYID)
|
|
43
|
+
ATTR_KEYID = GPGME_ATTR_KEYID
|
|
44
|
+
end
|
|
45
|
+
if defined?(GPGME_ATTR_KEY_CAPS)
|
|
46
|
+
ATTR_KEY_CAPS = GPGME_ATTR_KEY_CAPS
|
|
47
|
+
end
|
|
48
|
+
if defined?(GPGME_ATTR_KEY_DISABLED)
|
|
49
|
+
ATTR_KEY_DISABLED = GPGME_ATTR_KEY_DISABLED
|
|
50
|
+
end
|
|
51
|
+
if defined?(GPGME_ATTR_KEY_EXPIRED)
|
|
52
|
+
ATTR_KEY_EXPIRED = GPGME_ATTR_KEY_EXPIRED
|
|
53
|
+
end
|
|
54
|
+
if defined?(GPGME_ATTR_KEY_INVALID)
|
|
55
|
+
ATTR_KEY_INVALID = GPGME_ATTR_KEY_INVALID
|
|
56
|
+
end
|
|
57
|
+
if defined?(GPGME_ATTR_KEY_REVOKED)
|
|
58
|
+
ATTR_KEY_REVOKED = GPGME_ATTR_KEY_REVOKED
|
|
59
|
+
end
|
|
60
|
+
if defined?(GPGME_ATTR_LEN)
|
|
61
|
+
ATTR_LEN = GPGME_ATTR_LEN
|
|
62
|
+
end
|
|
63
|
+
if defined?(GPGME_ATTR_LEVEL)
|
|
64
|
+
ATTR_LEVEL = GPGME_ATTR_LEVEL
|
|
65
|
+
end
|
|
66
|
+
if defined?(GPGME_ATTR_NAME)
|
|
67
|
+
ATTR_NAME = GPGME_ATTR_NAME
|
|
68
|
+
end
|
|
69
|
+
if defined?(GPGME_ATTR_OTRUST)
|
|
70
|
+
ATTR_OTRUST = GPGME_ATTR_OTRUST
|
|
71
|
+
end
|
|
72
|
+
if defined?(GPGME_ATTR_SERIAL)
|
|
73
|
+
ATTR_SERIAL = GPGME_ATTR_SERIAL
|
|
74
|
+
end
|
|
75
|
+
if defined?(GPGME_ATTR_SIG_STATUS)
|
|
76
|
+
ATTR_SIG_STATUS = GPGME_ATTR_SIG_STATUS
|
|
77
|
+
end
|
|
78
|
+
if defined?(GPGME_ATTR_SIG_SUMMARY)
|
|
79
|
+
ATTR_SIG_SUMMARY = GPGME_ATTR_SIG_SUMMARY
|
|
80
|
+
end
|
|
81
|
+
if defined?(GPGME_ATTR_TYPE)
|
|
82
|
+
ATTR_TYPE = GPGME_ATTR_TYPE
|
|
83
|
+
end
|
|
84
|
+
if defined?(GPGME_ATTR_UID_INVALID)
|
|
85
|
+
ATTR_UID_INVALID = GPGME_ATTR_UID_INVALID
|
|
86
|
+
end
|
|
87
|
+
if defined?(GPGME_ATTR_UID_REVOKED)
|
|
88
|
+
ATTR_UID_REVOKED = GPGME_ATTR_UID_REVOKED
|
|
89
|
+
end
|
|
90
|
+
if defined?(GPGME_ATTR_USERID)
|
|
91
|
+
ATTR_USERID = GPGME_ATTR_USERID
|
|
92
|
+
end
|
|
93
|
+
if defined?(GPGME_ATTR_VALIDITY)
|
|
94
|
+
ATTR_VALIDITY = GPGME_ATTR_VALIDITY
|
|
95
|
+
end
|
|
34
96
|
DATA_ENCODING_ARMOR = GPGME_DATA_ENCODING_ARMOR
|
|
35
97
|
DATA_ENCODING_BASE64 = GPGME_DATA_ENCODING_BASE64
|
|
36
98
|
DATA_ENCODING_BINARY = GPGME_DATA_ENCODING_BINARY
|
|
@@ -39,6 +101,24 @@ module GPGME
|
|
|
39
101
|
if defined?(GPGME_ENCRYPT_NO_ENCRYPT_TO)
|
|
40
102
|
ENCRYPT_NO_ENCRYPT_TO = GPGME_ENCRYPT_NO_ENCRYPT_TO
|
|
41
103
|
end
|
|
104
|
+
if defined?(GPGME_ENCRYPT_PREPARE)
|
|
105
|
+
ENCRYPT_PREPARE = GPGME_ENCRYPT_PREPARE
|
|
106
|
+
end
|
|
107
|
+
if defined?(GPGME_ENCRYPT_EXPECT_SIGN)
|
|
108
|
+
ENCRYPT_EXPECT_SIGN = GPGME_ENCRYPT_EXPECT_SIGN
|
|
109
|
+
end
|
|
110
|
+
if defined?(GPGME_ENCRYPT_NO_COMPRESS)
|
|
111
|
+
ENCRYPT_NO_COMPRESS = GPGME_ENCRYPT_NO_COMPRESS
|
|
112
|
+
end
|
|
113
|
+
if defined?(GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK)
|
|
114
|
+
ENCRYPT_UNSIGNED_INTEGRITY_CHECK = GPGME_ENCRYPT_UNSIGNED_INTEGRITY_CHECK
|
|
115
|
+
end
|
|
116
|
+
if defined?(GPGME_ENCRYPT_SYMMETRIC)
|
|
117
|
+
ENCRYPT_SYMMETRIC = GPGME_ENCRYPT_SYMMETRIC
|
|
118
|
+
end
|
|
119
|
+
if defined?(GPGME_ENCRYPT_THROW_KEYIDS)
|
|
120
|
+
ENCRYPT_THROW_KEYIDS = GPGME_ENCRYPT_THROW_KEYIDS
|
|
121
|
+
end
|
|
42
122
|
IMPORT_NEW = GPGME_IMPORT_NEW
|
|
43
123
|
IMPORT_SECRET = GPGME_IMPORT_SECRET
|
|
44
124
|
IMPORT_SIG = GPGME_IMPORT_SIG
|
|
@@ -265,4 +345,31 @@ module GPGME
|
|
|
265
345
|
VALIDITY_FULL => :full,
|
|
266
346
|
VALIDITY_ULTIMATE => :ultimate
|
|
267
347
|
}
|
|
348
|
+
|
|
349
|
+
if defined?(GPGME_DELETE_ALLOW_SECRET)
|
|
350
|
+
DELETE_ALLOW_SECRET = GPGME_DELETE_ALLOW_SECRET
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
if defined?(GPGME_DELETE_FORCE)
|
|
354
|
+
DELETE_FORCE = GPGME_DELETE_FORCE
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Random number generation mode flags added in 2.0.0
|
|
358
|
+
if defined?(GPGME_RANDOM_MODE_NORMAL)
|
|
359
|
+
RANDOM_MODE_NORMAL = GPGME_RANDOM_MODE_NORMAL
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
if defined?(GPGME_RANDOM_MODE_ZBASE32)
|
|
363
|
+
RANDOM_MODE_ZBASE32 = GPGME_RANDOM_MODE_ZBASE32
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Decrypt flags added in 2.0.0
|
|
367
|
+
if defined?(GPGME_DECRYPT_LISTONLY)
|
|
368
|
+
DECRYPT_LISTONLY = GPGME_DECRYPT_LISTONLY
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# Key generation flags added in 2.0.0
|
|
372
|
+
if defined?(GPGME_CREATE_GROUP)
|
|
373
|
+
CREATE_GROUP = GPGME_CREATE_GROUP
|
|
374
|
+
end
|
|
268
375
|
end
|
data/lib/gpgme/crypto.rb
CHANGED
|
@@ -91,7 +91,9 @@ module GPGME
|
|
|
91
91
|
begin
|
|
92
92
|
if options[:sign]
|
|
93
93
|
if options[:signers]
|
|
94
|
-
|
|
94
|
+
# Optimization: reuse recipient Key objects if signers match
|
|
95
|
+
# to avoid redundant key lookups
|
|
96
|
+
signers = resolve_keys_for_signing(options[:signers], keys)
|
|
95
97
|
ctx.add_signer(*signers)
|
|
96
98
|
end
|
|
97
99
|
ctx.encrypt_sign(keys, plain_data, cipher_data, flags)
|
|
@@ -353,5 +355,58 @@ module GPGME
|
|
|
353
355
|
end
|
|
354
356
|
end
|
|
355
357
|
|
|
358
|
+
private
|
|
359
|
+
|
|
360
|
+
# Resolves keys for signing, reusing already-fetched recipient keys when possible
|
|
361
|
+
# to avoid redundant key lookups which can be slow.
|
|
362
|
+
#
|
|
363
|
+
# @param signers_input [Array, String, Key] The signer identifiers
|
|
364
|
+
# @param recipient_keys [Array<Key>, nil] Already-fetched recipient keys to check for reuse
|
|
365
|
+
# @return [Array<Key>] Keys suitable for signing
|
|
366
|
+
def resolve_keys_for_signing(signers_input, recipient_keys)
|
|
367
|
+
signers_input = [signers_input].flatten
|
|
368
|
+
recipient_keys ||= []
|
|
369
|
+
|
|
370
|
+
# Build a lookup hash of recipient keys by various identifiers
|
|
371
|
+
recipient_lookup = {}
|
|
372
|
+
recipient_keys.each do |key|
|
|
373
|
+
next unless key.is_a?(Key)
|
|
374
|
+
# Index by fingerprint, short key ID, and email
|
|
375
|
+
recipient_lookup[key.fingerprint] = key if key.fingerprint
|
|
376
|
+
recipient_lookup[key.fingerprint[-16..]] = key if key.fingerprint && key.fingerprint.length >= 16
|
|
377
|
+
recipient_lookup[key.fingerprint[-8..]] = key if key.fingerprint && key.fingerprint.length >= 8
|
|
378
|
+
if key.uids && !key.uids.empty?
|
|
379
|
+
key.uids.each do |uid|
|
|
380
|
+
recipient_lookup[uid.email] = key if uid.email && !uid.email.empty?
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
result = []
|
|
386
|
+
needs_lookup = []
|
|
387
|
+
|
|
388
|
+
signers_input.each do |signer|
|
|
389
|
+
case signer
|
|
390
|
+
when Key
|
|
391
|
+
# Already a key object, use directly if it can sign
|
|
392
|
+
result << signer if signer.usable_for?([:sign])
|
|
393
|
+
when String
|
|
394
|
+
# Check if we already have this key from recipients
|
|
395
|
+
if (existing = recipient_lookup[signer]) && existing.usable_for?([:sign])
|
|
396
|
+
result << existing
|
|
397
|
+
else
|
|
398
|
+
needs_lookup << signer
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Only do a Key.find for signers we couldn't resolve from recipients
|
|
404
|
+
unless needs_lookup.empty?
|
|
405
|
+
result += Key.find(:public, needs_lookup, :sign)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
result
|
|
409
|
+
end
|
|
410
|
+
|
|
356
411
|
end # module Crypto
|
|
357
412
|
end # module GPGME
|
data/lib/gpgme/ctx.rb
CHANGED
|
@@ -76,10 +76,12 @@ module GPGME
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
if block_given?
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
GPGME.synchronize do
|
|
80
|
+
begin
|
|
81
|
+
yield ctx
|
|
82
|
+
ensure
|
|
83
|
+
GPGME::gpgme_release(ctx)
|
|
84
|
+
end
|
|
83
85
|
end
|
|
84
86
|
else
|
|
85
87
|
ctx
|
|
@@ -447,14 +449,26 @@ module GPGME
|
|
|
447
449
|
# Delete the key from the key ring.
|
|
448
450
|
# If allow_secret is false, only public keys are deleted,
|
|
449
451
|
# otherwise secret keys are deleted as well.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
+
# If force is true, the confirmation dialog will not be displayed.
|
|
453
|
+
def delete_key(key, allow_secret = false, force = false)
|
|
454
|
+
err = nil
|
|
455
|
+
if defined?(GPGME::gpgme_op_delete_ext)
|
|
456
|
+
flag = 0
|
|
457
|
+
flag ^= GPGME::DELETE_ALLOW_SECRET if allow_secret
|
|
458
|
+
flag ^= GPGME::DELETE_FORCE if force
|
|
459
|
+
err = GPGME::gpgme_op_delete_ext(self, key, flag)
|
|
460
|
+
else
|
|
461
|
+
err = GPGME::gpgme_op_delete(self, key, allow_secret ? 1 : 0)
|
|
462
|
+
end
|
|
452
463
|
exc = GPGME::error_to_exception(err)
|
|
453
464
|
raise exc if exc
|
|
454
465
|
end
|
|
455
466
|
alias delete delete_key
|
|
456
467
|
|
|
457
468
|
# Edit attributes of the key in the local key ring.
|
|
469
|
+
#
|
|
470
|
+
# The callback receives (hook_value, status, args, fd) where status is
|
|
471
|
+
# a numeric status code (e.g., GPGME::GPGME_STATUS_GET_BOOL).
|
|
458
472
|
def edit_key(key, editfunc, hook_value = nil, out = Data.new)
|
|
459
473
|
err = GPGME::gpgme_op_edit(self, key, editfunc, hook_value, out)
|
|
460
474
|
exc = GPGME::error_to_exception(err)
|
|
@@ -463,6 +477,9 @@ module GPGME
|
|
|
463
477
|
alias edit edit_key
|
|
464
478
|
|
|
465
479
|
# Edit attributes of the key on the card.
|
|
480
|
+
#
|
|
481
|
+
# The callback receives (hook_value, status, args, fd) where status is
|
|
482
|
+
# a numeric status code (e.g., GPGME::GPGME_STATUS_GET_BOOL).
|
|
466
483
|
def edit_card_key(key, editfunc, hook_value = nil, out = Data.new)
|
|
467
484
|
err = GPGME::gpgme_op_card_edit(self, key, editfunc, hook_value, out)
|
|
468
485
|
exc = GPGME::error_to_exception(err)
|
|
@@ -561,6 +578,39 @@ module GPGME
|
|
|
561
578
|
raise exc if exc
|
|
562
579
|
end
|
|
563
580
|
|
|
581
|
+
# Generate cryptographically strong random bytes.
|
|
582
|
+
# Available since GPGME 2.0.0.
|
|
583
|
+
#
|
|
584
|
+
# @param [Integer] size Number of bytes to generate
|
|
585
|
+
# @param [Integer] mode Random generation mode (RANDOM_MODE_NORMAL or RANDOM_MODE_ZBASE32)
|
|
586
|
+
# @return [String] Random bytes as a binary string
|
|
587
|
+
def random_bytes(size, mode = GPGME::RANDOM_MODE_NORMAL)
|
|
588
|
+
result = GPGME::gpgme_op_random_bytes(self, size, mode)
|
|
589
|
+
if result.is_a?(String)
|
|
590
|
+
result
|
|
591
|
+
else
|
|
592
|
+
exc = GPGME::error_to_exception(result)
|
|
593
|
+
raise exc if exc
|
|
594
|
+
result
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
# Generate a cryptographically strong random unsigned integer value.
|
|
599
|
+
# Available since GPGME 2.0.0.
|
|
600
|
+
#
|
|
601
|
+
# @param [Integer] limit Upper limit for the random value (exclusive)
|
|
602
|
+
# @return [Integer] Random unsigned integer value in range [0, limit)
|
|
603
|
+
def random_value(limit)
|
|
604
|
+
result = GPGME::gpgme_op_random_value(self, limit)
|
|
605
|
+
if result.is_a?(Integer) && result >= 0
|
|
606
|
+
result
|
|
607
|
+
else
|
|
608
|
+
exc = GPGME::error_to_exception(result)
|
|
609
|
+
raise exc if exc
|
|
610
|
+
result
|
|
611
|
+
end
|
|
612
|
+
end
|
|
613
|
+
|
|
564
614
|
def inspect
|
|
565
615
|
"#<#{self.class} protocol=#{PROTOCOL_NAMES[protocol] || protocol}, \
|
|
566
616
|
armor=#{armor}, textmode=#{textmode}, \
|
|
@@ -570,10 +620,21 @@ keylist_mode=#{KEYLIST_MODE_NAMES[keylist_mode]}>"
|
|
|
570
620
|
private
|
|
571
621
|
|
|
572
622
|
def self.pass_function(pass, uid_hint, passphrase_info, prev_was_bad, fd)
|
|
623
|
+
# Write the passphrase directly using IO.for_fd. We set autoclose=false
|
|
624
|
+
# to prevent Ruby from closing the fd (which belongs to GPGME/gpg-agent).
|
|
625
|
+
# The IO object is used only within this method scope and not stored,
|
|
626
|
+
# so we also ensure it isn't prematurely collected by keeping a strong
|
|
627
|
+
# reference until we're done.
|
|
573
628
|
io = IO.for_fd(fd, 'w')
|
|
574
629
|
io.autoclose = false
|
|
575
|
-
|
|
576
|
-
|
|
630
|
+
begin
|
|
631
|
+
io.write "#{pass}\n"
|
|
632
|
+
io.flush
|
|
633
|
+
rescue => e
|
|
634
|
+
# If the fd has become invalid (e.g. agent communication error),
|
|
635
|
+
# re-raise as a more descriptive error.
|
|
636
|
+
raise e
|
|
637
|
+
end
|
|
577
638
|
end
|
|
578
639
|
|
|
579
640
|
end
|
data/lib/gpgme/io_callbacks.rb
CHANGED
|
@@ -9,7 +9,12 @@ module GPGME
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def write(hook, buffer, length)
|
|
12
|
-
|
|
12
|
+
data = buffer[0 .. length]
|
|
13
|
+
# Handle encoding conversion if the IO has a different encoding
|
|
14
|
+
if @io.respond_to?(:external_encoding) && @io.external_encoding
|
|
15
|
+
data = data.encode(@io.external_encoding, invalid: :replace, undef: :replace)
|
|
16
|
+
end
|
|
17
|
+
@io.write(data)
|
|
13
18
|
end
|
|
14
19
|
|
|
15
20
|
def seek(hook, offset, whence)
|
data/lib/gpgme/key.rb
CHANGED
|
@@ -156,10 +156,11 @@ module GPGME
|
|
|
156
156
|
|
|
157
157
|
##
|
|
158
158
|
# Delete this key. If it's public, and has a secret one it will fail unless
|
|
159
|
-
# +allow_secret+ is specified as true.
|
|
160
|
-
|
|
159
|
+
# +allow_secret+ is specified as true. Suppress the confirmation dialog, if
|
|
160
|
+
# +force+ is specified as true.
|
|
161
|
+
def delete!(allow_secret = false, force = false)
|
|
161
162
|
GPGME::Ctx.new do |ctx|
|
|
162
|
-
ctx.delete_key self, allow_secret
|
|
163
|
+
ctx.delete_key self, allow_secret, force
|
|
163
164
|
end
|
|
164
165
|
end
|
|
165
166
|
|
data/lib/gpgme/version.rb
CHANGED
data/lib/gpgme.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
require 'gpgme_n'
|
|
2
|
+
require 'monitor'
|
|
2
3
|
|
|
3
|
-
#
|
|
4
|
+
# This call initializes the GPGME library and must happen before
|
|
5
|
+
# any GPGME operations (e.g. Ctx.new) can succeed.
|
|
4
6
|
GPGME::gpgme_check_version(nil)
|
|
5
7
|
|
|
6
8
|
require 'gpgme/constants'
|
|
@@ -19,8 +21,61 @@ require 'gpgme/engine'
|
|
|
19
21
|
require 'gpgme/crypto'
|
|
20
22
|
|
|
21
23
|
module GPGME
|
|
24
|
+
|
|
25
|
+
# Mutex for serializing GPGME operations when thread safety is enabled.
|
|
26
|
+
# While the underlying GPGME C library supports separate contexts in
|
|
27
|
+
# separate threads, the communication with gpg-agent over Unix domain
|
|
28
|
+
# sockets can produce "Bad file descriptor" errors under heavy concurrent
|
|
29
|
+
# load. Thread-safe mode is enabled by default and can be disabled
|
|
30
|
+
# if not needed (e.g. single-threaded applications).
|
|
31
|
+
#
|
|
32
|
+
# A Monitor is used instead of a Mutex because GPGME operations are
|
|
33
|
+
# reentrant — e.g. Crypto#sign calls Ctx.new, and within that block,
|
|
34
|
+
# Key.find calls Ctx.new again.
|
|
35
|
+
#
|
|
36
|
+
# @example Disable thread-safe mode for single-threaded apps
|
|
37
|
+
# GPGME.thread_safe = false
|
|
38
|
+
#
|
|
39
|
+
@thread_safe_mutex = Monitor.new
|
|
40
|
+
@thread_safe = true
|
|
41
|
+
|
|
22
42
|
class << self
|
|
23
43
|
|
|
44
|
+
# Enable or disable thread-safe mode. Enabled by default. When
|
|
45
|
+
# enabled, all high-level GPGME operations (encrypt, decrypt, sign,
|
|
46
|
+
# verify, key listing, etc.) are serialized through a global monitor
|
|
47
|
+
# to prevent concurrent access to gpg-agent from causing "Bad file
|
|
48
|
+
# descriptor" errors. Disable for single-threaded apps if the
|
|
49
|
+
# synchronization overhead is undesirable.
|
|
50
|
+
#
|
|
51
|
+
# @param [Boolean] value false to disable thread-safe mode
|
|
52
|
+
attr_writer :thread_safe
|
|
53
|
+
|
|
54
|
+
# Returns true if thread-safe mode is enabled.
|
|
55
|
+
def thread_safe?
|
|
56
|
+
@thread_safe
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# The mutex used for thread-safe serialization. Can be used directly
|
|
60
|
+
# if you need finer-grained control over locking.
|
|
61
|
+
#
|
|
62
|
+
# @example manual locking
|
|
63
|
+
# GPGME.synchronize do
|
|
64
|
+
# # multiple GPGME operations atomically
|
|
65
|
+
# end
|
|
66
|
+
attr_reader :thread_safe_mutex
|
|
67
|
+
|
|
68
|
+
# Execute a block with the GPGME mutex held if thread-safe mode is
|
|
69
|
+
# enabled. If thread-safe mode is disabled, the block is executed
|
|
70
|
+
# directly without locking.
|
|
71
|
+
def synchronize(&block)
|
|
72
|
+
if @thread_safe
|
|
73
|
+
@thread_safe_mutex.synchronize(&block)
|
|
74
|
+
else
|
|
75
|
+
yield
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
24
79
|
# From the c extension
|
|
25
80
|
alias pubkey_algo_name gpgme_pubkey_algo_name
|
|
26
81
|
alias hash_algo_name gpgme_hash_algo_name
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/test/crypto_test.rb
CHANGED
|
@@ -90,7 +90,7 @@ describe GPGME::Crypto do
|
|
|
90
90
|
encrypted = crypto.encrypt TEXT[:plain], :sign => true
|
|
91
91
|
signatures = 0
|
|
92
92
|
|
|
93
|
-
crypto.
|
|
93
|
+
crypto.decrypt(encrypted) do |signature|
|
|
94
94
|
assert_instance_of GPGME::Signature, signature
|
|
95
95
|
signatures += 1
|
|
96
96
|
end
|
|
@@ -103,7 +103,7 @@ describe GPGME::Crypto do
|
|
|
103
103
|
encrypted = crypto.encrypt TEXT[:plain], :sign => true, :signers => KEYS.map{|k| k[:sha]}
|
|
104
104
|
signatures = 0
|
|
105
105
|
|
|
106
|
-
crypto.
|
|
106
|
+
crypto.decrypt(encrypted) do |signature|
|
|
107
107
|
assert_instance_of GPGME::Signature, signature
|
|
108
108
|
signatures += 1
|
|
109
109
|
end
|
data/test/ctx_test.rb
CHANGED
|
@@ -70,9 +70,6 @@ describe GPGME::Ctx do
|
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
it "should not segfault" do
|
|
73
|
-
cipher = GPGME::Data.new(KEY_1_ENCRYPTED)
|
|
74
|
-
ouput = GPGME::Data.new
|
|
75
|
-
|
|
76
73
|
GPGME::Ctx.new do |ctx|
|
|
77
74
|
assert_raises ArgumentError do
|
|
78
75
|
ctx.decrypt_result
|
|
@@ -214,7 +211,7 @@ describe GPGME::Ctx do
|
|
|
214
211
|
|
|
215
212
|
it "doesn't allow just any value" do
|
|
216
213
|
assert_raises GPGME::Error::InvalidValue do
|
|
217
|
-
|
|
214
|
+
GPGME::Ctx.new(:protocol => -200)
|
|
218
215
|
end
|
|
219
216
|
end
|
|
220
217
|
end
|
data/test/data_test.rb
CHANGED
|
@@ -25,14 +25,14 @@ describe GPGME::Data do
|
|
|
25
25
|
it "creates a data from a file" do
|
|
26
26
|
# magic fromfile
|
|
27
27
|
data = GPGME::Data.new(File.open(__FILE__))
|
|
28
|
-
assert_match
|
|
28
|
+
assert_match(/magic fromfile/, data.read)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
it "creates a data from file descriptor" do
|
|
32
32
|
# magic filedescriptor
|
|
33
33
|
File.open(__FILE__) do |f|
|
|
34
34
|
data = GPGME::Data.new(f.fileno)
|
|
35
|
-
assert_match
|
|
35
|
+
assert_match(/magic filedescriptor/, data.read)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require 'test_helper'
|
|
3
|
+
|
|
4
|
+
describe 'GPGME Encryption Flags' do
|
|
5
|
+
it 'should expose ENCRYPT_ALWAYS_TRUST' do
|
|
6
|
+
assert_equal 1, GPGME::ENCRYPT_ALWAYS_TRUST
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it 'should expose ENCRYPT_NO_ENCRYPT_TO if available' do
|
|
10
|
+
if defined?(GPGME::ENCRYPT_NO_ENCRYPT_TO)
|
|
11
|
+
assert GPGME::ENCRYPT_NO_ENCRYPT_TO.is_a?(Integer)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should expose ENCRYPT_PREPARE if available' do
|
|
16
|
+
if defined?(GPGME::ENCRYPT_PREPARE)
|
|
17
|
+
assert GPGME::ENCRYPT_PREPARE.is_a?(Integer)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should expose ENCRYPT_EXPECT_SIGN if available' do
|
|
22
|
+
if defined?(GPGME::ENCRYPT_EXPECT_SIGN)
|
|
23
|
+
assert GPGME::ENCRYPT_EXPECT_SIGN.is_a?(Integer)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'should expose ENCRYPT_NO_COMPRESS if available' do
|
|
28
|
+
if defined?(GPGME::ENCRYPT_NO_COMPRESS)
|
|
29
|
+
assert GPGME::ENCRYPT_NO_COMPRESS.is_a?(Integer)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'should expose ENCRYPT_UNSIGNED_INTEGRITY_CHECK if available' do
|
|
34
|
+
if defined?(GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK)
|
|
35
|
+
assert GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK.is_a?(Integer)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should expose ENCRYPT_SYMMETRIC if available' do
|
|
40
|
+
if defined?(GPGME::ENCRYPT_SYMMETRIC)
|
|
41
|
+
assert GPGME::ENCRYPT_SYMMETRIC.is_a?(Integer)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should expose ENCRYPT_THROW_KEYIDS if available' do
|
|
46
|
+
if defined?(GPGME::ENCRYPT_THROW_KEYIDS)
|
|
47
|
+
assert GPGME::ENCRYPT_THROW_KEYIDS.is_a?(Integer)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should use different flag values for different flags' do
|
|
52
|
+
flags = []
|
|
53
|
+
flags << GPGME::ENCRYPT_ALWAYS_TRUST
|
|
54
|
+
flags << GPGME::ENCRYPT_NO_ENCRYPT_TO if defined?(GPGME::ENCRYPT_NO_ENCRYPT_TO)
|
|
55
|
+
flags << GPGME::ENCRYPT_PREPARE if defined?(GPGME::ENCRYPT_PREPARE)
|
|
56
|
+
flags << GPGME::ENCRYPT_EXPECT_SIGN if defined?(GPGME::ENCRYPT_EXPECT_SIGN)
|
|
57
|
+
flags << GPGME::ENCRYPT_NO_COMPRESS if defined?(GPGME::ENCRYPT_NO_COMPRESS)
|
|
58
|
+
flags << GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK if defined?(GPGME::ENCRYPT_UNSIGNED_INTEGRITY_CHECK)
|
|
59
|
+
flags << GPGME::ENCRYPT_SYMMETRIC if defined?(GPGME::ENCRYPT_SYMMETRIC)
|
|
60
|
+
flags << GPGME::ENCRYPT_THROW_KEYIDS if defined?(GPGME::ENCRYPT_THROW_KEYIDS)
|
|
61
|
+
|
|
62
|
+
# All flags should be unique
|
|
63
|
+
assert_equal flags.length, flags.uniq.length
|
|
64
|
+
end
|
|
65
|
+
end
|