Initial commit

This commit is contained in:
oscarz
2024-08-12 10:49:20 +08:00
parent 3002510aaf
commit 00fd0adf89
331 changed files with 53210 additions and 130 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
//
// CryptorRSAConstants.swift
// CryptorRSA
//
// Created by Bill Abt on 1/18/17.
//
// Copyright © 2017 IBM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
// MARK: -
@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *)
public extension CryptorRSA {
// MARK: Constants
// MARK: Certificate Suffixes
/// X509 Certificate Extension
static let CER_SUFFIX: String = ".cer"
/// PEM Suffix
static let PEM_SUFFIX: String = ".pem"
/// DER Suffix
static let DER_SUFFIX: String = ".der"
// MARK: PEM Certificate Markers
/// PEM Begin Marker
static let PEM_BEGIN_MARKER: String = "-----BEGIN CERTIFICATE-----"
/// PEM End Marker
static let PEM_END_MARKER: String = "-----END CERTIFICATE-----"
// MARK: Public Key Markers
/// PK Begin Marker
static let PK_BEGIN_MARKER: String = "-----BEGIN PUBLIC KEY-----"
/// PK End Marker
static let PK_END_MARKER: String = "-----END PUBLIC KEY-----"
// MARK: Private Key Markers
/// SK Begin Marker
static let SK_BEGIN_MARKER: String = "-----BEGIN RSA PRIVATE KEY-----"
/// SK End Marker
static let SK_END_MARKER: String = "-----END RSA PRIVATE KEY-----"
// MARK: Generic Key Markers
/// Generic Begin Marker
static let GENERIC_BEGIN_MARKER: String = "-----BEGIN"
/// Generic End Marker
static let GENERIC_END_MARKER: String = "-----END"
}

View File

