@lanonasis/security-sdk
Official Security and Encryption SDK for the Lanonasis Ecosystem.
π― Overviewβ
The Security SDK provides enterprise-grade encryption and secure key management for all Lanonasis services. It implements the three-system security architecture with proper separation between external authentication, internal authentication, and secret storage.
Key Featuresβ
- AES-256-GCM Encryption - Industry-standard authenticated encryption for secret storage
- SHA-256 Hashing - Fast, secure hashing for external API keys
- PBKDF2-SHA512 - Slow, secure hashing for internal authentication
- Key Derivation - HKDF and PBKDF2 support for secure key generation
- Key Rotation - Seamless credential rotation without downtime
- API Key Generation - Cryptographically secure random keys
- Data Sanitization - Safe logging utilities for sensitive data
- TypeScript Support - Full type safety and IntelliSense
π¦ Installationβ
# NPM
npm install @lanonasis/security-sdk
# Yarn
yarn add @lanonasis/security-sdk
# Bun (recommended)
bun add @lanonasis/security-sdk
π Quick Startβ
Basic Encryptionβ
import { SecuritySDK } from '@lanonasis/security-sdk';
// Initialize with master key
const security = new SecuritySDK(process.env.ONASIS_MASTER_KEY);
// Encrypt sensitive data
const encrypted = security.encrypt(
{ stripe_key: 'sk_live_abc123' },
'user_123_stripe' // context for key derivation
);
// Store encrypted data in database
await db.insert({
encrypted_value: JSON.stringify(encrypted),
user_id: 'user_123'
});
// Later: Decrypt when needed
const decrypted = security.decryptJSON(encrypted, 'user_123_stripe');
console.log(decrypted.stripe_key); // 'sk_live_abc123'
API Key Hashingβ
import { hashApiKey, verifyApiKey } from '@lanonasis/security-sdk';
// Hash API key for storage (SHA-256)
const apiKey = 'lns_abc123xyz...';
const hash = hashApiKey(apiKey);
// Store hash in database
await db.insert({
key_hash: hash, // 64 hex characters
user_id: 'user_123'
});
// Later: Verify API key
const isValid = verifyApiKey(apiKey, hash);
if (isValid) {
// Grant access
}
Secure Token Generationβ
import { generateApiKey, generateToken } from '@lanonasis/security-sdk';
// Generate API key with prefix
const apiKey = generateApiKey(); // 'lns_...' (cryptographically secure)
// Generate random token
const token = generateToken(32); // 64 hex characters
π API Referenceβ
Encryption Methodsβ
encrypt(data, context, options?)β
Encrypts data using AES-256-GCM.
const encrypted = security.encrypt(
'sensitive-data',
'user_context',
{ keyId: 'optional-key-id' }
);
// Returns: { encrypted, iv, authTag, keyId }
decrypt(encryptedData, context)β
Decrypts previously encrypted data.
const decrypted = security.decrypt(encrypted, 'user_context');
// Returns: string (original data)
decryptJSON<T>(encryptedData, context)β
Decrypts and parses JSON data.
const decrypted = security.decryptJSON<{key: string}>(encrypted, 'user_context');
// Returns: typed object
Hashing Methodsβ
hashApiKey(apiKey: string): stringβ
Fast SHA-256 hashing for external API keys.
const hash = hashApiKey('lns_abc123'); // 64 hex characters
verifyApiKey(apiKey: string, hash: string): booleanβ
Constant-time verification of API keys.
const isValid = verifyApiKey('lns_abc123', storedHash);
hash(data, salt?)β
Secure PBKDF2 hashing for passwords/tokens.
const hashed = security.hash('password');
const isValid = security.verifyHash('password', hashed);
Utility Methodsβ
sanitize(data, showChars?)β
Sanitizes sensitive data for logging.
const sanitized = security.sanitize('sk_live_abc123def456');
// Returns: 'sk_l...f456'
rotate(oldEncrypted, context, newData?)β
Rotates encryption keys without downtime.
const newEncrypted = security.rotate(oldEncrypted, 'user_context');
generateApiKey(prefix?)β
Generates cryptographically secure API keys.
const apiKey = generateApiKey(); // 'lns_...'
generateToken(bytes?)β
Generates random tokens.
const token = generateToken(32); // 64 hex characters
π Security Architectureβ
Three-System Modelβ
The Security SDK implements three distinct cryptographic systems:
System 1: External API Keys (SHA-256)β
- Purpose: Authenticate public clients (Dashboard, CLI, SDK)
- Method: SHA-256 (one-way hashing)
- Use Case: Fast authentication, can't reverse
import { hashApiKey } from '@lanonasis/security-sdk';
const hash = hashApiKey('lns_user_key');
// Stored in: public.api_keys.key_hash
System 2: Internal API Keys (PBKDF2-SHA512)β
- Purpose: Service-to-service authentication
- Method: PBKDF2 with SHA-512 (100k iterations)
- Use Case: More secure, intentionally slow
const hash = security.hash('internal_service_key');
// Stored in: security_service.api_keys.key_hash
System 3: Stored Secrets (AES-256-GCM)β
- Purpose: Store third-party API keys/credentials
- Method: AES-256-GCM encryption (reversible)
- Use Case: Must retrieve original value
const encrypted = security.encrypt('sk_live_stripe_key', 'user_stripe');
// Stored in: security_service.stored_api_keys.encrypted_value
π οΈ Best Practicesβ
1. Master Key Managementβ
# Generate master key (do once)
import { SecuritySDK } from '@lanonasis/security-sdk';
const masterKey = SecuritySDK.generateMasterKey();
# Store in secure environment
ONASIS_MASTER_KEY=your_64_character_hex_key
# Use different keys per environment
ONASIS_MASTER_KEY_PROD=...
ONASIS_MASTER_KEY_STAGING=...
ONASIS_MASTER_KEY_DEV=...
2. Context Usageβ
Always use unique contexts for key derivation:
// β
GOOD: Unique per user + service
const context = `user_${userId}_${serviceName}`;
// β BAD: Reusing same context
const context = 'stripe'; // Don't do this!
3. Safe Loggingβ
import { sanitize } from '@lanonasis/security-sdk';
// β
GOOD: Sanitize before logging
logger.info('API key created', { key: sanitize(apiKey) });
// β BAD: Logging raw secrets
logger.info('API key created', { key: apiKey }); // Never do this!
4. Key Rotationβ
// Rotate credentials regularly
const rotationSchedule = async () => {
const oldEncrypted = await db.get('user_credentials');
const newEncrypted = security.rotate(oldEncrypted, context);
await db.update({ encrypted_value: newEncrypted });
};
π§ Integration Examplesβ
Express APIβ
import { verifyApiKey } from '@lanonasis/security-sdk';
app.use(async (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) return res.status(401).json({ error: 'Missing API key' });
const { data } = await db.query(
'SELECT key_hash FROM api_keys WHERE user_id = $1',
[req.user.id]
);
if (!verifyApiKey(apiKey, data.key_hash)) {
return res.status(401).json({ error: 'Invalid API key' });
}
next();
});
React Hookβ
import { useState, useEffect } from 'react';
import { sanitize } from '@lanonasis/security-sdk';
const useSecureStorage = () => {
const [keys, setKeys] = useState([]);
const storeKey = async (name: string, value: string) => {
// Send to backend for encryption
await fetch('/api/keys', {
method: 'POST',
body: JSON.stringify({ name, value }),
headers: { 'Content-Type': 'application/json' }
});
// Log sanitized version
console.log('Stored key:', sanitize(value));
};
return { keys, storeKey };
};
Nest.js Serviceβ
import { Injectable } from '@nestjs/common';
import { SecuritySDK, getSecuritySDK } from '@lanonasis/security-sdk';
@Injectable()
export class EncryptionService {
private security: SecuritySDK;
constructor() {
this.security = getSecuritySDK();
}
async encryptUserCredential(userId: string, credential: any) {
const encrypted = this.security.encrypt(
credential,
`user_${userId}_credentials`
);
await this.db.insert({
user_id: userId,
encrypted_value: JSON.stringify(encrypted)
});
}
}
π§ͺ Testingβ
import { SecuritySDK } from '@lanonasis/security-sdk';
describe('SecuritySDK', () => {
let security: SecuritySDK;
beforeEach(() => {
security = new SecuritySDK(SecuritySDK.generateMasterKey());
});
it('should encrypt and decrypt data', () => {
const data = { secret: 'test' };
const encrypted = security.encrypt(data, 'test-context');
const decrypted = security.decryptJSON(encrypted, 'test-context');
expect(decrypted).toEqual(data);
});
it('should hash and verify API keys', () => {
const apiKey = 'lns_test_key';
const hash = hashApiKey(apiKey);
expect(verifyApiKey(apiKey, hash)).toBe(true);
expect(verifyApiKey('wrong_key', hash)).toBe(false);
});
});
π Related Documentationβ
π Troubleshootingβ
"Invalid encrypted data format"β
Ensure encrypted data has all required fields:
const encrypted = {
encrypted: '...', // Required
iv: '...', // Required
authTag: '...', // Required
keyId: '...' // Optional
};
"Master key not set"β
Set the ONASIS_MASTER_KEY environment variable:
export ONASIS_MASTER_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
"Decryption failed"β
Ensure you're using the same context for encrypt/decrypt:
// β
GOOD
const encrypted = security.encrypt(data, 'user_123');
const decrypted = security.decrypt(encrypted, 'user_123'); // Same context
// β BAD
const encrypted = security.encrypt(data, 'user_123');
const decrypted = security.decrypt(encrypted, 'user_456'); // Wrong context!
π¦ Package Detailsβ
π‘ Supportβ
- Documentation: docs.lanonasis.com/sdks/security-sdk
- Issues: GitHub Issues
- Email: security@lanonasis.com
- Discord: Join our community