I'd like to use a PRNG like arc4random_uniform()
; however, Wikipedia seems to think that rc4 is insecure. I don't have the wherewithal to confirm myself, but security is a requirement for my use-case.

- 4,573
- 10
- 36
- 60
-
Without no experience at all with iOS and co., [this function](https://developer.apple.com/documentation/security/randomization_services) seems to be the official way (based on CTR-DRBG?), if you really ask for a CPRNG (arc4 is technically a stream-cipher, but it seems you only want the CPRNG capabilities). So what's wrong with this function? – sascha Jun 25 '17 at 23:47
-
@sascha: I did take a cursory look at that (i'll update the question shortly). [`SecRandomCopyBytes()`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) is designed to "generate an array of cryptographically secure random bytes". What i need is to generate random numbers below a given threshold; but to be fair, i'm still hunting for examples of people who accomplished this successfully with `SecRandomCopyBytes`. – Noob Saibot Jun 26 '17 at 00:02
-
Then you should grab some basics. Uniform sampling in [0,1) also starts with 32 or 64 random bits = 4 or 8 random bytes. – sascha Jun 26 '17 at 00:04
-
@sascha: Sounds like you're saying i should roll out a solution myself? Fair enough. Would you mind pointing me in the right direction? I had really hoped to avoid taking a whole class on crypto for this. – Noob Saibot Jun 26 '17 at 00:09
-
This has nothing to do with crypto. You already got the crypto-part! Obtaining random bytes is the core of many many algorithms. The only thing left for you is *classic stochastic sampling* (with some algorithmical quirks as fp-math and co). You did not specify what you want to sample, maybe uniform [0,1), maybe uniform_int (0, 10000). Those are classical examples and there are a lot of resources (and they are not much crypto-related; starting from secure random bytes and not doing something very wrong to create bias and everything is alright; if your task itself is well-defined) – sascha Jun 26 '17 at 00:12
-
"classic stochastic sampling". Got it. Thanks, @sascha! – Noob Saibot Jun 26 '17 at 00:13
1 Answers
arc4random_uniform
is documented as a "cryptographic pseudo-random number generator," so it should be fine for this purpose. Do not confuse the security problems of RC4 with arc4random
. See Zaph's answer for more details. (I've researched this before, and I remember arc4random
being just as secure as other approaches, but I trust Zaph more than I trust my own memory.)
That said, if you are nervous, the tool you want to use is SecRandomCopyBytes
(alternately you can read from /dev/random
, which is exactly what SecRandomCopyBytes
does by spec).
Getting a random value from SecRandomCopyBytes
is harder than it should be, but not too hard. Here's how you do it in a highly generic way (Swift 3):
extension Integer {
static func makeRandom() -> Self {
var result: Self = 0
withUnsafeMutablePointer(to: &result) { resultPtr in
resultPtr.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) { bytePtr in
SecRandomCopyBytes(nil, MemoryLayout<Self>.size, bytePtr)
}
}
return result
}
}
This works on any Integer
. Basically we interpret a bunch of random bytes as an Integer
. (BTW, this approach does not work nearly as well for floating point values. You can do it, but you'll discover that not all bit patterns are actually "numbers" in floating point. So it's a little more complicated.)
Now you want to get those values in a range without introducing bias. Just saying x % limit
creates modulo bias. Don't do that. The correct way approach is to do what arc4random_uniform
does. It's open source, so you can just go look at it. Applying the same approach in Swift looks like:
extension Int {
static func makeRandom(betweenZeroAnd limit: Int) -> Int {
assert(limit > 0)
// Convert our range from [0, Int.max) to [Int.max % limit, Int.max)
// This way, when we later % limit, there will be no bias
let minValue = Int.max % limit
var value = 0
// Keep guessing until we're in the range.
// In theory this could loop forever. It won't. A couple of times at worst
// (mostly because we'll pick some negatives that we'll throw away)
repeat {
value = makeRandom()
} while value < minValue
return value % limit
}
}
We can't build this on Integer
because there's no .max
property on Integer
.
In Swift 4 this is all cleaned up with FixedWidthInteger
, and we can make this more generic:
extension FixedWidthInteger {
static func makeRandom() -> Self {
var result: Self = 0
withUnsafeMutablePointer(to: &result) { resultPtr in
resultPtr.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<Self>.size) { bytePtr in
SecRandomCopyBytes(nil, MemoryLayout<Self>.size, bytePtr)
}
}
return result
}
static func makeRandom(betweenZeroAnd limit: Self) -> Self {
assert(limit > 0)
// Convert our range from [0, Int.max) to [Int.max % limit, Int.max)
// This way, when we later % limit, there will be no bias
let minValue = Self.max % limit
var value: Self = 0
// Keep guessing until we're in the range.
// In theory this could loop forever. It won't. A couple of times at worst
// (mostly because we'll pick some negatives that we'll throw away)
repeat {
value = makeRandom()
} while value < minValue
return value % limit
}
}

- 16,006
- 8
- 81
- 189

- 286,113
- 34
- 456
- 610
-
I think this is what i'm looking for; but just to be sure, would you mind linking to the `Integer` man page? I can't find it anywhere. – Noob Saibot Jun 26 '17 at 04:28
-
1Apple removed all the Swift 3.1 pages when the beta came out. You have to look back at archives like swiftdoc (or in my case, Dash): http://swiftdoc.org/v3.1/protocol/Integer/ – Rob Napier Jun 26 '17 at 04:31