Skip to content

Latest commit

 

History

History
466 lines (373 loc) · 14 KB

File metadata and controls

466 lines (373 loc) · 14 KB

Key Alias Security Enhancement

📖 This guide is also published at https://sbaiahmed1.github.io/react-native-biometrics/docs/guides/key-alias-security

Overview

This document describes the comprehensive security enhancements in the React Native Biometrics library, including configurable app-specific key aliases, advanced key integrity validation, signature verification, and enhanced logging capabilities for security monitoring.

Security Issue Fixed

Implementation (Secure)

  • App-Specific Default: Each app gets a unique default key alias based on bundle ID (iOS) or package name (Android)
  • Configurable Aliases: Developers can set custom key aliases for different use cases
  • Isolation: Each app's biometric keys are properly isolated

API Changes

New Configuration Functions

// Configure a custom key alias
await configureKeyAlias('com.myapp.biometric.main');

// Get the current default key alias
const defaultAlias = await getDefaultKeyAlias();

// Configure using a config object
await configure({
  keyAlias: 'com.myapp.biometric.main',
  keyPrefix: 'com.myapp.secure'
});

// Enable security logging for monitoring
import { setDebugMode, enableLogging, LogLevel } from '@sbaiahmed1/react-native-biometrics';

await setDebugMode(true);
enableLogging(true);
setLogLevel(LogLevel.INFO);

Enhanced Key Management

// Create keys with default (configured) alias
const keyResult = await createKeys();
console.log('Public key:', keyResult.publicKey);

// Create keys with a specific alias
await createKeys('com.myapp.biometric.backup');

// Get all keys for audit purposes
const allKeys = await getAllKeys();
console.log('Total keys:', allKeys.keys.length);

// Delete keys with default (configured) alias
const deleteResult = await deleteKeys();
console.log('Deletion success:', deleteResult.success);

// Delete keys with a specific alias
await deleteKeys('com.myapp.biometric.backup');

Default Key Alias Generation

iOS

  • Format: {bundleId}.ReactNativeBiometrics
  • Example: com.mycompany.myapp.ReactNativeBiometrics
  • Storage: Stored in UserDefaults with key ReactNativeBiometrics_KeyAlias

Android

  • Format: {packageName}.ReactNativeBiometrics
  • Example: com.mycompany.myapp.ReactNativeBiometrics
  • Storage: Stored in SharedPreferences with key keyAlias

Migration Guide

For Existing Apps

  1. No Action Required: Existing apps will continue to work with the old hardcoded alias until you explicitly configure a new one.

  2. Recommended Migration:

    // Get all existing keys
    const existingKeys = await getAllKeys();
    
    // Configure new app-specific alias
    await configureKeyAlias('com.myapp.biometric.main');
    
    // Create new keys with the new alias
    await createKeys();
    
    // Optionally delete old keys (be careful!)
    // await deleteKeys('ReactNativeBiometricsKey');
  3. Gradual Migration:

    // Check if old keys exist
    const allKeys = await getAllKeys();
    const hasOldKeys = allKeys.keys.some(key => 
      key.alias === 'ReactNativeBiometricsKey'
    );
    
    if (hasOldKeys) {
      // Use old keys for now, migrate later
      console.log('Using legacy keys');
    } else {
      // Configure new alias for new installations
      await configureKeyAlias('com.myapp.biometric.main');
    }

Advanced Security Features

Key Integrity Validation

// Comprehensive key integrity check
const integrityResult = await validateKeyIntegrity('com.myapp.biometric.main');

if (integrityResult.valid) {
  console.log('Key integrity verified');
  console.log('Hardware-backed:', integrityResult.integrityChecks.hardwareBacked);
  console.log('Key accessible:', integrityResult.integrityChecks.keyAccessible);
  console.log('Signature test:', integrityResult.integrityChecks.signatureTestPassed);
} else {
  console.error('Key integrity compromised:', integrityResult.error);
}

Signature Verification

// Generate and verify signatures for data integrity
const data = 'sensitive transaction data';

// Generate signature
const signatureResult = await verifyKeySignature('com.myapp.biometric.main', data);
if (signatureResult.success && signatureResult.signature) {
  const signature = signatureResult.signature;
  
  // Later, validate the signature
  const validationResult = await validateSignature(
    'com.myapp.biometric.main',
    data,
    signature
  );
  
  if (validationResult.valid) {
    console.log('Data integrity verified');
  } else {
    console.error('Data may have been tampered with');
  }
}

Key Attributes and Security Level

// Get detailed key attributes for security assessment
const keyAttributes = await getKeyAttributes('com.myapp.biometric.main');

