Initial commit
This commit is contained in:
commit
74347fa8d6
4 changed files with 92 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
20
Package.swift
Normal file
20
Package.swift
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// swift-tools-version:5.5
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "SwiftHawk",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v10_15),
|
||||||
|
.tvOS(.v13),
|
||||||
|
.watchOS(.v6),
|
||||||
|
.iOS(.v13)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(name: "SwiftHawk", targets: ["SwiftHawk"]),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(name: "SwiftHawk", dependencies: []),
|
||||||
|
]
|
||||||
|
)
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# SwiftHawk
|
||||||
|
|
||||||
|
[Hawk](https://github.com/mozilla/hawk) signature implementation for `URLRequest`
|
62
Sources/SwiftHawk/SwiftHawk.swift
Normal file
62
Sources/SwiftHawk/SwiftHawk.swift
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import Foundation
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
|
public struct HawkCredentials {
|
||||||
|
public var id: String
|
||||||
|
public var key: SymmetricKey
|
||||||
|
|
||||||
|
public init(id: String, key: SymmetricKey) {
|
||||||
|
self.id = id
|
||||||
|
self.key = key
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(id: String, key: String) {
|
||||||
|
self.init(id: id, key: SymmetricKey(data: Data(key.utf8)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension URLRequest {
|
||||||
|
mutating func sign<H: HashFunction>(credentials: HawkCredentials, hash: H.Type) {
|
||||||
|
guard let url = url else { return }
|
||||||
|
|
||||||
|
let nonce = makeNonce()
|
||||||
|
let timestamp = Int(Date().timeIntervalSince1970)
|
||||||
|
|
||||||
|
var path = url.path
|
||||||
|
if let query = url.query {
|
||||||
|
path += "?\(query)"
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = "hawk.1.header\n\(timestamp)\n\(nonce)\n\(httpMethod?.uppercased() ?? "GET")\n\(path)\n\(url.host ?? "")\n\(port)\n\n\n"
|
||||||
|
|
||||||
|
var hash = HMAC<H>(key: credentials.key)
|
||||||
|
hash.update(data: Data(string.utf8))
|
||||||
|
let signature = Data(hash.finalize()).base64EncodedString(options: [])
|
||||||
|
|
||||||
|
let header = "Hawk id=\"\(credentials.id)\", ts=\"\(timestamp)\", nonce=\"\(nonce)\", mac=\"\(signature)\""
|
||||||
|
|
||||||
|
addValue(header, forHTTPHeaderField: "Authorization")
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func sign(credentials: HawkCredentials) {
|
||||||
|
self.sign(credentials: credentials, hash: SHA256.self)
|
||||||
|
}
|
||||||
|
|
||||||
|
var port: Int {
|
||||||
|
if let port = url?.port {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
switch url?.scheme {
|
||||||
|
case "http": return 80
|
||||||
|
case "https": return 443
|
||||||
|
default: return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNonce() -> String {
|
||||||
|
SymmetricKey(size: .init(bitCount: 48)).withUnsafeBytes { ptr in
|
||||||
|
Data(ptr).base64EncodedString(options: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue