rnp 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,150 @@
1
+ module RNP
2
+
3
+ require_relative 'utils'
4
+
5
+ class PublicKey
6
+ attr_accessor :version,
7
+ :creation_time,
8
+ :expiration_time,
9
+ :public_key_algorithm,
10
+ :mpi,
11
+ :userids,
12
+ :parent,
13
+ :subkeys
14
+
15
+ def initialize
16
+ @version = nil
17
+ @creation_time = nil
18
+ @expiration_time = 0
19
+ @public_key_algorithm = nil
20
+ @mpi = {}
21
+ @userids = []
22
+ @parent = nil
23
+ @subkeys = []
24
+ end
25
+
26
+ def fingerprint
27
+ fp = LibRNP::PGPFingerprint.new
28
+ native_pubkey_ptr = LibC::calloc(1, LibRNP::PGPPubKey.size)
29
+ native_pubkey = LibRNP::PGPPubKey.new(native_pubkey_ptr)
30
+ native_pubkey_auto = FFI::AutoPointer.new(native_pubkey_ptr, LibRNP::PGPPubKey.method(:release))
31
+ to_native(native_pubkey)
32
+ hash = @version == 3 ? :PGP_HASH_MD5 : :PGP_HASH_SHA1
33
+ ret = LibRNP::pgp_fingerprint(fp, native_pubkey, hash)
34
+ raise 'pgp_fingerprint failed' if ret != 1
35
+ fp[:fingerprint].to_s[0, fp[:length]]
36
+ end
37
+
38
+ def fingerprint_hex
39
+ fingerprint.bytes.collect {|byte| '%02X' % byte}.join
40
+ end
41
+
42
+ def key_id
43
+ keyid_ptr = FFI::MemoryPointer.new(:uint8, LibRNP::PGP_KEY_ID_SIZE)
44
+ native_pubkey = LibRNP::PGPPubKey.new
45
+ to_native(native_pubkey)
46
+ ret = LibRNP::pgp_keyid(keyid_ptr, LibRNP::PGP_KEY_ID_SIZE, native_pubkey, :PGP_HASH_SHA1)
47
+ raise 'pgp_keyid failed' if ret != 1
48
+ keyid_ptr.read_bytes(LibRNP::PGP_KEY_ID_SIZE)
49
+ end
50
+
51
+ def key_id_hex
52
+ key_id.bytes.collect {|byte| '%02X' % byte}.join
53
+ end
54
+
55
+ def key_length
56
+ case @public_key_algorithm
57
+ when PublicKeyAlgorithm::RSA,
58
+ PublicKeyAlgorithm::RSA_ENCRYPT_ONLY,
59
+ PublicKeyAlgorithm::RSA_SIGN_ONLY
60
+ return RNP::bignum_byte_count(@mpi[:n]) * 8
61
+ when PublicKeyAlgorithm::DSA
62
+ case RNP::bignum_byte_count(@mpi[:q])
63
+ when 20
64
+ 1024
65
+ when 28
66
+ 2048
67
+ when 32
68
+ 3072
69
+ end
70
+ when PublicKeyAlgorithm::ELGAMAL
71
+ RNP::bignum_byte_count(@mpi[:y]) * 8
72
+ end
73
+ 0
74
+ end
75
+
76
+ def encrypt(data, armored=true, sk_algorithm=SymmetricKeyAlgorithm::CAST5)
77
+ cipher = SymmetricKeyAlgorithm::to_s(sk_algorithm)
78
+ memory = nil
79
+
80
+ begin
81
+ pubkey_ptr = LibC::calloc(1, LibRNP::PGPKey.size)
82
+ pubkey = LibRNP::PGPKey.new(pubkey_ptr)
83
+ pubkey_auto = FFI::AutoPointer.new(pubkey_ptr, LibRNP::PGPKey.method(:release))
84
+
85
+ to_native_key(pubkey)
86
+ data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
87
+ data_buf.write_bytes(data)
88
+ pgpio = LibRNP::PGPIO.new
89
+ pgpio[:outs] = LibC::fdopen($stdout.to_i, 'w')
90
+ pgpio[:errs] = LibC::fdopen($stderr.to_i, 'w')
91
+ pgpio[:res] = pgpio[:errs]
92
+ memory_ptr = LibRNP::pgp_encrypt_buf(pgpio, data_buf, data_buf.size, pubkey, armored ? 1 : 0, cipher)
93
+ return nil if memory_ptr.null?
94
+ memory = LibRNP::PGPMemory.new(memory_ptr)
95
+ memory[:buf].read_bytes(memory[:length])
96
+ ensure
97
+ LibRNP::pgp_memory_free(memory) if memory
98
+ end
99
+ end
100
+
101
+ def verify(data, armored=true)
102
+ RNP::verify([self], data, armored)
103
+ end
104
+
105
+ def add_subkey(subkey)
106
+ raise if subkey.subkeys.any?
107
+ subkey.parent = self
108
+ subkey.userids = @userids
109
+ @subkeys.push(subkey)
110
+ end
111
+
112
+ def self.from_native(native)
113
+ pubkey = PublicKey.new
114
+ pubkey.version = LibRNP::enum_value(native[:version])
115
+ pubkey.creation_time = Time.at(native[:birthtime])
116
+ if pubkey.version == 3
117
+ pubkey.expiration_time = Time.at(native[:birthtime]) + (native[:days_valid] * 86400)
118
+ end
119
+ pubkey.public_key_algorithm = PublicKeyAlgorithm::from_native(native[:alg])
120
+ pubkey.mpi = RNP::mpis_from_native(native[:alg], native)
121
+ pubkey
122
+ end
123
+
124
+ def to_native(native)
125
+ native[:version] = @version
126
+ native[:birthtime] = @creation_time.to_i
127
+ if @version == 3 and @expiration_time
128
+ native[:days_valid] = ((@expiration_time.to_i - @creation_time.to_i) / 86400).to_i
129
+ else
130
+ native[:duration] = (@expiration_time.to_i - @creation_time.to_i).to_i
131
+ end
132
+ native[:alg] = @public_key_algorithm
133
+ RNP::mpis_to_native(native[:alg], @mpi, native)
134
+ end
135
+
136
+ def to_native_key(native_key)
137
+ native_key[:type] = :PGP_PTAG_CT_PUBLIC_KEY
138
+ native_key[:sigid] = key_id
139
+ to_native(native_key[:key][:pubkey])
140
+ if not @parent
141
+ @userids.each {|userid|
142
+ LibRNP::dynarray_append_item(native_key, 'uid', :string, userid)
143
+ }
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ end # module RNP
150
+
@@ -0,0 +1,318 @@
1
+ module RNP
2
+
3
+ require 'forwardable'
4
+
5
+ require_relative 'publickey'
6
+ require_relative 'utils'
7
+
8
+ # Secret key
9
+ #
10
+ class SecretKey
11
+ extend Forwardable
12
+ delegate [:creation_time, :expiration_time, :expiration_time=,
13
+ :fingerprint, :fingerprint_hex, :key_id, :key_id_hex] => :@public_key
14
+
15
+ attr_accessor :public_key,
16
+ :string_to_key_usage,
17
+ :string_to_key_specifier,
18
+ :symmetric_key_algorithm,
19
+ :hash_algorithm,
20
+ :iv,
21
+ :check_hash,
22
+ :mpi,
23
+ :userids,
24
+ :parent,
25
+ :subkeys,
26
+ :raw_subpackets,
27
+ :encrypted,
28
+ :passphrase
29
+
30
+ def initialize
31
+ @public_key = nil
32
+ @string_to_key_usage = nil
33
+ @string_to_key_specifier = nil
34
+ @symmetric_key_algorithm = nil
35
+ @hash_algorithm = nil
36
+ @iv = nil
37
+ @check_hash = nil
38
+ @mpi = {}
39
+ @userids = []
40
+ @parent = nil
41
+ @subkeys = []
42
+ @raw_subpackets = []
43
+ @encrypted = false
44
+ @passphrase = ''
45
+ end
46
+
47
+ # Checks if a key is encrypted. An encrypted key requires a
48
+ # passphrase for signing/decrypting/etc and will have nil values
49
+ # for key material/mpis.
50
+ #
51
+ # @return [Boolean]
52
+ def encrypted?
53
+ @encrypted
54
+ end
55
+
56
+ # Decrypts data using this secret key.
57
+ #
58
+ # Note: {#passphrase} must be set to the correct passphrase prior
59
+ # to this call. If no passphrase is required, it should be set to
60
+ # '' (not nil).
61
+ #
62
+ # @param data [String] the encrypted data to be decrypted.
63
+ # @param armored [Boolean] whether the encrypted data is ASCII armored.
64
+ def decrypt(data, armored=true)
65
+ begin
66
+ rd, wr = IO.pipe
67
+ wr.write(@passphrase + "\n")
68
+ native_keyring_ptr = LibC::calloc(1, LibRNP::PGPKeyring.size)
69
+ native_keyring = LibRNP::PGPKeyring.new(native_keyring_ptr)
70
+ RNP::keys_to_native_keyring([self], native_keyring)
71
+ pgpio = create_pgpio
72
+ data_ptr = FFI::MemoryPointer.new(:uint8, data.bytesize)
73
+ data_ptr.write_bytes(data)
74
+ passfp = LibC::fdopen(rd.to_i, 'r')
75
+ mem_ptr = LibRNP::pgp_decrypt_buf(pgpio, data_ptr, data_ptr.size,
76
+ native_keyring, nil,
77
+ armored ? 1 : 0, 0, passfp, 1, nil)
78
+ return nil if mem_ptr.null?
79
+ mem = LibRNP::PGPMemory.new(mem_ptr)
80
+ mem[:buf].read_bytes(mem[:length])
81
+ ensure
82
+ rd.close
83
+ wr.close
84
+ end
85
+ end
86
+
87
+ # Signs data using this secret key.
88
+ #
89
+ # Note: {#passphrase} must be set to the correct passphrase prior
90
+ # to this call. If no passphrase is required, it should be set to ''.
91
+ #
92
+ # @param data [String] the data to be signed.
93
+ # @param armored [Boolean] whether the output should be ASCII armored.
94
+ # @param options [Hash] less-often used options that override defaults.
95
+ # * :from [Time] (defaults to Time.now) - signature creation time
96
+ # * :duration [Numeric] (defaults to 0) - signature duration/expiration
97
+ # * :hash_algorithm [RNP::HashAlgorithm] (defaults to SHA1) -
98
+ # hash algorithm to use
99
+ # * :cleartext [Boolean] (defaults to false) - whether this should be
100
+ # a cleartext/clearsign signature, which includes the original
101
+ # data in cleartext in the same document.
102
+ # @return [String] the signed data, or nil on error.
103
+ def sign(data, armored=true, options={})
104
+ valid_options = [:from, :duration, :hash_algorithm, :cleartext]
105
+ for option in options.keys
106
+ raise if not valid_options.include?(option)
107
+ end
108
+
109
+ armored = armored ? 1 : 0
110
+ from = options[:from] || Time.now
111
+ duration = options[:duration] || 0
112
+ hashalg = options[:hash_algorithm] || HashAlgorithm::SHA1
113
+ cleartext = options[:cleartext] ? 1 : 0
114
+
115
+ from = from.to_i
116
+ hashname = HashAlgorithm::to_s(hashalg)
117
+
118
+ pgpio = create_pgpio
119
+ data_buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
120
+ data_buf.write_bytes(data)
121
+ seckey = decrypted_seckey
122
+ return nil if not seckey
123
+ memory = nil
124
+ begin
125
+ memory_ptr = LibRNP::pgp_sign_buf(pgpio, data_buf, data_buf.size, seckey, from, duration, hashname, armored, cleartext)
126
+ return nil if not memory_ptr or memory_ptr.null?
127
+ memory = LibRNP::PGPMemory.new(memory_ptr)
128
+ signed_data = memory[:buf].read_bytes(memory[:length])
129
+ signed_data
130
+ ensure
131
+ LibRNP::pgp_memory_free(memory) if memory
132
+ LibRNP::pgp_seckey_free(seckey) if seckey
133
+ end
134
+ end
135
+
136
+ # Cleartext signs data using this secret key.
137
+ # This is a shortcut for {#sign}.
138
+ #
139
+ # Note: {#passphrase} must be set to the correct passphrase prior
140
+ # to this call. If no passphrase is required, it should be set to ''.
141
+ #
142
+ # @param data [String] the data to be signed.
143
+ # @param armored [Boolean] whether the output should be ASCII armored.
144
+ # @param options [Hash] less-often used options that override defaults.
145
+ # * :from [Time] (defaults to Time.now) - signature creation time
146
+ # * :duration [Integer] (defaults to 0) - signature duration/expiration
147
+ # * :hash_algorithm [{RNP::HashAlgorithm}] (defaults to SHA1) -
148
+ # hash algorithm to use
149
+ # @return [String] the signed data, or nil on error.
150
+ def clearsign(data, armored=true, options={})
151
+ options[:cleartext] = true
152
+ sign(data, armored, options)
153
+ end
154
+
155
+ # Creates a detached signature of a file.
156
+ #
157
+ # Note: {#passphrase} must be set to the correct passphrase prior
158
+ # to this call. If no passphrase is required, it should be set to ''.
159
+ #
160
+ # @param infile [String] the path to the input file for which a
161
+ # signature will be created.
162
+ # @param sigfile [String] the path to the signature file that will
163
+ # be created.
164
+ #
165
+ # This can be nil, in which case the filename will be the infile
166
+ # parameter with '.asc' appended.
167
+ #
168
+ # @param armored [Boolean] whether the output should be ASCII armored.
169
+ # @param options [Hash] less-often used options that override defaults.
170
+ # * :from [Time] (defaults to Time.now) - signature creation time
171
+ # * :duration [Integer] (defaults to 0) - signature duration/expiration
172
+ # * :hash_algorithm [{RNP::HashAlgorithm}] (defaults to SHA1) -
173
+ # hash algorithm to use
174
+ # @return [Boolean] whether the signing was successful.
175
+ def detached_sign(infile, sigfile=nil, armored=true, options={})
176
+ valid_options = [:from, :duration, :hash_algorithm]
177
+ for option in options.keys
178
+ raise if not valid_options.include?(option)
179
+ end
180
+
181
+ armored = armored ? 1 : 0
182
+ from = options[:from] || Time.now
183
+ duration = options[:duration] || 0
184
+ hashalg = options[:hash_algorithm] || HashAlgorithm::SHA1
185
+
186
+ hashname = HashAlgorithm::to_s(hashalg)
187
+ from = from.to_i
188
+
189
+ pgpio = create_pgpio
190
+ # Note: pgp_sign_detached calls pgp_seckey_free for us
191
+ seckey = decrypted_seckey
192
+ return false if not seckey
193
+ ret = LibRNP::pgp_sign_detached(pgpio, infile, sigfile, seckey, hashname, from, duration, armored, 1)
194
+ return ret == 1
195
+ end
196
+
197
+ def add_subkey(subkey)
198
+ raise if subkey.subkeys.any?
199
+ subkey.parent = self
200
+ subkey.userids = @userids
201
+ @subkeys.push(subkey)
202
+ end
203
+
204
+ def self.generate(passphrase, options={})
205
+ valid_options = [:key_length, :public_key_algorithm, :algorithm_params,
206
+ :hash_algorithm, :symmetric_key_algorithm]
207
+ for option in options.keys
208
+ raise if not valid_options.include?(option)
209
+ end
210
+
211
+ key_length = options[:key_length] || 4096
212
+ pkalg = options[:public_key_algorithm] || PublicKeyAlgorithm::RSA
213
+ pkalg_params = options[:algorithm_params] || {e: 65537}
214
+ hashalg = options[:hash_algorithm] || HashAlgorithm::SHA1
215
+ skalg = options[:symmetric_key_algorithm] || SymmetricKeyAlgorithm::CAST5
216
+ hashalg_s = HashAlgorithm::to_s(hashalg)
217
+ skalg_s = SymmetricKeyAlgorithm::to_s(skalg)
218
+
219
+ native_key = nil
220
+ begin
221
+ native_key = LibRNP::pgp_rsa_new_key(key_length, pkalg_params[:e], hashalg_s, skalg_s)
222
+ key = SecretKey::from_native(native_key[:key][:seckey])
223
+ key.passphrase = passphrase
224
+ key
225
+ ensure
226
+ LibRNP::pgp_keydata_free(native_key) if native_key
227
+ end
228
+ end
229
+
230
+ def self.from_native(sk, encrypted=false)
231
+ seckey = SecretKey.new
232
+ seckey.public_key = PublicKey::from_native(sk[:pubkey])
233
+ seckey.string_to_key_usage = LibRNP::enum_value(sk[:s2k_usage])
234
+ seckey.string_to_key_specifier = LibRNP::enum_value(sk[:s2k_specifier])
235
+ seckey.symmetric_key_algorithm = LibRNP::enum_value(sk[:alg])
236
+ seckey.hash_algorithm = LibRNP::enum_value(sk[:hash_alg]) || HashAlgorithm::SHA1
237
+ seckey.iv = sk[:iv].to_ptr.read_bytes(sk[:iv].size)
238
+ if not sk[:checkhash].null?
239
+ seckey.check_hash = sk[:checkhash].read_bytes(LibRNP::PGP_CHECKHASH_SIZE)
240
+ end
241
+ seckey.mpi = RNP::mpis_from_native(sk[:pubkey][:alg], sk)
242
+ seckey.encrypted = encrypted
243
+ seckey
244
+ end
245
+
246
+ def to_native(native)
247
+ @public_key.to_native(native[:pubkey])
248
+ native[:s2k_usage] = @string_to_key_usage
249
+ native[:s2k_specifier] = @string_to_key_specifier
250
+ native[:alg] = @symmetric_key_algorithm
251
+ native[:hash_alg] = @hash_algorithm
252
+ # zero IV, then copy
253
+ native[:iv].to_ptr.write_bytes("\x00" * native[:iv].size)
254
+ native[:iv].to_ptr.write_bytes(@iv) if @iv
255
+ RNP::mpis_to_native(PublicKeyAlgorithm::to_native(@public_key.public_key_algorithm), @mpi, native)
256
+ # note: this has to come after mpis_to_native because that frees ptrs
257
+ if @check_hash
258
+ native[:checkhash] = LibC::calloc(1, LibRNP::PGP_CHECKHASH_SIZE)
259
+ native[:checkhash].write_bytes(@check_hash)
260
+ end
261
+ end
262
+
263
+ def to_native_key(native_key)
264
+ raise if not native_key[:packets].null?
265
+ native_key[:type] = :PGP_PTAG_CT_SECRET_KEY
266
+ native_key[:sigid] = @public_key.key_id
267
+ to_native(native_key[:key][:seckey])
268
+ if not @parent
269
+ @userids.each {|userid|
270
+ LibRNP::dynarray_append_item(native_key, 'uid', :string, userid)
271
+ }
272
+ end
273
+ @raw_subpackets.each {|bytes|
274
+ packet = LibRNP::PGPSubPacket.new
275
+ length = bytes.bytesize
276
+ packet[:length] = length
277
+ packet[:raw] = LibC::calloc(1, length)
278
+ packet[:raw].write_bytes(bytes)
279
+ LibRNP::dynarray_append_item(native_key, 'packet', LibRNP::PGPSubPacket, packet)
280
+ }
281
+ end
282
+
283
+ def decrypted_seckey
284
+ if encrypted?
285
+ native_ptr = LibC::calloc(1, LibRNP::PGPKey.size)
286
+ native = LibRNP::PGPKey.new(native_ptr)
287
+ native_auto = FFI::AutoPointer.new(native_ptr, LibRNP::PGPKey.method(:release))
288
+ to_native_key(native)
289
+ rd, wr = IO.pipe
290
+ wr.write(@passphrase + "\n")
291
+ wr.close
292
+ passfp = LibC::fdopen(rd.to_i, 'r')
293
+ decrypted = LibRNP::pgp_decrypt_seckey(native, passfp)
294
+ rd.close
295
+ LibC::fclose(passfp)
296
+ return nil if not decrypted or decrypted.null?
297
+ LibRNP::PGPSecKey.new(decrypted)
298
+ else
299
+ native_ptr = LibC::calloc(1, LibRNP::PGPSecKey.size)
300
+ native = LibRNP::PGPSecKey.new(native_ptr)
301
+ to_native(native)
302
+ native
303
+ end
304
+ end
305
+
306
+ private
307
+ def create_pgpio
308
+ pgpio = LibRNP::PGPIO.new
309
+ pgpio[:outs] = LibC::fdopen($stdout.to_i, 'w')
310
+ pgpio[:errs] = LibC::fdopen($stderr.to_i, 'w')
311
+ pgpio[:res] = pgpio[:errs]
312
+ pgpio
313
+ end
314
+
315
+ end
316
+
317
+ end # module RNP
318
+
@@ -0,0 +1,119 @@
1
+ module RNP
2
+
3
+ def self.bignum_byte_count(bn)
4
+ # Note: This probably assumes that the ruby implementation
5
+ # uses the same BN representation that libnetpgp does.
6
+ # It may be better to convert and use BN_num_bytes (or bits).
7
+ bn.to_s(16).length / 2
8
+ end
9
+
10
+ def self.stream_errors(stream)
11
+ error_ptr = stream[:errors]
12
+
13
+ errors = []
14
+ until error_ptr.null?
15
+ error = LibRNP::PGPError.new(error_ptr)
16
+
17
+ error_desc = "#{error[:file]}:#{error[:line]}: #{error[:errcode]} #{error[:comment]}"
18
+ errors.push(error_desc)
19
+
20
+ error_ptr = error[:next]
21
+ end
22
+ errors
23
+ end
24
+
25
+ def self.mpi_from_native(native)
26
+ mpi = {}
27
+ native.members.each {|member|
28
+ if native[member].null?
29
+ mpi[member] = nil
30
+ else
31
+ mpi[member] = LibRNP::bn2hex(native[member]).hex
32
+ end
33
+ }
34
+ mpi
35
+ end
36
+
37
+ def self.mpis_from_native(alg, native)
38
+ case alg
39
+ when :PGP_PKA_RSA, :PGP_PKA_RSA_ENCRYPT_ONLY, :PGP_PKA_RSA_SIGN_ONLY
40
+ material = native[:key][:rsa]
41
+ when :PGP_PKA_DSA
42
+ material = native[:key][:dsa]
43
+ when :PGP_PKA_ELGAMAL
44
+ material = native[:key][:elgamal]
45
+ else
46
+ raise "Unsupported PK algorithm: #{alg}"
47
+ end
48
+ RNP::mpi_from_native(material)
49
+ end
50
+
51
+ def self.mpi_to_native(mpi, native)
52
+ mpi.each {|name,value|
53
+ if mpi[name] == nil
54
+ native[name] = nil
55
+ else
56
+ native[name] = LibRNP::num2bn(value)
57
+ end
58
+ }
59
+ end
60
+
61
+ def self.mpis_to_native(alg, mpi, native)
62
+ case alg
63
+ when :PGP_PKA_RSA, :PGP_PKA_RSA_ENCRYPT_ONLY, :PGP_PKA_RSA_SIGN_ONLY
64
+ material = native[:key][:rsa]
65
+ when :PGP_PKA_DSA
66
+ material = native[:key][:dsa]
67
+ when :PGP_PKA_ELGAMAL
68
+ material = native[:key][:elgamal]
69
+ else
70
+ raise "Unsupported PK algorithm: #{alg}"
71
+ end
72
+ # Ensure we're not leaking memory from a prior call.
73
+ # This just frees all the BNs.
74
+ if native.is_a?(LibRNP::PGPSecKey)
75
+ LibRNP::pgp_seckey_free(native)
76
+ elsif native.is_a?(LibRNP::PGPPubKey)
77
+ LibRNP::pgp_pubkey_free(native)
78
+ else
79
+ raise
80
+ end
81
+ RNP::mpi_to_native(mpi, material)
82
+ end
83
+
84
+
85
+ # Add a subkey binding signature (type 0x18) to a key.
86
+ # Note that this should be used for encryption subkeys.
87
+ #
88
+ # @param key [LibRNP::PGPKey]
89
+ # @param subkey [LibRNP::PGPKey]
90
+ def self.add_subkey_signature(key, subkey)
91
+ sig = nil
92
+ sigoutput = nil
93
+ mem_sig = nil
94
+ begin
95
+ sig = LibRNP::pgp_create_sig_new
96
+ LibRNP::pgp_sig_start_subkey_sig(sig, key[:key][:pubkey], subkey[:key][:pubkey], :PGP_SIG_SUBKEY)
97
+ LibRNP::pgp_add_time(sig, subkey[:key][:pubkey][:birthtime], 'birth')
98
+ # TODO expiration
99
+ LibRNP::pgp_add_issuer_keyid(sig, key[:sigid])
100
+ LibRNP::pgp_end_hashed_subpkts(sig)
101
+
102
+ sigoutput_ptr = FFI::MemoryPointer.new(:pointer)
103
+ mem_sig_ptr = FFI::MemoryPointer.new(:pointer)
104
+ LibRNP::pgp_setup_memory_write(sigoutput_ptr, mem_sig_ptr, 128)
105
+ sigoutput = LibRNP::PGPOutput.new(sigoutput_ptr.read_pointer)
106
+ LibRNP::pgp_write_sig(sigoutput, sig, key[:key][:pubkey], key[:key][:seckey])
107
+ mem_sig = LibRNP::PGPMemory.new(mem_sig_ptr.read_pointer)
108
+ sigpkt = LibRNP::PGPSubPacket.new
109
+ sigpkt[:length] = LibRNP::pgp_mem_len(mem_sig)
110
+ sigpkt[:raw] = LibRNP::pgp_mem_data(mem_sig)
111
+ LibRNP::pgp_add_subpacket(subkey, sigpkt)
112
+ ensure
113
+ LibRNP::pgp_create_sig_delete(sig) if sig
114
+ LibRNP::pgp_teardown_memory_write(sigoutput, mem_sig) if mem_sig
115
+ end
116
+ end
117
+
118
+ end # module RNP
119
+
@@ -0,0 +1,5 @@
1
+ require_relative 'highlevel/constants'
2
+ require_relative 'highlevel/publickey'
3
+ require_relative 'highlevel/secretkey'
4
+ require_relative 'highlevel/keyring'
5
+
@@ -0,0 +1,11 @@
1
+ module LibRNP
2
+ PGP_KEY_ID_SIZE = 8
3
+ PGP_FINGERPRINT_SIZE = 20
4
+ PGP_MAX_KEY_SIZE = 32
5
+ PGP_SALT_SIZE = 8
6
+ PGP_MAX_BLOCK_SIZE = 16
7
+ SHA_DIGEST_LENGTH = 20
8
+ PGP_SHA1_HASH_SIZE = SHA_DIGEST_LENGTH
9
+ PGP_CHECKHASH_SIZE = PGP_SHA1_HASH_SIZE
10
+ end
11
+