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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +219 -0
- data/lib/botan.rb +25 -0
- data/lib/botan/bcrypt.rb +49 -0
- data/lib/botan/cipher.rb +214 -0
- data/lib/botan/defaults.rb +21 -0
- data/lib/botan/digest.rb +145 -0
- data/lib/botan/error.rb +8 -0
- data/lib/botan/ffi/libbotan.rb +573 -0
- data/lib/botan/kdf.rb +87 -0
- data/lib/botan/mac.rb +90 -0
- data/lib/botan/pk/mceies.rb +62 -0
- data/lib/botan/pk/op/decrypt.rb +59 -0
- data/lib/botan/pk/op/encrypt.rb +62 -0
- data/lib/botan/pk/op/keyagreement.rb +59 -0
- data/lib/botan/pk/op/sign.rb +68 -0
- data/lib/botan/pk/op/verify.rb +71 -0
- data/lib/botan/pk/privatekey.rb +288 -0
- data/lib/botan/pk/publickey.rb +161 -0
- data/lib/botan/rng.rb +62 -0
- data/lib/botan/utils.rb +104 -0
- data/lib/botan/version.rb +8 -0
- data/lib/botan/x509/certificate.rb +139 -0
- data/lib/botan/x509/constraints.rb +20 -0
- metadata +197 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3e3d7ff23f00256e3b507bdbc67029f88887aa7
|
4
|
+
data.tar.gz: 2eff70990334914d19595cb04907f50256664674
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fd92e2df19b1b3b5092057cd3497774c263c9371caf12dd36df2e8355a20ae1446de63b090570c24d02581d3d9387e2ddedbfc72f85cb18aa905c3ffb01e47eb
|
7
|
+
data.tar.gz: 9280656ff057c834d6fffdd40448aadbcf6492b88121ac364988f44eeb98189035e938fa98c33feab2099d04a4bc6a0bc011897fcaa1f25a30d01cb5d1f8a570
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Ribose Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
# ruby-botan [](https://codecov.io/github/riboseinc/ruby-botan?branch=master)
|
2
|
+
|
3
|
+
ruby-botan is a Ruby interface to [Botan](https://botan.randombit.net/).
|
4
|
+
|
5
|
+
**Note**: Refer to the Botan documentation in addition to the documentation here. In particular, this note from the Botan manual applies here as well:
|
6
|
+
|
7
|
+
> You should have some knowledge of cryptography **before** trying to use the library. This is an area where it is very easy to make mistakes, and where things are often subtle and/or counterintuitive. Obviously the library tries to provide things at a high level precisely to minimize the number of ways things can go wrong, but naive use will almost certainly not result in a secure system.
|
8
|
+
|
9
|
+
# Requirements
|
10
|
+
|
11
|
+
## Ruby
|
12
|
+
|
13
|
+
ruby-botan is currently tested to work with:
|
14
|
+
|
15
|
+
* Ruby 2.3
|
16
|
+
* Ruby 2.4
|
17
|
+
|
18
|
+
## Botan
|
19
|
+
|
20
|
+
[Botan](https://botan.randombit.net/) version 2.2 or newer is required.
|
21
|
+
|
22
|
+
# Basic Usage
|
23
|
+
|
24
|
+
The samples below are meant to be a brief introduction to the library. Refer to the full documentation for full details.
|
25
|
+
|
26
|
+
Also see the [examples](https://github.com/riboseinc/ruby-botan/tree/master/examples) directory for examples on using various parts of the library.
|
27
|
+
|
28
|
+
## Utilities
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
Botan.hex_encode("\x01\x02\x03\x04")
|
32
|
+
|
33
|
+
Botan.hex_decode('01020304')
|
34
|
+
```
|
35
|
+
|
36
|
+
## RNG - Random Number Generation
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
# shortcut method that uses default RNG to get 10 bytes
|
40
|
+
Botan::RNG.get(10)
|
41
|
+
|
42
|
+
# create a different type of RNG, and reseed from the system RNG
|
43
|
+
rng = Botan::RNG.new('user')
|
44
|
+
rng.reseed
|
45
|
+
rng.get(5)
|
46
|
+
```
|
47
|
+
|
48
|
+
## Digest / Hash
|
49
|
+
|
50
|
+
There are a few different ways to utilize Digest. Which method you choose may depend on whether the data is immediately available in full, or whether it is becoming available over time.
|
51
|
+
|
52
|
+
### Simple One-Shot Hash
|
53
|
+
|
54
|
+
If you simply want to hash some data that you have immediately available in full, you may want to do something like the following.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
Botan::Digest::MD5.digest('my data')
|
58
|
+
|
59
|
+
Botan::Digest::SHA256.hexdigest('my data')
|
60
|
+
```
|
61
|
+
|
62
|
+
You may also use a longer form, in case there is not a pre-defined class (like `MD5` and `SHA256` above). For example:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
Botan::Digest.digest('Comb4P(SHA-160,RIPEMD-160)', 'my data')
|
66
|
+
|
67
|
+
Botan::Digest.hexdigest('SHA-3(224)', 'my data')
|
68
|
+
```
|
69
|
+
|
70
|
+
### Continuously Updated Hash
|
71
|
+
|
72
|
+
If you have a stream of incoming data that is not readily available that you want to hash, you may proceed in a couple of ways:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# MD5
|
76
|
+
md5 = Botan::Digest::MD5.new
|
77
|
+
md5.update('my ')
|
78
|
+
md5 << 'data'
|
79
|
+
md5.hexdigest
|
80
|
+
|
81
|
+
# Comb4P hash combiner
|
82
|
+
hash = Botan::Digest.new('Comb4P(SHA-160,RIPEMD-160)')
|
83
|
+
hash << 'my '
|
84
|
+
hash << 'data'
|
85
|
+
hash.hexdigest
|
86
|
+
```
|
87
|
+
|
88
|
+
## Cipher
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# encrypt
|
92
|
+
enc = Botan::Cipher.encryption('AES-128/CBC/PKCS7')
|
93
|
+
key = Botan::RNG.get(enc.key_length_max)
|
94
|
+
iv = Botan::RNG.get(enc.default_nonce_length)
|
95
|
+
enc.key = key
|
96
|
+
enc.iv = iv
|
97
|
+
ciphertext = enc.finish('my data')
|
98
|
+
|
99
|
+
# decrypt
|
100
|
+
dec = Botan::Cipher.decryption('AES-128/CBC/PKCS7')
|
101
|
+
dec.key = key
|
102
|
+
dec.iv = iv
|
103
|
+
plaintext = dec.finish(ciphertext)
|
104
|
+
```
|
105
|
+
|
106
|
+
## BCrypt
|
107
|
+
|
108
|
+
The `Botan::BCrypt` module supports simple bcrypt password hashing.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
# generate password hash
|
112
|
+
password_input = gets.chomp
|
113
|
+
password_hash = Botan::BCrypt.hash(password_input, work_factor: 10)
|
114
|
+
|
115
|
+
# check password
|
116
|
+
password_input = gets.chomp
|
117
|
+
Botan::BCrypt.valid?(password: password_input, phash: password_hash)
|
118
|
+
```
|
119
|
+
|
120
|
+
## KDF - Key Derivation Functions
|
121
|
+
|
122
|
+
The `Botan::KDF` module has a few different functions for key derivation.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
Botan::KDF.kdf(algo: 'KDF2(SHA-160)', secret: Botan::RNG.get(9), salt: Botan::RNG.get(7), key_length: 32)
|
126
|
+
|
127
|
+
Botan::KDF.pbkdf(algo: 'PBKDF2(CMAC(Blowfish))', password: 'some long passphrase', iterations: 150_000, key_length: 16)
|
128
|
+
|
129
|
+
Botan::KDF.pbkdf_timed(algo: 'PBKDF2(SHA-256)', password: 'my secret passphrase', key_length: 8, milliseconds: 100)
|
130
|
+
```
|
131
|
+
|
132
|
+
## MAC - Message Authentication Code
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
hmac = Botan::MAC.new('HMAC(SHA-256)')
|
136
|
+
hmac.key = Botan.hex_decode('0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20')
|
137
|
+
hmac << "\x61\x62\x63"
|
138
|
+
hmac.hexdigest
|
139
|
+
```
|
140
|
+
|
141
|
+
## PK - Public Key Cryptography
|
142
|
+
|
143
|
+
The `Botan::PK` module exposes functionality for public key loading, exporting, encryption, decryption, signing, and verification.
|
144
|
+
|
145
|
+
### Key Generation
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
# generate a 4096-bit RSA key
|
149
|
+
privkey = Botan::PK::PrivateKey.generate('RSA', params: '4096')
|
150
|
+
|
151
|
+
# generate an ECDSA key with group secp384r1
|
152
|
+
privkey = Botan::PK::PrivateKey.generate('ECDSA', params: 'secp384r1')
|
153
|
+
|
154
|
+
# generate a 4096-bit ElGamal key
|
155
|
+
privkey = Botan::PK::PrivateKey.generate('ElGamal', params: 'modp/ietf/4096')
|
156
|
+
```
|
157
|
+
|
158
|
+
### Key Loading
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
# load a public key
|
162
|
+
pubkey = Botan::PK::PublicKey.from_data(File.read('some_file.pem'))
|
163
|
+
|
164
|
+
# load an encrypted private key
|
165
|
+
privkey = Botan::PK::PrivateKey.from_data(File.read('some_file.pem'), password: 'my key password')
|
166
|
+
|
167
|
+
# load an unencrypted private key
|
168
|
+
privkey = Botan::PK::PrivateKey.from_data(File.read('some_file.pem'))
|
169
|
+
```
|
170
|
+
|
171
|
+
### Key Exporting
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# private key export (PEM)
|
175
|
+
pem = privkey.export_pem(password: 'my secret password')
|
176
|
+
|
177
|
+
# public key export (PEM)
|
178
|
+
pem = pubkey.export_pem
|
179
|
+
```
|
180
|
+
|
181
|
+
### Encryption / Decryption
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# using defaults
|
185
|
+
ciphertext = pubkey.encrypt('my data')
|
186
|
+
plaintext = privkey.decrypt(ciphertext)
|
187
|
+
```
|
188
|
+
|
189
|
+
### Signing / Verifying
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
data = 'my data'
|
193
|
+
|
194
|
+
# using defaults
|
195
|
+
signature = privkey.sign(data)
|
196
|
+
valid = pubkey.verify(data: data, signature: signature)
|
197
|
+
```
|
198
|
+
|
199
|
+
## X.509 Certificates
|
200
|
+
|
201
|
+
### Certificate Loading
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
# load from a file
|
205
|
+
cert = Botan::X509::Certificate.from_file('my cert.crt')
|
206
|
+
|
207
|
+
# load from some data in memory
|
208
|
+
cert = Botan::X509::Certificate.from_data(File.read('my cert.crt'))
|
209
|
+
```
|
210
|
+
|
211
|
+
### Certificate Properties
|
212
|
+
|
213
|
+
```ruby
|
214
|
+
# fingerprint
|
215
|
+
fpr = cert.fingerprint('SHA-256')
|
216
|
+
|
217
|
+
# subject's public key
|
218
|
+
pubkey = cert.subject_public_key
|
219
|
+
```
|
data/lib/botan.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# (c) 2017 Ribose Inc.
|
4
|
+
|
5
|
+
require 'botan/bcrypt'
|
6
|
+
require 'botan/cipher'
|
7
|
+
require 'botan/defaults'
|
8
|
+
require 'botan/error'
|
9
|
+
require 'botan/ffi/libbotan'
|
10
|
+
require 'botan/digest'
|
11
|
+
require 'botan/kdf'
|
12
|
+
require 'botan/mac'
|
13
|
+
require 'botan/pk/mceies'
|
14
|
+
require 'botan/pk/op/decrypt'
|
15
|
+
require 'botan/pk/op/encrypt'
|
16
|
+
require 'botan/pk/op/keyagreement'
|
17
|
+
require 'botan/pk/op/sign'
|
18
|
+
require 'botan/pk/op/verify'
|
19
|
+
require 'botan/pk/privatekey'
|
20
|
+
require 'botan/pk/publickey'
|
21
|
+
require 'botan/rng'
|
22
|
+
require 'botan/x509/constraints'
|
23
|
+
require 'botan/x509/certificate'
|
24
|
+
require 'botan/version'
|
25
|
+
|
data/lib/botan/bcrypt.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# (c) 2017 Ribose Inc.
|
4
|
+
|
5
|
+
require 'ffi'
|
6
|
+
|
7
|
+
require 'botan/rng'
|
8
|
+
require 'botan/utils'
|
9
|
+
|
10
|
+
module Botan
|
11
|
+
# bcrypt password hashing
|
12
|
+
#
|
13
|
+
# == Examples
|
14
|
+
# === examples/bcrypt.rb
|
15
|
+
# {include:file:examples/bcrypt.rb}
|
16
|
+
#
|
17
|
+
module BCrypt
|
18
|
+
# Generates a password hash using bcrypt.
|
19
|
+
#
|
20
|
+
# @param password [String] the password to hash
|
21
|
+
# @param work_factor [Integer] the bcrypt work factor
|
22
|
+
# @param rng [Botan::RNG] the RNG to use
|
23
|
+
# @return [String] the generated password hash
|
24
|
+
def self.hash(password, work_factor: 10, rng: Botan::RNG.new)
|
25
|
+
out_len = 64
|
26
|
+
out_buf = FFI::MemoryPointer.new(:uint8, out_len)
|
27
|
+
flags = 0
|
28
|
+
out_len_ptr = FFI::MemoryPointer.new(:size_t)
|
29
|
+
out_len_ptr.write(:size_t, out_len)
|
30
|
+
Botan.call_ffi(:botan_bcrypt_generate,
|
31
|
+
out_buf, out_len_ptr,
|
32
|
+
password, rng.ptr, work_factor, flags)
|
33
|
+
result = out_buf.read_bytes(out_len_ptr.read(:size_t))
|
34
|
+
result = result[0...-1] if result[-1] == "\x00"
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
# Checks a password against a bcrypt hash.
|
39
|
+
#
|
40
|
+
# @param password [String] the password to hash
|
41
|
+
# @param phash [String] the bcrypt hash
|
42
|
+
# @return [Boolean] true if the provided password is correct
|
43
|
+
def self.valid?(password:, phash:)
|
44
|
+
rc = Botan.call_ffi_rc(:botan_bcrypt_is_valid, password, phash)
|
45
|
+
rc.zero?
|
46
|
+
end
|
47
|
+
end # module
|
48
|
+
end # module
|
49
|
+
|
data/lib/botan/cipher.rb
ADDED
@@ -0,0 +1,214 @@
|
|
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/utils'
|
10
|
+
|
11
|
+
module Botan
|
12
|
+
# Cipher
|
13
|
+
#
|
14
|
+
# == Examples
|
15
|
+
# === examples/cipher.rb
|
16
|
+
# {include:file:examples/cipher.rb}
|
17
|
+
#
|
18
|
+
class Cipher
|
19
|
+
# Prefer the shortcuts {encryption} and {decryption} instead.
|
20
|
+
#
|
21
|
+
# @param algo [String] the algorithm to use (example: AES-128/CTR-BE)
|
22
|
+
# @param encrypt [Boolean] true if this will be used for encryption,
|
23
|
+
# false if it will be used for decryption.
|
24
|
+
def initialize(algo, encrypt:)
|
25
|
+
flags = encrypt ? 0 : 1
|
26
|
+
ptr = FFI::MemoryPointer.new(:pointer)
|
27
|
+
Botan.call_ffi(:botan_cipher_init, ptr, algo, flags)
|
28
|
+
ptr = ptr.read_pointer
|
29
|
+
raise Botan::Error, 'botan_cipher_init returned NULL' if ptr.null?
|
30
|
+
@ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def self.destroy(ptr)
|
35
|
+
LibBotan.botan_cipher_destroy(ptr)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a new cipher instance for encryption.
|
39
|
+
#
|
40
|
+
# @param algo [String] the algorithm to use (example: AES-128/CTR-BE)
|
41
|
+
# @return [Botan::Cipher] the cipher instance
|
42
|
+
def self.encryption(algo)
|
43
|
+
Cipher.new(algo, encrypt: true)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates a new cipher instance for decryption.
|
47
|
+
#
|
48
|
+
# @param algo [String] the algorithm to use (example: AES-128/CTR-BE)
|
49
|
+
# @return [Botan::Cipher] the cipher instance
|
50
|
+
def self.decryption(algo)
|
51
|
+
Cipher.new(algo, encrypt: false)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Retrieves the default nonce length for the cipher.
|
55
|
+
#
|
56
|
+
# @return [Integer]
|
57
|
+
def default_nonce_length
|
58
|
+
length_ptr = FFI::MemoryPointer.new(:size_t)
|
59
|
+
Botan.call_ffi(:botan_cipher_get_default_nonce_length, @ptr, length_ptr)
|
60
|
+
length_ptr.read(:size_t)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Retrieves the update granularity for the cipher.
|
64
|
+
#
|
65
|
+
# @return [Integer]
|
66
|
+
def update_granularity
|
67
|
+
gran_ptr = FFI::MemoryPointer.new(:size_t)
|
68
|
+
Botan.call_ffi(:botan_cipher_get_update_granularity, @ptr, gran_ptr)
|
69
|
+
gran_ptr.read(:size_t)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Retrieves the minimum key length for the cipher.
|
73
|
+
#
|
74
|
+
# @return [Integer]
|
75
|
+
def key_length_min
|
76
|
+
key_lengths[0]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Retrieves the maximum key length for the cipher.
|
80
|
+
#
|
81
|
+
# @return [Integer]
|
82
|
+
def key_length_max
|
83
|
+
key_lengths[1]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Retrieves the tag length when using AEAD modes.
|
87
|
+
#
|
88
|
+
# @return [Integer]
|
89
|
+
def tag_length
|
90
|
+
length_ptr = FFI::MemoryPointer.new(:size_t)
|
91
|
+
Botan.call_ffi(:botan_cipher_get_tag_length, @ptr, length_ptr)
|
92
|
+
length_ptr.read(:size_t)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Determines whether this is an AEAD mode.
|
96
|
+
#
|
97
|
+
# @return [Boolean] true if this is an AEAD mode
|
98
|
+
def authenticated?
|
99
|
+
tag_length.positive?
|
100
|
+
end
|
101
|
+
|
102
|
+
# Checks whether a nonce length is valid for this cipher.
|
103
|
+
#
|
104
|
+
# @param nonce_len [Integer] the nonce length to check
|
105
|
+
# @return [Boolean] true if the provided nonce length is valid
|
106
|
+
def valid_nonce_length?(nonce_len)
|
107
|
+
rc = Botan.call_ffi_rc(:botan_cipher_valid_nonce_length, @ptr, nonce_len)
|
108
|
+
rc == 1
|
109
|
+
end
|
110
|
+
|
111
|
+
# Resets the cipher state.
|
112
|
+
#
|
113
|
+
# @return [self]
|
114
|
+
def reset
|
115
|
+
Botan.call_ffi(:botan_cipher_clear, @ptr)
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sets the key to be used for the cipher.
|
120
|
+
#
|
121
|
+
# This should generally be the first thing called after
|
122
|
+
# creating a new cipher instance (or after reset).
|
123
|
+
#
|
124
|
+
# @param key [String] the key
|
125
|
+
def key=(key)
|
126
|
+
key_buf = FFI::MemoryPointer.from_data(key)
|
127
|
+
Botan.call_ffi(:botan_cipher_set_key, @ptr, key_buf, key_buf.size)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Sets the IV to be used for the cipher.
|
131
|
+
#
|
132
|
+
# This should generally be called after {#key=} or after
|
133
|
+
# {#auth_data=} (if using AEAD).
|
134
|
+
#
|
135
|
+
# @param iv [String] the IV
|
136
|
+
def iv=(iv)
|
137
|
+
start(iv)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sets the associated data when using AEAD modes.
|
141
|
+
#
|
142
|
+
# This should be called *after* {#key=} and before {#iv=}.
|
143
|
+
#
|
144
|
+
# @param ad [String] the associated data
|
145
|
+
def auth_data=(ad)
|
146
|
+
ad_buf = FFI::MemoryPointer.from_data(ad)
|
147
|
+
Botan.call_ffi(:botan_cipher_set_associated_data, @ptr, ad_buf, ad.size)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Process the data (encrypt/decrypt).
|
151
|
+
#
|
152
|
+
# @param data [String] the data to encrypt or decrypt.
|
153
|
+
# The size should likely be a multiple of {#update_granularity}.
|
154
|
+
# @return [String] the ciphertext or plaintext
|
155
|
+
def update(data)
|
156
|
+
_update(data, final: false)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Finalize the message processing.
|
160
|
+
#
|
161
|
+
# It is perfectly valid to skip {#update} and pass your
|
162
|
+
# entire message here.
|
163
|
+
#
|
164
|
+
# *Note*: Some ciphers may require a final piece of data of
|
165
|
+
# a certain size. See minimum_final_size in the Botan documentation.
|
166
|
+
#
|
167
|
+
# @param data [String] the data, if any
|
168
|
+
# @return [String] the ciphertext or plaintext
|
169
|
+
def finish(data = nil)
|
170
|
+
_update(data, final: true)
|
171
|
+
end
|
172
|
+
|
173
|
+
def inspect
|
174
|
+
Botan.inspect_ptr(self)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def start(nonce)
|
180
|
+
nonce_buf = FFI::MemoryPointer.from_data(nonce)
|
181
|
+
Botan.call_ffi(:botan_cipher_start, @ptr, nonce_buf, nonce_buf.size)
|
182
|
+
end
|
183
|
+
|
184
|
+
def key_lengths
|
185
|
+
kmin_ptr = FFI::MemoryPointer.new(:size_t)
|
186
|
+
kmax_ptr = FFI::MemoryPointer.new(:size_t)
|
187
|
+
Botan.call_ffi(:botan_cipher_query_keylen, @ptr, kmin_ptr, kmax_ptr)
|
188
|
+
[kmin_ptr.read(:size_t), kmax_ptr.read(:size_t)]
|
189
|
+
end
|
190
|
+
|
191
|
+
def _update(data, final:)
|
192
|
+
inp = data ? data : ''
|
193
|
+
flags = final ? 1 : 0
|
194
|
+
out_buf_size = inp.bytesize + (final ? tag_length : 0)
|
195
|
+
# FIXME: botan currently lacks a way of determining the size required
|
196
|
+
# here, taking in to account padding mechanism, etc.
|
197
|
+
out_buf_size += 128
|
198
|
+
out_buf = FFI::MemoryPointer.new(:uint8, out_buf_size)
|
199
|
+
out_written_ptr = FFI::MemoryPointer.new(:size_t)
|
200
|
+
input_buf = FFI::MemoryPointer.from_data(inp)
|
201
|
+
inp_consumed_ptr = FFI::MemoryPointer.new(:size_t)
|
202
|
+
Botan.call_ffi(:botan_cipher_update, @ptr, flags, out_buf, out_buf.size,
|
203
|
+
out_written_ptr, input_buf, input_buf.size,
|
204
|
+
inp_consumed_ptr)
|
205
|
+
consumed = inp_consumed_ptr.read(:size_t)
|
206
|
+
if consumed != inp.bytesize
|
207
|
+
raise Botan::Error, 'botan_cipher_update did not consume all input' \
|
208
|
+
" (#{consumed} out of #{inp.bytesize} bytes)"
|
209
|
+
end
|
210
|
+
out_buf.read_bytes(out_written_ptr.read(:size_t))
|
211
|
+
end
|
212
|
+
end # class
|
213
|
+
end # module
|
214
|
+
|