In Swift, use the default system random generator or SecRandomCopyBytes for anything secret, use Apple CryptoKit for hashing, signatures and encryption, never reuse a nonce, and avoid the algorithms CryptoKit keeps in its Insecure namespace for new work. Do not write your own cryptography.
Which random source is safe?
Swift's SystemRandomNumberGenerator is the generator used by default when you call random methods or shuffle a collection. Apple's documentation says it is automatically seeded, safe to use from multiple threads, and uses a cryptographically secure algorithm whenever possible. It adds a caution: the cryptographic quality of the stream may vary across platforms, so see each platform's underlying API. On Apple platforms it uses arc4random_buf. On Linux it uses getrandom when available and otherwise reads from /dev/urandom. On Windows it uses BCryptGenRandom.
For raw secret bytes such as tokens and salts, the Security framework's SecRandomCopyBytes fills a buffer with cryptographically secure random bytes. Apple's documentation says always to test the returned status to make sure the buffer was actually filled before using it.
var bytes = [UInt8](repeating: 0, count: 32)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
guard status == errSecSuccess else { throw TokenError.randomFailed }
A common bug is ignoring that status and using an all-zero buffer as a key or token. Another is seeding a custom generator with the clock. Use neither.
Why prefer CryptoKit?
Apple's CryptoKit documentation covers secure digests, public-key signatures and key exchange, Secure Enclave keys, symmetric keys, message authentication and encryption. Its advice is direct: prefer CryptoKit over lower-level interfaces. It frees your app from managing raw pointers and automatically handles tasks such as overwriting sensitive data during memory deallocation.
A minimal example of authenticated encryption with a freshly generated key:
import CryptoKit
let key = SymmetricKey(size: .bits256)
let sealed = try AES.GCM.seal(Data("account note".utf8), using: key)
let combined = sealed.combined // nonce, ciphertext and tag together
let box = try AES.GCM.SealedBox(combined: combined!)
let plain = try AES.GCM.open(box, using: key)
The sealed box carries the nonce and authentication tag, and opening fails if the data was modified.
What is the nonce rule?
The documentation for the AES-GCM nonce says it is a value used once during a cryptographic operation and then discarded. It states: do not reuse the same nonce for multiple calls to encryption APIs, because uniqueness per call is critical to protect the integrity of the encryption. The simplest way to follow it is to let CryptoKit generate the nonce for each seal call, as in the example above, and never to pass a fixed or counter-reset value.
What about old algorithms?
CryptoKit includes an Insecure container for older algorithms. Apple says these are not considered cryptographically secure, are provided only for backward compatibility with older services that require them, and should be avoided for new services. If you are verifying a legacy checksum from a vendor, using them is understandable. If you are hashing passwords or signing new data, it is not.
Where do keys live?
A key generated in memory is only as safe as the process holding it. For long-lived secrets, store keys in the Keychain, and for private keys that can stay on device, CryptoKit supports keys managed by the Secure Enclave. Check Apple's documentation for the exact key types supported and the flags that bind keys to the device.
Constructed example. A notes app encrypts local files. The first version used a hard-coded key string in the app binary. The fix: generate a symmetric key on first run, store it in the Keychain, encrypt each file with AES-GCM using a fresh nonce, and delete the old files after migration.
What to do
- Use SystemRandomNumberGenerator or SecRandomCopyBytes for every secret value, and check the status code.
- Remove any use of the clock, counters or standard library pseudo-random sources for tokens or keys.
- Use CryptoKit for hashing, signing and encryption instead of lower-level C APIs where possible.
- Let CryptoKit generate nonces, and never reuse one with the same key.
- Keep keys in the Keychain, or the Secure Enclave where supported.
- Search for use of the Insecure namespace and justify each hit in review.
- Never hard-code keys or ship them in the app bundle.
- Keep Swift packages that wrap cryptography updated and scanned.
Safeguard can scan Swift Package Manager and CocoaPods dependencies in pull requests and report vulnerable packages. It does not review your cryptographic design.
Quick check
Is Int.random safe for generating a password reset token?
The default generator is documented as using a cryptographically secure algorithm whenever possible, but quality can vary by platform. For tokens, prefer SecRandomCopyBytes and check its status, so the source is explicit.
Can I reuse an AES-GCM nonce if the data differs?
No. Apple's documentation says to never reuse the same nonce for multiple encryption calls with the same key. Let the API generate a fresh nonce each time.
Sources
- Apple: SystemRandomNumberGenerator
- Apple: SecRandomCopyBytes
- Apple: CryptoKit
- Apple: AES.GCM.Nonce
- Apple: CryptoKit Insecure
Last reviewed: 18 October 2026.