GitHub - mimiclone/argon2-swift: Pure Swift 6 implementation of Argon2

5 min read Original article ↗

SwiftArgon2

License: MIT Swift 6 Platforms

A memory-safe, zero-dependency Swift 6 implementation of Argon2, the winner of the Password Hashing Competition and the algorithm specified in RFC 9106.

Includes a slimmed-down memory-safe Swift implementation of Blake2b, containing only the operations needed for Argon2.

Why This Library Exists

Most existing Swift Argon2 packages are thin wrappers around the C reference implementation or libsodium. While these are battle-tested and fast, they bring C's memory-safety failure modes into your Swift application — buffer overruns, use-after-free, and uninitialized reads at the FFI boundary.

SwiftArgon2 is implemented in pure Swift, with one carefully-scoped exception (see Secure Memory Wiping below). It exists for applications where the security properties of memory-safe code are worth a moderate performance cost — typically client-side key derivation on mobile devices, where the algorithm is meant to be slow anyway.

Features

  • Pure Swift 6 with structured concurrency (async/await)
  • Zero dependencies beyond Foundation
  • Memory safe — no UnsafeMutableBufferPointer, no FFI except for secure memory wipe
  • RFC 9106 compliant — interoperable with the C reference implementation, libsodium, Password4j, Spring Security's Argon2PasswordEncoder, and any other spec-compliant verifier
  • All three variants — Argon2d, Argon2i, and Argon2id
  • Secure memory wiping of sensitive intermediate state (passwords, keys, memory matrix)
  • PHC string format — produces and consumes standard $argon2id$v=19$m=... strings without padding

Installation

Add this to your Package.swift dependencies:

.package(url: "https://github.com/mimiclone/argon2-swift.git", from: "1.0.4")

Then add SwiftArgon2 to your target's dependencies:

.target(
    name: "YourTarget",
    dependencies: [
        .product(name: "SwiftArgon2", package: "argon2-swift")
    ]
)

Usage

Hashing a password

import SwiftArgon2

// Argon2Params are optional and you will get these defaults if you omit them
let argon2 = try Argon2(params: Argon2Params(
    parallelism: 4,     // Number of lanes
    tagLength: 32,      // Output length in bytes
    memorySize: 65536,  // Memory size in KiB (64 MiB)
    iterations: 3,      // Number of passes
    variant: .argon2id  // .argon2d, .argon2i, or .argon2id
))

let hash = try await argon2.compute(
    password: "MyPassword".data(using: .utf8)!,
    salt: cryptographicallySecureRandomSalt   // 16+ bytes recommended
    // Optional: secret (keyed hash), associatedData
)

Producing a PHC-formatted string

let encoded = try await argon2.computeEncoded(
    password: passwordData,
    salt: saltData
)
// "$argon2id$v=19$m=65536,t=3,p=4$<salt>$<hash>"

The encoded string is interoperable with any RFC 9106-compliant verifier.

Performance

This library is roughly 3-5x slower than libsodium with p=1 (which uses hand-crafted NEON instructions for Blake2b). Notably though, this library is more performant than the reference C implementation when run on an ARM64 processor. This is because the reference implementation does not have hand-written SIMD intrinsics for NEON, and so falls back to portable scalar C.

The table below shows benchmarks from a MacBook Pro with an Apple M2 Max chipset:

Implementation Time Notes
SwiftArgon2, p=4 0.165s Parallel
libsodium, p=1 0.081s Hand-tuned NEON; p=4 not exposed
SwiftArgon2, p=1 0.463s Autovectorized
Reference C, p=4 (no SIMD) ~0.84s Portable scalar on arm64
Reference C, p=1 (no SIMD) ~0.65s Portable scalar on arm64

This library is primarily intended as a key derivation function on mobile devices, not as a server-side password hashing verifier.

For high-throughput server-side password verification (on x86), a C-based implementation will be more efficient.

Use in Xcode apps

Argon2 is intentionally compute-intensive. In Debug builds, Swift disables optimizations entirely (-Onone), which can make hashing take 20-100x longer than in Release builds. A hash that completes in 200ms in Release may take 10+ seconds in Debug.

For development, consider:

  • Reducing Argon2 parameters (memory cost, iterations) during Debug runs and using production parameters only in Release builds.
  • Testing authentication flows against Release builds.
  • As a last resort, adding -O to "Other Swift Flags" for your Debug configuration to force optimization across your entire app.

Secure Memory Wiping

SwiftArgon2 securely wipes sensitive memory after use (passwords, secret keys, the H0 digest, the full memory matrix, and intermediate scratch buffers). By default, this uses memset_s (Apple platforms) or explicit_bzero (Linux) via Swift's FFI. Plain Swift assignment cannot reliably zero memory because the compiler may optimize away writes to memory that's about to be deallocated.

This is the only non-pure-Swift code path in the library, and it is contained to a single function with a clearly documented purpose.

To build without FFI-based secure wipe (strictly pure-Swift mode), remove the MIMICLONE_SECURE_WIPE define from your build. In this mode, the library falls back to plain Swift assignment of zeros, which the compiler may elide. Memory wipes are not guaranteed in this mode. Choose this configuration only if you understand and accept the tradeoff.

You can verify the active mode at runtime via Argon2Configuration.secureWipeMode.

Platform Support

Tested on little-endian systems (Apple Silicon, Intel, modern ARM). This covers all current Apple platforms and the vast majority of Linux deployments. Big-endian support is theoretically correct per the spec but not exercised in CI.

Platform Status
macOS 15+ Tested
iOS 18+ Tested
watchOS 11+ Tested
tvOS 18+ Tested
visionOS 2+ Tested
Linux (Debian) Tested
Linux (Other flavors) Not Tested
Windows Not Tested

Test Vectors

The test suite includes the canonical RFC 9106 test vectors plus additional vectors generated against the C reference implementation covering:

  • All three variants (Argon2d, Argon2i, Argon2id)
  • Empty and non-empty secret/associated-data combinations
  • Single-lane and multi-lane configurations
  • Single-pass and multi-pass configurations
  • Multiple tag lengths
  • Production-scale parameters

If you discover an interoperability issue with another Argon2 implementation, please open an issue with the failing parameters.

Contributing

Bug reports and pull requests welcome at the issue tracker.

License

SwiftArgon2 is licensed under the MIT License.

Acknowledgments