openpgp 0.0.2 → 0.0.3
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/AUTHORS +1 -2
- data/CONTRIBUTORS +1 -0
- data/README +45 -13
- data/VERSION +1 -1
- data/lib/openpgp.rb +17 -3
- data/lib/openpgp/algorithm.rb +1 -0
- data/lib/openpgp/armor.rb +37 -9
- data/lib/openpgp/buffer.rb +57 -8
- data/lib/openpgp/cipher.rb +34 -3
- data/lib/openpgp/client/gnupg.rb +230 -20
- data/lib/openpgp/digest.rb +19 -0
- data/lib/openpgp/digest/md5.rb +1 -0
- data/lib/openpgp/digest/rmd160.rb +1 -0
- data/lib/openpgp/digest/sha1.rb +1 -0
- data/lib/openpgp/digest/sha2.rb +4 -0
- data/lib/openpgp/engine.rb +17 -0
- data/lib/openpgp/engine/gnupg.rb +58 -1
- data/lib/openpgp/engine/openssl.rb +13 -1
- data/lib/openpgp/message.rb +32 -2
- data/lib/openpgp/packet.rb +31 -11
- data/lib/openpgp/random.rb +14 -2
- data/lib/openpgp/s2k.rb +70 -2
- data/lib/openpgp/util.rb +14 -10
- data/lib/openpgp/version.rb +12 -4
- metadata +50 -20
- data/Rakefile +0 -5
data/lib/openpgp/digest.rb
CHANGED
@@ -14,6 +14,9 @@ module OpenPGP
|
|
14
14
|
|
15
15
|
DEFAULT = SHA1
|
16
16
|
|
17
|
+
##
|
18
|
+
# @param [Symbol, String, Integer] identifier
|
19
|
+
# @return [Class]
|
17
20
|
def self.for(identifier)
|
18
21
|
case identifier
|
19
22
|
when Symbol then const_get(identifier.to_s.upcase)
|
@@ -28,30 +31,46 @@ module OpenPGP
|
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
34
|
+
##
|
35
|
+
# @return [Integer]
|
31
36
|
def self.to_i() identifier end
|
32
37
|
|
38
|
+
##
|
39
|
+
# @return [Integer]
|
33
40
|
def self.identifier
|
34
41
|
const_get(:IDENTIFIER)
|
35
42
|
end
|
36
43
|
|
44
|
+
##
|
45
|
+
# @return [Symbol]
|
37
46
|
def self.algorithm
|
38
47
|
name.split('::').last.to_sym unless self == Digest
|
39
48
|
end
|
40
49
|
|
50
|
+
##
|
51
|
+
# @return [Integer]
|
41
52
|
def self.hexsize
|
42
53
|
size * 2
|
43
54
|
end
|
44
55
|
|
56
|
+
##
|
57
|
+
# @return [Integer]
|
45
58
|
def self.size
|
46
59
|
require 'digest' unless defined?(::Digest)
|
47
60
|
::Digest.const_get(algorithm).new.digest_length
|
48
61
|
end
|
49
62
|
|
63
|
+
##
|
64
|
+
# @param [String] data
|
65
|
+
# @return [String]
|
50
66
|
def self.hexdigest(data)
|
51
67
|
require 'digest' unless defined?(::Digest)
|
52
68
|
::Digest.const_get(algorithm).hexdigest(data).upcase
|
53
69
|
end
|
54
70
|
|
71
|
+
##
|
72
|
+
# @param [String] data
|
73
|
+
# @return [String]
|
55
74
|
def self.digest(data)
|
56
75
|
require 'digest' unless defined?(::Digest)
|
57
76
|
::Digest.const_get(algorithm).digest(data)
|
data/lib/openpgp/digest/md5.rb
CHANGED
data/lib/openpgp/digest/sha1.rb
CHANGED
data/lib/openpgp/digest/sha2.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
module OpenPGP
|
2
2
|
class Digest
|
3
|
+
##
|
3
4
|
class SHA224 < Digest
|
4
5
|
IDENTIFIER = 11
|
5
6
|
end
|
6
7
|
|
8
|
+
##
|
7
9
|
class SHA256 < Digest
|
8
10
|
IDENTIFIER = 8
|
9
11
|
end
|
10
12
|
|
13
|
+
##
|
11
14
|
class SHA384 < Digest
|
12
15
|
IDENTIFIER = 9
|
13
16
|
end
|
14
17
|
|
18
|
+
##
|
15
19
|
class SHA512 < Digest
|
16
20
|
IDENTIFIER = 10
|
17
21
|
end
|
data/lib/openpgp/engine.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
module OpenPGP
|
2
|
+
##
|
2
3
|
class Engine
|
3
4
|
autoload :GnuPG, 'openpgp/engine/gnupg'
|
4
5
|
autoload :OpenSSL, 'openpgp/engine/openssl'
|
5
6
|
|
7
|
+
##
|
8
|
+
# @return [Boolean]
|
6
9
|
def self.available?
|
7
10
|
begin
|
8
11
|
load!(true)
|
@@ -12,14 +15,25 @@ module OpenPGP
|
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
18
|
+
##
|
19
|
+
# @param [Boolean] reload
|
20
|
+
# @return [void]
|
21
|
+
# @raise [LoadError]
|
15
22
|
def self.load!(reload = false)
|
16
23
|
raise LoadError
|
17
24
|
end
|
18
25
|
|
26
|
+
##
|
27
|
+
# @return [void]
|
28
|
+
# @raise [LoadError]
|
19
29
|
def self.install!
|
20
30
|
load!
|
21
31
|
end
|
22
32
|
|
33
|
+
##
|
34
|
+
# @yield [engine]
|
35
|
+
# @yieldparam [Engine] engine
|
36
|
+
# @return [void]
|
23
37
|
def self.use(&block)
|
24
38
|
load!
|
25
39
|
block.call(self)
|
@@ -27,6 +41,9 @@ module OpenPGP
|
|
27
41
|
|
28
42
|
protected
|
29
43
|
|
44
|
+
##
|
45
|
+
# @param [Module] extension
|
46
|
+
# @return [void]
|
30
47
|
def self.install_extensions!(extension)
|
31
48
|
name = extension.name.split('::').last.to_sym
|
32
49
|
|
data/lib/openpgp/engine/gnupg.rb
CHANGED
@@ -6,6 +6,8 @@ module OpenPGP class Engine
|
|
6
6
|
class GnuPG < Engine
|
7
7
|
class Error < IOError; end
|
8
8
|
|
9
|
+
##
|
10
|
+
# @return [Boolean]
|
9
11
|
def self.available?
|
10
12
|
self.new.available?
|
11
13
|
end
|
@@ -19,9 +21,14 @@ module OpenPGP class Engine
|
|
19
21
|
:no_random_seed_file => true,
|
20
22
|
}
|
21
23
|
|
24
|
+
# @return [String]
|
22
25
|
attr_accessor :where
|
26
|
+
|
27
|
+
# @return [Hash{Symbol => Object}]
|
23
28
|
attr_accessor :options
|
24
29
|
|
30
|
+
##
|
31
|
+
# @param [Hash{Symbol => Object}] options
|
25
32
|
def initialize(options = {})
|
26
33
|
@where = '/usr/bin/env gpg' # FIXME
|
27
34
|
@options = OPTIONS.merge!(options)
|
@@ -29,18 +36,25 @@ module OpenPGP class Engine
|
|
29
36
|
|
30
37
|
##
|
31
38
|
# Determines if GnuPG is available.
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
32
41
|
def available?
|
33
42
|
!!version
|
34
43
|
end
|
35
44
|
|
36
45
|
##
|
37
46
|
# Returns the GnuPG version number.
|
47
|
+
#
|
48
|
+
# @return [String]
|
38
49
|
def version
|
39
50
|
exec(:version).readline =~ /^gpg \(GnuPG\) (.*)$/ ? $1 : nil
|
40
51
|
end
|
41
52
|
|
42
53
|
##
|
43
54
|
# Generates a new OpenPGP keypair and stores it GnuPG's keyring.
|
55
|
+
#
|
56
|
+
# @param [Hash{Symbol => String}] info
|
57
|
+
# @return [Integer]
|
44
58
|
def gen_key(info = {})
|
45
59
|
stdin, stdout, stderr = exec3(:gen_key) do |stdin, stdout, stderr|
|
46
60
|
stdin.puts "Key-Type: #{info[:key_type]}" if info[:key_type]
|
@@ -64,22 +78,34 @@ module OpenPGP class Engine
|
|
64
78
|
|
65
79
|
##
|
66
80
|
# Exports a specified key from the GnuPG keyring.
|
81
|
+
#
|
82
|
+
# @param [String] key_id
|
83
|
+
# @param [Hash{Symbol => Object}] options
|
84
|
+
# @return [Message]
|
67
85
|
def export(key_id = nil, opts = {})
|
68
86
|
OpenPGP::Message.parse(exec([:export, *[key_id].flatten], opts ).read)
|
69
87
|
end
|
70
88
|
|
71
|
-
##
|
72
89
|
##
|
73
90
|
# Imports a specified keyfile into the GnuPG keyring.
|
91
|
+
#
|
92
|
+
# @return [void]
|
74
93
|
def import()
|
75
94
|
# TODO
|
76
95
|
end
|
77
96
|
|
97
|
+
##
|
98
|
+
# @param [String] key_id
|
99
|
+
# @return [void]
|
78
100
|
def delete_secret_and_public_key(key_id)
|
79
101
|
opts = {:batch => true}
|
80
102
|
OpenPGP::Message.parse(exec([:delete_secret_and_public_key, key_fingerprint(key_id)], opts ).read)
|
81
103
|
end
|
82
104
|
|
105
|
+
##
|
106
|
+
# @param [String] key_id
|
107
|
+
# @param [Hash{Symbol => Object}] options
|
108
|
+
# @return [String]
|
83
109
|
def key_fingerprint(key_id, opts = {})
|
84
110
|
message = exec([:fingerprint, *[key_id].flatten], opts ).read
|
85
111
|
if message =~ /Key fingerprint = (.*)\n/
|
@@ -90,24 +116,36 @@ module OpenPGP class Engine
|
|
90
116
|
|
91
117
|
##
|
92
118
|
# Returns an array of key IDs/titles of the keys in the public keyring.
|
119
|
+
#
|
120
|
+
# @return [Array]
|
93
121
|
def list_keys()
|
94
122
|
# TODO
|
95
123
|
end
|
96
124
|
|
97
125
|
##
|
98
126
|
# Encrypts the given plaintext to the specified recipients.
|
127
|
+
#
|
128
|
+
# @param [String] plaintext
|
129
|
+
# @param [Hash{Symbol => Object}] options
|
130
|
+
# @return [String]
|
99
131
|
def encrypt(plaintext, options = {})
|
100
132
|
# TODO
|
101
133
|
end
|
102
134
|
|
103
135
|
##
|
104
136
|
# Decrypts the given ciphertext using the specified key ID.
|
137
|
+
#
|
138
|
+
# @param [String] ciphertext
|
139
|
+
# @param [Hash{Symbol => Object}] options
|
140
|
+
# @return [String]
|
105
141
|
def decrypt(ciphertext, options = {})
|
106
142
|
# TODO
|
107
143
|
end
|
108
144
|
|
109
145
|
##
|
110
146
|
# Makes an OpenPGP signature.
|
147
|
+
#
|
148
|
+
# @return [void]
|
111
149
|
def sign()
|
112
150
|
# TODO
|
113
151
|
end
|
@@ -139,6 +177,10 @@ module OpenPGP class Engine
|
|
139
177
|
##
|
140
178
|
# Executes a GnuPG command, yielding the standard input and returning
|
141
179
|
# the standard output.
|
180
|
+
#
|
181
|
+
# @param [String] command
|
182
|
+
# @param [Hash{Symbol => Object}] options
|
183
|
+
# @return [IO]
|
142
184
|
def exec(command, options = {}, &block) #:yields: stdin
|
143
185
|
exec4(command, options) do |pid, stdin, stdout, stderr|
|
144
186
|
block.call(stdin) if block_given?
|
@@ -152,6 +194,10 @@ module OpenPGP class Engine
|
|
152
194
|
##
|
153
195
|
# Executes a GnuPG command, yielding and returning the standard input,
|
154
196
|
# output and error.
|
197
|
+
#
|
198
|
+
# @param [String] command
|
199
|
+
# @param [Hash{Symbol => Object}] options
|
200
|
+
# @return [Array(IO, IO, IO)]
|
155
201
|
def exec3(command, options = {}, &block) #:yields: stdin, stdout, stderr
|
156
202
|
exec4(command, options) do |pid, stdin, stdout, stderr|
|
157
203
|
block.call(stdin, stdout, stderr) if block_given?
|
@@ -165,6 +211,10 @@ module OpenPGP class Engine
|
|
165
211
|
##
|
166
212
|
# Executes a GnuPG command, yielding the process identifier as well as
|
167
213
|
# the standard input, output and error.
|
214
|
+
#
|
215
|
+
# @param [String] command
|
216
|
+
# @param [Hash{Symbol => Object}] options
|
217
|
+
# @return [void]
|
168
218
|
def exec4(command, options = {}, &block) #:yields: pid, stdin, stdout, stderr
|
169
219
|
require 'rubygems'
|
170
220
|
require 'open4'
|
@@ -175,6 +225,10 @@ module OpenPGP class Engine
|
|
175
225
|
|
176
226
|
##
|
177
227
|
# Constructs the GnuPG command-line for use with +exec+.
|
228
|
+
#
|
229
|
+
# @param [String] command
|
230
|
+
# @param [Hash{Symbol => Object}] options
|
231
|
+
# @return [String]
|
178
232
|
def cmdline(command, options = {})
|
179
233
|
command = [command].flatten
|
180
234
|
cmdline = [where]
|
@@ -186,6 +240,9 @@ module OpenPGP class Engine
|
|
186
240
|
|
187
241
|
##
|
188
242
|
# Translates Ruby symbols into GnuPG option arguments.
|
243
|
+
#
|
244
|
+
# @param [String, #to_s] option
|
245
|
+
# @return [String]
|
189
246
|
def option(option)
|
190
247
|
"--" << option.to_s.gsub('_', '-')
|
191
248
|
end
|
@@ -1,15 +1,24 @@
|
|
1
1
|
module OpenPGP
|
2
2
|
class Engine
|
3
3
|
class OpenSSL < Engine
|
4
|
+
##
|
5
|
+
# @param [Boolean] reload
|
6
|
+
# @return [void]
|
7
|
+
# @raise [LoadError]
|
4
8
|
def self.load!(reload = false)
|
5
9
|
require 'openssl' unless defined?(::OpenSSL) || reload
|
6
10
|
end
|
7
11
|
|
12
|
+
##
|
13
|
+
# @return [void]
|
14
|
+
# @raise [LoadError]
|
8
15
|
def self.install!
|
9
16
|
load!
|
10
17
|
[Random, Digest].each { |mod| install_extensions! mod }
|
11
18
|
end
|
12
19
|
|
20
|
+
##
|
21
|
+
# @private
|
13
22
|
module Random #:nodoc:
|
14
23
|
def number(bits = 32, options = {})
|
15
24
|
::OpenSSL::BN.rand(bits)
|
@@ -24,6 +33,8 @@ module OpenPGP
|
|
24
33
|
end
|
25
34
|
end
|
26
35
|
|
36
|
+
##
|
37
|
+
# @private
|
27
38
|
module Digest #:nodoc:
|
28
39
|
def size
|
29
40
|
::OpenSSL::Digest.new(algorithm.to_s).digest_length
|
@@ -38,10 +49,11 @@ module OpenPGP
|
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
52
|
+
##
|
53
|
+
# @private
|
41
54
|
module Cipher #:nodoc:
|
42
55
|
# TODO
|
43
56
|
end
|
44
|
-
|
45
57
|
end
|
46
58
|
end
|
47
59
|
end
|
data/lib/openpgp/message.rb
CHANGED
@@ -8,10 +8,15 @@ module OpenPGP
|
|
8
8
|
class Message
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
+
# @return [Array<Packet>]
|
11
12
|
attr_accessor :packets
|
12
13
|
|
13
14
|
##
|
14
15
|
# Creates an encrypted OpenPGP message.
|
16
|
+
#
|
17
|
+
# @param [Object] data
|
18
|
+
# @param [Hash{Symbol => Object}] options
|
19
|
+
# @return [Message]
|
15
20
|
def self.encrypt(data, options = {}, &block)
|
16
21
|
if options[:symmetric]
|
17
22
|
key = (options[:key] || S2K::DEFAULT.new(options[:passphrase]))
|
@@ -38,6 +43,9 @@ module OpenPGP
|
|
38
43
|
end
|
39
44
|
|
40
45
|
##
|
46
|
+
# @param [Object] data
|
47
|
+
# @param [Hash{Symbol => Object}] options
|
48
|
+
# @return [Object]
|
41
49
|
def self.decrypt(data, options = {}, &block)
|
42
50
|
raise NotImplementedError # TODO
|
43
51
|
end
|
@@ -45,8 +53,10 @@ module OpenPGP
|
|
45
53
|
##
|
46
54
|
# Parses an OpenPGP message.
|
47
55
|
#
|
48
|
-
# @
|
49
|
-
# @
|
56
|
+
# @param [Buffer, #to_str] data
|
57
|
+
# @return [Message]
|
58
|
+
# @see http://tools.ietf.org/html/rfc4880#section-4.1
|
59
|
+
# @see http://tools.ietf.org/html/rfc4880#section-4.2
|
50
60
|
def self.parse(data)
|
51
61
|
data = Buffer.new(data.to_str) if data.respond_to?(:to_str)
|
52
62
|
|
@@ -61,36 +71,56 @@ module OpenPGP
|
|
61
71
|
msg
|
62
72
|
end
|
63
73
|
|
74
|
+
##
|
75
|
+
# @return [IO, #write] io
|
76
|
+
# @return [void]
|
64
77
|
def self.write(io = nil, &block)
|
65
78
|
data = self.new(&block).to_s
|
66
79
|
io.respond_to?(:write) ? io.write(data) : data
|
67
80
|
end
|
68
81
|
|
82
|
+
##
|
83
|
+
# @param [Array<Packet>] packets
|
69
84
|
def initialize(*packets, &block)
|
70
85
|
@packets = packets.flatten
|
71
86
|
block.call(self) if block_given?
|
72
87
|
end
|
73
88
|
|
89
|
+
##
|
90
|
+
# @yield [packet]
|
91
|
+
# @yieldparam [Packet] packet
|
92
|
+
# @return [Enumerator]
|
74
93
|
def each(&block) # :yields: packet
|
75
94
|
packets.each(&block)
|
76
95
|
end
|
77
96
|
|
97
|
+
##
|
98
|
+
# @return [Array<Packet>]
|
78
99
|
def to_a
|
79
100
|
packets.to_a
|
80
101
|
end
|
81
102
|
|
103
|
+
##
|
104
|
+
# @param [Packet] packet
|
105
|
+
# @return [self]
|
82
106
|
def <<(packet)
|
83
107
|
packets << packet
|
84
108
|
end
|
85
109
|
|
110
|
+
##
|
111
|
+
# @return [Boolean]
|
86
112
|
def empty?
|
87
113
|
packets.empty?
|
88
114
|
end
|
89
115
|
|
116
|
+
##
|
117
|
+
# @return [Integer]
|
90
118
|
def size
|
91
119
|
inject(0) { |sum, packet| sum + packet.size }
|
92
120
|
end
|
93
121
|
|
122
|
+
##
|
123
|
+
# @return [String]
|
94
124
|
def to_s
|
95
125
|
Buffer.write do |buffer|
|
96
126
|
packets.each do |packet|
|