Skip to main content

Integration Security - MLS Implementation

Overview

Security analysis of MLS integration points, including MLSCipherLayer, cascading cipher integration, module federation, and state persistence.


MLSCipherLayer Analysis

Location: src/crypto/CascadingCipher/layers/MLSCipherLayer.ts Purpose: Wrap MLS protocol for cascading cipher system

Security Review

export class MLSCipherLayer implements CipherLayer {
private mlsManager: MLSManager | null = null;
private groupId: string | null = null;

Assessment:

  • ✅ Clean interface design
  • ✅ Proper encapsulation
  • ⚠️ Stores manager/groupId in memory (state management)
  • ⚠️ No input validation at layer boundary

Encryption Flow Security

async encrypt(data: Uint8Array, keys: MLSKeys): Promise<EncryptedPayload> {
const base64Data = this.arrayBufferToBase64(data);
const envelope = await manager.encryptMessage(groupId, base64Data);

return {
ciphertext: envelope.ciphertext,
layerMetadata: {
processingTime: endTime - startTime, // ⚠️ Timing info
inputSize: data.length, // ⚠️ Size info
// ...
}
};
}

Issues Identified

  1. Base64 Encoding Overhead

    • MLS expects strings, binary data encoded as base64
    • 33% size increase
    • Performance impact
    • Assessment: Acceptable (MLS design constraint)
  2. Timing Information Exposure

    • processingTime included in metadata
    • Enables timing analysis
    • Severity: MEDIUM
    • Fix: Remove timing measurements
  3. Size Information Exposure

    • inputSize and outputSize in metadata
    • Correlation attacks possible
    • Severity: MEDIUM
    • Fix: Remove size information

Decryption Flow Security

async decrypt(payload: EncryptedPayload, keys: MLSKeys): Promise<Uint8Array> {
const envelope: MLSMessageEnvelope = {
groupId: payload.parameters.groupId,
ciphertext: payload.ciphertext,
timestamp: payload.parameters.timestamp,
};

const base64Data = await manager.decryptMessage(envelope);
return this.base64ToArrayBuffer(base64Data);
}

Analysis:

  • ✅ Proper envelope reconstruction
  • ✅ Correct parameter passing
  • ⚠️ No validation of payload structure
  • ⚠️ Trusts all parameters

Risk: If cascading cipher framework passes malicious payload, no validation


Cascading Cipher Integration

Layer Ordering Security

Typical Order:

  1. Noise Protocol (optional)
  2. MLS Layer ← Position critical
  3. PQ Kyber (optional)
  4. Traditional DH
  5. AES layers

Security Implications:

  • ✅ MLS after Noise = authenticated channel first
  • ✅ MLS provides group security
  • ⚠️ If MLS first = potential metadata leakage
  • ⚠️ Multiple encryption layers = performance cost

Recommendation: MLS should be second layer (after Noise/authentication)


Module Federation Security

Export Configuration

Location: Webpack config (module federation)

exposes: {
'./MLS/MLSManager': './src/crypto/MLS/MLSManager.tsx',
'./MLS/mlsCodec': './src/crypto/MLS/mlsCodec.ts'
}

Security Analysis:

Strengths:

  • ✅ Explicit exports (not wildcard)
  • ✅ Only public API exposed
  • ✅ Internal helpers not exported

Concerns:

  • ⚠️ mlsCodec exposed (serialization functions)
  • ⚠️ No version pinning in federation
  • ⚠️ Runtime module loading = potential injection

Cross-Origin Risks

Scenario: Remote module loaded from different origin

Risks:

  1. Supply Chain Attack

    • Compromised remote server
    • Malicious module injection
    • Backdoored crypto code
  2. Version Confusion

    • Incompatible versions loaded
    • Breaking changes undetected
    • State corruption

Mitigations:

  • ✅ Use HTTPS for remote modules
  • ⚠️ Implement Subresource Integrity (SRI)
  • ⚠️ Version pinning/semver enforcement
  • ⚠️ Content Security Policy (CSP)

State Persistence Security

Current Implementation

async exportGroupState(groupId: string): Promise<any> {
const exportData = {
groupId,
epoch: groupState.groupContext.epoch.toString(),
exported: Date.now(),
};
return exportData;
}

Analysis:

  • ⚠️ Minimal export (only metadata)
  • ⚠️ No actual state serialization
  • ⚠️ Cannot restore from export
  • ⚠️ Production note: "ts-mls ClientState contains non-serializable crypto keys"

Security Issues

  1. No Encryption of Persisted State

    • If state were serialized, would be plaintext
    • Contains secret keys
    • Risk: HIGH if persistence added
  2. No Integrity Protection

    • No HMAC or signature on export
    • State could be tampered with
    • Risk: MEDIUM
  3. No Version Control

