How a job interview led me to create Obscura - A password generator with real entropy
Introduction
During a recent interview for a Security Software Engineer position, I was given a challenge: create a secure password generator that takes into account real entropy. The interviewer wanted to see how I would approach the problem, and I was excited to take on the challenge.
At first, I thought it would be a simple task. I had used password generators before (with Math.Random stuffs), and I assumed it would be easy to create one that was secure. But the conversation quickly steered away from superficial implementations and into deeper, often overlooked topic: entropy—and how most so-called generators don't handle it properly.
The challenge
The interviewer insisted on entropy being measurable, adjustable, and predictable based on user choice. He wasn't looking for a simple tool that just throws random characters together; he wanted a password generator that would :
- quantify the information entropy of the generated password,
- adapt based on character pool constraints (like avoiding duplicates or special characters),
- prevent common patterns and sequences (e.g., "123" or "qwerty" or "abc"),
- and still generate passwords that are straightforward to use and robust against brute-force attacks.
That got me thinking about how most password generators out there are just glorified random characters generators.
My thought process: understanding entropy
I started by asking him about the definition of entropy in the context of password generation. He explained that entropy is a measure of uncertainty or randomness in a system. In the case of passwords, it refers to the unpredictability of the password itself. The more unpredictable a password is, the higher its entropy, and the more secure it is against brute-force attacks. (I said to my-self: come on man, you were supposed to make understand the concept not complicate it 🙁 ? because I don't know how to measure it, and I don't know how to calculate it).
After a few minutes of discussion, I was given the entropy formula which was quite simple but powerful:
E = log2(P^L)
Where:
- E is the entropy in bits
- P is the size of the character pool (e.g., 26 for lowercase letters, 10 for digits, etc.)
- L is the length of the password
And here is the example of the entropy calculation he gave me: For a password using 26 chars of lowercase letters and a length of 8:
E = log2(26^8) = 8 * log2(26) ≈ 37.6 bits
Strong passwords should have at least 80 bits of entropy, so I needed to ensure that my generator increases the entropy by using :
- A larger character pool (e.g., uppercase and lowercase letters, digits, special characters)
- A longer password length
- True randomness in character selection (not just pseudo-randomness)
After all these details, I understand two things:
- The larger the character pool and the longer the password, the stronger the password.
- Entropy is measured in bits, and more bits means more possible combinations.
But if we exclude similar characters, enforce uniqueness, and start with specific characters, the pool shrinks—reducing entropy. So we need to calculate entropy dynamically based on current user choice
The solution: Build a real password generator
I wrote the first version of the password generator in TypeScript, focused purely on logic. It supports options like:
- include/exclude uppercase, lowercase, digits, special characters
- exclude similar characters (e.g., "l" and "1", "O" and "0")
- avoid duplicates and sequential patterns
- generate multiple passwords at once
- and most importantly: realtime entropy calculation
Design approach
My implementation follows a modular approach centered around the core generatePassword
function. I defined a clear PasswordOptions
interface to make the code more readable and
type-safe.
export interface PasswordOptions {
length: number
includeLowercase: boolean
includeUppercase: boolean
includeDigits: boolean
includeSymbols: boolean
excludeSimilarCharacters: boolean
noDuplicateCharacters: boolean
noSequentialCharacters: boolean
beginWithLetter: boolean
quantity: number
}
Character Pool generation
The foundation of my code is the character pool selection. I defined constant of character sets and wrote a helper function to build customized pool based on user preferences:
function getCharacterPool(options: PasswordOptions): string {
let pool = ''
if (options.includeLowercase) pool += LOWERCASE
if (options.includeUppercase) pool += UPPERCASE
if (options.includeDigits) pool += DIGITS
if (options.includeSymbols) pool += SYMBOLS
if (options.excludeSimilarCharacters) {
pool = pool
.split('')
.filter((c) => !SIMILAR.includes(c))
.join('')
}
return pool
}
This approach gives users fine-grained control over the character set, which is directly impacting the entropy calculation.
Security considerations
I implemented several security features to ensure strong password generation:
- Cryptographically secure randomness: instead of using
Math.random()
, I used the Web Crypto APIcrypto.getRandomValues()
for true randomness:function getRandomChar(pool: string): string { const randomIndex = crypto.getRandomValues(new Uint32Array(1))[0] % pool.length return pool.charAt(randomIndex) }
- Sequential character detection: to prevent weak pattens like "abc" or "123", I added a
function to check for sequential characters:
function hasSequentialCharacters(password: string): boolean { for (let i = 0; i < password.length - 2; i++) { const a = password.charCodeAt(i) const b = password.charCodeAt(i + 1) const c = password.charCodeAt(i + 2) if ((b === a + 1 && c === b + 1) || (b === a - 1 && c === b - 1)) { return true } } return false }
- Input validation: The code prevents impossible combinations, like requiring unique characters
in a password longer than the available character pool:
Password generation logic
The core algorithm uses a retry mechanism to ensure all constraints are satisfied:
- generate teh first character (optionally a letter)
- build the rest of the password character by character
- validate against all constraints (uniqueness, sequential patterns, etc.)
- if any constraint fails, retry the entire password generation
This approach ensures that every generated password meets all security requirements.
Final output
The generatePassword
function ties everything together, enforcing sensible limits
(8–64 characters) and generating the requested quantity of passwords:
export function generatePasswords(options: PasswordOptions): string[] {
const safeLength = Math.max(8, Math.min(options.length, 64))
const passwords: string[] = []
for (let i = 0; i < options.quantity; i++) {
passwords.push(generatePassword({ ...options, length: safeLength }))
}
return passwords
}
This implementation balances security, usability, and performance, resulting in a robust password generator suitable for real-world applications.
Why this matter
I learned a lot from this interview challenge, and I realized that most password generators out there are just glorified random character generators. They don't take into account the real entropy. Good password hygiene is crucial—and we've all reused passwords at some point. Tools like Obscura give users confidence and control without compromising on privacy.
Here are few tips for better password security:
- always use unique passwords for each account
- prefer longer passwords (16+ characters)
- avoid predictable pattens or using personal information (like birthdays or names)
- use a password manager (Obscura is great for generating strong password to store there)
- never, and ever share your passwords with anyone
My journey experience
Unfortunately, my journey with the company didn't go further than the technical interview step. According to their feedback, they're specifically looking for someone with experience in software engineering and formal security certifications like CISSP, CLSSP, COMPTIA Security+, etc.
My background is primarily in software engineering in distributed systems, with a focus on Kubernetes and DevSecOps practices. While I have experience with security aspects like SAST/DAST code analysis, dependency vulnerability scanning, container security, IaC security reviews, and IAM/secrets management, they were looking for someone with a more specialized security background.
Despite not getting the position, I thoroughly enjoyed the interview process and learned a lot from the challenge. I decided to take the opportunity to build something useful out of it, and that's how I ended up creating Obscura.
Try it, Fork it, Share it or Contribute
You can try obscura at obscura.denisakp.me and check the code on GitHub. I would love to hear your feedback, suggestions, or contributions. This project is open-source, and I welcome any improvements or new features you might want to add.