SwiftCrypto (#3)
* Use swift-crypto for hashing requirements * Remove swift 5.0 as it won't compile * Optimization - stop recreating DateFormatter * Ressurected AWSCrypto so we can support earlier OS versions * Update CI for workdir issue, remove libssl-dev
This commit is contained in:
parent
f28d6f6503
commit
b8af0b2a43
|
@ -10,21 +10,16 @@ jobs:
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
- name: Build
|
- name: Build and Test
|
||||||
run: swift build
|
|
||||||
- name: Test
|
|
||||||
run: swift test
|
run: swift test
|
||||||
|
|
||||||
linux:
|
linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
tag: ['5.0', '5.1']
|
tag: ['5.1', '5.2']
|
||||||
container:
|
container:
|
||||||
image: swift:${{ matrix.tag }}
|
image: swift:${{ matrix.tag }}
|
||||||
volumes:
|
|
||||||
- $GITHUB_WORKSPACE:/src
|
|
||||||
options: --workdir /src
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
|
@ -33,9 +28,7 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
apt-get update -qq
|
apt-get update -qq
|
||||||
apt-get install -q -y tzdata libssl-dev zlib1g-dev
|
apt-get install -q -y tzdata zlib1g-dev
|
||||||
- name: Build
|
- name: Build and Test
|
||||||
run: swift build
|
|
||||||
- name: Test
|
|
||||||
run: swift test
|
run: swift test
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
FROM swift:5.1 as builder
|
FROM swift:5.2 as builder
|
||||||
|
|
||||||
RUN apt-get -qq update && apt-get install -y \
|
RUN apt-get -qq update && apt-get install -y \
|
||||||
libssl-dev zlib1g-dev \
|
zlib1g-dev \
|
||||||
&& rm -r /var/lib/apt/lists/*
|
&& rm -r /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /aws-signer
|
WORKDIR /aws-signer
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// swift-tools-version:5.0
|
// swift-tools-version:5.1
|
||||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
@ -15,24 +15,22 @@ let package = Package(
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(name: "AWSSigner", dependencies: ["AWSCrypto", "NIO", "NIOHTTP1"]),
|
.target(name: "AWSSigner", dependencies: ["AWSCrypto", "NIO", "NIOHTTP1"]),
|
||||||
.target(name: "AWSCrypto", dependencies: ["CAWSCrypto"]),
|
.target(name: "AWSCrypto", dependencies: []),
|
||||||
.target(name: "CAWSCrypto", dependencies: []),
|
|
||||||
|
|
||||||
.target(name: "HTTPClientAWSSigner", dependencies: ["AWSSigner", "AsyncHTTPClient"]),
|
.target(name: "HTTPClientAWSSigner", dependencies: ["AWSSigner", "AsyncHTTPClient"]),
|
||||||
|
|
||||||
.testTarget(name: "AWSSignerTests", dependencies: ["AWSSigner", "HTTPClientAWSSigner"]),
|
.testTarget(name: "AWSSignerTests", dependencies: ["AWSSigner", "HTTPClientAWSSigner"])
|
||||||
.testTarget(name: "AWSCryptoTests", dependencies: ["AWSCrypto"]),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
// switch for whether to use CAWSSigner to shim between OpenSSL versions
|
// switch for whether to use swift crypto. Swift crypto requires macOS10.15 or iOS13.I'd rather not pass this requirement on
|
||||||
#if os(Linux)
|
#if os(Linux)
|
||||||
let useOpenSSLShim = true
|
let useSwiftCrypto = true
|
||||||
#else
|
#else
|
||||||
let useOpenSSLShim = false
|
let useSwiftCrypto = false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Decide on where we get our SSL support from. Linux usses NIOSSL to provide SSL. Linux also needs CAWSSDKOpenSSL to shim across different OpenSSL versions for the HMAC functions.
|
// Use Swift cypto on Linux.
|
||||||
if useOpenSSLShim {
|
if useSwiftCrypto {
|
||||||
package.dependencies.append(.package(url: "https://github.com/apple/swift-nio-ssl-support.git", from: "1.0.0"))
|
package.dependencies.append(.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0"))
|
||||||
|
package.targets.first{$0.name == "AWSCrypto"}?.dependencies.append("Crypto")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// ByteArray.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
import protocol Foundation.ContiguousBytes
|
import protocol Foundation.ContiguousBytes
|
||||||
|
|
||||||
/// Protocol for object encapsulating an array of bytes
|
/// Protocol for object encapsulating an array of bytes
|
||||||
protocol ByteArray: Sequence, ContiguousBytes, CustomStringConvertible, Hashable where Element == UInt8 {
|
protocol ByteArray: Sequence, ContiguousBytes, Hashable where Element == UInt8 {
|
||||||
init(bytes: [UInt8])
|
init(bytes: [UInt8])
|
||||||
var bytes: [UInt8] { get set }
|
var bytes: [UInt8] { get set }
|
||||||
}
|
}
|
||||||
|
@ -21,10 +22,6 @@ extension ByteArray {
|
||||||
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
|
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
|
||||||
return try bytes.withUnsafeBytes(body)
|
return try bytes.withUnsafeBytes(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
// hex digest
|
|
||||||
return bytes.map{String(format: "%02x", $0)}.joined()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
// Digest.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
import protocol Foundation.ContiguousBytes
|
import protocol Foundation.ContiguousBytes
|
||||||
|
|
||||||
/// Protocol for Digest object returned from HashFunction
|
/// Protocol for Digest object returned from HashFunction
|
||||||
public protocol Digest: Sequence, ContiguousBytes, CustomStringConvertible, Hashable where Element == UInt8 {
|
public protocol Digest: Sequence, ContiguousBytes, Hashable where Element == UInt8 {
|
||||||
static var byteCount: Int {get}
|
static var byteCount: Int {get}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Protocol for Digest object consisting of a byte array
|
/// Protocol for Digest object consisting of a byte array
|
||||||
protocol ByteDigest: Digest, ByteArray { }
|
protocol ByteDigest: Digest, ByteArray { }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
// HMAC.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
|
import CommonCrypto
|
||||||
import protocol Foundation.DataProtocol
|
import protocol Foundation.DataProtocol
|
||||||
|
|
||||||
/// Hash Authentication Code returned by HMAC
|
/// Hash Authentication Code returned by HMAC
|
||||||
|
@ -8,10 +11,6 @@ public struct HashAuthenticationCode: ByteArray {
|
||||||
public var bytes: [UInt8]
|
public var bytes: [UInt8]
|
||||||
}
|
}
|
||||||
|
|
||||||
#if canImport(CommonCrypto)
|
|
||||||
|
|
||||||
import CommonCrypto
|
|
||||||
|
|
||||||
/// Object generating HMAC for data block given a symmetric key
|
/// Object generating HMAC for data block given a symmetric key
|
||||||
public struct HMAC<H: CCHashFunction> {
|
public struct HMAC<H: CCHashFunction> {
|
||||||
|
|
||||||
|
@ -66,63 +65,4 @@ extension HMAC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
import CAWSCrypto
|
|
||||||
|
|
||||||
public struct HMAC<H: OpenSSLHashFunction> {
|
|
||||||
|
|
||||||
let key: SymmetricKey
|
|
||||||
var context: OpaquePointer
|
|
||||||
|
|
||||||
/// return authentication code for data block given a symmetric key
|
|
||||||
public static func authenticationCode<D : DataProtocol>(for data: D, using key: SymmetricKey) -> HashAuthenticationCode {
|
|
||||||
var hmac = HMAC(key: key)
|
|
||||||
hmac.update(data: data)
|
|
||||||
return hmac.finalize()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// update HMAC calculation with a block of data
|
|
||||||
public mutating func update<D: DataProtocol>(data: D) {
|
|
||||||
if let digest = data.withContiguousStorageIfAvailable({ bytes in
|
|
||||||
return self.update(bufferPointer: bytes)
|
|
||||||
}) {
|
|
||||||
return digest
|
|
||||||
} else {
|
|
||||||
var buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: data.count)
|
|
||||||
data.copyBytes(to: buffer)
|
|
||||||
defer { buffer.deallocate() }
|
|
||||||
self.update(bufferPointer: .init(buffer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HMAC {
|
|
||||||
/// initialize HMAC with symmetric key
|
|
||||||
public init(key: SymmetricKey) {
|
|
||||||
self.key = key
|
|
||||||
self.context = AWSCRYPTO_HMAC_CTX_new()
|
|
||||||
self.initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// initialize HMAC calculation
|
|
||||||
mutating func initialize() {
|
|
||||||
HMAC_Init_ex(context, key.bytes, Int32(key.bytes.count), H.algorithm, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// update HMAC calculation with a buffer
|
|
||||||
mutating func update(bufferPointer: UnsafeBufferPointer<UInt8>) {
|
|
||||||
HMAC_Update(context, bufferPointer.baseAddress, bufferPointer.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// finalize HMAC calculation and return authentication code
|
|
||||||
public mutating func finalize() -> HashAuthenticationCode {
|
|
||||||
var authenticationCode: [UInt8] = .init(repeating: 0, count: H.Digest.byteCount)
|
|
||||||
var length: UInt32 = 0
|
|
||||||
HMAC_Final(context, &authenticationCode, &length)
|
|
||||||
AWSCRYPTO_HMAC_CTX_free(context)
|
|
||||||
return .init(bytes: authenticationCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
// HashFunction.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// HashFunction protocol and OpenSSL, CommonCrypto specialisations
|
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
|
import CommonCrypto
|
||||||
import protocol Foundation.DataProtocol
|
import protocol Foundation.DataProtocol
|
||||||
|
|
||||||
/// Protocol for Hashing function
|
/// Protocol for Hashing function
|
||||||
|
@ -59,55 +61,10 @@ extension HashFunction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if canImport(CommonCrypto)
|
|
||||||
|
|
||||||
import CommonCrypto
|
|
||||||
|
|
||||||
/// public protocol for Common Crypto hash functions
|
/// public protocol for Common Crypto hash functions
|
||||||
public protocol CCHashFunction: HashFunction {
|
public protocol CCHashFunction: HashFunction {
|
||||||
static var algorithm: CCHmacAlgorithm { get }
|
static var algorithm: CCHmacAlgorithm { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
import CAWSCrypto
|
|
||||||
|
|
||||||
/// public protocol for OpenSSL hash function
|
|
||||||
public protocol OpenSSLHashFunction: HashFunction {
|
|
||||||
static var algorithm: OpaquePointer { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// internal protocol for OpenSSL hash functions that hides implementation details
|
|
||||||
protocol _OpenSSLHashFunction: OpenSSLHashFunction where Digest: ByteDigest {
|
|
||||||
var context: OpaquePointer { get set }
|
|
||||||
init(context: OpaquePointer)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension _OpenSSLHashFunction {
|
|
||||||
/// initialization
|
|
||||||
public init() {
|
|
||||||
self.init(context: AWSCRYPTO_EVP_MD_CTX_new())
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// initialize hash function
|
|
||||||
mutating func initialize() {
|
|
||||||
EVP_DigestInit_ex(context, Self.algorithm, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// update hash function with data
|
|
||||||
public mutating func update(bufferPointer: UnsafeRawBufferPointer) {
|
|
||||||
EVP_DigestUpdate(context, bufferPointer.baseAddress, bufferPointer.count)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// finalize hash function and return digest
|
|
||||||
public mutating func finalize() -> Self.Digest {
|
|
||||||
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
|
|
||||||
var count: UInt32 = 0
|
|
||||||
EVP_DigestFinal_ex(context, &digest, &count)
|
|
||||||
AWSCRYPTO_EVP_MD_CTX_free(context)
|
|
||||||
return .init(bytes: .init(digest))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
// Insecure.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
public enum Insecure {}
|
public enum Insecure {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// MD5.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
#if canImport(CommonCrypto)
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
import CommonCrypto
|
import CommonCrypto
|
||||||
|
|
||||||
|
@ -40,21 +40,4 @@ public extension Insecure {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
import CAWSCrypto
|
|
||||||
|
|
||||||
public extension Insecure {
|
|
||||||
struct MD5Digest : ByteDigest {
|
|
||||||
public static var byteCount: Int { return Int(MD5_DIGEST_LENGTH) }
|
|
||||||
public var bytes: [UInt8]
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MD5: _OpenSSLHashFunction {
|
|
||||||
public typealias Digest = MD5Digest
|
|
||||||
public static var algorithm: OpaquePointer { return EVP_md5() }
|
|
||||||
var context: OpaquePointer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
// SHA1.swift
|
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
|
||||||
#if canImport(CommonCrypto)
|
|
||||||
|
|
||||||
import CommonCrypto
|
|
||||||
|
|
||||||
public extension Insecure {
|
|
||||||
|
|
||||||
struct SHA1Digest : ByteDigest {
|
|
||||||
public static var byteCount: Int { return Int(CC_SHA1_DIGEST_LENGTH) }
|
|
||||||
public var bytes: [UInt8]
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SHA1: CCHashFunction {
|
|
||||||
public typealias Digest = SHA1Digest
|
|
||||||
public static var algorithm: CCHmacAlgorithm { return CCHmacAlgorithm(kCCHmacAlgSHA1) }
|
|
||||||
var context: CC_SHA1_CTX
|
|
||||||
|
|
||||||
public static func hash(bufferPointer: UnsafeRawBufferPointer) -> Self.Digest {
|
|
||||||
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
|
|
||||||
CC_SHA1(bufferPointer.baseAddress, CC_LONG(bufferPointer.count), &digest)
|
|
||||||
return .init(bytes: digest)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
context = CC_SHA1_CTX()
|
|
||||||
CC_SHA1_Init(&context)
|
|
||||||
}
|
|
||||||
|
|
||||||
public mutating func update(bufferPointer: UnsafeRawBufferPointer) {
|
|
||||||
CC_SHA1_Update(&context, bufferPointer.baseAddress, CC_LONG(bufferPointer.count))
|
|
||||||
}
|
|
||||||
|
|
||||||
public mutating func finalize() -> Self.Digest {
|
|
||||||
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
|
|
||||||
CC_SHA1_Final(&digest, &context)
|
|
||||||
return .init(bytes: digest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
import CAWSCrypto
|
|
||||||
|
|
||||||
public extension Insecure {
|
|
||||||
struct SHA1Digest : ByteDigest {
|
|
||||||
public static var byteCount: Int { return Int(SHA_DIGEST_LENGTH) }
|
|
||||||
public var bytes: [UInt8]
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SHA1: _OpenSSLHashFunction {
|
|
||||||
public typealias Digest = SHA1Digest
|
|
||||||
public static var algorithm: OpaquePointer { return EVP_sha1() }
|
|
||||||
var context: OpaquePointer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// SHA2.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
#if canImport(CommonCrypto)
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
import CommonCrypto
|
import CommonCrypto
|
||||||
|
|
||||||
|
@ -101,41 +101,4 @@ public struct SHA512: CCHashFunction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
import CAWSCrypto
|
|
||||||
|
|
||||||
public struct SHA256Digest : ByteDigest {
|
|
||||||
public static var byteCount: Int { return Int(SHA256_DIGEST_LENGTH) }
|
|
||||||
public var bytes: [UInt8]
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SHA256: _OpenSSLHashFunction {
|
|
||||||
public typealias Digest = SHA256Digest
|
|
||||||
public static var algorithm: OpaquePointer { return EVP_sha256() }
|
|
||||||
var context: OpaquePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SHA384Digest : ByteDigest {
|
|
||||||
public static var byteCount: Int { return Int(SHA384_DIGEST_LENGTH) }
|
|
||||||
public var bytes: [UInt8]
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SHA384: _OpenSSLHashFunction {
|
|
||||||
public typealias Digest = SHA384Digest
|
|
||||||
public static var algorithm: OpaquePointer { return EVP_sha384() }
|
|
||||||
var context: OpaquePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SHA512Digest : ByteDigest {
|
|
||||||
public static var byteCount: Int { return Int(SHA512_DIGEST_LENGTH) }
|
|
||||||
public var bytes: [UInt8]
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SHA512: _OpenSSLHashFunction {
|
|
||||||
public typealias Digest = SHA512Digest
|
|
||||||
public static var algorithm: OpaquePointer { return EVP_sha512() }
|
|
||||||
var context: OpaquePointer
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// SymmetricKey.swift
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
|
|
||||||
// written by AdamFowler 2020/01/30
|
// written by AdamFowler 2020/01/30
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
|
||||||
import protocol Foundation.ContiguousBytes
|
import protocol Foundation.ContiguousBytes
|
||||||
|
|
||||||
/// Symmetric key object
|
/// Symmetric key object
|
||||||
|
@ -15,14 +17,12 @@ public struct SymmetricKey: ContiguousBytes {
|
||||||
let bytes = data.withUnsafeBytes { buffer in
|
let bytes = data.withUnsafeBytes { buffer in
|
||||||
return [UInt8](buffer)
|
return [UInt8](buffer)
|
||||||
}
|
}
|
||||||
self.init(bytes: bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(bytes: [UInt8]) {
|
|
||||||
self.bytes = bytes
|
self.bytes = bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
|
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
|
||||||
return try self.bytes.withUnsafeBytes(body)
|
return try self.bytes.withUnsafeBytes(body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Replicating the CryptoKit framework interface for < macOS 10.15
|
||||||
|
// written by AdamFowler 2020/01/30
|
||||||
|
|
||||||
|
#if os(Linux)
|
||||||
|
@_exported import Crypto
|
||||||
|
#endif
|
|
@ -4,7 +4,7 @@
|
||||||
//
|
//
|
||||||
// Created by Adam Fowler on 29/08/2019.
|
// Created by Adam Fowler on 29/08/2019.
|
||||||
//
|
//
|
||||||
import Foundation
|
import class Foundation.ProcessInfo
|
||||||
|
|
||||||
/// Protocol for providing credential details for accessing AWS services
|
/// Protocol for providing credential details for accessing AWS services
|
||||||
public protocol Credential {
|
public protocol Credential {
|
||||||
|
|
|
@ -7,7 +7,13 @@
|
||||||
// AWS documentation about signing requests is here https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html
|
// AWS documentation about signing requests is here https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import struct Foundation.CharacterSet
|
||||||
|
import struct Foundation.Data
|
||||||
|
import struct Foundation.Date
|
||||||
|
import class Foundation.DateFormatter
|
||||||
|
import struct Foundation.Locale
|
||||||
|
import struct Foundation.TimeZone
|
||||||
|
import struct Foundation.URL
|
||||||
import AWSCrypto
|
import AWSCrypto
|
||||||
import NIO
|
import NIO
|
||||||
import NIOHTTP1
|
import NIOHTTP1
|
||||||
|
@ -20,23 +26,25 @@ public struct AWSSigner {
|
||||||
public let name: String
|
public let name: String
|
||||||
/// AWS region you are working in
|
/// AWS region you are working in
|
||||||
public let region: String
|
public let region: String
|
||||||
|
|
||||||
static let hashedEmptyBody = SHA256.hash(data: [UInt8]()).description
|
static let hashedEmptyBody = SHA256.hash(data: [UInt8]()).hexDigest()
|
||||||
|
|
||||||
|
static private let timeStampDateFormatter: DateFormatter = createTimeStampDateFormatter()
|
||||||
|
|
||||||
/// Initialise the Signer class with AWS credentials
|
/// Initialise the Signer class with AWS credentials
|
||||||
public init(credentials: Credential, name: String, region: String) {
|
public init(credentials: Credential, name: String, region: String) {
|
||||||
self.credentials = credentials
|
self.credentials = credentials
|
||||||
self.name = name
|
self.name = name
|
||||||
self.region = region
|
self.region = region
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum for holding your body data
|
/// Enum for holding your body data
|
||||||
public enum BodyData {
|
public enum BodyData {
|
||||||
case string(String)
|
case string(String)
|
||||||
case data(Data)
|
case data(Data)
|
||||||
case byteBuffer(ByteBuffer)
|
case byteBuffer(ByteBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate signed headers, for a HTTP request
|
/// Generate signed headers, for a HTTP request
|
||||||
public func signHeaders(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: BodyData? = nil, date: Date = Date()) -> HTTPHeaders {
|
public func signHeaders(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: BodyData? = nil, date: Date = Date()) -> HTTPHeaders {
|
||||||
let bodyHash = AWSSigner.hashedPayload(body)
|
let bodyHash = AWSSigner.hashedPayload(body)
|
||||||
|
@ -49,28 +57,27 @@ public struct AWSSigner {
|
||||||
if let sessionToken = credentials.sessionToken {
|
if let sessionToken = credentials.sessionToken {
|
||||||
headers.add(name: "x-amz-security-token", value: sessionToken)
|
headers.add(name: "x-amz-security-token", value: sessionToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct signing data. Do this after adding the headers as it uses data from the headers
|
// construct signing data. Do this after adding the headers as it uses data from the headers
|
||||||
let signingData = AWSSigner.SigningData(url: url, method: method, headers: headers, body: body, bodyHash: bodyHash, date: dateString, signer: self)
|
let signingData = AWSSigner.SigningData(url: url, method: method, headers: headers, body: body, bodyHash: bodyHash, date: dateString, signer: self)
|
||||||
|
|
||||||
// construct authorization string
|
// construct authorization string
|
||||||
let authorization = "AWS4-HMAC-SHA256 " +
|
let authorization = "AWS4-HMAC-SHA256 " +
|
||||||
"Credential=\(credentials.accessKeyId)/\(signingData.date)/\(region)/\(name)/aws4_request, " +
|
"Credential=\(credentials.accessKeyId)/\(signingData.date)/\(region)/\(name)/aws4_request, " +
|
||||||
"SignedHeaders=\(signingData.signedHeaders), " +
|
"SignedHeaders=\(signingData.signedHeaders), " +
|
||||||
"Signature=\(signature(signingData: signingData))"
|
"Signature=\(signature(signingData: signingData))"
|
||||||
|
|
||||||
// add Authorization header
|
// add Authorization header
|
||||||
headers.add(name: "Authorization", value: authorization)
|
headers.add(name: "Authorization", value: authorization)
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a signed URL, for a HTTP request
|
/// Generate a signed URL, for a HTTP request
|
||||||
public func signURL(url: URL, method: HTTPMethod = .GET, body: BodyData? = nil, date: Date = Date(), expires: Int = 86400) -> URL {
|
public func signURL(url: URL, method: HTTPMethod = .GET, body: BodyData? = nil, date: Date = Date(), expires: Int = 86400) -> URL {
|
||||||
let headers = HTTPHeaders([("host", url.host ?? "")])
|
let headers = HTTPHeaders([("host", url.host ?? "")])
|
||||||
// Create signing data
|
// Create signing data
|
||||||
let signingData = AWSSigner.SigningData(url: url, method: method, headers: headers, body: body, date: AWSSigner.timestamp(date), signer: self)
|
var signingData = AWSSigner.SigningData(url: url, method: method, headers: headers, body: body, date: AWSSigner.timestamp(date), signer: self)
|
||||||
|
|
||||||
// Construct query string. Start with original query strings and append all the signing info.
|
// Construct query string. Start with original query strings and append all the signing info.
|
||||||
var query = url.query ?? ""
|
var query = url.query ?? ""
|
||||||
if query.count > 0 {
|
if query.count > 0 {
|
||||||
|
@ -89,19 +96,19 @@ public struct AWSSigner {
|
||||||
.sorted()
|
.sorted()
|
||||||
.joined(separator: "&")
|
.joined(separator: "&")
|
||||||
.queryEncode()
|
.queryEncode()
|
||||||
|
|
||||||
// update unsignedURL in the signingData so when the canonical request is constructed it includes all the signing query items
|
// update unsignedURL in the signingData so when the canonical request is constructed it includes all the signing query items
|
||||||
signingData.unsignedURL = URL(string: url.absoluteString.split(separator: "?")[0]+"?"+query)! // NEED TO DEAL WITH SITUATION WHERE THIS FAILS
|
signingData.unsignedURL = URL(string: url.absoluteString.split(separator: "?")[0]+"?"+query)! // NEED TO DEAL WITH SITUATION WHERE THIS FAILS
|
||||||
query += "&X-Amz-Signature=\(signature(signingData: signingData))"
|
query += "&X-Amz-Signature=\(signature(signingData: signingData))"
|
||||||
|
|
||||||
// Add signature to query items and build a new Request
|
// Add signature to query items and build a new Request
|
||||||
let signedURL = URL(string: url.absoluteString.split(separator: "?")[0]+"?"+query)!
|
let signedURL = URL(string: url.absoluteString.split(separator: "?")[0]+"?"+query)!
|
||||||
|
|
||||||
return signedURL
|
return signedURL
|
||||||
}
|
}
|
||||||
|
|
||||||
/// structure used to store data used throughout the signing process
|
/// structure used to store data used throughout the signing process
|
||||||
class SigningData {
|
struct SigningData {
|
||||||
let url : URL
|
let url : URL
|
||||||
let method : HTTPMethod
|
let method : HTTPMethod
|
||||||
let hashedPayload : String
|
let hashedPayload : String
|
||||||
|
@ -109,9 +116,9 @@ public struct AWSSigner {
|
||||||
let headersToSign: [String: String]
|
let headersToSign: [String: String]
|
||||||
let signedHeaders : String
|
let signedHeaders : String
|
||||||
var unsignedURL : URL
|
var unsignedURL : URL
|
||||||
|
|
||||||
var date : String { return String(datetime.prefix(8))}
|
var date : String { return String(datetime.prefix(8))}
|
||||||
|
|
||||||
init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: BodyData? = nil, bodyHash: String? = nil, date: String, signer: AWSSigner) {
|
init(url: URL, method: HTTPMethod = .GET, headers: HTTPHeaders = HTTPHeaders(), body: BodyData? = nil, bodyHash: String? = nil, date: String, signer: AWSSigner) {
|
||||||
self.url = url
|
self.url = url
|
||||||
self.method = method
|
self.method = method
|
||||||
|
@ -125,7 +132,7 @@ public struct AWSSigner {
|
||||||
} else {
|
} else {
|
||||||
self.hashedPayload = AWSSigner.hashedPayload(body)
|
self.hashedPayload = AWSSigner.hashedPayload(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
let headersNotToSign: Set<String> = [
|
let headersNotToSign: Set<String> = [
|
||||||
"Authorization"
|
"Authorization"
|
||||||
]
|
]
|
||||||
|
@ -142,7 +149,7 @@ public struct AWSSigner {
|
||||||
self.signedHeaders = signedHeadersArray.sorted().joined(separator: ";")
|
self.signedHeaders = signedHeadersArray.sorted().joined(separator: ";")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stage 3 Calculating signature as in https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
|
// Stage 3 Calculating signature as in https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
|
||||||
func signature(signingData: SigningData) -> String {
|
func signature(signingData: SigningData) -> String {
|
||||||
let kDate = HMAC<SHA256>.authenticationCode(for: Data(signingData.date.utf8), using: SymmetricKey(data: Array("AWS4\(credentials.secretAccessKey)".utf8)))
|
let kDate = HMAC<SHA256>.authenticationCode(for: Data(signingData.date.utf8), using: SymmetricKey(data: Array("AWS4\(credentials.secretAccessKey)".utf8)))
|
||||||
|
@ -150,18 +157,18 @@ public struct AWSSigner {
|
||||||
let kService = HMAC<SHA256>.authenticationCode(for: Data(name.utf8), using: SymmetricKey(data: kRegion))
|
let kService = HMAC<SHA256>.authenticationCode(for: Data(name.utf8), using: SymmetricKey(data: kRegion))
|
||||||
let kSigning = HMAC<SHA256>.authenticationCode(for: Data("aws4_request".utf8), using: SymmetricKey(data: kService))
|
let kSigning = HMAC<SHA256>.authenticationCode(for: Data("aws4_request".utf8), using: SymmetricKey(data: kService))
|
||||||
let kSignature = HMAC<SHA256>.authenticationCode(for: stringToSign(signingData: signingData), using: SymmetricKey(data: kSigning))
|
let kSignature = HMAC<SHA256>.authenticationCode(for: stringToSign(signingData: signingData), using: SymmetricKey(data: kSigning))
|
||||||
return kSignature.description
|
return kSignature.hexDigest()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stage 2 Create the string to sign as in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
/// Stage 2 Create the string to sign as in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||||
func stringToSign(signingData: SigningData) -> Data {
|
func stringToSign(signingData: SigningData) -> Data {
|
||||||
let stringToSign = "AWS4-HMAC-SHA256\n" +
|
let stringToSign = "AWS4-HMAC-SHA256\n" +
|
||||||
"\(signingData.datetime)\n" +
|
"\(signingData.datetime)\n" +
|
||||||
"\(signingData.date)/\(region)/\(name)/aws4_request\n" +
|
"\(signingData.date)/\(region)/\(name)/aws4_request\n" +
|
||||||
SHA256.hash(data: canonicalRequest(signingData: signingData)).description
|
SHA256.hash(data: canonicalRequest(signingData: signingData)).hexDigest()
|
||||||
return Data(stringToSign.utf8)
|
return Data(stringToSign.utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stage 1 Create the canonical request as in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
/// Stage 1 Create the canonical request as in https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||||
func canonicalRequest(signingData: SigningData) -> Data {
|
func canonicalRequest(signingData: SigningData) -> Data {
|
||||||
let canonicalHeaders = signingData.headersToSign.map { return "\($0.key.lowercased()):\($0.value.trimmingCharacters(in: CharacterSet.whitespaces))" }
|
let canonicalHeaders = signingData.headersToSign.map { return "\($0.key.lowercased()):\($0.value.trimmingCharacters(in: CharacterSet.whitespaces))" }
|
||||||
|
@ -175,20 +182,20 @@ public struct AWSSigner {
|
||||||
signingData.hashedPayload
|
signingData.hashedPayload
|
||||||
return Data(canonicalRequest.utf8)
|
return Data(canonicalRequest.utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a SHA256 hash of the Requests body
|
/// Create a SHA256 hash of the Requests body
|
||||||
static func hashedPayload(_ payload: BodyData?) -> String {
|
static func hashedPayload(_ payload: BodyData?) -> String {
|
||||||
guard let payload = payload else { return hashedEmptyBody }
|
guard let payload = payload else { return hashedEmptyBody }
|
||||||
let hash : String?
|
let hash : String?
|
||||||
switch payload {
|
switch payload {
|
||||||
case .string(let string):
|
case .string(let string):
|
||||||
hash = SHA256.hash(data: Data(string.utf8)).description
|
hash = SHA256.hash(data: Data(string.utf8)).hexDigest()
|
||||||
case .data(let data):
|
case .data(let data):
|
||||||
hash = SHA256.hash(data: data).description
|
hash = SHA256.hash(data: data).hexDigest()
|
||||||
case .byteBuffer(let byteBuffer):
|
case .byteBuffer(let byteBuffer):
|
||||||
let byteBufferView = byteBuffer.readableBytesView
|
let byteBufferView = byteBuffer.readableBytesView
|
||||||
hash = byteBufferView.withContiguousStorageIfAvailable { bytes in
|
hash = byteBufferView.withContiguousStorageIfAvailable { bytes in
|
||||||
return SHA256.hash(data: bytes).description
|
return SHA256.hash(data: bytes).hexDigest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let hash = hash {
|
if let hash = hash {
|
||||||
|
@ -197,19 +204,23 @@ public struct AWSSigner {
|
||||||
return hashedEmptyBody
|
return hashedEmptyBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return a hexEncoded string buffer from an array of bytes
|
/// return a hexEncoded string buffer from an array of bytes
|
||||||
static func hexEncoded(_ buffer: [UInt8]) -> String {
|
static func hexEncoded(_ buffer: [UInt8]) -> String {
|
||||||
return buffer.map{String(format: "%02x", $0)}.joined(separator: "")
|
return buffer.map{String(format: "%02x", $0)}.joined(separator: "")
|
||||||
}
|
}
|
||||||
|
/// create timestamp dateformatter
|
||||||
/// return a timestamp formatted for signing requests
|
static private func createTimeStampDateFormatter() -> DateFormatter {
|
||||||
static func timestamp(_ date: Date) -> String {
|
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
|
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
|
||||||
formatter.timeZone = TimeZone(abbreviation: "UTC")
|
formatter.timeZone = TimeZone(abbreviation: "UTC")
|
||||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||||
return formatter.string(from: date)
|
return formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return a timestamp formatted for signing requests
|
||||||
|
static func timestamp(_ date: Date) -> String {
|
||||||
|
return timeStampDateFormatter.string(from: date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,16 +228,23 @@ extension String {
|
||||||
func queryEncode() -> String {
|
func queryEncode() -> String {
|
||||||
return addingPercentEncoding(withAllowedCharacters: String.queryAllowedCharacters) ?? self
|
return addingPercentEncoding(withAllowedCharacters: String.queryAllowedCharacters) ?? self
|
||||||
}
|
}
|
||||||
|
|
||||||
func uriEncode() -> String {
|
func uriEncode() -> String {
|
||||||
return addingPercentEncoding(withAllowedCharacters: String.uriAllowedCharacters) ?? self
|
return addingPercentEncoding(withAllowedCharacters: String.uriAllowedCharacters) ?? self
|
||||||
}
|
}
|
||||||
|
|
||||||
func uriEncodeWithSlash() -> String {
|
func uriEncodeWithSlash() -> String {
|
||||||
return addingPercentEncoding(withAllowedCharacters: String.uriAllowedWithSlashCharacters) ?? self
|
return addingPercentEncoding(withAllowedCharacters: String.uriAllowedWithSlashCharacters) ?? self
|
||||||
}
|
}
|
||||||
|
|
||||||
static let uriAllowedWithSlashCharacters = CharacterSet(charactersIn:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~/")
|
static let uriAllowedWithSlashCharacters = CharacterSet(charactersIn:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~/")
|
||||||
static let uriAllowedCharacters = CharacterSet(charactersIn:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
|
static let uriAllowedCharacters = CharacterSet(charactersIn:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
|
||||||
static let queryAllowedCharacters = CharacterSet(charactersIn:"/;+").inverted
|
static let queryAllowedCharacters = CharacterSet(charactersIn:"/;+").inverted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension Sequence where Element == UInt8 {
|
||||||
|
/// return a hexEncoded string buffer from an array of bytes
|
||||||
|
func hexDigest() -> String {
|
||||||
|
return self.map{String(format: "%02x", $0)}.joined(separator: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
//
|
|
||||||
// c_awscrypto.h
|
|
||||||
// AWSSDKSwiftCore
|
|
||||||
//
|
|
||||||
// Created by Adam Fowler on 2019/08/08.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef C_AWSSDK_OPENSSL_H
|
|
||||||
#define C_AWSSDK_OPENSSL_H
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
|
|
||||||
#include <openssl/hmac.h>
|
|
||||||
#include <openssl/md5.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
|
|
||||||
HMAC_CTX *AWSCRYPTO_HMAC_CTX_new();
|
|
||||||
void AWSCRYPTO_HMAC_CTX_free(HMAC_CTX* ctx);
|
|
||||||
|
|
||||||
EVP_MD_CTX *AWSCRYPTO_EVP_MD_CTX_new(void);
|
|
||||||
void AWSCRYPTO_EVP_MD_CTX_free(EVP_MD_CTX *ctx);
|
|
||||||
|
|
||||||
#endif // __linux__
|
|
||||||
|
|
||||||
#endif // C_AWSSDK_OPENSSL_H
|
|
|
@ -1,56 +0,0 @@
|
||||||
//
|
|
||||||
// shims.c
|
|
||||||
// AWSSDKSwiftCore
|
|
||||||
//
|
|
||||||
// Created by Adam Fowler on 2019/08/08.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
|
|
||||||
// These are functions that shim over differences in different OpenSSL versions,
|
|
||||||
// which are best handled by using the C preprocessor.
|
|
||||||
#include "c_awscrypto.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
HMAC_CTX *AWSCRYPTO_HMAC_CTX_new() {
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
|
|
||||||
HMAC_CTX *ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
|
|
||||||
if (ctx != NULL) {
|
|
||||||
HMAC_CTX_init(ctx);
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
#else
|
|
||||||
return HMAC_CTX_new();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void AWSCRYPTO_HMAC_CTX_free(HMAC_CTX* ctx) {
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
|
|
||||||
if (ctx != NULL) {
|
|
||||||
HMAC_CTX_cleanup(ctx);
|
|
||||||
OPENSSL_free(ctx);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
HMAC_CTX_free(ctx);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
EVP_MD_CTX *AWSCRYPTO_EVP_MD_CTX_new(void) {
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
|
|
||||||
return EVP_MD_CTX_create();
|
|
||||||
#else
|
|
||||||
return EVP_MD_CTX_new();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void AWSCRYPTO_EVP_MD_CTX_free(EVP_MD_CTX *ctx) {
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
|
|
||||||
if (ctx != NULL) {
|
|
||||||
EVP_MD_CTX_destroy(ctx);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
EVP_MD_CTX_free(ctx);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // __linux__
|
|
|
@ -1,93 +0,0 @@
|
||||||
import XCTest
|
|
||||||
@testable import AWSCrypto
|
|
||||||
|
|
||||||
final class AWSCryptoTests: XCTestCase {
|
|
||||||
|
|
||||||
// create a buffer of random values. Will always create the same given you supply the same z and w values
|
|
||||||
// Random number generator from https://www.codeproject.com/Articles/25172/Simple-Random-Number-Generation
|
|
||||||
func createRandomBuffer(_ w: UInt, _ z: UInt, size: Int) -> [UInt8] {
|
|
||||||
var z = z
|
|
||||||
var w = w
|
|
||||||
func getUInt8() -> UInt8
|
|
||||||
{
|
|
||||||
z = 36969 * (z & 65535) + (z >> 16);
|
|
||||||
w = 18000 * (w & 65535) + (w >> 16);
|
|
||||||
return UInt8(((z << 16) + w) & 0xff);
|
|
||||||
}
|
|
||||||
var data = Array<UInt8>(repeating: 0, count: size)
|
|
||||||
for i in 0..<size {
|
|
||||||
data[i] = getUInt8()
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMD5() {
|
|
||||||
let data = createRandomBuffer(34, 2345, size: 234896)
|
|
||||||
let digest = Insecure.MD5.hash(data: data)
|
|
||||||
XCTAssertEqual(digest.description, "3abdd8d79be09bc250d60ada1f000912")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSHA1() {
|
|
||||||
let data = createRandomBuffer(41, 83, size: 562384)
|
|
||||||
let digest = Insecure.SHA1.hash(data: data)
|
|
||||||
XCTAssertEqual(digest.description, "2ea7cc3ce53e940a5877cbf5c8dfc992e4833fb3")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSHA256() {
|
|
||||||
let data = createRandomBuffer(872, 12489, size: 562741)
|
|
||||||
let digest = SHA256.hash(data: data)
|
|
||||||
XCTAssertEqual(digest.description, "3cff070559024d8652d1257e5f455787e95ebd8e95378d62df1a466f78860f74")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSHA384() {
|
|
||||||
let data = createRandomBuffer(872, 12489, size: 562741)
|
|
||||||
let digest = SHA384.hash(data: data)
|
|
||||||
XCTAssertEqual(digest.description, "d03a6a749dd66fb7bb261e34014c69e217684440b0c853727ac5bc12147edddc304cadbec8df8f77ec2ee44cc6b53bc3")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSHA512() {
|
|
||||||
let data = createRandomBuffer(872, 12489, size: 562741)
|
|
||||||
let digest = SHA512.hash(data: data)
|
|
||||||
XCTAssertEqual(digest.description, "15fc2df3a1c3649b83baf0f28d1a611bee8339a050d9d2c2ac4afad18f3187f725530b09bb6b2044131648d11d608c394804bc02ce2110b76d231ea75201000d")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSHA256InitUpdateFinal() {
|
|
||||||
let data = createRandomBuffer(8372, 12489, size: 562741)
|
|
||||||
let digest = SHA256.hash(data: data)
|
|
||||||
|
|
||||||
var sha256 = SHA256()
|
|
||||||
sha256.update(data: data[0..<238768])
|
|
||||||
sha256.update(data: data[238768..<562741])
|
|
||||||
let digest2 = sha256.finalize()
|
|
||||||
|
|
||||||
XCTAssertEqual(digest, digest2)
|
|
||||||
XCTAssertEqual(digest.description, digest2.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testHMAC() {
|
|
||||||
let data = createRandomBuffer(1, 91, size: 347237)
|
|
||||||
let key = createRandomBuffer(102, 3, size: 32)
|
|
||||||
let authenticationKey = HMAC<SHA256>.authenticationCode(for: data, using: SymmetricKey(bytes: key))
|
|
||||||
XCTAssertEqual(authenticationKey.description, "ddec250211f1b546254bab3fb027af1acc4842898e8af6eeadcdbf8e2c6c1ff5")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testHMACInitUpdateFinal() {
|
|
||||||
let data = createRandomBuffer(21, 81, size: 762061)
|
|
||||||
let key = createRandomBuffer(102, 3, size: 32)
|
|
||||||
let authenticationKey = HMAC<SHA256>.authenticationCode(for: data, using: SymmetricKey(bytes: key))
|
|
||||||
|
|
||||||
var hmac = HMAC<SHA256>(key: SymmetricKey(bytes: key))
|
|
||||||
hmac.update(data: data[0..<126749])
|
|
||||||
hmac.update(data: data[126749..<762061])
|
|
||||||
let authenticationKey2 = hmac.finalize()
|
|
||||||
|
|
||||||
XCTAssertEqual(authenticationKey, authenticationKey2)
|
|
||||||
XCTAssertEqual(authenticationKey.description, authenticationKey2.description)
|
|
||||||
}
|
|
||||||
|
|
||||||
static var allTests = [
|
|
||||||
("testMD5", testMD5),
|
|
||||||
("testSHA256", testSHA256),
|
|
||||||
("testHMAC", testHMAC),
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,9 +1,7 @@
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable import AWSSignerTests
|
@testable import AWSSignerTests
|
||||||
@testable import AWSCryptoTests
|
|
||||||
|
|
||||||
XCTMain([
|
XCTMain([
|
||||||
testCase(AWSSignerTests.allTests),
|
testCase(AWSSignerTests.allTests)
|
||||||
testCase(AWSCryptoTests.allTests)
|
|
||||||
])
|
])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
FROM swift:5.1.3 as builder
|
FROM swift:5.2 as builder
|
||||||
|
|
||||||
RUN apt-get -qq update && apt-get install -y \
|
RUN apt-get -qq update && apt-get install -y \
|
||||||
libssl-dev zlib1g-dev \
|
zlib1g-dev \
|
||||||
&& rm -r /var/lib/apt/lists/*
|
&& rm -r /var/lib/apt/lists/*
|
||||||
|
|
Loading…
Reference in New Issue