flores 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e913b4627dc9ac067842992242889c14e47d760
4
- data.tar.gz: 4102c950d2497218313c0126a77c853800fc6c65
3
+ metadata.gz: 0109a1c44c651b3d002eef3719f0888c5723dee9
4
+ data.tar.gz: bd9a91773854cbbbeaf7833297d9a20f3c11d3dc
5
5
  SHA512:
6
- metadata.gz: af96bea45346f686ee75242aa2b09f7d9906b5fd6a85b3710ea1e8be4622c33da75bb007b13eaea19ce3e16afbb355ad26ebf23dbe1c0fa7b33610655fedc986
7
- data.tar.gz: 2a2612427e400a93ac1ad7910c1b2d2d9637a9f42c3e8a4c7c96ac1042fb4cbda6df50d060283de7c3c0122d6c8ecfdc84de68bb515b6d5f7ce5a0e34dfc0b0a
6
+ metadata.gz: be9b7d18c06c04a310af5d11ec0ce5676be812bc8474e4743696d842fe369b8aed382d8b00504dc3201d60e80c8374de02282730c7c481d5ec4d9be5dbbdf406
7
+ data.tar.gz: f7e581e34aac90f9f96add6014949e0ab5fa486c5674056e8413c19c85634b6ef12fbaa05dcc0768ea9e3cf8fa1766688a9d2903635df9e46ccebb048d311751
@@ -2,7 +2,7 @@ Gem::Specification.new do |spec|
2
2
  files = %x(git ls-files).split("\n")
3
3
 
4
4
  spec.name = "flores"
5
- spec.version = "0.0.6"
5
+ spec.version = "0.0.7"
6
6
  spec.summary = "Fuzz, randomize, and stress your tests"
7
7
  spec.description = <<-DESCRIPTION
8
8
  Add fuzzing, randomization, and stress to your tests.
@@ -17,6 +17,7 @@
17
17
 
18
18
  require "flores/namespace"
19
19
  require "flores/random"
20
+ require "flores/pki/csr"
20
21
  require "English"
21
22
  require "openssl"
22
23
 
@@ -65,233 +66,4 @@ module Flores::PKI
65
66
  return [certificate, key]
66
67
  end
67
68
  end
