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 +19 -0
- data/README +104 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/bin/openpgp +3 -0
- data/lib/openpgp/algorithm.rb +57 -0
- data/lib/openpgp/armor.rb +120 -0
- data/lib/openpgp/buffer.rb +100 -0
- data/lib/openpgp/cipher/3des.rb +9 -0
- data/lib/openpgp/cipher/aes.rb +26 -0
- data/lib/openpgp/cipher/blowfish.rb +9 -0
- data/lib/openpgp/cipher/cast5.rb +9 -0
- data/lib/openpgp/cipher/idea.rb +9 -0
- data/lib/openpgp/cipher/twofish.rb +9 -0
- data/lib/openpgp/cipher.rb +117 -0
- data/lib/openpgp/client/gnupg.rb +583 -0
- data/lib/openpgp/digest/md5.rb +7 -0
- data/lib/openpgp/digest/rmd160.rb +7 -0
- data/lib/openpgp/digest/sha1.rb +7 -0
- data/lib/openpgp/digest/sha2.rb +19 -0
- data/lib/openpgp/digest.rb +60 -0
- data/lib/openpgp/engine/gnupg.rb +194 -0
- data/lib/openpgp/engine/openssl.rb +47 -0
- data/lib/openpgp/engine.rb +46 -0
- data/lib/openpgp/message.rb +106 -0
- data/lib/openpgp/packet.rb +507 -0
- data/lib/openpgp/random.rb +31 -0
- data/lib/openpgp/s2k.rb +186 -0
- data/lib/openpgp/util.rb +65 -0
- data/lib/openpgp/version.rb +14 -0
- data/lib/openpgp.rb +17 -0
- metadata +96 -0
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,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,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,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
|