klacointe-openpgp 0.0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,507 @@
1
+ module OpenPGP
2
+ ##
3
+ # OpenPGP packet.
4
+ #
5
+ # @see http://tools.ietf.org/html/rfc4880#section-4.1
6
+ # @see http://tools.ietf.org/html/rfc4880#section-4.3
7
+ class Packet
8
+ attr_accessor :tag, :size, :data
9
+
10
+ ##
11
+ # Returns the implementation class for a packet tag.
12
+ def self.for(tag)
13
+ @@tags[tag.to_i] || self
14
+ end
15
+
16
+ ##
17
+ # Returns the packet tag for this class.
18
+ def self.tag
19
+ @@tags.index(self)
20
+ end
21
+
22
+ ##
23
+ # Parses an OpenPGP packet.
24
+ #
25
+ # @see http://tools.ietf.org/html/rfc4880#section-4.2
26
+ def self.parse(data)
27
+ data = Buffer.new(data.to_str) if data.respond_to?(:to_str)
28
+
29
+ unless data.eof?
30
+ new = ((tag = data.getc) & 64).nonzero? # bit 6 indicates new packet format if set
31
+ data.ungetc(tag)
32
+ send(new ? :parse_new_format : :parse_old_format, data)
33
+ end
34
+ end
35
+
36
+ ##
37
+ # Parses a new-format (RFC 4880) OpenPGP packet.
38
+ #
39
+ # @see http://tools.ietf.org/html/rfc4880#section-4.2.2
40
+ def self.parse_new_format(data)
41
+ tag = data.getc & 63
42
+ len = data.getc
43
+
44
+ case len
45
+ when 0..191 # 4.2.2.1. One-Octet Lengths
46
+ data_length = len
47
+ when 192..223 # 4.2.2.2. Two-Octet Lengths
48
+ data_length = ((len - 192) << 8) + data.getc + 192
49
+ when 224..254 # 4.2.2.4. Partial Body Lengths
50
+ data_length = 1 << (len & 0x1f)
51
+ when 255 # 4.2.2.3. Five-Octet Lengths
52
+ data_length = (data.getc << 24) | (data.getc << 16) | (data.getc << 8) | data.getc
53
+ end
54
+
55
+ Packet.for(tag).parse_body(Buffer.new(data.read(data_length)), :tag => tag)
56
+ end
57
+
58
+ ##
59
+ # Parses an old-format (PGP 2.6.x) OpenPGP packet.
60
+ #
61
+ # @see http://tools.ietf.org/html/rfc4880#section-4.2.1
62
+ def self.parse_old_format(data)
63
+ len = (tag = data.getc) & 3
64
+ tag = (tag >> 2) & 15
65
+
66
+ case len
67
+ when 0 # The packet has a one-octet length. The header is 2 octets long.
68
+ data_length = data.getc
69
+ when 1 # The packet has a two-octet length. The header is 3 octets long.
70
+ data_length = data.read(2).unpack('n').first
71
+ when 2 # The packet has a four-octet length. The header is 5 octets long.
72
+ data_length = data.read(4).unpack('N').first
73
+ when 3 # The packet is of indeterminate length. The header is 1 octet long.
74
+ data_length = false # read to EOF
75
+ else
76
+ raise "Invalid OpenPGP packet length-type: expected 0..3 but got #{len}"
77
+ end
78
+
79
+ Packet.for(tag).parse_body(Buffer.new(data_length ? data.read(data_length) : data.read), :tag => tag)
80
+ end
81
+
82
+ ##
83
+ def self.parse_body(body, options = {})
84
+ self.new(options)
85
+ end
86
+
87
+ def initialize(options = {}, &block)
88
+ options.each { |k, v| send("#{k}=", v) }
89
+ block.call(self) if block_given?
90
+ end
91
+
92
+ #def to_s() body end
93
+
94
+ def size() body.size end
95
+
96
+ def body
97
+ respond_to?(:write_body) ? Buffer.write { |buffer| write_body(buffer) } : ""
98
+ end
99
+
100
+ ##
101
+ # OpenPGP Public-Key Encrypted Session Key packet (tag 1).
102
+ #
103
+ # @see http://tools.ietf.org/html/rfc4880#section-5.1
104
+ # @see http://tools.ietf.org/html/rfc4880#section-13.1
105
+ class AsymmetricSessionKey < Packet
106
+ attr_accessor :version, :key_id, :algorithm
107
+
108
+ def self.parse_body(body, options = {})
109
+ case version = body.read_byte
110
+ when 3
111
+ self.new(:version => version, :key_id => body.read_number(8, 16), :algorithm => body.read_byte)
112
+ # TODO: read the encrypted session key.
113
+ else
114
+ raise "Invalid OpenPGP public-key ESK packet version: #{version}"
115
+ end
116
+ end
117
+ end
118
+
119
+ ##
120
+ # OpenPGP Signature packet (tag 2).
121
+ #
122
+ # @see http://tools.ietf.org/html/rfc4880#section-5.2
123
+ class Signature < Packet
124
+ attr_accessor :version, :type
125
+ attr_accessor :key_algorithm, :hash_algorithm
126
+ attr_accessor :key_id
127
+ attr_accessor :fields
128
+
129
+ def self.parse_body(body, options = {})
130
+ case version = body.read_byte
131
+ when 3 then self.new(:version => 3).send(:read_v3_signature, body)
132
+ when 4 then self.new(:version => 4).send(:read_v4_signature, body)
133
+ else raise "Invalid OpenPGP signature packet version: #{version}"
134
+ end
135
+ end
136
+
137
+ protected
138
+
139
+ ##
140
+ # @see http://tools.ietf.org/html/rfc4880#section-5.2.2
141
+ def read_v3_signature(body)
142
+ raise "Invalid OpenPGP signature packet V3 header" if body.read_byte != 5
143
+ @type, @timestamp, @key_id = body.read_byte, body.read_number(4), body.read_number(8, 16)
144
+ @key_algorithm, @hash_algorithm = body.read_byte, body.read_byte
145
+ body.read_bytes(2)
146
+ read_signature(body)
147
+ self
148
+ end
149
+
150
+ ##
151
+ # @see http://tools.ietf.org/html/rfc4880#section-5.2.3
152
+ def read_v4_signature(body)
153
+ @type = body.read_byte
154
+ @key_algorithm, @hash_algorithm = body.read_byte, body.read_byte
155
+ body.read_bytes(hashed_count = body.read_number(2))
156
+ body.read_bytes(unhashed_count = body.read_number(2))
157
+ body.read_bytes(2)
158
+ read_signature(body)
159
+ self
160
+ end
161
+
162
+ ##
163
+ # @see http://tools.ietf.org/html/rfc4880#section-5.2.2
164
+ def read_signature(body)
165
+ case key_algorithm
166
+ when Algorithm::Asymmetric::RSA
167
+ @fields = [body.read_mpi]
168
+ when Algorithm::Asymmetric::DSA
169
+ @fields = [body.read_mpi, body.read_mpi]
170
+ else
171
+ raise "Unknown OpenPGP signature packet public-key algorithm: #{key_algorithm}"
172
+ end
173
+ end
174
+ end
175
+
176
+ ##
177
+ # OpenPGP Symmetric-Key Encrypted Session Key packet (tag 3).
178
+ #
179
+ # @see http://tools.ietf.org/html/rfc4880#section-5.3
180
+ class SymmetricSessionKey < Packet
181
+ attr_accessor :version, :algorithm, :s2k
182
+
183
+ def self.parse_body(body, options = {})
184
+ case version = body.read_byte
185
+ when 4
186
+ self.new({:version => version, :algorithm => body.read_byte, :s2k => body.read_s2k}.merge(options))
187
+ else
188
+ raise "Invalid OpenPGP symmetric-key ESK packet version: #{version}"
189
+ end
190
+ end
191
+
192
+ def initialize(options = {}, &block)
193
+ defaults = {
194
+ :version => 4,
195
+ :algorithm => Cipher::DEFAULT.to_i,
196
+ :s2k => S2K::DEFAULT.new,
197
+ }
198
+ super(defaults.merge(options), &block)
199
+ end
200
+
201
+ def write_body(buffer)
202
+ buffer.write_byte(version)
203
+ buffer.write_byte(algorithm.to_i)
204
+ buffer.write_s2k(s2k)
205
+ end
206
+ end
207
+
208
+ ##
209
+ # OpenPGP One-Pass Signature packet (tag 4).
210
+ #
211
+ # @see http://tools.ietf.org/html/rfc4880#section-5.4
212
+ class OnePassSignature < Packet
213
+ # TODO
214
+ end
215
+
216
+ ##
217
+ # OpenPGP Public-Key packet (tag 6).
218
+ #
219
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.1.1
220
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.2
221
+ # @see http://tools.ietf.org/html/rfc4880#section-11.1
222
+ # @see http://tools.ietf.org/html/rfc4880#section-12
223
+ class PublicKey < Packet
224
+ attr_accessor :size
225
+ attr_accessor :version, :timestamp, :algorithm
226
+ attr_accessor :key, :key_fields, :key_id, :fingerprint
227
+
228
+ #def parse(data) # FIXME
229
+ def self.parse_body(body, options = {})
230
+ case version = body.read_byte
231
+ when 2, 3
232
+ # TODO
233
+ when 4
234
+ packet = self.new(:version => version, :timestamp => body.read_timestamp, :algorithm => body.read_byte, :key => {}, :size => body.size)
235
+ packet.read_key_material(body)
236
+ packet
237
+ else
238
+ raise "Invalid OpenPGP public-key packet version: #{version}"
239
+ end
240
+ end
241
+
242
+ ##
243
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.2
244
+ def read_key_material(body)
245
+ @key_fields = case algorithm
246
+ when Algorithm::Asymmetric::RSA then [:n, :e]
247
+ when Algorithm::Asymmetric::ELG_E then [:p, :g, :y]
248
+ when Algorithm::Asymmetric::DSA then [:p, :q, :g, :y]
249
+ else raise "Unknown OpenPGP key algorithm: #{algorithm}"
250
+ end
251
+ @key_fields.each { |field| key[field] = body.read_mpi }
252
+ @key_id = fingerprint[-8..-1]
253
+ end
254
+
255
+ ##
256
+ # @see http://tools.ietf.org/html/rfc4880#section-12.2
257
+ # @see http://tools.ietf.org/html/rfc4880#section-3.3
258
+ def fingerprint
259
+ @fingerprint ||= case version
260
+ when 2, 3
261
+ Digest::MD5.hexdigest([key[:n], key[:e]].join).upcase
262
+ when 4
263
+ material = [0x99.chr, [size].pack('n'), version.chr, [timestamp].pack('N'), algorithm.chr]
264
+ key_fields.each do |key_field|
265
+ material << [OpenPGP.bitlength(key[key_field])].pack('n')
266
+ material << key[key_field]
267
+ end
268
+ Digest::SHA1.hexdigest(material.join).upcase
269
+ end
270
+ end
271
+ end
272
+
273
+ ##
274
+ # OpenPGP Public-Subkey packet (tag 14).
275
+ #
276
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.1.2
277
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.2
278
+ # @see http://tools.ietf.org/html/rfc4880#section-11.1
279
+ # @see http://tools.ietf.org/html/rfc4880#section-12
280
+ class PublicSubkey < PublicKey
281
+ # TODO
282
+ end
283
+
284
+ ##
285
+ # OpenPGP Secret-Key packet (tag 5).
286
+ #
287
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.1.3
288
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.3
289
+ # @see http://tools.ietf.org/html/rfc4880#section-11.2
290
+ # @see http://tools.ietf.org/html/rfc4880#section-12
291
+ class SecretKey < PublicKey
292
+ # TODO
293
+ end
294
+
295
+ ##
296
+ # OpenPGP Secret-Subkey packet (tag 7).
297
+ #
298
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.1.4
299
+ # @see http://tools.ietf.org/html/rfc4880#section-5.5.3
300
+ # @see http://tools.ietf.org/html/rfc4880#section-11.2
301
+ # @see http://tools.ietf.org/html/rfc4880#section-12
302
+ class SecretSubkey < SecretKey
303
+ # TODO
304
+ end
305
+
306
+ ##
307
+ # OpenPGP Compressed Data packet (tag 8).
308
+ #
309
+ # @see http://tools.ietf.org/html/rfc4880#section-5.6
310
+ class CompressedData < Packet
311
+ # TODO
312
+ end
313
+
314
+ ##
315
+ # OpenPGP Symmetrically Encrypted Data packet (tag 9).
316
+ #
317
+ # @see http://tools.ietf.org/html/rfc4880#section-5.7
318
+ class EncryptedData < Packet
319
+ attr_accessor :data
320
+
321
+ def self.parse_body(body, options = {})
322
+ self.new({:data => body.read}.merge(options))
323
+ end
324
+
325
+ def initialize(options = {}, &block)
326
+ super(options, &block)
327
+ end
328
+
329
+ def write_body(buffer)
330
+ buffer.write(data)
331
+ end
332
+ end
333
+
334
+ ##
335
+ # OpenPGP Marker packet (tag 10).
336
+ #
337
+ # @see http://tools.ietf.org/html/rfc4880#section-5.8
338
+ class Marker < Packet
339
+ # TODO
340
+ end
341
+
342
+ ##
343
+ # OpenPGP Literal Data packet (tag 11).
344
+ #
345
+ # @see http://tools.ietf.org/html/rfc4880#section-5.9
346
+ class LiteralData < Packet
347
+ attr_accessor :format, :filename, :timestamp, :data
348
+
349
+ def self.parse_body(body, options = {})
350
+ defaults = {
351
+ :format => body.read_byte.chr.to_sym,
352
+ :filename => body.read_string,
353
+ :timestamp => body.read_timestamp,
354
+ :data => body.read,
355
+ }
356
+ self.new(defaults.merge(options))
357
+ end
358
+
359
+ def initialize(options = {}, &block)
360
+ defaults = {
361
+ :format => :b,
362
+ :filename => "",
363
+ :timestamp => 0,
364
+ :data => "",
365
+ }
366
+ super(defaults.merge(options), &block)
367
+ end
368
+
369
+ def write_body(buffer)
370
+ buffer.write_byte(format)
371
+ buffer.write_string(filename)
372
+ buffer.write_timestamp(timestamp)
373
+ buffer.write(data.to_s)
374
+ end
375
+
376
+ EYES_ONLY = '_CONSOLE'
377
+
378
+ def eyes_only!() filename = EYES_ONLY end
379
+ def eyes_only?() filename == EYES_ONLY end
380
+ end
381
+
382
+ ##
383
+ # OpenPGP Trust packet (tag 12).
384
+ #
385
+ # @see http://tools.ietf.org/html/rfc4880#section-5.10
386
+ class Trust < Packet
387
+ attr_accessor :data
388
+
389
+ def self.parse_body(body, options = {})
390
+ self.new({:data => body.read}.merge(options))
391
+ end
392
+
393
+ def write_body(buffer)
394
+ buffer.write(data)
395
+ end
396
+ end
397
+
398
+ ##
399
+ # OpenPGP User ID packet (tag 13).
400
+ #
401
+ # @see http://tools.ietf.org/html/rfc4880#section-5.11
402
+ # @see http://tools.ietf.org/html/rfc2822
403
+ class UserID < Packet
404
+ attr_accessor :name, :comment, :email
405
+
406
+ def self.parse_body(body, options = {})
407
+ case body.read
408
+ # User IDs of the form: "name (comment) <email>"
409
+ when /^([^\(]+)\(([^\)]+)\)\s+<([^>]+)>$/
410
+ self.new(:name => $1.strip, :comment => $2.strip, :email => $3.strip)
411
+ # User IDs of the form: "name <email>"
412
+ when /^([^<]+)\s+<([^>]+)>$/
413
+ self.new(:name => $1.strip, :comment => nil, :email => $2.strip)
414
+ # User IDs of the form: "name"
415
+ when /^([^<]+)$/
416
+ self.new(:name => $1.strip, :comment => nil, :email => nil)
417
+ # User IDs of the form: "<email>"
418
+ when /^<([^>]+)>$/
419
+ self.new(:name => nil, :comment => nil, :email => $1.strip)
420
+ else
421
+ self.new(:name => nil, :comment => nil, :email => nil)
422
+ end
423
+ end
424
+
425
+ def write_body(buffer)
426
+ buffer.write(to_s)
427
+ end
428
+
429
+ def to_s
430
+ text = []
431
+ text << name if name
432
+ text << "(#{comment})" if comment
433
+ text << "<#{email}>" if email
434
+ text.join(' ')
435
+ end
436
+ end
437
+
438
+ ##
439
+ # OpenPGP User Attribute packet (tag 17).
440
+ #
441
+ # @see http://tools.ietf.org/html/rfc4880#section-5.12
442
+ # @see http://tools.ietf.org/html/rfc4880#section-11.1
443
+ class UserAttribute < Packet
444
+ attr_accessor :packets
445
+
446
+ # TODO
447
+ end
448
+
449
+ ##
450
+ # OpenPGP Sym. Encrypted Integrity Protected Data packet (tag 18).
451
+ #
452
+ # @see http://tools.ietf.org/html/rfc4880#section-5.13
453
+ class IntegrityProtectedData < Packet
454
+ attr_accessor :version
455
+
456
+ def self.parse_body(body, options = {})
457
+ case version = body.read_byte
458
+ when 1
459
+ self.new(:version => version) # TODO: read the encrypted data.
460
+ else
461
+ raise "Invalid OpenPGP integrity-protected data packet version: #{version}"
462
+ end
463
+ end
464
+ end
465
+
466
+ ##
467
+ # OpenPGP Modification Detection Code packet (tag 19).
468
+ #
469
+ # @see http://tools.ietf.org/html/rfc4880#section-5.14
470
+ class ModificationDetectionCode < Packet
471
+ # TODO
472
+ end
473
+
474
+ ##
475
+ # OpenPGP Private or Experimental packet (tags 60..63).
476
+ #
477
+ # @see http://tools.ietf.org/html/rfc4880#section-4.3
478
+ class Experimental < Packet; end
479
+
480
+ protected
481
+ ##
482
+ # @see http://tools.ietf.org/html/rfc4880#section-4.3
483
+ @@tags = {
484
+ 1 => AsymmetricSessionKey, # Public-Key Encrypted Session Key
485
+ 2 => Signature, # Signature Packet
486
+ 3 => SymmetricSessionKey, # Symmetric-Key Encrypted Session Key Packet
487
+ 4 => OnePassSignature, # One-Pass Signature Packet
488
+ 5 => SecretKey, # Secret-Key Packet
489
+ 6 => PublicKey, # Public-Key Packet
490
+ 7 => SecretSubkey, # Secret-Subkey Packet
491
+ 8 => CompressedData, # Compressed Data Packet
492
+ 9 => EncryptedData, # Symmetrically Encrypted Data Packet
493
+ 10 => Marker, # Marker Packet
494
+ 11 => LiteralData, # Literal Data Packet
495
+ 12 => Trust, # Trust Packet
496
+ 13 => UserID, # User ID Packet
497
+ 14 => PublicSubkey, # Public-Subkey Packet
498
+ 17 => UserAttribute, # User Attribute Packet
499
+ 18 => IntegrityProtectedData, # Sym. Encrypted and Integrity Protected Data Packet
500
+ 19 => ModificationDetectionCode, # Modification Detection Code Packet
501
+ 60 => Experimental, # Private or Experimental Values
502
+ 61 => Experimental, # Private or Experimental Values
503
+ 62 => Experimental, # Private or Experimental Values
504
+ 63 => Experimental, # Private or Experimental Values
505
+ }
506
+ end
507
+ end
@@ -0,0 +1,31 @@
1
+ module OpenPGP
2
+ module Random
3
+ ##
4
+ # Generates a random number.
5
+ def self.number(bits = 32, options = {})
6
+ octets = bytes((bits / 8.0).ceil).unpack('C*')
7
+ number = octets.inject { |number, octet| number = (number << 8) | octet }
8
+ number & ((1 << bits) - 1)
9
+ end
10
+
11
+ ##
12
+ # Generates a pseudo-random prime number of the specified bit length.
13
+ #
14
+ # @see http://openssl.org/docs/crypto/BN_generate_prime.html
15
+ # @see http://openssl.org/docs/apps/genrsa.html
16
+ def self.prime(bits, options = {})
17
+ raise NotImplementedError # TODO
18
+ end
19
+
20
+ ##
21
+ # Generates a random byte.
22
+ def self.byte() bytes(1) end
23
+
24
+ ##
25
+ # Generates a string of random bytes.
26
+ def self.bytes(count, &block)
27
+ octets = File.open('/dev/urandom', 'r') {|f| f.read(count) } # FIXME
28
+ block_given? ? octets.each_byte(&block) : octets
29
+ end
30
+ end
31
+ end