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

176
Pods/BlueRSA/LICENSE generated Normal file
View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

318
Pods/BlueRSA/README.md generated Normal file
View File

@ -0,0 +1,318 @@
<p align="center">
<a href="https://www.kitura.io/packages.html#all">
<img src="https://img.shields.io/badge/docs-kitura.io-1FBCE4.svg" alt="APIDoc">
</a>
<a href="https://travis-ci.org/IBM-Swift/BlueRSA">
<img src="https://travis-ci.org/IBM-Swift/BlueRSA.svg?branch=master" alt="Build Status - Master">
</a>
<img src="https://img.shields.io/badge/os-macOS-green.svg?style=flat" alt="macOS">
<img src="https://img.shields.io/badge/os-iOS-green.svg?style=flat" alt="iOS">
<img src="https://img.shields.io/badge/os-linux-green.svg?style=flat" alt="Linux">
<img src="https://img.shields.io/badge/license-Apache2-blue.svg?style=flat" alt="Apache 2">
<a href="http://swift-at-ibm-slack.mybluemix.net/">
<img src="http://swift-at-ibm-slack.mybluemix.net/badge.svg" alt="Slack Status">
</a>
</p>
# BlueRSA
Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux (using OpenSSL) is working but is still somewhat of a work in progress.
## Contents
* CryptorRSA: Utility functions for RSA encryption and signing. Pure Swift
## Prerequisites
### Swift
* Swift Open Source `swift-4.0.0-RELEASE` toolchain (**Minimum REQUIRED for latest release**)
* Swift Open Source `swift-4.2-RELEASE` toolchain (**Recommended**)
* Swift toolchain included in *Xcode Version 10.0 (10A255) or higher*.
### macOS
* macOS 10.12.0 (*Sierra*) or higher
* Xcode Version 9.0 (9A325) or higher using the included toolchain (**Minimum REQUIRED for latest release**).
* Xcode Version 10.0 (10A255) or higher using the included toolchain (**Recommended**).
### iOS
* iOS 10.3 or higher
* Xcode Version 9.0 (9A325) or higher using the included toolchain (**Minimum REQUIRED for latest release**).
* Xcode Version 10.0 (10A255) or higher using the included toolchain (**Recommended**).
### Linux
* Ubuntu 16.04 (or 16.10 but only tested on 16.04) and 18.04.
* One of the Swift Open Source toolchain listed above.
* OpenSSL is provided by the distribution. **Note:** 1.0.x, 1.1.x and later releases of OpenSSL are supported.
* The appropriate **libssl-dev** package is required to be installed when building.
## Build
To build CryptorRSA from the command line:
```
% cd <path-to-clone>
% swift build
```
## Testing
To run the supplied unit tests for **CryptorRSA** from the command line:
```
% cd <path-to-clone>
% swift build
% swift test
```
## Using CryptorRSA
### Including in your project
#### Swift Package Manager
To include BlueRSA into a Swift Package Manager package, add it to the `dependencies` attribute defined in your `Package.swift` file. You can select the version using the `majorVersion` and `minor` parameters. For example:
```
dependencies: [
.Package(url: "https://github.com/IBM-Swift/BlueRSA", majorVersion: <majorVersion>, minor: <minor>)
]
```
#### Carthage
To include BlueRSA in a project using Carthage, add a line to your `Cartfile` with the GitHub organization and project names and version. For example:
```
github "IBM-Swift/BlueRSA" ~> <majorVersion>.<minor>
```
### Before starting
The first you need to do is import the CryptorRSA framework. This is done by the following:
```
import CryptorRSA
```
### Data Types
BlueRSA supports the following *major* data types:
* Key Handling
- `CryptorRSA.PublicKey` - Represents an RSA Public Key.
- `CryptorRSA.PrivateKey` - Represents an RSA Private Key.
* Data Handling
- `CryptorRSA.EncryptedData` - Represents encrypted data.
- `CryptorRSA.PlaintextData` - Represents plaintext or decrypted data.
- `CryptorRSA.SignedData` - Represents signed data.
### Key Handling
**BlueRSA** provides seven (7) functions each for creating public and private keys from data. They are as follows (where *createXXXX* is either `createPublicKey` or `createPrivateKey` depending on what you're trying to create):
- `CryptorRSA.createXXXX(with data: Data) throws` - This creates either a private or public key containing the data provided. *It is assumed that the data being provided is in the proper format.*
- `CryptorRSA.createXXXX(withBase64 base64String: String) throws` - This creates either a private or public key using the `Base64 encoded String` provided.
- `CryptorRSA.createXXXX(withPEM pemString: String) throws` - This creates either a private or public key using the `PEM encoded String` provided.
- `CryptorRSA.createXXXX(withPEMNamed pemName: String, onPath path: String) throws` - This creates either a private or public key using the `PEM encoded file` pointed at by the `pemName` and located on the path specified by `path` provided.
- `CryptorRSA.createXXXX(withDERNamed derName: String, onPath path: String) throws` - This creates either a private or public key using the `DER encoded file` pointed at by the `derName` and located on the path specified by `path` provided.
- `CryptorRSA.createXXXX(withPEMNamed pemName: String, in bundle: Bundle = Bundle.main) throws` - This creates either a private or public key using the `PEM encoded file` pointed at by the `pemName` and located in the `Bundle` specified by `bundle` provided. By default this API will look in the `main` bundle. **Note: Apple Platforms Only**
- `CryptorRSA.createXXXX(withDERNamed derName: String, in bundle: Bundle = Bundle.main) throws` - This creates either a private or public key using the `DER encoded file` pointed at by the `derName` and located in the `Bundle` specified by `bundle` provided. By default this API will look in the `main` bundle. **Note: Apple Platforms Only**
Additionally, there are three APIs for creating a *public key* by extracting the key from a PEM formatted certificate: They are:
- `CryptorRSA.createPublicKey(extractingFrom data: Data) throws` - This creates either a public key by extracting from the `PEM encoded certificate` pointed at by the `data`.
- `CryptorRSA.createPublicKey(extractingFrom certName: String, onPath path: String) throws` - This creates a public key by extracting from the `PEM encoded certificate` pointed at by the `certName` and located on the path specified by `path` provided.
- `CryptorRSA.createPublicKey(extractingFrom certName: String, in bundle: Bundle = Bundle.main) throws` - This creates a public key using the `PEM encoded certificate` pointed at by the `derName` and located in the `Bundle` specified by `bundle` provided. By default this API will look in the `main` bundle. **Note: Apple Platforms Only**
**Example**
The following example illustrates creating a public key given PEM encoded file located on a certain path. *Note: Exception handling omitted for brevity.
```
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
...
<Do something with the key...>
```
### Data Encryption and Decryption Handling
**BlueRSA** provides functions for the creation of each of the three (3) data handling types:
**Plaintext Data Handling and Signing**
There are two class level functions for creating a `PlaintextData` object. These are:
- `CryptorRSA.createPlaintext(with data: Data) -> PlaintextData` - This function creates a `PlaintextData` containing the specified `data`.
- `CryptorRSA.createPlaintext(with string: String, using encoding: String.Encoding) throws -> PlaintextData` - This function creates a `PlaintextData` object using the `string` encoded with the specified `encoding` as the data.
Once the `PlaintextData` object is created, there are two instance functions that can be used to manipulate the contained data. These are:
- `encrypted(with key: PublicKey, algorithm: Data.Algorithm) throws -> EncryptedData?` - This function allows you to encrypt containing data using the public `key` and `algorithm` specified. This function returns an optional `EncryptedData` object containing the encryped data.
- `signed(with key: PrivateKey, algorithm: Data.Algorithm) throws -> SignedData?` - This function allows you to sign the contained data using the private `key` and `algorithm` specified. This function returns an optional `SignedData` object containing the signature of the signed data.
**Example**
- *Encryption*: **Note:** Exception handling omitted for brevity.
```
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let myData: Data = <... Data to be encrypted ...>
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let encryptedData = try myPlaintext.encrypt(with: publicKey, algorithm: .sha1)
...
< Do something with the encrypted data...>
```
- *Signing*: **Note:** Exception handling omitted for brevity.
```
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let myData: Data = <... Data to be signed ...>
let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: keyName, onPath: keyPath)
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let signedData = try myPlaintext.signed(with: privateKey, algorithm: .sha1)
...
< Do something with the signed data...>
```
**Encrypted Data Handling**
There are two class level functions for creating a `EncryptedData` object. These are:
- `CryptorRSA.createEncrypted(with data: Data) -> EncryptedData` - This function creates a `EncryptedData` containing the specified encrypted `data`.
- `CryptorRSA.createEncrypted(with base64String: String) throws -> EncryptedData` - This function creates a `EncrpytedData` using the *Base64* representation of already encrypted data.
Once the `EncryptedData` object is created, there is an instance function that can be used to decrypt the enclosed data:
- `decrypted(with key: PrivateKey, algorithm: Data.Algorithm) throws -> DecryptedData?` - This function allows you to decrypt containing data using the public `key` and `algorithm` specified. This function returns an optional `DecryptedData` object containing the encryped data.
BlueRSA currently supports `OAEP` padding, which is the recommended padding algorithm.
**Example**
- *Decryption*: **Note**: Exception handling omitted for brevity.
```
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
let pkeyName = ...
let pkeyPath = ...
let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: pkeyName, onPath: pkeyPath)
let myData: Data = <... Data to be encrypted ...>
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let encryptedData = try myPlaintext.encrypt(with: publicKey, algorithm: .sha1)
let decryptedData = try encryptedData.decrypt(with: privateKey, algorithm: .sha1)
...
< Do something with the decrypted data...>
```
### Signature Verification Handling
There is a single class level function that can be used to create a `SignedData` object. It is:
- `CryptorRSA.createSigned(with data: Data) -> SignedData` - This function creates a `SignedData` containing the specified signed `data`.
Once created or obtained `PlaintextData` and `SignedData`, there is an instance function which can be used to verify the signature contained therein:
- `verify(with key: PublicKey, signature: SignedData, algorithm: Data.Algorithm) throws -> Bool` - This function is used to verify, using the public `key` and `algorithm`, the `signature`. Returns true if the signature is valid, false otherwise.
- *Verifying*: **Note:** Exception handling omitted for brevity.
```
import Foundation
import CryptorRSA
...
let keyName = ...
let keyPath = ...
let publicKey = try CryptorRSA.createPublicKey(withPEMNamed: keyName, onPath: keyPath)
let pkeyName = ...
let pkeyPath = ...
let privateKey = try CryptorRSA.createPrivateKey(withPEMNamed: pkeyName, onPath: pkeyPath)
let myData: Data = <... Data to be signed ...>
let myPlaintext = CryptorRSA.createPlaintext(with: myData)
let signedData = try myPlaintext.signed(with: privateKey, algorithm: .sha1)
if try myPlaintext.verify(with: publicKey, signature: signedData, algorithm: .sha1) {
print("Signature verified")
} else {
print("Signature Verification Failed")
}
```
### Data Type Utility Functions
All three of the data handling types have two common utility instance functions. These are:
- `digest(using algorithm: Data.Algorithm) throws -> Data` - This function returns a `Data` object containing a digest constructed using the specified `algorithm`.
- `string(using encoding: String.Encoding) throws -> String` - This functions returns a `String` representation of the data using the specified `encoding`.
## Community
We love to talk server-side Swift and Kitura. Join our [Slack](http://swift-at-ibm-slack.mybluemix.net/) to meet the team!
## License
This library is licensed under Apache 2.0. Full license text is available in [LICENSE](https://github.com/IBM-Swift/BlueRSA/blob/master/LICENSE).

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)
}
}