if (keyAttributes.exists && keyAttributes.attributes) {
  const attrs = keyAttributes.attributes;
  console.log('Algorithm:', attrs.algorithm);
  console.log('Key size:', attrs.keySize);
  console.log('Security level:', attrs.securityLevel);
  console.log('Hardware-backed:', attrs.hardwareBacked);
  console.log('User auth required:', attrs.userAuthenticationRequired);
  console.log('Creation date:', attrs.creationDate);
}

Security Best Practices

1. Use Descriptive Key Aliases

// Good: Descriptive and app-specific
await configureKeyAlias('com.myapp.biometric.login');
await configureKeyAlias('com.myapp.biometric.payment');

// Avoid: Generic or potentially conflicting
await configureKeyAlias('biometric');
await configureKeyAlias('key');

2. Multiple Key Aliases for Different Purposes

// Different aliases for different security contexts
const LOGIN_KEY_ALIAS = 'com.myapp.biometric.login';
const PAYMENT_KEY_ALIAS = 'com.myapp.biometric.payment';
const ADMIN_KEY_ALIAS = 'com.myapp.biometric.admin';

// Create keys for different purposes
await createKeys(LOGIN_KEY_ALIAS);
await createKeys(PAYMENT_KEY_ALIAS);

3. Key Rotation Strategy

// Implement secure key rotation with integrity validation
const rotateKeys = async () => {
  const oldAlias = 'com.myapp.biometric.v1';
  const newAlias = 'com.myapp.biometric.v2';
  
  try {
    // Create new keys
    const newKeyResult = await createKeys(newAlias);
    
    // Validate new key integrity
    const integrityCheck = await validateKeyIntegrity(newAlias);
    if (!integrityCheck.valid) {
      throw new Error('New key integrity validation failed');
    }
    
    // Test signature with new key
    const testData = 'key rotation test';
    const signatureTest = await verifyKeySignature(newAlias, testData);
    if (!signatureTest.success) {
      throw new Error('New key signature test failed');
    }
    
    // Persist the new alias only after all validation succeeded
    await configureKeyAlias(newAlias);
    
    // Delete old keys after successful validation
    await deleteKeys(oldAlias);
    
    console.log('Key rotation completed successfully');
  } catch (error) {
    console.error('Key rotation failed:', error);
    // Rollback: point the configured alias back at the old keys, then remove the new keys.
    // (Covers the case where the failure happened after configureKeyAlias(newAlias).)
    await configureKeyAlias(oldAlias);
    await deleteKeys(newAlias);
    throw error;
  }
};

Validation Rules

Key Alias Requirements

  • Minimum Length: 3 characters
  • Maximum Length: 100 characters
  • Allowed Characters: Letters, numbers, dots, hyphens, underscores
  • Pattern: Must match ^[a-zA-Z0-9._-]+$

Examples

// Valid aliases
'com.myapp.biometric'
'myapp_biometric_key'
'biometric-key-v1'
'app.biometric.2024'

// Invalid aliases
''                    // Too short
'a'                   // Too short
'key with spaces'     // Contains spaces
'key@domain.com'      // Contains @
'key#1'              // Contains #

Error Handling and Security Monitoring

Enhanced Error Handling

import { logger, getLogs, clearLogs } from '@sbaiahmed1/react-native-biometrics';

try {
  await configureKeyAlias('com.myapp.biometric');
} catch (error) {
  if (error.message.includes('Invalid key alias')) {
    // Handle validation error
    logger.error('Key alias format is invalid', 'configureKeyAlias', error);
  } else if (error.message.includes('Key already exists')) {
    // Handle key collision
    logger.warn('Key alias already in use', 'configureKeyAlias', error);
  } else {
    // Handle other errors
    logger.error('Failed to configure key alias', 'configureKeyAlias', error);
  }
}

Security Event Logging

// Monitor security events
const monitorSecurityEvents = async () => {
  try {
    // Enable comprehensive logging
    await setDebugMode(true);
    
    // Perform security operations
    await createKeys('com.myapp.secure.key');
    const integrity = await validateKeyIntegrity('com.myapp.secure.key');
    
    // Get security logs for analysis
    const logs = getLogs();
    const securityLogs = logs.filter(log => 
      log.context?.includes('security') || 
      log.context?.includes('integrity') ||
      log.context?.includes('signature')
    );
    
    // Send to security monitoring system
    await sendToSecurityMonitoring(securityLogs);
    
    // Clear logs after processing
    clearLogs();
  } catch (error) {
    logger.error('Security monitoring failed', 'monitorSecurityEvents', error);
  }
};

