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,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
+