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