diff --git a/lib/alexa_ruby/request/base_request/validator.rb b/lib/alexa_ruby/request/base_request/validator.rb index b2a10c9..00d1243 100644 --- a/lib/alexa_ruby/request/base_request/validator.rb +++ b/lib/alexa_ruby/request/base_request/validator.rb @@ -25,7 +25,11 @@ def initialize(cert_chain_url, signature, request, timestamp_diff = nil) # # @return [Boolean] def valid_request? - raise ArgumentError, 'Outdated request' unless timestamp_tolerant? + unless timestamp_tolerant? + raise ArgumentError, + 'Outdated request: request timestamp is more than ' \ + "#{@timestamp_diff} seconds later than current time" + end valid_uri? && valid_certificates? end diff --git a/lib/alexa_ruby/request/base_request/validator/certificates.rb b/lib/alexa_ruby/request/base_request/validator/certificates.rb index 12bcb9a..b5a6a90 100644 --- a/lib/alexa_ruby/request/base_request/validator/certificates.rb +++ b/lib/alexa_ruby/request/base_request/validator/certificates.rb @@ -20,10 +20,7 @@ def initialize(certificates_chain_url, signature, request) # # @return [Boolean] def valid? - raise ArgumentError, 'Inactive Amazon SSL certificate' unless active? - raise ArgumentError, 'Inactive host in SSL certificate' unless amazon? - raise ArgumentError, 'Signature and request mismatch' unless verified? - true + active? && amazon? && verified? end private @@ -42,14 +39,24 @@ def download_certificates(certificates_chain_url) # @return [Boolean] def active? now = Time.now - @cert.not_before < now && @cert.not_after > now + (@cert.not_before < now && @cert.not_after > now) || + raise( + ArgumentError, + 'Amazon SSL certificate is outdated ' \ + "specified dates: #{@cert.not_before} - #{@cert.not_after}" + ) end # Check if Subject Alternative Names includes Amazon domain name # # @return [Boolean] def amazon? - @cert.subject.to_a.flatten.include? 'echo-api.amazon.com' + @cert.subject.to_a.flatten.include?('echo-api.amazon.com') || + raise( + ArgumentError, + 'Certificate must be issued for "echo-api.amazon.com" ' \ + "(given certificate subject: #{@cert.subject.to_a})" + ) end # Check if given signature matches given request @@ -58,7 +65,12 @@ def amazon? def verified? sign = decode_signature pkey = public_key - pkey.verify(hash, sign, @request) + pkey.verify(hash, sign, @request) || + raise( + ArgumentError, + 'Given request signature does not match with request SHA1 hash ' \ + "(signature: #{sign})" + ) end # Decode base64-encoded signature diff --git a/lib/alexa_ruby/request/base_request/validator/uri.rb b/lib/alexa_ruby/request/base_request/validator/uri.rb index b6a4a8d..2d75640 100644 --- a/lib/alexa_ruby/request/base_request/validator/uri.rb +++ b/lib/alexa_ruby/request/base_request/validator/uri.rb @@ -14,11 +14,7 @@ def initialize(uri) # # @return [Boolean] def valid? - raise ArgumentError, 'Certificates chain URL must be HTTPS' unless https? - raise ArgumentError, 'Not Amazon host in certificates URL' unless amazon? - raise ArgumentError, 'Invalid certificates chain URL' unless echo_api? - raise ArgumentError, 'Certificates chain URL must be HTTPS' unless port? - true + https? && amazon? && echo_api? && port? end private @@ -27,28 +23,48 @@ def valid? # # @return [Boolean] def https? - @uri.scheme == 'https' + @uri.scheme == 'https' || + raise( + ArgumentError, + 'Certificates chain URL must be an HTTPS-enabled endpoint ' \ + "(current endpoint: #{@uri})" + ) end # Check if URI host is a valid Amazon host # # @return [Boolean] def amazon? - @uri.host.casecmp('s3.amazonaws.com').zero? + @uri.host.casecmp('s3.amazonaws.com').zero? || + raise( + ArgumentError, + 'Certificates chain host must be equal to "s3.amazonaws.com" ' \ + "(current host: #{@uri.host})" + ) end # Check if URI path starts with /echo.api/ # # @return [Boolean] def echo_api? - @uri.path[0..9] == '/echo.api/' + @uri.path[0..9] == '/echo.api/' || + raise( + ArgumentError, + 'Certificates chain URL path must start with "/echo.api/" ' \ + "(current path: #{@uri.path})" + ) end # Check if URI port is 443 if port is present # # @return [Boolean] def port? - @uri.port.nil? || @uri.port == 443 + @uri.port.nil? || @uri.port == 443 || + raise( + ArgumentError, + 'If certificates chain URL has a port specified, it must be 443 ' \ + "(current port: #{@uri.port})" + ) end end end