Skip to content
This repository was archived by the owner on Apr 20, 2024. It is now read-only.

Commit 2222679

Browse files
committed
Now able to properly sign all responses.
1 parent 7cc1124 commit 2222679

File tree

6 files changed

+128
-56
lines changed

6 files changed

+128
-56
lines changed

Sources/Driver/AWSDriver.swift

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import HTTP
22
import Transport
3+
import Foundation
34

45
class AWSDriver {
56
enum Error: Swift.Error {

Sources/Driver/Authentication/AWSSignatureV4.swift

+91-31
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public enum AccessControlList: String {
1717

1818
public struct AWSSignatureV4 {
1919
public enum Method: String {
20+
case delete = "DELETE"
2021
case get = "GET"
2122
case post = "POST"
2223
case put = "PUT"
@@ -28,7 +29,6 @@ public struct AWSSignatureV4 {
2829
let accessKey: String
2930
let secretKey: String
3031

31-
//used for unit tests
3232
var unitTestDate: Date?
3333

3434
var amzDate: String {
@@ -38,17 +38,16 @@ public struct AWSSignatureV4 {
3838
return dateFormatter.string(from: unitTestDate ?? Date())
3939
}
4040

41-
//TODO(Brett): public init
42-
init(
41+
public init(
4342
service: String,
4443
host: String,
45-
region: String,
44+
region: Region,
4645
accessKey: String,
4746
secretKey: String
4847
) {
4948
self.service = service
5049
self.host = host
51-
self.region = region
50+
self.region = region.rawValue
5251
self.accessKey = accessKey
5352
self.secretKey = secretKey
5453
}
@@ -59,7 +58,12 @@ public struct AWSSignatureV4 {
5958
scope: String,
6059
canonicalHash: String
6160
) -> String {
62-
return "\(algorithm)\n\(date)\n\(scope)\n\(canonicalHash)"
61+
return [
62+
algorithm,
63+
date,
64+
scope,
65+
canonicalHash
66+
].joined(separator: "\n")
6367
}
6468

6569
func getSignature(_ stringToSign: String) throws -> String {
@@ -73,28 +77,41 @@ public struct AWSSignatureV4 {
7377
}
7478

7579
func getCredentialScope() -> String {
76-
return "\(dateStamp())/\(self.region)/\(self.service)/aws4_request"
80+
return [
81+
dateStamp(),
82+
region,
83+
service,
84+
"aws4_request"
85+
].joined(separator: "/")
7786
}
7887

79-
func getCanonicalRequest(method: Method, path: String, query: String = "") throws -> String {
88+
func getCanonicalRequest(
89+
payload: Payload,
90+
method: Method,
91+
path: String,
92+
query: String,
93+
headers: [String : String] = [:]
94+
) throws -> String {
8095
let path = try path.percentEncode(allowing: Byte.awsPathAllowed)
8196
let query = try query.percentEncode(allowing: Byte.awsQueryAllowed)
82-
83-
var request: String = ""
97+
let payloadHash = try payload.hashed()
8498

85-
let headers = "host:\(host)\nx-amz-date:\(amzDate)\n"
86-
let signedHeaders = "host;x-amz-date"
87-
let payload: Payload = .none
88-
var payloadHash: String
89-
90-
do {
91-
payloadHash = try payload.hashed()
92-
request = "\(method.rawValue)\n\(path)\n\(query)\n\(headers)\n\(signedHeaders)\n\(payloadHash)"
93-
} catch {
94-
95-
}
96-
97-
return request
99+
var headers = headers
100+
generateHeadersToSign(headers: &headers, host: host, hash: payloadHash)
101+
102+
let sortedHeaders = alphabetize(headers)
103+
let canonicalHeaders = createCanonicalHeaders(sortedHeaders)
104+
let headersToSign = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")
105+
106+
return [
107+
method.rawValue,
108+
path,
109+
query,
110+
canonicalHeaders,
111+
"",
112+
headersToSign,
113+
payloadHash
114+
].joined(separator: "\n")
98115
}
99116

100117
func dateStamp() -> String {
@@ -105,6 +122,53 @@ public struct AWSSignatureV4 {
105122
}
106123
}
107124

125+
extension AWSSignatureV4 {
126+
func generateHeadersToSign(
127+
headers: inout [String: String],
128+
host: String,
129+
hash: String
130+
) {
131+
headers["host"] = host
132+
headers["X-Amz-Date"] = amzDate
133+
134+
if hash != "UNSIGNED-PAYLOAD" {
135+
headers["x-amz-content-sha256"] = hash
136+
}
137+
}
138+
139+
func alphabetize(_ dict: [String : String]) -> [(key: String, value: String)] {
140+
return dict.sorted(by: { $0.0.lowercased() < $1.0.lowercased() })
141+
}
142+
143+
func createCanonicalHeaders(_ headers: [(key: String, value: String)]) -> String {
144+
return headers.map {
145+
"\($0.key.lowercased()):\($0.value)"
146+
}.joined(separator: "\n")
147+
}
148+
149+
func signPayload(
150+
_ payload: Payload,
151+
mime: String?,
152+
headers: inout [HeaderKey : String]
153+
) throws {
154+
/*let contentLength: Int
155+
156+
switch payload {
157+
case .bytes(let bytes):
158+
contentLength = bytes.count
159+
default:
160+
contentLength = 0
161+
}
162+
163+
headers["Content-Length"] = "\(contentLength)"
164+
if let mime = mime {
165+
headers["Content-Type"] = mime
166+
}
167+
168+
headers["x-amz-content-sha256"] = try payload.hashed()*/
169+
}
170+
}
171+
108172
extension AWSSignatureV4 {
109173
public func sign(
110174
payload: Payload = .none,
@@ -117,11 +181,12 @@ extension AWSSignatureV4 {
117181
let credentialScope = getCredentialScope()
118182

119183
let canonicalRequest = try getCanonicalRequest(
184+
payload: payload,
120185
method: method,
121186
path: path,
122187
query: query ?? ""
123188
)
124-
189+
125190
let canonicalHash = try Hash.make(.sha256, canonicalRequest).hexString
126191

127192
let stringToSign = getStringToSign(
@@ -133,17 +198,12 @@ extension AWSSignatureV4 {
133198

134199
let signature = try getSignature(stringToSign)
135200

136-
let authorizationHeader = "\(algorithm) Credential=\(accessKey)/\(credentialScope), SignedHeaders=host;x-amz-date, Signature=\(signature)"
201+
let authorizationHeader = "\(algorithm) Credential=\(accessKey)/\(credentialScope), SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=\(signature)"
137202

138203
return [
139204
"X-Amz-Date": amzDate,
205+
"x-amz-content-sha256": try payload.hashed(),
140206
"Authorization": authorizationHeader
141207
]
142208
}
143209
}
144-
145-
extension Data {
146-
func hexEncodedString() -> String {
147-
return map { String(format: "%02hhx", $0) }.joined()
148-
}
149-
}

Sources/Driver/Authentication/Payload.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ extension Payload {
1717
return "UNSIGNED-PAYLOAD"
1818

1919
case .none:
20-
return try Hash.make(.sha256, []).hexString
20+
return "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
2121
}
2222
}
2323
}

Tests/AWSTests/AWSTests.swift

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import XCTest
2+
import HTTP
3+
import Transport
4+
@testable import Driver
25

36
class AWSTests: XCTestCase {
47
static var allTests = [

0 commit comments

Comments
 (0)