Initial commit
This commit is contained in:
Generated
+176
@@ -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
|
||||
Generated
+235
@@ -0,0 +1,235 @@
|
||||
<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/BlueCryptor">
|
||||
<img src="https://travis-ci.org/IBM-Swift/BlueCryptor.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>
|
||||
|
||||
# BlueCryptor
|
||||
Swift cross-platform crypto library derived from [IDZSwiftCommonCrypto](https://github.com/iosdevzone/IDZSwiftCommonCrypto).
|
||||
|
||||
**IMPORTANT NOTE:** This release is **NOT** entirely source code compatible with previous releases. There are instances where *exceptions* are thrown now instead of the framework calling *fatalError()*. This means that there are more *recoverable* errors in the library than before. The only time that *fatalError()* is called is to indicate either a *programming error* or a *non-recoverable system error*.
|
||||
|
||||
**Note:** On macOS and iOS, _BlueCryptor_ uses the Apple provided *CommonCrypto* library. On Linux, it uses *libcrypto from the OpenSSL project*.
|
||||
|
||||
## 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.11.6 (*El Capitan*) or higher.
|
||||
* Xcode Version 9.0 or higher using one of the above toolchains.
|
||||
* Xcode Version 10.0 (10A255) or higher using the included toolchain (*Recommended*).
|
||||
* CommonCrypto is provided by macOS.
|
||||
|
||||
### iOS
|
||||
|
||||
* iOS 10.0 or higher
|
||||
* Xcode Version 9.0 or higher using one of the above toolchains.
|
||||
* Xcode Version 10.0 (10A255) or higher using the included toolchain (*Recommended*).
|
||||
* CommonCrypto is provided by iOS.
|
||||
|
||||
### 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 **Cryptor** from the command line:
|
||||
|
||||
```
|
||||
% cd <path-to-clone>
|
||||
% swift build
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
To run the supplied unit tests for **Cryptor** from the command line:
|
||||
|
||||
```
|
||||
% cd <path-to-clone>
|
||||
% swift build
|
||||
% swift test
|
||||
```
|
||||
|
||||
## Getting started
|
||||
|
||||
### Including in your project
|
||||
|
||||
#### Swift Package Manager
|
||||
|
||||
To include BlueCryptor 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/BlueCryptor.git", majorVersion: <majorVersion>, minor: <minor>)
|
||||
]
|
||||
```
|
||||
|
||||
#### Carthage
|
||||
To include BlueCryptor 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/BlueCryptor" ~> <majorVersion>.<minor>
|
||||
```
|
||||
|
||||
#### CocoaPods
|
||||
To include BlueCryptor in a project using CocoaPods, you just add `BlueCryptor` to your `Podfile`, for example:
|
||||
```
|
||||
platform :ios, '10.0'
|
||||
|
||||
target 'MyApp' do
|
||||
use_frameworks!
|
||||
pod 'BlueCryptor'
|
||||
end
|
||||
```
|
||||
|
||||
### Before starting
|
||||
|
||||
The first thing you need to do is import the Cryptor framework. This is done by the following:
|
||||
```swift
|
||||
import Cryptor
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Cryptor
|
||||
|
||||
The following code demonstrates encryption and decryption using `AES` single block CBC mode using optional chaining.
|
||||
```swift
|
||||
let key = CryptoUtils.byteArray(fromHex: "2b7e151628aed2a6abf7158809cf4f3c")
|
||||
let iv = CryptoUtils.byteArray(fromHex: "00000000000000000000000000000000")
|
||||
let plainText = CryptoUtils.byteArray(fromHex: "6bc1bee22e409f96e93d7e117393172a")
|
||||
|
||||
var textToCipher = plainText
|
||||
if plainText.count % Cryptor.Algorithm.aes.blockSize != 0 {
|
||||
textToCipher = CryptoUtils.zeroPad(byteArray: plainText, blockSize: Cryptor.Algorithm.aes.blockSize)
|
||||
}
|
||||
do {
|
||||
let cipherText = try Cryptor(operation: .encrypt, algorithm: .aes, options: .none, key: key, iv: iv).update(byteArray: textToCipher)?.final()
|
||||
|
||||
print(CryptoUtils.hexString(from: cipherText!))
|
||||
|
||||
let decryptedText = try Cryptor(operation: .decrypt, algorithm: .aes, options: .none, key: key, iv: iv).update(byteArray: cipherText!)?.final()
|
||||
|
||||
print(CryptoUtils.hexString(from: decryptedText!))
|
||||
} catch let error {
|
||||
guard let err = error as? CryptorError else {
|
||||
// Handle non-Cryptor error...
|
||||
return
|
||||
}
|
||||
// Handle Cryptor error... (See Status.swift for types of errors thrown)
|
||||
}
|
||||
```
|
||||
|
||||
### Digest
|
||||
|
||||
The following example illustrates generating an `MD5` digest from both a `String` and an instance of `NSData`.
|
||||
```swift
|
||||
let qbfBytes : [UInt8] = [0x54,0x68,0x65,0x20,0x71,0x75,0x69,0x63,0x6b,0x20,0x62,0x72,0x6f,0x77,0x6e,0x20,0x66,0x6f,0x78,0x20,0x6a,0x75,0x6d,0x70,0x73,0x20,0x6f,0x76,0x65,0x72,0x20,0x74,0x68,0x65,0x20,0x6c,0x61,0x7a,0x79,0x20,0x64,0x6f,0x67,0x2e]
|
||||
let qbfString = "The quick brown fox jumps over the lazy dog."
|
||||
|
||||
// String...
|
||||
let md5 = Digest(using: .md5)
|
||||
md5.update(string: qfbString)
|
||||
let digest = md5.final()
|
||||
|
||||
// NSData using optional chaining...
|
||||
let qbfData = CryptoUtils.data(from: qbfBytes)
|
||||
let digest = Digest(using: .md5).update(data: qbfData)?.final()
|
||||
```
|
||||
|
||||
### HMAC
|
||||
|
||||
The following demonstrates generating an `SHA256` HMAC using byte arrays for keys and data.
|
||||
```swift
|
||||
let myKeyData = "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"
|
||||
let myData = "4869205468657265"
|
||||
let key = CryptoUtils.byteArray(fromHex: myKeyData)
|
||||
let data : [UInt8] = CryptoUtils.byteArray(fromHex: myData)
|
||||
|
||||
let hmac = HMAC(using: HMAC.Algorithm.sha256, key: key).update(byteArray: data)?.final()
|
||||
```
|
||||
|
||||
### Key Derivation
|
||||
|
||||
The following illustrates generating a key using a password, salt, number of rounds and a specified derived key length using the SHA1 algorithm. Then it shows how to generate a `String` from resultant key.
|
||||
```swift
|
||||
let password = "password"
|
||||
let salt = salt
|
||||
let rounds: UInt = 2
|
||||
let derivedKeyLen = 20
|
||||
do {
|
||||
let key = PBKDF.deriveKey(fromPassword: password, salt: salt, prf: .sha1, rounds: rounds, derivedKeyLength: derivedKeyLen)
|
||||
let keyString = CryptoUtils.hexString(from: key)
|
||||
} catch let error {
|
||||
guard let err = error as? CryptorError else {
|
||||
// Handle non-Cryptor error...
|
||||
return
|
||||
}
|
||||
// Handle Cryptor error... (See Status.swift for types of errors thrown)
|
||||
}
|
||||
```
|
||||
|
||||
### Random Byte Generation
|
||||
|
||||
The following demonstrates generating random bytes of a given length.
|
||||
```swift
|
||||
let numberOfBytes = 256*256
|
||||
do {
|
||||
let randomBytes = try Random.generate(byteCount: numberOfBytes)
|
||||
} catch {
|
||||
print("Error generating random bytes")
|
||||
}
|
||||
```
|
||||
|
||||
### Utilities
|
||||
|
||||
**Cryptor** also provides a set of data manipulation utility functions for conversion of data from various formats:
|
||||
- To byteArray (`[UInt8]`)
|
||||
- From hex string
|
||||
- From UTF8 string
|
||||
- To `Data`
|
||||
- From hex string
|
||||
- From byte array (`[UInt8]`)
|
||||
- To `NSData`
|
||||
- From hex string
|
||||
- From byte array (`[UInt8]`)
|
||||
- To `NSString`
|
||||
- From byte array (`[UInt8]`)
|
||||
- To hexList (`String`)
|
||||
- From byte array (`[UInt8]`)
|
||||
|
||||
Also provided are an API to pad a byte array (`[UInt8]`) such that it is an integral number of `block size in bytes` long.
|
||||
- ```func zeroPad(byteArray: [UInt8], blockSize: Int) -> [UInt8]```
|
||||
- ```func zeroPad(string: String, blockSize: Int) -> [UInt8]```
|
||||
|
||||
## Restrictions
|
||||
|
||||
The following algorithm is not available on Linux since it is not supported by *OpenSSL*.
|
||||
- Digest: MD2
|
||||
|
||||
In all cases, use of unsupported APIs or algorithms will result in a Swift `fatalError()`, terminating the program and should be treated as a programming error.
|
||||
|
||||
## 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/BlueCryptor/blob/master/LICENSE).
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// Crypto.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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
|
||||
|
||||
///
|
||||
/// Implements a simplified API for calculating digests over single buffers
|
||||
///
|
||||
public protocol CryptoDigest {
|
||||
|
||||
/// Calculates a message digest
|
||||
func digest(using algorithm: Digest.Algorithm) -> Self
|
||||
}
|
||||
|
||||
///
|
||||
/// Extension to the CryptoDigest to return the digest appropriate to the selected algorithm.
|
||||
///
|
||||
extension CryptoDigest {
|
||||
|
||||
/// An MD2 digest of this object
|
||||
public var md2: Self {
|
||||
return self.digest(using: .md2)
|
||||
}
|
||||
|
||||
/// An MD4 digest of this object
|
||||
public var md4: Self {
|
||||
return self.digest(using: .md4)
|
||||
}
|
||||
|
||||
/// An MD5 digest of this object
|
||||
public var md5: Self {
|
||||
return self.digest(using: .md5)
|
||||
}
|
||||
|
||||
/// An SHA1 digest of this object
|
||||
public var sha1: Self {
|
||||
return self.digest(using: .sha1)
|
||||
}
|
||||
|
||||
/// An SHA224 digest of this object
|
||||
public var sha224: Self {
|
||||
return self.digest(using: .sha224)
|
||||
}
|
||||
|
||||
/// An SHA256 digest of this object
|
||||
public var sha256: Self {
|
||||
return self.digest(using: .sha256)
|
||||
}
|
||||
|
||||
/// An SHA384 digest of this object
|
||||
public var sha384: Self {
|
||||
return self.digest(using: .sha384)
|
||||
}
|
||||
|
||||
/// An SHA512 digest of this object
|
||||
public var sha512: Self {
|
||||
return self.digest(using: .sha512)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Extension for Data to return an Data object containing the digest.
|
||||
///
|
||||
extension Data: CryptoDigest {
|
||||
///
|
||||
/// Calculates the Message Digest for this data.
|
||||
///
|
||||
/// - Parameter algorithm: The digest algorithm to use
|
||||
///
|
||||
/// - Returns: An `Data` object containing the message digest
|
||||
///
|
||||
public func digest(using algorithm: Digest.Algorithm) -> Data {
|
||||
|
||||
// This force unwrap may look scary but for CommonCrypto this cannot fail.
|
||||
// The API allows for optionals to support the OpenSSL implementation which can.
|
||||
#if swift(>=5.0)
|
||||
return self.withUnsafeBytes() {
|
||||
|
||||
let result = (Digest(using: algorithm).update(from: $0.baseAddress!, byteCount: self.count)?.final())!
|
||||
let data = type(of: self).init(bytes: result, count: result.count)
|
||||
return data
|
||||
}
|
||||
#else
|
||||
return self.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) -> Data in
|
||||
|
||||
let result = (Digest(using: algorithm).update(from: buffer, byteCount: self.count)?.final())!
|
||||
let data = type(of: self).init(bytes: result, count: result.count)
|
||||
return data
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Extension for String to return a String containing the digest.
|
||||
///
|
||||
extension String: CryptoDigest {
|
||||
///
|
||||
/// Calculates the Message Digest for this string.
|
||||
/// The string is converted to raw data using UTF8.
|
||||
///
|
||||
/// - Parameter algorithm: The digest algorithm to use
|
||||
///
|
||||
/// - Returns: A hex string of the calculated digest
|
||||
///
|
||||
public func digest(using algorithm: Digest.Algorithm) -> String {
|
||||
|
||||
// This force unwrap may look scary but for CommonCrypto this cannot fail.
|
||||
// The API allows for optionals to support the OpenSSL implementation which can.
|
||||
let result = (Digest(using: algorithm).update(string: self as String)?.final())!
|
||||
return CryptoUtils.hexString(from: result)
|
||||
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// Cryptor.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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
|
||||
|
||||
///
|
||||
/// Encrypts or decrypts, accumulating result.
|
||||
///
|
||||
/// Useful for small in-memory buffers.
|
||||
///
|
||||
/// For large files or network streams use StreamCryptor.
|
||||
///
|
||||
public class Cryptor: StreamCryptor, Updatable {
|
||||
|
||||
/// Internal accumulator for gathering data from the update() and final() functions.
|
||||
var accumulator: [UInt8] = []
|
||||
|
||||
///
|
||||
/// Retrieves the encrypted or decrypted data.
|
||||
///
|
||||
///- Returns: the encrypted or decrypted data or nil if an error occured.
|
||||
///
|
||||
public func final() -> [UInt8]? {
|
||||
|
||||
let byteCount = Int(self.getOutputLength(inputByteCount: 0, isFinal: true))
|
||||
var dataOut = Array<UInt8>(repeating: 0, count:byteCount)
|
||||
var dataOutMoved = 0
|
||||
(dataOutMoved, self.status) = final(byteArrayOut: &dataOut)
|
||||
if self.status != .success {
|
||||
return nil
|
||||
}
|
||||
accumulator += dataOut[0..<Int(dataOutMoved)]
|
||||
return accumulator
|
||||
}
|
||||
|
||||
///
|
||||
/// Upates the accumulated encrypted/decrypted data with the contents
|
||||
/// of a raw byte buffer.
|
||||
///
|
||||
/// It is not envisaged the users of the framework will need to call this directly.
|
||||
///
|
||||
/// - Returns: this Cryptor object or nil if an error occurs (for optional chaining)
|
||||
///
|
||||
public func update(from buffer: UnsafeRawPointer, byteCount: Int) -> Self? {
|
||||
|
||||
let outputLength = Int(self.getOutputLength(inputByteCount: byteCount, isFinal: false))
|
||||
var dataOut = Array<UInt8>(repeating: 0, count:outputLength)
|
||||
var dataOutMoved = 0
|
||||
_ = update(bufferIn: buffer, byteCountIn: byteCount, bufferOut: &dataOut, byteCapacityOut: dataOut.count, byteCountOut: &dataOutMoved)
|
||||
if self.status != .success {
|
||||
return nil
|
||||
}
|
||||
accumulator += dataOut[0..<Int(dataOutMoved)]
|
||||
return self
|
||||
}
|
||||
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
//
|
||||
// Digest.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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 os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
import CommonCrypto
|
||||
#elseif os(Linux)
|
||||
import OpenSSL
|
||||
typealias CC_LONG = size_t
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Public API for message digests.
|
||||
///
|
||||
/// Usage is straightforward
|
||||
///
|
||||
/// ```
|
||||
/// let s = "The quick brown fox jumps over the lazy dog."
|
||||
/// var md5: Digest = Digest(using:.md5)
|
||||
/// md5.update(s)
|
||||
/// let digest = md5.final()
|
||||
///```
|
||||
///
|
||||
public class Digest: Updatable {
|
||||
|
||||
///
|
||||
/// The status of the Digest.
|
||||
/// For CommonCrypto this will always be `.Success`.
|
||||
/// It is here to provide for engines which can fail.
|
||||
///
|
||||
public var status = Status.success
|
||||
|
||||
///
|
||||
/// Enumerates available Digest algorithms
|
||||
///
|
||||
public enum Algorithm {
|
||||
|
||||
/// Message Digest 2 See: http://en.wikipedia.org/wiki/MD2_(cryptography)
|
||||
case md2
|
||||
|
||||
/// Message Digest 4
|
||||
case md4
|
||||
|
||||
/// Message Digest 5
|
||||
case md5
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
private var engine: DigestEngine
|
||||
|
||||
///
|
||||
/// Create an algorithm-specific digest calculator
|
||||
///
|
||||
/// - Parameter alrgorithm: the desired message digest algorithm
|
||||
///
|
||||
public init(using algorithm: Algorithm) {
|
||||
|
||||
switch algorithm {
|
||||
|
||||
case .md2:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_MD2_CTX>(initializer:CC_MD2_Init, updater:CC_MD2_Update, finalizer:CC_MD2_Final, length:CC_MD2_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
fatalError("MD2 digest not supported by OpenSSL")
|
||||
#endif
|
||||
|
||||
case .md4:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_MD4_CTX>(initializer:CC_MD4_Init, updater:CC_MD4_Update, finalizer:CC_MD4_Final, length:CC_MD4_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<MD4_CTX>(initializer:MD4_Init, updater:MD4_Update, finalizer:MD4_Final, length:MD4_DIGEST_LENGTH)
|
||||
#endif
|
||||
|
||||
case .md5:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_MD5_CTX>(initializer:CC_MD5_Init, updater:CC_MD5_Update, finalizer:CC_MD5_Final, length:CC_MD5_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<MD5_CTX>(initializer:MD5_Init, updater:MD5_Update, finalizer:MD5_Final, length:MD5_DIGEST_LENGTH)
|
||||
#endif
|
||||
|
||||
case .sha1:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_SHA1_CTX>(initializer:CC_SHA1_Init, updater:CC_SHA1_Update, finalizer:CC_SHA1_Final, length:CC_SHA1_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<SHA_CTX>(initializer:SHA1_Init, updater:SHA1_Update, finalizer:SHA1_Final, length:SHA_DIGEST_LENGTH)
|
||||
#endif
|
||||
|
||||
case .sha224:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_SHA256_CTX>(initializer:CC_SHA224_Init, updater:CC_SHA224_Update, finalizer:CC_SHA224_Final, length:CC_SHA224_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<SHA256_CTX>(initializer:SHA224_Init, updater:SHA224_Update, finalizer:SHA224_Final, length:SHA224_DIGEST_LENGTH)
|
||||
#endif
|
||||
|
||||
case .sha256:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_SHA256_CTX>(initializer:CC_SHA256_Init, updater:CC_SHA256_Update, finalizer:CC_SHA256_Final, length:CC_SHA256_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<SHA256_CTX>(initializer: SHA256_Init, updater:SHA256_Update, finalizer:SHA256_Final, length:SHA256_DIGEST_LENGTH)
|
||||
#endif
|
||||
|
||||
case .sha384:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_SHA512_CTX>(initializer:CC_SHA384_Init, updater:CC_SHA384_Update, finalizer:CC_SHA384_Final, length:CC_SHA384_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<SHA512_CTX>(initializer:SHA384_Init, updater:SHA384_Update, finalizer:SHA384_Final, length:SHA384_DIGEST_LENGTH)
|
||||
#endif
|
||||
|
||||
case .sha512:
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
engine = DigestEngineCC<CC_SHA512_CTX>(initializer:CC_SHA512_Init, updater:CC_SHA512_Update, finalizer:CC_SHA512_Final, length:CC_SHA512_DIGEST_LENGTH)
|
||||
#elseif os(Linux)
|
||||
engine = DigestEngineCC<SHA512_CTX>(initializer:SHA512_Init, updater:SHA512_Update, finalizer:SHA512_Final, length:SHA512_DIGEST_LENGTH)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Low-level update routine. Updates the message digest calculation with
|
||||
/// the contents of a byte buffer.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - buffer: The buffer
|
||||
/// - byteCount: Number of bytes in buffer
|
||||
///
|
||||
/// - Returns: This Digest object (for optional chaining)
|
||||
///
|
||||
public func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? {
|
||||
|
||||
engine.update(buffer: buffer, byteCount: CC_LONG(byteCount))
|
||||
return self
|
||||
}
|
||||
|
||||
///
|
||||
/// Completes the calculate of the messge digest
|
||||
///
|
||||
/// - Returns: The message digest
|
||||
///
|
||||
public func final() -> [UInt8] {
|
||||
|
||||
return engine.final()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Internal Classes
|
||||
|
||||
///
|
||||
/// Defines the interface between the Digest class and an
|
||||
/// algorithm specific DigestEngine
|
||||
///
|
||||
private protocol DigestEngine {
|
||||
|
||||
///
|
||||
/// Update method
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - buffer: The buffer to add.
|
||||
/// - byteCount: The length of the buffer.
|
||||
///
|
||||
func update(buffer: UnsafeRawPointer, byteCount: CC_LONG)
|
||||
|
||||
///
|
||||
/// Finalizer routine
|
||||
///
|
||||
/// - Returns: Byte array containing the digest.
|
||||
///
|
||||
func final() -> [UInt8]
|
||||
}
|
||||
|
||||
///
|
||||
/// Wraps the underlying algorithm specific structures and calls
|
||||
/// in a generic interface.
|
||||
///
|
||||
/// - Parameter CTX: The context for the digest.
|
||||
///
|
||||
private class DigestEngineCC<CTX>: DigestEngine {
|
||||
|
||||
typealias Context = UnsafeMutablePointer<CTX>
|
||||
typealias Buffer = UnsafeRawPointer
|
||||
typealias Digest = UnsafeMutablePointer<UInt8>
|
||||
typealias Initializer = (Context) -> (Int32)
|
||||
typealias Updater = (Context, Buffer, CC_LONG) -> (Int32)
|
||||
typealias Finalizer = (Digest, Context) -> (Int32)
|
||||
|
||||
let context = Context.allocate(capacity: 1)
|
||||
var initializer: Initializer
|
||||
var updater: Updater
|
||||
var finalizer: Finalizer
|
||||
var length: Int32
|
||||
|
||||
///
|
||||
/// Default initializer
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - initializer: The digest initializer routine.
|
||||
/// - updater: The digest updater routine.
|
||||
/// - finalizer: The digest finalizer routine.
|
||||
/// - length: The digest length.
|
||||
///
|
||||
init(initializer: @escaping Initializer, updater: @escaping Updater, finalizer: @escaping Finalizer, length: Int32) {
|
||||
|
||||
self.initializer = initializer
|
||||
self.updater = updater
|
||||
self.finalizer = finalizer
|
||||
self.length = length
|
||||
_ = initializer(context)
|
||||
}
|
||||
|
||||
///
|
||||
/// Cleanup
|
||||
///
|
||||
deinit {
|
||||
|
||||
#if swift(>=4.1)
|
||||
context.deallocate()
|
||||
#else
|
||||
context.deallocate(capacity: 1)
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Update method
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - buffer: The buffer to add.
|
||||
/// - byteCount: The length of the buffer.
|
||||
///
|
||||
func update(buffer: Buffer, byteCount: CC_LONG) {
|
||||
|
||||
_ = updater(context, buffer, byteCount)
|
||||
}
|
||||
|
||||
///
|
||||
/// Finalizer routine
|
||||
///
|
||||
/// - Returns: Byte array containing the digest.
|
||||
///
|
||||
func final() -> [UInt8] {
|
||||
|
||||
let digestLength = Int(self.length)
|
||||
var digest = Array<UInt8>(repeating: 0, count:digestLength)
|
||||
_ = finalizer(&digest, context)
|
||||
return digest
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
//
|
||||
// HMAC.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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 os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
import CommonCrypto
|
||||
#elseif os(Linux)
|
||||
import OpenSSL
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Calculates a cryptographic Hash-Based Message Authentication Code (HMAC).
|
||||
///
|
||||
public class HMAC: Updatable {
|
||||
|
||||
///
|
||||
/// Enumerates available algorithms.
|
||||
///
|
||||
public enum Algorithm {
|
||||
|
||||
/// Message Digest 5
|
||||
case md5
|
||||
|
||||
/// 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
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
static let fromNative: [CCHmacAlgorithm: Algorithm] = [
|
||||
CCHmacAlgorithm(kCCHmacAlgSHA1): .sha1,
|
||||
CCHmacAlgorithm(kCCHmacAlgSHA1): .md5,
|
||||
CCHmacAlgorithm(kCCHmacAlgSHA256): .sha256,
|
||||
CCHmacAlgorithm(kCCHmacAlgSHA384): .sha384,
|
||||
CCHmacAlgorithm(kCCHmacAlgSHA512): .sha512,
|
||||
CCHmacAlgorithm(kCCHmacAlgSHA224): .sha224
|
||||
]
|
||||
|
||||
static func fromNativeValue(nativeAlg: CCHmacAlgorithm) -> Algorithm? {
|
||||
|
||||
return fromNative[nativeAlg]
|
||||
}
|
||||
|
||||
func nativeValue() -> CCHmacAlgorithm {
|
||||
|
||||
switch self {
|
||||
|
||||
case .sha1:
|
||||
return CCHmacAlgorithm(kCCHmacAlgSHA1)
|
||||
case .md5:
|
||||
return CCHmacAlgorithm(kCCHmacAlgMD5)
|
||||
case .sha224:
|
||||
return CCHmacAlgorithm(kCCHmacAlgSHA224)
|
||||
case .sha256:
|
||||
return CCHmacAlgorithm(kCCHmacAlgSHA256)
|
||||
case .sha384:
|
||||
return CCHmacAlgorithm(kCCHmacAlgSHA384)
|
||||
case .sha512:
|
||||
return CCHmacAlgorithm(kCCHmacAlgSHA512)
|
||||
}
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
func nativeValue() -> OpaquePointer? {
|
||||
|
||||
switch self {
|
||||
|
||||
case .sha1:
|
||||
return .init(EVP_sha1())
|
||||
case .md5:
|
||||
return .init(EVP_md5())
|
||||
case .sha224:
|
||||
return .init(EVP_sha224())
|
||||
case .sha256:
|
||||
return .init(EVP_sha256())
|
||||
case .sha384:
|
||||
return .init(EVP_sha384())
|
||||
case .sha512:
|
||||
return .init(EVP_sha512())
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Obtains the digest length produced by this algorithm (in bytes).
|
||||
///
|
||||
public func digestLength() -> Int {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
switch self {
|
||||
|
||||
case .sha1:
|
||||
return Int(CC_SHA1_DIGEST_LENGTH)
|
||||
case .md5:
|
||||
return Int(CC_MD5_DIGEST_LENGTH)
|
||||
case .sha224:
|
||||
return Int(CC_SHA224_DIGEST_LENGTH)
|
||||
case .sha256:
|
||||
return Int(CC_SHA256_DIGEST_LENGTH)
|
||||
case .sha384:
|
||||
return Int(CC_SHA384_DIGEST_LENGTH)
|
||||
case .sha512:
|
||||
return Int(CC_SHA512_DIGEST_LENGTH)
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
switch self {
|
||||
|
||||
case .sha1:
|
||||
return Int(SHA_DIGEST_LENGTH)
|
||||
case .md5:
|
||||
return Int(MD5_DIGEST_LENGTH)
|
||||
case .sha224:
|
||||
return Int(SHA224_DIGEST_LENGTH)
|
||||
case .sha256:
|
||||
return Int(SHA256_DIGEST_LENGTH)
|
||||
case .sha384:
|
||||
return Int(SHA384_DIGEST_LENGTH)
|
||||
case .sha512:
|
||||
return Int(SHA512_DIGEST_LENGTH)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Context
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
typealias Context = UnsafeMutablePointer<CCHmacContext>
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
typealias Context = OpaquePointer?
|
||||
|
||||
#endif
|
||||
|
||||
/// Status of the calculation
|
||||
public internal(set) var status: Status = .success
|
||||
|
||||
#if os(Linux)
|
||||
private let context = HMAC_CTX_new_wrapper()
|
||||
#else
|
||||
private let context = Context.allocate(capacity: 1)
|
||||
#endif
|
||||
private var algorithm: Algorithm
|
||||
|
||||
// MARK: Lifecycle Methods
|
||||
|
||||
///
|
||||
/// Creates a new HMAC instance with the specified algorithm and key.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - algorithm: Selects the algorithm
|
||||
/// - keyBuffer: Specifies pointer to the key
|
||||
/// - keyByteCount: Number of bytes on keyBuffer
|
||||
///
|
||||
init(using algorithm: Algorithm, keyBuffer: UnsafeRawPointer, keyByteCount: Int) {
|
||||
|
||||
self.algorithm = algorithm
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
CCHmacInit(context, algorithm.nativeValue(), keyBuffer, size_t(keyByteCount))
|
||||
#elseif os(Linux)
|
||||
HMAC_Init_wrapper(context, keyBuffer, Int32(keyByteCount), .make(optional: algorithm.nativeValue()))
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a new HMAC instance with the specified algorithm and key.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - algorithm: Selects the algorithm
|
||||
/// - key: Specifies the key as Data
|
||||
///
|
||||
public init(using algorithm: Algorithm, key: Data) {
|
||||
|
||||
self.algorithm = algorithm
|
||||
#if swift(>=5.0)
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
key.withUnsafeBytes() {
|
||||
CCHmacInit(context, algorithm.nativeValue(), $0.baseAddress, size_t(key.count))
|
||||
}
|
||||
#elseif os(Linux)
|
||||
_ = key.withUnsafeBytes() {
|
||||
HMAC_Init_wrapper(context, $0.baseAddress, Int32(key.count), .make(optional: algorithm.nativeValue()))
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
key.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
|
||||
CCHmacInit(context, algorithm.nativeValue(), buffer, size_t(key.count))
|
||||
}
|
||||
#elseif os(Linux)
|
||||
_ = key.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
|
||||
HMAC_Init_wrapper(context, buffer, Int32(key.count), .make(optional: algorithm.nativeValue()))
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a new HMAC instance with the specified algorithm and key.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - algorithm: Selects the algorithm
|
||||
/// - key: Specifies the key as NSData
|
||||
///
|
||||
public init(using algorithm: Algorithm, key: NSData) {
|
||||
|
||||
self.algorithm = algorithm
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
CCHmacInit(context, algorithm.nativeValue(), key.bytes, size_t(key.length))
|
||||
#elseif os(Linux)
|
||||
HMAC_Init_wrapper(context, key.bytes, Int32(key.length), .make(optional: algorithm.nativeValue()))
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a new HMAC instance with the specified algorithm and key.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - algorithm: Selects the algorithm
|
||||
/// - key: Specifies the key as byte array.
|
||||
///
|
||||
public init(using algorithm: Algorithm, key: [UInt8]) {
|
||||
|
||||
self.algorithm = algorithm
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
CCHmacInit(context, algorithm.nativeValue(), key, size_t(key.count))
|
||||
#elseif os(Linux)
|
||||
HMAC_Init_wrapper(context, key, Int32(key.count), .make(optional: algorithm.nativeValue()))
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a new HMAC instance with the specified algorithm and key string.
|
||||
/// The key string is converted to bytes using UTF8 encoding.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - algorithm: Selects the algorithm
|
||||
/// - key: Specifies the key as String
|
||||
///
|
||||
public init(using algorithm: Algorithm, key: String) {
|
||||
|
||||
self.algorithm = algorithm
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
CCHmacInit(context, algorithm.nativeValue(), key, size_t(key.lengthOfBytes(using: String.Encoding.utf8)))
|
||||
#elseif os(Linux)
|
||||
HMAC_Init_wrapper(context, key, Int32(key.utf8.count), .make(optional: algorithm.nativeValue()))
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Cleanup
|
||||
///
|
||||
deinit {
|
||||
#if os(Linux)
|
||||
HMAC_CTX_free_wrapper(.make(optional: context))
|
||||
#else
|
||||
#if swift(>=4.1)
|
||||
context.deallocate()
|
||||
#else
|
||||
context.deallocate(capacity: 1)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: Public Methods
|
||||
|
||||
///
|
||||
/// Updates the calculation of the HMAC with the contents of a buffer.
|
||||
///
|
||||
/// - Parameter buffer: Update buffer
|
||||
///
|
||||
/// - Returns: The 'in-progress' calculated HMAC
|
||||
///
|
||||
public func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self? {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
CCHmacUpdate(context, buffer, byteCount)
|
||||
#elseif os(Linux)
|
||||
HMAC_Update(context, buffer.assumingMemoryBound(to: UInt8.self), byteCount)
|
||||
#endif
|
||||
return self
|
||||
}
|
||||
|
||||
///
|
||||
/// Finalizes the HMAC calculation
|
||||
///
|
||||
/// - Returns: The final calculated HMAC
|
||||
///
|
||||
public func final() -> [UInt8] {
|
||||
|
||||
var hmac = Array<UInt8>(repeating: 0, count:algorithm.digestLength())
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
CCHmacFinal(context, &hmac)
|
||||
#elseif os(Linux)
|
||||
var length: UInt32 = 0
|
||||
HMAC_Final(context, &hmac, &length)
|
||||
#endif
|
||||
return hmac
|
||||
}
|
||||
}
|
||||
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// KeyDerivation.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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 os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
import CommonCrypto
|
||||
#elseif os(Linux)
|
||||
import OpenSSL
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Derives key material from a password or passphrase.
|
||||
///
|
||||
public class PBKDF {
|
||||
|
||||
/// Enumerates available pseudo random algorithms
|
||||
public enum PseudoRandomAlgorithm {
|
||||
|
||||
/// 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
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
/// Return the OS native value
|
||||
func nativeValue() -> CCPseudoRandomAlgorithm {
|
||||
|
||||
switch self {
|
||||
|
||||
case .sha1:
|
||||
return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1)
|
||||
case .sha224:
|
||||
return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA224)
|
||||
case .sha256:
|
||||
return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256)
|
||||
case .sha384:
|
||||
return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA384)
|
||||
case .sha512:
|
||||
return CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA512)
|
||||
}
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
/// Return the OS native value
|
||||
func nativeValue() -> OpaquePointer? {
|
||||
|
||||
switch self {
|
||||
|
||||
case .sha1:
|
||||
return .init(EVP_sha1())
|
||||
case .sha224:
|
||||
return .init(EVP_sha224())
|
||||
case .sha256:
|
||||
return .init(EVP_sha256())
|
||||
case .sha384:
|
||||
return .init(EVP_sha384())
|
||||
case .sha512:
|
||||
return .init(EVP_sha512())
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the (approximate) number of iterations of the key derivation algorithm that need
|
||||
/// to be run to achieve a particular delay (or calculation time).
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - passwordLength: Password length in bytes
|
||||
/// - saltLength: Salt length in bytes
|
||||
/// - algorithm: The PseudoRandomAlgorithm to use
|
||||
/// - derivedKeyLength: The desired key length
|
||||
/// - msec: The desired calculation time
|
||||
///
|
||||
/// - Returns: The number of times the algorithm should be run
|
||||
///
|
||||
public class func calibrate(passwordLength: Int, saltLength: Int, algorithm: PseudoRandomAlgorithm, derivedKeyLength: Int, msec: UInt32) -> UInt {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
return UInt(CCCalibratePBKDF(CCPBKDFAlgorithm(kCCPBKDF2), passwordLength, saltLength, algorithm.nativeValue(), derivedKeyLength, msec))
|
||||
#elseif os(Linux)
|
||||
// Value as per RFC 2898.
|
||||
return UInt(1000 * UInt(msec))
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Derives key material from a password and salt.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - password: The password string, will be converted using UTF8
|
||||
/// - salt: The salt string will be converted using UTF8
|
||||
/// - prf: The pseudo random function
|
||||
/// - round: The number of rounds
|
||||
/// - derivedKeyLength: The length of the desired derived key, in bytes.
|
||||
///
|
||||
/// - Returns: The derived key
|
||||
///
|
||||
public class func deriveKey(fromPassword password: String, salt: String, prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) throws -> [UInt8] {
|
||||
|
||||
var derivedKey = Array<UInt8>(repeating: 0, count:Int(derivedKeyLength))
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, salt, salt.utf8.count, prf.nativeValue(), rounds, &derivedKey, derivedKey.count)
|
||||
if status != Int32(kCCSuccess) {
|
||||
|
||||
throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).")
|
||||
}
|
||||
#elseif os(Linux)
|
||||
let status = PKCS5_PBKDF2_HMAC(password, Int32(password.utf8.count), salt, Int32(salt.utf8.count), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKey.count), &derivedKey)
|
||||
if status != 1 {
|
||||
let error = ERR_get_error()
|
||||
|
||||
throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))")
|
||||
}
|
||||
#endif
|
||||
return derivedKey
|
||||
}
|
||||
|
||||
///
|
||||
/// Derives key material from a password and salt.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - password: The password string, will be converted using UTF8
|
||||
/// - salt: The salt array of bytes
|
||||
/// - prf: The pseudo random function
|
||||
/// - round: The number of rounds
|
||||
/// - derivedKeyLength: The length of the desired derived key, in bytes.
|
||||
///
|
||||
/// - Returns: The derived key
|
||||
///
|
||||
public class func deriveKey(fromPassword password: String, salt: [UInt8], prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) throws -> [UInt8] {
|
||||
|
||||
var derivedKey = Array<UInt8>(repeating: 0, count:Int(derivedKeyLength))
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.utf8.count, salt, salt.count, prf.nativeValue(), rounds, &derivedKey, derivedKey.count)
|
||||
if status != Int32(kCCSuccess) {
|
||||
|
||||
throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).")
|
||||
}
|
||||
#elseif os(Linux)
|
||||
let status = PKCS5_PBKDF2_HMAC(password, Int32(password.utf8.count), salt, Int32(salt.count), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKey.count), &derivedKey)
|
||||
if status != 1 {
|
||||
let error = ERR_get_error()
|
||||
|
||||
throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))")
|
||||
}
|
||||
#endif
|
||||
return derivedKey
|
||||
}
|
||||
|
||||
///
|
||||
/// Derives key material from a password buffer.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - password: Pointer to the password buffer
|
||||
/// - passwordLength: Password length in bytes
|
||||
/// - salt: Pointer to the salt buffer
|
||||
/// - saltLength: Salt length in bytes
|
||||
/// - prf: The PseudoRandomAlgorithm to use
|
||||
/// - rounds: The number of rounds of the algorithm to use
|
||||
/// - derivedKey: Pointer to the derived key buffer.
|
||||
/// - derivedKeyLength: The desired key length
|
||||
///
|
||||
/// - Returns: The number of times the algorithm should be run
|
||||
///
|
||||
public class func deriveKey(fromPassword password: UnsafePointer<Int8>, passwordLen: Int, salt: UnsafePointer<UInt8>, saltLen: Int, prf: PseudoRandomAlgorithm, rounds: uint, derivedKey: UnsafeMutablePointer<UInt8>, derivedKeyLen: Int) throws {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
let status: Int32 = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, passwordLen, salt, saltLen, prf.nativeValue(), rounds, derivedKey, derivedKeyLen)
|
||||
if status != Int32(kCCSuccess) {
|
||||
|
||||
throw CryptorError.fail(status, "ERROR: CCKeyDerivationPBDK failed with status \(status).")
|
||||
}
|
||||
#elseif os(Linux)
|
||||
let status = PKCS5_PBKDF2_HMAC(password, Int32(passwordLen), salt, Int32(saltLen), Int32(rounds), .make(optional: prf.nativeValue()), Int32(derivedKeyLen), derivedKey)
|
||||
if status != 1 {
|
||||
let error = ERR_get_error()
|
||||
|
||||
throw CryptorError.fail(Int32(error), "ERROR: PKCS5_PBKDF2_HMAC failed, reason: \(errToString(ERR_error_string(error, nil)))")
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// Random.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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 os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
import CommonCrypto
|
||||
#elseif os(Linux)
|
||||
import OpenSSL
|
||||
#endif
|
||||
|
||||
public typealias RNGStatus = Status
|
||||
|
||||
///
|
||||
/// Generates buffers of random bytes.
|
||||
///
|
||||
public class Random {
|
||||
|
||||
///
|
||||
/// Wraps native call.
|
||||
///
|
||||
/// - Note: CCRNGStatus is typealiased to CCStatus but this routine can only return kCCSuccess or kCCRNGFailure
|
||||
///
|
||||
/// - Parameter bytes: A pointer to the buffer that will receive the bytes
|
||||
///
|
||||
/// - Returns: `.success` or `.rngFailure` as appropriate.
|
||||
///
|
||||
public class func generate(bytes: UnsafeMutablePointer<UInt8>, byteCount: Int) -> RNGStatus {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
let statusCode = CCRandomGenerateBytes(bytes, byteCount)
|
||||
guard let status = Status(rawValue: statusCode) else {
|
||||
fatalError("CCRandomGenerateBytes returned unexpected status code: \(statusCode)")
|
||||
}
|
||||
return status
|
||||
#elseif os(Linux)
|
||||
let statusCode = RAND_bytes(bytes, Int32(byteCount))
|
||||
if statusCode != 1 {
|
||||
|
||||
let errCode = ERR_get_error()
|
||||
return Status.rngFailure(errCode)
|
||||
}
|
||||
return Status.success
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Generates an array of random bytes.
|
||||
///
|
||||
/// - Parameter bytesCount: Number of random bytes to generate
|
||||
///
|
||||
/// - Returns: an array of random bytes
|
||||
///
|
||||
/// - Throws: `.success` or an `.rngFailure` on failure
|
||||
///
|
||||
public class func generate(byteCount: Int) throws -> [UInt8] {
|
||||
|
||||
guard byteCount > 0 else {
|
||||
throw RNGStatus.paramError
|
||||
}
|
||||
|
||||
var bytes = Array(repeating: UInt8(0), count:byteCount)
|
||||
let status = generate(bytes: &bytes, byteCount: byteCount)
|
||||
|
||||
if status != .success {
|
||||
throw status
|
||||
}
|
||||
|
||||
return bytes
|
||||
}
|
||||
|
||||
///
|
||||
/// A version of generateBytes that always throws an error.
|
||||
///
|
||||
/// Use it to test that code handles this.
|
||||
///
|
||||
/// - Parameter bytesCount: Number of random bytes to generate
|
||||
///
|
||||
/// - Returns: An array of random bytes
|
||||
///
|
||||
public class func generateBytesThrow(byteCount: Int) throws -> [UInt8] {
|
||||
|
||||
if byteCount <= 0 {
|
||||
|
||||
fatalError("generate: byteCount must be positve and non-zero")
|
||||
}
|
||||
var bytes: [UInt8] = Array(repeating: UInt8(0), count:byteCount)
|
||||
let status = generate(bytes: &bytes, byteCount: byteCount)
|
||||
throw status
|
||||
//return bytes
|
||||
}
|
||||
}
|
||||
+100
@@ -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)
|
||||
}
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
//
|
||||
// Status.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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 os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
import CommonCrypto
|
||||
#elseif os(Linux)
|
||||
import OpenSSL
|
||||
#endif
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
///
|
||||
/// Links the native CommonCryptoStatus enumeration to Swift versions.
|
||||
///
|
||||
public enum Status: CCCryptorStatus, Swift.Error, CustomStringConvertible {
|
||||
|
||||
/// Successful
|
||||
case success
|
||||
|
||||
/// Parameter Error
|
||||
case paramError
|
||||
|
||||
/// Buffer too Small
|
||||
case bufferTooSmall
|
||||
|
||||
/// Memory Failure
|
||||
case memoryFailure
|
||||
|
||||
/// Alignment Error
|
||||
case alignmentError
|
||||
|
||||
/// Decode Error
|
||||
case decodeError
|
||||
|
||||
/// Unimplemented
|
||||
case unimplemented
|
||||
|
||||
/// Overflow
|
||||
case overflow
|
||||
|
||||
/// Random Number Generator Err
|
||||
case rngFailure
|
||||
|
||||
///
|
||||
/// Converts this value to a native `CCCryptorStatus` value.
|
||||
///
|
||||
public func toRaw() -> CCCryptorStatus {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success:
|
||||
return CCCryptorStatus(kCCSuccess)
|
||||
case .paramError:
|
||||
return CCCryptorStatus(kCCParamError)
|
||||
case .bufferTooSmall:
|
||||
return CCCryptorStatus(kCCBufferTooSmall)
|
||||
case .memoryFailure:
|
||||
return CCCryptorStatus(kCCMemoryFailure)
|
||||
case .alignmentError:
|
||||
return CCCryptorStatus(kCCAlignmentError)
|
||||
case .decodeError:
|
||||
return CCCryptorStatus(kCCDecodeError)
|
||||
case .unimplemented:
|
||||
return CCCryptorStatus(kCCUnimplemented)
|
||||
case .overflow:
|
||||
return CCCryptorStatus(kCCOverflow)
|
||||
case .rngFailure:
|
||||
return CCCryptorStatus(kCCRNGFailure)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Human readable descriptions of the values. (Not needed in Swift 2.0?)
|
||||
///
|
||||
static let descriptions = [
|
||||
success: "Success",
|
||||
paramError: "ParamError",
|
||||
bufferTooSmall: "BufferTooSmall",
|
||||
memoryFailure: "MemoryFailure",
|
||||
alignmentError: "AlignmentError",
|
||||
decodeError: "DecodeError",
|
||||
unimplemented: "Unimplemented",
|
||||
overflow: "Overflow",
|
||||
rngFailure: "RNGFailure"
|
||||
]
|
||||
|
||||
///
|
||||
/// Obtain human-readable string from enum value.
|
||||
///
|
||||
public var description: String {
|
||||
|
||||
return (Status.descriptions[self] != nil) ? Status.descriptions[self]! : ""
|
||||
}
|
||||
|
||||
///
|
||||
/// Create enum value from raw `CCCryptorStatus` value.
|
||||
///
|
||||
public static func fromRaw(status: CCCryptorStatus) -> Status? {
|
||||
|
||||
let from = [
|
||||
kCCSuccess: success,
|
||||
kCCParamError: paramError,
|
||||
kCCBufferTooSmall: bufferTooSmall,
|
||||
kCCMemoryFailure: memoryFailure,
|
||||
kCCAlignmentError: alignmentError,
|
||||
kCCDecodeError: decodeError,
|
||||
kCCUnimplemented: unimplemented,
|
||||
kCCOverflow: overflow,
|
||||
kCCRNGFailure: rngFailure
|
||||
]
|
||||
|
||||
return from[Int(status)]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
///
|
||||
/// Error status
|
||||
///
|
||||
public enum Status: Swift.Error, CustomStringConvertible {
|
||||
|
||||
/// Success
|
||||
case success
|
||||
|
||||
/// Unimplemented with reason
|
||||
case unimplemented(String)
|
||||
|
||||
/// Not supported with reason
|
||||
case notSupported(String)
|
||||
|
||||
/// Parameter Error
|
||||
case paramError
|
||||
|
||||
/// Failure with error code
|
||||
case fail(UInt)
|
||||
|
||||
/// Random Byte Generator Failure with error code
|
||||
case rngFailure(UInt)
|
||||
|
||||
/// The error code itself
|
||||
public var code: Int {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success:
|
||||
return 0
|
||||
|
||||
case .notSupported:
|
||||
return -1
|
||||
|
||||
case .unimplemented:
|
||||
return -2
|
||||
|
||||
case .paramError:
|
||||
return -3
|
||||
|
||||
case .fail(let code):
|
||||
return Int(code)
|
||||
|
||||
case .rngFailure(let code):
|
||||
return Int(code)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Create enum value from raw `SSL error code` value.
|
||||
///
|
||||
public static func fromRaw(status: UInt) -> Status? {
|
||||
|
||||
return Status.fail(status)
|
||||
}
|
||||
|
||||
///
|
||||
/// Obtain human-readable string for the error code.
|
||||
///
|
||||
public var description: String {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success:
|
||||
return "No error"
|
||||
|
||||
case .notSupported(let reason):
|
||||
return "Not supported: \(reason)"
|
||||
|
||||
case .unimplemented(let reason):
|
||||
return "Not implemented: \(reason)"
|
||||
|
||||
case .paramError:
|
||||
return "Invalid parameters passed"
|
||||
|
||||
case .fail(let errorCode):
|
||||
return "ERROR: code: \(errorCode), reason: \(errToString(ERR_error_string(UInt(errorCode), nil)))"
|
||||
|
||||
case .rngFailure(let errorCode):
|
||||
return "Random Byte Generator ERROR: code: \(errorCode), reason: \(errToString(ERR_error_string(UInt(errorCode), nil)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Operators
|
||||
|
||||
func == (lhs: Status, rhs: Status) -> Bool {
|
||||
|
||||
return lhs.code == rhs.code
|
||||
}
|
||||
|
||||
func != (lhs: Status, rhs: Status) -> Bool {
|
||||
|
||||
return lhs.code != rhs.code
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///
|
||||
/// CryptorError
|
||||
/// Thrown in caaes where a _fatalError()_ is **NOT** appropriate.
|
||||
///
|
||||
public enum CryptorError: Swift.Error, CustomStringConvertible {
|
||||
|
||||
/// Success
|
||||
case success
|
||||
|
||||
/// Invalid key size
|
||||
case invalidKeySize
|
||||
|
||||
/// Invalid IV size
|
||||
case invalidIVSizeOrLength
|
||||
|
||||
/// Fail with code and string
|
||||
case fail(Int32, String)
|
||||
|
||||
/// The error code itself
|
||||
public var errCode: Int32 {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success:
|
||||
return 0
|
||||
|
||||
case .invalidKeySize:
|
||||
return -1
|
||||
|
||||
case .invalidIVSizeOrLength:
|
||||
return -2
|
||||
|
||||
case .fail(let errCode, _):
|
||||
return Int32(errCode)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error Description
|
||||
public var description: String {
|
||||
|
||||
switch self {
|
||||
|
||||
case .success:
|
||||
return "Success"
|
||||
|
||||
case .invalidKeySize:
|
||||
return "Invalid key size."
|
||||
|
||||
case .invalidIVSizeOrLength:
|
||||
return "Invalid IV size or length."
|
||||
|
||||
case .fail(_, let reason):
|
||||
return reason
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+939
@@ -0,0 +1,939 @@
|
||||
//
|
||||
// StreamCryptor.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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 os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
import CommonCrypto
|
||||
#elseif os(Linux)
|
||||
import OpenSSL
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Encrypts or decrypts return results as they become available.
|
||||
///
|
||||
/// - Note: The underlying cipher may be a block or a stream cipher.
|
||||
///
|
||||
/// Use for large files or network streams.
|
||||
///
|
||||
/// For small, in-memory buffers Cryptor may be easier to use.
|
||||
///
|
||||
public class StreamCryptor {
|
||||
|
||||
#if os(Linux)
|
||||
|
||||
//
|
||||
// Key sizes
|
||||
//
|
||||
static let kCCKeySizeAES128 = 16
|
||||
static let kCCKeySizeAES192 = 24
|
||||
static let kCCKeySizeAES256 = 32
|
||||
static let kCCKeySizeDES = 8
|
||||
static let kCCKeySize3DES = 24
|
||||
static let kCCKeySizeMinCAST = 5
|
||||
static let kCCKeySizeMaxCAST = 16
|
||||
static let kCCKeySizeMinRC2 = 1
|
||||
static let kCCKeySizeMaxRC2 = 128
|
||||
static let kCCKeySizeMinBlowfish = 8
|
||||
static let kCCKeySizeMaxBlowfish = 56
|
||||
|
||||
//
|
||||
// Block sizes
|
||||
//
|
||||
static let kCCBlockSizeAES128 = 16
|
||||
static let kCCBlockSizeDES = 8
|
||||
static let kCCBlockSize3DES = 8
|
||||
static let kCCBlockSizeCAST = 8
|
||||
static let kCCBlockSizeRC2 = 8
|
||||
static let kCCBlockSizeBlowfish = 8
|
||||
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Enumerates Cryptor operations
|
||||
///
|
||||
public enum Operation {
|
||||
|
||||
/// Encrypting
|
||||
case encrypt
|
||||
|
||||
/// Decrypting
|
||||
case decrypt
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
/// Convert to native `CCOperation`
|
||||
func nativeValue() -> CCOperation {
|
||||
|
||||
switch self {
|
||||
|
||||
case .encrypt:
|
||||
return CCOperation(kCCEncrypt)
|
||||
|
||||
case .decrypt:
|
||||
return CCOperation(kCCDecrypt)
|
||||
}
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
/// Convert to native value
|
||||
func nativeValue() -> UInt32 {
|
||||
|
||||
switch self {
|
||||
|
||||
case .encrypt:
|
||||
return 0
|
||||
|
||||
case .decrypt:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Enumerates valid key sizes.
|
||||
///
|
||||
public enum ValidKeySize {
|
||||
|
||||
case fixed(Int)
|
||||
case discrete([Int])
|
||||
case range(Int, Int)
|
||||
|
||||
///
|
||||
/// Determines if a given `keySize` is valid for this algorithm.
|
||||
///
|
||||
/// - Parameter keySize: The size to test for validity.
|
||||
///
|
||||
/// - Returns: True if valid, false otherwise.
|
||||
///
|
||||
func isValidKeySize(keySize: Int) -> Bool {
|
||||
|
||||
switch self {
|
||||
|
||||
case .fixed(let fixed):
|
||||
return (fixed == keySize)
|
||||
|
||||
case .range(let min, let max):
|
||||
return ((keySize >= min) && (keySize <= max))
|
||||
|
||||
case .discrete(let values):
|
||||
return values.contains(keySize)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the next valid key size; that is, the first valid key size larger
|
||||
/// than the given value.
|
||||
///
|
||||
/// - Parameter keySize: The size for which the `next` size is desired.
|
||||
///
|
||||
/// - Returns: Will return `nil` if the passed in `keySize` is greater than the max.
|
||||
///
|
||||
func paddedKeySize(keySize: Int) -> Int? {
|
||||
|
||||
switch self {
|
||||
|
||||
case .fixed(let fixed):
|
||||
return (keySize <= fixed) ? fixed : nil
|
||||
|
||||
case .range(let min, let max):
|
||||
return (keySize > max) ? nil : ((keySize < min) ? min : keySize)
|
||||
|
||||
case .discrete(let values):
|
||||
return values.sorted().reduce(nil) { answer, current in
|
||||
return answer ?? ((current >= keySize) ? current : nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Maps CommonCryptoOptions onto a Swift struct.
|
||||
///
|
||||
public struct Options: OptionSet {
|
||||
|
||||
public typealias RawValue = Int
|
||||
public let rawValue: RawValue
|
||||
|
||||
/// Convert from a native value (i.e. `0`, `kCCOptionpkcs7Padding`, `kCCOptionECBMode`)
|
||||
public init(rawValue: RawValue) {
|
||||
self.rawValue = rawValue
|
||||
}
|
||||
|
||||
/// Convert from a native value (i.e. `0`, `kCCOptionpkcs7Padding`, `kCCOptionECBMode`)
|
||||
public init(_ rawValue: RawValue) {
|
||||
self.init(rawValue: rawValue)
|
||||
}
|
||||
|
||||
/// No options
|
||||
public static let none = Options(rawValue: 0)
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
/// Use padding. Needed unless the input is a integral number of blocks long.
|
||||
public static var pkcs7Padding = Options(rawValue:kCCOptionPKCS7Padding)
|
||||
|
||||
/// Electronic Code Book Mode. Don't use this.
|
||||
public static var ecbMode = Options(rawValue:kCCOptionECBMode)
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
/// Use padding. Needed unless the input is a integral number of blocks long.
|
||||
public static var pkcs7Padding = Options(rawValue:0x0001)
|
||||
|
||||
/// Electronic Code Book Mode. Don't use this.
|
||||
public static var ecbMode = Options(rawValue:0x0002)
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Enumerates available algorithms
|
||||
///
|
||||
public enum Algorithm {
|
||||
|
||||
/// Advanced Encryption Standard
|
||||
/// - Note: aes and aes128 are equivalent.
|
||||
case aes, aes128, aes192, aes256
|
||||
|
||||
/// Data Encryption Standard
|
||||
case des
|
||||
|
||||
/// Triple des
|
||||
case tripleDes
|
||||
|
||||
/// cast
|
||||
case cast
|
||||
|
||||
/// rc2
|
||||
case rc2
|
||||
|
||||
/// blowfish
|
||||
case blowfish
|
||||
|
||||
/// Blocksize, in bytes, of algorithm.
|
||||
public var blockSize: Int {
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128, .aes192, .aes256:
|
||||
return kCCBlockSizeAES128
|
||||
|
||||
case .des:
|
||||
return kCCBlockSizeDES
|
||||
|
||||
case .tripleDes:
|
||||
return kCCBlockSize3DES
|
||||
|
||||
case .cast:
|
||||
return kCCBlockSizeCAST
|
||||
|
||||
case .rc2:
|
||||
return kCCBlockSizeRC2
|
||||
|
||||
case .blowfish:
|
||||
return kCCBlockSizeBlowfish
|
||||
}
|
||||
}
|
||||
|
||||
public var defaultKeySize: Int {
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128:
|
||||
return kCCKeySizeAES128
|
||||
|
||||
case .aes192:
|
||||
return kCCKeySizeAES192
|
||||
|
||||
case .aes256:
|
||||
return kCCKeySizeAES256
|
||||
|
||||
case .des:
|
||||
return kCCKeySizeDES
|
||||
|
||||
case .tripleDes:
|
||||
return kCCKeySize3DES
|
||||
|
||||
case .cast:
|
||||
return kCCKeySizeMinCAST
|
||||
|
||||
case .rc2:
|
||||
return kCCKeySizeMinRC2
|
||||
|
||||
case .blowfish:
|
||||
return kCCKeySizeMinBlowfish
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
/// Native, CommonCrypto constant for algorithm.
|
||||
func nativeValue() -> CCAlgorithm {
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128, .aes192, .aes256:
|
||||
return CCAlgorithm(kCCAlgorithmAES)
|
||||
|
||||
case .des:
|
||||
return CCAlgorithm(kCCAlgorithmDES)
|
||||
|
||||
case .tripleDes:
|
||||
return CCAlgorithm(kCCAlgorithm3DES)
|
||||
|
||||
case .cast:
|
||||
return CCAlgorithm(kCCAlgorithmCAST)
|
||||
|
||||
case .rc2:
|
||||
return CCAlgorithm(kCCAlgorithmRC2)
|
||||
|
||||
case .blowfish:
|
||||
return CCAlgorithm(kCCAlgorithmBlowfish)
|
||||
}
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
/// Native, OpenSSL function for algorithm.
|
||||
func nativeValue(options: Options) -> OpaquePointer? {
|
||||
|
||||
if options == .pkcs7Padding || options == .none {
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128:
|
||||
return .init(EVP_aes_128_cbc())
|
||||
|
||||
case .aes192:
|
||||
return .init(EVP_aes_192_cbc())
|
||||
|
||||
case .aes256:
|
||||
return .init(EVP_aes_256_cbc())
|
||||
|
||||
case .des:
|
||||
return .init(EVP_des_cbc())
|
||||
|
||||
case .tripleDes:
|
||||
return .init(EVP_des_ede3_cbc())
|
||||
|
||||
case .cast:
|
||||
return .init(EVP_cast5_cbc())
|
||||
|
||||
case .rc2:
|
||||
return .init(EVP_rc2_cbc())
|
||||
|
||||
case .blowfish:
|
||||
return .init(EVP_bf_cbc())
|
||||
}
|
||||
}
|
||||
|
||||
if options == .ecbMode {
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128:
|
||||
return .init(EVP_aes_128_ecb())
|
||||
|
||||
case .aes192:
|
||||
return .init(EVP_aes_192_ecb())
|
||||
|
||||
case .aes256:
|
||||
return .init(EVP_aes_256_ecb())
|
||||
|
||||
case .des:
|
||||
return .init(EVP_des_ecb())
|
||||
|
||||
case .tripleDes:
|
||||
return .init(EVP_des_ede3_ecb())
|
||||
|
||||
case .cast:
|
||||
return .init(EVP_cast5_ecb())
|
||||
|
||||
case .rc2:
|
||||
return .init(EVP_rc2_ecb())
|
||||
|
||||
case .blowfish:
|
||||
return .init(EVP_bf_ecb())
|
||||
}
|
||||
}
|
||||
|
||||
fatalError("Unsupported options and/or algorithm.")
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///
|
||||
/// Determines the valid key size for this algorithm
|
||||
///
|
||||
/// - Returns: Valid key size for this algorithm.
|
||||
///
|
||||
func validKeySize() -> ValidKeySize {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128, .aes192, .aes256:
|
||||
return .discrete([kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256])
|
||||
|
||||
case .des:
|
||||
return .fixed(kCCKeySizeDES)
|
||||
|
||||
case .tripleDes:
|
||||
return .fixed(kCCKeySize3DES)
|
||||
|
||||
case .cast:
|
||||
return .range(kCCKeySizeMinCAST, kCCKeySizeMaxCAST)
|
||||
|
||||
case .rc2:
|
||||
return .range(kCCKeySizeMinRC2, kCCKeySizeMaxRC2)
|
||||
|
||||
case .blowfish:
|
||||
return .range(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish)
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
switch self {
|
||||
|
||||
case .aes, .aes128:
|
||||
return .fixed(kCCKeySizeAES128)
|
||||
|
||||
case .aes192:
|
||||
return .fixed(kCCKeySizeAES192)
|
||||
|
||||
case .aes256:
|
||||
return .fixed(kCCKeySizeAES256)
|
||||
|
||||
case .des:
|
||||
return .fixed(kCCKeySizeDES)
|
||||
|
||||
case .tripleDes:
|
||||
return .fixed(kCCKeySize3DES)
|
||||
|
||||
case .cast:
|
||||
return .range(kCCKeySizeMinCAST, kCCKeySizeMaxCAST)
|
||||
|
||||
case .rc2:
|
||||
return .range(kCCKeySizeMinRC2, kCCKeySizeMaxRC2)
|
||||
|
||||
case .blowfish:
|
||||
return .range(kCCKeySizeMinBlowfish, kCCKeySizeMaxBlowfish)
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Tests if a given keySize is valid for this algorithm
|
||||
///
|
||||
/// - Parameter keySize: The key size to be validated.
|
||||
///
|
||||
/// - Returns: True if valid, false otherwise.
|
||||
///
|
||||
func isValidKeySize(keySize: Int) -> Bool {
|
||||
return self.validKeySize().isValidKeySize(keySize: keySize)
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the next, if any, valid keySize greater or equal to a given `keySize` for this algorithm
|
||||
///
|
||||
/// - Parameter keySize: Key size for which the next size is requested.
|
||||
///
|
||||
/// - Returns: Next key size or nil
|
||||
///
|
||||
func paddedKeySize(keySize: Int) -> Int? {
|
||||
return self.validKeySize().paddedKeySize(keySize: keySize)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// The status code resulting from the last method call to this Cryptor.
|
||||
/// Used to get additional information when optional chaining collapes.
|
||||
///
|
||||
public internal(set) var status: Status = .success
|
||||
|
||||
///
|
||||
/// Context obtained. True if we have it, false otherwise.
|
||||
///
|
||||
private var haveContext: Bool = false
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
/// CommonCrypto Context
|
||||
private var context = UnsafeMutablePointer<CCCryptorRef?>.allocate(capacity: 1)
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
/// OpenSSL Cipher Context
|
||||
private let context: OpaquePointer? = .init(EVP_CIPHER_CTX_new())
|
||||
|
||||
/// Operation
|
||||
private var operation: Operation = .encrypt
|
||||
|
||||
/// The algorithm
|
||||
private var algorithm: Algorithm
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// MARK: Lifecycle Methods
|
||||
|
||||
///
|
||||
/// Default Initializer
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - operation: The operation to perform see Operation (Encrypt, Decrypt)
|
||||
/// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish)
|
||||
/// - keyBuffer: Pointer to key buffer
|
||||
/// - keyByteCount: Number of bytes in the key
|
||||
/// - ivBuffer: Initialization vector buffer
|
||||
/// - ivLength: Length of the ivBuffer
|
||||
///
|
||||
/// - Returns: New StreamCryptor instance.
|
||||
///
|
||||
public init(operation: Operation, algorithm: Algorithm, options: Options, keyBuffer: [UInt8], keyByteCount: Int, ivBuffer: UnsafePointer<UInt8>, ivLength: Int = 0) throws {
|
||||
|
||||
guard algorithm.isValidKeySize(keySize: keyByteCount) else {
|
||||
throw CryptorError.invalidKeySize
|
||||
}
|
||||
|
||||
guard options.contains(.ecbMode) || ivLength == algorithm.blockSize else {
|
||||
throw CryptorError.invalidIVSizeOrLength
|
||||
}
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
let rawStatus = CCCryptorCreate(operation.nativeValue(), algorithm.nativeValue(), CCOptions(options.rawValue), keyBuffer, keyByteCount, ivBuffer, self.context)
|
||||
|
||||
if let status = Status.fromRaw(status: rawStatus) {
|
||||
|
||||
self.status = status
|
||||
|
||||
} else {
|
||||
|
||||
throw CryptorError.fail(rawStatus, "Cryptor init returned unexpected status.")
|
||||
}
|
||||
|
||||
self.haveContext = true
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
self.algorithm = algorithm
|
||||
self.operation = operation
|
||||
|
||||
var rawStatus: Int32
|
||||
|
||||
switch self.operation {
|
||||
|
||||
case .encrypt:
|
||||
rawStatus = EVP_EncryptInit_ex(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), nil, keyBuffer, ivBuffer)
|
||||
|
||||
case .decrypt:
|
||||
rawStatus = EVP_DecryptInit_ex(.make(optional: self.context), .make(optional: algorithm.nativeValue(options: options)), nil, keyBuffer, ivBuffer)
|
||||
}
|
||||
|
||||
if rawStatus == 0 {
|
||||
|
||||
let errorCode = ERR_get_error()
|
||||
if let status = Status.fromRaw(status: errorCode) {
|
||||
self.status = status
|
||||
} else {
|
||||
|
||||
throw CryptorError.fail(Int32(errorCode), "Cryptor init returned unexpected status.")
|
||||
}
|
||||
}
|
||||
|
||||
self.haveContext = true
|
||||
|
||||
// Default to no padding...
|
||||
var needPadding: Int32 = 0
|
||||
if options == .pkcs7Padding {
|
||||
needPadding = 1
|
||||
}
|
||||
|
||||
// Note: This call must be AFTER the init call above...
|
||||
EVP_CIPHER_CTX_set_padding(.make(optional: self.context), needPadding)
|
||||
|
||||
self.status = Status.success
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a new StreamCryptor
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - operation: The operation to perform see Operation (Encrypt, Decrypt)
|
||||
/// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish)
|
||||
/// - key: A byte array containing key data
|
||||
/// - iv: A byte array containing initialization vector
|
||||
///
|
||||
/// - Returns: New StreamCryptor instance.
|
||||
///
|
||||
public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: [UInt8], iv: [UInt8]) throws {
|
||||
|
||||
guard let paddedKeySize = algorithm.paddedKeySize(keySize: key.count) else {
|
||||
throw CryptorError.invalidKeySize
|
||||
}
|
||||
|
||||
try self.init(operation:operation,
|
||||
algorithm:algorithm,
|
||||
options:options,
|
||||
keyBuffer:CryptoUtils.zeroPad(byteArray:key, blockSize: paddedKeySize),
|
||||
keyByteCount:paddedKeySize,
|
||||
ivBuffer:iv,
|
||||
ivLength:iv.count)
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates a new StreamCryptor
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - operation: The operation to perform see Operation (Encrypt, Decrypt)
|
||||
/// - algorithm: The algorithm to use see Algorithm (AES, des, tripleDes, cast, rc2, blowfish)
|
||||
/// - key: A string containing key data (will be interpreted as UTF8)
|
||||
/// - iv: A string containing initialization vector data (will be interpreted as UTF8)
|
||||
///
|
||||
/// - Returns: New StreamCryptor instance.
|
||||
///
|
||||
public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: String, iv: String) throws {
|
||||
|
||||
let keySize = key.utf8.count
|
||||
guard let paddedKeySize = algorithm.paddedKeySize(keySize: keySize) else {
|
||||
throw CryptorError.invalidKeySize
|
||||
}
|
||||
|
||||
try self.init(operation:operation,
|
||||
algorithm:algorithm,
|
||||
options:options,
|
||||
keyBuffer:CryptoUtils.zeroPad(string: key, blockSize: paddedKeySize),
|
||||
keyByteCount:paddedKeySize,
|
||||
ivBuffer:iv,
|
||||
ivLength:iv.utf8.count)
|
||||
}
|
||||
|
||||
///
|
||||
/// Cleanup
|
||||
///
|
||||
deinit {
|
||||
|
||||
// Ensure we've got a context before attempting to get rid of it...
|
||||
if self.haveContext == false {
|
||||
return
|
||||
}
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
// Ensure we've got a context before attempting to get rid of it...
|
||||
if self.context.pointee == nil {
|
||||
return
|
||||
}
|
||||
|
||||
let rawStatus = CCCryptorRelease(self.context.pointee)
|
||||
if let status = Status.fromRaw(status: rawStatus) {
|
||||
|
||||
if status != .success {
|
||||
|
||||
NSLog("WARNING: CCCryptoRelease failed with status \(rawStatus).")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fatalError("CCCryptorUpdate returned unexpected status.")
|
||||
}
|
||||
|
||||
#if swift(>=4.1)
|
||||
context.deallocate()
|
||||
#else
|
||||
context.deallocate(capacity: 1)
|
||||
#endif
|
||||
|
||||
self.haveContext = false
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
EVP_CIPHER_CTX_free(.make(optional: self.context))
|
||||
self.haveContext = false
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: Public Methods
|
||||
|
||||
///
|
||||
/// Add the contents of an Data buffer to the current encryption/decryption operation.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - dataIn: The input data
|
||||
/// - byteArrayOut: Output data
|
||||
///
|
||||
/// - Returns: A tuple containing the number of output bytes produced and the status (see Status)
|
||||
///
|
||||
public func update(dataIn: Data, byteArrayOut: inout [UInt8]) -> (Int, Status) {
|
||||
|
||||
let dataOutAvailable = byteArrayOut.count
|
||||
var dataOutMoved = 0
|
||||
#if swift(>=5.0)
|
||||
dataIn.withUnsafeBytes() {
|
||||
_ = update(bufferIn: $0.baseAddress!, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
|
||||
}
|
||||
#else
|
||||
dataIn.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
|
||||
_ = update(bufferIn: buffer, byteCountIn: dataIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
|
||||
}
|
||||
#endif
|
||||
return (dataOutMoved, self.status)
|
||||
}
|
||||
|
||||
///
|
||||
/// Add the contents of an NSData buffer to the current encryption/decryption operation.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - dataIn: The input data
|
||||
/// - byteArrayOut: Output data
|
||||
///
|
||||
/// - Returns: A tuple containing the number of output bytes produced and the status (see Status)
|
||||
///
|
||||
public func update(dataIn: NSData, byteArrayOut: inout [UInt8]) -> (Int, Status) {
|
||||
|
||||
let dataOutAvailable = byteArrayOut.count
|
||||
var dataOutMoved = 0
|
||||
var ptr = dataIn.bytes.assumingMemoryBound(to: UInt8.self).pointee
|
||||
_ = update(bufferIn: &ptr, byteCountIn: dataIn.length, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
|
||||
return (dataOutMoved, self.status)
|
||||
}
|
||||
|
||||
///
|
||||
/// Add the contents of a byte array to the current encryption/decryption operation.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - byteArrayIn: The input data
|
||||
/// - byteArrayOut: Output data
|
||||
///
|
||||
/// - Returns: A tuple containing the number of output bytes produced and the status (see Status)
|
||||
///
|
||||
public func update(byteArrayIn: [UInt8], byteArrayOut: inout [UInt8]) -> (Int, Status) {
|
||||
|
||||
let dataOutAvailable = byteArrayOut.count
|
||||
var dataOutMoved = 0
|
||||
_ = update(bufferIn: byteArrayIn, byteCountIn: byteArrayIn.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
|
||||
return (dataOutMoved, self.status)
|
||||
}
|
||||
|
||||
///
|
||||
/// Add the contents of a string (interpreted as UTF8) to the current encryption/decryption operation.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - byteArrayIn: The input data
|
||||
/// - byteArrayOut: Output data
|
||||
///
|
||||
/// - Returns: A tuple containing the number of output bytes produced and the status (see Status)
|
||||
///
|
||||
public func update(stringIn: String, byteArrayOut: inout [UInt8]) -> (Int, Status) {
|
||||
|
||||
let dataOutAvailable = byteArrayOut.count
|
||||
var dataOutMoved = 0
|
||||
_ = update(bufferIn: stringIn, byteCountIn: stringIn.utf8.count, bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
|
||||
return (dataOutMoved, self.status)
|
||||
}
|
||||
|
||||
///
|
||||
/// Retrieves all remaining encrypted or decrypted data from this cryptor.
|
||||
///
|
||||
/// - Note: If the underlying algorithm is an block cipher and the padding option has
|
||||
/// not been specified and the cumulative input to the cryptor has not been an integral
|
||||
/// multiple of the block length this will fail with an alignment error.
|
||||
///
|
||||
/// - Note: This method updates the status property
|
||||
///
|
||||
/// - Parameter byteArrayOut: The output bffer
|
||||
///
|
||||
/// - Returns: a tuple containing the number of output bytes produced and the status (see Status)
|
||||
///
|
||||
public func final(byteArrayOut: inout [UInt8]) -> (Int, Status) {
|
||||
|
||||
let dataOutAvailable = byteArrayOut.count
|
||||
var dataOutMoved = 0
|
||||
_ = final(bufferOut: &byteArrayOut, byteCapacityOut: dataOutAvailable, byteCountOut: &dataOutMoved)
|
||||
return (dataOutMoved, self.status)
|
||||
}
|
||||
|
||||
// MARK: - Low-level interface
|
||||
|
||||
///
|
||||
/// Update the buffer
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - bufferIn: Pointer to input buffer
|
||||
/// - inByteCount: Number of bytes contained in input buffer
|
||||
/// - bufferOut: Pointer to output buffer
|
||||
/// - outByteCapacity: Capacity of the output buffer in bytes
|
||||
/// - outByteCount: On successful completion, the number of bytes written to the output buffer
|
||||
///
|
||||
/// - Returns: Status of the update
|
||||
///
|
||||
public func update(bufferIn: UnsafeRawPointer, byteCountIn: Int, bufferOut: UnsafeMutablePointer<UInt8>, byteCapacityOut: Int, byteCountOut: inout Int) -> Status {
|
||||
|
||||
if self.status == .success {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
let rawStatus = CCCryptorUpdate(self.context.pointee, bufferIn, byteCountIn, bufferOut, byteCapacityOut, &byteCountOut)
|
||||
if let status = Status.fromRaw(status: rawStatus) {
|
||||
self.status = status
|
||||
} else {
|
||||
fatalError("CCCryptorUpdate returned unexpected status.")
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
var rawStatus: Int32
|
||||
var outLength: Int32 = 0
|
||||
|
||||
switch self.operation {
|
||||
|
||||
case .encrypt:
|
||||
rawStatus = EVP_EncryptUpdate(.make(optional: self.context), bufferOut, &outLength, bufferIn.assumingMemoryBound(to: UInt8.self), Int32(byteCountIn))
|
||||
|
||||
case .decrypt:
|
||||
rawStatus = EVP_DecryptUpdate(.make(optional: self.context), bufferOut, &outLength, bufferIn.assumingMemoryBound(to: UInt8.self), Int32(byteCountIn))
|
||||
}
|
||||
|
||||
byteCountOut = Int(outLength)
|
||||
|
||||
if rawStatus == 0 {
|
||||
|
||||
let errorCode = ERR_get_error()
|
||||
if let status = Status.fromRaw(status: errorCode) {
|
||||
self.status = status
|
||||
} else {
|
||||
fatalError("Cryptor update returned unexpected status.")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
self.status = Status.success
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return self.status
|
||||
}
|
||||
|
||||
///
|
||||
/// Retrieves all remaining encrypted or decrypted data from this cryptor.
|
||||
///
|
||||
/// - Note: If the underlying algorithm is an block cipher and the padding option has
|
||||
/// not been specified and the cumulative input to the cryptor has not been an integral
|
||||
/// multiple of the block length this will fail with an alignment error.
|
||||
///
|
||||
/// - Note: This method updates the status property
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - bufferOut: Pointer to output buffer
|
||||
/// - outByteCapacity: Capacity of the output buffer in bytes
|
||||
/// - outByteCount: On successful completion, the number of bytes written to the output buffer
|
||||
///
|
||||
/// - Returns: Status of the update
|
||||
///
|
||||
public func final(bufferOut: UnsafeMutablePointer<UInt8>, byteCapacityOut: Int, byteCountOut: inout Int) -> Status {
|
||||
|
||||
if self.status == Status.success {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
let rawStatus = CCCryptorFinal(self.context.pointee, bufferOut, byteCapacityOut, &byteCountOut)
|
||||
if let status = Status.fromRaw(status: rawStatus) {
|
||||
self.status = status
|
||||
} else {
|
||||
fatalError("CCCryptorUpdate returned unexpected status.")
|
||||
}
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
var rawStatus: Int32
|
||||
var outLength: Int32 = Int32(byteCapacityOut)
|
||||
|
||||
switch self.operation {
|
||||
|
||||
case .encrypt:
|
||||
rawStatus = EVP_EncryptFinal_ex(.make(optional: self.context), bufferOut, &outLength)
|
||||
|
||||
case .decrypt:
|
||||
rawStatus = EVP_DecryptFinal_ex(.make(optional: self.context), bufferOut, &outLength)
|
||||
}
|
||||
|
||||
byteCountOut = Int(outLength)
|
||||
|
||||
if rawStatus == 0 {
|
||||
|
||||
let errorCode = ERR_get_error()
|
||||
if let status = Status.fromRaw(status: errorCode) {
|
||||
self.status = status
|
||||
} else {
|
||||
fatalError("Cryptor final returned unexpected status.")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
self.status = Status.success
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return self.status
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the number of bytes that will be output by this Cryptor if inputBytes of additional
|
||||
/// data is input.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - inputByteCount: Number of bytes that will be input.
|
||||
/// - isFinal: True if buffer to be input will be the last input buffer, false otherwise.
|
||||
///
|
||||
/// - Returns: The final output length
|
||||
///
|
||||
public func getOutputLength(inputByteCount: Int, isFinal: Bool = false) -> Int {
|
||||
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
|
||||
return CCCryptorGetOutputLength(self.context.pointee, inputByteCount, isFinal)
|
||||
|
||||
#elseif os(Linux)
|
||||
|
||||
if inputByteCount == 0 {
|
||||
return self.algorithm.blockSize
|
||||
}
|
||||
|
||||
return (inputByteCount + self.algorithm.blockSize - (inputByteCount % self.algorithm.blockSize))
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Updateable.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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
|
||||
|
||||
///
|
||||
/// A protocol for calculations that can be updated with incremental data buffers.
|
||||
///
|
||||
public protocol Updatable {
|
||||
|
||||
/// Status of the calculation.
|
||||
var status: Status { get }
|
||||
|
||||
///
|
||||
/// Low-level update routine.
|
||||
/// Updates the calculation with the contents of a data buffer.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - buffer: Pointer to the data buffer
|
||||
/// - byteCount: Length of the buffer in bytes
|
||||
///
|
||||
/// - Returns: `Self` if no error for optional chaining, nil otherwise
|
||||
///
|
||||
func update(from buffer: UnsafeRawPointer, byteCount: size_t) -> Self?
|
||||
}
|
||||
|
||||
///
|
||||
/// Factors out common update code from Digest, HMAC and Cryptor.
|
||||
///
|
||||
extension Updatable {
|
||||
///
|
||||
/// Updates the current calculation with data contained in an `NSData` object.
|
||||
///
|
||||
/// - Parameter data: The `NSData` object
|
||||
///
|
||||
/// - Returns: Optional `Self` or nil
|
||||
///
|
||||
public func update(data: NSData) -> Self? {
|
||||
|
||||
_ = update(from: data.bytes, byteCount: size_t(data.length))
|
||||
return self.status == .success ? self : nil
|
||||
}
|
||||
|
||||
///
|
||||
/// Updates the current calculation with data contained in an `Data` object.
|
||||
///
|
||||
/// - Parameters data: The `Data` object
|
||||
///
|
||||
/// - Returns: Optional `Self` or nil
|
||||
///
|
||||
public func update(data: Data) -> Self? {
|
||||
|
||||
#if swift(>=5.0)
|
||||
_ = data.withUnsafeBytes() {
|
||||
|
||||
_ = update(from: $0.baseAddress!, byteCount: size_t(data.count))
|
||||
}
|
||||
#else
|
||||
_ = data.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
|
||||
|
||||
_ = update(from: buffer, byteCount: size_t(data.count))
|
||||
}
|
||||
#endif
|
||||
return self.status == .success ? self : nil
|
||||
}
|
||||
|
||||
///
|
||||
/// Updates the current calculation with data contained in a byte array.
|
||||
///
|
||||
/// - Parameter byteArray: The byte array
|
||||
///
|
||||
/// - Returns: Optional `Self` or nil
|
||||
///
|
||||
public func update(byteArray: [UInt8]) -> Self? {
|
||||
|
||||
_ = update(from: byteArray, byteCount: size_t(byteArray.count))
|
||||
return self.status == .success ? self : nil
|
||||
}
|
||||
|
||||
///
|
||||
/// Updates the current calculation with data contained in a String.
|
||||
/// The corresponding data will be generated using UTF8 encoding.
|
||||
///
|
||||
/// - Parameter string: The string of data
|
||||
///
|
||||
/// - Returns: Optional `Self` or nil
|
||||
///
|
||||
public func update(string: String) -> Self? {
|
||||
|
||||
_ = update(from: string, byteCount: size_t(string.utf8.count))
|
||||
return self.status == .success ? self : nil
|
||||
}
|
||||
}
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
//
|
||||
// Utilities.swift
|
||||
// Cryptor
|
||||
//
|
||||
// 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
|
||||
|
||||
//
|
||||
// Replaces Swift's native `fatalError` function to allow redirection
|
||||
// For more details about how this all works see:
|
||||
// https://marcosantadev.com/test-swift-fatalerror/
|
||||
//
|
||||
func fatalError(_ message: @autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line) -> Never {
|
||||
|
||||
FatalErrorUtil.fatalErrorClosure(message(), file, line)
|
||||
}
|
||||
|
||||
// Convert an UnsafeMutablePointer<Int8>? to a String, providing a
|
||||
// default value of empty string if the pointer is nil.
|
||||
//
|
||||
// - Parameter ptr: Pointer to string to be converted.
|
||||
//
|
||||
// - Returns: Converted string.
|
||||
//
|
||||
func errToString(_ ptr: UnsafeMutablePointer<Int8>?) -> String {
|
||||
if let ptr = ptr {
|
||||
return String(cString: ptr)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Allows redirection of `fatalError` for Unit Testing or for
|
||||
/// library users that want to handle such errors in another way.
|
||||
///
|
||||
struct FatalErrorUtil {
|
||||
|
||||
static var fatalErrorClosure: (String, StaticString, UInt) -> Never = defaultFatalErrorClosure
|
||||
private static let defaultFatalErrorClosure = { Swift.fatalError($0, file: $1, line: $2) }
|
||||
static func replaceFatalError(closure: @escaping (String, StaticString, UInt) -> Never) {
|
||||
fatalErrorClosure = closure
|
||||
}
|
||||
static func restoreFatalError() {
|
||||
fatalErrorClosure = defaultFatalErrorClosure
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Various utility functions for conversions
|
||||
///
|
||||
public struct CryptoUtils {
|
||||
|
||||
///
|
||||
/// Converts a single hexadecimal digit encoded as a Unicode Scalar to it's corresponding value.
|
||||
///
|
||||
/// - Parameter digit: A Unicode scalar in the set 0..9a..fA..F
|
||||
///
|
||||
/// - Returns: The hexadecimal value of the digit
|
||||
///
|
||||
static func convert(hexDigit digit: UnicodeScalar) -> UInt8? {
|
||||
|
||||
switch digit {
|
||||
|
||||
case UnicodeScalar(unicodeScalarLiteral:"0")...UnicodeScalar(unicodeScalarLiteral:"9"):
|
||||
return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"0").value)
|
||||
|
||||
case UnicodeScalar(unicodeScalarLiteral:"a")...UnicodeScalar(unicodeScalarLiteral:"f"):
|
||||
return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"a").value + 0xa)
|
||||
|
||||
case UnicodeScalar(unicodeScalarLiteral:"A")...UnicodeScalar(unicodeScalarLiteral:"F"):
|
||||
return UInt8(digit.value - UnicodeScalar(unicodeScalarLiteral:"A").value + 0xa)
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a string of hexadecimal digits to a byte array.
|
||||
///
|
||||
/// - Parameter string: The hex string (must contain an even number of digits)
|
||||
///
|
||||
/// - Returns: A byte array or [] if the input is not valid hexadecimal
|
||||
///
|
||||
public static func byteArray(fromHex string: String) -> [UInt8] {
|
||||
|
||||
var iterator = string.unicodeScalars.makeIterator()
|
||||
var byteArray: [UInt8] = []
|
||||
while let msn = iterator.next() {
|
||||
|
||||
if let lsn = iterator.next(),
|
||||
let hexMSN = convert(hexDigit: msn) ,
|
||||
let hexLSN = convert(hexDigit: lsn) {
|
||||
|
||||
byteArray += [ (hexMSN << 4 | hexLSN) ]
|
||||
|
||||
} else {
|
||||
|
||||
// @TODO: In the next major release this function should throw instead of returning an empty array.
|
||||
return []
|
||||
}
|
||||
}
|
||||
return byteArray
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a UTF-8 String to a byte array.
|
||||
///
|
||||
/// - Parameter string: the string
|
||||
///
|
||||
/// - Returns: A byte array
|
||||
///
|
||||
public static func byteArray(from string: String) -> [UInt8] {
|
||||
|
||||
let array = [UInt8](string.utf8)
|
||||
return array
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a string of hexadecimal digits to an `NSData` object.
|
||||
///
|
||||
/// - Parameter string: The hex string (must contain an even number of digits)
|
||||
///
|
||||
/// - Returns: An `NSData` object
|
||||
///
|
||||
public static func data(fromHex string: String) -> NSData {
|
||||
|
||||
let a = byteArray(fromHex: string)
|
||||
return NSData(bytes:a, length:a.count)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a string of hexadecimal digits to an `Data` object.
|
||||
///
|
||||
/// - Parameter string: The hex string (must contain an even number of digits)
|
||||
///
|
||||
/// - Returns: An `Data` object
|
||||
///
|
||||
public static func data(fromHex string: String) -> Data {
|
||||
|
||||
let a = byteArray(fromHex: string)
|
||||
return Data(bytes: a, count: a.count)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a byte array to an `NSData` object.
|
||||
///
|
||||
/// - Parameter byteArray: The byte array
|
||||
///
|
||||
/// - Returns: An `NSData` object
|
||||
///
|
||||
public static func data(from byteArray: [UInt8]) -> NSData {
|
||||
|
||||
return NSData(bytes:byteArray, length:byteArray.count)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a byte array to an `Data` object.
|
||||
///
|
||||
/// - Parameter byteArray: The byte array
|
||||
///
|
||||
/// - Returns: An `Data` object
|
||||
///
|
||||
public static func data(from byteArray: [UInt8]) -> Data {
|
||||
|
||||
return Data(bytes: byteArray, count: byteArray.count)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a byte array to a string of hexadecimal digits.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - byteArray: The Swift array
|
||||
/// - uppercase: True to use uppercase for letter digits, lowercase otherwise
|
||||
///
|
||||
/// - Returns: A String
|
||||
///
|
||||
public static func hexString(from byteArray: [UInt8], uppercase: Bool = false) -> String {
|
||||
|
||||
return byteArray.map() { String(format: (uppercase) ? "%02X" : "%02x", $0) }.reduce("", +)
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a Swift array to an `NSString` object.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - byteArray: The Swift array
|
||||
/// - uppercase: True to use uppercase for letter digits, lowercase otherwise
|
||||
///
|
||||
/// - Returns: An `NSString` object
|
||||
///
|
||||
public static func hexNSString(from byteArray: [UInt8], uppercase: Bool = false) -> NSString {
|
||||
|
||||
let formatString = (uppercase) ? "%02X" : "%02x"
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
return byteArray.map() { String(format: formatString, $0) }.reduce("", +) as NSString
|
||||
#else
|
||||
let aString = byteArray.map() { String(format: formatString, $0) }.reduce("", +)
|
||||
return NSString(string: aString)
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a byte array to a String containing a comma separated list of bytes.
|
||||
/// This is used to generate test data programmatically.
|
||||
///
|
||||
/// - Parameter byteArray: The byte array
|
||||
///
|
||||
/// - Returns: A String
|
||||
///
|
||||
public static func hexList(from byteArray: [UInt8]) -> String {
|
||||
|
||||
return byteArray.map() { String(format:"0x%02x, ", $0) }.reduce("", +)
|
||||
}
|
||||
|
||||
///
|
||||
/// Zero pads a byte array such that it is an integral number of `blockSizeinBytes` long.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - byteArray: The byte array
|
||||
/// - blockSizeInBytes: The block size in bytes.
|
||||
///
|
||||
/// - Returns: A Swift string
|
||||
///
|
||||
public static func zeroPad(byteArray: [UInt8], blockSize: Int) -> [UInt8] {
|
||||
|
||||
let pad = blockSize - (byteArray.count % blockSize)
|
||||
guard pad != 0 else {
|
||||
return byteArray
|
||||
}
|
||||
return byteArray + Array<UInt8>(repeating: 0, count: pad)
|
||||
}
|
||||
|
||||
///
|
||||
/// Zero pads a String (after UTF8 conversion) such that it is an integral number of `blockSizeinBytes` long.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - string: The String
|
||||
/// - blockSizeInBytes: The block size in bytes
|
||||
///
|
||||
/// - Returns: A byte array
|
||||
///
|
||||
public static func zeroPad(string: String, blockSize: Int) -> [UInt8] {
|
||||
|
||||
return zeroPad(byteArray: Array<UInt8>(string.utf8), blockSize: blockSize)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user