Skip to content
Merged
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
19 changes: 19 additions & 0 deletions Src/Password_Generator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.15)
project(Password_Generator LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(pwg
src/Random.cpp
src/PasswordGenerator.cpp
src/PasswordStrengthEvaluator.cpp
)

target_include_directories(pwg PUBLIC include)

add_executable(password_generator
src/main.cpp
)

target_link_libraries(password_generator PRIVATE pwg)
58 changes: 58 additions & 0 deletions Src/Password_Generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,61 @@ The project is designed to be **modular, maintainable, and easy to extend**, fol
1. Clone the repository:
```bash
git clone https://github.com/your-username/your-repo.git
```
2. Navigate to the Password Generator project directory:
```bash
cd your-repo/Src/Password_Generator
```
3. Build with CMake:
```bash
mkdir -p build && cd build
cmake ..
cmake --build .
```
4. Run:
```bash
./password_generator
```

Alternative: build without CMake (ensure you are in Src/Password_Generator)
```bash
g++ -std=cpp17 -Iinclude src/*.cpp -o password_generator
./password_generator
```

## Usage
The app runs in interactive CLI mode.

- Option 1 (Preset): Choose weak, medium, or strong, then how many passwords to generate.
- 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.

Example session
```
C++ Password Generator
1) Preset (weak/medium/strong)
2) Custom
Choose option [1/2]: 1
Preset [weak/medium/strong]: strong
How many passwords to generate [1]: 3
[1] 2Gz!tq@K3wqM)h^L (entropy: 95 bits, strength: strong)
[2] Lz5{nZV8pQ%g=J7! (entropy: 95 bits, strength: strong)
[3] 7Y@kR`mT2s&Qh9)C (entropy: 95 bits, strength: strong)
```

Options explained
- length: number of characters in each password.
- includeLowercase/includeUppercase/includeDigits/includeSymbols: toggle character sets.
- requireEachSelectedType: if enabled, guarantees at least one character from each selected set and shuffles the result.
- count: how many passwords to output.

## Design overview (OOP + SOLID)
- PasswordGenerator: single responsibility to generate passwords; depends on abstractions.
- IRandom + MTRandom: randomness abstraction and mt19937-based implementation (Dependency Inversion).
- UserPreferences: clean container for user options and presets (Weak/Medium/Strong).
- IPasswordStrengthEvaluator + EntropyPasswordStrengthEvaluator: pluggable strength evaluation.
- CharacterSets: centralized immutable character sets.
- Dependency Injection: Generator receives IRandom; evaluator is independent and reusable.

## Notes
- Requires a C++17-compatible compiler and CMake 3.15+ (if using CMake).
- Input validated with helpful error messages (e.g., length must be > 0, at least one character set required).
7 changes: 7 additions & 0 deletions Src/Password_Generator/include/CharacterSets.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
namespace charset {
inline constexpr const char* LOWER = "abcdefghijklmnopqrstuvwxyz";
inline constexpr const char* UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
inline constexpr const char* DIGITS = "0123456789";
inline constexpr const char* SYMBOLS= "!@#$%^&*()-_=+[]{}|;:,.<>?/~`";
}
11 changes: 11 additions & 0 deletions Src/Password_Generator/include/IPasswordStrengthEvaluator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once
#include <string>

enum class PasswordStrength { Weak, Medium, Strong };

struct IPasswordStrengthEvaluator {
virtual ~IPasswordStrengthEvaluator() = default;
virtual double entropyBits(const std::string& password) const = 0;
virtual PasswordStrength classify(double entropyBits) const = 0;
virtual const char* toString(PasswordStrength s) const = 0;
};
7 changes: 7 additions & 0 deletions Src/Password_Generator/include/IRandom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <cstddef>

struct IRandom {
virtual ~IRandom() = default;
virtual std::size_t nextIndex(std::size_t upperExclusive) = 0; // [0, upperExclusive)
};
22 changes: 22 additions & 0 deletions Src/Password_Generator/include/PasswordGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include "IRandom.h"
#include "UserPreferences.h"
#include <string>
#include <vector>

class PasswordGenerator {
public:
explicit PasswordGenerator(IRandom& rng);

std::string generate(const UserPreferences& prefs);
std::vector<std::string> generateMany(const UserPreferences& prefs);

private:
IRandom& rng_;

std::string buildAlphabet(const UserPreferences& prefs) const;
void ensureValid(const UserPreferences& prefs) const;
char randomFrom(const std::string& alphabet);
void fisherYatesShuffle(std::string& s);
std::string generateWithEachType(const UserPreferences& prefs, const std::string& alphabet);
};
9 changes: 9 additions & 0 deletions Src/Password_Generator/include/PasswordStrengthEvaluator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once
#include "IPasswordStrengthEvaluator.h"

class EntropyPasswordStrengthEvaluator final : public IPasswordStrengthEvaluator {
public:
double entropyBits(const std::string& password) const override;
PasswordStrength classify(double entropyBits) const override;
const char* toString(PasswordStrength s) const override;
};
12 changes: 12 additions & 0 deletions Src/Password_Generator/include/Random.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once
#include "IRandom.h"
#include <random>

class MTRandom final : public IRandom {
public:
explicit MTRandom(unsigned int seed = std::random_device{}());
std::size_t nextIndex(std::size_t upperExclusive) override;

private:
std::mt19937 engine_;
};
22 changes: 22 additions & 0 deletions Src/Password_Generator/include/UserPreferences.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once
#include <cstddef>

