Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/algorithms/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// OTP Algorithm Registry
// Central registry for all OTP algorithm versions

import { OTPAlgorithm, OTPVersion, DEFAULT_OTP_VERSION, LATEST_OTP_VERSION, OTP_VERSION_LABELS, OTP_VERSION_DESCRIPTIONS } from './types';
import otpV0Algorithm from './otp_v0';
import otpV1Algorithm from './otp_v1';

// Algorithm registry
const algorithms: Map<OTPVersion, OTPAlgorithm> = new Map([
['otp_v0', otpV0Algorithm],
['otp_v1', otpV1Algorithm],
]);

/**
* Get an OTP algorithm by version
*/
export function getAlgorithm(version: OTPVersion): OTPAlgorithm {
const algorithm = algorithms.get(version);
if (!algorithm) {
console.warn(`Unknown OTP version: ${version}, falling back to ${DEFAULT_OTP_VERSION}`);
const defaultAlgorithm = algorithms.get(DEFAULT_OTP_VERSION);
if (!defaultAlgorithm) {
throw new Error(`Default OTP algorithm (${DEFAULT_OTP_VERSION}) is not registered`);
}
return defaultAlgorithm;
}
return algorithm;
}

/**
* Get all available OTP versions
*/
export function getAvailableVersions(): OTPVersion[] {
return Array.from(algorithms.keys());
}

/**
* Check if a version is valid
*/
export function isValidVersion(version: string): version is OTPVersion {
return algorithms.has(version as OTPVersion);
}

/**
* Encrypt a message using the specified OTP version
*/
export function encryptWithVersion(
message: string,
rawKey: string,
offset: number,
version: OTPVersion = DEFAULT_OTP_VERSION
): string {
const algorithm = getAlgorithm(version);
const transformedKey = algorithm.transformKey(rawKey);
return algorithm.encrypt(message, transformedKey, offset);
}

/**
* Decrypt a message using the specified OTP version
*/
export function decryptWithVersion(
encrypted: string,
rawKey: string,
offset: number,
version: OTPVersion = DEFAULT_OTP_VERSION
): string {
const algorithm = getAlgorithm(version);
const transformedKey = algorithm.transformKey(rawKey);
return algorithm.decrypt(encrypted, transformedKey, offset);
}

/**
* Transform a raw key using the specified OTP version
*/
export function transformKeyWithVersion(
rawKey: string,
version: OTPVersion = DEFAULT_OTP_VERSION
): string {
const algorithm = getAlgorithm(version);
return algorithm.transformKey(rawKey);
}

// Re-export types and constants
export {
OTPAlgorithm,
OTPVersion,
DEFAULT_OTP_VERSION,
LATEST_OTP_VERSION,
OTP_VERSION_LABELS,
OTP_VERSION_DESCRIPTIONS,
};

// Export individual algorithms
export { otpV0Algorithm, otpV1Algorithm };
74 changes: 74 additions & 0 deletions src/algorithms/otp_v0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// OTP v0 Algorithm - Legacy Implementation
// This is the original OTP algorithm with basic key transformation

import { OTPAlgorithm, OTPVersion } from '../types';

/**
* Simple XOR-based encryption using key stream
*/
function xorEncrypt(message: string, key: string, offset: number): string {
const result: number[] = [];
const keyLength = key.length;

if (keyLength === 0) {
return message;
}

for (let i = 0; i < message.length; i++) {
const messageCharCode = message.charCodeAt(i);
const keyIndex = (offset + i) % keyLength;
const keyCharCode = key.charCodeAt(keyIndex);
result.push(messageCharCode ^ keyCharCode);
}

// Convert to base64 for safe transmission
return btoa(String.fromCharCode(...result));
}

/**
* Simple XOR-based decryption using key stream
*/
function xorDecrypt(encrypted: string, key: string, offset: number): string {
const keyLength = key.length;

if (keyLength === 0) {
return encrypted;
}

try {
// Decode from base64
const decoded = atob(encrypted);
const result: number[] = [];

for (let i = 0; i < decoded.length; i++) {
const encryptedCharCode = decoded.charCodeAt(i);
const keyIndex = (offset + i) % keyLength;
const keyCharCode = key.charCodeAt(keyIndex);
result.push(encryptedCharCode ^ keyCharCode);
}

return String.fromCharCode(...result);
} catch {
// If decryption fails, return an empty string to avoid leaking encryption format info
console.warn('Decryption failed in OTP v0');
return '';
}
}

/**
* Basic key transformation - just returns the key as-is
*/
function transformKey(rawKey: string): string {
return rawKey;
}

const otpV0Algorithm: OTPAlgorithm = {
version: 'otp_v0' as OTPVersion,
name: 'OTP v0 (Legacy)',
description: 'Original OTP encryption with basic key transformation',
transformKey,
encrypt: xorEncrypt,
decrypt: xorDecrypt,
};

export default otpV0Algorithm;
142 changes: 142 additions & 0 deletions src/algorithms/otp_v1/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// OTP v1 Algorithm - Enhanced Implementation
// This is the enhanced OTP algorithm with improved key expansion and security

import { OTPAlgorithm, OTPVersion } from '../types';

/**
* System message threshold - keys larger than this trigger a system message
*/
export const SYSTEM_MESSAGE_KEY_THRESHOLD = 256;

