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

Commit c1ac522

Browse files
authored
Merge pull request #4 from nodes-vapor/signature-test-suite
Amazon's signature test suite
2 parents 34574b7 + b34ae48 commit c1ac522

File tree

13 files changed

+480
-130
lines changed

13 files changed

+480
-130
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.build/
12
.DS_Store
23
*.xcodeproj
34
Packages/

Package.swift

+8-6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import PackageDescription
22

33
let package = Package(
44
name: "AWS",
5-
/*targets: [
6-
Target(name: "AWS"),
7-
Target(name: "EC2", dependencies: ["AWS"]),
8-
Target(name: "S3", dependencies: ["AWS"])
9-
],*/
5+
targets: [
6+
Target(name: "AWS", dependencies: ["EC2", "S3", "Driver"]),
7+
Target(name: "EC2", dependencies: ["Driver"]),
8+
Target(name: "S3", dependencies: ["Driver"]),
9+
Target(name: "Driver")
10+
],
1011
dependencies: [
1112
.Package(url: "https://github.com/vapor/crypto.git", majorVersion: 1),
12-
.Package(url: "https://github.com/vapor/engine.git", majorVersion: 1)
13+
.Package(url: "https://github.com/vapor/engine.git", majorVersion: 1),
14+
.Package(url: "https://github.com/JustinM1/S3SignerAWS.git", majorVersion: 1),
1315
]
1416
)

Sources/AWS/AWS.swift

Whitespace-only changes.

Sources/AWS/AWSApi/CallAWS.swift

-44
This file was deleted.

Sources/AWS/S3/S3.swift

-41
This file was deleted.

Sources/Driver/AWSDriver.swift

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import HTTP
2+
import Transport
3+
4+
public enum AccessControlList: String {
5+
case privateAccess = "private"
6+
case publicRead = "public-read"
7+
case publicReadWrite = "public-read-write"
8+
case awsExecRead = "aws-exec-read"
9+
case authenticatedRead = "authenticated-read"
10+
case bucketOwnerRead = "bucket-owner-read"
11+
case bucketOwnerFullControl = "bucket-owner-full-control"
12+
}
13+
14+
class AWSDriver {
15+
enum Error: Swift.Error {
16+
case unsupported(String)
17+
}
18+
19+
public func put() {
20+
}
21+
22+
public func get() {
23+
}
24+
25+
public func delete() {
26+
27+
}
28+
29+
public func call(authentication: Authentication) throws -> Response {
30+
let headers = authentication.getCanonicalHeaders()
31+
32+
let response: Response
33+
switch authentication.method {
34+
case .get:
35+
response = try BasicClient.get(
36+
"\(authentication.baseURL)?\(authentication.requestParam)",
37+
headers: headers
38+
)
39+
40+
default:
41+
throw Error.unsupported("method: \(authentication.method)")
42+
}
43+
44+
return response
45+
}
46+
}

Sources/AWS/Authentication/Authentication.swift renamed to Sources/Driver/Authentication/Authentication.swift

+60-35
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,66 @@ import HTTP
55
import Essentials
66
import Foundation
77