struct UserPreferences {
std::size_t length{12};
bool includeLowercase{true};
bool includeUppercase{true};
bool includeDigits{true};
bool includeSymbols{false};
std::size_t count{1};
bool requireEachSelectedType{true};

static UserPreferences Weak() {
return UserPreferences{8, true, false, false, false, 1, false};
}
static UserPreferences Medium() {
return UserPreferences{12, true, true, true, false, 1, true};
}
static UserPreferences Strong() {
return UserPreferences{16, true, true, true, true, 1, true};
}
};
80 changes: 80 additions & 0 deletions Src/Password_Generator/src/PasswordGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include "PasswordGenerator.h"
#include "CharacterSets.h"
#include <stdexcept>

PasswordGenerator::PasswordGenerator(IRandom& rng) : rng_{rng} {}

void PasswordGenerator::ensureValid(const UserPreferences& prefs) const {
if (prefs.length == 0) throw std::invalid_argument("Password length must be > 0");
if (!prefs.includeLowercase && !prefs.includeUppercase && !prefs.includeDigits && !prefs.includeSymbols) {
throw std::invalid_argument("At least one character set must be enabled");
}
std::size_t types = (prefs.includeLowercase?1:0) + (prefs.includeUppercase?1:0) +
(prefs.includeDigits?1:0) + (prefs.includeSymbols?1:0);
if (prefs.requireEachSelectedType && prefs.length < types) {
throw std::invalid_argument("Length too short to include at least one of each selected type");
}
}

std::string PasswordGenerator::buildAlphabet(const UserPreferences& prefs) const {
std::string alphabet;
if (prefs.includeLowercase) alphabet += charset::LOWER;
if (prefs.includeUppercase) alphabet += charset::UPPER;
if (prefs.includeDigits) alphabet += charset::DIGITS;
if (prefs.includeSymbols) alphabet += charset::SYMBOLS;
return alphabet;
}

char PasswordGenerator::randomFrom(const std::string& alphabet) {
return alphabet[rng_.nextIndex(alphabet.size())];
}

void PasswordGenerator::fisherYatesShuffle(std::string& s) {
if (s.size() <= 1) return;
for (std::size_t i = s.size() - 1; i > 0; --i) {
std::size_t j = rng_.nextIndex(i + 1);
std::swap(s[i], s[j]);
}
}

std::string PasswordGenerator::generateWithEachType(const UserPreferences& prefs, const std::string& alphabet) {
std::string pw;
pw.reserve(prefs.length);

// Ensure at least one char from each selected set
if (prefs.includeLowercase) pw.push_back(randomFrom(charset::LOWER));
if (prefs.includeUppercase) pw.push_back(randomFrom(charset::UPPER));
if (prefs.includeDigits) pw.push_back(randomFrom(charset::DIGITS));
if (prefs.includeSymbols) pw.push_back(randomFrom(charset::SYMBOLS));

// Fill remaining with random chars from the combined alphabet
while (pw.size() < prefs.length) {
pw.push_back(randomFrom(alphabet));
}

fisherYatesShuffle(pw);
return pw;
}

std::string PasswordGenerator::generate(const UserPreferences& prefs) {
ensureValid(prefs);
const std::string alphabet = buildAlphabet(prefs);
if (prefs.requireEachSelectedType) return generateWithEachType(prefs, alphabet);

std::string pw;
pw.reserve(prefs.length);
for (std::size_t i = 0; i < prefs.length; ++i) {
pw.push_back(randomFrom(alphabet));
}
return pw;
}

std::vector<std::string> PasswordGenerator::generateMany(const UserPreferences& prefs) {
if (prefs.count == 0) throw std::invalid_argument("Count must be > 0");
std::vector<std::string> out;
out.reserve(prefs.count);
for (std::size_t i = 0; i < prefs.count; ++i) {
out.push_back(generate(prefs));
}
return out;
}
41 changes: 41 additions & 0 deletions Src/Password_Generator/src/PasswordStrengthEvaluator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "PasswordStrengthEvaluator.h"
#include "CharacterSets.h"
#include <cmath>
#include <string_view>

static bool containsAny(std::string_view s, std::string_view bag) {
for (char c : s) {
for (char b : bag) {
if (c == b) return true;
}
}
return false;
}

double EntropyPasswordStrengthEvaluator::entropyBits(const std::string& password) const {
using namespace std::literals;
std::string_view pw{password};
double alphabetSize = 0.0;
if (containsAny(pw, charset::LOWER)) alphabetSize += std::char_traits<char>::length(charset::LOWER);
if (containsAny(pw, charset::UPPER)) alphabetSize += std::char_traits<char>::length(charset::UPPER);
if (containsAny(pw, charset::DIGITS)) alphabetSize += std::char_traits<char>::length(charset::DIGITS);
if (containsAny(pw, charset::SYMBOLS))alphabetSize += std::char_traits<char>::length(charset::SYMBOLS);

if (alphabetSize <= 1.0) return 0.0;
return static_cast<double>(password.size()) * std::log2(alphabetSize);
}

PasswordStrength EntropyPasswordStrengthEvaluator::classify(double e) const {
if (e < 36.0) return PasswordStrength::Weak;
if (e < 60.0) return PasswordStrength::Medium;
return PasswordStrength::Strong;
}

const char* EntropyPasswordStrengthEvaluator::toString(PasswordStrength s) const {
switch (s) {
case PasswordStrength::Weak: return "weak";
case PasswordStrength::Medium: return "medium";
case PasswordStrength::Strong: return "strong";
}
return "unknown";
}
10 changes: 10 additions & 0 deletions Src/Password_Generator/src/Random.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "Random.h"
#include <stdexcept>

MTRandom::MTRandom(unsigned int seed) : engine_{seed} {}

std::size_t MTRandom::nextIndex(std::size_t upperExclusive) {
if (upperExclusive == 0) throw std::invalid_argument("upperExclusive must be > 0");
std::uniform_int_distribution<std::size_t> dist(0, upperExclusive - 1);
return dist(engine_);
}
Loading
Loading