Move crypto code to AWSCrypto module

This commit is contained in:
Adam Fowler 2020-02-01 10:42:44 +00:00
parent 8c14c703ee
commit 81dc1183ed
16 changed files with 551 additions and 120 deletions

View File

@ -14,9 +14,14 @@ let package = Package(
.package(url: "https://github.com/swift-server/async-http-client", .upToNextMajor(from: "1.0.0"))
],
targets: [
.target(name: "AWSSigner", dependencies: ["NIOHTTP1"]),
.target(name: "AWSSigner", dependencies: ["AWSCrypto", "NIO", "NIOHTTP1"]),
.target(name: "AWSCrypto", dependencies: ["CAWSCrypto"]),
.target(name: "CAWSCrypto", dependencies: []),
.target(name: "HTTPClientAWSSigner", dependencies: ["AWSSigner", "AsyncHTTPClient"]),
.testTarget(name: "AWSSignerTests", dependencies: ["AWSSigner", "HTTPClientAWSSigner"]),
.testTarget(name: "AWSCryptoTests", dependencies: ["AWSCrypto"]),
]
)
@ -27,12 +32,7 @@ let useOpenSSLShim = true
let useOpenSSLShim = false
#endif
// AWSSDKSwiftCore target
let awsSdkSwiftCoreTarget = package.targets.first(where: {$0.name == "AWSSigner"})
// 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.targets.append(.target(name: "CAWSSigner"))
awsSdkSwiftCoreTarget?.dependencies.append("CAWSSigner")
package.dependencies.append(.package(url: "https://github.com/apple/swift-nio-ssl-support.git", from: "1.0.0"))
}

View File

@ -0,0 +1,30 @@
// ByteArray.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// written by AdamFowler 2020/01/30
import protocol Foundation.ContiguousBytes
/// Protocol for object encapsulating an array of bytes
protocol ByteArray: Sequence, ContiguousBytes, CustomStringConvertible, Hashable where Element == UInt8 {
init(bytes: [UInt8])
var bytes: [UInt8] { get set }
}
extension ByteArray {
public func makeIterator() -> Array<UInt8>.Iterator {
return self.bytes.makeIterator()
}
public init?(bufferPointer: UnsafeRawBufferPointer) {
self.init(bytes: [UInt8](bufferPointer))
}
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()
}
}

View File

@ -0,0 +1,13 @@
// Digest.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// written by AdamFowler 2020/01/30
import protocol Foundation.ContiguousBytes
/// Protocol for Digest object returned from HashFunction
public protocol Digest: Sequence, ContiguousBytes, CustomStringConvertible, Hashable where Element == UInt8 {
static var byteCount: Int {get}
}
/// Protocol for Digest object consisting of a byte array
protocol ByteDigest: Digest, ByteArray { }

View File

