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/packet.rb
CHANGED
@@ -9,12 +9,17 @@ module OpenPGP
|
|
9
9
|
|
10
10
|
##
|
11
11
|
# Returns the implementation class for a packet tag.
|
12
|
+
#
|
13
|
+
# @param [Integer, #to_i] tag
|
14
|
+
# @return [Class]
|
12
15
|
def self.for(tag)
|
13
16
|
@@tags[tag.to_i] || self
|
14
17
|
end
|
15
18
|
|
16
19
|
##
|
17
20
|
# Returns the packet tag for this class.
|
21
|
+
#
|
22
|
+
# @return [Integer]
|
18
23
|
def self.tag
|
19
24
|
@@tags.index(self)
|
20
25
|
end
|
@@ -22,13 +27,15 @@ module OpenPGP
|
|
22
27
|
##
|
23
28
|
# Parses an OpenPGP packet.
|
24
29
|
#
|
25
|
-
# @
|
30
|
+
# @param [Buffer, #to_str] data
|
31
|
+
# @return [Packet]
|
32
|
+
# @see http://tools.ietf.org/html/rfc4880#section-4.2
|
26
33
|
def self.parse(data)
|
27
34
|
data = Buffer.new(data.to_str) if data.respond_to?(:to_str)
|
28
35
|
|
29
36
|
unless data.eof?
|
30
|
-
new = ((tag = data.
|
31
|
-
data.ungetc(tag)
|
37
|
+
new = ((tag = data.getbyte) & 64).nonzero? # bit 6 indicates new packet format if set
|
38
|
+
data.ungetbyte(tag) rescue data.ungetc(tag.ord) # FIXME in backports/1.8.7
|
32
39
|
send(new ? :parse_new_format : :parse_old_format, data)
|
33
40
|
end
|
34
41
|
end
|
@@ -36,20 +43,22 @@ module OpenPGP
|
|
36
43
|
##
|
37
44
|
# Parses a new-format (RFC 4880) OpenPGP packet.
|
38
45
|
#
|
39
|
-
# @
|
46
|
+
# @param [Buffer, #to_str] data
|
47
|
+
# @return [Packet]
|
48
|
+
# @see http://tools.ietf.org/html/rfc4880#section-4.2.2
|
40
49
|
def self.parse_new_format(data)
|
41
|
-
tag = data.
|
42
|
-
len = data.
|
50
|
+
tag = data.getbyte & 63
|
51
|
+
len = data.getbyte
|
43
52
|
|
44
53
|
case len
|
45
54
|
when 0..191 # 4.2.2.1. One-Octet Lengths
|
46
55
|
data_length = len
|
47
56
|
when 192..223 # 4.2.2.2. Two-Octet Lengths
|
48
|
-
data_length = ((len - 192) << 8) + data.
|
57
|
+
data_length = ((len - 192) << 8) + data.getbyte + 192
|
49
58
|
when 224..254 # 4.2.2.4. Partial Body Lengths
|
50
59
|
data_length = 1 << (len & 0x1f)
|
51
60
|
when 255 # 4.2.2.3. Five-Octet Lengths
|
52
|
-
data_length = (data.
|
61
|
+
data_length = (data.getbyte << 24) | (data.getbyte << 16) | (data.getbyte << 8) | data.getbyte
|
53
62
|
end
|
54
63
|
|
55
64
|
Packet.for(tag).parse_body(Buffer.new(data.read(data_length)), :tag => tag)
|
@@ -58,14 +67,16 @@ module OpenPGP
|
|
58
67
|
##
|
59
68
|
# Parses an old-format (PGP 2.6.x) OpenPGP packet.
|
60
69
|
#
|
61
|
-
# @
|
70
|
+
# @param [Buffer, #to_str] data
|
71
|
+
# @return [Packet]
|
72
|
+
# @see http://tools.ietf.org/html/rfc4880#section-4.2.1
|
62
73
|
def self.parse_old_format(data)
|
63
|
-
len = (tag = data.
|
74
|
+
len = (tag = data.getbyte) & 3
|
64
75
|
tag = (tag >> 2) & 15
|
65
76
|
|
66
77
|
case len
|
67
78
|
when 0 # The packet has a one-octet length. The header is 2 octets long.
|
68
|
-
data_length = data.
|
79
|
+
data_length = data.getbyte
|
69
80
|
when 1 # The packet has a two-octet length. The header is 3 octets long.
|
70
81
|
data_length = data.read(2).unpack('n').first
|
71
82
|
when 2 # The packet has a four-octet length. The header is 5 octets long.
|
@@ -80,10 +91,15 @@ module OpenPGP
|
|
80
91
|
end
|
81
92
|
|
82
93
|
##
|
94
|
+
# @param [Buffer] body
|
95
|
+
# @param [Hash{Symbol => Object}] options
|
96
|
+
# @return [Packet]
|
83
97
|
def self.parse_body(body, options = {})
|
84
98
|
self.new(options)
|
85
99
|
end
|
86
100
|
|
101
|
+
##
|
102
|
+
# @param [Hash{Symbol => Object}] options
|
87
103
|
def initialize(options = {}, &block)
|
88
104
|
options.each { |k, v| send("#{k}=", v) }
|
89
105
|
block.call(self) if block_given?
|
@@ -91,8 +107,12 @@ module OpenPGP
|
|
91
107
|
|
92
108
|
#def to_s() body end
|
93
109
|
|
110
|
+
##
|
111
|
+
# @return [Integer]
|
94
112
|
def size() body.size end
|
95
113
|
|
114
|
+
##
|
115
|
+
# @return [String]
|
96
116
|
def body
|
97
117
|
respond_to?(:write_body) ? Buffer.write { |buffer| write_body(buffer) } : ""
|
98
118
|
end
|
data/lib/openpgp/random.rb
CHANGED
@@ -2,6 +2,10 @@ module OpenPGP
|
|
2
2
|
module Random
|
3
3
|
##
|
4
4
|
# Generates a random number.
|
5
|
+
#
|
6
|
+
# @param [Integer] bits
|
7
|
+
# @param [Hash{Symbol => Object}] options
|
8
|
+
# @return [Integer]
|
5
9
|
def self.number(bits = 32, options = {})
|
6
10
|
octets = bytes((bits / 8.0).ceil).unpack('C*')
|
7
11
|
number = octets.inject { |number, octet| number = (number << 8) | octet }
|
@@ -11,18 +15,26 @@ module OpenPGP
|
|
11
15
|
##
|
12
16
|
# Generates a pseudo-random prime number of the specified bit length.
|
13
17
|
#
|
14
|
-
# @
|
15
|
-
# @
|
18
|
+
# @param [Integer] bits
|
19
|
+
# @param [Hash{Symbol => Object}] options
|
20
|
+
# @return [Integer]
|
21
|
+
# @see http://openssl.org/docs/crypto/BN_generate_prime.html
|
22
|
+
# @see http://openssl.org/docs/apps/genrsa.html
|
16
23
|
def self.prime(bits, options = {})
|
17
24
|
raise NotImplementedError # TODO
|
18
25
|
end
|
19
26
|
|
20
27
|
##
|
21
28
|
# Generates a random byte.
|
29
|
+
#
|
30
|
+
# @return [String]
|
22
31
|
def self.byte() bytes(1) end
|
23
32
|
|
24
33
|
##
|
25
34
|
# Generates a string of random bytes.
|
35
|
+
#
|
36
|
+
# @param [Integer] count
|
37
|
+
# @return [String]
|
26
38
|
def self.bytes(count, &block)
|
27
39
|
octets = File.open('/dev/urandom', 'r') {|f| f.read(count) } # FIXME
|
28
40
|
block_given? ? octets.each_byte(&block) : octets
|
data/lib/openpgp/s2k.rb
CHANGED
@@ -4,9 +4,15 @@ module OpenPGP
|
|
4
4
|
#
|
5
5
|
# @see http://tools.ietf.org/html/rfc4880#section-3.7
|
6
6
|
class S2K
|
7
|
+
# @return [String]
|
7
8
|
attr_accessor :passphrase
|
9
|
+
|
10
|
+
# @return [Integer]
|
8
11
|
attr_accessor :algorithm
|
9
12
|
|
13
|
+
##
|
14
|
+
# @param [Buffer] input
|
15
|
+
# @return [S2K]
|
10
16
|
def self.parse(input)
|
11
17
|
case mode = input.read_byte
|
12
18
|
when 0 then S2K::Simple.parse(input) # Simple S2K
|
@@ -17,10 +23,15 @@ module OpenPGP
|
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
26
|
+
##
|
27
|
+
# @return [Integer]
|
20
28
|
def self.identifier
|
21
29
|
const_get(:IDENTIFIER)
|
22
30
|
end
|
23
31
|
|
32
|
+
##
|
33
|
+
# @param [String, #to_s] passphrase
|
34
|
+
# @param [Hash{Symbol => Object}] options
|
24
35
|
def initialize(passphrase = nil, options = {}, &block)
|
25
36
|
@passphrase = passphrase.to_s
|
26
37
|
options.each { |k, v| instance_variable_set("@#{k}", v) }
|
@@ -28,23 +39,34 @@ module OpenPGP
|
|
28
39
|
block.call(self) if block_given?
|
29
40
|
end
|
30
41
|
|
42
|
+
##
|
43
|
+
# @param [Buffer] buffer
|
44
|
+
# @return [void]
|
31
45
|
def write(buffer)
|
32
46
|
buffer.write_byte(identifier)
|
33
47
|
buffer.write_byte(digest.to_i)
|
34
48
|
end
|
35
49
|
|
50
|
+
##
|
51
|
+
# @return [Integer]
|
36
52
|
def identifier
|
37
53
|
@identifier || self.class.identifier
|
38
54
|
end
|
39
55
|
|
56
|
+
##
|
57
|
+
# @return [Hash]
|
40
58
|
def to_hash
|
41
59
|
{:mode => identifier, :algorithm => digest.to_i}
|
42
60
|
end
|
43
61
|
|
62
|
+
##
|
63
|
+
# @return [String]
|
44
64
|
def to_s
|
45
65
|
Buffer.write { |buffer| write(buffer) }
|
46
66
|
end
|
47
67
|
|
68
|
+
##
|
69
|
+
# @return [Object]
|
48
70
|
def to_key(key_size = 16)
|
49
71
|
key = if digest.size >= key_size
|
50
72
|
digest.digest(digest_input)
|
@@ -58,6 +80,8 @@ module OpenPGP
|
|
58
80
|
key[0, key_size]
|
59
81
|
end
|
60
82
|
|
83
|
+
##
|
84
|
+
# @return [Class]
|
61
85
|
def digest
|
62
86
|
@digest ||= case algorithm
|
63
87
|
when nil then Digest::DEFAULT
|
@@ -68,10 +92,17 @@ module OpenPGP
|
|
68
92
|
end
|
69
93
|
end
|
70
94
|
|
95
|
+
##
|
96
|
+
# @param [Integer] length
|
97
|
+
# @return [String]
|
71
98
|
def digest_input_with_preload(length = 0)
|
72
99
|
("\0" * length) << digest_input
|
73
100
|
end
|
74
101
|
|
102
|
+
##
|
103
|
+
# @return [String]
|
104
|
+
# @raise [NotImplementedError] unless implemented in subclass
|
105
|
+
# @abstract
|
75
106
|
def digest_input
|
76
107
|
raise NotImplementedError
|
77
108
|
end
|
@@ -81,10 +112,15 @@ module OpenPGP
|
|
81
112
|
class Simple < S2K
|
82
113
|
IDENTIFIER = 0x00
|
83
114
|
|
115
|
+
##
|
116
|
+
# @param [Buffer] input
|
117
|
+
# @return [S2K]
|
84
118
|
def self.parse(input)
|
85
119
|
self.new(nil, :algorithm => input.read_byte)
|
86
120
|
end
|
87
121
|
|
122
|
+
##
|
123
|
+
# @return [String]
|
88
124
|
def digest_input
|
89
125
|
passphrase
|
90
126
|
end
|
@@ -95,27 +131,40 @@ module OpenPGP
|
|
95
131
|
class Salted < S2K
|
96
132
|
IDENTIFIER = 0x01
|
97
133
|
|
134
|
+
##
|
135
|
+
# @param [Buffer] input
|
136
|
+
# @return [S2K]
|
98
137
|
def self.parse(input)
|
99
138
|
self.new(nil, :algorithm => input.read_byte, :salt => input.read_bytes(8))
|
100
139
|
end
|
101
140
|
|
141
|
+
# @return [String]
|
102
142
|
attr_accessor :salt
|
103
143
|
|
144
|
+
##
|
145
|
+
# @param [String, #to_s] passphrase
|
146
|
+
# @param [Hash{Symbol => Object}] options
|
104
147
|
def initialize(passphrase = nil, options = {}, &block)
|
105
148
|
super(passphrase, options, &block)
|
106
|
-
|
107
149
|
@salt = Random.bytes(8) unless @salt
|
108
150
|
end
|
109
151
|
|
152
|
+
##
|
153
|
+
# @param [Buffer] buffer
|
154
|
+
# @return [void]
|
110
155
|
def write(buffer)
|
111
156
|
super(buffer)
|
112
157
|
buffer.write_bytes(salt)
|
113
158
|
end
|
114
159
|
|
160
|
+
##
|
161
|
+
# @return [Hash]
|
115
162
|
def to_hash
|
116
163
|
super.merge({:salt => salt})
|
117
164
|
end
|
118
165
|
|
166
|
+
##
|
167
|
+
# @return [String]
|
119
168
|
def digest_input
|
120
169
|
salt.to_s[0, 8] << passphrase
|
121
170
|
end
|
@@ -126,29 +175,42 @@ module OpenPGP
|
|
126
175
|
class Iterated < Salted
|
127
176
|
IDENTIFIER = 0x03
|
128
177
|
|
178
|
+
##
|
179
|
+
# @param [Buffer] input
|
180
|
+
# @return [S2K]
|
129
181
|
def self.parse(input)
|
130
182
|
self.new(nil, :algorithm => input.read_byte, :salt => input.read_bytes(8)) do |s2k|
|
131
183
|
s2k.count = s2k.decode_count(input.read_byte)
|
132
184
|
end
|
133
185
|
end
|
134
186
|
|
187
|
+
# @return [Integer]
|
135
188
|
attr_reader :count
|
136
189
|
|
190
|
+
##
|
191
|
+
# @param [String, #to_s] passphrase
|
192
|
+
# @param [Hash{Symbol => Object}] options
|
137
193
|
def initialize(passphrase = nil, options = {}, &block)
|
138
194
|
super(passphrase, options, &block)
|
139
|
-
|
140
195
|
@count = 65536 unless @count
|
141
196
|
end
|
142
197
|
|
198
|
+
##
|
199
|
+
# @param [Buffer] buffer
|
200
|
+
# @return [void]
|
143
201
|
def write(buffer)
|
144
202
|
super(buffer)
|
145
203
|
buffer.write_byte(encode_count(count))
|
146
204
|
end
|
147
205
|
|
206
|
+
##
|
207
|
+
# @return [Hash]
|
148
208
|
def to_hash
|
149
209
|
super.merge(:count => count)
|
150
210
|
end
|
151
211
|
|
212
|
+
##
|
213
|
+
# @return [String]
|
152
214
|
def digest_input
|
153
215
|
buffer = Buffer.write do |buffer|
|
154
216
|
iterations = count
|
@@ -163,10 +225,16 @@ module OpenPGP
|
|
163
225
|
|
164
226
|
EXPBIAS = 6
|
165
227
|
|
228
|
+
##
|
229
|
+
# @param [Integer] count
|
230
|
+
# @return [Integer]
|
166
231
|
def decode_count(count)
|
167
232
|
(16 + (count & 15)) << ((count >> 4) + EXPBIAS)
|
168
233
|
end
|
169
234
|
|
235
|
+
##
|
236
|
+
# @param [Integer] iterations
|
237
|
+
# @return [Integer]
|
170
238
|
def encode_count(iterations)
|
171
239
|
case iterations
|
172
240
|
when 0..1024 then 0
|
data/lib/openpgp/util.rb
CHANGED
@@ -1,36 +1,36 @@
|
|
1
1
|
module OpenPGP
|
2
2
|
##
|
3
|
-
# Alias for OpenPGP::Armor.encode
|
3
|
+
# Alias for {OpenPGP::Armor.encode}.
|
4
4
|
def self.enarmor(data, marker = :message, options = {})
|
5
5
|
Armor.encode(data, marker, options)
|
6
6
|
end
|
7
7
|
|
8
8
|
##
|
9
|
-
# Alias for OpenPGP::Armor.decode
|
9
|
+
# Alias for {OpenPGP::Armor.decode}.
|
10
10
|
def self.dearmor(text, marker = nil, options = {})
|
11
11
|
Armor.decode(text, marker, options)
|
12
12
|
end
|
13
13
|
|
14
14
|
##
|
15
|
-
# Alias for OpenPGP::Message.encrypt
|
15
|
+
# Alias for {OpenPGP::Message.encrypt}.
|
16
16
|
def self.encrypt(data, options = {})
|
17
17
|
(msg = Message.encrypt(data, options)) ? msg.to_s : nil
|
18
18
|
end
|
19
19
|
|
20
20
|
##
|
21
|
-
# Alias for OpenPGP::Message.decrypt
|
21
|
+
# Alias for {OpenPGP::Message.decrypt}.
|
22
22
|
def self.decrypt(data, options = {})
|
23
23
|
raise NotImplementedError # TODO
|
24
24
|
end
|
25
25
|
|
26
26
|
##
|
27
|
-
# Alias for OpenPGP::Message.sign
|
27
|
+
# Alias for {OpenPGP::Message.sign}.
|
28
28
|
def self.sign
|
29
29
|
raise NotImplementedError # TODO
|
30
30
|
end
|
31
31
|
|
32
32
|
##
|
33
|
-
# Alias for OpenPGP::Message.verify
|
33
|
+
# Alias for {OpenPGP::Message.verify}.
|
34
34
|
def self.verify
|
35
35
|
raise NotImplementedError # TODO
|
36
36
|
end
|
@@ -41,8 +41,10 @@ module OpenPGP
|
|
41
41
|
CRC24_POLY = 0x01864cfb
|
42
42
|
|
43
43
|
##
|
44
|
-
# @
|
45
|
-
# @
|
44
|
+
# @param [String] data
|
45
|
+
# @return [Integer]
|
46
|
+
# @see http://tools.ietf.org/html/rfc4880#section-6
|
47
|
+
# @see http://tools.ietf.org/html/rfc4880#section-6.1
|
46
48
|
def self.crc24(data)
|
47
49
|
crc = CRC24_INIT
|
48
50
|
data.each_byte do |octet|
|
@@ -58,8 +60,10 @@ module OpenPGP
|
|
58
60
|
##
|
59
61
|
# Returns the bit length of a multiprecision integer (MPI).
|
60
62
|
#
|
61
|
-
# @
|
63
|
+
# @param [String] data
|
64
|
+
# @return [Integer]
|
65
|
+
# @see http://tools.ietf.org/html/rfc4880#section-3.2
|
62
66
|
def self.bitlength(data)
|
63
|
-
data.empty? ? 0 : (data.size - 1) * 8 + (Math.log(data[0]) / Math.log(2)).floor + 1
|
67
|
+
data.empty? ? 0 : (data.size - 1) * 8 + (Math.log(data[0].ord) / Math.log(2)).floor + 1
|
64
68
|
end
|
65
69
|
end
|
data/lib/openpgp/version.rb
CHANGED
@@ -2,13 +2,21 @@ module OpenPGP
|
|
2
2
|
module VERSION
|
3
3
|
MAJOR = 0
|
4
4
|
MINOR = 0
|
5
|
-
TINY =
|
5
|
+
TINY = 3
|
6
6
|
EXTRA = nil
|
7
7
|
|
8
|
-
STRING = [MAJOR, MINOR, TINY].join('.')
|
9
|
-
STRING << "-#{EXTRA}" if EXTRA
|
8
|
+
STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
|
10
9
|
|
11
|
-
|
10
|
+
##
|
11
|
+
# @return [String]
|
12
|
+
def self.to_s() STRING end
|
13
|
+
|
14
|
+
##
|
15
|
+
# @return [String]
|
12
16
|
def self.to_str() STRING end
|
17
|
+
|
18
|
+
##
|
19
|
+
# @return [Array(Integer, Integer, Integer)]
|
20
|
+
def self.to_a() [MAJOR, MINOR, TINY] end
|
13
21
|
end
|
14
22
|
end
|