@ -0,0 +1,329 @@
//
// CryptorRSADigest.swift
// CryptorRSA
//
// Created by Bill Abt on 1/18/17.
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import CommonCrypto
#elseif os(Linux)
import OpenSSL
public typealias CC_LONG = size_t
#endif
import Foundation
// MARK: -- RSA Digest Extension for Data
///
/// Digest Handling Extension
///
extension Data {
// MARK: Enums
///
/// Enumerates available Digest algorithms
///
public enum Algorithm {
/// Secure Hash Algorithm 1
case sha1
/// Secure Hash Algorithm 2 224-bit
case sha224
/// Secure Hash Algorithm 2 256-bit
case sha256
/// Secure Hash Algorithm 2 384-bit
case sha384
/// Secure Hash Algorithm 2 512-bit
case sha512
/// Secure Hash Algorithm 1 using AES-GCM envelope encryption.
/// use this algorithm for cross platform encryption/decryption.
case gcm
/// Digest Length
public var length: CC_LONG {
#if os(Linux)
switch self {
case .sha1:
return CC_LONG(SHA_DIGEST_LENGTH)
case .sha224:
return CC_LONG(SHA224_DIGEST_LENGTH)
case .sha256:
return CC_LONG(SHA256_DIGEST_LENGTH)
case .sha384:
return CC_LONG(SHA384_DIGEST_LENGTH)
case .sha512:
return CC_LONG(SHA512_DIGEST_LENGTH)
case .gcm:
return CC_LONG(SHA_DIGEST_LENGTH)
}
#else
switch self {
case .sha1:
return CC_LONG(CC_SHA1_DIGEST_LENGTH)
case .sha224:
return CC_LONG(CC_SHA224_DIGEST_LENGTH)
case .sha256:
return CC_LONG(CC_SHA256_DIGEST_LENGTH)
case .sha384:
return CC_LONG(CC_SHA384_DIGEST_LENGTH)
case .sha512:
return CC_LONG(CC_SHA512_DIGEST_LENGTH)
case .gcm:
return CC_LONG(CC_SHA1_DIGEST_LENGTH)
}
#endif
}
#if os(Linux)
// Hash, padding type
public var algorithmForSignature: (OpaquePointer?, Int32) {
switch self {
case .sha1:
return (.init(EVP_sha1()), RSA_PKCS1_PADDING)
case .sha224:
return (.init(EVP_sha224()), RSA_PKCS1_PADDING)
case .sha256:
return (.init(EVP_sha256()), RSA_PKCS1_PADDING)
case .sha384:
return (.init(EVP_sha384()), RSA_PKCS1_PADDING)
case .sha512:
return (.init(EVP_sha512()), RSA_PKCS1_PADDING)
case .gcm:
return (.init(EVP_sha1()), RSA_PKCS1_PADDING)
}
}
// HMAC type, symmetric encryption, padding type
public var alogrithmForEncryption: (OpaquePointer?, OpaquePointer?, Int32) {
switch self {
case .sha1:
return (.init(EVP_sha1()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING)
case .sha224:
return (.init(EVP_sha224()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING)
case .sha256:
return (.init(EVP_sha256()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING)
case .sha384:
return (.init(EVP_sha384()), .init(EVP_aes_256_cbc()), RSA_PKCS1_OAEP_PADDING)
case .sha512:
return (.init(EVP_sha512()), .init(EVP_aes_128_gcm()), RSA_PKCS1_OAEP_PADDING)
case .gcm:
return (.init(EVP_sha1()), .init(EVP_aes_128_gcm()), RSA_PKCS1_OAEP_PADDING)
}
}
#else
@available(macOS 10.12, iOS 10.0, watchOS 3.3, tvOS 12.0, *)
public var algorithmForSignature: SecKeyAlgorithm {
switch self {
case .sha1:
return .rsaSignatureMessagePKCS1v15SHA1
case .sha224:
return .rsaSignatureMessagePKCS1v15SHA224
case .sha256:
return .rsaSignatureMessagePKCS1v15SHA256
case .sha384:
return .rsaSignatureMessagePKCS1v15SHA384
case .sha512:
return .rsaSignatureMessagePKCS1v15SHA512
case .gcm:
return .rsaSignatureMessagePKCS1v15SHA1
}
}
@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4.0, *)
var algorithmForPssSignature: SecKeyAlgorithm {
switch self {
case .sha1:
return .rsaSignatureMessagePSSSHA1
case .sha224:
return .rsaSignatureMessagePSSSHA224
case .sha256:
return .rsaSignatureMessagePSSSHA256
case .sha384:
return .rsaSignatureMessagePSSSHA384
case .sha512:
return .rsaSignatureMessagePSSSHA512
case .gcm:
return .rsaSignatureMessagePSSSHA1
}
}
@available(macOS 10.12, iOS 10.0, watchOS 3.3, tvOS 12.0, *)
public var alogrithmForEncryption: SecKeyAlgorithm {
switch self {
case .sha1:
return .rsaEncryptionOAEPSHA1AESGCM
case .sha224:
return .rsaEncryptionOAEPSHA224AESGCM
case .sha256:
return .rsaEncryptionOAEPSHA256AESGCM
case .sha384:
return .rsaEncryptionOAEPSHA384AESGCM
case .sha512:
return .rsaEncryptionOAEPSHA512AESGCM
case .gcm:
return .rsaEncryptionOAEPSHA1AESGCM
}
}
#endif
/// The platform/alogorithm dependent function to be used.
/// (UnsafePointer<UInt8>!, Int, UnsafeMutablePointer<UInt8>!) -> UnsafeMutablePointer<UInt8>!
#if os(Linux)
public var engine: (_ data: UnsafePointer<UInt8>, _ len: CC_LONG, _ md: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8>? {
switch self {
case .sha1:
return SHA1
case .sha224:
return SHA224
case .sha256:
return SHA256
case .sha384:
return SHA384
case .sha512:
return SHA512
case .gcm:
return SHA1
}
}
#else
public var engine: (_ data: UnsafeRawPointer, _ len: CC_LONG, _ md: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8>? {
switch self {
case .sha1:
return CC_SHA1
case .sha224:
return CC_SHA224
case .sha256:
return CC_SHA256
case .sha384:
return CC_SHA384
case .sha512:
return CC_SHA512
case .gcm:
return CC_SHA1
}
}
#endif
}
// MARK: Functions
///
/// Return a digest of the data based on the alogorithm selected.
///
/// - Parameters:
/// - alogorithm: The digest `Alogorithm` to use.
///
/// - Returns: `Data` containing the data in digest form.
///
public func digest(using alogorithm: Algorithm) throws -> Data {
var hash = [UInt8](repeating: 0, count: Int(alogorithm.length))
self.withUnsafeBytes { ptr in
guard let baseAddress = ptr.baseAddress else { return }
_ = alogorithm.engine(baseAddress.assumingMemoryBound(to: UInt8.self), CC_LONG(self.count), &hash)
}
return Data(hash)
}
}

View File

@ -0,0 +1,108 @@
//
// CryptorRSAErrors.swift
// CryptorRSA
//
// Created by Bill Abt on 1/18/17.
//
// Copyright © 2017 IBM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
// MARK: -
@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *)
extension CryptorRSA {
// MARK: Constants
// MARK: -- Generic
// MARK: -- Errors: Domain and Codes
public static let ERR_DOMAIN = "com.ibm.oss.CryptorRSA.ErrorDomain"
public static let ERR_ADD_KEY = -9999
public static let ERR_DELETE_KEY = -9998
public static let ERR_STRIP_PK_HEADER = -9997
public static let ERR_INIT_PK = -9996
public static let ERR_BASE64_PEM_DATA = -9995
public static let ERR_STRING_ENCODING = -9994
public static let ERR_KEY_NOT_PUBLIC = -9993
public static let ERR_KEY_NOT_PRIVATE = -9992
public static let ERR_NOT_ENCRYPTED = -9991
public static let ERR_ENCRYPTION_FAILED = -9990
public static let ERR_NOT_SIGNED_DATA = -9989
public static let ERR_NOT_PLAINTEXT = -9988
public static let ERR_DECRYPTION_FAILED = -9997
public static let ERR_SIGNING_FAILED = -9986
public static let ERR_VERIFICATION_FAILED = -9985
public static let ERR_CREATE_CERT_FAILED = -9984
public static let ERR_EXTRACT_PUBLIC_KEY_FAILED = -9983
public static let ERR_EXTRACT_PRIVATE_KEY_FAILED = -9983
public static let ERR_NOT_IMPLEMENTED = -9982
// MARK: -- Error
///
/// `RSA` specific error structure.
///
public struct Error: Swift.Error, CustomStringConvertible {
// MARK: -- Public Properties
///
/// The error domain.
///
public let domain: String = ERR_DOMAIN
///
/// The error code: **see constants above for possible errors** (Readonly)
///
public internal(set) var errorCode: Int32
///
/// The reason for the error **(if available)** (Readonly)
///
public internal(set) var errorReason: String?
///
/// Returns a string description of the error. (Readonly)
///
public var description: String {
let reason: String = self.errorReason ?? "Reason: Unavailable"
return "Error code: \(self.errorCode)(0x\(String(self.errorCode, radix: 16, uppercase: true))), \(reason)"
}
// MARK: -- Public Functions
///
/// Initializes an Error Instance
///
/// - Parameters:
/// - code: Error code
/// - reason: Optional Error Reason
///
/// - Returns: Error instance
///
public init(code: Int, reason: String?) {
self.errorCode = Int32(code)
self.errorReason = reason
}
}
}

View File

@ -0,0 +1,932 @@
//
// CryptorRSAKey.swift
// CryptorRSA
//
// Created by Bill Abt on 1/18/17.
//
// Copyright © 2017 IBM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import CommonCrypto
#elseif os(Linux)
import OpenSSL
#endif
import Foundation
// MARK: -
@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *)
extension CryptorRSA {
// MARK: Type Aliases
#if os(Linux)
public typealias NativeKey = OpaquePointer?
#else
public typealias NativeKey = SecKey
#endif
// MARK: Class Functions
// MARK: -- Public Key Creation
///
/// Creates a public key with DER data.
///
/// - Parameters:
/// - data: Key data
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(with data: Data) throws -> PublicKey {
return try PublicKey(with: data)
}
///
/// Creates a public key by extracting it from a certificate.
///
/// - Parameters:
/// - data: `Data` representing the certificate.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(extractingFrom data: Data) throws -> PublicKey {
// Extact the data as a base64 string...
let str = String(data: data, encoding: .utf8)
guard let tmp = str else {
throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Unable to create certificate from certificate data, incorrect format.")
}
// Get the Base64 representation of the PEM encoded string after stripping off the PEM markers...
let base64 = try CryptorRSA.base64String(for: tmp)
let data = Data(base64Encoded: base64)!
// Call the internal function to finish up...
return try CryptorRSA.createPublicKey(data: data)
}
///
/// Creates a key with a base64-encoded string.
///
/// - Parameters:
/// - base64String: Base64-encoded key data
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(withBase64 base64String: String) throws -> PublicKey {
guard let data = Data(base64Encoded: base64String, options: [.ignoreUnknownCharacters]) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't decode base64 string")
}
return try PublicKey(with: data)
}
///
/// Creates a key with a PEM string.
///
/// - Parameters:
/// - pemString: PEM-encoded key string
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(withPEM pemString: String) throws -> PublicKey {
// Get the Base64 representation of the PEM encoded string after stripping off the PEM markers
let base64String = try CryptorRSA.base64String(for: pemString)
return try createPublicKey(withBase64: base64String)
}
///
/// Creates a key with a PEM file.
///
/// - Parameters:
/// - pemName: Name of the PEM file
/// - path: Path where the file is located.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(withPEMNamed pemName: String, onPath path: String) throws -> PublicKey {
var certName = pemName
if !pemName.hasSuffix(PEM_SUFFIX) {
certName = pemName.appending(PEM_SUFFIX)
}
let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized
let keyString = try String(contentsOf: fullPath, encoding: .utf8)
return try createPublicKey(withPEM: keyString)
}
///
/// Creates a key with a DER file.
///
/// - Parameters:
/// - derName: Name of the DER file
/// - path: Path where the file is located.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(withDERNamed derName: String, onPath path: String) throws -> PublicKey {
var certName = derName
if !derName.hasSuffix(DER_SUFFIX) {
certName = derName.appending(DER_SUFFIX)
}
let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized
let data = try Data(contentsOf: fullPath)
return try PublicKey(with: data)
}
///
/// Creates a public key by extracting it from a certificate.
///
/// - Parameters:
/// - certName: Name of the certificate file.
/// - path: Path where the file is located.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(extractingFrom certName: String, onPath path: String) throws -> PublicKey {
var certNameFull = certName
if !certName.hasSuffix(CER_SUFFIX) {
certNameFull = certName.appending(CER_SUFFIX)
}
let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certNameFull) ).standardized
// Get the Base64 representation of the PEM encoded string after stripping off the PEM markers...
let tmp = try String(contentsOf: fullPath, encoding: .utf8)
let base64 = try CryptorRSA.base64String(for: tmp)
let data = Data(base64Encoded: base64)!
return try CryptorRSA.createPublicKey(data: data)
}
///
/// Creates a key with a PEM file.
///
/// - Parameters:
/// - pemName: Name of the PEM file
/// - bundle: Bundle in which to look for the PEM file. Defaults to the main bundle.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws -> PublicKey {
guard let path = bundle.path(forResource: pemName, ofType: PEM_SUFFIX) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't find a PEM file named '\(pemName)'")
}
let keyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
return try createPublicKey(withPEM: keyString)
}
///
/// Creates a key with a DER file.
///
/// - Parameters:
/// - derName: Name of the DER file
/// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws -> PublicKey {
guard let path = bundle.path(forResource: derName, ofType: DER_SUFFIX) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't find a DER file named '\(derName)'")
}
let data = try Data(contentsOf: URL(fileURLWithPath: path))
return try PublicKey(with: data)
}
///
/// Creates a public key by extracting it from a certificate.
///
/// - Parameters:
/// - certName: Name of the certificate file.
/// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle.
///
/// - Returns: New `PublicKey` instance.
///
public class func createPublicKey(extractingFrom certName: String, in bundle: Bundle = Bundle.main) throws -> PublicKey {
guard let path = bundle.path(forResource: certName, ofType: CER_SUFFIX) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't find a certificate file named '\(certName)'")
}
// Import the data from the file...
let tmp = try String(contentsOf: URL(fileURLWithPath: path))
let base64 = try CryptorRSA.base64String(for: tmp)
let data = Data(base64Encoded: base64)!
// Call the internal function to finish up...
return try CryptorRSA.createPublicKey(data: data)
}
///
/// Creates a public key by extracting it from certificate data.
///
/// - Parameters:
/// - data: `Data` representing the certificate.
///
/// - Returns: New `PublicKey` instance.
///
internal class func createPublicKey(data: Data) throws -> PublicKey {
#if os(Linux)
let certbio = BIO_new(BIO_s_mem())
defer {
BIO_free(certbio)
}
// Move the key data to BIO
try data.withUnsafeBytes() { (buffer: UnsafeRawBufferPointer) in
let len = BIO_write(certbio, buffer.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(data.count))
guard len != 0 else {
let source = "Couldn't create BIO reference from key data"
if let reason = CryptorRSA.getLastError(source: source) {
throw Error(code: ERR_ADD_KEY, reason: reason)
}
throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.")
}
// The below is equivalent of BIO_flush...
BIO_ctrl(certbio, BIO_CTRL_FLUSH, 0, nil)
}
let cert = d2i_X509_bio(certbio, nil)
if cert == nil {
print("Error loading cert into memory\n")
throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Error loading cert into memory.")
}
// Extract the certificate's public key data.
let evp_key: OpaquePointer? = .init(X509_get_pubkey(cert))
if evp_key == nil {
throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Error getting public key from certificate")
}
return PublicKey(with: evp_key)
#else
// Create a DER-encoded X.509 certificate object from the DER data...
let certificateData = SecCertificateCreateWithData(nil, data as CFData)
guard let certData = certificateData else {
throw Error(code: ERR_CREATE_CERT_FAILED, reason: "Unable to create certificate from certificate data.")
}
var key: SecKey? = nil
#if swift(>=4.2)
#if os(macOS)
if #available(macOS 10.14, *) {
key = SecCertificateCopyKey(certData)
} else {
// Now extract the public key from it...
let status: OSStatus = withUnsafeMutablePointer(to: &key) { ptr in
// Retrieves the public key from a certificate...
SecCertificateCopyPublicKey(certData, UnsafeMutablePointer(ptr))
}
if status != errSecSuccess {
throw Error(code: ERR_EXTRACT_PUBLIC_KEY_FAILED, reason: "Unable to extract public key from data.")
}
}
#else
let copyKey: (SecCertificate) -> SecKey?
#if targetEnvironment(macCatalyst)
copyKey = SecCertificateCopyKey
#else
if #available(iOS 12.0, watchOS 5.0, *) {
copyKey = SecCertificateCopyKey
} else {
copyKey = SecCertificateCopyPublicKey
}
#endif
key = copyKey(certData)
#endif
#else
#if os(macOS)
// Now extract the public key from it...
let status: OSStatus = withUnsafeMutablePointer(to: &key) { ptr in
// Retrieves the public key from a certificate...
SecCertificateCopyPublicKey(certData, UnsafeMutablePointer(ptr))
}
if status != errSecSuccess {
throw Error(code: ERR_EXTRACT_PUBLIC_KEY_FAILED, reason: "Unable to extract public key from data.")
}
#else
key = SecCertificateCopyPublicKey(certData)
#endif
#endif
guard let createdKey = key else {
throw Error(code: ERR_EXTRACT_PUBLIC_KEY_FAILED, reason: "Unable to extract public key from data.")
}
return PublicKey(with: createdKey)
#endif
}
// MARK: -- Private Key Creation
///
/// Creates a private key with data.
///
/// - Parameters:
/// - data: Key data
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(with data: Data) throws -> PrivateKey {
return try PrivateKey(with: data)
}
///
/// Creates a key with a base64-encoded string.
///
/// - Parameters:
/// - base64String: Base64-encoded key data
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(withBase64 base64String: String) throws -> PrivateKey {
guard let data = Data(base64Encoded: base64String, options: [.ignoreUnknownCharacters]) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't decode base 64 string")
}
return try PrivateKey(with: data)
}
///
/// Creates a key with a PEM string.
///
/// - Parameters:
/// - pemString: PEM-encoded key string
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(withPEM pemString: String) throws -> PrivateKey {
let base64String = try CryptorRSA.base64String(for: pemString)
return try CryptorRSA.createPrivateKey(withBase64: base64String)
}
///
/// Creates a key with a PEM file.
///
/// - Parameters:
/// - pemName: Name of the PEM file
/// - path: Path where the file is located.
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(withPEMNamed pemName: String, onPath path: String) throws -> PrivateKey {
var certName = pemName
if !pemName.hasSuffix(PEM_SUFFIX) {
certName = pemName.appending(PEM_SUFFIX)
}
let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized
let keyString = try String(contentsOf: fullPath, encoding: .utf8)
return try CryptorRSA.createPrivateKey(withPEM: keyString)
}
///
/// Creates a key with a DER file.
///
/// - Parameters:
/// - derName: Name of the DER file
/// - path: Path where the file is located.
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(withDERNamed derName: String, onPath path: String) throws -> PrivateKey {
var certName = derName
if !derName.hasSuffix(DER_SUFFIX) {
certName = derName.appending(DER_SUFFIX)
}
let fullPath = URL(fileURLWithPath: #file).appendingPathComponent( path.appending(certName) ).standardized
let data = try Data(contentsOf: fullPath)
return try PrivateKey(with: data)
}
///
/// Creates a key with a PEM file.
///
/// - Parameters:
/// - pemName: Name of the PEM file
/// - bundle: Bundle in which to look for the PEM file. Defaults to the main bundle.
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws -> PrivateKey {
guard let path = bundle.path(forResource: pemName, ofType: PEM_SUFFIX) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't find a PEM file named '\(pemName)'")
}
let keyString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
return try CryptorRSA.createPrivateKey(withPEM: keyString)
}
///
/// Creates a key with a DER file.
///
/// - Parameters:
/// - derName: Name of the DER file
/// - bundle: Bundle in which to look for the DER file. Defaults to the main bundle.
///
/// - Returns: New `PrivateKey` instance.
///
public class func createPrivateKey(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws -> PrivateKey {
guard let path = bundle.path(forResource: derName, ofType: DER_SUFFIX) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't find a DER file named '\(derName)'")
}
let data = try Data(contentsOf: URL(fileURLWithPath: path))
return try PrivateKey(with: data)
}
/// Create a new RSA public/private key pair.
///
/// - Parameters:
/// - keySize: The size of the generated RSA keys in bits.
///
/// - Returns: A tuple containing the (`PrivateKey`, `PublicKey`) instances.
///
public class func makeKeyPair(_ keySize: RSAKey.KeySize) throws -> (PrivateKey, PublicKey) {
#if os(Linux)
var pkey = EVP_PKEY_new()
let ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nil)
defer {
EVP_PKEY_CTX_free(ctx)
}
guard EVP_PKEY_keygen_init(ctx) == 1,
EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, Int32(keySize.bits), nil) == 1,
EVP_PKEY_keygen(ctx, &pkey) == 1
else {
EVP_PKEY_free(pkey)
throw Error(code: ERR_INIT_PK, reason: "Could not generate rsa pair for \(keySize.bits) bits")
}
let privKey = PrivateKey(with: .make(optional: pkey))
let publicPem = try RSAKey.getPEMString(reference: privKey.reference, keyType: .publicType, stripped: false)
let pubKey = try CryptorRSA.createPublicKey(withPEM: publicPem)
return(privKey, pubKey)
#else
let parameters: [String: AnyObject] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits as String: keySize.bits as AnyObject,
kSecPublicKeyAttrs as String: [ kSecAttrIsPermanent as String: true as AnyObject ] as AnyObject,
kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: true as AnyObject ] as AnyObject,
]
var pubKey, privKey: SecKey?
let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)
guard status == 0, let newPubKey = pubKey, let newPrivKey = privKey else {
throw Error(code: ERR_INIT_PK, reason: "Could not generate rsa pair for \(keySize.bits) bits")
}
let privateKey = PrivateKey(with: newPrivKey)
let publicKey = PublicKey(with: newPubKey)
return (privateKey, publicKey)
#endif
}
// MARK: -
///
/// RSA Key Creation and Handling
///
public class RSAKey {
// MARK: Enums
/// Denotes the type of key this represents.
public enum KeyType {
/// Public
case publicType
/// Private
case privateType
}
/// Denotes the size of the RSA key.
public struct KeySize {
let bits: Int
/// A 1024 bit RSA key. Not recommended since this may become breakable in the near future.
public static let bits1024 = KeySize(bits: 1024)
/// A 2048 bit RSA key. Recommended if security will not be required beyond 2030.
public static let bits2048 = KeySize(bits: 2048)
/// A 3072 bit RSA key. Recommended if security is required beyond 2030.
public static let bits3072 = KeySize(bits: 3072)
/// A 4096 bit RSA key.
public static let bits4096 = KeySize(bits: 4096)
}
// MARK: Properties
/// The RSA key as a PKCS#1 PEM String
public let pemString: String
/// The stored key
internal let reference: NativeKey
#if os(Linux)
var publicKeyBytes: Data?
deinit {
EVP_PKEY_free(.make(optional: reference))
}
#endif
/// Represents the type of key data contained.
public internal(set) var type: KeyType = .publicType
// MARK: Initializers
///
/// Create a key using key data (in DER format).
///
/// - Parameters:
/// - data: Key data.
/// - type: Type of key data.
///
/// - Returns: New `RSAKey` instance.
///
internal init(with data: Data, type: KeyType) throws {
var data = data
// If data is a PEM String, strip the headers and convert to der.
if let pemString = String(data: data, encoding: .utf8),
let base64String = try? CryptorRSA.base64String(for: pemString),
let base64Data = Data(base64Encoded: base64String) {
data = base64Data
}
data = try CryptorRSA.stripX509CertificateHeader(for: data)
self.pemString = CryptorRSA.convertDerToPem(from: data, type: type)
self.type = type
reference = try CryptorRSA.createKey(from: data, type: type)
#if os(Linux)
if let pubString = try? RSAKey.getPEMString(reference: reference, keyType: .publicType, stripped: true),
let base64String = try? CryptorRSA.base64String(for: pubString),
let derData = Data(base64Encoded: base64String) {
self.publicKeyBytes = derData
}
#endif
}
///
/// Create a key using a native key.
///
/// - Parameters:
/// - nativeKey: Native key representation.
/// - type: Type of key.
///
/// - Returns: New `RSAKey` instance.
///
internal init(with nativeKey: NativeKey, type: KeyType) {
self.type = type
self.reference = nativeKey
self.pemString = (try? RSAKey.getPEMString(reference: nativeKey, type: type)) ?? ""
#if os(Linux)
if let base64String = try? CryptorRSA.base64String(for: pemString),
let derData = Data(base64Encoded: base64String) {
self.publicKeyBytes = derData
}
#endif
}
#if os(Linux) && !swift(>=4.1)
///
/// Create a key using a native key.
///
/// - Parameters:
/// - nativeKey: Pointer to RSA key structure.
/// - type: Type of key.
///
/// - Returns: New `RSAKey` instance.
///
internal init(with nativeKey: UnsafeMutablePointer<EVP_PKEY>, type: KeyType) {
self.type = type
self.reference = .make(optional: nativeKey)
self.pemString = (try? RSAKey.getPEMString(reference: .init(nativeKey), type: type)) ?? ""
if let base64String = try? CryptorRSA.base64String(for: pemString),
let derData = Data(base64Encoded: base64String) {
self.publicKeyBytes = derData
}
}
#endif
///
/// Get the RSA key as a PEM String.
///
/// - Returns: The RSA Key in PEM format.
///
static func getPEMString(reference: NativeKey, type: KeyType) throws -> String {
#if os(Linux)
return try getPEMString(reference: reference, keyType: type, stripped: true)
#else
var error: Unmanaged<CFError>? = nil
guard let keyBytes = SecKeyCopyExternalRepresentation(reference, &error) else {
guard let error = error?.takeRetainedValue() else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't read PEM String")
}
throw error
}
return CryptorRSA.convertDerToPem(from: keyBytes as Data, type: type)
#endif
}
#if os(Linux)
///
/// Get a PEM string of a native key.
///
/// - Parameters:
/// - reference: Native key.
/// - keyType: Type of key.
/// - stripped: `true` to return string stripped, `false` otherwise.
///
/// - Returns: The PEM string.
///
static func getPEMString(reference: NativeKey, keyType: KeyType, stripped: Bool) throws -> String {
let asn1Bio = BIO_new(BIO_s_mem())
defer { BIO_free_all(asn1Bio) }
if keyType == .publicType {
PEM_write_bio_PUBKEY(asn1Bio, .make(optional: reference))
} else {
PEM_write_bio_PrivateKey(asn1Bio, .make(optional: reference), nil, nil, 0, nil, nil)
}
// 4096 bit rsa PEM key is 3272 bytes of data
let asn1 = UnsafeMutablePointer<UInt8>.allocate(capacity: 3500)
let readLength = BIO_read(asn1Bio, asn1, 3500)
let pemData = Data(bytes: asn1, count: Int(readLength))
#if swift(>=4.1)
asn1.deallocate()
#else
asn1.deallocate(capacity: 3500)
#endif
guard let pemString = String(data: pemData, encoding: .utf8) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't utf8 decode pemString")
}
if !stripped {
return pemString
} else {
let derString = try CryptorRSA.base64String(for: pemString)
guard let derData = Data(base64Encoded: derString) else {
throw Error(code: ERR_INIT_PK, reason: "Couldn't read PEM String")
}
let strippedDer = try CryptorRSA.stripX509CertificateHeader(for: derData)
let pkcs1PEM = CryptorRSA.convertDerToPem(from: strippedDer, type: keyType)
return pkcs1PEM
}
}
#endif
}
// MARK: -
///
/// Public Key - Represents public key data.
///
public class PublicKey: RSAKey {
/// MARK: Statics
/// Regular expression for the PK using the begin and end markers.
static let publicKeyRegex: NSRegularExpression? = {
let publicKeyRegex = "(\(CryptorRSA.PK_BEGIN_MARKER).+?\(CryptorRSA.PK_END_MARKER))"
return try? NSRegularExpression(pattern: publicKeyRegex, options: .dotMatchesLineSeparators)
}()
// MARK: -- Static Functions
///
/// Takes an input string, scans for public key sections, and then returns a Key for any valid keys found
/// - This method scans the file for public key armor - if no keys are found, an empty array is returned
/// - Each public key block found is "parsed" by `publicKeyFromPEMString()`
/// - should that method throw, the error is _swallowed_ and not rethrown
///
/// - Parameters:
/// - pemString: The string to use to parse out values
///
/// - Returns: An array of `PublicKey` objects containing just public keys.
///
public static func publicKeys(withPEM pemString: String) -> [PublicKey] {
// If our regexp isn't valid, or the input string is empty, we can't move forward
guard let publicKeyRegexp = publicKeyRegex, pemString.count > 0 else {
return []
}
let all = NSRange(
location: 0,
length: pemString.count
)
let matches = publicKeyRegexp.matches(
in: pemString,
options: NSRegularExpression.MatchingOptions(rawValue: 0),
range: all
)
#if swift(>=4.1)
let keys = matches.compactMap { result -> PublicKey? in
let match = result.range(at: 1)
let start = pemString.index(pemString.startIndex, offsetBy: match.location)
let end = pemString.index(start, offsetBy: match.length)
let range = start..<end
let thisKey = pemString[range]
return try? CryptorRSA.createPublicKey(withPEM: String(thisKey))
}
#else
let keys = matches.flatMap { result -> PublicKey? in
let match = result.range(at: 1)
let start = pemString.index(pemString.startIndex, offsetBy: match.location)
let end = pemString.index(start, offsetBy: match.length)
let range = start..<end
let thisKey = pemString[range]
return try? CryptorRSA.createPublicKey(withPEM: String(thisKey))
}
#endif
return keys
}
// MARK: -- Initializers
///
/// Create a public key using key data.
///
/// - Parameters:
/// - data: Key data
///
/// - Returns: New `PublicKey` instance.
///
public init(with data: Data) throws {
try super.init(with: data, type: .publicType)
}
///
/// Create a key using a native key.
///
/// - Parameters:
/// - nativeKey: Native key representation.
///
/// - Returns: New `PublicKey` instance.
///
public init(with nativeKey: NativeKey) {
super.init(with: nativeKey, type: .publicType)
}
#if os(Linux) && !swift(>=4.1)
///
/// Create a key using a native key.
///
/// - Parameters:
/// - nativeKey: Pointer to RSA key structure.
///
/// - Returns: New `RSAKey` instance.
///
public init(with nativeKey: UnsafeMutablePointer<EVP_PKEY>) {
super.init(with: nativeKey, type: .publicType)
}
#endif
}
// MARK: -
///
/// Private Key - Represents private key data.
///
public class PrivateKey: RSAKey {
// MARK: -- Initializers
///
/// Create a private key using key data.
///
/// - Parameters:
/// - data: Key data
///
/// - Returns: New `PrivateKey` instance.
///
public init(with data: Data) throws {
try super.init(with: data, type: .privateType)
}
///
/// Create a key using a native key.
///
/// - Parameters:
/// - nativeKey: Native key representation.
///
/// - Returns: New `PrivateKey` instance.
///
public init(with nativeKey: NativeKey) {
super.init(with: nativeKey, type: .privateType)
}
}
}