Testing

Unit Tests

// Test key alias validation
it('should reject invalid key aliases', async () => {
  await expect(configureKeyAlias('')).rejects.toThrow('Invalid key alias');
  await expect(configureKeyAlias('a')).rejects.toThrow('Invalid key alias');
  await expect(configureKeyAlias('key with spaces')).rejects.toThrow('Invalid key alias');
});

// Test key isolation
it('should isolate keys by alias', async () => {
  await createKeys('alias1');
  await createKeys('alias2');
  
  const keys = await getAllKeys();
  expect(keys.keys).toHaveLength(2);
  
  await deleteKeys('alias1');
  const remainingKeys = await getAllKeys();
  expect(remainingKeys.keys).toHaveLength(1);
  expect(remainingKeys.keys[0].alias).toBe('alias2');
});

Performance Considerations

  • Configuration Persistence: Key alias configuration is persisted locally and loaded once during module initialization
  • No Network Calls: All key alias operations are local
  • Minimal Overhead: Key alias resolution adds negligible performance overhead

Security Audit Checklist

  • ✅ Hardcoded key aliases removed
  • ✅ App-specific default aliases implemented
  • ✅ Key alias validation implemented
  • ✅ Secure storage for configuration
  • ✅ Cross-app key isolation verified
  • ✅ Migration path documented
  • ✅ Error handling implemented
  • ✅ Input validation added

Security Compliance and Auditing

Compliance Checklist

// Security compliance validation
const validateSecurityCompliance = async (keyAlias: string) => {
  const compliance = {
    keyExists: false,
    hardwareBacked: false,
    integrityValid: false,
    signatureWorking: false,
    userAuthRequired: false,
    securityLevel: 'unknown'
  };
  
  try {
    // Check key existence and attributes
    const attributes = await getKeyAttributes(keyAlias);
    if (attributes.exists) {
      compliance.keyExists = true;
      compliance.hardwareBacked = attributes.attributes?.hardwareBacked || false;
      compliance.userAuthRequired = attributes.attributes?.userAuthenticationRequired || false;
      compliance.securityLevel = attributes.attributes?.securityLevel || 'unknown';
    }
    
    // Validate key integrity
    const integrity = await validateKeyIntegrity(keyAlias);
    compliance.integrityValid = integrity.valid;
    compliance.signatureWorking = integrity.integrityChecks.signatureTestPassed;
    
    return compliance;
  } catch (error) {
    logger.error('Security compliance check failed', 'validateSecurityCompliance', error);
    return compliance;
  }
};

Audit Trail

// Generate security audit report
const generateSecurityAudit = async () => {
  const audit = {
    timestamp: new Date().toISOString(),
    keys: [],
    securityEvents: [],
    complianceStatus: 'unknown'
  };
  
  try {
    // Get all keys for audit
    const allKeys = await getAllKeys();
    
    for (const key of allKeys.keys) {
      const compliance = await validateSecurityCompliance(key.alias);
      audit.keys.push({
        alias: key.alias,
        compliance
      });
    }
    
    // Get security-related logs
    const logs = getLogs();
    audit.securityEvents = logs.filter(log => 
      log.level === 'error' || log.level === 'warn'
    );
    
    // Determine overall compliance status.
    // Note: every() returns true for an empty array, so treat "no keys" as its own status.
    const allCompliant = audit.keys.length > 0 && audit.keys.every(key => 
      key.compliance.keyExists && 
      key.compliance.hardwareBacked && 
      key.compliance.integrityValid &&
      key.compliance.signatureWorking &&
      key.compliance.userAuthRequired
    );
    
    audit.complianceStatus =
      audit.keys.length === 0
        ? 'no-keys-audited'
        : allCompliant
          ? 'compliant'
          : 'non-compliant';
    
    return audit;
  } catch (error) {
    logger.error('Security audit generation failed', 'generateSecurityAudit', error);
    return audit;
  }
};

Future Enhancements

  1. Key Alias Encryption: Encrypt key alias configuration in storage
  2. Automatic Key Rotation: Scheduled key rotation policies with integrity validation
  3. Advanced Audit Logging: Comprehensive security event logging with tamper detection
  4. Remote Security Policies: Support for remote security configuration and compliance policies
  5. Key Alias Templates: Predefined templates for industry-specific security requirements
  6. Biometric Template Protection: Enhanced protection for biometric template data
  7. Multi-Factor Key Validation: Combine multiple validation methods for enhanced security
  8. Real-time Security Monitoring: Live monitoring of key integrity and usage patterns