openpgp 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|