68
-
69
- # A certificate signing request.
70
- #
71
- # From here, you can configure a certificate to be created based on your
72
- # desired configuration.
73
- #
74
- # Example making a root CA:
75
- #
76
- # key = OpenSSL::PKey::RSA.generate(4096, 65537)
77
- # csr = Flores::PKI::CertificateSigningRequest.new
78
- # csr.subject = "OU=Fancy Pants Inc."
79
- # certificate = csr.create_root(key)
80
- #
81
- # Example making an intermediate CA:
82
- #
83
- # root_key = OpenSSL::PKey::RSA.generate(4096, 65537)
84
- # root_csr = Flores::PKI::CertificateSigningRequest.new
85
- # root_csr.subject = "OU=Fancy Pants Inc."
86
- # root_csr.public_key = root_key.public
87
- # root_certificate = csr.create_root(root_key)
88
- #
89
- # intermediate_key = OpenSSL::PKey::RSA.generate(4096, 65537)
90
- # intermediate_csr = Flores::PKI::CertificateSigningRequest.new
91
- # intermediate_csr.public_key = intermediate_key.public
92
- # intermediate_csr.subject = "OU=Fancy Pants Inc. Intermediate 1"
93
- # intermediate_certificate = csr.create_intermediate(root_certificate, root_key)
94
- class CertificateSigningRequest
95
- # raised when an invalid signing configuration is given
96
- class InvalidRequest < StandardError; end
97
-
98
- # raised when invalid data is present in a certificate request
99
- class InvalidData < StandardError; end
100
-
101
- # raised when an invalid subject (format, or whatever) is given in a certificate request
102
- class InvalidSubject < InvalidData; end
103
-
104
- # raised when an invalid time value is given for a certificate request
105
- class InvalidTime < InvalidData; end
106
-
107
- def initialize
108
- self.serial = Flores::PKI.random_serial
109
- self.digest_method = default_digest_method
110
- end
111
-
112
- private
113
-
114
- def validate_subject(value)
115
- OpenSSL::X509::Name.parse(value)
116
- rescue OpenSSL::X509::NameError => e
117
- raise InvalidSubject, "Invalid subject '#{value}'. (#{e})"
118
- rescue TypeError => e
119
- # Bug(?) in MRI 2.1.6(?)
120
- raise InvalidSubject, "Invalid subject '#{value}'. (#{e})"
121
- end
122
-
123
- def subject=(value)
124
- @subject = validate_subject(value)
125
- end
126
-
127
- attr_reader :subject
128
-
129
- def subject_alternates=(values)
130
- @subject_alternates = values
131
- end
132
-
133
- attr_reader :subject_alternates
134
-
135
- def public_key=(value)
136
- @public_key = validate_public_key(value)
137
- end
138
-
139
- def validate_public_key(value)
140
- raise InvalidData, "public key must be a OpenSSL::PKey::PKey" unless value.is_a? OpenSSL::PKey::PKey
141
- value
142
- end
143
-
144
- attr_reader :public_key
145
-
146
- def start_time=(value)
147
- @start_time = validate_time(value)
148
- end
149
-
150
- attr_reader :start_time
151
-
152
- def expire_time=(value)
153
- @expire_time = validate_time(value)
154
- end
155
-
156
- attr_reader :expire_time
157
-
158
- def validate_time(value)
159
- raise InvalidTime, "#{value.inspect} (class #{value.class.name})" unless value.is_a?(Time)
160
- value
161
- end
162
-
163
- def certificate
164
- return @certificate if @certificate
165
- @certificate = OpenSSL::X509::Certificate.new
166
-
167
- # RFC5280
168
- # > 4.1.2.1. Version
169
- # > version MUST be 3 (value is 2).
170
- #
171
- # Version value of '2' means a v3 certificate.
172
- @certificate.version = 2
173
-
174
- @certificate.subject = subject
175
- @certificate.not_before = start_time
176
- @certificate.not_after = expire_time
177
- @certificate.public_key = public_key
178
- @certificate
179
- end
180
-
181
- def default_digest_method
182
- OpenSSL::Digest::SHA256.new
183
- end
184
-
185
- def self_signed?
186
- @signing_certificate.nil?
187
- end
188
-
189
- def validate!
190
- if self_signed?
191
- if @signing_key.nil?
192
- raise InvalidRequest, "No signing_key given. Cannot sign key."
193
- end
194
- elsif @signing_certificate.nil? && @signing_key
195
- raise InvalidRequest, "signing_key given, but no signing_certificate is set"
196
- elsif @signing_certificate && @signing_key.nil?
197
- raise InvalidRequest, "signing_certificate given, but no signing_key is set"
198
- end
199
- end
200
-
201
- def create
202
- validate!
203
- extensions = OpenSSL::X509::ExtensionFactory.new
204
- extensions.subject_certificate = certificate
205
- extensions.issuer_certificate = self_signed? ? certificate : signing_certificate
206
-
207
- certificate.issuer = extensions.issuer_certificate.subject
208
- certificate.add_extension(extensions.create_extension("subjectKeyIdentifier", "hash", false))
209
-
210
- # RFC 5280 4.2.1.1. Authority Key Identifier
211
- # This is "who signed this key"
212
- certificate.add_extension(extensions.create_extension("authorityKeyIdentifier", "keyid:always", false))
213
- #certificate.add_extension(extensions.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always", false))
214
-
215
- if want_signature_ability?
216
- # Create a CA.
217
- certificate.add_extension(extensions.create_extension("basicConstraints", "CA:TRUE", true))
218
- # Rough googling seems to indicate at least keyCertSign is required for CA and intermediate certs.
219
- certificate.add_extension(extensions.create_extension("keyUsage", "keyCertSign, cRLSign, digitalSignature", true))
220
- else
221
- # Create a client+server certificate
222
- #
223
- # It feels weird to create a certificate that's valid as both server and client, but a brief inspection of major
224
- # web properties (apple.com, google.com, yahoo.com, github.com, fastly.com, mozilla.com, amazon.com) reveals that
225
- # major web properties have certificates with both clientAuth and serverAuth extended key usages. Further,
226
- # these major server certificates all have digitalSignature and keyEncipherment for key usage.
227
- #
228
- # Here's the command I used to check this:
229
- # echo mozilla.com apple.com github.com google.com yahoo.com fastly.com elastic.co amazon.com \
230
- # | xargs -n1 sh -c 'openssl s_client -connect $1:443 \
231
- # | sed -ne "/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p" \
232
- # | openssl x509 -text -noout | sed -ne "/X509v3 extensions/,/Signature Algorithm/p" | sed -e "s/^/$1 /"' - \
233
- # | grep -A2 'Key Usage'
234
- certificate.add_extension(extensions.create_extension("keyUsage", "digitalSignature, keyEncipherment", true))
235
- certificate.add_extension(extensions.create_extension("extendedKeyUsage", "clientAuth, serverAuth", false))
236
- end
237
-
238
- if @subject_alternates
239
- certificate.add_extension(extensions.create_extension("subjectAltName", @subject_alternates.join(",")))
240
- end
241
-
242
- certificate.serial = OpenSSL::BN.new(serial)
243
- certificate.sign(signing_key, digest_method)
244
- certificate
245
- end
246
-
247
- # Set the certificate which is going to be signing this request.
248
- def signing_certificate=(certificate)
249
- raise InvalidData, "signing_certificate must be an OpenSSL::X509::Certificate" unless certificate.is_a?(OpenSSL::X509::Certificate)
250
- @signing_certificate = certificate
251
- end
252
- attr_reader :signing_certificate
253
-
254
- attr_reader :signing_key
255
- def signing_key=(private_key)
256
- raise InvalidData, "signing_key must be an OpenSSL::PKey::PKey (or a subclass)" unless private_key.is_a?(OpenSSL::PKey::PKey)
257
- @signing_key = private_key
258
- end
259
-
260
- def want_signature_ability=(value)
261
- raise InvalidData, "want_signature_ability must be a boolean" unless value == true || value == false
262
- @want_signature_ability = value
263
- end
264
-
265
- def want_signature_ability?
266
- @want_signature_ability == true
267
- end
268
-
269
- attr_reader :digest_method
270
- def digest_method=(value)
271
- raise InvalidData, "digest_method must be a OpenSSL::Digest (or a subclass)" unless value.is_a?(OpenSSL::Digest)
272
- @digest_method = value
273
- end
274
-
275
- attr_reader :serial
276
- def serial=(value)
277
- begin
278
- Integer(value)
279
- rescue
280
- raise InvalidData, "Invalid serial value. Must be a number (or a String containing only nubers)"
281
- end
282
- @serial = value
283
- end
284
-
285
- public(:serial, :serial=)
286
- public(:subject, :subject=)
287
- public(:subject_alternates, :subject_alternates=)
288
- public(:public_key, :public_key=)
289
- public(:start_time, :start_time=)
290
- public(:expire_time, :expire_time=)
291
- public(:digest_method, :digest_method=)
292
- public(:want_signature_ability?, :want_signature_ability=)
293
- public(:signing_key, :signing_key=)
294
- public(:signing_certificate, :signing_certificate=)
295
- public(:create)
296
- end # class CertificateSigningRequest
297
69
  end # Flores::PKI
