Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
2.0.0
Switch branches/tags
Go to file
@mahmoud-adam85
Latest commit 7aea3c8 Aug 1, 2018 History
2 contributors

Users who have contributed to this file

@mahmoud-adam85 @naira-cliqz
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import FxA
import Shared
open class HawkHelper {
fileprivate let nonceLengthInBytes: UInt = 8
let id: String
let key: Data
public init(id: String, key: Data) {
self.id = id
self.key = key
}
// Produce a HAWK value suitable for an "Authorization: value" header, timestamped now.
open func getAuthorizationValueFor(_ request: URLRequest) -> String {
let timestampInSeconds: Int64 = Int64(Date().timeIntervalSince1970)
return getAuthorizationValueFor(request, at: timestampInSeconds)
}
// Produce a HAWK value suitable for an "Authorization: value" header.
func getAuthorizationValueFor(_ request: URLRequest, at timestampInSeconds: Int64) -> String {
let nonce = Data.randomOfLength(nonceLengthInBytes)!.base64EncodedString
let extra = ""
return getAuthorizationValueFor(request, at: timestampInSeconds, nonce: nonce, extra: extra)
}
func getAuthorizationValueFor(_ request: URLRequest, at timestampInSeconds: Int64, nonce: String, extra: String) -> String {
let timestampString = String(timestampInSeconds)
let hashString = HawkHelper.getPayloadHashFor(request)
let requestString = HawkHelper.getRequestStringFor(request, timestampString: timestampString, nonce: nonce, hash: hashString, extra: extra)
let macString = HawkHelper.getSignatureFor(requestString.utf8EncodedData, key: self.key)
let s = NSMutableString(string: "Hawk ")
func append(_ key: String, value: String) {
s.append(key)
s.append("=\"")
s.append(value)
s.append("\", ")
}
append("id", value: id)
append("ts", value: timestampString)
append("nonce", value: nonce)
if !hashString.isEmpty {
append("hash", value: hashString)
}
if !extra.isEmpty {
append("ext", value: HawkHelper.escapeExtraHeaderAttribute(extra))
}
append("mac", value: macString)
// Drop the trailing "\",".
return s.substring(to: s.length - 2)
}
class func getSignatureFor(_ input: Data, key: Data) -> String {
return input.hmacSha256WithKey(key).base64EncodedString
}
class func getRequestStringFor(_ request: URLRequest, timestampString: String, nonce: String, hash: String, extra: String) -> String {
let s = NSMutableString(string: "hawk.1.header\n")
func append(_ line: String) {
s.append(line)
s.append("\n")
}
append(timestampString)
append(nonce)
append((request as NSURLRequest).httpMethod?.uppercased() ?? "GET")
let url = request.url!
s.append(url.path)
if let query = url.query {
s.append("?")
s.append(query)
}
if let fragment = url.fragment {
s.append("#")
s.append(fragment)
}
s.append("\n")
append(url.host!)
if let port = (url as NSURL).port {
append(String(describing: port))
} else {
if url.scheme?.lowercased() == "https" {
append("443")
} else {
append("80")
}
}
append(hash)
if !extra.isEmpty {
append(HawkHelper.escapeExtraString(extra))
} else {
append("")
}
return s as String
}
class func getPayloadHashFor(_ request: URLRequest) -> String {
if let body = request.httpBody {
var d = Data()
func append(_ s: String) {
let data = s.utf8EncodedData
d.append(data)
}
append("hawk.1.payload\n")
append(getBaseContentTypeFor(request.value(forHTTPHeaderField: "Content-Type")))
append("\n") // Trailing newline is specified by Hawk.
d.append(body)
append("\n") // Trailing newline is specified by Hawk.
return d.sha256.base64EncodedString
} else {
return ""
}
}
class func getBaseContentTypeFor(_ contentType: String?) -> String {
if let contentType = contentType {
if let index = contentType.index(of: ";") {
return String(contentType[..<index]).trimmingCharacters(in: .whitespaces)
} else {
return contentType.trimmingCharacters(in: .whitespaces)
}
} else {
return "text/plain"
}
}
class func escapeExtraHeaderAttribute(_ extra: String) -> String {
return extra.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\"")
}
class func escapeExtraString(_ extra: String) -> String {
return extra.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\n", with: "\\n")
}
}
extension URLRequest {
mutating func addAuthorizationHeader(forHKDFSHA256Key bytes: Data) {
let tokenId = bytes.subdata(in: 0..<KeyLength)
let reqHMACKey = bytes.subdata(in: KeyLength..<(2 * KeyLength))
let hawkHelper = HawkHelper(id: tokenId.hexEncodedString, key: reqHMACKey)
let hawkValue = hawkHelper.getAuthorizationValueFor(self)
setValue(hawkValue, forHTTPHeaderField: "Authorization")
}
}