aws4 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +16 -0
  3. data/Rakefile +9 -0
  4. data/aws4.gemspec +18 -0
  5. data/lib/aws4.rb +3 -0
  6. data/lib/aws4/signer.rb +104 -0
  7. data/lib/aws4/version.rb +3 -0
  8. data/readme.md +27 -0
  9. data/test/benchmark_test.rb +31 -0
  10. data/test/signer_test.rb +256 -0
  11. data/test/suite/get-header-key-duplicate.authz +1 -0
  12. data/test/suite/get-header-key-duplicate.creq +9 -0
  13. data/test/suite/get-header-key-duplicate.req +7 -0
  14. data/test/suite/get-header-key-duplicate.sreq +8 -0
  15. data/test/suite/get-header-key-duplicate.sts +4 -0
  16. data/test/suite/get-header-value-multiline.req +7 -0
  17. data/test/suite/get-header-value-order.authz +1 -0
  18. data/test/suite/get-header-value-order.creq +9 -0
  19. data/test/suite/get-header-value-order.req +8 -0
  20. data/test/suite/get-header-value-order.sreq +9 -0
  21. data/test/suite/get-header-value-order.sts +4 -0
  22. data/test/suite/get-header-value-trim.authz +1 -0
  23. data/test/suite/get-header-value-trim.creq +9 -0
  24. data/test/suite/get-header-value-trim.req +5 -0
  25. data/test/suite/get-header-value-trim.sreq +6 -0
  26. data/test/suite/get-header-value-trim.sts +4 -0
  27. data/test/suite/get-relative-relative.authz +1 -0
  28. data/test/suite/get-relative-relative.creq +8 -0
  29. data/test/suite/get-relative-relative.req +4 -0
  30. data/test/suite/get-relative-relative.sreq +5 -0
  31. data/test/suite/get-relative-relative.sts +4 -0
  32. data/test/suite/get-relative.authz +1 -0
  33. data/test/suite/get-relative.creq +8 -0
  34. data/test/suite/get-relative.req +4 -0
  35. data/test/suite/get-relative.sreq +5 -0
  36. data/test/suite/get-relative.sts +4 -0
  37. data/test/suite/get-slash-dot-slash.authz +1 -0
  38. data/test/suite/get-slash-dot-slash.creq +8 -0
  39. data/test/suite/get-slash-dot-slash.req +4 -0
  40. data/test/suite/get-slash-dot-slash.sreq +5 -0
  41. data/test/suite/get-slash-dot-slash.sts +4 -0
  42. data/test/suite/get-slash-pointless-dot.authz +1 -0
  43. data/test/suite/get-slash-pointless-dot.creq +8 -0
  44. data/test/suite/get-slash-pointless-dot.req +4 -0
  45. data/test/suite/get-slash-pointless-dot.sreq +5 -0
  46. data/test/suite/get-slash-pointless-dot.sts +4 -0
  47. data/test/suite/get-slash.authz +1 -0
  48. data/test/suite/get-slash.creq +8 -0
  49. data/test/suite/get-slash.req +4 -0
  50. data/test/suite/get-slash.sreq +5 -0
  51. data/test/suite/get-slash.sts +4 -0
  52. data/test/suite/get-slashes.authz +1 -0
  53. data/test/suite/get-slashes.creq +8 -0
  54. data/test/suite/get-slashes.req +4 -0
  55. data/test/suite/get-slashes.sreq +5 -0
  56. data/test/suite/get-slashes.sts +4 -0
  57. data/test/suite/get-space.authz +1 -0
  58. data/test/suite/get-space.creq +8 -0
  59. data/test/suite/get-space.req +4 -0
  60. data/test/suite/get-space.sreq +5 -0
  61. data/test/suite/get-space.sts +4 -0
  62. data/test/suite/get-unreserved.authz +1 -0
  63. data/test/suite/get-unreserved.creq +8 -0
  64. data/test/suite/get-unreserved.req +4 -0
  65. data/test/suite/get-unreserved.sreq +5 -0
  66. data/test/suite/get-unreserved.sts +4 -0
  67. data/test/suite/get-utf8.authz +1 -0
  68. data/test/suite/get-utf8.creq +8 -0
  69. data/test/suite/get-utf8.req +4 -0
  70. data/test/suite/get-utf8.sreq +5 -0
  71. data/test/suite/get-utf8.sts +4 -0
  72. data/test/suite/get-vanilla-empty-query-key.authz +1 -0
  73. data/test/suite/get-vanilla-empty-query-key.creq +8 -0
  74. data/test/suite/get-vanilla-empty-query-key.req +4 -0
  75. data/test/suite/get-vanilla-empty-query-key.sreq +5 -0
  76. data/test/suite/get-vanilla-empty-query-key.sts +4 -0
  77. data/test/suite/get-vanilla-query-order-key-case.authz +1 -0
  78. data/test/suite/get-vanilla-query-order-key-case.creq +8 -0
  79. data/test/suite/get-vanilla-query-order-key-case.req +4 -0
  80. data/test/suite/get-vanilla-query-order-key-case.sreq +5 -0
  81. data/test/suite/get-vanilla-query-order-key-case.sts +4 -0
  82. data/test/suite/get-vanilla-query-order-key.authz +1 -0
  83. data/test/suite/get-vanilla-query-order-key.creq +8 -0
  84. data/test/suite/get-vanilla-query-order-key.req +4 -0
  85. data/test/suite/get-vanilla-query-order-key.sreq +5 -0
  86. data/test/suite/get-vanilla-query-order-key.sts +4 -0
  87. data/test/suite/get-vanilla-query-order-value.authz +1 -0
  88. data/test/suite/get-vanilla-query-order-value.creq +8 -0
  89. data/test/suite/get-vanilla-query-order-value.req +4 -0
  90. data/test/suite/get-vanilla-query-order-value.sreq +5 -0
  91. data/test/suite/get-vanilla-query-order-value.sts +4 -0
  92. data/test/suite/get-vanilla-query-unreserved.authz +1 -0
  93. data/test/suite/get-vanilla-query-unreserved.creq +8 -0
  94. data/test/suite/get-vanilla-query-unreserved.req +4 -0
  95. data/test/suite/get-vanilla-query-unreserved.sreq +5 -0
  96. data/test/suite/get-vanilla-query-unreserved.sts +4 -0
  97. data/test/suite/get-vanilla-query.authz +1 -0
  98. data/test/suite/get-vanilla-query.creq +8 -0
  99. data/test/suite/get-vanilla-query.req +4 -0
  100. data/test/suite/get-vanilla-query.sreq +5 -0
  101. data/test/suite/get-vanilla-query.sts +4 -0
  102. data/test/suite/get-vanilla-ut8-query.authz +1 -0
  103. data/test/suite/get-vanilla-ut8-query.creq +8 -0
  104. data/test/suite/get-vanilla-ut8-query.req +4 -0
  105. data/test/suite/get-vanilla-ut8-query.sreq +5 -0
  106. data/test/suite/get-vanilla-ut8-query.sts +4 -0
  107. data/test/suite/get-vanilla.authz +1 -0
  108. data/test/suite/get-vanilla.creq +8 -0
  109. data/test/suite/get-vanilla.req +4 -0
  110. data/test/suite/get-vanilla.sreq +5 -0
  111. data/test/suite/get-vanilla.sts +4 -0
  112. data/test/suite/post-header-key-case.authz +1 -0
  113. data/test/suite/post-header-key-case.creq +8 -0
  114. data/test/suite/post-header-key-case.req +4 -0
  115. data/test/suite/post-header-key-case.sreq +5 -0
  116. data/test/suite/post-header-key-case.sts +4 -0
  117. data/test/suite/post-header-key-sort.authz +1 -0
  118. data/test/suite/post-header-key-sort.creq +9 -0
  119. data/test/suite/post-header-key-sort.req +5 -0
  120. data/test/suite/post-header-key-sort.sreq +6 -0
  121. data/test/suite/post-header-key-sort.sts +4 -0
  122. data/test/suite/post-header-value-case.authz +1 -0
  123. data/test/suite/post-header-value-case.creq +9 -0
  124. data/test/suite/post-header-value-case.req +5 -0
  125. data/test/suite/post-header-value-case.sreq +6 -0
  126. data/test/suite/post-header-value-case.sts +4 -0
  127. data/test/suite/post-vanilla-empty-query-value.authz +1 -0
  128. data/test/suite/post-vanilla-empty-query-value.creq +8 -0
  129. data/test/suite/post-vanilla-empty-query-value.req +4 -0
  130. data/test/suite/post-vanilla-empty-query-value.sreq +5 -0
  131. data/test/suite/post-vanilla-empty-query-value.sts +4 -0
  132. data/test/suite/post-vanilla-query-nonunreserved.authz +1 -0
  133. data/test/suite/post-vanilla-query-nonunreserved.creq +8 -0
  134. data/test/suite/post-vanilla-query-nonunreserved.req +4 -0
  135. data/test/suite/post-vanilla-query-nonunreserved.sreq +5 -0
  136. data/test/suite/post-vanilla-query-nonunreserved.sts +4 -0
  137. data/test/suite/post-vanilla-query-space.authz +1 -0
  138. data/test/suite/post-vanilla-query-space.creq +8 -0
  139. data/test/suite/post-vanilla-query-space.req +4 -0
  140. data/test/suite/post-vanilla-query-space.sreq +5 -0
  141. data/test/suite/post-vanilla-query-space.sts +4 -0
  142. data/test/suite/post-vanilla-query.authz +1 -0
  143. data/test/suite/post-vanilla-query.creq +8 -0
  144. data/test/suite/post-vanilla-query.req +4 -0
  145. data/test/suite/post-vanilla-query.sreq +5 -0
  146. data/test/suite/post-vanilla-query.sts +4 -0
  147. data/test/suite/post-vanilla.authz +1 -0
  148. data/test/suite/post-vanilla.creq +8 -0
  149. data/test/suite/post-vanilla.req +4 -0
  150. data/test/suite/post-vanilla.sreq +5 -0
  151. data/test/suite/post-vanilla.sts +4 -0
  152. data/test/suite/post-x-www-form-urlencoded-parameters.authz +1 -0
  153. data/test/suite/post-x-www-form-urlencoded-parameters.creq +9 -0
  154. data/test/suite/post-x-www-form-urlencoded-parameters.req +6 -0
  155. data/test/suite/post-x-www-form-urlencoded-parameters.sreq +7 -0
  156. data/test/suite/post-x-www-form-urlencoded-parameters.sts +4 -0
  157. data/test/suite/post-x-www-form-urlencoded.authz +1 -0
  158. data/test/suite/post-x-www-form-urlencoded.creq +9 -0
  159. data/test/suite/post-x-www-form-urlencoded.req +6 -0
  160. data/test/suite/post-x-www-form-urlencoded.sreq +7 -0
  161. data/test/suite/post-x-www-form-urlencoded.sts +4 -0
  162. metadata +223 -0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ aws4 (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ rake (10.0.4)
10
+
11
+ PLATFORMS
12
+ ruby
13
+
14
+ DEPENDENCIES
15
+ aws4!
16
+ rake
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs.push "lib"
5
+ t.test_files = FileList['test/*_test.rb']
6
+ t.verbose = true
7
+ end
8
+
9
+ task :default => :test
@@ -0,0 +1,18 @@
1
+ require "./lib/aws4/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'aws4'
5
+ s.version = AWS4::VERSION
6
+ s.summary = "A ruby gem for AWS Signature version 4"
7
+ s.description = "The approach is HTTP library agnostic, so you must supply method, uri, headers, and body"
8
+ s.authors = ["Brandon Keene"]
9
+ s.email = ["bkeene@gmail.com"]
10
+ s.require_path = 'lib'
11
+ s.files = `git ls-files`.split("\n")
12
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
13
+ s.executables = []
14
+ s.homepage = 'http://github.com/cmdrkeene/aws4'
15
+
16
+ s.add_development_dependency "rake"
17
+ end
18
+
@@ -0,0 +1,3 @@
1
+ # AWS4
2
+ require "aws4/version"
3
+ require "aws4/signer"
@@ -0,0 +1,104 @@
1
+ # encoding: UTF-8
2
+ require "openssl"
3
+ require "time"
4
+ require "uri"
5
+ require "pathname"
6
+
7
+ module AWS4
8
+ class Signer
9
+ attr_reader :access_key, :secret_key, :region
10
+ attr_reader :date, :method, :uri, :headers, :body
11
+
12
+ def initialize(config)
13
+ @access_key = config[:access_key] || config["access_key"]
14
+ @secret_key = config[:secret_key] || config["secret_key"]
15
+ @region = config[:region] || config["region"]
16
+ end
17
+
18
+ def sign(method, uri, headers, body, debug = false)
19
+ @method = method.upcase
20
+ @uri = uri
21
+ @headers = headers
22
+ @body = body
23
+ @date = Time.parse(headers["Date"] || headers["DATE"] || headers["date"]).utc.strftime("%Y%m%dT%H%M%SZ")
24
+ dump if debug
25
+ signed = headers.dup
26
+ signed['Authorization'] = authorization(headers)
27
+ signed
28
+ end
29
+
30
+ private
31
+
32
+ def service
33
+ @uri.host.split(".", 2)[0]
34
+ end
35
+
36
+ def authorization(headers)
37
+ parts = []
38
+ parts << "AWS4-HMAC-SHA256 Credential=#{access_key}/#{credential_string}"
39
+ parts << "SignedHeaders=#{headers.keys.map(&:downcase).sort.join(";")}"
40
+ parts << "Signature=#{signature}"
41
+ parts.join(', ')
42
+ end
43
+
44
+ def signature
45
+ k_secret = secret_key
46
+ k_date = hmac("AWS4" + k_secret, date[0,8])
47
+ k_region = hmac(k_date, region)
48
+ k_service = hmac(k_region, service)
49
+ k_credentials = hmac(k_service, 'aws4_request')
50
+ hexhmac(k_credentials, string_to_sign)
51
+ end
52
+
53
+ def string_to_sign
54
+ parts = []
55
+ parts << 'AWS4-HMAC-SHA256'
56
+ parts << date
57
+ parts << credential_string
58
+ parts << hexdigest(canonical_request)
59
+ parts.join("\n")
60
+ end
61
+
62
+ def credential_string
63
+ parts = []
64
+ parts << date[0,8]
65
+ parts << region
66
+ parts << service
67
+ parts << 'aws4_request'
68
+ parts.join("/")
69
+ end
70
+
71
+ def canonical_request
72
+ parts = []
73
+ parts << method
74
+ parts << Pathname.new(uri.path).cleanpath.to_s
75
+ parts << uri.query
76
+ parts << headers.sort.map {|k, v| [k.downcase,v.strip].join(':')}.join("\n") + "\n"
77
+ parts << headers.sort.map {|k, v| k.downcase}.join(";")
78
+ parts << hexdigest(body || '')
79
+ parts.join("\n")
80
+ end
81
+
82
+ def hexdigest(value)
83
+ digest = Digest::SHA256.new
84
+ digest.update(value)
85
+ digest.hexdigest
86
+ end
87
+
88
+ def hmac(key, value)
89
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, value)
90
+ end
91
+
92
+ def hexhmac(key, value)
93
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha256'), key, value)
94
+ end
95
+
96
+ def dump
97
+ puts "string to sign"
98
+ puts string_to_sign
99
+ puts "canonical_request"
100
+ puts canonical_request
101
+ puts "authorization"
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,3 @@
1
+ module AWS4
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ This gem signs HTTP headers with the AWS4 signature for use with Amazon’s AWS APIs.
2
+
3
+ You MUST supply a `Date` header.
4
+
5
+ ## Usage
6
+
7
+ # create a signer
8
+ signer = AWS4::Signer.new(
9
+ access_key: "key",
10
+ secret_key: "secret",
11
+ region: "us-east-1",
12
+ host: "dynamodb.us-east-1.amazonaws.com"
13
+ )
14
+
15
+ # build request
16
+ uri = URI("https://dynamodb.us-east-1.amazonaws.com/")
17
+ headers = {
18
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
19
+ "Content-Type" => "application/json; charset=utf8"
20
+ }
21
+ body="{}"
22
+
23
+ # sign headers
24
+ headers = signer.sign("POST", uri, headers, body)
25
+
26
+ # send request using library of choice
27
+
@@ -0,0 +1,31 @@
1
+ # encoding: UTF-8
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
4
+ require 'aws4/signer'
5
+ require "benchmark"
6
+
7
+ describe "benchmark" do
8
+ it "runs quickly" do
9
+ trials = 10_000
10
+ signer = AWS4::Signer.new(
11
+ access_key: "AKIDEXAMPLE",
12
+ secret_key: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
13
+ region: "us-east-1",
14
+ host: "host.foo.com"
15
+ )
16
+ uri = URI("http://host.foo.com/")
17
+ headers = {
18
+ "Host" => "host.foo.com",
19
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
20
+ "Content-Type" => "application/x-www-form-urlencoded; charset=utf8"
21
+ }
22
+ body = "foo=bar"
23
+
24
+ t = Benchmark.realtime do
25
+ trials.times do
26
+ signed = signer.sign("POST", uri, headers, body)
27
+ end
28
+ end
29
+ puts "#{(trials/t).round(1)} signatures/second (#{((t/trials.to_f)*1000).round(3)}ms/signature)"
30
+ end
31
+ end
@@ -0,0 +1,256 @@
1
+ # encoding: UTF-8
2
+ require 'minitest/spec'
3
+ require 'minitest/autorun'
4
+ require 'aws4/signer'
5
+
6
+ describe AWS4::Signer do
7
+ def signer
8
+ @signer ||= AWS4::Signer.new(
9
+ access_key: "AKIDEXAMPLE",
10
+ secret_key: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
11
+ region: "us-east-1",
12
+ host: "host.foo.com"
13
+ )
14
+ end
15
+
16
+ it "signs get-vanilla" do
17
+ uri = URI("http://host.foo.com/")
18
+ headers = {
19
+ "Host" => "host.foo.com",
20
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
21
+ }
22
+ body = ""
23
+ signed = signer.sign("GET", uri, headers, body)
24
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
25
+ signed["Host"].must_equal("host.foo.com")
26
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470")
27
+ end
28
+
29
+ it "signs get-relative" do
30
+ uri = URI("http://host.foo.com/foo/..")
31
+ headers = {
32
+ "Host" => "host.foo.com",
33
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
34
+ }
35
+ body = ""
36
+ signed = signer.sign("GET", uri, headers, body)
37
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
38
+ signed["Host"].must_equal("host.foo.com")
39
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470")
40
+ end
41
+
42
+ it "signs get-relative-relative" do
43
+ uri = URI("http://host.foo.com/foo/bar/../..")
44
+ headers = {
45
+ "Host" => "host.foo.com",
46
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
47
+ }
48
+ body = ""
49
+ signed = signer.sign("GET", uri, headers, body)
50
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
51
+ signed["Host"].must_equal("host.foo.com")
52
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470")
53
+ end
54
+
55
+ it "signs get-slash-pointless-dot" do
56
+ uri = URI("http://host.foo.com/./foo")
57
+ headers = {
58
+ "Host" => "host.foo.com",
59
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
60
+ }
61
+ body = ""
62
+ signed = signer.sign("GET", uri, headers, body)
63
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
64
+ signed["Host"].must_equal("host.foo.com")
65
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a")
66
+ end
67
+
68
+ it "signs get-slash" do
69
+ uri = URI("http://host.foo.com//")
70
+ headers = {
71
+ "Host" => "host.foo.com",
72
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
73
+ }
74
+ body = ""
75
+ signed = signer.sign("GET", uri, headers, body)
76
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
77
+ signed["Host"].must_equal("host.foo.com")
78
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470")
79
+ end
80
+
81
+ it "signs get-vanilla-empty-query" do
82
+ uri = URI("http://host.foo.com/?foo=bar")
83
+ headers = {
84
+ "Host" => "host.foo.com",
85
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
86
+ }
87
+ body = ""
88
+ signed = signer.sign("GET", uri, headers, body)
89
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
90
+ signed["Host"].must_equal("host.foo.com")
91
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=56c054473fd260c13e4e7393eb203662195f5d4a1fada5314b8b52b23f985e9f")
92
+ end
93
+
94
+ it "signs get-unreserved" do
95
+ uri = URI("http://host.foo.com/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
96
+ headers = {
97
+ "Host" => "host.foo.com",
98
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
99
+ }
100
+ body = ""
101
+ signed = signer.sign("GET", uri, headers, body)
102
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
103
+ signed["Host"].must_equal("host.foo.com")
104
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=830cc36d03f0f84e6ee4953fbe701c1c8b71a0372c63af9255aa364dd183281e")
105
+ end
106
+
107
+ it "signs get-header-value-trim" do
108
+ uri = URI("http://host.foo.com/")
109
+ headers = {
110
+ "Host" => "host.foo.com",
111
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
112
+ "p" => " phfft "
113
+ }
114
+ body = ""
115
+ signed = signer.sign("POST", uri, headers, body)
116
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
117
+ signed["Host"].must_equal("host.foo.com")
118
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592")
119
+ end
120
+
121
+ it "signs get-vanilla-query-order" do
122
+ uri = URI("http://host.foo.com/?foo=Zoo&foo=aha")
123
+ headers = {
124
+ "Host" => "host.foo.com",
125
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
126
+ }
127
+ body = ""
128
+ signed = signer.sign("GET", uri, headers, body)
129
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
130
+ signed["Host"].must_equal("host.foo.com")
131
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09")
132
+ end
133
+
134
+ it "signs get-vanilla-query-order-key" do
135
+ uri = URI("http://host.foo.com/?a=foo&b=foo")
136
+ headers = {
137
+ "Host" => "host.foo.com",
138
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
139
+ }
140
+ body = ""
141
+ signed = signer.sign("GET", uri, headers, body)
142
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
143
+ signed["Host"].must_equal("host.foo.com")
144
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b")
145
+ end
146
+
147
+ it "signs get-vanilla-utf8-query" do
148
+ uri = URI(URI::encode("http://host.foo.com/?ሴ=bar"))
149
+ headers = {
150
+ "Host" => "host.foo.com",
151
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
152
+ }
153
+ body = ""
154
+ signed = signer.sign("GET", uri, headers, body)
155
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
156
+ signed["Host"].must_equal("host.foo.com")
157
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c")
158
+ end
159
+
160
+ it "signs post-vanilla" do
161
+ uri = URI("http://host.foo.com/")
162
+ headers = {
163
+ "Host" => "host.foo.com",
164
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
165
+ }
166
+ body = ""
167
+ signed = signer.sign("POST", uri, headers, body)
168
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
169
+ signed["Host"].must_equal("host.foo.com")
170
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726")
171
+ end
172
+
173
+ it "signs post-vanilla-empty-query-value" do
174
+ uri = URI("http://host.foo.com/?foo=bar")
175
+ headers = {
176
+ "Host" => "host.foo.com",
177
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT"
178
+ }
179
+ body = ""
180
+ signed = signer.sign("POST", uri, headers, body)
181
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
182
+ signed["Host"].must_equal("host.foo.com")
183
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92")
184
+ end
185
+
186
+ it "signs post-header-key-sort" do
187
+ uri = URI("http://host.foo.com/")
188
+ headers = {
189
+ "Host" => "host.foo.com",
190
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
191
+ "ZOO" => "zoobar"
192
+ }
193
+ body = ""
194
+ signed = signer.sign("POST", uri, headers, body)
195
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
196
+ signed["Host"].must_equal("host.foo.com")
197
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a")
198
+ end
199
+
200
+ it "signs post-header-key-case" do
201
+ uri = URI("http://host.foo.com/")
202
+ headers = {
203
+ "Host" => "host.foo.com",
204
+ "DATE" => "Mon, 09 Sep 2011 23:36:00 GMT"
205
+ }
206
+ body = ""
207
+ signed = signer.sign("POST", uri, headers, body)
208
+ signed["DATE"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
209
+ signed["Host"].must_equal("host.foo.com")
210
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=22902d79e148b64e7571c3565769328423fe276eae4b26f83afceda9e767f726")
211
+ end
212
+
213
+ it "signs post-header-value-case" do
214
+ uri = URI("http://host.foo.com/")
215
+ headers = {
216
+ "Host" => "host.foo.com",
217
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
218
+ "zoo" => "ZOOBAR"
219
+ }
220
+ body = ""
221
+ signed = signer.sign("POST", uri, headers, body)
222
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
223
+ signed["Host"].must_equal("host.foo.com")
224
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7")
225
+ end
226
+
227
+ it "signs post-x-www-form-urlencoded" do
228
+ uri = URI("http://host.foo.com/")
229
+ headers = {
230
+ "Host" => "host.foo.com",
231
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
232
+ "Content-Type" => "application/x-www-form-urlencoded"
233
+ }
234
+ body = "foo=bar"
235
+ signed = signer.sign("POST", uri, headers, body)
236
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
237
+ signed["Host"].must_equal("host.foo.com")
238
+ signed["Content-Type"].must_equal("application/x-www-form-urlencoded")
239
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc")
240
+ end
241
+
242
+ it "signs post-x-www-form-urlencoded-parameters" do
243
+ uri = URI("http://host.foo.com/")
244
+ headers = {
245
+ "Host" => "host.foo.com",
246
+ "Date" => "Mon, 09 Sep 2011 23:36:00 GMT",
247
+ "Content-Type" => "application/x-www-form-urlencoded; charset=utf8"
248
+ }
249
+ body = "foo=bar"
250
+ signed = signer.sign("POST", uri, headers, body)
251
+ signed["Date"].must_equal("Mon, 09 Sep 2011 23:36:00 GMT")
252
+ signed["Host"].must_equal("host.foo.com")
253
+ signed["Content-Type"].must_equal("application/x-www-form-urlencoded; charset=utf8")
254
+ signed["Authorization"].must_equal("AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=b105eb10c6d318d2294de9d49dd8b031b55e3c3fe139f2e637da70511e9e7b71")
255
+ end
256
+ end