View File

@ -0,0 +1,393 @@
//
// Utilities.swift
// CryptorRSA
//
// Created by Bill Abt on 1/17/17.
//
// Copyright © 2017 IBM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import CommonCrypto
#elseif os(Linux)
import OpenSSL
#endif
import Foundation
// MARK: -- RSAUtilities
///
/// Various RSA Related Utility Functions
///
@available(macOS 10.12, iOS 10.3, watchOS 3.3, tvOS 12.0, *)
public extension CryptorRSA {
#if os(Linux)
///
/// Create a key from key data.
///
/// - Parameters:
/// - keyData: `Data` representation of the key.
/// - type: Type of key data.
///
/// - Returns: `RSA` representation of the key.
///
static func createKey(from keyData: Data, type: CryptorRSA.RSAKey.KeyType) throws -> NativeKey {
var keyData = keyData
// If data is a PEM String, strip the headers and convert to der.
if let pemString = String(data: keyData, encoding: .utf8),
let base64String = try? CryptorRSA.base64String(for: pemString),
let base64Data = Data(base64Encoded: base64String) {
keyData = base64Data
}
let headerKey = CryptorRSA.addX509CertificateHeader(for: keyData)
// Create a memory BIO...
let bio = BIO_new(BIO_s_mem())
defer {
BIO_free(bio)
}
// Create a BIO object with the key data...
try headerKey.withUnsafeBytes() { (buffer: UnsafeRawBufferPointer) in
let len = BIO_write(bio, buffer.baseAddress?.assumingMemoryBound(to: UInt8.self), Int32(headerKey.count))
guard len != 0 else {
let source = "Couldn't create BIO reference from key data"
if let reason = CryptorRSA.getLastError(source: source) {
throw Error(code: ERR_ADD_KEY, reason: reason)
}
throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.")
}
// The below is equivalent of BIO_flush...
BIO_ctrl(bio, BIO_CTRL_FLUSH, 0, nil)
}
var evp_key: OpaquePointer?
// Read in the key data and process depending on key type...
if type == .publicType {
evp_key = .init(d2i_PUBKEY_bio(bio, nil))
} else {
evp_key = .init(d2i_PrivateKey_bio(bio, nil))
}
return evp_key
}
///
/// Retrieve the OpenSSL error and text.
///
/// - Parameters:
/// - source: The string describing the error.
///
/// - Returns: `String` containing the error or `nil` if no error found.
///
static func getLastError(source: String) -> String? {
var errorString: String
let errorCode = Int32(ERR_get_error())
if errorCode == 0 {
return nil
}
if let errorStr = ERR_reason_error_string(UInt(errorCode)) {
errorString = String(validatingUTF8: errorStr)!
} else {
errorString = "Could not determine error reason."
}
let reason = "ERROR: \(source), code: \(errorCode), reason: \(errorString)"
return reason
}
#else
///
/// Create a key from key data.
///
/// - Parameters:
/// - keyData: `Data` representation of the key.
/// - type: Type of key data.
///
/// - Returns: `SecKey` representation of the key.
///
static func createKey(from keyData: Data, type: CryptorRSA.RSAKey.KeyType) throws -> NativeKey {
let keyClass = type == .publicType ? kSecAttrKeyClassPublic : kSecAttrKeyClassPrivate
let sizeInBits = keyData.count * MemoryLayout<UInt8>.size
let keyDict: [CFString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: keyClass,
kSecAttrKeySizeInBits: NSNumber(value: sizeInBits)
]
guard let key = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, nil) else {
throw Error(code: ERR_ADD_KEY, reason: "Couldn't create key reference from key data")
}
return key
}
#endif
///
/// Convert DER data to PEM data.
///
/// - Parameters:
/// - derData: `Data` in DER format.
/// - type: Type of key data.
///
/// - Returns: PEM `Data` representation.
///
static func convertDerToPem(from derData: Data, type: CryptorRSA.RSAKey.KeyType) -> String {
// First convert the DER data to a base64 string...
let base64String = derData.base64EncodedString()
// Split the string into strings of length 65...
let lines = base64String.split(to: 65)
// Join those lines with a new line...
let joinedLines = lines.joined(separator: "\n")
// Add the appropriate header and footer depending on whether the key is public or private...
if type == .publicType {
return ("-----BEGIN RSA PUBLIC KEY-----\n" + joinedLines + "\n-----END RSA PUBLIC KEY-----")
} else {
return (CryptorRSA.SK_BEGIN_MARKER + "\n" + joinedLines + "\n" + CryptorRSA.SK_END_MARKER)
}
}
///
/// Get the Base64 representation of a PEM encoded string after stripping off the PEM markers.
///
/// - Parameters:
/// - pemString: `String` containing PEM formatted data.
///
/// - Returns: Base64 encoded `String` containing the data.
///
static func base64String(for pemString: String) throws -> String {
// Filter looking for new lines...
var lines = pemString.components(separatedBy: "\n").filter { line in
return !line.hasPrefix(CryptorRSA.GENERIC_BEGIN_MARKER) && !line.hasPrefix(CryptorRSA.GENERIC_END_MARKER)
}
// No lines, no data...
guard lines.count != 0 else {
throw Error(code: ERR_BASE64_PEM_DATA, reason: "Couldn't get data from PEM key: no data available after stripping headers.")
}
// Strip off any carriage returns...
lines = lines.map { $0.replacingOccurrences(of: "\r", with: "") }
return lines.joined(separator: "")
}
///
/// This function strips the x509 from a provided ASN.1 DER public key. If the key doesn't contain a header,
/// the DER data is returned as is.
///
/// - Parameters:
/// - keyData: `Data` containing the public key with or without the x509 header.
///
/// - Returns: `Data` containing the public with header (if present) removed.
///
static func stripX509CertificateHeader(for keyData: Data) throws -> Data {
// If private key in pkcs8 format, strip the header
if keyData[26] == 0x30 {
return(keyData.advanced(by: 26))
}
let count = keyData.count / MemoryLayout<CUnsignedChar>.size
guard count > 0 else {
throw Error(code: ERR_STRIP_PK_HEADER, reason: "Provided public key is empty")
}
let byteArray = [UInt8](keyData)
var index = 0
guard byteArray[index] == 0x30 else {
throw Error(code: ERR_STRIP_PK_HEADER, reason: "Provided key doesn't have a valid ASN.1 structure (first byte should be 0x30 == SEQUENCE)")
}
index += 1
if byteArray[index] > 0x80 {
index += Int(byteArray[index]) - 0x80 + 1
} else {
index += 1
}
// If current byte marks an integer (0x02), it means the key doesn't have a X509 header and just
// contains its modulo & public exponent. In this case, we can just return the provided DER data as is.
if Int(byteArray[index]) == 0x02 {
return keyData
}
// Now that we've excluded the possibility of headerless key, we're looking for a valid X509 header sequence.
// It should look like this:
// 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00
guard Int(byteArray[index]) == 0x30 else {
throw Error(code: ERR_STRIP_PK_HEADER, reason: "Provided key doesn't have a valid X509 header")
}
index += 15
if byteArray[index] != 0x03 {
throw Error(code: ERR_STRIP_PK_HEADER, reason: "Invalid byte at index \(index - 1) (\(byteArray[index - 1])) for public key header")
}
index += 1
if byteArray[index] > 0x80 {
index += Int(byteArray[index]) - 0x80 + 1
} else {
index += 1
}
guard byteArray[index] == 0 else {
throw Error(code: ERR_STRIP_PK_HEADER, reason: "Invalid byte at index \(index - 1) (\(byteArray[index - 1])) for public key header")
}
index += 1
let strippedKeyBytes = [UInt8](byteArray[index...keyData.count - 1])
let data = Data(bytes: UnsafePointer<UInt8>(strippedKeyBytes), count: keyData.count - index)
return data
}
///
/// Add an X509 certificate header to key data.
///
/// - Parameters:
/// - keyData: Data to add the header to.
///
/// - Returns: The modified key data.
///
static func addX509CertificateHeader(for keyData: Data) -> Data {
if keyData.count == 140 {
return Data([0x30, 0x81, 0x9F,
0x30, 0x0D,
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
0x05, 0x00,
0x03, 0x81, 0x8D, 0x00]) + keyData
} else if keyData.count == 270 {
return Data([0x30, 0x82, 0x01, 0x22,
0x30, 0x0D,
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
0x05, 0x00,
0x03, 0x82, 0x01, 0x0F, 0x00]) + keyData
} else if keyData.count == 398 {
return Data([0x30, 0x82, 0x01, 0xA2,
0x30, 0x0D,
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
0x05, 0x00,
0x03, 0x82, 0x01, 0x8F, 0x00]) + keyData
} else if keyData.count == 526 {
return Data([0x30, 0x82, 0x02, 0x22,
0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
0x05, 0x00,
0x03, 0x82, 0x02, 0x0F, 0x00]) + keyData
} else {
return keyData
}
}
}
extension String {
///
/// Split a string to a specified length.
///
/// - Parameters:
/// - length: Length of each split string.
///
/// - Returns: `[String]` containing each string.
///
func split(to length: Int) -> [String] {
var result = [String]()
var collectedCharacters = [Character]()
collectedCharacters.reserveCapacity(length)
var count = 0
for character in self {
collectedCharacters.append(character)
count += 1
if count == length {
// Reached the desired length
count = 0
result.append(String(collectedCharacters))
collectedCharacters.removeAll(keepingCapacity: true)
}
}
// Append the remainder
if !collectedCharacters.isEmpty {
result.append(String(collectedCharacters))
}
return result
}
}
// MARK: -
#if !os(Linux)
// MARK: -- CFString Extension for Hashing
///
/// Extension to CFString to make it hashable.
///
extension CFString: Hashable {
/// Return the hash value of a CFString
public var hashValue: Int {
return (self as String).hashValue
}
/// Comparison of CFStrings
static public func == (lhs: CFString, rhs: CFString) -> Bool {
return lhs as String == rhs as String
}
}
#endif

