rnp 0.1.0 → 0.2.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +26 -0
  8. data/README.adoc +208 -0
  9. data/Rakefile +6 -0
  10. data/Use_Cases.adoc +119 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/example-usage.rb +766 -0
  14. data/examples/highlevel/decrypt_mem.rb +44 -0
  15. data/examples/highlevel/encrypt_mem.rb +46 -0
  16. data/examples/lowlevel/decrypt_file.rb +76 -0
  17. data/examples/lowlevel/decrypt_mem.rb +80 -0
  18. data/examples/lowlevel/encrypt_file.rb +68 -0
  19. data/examples/lowlevel/encrypt_mem.rb +75 -0
  20. data/examples/lowlevel/load_pubkey.rb +118 -0
  21. data/examples/lowlevel/print_keyring_file.rb +68 -0
  22. data/examples/lowlevel/print_keyring_mem.rb +96 -0
  23. data/examples/lowlevel/sign_file.rb +104 -0
  24. data/examples/lowlevel/sign_mem.rb +96 -0
  25. data/examples/lowlevel/verify_file.rb +55 -0
  26. data/examples/lowlevel/verify_mem.rb +61 -0
  27. data/lib/rnp/highlevel/constants.rb +96 -0
  28. data/lib/rnp/highlevel/keyring.rb +259 -0
  29. data/lib/rnp/highlevel/publickey.rb +150 -0
  30. data/lib/rnp/highlevel/secretkey.rb +318 -0
  31. data/lib/rnp/highlevel/utils.rb +119 -0
  32. data/lib/rnp/highlevel.rb +5 -0
  33. data/lib/rnp/lowlevel/constants.rb +11 -0
  34. data/lib/rnp/lowlevel/dynarray.rb +129 -0
  35. data/lib/rnp/lowlevel/enums.rb +243 -0
  36. data/lib/rnp/lowlevel/libc.rb +28 -0
  37. data/lib/rnp/lowlevel/libopenssl.rb +15 -0
  38. data/lib/rnp/lowlevel/librnp.rb +213 -0
  39. data/lib/rnp/lowlevel/structs.rb +541 -0
  40. data/lib/rnp/lowlevel/utils.rb +25 -0
  41. data/lib/rnp/lowlevel.rb +6 -0
  42. data/lib/rnp/version.rb +3 -0
  43. data/lib/rnp.rb +5 -0
  44. data/rnp/lib/rnp.rb +5 -0
  45. data/rnp/spec/rnp_spec.rb +11 -0
  46. data/rnp.gemspec +35 -0
  47. 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
+