Skip to content

Commit 4710011

Browse files
authored
Merge pull request #5 from sanghaibiraj/main
feat: password generator
2 parents d38c44d + 951e03c commit 4710011

File tree

13 files changed

+395
-0
lines changed

13 files changed

+395
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
project(Password_Generator LANGUAGES CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
add_library(pwg
8+
src/Random.cpp
9+
src/PasswordGenerator.cpp
10+
src/PasswordStrengthEvaluator.cpp
11+
)
12+
13+
target_include_directories(pwg PUBLIC include)
14+
15+
add_executable(password_generator
16+
src/main.cpp
17+
)
18+
19+
target_link_libraries(password_generator PRIVATE pwg)

Src/Password_Generator/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,61 @@ The project is designed to be **modular, maintainable, and easy to extend**, fol
3232
1. Clone the repository:
3333
```bash
3434
git clone https://github.com/your-username/your-repo.git
35+
```
36+
2. Navigate to the Password Generator project directory:
37+
```bash
38+
cd your-repo/Src/Password_Generator
39+
```
40+
3. Build with CMake:
41+
```bash
42+
mkdir -p build && cd build
43+
cmake ..
44+
cmake --build .
45+
```
46+
4. Run:
47+
```bash
48+
./password_generator
49+
```
50+
51+
Alternative: build without CMake (ensure you are in Src/Password_Generator)
52+
```bash
53+
g++ -std=cpp17 -Iinclude src/*.cpp -o password_generator
54+
./password_generator
55+
```
56+
57+
## Usage
58+
The app runs in interactive CLI mode.
59+
60+
- Option 1 (Preset): Choose weak, medium, or strong, then how many passwords to generate.
61+
- Option 2 (Custom): Choose length, inclusion of lowercase/uppercase/digits/symbols, whether to require at least one of each selected type, and how many passwords.
62+
63+
Example session
64+
```
65+
C++ Password Generator
66+
1) Preset (weak/medium/strong)
67+
2) Custom
68+
Choose option [1/2]: 1
69+
Preset [weak/medium/strong]: strong
70+
How many passwords to generate [1]: 3
71+
[1] 2Gz!tq@K3wqM)h^L (entropy: 95 bits, strength: strong)
72+
[2] Lz5{nZV8pQ%g=J7! (entropy: 95 bits, strength: strong)
73+
[3] 7Y@kR`mT2s&Qh9)C (entropy: 95 bits, strength: strong)
74+
```
75+
76+
Options explained
77+
- length: number of characters in each password.
78+
- includeLowercase/includeUppercase/includeDigits/includeSymbols: toggle character sets.
79+
- requireEachSelectedType: if enabled, guarantees at least one character from each selected set and shuffles the result.
80+
- count: how many passwords to output.
81+
82+
## Design overview (OOP + SOLID)
83+
- PasswordGenerator: single responsibility to generate passwords; depends on abstractions.
84+
- IRandom + MTRandom: randomness abstraction and mt19937-based implementation (Dependency Inversion).
85+
- UserPreferences: clean container for user options and presets (Weak/Medium/Strong).
86+
- IPasswordStrengthEvaluator + EntropyPasswordStrengthEvaluator: pluggable strength evaluation.
87+
- CharacterSets: centralized immutable character sets.
88+
- Dependency Injection: Generator receives IRandom; evaluator is independent and reusable.
89+
90+
## Notes
91+
- Requires a C++17-compatible compiler and CMake 3.15+ (if using CMake).
92+
- Input validated with helpful error messages (e.g., length must be > 0, at least one character set required).
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
namespace charset {
3+
inline constexpr const char* LOWER = "abcdefghijklmnopqrstuvwxyz";
4+
inline constexpr const char* UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
5+
inline constexpr const char* DIGITS = "0123456789";
6+
inline constexpr const char* SYMBOLS= "!@#$%^&*()-_=+[]{}|;:,.<>?/~`";
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
#include <string>
3+
4+
enum class PasswordStrength { Weak, Medium, Strong };
5+
6+
struct IPasswordStrengthEvaluator {
7+
virtual ~IPasswordStrengthEvaluator() = default;
8+
virtual double entropyBits(const std::string& password) const = 0;
9+
virtual PasswordStrength classify(double entropyBits) const = 0;
10+
virtual const char* toString(PasswordStrength s) const = 0;
11+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#pragma once
2+
#include <cstddef>
3+
4+
struct IRandom {
5+
virtual ~IRandom() = default;
6+
virtual std::size_t nextIndex(std::size_t upperExclusive) = 0; // [0, upperExclusive)
7+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
#include "IRandom.h"
3+
#include "UserPreferences.h"
4+
#include <string>
5+
#include <vector>
6+
7+
class PasswordGenerator {
8+
public:
9+
explicit PasswordGenerator(IRandom& rng);
10+
11+
std::string generate(const UserPreferences& prefs);
12+
std::vector<std::string> generateMany(const UserPreferences& prefs);
13+
14+
private:
15+
IRandom& rng_;
16+
17+
std::string buildAlphabet(const UserPreferences& prefs) const;
18+
void ensureValid(const UserPreferences& prefs) const;
19+
char randomFrom(const std::string& alphabet);
20+
void fisherYatesShuffle(std::string& s);
21+
std::string generateWithEachType(const UserPreferences& prefs, const std::string& alphabet);
22+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
#include "IPasswordStrengthEvaluator.h"
3+
4+
class EntropyPasswordStrengthEvaluator final : public IPasswordStrengthEvaluator {
5+
public:
6+
double entropyBits(const std::string& password) const override;
7+
PasswordStrength classify(double entropyBits) const override;
8+
const char* toString(PasswordStrength s) const override;
9+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
#include "IRandom.h"
3+
#include <random>
4+
5+
class MTRandom final : public IRandom {
6+
public:
7+
explicit MTRandom(unsigned int seed = std::random_device{}());
8+
std::size_t nextIndex(std::size_t upperExclusive) override;
9+
10+
private:
11+
std::mt19937 engine_;
12+
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
#include <cstddef>
3+
4+
struct UserPreferences {
5+
std::size_t length{12};
6+
bool includeLowercase{true};
7+
bool includeUppercase{true};
8+
bool includeDigits{true};
9+
bool includeSymbols{false};
10+
std::size_t count{1};
11+
bool requireEachSelectedType{true};
12+
13+
static UserPreferences Weak() {
14+
return UserPreferences{8, true, false, false, false, 1, false};
15+
}
16+
static UserPreferences Medium() {
17+
return UserPreferences{12, true, true, true, false, 1, true};
18+
}
19+
static UserPreferences Strong() {
20+
return UserPreferences{16, true, true, true, true, 1, true};
21+
}
22+
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "PasswordGenerator.h"
2+
#include "CharacterSets.h"
3+
#include <stdexcept>
4+
5+
PasswordGenerator::PasswordGenerator(IRandom& rng) : rng_{rng} {}
6+
7+
void PasswordGenerator::ensureValid(const UserPreferences& prefs) const {
8+
if (prefs.length == 0) throw std::invalid_argument("Password length must be > 0");
9+
if (!prefs.includeLowercase && !prefs.includeUppercase && !prefs.includeDigits && !prefs.includeSymbols) {
10+
throw std::invalid_argument("At least one character set must be enabled");
11+
}
12+
std::size_t types = (prefs.includeLowercase?1:0) + (prefs.includeUppercase?1:0) +
13+
(prefs.includeDigits?1:0) + (prefs.includeSymbols?1:0);
14+
if (prefs.requireEachSelectedType && prefs.length < types) {
15+
throw std::invalid_argument("Length too short to include at least one of each selected type");
16+
}
17+
}
18+
19+
std::string PasswordGenerator::buildAlphabet(const UserPreferences& prefs) const {
20+
std::string alphabet;
21+
if (prefs.includeLowercase) alphabet += charset::LOWER;
22+
if (prefs.includeUppercase) alphabet += charset::UPPER;
23+
if (prefs.includeDigits) alphabet += charset::DIGITS;
24+
if (prefs.includeSymbols) alphabet += charset::SYMBOLS;
25+
return alphabet;
26+
}
27+
28+
char PasswordGenerator::randomFrom(const std::string& alphabet) {
29+
return alphabet[rng_.nextIndex(alphabet.size())];
30+
}
31+
32+
void PasswordGenerator::fisherYatesShuffle(std::string& s) {
33+
if (s.size() <= 1) return;
34+
for (std::size_t i = s.size() - 1; i > 0; --i) {
35+
std::size_t j = rng_.nextIndex(i + 1);
36+
std::swap(s[i], s[j]);
37+
}
38+
}
39+
40+
std::string PasswordGenerator::generateWithEachType(const UserPreferences& prefs, const std::string& alphabet) {
41+
std::string pw;
42+
pw.reserve(prefs.length);
43+
44+
// Ensure at least one char from each selected set
45+
if (prefs.includeLowercase) pw.push_back(randomFrom(charset::LOWER));
46+
if (prefs.includeUppercase) pw.push_back(randomFrom(charset::UPPER));
47+
if (prefs.includeDigits) pw.push_back(randomFrom(charset::DIGITS));
48+
if (prefs.includeSymbols) pw.push_back(randomFrom(charset::SYMBOLS));
49+
50+
// Fill remaining with random chars from the combined alphabet
51+
while (pw.size() < prefs.length) {
52+
pw.push_back(randomFrom(alphabet));
53+
}
54+
55+
fisherYatesShuffle(pw);
56+
return pw;
57+
}
58+
59+
std::string PasswordGenerator::generate(const UserPreferences& prefs) {
60+
ensureValid(prefs);
61+
const std::string alphabet = buildAlphabet(prefs);
62+
if (prefs.requireEachSelectedType) return generateWithEachType(prefs, alphabet);
63+
64+
std::string pw;
65+
pw.reserve(prefs.length);
66+
for (std::size_t i = 0; i < prefs.length; ++i) {
67+
pw.push_back(randomFrom(alphabet));
68+
}
69+
return pw;
70+
}
71+
72+
std::vector<std::string> PasswordGenerator::generateMany(const UserPreferences& prefs) {
73+
if (prefs.count == 0) throw std::invalid_argument("Count must be > 0");
74+
std::vector<std::string> out;
75+
out.reserve(prefs.count);
76+
for (std::size_t i = 0; i < prefs.count; ++i) {
77+
out.push_back(generate(prefs));
78+
}
79+
return out;
80+
}

0 commit comments

Comments
 (0)