In the "Cryptography for Mobile Apps" chapter, we introduced general cryptography best practices and described typical problems that may occur when cryptography is used incorrectly. In this chapter, we'll detail the cryptography APIs available for iOS. We'll show how to identify usage of those APIs in the source code and how to interpret cryptographic configurations. When you're reviewing code, compare the cryptographic parameters with the current best practices linked in this guide.
Apple provides libraries that include implementations of most common cryptographic algorithms. Apple's Cryptographic Services Guide is a great reference. It contains generalized documentation of how to use standard libraries to initialize and use cryptographic primitives, information that is useful for source code analysis.
The most commonly used Class for cryptographic operations is the CommonCrypto, which is packed with the iOS runtime. The functionality offered by the CommonCrypto object can best be dissected by having a look at the source code of the header file :
- The
Commoncryptor.h
gives the parameters for the symmetric cryptographic operations, - The
CommonDigest.h
gives the parameters for the hashing Algorithms - The
CommonHMAC.h
gives the parameters for the supported HMAC operations. - The
CommonKeyDerivation.h
gives the parameters for supported KDF functions - The
CommonSymmetricKeywrap.h
gives the function used for wrappnig a symmetric key with a Key Encryption Key.
Unfortunately, CommonCryptor lacks a few types of operations in its public APIs, such as: GCM mode is only available in its private APIs See its sourcecode. For this, an additional binding header is necessary or other wrapper libraries can be used.
Next, for asymmetric operations, Apple provides SecKey. Apple provides a nice guide in its Developer Documentation on how to use this.
As noted before: some wrapper-libraries exist for both in order to provide convenience. Typical libraries that are used are, for instance:
There are various third party libraries available, such as:
- CJOSE: With the rise of JWE, and the lack of public support for AES GCM, other libraries have found their way, such as CJOSE. CJOSE still requires a higher level wrapping as they only provide a C/C++ implementation.
- CryptoSwift: A library in Swift, which can be found at Github. The library supports various hash-functions, MAC-functions, CRC-functions, symmetric ciphers, and password-based key derivation functions. It is not a wrapper, but a fully self-implemented version of each of the ciphers. It is important to verify the effective implementation of a function.
- OpenSSL: OpenSSL is the toolkit library used for TLS, written in . Most of its cryptographic functions can be used to do the various cryptographic actions necessary, such as creating (H)MACs, signatures, symmetric- & assymmetric ciphers, hashing, etc.. There are various wrappers, such as OpenSSL and MIHCrypto.
- LibSodium: Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. It is a portable, cross-compilable, installable, packageable fork of NaCl, with a compatible API, and an extended API to improve usability even further. See LibSodiums documentation for more details. There are some wrapper libraries, such as Swift-sodium, NAChloride, and libsodium-ios.
- Tink: A new cryptography library by Google. Google explains its reasoning behind the library in its security blog. The sources can be found at Tinks Github repository.
- Themis: a Crypto library for storage and messaging for Swift, Obj-C, Android/Java, С++, JS, Python, Ruby, PHP, Go. Themis uses LibreSSL/OpenSSL engine libcrypto as a dependency. It supports Objective-C and Swift for key generation, secure messaging (e.g. payload encryption and signing), secure storage and setting up a secure session. See their wiki for more details.
- Others: There are many other libraries, such as CocoaSecurity, Objective-C-RSA, and aerogear-ios-crypto. Some of these are no longer maintained and might never have been security reviewed. Like always, it is recommended to look for supported and maintained libraries.
- DIY: An increasing amount of developers have created their own implementation of a cipher or a cryptographic function. This practice is highly discouraged and should be vetted very thoroughly by a cryptography expert if used.
A lot has been said about deprecated algorithms and cryptographic configurations in section Cryptography for Mobile Apps
. Obviously, these should be verified for each of the mentioned libraries in this chapter.
Pay attention to how-to-be-removed key-holding datastructures and plain-text data structures are defined. If the keyword let
is used, then you create an immutable structure which is harder to wipe from memory. Make sure that it is part of a parent structure which can be easily removed from memory (e.g. a struct
that lives temporally).
#####CommonCryptor
If the app uses standard cryptographic implementations provided by Apple, the easiest way to determine the status of the related algorithm is to check for calls to functions from CommonCryptor
, such as CCCrypt
and CCCryptorCreate
. The source code contains the signatures of all functions of CommonCryptor.h. For instance, CCCryptorCreate
has following signature:
CCCryptorStatus CCCryptorCreate(
CCOperation op, /* kCCEncrypt, etc. */
CCAlgorithm alg, /* kCCAlgorithmDES, etc. */
CCOptions options, /* kCCOptionPKCS7Padding, etc. */
const void *key, /* raw key material */
size_t keyLength,
const void *iv, /* optional initialization vector */
CCCryptorRef *cryptorRef); /* RETURNED */
You can then compare all the enum
types to determine which algorithm, padding, and key material is used. Pay attention to the keying material: the key should be generated securely - either using a key derivation function or a random-number generation function.
Note that functions which are noted in chapter "Cryptography for Mobile Apps" as deprecated, are still programmatically supported. They should not be used.
Given the continuous evolution of all third party libraries, this should not be the place to evaluate each library in terms of static analysis. Still there are some points of attention:
- Find the library being used: This can be done using the following methods:
- Check the cartfile if Carthage is used.
- Check the podfile if Cocoapods is used.
- Check the linked libraries: Open the xcodeproj file and check the project properties. Go to the tab "Build Phases" and check the entries in "Link Binary With Libraries" for any of the libraries. See earlier sections on how to obtain similar information using MobSF.
- In the case of copy-pasted sources: search the headerfiles (in case of using Objective-C) and otherwise the Swift files for known methodnames for known libraries.
- Check the cartfile if Carthage is used.
- Determine the version being used: Always check the version of the library being used and check whether there is a new version available in which possible vulnerabilities or shortcomings are patched. Even without a newer version of a library, it can be the case that cryptographic functions have not been reviewed yet. Therefore we always recommend using a library that has been validated or ensure that you have the ability, knowledge and experience to do validation yourself.
- By hand?: We recommend not to roll your own crypto, nor to implement known cryptographic functions yourself.
Apple provides a Randomization Services API, which generates cryptographically secure random numbers.
The Randomization Services API uses the SecRandomCopyBytes
function to generate numbers. This is a wrapper function for the /dev/random
device file, which provides cryptographically secure pseudorandom values from 0 to 255. Make sure that all random numbers are generated with this API. There is no reason for developers to use a different one.
In Swift, the SecRandomCopyBytes
API is defined as follows:
func SecRandomCopyBytes(_ rnd: SecRandomRef?,
_ count: Int,
_ bytes: UnsafeMutablePointer<UInt8>) -> Int32
The Objective-C version is
int SecRandomCopyBytes(SecRandomRef rnd, size_t count, uint8_t *bytes);
The following is an example of the APIs usage:
int result = SecRandomCopyBytes(kSecRandomDefault, 16, randomBytes);
Note: if other mechanims are used for random numbers in the code: verify that these are either wrappers around the APIs mentioned above or review them for their secure-randomness. Often this is too hard, which means you can best stick with the implementation above.
If you want to test for randomness, you can try to capture a large set of numbers and check with the Burpsuite's sequencer to see how good the quality of the randomness is.
There are various methods on how to store the key on the device. Not storing a key at all will ensure that no keymaterial can be dumped. This can be archieved by using a Password Key Derivation function, such as PKBDF-2. See the example below:
func pbkdf2SHA1(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA1), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
}
func pbkdf2SHA256(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA256), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
}
func pbkdf2SHA512(password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
return pbkdf2(hash:CCPBKDFAlgorithm(kCCPRFHmacAlgSHA512), password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
}
func pbkdf2(hash :CCPBKDFAlgorithm, password: String, salt: Data, keyByteCount: Int, rounds: Int) -> Data? {
let passwordData = password.data(using:String.Encoding.utf8)!
var derivedKeyData = Data(repeating:0, count:keyByteCount)
let derivedKeyDataLength = derivedKeyData.count
let derivationStatus = derivedKeyData.withUnsafeMutableBytes {derivedKeyBytes in
salt.withUnsafeBytes { saltBytes in
CCKeyDerivationPBKDF(
CCPBKDFAlgorithm(kCCPBKDF2),
password, passwordData.count,
saltBytes, salt.count,
hash,
UInt32(rounds),
derivedKeyBytes, derivedKeyDataLength)
}
}
if (derivationStatus != 0) {
print("Error: \(derivationStatus)")
return nil;
}
return derivedKeyData
}
func testKeyDerivation(){
//test run in the 'Arcane' librarie its testingsuite to show how you can use it
let password = "password"
//let salt = "saltData".data(using: String.Encoding.utf8)!
let salt = Data(bytes: [0x73, 0x61, 0x6c, 0x74, 0x44, 0x61, 0x74, 0x61])
let keyByteCount = 16
let rounds = 100000
let derivedKey = pbkdf2SHA1(password:password, salt:salt, keyByteCount:keyByteCount, rounds:rounds)
print("derivedKey (SHA1): \(derivedKey! as NSData)")
}
Source: https://stackoverflow.com/questions/8569555/pbkdf2-using-commoncrypto-on-ios, tested in the testsuite of the Arcane
library
When you need to store the key, it is recommended to use the Keychain as long as the protection class chosen is not kSecAttrAccessibleAlways
. Storing keys in any other location, such as the NSUserDefaults
, Propertylists or by any other sink from Coredata or Realm, is usually less secure than using the KeyChain.
Even when the sync of CoreData or Realm is protected by using NSFileProtectionComplete
data protection class, we still recommend using the KeyChain. See the Testing Data Storage section for more details.
The KeyChain supports two type of storage mechanisms: a key is either secured by an encryption key stored in the secure-enclave or the key itself is within the secure enclave. The latter only holds when you use an ECDH singing key. See the Apple Documentation for more details on its implementation.
The last three options are to use hardcoded encryption keys in the source code, having a predictable key derivation function based on stable attributes, and storing generated keys in places that are shared with other applications. Obviously, hardcoded encryption keys are not the way to go. This means every instance of the application uses the same encryption key. An attacker needs only to do the work once, to extract the key from the source code - whether stored natively or in objective-C/Swift. Consequently, he can decrypt any other data that he can obtain which was encrypted by the application. Next, when you have a predictable key derivation function based on identifiers which are accessible to other applications, the attacker only needs to find the KDF and apply it to the device in order to find the key. Lastly, storing encryption keys publicly also is highly discouraged.
There are various keywords to look for: check the libraries mentioned in the overview and static analysis of the section "Verifying the Configuration of Cryptographic Standard Algorithms" for which keywords you can best check on how keys are stored. Always make sure that:
- keys are not syncrhonized over devices if it is used to protect high-risk data.
- keys are not stored without additional protection.
- keys are not hardcoded.
- keys are not derived from stable features of the device.
- keys are not hidden by use of lower level languages (e.g. C/C++).
- keys are not imported from unsafe locations.
Most of the recommendations for static analysis can already be found in chapter "Testing Data Storage for iOS". Next, you can read up on it at the following pages:
- Apple Developer Documentation: Certificates and keys
- Apple Developer Documentation: Generating new keys
- Apple Developer Documentation: Key generation attributes
Hook cryptographic methods and analyze the keys that are being used. Monitor file system access while cryptographic operations are being performed to assess where key material is written to or read from.
- Apple's Cryptographic Services Guide
- Apple Developer Documentation on randomization SecKey
- Apple Documentation on Secure Enclave
- source code of the header file
- GCM in CommonCrypto
- Apple Developer Documentation on SecKey
- IDZSwiftCommonCrypto,
- Heimdall,
- SwiftyRSA,
- SwiftSSL,
- RNCryptor,
- Arcane
- CJOSE
- CryptoSwift
- OpenSSL
- LibSodiums documentation
- Google on Tink
- Themis
- cartfile
- podfile
- Apple Developer Documentation on randomization
- Apple Developer Documentation on secrandomcopybytes
- BurpSuite's sequencer
- Apple Developer Documentation: Certificates and keys
- Apple Developer Documentation: Generating new keys
- Apple Developer Documentation: Key generation attributes
- M5 - Insufficient Cryptography - https://www.owasp.org/index.php/Mobile_Top_10_2016-M5-Insufficient_Cryptography
- V3.1: "The app does not rely on symmetric cryptography with hardcoded keys as a sole method of encryption."
- V3.3: "The app uses cryptographic primitives that are appropriate for the particular use case, configured with parameters that adhere to industry best practices."
- V3.4: "The app does not use cryptographic protocols or algorithms that are widely considered depreciated for security purposes."
- V3.6: "All random values are generated using a sufficiently secure random number generator."
- CWE-337 - Predictable Seed in PRNG
- CWE-338 - Use of Cryptographically Weak Pseudo Random Number Generator (PRNG)