    • Format could change between versions
    • No migration strategy
    • Risk: LOW (operational)

Recommendation: If adding persistence:

async exportGroupState(groupId: string, encryptionKey: Uint8Array): Promise<string> {
const state = serializeState(groupState);
const encrypted = await encryptWithAES(state, encryptionKey);
const hmac = await calculateHMAC(encrypted, encryptionKey);
return JSON.stringify({ version: 1, encrypted, hmac });
}

API Security

Constructor Injection

constructor(mlsManager?: MLSManager, groupId?: string) {
if (mlsManager) this.mlsManager = mlsManager;
if (groupId) this.groupId = groupId;
}

Analysis:

  • ✅ Optional dependency injection (good for testing)
  • ⚠️ No validation of injected manager
  • ⚠️ No validation of groupId format
  • ⚠️ Could inject malicious manager

Risk: LOW (application controls instantiation)


Key Management

Key Structure

export interface MLSKeys {
mlsManager: MLSManager;
groupId: string;
}

Analysis:

  • ✅ Simple interface
  • ✅ Manager reference (not keys directly)
  • ⚠️ No key rotation API at layer level
  • ⚠️ Trusts manager is initialized

Key Lifecycle

  1. Creation: Via MLSManager.initialize()
  2. Storage: In MLSManager (in-memory)
  3. Usage: Passed to encrypt/decrypt
  4. Rotation: Via MLSManager.updateKey()
  5. Destruction: Via MLSManager.destroy()

Security Properties:

  • ✅ Keys never exposed directly
  • ✅ Encapsulated in manager
  • ⚠️ No key derivation at layer level
  • ⚠️ No key backup/recovery

Error Handling at Integration Points

Layer Error Propagation

} catch (error) {
throw new CipherLayerError(
`MLS encryption failed: ${error.message}`, // ⚠️ Exposes internal error
this.name,
'encrypt',
error as Error
);
}

Issues:

  • ⚠️ Internal error messages bubble up
  • ⚠️ MLSManager errors exposed to layer above
  • ⚠️ Could leak implementation details

Fix:

} catch (error) {
throw new CipherLayerError(
`MLS encryption failed`, // Generic message
this.name,
'encrypt'
// Don't pass original error
);
}

Recommendations

Critical (P0)

  1. Remove timing measurements from metadata

    // Remove: processingTime, inputSize, outputSize
  2. Add input validation at layer boundary

    validateKeys(keys: MLSKeys): boolean {
    if (!keys.mlsManager) throw new Error('Invalid manager');
    if (!keys.groupId) throw new Error('Invalid groupId');
    // Add format validation
    }

High (P1)

  1. Implement secure state persistence

    • Encrypt exported state
    • Add integrity protection (HMAC)
    • Version control for format changes
  2. Add module federation security

    • Implement SRI for remote modules
    • Version pinning/enforcement
    • CSP headers
  3. Sanitize error messages

    • Don't expose internal errors
    • Generic messages at boundaries
    • Secure logging only

Medium (P2)

  1. Add layer-specific monitoring

    • Track encryption failures
    • Monitor for anomalies
    • Alert on suspicious patterns
  2. Implement key rotation at layer level

    • Expose rotation API
    • Handle rotation gracefully
    • Update references

Integration Test Recommendations

Security-Focused Integration Tests

describe('MLSCipherLayer Integration Security', () => {
test('should not leak timing information', async () => {
const layer = new MLSCipherLayer(manager, groupId);
const payload = await layer.encrypt(data, keys);

// Verify no timing in metadata
expect(payload.layerMetadata.processingTime).toBeUndefined();
});

test('should validate keys at boundary', async () => {
const layer = new MLSCipherLayer();
const invalidKeys = { mlsManager: null, groupId: null };

await expect(
layer.encrypt(data, invalidKeys)
).rejects.toThrow('Invalid keys');
});

test('should sanitize errors from MLSManager', async () => {
// Mock MLSManager to throw detailed error
const mockManager = {
encryptMessage: jest.fn().mockRejectedValue(
new Error('Internal: epoch 5 key not found at index 42')
)
};

const layer = new MLSCipherLayer(mockManager, 'test');

await expect(
layer.encrypt(data, keys)
).rejects.toThrow('MLS encryption failed'); // Generic

await expect(
layer.encrypt(data, keys)
).rejects.not.toThrow(/epoch.*index/); // No details
});
});

Conclusion

Integration Security: 🟡 MODERATE

Strengths:

  • ✅ Clean API design
  • ✅ Proper encapsulation
  • ✅ Keys managed centrally
  • ✅ Explicit module exports

Issues:

  • ⚠️ Timing/size information exposure
  • ⚠️ No input validation at boundaries
  • ⚠️ Error messages not sanitized
  • ⚠️ State persistence incomplete

Risk Level: 🟡 MEDIUM

Recommendation: Address P0 and P1 issues before production deployment.