@ -0,0 +1,128 @@
// HMAC.swift
// based on the Vapor/open-crypto project which tries to replicate the CryptoKit framework interface
// written by AdamFowler 2020/01/30
import protocol Foundation.DataProtocol
/// Hash Authentication Code returned by HMAC
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> {
let key: [UInt8]
var context: CCHmacContext
/// return authentication code for data block given a symmetric key
public static func authenticationCode<D : DataProtocol>(for data: D, using key: [UInt8]) -> 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(bytes: .init(bytes))
}) {
return digest
} else {
var buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: data.count)
data.copyBytes(to: buffer)
defer { buffer.deallocate() }
self.update(bytes: .init(buffer))
}
}
}
extension HMAC {
/// initialize HMAC with symmetric key
public init(key: [UInt8]) {
self.key = key
self.context = CCHmacContext()
self.initialize()
}
/// initialize HMAC calculation
mutating func initialize() {
CCHmacInit(&context, H.algorithm, key, key.count)
}
/// update HMAC calculation with a buffer
public mutating func update(bytes: UnsafeRawBufferPointer) {
CCHmacUpdate(&context, bytes.baseAddress, bytes.count)
}
/// finalize HMAC calculation and return authentication code
public mutating func finalize() -> HashAuthenticationCode {
var authenticationCode: [UInt8] = .init(repeating: 0, count: H.Digest.byteCount)
CCHmacFinal(&context, &authenticationCode)
return .init(bytes: authenticationCode)
}
}
#else
import CAWSCrypto
public struct HMAC<H: OpenSSLHashFunction> {
let key: [UInt8]
var context: OpaquePointer
/// return authentication code for data block given a symmetric key
public static func authenticationCode<D : DataProtocol>(for data: D, using key: [UInt8]) -> 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(bytes: bytes)
}) {
return digest
} else {
var buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: data.count)
data.copyBytes(to: buffer)
defer { buffer.deallocate() }
self.update(bytes: .init(buffer))
}
}
}
extension HMAC {
/// initialize HMAC with symmetric key
public init(key: [UInt8]) {
self.key = key
self.context = AWSCRYPTO_HMAC_CTX_new()
self.initialize()
}
/// initialize HMAC calculation
mutating func initialize() {
HMAC_Init_ex(context, key, Int32(key.count), H.algorithm, nil)
}
/// update HMAC calculation with a buffer
mutating func update(bytes: UnsafeBufferPointer<UInt8>) {
HMAC_Update(context, bytes.baseAddress, bytes.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

@ -0,0 +1,135 @@
// HashFunction.swift
// 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
import protocol Foundation.DataProtocol
/// Protocol for Hashing function
public protocol HashFunction {
/// associated digest object
associatedtype Digest: AWSCrypto.Digest
/// hash raw buffer
static func hash(bytes: UnsafeRawBufferPointer) -> Self.Digest
/// initialization
init()
/// update hash function with data
mutating func update(bytes: UnsafeRawBufferPointer)
/// finalize hash function and return digest
mutating func finalize() -> Self.Digest
}
extension HashFunction {
/// default version of hash which call init, update and finalize
public static func hash(bytes: UnsafeRawBufferPointer) -> Self.Digest {
var function = Self()
function.update(bytes: bytes)
return function.finalize()
}
/// version of hash that takes data in any form that complies with DataProtocol
public static func hash<D: DataProtocol>(data: D) -> Self.Digest {
if let digest = data.withContiguousStorageIfAvailable({ bytes in
return self.hash(bytes: .init(bytes))
}) {
return digest
} else {
var buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: data.count)
data.copyBytes(to: buffer)
defer { buffer.deallocate() }
return self.hash(bytes: .init(buffer))
}
}
/// version of update that takes data in any form that complies with DataProtocol
public mutating func update<D: DataProtocol>(data: D) {
if let digest = data.withContiguousStorageIfAvailable({ bytes in
return self.update(bytes: .init(bytes))
}) {
return digest
} else {
var buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: data.count)
data.copyBytes(to: buffer)
defer { buffer.deallocate() }
self.update(bytes: .init(buffer))
}
}
}
#if canImport(CommonCrypto)
import CommonCrypto
/// public protocol for Common Crypto hash functions
public protocol CCHashFunction: HashFunction {
static var algorithm: CCHmacAlgorithm { get }
}
/// internal protocol for Common Crypto hash functions that hides implementation details
protocol _CCHashFunction: CCHashFunction where Digest: ByteDigest {
/// Context type that holds the hash function context
associatedtype Context
/// hashing context instance
var context: Context { get set }
/// creates a hashing context
static func createContext() -> Context
/// initialization with context, required by init()
init(context: Context)
/// init hash function
func initFunction(_ :UnsafeMutablePointer<Context>)
/// update hash function
func updateFunction(_ :UnsafeMutablePointer<Context>, _ :UnsafeRawPointer, _ :CC_LONG)
/// finalize hash function
func finalFunction(_ :UnsafeMutablePointer<UInt8>, _ :UnsafeMutablePointer<Context>) -> Int32
}
#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(bytes: UnsafeRawBufferPointer) {
EVP_DigestUpdate(context, bytes.baseAddress, bytes.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

@ -0,0 +1,55 @@
// MD5.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 struct MD5Digest : ByteDigest {
public static var byteCount: Int { return Int(CC_MD5_DIGEST_LENGTH) }
public var bytes: [UInt8]
}
public struct MD5: CCHashFunction {
public typealias Digest = MD5Digest
public static var algorithm: CCHmacAlgorithm { return CCHmacAlgorithm(kCCHmacAlgMD5) }
var context: CC_MD5_CTX
public static func hash(bytes: UnsafeRawBufferPointer) -> Self.Digest {
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
CC_MD5(bytes.baseAddress, CC_LONG(bytes.count), &digest)
return .init(bytes: digest)
}
public init() {
context = CC_MD5_CTX()
CC_MD5_Init(&context)
}
public mutating func update(bytes: UnsafeRawBufferPointer) {
CC_MD5_Update(&context, bytes.baseAddress, CC_LONG(bytes.count))
}
public mutating func finalize() -> Self.Digest {
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
CC_MD5_Final(&digest, &context)
return .init(bytes: digest)
}
}
#else
import CAWSCrypto
public struct MD5Digest : ByteDigest {
public static var byteCount: Int { return Int(MD5_DIGEST_LENGTH) }
public var bytes: [UInt8]
}
public struct MD5: _OpenSSLHashFunction {
public typealias Digest = MD5Digest
public static var algorithm: OpaquePointer { return EVP_md5() }
var context: OpaquePointer
}
#endif

View File

@ -0,0 +1,55 @@
// SHA2.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)
public struct SHA256Digest : ByteDigest {
public static var byteCount: Int { return Int(CC_SHA256_DIGEST_LENGTH) }
public var bytes: [UInt8]
}
import CommonCrypto
public struct SHA256: CCHashFunction {
public typealias Digest = SHA256Digest
public static var algorithm: CCHmacAlgorithm { return CCHmacAlgorithm(kCCHmacAlgSHA256) }
var context: CC_SHA256_CTX
public static func hash(bytes: UnsafeRawBufferPointer) -> Self.Digest {
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
CC_SHA256(bytes.baseAddress, CC_LONG(bytes.count), &digest)
return .init(bytes: digest)
}
public init() {
context = CC_SHA256_CTX()
CC_SHA256_Init(&context)
}
public mutating func update(bytes: UnsafeRawBufferPointer) {
CC_SHA256_Update(&context, bytes.baseAddress, CC_LONG(bytes.count))
}
public mutating func finalize() -> Self.Digest {
var digest: [UInt8] = .init(repeating: 0, count: Digest.byteCount)
CC_SHA256_Final(&digest, &context)
return .init(bytes: digest)
}
}
#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
}
#endif

