botan 0.1.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.
@@ -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
+