klacointe-openpgp 0.0.1.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/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
|