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:
Adam Fowler 2020-08-03 11:28:49 +01:00 committed by GitHub
parent f28d6f6503
commit b8af0b2a43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 120 additions and 496 deletions

View File

@ -10,21 +10,16 @@ jobs:
uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Build
run: swift build
- name: Test
- name: Build and Test
run: swift test
linux:
runs-on: ubuntu-latest
strategy:
matrix:
tag: ['5.0', '5.1']
tag: ['5.1', '5.2']
container:
image: swift:${{ matrix.tag }}
volumes:
- $GITHUB_WORKSPACE:/src
options: --workdir /src
steps:
- name: Checkout
uses: actions/checkout@v1
@ -33,9 +28,7 @@ jobs:
- name: Install dependencies
run: |
apt-get update -qq
apt-get install -q -y tzdata libssl-dev zlib1g-dev
- name: Build
run: swift build
- name: Test
apt-get install -q -y tzdata zlib1g-dev
- name: Build and Test
run: swift test

View File

@ -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 \
libssl-dev zlib1g-dev \
zlib1g-dev \
&& rm -r /var/lib/apt/lists/*
WORKDIR /aws-signer

View File

@ -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.
import PackageDescription
@ -15,24 +15,22 @@ let package = Package(
],
targets: [
.target(name: "AWSSigner", dependencies: ["AWSCrypto", "NIO", "NIOHTTP1"]),
.target(name: "AWSCrypto", dependencies: ["CAWSCrypto"]),
.target(name: "CAWSCrypto", dependencies: []),
.target(name: "AWSCrypto", dependencies: []),
.target(name: "HTTPClientAWSSigner", dependencies: ["AWSSigner", "AsyncHTTPClient"]),
.testTarget(name: "AWSSignerTests", dependencies: ["AWSSigner", "HTTPClientAWSSigner"]),
.testTarget(name: "AWSCryptoTests", dependencies: ["AWSCrypto"]),
.testTarget(name: "AWSSignerTests", dependencies: ["AWSSigner", "HTTPClientAWSSigner"])
]
)
// 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)
let useOpenSSLShim = true
let useSwiftCrypto = true
#else
let useOpenSSLShim = false
let useSwiftCrypto = false
#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.
if useOpenSSLShim {
package.dependencies.append(.package(url: "https://github.com/apple/swift-nio-ssl-support.git", from: "1.0.0"))
// Use Swift cypto on Linux.
if useSwiftCrypto {
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")
}

View File

@ -1,10 +1,11 @@
// ByteArray.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if !os(Linux)
import protocol Foundation.ContiguousBytes
/// 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])
var bytes: [UInt8] { get set }
}
@ -21,10 +22,6 @@ extension ByteArray {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
return try bytes.withUnsafeBytes(body)
}
public var description: String {
// hex digest
return bytes.map{String(format: "%02x", $0)}.joined()
}
}
#endif

View File

@ -1,13 +1,16 @@
// Digest.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if !os(Linux)
import protocol Foundation.ContiguousBytes
/// 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}
}
/// Protocol for Digest object consisting of a byte array
protocol ByteDigest: Digest, ByteArray { }
#endif

View File

@ -1,6 +1,9 @@
// HMAC.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if !os(Linux)
import CommonCrypto
import protocol Foundation.DataProtocol
/// Hash Authentication Code returned by HMAC
@ -8,10 +11,6 @@ public struct HashAuthenticationCode: ByteArray {
public var bytes: [UInt8]
}
#if canImport(CommonCrypto)
import CommonCrypto
/// Object generating HMAC for data block given a symmetric key
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

View File

@ -1,7 +1,9 @@
// HashFunction.swift
// HashFunction protocol and OpenSSL, CommonCrypto specialisations
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if !os(Linux)
import CommonCrypto
import protocol Foundation.DataProtocol
/// Protocol for Hashing function
@ -59,55 +61,10 @@ extension HashFunction {
}
}
#if canImport(CommonCrypto)
import CommonCrypto
/// public protocol for Common Crypto hash functions
public protocol CCHashFunction: HashFunction {
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

View File

@ -1,5 +1,8 @@
// Insecure.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if !os(Linux)
public enum Insecure {}
#endif

View File

@ -1,7 +1,7 @@
// MD5.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if canImport(CommonCrypto)
#if !os(Linux)
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

View File

@ -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

View File

@ -1,7 +1,7 @@
// SHA2.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if canImport(CommonCrypto)
#if !os(Linux)
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

View File

@ -1,6 +1,8 @@
// SymmetricKey.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// Replicating the CryptoKit framework interface for < macOS 10.15
// written by AdamFowler 2020/01/30
#if !os(Linux)
import protocol Foundation.ContiguousBytes
/// Symmetric key object
@ -15,14 +17,12 @@ public struct SymmetricKey: ContiguousBytes {
let bytes = data.withUnsafeBytes { buffer in
return [UInt8](buffer)
}
self.init(bytes: bytes)
}
public init(bytes: [UInt8]) {
self.bytes = bytes
}
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
return try self.bytes.withUnsafeBytes(body)
}
}
#endif

View File

@ -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

View File

@ -4,7 +4,7 @@
//
// Created by Adam Fowler on 29/08/2019.
//
import Foundation
import class Foundation.ProcessInfo
/// Protocol for providing credential details for accessing AWS services
public protocol Credential {

View File

@ -7,7 +7,13 @@
// 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 NIO
import NIOHTTP1
@ -20,23 +26,25 @@ public struct AWSSigner {
public let name: String
/// AWS region you are working in
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
public init(credentials: Credential, name: String, region: String) {
self.credentials = credentials
self.name = name
self.region = region
}
/// Enum for holding your body data
public enum BodyData {
case string(String)
case data(Data)
case byteBuffer(ByteBuffer)
}
/// 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 {
let bodyHash = AWSSigner.hashedPayload(body)
@ -49,28 +57,27 @@ public struct AWSSigner {
if let sessionToken = credentials.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
let signingData = AWSSigner.SigningData(url: url, method: method, headers: headers, body: body, bodyHash: bodyHash, date: dateString, signer: self)
// construct authorization string
let authorization = "AWS4-HMAC-SHA256 " +
"Credential=\(credentials.accessKeyId)/\(signingData.date)/\(region)/\(name)/aws4_request, " +
"SignedHeaders=\(signingData.signedHeaders), " +
"Signature=\(signature(signingData: signingData))"
// add Authorization header
headers.add(name: "Authorization", value: authorization)
return headers
}
/// 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 {
let headers = HTTPHeaders([("host", url.host ?? "")])
// 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.
var query = url.query ?? ""
if query.count > 0 {
@ -89,19 +96,19 @@ public struct AWSSigner {
.sorted()
.joined(separator: "&")
.queryEncode()
// 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
query += "&X-Amz-Signature=\(signature(signingData: signingData))"
// Add signature to query items and build a new Request
let signedURL = URL(string: url.absoluteString.split(separator: "?")[0]+"?"+query)!
return signedURL
}
/// structure used to store data used throughout the signing process
class SigningData {
struct SigningData {
let url : URL
let method : HTTPMethod
let hashedPayload : String
@ -109,9 +116,9 @@ public struct AWSSigner {
let headersToSign: [String: String]
let signedHeaders : String
var unsignedURL : URL
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) {
self.url = url
self.method = method
@ -125,7 +132,7 @@ public struct AWSSigner {
} else {
self.hashedPayload = AWSSigner.hashedPayload(body)
}
let headersNotToSign: Set<String> = [
"Authorization"
]
@ -142,7 +149,7 @@ public struct AWSSigner {
self.signedHeaders = signedHeadersArray.sorted().joined(separator: ";")
}
}
// Stage 3 Calculating signature as in https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
func signature(signingData: SigningData) -> String {
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 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))
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
func stringToSign(signingData: SigningData) -> Data {
let stringToSign = "AWS4-HMAC-SHA256\n" +
"\(signingData.datetime)\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)
}
/// 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 {
let canonicalHeaders = signingData.headersToSign.map { return "\($0.key.lowercased()):\($0.value.trimmingCharacters(in: CharacterSet.whitespaces))" }
@ -175,20 +182,20 @@ public struct AWSSigner {
signingData.hashedPayload
return Data(canonicalRequest.utf8)
}
/// Create a SHA256 hash of the Requests body
static func hashedPayload(_ payload: BodyData?) -> String {
guard let payload = payload else { return hashedEmptyBody }
let hash : String?
switch payload {
case .string(let string):
hash = SHA256.hash(data: Data(string.utf8)).description
hash = SHA256.hash(data: Data(string.utf8)).hexDigest()
case .data(let data):
hash = SHA256.hash(data: data).description
hash = SHA256.hash(data: data).hexDigest()
case .byteBuffer(let byteBuffer):
let byteBufferView = byteBuffer.readableBytesView
hash = byteBufferView.withContiguousStorageIfAvailable { bytes in
return SHA256.hash(data: bytes).description
return SHA256.hash(data: bytes).hexDigest()
}
}
if let hash = hash {
@ -197,19 +204,23 @@ public struct AWSSigner {
return hashedEmptyBody
}
}
/// return a hexEncoded string buffer from an array of bytes
static func hexEncoded(_ buffer: [UInt8]) -> String {
return buffer.map{String(format: "%02x", $0)}.joined(separator: "")
}
/// return a timestamp formatted for signing requests
static func timestamp(_ date: Date) -> String {
/// create timestamp dateformatter
static private func createTimeStampDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
formatter.timeZone = TimeZone(abbreviation: "UTC")
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 {
return addingPercentEncoding(withAllowedCharacters: String.queryAllowedCharacters) ?? self
}
func uriEncode() -> String {
return addingPercentEncoding(withAllowedCharacters: String.uriAllowedCharacters) ?? self
}
func uriEncodeWithSlash() -> String {
return addingPercentEncoding(withAllowedCharacters: String.uriAllowedWithSlashCharacters) ?? self
}
static let uriAllowedWithSlashCharacters = CharacterSet(charactersIn:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~/")
static let uriAllowedCharacters = CharacterSet(charactersIn:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
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: "")
}
}

View File

@ -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

View File

@ -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__

View File

@ -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),
]
}

View File

@ -1,9 +1,7 @@
import XCTest
@testable import AWSSignerTests
@testable import AWSCryptoTests
XCTMain([
testCase(AWSSignerTests.allTests),
testCase(AWSCryptoTests.allTests)
testCase(AWSSignerTests.allTests)
])

View File

@ -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 \
libssl-dev zlib1g-dev \
zlib1g-dev \
&& rm -r /var/lib/apt/lists/*