klacointe-openpgp 0.0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Arto Bendiken <http://ar.to/>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
+ IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,104 @@
1
+ = OpenPGP.rb
2
+
3
+ This is a pure-Ruby implementation of the OpenPGP Message Format (RFC 4880).
4
+
5
+ * http://openpgp.rubyforge.org
6
+ * http://github.com/bendiken/openpgp
7
+
8
+
9
+ === About OpenPGP
10
+
11
+ OpenPGP is the most widely-used e-mail encryption standard in the world. It
12
+ is defined by the OpenPGP Working Group of the Internet Engineering Task
13
+ Force (IETF) Proposed Standard RFC 4880. The OpenPGP standard was originally
14
+ derived from PGP (Pretty Good Privacy), first created by Phil Zimmermann in
15
+ 1991.
16
+
17
+ * http://tools.ietf.org/html/rfc4880
18
+ * http://www.openpgp.org
19
+
20
+
21
+ == Features
22
+
23
+ * Encodes and decodes ASCII-armored OpenPGP messages.
24
+ * Parses OpenPGP messages into their constituent packets.
25
+ * Supports both old-format (PGP 2.6.x) and new-format (RFC 4880) packets.
26
+ * Includes a GnuPG wrapper for features that are not natively supported.
27
+
28
+
29
+ == Examples
30
+
31
+ require 'openpgp'
32
+
33
+
34
+ === Decoding an ASCII-armored message
35
+
36
+ require 'open-uri'
37
+ text = open('http://ar.to/pgp.txt').read
38
+
39
+ msg = OpenPGP::Message.parse(OpenPGP.dearmor(text))
40
+
41
+
42
+ === Generating a new keypair
43
+
44
+ gpg = OpenPGP::Engine::GnuPG.new(:homedir => '~/.gnupg')
45
+ key_id = gpg.gen_key({
46
+ :key_type => 'DSA',
47
+ :key_length => 1024,
48
+ :subkey_type => 'ELG-E',
49
+ :subkey_length => 1024,
50
+ :name => 'J. Random Hacker',
51
+ :comment => nil,
52
+ :email => 'jhacker@example.org',
53
+ :passphrase => 'secret passphrase',
54
+ })
55
+
56
+
57
+ == Documentation
58
+
59
+ * http://openpgp.rubyforge.org
60
+
61
+
62
+ == Download
63
+
64
+ To get a local working copy of the development repository, do:
65
+
66
+ % git clone git://github.com/bendiken/openpgp.git
67
+
68
+ Alternatively, you can download the latest development version as a tarball
69
+ as follows:
70
+
71
+ % wget http://github.com/bendiken/openpgp/tarball/master
72
+
73
+
74
+ == Installation
75
+
76
+ The recommended installation method is via RubyGems. To install the latest
77
+ official release from RubyForge, do:
78
+
79
+ % [sudo] gem install openpgp
80
+
81
+ To use the very latest bleeding-edge development version, install the gem
82
+ directly from GitHub as follows:
83
+
84
+ % [sudo] gem install bendiken-openpgp -s http://gems.github.com
85
+
86
+
87
+ == Resources
88
+
89
+ * http://openpgp.rubyforge.org
90
+ * http://github.com/bendiken/openpgp
91
+ * http://rubyforge.org/projects/openpgp
92
+ * http://raa.ruby-lang.org/project/openpgp
93
+ * http://www.ohloh.net/p/openpgp
94
+
95
+
96
+ == Authors
97
+
98
+ * Arto Bendiken (mailto:arto.bendiken@gmail.com) - http://ar.to
99
+
100
+
101
+ == License
102
+
103
+ All source code is available under the terms of the MIT license. For more
104
+ information, see the accompanying LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'lib')))
3
+ require 'rubygems'
4
+ require 'rakefile' # http://github.com/bendiken/rakefile
5
+ require 'openpgp'
6
+
7
+ desc "Generate YARD documentation (with title)"
8
+ task :yardocs => :yardoc do
9
+ # FIXME: fork YARD and patch it to allow the title to be configured
10
+ sh "sed -i 's/YARD Documentation/OpenPGP.rb Documentation/' doc/yard/index.html"
11
+
12
+ # TODO: investigate why YARD doesn't auto-link URLs like RDoc does
13
+ html = File.read(file = 'doc/yard/readme.html')
14
+ html.gsub!(/>(http:\/\/)([\w\d\.\/\-]+)/, '><a href="\1\2" target="_blank">\2</a>')
15
+ html.gsub!(/(http:\/\/ar\.to\/[\w\d\.\/]+)/, '<a href="\1">\1</a>')
16
+ html.gsub!(/(http:\/\/ar\.to)([^\/]+)/, '<a href="\1" target="_top">ar.to</a>\2')
17
+ html.gsub!(/(mailto:[^\)]+)/, '<a href="\1">\1</a>')
18
+ File.open(file, 'wb') { |f| f.puts html }
19
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1.2
data/bin/openpgp ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
3
+ require 'openpgp'
@@ -0,0 +1,57 @@
1
+ module OpenPGP
2
+ module Algorithm
3
+ ##
4
+ # OpenPGP public-key algorithms.
5
+ #
6
+ # @see http://tools.ietf.org/html/rfc4880#section-9.1
7
+ module Asymmetric
8
+ RSA = 1
9
+ RSA_E = 2
10
+ RSA_S = 3
11
+ ELG_E = 16
12
+ DSA = 17
13
+ end
14
+
15
+ ##
16
+ # OpenPGP symmetric-key algorithms.
17
+ #
18
+ # @see http://tools.ietf.org/html/rfc4880#section-9.2
19
+ module Symmetric
20
+ NONE = 0
21
+ IDEA = 1
22
+ TRIPLEDES = 2
23
+ CAST5 = 3
24
+ BLOWFISH = 4
25
+ AES = 7
26
+ AES128 = 7
27
+ AES192 = 8
28
+ AES256 = 9
29
+ TWOFISH = 10
30
+ end
31
+
32
+ ##
33
+ # OpenPGP compression algorithms.
34
+ #
35
+ # @see http://tools.ietf.org/html/rfc4880#section-9.3
36
+ module Compression
37
+ NONE = 0
38
+ ZIP = 1
39
+ ZLIB = 2
40
+ BZIP2 = 3
41
+ end
42
+
43
+ ##
44
+ # OpenPGP hash algorithms.
45
+ #
46
+ # @see http://tools.ietf.org/html/rfc4880#section-9.4
47
+ module Digest
48
+ MD5 = 1
49
+ SHA1 = 2
50
+ RIPEMD160 = 3
51
+ SHA256 = 8
52
+ SHA384 = 9
53
+ SHA512 = 10
54
+ SHA224 = 11
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,120 @@
1
+ module OpenPGP
2
+ ##
3
+ # OpenPGP ASCII Armor utilities.
4
+ #
5
+ # @see http://tools.ietf.org/html/rfc4880#section-6.2
6
+ module Armor
7
+ ##
8
+ # @see http://tools.ietf.org/html/rfc4880#section-6.2
9
+ module Markers
10
+ MESSAGE = 'MESSAGE'
11
+ PUBLIC_KEY_BLOCK = 'PUBLIC KEY BLOCK'
12
+ PRIVATE_KEY_BLOCK = 'PRIVATE KEY BLOCK'
13
+ SIGNATURE = 'SIGNATURE'
14
+ ARMORED_FILE = 'ARMORED FILE' # a GnuPG extension
15
+ end
16
+
17
+ def self.marker(marker)
18
+ marker = Markers.const_get(marker.to_s.upcase.to_sym) if marker.is_a?(Symbol)
19
+ marker.to_s.upcase
20
+ end
21
+
22
+ ##
23
+ # @see http://tools.ietf.org/html/rfc4880#section-6.2
24
+ def self.header(marker)
25
+ "-----BEGIN PGP #{marker(marker)}-----"
26
+ end
27
+
28
+ ##
29
+ # @see http://tools.ietf.org/html/rfc4880#section-6.2
30
+ def self.footer(marker)
31
+ "-----END PGP #{marker(marker)}-----"
32
+ end
33
+
34
+ ##
35
+ # @see http://tools.ietf.org/html/rfc4880#section-6
36
+ # @see http://tools.ietf.org/html/rfc4880#section-6.2
37
+ # @see http://tools.ietf.org/html/rfc2045
38
+ def self.encode(data, marker = :message, options = {})
39
+ Buffer.write do |text|
40
+ text << self.header(marker) << "\n"
41
+ text << "Version: #{options[:version]}\n" if options[:version]
42
+ text << "Comment: #{options[:comment]}\n" if options[:comment]
43
+ if options[:headers]
44
+ options[:headers].each { |key, value| text << "#{key}: #{value}\n" }
45
+ end
46
+ text << "\n" << encode64(data, options[:line_length])
47
+ text << "=" << encode64([OpenPGP.crc24(data)].pack('N')[1, 3])
48
+ text << self.footer(marker) << "\n"
49
+ end
50
+ end
51
+
52
+ ##
53
+ # @see http://tools.ietf.org/html/rfc4880#section-6
54
+ # @see http://tools.ietf.org/html/rfc2045
55
+ def self.decode(text, marker = nil, options = {})
56
+ data, crc, state = Buffer.new, nil, :begin
57
+
58
+ text.each_line do |line|
59
+ line.chomp!
60
+ case state
61
+ when :begin
62
+ case line
63
+ when /^-----BEGIN PGP ([^-]+)-----$/
64
+ state = :head if marker.nil? || marker(marker) == $1
65
+ end
66
+ when :head
67
+ state = :body if line =~ /^\s*$/
68
+ when :body
69
+ case line
70
+ when /^=(....)$/
71
+ crc = ("\0" << decode64($1)).unpack('N').first
72
+ state = :end
73
+ when /^-----END PGP ([^-]+)-----$/
74
+ state = :end
75
+ else
76
+ data << decode64(line)
77
+ end
78
+ when :end
79
+ break
80
+ end
81
+ end
82
+
83
+ data = data.string
84
+ if options[:crc] && crc != (crc_data = OpenPGP.crc24(data))
85
+ raise CRCError.new("ASCII armor says 0x#{crc.to_s(16)}, but data has 0x#{crc_data.to_s(16)}")
86
+ end
87
+ data
88
+ end
89
+
90
+ class CRCError < IOError; end
91
+
92
+ protected
93
+
94
+ ##
95
+ # Returns the Base64-encoded version of +input+, with a configurable
96
+ # output line length.
97
+ def self.encode64(input, line_length = nil)
98
+ if line_length.nil?
99
+ [input].pack('m')
100
+ elsif line_length % 4 == 0
101
+ [input].pack("m#{(line_length / 4) * 3}")
102
+ else
103
+ output = []
104
+ [input].pack('m').delete("\n").scan(/.{1,#{line_length}}/) do
105
+ output << $&
106
+ end
107
+ output << ''
108
+ output.join("\n")
109
+ end
110
+ end
111
+
112
+ ##
113
+ # Returns the Base64-decoded version of +input+.
114
+ def self.decode64(input)
115
+ input.unpack('m').first
116
+ end
117
+ end
118
+
119
+ include Armor::Markers
120
+ end
@@ -0,0 +1,100 @@
1
+ require 'stringio'
2
+
3
+ module OpenPGP
4
+ ##
5
+ class Buffer < StringIO
6
+ def self.write(*args, &block)
7
+ buffer = self.new(*args, &block)
8
+ buffer.string
9
+ end
10
+
11
+ def initialize(*args, &block)
12
+ super
13
+ block.call(self) if block_given?
14
+ end
15
+
16
+ ##
17
+ def read_string
18
+ read_bytes(length = read_byte)
19
+ end
20
+
21
+ ##
22
+ def write_string(value)
23
+ value = value.to_s
24
+ self << [value.size].pack('C')
25
+ self << value unless value.empty?
26
+ end
27
+
28
+ ##
29
+ # @see http://tools.ietf.org/html/rfc4880#section-3.5
30
+ def read_timestamp
31
+ read_unpacked(4, 'N')
32
+ end
33
+
34
+ ##
35
+ # @see http://tools.ietf.org/html/rfc4880#section-3.5
36
+ def write_timestamp(value)
37
+ self << [value.to_i].pack('N')
38
+ end
39
+
40
+ ##
41
+ # @see http://tools.ietf.org/html/rfc4880#section-3.1
42
+ def read_number(count, base = nil)
43
+ number, shift = 0, count * 8
44
+ read_bytes(count).each_byte do |octet|
45
+ number += octet << (shift -= 8)
46
+ end
47
+ !base ? number : number.to_s(base).upcase
48
+ end
49
+
50
+ ##
51
+ # @see http://tools.ietf.org/html/rfc4880#section-3.1
52
+ def write_number
53
+ # TODO
54
+ end
55
+
56
+ ##
57
+ # @see http://tools.ietf.org/html/rfc4880#section-3.2
58
+ def read_mpi
59
+ length = read_unpacked(2, 'n') # length in bits
60
+ length = ((length + 7) / 8.0).floor # length in bytes
61
+ read_bytes(length)
62
+ end
63
+
64
+ ##
65
+ # @see http://tools.ietf.org/html/rfc4880#section-3.2
66
+ def write_mpi
67
+ # TODO
68
+ end
69
+
70
+ ##
71
+ # @see http://tools.ietf.org/html/rfc4880#section-3.7
72
+ def read_s2k() S2K.parse(self) end
73
+
74
+ def write_s2k(s2k) s2k.write(self) end
75
+
76
+ def read_unpacked(count, format)
77
+ read_bytes(count).unpack(format).first
78
+ end
79
+
80
+ def write_unpacked
81
+ # TODO
82
+ end
83
+
84
+ def read_bytes(count)
85
+ read(count)
86
+ end
87
+
88
+ def write_bytes(value)
89
+ self << value
90
+ end
91
+
92
+ def read_byte
93
+ getc
94
+ end
95
+
96
+ def write_byte(value)
97
+ self << (value.respond_to?(:chr) ? value : value.to_s[0]).chr
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class TripleDES < Cipher
5
+ IDENTIFIER = 2
6
+ ENGINE = 'DES-EDE3'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class AES < Cipher
5
+ ENGINE = 'AES-128-ECB'
6
+ end
7
+
8
+ ##
9
+ class AES128 < AES
10
+ IDENTIFIER = 7
11
+ ENGINE = 'AES-128-ECB'
12
+ end
13
+
14
+ ##
15
+ class AES192 < AES
16
+ IDENTIFIER = 8
17
+ ENGINE = 'AES-192-ECB'
18
+ end
19
+
20
+ ##
21
+ class AES256 < AES
22
+ IDENTIFIER = 9
23
+ ENGINE = 'AES-256-ECB'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class Blowfish < Cipher
5
+ IDENTIFIER = 4
6
+ ENGINE = 'BF-ECB'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class CAST5 < Cipher
5
+ IDENTIFIER = 3
6
+ ENGINE = 'CAST5-ECB'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class IDEA < Cipher
5
+ IDENTIFIER = 1
6
+ ENGINE = 'IDEA-ECB'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module OpenPGP
2
+ class Cipher
3
+ ##
4
+ class Twofish < Cipher
5
+ IDENTIFIER = 10
6
+ ENGINE = nil # N/A
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,117 @@
1
+ require 'openssl'
2
+
3
+ module OpenPGP
4
+ ##
5
+ # OpenPGP cipher algorithm.
6
+ class Cipher
7
+ autoload :IDEA, 'openpgp/cipher/idea'
8
+ autoload :TripleDES, 'openpgp/cipher/3des'
9
+ autoload :CAST5, 'openpgp/cipher/cast5'
10
+ autoload :Blowfish, 'openpgp/cipher/blowfish'
11
+ autoload :AES128, 'openpgp/cipher/aes'
12
+ autoload :AES192, 'openpgp/cipher/aes'
13
+ autoload :AES256, 'openpgp/cipher/aes'
14
+ autoload :Twofish, 'openpgp/cipher/twofish'
15
+
16
+ DEFAULT = AES128
17
+
18
+ ##
19
+ # @see http://tools.ietf.org/html/rfc4880#section-9.2
20
+ def self.for(identifier)
21
+ case identifier
22
+ when Symbol then const_get(identifier.to_s.upcase)
23
+ when String then const_get(identifier.upcase.to_sym)
24
+ when 1 then IDEA
25
+ when 2 then TripleDES
26
+ when 3 then CAST5
27
+ when 4 then Blowfish
28
+ when 7 then AES128
29
+ when 8 then AES192
30
+ when 9 then AES256
31
+ when 10 then Twofish
32
+ end
33
+ end
34
+
35
+ attr_accessor :key, :options
36
+ attr_accessor :engine
37
+
38
+ def initialize(key, options = {})
39
+ @key = case key
40
+ when S2K then key.to_key(key_size)
41
+ else S2K::Simple.new(key).to_key(key_size)
42
+ end
43
+ @options = options
44
+ end
45
+
46
+ def self.to_i() identifier end
47
+
48
+ def self.identifier
49
+ const_get(:IDENTIFIER)
50
+ end
51
+
52
+ def identifier()
53
+ self.class.identifier
54
+ end
55
+
56
+ def key_size
57
+ @key_size ||= engine.key_len
58
+ end
59
+
60
+ def block_size
61
+ @block_size ||= engine.block_size
62
+ end
63
+
64
+ def engine
65
+ @engine ||= Engine::OpenSSL.use do
66
+ OpenSSL::Cipher.new(self.class.const_get(:ENGINE))
67
+ end
68
+ end
69
+
70
+ ##
71
+ # @see http://tools.ietf.org/html/rfc4880#section-13.9
72
+ def encrypt(plaintext)
73
+ ciphertext = String.new
74
+
75
+ engine.reset
76
+ engine.encrypt
77
+
78
+ # IV
79
+ rblock = Random.bytes(block_size)
80
+ iblock = encrypt_block("\0" * block_size)
81
+ block_size.times do |i|
82
+ ciphertext << (iblock[i] ^= rblock[i]).chr
83
+ end
84
+
85
+ # Checksum
86
+ iblock = encrypt_block(iblock)
87
+ ciphertext << (iblock[0] ^ rblock[block_size - 2]).chr
88
+ ciphertext << (iblock[1] ^ rblock[block_size - 1]).chr
89
+
90
+ # Resync
91
+ iblock = ciphertext[2..-1]
92
+
93
+ # Encrypt
94
+ plaintext.size.times do |n|
95
+ if (i = n % block_size) == 0
96
+ iblock = encrypt_block(iblock)
97
+ end
98
+ ciphertext << (iblock[i] ^= plaintext[n]).chr
99
+ end
100
+
101
+ ciphertext
102
+ end
103
+
104
+ def decrypt(ciphertext)
105
+ # TODO
106
+ engine.reset
107
+ engine.decrypt
108
+ end
109
+
110
+ def encrypt_block(block)
111
+ engine.encrypt
112
+ engine.key = @key
113
+ engine.iv = (@iv ||= "\0" * engine.iv_len)
114
+ engine.update(block) << engine.final
115
+ end
116
+ end
117
+ end