How to generate secure random data?
In our development career, we may encounter tasks in which, for example, we will have to use various types of encryption algorithms. We will see then that one of the acceptance criteria can be the creation of an encryption key, which is a secure random data.
What does it mean? To generate random data, we can create our own algorithm in which we use specific rules. Such an approach may prove to be not entirely safe and correct.
You must remember that, many security operations rely on randomization to avoid reproducibility. This also applies to something as simple as generating a password string that cannot be easily guessed. If the string characters are truly random, the attacker has no choice but to try every possible combination one at a time in a brute force attack. With sufficiently long strings, this becomes unfeasible.
As you can see, the strength of such a password depends on the quality of the randomization. So true randomization can be difficult to achieve when you try to invent an algorithm yourself according to well-defined rules, because if an attacker can infer patterns in insufficiently randomized data, your system becomes compromised.
In order not to reinvent the wheel (an attempt to invent complicated rules of randomness), Apple comes to the rescue with its Randomization Service API. This API uses the SecRandomCopyBytes function and multiple patterns to generate a cryptographically secure set of random numbers (values from 0 to 255).
Below I present a ready-made solution on how to generate random data by using this API.
import Security enum SecureRandomError: Error { case failed(status: OSStatus) case countMustBeGreaterThanZero } enum SecureRandom { /// Generates secure random data with a given count. /// /// - Parameter count: The count of the random generated data. Must be greater than 0. /// - Returns: The random generated data. /// - Throws: `SecureRandomError` if any error occurs during generation of secure random bytes. static func generate(count: Int) throws -> Data { // swiftlint:disable:next empty_count guard count > 0 else { throw SecureRandomError.countMustBeGreaterThanZero } var generatedRandom = Data(count: count) let randomGenerationStatus = generatedRandom.withUnsafeMutableBytes { mutableRandomBytes in // Force unwrapping is ok, since the buffer is guaranteed not to be empty. // From the docs: If the baseAddress of this buffer is nil, the count is zero. // swiftlint:disable:next force_unwrapping SecRandomCopyBytes(kSecRandomDefault, count, mutableRandomBytes.baseAddress!) } guard randomGenerationStatus == errSecSuccess else { throw SecureRandomError.failed(status: randomGenerationStatus) } return generatedRandom } }