View File

@ -7,14 +7,14 @@
import Foundation
/// Protocol for providing credential details for accessing AWS services
public protocol CredentialProvider {
public protocol Credential {
var accessKeyId: String {get}
var secretAccessKey: String {get}
var sessionToken: String? {get}
}
/// basic version of CredentialProvider where you supply the credentials
public struct Credential : CredentialProvider {
/// basic version of Credential where you supply the credentials
public struct StaticCredential: Credential {
public let accessKeyId: String
public let secretAccessKey: String
public let sessionToken: String?
@ -26,8 +26,8 @@ public struct Credential : CredentialProvider {
}
}
/// environment variable version of credential provider that uses system environment variables to get credential details
public struct EnvironmentCredential: CredentialProvider {
/// environment variable version of credential that uses system environment variables to get credential details
public struct EnvironmentCredential: Credential {
public let accessKeyId: String
public let secretAccessKey: String
public let sessionToken: String?

View File

@ -1,80 +0,0 @@
//
// hash.swift
// AsyncHTTPClient
//
// Created by Adam Fowler on 2019/08/29.
//
import Foundation
// use CAWSSigner if available, otherwise use CommonCrypto
// Package.swift includes CAWSSigner target if we are running on Linux
#if canImport(CAWSSigner)
import CAWSSigner
public func sha256(_ string: String) -> [UInt8] {
let bytes = Array(string.utf8)
return sha256(bytes)
}
public func sha256(_ bytes: [UInt8]) -> [UInt8] {
var hash = [UInt8](repeating: 0, count: Int(SHA256_DIGEST_LENGTH))
SHA256(bytes, bytes.count, &hash)
return hash
}
public func sha256(_ buffer: UnsafeBufferPointer<UInt8>) -> [UInt8] {
var hash = [UInt8](repeating: 0, count: Int(SHA256_DIGEST_LENGTH))
SHA256(buffer.baseAddress, buffer.count, &hash)
return hash
}
func hmac(string: String, key: [UInt8]) -> [UInt8] {
let context = AWS_SIGNER_HMAC_CTX_new()
HMAC_Init_ex(context, key, Int32(key.count), EVP_sha256(), nil)
let bytes = Array(string.utf8)
HMAC_Update(context, bytes, bytes.count)
var digest = [UInt8](repeating: 0, count: Int(EVP_MAX_MD_SIZE))
var length: UInt32 = 0
HMAC_Final(context, &digest, &length)
AWS_SIGNER_HMAC_CTX_free(context)
return Array(digest[0..<Int(length)])
}
#elseif canImport(CommonCrypto)
import CommonCrypto
public func sha256(_ string: String) -> [UInt8] {
let bytes = Array(string.utf8)
return sha256(bytes)
}
public func sha256(_ bytes: [UInt8]) -> [UInt8] {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(bytes, CC_LONG(bytes.count), &hash)
return hash
}
public func sha256(_ buffer: UnsafeBufferPointer<UInt8>) -> [UInt8] {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(buffer.baseAddress, CC_LONG(buffer.count), &hash)
return hash
}
public func hmac(string: String, key: [UInt8]) -> [UInt8] {
var context = CCHmacContext()
CCHmacInit(&context, CCHmacAlgorithm(kCCHmacAlgSHA256), key, key.count)
let bytes = Array(string.utf8)
CCHmacUpdate(&context, bytes, bytes.count)
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmacFinal(&context, &digest)
return digest
}
#endif

View File

@ -8,22 +8,23 @@
//
import Foundation
import AWSCrypto
import NIO
import NIOHTTP1
/// Amazon Web Services V4 Signer
public final class AWSSigner {
public struct AWSSigner {
/// security credentials for accessing AWS services
public let credentials: CredentialProvider
public let credentials: Credential
/// service signing name. In general this is the same as the service name
public let name: String
/// AWS region you are working in
public let region: String
static let hashedEmptyBody = AWSSigner.hexEncoded(sha256([UInt8]()))
static let hashedEmptyBody = SHA256.hash(data: [UInt8]()).description
/// Initialise the Signer class with AWS credentials
public init(credentials: CredentialProvider, name: String, region: String) {
public init(credentials: Credential, name: String, region: String) {
self.credentials = credentials
self.name = name
self.region = region
@ -144,25 +145,25 @@ public final class AWSSigner {
// 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(string:signingData.date, key:Array("AWS4\(credentials.secretAccessKey)".utf8))
let kRegion = hmac(string: region, key: kDate)
let kService = hmac(string: name, key: kRegion)
let kSigning = hmac(string: "aws4_request", key: kService)
let kSignature = hmac(string: stringToSign(signingData: signingData), key: kSigning)
return AWSSigner.hexEncoded(kSignature)
let kDate = HMAC<SHA256>.authenticationCode(for: Data(signingData.date.utf8), using: Array("AWS4\(credentials.secretAccessKey)".utf8))
let kRegion = HMAC<SHA256>.authenticationCode(for: Data(region.utf8), using: kDate.bytes)
let kService = HMAC<SHA256>.authenticationCode(for: Data(name.utf8), using: kRegion.bytes)
let kSigning = HMAC<SHA256>.authenticationCode(for: Data("aws4_request".utf8), using: kService.bytes)
let kSignature = HMAC<SHA256>.authenticationCode(for: stringToSign(signingData: signingData), using: kSigning.bytes)
return kSignature.description
}
/// 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) -> String {
func stringToSign(signingData: SigningData) -> Data {
let stringToSign = "AWS4-HMAC-SHA256\n" +
"\(signingData.datetime)\n" +
"\(signingData.date)/\(region)/\(name)/aws4_request\n" +
AWSSigner.hexEncoded(sha256(canonicalRequest(signingData: signingData)))
return stringToSign
SHA256.hash(data: canonicalRequest(signingData: signingData)).description
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) -> String {
func canonicalRequest(signingData: SigningData) -> Data {
let canonicalHeaders = signingData.headersToSign.map { return "\($0.key.lowercased()):\($0.value.trimmingCharacters(in: CharacterSet.whitespaces))" }
.sorted()
.joined(separator: "\n")
@ -172,28 +173,26 @@ public final class AWSSigner {
"\(canonicalHeaders)\n\n" +
"\(signingData.signedHeaders)\n" +
signingData.hashedPayload
return canonicalRequest
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 : [UInt8]?
let hash : String?
switch payload {
case .string(let string):
hash = sha256(string)
hash = SHA256.hash(data: Data(string.utf8)).description
case .data(let data):
hash = data.withUnsafeBytes { bytes in
return sha256(bytes.bindMemory(to: UInt8.self))
}
hash = SHA256.hash(data: data).description
case .byteBuffer(let byteBuffer):
let byteBufferView = byteBuffer.readableBytesView
hash = byteBufferView.withContiguousStorageIfAvailable { bytes in
return sha256(bytes)
return SHA256.hash(bytes: .init(bytes)).description
}
}
if let hash = hash {
return AWSSigner.hexEncoded(hash)
return hash
} else {
return hashedEmptyBody
}

View File

@ -1,5 +1,5 @@
//
// c_awssdk_openssl.h
// c_awscrypto.h
// AWSSDKSwiftCore
//
// Created by Adam Fowler on 2019/08/08.
@ -8,11 +8,18 @@
#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 *AWS_SIGNER_HMAC_CTX_new();
void AWS_SIGNER_HMAC_CTX_free(HMAC_CTX* ctx);
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

@ -5,12 +5,14 @@
// 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_aws_signer.h"
#include "c_awscrypto.h"
#include <string.h>
HMAC_CTX *AWS_SIGNER_HMAC_CTX_new() {
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) {
@ -22,7 +24,7 @@ HMAC_CTX *AWS_SIGNER_HMAC_CTX_new() {
#endif
}
void AWS_SIGNER_HMAC_CTX_free(HMAC_CTX* ctx) {
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);
@ -32,3 +34,23 @@ void AWS_SIGNER_HMAC_CTX_free(HMAC_CTX* ctx) {
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

@ -0,0 +1,45 @@
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 = MD5.hash(data: data)
print(digest)
XCTAssertEqual(digest.description, "3abdd8d79be09bc250d60ada1f000912")
}
func testSHA256() {
let data = createRandomBuffer(872, 12489, size: 562741)
let digest = SHA256.hash(data: data)
print(digest)
XCTAssertEqual(digest.description, "3cff070559024d8652d1257e5f455787e95ebd8e95378d62df1a466f78860f74")
}
func testHMAC() {
let data = createRandomBuffer(1, 91, size: 347237)
let key = createRandomBuffer(102, 3, size: 32)
let authenticationKey = HMAC<SHA256>.authenticationCode(for: data, using: key)
print(authenticationKey)
XCTAssertEqual(authenticationKey.description, "ddec250211f1b546254bab3fb027af1acc4842898e8af6eeadcdbf8e2c6c1ff5")
}
}

View File

@ -4,7 +4,7 @@ import NIO
@testable import AWSSigner
final class AWSSignerTests: XCTestCase {
let credentials : CredentialProvider = Credential(accessKeyId: "MYACCESSKEY", secretAccessKey: "MYSECRETACCESSKEY")
let credentials : Credential = StaticCredential(accessKeyId: "MYACCESSKEY", secretAccessKey: "MYSECRETACCESSKEY")
func testSignGetHeaders() {
let signer = AWSSigner(credentials: credentials, name: "glacier", region:"us-east-1")

5
docker/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM swift:5.1.3 as builder
RUN apt-get -qq update && apt-get install -y \
libssl-dev zlib1g-dev \
&& rm -r /var/lib/apt/lists/*

17
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,17 @@
# run this with docker-compose -f docker/docker-compose.yml run test
version: "3.3"
services:
common: &common
build: .
volumes:
- ..:/working
working_dir: /working
test:
<<: *common
command: /bin/bash -xcl "swift test"
shell:
<<: *common
entrypoint: /bin/bash