Article 1: The LockRoot Cryptographic Pipeline: From Password to Ciphertext

1.1 The Challenge of Local Cryptography

Cloud-based password managers offload a significant portion of their security model to their backend servers (e.g., rate-limiting login attempts, rotating salts, and enforcing MFA). A purely local, offline password manager like LockRoot must rely entirely on mathematics to defend the vault data against an attacker who has stolen the encrypted file. The cryptographic pipeline must be expensive enough to deter offline brute-forcing but fast enough to provide a seamless user experience on mobile hardware.

1.2 The Argon2id Implementation

The pipeline begins when the user enters their master password. LockRoot requires a minimum of 12 characters. The password is immediately converted to a UTF-8 byte array (or Data object on iOS).

LockRoot utilizes Argon2id (RFC 9106) as its Key Derivation Function (KDF). Argon2id is a hybrid algorithm designed to resist parallelized GPU attacks (via memory-hardness) and side-channel timing attacks.

The derivation parameters are dynamically read from the vault’s JSON envelope:

  • Memory: 65,536 KiB (64 MiB)
  • Iterations: 3
  • Parallelism: 2
  • Salt: 32 bytes (randomly generated during vault creation).

On Android, this is executed within CryptoService.kt utilizing the BouncyCastle provider:

val builder = Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
    .withVersion(Argon2Parameters.ARGON2_VERSION_13)
    .withIterations(3)
    .withMemoryAsKB(65536)
    .withParallelism(2)
    .withSalt(saltBytes)

val generator = Argon2BytesGenerator()
generator.init(builder.build())
generator.generateBytes(passwordBytes, outputKeyBytes, 0, outputKeyBytes.size)

On iOS, the Argon2Swift wrapper binds directly to the C implementation.

1.3 Authenticated Encryption (AES-256-GCM)

Once the 256-bit key is derived, the pipeline moves to encryption. LockRoot universally utilizes AES-256-GCM. A fresh 12-byte (96-bit) nonce is generated via the OS-level CSPRNG (SecureRandom on Android, randombytes_buf via Clibsodium on iOS).

The payload (the serialized JSON containing the vault entries) is encrypted, generating the ciphertext and a 16-byte (128-bit) authentication tag. The tag guarantees the authenticity and integrity of the encrypted data; any modification to the ciphertext will cause the GCM polynomial evaluator to reject the payload during decryption.

1.4 Memory Scrubbing

The final, critical step of the pipeline is memory erasure. The original password bytes are explicitly zeroed to prevent extraction via memory forensics. The derived AES key is held in memory only while the application is actively unlocked and is subsequently zeroed when the app enters the background or the inactivity timer expires.