FoundationSecurity.swift 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. //////////////////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // FoundationSecurity.swift
  4. // Starscream
  5. //
  6. // Created by Dalton Cherry on 3/16/19.
  7. // Copyright © 2019 Vluxe. All rights reserved.
  8. //
  9. // Licensed under the Apache License, Version 2.0 (the "License");
  10. // you may not use this file except in compliance with the License.
  11. // You may obtain a copy of the License at
  12. //
  13. // http://www.apache.org/licenses/LICENSE-2.0
  14. //
  15. // Unless required by applicable law or agreed to in writing, software
  16. // distributed under the License is distributed on an "AS IS" BASIS,
  17. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. // See the License for the specific language governing permissions and
  19. // limitations under the License.
  20. //
  21. //////////////////////////////////////////////////////////////////////////////////////////////////
  22. import Foundation
  23. import CommonCrypto
  24. public enum FoundationSecurityError: Error {
  25. case invalidRequest
  26. }
  27. public class FoundationSecurity {
  28. var allowSelfSigned = false
  29. public init(allowSelfSigned: Bool = false) {
  30. self.allowSelfSigned = allowSelfSigned
  31. }
  32. }
  33. extension FoundationSecurity: CertificatePinning {
  34. public func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ())) {
  35. if allowSelfSigned {
  36. completion(.success)
  37. return
  38. }
  39. SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, domain as NSString?))
  40. handleSecurityTrust(trust: trust, completion: completion)
  41. }
  42. private func handleSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) {
  43. if #available(iOS 12.0, OSX 10.14, watchOS 5.0, tvOS 12.0, *) {
  44. var error: CFError?
  45. if SecTrustEvaluateWithError(trust, &error) {
  46. completion(.success)
  47. } else {
  48. completion(.failed(error))
  49. }
  50. } else {
  51. handleOldSecurityTrust(trust: trust, completion: completion)
  52. }
  53. }
  54. private func handleOldSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) {
  55. var result: SecTrustResultType = .unspecified
  56. SecTrustEvaluate(trust, &result)
  57. if result == .unspecified || result == .proceed {
  58. completion(.success)
  59. } else {
  60. let e = CFErrorCreate(kCFAllocatorDefault, "FoundationSecurityError" as NSString?, Int(result.rawValue), nil)
  61. completion(.failed(e))
  62. }
  63. }
  64. }
  65. extension FoundationSecurity: HeaderValidator {
  66. public func validate(headers: [String: String], key: String) -> Error? {
  67. if let acceptKey = headers[HTTPWSHeader.acceptName] {
  68. let sha = "\(key)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64()
  69. if sha != acceptKey {
  70. return WSError(type: .securityError, message: "accept header doesn't match", code: SecurityErrorCode.acceptFailed.rawValue)
  71. }
  72. }
  73. return nil
  74. }
  75. }
  76. private extension String {
  77. func sha1Base64() -> String {
  78. let data = self.data(using: .utf8)!
  79. let pointer = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
  80. var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
  81. CC_SHA1(bytes.baseAddress, CC_LONG(data.count), &digest)
  82. return digest
  83. }
  84. return Data(pointer).base64EncodedString()
  85. }
  86. }