View File

@ -0,0 +1,38 @@
// Copyright © 2019 IBM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
#if !swift(>=5.0)
// Extension to allow Swift 5 `withUnsafeBytes` API for earlier versions
internal extension Data {
func withUnsafeBytes<T>(_ body: (UnsafeRawBufferPointer) throws -> T) rethrows -> T {
let c = count
return try withUnsafeBytes { (p: UnsafePointer<UInt8>) throws -> T in
try body(UnsafeRawBufferPointer(start: p, count: c))
}
}
mutating func withUnsafeMutableBytes<T>(_ body: (UnsafeMutableRawBufferPointer) throws -> T) rethrows -> T {
let c = count
return try withUnsafeMutableBytes { (p: UnsafeMutablePointer<UInt8>) throws -> T in
try body(UnsafeMutableRawBufferPointer(start: p, count: c))
}
}
init(_ bytes: [UInt8]) {
self.init(bytes: bytes)
}
}
#endif

View File

@ -0,0 +1,100 @@
//===----------------------------------------------------------------------===//
//
// This source file is taken from SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
//===----------------------------------------------------------------------===//
// MARK:- Awful code begins here
// Hello dear reader. Let me explain what we're doing here.
//
// From OpenSSL 1.0 to OpenSSL 1.1 one of the major breaking changes was the so-called
// "great opaquifiying". Essentially, OpenSSL took all of its public structures and made
// them opaque, such that they cannot be introspected from client code. This is a great
// forward step, and brings them more in line with modern C library practices.
//
// However, it's an *enormous* inconvenience from Swift code. This is because the Swift
// translation of the C type `SSL_CTX *` changed from `UnsafeMutablePointer<SSL_CTX>` to
// `OpaquePointer`.
//
// This change exists for reasonable enough reasons in Swift land (see
// https://forums.swift.org/t/opaque-pointers-in-swift/6875 for a discussion), but
// nonetheless causes enormous problems in our codebase.
//
// Our cheap way out is to make everything an OpaquePointer, and then provide initializers
// between OpaquePointer and the typed pointers. This allows us to tolerate either pointer
// type in our Swift code by bridging them over to OpaquePointer and back, and lets the
// compiler worry about how exactly to make that work.
//
// Now, in fact, Swift already has initializers between the pointer types. What it does
// not have is self-initializers: the ability to create an `OpaquePointer` from an `OpaquePointer`,
// or an `UnsafePointer<T>` from an `UnsafePointer<T>`. We add those two initializers here.
// We also add a special "make" function that exists to handle the special case of optional pointer
// values, which we mostly encounter in the ALPN callbacks.
//
// The *downside* of this approach is that we totally break the pointer type system. It becomes
// trivially possible to alias a pointer of type T to type U through two calls to init. This
// is not a thing we want to widely promote. For this reason, these extensions are hidden in
// this file, where we can laugh and jeer at them and generally make them feel bad about
// themselves.
//
// Hopefully, in time, these extensions can be removed.
extension UnsafePointer {
init(_ ptr: UnsafePointer<Pointee>) {
self = ptr
}
static func make(optional ptr: UnsafePointer<Pointee>?) -> UnsafePointer<Pointee>? {
return ptr.map(UnsafePointer<Pointee>.init)
}
static func make(optional ptr: OpaquePointer?) -> UnsafePointer<Pointee>? {
return ptr.map(UnsafePointer<Pointee>.init)
}
}
extension UnsafeMutablePointer {
init(_ ptr: UnsafeMutableRawPointer) {
let x = UnsafeMutablePointer<Pointee>(bitPattern: UInt(bitPattern: ptr))!
self = x
}
static func make(optional ptr: UnsafeMutablePointer<Pointee>?) -> UnsafeMutablePointer<Pointee>? {
return ptr.map(UnsafeMutablePointer<Pointee>.init)
}
static func make(optional ptr: UnsafeMutableRawPointer?) -> UnsafeMutablePointer<Pointee>? {
return ptr.map(UnsafeMutablePointer<Pointee>.init)
}
static func make(optional ptr: OpaquePointer?) -> UnsafeMutablePointer<Pointee>? {
return ptr.map(UnsafeMutablePointer<Pointee>.init)
}
}
extension UnsafeMutableRawPointer {
static func make(optional ptr: OpaquePointer?) -> UnsafeMutableRawPointer? {
return ptr.map(UnsafeMutableRawPointer.init)
}
}
extension OpaquePointer {
init(_ ptr: OpaquePointer) {
self = ptr
}
static func make(optional ptr: OpaquePointer?) -> OpaquePointer? {
return ptr.map(OpaquePointer.init)
}
static func make(optional ptr: UnsafeMutableRawPointer?) -> OpaquePointer? {
return ptr.map(OpaquePointer.init)
}
static func make<Pointee>(optional ptr: UnsafeMutablePointer<Pointee>?) -> OpaquePointer? {
return ptr.map(OpaquePointer.init)
}
}