diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index bcf97db..4ce53a1 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -8,7 +8,11 @@ on: jobs: build: - runs-on: macos-12 + name: Lint & Build swift on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-12] steps: - uses: actions/checkout@v2 - uses: actions/cache@v2 diff --git a/Package.swift b/Package.swift index 8affa27..d3a7605 100644 --- a/Package.swift +++ b/Package.swift @@ -3,6 +3,17 @@ import PackageDescription +// Currently, CommonCrypto and CryptoKit are not available under Linux. +// If CommonCrypto is not available, swift-crypto should be used. + +#if canImport(CommonCrypto) +let dependencies: [Package.Dependency] = [] +let tDependencies: [Target.Dependency] = [] +#else // for Linux +let dependencies: [Package.Dependency] = [.package(url: "https://github.com/apple/swift-crypto.git", from: "2.0.0")] +let tDependencies: [Target.Dependency] = [.product(name: "Crypto", package: "swift-crypto")] +#endif + let package = Package( name: "TwitterAPIKit", platforms: [ @@ -17,16 +28,13 @@ let package = Package( name: "TwitterAPIKit", targets: ["TwitterAPIKit"]), ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], + dependencies: dependencies, targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "TwitterAPIKit", - dependencies: []), + dependencies: tDependencies), .testTarget( name: "TwitterAPIKitTests", dependencies: ["TwitterAPIKit"]), diff --git a/Sources/TwitterAPIKit/Extensions/Data.swift b/Sources/TwitterAPIKit/Extensions/Data.swift index 1a96694..8366fa3 100644 --- a/Sources/TwitterAPIKit/Extensions/Data.swift +++ b/Sources/TwitterAPIKit/Extensions/Data.swift @@ -1,31 +1,6 @@ -import CommonCrypto import Foundation extension Data { - func hmac(key: Data) -> Data { - - // Thanks: https://github.com/jernejstrasner/SwiftCrypto - - let digestLen = Int(CC_SHA1_DIGEST_LENGTH) - return withUnsafeBytes { bytes -> Data in - let result = UnsafeMutablePointer.allocate(capacity: digestLen) - defer { - result.deallocate() - } - - key.withUnsafeBytes { body in - CCHmac( - CCHmacAlgorithm(kCCHmacAlgSHA1), - body.baseAddress, - key.count, bytes.baseAddress, - count, - result - ) - } - - return Data(bytes: result, count: digestLen) - } - } func split(separator: Data, omittingEmptySubsequences: Bool = true) -> [Data] { var current = startIndex diff --git a/Sources/TwitterAPIKit/Extensions/HMAC.swift b/Sources/TwitterAPIKit/Extensions/HMAC.swift new file mode 100644 index 0000000..fa2ab41 --- /dev/null +++ b/Sources/TwitterAPIKit/Extensions/HMAC.swift @@ -0,0 +1,44 @@ +import Foundation + +#if canImport(CommonCrypto) + import CommonCrypto + + extension Data { + fileprivate func hmac(key: Data) -> Data { + + // Thanks: https://github.com/jernejstrasner/SwiftCrypto + + let digestLen = Int(CC_SHA1_DIGEST_LENGTH) + return withUnsafeBytes { bytes -> Data in + let result = UnsafeMutablePointer.allocate(capacity: digestLen) + defer { + result.deallocate() + } + + key.withUnsafeBytes { body in + CCHmac( + CCHmacAlgorithm(kCCHmacAlgSHA1), + body.baseAddress, + key.count, bytes.baseAddress, + count, + result + ) + } + + return Data(bytes: result, count: digestLen) + } + } + } + + func createHMACSHA1(key: Data, message: Data) -> Data { + return message.hmac(key: key) + } + +#elseif canImport(Crypto) // for Linux + import Crypto + func createHMACSHA1(key: Data, message: Data) -> Data { + return Data(HMAC.authenticationCode(for: message, using: SymmetricKey(data: key))) + } +#else + #error("Crypto is not available in this environment.") +#endif diff --git a/Sources/TwitterAPIKit/OAuthHelper.swift b/Sources/TwitterAPIKit/OAuthHelper.swift index 1830750..669ec11 100644 --- a/Sources/TwitterAPIKit/OAuthHelper.swift +++ b/Sources/TwitterAPIKit/OAuthHelper.swift @@ -1,4 +1,3 @@ -import CommonCrypto import Foundation private let oauthVersion = "1.0" @@ -69,6 +68,6 @@ private func oauthSignature( let key = signingKey.data(using: .utf8)! let msg = signatureBaseString.data(using: .utf8)! - let sha1 = msg.hmac(key: key) + let sha1 = createHMACSHA1(key: key, message: msg) return sha1.base64EncodedString(options: []) } diff --git a/Sources/TwitterAPIKit/TwitterAPIKit.swift b/Sources/TwitterAPIKit/TwitterAPIKit.swift index 143c464..7d93ccc 100644 --- a/Sources/TwitterAPIKit/TwitterAPIKit.swift +++ b/Sources/TwitterAPIKit/TwitterAPIKit.swift @@ -1,5 +1,9 @@ import Foundation +#if canImport(FoundationNetworking) + @_exported import FoundationNetworking +#endif + public enum TwitterBaseURLType { case twitter case api diff --git a/Sources/TwitterAPIKit/TwitterAPIResponse.swift b/Sources/TwitterAPIKit/TwitterAPIResponse.swift index d561101..511c1b8 100644 --- a/Sources/TwitterAPIKit/TwitterAPIResponse.swift +++ b/Sources/TwitterAPIKit/TwitterAPIResponse.swift @@ -143,8 +143,12 @@ extension String { } fileprivate var unescapingUnicodeCharacters: String { - let mutableString = NSMutableString(string: self) - CFStringTransform(mutableString, nil, "Any-Hex/Java" as NSString, true) - return mutableString as String + #if os(Linux) + return self + #else + let mutableString = NSMutableString(string: self) + CFStringTransform(mutableString, nil, "Any-Hex/Java" as NSString, true) + return mutableString as String + #endif } }