openpgp 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +2 -0
- data/README +55 -48
- data/UNLICENSE +24 -0
- data/VERSION +1 -1
- data/lib/openpgp.rb +16 -5
- data/lib/openpgp/algorithm.rb +1 -1
- data/lib/openpgp/armor.rb +55 -49
- data/lib/openpgp/buffer.rb +100 -0
- data/lib/openpgp/cipher.rb +117 -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/client/gnupg.rb +583 -0
- data/lib/openpgp/digest.rb +60 -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/engine.rb +46 -0
- data/lib/openpgp/{gnupg.rb → engine/gnupg.rb} +31 -7
- data/lib/openpgp/engine/openssl.rb +47 -0
- data/lib/openpgp/message.rb +55 -4
- data/lib/openpgp/packet.rb +240 -27
- 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 +5 -2
- metadata +40 -9
- data/LICENSE +0 -19
@@ -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
|
data/lib/openpgp/s2k.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
module OpenPGP
|
2
|
+
##
|
3
|
+
# OpenPGP string-to-key (S2K) specifiers.
|
4
|
+
#
|
5
|
+
# @see http://tools.ietf.org/html/rfc4880#section-3.7
|
6
|
+
class S2K
|
7
|
+
attr_accessor :passphrase
|
8
|
+
attr_accessor :algorithm
|
9
|
+
|
10
|
+
def self.parse(input)
|
11
|
+
case mode = input.read_byte
|
12
|
+
when 0 then S2K::Simple.parse(input) # Simple S2K
|
13
|
+
when 1 then S2K::Salted.parse(input) # Salted S2K
|
14
|
+
when 3 then S2K::Iterated.parse(input) # Iterated and Salted S2K
|
15
|
+
when 100..110 then S2K.new(:data => input.read) # Private/Experimental S2K
|
16
|
+
else # TODO
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.identifier
|
21
|
+
const_get(:IDENTIFIER)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(passphrase = nil, options = {}, &block)
|
25
|
+
@passphrase = passphrase.to_s
|
26
|
+
options.each { |k, v| instance_variable_set("@#{k}", v) }
|
27
|
+
|
28
|
+
block.call(self) if block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(buffer)
|
32
|
+
buffer.write_byte(identifier)
|
33
|
+
buffer.write_byte(digest.to_i)
|
34
|
+
end
|
35
|
+
|
36
|
+
def identifier
|
37
|
+
@identifier || self.class.identifier
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_hash
|
41
|
+
{:mode => identifier, :algorithm => digest.to_i}
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
Buffer.write { |buffer| write(buffer) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_key(key_size = 16)
|
49
|
+
key = if digest.size >= key_size
|
50
|
+
digest.digest(digest_input)
|
51
|
+
else
|
52
|
+
Buffer.write do |buffer|
|
53
|
+
(key_size / digest.size.to_f).ceil.times do |i|
|
54
|
+
buffer << digest.digest(digest_input_with_preload(i))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
key[0, key_size]
|
59
|
+
end
|
60
|
+
|
61
|
+
def digest
|
62
|
+
@digest ||= case algorithm
|
63
|
+
when nil then Digest::DEFAULT
|
64
|
+
when Digest then algorithm
|
65
|
+
when Symbol then Digest.for(algorithm)
|
66
|
+
when String then Digest.for(algorithm)
|
67
|
+
else Digest.for(algorithm.to_i)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def digest_input_with_preload(length = 0)
|
72
|
+
("\0" * length) << digest_input
|
73
|
+
end
|
74
|
+
|
75
|
+
def digest_input
|
76
|
+
raise NotImplementedError
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# @see http://tools.ietf.org/html/rfc4880#section-3.7.1.1
|
81
|
+
class Simple < S2K
|
82
|
+
IDENTIFIER = 0x00
|
83
|
+
|
84
|
+
def self.parse(input)
|
85
|
+
self.new(nil, :algorithm => input.read_byte)
|
86
|
+
end
|
87
|
+
|
88
|
+
def digest_input
|
89
|
+
passphrase
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# @see http://tools.ietf.org/html/rfc4880#section-3.7.1.2
|
95
|
+
class Salted < S2K
|
96
|
+
IDENTIFIER = 0x01
|
97
|
+
|
98
|
+
def self.parse(input)
|
99
|
+
self.new(nil, :algorithm => input.read_byte, :salt => input.read_bytes(8))
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_accessor :salt
|
103
|
+
|
104
|
+
def initialize(passphrase = nil, options = {}, &block)
|
105
|
+
super(passphrase, options, &block)
|
106
|
+
|
107
|
+
@salt = Random.bytes(8) unless @salt
|
108
|
+
end
|
109
|
+
|
110
|
+
def write(buffer)
|
111
|
+
super(buffer)
|
112
|
+
buffer.write_bytes(salt)
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_hash
|
116
|
+
super.merge({:salt => salt})
|
117
|
+
end
|
118
|
+
|
119
|
+
def digest_input
|
120
|
+
salt.to_s[0, 8] << passphrase
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# @see http://tools.ietf.org/html/rfc4880#section-3.7.1.3
|
126
|
+
class Iterated < Salted
|
127
|
+
IDENTIFIER = 0x03
|
128
|
+
|
129
|
+
def self.parse(input)
|
130
|
+
self.new(nil, :algorithm => input.read_byte, :salt => input.read_bytes(8)) do |s2k|
|
131
|
+
s2k.count = s2k.decode_count(input.read_byte)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
attr_reader :count
|
136
|
+
|
137
|
+
def initialize(passphrase = nil, options = {}, &block)
|
138
|
+
super(passphrase, options, &block)
|
139
|
+
|
140
|
+
@count = 65536 unless @count
|
141
|
+
end
|
142
|
+
|
143
|
+
def write(buffer)
|
144
|
+
super(buffer)
|
145
|
+
buffer.write_byte(encode_count(count))
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_hash
|
149
|
+
super.merge(:count => count)
|
150
|
+
end
|
151
|
+
|
152
|
+
def digest_input
|
153
|
+
buffer = Buffer.write do |buffer|
|
154
|
+
iterations = count
|
155
|
+
while iterations > 0
|
156
|
+
buffer << (digest_input = super())
|
157
|
+
iterations -= digest_input.size
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
protected
|
163
|
+
|
164
|
+
EXPBIAS = 6
|
165
|
+
|
166
|
+
def decode_count(count)
|
167
|
+
(16 + (count & 15)) << ((count >> 4) + EXPBIAS)
|
168
|
+
end
|
169
|
+
|
170
|
+
def encode_count(iterations)
|
171
|
+
case iterations
|
172
|
+
when 0..1024 then 0
|
173
|
+
when 65011712..(1.0/0) then 255
|
174
|
+
else
|
175
|
+
count1 = iterations >> 6
|
176
|
+
count2 = (count2 || 0) + 1 while count1 >= 32 && count1 >>= 1
|
177
|
+
result = (count2 << 4) | (count1 - 16)
|
178
|
+
result += 1 if decode_count(result) < iterations
|
179
|
+
result
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
DEFAULT = Iterated
|
185
|
+
end
|
186
|
+
end
|
data/lib/openpgp/util.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
module OpenPGP
|
2
|
+
##
|
3
|
+
# Alias for OpenPGP::Armor.encode().
|
4
|
+
def self.enarmor(data, marker = :message, options = {})
|
5
|
+
Armor.encode(data, marker, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
##
|
9
|
+
# Alias for OpenPGP::Armor.decode().
|
10
|
+
def self.dearmor(text, marker = nil, options = {})
|
11
|
+
Armor.decode(text, marker, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Alias for OpenPGP::Message.encrypt().
|
16
|
+
def self.encrypt(data, options = {})
|
17
|
+
(msg = Message.encrypt(data, options)) ? msg.to_s : nil
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Alias for OpenPGP::Message.decrypt().
|
22
|
+
def self.decrypt(data, options = {})
|
23
|
+
raise NotImplementedError # TODO
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Alias for OpenPGP::Message.sign().
|
28
|
+
def self.sign
|
29
|
+
raise NotImplementedError # TODO
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Alias for OpenPGP::Message.verify().
|
34
|
+
def self.verify
|
35
|
+
raise NotImplementedError # TODO
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# @see http://tools.ietf.org/html/rfc4880#section-6.1
|
40
|
+
CRC24_INIT = 0x00b704ce
|
41
|
+
CRC24_POLY = 0x01864cfb
|
42
|
+
|
43
|
+
##
|
44
|
+
# @see http://tools.ietf.org/html/rfc4880#section-6
|
45
|
+
# @see http://tools.ietf.org/html/rfc4880#section-6.1
|
46
|
+
def self.crc24(data)
|
47
|
+
crc = CRC24_INIT
|
48
|
+
data.each_byte do |octet|
|
49
|
+
crc ^= octet << 16
|
50
|
+
8.times do
|
51
|
+
crc <<= 1
|
52
|
+
crc ^= CRC24_POLY if (crc & 0x01000000).nonzero?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
crc &= 0x00ffffff
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Returns the bit length of a multiprecision integer (MPI).
|
60
|
+
#
|
61
|
+
# @see http://tools.ietf.org/html/rfc4880#section-3.2
|
62
|
+
def self.bitlength(data)
|
63
|
+
data.empty? ? 0 : (data.size - 1) * 8 + (Math.log(data[0]) / Math.log(2)).floor + 1
|
64
|
+
end
|
65
|
+
end
|
data/lib/openpgp/version.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
module OpenPGP
|
2
|
-
module
|
2
|
+
module VERSION
|
3
3
|
MAJOR = 0
|
4
4
|
MINOR = 0
|
5
|
-
TINY =
|
5
|
+
TINY = 2
|
6
6
|
EXTRA = nil
|
7
7
|
|
8
8
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
9
9
|
STRING << "-#{EXTRA}" if EXTRA
|
10
|
+
|
11
|
+
def self.to_s() STRING end
|
12
|
+
def self.to_str() STRING end
|
10
13
|
end
|
11
14
|
end
|
metadata
CHANGED
@@ -1,19 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openpgp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arto Bendiken
|
8
|
+
- "K\xC3\xA9vin Lacointe"
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date: 2009-
|
13
|
+
date: 2009-12-20 00:00:00 +01:00
|
13
14
|
default_executable: openpgp
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
17
|
+
name: open4
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "0"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: bendiken-rakefile
|
17
28
|
type: :development
|
18
29
|
version_requirement:
|
19
30
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -22,7 +33,7 @@ dependencies:
|
|
22
33
|
- !ruby/object:Gem::Version
|
23
34
|
version: "0"
|
24
35
|
version:
|
25
|
-
description: " OpenPGP.rb is a pure-Ruby implementation of the OpenPGP Message Format
|
36
|
+
description: " OpenPGP.rb is a pure-Ruby implementation of the OpenPGP Message Format\n (RFC 4880), the most widely-used e-mail encryption standard in the world.\n"
|
26
37
|
email: arto.bendiken@gmail.com
|
27
38
|
executables:
|
28
39
|
- openpgp
|
@@ -31,22 +42,42 @@ extensions: []
|
|
31
42
|
extra_rdoc_files: []
|
32
43
|
|
33
44
|
files:
|
34
|
-
-
|
45
|
+
- AUTHORS
|
35
46
|
- README
|
36
47
|
- Rakefile
|
48
|
+
- UNLICENSE
|
37
49
|
- VERSION
|
38
50
|
- bin/openpgp
|
39
|
-
- lib/openpgp.rb
|
40
51
|
- lib/openpgp/algorithm.rb
|
41
52
|
- lib/openpgp/armor.rb
|
42
|
-
- lib/openpgp/
|
53
|
+
- lib/openpgp/buffer.rb
|
54
|
+
- lib/openpgp/cipher/3des.rb
|
55
|
+
- lib/openpgp/cipher/aes.rb
|
56
|
+
- lib/openpgp/cipher/blowfish.rb
|
57
|
+
- lib/openpgp/cipher/cast5.rb
|
58
|
+
- lib/openpgp/cipher/idea.rb
|
59
|
+
- lib/openpgp/cipher/twofish.rb
|
60
|
+
- lib/openpgp/cipher.rb
|
61
|
+
- lib/openpgp/client/gnupg.rb
|
62
|
+
- lib/openpgp/digest/md5.rb
|
63
|
+
- lib/openpgp/digest/rmd160.rb
|
64
|
+
- lib/openpgp/digest/sha1.rb
|
65
|
+
- lib/openpgp/digest/sha2.rb
|
66
|
+
- lib/openpgp/digest.rb
|
67
|
+
- lib/openpgp/engine/gnupg.rb
|
68
|
+
- lib/openpgp/engine/openssl.rb
|
69
|
+
- lib/openpgp/engine.rb
|
43
70
|
- lib/openpgp/message.rb
|
44
71
|
- lib/openpgp/packet.rb
|
72
|
+
- lib/openpgp/random.rb
|
73
|
+
- lib/openpgp/s2k.rb
|
74
|
+
- lib/openpgp/util.rb
|
45
75
|
- lib/openpgp/version.rb
|
76
|
+
- lib/openpgp.rb
|
46
77
|
has_rdoc: false
|
47
78
|
homepage: http://github.com/bendiken/openpgp
|
48
79
|
licenses:
|
49
|
-
-
|
80
|
+
- Public Domain
|
50
81
|
post_install_message:
|
51
82
|
rdoc_options: []
|
52
83
|
|
@@ -67,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
98
|
requirements:
|
68
99
|
- GnuPG >= 1.4.7 (not required, but enables extra functionality)
|
69
100
|
rubyforge_project: openpgp
|
70
|
-
rubygems_version: 1.3.
|
101
|
+
rubygems_version: 1.3.5
|
71
102
|
signing_key:
|
72
103
|
specification_version: 3
|
73
104
|
summary: A pure-Ruby implementation of the OpenPGP Message Format (RFC 4880).
|
data/LICENSE
DELETED
@@ -1,19 +0,0 @@
|
|
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.
|