/**
* Simple hash function for key expansion
* Uses a basic mixing algorithm to expand short keys
*/
function hashExpand(input: string, targetLength: number): string {
if (input.length === 0) {
return '';
}

// Start with the input repeated to at least target length
let expanded = input;
while (expanded.length < targetLength) {
expanded += input;
}

// Mix the bytes using a simple algorithm
const result: number[] = [];
let accumulator = 0;

for (let i = 0; i < targetLength; i++) {
const charCode = expanded.charCodeAt(i % expanded.length);
accumulator = (accumulator + charCode * 31 + i * 17) % 256;
result.push(accumulator ^ charCode);
}

return String.fromCharCode(...result);
}

/**
* Enhanced key transformation with expansion
* Expands short keys to improve security
*/
function transformKey(rawKey: string): string {
if (rawKey.length === 0) {
return '';
}

// Minimum expanded key length for v1
const minLength = Math.max(rawKey.length * 4, 1024);
return hashExpand(rawKey, minLength);
}

/**
* Enhanced XOR encryption with key stream
*/
function encrypt(message: string, key: string, offset: number): string {
const result: number[] = [];
const keyLength = key.length;

if (keyLength === 0) {
return message;
}

for (let i = 0; i < message.length; i++) {
const messageCharCode = message.charCodeAt(i);
// Use modular arithmetic with offset for key position
const keyIndex = (offset + i) % keyLength;
const keyCharCode = key.charCodeAt(keyIndex);
// Additional mixing step for v1
const mixedKey = (keyCharCode + i) % 256;
result.push(messageCharCode ^ mixedKey);
}

// Convert to base64 for safe transmission
return btoa(String.fromCharCode(...result));
}

/**
* Enhanced XOR decryption with key stream
*/
function decrypt(encrypted: string, key: string, offset: number): string {
const keyLength = key.length;

if (keyLength === 0) {
return encrypted;
}

try {
// Decode from base64
const decoded = atob(encrypted);
const result: number[] = [];

for (let i = 0; i < decoded.length; i++) {
const encryptedCharCode = decoded.charCodeAt(i);
const keyIndex = (offset + i) % keyLength;
const keyCharCode = key.charCodeAt(keyIndex);
// Same mixing step as encryption for v1
const mixedKey = (keyCharCode + i) % 256;
result.push(encryptedCharCode ^ mixedKey);
}

return String.fromCharCode(...result);
} catch {
// If decryption fails, return an empty string to avoid leaking encryption format info
console.warn('Decryption failed in OTP v1');
return '';
}
}

/**
* Check if key length exceeds the system message threshold
*/
export function shouldShowSystemMessage(keyLength: number): boolean {
return keyLength > SYSTEM_MESSAGE_KEY_THRESHOLD;
}

/**
* Generate system message for large keys
* @param keyLength Length of the encryption key
* @param messageGenerator Optional custom message generator function
*/
export function generateLargeKeySystemMessage(
keyLength: number,
messageGenerator?: (keyLength: number) => string
): string {
if (messageGenerator) {
return messageGenerator(keyLength);
}
// Default message (Turkish) - use i18n in calling code for proper localization
return `<system>Şifreleme anahtarı ${keyLength} karakter uzunluğunda. Bu uzun bir anahtar olduğundan, güvenlik artırılmış durumda.</system>`;
}

const otpV1Algorithm: OTPAlgorithm = {
version: 'otp_v1' as OTPVersion,
name: 'OTP v1 (Enhanced)',
description: 'Enhanced OTP encryption with improved key expansion and security',
transformKey,
encrypt,
decrypt,
};

export default otpV1Algorithm;
71 changes: 71 additions & 0 deletions src/algorithms/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// OTP Algorithm Type Definitions

/**
* Supported OTP versions
*/
export type OTPVersion = 'otp_v0' | 'otp_v1';

/**
* Default OTP version for new DMs
*/
export const DEFAULT_OTP_VERSION: OTPVersion = 'otp_v1';

/**
* Latest OTP version available
*/
export const LATEST_OTP_VERSION: OTPVersion = 'otp_v1';

/**
* Human-readable labels for OTP versions
*/
export const OTP_VERSION_LABELS: Record<OTPVersion, string> = {
otp_v0: 'OTP v0 (Legacy)',
otp_v1: 'OTP v1 (Enhanced)',
};

/**
* Descriptions for OTP versions
*/
export const OTP_VERSION_DESCRIPTIONS: Record<OTPVersion, string> = {
otp_v0: 'Original OTP encryption with basic key transformation',
otp_v1: 'Enhanced OTP encryption with improved key expansion and security',
};

/**
* Interface for OTP algorithm implementations
*/
export interface OTPAlgorithm {
/** Version identifier */
version: OTPVersion;

/** Display name */
name: string;

/** Description of the algorithm */
description: string;

/**
* Transform a raw key into the algorithm's internal format
* @param rawKey The original key provided by user
* @returns Transformed key ready for encryption/decryption
*/
transformKey(rawKey: string): string;

/**
* Encrypt a message using the transformed key
* @param message Plaintext message to encrypt
* @param key Transformed key (output of transformKey)
* @param offset Current offset in the key stream
* @returns Encrypted message
*/
encrypt(message: string, key: string, offset: number): string;

/**
* Decrypt a message using the transformed key
* @param encrypted Encrypted message
* @param key Transformed key (output of transformKey)
* @param offset Current offset in the key stream
* @returns Decrypted plaintext message
*/
decrypt(encrypted: string, key: string, offset: number): string;
}
3 changes: 3 additions & 0 deletions src/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// i18n Index
export { translations, t, setLanguage, getLanguage, stripSystemTags } from './translations';
export type { Language, TranslationKey } from './translations';
Loading