8-
class Authentication {
9-
enum Method {
10-
case get
11-
case post
8+
struct Authentication {
9+
enum Method: String {
10+
case get = "GET"
11+
case put = "PUT"
12+
case post = "POST"
1213
}
1314

1415
let method: Method
1516
let service: String
1617
let host: String
1718
let region: String
1819
let baseURL: String
19-
var amzDate: String
2020
let key: String
2121
let secret: String
22-
let requestParam: String!
22+
let requestParam: String?
23+
24+
//used for unit tests
25+
var unitTestDate: Date?
26+
27+
static let awsQueryAllowed = CharacterSet(
28+
charactersIn: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~="
29+
)
30+
31+
static let awsPathAllowed = CharacterSet(
32+
charactersIn: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~/"
33+
)
34+
35+
var amzDate: String {
36+
let dateFormatter = DateFormatter()
37+
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
38+
dateFormatter.dateFormat = "YYYYMMdd'T'HHmmss'Z'"
39+
return dateFormatter.string(from: unitTestDate ?? Date())
40+
}
2341

24-
public init(method: Method, service: String, host: String, region: String, baseURL: String, key: String, secret: String, requestParam: String!) {
42+
init(
43+
method: Method,
44+
service: String,
45+
host: String,
46+
region: String,
47+
baseURL: String,
48+
key: String,
49+
secret: String,
50+
requestParam: String? = nil
51+
) {
2552
self.method = method
2653
self.service = service
2754
self.host = host
2855
self.region = region
29-
self.baseURL = baseURL
30-
self.amzDate = ""
56+
//TODO(Brett): Proper encoding and error handling.
57+
self.baseURL = baseURL.addingPercentEncoding(
58+
withAllowedCharacters: Authentication.awsPathAllowed
59+
)!
3160
self.key = key
3261
self.secret = secret
33-
self.requestParam = requestParam.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
62+
self.requestParam = requestParam?.addingPercentEncoding(
63+
withAllowedCharacters: Authentication.awsQueryAllowed
64+
)
3465
}
3566

36-
public func getSignature(stringToSign: String) throws -> String {
67+
func getSignature(stringToSign: String) throws -> String {
3768
let dateHMAC = try HMAC.make(.sha256, dateStamp().bytes, key: "AWS4\(self.secret)".bytes)
3869
let regionHMAC = try HMAC.make(.sha256, self.region.bytes, key: dateHMAC)
3970
let serviceHMAC = try HMAC.make(.sha256, self.service.bytes, key: regionHMAC)
@@ -44,11 +75,15 @@ class Authentication {
4475
return Data(bytes: signature).hexEncodedString()
4576
}
4677

47-
public func canonicalRequest() -> String {
78+
func getCredentialScope() -> String {
79+
return "\(dateStamp())/\(self.region)/\(self.service)/aws4_request"
80+
}
81+
82+
func getCanonicalRequest() -> String {
4883
var request: String = ""
4984

50-
let uri = "/"
51-
let queryString = self.requestParam
85+
let uri = baseURL
86+
let queryString = self.requestParam ?? ""
5287
let headers = "host:\(host)\nx-amz-date:\(amzDate)\n"
5388
let signedHeaders = "host;x-amz-date"
5489
var payload: [UInt8]
@@ -57,24 +92,25 @@ class Authentication {
5792
do {
5893
payload = try Hash.make(.sha256, "")
5994
payloadHash = Data(bytes: payload).hexEncodedString()
60-
request = "\(method)\n\(uri)\n\(queryString)\n\(headers)\n\(signedHeaders)\n\(payloadHash)"
95+
request = "\(method.rawValue)\n\(uri)\n\(queryString)\n\(headers)\n\(signedHeaders)\n\(payloadHash)"
6196
} catch {
6297

6398
}
6499

65100
return request
66101
}
67-
68-
public func authorizationHeader() -> String {
102+
103+
func authorizationHeader() -> String {
69104
let algorithm = "AWS4-HMAC-SHA256"
70-
let credentialScope = "\(dateStamp())/\(self.region)/\(self.service)/aws4_request"
105+
let credentialScope = getCredentialScope()
71106
let canonicalHash: String
72107
let canonical: [UInt8]
73108
var stringToSign: String = ""
74109

75110
do {
76-
canonical = try Hash.make(.sha256, canonicalRequest())
111+
canonical = try Hash.make(.sha256, getCanonicalRequest())
77112
canonicalHash = Data(bytes: canonical).hexEncodedString()
113+
//TODO(Brett): pull out to make testable
78114
stringToSign = "\(algorithm)\n\(amzDate)\n\(credentialScope)\n\(canonicalHash)"
79115
} catch {
80116
}
@@ -89,26 +125,15 @@ class Authentication {
89125
return "\(algorithm) Credential=\(self.key)/\(credentialScope), SignedHeaders=host;x-amz-date, Signature=\(signature)"
90126
}
91127

92-
public func getAWSHeaders() -> [HeaderKey : String] {
93-
amzDateHeader()
128+
func getCanonicalHeaders() -> [HeaderKey : String] {
94129
return [
95-
"Authorization": authorizationHeader(),
96-
"x-amz-date": amzDate
130+
"X-Amz-Date": amzDate,
131+
"Authorization": authorizationHeader()
97132
]
98133
}
99134

100-
public func amzDateHeader() {
101-
let date = Date()
102-
103-
let dateFormatter = DateFormatter()
104-
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
105-
dateFormatter.dateFormat = "YYYYMMdd'T'HHmmss'Z'"
106-
107-
self.amzDate = dateFormatter.string(from: date)
108-
}
109-
110-
public func dateStamp() -> String {
111-
let date = Date()
135+
func dateStamp() -> String {
136+
let date = unitTestDate ?? Date()
112137

113138
let dateFormatter = DateFormatter()
114139

Sources/AWS/EC2/EC2.swift renamed to Sources/EC2/EC2.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class EC2 {
1919

2020
public func describeInstances() throws -> String {
2121
//TODO(Brett): wrap this result in a model instead of a string type
22-
let response = try CallAWS().call(
22+
/*let response = try AWSDriver().call(
2323
method: .get,
2424
service: service,
2525
host: host,
@@ -30,6 +30,7 @@ class EC2 {
3030
requestParam: "Action=DescribeRegions&Version=2015-10-01"
3131
)
3232

33-
return response.description
33+
return response.description*/
34+
return ""
3435
}
3536
}

0 commit comments

Comments
 (0)