rnp 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +26 -0
- data/README.adoc +208 -0
- data/Rakefile +6 -0
- data/Use_Cases.adoc +119 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/example-usage.rb +766 -0
- data/examples/highlevel/decrypt_mem.rb +44 -0
- data/examples/highlevel/encrypt_mem.rb +46 -0
- data/examples/lowlevel/decrypt_file.rb +76 -0
- data/examples/lowlevel/decrypt_mem.rb +80 -0
- data/examples/lowlevel/encrypt_file.rb +68 -0
- data/examples/lowlevel/encrypt_mem.rb +75 -0
- data/examples/lowlevel/load_pubkey.rb +118 -0
- data/examples/lowlevel/print_keyring_file.rb +68 -0
- data/examples/lowlevel/print_keyring_mem.rb +96 -0
- data/examples/lowlevel/sign_file.rb +104 -0
- data/examples/lowlevel/sign_mem.rb +96 -0
- data/examples/lowlevel/verify_file.rb +55 -0
- data/examples/lowlevel/verify_mem.rb +61 -0
- data/lib/rnp/highlevel/constants.rb +96 -0
- data/lib/rnp/highlevel/keyring.rb +259 -0
- data/lib/rnp/highlevel/publickey.rb +150 -0
- data/lib/rnp/highlevel/secretkey.rb +318 -0
- data/lib/rnp/highlevel/utils.rb +119 -0
- data/lib/rnp/highlevel.rb +5 -0
- data/lib/rnp/lowlevel/constants.rb +11 -0
- data/lib/rnp/lowlevel/dynarray.rb +129 -0
- data/lib/rnp/lowlevel/enums.rb +243 -0
- data/lib/rnp/lowlevel/libc.rb +28 -0
- data/lib/rnp/lowlevel/libopenssl.rb +15 -0
- data/lib/rnp/lowlevel/librnp.rb +213 -0
- data/lib/rnp/lowlevel/structs.rb +541 -0
- data/lib/rnp/lowlevel/utils.rb +25 -0
- data/lib/rnp/lowlevel.rb +6 -0
- data/lib/rnp/version.rb +3 -0
- data/lib/rnp.rb +5 -0
- data/rnp/lib/rnp.rb +5 -0
- data/rnp/spec/rnp_spec.rb +11 -0
- data/rnp.gemspec +35 -0
- metadata +82 -9
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
require_relative '../../lib/rnp'
|
6
|
+
|
7
|
+
options = {keys_armored: false, cleartext: false, output_armored: false, detached: false}
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: #{$0} [options] <seckey> <input-file> <output-file>"
|
10
|
+
opts.on('-k', '--keys-armored', 'Seckey is ASCII armored') do
|
11
|
+
options[:keys_armored] = true
|
12
|
+
end
|
13
|
+
opts.on('-c', '--clear-sign', 'Cleartext signature') do
|
14
|
+
options[:cleartext] = true
|
15
|
+
end
|
16
|
+
opts.on('-a', '--armored', 'Output file will be ASCII armored') do
|
17
|
+
options[:output_armored] = true
|
18
|
+
end
|
19
|
+
opts.on('-d', '--detached', 'Detached signature') do
|
20
|
+
options[:detached] = true
|
21
|
+
end
|
22
|
+
opts.on('-h', '--help', 'Print this help') do
|
23
|
+
puts opts
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
parser.parse!
|
28
|
+
|
29
|
+
if ARGV.length != 3
|
30
|
+
parser.display
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
|
34
|
+
seckey_filename = ARGV.shift
|
35
|
+
input_filename = ARGV.shift
|
36
|
+
output_filename = ARGV.shift
|
37
|
+
|
38
|
+
# Load keys/keyring
|
39
|
+
keyring_mem = LibC::calloc(1, LibRNP::PGPKeyring.size)
|
40
|
+
keyring = LibRNP::PGPKeyring.new(keyring_mem)
|
41
|
+
if 1 != LibRNP::pgp_keyring_fileread(keyring, options[:keys_armored] ? 1 : 0, seckey_filename)
|
42
|
+
puts 'Errors encountered while loading keyring.'
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
# Find first seckey
|
46
|
+
keycount = LibRNP::dynarray_count(keyring, 'key')
|
47
|
+
seckey = nil
|
48
|
+
(0..keycount - 1).each {|keyn|
|
49
|
+
key = LibRNP::dynarray_get_item(keyring, 'key', LibRNP::PGPKey, keyn)
|
50
|
+
seckey = key if LibRNP::pgp_is_key_secret(key)
|
51
|
+
break if seckey != nil
|
52
|
+
}
|
53
|
+
if seckey == nil
|
54
|
+
puts 'No seckey found'
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
'''
|
59
|
+
This is a bit convoluted because pgp_decrypt_seckey expects a FILE*.
|
60
|
+
It may be cleaner to reimplement the short pgp_decrypt_seckey function
|
61
|
+
in ruby as it does not do a lot.
|
62
|
+
'''
|
63
|
+
rd, wr = IO.pipe
|
64
|
+
print 'Enter passphrase: '
|
65
|
+
passphrase = $stdin.noecho(&:gets)
|
66
|
+
puts ''
|
67
|
+
wr.write passphrase
|
68
|
+
wr.close
|
69
|
+
passfp = LibC::fdopen(rd.to_i, 'r')
|
70
|
+
seckey = LibRNP::pgp_decrypt_seckey(seckey, passfp)
|
71
|
+
rd.close
|
72
|
+
LibC::fclose(passfp)
|
73
|
+
|
74
|
+
if seckey == nil
|
75
|
+
puts 'Invalid passphrase.'
|
76
|
+
exit 1
|
77
|
+
end
|
78
|
+
seckey = LibRNP::PGPSecKey.new(seckey)
|
79
|
+
|
80
|
+
pgpio = LibRNP::PGPIO.new
|
81
|
+
stdout_fp = LibC::fdopen($stdout.to_i, 'w')
|
82
|
+
stderr_fp = LibC::fdopen($stderr.to_i, 'w')
|
83
|
+
pgpio[:outs] = stdout_fp
|
84
|
+
pgpio[:errs] = stderr_fp
|
85
|
+
pgpio[:res] = stdout_fp
|
86
|
+
|
87
|
+
overwrite = 1
|
88
|
+
from = Time.now.to_i
|
89
|
+
duration = 0
|
90
|
+
armored = options[:output_armored] ? 1 : 0
|
91
|
+
|
92
|
+
# see pgp_str_to_hash_alg
|
93
|
+
hashname = 'sha1'
|
94
|
+
if options[:detached]
|
95
|
+
ret = LibRNP::pgp_sign_detached(pgpio, input_filename, output_filename, seckey, hashname, from, duration, armored, overwrite)
|
96
|
+
else
|
97
|
+
cleartext = options[:cleartext] ? 1 : 0
|
98
|
+
ret = LibRNP::pgp_sign_file(pgpio, input_filename, output_filename, seckey, hashname, from, duration, armored, cleartext, overwrite)
|
99
|
+
end
|
100
|
+
if ret == 1
|
101
|
+
puts 'Success'
|
102
|
+
else
|
103
|
+
puts 'Failed!'
|
104
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
require_relative '../../lib/rnp'
|
6
|
+
|
7
|
+
options = {armored: false, keys_armored: false, cleartext: false}
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: #{$0} [options] <seckey> <passphrase>"
|
10
|
+
opts.on('-k', '--keys-armored', 'Keys are ASCII armored') do
|
11
|
+
options[:keys_armored] = true
|
12
|
+
end
|
13
|
+
opts.on('-a', '--armored', 'Input is ASCII armored') do
|
14
|
+
options[:armored] = true
|
15
|
+
end
|
16
|
+
opts.on('-c', '--clear-sign', 'Cleartext signature') do
|
17
|
+
options[:cleartext] = true
|
18
|
+
end
|
19
|
+
opts.on('-h', '--help', 'Print this help') do
|
20
|
+
puts opts
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
end
|
24
|
+
parser.parse!
|
25
|
+
|
26
|
+
if ARGV.length != 2
|
27
|
+
parser.display
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
|
31
|
+
seckey_filename = ARGV.shift
|
32
|
+
passphrase = ARGV.shift + "\n"
|
33
|
+
|
34
|
+
# Load seckey/keyring
|
35
|
+
seckeyring_mem = LibC::calloc(1, LibRNP::PGPKeyring.size)
|
36
|
+
seckeyring = LibRNP::PGPKeyring.new(seckeyring_mem)
|
37
|
+
if 1 != LibRNP::pgp_keyring_fileread(seckeyring, options[:keys_armored] ? 1 : 0, seckey_filename)
|
38
|
+
puts 'Errors encountered while loading secret keyring.'
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
# Find first seckey
|
42
|
+
keycount = LibRNP::dynarray_count(seckeyring, 'key')
|
43
|
+
seckey = nil
|
44
|
+
(0..keycount - 1).each {|keyn|
|
45
|
+
key = LibRNP::dynarray_get_item(seckeyring, 'key', LibRNP::PGPKey, keyn)
|
46
|
+
seckey = key if LibRNP::pgp_is_key_secret(key)
|
47
|
+
break if seckey != nil
|
48
|
+
}
|
49
|
+
if seckey == nil
|
50
|
+
puts 'No seckey found'
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
pgpio = LibRNP::PGPIO.new
|
55
|
+
stderr_fp = LibC::fdopen($stderr.to_i, 'w')
|
56
|
+
# send all to stderr
|
57
|
+
pgpio[:outs] = stderr_fp
|
58
|
+
pgpio[:errs] = stderr_fp
|
59
|
+
pgpio[:res] = stderr_fp
|
60
|
+
|
61
|
+
rd, wr = IO.pipe
|
62
|
+
wr.write passphrase
|
63
|
+
wr.close
|
64
|
+
passfp = LibC::fdopen(rd.to_i, 'r')
|
65
|
+
seckey = LibRNP::pgp_decrypt_seckey(seckey, passfp)
|
66
|
+
rd.close
|
67
|
+
LibC::fclose(passfp)
|
68
|
+
|
69
|
+
if seckey == nil
|
70
|
+
puts 'Invalid passphrase.'
|
71
|
+
exit 1
|
72
|
+
end
|
73
|
+
seckey = LibRNP::PGPSecKey.new(seckey)
|
74
|
+
|
75
|
+
armored = options[:armored] ? 1 : 0
|
76
|
+
cleartext = options[:cleartext] ? 1 : 0
|
77
|
+
from = Time.now.to_i
|
78
|
+
duration = 0
|
79
|
+
# see pgp_str_to_hash_alg
|
80
|
+
hashname = 'sha1'
|
81
|
+
|
82
|
+
$stdin.binmode
|
83
|
+
data = $stdin.read
|
84
|
+
data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
|
85
|
+
data_buf.put_bytes(0, data)
|
86
|
+
memory_ptr = LibRNP::pgp_sign_buf(pgpio, data_buf, data_buf.size, seckey, from, duration, hashname, armored, cleartext)
|
87
|
+
if not memory_ptr.null?
|
88
|
+
memory = LibRNP::PGPMemory.new(memory_ptr)
|
89
|
+
$stdout.binmode
|
90
|
+
$stdout.puts memory[:buf].read_bytes(memory[:length])
|
91
|
+
LibRNP::pgp_memory_free(memory)
|
92
|
+
$stderr.puts 'Success'
|
93
|
+
else
|
94
|
+
$stderr.puts 'Failed!'
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
require_relative '../../lib/rnp'
|
6
|
+
|
7
|
+
options = {keys_armored: false, armored: false}
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: #{$0} [options] <pubkey> <input-file>"
|
10
|
+
opts.on('-k', '--keys-armored', 'Pubkey is ASCII armored') do
|
11
|
+
options[:keys_armored] = true
|
12
|
+
end
|
13
|
+
opts.on('-a', '--armored', 'Input file is ASCII armored') do
|
14
|
+
options[:armored] = true
|
15
|
+
end
|
16
|
+
opts.on('-h', '--help', 'Print this help') do
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
parser.parse!
|
22
|
+
|
23
|
+
if ARGV.length != 2
|
24
|
+
parser.display
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
pubkey_filename = ARGV.shift
|
29
|
+
input_filename = ARGV.shift
|
30
|
+
|
31
|
+
# Load keys/keyring
|
32
|
+
keyring_mem = LibC::calloc(1, LibRNP::PGPKeyring.size)
|
33
|
+
keyring = LibRNP::PGPKeyring.new(keyring_mem)
|
34
|
+
if 1 != LibRNP::pgp_keyring_fileread(keyring, options[:keys_armored] ? 1 : 0, pubkey_filename)
|
35
|
+
puts 'Errors encountered while loading keyring.'
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
pgpio = LibRNP::PGPIO.new
|
40
|
+
stdout_fp = LibC::fdopen($stdout.to_i, 'w')
|
41
|
+
stderr_fp = LibC::fdopen($stderr.to_i, 'w')
|
42
|
+
pgpio[:outs] = stdout_fp
|
43
|
+
pgpio[:errs] = stderr_fp
|
44
|
+
pgpio[:res] = stdout_fp
|
45
|
+
|
46
|
+
armored = options[:armored] ? 1 : 0
|
47
|
+
|
48
|
+
validation = LibRNP::PGPValidation.new
|
49
|
+
ret = LibRNP::pgp_validate_file(pgpio, validation, input_filename, nil, armored, keyring)
|
50
|
+
if ret == 1
|
51
|
+
puts 'Success'
|
52
|
+
else
|
53
|
+
puts 'Failed!'
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
require_relative '../../lib/rnp'
|
6
|
+
|
7
|
+
options = {keys_armored: false, armored: false}
|
8
|
+
parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: #{$0} [options] <pubkey>"
|
10
|
+
opts.on('-k', '--keys-armored', 'Pubkey is ASCII armored') do
|
11
|
+
options[:keys_armored] = true
|
12
|
+
end
|
13
|
+
opts.on('-a', '--armored', 'Input file is ASCII armored') do
|
14
|
+
options[:armored] = true
|
15
|
+
end
|
16
|
+
opts.on('-h', '--help', 'Print this help') do
|
17
|
+
puts opts
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
parser.parse!
|
22
|
+
|
23
|
+
if ARGV.length != 1
|
24
|
+
parser.display
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
|
28
|
+
pubkey_filename = ARGV.shift
|
29
|
+
|
30
|
+
# Load keys/keyring
|
31
|
+
keyring_mem = LibC::calloc(1, LibRNP::PGPKeyring.size)
|
32
|
+
keyring = LibRNP::PGPKeyring.new(keyring_mem)
|
33
|
+
if 1 != LibRNP::pgp_keyring_fileread(keyring, options[:keys_armored] ? 1 : 0, pubkey_filename)
|
34
|
+
puts 'Errors encountered while loading keyring.'
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
|
38
|
+
pgpio = LibRNP::PGPIO.new
|
39
|
+
stderr_fp = LibC::fdopen($stderr.to_i, 'w')
|
40
|
+
pgpio[:outs] = stderr_fp
|
41
|
+
pgpio[:errs] = stderr_fp
|
42
|
+
pgpio[:res] = stderr_fp
|
43
|
+
|
44
|
+
armored = options[:armored] ? 1 : 0
|
45
|
+
|
46
|
+
validation = LibRNP::PGPValidation.new
|
47
|
+
mem_ptr = LibC::calloc(1, LibRNP::PGPMemory.size)
|
48
|
+
mem = LibRNP::PGPMemory.new(mem_ptr)
|
49
|
+
|
50
|
+
$stdin.binmode
|
51
|
+
data = $stdin.read
|
52
|
+
data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
|
53
|
+
data_buf.put_bytes(0, data)
|
54
|
+
LibRNP::pgp_memory_add(mem, data_buf, data_buf.size)
|
55
|
+
ret = LibRNP::pgp_validate_mem(pgpio, validation, mem, nil, armored, keyring)
|
56
|
+
if ret == 1
|
57
|
+
puts 'Success'
|
58
|
+
else
|
59
|
+
puts 'Failed!'
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
module RNP
|
4
|
+
|
5
|
+
class PublicKeyAlgorithm
|
6
|
+
NONE = 0
|
7
|
+
RSA = 1
|
8
|
+
RSA_ENCRYPT_ONLY = 2
|
9
|
+
RSA_SIGN_ONLY = 3
|
10
|
+
ELGAMAL = 16
|
11
|
+
DSA = 17
|
12
|
+
ECDH = 18
|
13
|
+
ECDSA = 19
|
14
|
+
FORMERLY_ELGAMAL = 20
|
15
|
+
|
16
|
+
def self.from_native(alg)
|
17
|
+
raise if alg.class != Symbol
|
18
|
+
LibRNP::PGP_PUBKEY_ALG_T[alg]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.to_native(alg)
|
22
|
+
# avoid a warning on newer versions of ruby
|
23
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0')
|
24
|
+
raise if alg.class != Integer
|
25
|
+
else
|
26
|
+
raise if alg.class != Fixnum
|
27
|
+
end
|
28
|
+
LibRNP::PGP_PUBKEY_ALG_T[alg]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class HashAlgorithm
|
34
|
+
MD5 = 1
|
35
|
+
SHA1 = 2
|
36
|
+
RIPEMD = 3
|
37
|
+
SHA256 = 8
|
38
|
+
SHA384 = 9
|
39
|
+
SHA512 = 10
|
40
|
+
SHA224 = 11
|
41
|
+
|
42
|
+
# see pgp_str_to_hash_alg
|
43
|
+
STRING_MAPPING = {
|
44
|
+
MD5 => 'md5',
|
45
|
+
SHA1 => 'sha1',
|
46
|
+
SHA256 => 'sha256',
|
47
|
+
SHA384 => 'sha384',
|
48
|
+
SHA512 => 'sha512'
|
49
|
+
}
|
50
|
+
|
51
|
+
def self.to_s(alg)
|
52
|
+
STRING_MAPPING[alg]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
class SymmetricKeyAlgorithm
|
58
|
+
PLAINTEXT = 0
|
59
|
+
IDEA = 1
|
60
|
+
TRIPLEDES = 2
|
61
|
+
CAST5 = 3
|
62
|
+
BLOWFISH = 4
|
63
|
+
AES128 = 7
|
64
|
+
AES192 = 8
|
65
|
+
AES256 = 9
|
66
|
+
TWOFISH256 = 10
|
67
|
+
|
68
|
+
# see pgp_str_to_cipher
|
69
|
+
STRING_MAPPING = {
|
70
|
+
IDEA => 'idea',
|
71
|
+
TRIPLEDES => 'tripledes',
|
72
|
+
CAST5 => 'cast5',
|
73
|
+
AES128 => 'aes128',
|
74
|
+
AES256 => 'aes256'
|
75
|
+
}
|
76
|
+
|
77
|
+
def self.to_s(alg)
|
78
|
+
STRING_MAPPING[alg]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class StringToKeyUsage
|
84
|
+
NONE = 0
|
85
|
+
ENCRYPTED_AND_HASHED = 254
|
86
|
+
ENCRYPTED = 255
|
87
|
+
end
|
88
|
+
|
89
|
+
class StringToKeySpecifier
|
90
|
+
SIMPLE = 0
|
91
|
+
SALTED = 1
|
92
|
+
ITERATED_AND_SALTED = 3
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require 'English'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module RNP
|
5
|
+
|
6
|
+
class Keyring
|
7
|
+
extend Forwardable
|
8
|
+
delegate [:size, :each, :select, :[], :push, :clear] => :@keys
|
9
|
+
|
10
|
+
attr_reader :keys
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@keys = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load(data, armored=true, &passphrase_provider)
|
17
|
+
kr = Keyring.new
|
18
|
+
kr.add(data, armored, &passphrase_provider)
|
19
|
+
kr
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(data, armored=true, &passphrase_provider)
|
23
|
+
keys = RNP::load_keys(data, armored, &passphrase_provider)
|
24
|
+
@keys.push(*keys)
|
25
|
+
keys.size
|
26
|
+
end
|
27
|
+
|
28
|
+
def verify(data, armored=true)
|
29
|
+
RNP::verify(@keys, data, armored)
|
30
|
+
end
|
31
|
+
|
32
|
+
def export(key, armored=true)
|
33
|
+
raise if key.parent
|
34
|
+
is_public = key.is_a?(PublicKey)
|
35
|
+
if is_public
|
36
|
+
seckey = secret_keys.find {|sk| sk.key_id == key.key_id}
|
37
|
+
else
|
38
|
+
seckey = key
|
39
|
+
end
|
40
|
+
return nil if not seckey
|
41
|
+
output_ptr = FFI::MemoryPointer.new(:pointer)
|
42
|
+
mem_ptr = FFI::MemoryPointer.new(:pointer)
|
43
|
+
output = nil
|
44
|
+
mem = nil
|
45
|
+
decrypted_seckey = nil
|
46
|
+
begin
|
47
|
+
LibRNP::pgp_setup_memory_write(output_ptr, mem_ptr, 4096)
|
48
|
+
output = LibRNP::PGPOutput.new(output_ptr.read_pointer)
|
49
|
+
mem = LibRNP::PGPMemory.new(mem_ptr.read_pointer)
|
50
|
+
native_ptr = LibC::calloc(1, LibRNP::PGPKey.size)
|
51
|
+
native = LibRNP::PGPKey.new(native_ptr)
|
52
|
+
native_auto = FFI::AutoPointer.new(native_ptr, LibRNP::PGPKey.method(:release))
|
53
|
+
key.to_native_key(native)
|
54
|
+
decrypted_seckey = seckey.decrypted_seckey
|
55
|
+
return nil if not decrypted_seckey
|
56
|
+
# this is necessary for signatures
|
57
|
+
seckey = SecretKey.from_native(decrypted_seckey)
|
58
|
+
seckey.to_native(native[:key][:seckey])
|
59
|
+
native[:type] = :PGP_PTAG_CT_SECRET_KEY
|
60
|
+
if is_public
|
61
|
+
LibRNP::dynarray_clear(native, 'uid', :string)
|
62
|
+
key.userids.each {|userid|
|
63
|
+
LibRNP::pgp_add_selfsigned_userid(native, userid)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
# PGPKeyring is a ManagedStruct
|
67
|
+
subkeysring_ptr = LibC::calloc(1, LibRNP::PGPKeyring.size)
|
68
|
+
subkeysring = LibRNP::PGPKeyring.new(subkeysring_ptr)
|
69
|
+
RNP::keys_to_native_keyring(key.subkeys, subkeysring)
|
70
|
+
# add a binding signature to each subkey
|
71
|
+
(0..LibRNP::dynarray_count(subkeysring, 'key') - 1).each {|n|
|
72
|
+
subkey = LibRNP::dynarray_get_item(subkeysring, 'key', LibRNP::PGPKey, n)
|
73
|
+
LibRNP::dynarray_clear(subkey, 'packet', LibRNP::PGPSubPacket)
|
74
|
+
RNP::add_subkey_signature(native, subkey)
|
75
|
+
}
|
76
|
+
if is_public
|
77
|
+
ret = LibRNP::pgp_write_xfer_pubkey(output, native, subkeysring, armored ? 1 : 0)
|
78
|
+
else
|
79
|
+
decrypted_key_ptr = LibC::calloc(1, LibRNP::PGPKey.size)
|
80
|
+
decrypted_key = LibRNP::PGPKey.new(decrypted_key_ptr)
|
81
|
+
decrypted_key_auto = FFI::AutoPointer.new(decrypted_key_ptr, LibRNP::PGPKey.method(:release))
|
82
|
+
seckey.to_native_key(decrypted_key)
|
83
|
+
key.userids.each {|userid|
|
84
|
+
LibRNP::pgp_add_selfsigned_userid(decrypted_key, userid)
|
85
|
+
}
|
86
|
+
decrypted_key[:key][:seckey][:s2k_usage] = :PGP_S2KU_ENCRYPTED_AND_HASHED
|
87
|
+
decrypted_key[:key][:seckey][:alg] = :PGP_SA_CAST5
|
88
|
+
decrypted_key[:key][:seckey][:s2k_specifier] = :PGP_S2KS_SALTED
|
89
|
+
(0..LibRNP::dynarray_count(subkeysring, 'key') - 1).each {|n|
|
90
|
+
subkey = LibRNP::dynarray_get_item(subkeysring, 'key', LibRNP::PGPKey, n)
|
91
|
+
subkey[:key][:seckey][:s2k_usage] = :PGP_S2KU_ENCRYPTED_AND_HASHED
|
92
|
+
subkey[:key][:seckey][:alg] = :PGP_SA_CAST5
|
93
|
+
subkey[:key][:seckey][:s2k_specifier] = :PGP_S2KS_SALTED
|
94
|
+
}
|
95
|
+
ret = LibRNP::pgp_write_xfer_seckey(output, decrypted_key, key.passphrase, key.passphrase.size, subkeysring, armored ? 1 : 0)
|
96
|
+
end
|
97
|
+
return nil if ret != 1
|
98
|
+
data = mem[:buf].read_bytes(mem[:length])
|
99
|
+
data
|
100
|
+
ensure
|
101
|
+
LibRNP::pgp_teardown_memory_write(output, mem) if mem
|
102
|
+
LibRNP::pgp_seckey_free(decrypted_seckey) if decrypted_seckey
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def public_keys
|
107
|
+
self.select {|key|
|
108
|
+
key.is_a?(PublicKey)
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def secret_keys
|
113
|
+
self.select {|key|
|
114
|
+
key.is_a?(SecretKey)
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_native(native)
|
119
|
+
keys_to_native_keyring(@keys, native)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
PARSE_KEYRING = Proc.new do |state, passphrase_provider, pkt, data|
|
125
|
+
next :PGP_RELEASE_MEMORY if state[:errors].any?
|
126
|
+
|
127
|
+
begin
|
128
|
+
lastkey = state[:keys].last
|
129
|
+
case pkt[:tag]
|
130
|
+
when :PGP_PTAG_CT_PUBLIC_KEY
|
131
|
+
key = PublicKey::from_native(pkt[:u][:pubkey])
|
132
|
+
state[:keys].push(key)
|
133
|
+
when :PGP_PTAG_CT_PUBLIC_SUBKEY
|
134
|
+
key = PublicKey::from_native(pkt[:u][:pubkey])
|
135
|
+
lastkey.add_subkey(key)
|
136
|
+
state[:keys].push(key)
|
137
|
+
when :PGP_PTAG_CT_ENCRYPTED_SECRET_KEY
|
138
|
+
key = SecretKey::from_native(pkt[:u][:seckey], true)
|
139
|
+
state[:keys].push(key)
|
140
|
+
when :PGP_PTAG_CT_ENCRYPTED_SECRET_SUBKEY
|
141
|
+
key = SecretKey::from_native(pkt[:u][:seckey], true)
|
142
|
+
lastkey.add_subkey(key)
|
143
|
+
state[:keys].push(key)
|
144
|
+
when :PGP_PTAG_CT_SECRET_KEY
|
145
|
+
key = SecretKey::from_native(pkt[:u][:seckey])
|
146
|
+
if state[:passphrase]
|
147
|
+
key.passphrase = state[:passphrase]
|
148
|
+
state[:passphrase] = nil
|
149
|
+
end
|
150
|
+
state[:keys].push(key)
|
151
|
+
when :PGP_PTAG_CT_SECRET_SUBKEY
|
152
|
+
key = SecretKey::from_native(pkt[:u][:seckey])
|
153
|
+
lastkey.add_subkey(key)
|
154
|
+
state[:keys].push(key)
|
155
|
+
when :PGP_GET_PASSPHRASE
|
156
|
+
seckey_ptr = pkt[:u][:skey_passphrase][:seckey]
|
157
|
+
seckey = LibRNP::PGPSecKey.new(seckey_ptr)
|
158
|
+
key = SecretKey::from_native(seckey)
|
159
|
+
passphrase = passphrase_provider.call(key)
|
160
|
+
if passphrase and passphrase != ''
|
161
|
+
passphrase_mem = LibC::calloc(1, passphrase.bytesize + 1)
|
162
|
+
passphrase_mem.write_bytes(passphrase)
|
163
|
+
pkt[:u][:skey_passphrase][:passphrase].write_pointer(passphrase_mem)
|
164
|
+
state[:passphrase] = passphrase
|
165
|
+
next :PGP_KEEP_MEMORY
|
166
|
+
end
|
167
|
+
when :PGP_PARSER_PACKET_END
|
168
|
+
if lastkey.is_a? RNP::SecretKey
|
169
|
+
raw_packet = pkt[:u][:packet]
|
170
|
+
bytes = raw_packet[:raw].read_bytes(raw_packet[:length])
|
171
|
+
lastkey.raw_subpackets.push(bytes)
|
172
|
+
end
|
173
|
+
when :PGP_PTAG_CT_USER_ID
|
174
|
+
lastkey.userids.push(pkt[:u][:userid].force_encoding('utf-8'))
|
175
|
+
when :PGP_PTAG_SS_KEY_EXPIRY
|
176
|
+
lastkey.expiration_time = lastkey.creation_time + pkt[:u][:ss_time]
|
177
|
+
else
|
178
|
+
# For debugging
|
179
|
+
#puts "Unhandled tag: #{pkt[:tag]}"
|
180
|
+
end # case
|
181
|
+
rescue
|
182
|
+
state[:errors].push($ERROR_INFO)
|
183
|
+
end
|
184
|
+
next :PGP_RELEASE_MEMORY
|
185
|
+
end
|
186
|
+
|
187
|
+
DEFAULT_PASSPHRASE_PROVIDER = Proc.new do |seckey|
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.load_keys(data, armored=true, &passphrase_provider)
|
192
|
+
# Just for readability
|
193
|
+
print_errors = 0
|
194
|
+
stream_mem = LibC::calloc(1, LibRNP::PGPStream.size)
|
195
|
+
# This will free the above memory (PGPStream is a ManagedStruct)
|
196
|
+
stream = LibRNP::PGPStream.new(stream_mem)
|
197
|
+
stream[:readinfo][:accumulate] = 1
|
198
|
+
LibRNP::pgp_parse_options(stream, :PGP_PTAG_SS_ALL, :PGP_PARSE_PARSED)
|
199
|
+
|
200
|
+
# This memory will be GC'd
|
201
|
+
mem = FFI::MemoryPointer.new(:uint8, data.bytesize)
|
202
|
+
mem.write_bytes(data)
|
203
|
+
|
204
|
+
LibRNP::pgp_reader_set_memory(stream, mem, mem.size)
|
205
|
+
state = {keys: [], errors: []}
|
206
|
+
provider = block_given? ? passphrase_provider : DEFAULT_PASSPHRASE_PROVIDER
|
207
|
+
callback = RNP::PARSE_KEYRING.curry[state][provider]
|
208
|
+
LibRNP::pgp_set_callback(stream, callback, nil)
|
209
|
+
LibRNP::pgp_reader_push_dearmour(stream) if armored
|
210
|
+
if LibRNP::pgp_parse(stream, print_errors) != 1
|
211
|
+
state[:errors].push('pgp_parse failed')
|
212
|
+
end
|
213
|
+
LibRNP::pgp_reader_pop_dearmour(stream) if armored
|
214
|
+
|
215
|
+
errors = stream_errors(stream)
|
216
|
+
state[:errors].push(errors) if errors.any?
|
217
|
+
|
218
|
+
raise state[:errors].join("\n") if state[:errors].any?
|
219
|
+
state[:keys]
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.keys_to_native_keyring(keys, native)
|
223
|
+
raise if not native[:keys].null?
|
224
|
+
|
225
|
+
for key in keys
|
226
|
+
native_key = LibRNP::PGPKey.new
|
227
|
+
key.to_native_key(native_key)
|
228
|
+
LibRNP::dynarray_append_item(native, 'key', LibRNP::PGPKey, native_key)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.verify(keys, data, armored=true)
|
233
|
+
native_keyring_ptr = LibC::calloc(1, LibRNP::PGPKeyring.size)
|
234
|
+
native_keyring = LibRNP::PGPKeyring.new(native_keyring_ptr)
|
235
|
+
RNP::keys_to_native_keyring(keys, native_keyring)
|
236
|
+
|
237
|
+
pgpio = LibRNP::PGPIO.new
|
238
|
+
pgpio[:outs] = LibC::fdopen($stdout.to_i, 'w')
|
239
|
+
pgpio[:errs] = LibC::fdopen($stderr.to_i, 'w')
|
240
|
+
pgpio[:res] = pgpio[:errs]
|
241
|
+
|
242
|
+
data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
|
243
|
+
data_buf.write_bytes(data)
|
244
|
+
|
245
|
+
# pgp_validate_mem frees this
|
246
|
+
mem_ptr = LibC::calloc(1, LibRNP::PGPMemory.size)
|
247
|
+
mem = LibRNP::PGPMemory.new(mem_ptr)
|
248
|
+
LibRNP::pgp_memory_add(mem, data_buf, data_buf.size)
|
249
|
+
|
250
|
+
# ManagedStruct, this frees itself
|
251
|
+
result_ptr = LibC::calloc(1, LibRNP::PGPValidation.size)
|
252
|
+
result = LibRNP::PGPValidation.new(result_ptr)
|
253
|
+
|
254
|
+
ret = LibRNP::pgp_validate_mem(pgpio, result, mem, nil, armored ? 1 : 0, native_keyring)
|
255
|
+
ret == 1
|
256
|
+
end
|
257
|
+
|
258
|
+
end # module RNP
|
259
|
+
|