rnp 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +5 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.adoc +3 -182
  5. data/lib/rnp.rb +12 -3
  6. data/lib/rnp/error.rb +40 -0
  7. data/lib/rnp/ffi/librnp.rb +306 -0
  8. data/lib/rnp/input.rb +99 -0
  9. data/lib/rnp/key.rb +275 -0
  10. data/lib/rnp/misc.rb +71 -0
  11. data/lib/rnp/op/encrypt.rb +181 -0
  12. data/lib/rnp/op/sign.rb +139 -0
  13. data/lib/rnp/op/verify.rb +147 -0
  14. data/lib/rnp/output.rb +121 -0
  15. data/lib/rnp/rnp.rb +595 -0
  16. data/lib/rnp/utils.rb +44 -0
  17. data/lib/rnp/version.rb +8 -3
  18. metadata +124 -50
  19. data/.gitignore +0 -12
  20. data/.rspec +0 -2
  21. data/.travis.yml +0 -5
  22. data/CODE_OF_CONDUCT.md +0 -74
  23. data/Gemfile +0 -4
  24. data/Rakefile +0 -6
  25. data/Use_Cases.adoc +0 -119
  26. data/bin/console +0 -14
  27. data/bin/setup +0 -8
  28. data/example-usage.rb +0 -766
  29. data/examples/highlevel/decrypt_mem.rb +0 -44
  30. data/examples/highlevel/encrypt_mem.rb +0 -46
  31. data/examples/lowlevel/decrypt_file.rb +0 -76
  32. data/examples/lowlevel/decrypt_mem.rb +0 -80
  33. data/examples/lowlevel/encrypt_file.rb +0 -68
  34. data/examples/lowlevel/encrypt_mem.rb +0 -75
  35. data/examples/lowlevel/load_pubkey.rb +0 -118
  36. data/examples/lowlevel/print_keyring_file.rb +0 -68
  37. data/examples/lowlevel/print_keyring_mem.rb +0 -96
  38. data/examples/lowlevel/sign_file.rb +0 -104
  39. data/examples/lowlevel/sign_mem.rb +0 -96
  40. data/examples/lowlevel/verify_file.rb +0 -55
  41. data/examples/lowlevel/verify_mem.rb +0 -61
  42. data/lib/rnp/highlevel.rb +0 -5
  43. data/lib/rnp/highlevel/constants.rb +0 -96
  44. data/lib/rnp/highlevel/keyring.rb +0 -259
  45. data/lib/rnp/highlevel/publickey.rb +0 -150
  46. data/lib/rnp/highlevel/secretkey.rb +0 -318
  47. data/lib/rnp/highlevel/utils.rb +0 -119
  48. data/lib/rnp/lowlevel.rb +0 -6
  49. data/lib/rnp/lowlevel/constants.rb +0 -11
  50. data/lib/rnp/lowlevel/dynarray.rb +0 -129
  51. data/lib/rnp/lowlevel/enums.rb +0 -243
  52. data/lib/rnp/lowlevel/libc.rb +0 -28
  53. data/lib/rnp/lowlevel/libopenssl.rb +0 -15
  54. data/lib/rnp/lowlevel/librnp.rb +0 -213
  55. data/lib/rnp/lowlevel/structs.rb +0 -541
  56. data/lib/rnp/lowlevel/utils.rb +0 -25
  57. data/rnp.gemspec +0 -35
  58. data/rnp/lib/rnp.rb +0 -5
  59. data/rnp/spec/rnp_spec.rb +0 -11
@@ -1,318 +0,0 @@
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
-
@@ -1,119 +0,0 @@
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
-
@@ -1,6 +0,0 @@
1
- require_relative 'lowlevel/librnp'
2
- require_relative 'lowlevel/libc'
3
- require_relative 'lowlevel/libopenssl'
4
- require_relative 'lowlevel/dynarray'
5
- require_relative 'lowlevel/utils'
6
-
@@ -1,11 +0,0 @@
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
-