@@ -0,0 +1,232 @@
1
+ require "flores/namespace"
2
+
3
+ module Flores::PKI
4
+ # A certificate signing request.
5
+ #
6
+ # From here, you can configure a certificate to be created based on your
7
+ # desired configuration.
8
+ #
9
+ # Example making a root CA:
10
+ #
11
+ # key = OpenSSL::PKey::RSA.generate(4096, 65537)
12
+ # csr = Flores::PKI::CertificateSigningRequest.new
13
+ # csr.subject = "OU=Fancy Pants Inc."
14
+ # certificate = csr.create_root(key)
15
+ #
16
+ # Example making an intermediate CA:
17
+ #
18
+ # root_key = OpenSSL::PKey::RSA.generate(4096, 65537)
19
+ # root_csr = Flores::PKI::CertificateSigningRequest.new
20
+ # root_csr.subject = "OU=Fancy Pants Inc."
21
+ # root_csr.public_key = root_key.public
22
+ # root_certificate = csr.create_root(root_key)
23
+ #
24
+ # intermediate_key = OpenSSL::PKey::RSA.generate(4096, 65537)
25
+ # intermediate_csr = Flores::PKI::CertificateSigningRequest.new
26
+ # intermediate_csr.public_key = intermediate_key.public
27
+ # intermediate_csr.subject = "OU=Fancy Pants Inc. Intermediate 1"
28
+ # intermediate_certificate = csr.create_intermediate(root_certificate, root_key)
29
+ class CertificateSigningRequest
30
+ # raised when an invalid signing configuration is given
31
+ class InvalidRequest < StandardError; end
32
+
33
+ # raised when invalid data is present in a certificate request
34
+ class InvalidData < StandardError; end
35
+
36
+ # raised when an invalid subject (format, or whatever) is given in a certificate request
37
+ class InvalidSubject < InvalidData; end
38
+
39
+ # raised when an invalid time value is given for a certificate request
40
+ class InvalidTime < InvalidData; end
41
+
42
+ def initialize
43
+ self.serial = Flores::PKI.random_serial
44
+ self.digest_method = default_digest_method
45
+ end
46
+
47
+ private
48
+
49
+ def validate_subject(value)
50
+ OpenSSL::X509::Name.parse(value)
51
+ rescue OpenSSL::X509::NameError => e
52
+ raise InvalidSubject, "Invalid subject '#{value}'. (#{e})"
53
+ rescue TypeError => e
54
+ # Bug(?) in MRI 2.1.6(?)
55
+ raise InvalidSubject, "Invalid subject '#{value}'. (#{e})"
56
+ end
57
+
58
+ def subject=(value)
59
+ @subject = validate_subject(value)
60
+ end
61
+
62
+ attr_reader :subject
63
+
64
+ def subject_alternates=(values)
65
+ @subject_alternates = values
66
+ end
67
+
68
+ attr_reader :subject_alternates
69
+
70
+ def public_key=(value)
71
+ @public_key = validate_public_key(value)
72
+ end
73
+
74
+ def validate_public_key(value)
75
+ raise InvalidData, "public key must be a OpenSSL::PKey::PKey" unless value.is_a? OpenSSL::PKey::PKey
76
+ value
77
+ end
78
+
79
+ attr_reader :public_key
80
+
81
+ def start_time=(value)
82
+ @start_time = validate_time(value)
83
+ end
84
+
85
+ attr_reader :start_time
86
+
87
+ def expire_time=(value)
88
+ @expire_time = validate_time(value)
89
+ end
90
+
91
+ attr_reader :expire_time
92
+
93
+ def validate_time(value)
94
+ raise InvalidTime, "#{value.inspect} (class #{value.class.name})" unless value.is_a?(Time)
95
+ value
96
+ end
97
+
98
+ def certificate
99
+ return @certificate if @certificate
100
+ @certificate = OpenSSL::X509::Certificate.new
101
+
102
+ # RFC5280
103
+ # > 4.1.2.1. Version
104
+ # > version MUST be 3 (value is 2).
105
+ #
106
+ # Version value of '2' means a v3 certificate.
107
+ @certificate.version = 2
108
+
109
+ @certificate.subject = subject
110
+ @certificate.not_before = start_time
111
+ @certificate.not_after = expire_time
112
+ @certificate.public_key = public_key
113
+ @certificate
114
+ end
115
+
116
+ def default_digest_method
117
+ OpenSSL::Digest::SHA256.new
118
+ end
119
+
120
+ def self_signed?
121
+ @signing_certificate.nil?
122
+ end
123
+
124
+ def validate!
125
+ if self_signed?
126
+ if @signing_key.nil?
127
+ raise InvalidRequest, "No signing_key given. Cannot sign key."
128
+ end
129
+ elsif @signing_certificate.nil? && @signing_key
130
+ raise InvalidRequest, "signing_key given, but no signing_certificate is set"
131
+ elsif @signing_certificate && @signing_key.nil?
132
+ raise InvalidRequest, "signing_certificate given, but no signing_key is set"
133
+ end
134
+ end
135
+
136
+ def create
137
+ validate!
138
+ extensions = OpenSSL::X509::ExtensionFactory.new
139
+ extensions.subject_certificate = certificate
140
+ extensions.issuer_certificate = self_signed? ? certificate : signing_certificate
141
+
142
+ certificate.issuer = extensions.issuer_certificate.subject
143
+ certificate.add_extension(extensions.create_extension("subjectKeyIdentifier", "hash", false))
144
+
145
+ # RFC 5280 4.2.1.1. Authority Key Identifier
146
+ # This is "who signed this key"
147
+ certificate.add_extension(extensions.create_extension("authorityKeyIdentifier", "keyid:always", false))
148
+ #certificate.add_extension(extensions.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always", false))
149
+
150
+ if want_signature_ability?
151
+ # Create a CA.
152
+ certificate.add_extension(extensions.create_extension("basicConstraints", "CA:TRUE", true))
153
+ # Rough googling seems to indicate at least keyCertSign is required for CA and intermediate certs.
154
+ certificate.add_extension(extensions.create_extension("keyUsage", "keyCertSign, cRLSign, digitalSignature", true))
155
+ else
156
+ # Create a client+server certificate
157
+ #
158
+ # It feels weird to create a certificate that's valid as both server and client, but a brief inspection of major
159
+ # web properties (apple.com, google.com, yahoo.com, github.com, fastly.com, mozilla.com, amazon.com) reveals that
160
+ # major web properties have certificates with both clientAuth and serverAuth extended key usages. Further,
161
+ # these major server certificates all have digitalSignature and keyEncipherment for key usage.
162
+ #
163
+ # Here's the command I used to check this:
164
+ # echo mozilla.com apple.com github.com google.com yahoo.com fastly.com elastic.co amazon.com \
165
+ # | xargs -n1 sh -c 'openssl s_client -connect $1:443 \
166
+ # | sed -ne "/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p" \
167
+ # | openssl x509 -text -noout | sed -ne "/X509v3 extensions/,/Signature Algorithm/p" | sed -e "s/^/$1 /"' - \
168
+ # | grep -A2 'Key Usage'
169
+ certificate.add_extension(extensions.create_extension("keyUsage", "digitalSignature, keyEncipherment", true))
170
+ certificate.add_extension(extensions.create_extension("extendedKeyUsage", "clientAuth, serverAuth", false))
171
+ end
172
+
173
+ if @subject_alternates
174
+ certificate.add_extension(extensions.create_extension("subjectAltName", @subject_alternates.join(",")))
175
+ end
176
+
177
+ certificate.serial = OpenSSL::BN.new(serial)
178
+ certificate.sign(signing_key, digest_method)
179
+ certificate
180
+ end
181
+
182
+ # Set the certificate which is going to be signing this request.
183
+ def signing_certificate=(certificate)
184
+ raise InvalidData, "signing_certificate must be an OpenSSL::X509::Certificate" unless certificate.is_a?(OpenSSL::X509::Certificate)
185
+ @signing_certificate = certificate
186
+ end
187
+ attr_reader :signing_certificate
188
+
189
+ attr_reader :signing_key
190
+ def signing_key=(private_key)
191
+ raise InvalidData, "signing_key must be an OpenSSL::PKey::PKey (or a subclass)" unless private_key.is_a?(OpenSSL::PKey::PKey)
192
+ @signing_key = private_key
193
+ end
194
+
195
+ def want_signature_ability=(value)
196
+ raise InvalidData, "want_signature_ability must be a boolean" unless value == true || value == false
197
+ @want_signature_ability = value
198
+ end
199
+
200
+ def want_signature_ability?
201
+ @want_signature_ability == true
202
+ end
203
+
204
+ attr_reader :digest_method
205
+ def digest_method=(value)
206
+ raise InvalidData, "digest_method must be a OpenSSL::Digest (or a subclass)" unless value.is_a?(OpenSSL::Digest)
207
+ @digest_method = value
208
+ end
209
+
210
+ attr_reader :serial
211
+ def serial=(value)
212
+ begin
213
+ Integer(value)
214
+ rescue
215
+ raise InvalidData, "Invalid serial value. Must be a number (or a String containing only nubers)"
216
+ end
217
+ @serial = value
218
+ end
219
+
220
+ public(:serial, :serial=)
221
+ public(:subject, :subject=)
222
+ public(:subject_alternates, :subject_alternates=)
223
+ public(:public_key, :public_key=)
224
+ public(:start_time, :start_time=)
225
+ public(:expire_time, :expire_time=)
226
+ public(:digest_method, :digest_method=)
227
+ public(:want_signature_ability?, :want_signature_ability=)
228
+ public(:signing_key, :signing_key=)
229
+ public(:signing_certificate, :signing_certificate=)
230
+ public(:create)
231
+ end # class CertificateSigningRequest
232
+ end
@@ -160,7 +160,7 @@ module Flores::Random
160
160
  IPV6_SEGMENT = 1 << 16
