Safely Generating Cryptographically Secure Random Numbers With Swift

We’ll take a look at how safely generate cryptographically secure random bytes as an array of bytes, an UInt, or as a hexadecimal string.

Apple has provided a function called SecRandomCopyBytes() inside their Security framework that allows us to create an array of cryptographically secure random bytes.There are other ways to generate random data on iOS and OS X, most notably, arc4random() but this has a few problem: (1) arc4random() can only generate, at most, a 32 bit integer, (2) Apple’s arc4random() isn’t secure because arc4random() relies on an underlining stream cipher to achieve secure random data; Apple’s implementation uses RC4, which is now considered broken. SecRandomCopyBytes uses CTR_DRBG which is still secure.

So now you know a little bit about why we want to use SecRandomCopyBytes(). We will be using Swift 2* to access SecRandomCopyBytes() without using withUnsafeMutablePointer() and unsafeBitCast(). Lets begin by looking at some docs

// Obj-C
int SecRandomCopyBytes ( SecRandomRef rnd, size_t count, uint8_t *bytes ); 

// Swift
func SecRandomCopyBytes(_ rnd: SecRandomRef,
                      _ count: Int,
                      _ bytes: UnsafeMutablePointer<UInt8>) -> Int32

rnd is the random number generator (RNG) object to use. kSecRandomDefault is the default RNG.

count is the number of bytes to return in the array pointed to by the bytes parameter.

bytes is the random bytes generated by the function.

Objective-C

// Obj-C
@import Security;

uint32_t randomNum = 0; 
SecRandomCopyBytes(kSecRandomDefault, 4, (uint8_t*) &randomNum);

First, we declared an unsigned 32-bit integer and initialized it to 0. Next, we call SecRandomCopyBytes passing 4 as the number of bytes (4 bytes, 32 bits), as well as the address of the randomNum integer we just declared. Awesome.

Swift, First Attempt

// Swift
import Security

var randomNum: UInt32 = 0
SecRandomCopyBytes(kSecRandomDefault, 4, &randomNum)

This won’t work. A compiler error is generated because UInt32 is not the same as UInt8. Yay for strongly typed languages! (Seriously though, I think they’re great.)

Swift, Playing it Safe

// Swift 
import Security

let bytesCount = 4 // number of bytes
var randomNum: UInt32 = 0 // variable for random unsigned 32 bit integer
var randomBytes = [UInt8](count: bytesCount, repeatedValue: 0) // array to hold randoms bytes

// Gen random bytes
SecRandomCopyBytes(kSecRandomDefault, bytesCount, &randomBytes)

// Turn bytes into data and pass data bytes into int
NSData(bytes: randomBytes, length: bytesCount).getBytes(&randomNum, length: bytesCount)

So what’s going on here? First, we initialize a variable for the number of bytes we want to use (4 bytes, 32 bits). Next, we initialize a variable for the random number we’re trying to generate and we initialize an array to hold the random bytes. Then, we can call SecRandomCopyBytes() passing in the default RNG, the number of bytes we want to generate, and the address to the array of bytes. Finally, we create a NSData object based on the array of bytes and we get the bytes ( .getBytes() ) by passing in the address where the bytes will be going (which is an unsigned 32 bit int in our case) and the number of bytes. randomNum now has 32 bits of random data.

Awesome! We’re done now, right? Not quite. The method above work if we want a random integer but this limits us to 64 bit numbers. What happens when we want 128 or 256 bit numbers? Lets take a look (hint: hexadecimal string).

Swift, Playing it Safe With Large Numbers

// Swift 
import Security

let bytesCount = 32 // number of bytes
var randomNum = "" // hexadecimal version of randomBytes
var randomBytes = [UInt8](count: bytesCount, repeatedValue: 0) // array to hold randoms bytes

// Gen random bytes
SecRandomCopyBytes(kSecRandomDefault, bytesCount, &randomBytes)

// Turn randomBytes into array of hexadecimal strings
// Join array of strings into single string
randomNum = randomBytes.map({String(format: "%02hhx", $0)}).joinWithSeparator("")

Here we’re using a string to hold a hexadecimal representation of a 256 bit number. Instead of using NSData() we turn randomBytes into an array of hexadecimal strings. We use the string formatter "%02hhx" to turn our byte (denoted $0) into a hex representation. Lastly, we join the array of strings into a single string using .joinWithSeparator() Awesome! Now we know how to generate cryptographically secure random integers and hexadecimal strings.

Wrapping Up

I hope you learn something here, I know I have while researching iOS security. Swift has also become my favorite language and really love writing code in it. I can’t wait to see how Swift develops as an open source language. Thanks for reading.

Follow me on twitter for more!

Notes:

*The SecRandomCopyBytes() call works the same way in Swift 1.2 but some of the other syntax has changed in Swift 2, example: String.join([String]) has become [String].joinWithSeparator("").