punchnet-macos/Tun/SwCrypt.swift
2025-05-12 16:25:33 +08:00

2424 lines
92 KiB
Swift

import Foundation
open class SwKeyStore {
public enum SecError: OSStatus, Error {
case unimplemented = -4
case param = -50
case allocate = -108
case notAvailable = -25291
case authFailed = -25293
case duplicateItem = -25299
case itemNotFound = -25300
case interactionNotAllowed = -25308
case decode = -26275
case missingEntitlement = -34018
public static var debugLevel = 1
init(_ status: OSStatus, function: String = #function, file: String = #file, line: Int = #line) {
self = SecError(rawValue: status)!
if SecError.debugLevel > 0 {
print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))")
}
}
init(_ type: SecError, function: String = #function, file: String = #file, line: Int = #line) {
self = type
if SecError.debugLevel > 0 {
print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))")
}
}
}
public static func upsertKey(_ pemKey: String, keyTag: String,
options: [NSString : AnyObject] = [:]) throws {
let pemKeyAsData = pemKey.data(using: String.Encoding.utf8)!
var parameters: [NSString : AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrIsPermanent: true as AnyObject,
kSecAttrApplicationTag: keyTag as AnyObject,
kSecValueData: pemKeyAsData as AnyObject
]
options.forEach { k, v in
parameters[k] = v
}
var status = SecItemAdd(parameters as CFDictionary, nil)
if status == errSecDuplicateItem {
try delKey(keyTag)
status = SecItemAdd(parameters as CFDictionary, nil)
}
guard status == errSecSuccess else { throw SecError(status) }
}
public static func getKey(_ keyTag: String) throws -> String {
let parameters: [NSString : AnyObject] = [
kSecClass : kSecClassKey,
kSecAttrKeyType : kSecAttrKeyTypeRSA,
kSecAttrApplicationTag : keyTag as AnyObject,
kSecReturnData : true as AnyObject
]
var data: AnyObject?
let status = SecItemCopyMatching(parameters as CFDictionary, &data)
guard status == errSecSuccess else { throw SecError(status) }
guard let pemKeyAsData = data as? Data else {
throw SecError(.decode)
}
guard let result = String(data: pemKeyAsData, encoding: String.Encoding.utf8) else {
throw SecError(.decode)
}
return result
}
public static func delKey(_ keyTag: String) throws {
let parameters: [NSString : AnyObject] = [
kSecClass : kSecClassKey,
kSecAttrApplicationTag: keyTag as AnyObject
]
let status = SecItemDelete(parameters as CFDictionary)
guard status == errSecSuccess else { throw SecError(status) }
}
}
open class SwKeyConvert {
public enum SwError: Error {
case invalidKey
case badPassphrase
case keyNotEncrypted
public static var debugLevel = 1
init(_ type: SwError, function: String = #function, file: String = #file, line: Int = #line) {
self = type
if SwError.debugLevel > 0 {
print("\(file):\(line): [\(function)] \(self._domain): \(self)")
}
}
}
open class PrivateKey {
public static func pemToPKCS1DER(_ pemKey: String) throws -> Data {
guard let derKey = try? PEM.PrivateKey.toDER(pemKey) else {
throw SwError(.invalidKey)
}
guard let pkcs1DERKey = PKCS8.PrivateKey.stripHeaderIfAny(derKey) else {
throw SwError(.invalidKey)
}
return pkcs1DERKey
}
public static func derToPKCS1PEM(_ derKey: Data) -> String {
return PEM.PrivateKey.toPEM(derKey)
}
public typealias EncMode = PEM.EncryptedPrivateKey.EncMode
public static func encryptPEM(_ pemKey: String, passphrase: String,
mode: EncMode) throws -> String {
do {
let derKey = try PEM.PrivateKey.toDER(pemKey)
return PEM.EncryptedPrivateKey.toPEM(derKey, passphrase: passphrase, mode: mode)
} catch {
throw SwError(.invalidKey)
}
}
public static func decryptPEM(_ pemKey: String, passphrase: String) throws -> String {
do {
let derKey = try PEM.EncryptedPrivateKey.toDER(pemKey, passphrase: passphrase)
return PEM.PrivateKey.toPEM(derKey)
} catch PEM.SwError.badPassphrase {
throw SwError(.badPassphrase)
} catch PEM.SwError.keyNotEncrypted {
throw SwError(.keyNotEncrypted)
} catch {
throw SwError(.invalidKey)
}
}
}
open class PublicKey {
public static func pemToPKCS1DER(_ pemKey: String) throws -> Data {
guard let derKey = try? PEM.PublicKey.toDER(pemKey) else {
throw SwError(.invalidKey)
}
guard let pkcs1DERKey = PKCS8.PublicKey.stripHeaderIfAny(derKey) else {
throw SwError(.invalidKey)
}
return pkcs1DERKey
}
public static func pemToPKCS8DER(_ pemKey: String) throws -> Data {
guard let derKey = try? PEM.PublicKey.toDER(pemKey) else {
throw SwError(.invalidKey)
}
return derKey
}
public static func derToPKCS1PEM(_ derKey: Data) -> String {
return PEM.PublicKey.toPEM(derKey)
}
public static func derToPKCS8PEM(_ derKey: Data) -> String {
let pkcs8Key = PKCS8.PublicKey.addHeader(derKey)
return PEM.PublicKey.toPEM(pkcs8Key)
}
}
}
open class PKCS8 {
open class PrivateKey {
// https://lapo.it/asn1js/
public static func getPKCS1DEROffset(_ derKey: Data) -> Int? {
let bytes = derKey.bytesView
var offset = 0
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x30 else { return nil }
offset += 1
guard bytes.length > offset else { return nil }
if bytes[offset] > 0x80 {
offset += Int(bytes[offset]) - 0x80
}
offset += 1
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x02 else { return nil }
offset += 3
// without PKCS8 header
guard bytes.length > offset else { return nil }
if bytes[offset] == 0x02 {
return 0
}
let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
guard bytes.length > offset + OID.count else { return nil }
let slice = derKey.bytesViewRange(NSRange(location: offset, length: OID.count))
guard OID.elementsEqual(slice) else { return nil }
offset += OID.count
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x04 else { return nil }
offset += 1
guard bytes.length > offset else { return nil }
if bytes[offset] > 0x80 {
offset += Int(bytes[offset]) - 0x80
}
offset += 1
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x30 else { return nil }
return offset
}
public static func stripHeaderIfAny(_ derKey: Data) -> Data? {
guard let offset = getPKCS1DEROffset(derKey) else {
return nil
}
return derKey.subdata(in: offset..<derKey.count)
}
public static func hasCorrectHeader(_ derKey: Data) -> Bool {
return getPKCS1DEROffset(derKey) != nil
}
}
open class PublicKey {
public static func addHeader(_ derKey: Data) -> Data {
var result = Data()
let encodingLength: Int = encodedOctets(derKey.count + 1).count
let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
var builder: [UInt8] = []
// ASN.1 SEQUENCE
builder.append(0x30)
// Overall size, made of OID + bitstring encoding + actual key
let size = OID.count + 2 + encodingLength + derKey.count
let encodedSize = encodedOctets(size)
builder.append(contentsOf: encodedSize)
result.append(builder, count: builder.count)
result.append(OID, count: OID.count)
builder.removeAll(keepingCapacity: false)
builder.append(0x03)
builder.append(contentsOf: encodedOctets(derKey.count + 1))
builder.append(0x00)
result.append(builder, count: builder.count)
// Actual key bytes
result.append(derKey)
return result
}
// https://lapo.it/asn1js/
public static func getPKCS1DEROffset(_ derKey: Data) -> Int? {
let bytes = derKey.bytesView
var offset = 0
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x30 else { return nil }
offset += 1
guard bytes.length > offset else { return nil }
if bytes[offset] > 0x80 {
offset += Int(bytes[offset]) - 0x80
}
offset += 1
// without PKCS8 header
guard bytes.length > offset else { return nil }
if bytes[offset] == 0x02 {
return 0
}
let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
guard bytes.length > offset + OID.count else { return nil }
let slice = derKey.bytesViewRange(NSRange(location: offset, length: OID.count))
guard OID.elementsEqual(slice) else { return nil }
offset += OID.count
// Type
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x03 else { return nil }
offset += 1
guard bytes.length > offset else { return nil }
if bytes[offset] > 0x80 {
offset += Int(bytes[offset]) - 0x80
}
offset += 1
// Contents should be separated by a null from the header
guard bytes.length > offset else { return nil }
guard bytes[offset] == 0x00 else { return nil }
offset += 1
guard bytes.length > offset else { return nil }
return offset
}
public static func stripHeaderIfAny(_ derKey: Data) -> Data? {
guard let offset = getPKCS1DEROffset(derKey) else {
return nil
}
return derKey.subdata(in: offset..<derKey.count)
}
public static func hasCorrectHeader(_ derKey: Data) -> Bool {
return getPKCS1DEROffset(derKey) != nil
}
fileprivate static func encodedOctets(_ int: Int) -> [UInt8] {
// Short form
if int < 128 {
return [UInt8(int)]
}
// Long form
let i = (int / 256) + 1
var len = int
var result: [UInt8] = [UInt8(i + 0x80)]
for _ in 0..<i {
result.insert(UInt8(len & 0xFF), at: 1)
len = len >> 8
}
return result
}
}
}
open class PEM {
public enum SwError: Error {
case parse(String)
case badPassphrase
case keyNotEncrypted
public static var debugLevel = 1
init(_ type: SwError, function: String = #function, file: String = #file, line: Int = #line) {
self = type
if SwError.debugLevel > 0 {
print("\(file):\(line): [\(function)] \(self._domain): \(self)")
}
}
}
open class PrivateKey {
public static func toDER(_ pemKey: String) throws -> Data {
guard let strippedKey = stripHeader(pemKey) else {
throw SwError(.parse("header"))
}
guard let data = PEM.base64Decode(strippedKey) else {
throw SwError(.parse("base64decode"))
}
return data
}
public static func toPEM(_ derKey: Data) -> String {
let base64 = PEM.base64Encode(derKey)
return addRSAHeader(base64)
}
fileprivate static let prefix = "-----BEGIN PRIVATE KEY-----\n"
fileprivate static let suffix = "\n-----END PRIVATE KEY-----"
fileprivate static let rsaPrefix = "-----BEGIN RSA PRIVATE KEY-----\n"
fileprivate static let rsaSuffix = "\n-----END RSA PRIVATE KEY-----"
fileprivate static func addHeader(_ base64: String) -> String {
return prefix + base64 + suffix
}
fileprivate static func addRSAHeader(_ base64: String) -> String {
return rsaPrefix + base64 + rsaSuffix
}
fileprivate static func stripHeader(_ pemKey: String) -> String? {
return PEM.stripHeaderFooter(pemKey, header: prefix, footer: suffix) ??
PEM.stripHeaderFooter(pemKey, header: rsaPrefix, footer: rsaSuffix)
}
}
open class PublicKey {
public static func toDER(_ pemKey: String) throws -> Data {
guard let strippedKey = stripHeader(pemKey) else {
throw SwError(.parse("header"))
}
guard let data = PEM.base64Decode(strippedKey) else {
throw SwError(.parse("base64decode"))
}
return data
}
public static func toPEM(_ derKey: Data) -> String {
let base64 = PEM.base64Encode(derKey)
return addHeader(base64)
}
fileprivate static let pemPrefix = "-----BEGIN PUBLIC KEY-----\n"
fileprivate static let pemSuffix = "\n-----END PUBLIC KEY-----"
fileprivate static func addHeader(_ base64: String) -> String {
return pemPrefix + base64 + pemSuffix
}
fileprivate static func stripHeader(_ pemKey: String) -> String? {
return PEM.stripHeaderFooter(pemKey, header: pemPrefix, footer: pemSuffix)
}
}
// OpenSSL PKCS#1 compatible encrypted private key
open class EncryptedPrivateKey {
public enum EncMode {
case aes128CBC, aes256CBC
}
public static func toDER(_ pemKey: String, passphrase: String) throws -> Data {
guard let strippedKey = PrivateKey.stripHeader(pemKey) else {
throw SwError(.parse("header"))
}
guard let mode = getEncMode(strippedKey) else {
throw SwError(.keyNotEncrypted)
}
guard let iv = getIV(strippedKey) else {
throw SwError(.parse("iv"))
}
let aesKey = getAESKey(mode, passphrase: passphrase, iv: iv)
let base64Data = String(strippedKey[strippedKey.index(strippedKey.startIndex, offsetBy: aesHeaderLength)...])
guard let data = PEM.base64Decode(base64Data) else {
throw SwError(.parse("base64decode"))
}
guard let decrypted = try? decryptKey(data, key: aesKey, iv: iv) else {
throw SwError(.badPassphrase)
}
guard PKCS8.PrivateKey.hasCorrectHeader(decrypted) else {
throw SwError(.badPassphrase)
}
return decrypted
}
public static func toPEM(_ derKey: Data, passphrase: String, mode: EncMode) -> String {
let iv = CC.generateRandom(16)
let aesKey = getAESKey(mode, passphrase: passphrase, iv: iv)
let encrypted = encryptKey(derKey, key: aesKey, iv: iv)
let encryptedDERKey = addEncryptHeader(encrypted, iv: iv, mode: mode)
return PrivateKey.addRSAHeader(encryptedDERKey)
}
fileprivate static let aes128CBCInfo = "Proc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,"
fileprivate static let aes256CBCInfo = "Proc-Type: 4,ENCRYPTED\nDEK-Info: AES-256-CBC,"
fileprivate static let aesInfoLength = aes128CBCInfo.count
fileprivate static let aesIVInHexLength = 32
fileprivate static let aesHeaderLength = aesInfoLength + aesIVInHexLength
fileprivate static func addEncryptHeader(_ key: Data, iv: Data, mode: EncMode) -> String {
return getHeader(mode) + iv.hexadecimalString() + "\n\n" + PEM.base64Encode(key)
}
fileprivate static func getHeader(_ mode: EncMode) -> String {
switch mode {
case .aes128CBC: return aes128CBCInfo
case .aes256CBC: return aes256CBCInfo
}
}
fileprivate static func getEncMode(_ strippedKey: String) -> EncMode? {
if strippedKey.hasPrefix(aes128CBCInfo) {
return .aes128CBC
}
if strippedKey.hasPrefix(aes256CBCInfo) {
return .aes256CBC
}
return nil
}
fileprivate static func getIV(_ strippedKey: String) -> Data? {
let ivInHex = String(strippedKey[strippedKey.index(strippedKey.startIndex, offsetBy: aesInfoLength) ..< strippedKey.index(strippedKey.startIndex, offsetBy: aesHeaderLength)])
return ivInHex.dataFromHexadecimalString()
}
fileprivate static func getAESKey(_ mode: EncMode, passphrase: String, iv: Data) -> Data {
switch mode {
case .aes128CBC: return getAES128Key(passphrase, iv: iv)
case .aes256CBC: return getAES256Key(passphrase, iv: iv)
}
}
fileprivate static func getAES128Key(_ passphrase: String, iv: Data) -> Data {
// 128bit_Key = MD5(Passphrase + Salt)
let pass = passphrase.data(using: String.Encoding.utf8)!
let salt = iv.subdata(in: 0..<8)
var key = pass
key.append(salt)
return CC.digest(key, alg: .md5)
}
fileprivate static func getAES256Key(_ passphrase: String, iv: Data) -> Data {
// 128bit_Key = MD5(Passphrase + Salt)
// 256bit_Key = 128bit_Key + MD5(128bit_Key + Passphrase + Salt)
let pass = passphrase.data(using: String.Encoding.utf8)!
let salt = iv.subdata(in: 0 ..< 8)
var first = pass
first.append(salt)
let aes128Key = CC.digest(first, alg: .md5)
var sec = aes128Key
sec.append(pass)
sec.append(salt)
var aes256Key = aes128Key
aes256Key.append(CC.digest(sec, alg: .md5))
return aes256Key
}
fileprivate static func encryptKey(_ data: Data, key: Data, iv: Data) -> Data {
return try! CC.crypt(
.encrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding,
data: data, key: key, iv: iv)
}
fileprivate static func decryptKey(_ data: Data, key: Data, iv: Data) throws -> Data {
return try CC.crypt(
.decrypt, blockMode: .cbc, algorithm: .aes, padding: .pkcs7Padding,
data: data, key: key, iv: iv)
}
}
fileprivate static func stripHeaderFooter(_ data: String, header: String, footer: String) -> String? {
guard data.hasPrefix(header) else {
return nil
}
guard let r = data.range(of: footer) else {
return nil
}
return String(data[header.endIndex ..< r.lowerBound])
}
fileprivate static func base64Decode(_ base64Data: String) -> Data? {
return Data(base64Encoded: base64Data, options: [.ignoreUnknownCharacters])
}
fileprivate static func base64Encode(_ key: Data) -> String {
return key.base64EncodedString(
options: [.lineLength64Characters, .endLineWithLineFeed])
}
}
open class CC {
public typealias CCCryptorStatus = Int32
public enum CCError: CCCryptorStatus, Error {
case paramError = -4300
case bufferTooSmall = -4301
case memoryFailure = -4302
case alignmentError = -4303
case decodeError = -4304
case unimplemented = -4305
case overflow = -4306
case rngFailure = -4307
case unspecifiedError = -4308
case callSequenceError = -4309
case keySizeError = -4310
case invalidKey = -4311
public static var debugLevel = 1
init(_ status: CCCryptorStatus, function: String = #function,
file: String = #file, line: Int = #line) {
self = CCError(rawValue: status)!
if CCError.debugLevel > 0 {
print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))")
}
}
init(_ type: CCError, function: String = #function, file: String = #file, line: Int = #line) {
self = type
if CCError.debugLevel > 0 {
print("\(file):\(line): [\(function)] \(self._domain): \(self) (\(self.rawValue))")
}
}
}
public static func generateRandom(_ size: Int) -> Data {
var data = Data(count: size)
data.withUnsafeMutableBytes { dataBytes -> Void in
_ = CCRandomGenerateBytes!(dataBytes.baseAddress!, size)
return
}
return data
}
public typealias CCDigestAlgorithm = UInt32
public enum DigestAlgorithm: CCDigestAlgorithm {
case none = 0
case md5 = 3
case rmd128 = 4, rmd160 = 5, rmd256 = 6, rmd320 = 7
case sha1 = 8
case sha224 = 9, sha256 = 10, sha384 = 11, sha512 = 12
var length: Int {
return CCDigestGetOutputSize!(self.rawValue)
}
}
public static func digest(_ data: Data, alg: DigestAlgorithm) -> Data {
var output = Data(count: alg.length)
withUnsafePointers(data, &output, { dataBytes, outputBytes in
_ = CCDigest!(alg.rawValue,
dataBytes,
data.count,
outputBytes)
})
return output
}
public typealias CCHmacAlgorithm = UInt32
public enum HMACAlg: CCHmacAlgorithm {
case sha1, md5, sha256, sha384, sha512, sha224
var digestLength: Int {
switch self {
case .sha1: return 20
case .md5: return 16
case .sha256: return 32
case .sha384: return 48
case .sha512: return 64
case .sha224: return 28
}
}
}
public static func HMAC(_ data: Data, alg: HMACAlg, key: Data) -> Data {
var buffer = Data(count: alg.digestLength)
withUnsafePointers(key, data, &buffer, { keyBytes, dataBytes, bufferBytes in
CCHmac!(alg.rawValue,
keyBytes, key.count,
dataBytes, data.count,
bufferBytes)
})
return buffer
}
public typealias CCOperation = UInt32
public enum OpMode: CCOperation {
case encrypt = 0, decrypt
}
public typealias CCMode = UInt32
public enum BlockMode: CCMode {
case ecb = 1, cbc, cfb, ctr, f8, lrw, ofb, xts, rc4, cfb8
var needIV: Bool {
switch self {
case .cbc, .cfb, .ctr, .ofb, .cfb8: return true
default: return false
}
}
}
public enum AuthBlockMode: CCMode {
case gcm = 11, ccm
}
public typealias CCAlgorithm = UInt32
public enum Algorithm: CCAlgorithm {
case aes = 0, des, threeDES, cast, rc4, rc2, blowfish
var blockSize: Int? {
switch self {
case .aes: return 16
case .des: return 8
case .threeDES: return 8
case .cast: return 8
case .rc2: return 8
case .blowfish: return 8
default: return nil
}
}
}
public typealias CCPadding = UInt32
public enum Padding: CCPadding {
case noPadding = 0, pkcs7Padding
}
public static func crypt(_ opMode: OpMode, blockMode: BlockMode,
algorithm: Algorithm, padding: Padding,
data: Data, key: Data, iv: Data) throws -> Data {
if blockMode.needIV {
guard iv.count == algorithm.blockSize else { throw CCError(.paramError) }
}
var cryptor: CCCryptorRef? = nil
var status = withUnsafePointers(iv, key, { ivBytes, keyBytes in
return CCCryptorCreateWithMode!(
opMode.rawValue, blockMode.rawValue,
algorithm.rawValue, padding.rawValue,
ivBytes, keyBytes, key.count,
nil, 0, 0,
CCModeOptions(), &cryptor)
})
guard status == noErr else { throw CCError(status) }
defer { _ = CCCryptorRelease!(cryptor!) }
let needed = CCCryptorGetOutputLength!(cryptor!, data.count, true)
var result = Data(count: needed)
let rescount = result.count
var updateLen: size_t = 0
status = withUnsafePointers(data, &result, { dataBytes, resultBytes in
return CCCryptorUpdate!(
cryptor!,
dataBytes, data.count,
resultBytes, rescount,
&updateLen)
})
guard status == noErr else { throw CCError(status) }
var finalLen: size_t = 0
status = result.withUnsafeMutableBytes { resultBytes -> OSStatus in
return CCCryptorFinal!(
cryptor!,
resultBytes.baseAddress! + updateLen,
rescount - updateLen,
&finalLen)
}
guard status == noErr else { throw CCError(status) }
result.count = updateLen + finalLen
return result
}
// The same behaviour as in the CCM pdf
// http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C_updated-July20_2007.pdf
public static func cryptAuth(_ opMode: OpMode, blockMode: AuthBlockMode, algorithm: Algorithm,
data: Data, aData: Data,
key: Data, iv: Data, tagLength: Int) throws -> Data {
let cryptFun = blockMode == .gcm ? GCM.crypt : CCM.crypt
if opMode == .encrypt {
let (cipher, tag) = try cryptFun(opMode, algorithm, data,
key, iv, aData, tagLength)
var result = cipher
result.append(tag)
return result
} else {
let cipher = data.subdata(in: 0..<(data.count - tagLength))
let tag = data.subdata(
in: (data.count - tagLength)..<data.count)
let (plain, vTag) = try cryptFun(opMode, algorithm, cipher,
key, iv, aData, tagLength)
guard tag == vTag else {
throw CCError(.decodeError)
}
return plain
}
}
public static func digestAvailable() -> Bool {
return CCDigest != nil &&
CCDigestGetOutputSize != nil
}
public static func randomAvailable() -> Bool {
return CCRandomGenerateBytes != nil
}
public static func hmacAvailable() -> Bool {
return CCHmac != nil
}
public static func cryptorAvailable() -> Bool {
return CCCryptorCreateWithMode != nil &&
CCCryptorGetOutputLength != nil &&
CCCryptorUpdate != nil &&
CCCryptorFinal != nil &&
CCCryptorRelease != nil
}
public static func available() -> Bool {
return digestAvailable() &&
randomAvailable() &&
hmacAvailable() &&
cryptorAvailable() &&
KeyDerivation.available() &&
KeyWrap.available() &&
RSA.available() &&
DH.available() &&
EC.available() &&
CRC.available() &&
CMAC.available() &&
GCM.available() &&
CCM.available()
}
fileprivate typealias CCCryptorRef = UnsafeRawPointer
fileprivate typealias CCRNGStatus = CCCryptorStatus
fileprivate typealias CC_LONG = UInt32
fileprivate typealias CCModeOptions = UInt32
fileprivate typealias CCRandomGenerateBytesT = @convention(c) (
_ bytes: UnsafeMutableRawPointer,
_ count: size_t) -> CCRNGStatus
fileprivate typealias CCDigestGetOutputSizeT = @convention(c) (
_ algorithm: CCDigestAlgorithm) -> size_t
fileprivate typealias CCDigestT = @convention(c) (
_ algorithm: CCDigestAlgorithm,
_ data: UnsafeRawPointer,
_ dataLen: size_t,
_ output: UnsafeMutableRawPointer) -> CInt
fileprivate typealias CCHmacT = @convention(c) (
_ algorithm: CCHmacAlgorithm,
_ key: UnsafeRawPointer,
_ keyLength: Int,
_ data: UnsafeRawPointer,
_ dataLength: Int,
_ macOut: UnsafeMutableRawPointer) -> Void
fileprivate typealias CCCryptorCreateWithModeT = @convention(c)(
_ op: CCOperation,
_ mode: CCMode,
_ alg: CCAlgorithm,
_ padding: CCPadding,
_ iv: UnsafeRawPointer?,
_ key: UnsafeRawPointer, _ keyLength: Int,
_ tweak: UnsafeRawPointer?, _ tweakLength: Int,
_ numRounds: Int32, _ options: CCModeOptions,
_ cryptorRef: UnsafeMutablePointer<CCCryptorRef?>) -> CCCryptorStatus
fileprivate typealias CCCryptorGetOutputLengthT = @convention(c)(
_ cryptorRef: CCCryptorRef,
_ inputLength: size_t,
_ final: Bool) -> size_t
fileprivate typealias CCCryptorUpdateT = @convention(c)(
_ cryptorRef: CCCryptorRef,
_ dataIn: UnsafeRawPointer,
_ dataInLength: Int,
_ dataOut: UnsafeMutableRawPointer,
_ dataOutAvailable: Int,
_ dataOutMoved: UnsafeMutablePointer<Int>) -> CCCryptorStatus
fileprivate typealias CCCryptorFinalT = @convention(c)(
_ cryptorRef: CCCryptorRef,
_ dataOut: UnsafeMutableRawPointer,
_ dataOutAvailable: Int,
_ dataOutMoved: UnsafeMutablePointer<Int>) -> CCCryptorStatus
fileprivate typealias CCCryptorReleaseT = @convention(c)
(_ cryptorRef: CCCryptorRef) -> CCCryptorStatus
fileprivate static let dl = dlopen("/usr/lib/system/libcommonCrypto.dylib", RTLD_NOW)
fileprivate static let CCRandomGenerateBytes: CCRandomGenerateBytesT? =
getFunc(dl!, f: "CCRandomGenerateBytes")
fileprivate static let CCDigestGetOutputSize: CCDigestGetOutputSizeT? =
getFunc(dl!, f: "CCDigestGetOutputSize")
fileprivate static let CCDigest: CCDigestT? = getFunc(dl!, f: "CCDigest")
fileprivate static let CCHmac: CCHmacT? = getFunc(dl!, f: "CCHmac")
fileprivate static let CCCryptorCreateWithMode: CCCryptorCreateWithModeT? =
getFunc(dl!, f: "CCCryptorCreateWithMode")
fileprivate static let CCCryptorGetOutputLength: CCCryptorGetOutputLengthT? =
getFunc(dl!, f: "CCCryptorGetOutputLength")
fileprivate static let CCCryptorUpdate: CCCryptorUpdateT? =
getFunc(dl!, f: "CCCryptorUpdate")
fileprivate static let CCCryptorFinal: CCCryptorFinalT? =
getFunc(dl!, f: "CCCryptorFinal")
fileprivate static let CCCryptorRelease: CCCryptorReleaseT? =
getFunc(dl!, f: "CCCryptorRelease")
open class GCM {
public static func crypt(_ opMode: OpMode, algorithm: Algorithm, data: Data,
key: Data, iv: Data,
aData: Data, tagLength: Int) throws -> (Data, Data) {
var result = Data(count: data.count)
var tagLength_ = tagLength
var tag = Data(count: tagLength)
let status = withUnsafePointers(key, iv, aData, data, &result, &tag, {
keyBytes, ivBytes, aDataBytes, dataBytes, resultBytes, tagBytes in
return CCCryptorGCM!(opMode.rawValue, algorithm.rawValue,
keyBytes, key.count, ivBytes, iv.count,
aDataBytes, aData.count,
dataBytes, data.count,
resultBytes, tagBytes, &tagLength_)
})
guard status == noErr else { throw CCError(status) }
tag.count = tagLength_
return (result, tag)
}
public static func available() -> Bool {
if CCCryptorGCM != nil {
return true
}
return false
}
fileprivate typealias CCCryptorGCMT = @convention(c) (_ op: CCOperation, _ alg: CCAlgorithm,
_ key: UnsafeRawPointer, _ keyLength: Int,
_ iv: UnsafeRawPointer, _ ivLen: Int,
_ aData: UnsafeRawPointer, _ aDataLen: Int,
_ dataIn: UnsafeRawPointer, _ dataInLength: Int,
_ dataOut: UnsafeMutableRawPointer,
_ tag: UnsafeRawPointer, _ tagLength: UnsafeMutablePointer<Int>) -> CCCryptorStatus
fileprivate static let CCCryptorGCM: CCCryptorGCMT? = getFunc(dl!, f: "CCCryptorGCM")
}
open class CCM {
public static func crypt(_ opMode: OpMode, algorithm: Algorithm, data: Data,
key: Data, iv: Data,
aData: Data, tagLength: Int) throws -> (Data, Data) {
var cryptor: CCCryptorRef? = nil
var status = key.withUnsafeBytes { keyBytes -> OSStatus in
CCCryptorCreateWithMode!(
opMode.rawValue, AuthBlockMode.ccm.rawValue,
algorithm.rawValue, Padding.noPadding.rawValue,
nil, keyBytes.baseAddress!, key.count, nil, 0,
0, CCModeOptions(), &cryptor)
}
guard status == noErr else { throw CCError(status) }
defer { _ = CCCryptorRelease!(cryptor!) }
status = CCCryptorAddParameter!(cryptor!,
Parameter.dataSize.rawValue, nil, data.count)
guard status == noErr else { throw CCError(status) }
status = CCCryptorAddParameter!(cryptor!, Parameter.macSize.rawValue, nil, tagLength)
guard status == noErr else { throw CCError(status) }
status = iv.withUnsafeBytes { ivBytes -> OSStatus in
CCCryptorAddParameter!(cryptor!, Parameter.iv.rawValue, ivBytes.baseAddress!, iv.count)
}
guard status == noErr else { throw CCError(status) }
status = aData.withUnsafeBytes { aDataBytes -> OSStatus in
CCCryptorAddParameter!(cryptor!, Parameter.authData.rawValue, aDataBytes.baseAddress!, aData.count)
}
guard status == noErr else { throw CCError(status) }
var result = Data(count: data.count)
let rescount = result.count
var updateLen: size_t = 0
status = withUnsafePointers(data, &result, { dataBytes, resultBytes in
return CCCryptorUpdate!(
cryptor!, dataBytes, data.count,
resultBytes, rescount,
&updateLen)
})
guard status == noErr else { throw CCError(status) }
var finalLen: size_t = 0
status = result.withUnsafeMutableBytes { resultBytes -> OSStatus in
CCCryptorFinal!(cryptor!, resultBytes.baseAddress! + updateLen,
rescount - updateLen,
&finalLen)
}
guard status == noErr else { throw CCError(status) }
result.count = updateLen + finalLen
var tagLength_ = tagLength
var tag = Data(count: tagLength)
status = tag.withUnsafeMutableBytes { tagBytes -> OSStatus in
CCCryptorGetParameter!(cryptor!, Parameter.authTag.rawValue,
tagBytes.baseAddress!, &tagLength_)
}
guard status == noErr else { throw CCError(status) }
tag.count = tagLength_
return (result, tag)
}
public static func available() -> Bool {
if CCCryptorAddParameter != nil &&
CCCryptorGetParameter != nil {
return true
}
return false
}
fileprivate typealias CCParameter = UInt32
fileprivate enum Parameter: CCParameter {
case iv, authData, macSize, dataSize, authTag
}
fileprivate typealias CCCryptorAddParameterT = @convention(c) (_ cryptorRef: CCCryptorRef,
_ parameter: CCParameter,
_ data: UnsafeRawPointer?, _ dataLength: size_t) -> CCCryptorStatus
fileprivate static let CCCryptorAddParameter: CCCryptorAddParameterT? =
getFunc(dl!, f: "CCCryptorAddParameter")
fileprivate typealias CCCryptorGetParameterT = @convention(c) (_ cryptorRef: CCCryptorRef,
_ parameter: CCParameter,
_ data: UnsafeRawPointer, _ dataLength: UnsafeMutablePointer<size_t>) -> CCCryptorStatus
fileprivate static let CCCryptorGetParameter: CCCryptorGetParameterT? =
getFunc(dl!, f: "CCCryptorGetParameter")
}
open class RSA {
public typealias CCAsymmetricPadding = UInt32
public enum AsymmetricPadding: CCAsymmetricPadding {
case pkcs1 = 1001
case oaep = 1002
}
public enum AsymmetricSAPadding: UInt32 {
case pkcs15 = 1001
case pss = 1002
}
public static func generateKeyPair(_ keySize: Int = 4096) throws -> (Data, Data) {
var privateKey: CCRSACryptorRef? = nil
var publicKey: CCRSACryptorRef? = nil
let status = CCRSACryptorGeneratePair!(
keySize,
65537,
&publicKey,
&privateKey)
guard status == noErr else { throw CCError(status) }
defer {
CCRSACryptorRelease!(privateKey!)
CCRSACryptorRelease!(publicKey!)
}
let privDERKey = try exportToDERKey(privateKey!)
let pubDERKey = try exportToDERKey(publicKey!)
return (privDERKey, pubDERKey)
}
public static func getPublicKeyFromPrivateKey(_ derKey: Data) throws -> Data {
let key = try importFromDERKey(derKey)
defer { CCRSACryptorRelease!(key) }
guard getKeyType(key) == .privateKey else { throw CCError(.paramError) }
let publicKey = CCRSACryptorGetPublicKeyFromPrivateKey!(key)
defer { CCRSACryptorRelease!(publicKey) }
let pubDERKey = try exportToDERKey(publicKey)
return pubDERKey
}
public static func encrypt(_ data: Data, derKey: Data, tag: Data,
padding: AsymmetricPadding,
digest: DigestAlgorithm) throws -> Data {
let key = try importFromDERKey(derKey)
defer { CCRSACryptorRelease!(key) }
var bufferSize = getKeySize(key)
var buffer = Data(count: bufferSize)
let status = withUnsafePointers(data, tag, &buffer, {
dataBytes, tagBytes, bufferBytes in
return CCRSACryptorEncrypt!(
key,
padding.rawValue,
dataBytes, data.count,
bufferBytes, &bufferSize,
tagBytes, tag.count,
digest.rawValue)
})
guard status == noErr else { throw CCError(status) }
buffer.count = bufferSize
return buffer
}
public static func decrypt(_ data: Data, derKey: Data, tag: Data,
padding: AsymmetricPadding,
digest: DigestAlgorithm) throws -> (Data, Int) {
let key = try importFromDERKey(derKey)
defer { CCRSACryptorRelease!(key) }
let blockSize = getKeySize(key)
var bufferSize = blockSize
var buffer = Data(count: bufferSize)
let status = withUnsafePointers(data, tag, &buffer, {
dataBytes, tagBytes, bufferBytes in
return CCRSACryptorDecrypt!(
key,
padding.rawValue,
dataBytes, data.count,
bufferBytes, &bufferSize,
tagBytes, tag.count,
digest.rawValue)
})
guard status == noErr else { throw CCError(status) }
buffer.count = bufferSize
return (buffer, blockSize)
}
fileprivate static func importFromDERKey(_ derKey: Data) throws -> CCRSACryptorRef {
var key: CCRSACryptorRef? = nil
let status = derKey.withUnsafeBytes { derKeyBytes -> OSStatus in
CCRSACryptorImport!(
derKeyBytes.baseAddress!,
derKey.count,
&key)
}
guard status == noErr else { throw CCError(status) }
return key!
}
fileprivate static func exportToDERKey(_ key: CCRSACryptorRef) throws -> Data {
var derKeyLength = 8192
var derKey = Data(count: derKeyLength)
let status = derKey.withUnsafeMutableBytes { derKeyBytes -> OSStatus in
CCRSACryptorExport!(key, derKeyBytes.baseAddress!, &derKeyLength)
}
guard status == noErr else { throw CCError(status) }
derKey.count = derKeyLength
return derKey
}
fileprivate static func getKeyType(_ key: CCRSACryptorRef) -> KeyType {
return KeyType(rawValue: CCRSAGetKeyType!(key))!
}
fileprivate static func getKeySize(_ key: CCRSACryptorRef) -> Int {
return Int(CCRSAGetKeySize!(key)/8)
}
public static func sign(_ message: Data, derKey: Data, padding: AsymmetricSAPadding,
digest: DigestAlgorithm, saltLen: Int) throws -> Data {
let key = try importFromDERKey(derKey)
defer { CCRSACryptorRelease!(key) }
guard getKeyType(key) == .privateKey else { throw CCError(.paramError) }
let keySize = getKeySize(key)
switch padding {
case .pkcs15:
let hash = CC.digest(message, alg: digest)
var signedDataLength = keySize
var signedData = Data(count:signedDataLength)
let status = withUnsafePointers(hash, &signedData, {
hashBytes, signedDataBytes in
return CCRSACryptorSign!(
key,
AsymmetricPadding.pkcs1.rawValue,
hashBytes, hash.count,
digest.rawValue, 0 /*unused*/,
signedDataBytes, &signedDataLength)
})
guard status == noErr else { throw CCError(status) }
signedData.count = signedDataLength
return signedData
case .pss:
let encMessage = try add_pss_padding(
digest,
saltLength: saltLen,
keyLength: keySize,
message: message)
return try crypt(encMessage, key: key)
}
}
public static func verify(_ message: Data, derKey: Data, padding: AsymmetricSAPadding,
digest: DigestAlgorithm, saltLen: Int,
signedData: Data) throws -> Bool {
let key = try importFromDERKey(derKey)
defer { CCRSACryptorRelease!(key) }
guard getKeyType(key) == .publicKey else { throw CCError(.paramError) }
let keySize = getKeySize(key)
switch padding {
case .pkcs15:
let hash = CC.digest(message, alg: digest)
let status = withUnsafePointers(hash, signedData, {
hashBytes, signedDataBytes in
return CCRSACryptorVerify!(
key,
padding.rawValue,
hashBytes, hash.count,
digest.rawValue, 0 /*unused*/,
signedDataBytes, signedData.count)
})
let kCCNotVerified: CCCryptorStatus = -4306
if status == kCCNotVerified {
return false
}
guard status == noErr else { throw CCError(status) }
return true
case .pss:
let encoded = try crypt(signedData, key:key)
return try verify_pss_padding(
digest,
saltLength: saltLen,
keyLength: keySize,
message: message,
encMessage: encoded)
}
}
fileprivate static func crypt(_ data: Data, key: CCRSACryptorRef) throws -> Data {
var outLength = data.count
var out = Data(count: outLength)
let status = withUnsafePointers(data, &out, { dataBytes, outBytes in
return CCRSACryptorCrypt!(
key,
dataBytes, data.count,
outBytes, &outLength)
})
guard status == noErr else { throw CCError(status) }
out.count = outLength
return out
}
fileprivate static func mgf1(_ digest: DigestAlgorithm,
seed: Data, maskLength: Int) -> Data {
var tseed = seed
tseed.append(contentsOf: [0,0,0,0] as [UInt8])
var interval = maskLength / digest.length
if maskLength % digest.length != 0 {
interval += 1
}
func pack(_ n: Int) -> [UInt8] {
return [
UInt8(n>>24 & 0xff),
UInt8(n>>16 & 0xff),
UInt8(n>>8 & 0xff),
UInt8(n>>0 & 0xff)
]
}
var mask = Data()
for counter in 0 ..< interval {
tseed.replaceSubrange((tseed.count - 4) ..< tseed.count, with: pack(counter))
mask.append(CC.digest(tseed, alg: digest))
}
mask.count = maskLength
return mask
}
fileprivate static func xorData(_ data1: Data, _ data2: Data) -> Data {
precondition(data1.count == data2.count)
var ret = Data(count: data1.count)
let retcount = ret.count
withUnsafePointers(data1, data2, &ret, {(
b1: UnsafePointer<UInt8>,
b2: UnsafePointer<UInt8>,
r: UnsafeMutablePointer<UInt8>) in
for i in 0 ..< retcount {
r[i] = b1[i] ^ b2[i]
}
})
return ret
}
fileprivate static func add_pss_padding(_ digest: DigestAlgorithm,
saltLength: Int,
keyLength: Int,
message: Data) throws -> Data {
if keyLength < 16 || saltLength < 0 {
throw CCError(.paramError)
}
// The maximal bit size of a non-negative integer is one less than the bit
// size of the key since the first bit is used to store sign
let emBits = keyLength * 8 - 1
var emLength = emBits / 8
if emBits % 8 != 0 {
emLength += 1
}
let hash = CC.digest(message, alg: digest)
if emLength < hash.count + saltLength + 2 {
throw CCError(.paramError)
}
let salt = CC.generateRandom(saltLength)
var mPrime = Data(count: 8)
mPrime.append(hash)
mPrime.append(salt)
let mPrimeHash = CC.digest(mPrime, alg: digest)
let padding = Data(count: emLength - saltLength - hash.count - 2)
var db = padding
db.append([0x01] as [UInt8], count: 1)
db.append(salt)
let dbMask = mgf1(digest, seed: mPrimeHash, maskLength: emLength - hash.count - 1)
var maskedDB = xorData(db, dbMask)
let zeroBits = 8 * emLength - emBits
maskedDB.withUnsafeMutableBytes { maskedDBBytes -> Void in
maskedDBBytes.bindMemory(to: UInt8.self).baseAddress![0] &= 0xff >> UInt8(zeroBits)
}
var ret = maskedDB
ret.append(mPrimeHash)
ret.append([0xBC] as [UInt8], count: 1)
return ret
}
fileprivate static func verify_pss_padding(_ digest: DigestAlgorithm,
saltLength: Int, keyLength: Int,
message: Data,
encMessage: Data) throws -> Bool {
if keyLength < 16 || saltLength < 0 {
throw CCError(.paramError)
}
guard encMessage.count > 0 else {
return false
}
let emBits = keyLength * 8 - 1
var emLength = emBits / 8
if emBits % 8 != 0 {
emLength += 1
}
let hash = CC.digest(message, alg: digest)
if emLength < hash.count + saltLength + 2 {
return false
}
if encMessage.bytesView[encMessage.count-1] != 0xBC {
return false
}
let zeroBits = 8 * emLength - emBits
let zeroBitsM = 8 - zeroBits
let maskedDBLength = emLength - hash.count - 1
let maskedDB = encMessage.subdata(in: 0..<maskedDBLength)
if Int(maskedDB.bytesView[0]) >> zeroBitsM != 0 {
return false
}
let mPrimeHash = encMessage.subdata(in: maskedDBLength ..< maskedDBLength + hash.count)
let dbMask = mgf1(digest, seed: mPrimeHash, maskLength: emLength - hash.count - 1)
var db = xorData(maskedDB, dbMask)
db.withUnsafeMutableBytes { dbBytes -> Void in
dbBytes.bindMemory(to: UInt8.self).baseAddress![0] &= 0xff >> UInt8(zeroBits)
}
let zeroLength = emLength - hash.count - saltLength - 2
let zeroString = Data(count:zeroLength)
if db.subdata(in: 0 ..< zeroLength) != zeroString {
return false
}
if db.bytesView[zeroLength] != 0x01 {
return false
}
let salt = db.subdata(in: (db.count - saltLength) ..< db.count)
var mPrime = Data(count:8)
mPrime.append(hash)
mPrime.append(salt)
let mPrimeHash2 = CC.digest(mPrime, alg: digest)
if mPrimeHash != mPrimeHash2 {
return false
}
return true
}
public static func available() -> Bool {
return CCRSACryptorGeneratePair != nil &&
CCRSACryptorGetPublicKeyFromPrivateKey != nil &&
CCRSACryptorRelease != nil &&
CCRSAGetKeyType != nil &&
CCRSAGetKeySize != nil &&
CCRSACryptorEncrypt != nil &&
CCRSACryptorDecrypt != nil &&
CCRSACryptorExport != nil &&
CCRSACryptorImport != nil &&
CCRSACryptorSign != nil &&
CCRSACryptorVerify != nil &&
CCRSACryptorCrypt != nil
}
fileprivate typealias CCRSACryptorRef = UnsafeRawPointer
fileprivate typealias CCRSAKeyType = UInt32
fileprivate enum KeyType: CCRSAKeyType {
case publicKey = 0, privateKey
case blankPublicKey = 97, blankPrivateKey
case badKey = 99
}
fileprivate typealias CCRSACryptorGeneratePairT = @convention(c) (
_ keySize: Int,
_ e: UInt32,
_ publicKey: UnsafeMutablePointer<CCRSACryptorRef?>,
_ privateKey: UnsafeMutablePointer<CCRSACryptorRef?>) -> CCCryptorStatus
fileprivate static let CCRSACryptorGeneratePair: CCRSACryptorGeneratePairT? =
getFunc(CC.dl!, f: "CCRSACryptorGeneratePair")
fileprivate typealias CCRSACryptorGetPublicKeyFromPrivateKeyT = @convention(c) (CCRSACryptorRef) -> CCRSACryptorRef
fileprivate static let CCRSACryptorGetPublicKeyFromPrivateKey: CCRSACryptorGetPublicKeyFromPrivateKeyT? =
getFunc(CC.dl!, f: "CCRSACryptorGetPublicKeyFromPrivateKey")
fileprivate typealias CCRSACryptorReleaseT = @convention(c) (CCRSACryptorRef) -> Void
fileprivate static let CCRSACryptorRelease: CCRSACryptorReleaseT? =
getFunc(dl!, f: "CCRSACryptorRelease")
fileprivate typealias CCRSAGetKeyTypeT = @convention(c) (CCRSACryptorRef) -> CCRSAKeyType
fileprivate static let CCRSAGetKeyType: CCRSAGetKeyTypeT? = getFunc(dl!, f: "CCRSAGetKeyType")
fileprivate typealias CCRSAGetKeySizeT = @convention(c) (CCRSACryptorRef) -> Int32
fileprivate static let CCRSAGetKeySize: CCRSAGetKeySizeT? = getFunc(dl!, f: "CCRSAGetKeySize")
fileprivate typealias CCRSACryptorEncryptT = @convention(c) (
_ publicKey: CCRSACryptorRef,
_ padding: CCAsymmetricPadding,
_ plainText: UnsafeRawPointer,
_ plainTextLen: Int,
_ cipherText: UnsafeMutableRawPointer,
_ cipherTextLen: UnsafeMutablePointer<Int>,
_ tagData: UnsafeRawPointer,
_ tagDataLen: Int,
_ digestType: CCDigestAlgorithm) -> CCCryptorStatus
fileprivate static let CCRSACryptorEncrypt: CCRSACryptorEncryptT? =
getFunc(dl!, f: "CCRSACryptorEncrypt")
fileprivate typealias CCRSACryptorDecryptT = @convention (c) (
_ privateKey: CCRSACryptorRef,
_ padding: CCAsymmetricPadding,
_ cipherText: UnsafeRawPointer,
_ cipherTextLen: Int,
_ plainText: UnsafeMutableRawPointer,
_ plainTextLen: UnsafeMutablePointer<Int>,
_ tagData: UnsafeRawPointer,
_ tagDataLen: Int,
_ digestType: CCDigestAlgorithm) -> CCCryptorStatus
fileprivate static let CCRSACryptorDecrypt: CCRSACryptorDecryptT? =
getFunc(dl!, f: "CCRSACryptorDecrypt")
fileprivate typealias CCRSACryptorExportT = @convention(c) (
_ key: CCRSACryptorRef,
_ out: UnsafeMutableRawPointer,
_ outLen: UnsafeMutablePointer<Int>) -> CCCryptorStatus
fileprivate static let CCRSACryptorExport: CCRSACryptorExportT? =
getFunc(dl!, f: "CCRSACryptorExport")
fileprivate typealias CCRSACryptorImportT = @convention(c) (
_ keyPackage: UnsafeRawPointer,
_ keyPackageLen: Int,
_ key: UnsafeMutablePointer<CCRSACryptorRef?>) -> CCCryptorStatus
fileprivate static let CCRSACryptorImport: CCRSACryptorImportT? =
getFunc(dl!, f: "CCRSACryptorImport")
fileprivate typealias CCRSACryptorSignT = @convention(c) (
_ privateKey: CCRSACryptorRef,
_ padding: CCAsymmetricPadding,
_ hashToSign: UnsafeRawPointer,
_ hashSignLen: size_t,
_ digestType: CCDigestAlgorithm,
_ saltLen: size_t,
_ signedData: UnsafeMutableRawPointer,
_ signedDataLen: UnsafeMutablePointer<Int>) -> CCCryptorStatus
fileprivate static let CCRSACryptorSign: CCRSACryptorSignT? =
getFunc(dl!, f: "CCRSACryptorSign")
fileprivate typealias CCRSACryptorVerifyT = @convention(c) (
_ publicKey: CCRSACryptorRef,
_ padding: CCAsymmetricPadding,
_ hash: UnsafeRawPointer,
_ hashLen: size_t,
_ digestType: CCDigestAlgorithm,
_ saltLen: size_t,
_ signedData: UnsafeRawPointer,
_ signedDataLen: size_t) -> CCCryptorStatus
fileprivate static let CCRSACryptorVerify: CCRSACryptorVerifyT? =
getFunc(dl!, f: "CCRSACryptorVerify")
fileprivate typealias CCRSACryptorCryptT = @convention(c) (
_ rsaKey: CCRSACryptorRef,
_ data: UnsafeRawPointer, _ dataLength: size_t,
_ out: UnsafeMutableRawPointer,
_ outLength: UnsafeMutablePointer<size_t>) -> CCCryptorStatus
fileprivate static let CCRSACryptorCrypt: CCRSACryptorCryptT? =
getFunc(dl!, f: "CCRSACryptorCrypt")
}
open class DH {
public enum DHParam {
case rfc3526Group5
case rfc2409Group2
}
// this is stateful in CommonCrypto too, sry
open class DH {
fileprivate var ref: CCDHRef? = nil
public init(dhParam: DHParam) throws {
if dhParam == .rfc3526Group5 {
ref = CCDHCreate!(kCCDHRFC3526Group5!)
} else {
ref = CCDHCreate!(kCCDHRFC2409Group2!)
}
guard ref != nil else {
throw CCError(.paramError)
}
}
open func generateKey() throws -> Data {
var outputLength = 8192
var output = Data(count: outputLength)
let status = output.withUnsafeMutableBytes { outputBytes -> OSStatus in
CCDHGenerateKey!(ref!, outputBytes.baseAddress!, &outputLength)
}
output.count = outputLength
guard status != -1 else {
throw CCError(.paramError)
}
return output
}
open func computeKey(_ peerKey: Data) throws -> Data {
var sharedKeyLength = 8192
var sharedKey = Data(count: sharedKeyLength)
let status = withUnsafePointers(peerKey, &sharedKey, {
peerKeyBytes, sharedKeyBytes in
return CCDHComputeKey!(
sharedKeyBytes, &sharedKeyLength,
peerKeyBytes, peerKey.count,
ref!)
})
sharedKey.count = sharedKeyLength
guard status == 0 else {
throw CCError(.paramError)
}
return sharedKey
}
deinit {
if ref != nil {
CCDHRelease!(ref!)
}
}
}
public static func available() -> Bool {
return CCDHCreate != nil &&
CCDHRelease != nil &&
CCDHGenerateKey != nil &&
CCDHComputeKey != nil &&
CCDHParametersCreateFromData != nil &&
CCDHParametersRelease != nil
}
fileprivate typealias CCDHParameters = UnsafeRawPointer
fileprivate typealias CCDHRef = UnsafeRawPointer
fileprivate typealias kCCDHRFC3526Group5TM = UnsafePointer<CCDHParameters>
fileprivate static let kCCDHRFC3526Group5M: kCCDHRFC3526Group5TM? =
getFunc(dl!, f: "kCCDHRFC3526Group5")
fileprivate static let kCCDHRFC3526Group5 = kCCDHRFC3526Group5M?.pointee
fileprivate typealias kCCDHRFC2409Group2TM = UnsafePointer<CCDHParameters>
fileprivate static let kCCDHRFC2409Group2M: kCCDHRFC2409Group2TM? =
getFunc(dl!, f: "kCCDHRFC2409Group2")
fileprivate static let kCCDHRFC2409Group2 = kCCDHRFC2409Group2M?.pointee
fileprivate typealias CCDHCreateT = @convention(c) (
_ dhParameter: CCDHParameters) -> CCDHRef
fileprivate static let CCDHCreate: CCDHCreateT? = getFunc(dl!, f: "CCDHCreate")
fileprivate typealias CCDHReleaseT = @convention(c) (
_ ref: CCDHRef) -> Void
fileprivate static let CCDHRelease: CCDHReleaseT? = getFunc(dl!, f: "CCDHRelease")
fileprivate typealias CCDHGenerateKeyT = @convention(c) (
_ ref: CCDHRef,
_ output: UnsafeMutableRawPointer, _ outputLength: UnsafeMutablePointer<size_t>) -> CInt
fileprivate static let CCDHGenerateKey: CCDHGenerateKeyT? = getFunc(dl!, f: "CCDHGenerateKey")
fileprivate typealias CCDHComputeKeyT = @convention(c) (
_ sharedKey: UnsafeMutableRawPointer, _ sharedKeyLen: UnsafeMutablePointer<size_t>,
_ peerPubKey: UnsafeRawPointer, _ peerPubKeyLen: size_t,
_ ref: CCDHRef) -> CInt
fileprivate static let CCDHComputeKey: CCDHComputeKeyT? = getFunc(dl!, f: "CCDHComputeKey")
fileprivate typealias CCDHParametersCreateFromDataT = @convention(c) (
_ p: UnsafeRawPointer, _ pLen: Int,
_ g: UnsafeRawPointer, _ gLen: Int,
_ l: Int) -> CCDHParameters
fileprivate static let CCDHParametersCreateFromData: CCDHParametersCreateFromDataT? = getFunc(dl!, f: "CCDHParametersCreateFromData")
fileprivate typealias CCDHParametersReleaseT = @convention(c) (
_ parameters: CCDHParameters) -> Void
fileprivate static let CCDHParametersRelease: CCDHParametersReleaseT? = getFunc(dl!, f: "CCDHParametersRelease")
}
open class EC {
public static func generateKeyPair(_ keySize: Int) throws -> (Data, Data) {
var privKey: CCECCryptorRef? = nil
var pubKey: CCECCryptorRef? = nil
let status = CCECCryptorGeneratePair!(
keySize,
&pubKey,
&privKey)
guard status == noErr else { throw CCError(status) }
defer {
CCECCryptorRelease!(privKey!)
CCECCryptorRelease!(pubKey!)
}
let privKeyDER = try exportKey(privKey!, format: .binary, type: .keyPrivate)
let pubKeyDER = try exportKey(pubKey!, format: .binary, type: .keyPublic)
return (privKeyDER, pubKeyDER)
}
public static func getPublicKeyFromPrivateKey(_ privateKey: Data) throws -> Data {
let privKey = try importKey(privateKey, format: .binary, keyType: .keyPrivate)
defer { CCECCryptorRelease!(privKey) }
let pubKey = CCECCryptorGetPublicKeyFromPrivateKey!(privKey)
defer { CCECCryptorRelease!(pubKey) }
let pubKeyDER = try exportKey(pubKey, format: .binary, type: .keyPublic)
return pubKeyDER
}
public static func signHash(_ privateKey: Data, hash: Data) throws -> Data {
let privKey = try importKey(privateKey, format: .binary, keyType: .keyPrivate)
defer { CCECCryptorRelease!(privKey) }
var signedDataLength = 4096
var signedData = Data(count:signedDataLength)
let status = withUnsafePointers(hash, &signedData, {
hashBytes, signedDataBytes in
return CCECCryptorSignHash!(
privKey,
hashBytes, hash.count,
signedDataBytes, &signedDataLength)
})
guard status == noErr else { throw CCError(status) }
signedData.count = signedDataLength
return signedData
}
public static func verifyHash(_ publicKey: Data,
hash: Data,
signedData: Data) throws -> Bool {
let pubKey = try importKey(publicKey, format: .binary, keyType: .keyPublic)
defer { CCECCryptorRelease!(pubKey) }
var valid: UInt32 = 0
let status = withUnsafePointers(hash, signedData, { hashBytes, signedDataBytes in
return CCECCryptorVerifyHash!(
pubKey,
hashBytes, hash.count,
signedDataBytes, signedData.count,
&valid)
})
guard status == noErr else { throw CCError(status) }
return valid != 0
}
public static func computeSharedSecret(_ privateKey: Data,
publicKey: Data) throws -> Data {
let privKey = try importKey(privateKey, format: .binary, keyType: .keyPrivate)
let pubKey = try importKey(publicKey, format: .binary, keyType: .keyPublic)
defer {
CCECCryptorRelease!(privKey)
CCECCryptorRelease!(pubKey)
}
var outSize = 8192
var result = Data(count:outSize)
let status = result.withUnsafeMutableBytes { resultBytes -> OSStatus in
CCECCryptorComputeSharedSecret!(privKey, pubKey, resultBytes.baseAddress!, &outSize)
}
guard status == noErr else { throw CCError(status) }
result.count = outSize
return result
}
public struct KeyComponents {
public init(_ keySize: Int, _ x: Data, _ y: Data, _ d: Data) {
self.keySize = keySize
self.x = x
self.y = y
self.d = d
}
public var keySize: Int
public var x: Data
public var y: Data
public var d: Data
}
public static func getPublicKeyComponents(_ keyData: Data) throws -> KeyComponents {
let key = try importKey(keyData, format: .binary, keyType: .keyPublic)
defer { CCECCryptorRelease!(key) }
return try getKeyComponents(key)
}
public static func getPrivateKeyComponents(_ keyData: Data) throws -> KeyComponents {
let key = try importKey(keyData, format: .binary, keyType: .keyPrivate)
defer { CCECCryptorRelease!(key) }
return try getKeyComponents(key)
}
fileprivate static func getKeyComponents(_ key: CCECCryptorRef) throws -> KeyComponents {
var keySize = 0, xSize = 8192, ySize = 8192, dSize = 8192
var x = Data(count: xSize), y = Data(count: ySize), d = Data(count: dSize)
let status = withUnsafePointers(&x, &y, &d, { xBytes, yBytes, dBytes in
return CCECCryptorGetKeyComponents!(key, &keySize,
xBytes, &xSize,
yBytes, &ySize,
dBytes, &dSize)
})
guard status == noErr else { throw CCError(status) }
x.count = xSize
y.count = ySize
d.count = dSize
if getKeyType(key) == .keyPublic {
d.count = 0
}
return KeyComponents(keySize, x, y, d)
}
public static func createFromData(_ keySize: size_t, _ x: Data, _ y: Data) throws -> Data {
var pubKey: CCECCryptorRef? = nil
let status = withUnsafePointers(x, y, { xBytes, yBytes in
return CCECCryptorCreateFromData!(keySize, xBytes, x.count,
yBytes, y.count, &pubKey)
})
guard status == noErr else { throw CCError(status) }
defer { CCECCryptorRelease!(pubKey!) }
let pubKeyBin = try exportKey(pubKey!, format: .binary, type: .keyPublic)
return pubKeyBin
}
fileprivate static func importKey(_ key: Data, format: KeyExternalFormat,
keyType: KeyType) throws -> CCECCryptorRef {
var impKey: CCECCryptorRef? = nil
let status = key.withUnsafeBytes { keyBytes -> OSStatus in
CCECCryptorImportKey!(format.rawValue,
keyBytes.baseAddress!, key.count,
keyType.rawValue, &impKey)
}
guard status == noErr else { throw CCError(status) }
return impKey!
}
fileprivate static func exportKey(_ key: CCECCryptorRef, format: KeyExternalFormat,
type: KeyType) throws -> Data {
var expKeyLength = 8192
var expKey = Data(count:expKeyLength)
let status = expKey.withUnsafeMutableBytes { expKeyBytes -> OSStatus in
CCECCryptorExportKey!(
format.rawValue,
expKeyBytes.baseAddress!,
&expKeyLength,
type.rawValue,
key)
}
guard status == noErr else { throw CCError(status) }
expKey.count = expKeyLength
return expKey
}
fileprivate static func getKeyType(_ key: CCECCryptorRef) -> KeyType {
return KeyType(rawValue: CCECGetKeyType!(key))!
}
public static func available() -> Bool {
return CCECCryptorGeneratePair != nil &&
CCECCryptorImportKey != nil &&
CCECCryptorExportKey != nil &&
CCECCryptorRelease != nil &&
CCECCryptorSignHash != nil &&
CCECCryptorVerifyHash != nil &&
CCECCryptorComputeSharedSecret != nil &&
CCECCryptorGetKeyComponents != nil &&
CCECCryptorCreateFromData != nil &&
CCECGetKeyType != nil &&
CCECCryptorGetPublicKeyFromPrivateKey != nil
}
fileprivate enum KeyType: CCECKeyType {
case keyPublic = 0, keyPrivate
case blankPublicKey = 97, blankPrivateKey
case badKey = 99
}
fileprivate typealias CCECKeyType = UInt32
fileprivate typealias CCECKeyExternalFormat = UInt32
fileprivate enum KeyExternalFormat: CCECKeyExternalFormat {
case binary = 0, der
}
fileprivate typealias CCECCryptorRef = UnsafeRawPointer
fileprivate typealias CCECCryptorGeneratePairT = @convention(c) (
_ keySize: size_t ,
_ publicKey: UnsafeMutablePointer<CCECCryptorRef?>,
_ privateKey: UnsafeMutablePointer<CCECCryptorRef?>) -> CCCryptorStatus
fileprivate static let CCECCryptorGeneratePair: CCECCryptorGeneratePairT? =
getFunc(dl!, f: "CCECCryptorGeneratePair")
fileprivate typealias CCECCryptorImportKeyT = @convention(c) (
_ format: CCECKeyExternalFormat,
_ keyPackage: UnsafeRawPointer, _ keyPackageLen: size_t,
_ keyType: CCECKeyType, _ key: UnsafeMutablePointer<CCECCryptorRef?>) -> CCCryptorStatus
fileprivate static let CCECCryptorImportKey: CCECCryptorImportKeyT? =
getFunc(dl!, f: "CCECCryptorImportKey")
fileprivate typealias CCECCryptorExportKeyT = @convention(c) (
_ format: CCECKeyExternalFormat,
_ keyPackage: UnsafeRawPointer,
_ keyPackageLen: UnsafePointer<size_t>,
_ keyType: CCECKeyType, _ key: CCECCryptorRef) -> CCCryptorStatus
fileprivate static let CCECCryptorExportKey: CCECCryptorExportKeyT? =
getFunc(dl!, f: "CCECCryptorExportKey")
fileprivate typealias CCECCryptorReleaseT = @convention(c) (
_ key: CCECCryptorRef) -> Void
fileprivate static let CCECCryptorRelease: CCECCryptorReleaseT? =
getFunc(dl!, f: "CCECCryptorRelease")
fileprivate typealias CCECCryptorSignHashT = @convention(c)(
_ privateKey: CCECCryptorRef,
_ hashToSign: UnsafeRawPointer,
_ hashSignLen: size_t,
_ signedData: UnsafeMutableRawPointer,
_ signedDataLen: UnsafeMutablePointer<size_t>) -> CCCryptorStatus
fileprivate static let CCECCryptorSignHash: CCECCryptorSignHashT? =
getFunc(dl!, f: "CCECCryptorSignHash")
fileprivate typealias CCECCryptorVerifyHashT = @convention(c)(
_ publicKey: CCECCryptorRef,
_ hash: UnsafeRawPointer, _ hashLen: size_t,
_ signedData: UnsafeRawPointer, _ signedDataLen: size_t,
_ valid: UnsafeMutablePointer<UInt32>) -> CCCryptorStatus
fileprivate static let CCECCryptorVerifyHash: CCECCryptorVerifyHashT? =
getFunc(dl!, f: "CCECCryptorVerifyHash")
fileprivate typealias CCECCryptorComputeSharedSecretT = @convention(c)(
_ privateKey: CCECCryptorRef,
_ publicKey: CCECCryptorRef,
_ out: UnsafeMutableRawPointer,
_ outLen: UnsafeMutablePointer<size_t>) -> CCCryptorStatus
fileprivate static let CCECCryptorComputeSharedSecret: CCECCryptorComputeSharedSecretT? =
getFunc(dl!, f: "CCECCryptorComputeSharedSecret")
fileprivate typealias CCECCryptorGetKeyComponentsT = @convention(c)(
_ ecKey: CCECCryptorRef,
_ keySize: UnsafeMutablePointer<Int>,
_ qX: UnsafeMutableRawPointer,
_ qXLength: UnsafeMutablePointer<Int>,
_ qY: UnsafeMutableRawPointer,
_ qYLength: UnsafeMutablePointer<Int>,
_ d: UnsafeMutableRawPointer?,
_ dLength: UnsafeMutablePointer<Int>) -> CCCryptorStatus
fileprivate static let CCECCryptorGetKeyComponents: CCECCryptorGetKeyComponentsT? =
getFunc(dl!, f: "CCECCryptorGetKeyComponents")
fileprivate typealias CCECCryptorCreateFromDataT = @convention(c)(
_ keySize: size_t,
_ qX: UnsafeRawPointer,
_ qXLength: size_t,
_ qY: UnsafeRawPointer,
_ qYLength: size_t,
_ publicKey: UnsafeMutablePointer<CCECCryptorRef?>) -> CCCryptorStatus
fileprivate static let CCECCryptorCreateFromData: CCECCryptorCreateFromDataT? =
getFunc(dl!, f: "CCECCryptorCreateFromData")
fileprivate typealias CCECGetKeyTypeT = @convention(c) (
_ key: CCECCryptorRef) -> CCECKeyType
fileprivate static let CCECGetKeyType: CCECGetKeyTypeT? =
getFunc(dl!, f: "CCECGetKeyType")
fileprivate typealias CCECCryptorGetPublicKeyFromPrivateKeyT = @convention(c) (
_ key: CCECCryptorRef) -> CCECCryptorRef
fileprivate static let CCECCryptorGetPublicKeyFromPrivateKey: CCECCryptorGetPublicKeyFromPrivateKeyT? =
getFunc(dl!, f: "CCECCryptorGetPublicKeyFromPrivateKey")
}
open class CRC {
public typealias CNcrc = UInt32
public enum Mode: CNcrc {
case crc8 = 10,
crc8ICODE = 11,
crc8ITU = 12,
crc8ROHC = 13,
crc8WCDMA = 14,
crc16 = 20,
crc16CCITTTrue = 21,
crc16CCITTFalse = 22,
crc16USB = 23,
crc16XMODEM = 24,
crc16DECTR = 25,
crc16DECTX = 26,
crc16ICODE = 27,
crc16VERIFONE = 28,
crc16A = 29,
crc16B = 30,
crc16Fletcher = 31,
crc32Adler = 40,
crc32 = 41,
crc32CASTAGNOLI = 42,
crc32BZIP2 = 43,
crc32MPEG2 = 44,
crc32POSIX = 45,
crc32XFER = 46,
crc64ECMA182 = 60
}
public static func crc(_ input: Data, mode: Mode) throws -> UInt64 {
var result: UInt64 = 0
let status = input.withUnsafeBytes { inputBytes -> OSStatus in
CNCRC!(
mode.rawValue,
inputBytes.baseAddress!, input.count,
&result)
}
guard status == noErr else {
throw CCError(status)
}
return result
}
public static func available() -> Bool {
return CNCRC != nil
}
fileprivate typealias CNCRCT = @convention(c) (
_ algorithm: CNcrc,
_ input: UnsafeRawPointer, _ inputLen: size_t,
_ result: UnsafeMutablePointer<UInt64>) -> CCCryptorStatus
fileprivate static let CNCRC: CNCRCT? = getFunc(dl!, f: "CNCRC")
}
open class CMAC {
public static func AESCMAC(_ data: Data, key: Data) -> Data {
var result = Data(count: 16)
withUnsafePointers(key, data, &result, { keyBytes, dataBytes, resultBytes in
CCAESCmac!(keyBytes,
dataBytes, data.count,
resultBytes)
})
return result
}
public static func available() -> Bool {
return CCAESCmac != nil
}
fileprivate typealias CCAESCmacT = @convention(c) (
_ key: UnsafeRawPointer,
_ data: UnsafeRawPointer, _ dataLen: size_t,
_ macOut: UnsafeMutableRawPointer) -> Void
fileprivate static let CCAESCmac: CCAESCmacT? = getFunc(dl!, f: "CCAESCmac")
}
open class KeyDerivation {
public typealias CCPseudoRandomAlgorithm = UInt32
public enum PRFAlg: CCPseudoRandomAlgorithm {
case sha1 = 1, sha224, sha256, sha384, sha512
var cc: CC.HMACAlg {
switch self {
case .sha1: return .sha1
case .sha224: return .sha224
case .sha256: return .sha256
case .sha384: return .sha384
case .sha512: return .sha512
}
}
}
public static func PBKDF2(_ password: String, salt: Data,
prf: PRFAlg, rounds: UInt32) throws -> Data {
var result = Data(count:prf.cc.digestLength)
let rescount = result.count
let passwData = password.data(using: String.Encoding.utf8)!
let status = withUnsafePointers(passwData, salt, &result, {
passwDataBytes, saltBytes, resultBytes in
return CCKeyDerivationPBKDF!(PBKDFAlgorithm.pbkdf2.rawValue,
passwDataBytes, passwData.count,
saltBytes, salt.count,
prf.rawValue, rounds,
resultBytes, rescount)
})
guard status == noErr else { throw CCError(status) }
return result
}
public static func available() -> Bool {
return CCKeyDerivationPBKDF != nil
}
fileprivate typealias CCPBKDFAlgorithm = UInt32
fileprivate enum PBKDFAlgorithm: CCPBKDFAlgorithm {
case pbkdf2 = 2
}
fileprivate typealias CCKeyDerivationPBKDFT = @convention(c) (
_ algorithm: CCPBKDFAlgorithm,
_ password: UnsafeRawPointer, _ passwordLen: size_t,
_ salt: UnsafeRawPointer, _ saltLen: size_t,
_ prf: CCPseudoRandomAlgorithm, _ rounds: uint,
_ derivedKey: UnsafeMutableRawPointer, _ derivedKeyLen: size_t) -> CCCryptorStatus
fileprivate static let CCKeyDerivationPBKDF: CCKeyDerivationPBKDFT? =
getFunc(dl!, f: "CCKeyDerivationPBKDF")
}
open class KeyWrap {
fileprivate static let rfc3394IVData: [UInt8] = [0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6]
public static let rfc3394IV = Data(bytes: UnsafePointer<UInt8>(rfc3394IVData), count:rfc3394IVData.count)
public static func SymmetricKeyWrap(_ iv: Data,
kek: Data,
rawKey: Data) throws -> Data {
let alg = WrapAlg.aes.rawValue
var wrappedKeyLength = CCSymmetricWrappedSize!(alg, rawKey.count)
var wrappedKey = Data(count:wrappedKeyLength)
let status = withUnsafePointers(iv, kek, rawKey, &wrappedKey, {
ivBytes, kekBytes, rawKeyBytes, wrappedKeyBytes in
return CCSymmetricKeyWrap!(
alg,
ivBytes, iv.count,
kekBytes, kek.count,
rawKeyBytes, rawKey.count,
wrappedKeyBytes, &wrappedKeyLength)
})
guard status == noErr else { throw CCError(status) }
wrappedKey.count = wrappedKeyLength
return wrappedKey
}
public static func SymmetricKeyUnwrap(_ iv: Data,
kek: Data,
wrappedKey: Data) throws -> Data {
let alg = WrapAlg.aes.rawValue
var rawKeyLength = CCSymmetricUnwrappedSize!(alg, wrappedKey.count)
var rawKey = Data(count:rawKeyLength)
let status = withUnsafePointers(iv, kek, wrappedKey, &rawKey, {
ivBytes, kekBytes, wrappedKeyBytes, rawKeyBytes in
return CCSymmetricKeyUnwrap!(
alg,
ivBytes, iv.count,
kekBytes, kek.count,
wrappedKeyBytes, wrappedKey.count,
rawKeyBytes, &rawKeyLength)
})
guard status == noErr else { throw CCError(status) }
rawKey.count = rawKeyLength
return rawKey
}
public static func available() -> Bool {
return CCSymmetricKeyWrap != nil &&
CCSymmetricKeyUnwrap != nil &&
CCSymmetricWrappedSize != nil &&
CCSymmetricUnwrappedSize != nil
}
fileprivate enum WrapAlg: CCWrappingAlgorithm {
case aes = 1
}
fileprivate typealias CCWrappingAlgorithm = UInt32
fileprivate typealias CCSymmetricKeyWrapT = @convention(c) (
_ algorithm: CCWrappingAlgorithm,
_ iv: UnsafeRawPointer, _ ivLen: size_t,
_ kek: UnsafeRawPointer, _ kekLen: size_t,
_ rawKey: UnsafeRawPointer, _ rawKeyLen: size_t,
_ wrappedKey: UnsafeMutableRawPointer,
_ wrappedKeyLen: UnsafePointer<size_t>) -> CCCryptorStatus
fileprivate static let CCSymmetricKeyWrap: CCSymmetricKeyWrapT? = getFunc(dl!, f: "CCSymmetricKeyWrap")
fileprivate typealias CCSymmetricKeyUnwrapT = @convention(c) (
_ algorithm: CCWrappingAlgorithm,
_ iv: UnsafeRawPointer, _ ivLen: size_t,
_ kek: UnsafeRawPointer, _ kekLen: size_t,
_ wrappedKey: UnsafeRawPointer, _ wrappedKeyLen: size_t,
_ rawKey: UnsafeMutableRawPointer,
_ rawKeyLen: UnsafePointer<size_t>) -> CCCryptorStatus
fileprivate static let CCSymmetricKeyUnwrap: CCSymmetricKeyUnwrapT? =
getFunc(dl!, f: "CCSymmetricKeyUnwrap")
fileprivate typealias CCSymmetricWrappedSizeT = @convention(c) (
_ algorithm: CCWrappingAlgorithm,
_ rawKeyLen: size_t) -> size_t
fileprivate static let CCSymmetricWrappedSize: CCSymmetricWrappedSizeT? =
getFunc(dl!, f: "CCSymmetricWrappedSize")
fileprivate typealias CCSymmetricUnwrappedSizeT = @convention(c) (
_ algorithm: CCWrappingAlgorithm,
_ wrappedKeyLen: size_t) -> size_t
fileprivate static let CCSymmetricUnwrappedSize: CCSymmetricUnwrappedSizeT? =
getFunc(dl!, f: "CCSymmetricUnwrappedSize")
}
}
private func getFunc<T>(_ from: UnsafeMutableRawPointer, f: String) -> T? {
let sym = dlsym(from, f)
guard sym != nil else {
return nil
}
return unsafeBitCast(sym, to: T.self)
}
extension Data {
/// Create hexadecimal string representation of Data object.
///
/// - returns: String representation of this Data object.
public func hexadecimalString() -> String {
return self.withUnsafeBytes { data -> String in
var hexstr = String()
for i in data.bindMemory(to: UInt8.self) {
hexstr += String(format: "%02X", i)
}
return hexstr
}
}
public func arrayOfBytes() -> [UInt8] {
let count = self.count / MemoryLayout<UInt8>.size
var bytesArray = [UInt8](repeating: 0, count: count)
self.copyBytes(to: &bytesArray, count: count * MemoryLayout<UInt8>.size)
return bytesArray
}
fileprivate var bytesView: BytesView { return BytesView(self) }
fileprivate func bytesViewRange(_ range: NSRange) -> BytesView {
return BytesView(self, range: range)
}
fileprivate struct BytesView: Collection {
// The view retains the Data. That's on purpose.
// Data doesn't retain the view, so there's no loop.
let data: Data
init(_ data: Data) {
self.data = data
self.startIndex = 0
self.endIndex = data.count
}
init(_ data: Data, range: NSRange ) {
self.data = data
self.startIndex = range.location
self.endIndex = range.location + range.length
}
subscript (position: Int) -> UInt8 {
return data.withUnsafeBytes({ dataBytes -> UInt8 in
dataBytes.bindMemory(to: UInt8.self)[position]
})
}
subscript (bounds: Range<Int>) -> Data {
return data.subdata(in: bounds)
}
fileprivate func formIndex(after i: inout Int) {
i += 1
}
fileprivate func index(after i: Int) -> Int {
return i + 1
}
var startIndex: Int
var endIndex: Int
var length: Int { return endIndex - startIndex }
}
}
extension String {
/// Create Data from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a Data object. Note, if the string has
/// any spaces, those are removed. Also if the string started with a '<' or ended with a '>',
/// those are removed, too. This does no validation of the string to ensure it's a valid
/// hexadecimal string
///
/// The use of `strtoul` inspired by Martin R at http://stackoverflow.com/a/26284562/1271826
///
/// - returns: Data represented by this hexadecimal string.
/// Returns nil if string contains characters outside the 0-9 and a-f range.
public func dataFromHexadecimalString() -> Data? {
let trimmedString = self.trimmingCharacters(
in: CharacterSet(charactersIn: "<> ")).replacingOccurrences(
of: " ", with: "")
// make sure the cleaned up string consists solely of hex digits,
// and that we have even number of them
let regex = try! NSRegularExpression(pattern: "^[0-9a-f]*$", options: .caseInsensitive)
let found = regex.firstMatch(in: trimmedString, options: [],
range: NSRange(location: 0,
length: trimmedString.count))
guard found != nil &&
found?.range.location != NSNotFound &&
trimmedString.count % 2 == 0 else {
return nil
}
// everything ok, so now let's build Data
var data = Data(capacity: trimmedString.count / 2)
var index: String.Index? = trimmedString.startIndex
while let i = index {
let byteString = String(trimmedString[i ..< trimmedString.index(i, offsetBy: 2)])
let num = UInt8(byteString.withCString { strtoul($0, nil, 16) })
data.append([num] as [UInt8], count: 1)
index = trimmedString.index(i, offsetBy: 2, limitedBy: trimmedString.endIndex)
if index == trimmedString.endIndex { break }
}
return data
}
}
fileprivate func withUnsafePointers<A0, A1, Result>(
_ arg0: Data,
_ arg1: Data,
_ body: (
UnsafePointer<A0>, UnsafePointer<A1>) throws -> Result
) rethrows -> Result {
return try arg0.withUnsafeBytes { p0 -> Result in
return try arg1.withUnsafeBytes { p1 -> Result in
return try body(p0.bindMemory(to: A0.self).baseAddress!,
p1.bindMemory(to: A1.self).baseAddress!)
}
}
}
fileprivate func withUnsafePointers<A0, A1, Result>(
_ arg0: Data,
_ arg1: inout Data,
_ body: (
UnsafePointer<A0>,
UnsafeMutablePointer<A1>) throws -> Result
) rethrows -> Result {
return try arg0.withUnsafeBytes { p0 -> Result in
return try arg1.withUnsafeMutableBytes { p1 -> Result in
return try body(p0.bindMemory(to: A0.self).baseAddress!,
p1.bindMemory(to: A1.self).baseAddress!)
}
}
}
fileprivate func withUnsafePointers<A0, A1, A2, Result>(
_ arg0: Data,
_ arg1: Data,
_ arg2: inout Data,
_ body: (
UnsafePointer<A0>,
UnsafePointer<A1>,
UnsafeMutablePointer<A2>) throws -> Result
) rethrows -> Result {
return try arg0.withUnsafeBytes { p0 -> Result in
return try arg1.withUnsafeBytes { p1 -> Result in
return try arg2.withUnsafeMutableBytes { p2 -> Result in
return try body(p0.bindMemory(to: A0.self).baseAddress!,
p1.bindMemory(to: A1.self).baseAddress!,
p2.bindMemory(to: A2.self).baseAddress!)
}
}
}
}
fileprivate func withUnsafePointers<A0, A1, A2, Result>(
_ arg0: inout Data,
_ arg1: inout Data,
_ arg2: inout Data,
_ body: (
UnsafeMutablePointer<A0>,
UnsafeMutablePointer<A1>,
UnsafeMutablePointer<A2>) throws -> Result
) rethrows -> Result {
return try arg0.withUnsafeMutableBytes { p0 -> Result in
return try arg1.withUnsafeMutableBytes { p1 -> Result in
return try arg2.withUnsafeMutableBytes { p2 -> Result in
return try body(p0.bindMemory(to: A0.self).baseAddress!,
p1.bindMemory(to: A1.self).baseAddress!,
p2.bindMemory(to: A2.self).baseAddress!)
}
}
}
}
fileprivate func withUnsafePointers<A0, A1, A2, A3, Result>(
_ arg0: Data,
_ arg1: Data,
_ arg2: Data,
_ arg3: inout Data,
_ body: (
UnsafePointer<A0>,
UnsafePointer<A1>,
UnsafePointer<A2>,
UnsafeMutablePointer<A3>) throws -> Result
) rethrows -> Result {
return try arg0.withUnsafeBytes { p0 -> Result in
return try arg1.withUnsafeBytes { p1 -> Result in
return try arg2.withUnsafeBytes { p2 -> Result in
return try arg3.withUnsafeMutableBytes { p3 -> Result in
return try body(p0.bindMemory(to: A0.self).baseAddress!,
p1.bindMemory(to: A1.self).baseAddress!,
p2.bindMemory(to: A2.self).baseAddress!,
p3.bindMemory(to: A3.self).baseAddress!)
}
}
}
}
}
fileprivate func withUnsafePointers<A0, A1, A2, A3, A4, A5, Result>(
_ arg0: Data,
_ arg1: Data,
_ arg2: Data,
_ arg3: Data,
_ arg4: inout Data,
_ arg5: inout Data,
_ body: (
UnsafePointer<A0>,
UnsafePointer<A1>,
UnsafePointer<A2>,
UnsafePointer<A3>,
UnsafeMutablePointer<A4>,
UnsafeMutablePointer<A5>) throws -> Result
) rethrows -> Result {
return try arg0.withUnsafeBytes { p0 -> Result in
return try arg1.withUnsafeBytes { p1 -> Result in
return try arg2.withUnsafeBytes { p2 -> Result in
return try arg3.withUnsafeBytes { p3 -> Result in
return try arg4.withUnsafeMutableBytes { p4 -> Result in
return try arg5.withUnsafeMutableBytes { p5 -> Result in
return try body(p0.bindMemory(to: A0.self).baseAddress!,
p1.bindMemory(to: A1.self).baseAddress!,
p2.bindMemory(to: A2.self).baseAddress!,
p3.bindMemory(to: A3.self).baseAddress!,
p4.bindMemory(to: A4.self).baseAddress!,
p5.bindMemory(to: A5.self).baseAddress!)
}
}
}
}
}
}
}