161
161
 
162
162
  def self.ipv6_pack(length)
163
- length.times.collect { integer(0..IPV6_SEGMENT).to_s(16) }.join(":")
163
+ length.times.collect { integer(0...IPV6_SEGMENT).to_s(16) }.join(":")
164
164
  end
165
165
 
166
166
  def self.ipv6_abbreviation(length)
@@ -143,11 +143,28 @@ module Flores::RSpec::Analyze
143
143
  sample = instances.sample(1)
144
144
  [
145
145
  " #{percent_s(instances.length)} -> [#{instances.length}] #{error}",
146
- " Sample exception for #{sample.first[0]}",
147
- sample.first[1].to_s.gsub(/^/, " ")
146
+ " Sample failure",
147
+ " Inputs:",
148
+ *render_values(sample.first[0]).map { |x| " #{x}" },
149
+ " Exception:",
150
+ sample.first[1].to_s.gsub(/^/, " ")
148
151
  ]
149
152
  end # def error_summary
150
153
 
154
+ def render_values(values)
155
+ # values should be an RSpec::Core::MemoizedHelpers::ThreadsafeMemoized
156
+ lets = values.instance_eval { @memoized }
157
+ return ["<nothing>"] if lets.nil?
158
+
159
+ lets.sort_by { |k,v| v.to_s.size }.map do |k,v|
160
+ if v.to_s.size > 50
161
+ v = v.to_s[0, 50] + "..."
162
+ end
163
+ "#{k}=#{v}"
164
+ end
165
+ end
166
+
167
+
151
168
  def error_sample_states(error, instances)
152
169
  [
153
170
  " Samples causing #{error}:",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flores
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Sissel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-31 00:00:00.000000000 Z
11
+ date: 2016-11-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |2
14
14
  Add fuzzing, randomization, and stress to your tests.
@@ -36,6 +36,7 @@ files:
36
36
  - flores.gemspec
37
37
  - lib/flores/namespace.rb
38
38
  - lib/flores/pki.rb
39
+ - lib/flores/pki/csr.rb
39
40
  - lib/flores/random.rb
40
41
  - lib/flores/rspec.rb
41
42
  - lib/flores/rspec/analyze.rb
@@ -67,8 +68,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
68
  version: '0'
68
69
  requirements: []
69
70
  rubyforge_project:
70
- rubygems_version: 2.4.8
71
+ rubygems_version: 2.5.1
71
72
  signing_key:
72
73
  specification_version: 4
73
74
  summary: Fuzz, randomize, and stress your tests
74
75
  test_files: []
76
+ has_rdoc: