Security Architecture · Deep Dive

Zero trust. Zero compromise.

A complete breakdown of how NeuroKey protects your data, what threats it defends against, and the cryptographic guarantees it provides.

AES-256PBKDF2 (5k)k-AnonymitySecure EnclaveZero Network

Threat model

Security is only meaningful in the context of what you're protecting against. Here's every threat we've modeled and how NeuroKey addresses each one.

Eliminated

Server breach / data exfiltration

No server exists. There is nothing to breach.

Eliminated

Brute-force of master password

Master password is never stored in plaintext. It is aggressively hashed via PBKDF2 strictly for key derivation.

Mitigated

Device theft

Vault requires biometric unlock. Encrypted with AES-256 at rest.

Mitigated

Physical memory extraction

Key is zeroed from memory after use. Secure Enclave prevents extraction of the biometric handle.

Mitigated

Reverse engineering & app tampering

Production Android builds utilize ProGuard (R8) code obfuscation, shrinking resources and masking execution logic.

Encryption pipeline

A step-by-step walkthrough of how your vault goes from plaintext to ciphertext — and back.

01

Biometric trigger

User authenticates via Face ID, Touch ID, or Android Biometrics. The OS handles the scan and passes a success token to the Expo runtime to unlock the SecureStore.

import * as LocalAuth from 'expo-local-authentication';

const result = await LocalAuth.authenticateAsync({
  promptMessage: 'Unlock NeuroKey Vault',
  fallbackLabel: 'Use Master Password',
  disableDeviceFallback: false,
});

// On success → release SecureStore key handle
02

PBKDF2 key derivation

The biometric byte array is used as PBKDF2 key material. With 5,000 SHA-256 rounds and a random 256-bit salt, the resulting AES-256 key is computationally infeasible to brute-force.

const rawKey = await crypto.subtle.importKey(
  "raw", biometricBytes, "PBKDF2",
  false, ["deriveKey"]
);
const aesKey = await crypto.subtle.deriveKey(
  { name: "PBKDF2", salt, iterations: 5_000,
    hash: "SHA-256" },
  rawKey,
  { name: "AES", length: 256 },
  false, ["encrypt", "decrypt"]
);
03

AES-256 encryption

The vault JSON is encrypted with AES-256. A fresh random 96-bit IV is generated for every write. The authentication tag (128-bit) detects any byte-level tampering on the ciphertext.

const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
  { name: "AES", iv,
    additionalData: vaultMetadata },
  aesKey,
  new TextEncoder().encode(vaultJSON)
);
// Ciphertext includes 128-bit auth tag
// Any tampering → decryption throws
04

Encrypted AsyncStorage & SecureStore

The encrypted JSON blob is written to AsyncStorage. The highly sensitive decryption keys are stored separately in the device's hardware-backed Keychain/Keystore via expo-secure-store.

import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';

// 1. Store the encrypted vault blob
await AsyncStorage.setItem('@neurokey_vault', ciphertext);

// 2. Lock the AES key in hardware Keystore
await SecureStore.setItemAsync(
  'ENCRYPTION_KEY_HANDLE', 
  aesKey, 
  { requireAuthentication: true }
);
Breach Radar

How k-anonymity keeps your password private.

To check if a password has been leaked, NeuroKey uses the k-anonymity model from the Have I Been Pwned API. Here's the exact protocol:

  1. 1

    Hash the password

    Compute SHA-1 of the candidate password locally. Example: SHA-1("hunter2") = AB87D2...

  2. 2

    Truncate to 5 chars

    Take only the first 5 hex characters (the "prefix"): AB87D. This is what's transmitted.

  3. 3

    Query the API

    Send GET /range/AB87D. The API returns all hash suffixes that share this prefix — typically 400–900 entries.

  4. 4

    Match locally

    Compare the full local hash against the returned suffixes. A match means the password is in a breach database. No full hash or plaintext ever leaves your device.

k-anonymity protocol

1. Local hash

SHA1("hunter2") = AB87D24BEA...

2. Prefix sent to API

GET /range/AB87D → server never sees full hash

3. API returns suffixes

24BEA...:34221
7F3AC...:891
C4D90...:12
...(600+ entries)

4. Local match check

24BEA matched! Password found in 34,221 breaches.

Don't trust. Verify.

NeuroKey is fully open source. Every cryptographic claim on this page can be verified in the source code. No black boxes.

0

Network requests per vault operation

5k

PBKDF2 iterations per key derivation

256-bit

AES key length

0

Bytes stored on any server