/**
Gets application (alice) PublicKey for exchange
- returns: the public key that need to be sent to Bob
*/
public func getApplicationPublicKeyForExchange() throws -> SecKey? {
if #available(iOS 10.0, *) {
// generate a key for application
let attributes: [String: Any] =
[kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]
]
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
self.appPrivateKey = privateKey
self.appPublicKey = SecKeyCopyPublicKey(privateKey)
return self.appPublicKey
} else {
return nil
}
}
/**
Converts a base64 string into a SecKey
- parameter b64Key: String encoded base64
- returns: SecKey
*/
func secKey(from b64Key: String) throws -> SecKey? {
if #available(iOS 10.0, *) {
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
]
guard let data = Data(base64Encoded: b64Key),
let key = SecKeyCreateWithData(data as CFData,
attributes as CFDictionary,
&error) else {
throw error!.takeRetainedValue() as Error
}
return key
}
return nil
}
/**
Generates the shared secret using Bob's public key and the app private key
- parameter publicKey: SecKey the publickKey received from Bob
- parameter sharedSecret: The secret received from Bob. This was created by using the Alice's public key
*/
public func getSharedSecret(for publicKey: SecKey?) throws -> CFData? {
if #available(iOS 10.0, *) {
guard let publicKey = publicKey, let privateKey = self.appPrivateKey else {
return nil
}
let attributes: [String: Any] =
[kSecAttrKeySizeInBits as String: 256,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false]
]
guard let sharedSecret = SecKeyCopyKeyExchangeResult(privateKey,
SecKeyAlgorithm.ecdhKeyExchangeStandardX963SHA256,
publicKey,
attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
return sharedSecret
} else {
return nil
}
}
// MARK: - AES256
enum AESError: Swift.Error {
case keyGeneration(status: Int)
case cryptoFailed(status: CCCryptorStatus)
case badKeyLength
case badInputVectorLength
}
func encrypt(_ digest: Data, key: Data) throws -> Data {
return try crypt(input: digest, key: key, operation: CCOperation(kCCEncrypt))
}
func decrypt(_ encrypted: Data, key: Data) throws -> Data {
return try crypt(input: encrypted, key: key, operation: CCOperation(kCCDecrypt))
}
private func crypt(input: Data, key: Data, operation: CCOperation) throws -> Data {
// should be the same???
var outLength = Int(0)
var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSizeAES128)
var status: CCCryptorStatus = CCCryptorStatus(kCCSuccess)
input.withUnsafeBytes { (encryptedBytes: UnsafePointer<UInt8>!) -> () in
// iv.withUnsafeBytes { (ivBytes: UnsafePointer<UInt8>!) in
key.withUnsafeBytes { (keyBytes: UnsafePointer<UInt8>!) -> () in
status = CCCrypt(operation,
CCAlgorithm(kCCAlgorithmAES), // algorithm
CCOptions(kCCOptionPKCS7Padding), // options
keyBytes, // key
key.count, // keylength
nil, // iv
encryptedBytes, // dataIn
input.count, // dataInLength
&outBytes, // dataOut
outBytes.count, // dataOutAvailable
&outLength) // dataOutMoved
}
// }
}
guard status == kCCSuccess else {
throw AESError.cryptoFailed(status: status)
}
return Data(bytes: UnsafePointer<UInt8>(outBytes), count: outLength)
}
extension String {
func fromBase64() -> String? {
guard let data = Data(base64Encoded: self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
}