botan 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2017 Ribose Inc.
4
+
5
+ require 'ffi'
6
+
7
+ require 'botan/defaults'
8
+ require 'botan/error'
9
+ require 'botan/ffi/libbotan'
10
+ require 'botan/pk/publickey'
11
+ require 'botan/utils'
12
+
13
+ module Botan
14
+ module PK
15
+ # Public Key Verify Operation
16
+ #
17
+ # See {Botan::PK::PublicKey#verify} for a simpler interface.
18
+ class Verify
19
+ # @param key [Botan::PK::PublicKey] the public key
20
+ # @param padding [String] the padding method name
21
+ def initialize(key:, padding: nil)
22
+ padding ||= Botan::DEFAULT_EMSA[key.algo]
23
+ unless key.instance_of?(PublicKey)
24
+ raise Botan::Error, 'Verify requires an instance of PublicKey'
25
+ end
26
+ ptr = FFI::MemoryPointer.new(:pointer)
27
+ flags = 0
28
+ Botan.call_ffi(:botan_pk_op_verify_create,
29
+ ptr, key.ptr, padding, flags)
30
+ ptr = ptr.read_pointer
31
+ if ptr.null?
32
+ raise Botan::Error, 'botan_pk_op_verify_create returned NULL'
33
+ end
34
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
35
+ end
36
+
37
+ # @api private
38
+ def self.destroy(ptr)
39
+ LibBotan.botan_pk_op_verify_destroy(ptr)
40
+ end
41
+
42
+ # Adds more data to the message currently being verified.
43
+ #
44
+ # @param msg [String] the data to add
45
+ # @return [self]
46
+ def update(msg)
47
+ msg_buf = FFI::MemoryPointer.from_data(msg)
48
+ Botan.call_ffi(:botan_pk_op_verify_update, @ptr, msg_buf, msg_buf.size)
49
+ self
50
+ end
51
+
52
+ # Checks the signature against the previously-provided data.
53
+ #
54
+ # @param signature [String] the signature to check
55
+ # @return [Boolean] true if the signature is valid
56
+ def check_signature(signature)
57
+ sig_buf = FFI::MemoryPointer.from_data(signature)
58
+ rc = Botan.call_ffi_rc(:botan_pk_op_verify_finish,
59
+ @ptr, sig_buf, sig_buf.size)
60
+ rc.zero?
61
+ end
62
+
63
+ def inspect
64
+ Botan.inspect_ptr(self)
65
+ end
66
+
67
+ alias << update
68
+ end # class
69
+ end # module
70
+ end # module
71
+
@@ -0,0 +1,288 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2017 Ribose Inc.
4
+
5
+ require 'ffi'
6
+ require 'forwardable'
7
+
8
+ require 'botan/error'
9
+ require 'botan/ffi/libbotan'
10
+ require 'botan/pk/op/decrypt'
11
+ require 'botan/pk/op/sign'
12
+ require 'botan/pk/publickey'
13
+ require 'botan/rng'
14
+ require 'botan/utils'
15
+
16
+ module Botan
17
+ module PK
18
+ # Private Key
19
+ class PrivateKey
20
+ extend ::Forwardable
21
+ delegate %i[algo encrypt estimated_strength verify] => :public_key
22
+ # @!method algo
23
+ # @see Botan::PK::PublicKey#algo
24
+ # @!method encrypt
25
+ # @see Botan::PK::PublicKey#encrypt
26
+ # @!method estimated_strength
27
+ # @see Botan::PK::PublicKey#estimated_strength
28
+ # @!method verify
29
+ # @see Botan::PK::PublicKey#verify
30
+
31
+ # @api private
32
+ attr_reader :ptr
33
+ # @api private
34
+ #
35
+ # See {generate} and {from_data} instead.
36
+ def initialize(ptr)
37
+ raise Botan::Error, 'PrivateKey received a NULL pointer' if ptr.null?
38
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
39
+ end
40
+
41
+ # @api private
42
+ def self.destroy(ptr)
43
+ LibBotan.botan_privkey_destroy(ptr)
44
+ end
45
+
46
+ # Generates a new key pair.
47
+ #
48
+ # @param algo [String] the public-key algorithm name
49
+ # @param params [String] algorithm-specific parameters
50
+ # @param rng [Botan::RNG] the RNG to use
51
+ # @return [Botan::PK::PrivateKey]
52
+ def self.generate(algo, params: nil, rng: Botan::RNG.new)
53
+ ptr = FFI::MemoryPointer.new(:pointer)
54
+ Botan.call_ffi(:botan_privkey_create, ptr, algo, params, rng.ptr)
55
+ ptr = ptr.read_pointer
56
+ raise Botan::Error, 'botan_privkey_create failed' if ptr.null?
57
+ PrivateKey.new(ptr)
58
+ end
59
+
60
+ # Creates a {PrivateKey} from BER/PEM data.
61
+ #
62
+ # @param data [String] the private key data in a supported format
63
+ # @return [Botan::PK::PrivateKey]
64
+ def self.from_data(data, password: nil, rng: Botan::RNG.new)
65
+ ptr = FFI::MemoryPointer.new(:pointer)
66
+ buf = FFI::MemoryPointer.from_data(data)
67
+ Botan.call_ffi(:botan_privkey_load, ptr, rng.ptr,
68
+ buf, buf.size, password)
69
+ PrivateKey.new(ptr.read_pointer)
70
+ end
71
+
72
+ # Returns the {PublicKey} portion of the key pair.
73
+ #
74
+ # @return [Botan::PK::PublicKey]
75
+ def public_key
76
+ pubkey_ptr = FFI::MemoryPointer.new(:pointer)
77
+ Botan.call_ffi(:botan_privkey_export_pubkey, pubkey_ptr, @ptr)
78
+ PublicKey.new(pubkey_ptr.read_pointer)
79
+ end
80
+
81
+ # Exports the *unencrypted* key with PEM encoding.
82
+ #
83
+ # @return [String]
84
+ def export_pem!
85
+ export(pem: true)
86
+ end
87
+
88
+ # Exports the *unencrypted* key with DER encoding.
89
+ #
90
+ # @return [String]
91
+ def export_der!
92
+ export(pem: false)
93
+ end
94
+
95
+ # Exports the encrypted key with PEM encoding.
96
+ #
97
+ # @param password [String] the password for encrypting/decrypting
98
+ # @param cipher [String] the name of the cipher to use
99
+ # @param pbkdf [String] the name of the PBKDF algorithm to use
100
+ # @param iterations [Integer] the number of iterations for PBKDF
101
+ # @param rng [Botan::RNG] the RNG to use
102
+ # @return [String]
103
+ def export_pem(password:,
104
+ cipher: nil,
105
+ pbkdf: nil,
106
+ iterations: Botan::DEFAULT_KDF_ITERATIONS,
107
+ rng: Botan::RNG.new)
108
+ export_encrypted(password: password,
109
+ pem: true,
110
+ cipher: cipher,
111
+ pbkdf: pbkdf,
112
+ iterations: iterations,
113
+ rng: rng)
114
+ end
115
+
116
+ # Exports the encrypted key with DER encoding.
117
+ #
118
+ # @param password [String] the password for encrypting/decrypting
119
+ # @param cipher [String] the name of the cipher to use
120
+ # @param pbkdf [String] the name of the PBKDF algorithm to use
121
+ # @param iterations [Integer] the number of iterations for PBKDF
122
+ # @param rng [Botan::RNG] the RNG to use
123
+ # @return [String]
124
+ def export_der(password:,
125
+ cipher: nil,
126
+ pbkdf: nil,
127
+ iterations: Botan::DEFAULT_KDF_ITERATIONS,
128
+ rng: Botan::RNG.new)
129
+ export_encrypted(password: password,
130
+ pem: true,
131
+ cipher: cipher,
132
+ pbkdf: pbkdf,
133
+ iterations: iterations,
134
+ rng: rng)
135
+ end
136
+
137
+ # Exports the encrypted key with PEM encoding, using a timed PBKDF.
138
+ #
139
+ # @param password [String] the password for encrypting/decrypting
140
+ # @param milliseconds [Integer] the minimum number of milliseconds to
141
+ # run the PBKDF.
142
+ # @param cipher [String] the name of the cipher to use
143
+ # @param pbkdf [String] the name of the PBKDF algorithm to use
144
+ # @param rng [Botan::RNG] the RNG to use
145
+ # @return [Hash<Symbol>]
146
+ # * :iterations [Integer] the iteration count used
147
+ # * :data [String] the PEM-encoded key
148
+ def export_pem_timed(password:,
149
+ milliseconds:,
150
+ cipher: nil,
151
+ pbkdf: nil,
152
+ rng: Botan::RNG.new)
153
+ export_encrypted_timed(password: password,
154
+ pem: true,
155
+ milliseconds: milliseconds,
156
+ cipher: cipher,
157
+ pbkdf: pbkdf,
158
+ rng: rng)
159
+ end
160
+
161
+ # Exports the encrypted key with DER encoding, using a timed PBKDF.
162
+ #
163
+ # @param password [String] the password for encrypting/decrypting
164
+ # @param milliseconds [Integer] the minimum number of milliseconds to
165
+ # run the PBKDF.
166
+ # @param cipher [String] the name of the cipher to use
167
+ # @param pbkdf [String] the name of the PBKDF algorithm to use
168
+ # @param rng [Botan::RNG] the RNG to use
169
+ # @return [Hash<Symbol>]
170
+ # * :iterations [Integer] the iteration count used
171
+ # * :data [String] the DER-encoded key
172
+ def export_der_timed(password:,
173
+ milliseconds:,
174
+ cipher: nil,
175
+ pbkdf: nil,
176
+ rng: Botan::RNG.new)
177
+ export_encrypted_timed(password: password,
178
+ pem: false,
179
+ milliseconds: milliseconds,
180
+ cipher: cipher,
181
+ pbkdf: pbkdf,
182
+ rng: rng)
183
+ end
184
+
185
+ # Checks whether the key appears to be valid.
186
+ #
187
+ # @param rng [Botan::RNG] the RNG to use
188
+ # @param thorough [Boolean] whether to perform more thorough checks
189
+ # that may be slower
190
+ # @return [Boolean] true if the key appears to be valid
191
+ def valid?(rng = nil, thorough = false)
192
+ rng ||= Botan::RNG.new
193
+ flags = thorough ? 1 : 0
194
+ rc = LibBotan.botan_privkey_check_key(@ptr, rng.ptr, flags)
195
+ rc.zero?
196
+ end
197
+
198
+ # Retrieves a field of key material.
199
+ #
200
+ # For example, the 'e' field of an RSA key might return
201
+ # the value 0x1001.
202
+ #
203
+ # @param field [String] the name of the field to retrieve
204
+ # @return [Integer]
205
+ def get_field(field)
206
+ mp = nil
207
+ mp_ptr = FFI::MemoryPointer.new(:pointer)
208
+ Botan.call_ffi(:botan_mp_init, mp_ptr)
209
+ mp = mp_ptr.read_pointer
210
+ Botan.call_ffi(:botan_privkey_get_field, mp, @ptr, field)
211
+ hex_str = Botan.call_ffi_with_buffer(lambda { |b, bl|
212
+ LibBotan.botan_mp_to_str(mp, 16, b, bl)
213
+ }, string: true)
214
+ hex_str.hex
215
+ ensure
216
+ LibBotan.botan_mp_destroy(mp) if mp && !mp.null?
217
+ end
218
+
219
+ # Decrypts data using the key.
220
+ #
221
+ # @param data [String] the data to decrypt
222
+ # @param padding [String] the padding method to use
223
+ # @return [String] the decrypted data
224
+ def decrypt(data, padding: nil)
225
+ dec = Botan::PK::Decrypt.new(key: self, padding: padding)
226
+ dec.decrypt(data)
227
+ end
228
+
229
+ # Creates a signature using the key.
230
+ #
231
+ # @param data [String] the data to sign
232
+ # @param padding [String] the padding method
233
+ # @param rng [Botan::RNG] the RNG to use
234
+ # @return [String] the generated signature
235
+ def sign(data, padding: nil, rng: Botan::RNG.new)
236
+ sign = Botan::PK::Sign.new(key: self, padding: padding)
237
+ sign << data
238
+ sign.finish(rng)
239
+ end
240
+
241
+ def inspect
242
+ Botan.inspect_ptr(self)
243
+ end
244
+
245
+ private
246
+
247
+ def export(pem:)
248
+ flags = pem ? 1 : 0
249
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
250
+ LibBotan.botan_privkey_export(@ptr, b, bl, flags)
251
+ }, string: pem)
252
+ end
253
+
254
+ def export_encrypted(password:,
255
+ pem: true,
256
+ cipher: nil,
257
+ pbkdf: nil,
258
+ iterations: Botan::DEFAULT_KDF_ITERATIONS,
259
+ rng: Botan::RNG.new)
260
+ flags = pem ? 1 : 0
261
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
262
+ LibBotan.botan_privkey_export_encrypted_pbkdf_iter(
263
+ @ptr, b, bl, rng.ptr, password, iterations,
264
+ cipher, pbkdf, flags
265
+ )
266
+ }, string: pem)
267
+ end
268
+
269
+ def export_encrypted_timed(password:,
270
+ milliseconds:,
271
+ pem: true,
272
+ cipher: nil,
273
+ pbkdf: nil,
274
+ rng: Botan::RNG.new)
275
+ flags = pem ? 1 : 0
276
+ iterations_ptr = FFI::MemoryPointer.new(:size_t)
277
+ data = Botan.call_ffi_with_buffer(lambda { |b, bl|
278
+ LibBotan.botan_privkey_export_encrypted_pbkdf_msec(
279
+ @ptr, b, bl, rng.ptr, password, milliseconds,
280
+ iterations_ptr, cipher, pbkdf, flags
281
+ )
282
+ }, string: pem)
283
+ { data: data, iterations: iterations_ptr.read(:size_t) }
284
+ end
285
+ end # class
286
+ end # module
287
+ end # module
288
+
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2017 Ribose Inc.
4
+
5
+ require 'ffi'
6
+
7
+ require 'botan/error'
8
+ require 'botan/ffi/libbotan'
9
+ require 'botan/pk/op/encrypt'
10
+ require 'botan/pk/op/verify'
11
+ require 'botan/rng'
12
+ require 'botan/utils'
13
+
14
+ module Botan
15
+ module PK
16
+ # Public Key
17
+ class PublicKey
18
+ # @api private
19
+ attr_reader :ptr
20
+ def initialize(ptr)
21
+ raise Botan::Error, 'PublicKey received a NULL pointer' if ptr.null?
22
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
23
+ end
24
+
25
+ # @api private
26
+ def self.destroy(ptr)
27
+ LibBotan.botan_pubkey_destroy(ptr)
28
+ end
29
+
30
+ # Creates a {PublicKey} from BER/PEM data.
31
+ #
32
+ # @param data [String] the public key data in a supported format
33
+ # @return [Botan::PK::PublicKey]
34
+ def self.from_data(data)
35
+ ptr = FFI::MemoryPointer.new(:pointer)
36
+ buf = FFI::MemoryPointer.from_data(data)
37
+ Botan.call_ffi(:botan_pubkey_load, ptr, buf, buf.size)
38
+ PublicKey.new(ptr.read_pointer)
39
+ end
40
+
41
+ # Returns the estimated strength of the key.
42
+ #
43
+ # @return [Integer]
44
+ def estimated_strength
45
+ strength_ptr = FFI::MemoryPointer.new(:size_t)
46
+ Botan.call_ffi(:botan_pubkey_estimated_strength, @ptr, strength_ptr)
47
+ strength_ptr.read(:size_t)
48
+ end
49
+
50
+ # Returns the public-key algorithm name.
51
+ #
52
+ # @return [String]
53
+ def algo
54
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
55
+ LibBotan.botan_pubkey_algo_name(@ptr, b, bl)
56
+ }, guess: 32, string: true)
57
+ end
58
+
59
+ # Returns the PEM-encoded public key.
60
+ #
61
+ # @return [String]
62
+ def export_pem
63
+ export(pem: true)
64
+ end
65
+
66
+ # Returns the DER-encoded public key.
67
+ #
68
+ # @return [String]
69
+ def export_der
70
+ export(pem: false)
71
+ end
72
+
73
+ def to_s
74
+ export_pem
75
+ end
76
+
77
+ # Returns the fingerprint of the key.
78
+ #
79
+ # @param hash [String] the hash algorithm to use for the calculation
80
+ # @return [String]
81
+ def fingerprint(hash = 'SHA-256')
82
+ n = Botan::Digest.new(hash).length
83
+ buf = FFI::MemoryPointer.new(:uint8, n)
84
+ buf_len_ptr = FFI::MemoryPointer.new(:size_t)
85
+ buf_len_ptr.write(:size_t, n)
86
+ Botan.call_ffi(:botan_pubkey_fingerprint, @ptr, hash, buf, buf_len_ptr)
87
+ buf.read_bytes(buf_len_ptr.read(:size_t))
88
+ end
89
+
90
+ # Checks whether the key appears to be valid.
91
+ #
92
+ # @param rng [Botan::RNG] the RNG to use
93
+ # @param thorough [Boolean] whether to perform more thorough checks
94
+ # that may be slower
95
+ # @return [Boolean] true if the key appears to be valid
96
+ def valid?(rng = nil, thorough = false)
97
+ rng ||= Botan::RNG.new
98
+ flags = thorough ? 1 : 0
99
+ rc = LibBotan.botan_pubkey_check_key(@ptr, rng.ptr, flags)
100
+ rc.zero?
101
+ end
102
+
103
+ # Retrieves a field of key material.
104
+ #
105
+ # For example, the 'e' field of an RSA key might return
106
+ # the value 0x1001.
107
+ #
108
+ # @param field [String] the name of the field to retrieve
109
+ # @return [Integer]
110
+ def get_field(field)
111
+ mp = nil
112
+ mp_ptr = FFI::MemoryPointer.new(:pointer)
113
+ Botan.call_ffi(:botan_mp_init, mp_ptr)
114
+ mp = mp_ptr.read_pointer
115
+ Botan.call_ffi(:botan_pubkey_get_field, mp, @ptr, field)
116
+ hex_str = Botan.call_ffi_with_buffer(lambda { |b, bl|
117
+ LibBotan.botan_mp_to_str(mp, 16, b, bl)
118
+ }, string: true)
119
+ hex_str.hex
120
+ ensure
121
+ LibBotan.botan_mp_destroy(mp) if mp && !mp.null?
122
+ end
123
+
124
+ # Encrypts data using the key.
125
+ #
126
+ # @param data [String] the data to encrypt
127
+ # @param padding [String] the padding method to use
128
+ # @param rng [Botan::RNG] the RNG to use
129
+ # @return [String] the encrypted data
130
+ def encrypt(data, padding: nil, rng: Botan::RNG.new)
131
+ enc = Botan::PK::Encrypt.new(key: self, padding: padding)
132
+ enc.encrypt(data, rng: rng)
133
+ end
134
+
135
+ # Verifies a signature using the key.
136
+ #
137
+ # @param data [String] the signature data to verify
138
+ # @param padding [String] the padding method
139
+ # @return [Boolean] true if the signature is valid
140
+ def verify(data:, signature:, padding: nil)
141
+ verify = Botan::PK::Verify.new(key: self, padding: padding)
142
+ verify << data
143
+ verify.check_signature(signature)
144
+ end
145
+
146
+ def inspect
147
+ Botan.inspect_ptr(self)
148
+ end
149
+
150
+ private
151
+
152
+ def export(pem:)
153
+ flags = pem ? 1 : 0
154
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
155
+ LibBotan.botan_pubkey_export(@ptr, b, bl, flags)
156
+ }, string: pem)
157
+ end
158
+ end # class
159
+ end # module
160
+ end # module
161
+