botan 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
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
+ # Random Number Generator
13
+ class RNG
14
+ # @api private
15
+ attr_reader :ptr
16
+ # @param rng_type [String] the type of RNG to create
17
+ def initialize(rng_type = nil)
18
+ ptr = FFI::MemoryPointer.new(:pointer)
19
+ Botan.call_ffi(:botan_rng_init, ptr, rng_type)
20
+ ptr = ptr.read_pointer
21
+ raise Botan::Error, 'botan_rng_init returned NULL' 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_rng_destroy(ptr)
28
+ end
29
+
30
+ # Retrieves some data from the default RNG.
31
+ #
32
+ # @param length [Integer] the number of bytes to retrieve
33
+ # @return [String]
34
+ def self.get(length)
35
+ RNG.new.get(length)
36
+ end
37
+
38
+ # Reseeds the RNG from the system RNG.
39
+ #
40
+ # @param bits [Integer] the number of bits to reseed with
41
+ # @return [self]
42
+ def reseed(bits = 256)
43
+ Botan.call_ffi(:botan_rng_reseed, @ptr, bits)
44
+ self
45
+ end
46
+
47
+ # Retrieves some data from the RNG.
48
+ #
49
+ # @param length [Integer] the number of bytes to retrieve
50
+ # @return [String]
51
+ def get(length)
52
+ out_buf = FFI::MemoryPointer.new(:uint8, length)
53
+ Botan.call_ffi(:botan_rng_get, @ptr, out_buf, length)
54
+ out_buf.read_bytes(length)
55
+ end
56
+
57
+ def inspect
58
+ Botan.inspect_ptr(self)
59
+ end
60
+ end # class
61
+ end # module
62
+
@@ -0,0 +1,104 @@
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
+
10
+ module Botan
11
+ # Encodes the provided data as a hexadecimal string.
12
+ #
13
+ # @param data [String] the data to encode
14
+ # @return [String] the data as a hexadecimal string
15
+ def self.hex_encode(data)
16
+ data.unpack('H*')[0]
17
+ end
18
+
19
+ # Decodes the provided hexadecimal string to a byte string.
20
+ #
21
+ # @param hexs [String] the hexadecimal string
22
+ # @return [String] the decoded data
23
+ def self.hex_decode(hexs)
24
+ [hexs].pack('H*')
25
+ end
26
+
27
+ # @api private
28
+ #
29
+ # Calls the LibBotan FFI function indicated and returns the return code.
30
+ # If the return code is <0, an error will be raised.
31
+ #
32
+ # @param fn [Symbol] the name of the function to call
33
+ # @param args the arguments to pass to the FFI function
34
+ # @return [Integer] the return code
35
+ def self.call_ffi_rc(fn, *args)
36
+ rc = LibBotan.method(fn).call(*args)
37
+ raise Botan::Error, "FFI call to #{fn} failed (rc: #{rc})" if rc.negative?
38
+ rc
39
+ end
40
+
41
+ # @api private
42
+ #
43
+ # Calls the LibBotan FFI function indicated.
44
+ # If the return code is <0, an error will be raised.
45
+ #
46
+ # @param fn [Symbol] the name of the function to call
47
+ # @param args the arguments to pass to the FFI function
48
+ # @return [void]
49
+ def self.call_ffi(fn, *args)
50
+ call_ffi_rc(fn, *args)
51
+ nil
52
+ end
53
+
54
+ # @api private
55
+ #
56
+ # Calls the LibBotan FFI function indicated
57
+ # If the return code is <0, an error will be raised.
58
+ #
59
+ # @param fn [#call] a proc/lambda taking two parameters.
60
+ # @param guess [Integer] an initial guess for the buffer size
61
+ # @param string [Boolean] true if the returned buffer is expected
62
+ # to be a NULL-terminated string.
63
+ # @return [String] the data
64
+ def self.call_ffi_with_buffer(fn, guess: 4096, string: false)
65
+ buf = FFI::MemoryPointer.new(:uint8, guess)
66
+ buf_len_ptr = FFI::MemoryPointer.new(:size_t)
67
+ buf_len_ptr.write(:size_t, buf.size)
68
+
69
+ rc = fn.call(buf, buf_len_ptr)
70
+ buf_len = buf_len_ptr.read(:size_t)
71
+ # Call should only fail if buffer was inadequate, and should
72
+ # only succeed if buffer was adequate.
73
+ if (rc.negative? && buf_len <= buf.size) || (rc >= 0 && buf_len > buf.size)
74
+ raise Botan::Error, 'FFI call unexpectedly failed'
75
+ end
76
+
77
+ if rc.negative?
78
+ return call_ffi_with_buffer(fn, guess: buf_len, string: string)
79
+ else
80
+ string ? buf.read_string : buf.read_bytes(buf_len)
81
+ end
82
+ end
83
+
84
+ # @api private
85
+ def self.inspect_ptr(myself)
86
+ ptr_format = "0x%0#{FFI::Pointer.size * 2}x"
87
+ ptr_s = format(ptr_format, myself.instance_variable_get(:@ptr).address)
88
+ class_name = myself.class.to_s
89
+ "#<#{class_name}:#{ptr_s}>"
90
+ end
91
+
92
+ # TODO: Upstream this.
93
+ unless FFI::MemoryPointer.respond_to?(:from_data)
94
+ # @api private
95
+ class << FFI::MemoryPointer
96
+ def from_data(data)
97
+ buf = FFI::MemoryPointer.new(:uint8, data.bytesize)
98
+ buf.write_bytes(data)
99
+ buf
100
+ end
101
+ end
102
+ end
103
+ end # module
104
+
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2017 Ribose Inc.
4
+
5
+ module Botan
6
+ VERSION = '0.1.0'
7
+ end # module
8
+
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2017 Ribose Inc.
4
+
5
+ require 'date'
6
+ require 'ffi'
7
+
8
+ require 'botan/error'
9
+ require 'botan/ffi/libbotan'
10
+ require 'botan/pk/publickey'
11
+ require 'botan/utils'
12
+
13
+ module Botan
14
+ module X509
15
+ class Certificate
16
+ def initialize(ptr)
17
+ if ptr.null?
18
+ raise Botan::Error, 'X509::Certificate received a NULL pointer'
19
+ end
20
+ @ptr = FFI::AutoPointer.new(ptr, self.class.method(:destroy))
21
+ end
22
+
23
+ def self.destroy(ptr)
24
+ LibBotan.botan_x509_cert_destroy(ptr)
25
+ end
26
+
27
+ def self.from_file(filename)
28
+ ptr = FFI::MemoryPointer.new(:pointer)
29
+ Botan.call_ffi(:botan_x509_cert_load_file, ptr, filename)
30
+ Certificate.new(ptr.read_pointer)
31
+ end
32
+
33
+ def self.from_data(data)
34
+ ptr = FFI::MemoryPointer.new(:pointer)
35
+ buf = FFI::MemoryPointer.from_data(data)
36
+ Botan.call_ffi(:botan_x509_cert_load, ptr, buf, buf.size)
37
+ Certificate.new(ptr.read_pointer)
38
+ end
39
+
40
+ def time_starts
41
+ time = Botan.call_ffi_with_buffer(lambda { |b, bl|
42
+ LibBotan.botan_x509_cert_get_time_starts(@ptr, b, bl)
43
+ }, guess: 16, string: true)
44
+ case time.size
45
+ when 13
46
+ ::DateTime.strptime(time, '%y%m%d%H%M%SZ')
47
+ when 15
48
+ ::DateTime.strptime(time, '%Y%m%d%H%M%SZ')
49
+ else
50
+ raise Botan::Error, 'X509::Certificate time_starts invalid format'
51
+ end
52
+ end
53
+
54
+ def time_expires
55
+ time = Botan.call_ffi_with_buffer(lambda { |b, bl|
56
+ LibBotan.botan_x509_cert_get_time_expires(@ptr, b, bl)
57
+ }, guess: 16, string: true)
58
+ case time.size
59
+ when 13
60
+ ::DateTime.strptime(time, '%y%m%d%H%M%SZ')
61
+ when 15
62
+ ::DateTime.strptime(time, '%Y%m%d%H%M%SZ')
63
+ else
64
+ raise Botan::Error, 'X509::Certificate time_expires invalid format'
65
+ end
66
+ end
67
+
68
+ def to_s
69
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
70
+ LibBotan.botan_x509_cert_to_string(@ptr, b, bl)
71
+ }, string: true)
72
+ end
73
+
74
+ def fingerprint(hash_algo = 'SHA-256')
75
+ n = Botan::Digest.new(hash_algo).length * 3
76
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
77
+ LibBotan.botan_x509_cert_get_fingerprint(@ptr, hash_algo, b, bl)
78
+ }, guess: n, string: true)
79
+ end
80
+
81
+ def serial_number
82
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
83
+ LibBotan.botan_x509_cert_get_serial_number(@ptr, b, bl)
84
+ })
85
+ end
86
+
87
+ def authority_key_id
88
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
89
+ LibBotan.botan_x509_cert_get_authority_key_id(@ptr, b, bl)
90
+ })
91
+ end
92
+
93
+ def subject_key_id
94
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
95
+ LibBotan.botan_x509_cert_get_subject_key_id(@ptr, b, bl)
96
+ })
97
+ end
98
+
99
+ def subject_public_key_bits
100
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
101
+ LibBotan.botan_x509_cert_get_public_key_bits(@ptr, b, bl)
102
+ })
103
+ end
104
+
105
+ def subject_public_key
106
+ ptr = FFI::MemoryPointer.new(:pointer)
107
+ Botan.call_ffi(:botan_x509_cert_get_public_key, @ptr, ptr)
108
+ pub = ptr.read_pointer
109
+ if pub.null?
110
+ raise Botan::Error, 'botan_x509_cert_get_public_key returned NULL'
111
+ end
112
+ Botan::PK::PublicKey.new(pub)
113
+ end
114
+
115
+ def issuer_info(key, index = 0)
116
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
117
+ LibBotan.botan_x509_cert_get_issuer_dn(@ptr, key, index, b, bl)
118
+ }, string: true)
119
+ end
120
+
121
+ def subject_info(key, index = 0)
122
+ Botan.call_ffi_with_buffer(lambda { |b, bl|
123
+ LibBotan.botan_x509_cert_get_subject_dn(@ptr, key, index, b, bl)
124
+ }, string: true)
125
+ end
126
+
127
+ def allowed_usage?(usage)
128
+ rc = Botan.call_ffi_rc(:botan_x509_cert_allowed_usage,
129
+ @ptr, usage)
130
+ rc.zero?
131
+ end
132
+
133
+ def inspect
134
+ Botan.inspect_ptr(self)
135
+ end
136
+ end # class
137
+ end # module
138
+ end # module
139
+
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # (c) 2017 Ribose Inc.
4
+
5
+ module Botan
6
+ module X509
7
+ module Constraints
8
+ DECIPHER_ONLY = 1 << 7
9
+ ENCIPHER_ONLY = 1 << 8
10
+ CRL_SIGN = 1 << 9
11
+ KEY_CERT_SIGN = 1 << 10
12
+ KEY_AGREEMENT = 1 << 11
13
+ DATA_ENCIPHERMENT = 1 << 12
14
+ KEY_ENCIPHERMENT = 1 << 13
15
+ NON_REPUDIATION = 1 << 14
16
+ DIGITAL_SIGNATURE = 1 << 15
17
+ end
18
+ end # module
19
+ end # module
20
+
metadata ADDED
@@ -0,0 +1,197 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: botan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ribose Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.14'
69
+ - !ruby/object:Gem::Dependency
70
+ name: codecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.7
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.7
97
+ - !ruby/object:Gem::Dependency
98
+ name: redcarpet
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.4'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.49.1
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.49.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: ffi
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.9'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.9'
139
+ description:
140
+ email:
141
+ - packaging@ribose.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files:
145
+ - README.md
146
+ - LICENSE.txt
147
+ files:
148
+ - LICENSE.txt
149
+ - README.md
150
+ - lib/botan.rb
151
+ - lib/botan/bcrypt.rb
152
+ - lib/botan/cipher.rb
153
+ - lib/botan/defaults.rb
154
+ - lib/botan/digest.rb
155
+ - lib/botan/error.rb
156
+ - lib/botan/ffi/libbotan.rb
157
+ - lib/botan/kdf.rb
158
+ - lib/botan/mac.rb
159
+ - lib/botan/pk/mceies.rb
160
+ - lib/botan/pk/op/decrypt.rb
161
+ - lib/botan/pk/op/encrypt.rb
162
+ - lib/botan/pk/op/keyagreement.rb
163
+ - lib/botan/pk/op/sign.rb
164
+ - lib/botan/pk/op/verify.rb
165
+ - lib/botan/pk/privatekey.rb
166
+ - lib/botan/pk/publickey.rb
167
+ - lib/botan/rng.rb
168
+ - lib/botan/utils.rb
169
+ - lib/botan/version.rb
170
+ - lib/botan/x509/certificate.rb
171
+ - lib/botan/x509/constraints.rb
172
+ homepage: https://www.ribose.com
173
+ licenses:
174
+ - MIT
175
+ metadata:
176
+ yard.run: yard
177
+ post_install_message:
178
+ rdoc_options: []
179
+ require_paths:
180
+ - lib
181
+ required_ruby_version: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ requirements: []
192
+ rubyforge_project:
193
+ rubygems_version: 2.5.2
194
+ signing_key:
195
+ specification_version: 4
196
+ summary: The Ruby interface for Botan.
197
+ test_files: []