passphera-core 0.8.1__tar.gz → 0.9.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: passphera-core
3
- Version: 0.8.1
3
+ Version: 0.9.0
4
4
  Summary: The core system of passphera project
5
5
  Home-page: https://github.com/passphera/core
6
6
  Author: Fathi Abdelmalek
7
7
  Author-email: abdelmalek.fathi.2001@gmail.com
8
- Classifier: Development Status :: 2 - Pre-Alpha
8
+ Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python
@@ -1,2 +1,2 @@
1
- from passphera_core.generator import PasswordGenerator
1
+ from passphera_core.entities.generator import Generator
2
2
  from passphera_core.exceptions import InvalidAlgorithmException
@@ -0,0 +1,125 @@
1
+ from dataclasses import dataclass, field
2
+ from datetime import datetime, timezone
3
+ from uuid import UUID, uuid4
4
+
5
+ from cryptography.fernet import Fernet
6
+
7
+ from cipherspy.cipher import *
8
+ from cipherspy.cipher.base_cipher import BaseCipherAlgorithm
9
+ from cipherspy.utilities import generate_salt, derive_key
10
+
11
+ from passphera_core import exceptions
12
+
13
+
14
+ @dataclass
15
+ class Password:
16
+ id: UUID = field(default_factory=uuid4)
17
+ user_id: UUID = field(default_factory=uuid4)
18
+ created_at: datetime = field(default=datetime.now(timezone.utc))
19
+ updated_at: datetime = field(default=datetime.now(timezone.utc))
20
+ context: str = field(default_factory=str)
21
+ text: str = field(default_factory=str)
22
+ password: str = field(default_factory=str)
23
+ salt: bytes = field(default_factory=bytes)
24
+
25
+ def encrypt(self) -> None:
26
+ self.salt = generate_salt()
27
+ key = derive_key(self.password, self.salt)
28
+ self.password = Fernet(key).encrypt(self.password.encode()).decode()
29
+
30
+ def decrypt(self) -> str:
31
+ key = derive_key(self.password, self.salt)
32
+ return Fernet(key).decrypt(self.password.encode()).decode()
33
+
34
+
35
+ @dataclass
36
+ class GeneratorConfig:
37
+ id: UUID = field(default_factory=uuid4)
38
+ generator_id: UUID = field(default_factory=uuid4)
39
+ shift: int = field(default=3)
40
+ multiplier: int = field(default=3)
41
+ key: str = field(default="hill")
42
+ algorithm: str = field(default="hill")
43
+ prefix: str = field(default="secret")
44
+ postfix: str = field(default="secret")
45
+ characters_replacements: dict[str, str] = field(default_factory=dict[str, str])
46
+ _cipher_registry: dict[str, BaseCipherAlgorithm] = field(default_factory=lambda: {
47
+ 'caesar': CaesarCipherAlgorithm,
48
+ 'affine': AffineCipherAlgorithm,
49
+ 'playfair': PlayfairCipherAlgorithm,
50
+ 'hill': HillCipherAlgorithm,
51
+ }, init=False)
52
+
53
+ def get_algorithm(self) -> BaseCipherAlgorithm:
54
+ """
55
+ Get the primary algorithm used to cipher the password
56
+ :return: BaseCipherAlgorithm: The primary algorithm used for the cipher
57
+ """
58
+ if self.algorithm.lower() not in self._cipher_registry:
59
+ raise exceptions.InvalidAlgorithmException(self.algorithm)
60
+ return self._cipher_registry[self.algorithm.lower()]
61
+
62
+ def replace_character(self, char: str, replacement: str) -> None:
63
+ """
64
+ Replace a character with another character or set of characters
65
+ Eg: pg.replace_character('a', '@1')
66
+ :param char: The character to be replaced
67
+ :param replacement: The (character|set of characters) to replace the first one
68
+ :return:
69
+ """
70
+ self.characters_replacements[char[0]] = replacement
71
+
72
+ def reset_character(self, char: str) -> None:
73
+ """
74
+ Reset a character to its original value (remove its replacement from characters_replacements)
75
+ :param char: The character to be reset to its original value
76
+ :return:
77
+ """
78
+ self.characters_replacements.pop(char, None)
79
+
80
+
81
+ @dataclass
82
+ class Generator:
83
+ id: UUID = field(default_factory=uuid4)
84
+ user_id: UUID = field(default_factory=uuid4)
85
+ created_at: datetime = field(default_factory=datetime.now)
86
+ updated_at: datetime = field(default_factory=datetime.now)
87
+ config: GeneratorConfig = field(default_factory=GeneratorConfig)
88
+
89
+ def apply_replacements(self, password: str) -> str:
90
+ """
91
+ Replace character from the ciphered password with character replacements from the generator configurations
92
+ :return: str: The new ciphered password after character replacements
93
+ """
94
+ translation_table = str.maketrans(self.config.characters_replacements)
95
+ return password.translate(translation_table)
96
+
97
+ def generate_password(self, text: str) -> str:
98
+ """
99
+ Generate a strong password string using the raw password (add another layer of encryption to it)
100
+ :return: str: The generated ciphered password
101
+ """
102
+ affine = AffineCipherAlgorithm(self.config.shift, self.config.multiplier)
103
+ intermediate = affine.encrypt(f"{self.config.prefix}{text}{self.config.postfix}")
104
+ main_algorithm = self.config.get_algorithm()
105
+ password = main_algorithm.encrypt(intermediate)
106
+ password = self.apply_replacements(password)
107
+ return ''.join(c.upper() if c in text else c for c in password)
108
+
109
+
110
+ @dataclass
111
+ class User:
112
+ id: UUID = field(default_factory=uuid4)
113
+ created_at: datetime = field(default_factory=datetime.now)
114
+ updated_at: datetime = field(default_factory=datetime.now)
115
+ username: str = field(default_factory=str)
116
+ email: str = field(default_factory=str)
117
+ password: str = field(default_factory=str)
118
+ generator: UUID = field(default_factory=UUID)
119
+ passwords: list[UUID] = field(default_factory=list[UUID])
120
+
121
+ def add_password(self, password_id: UUID) -> None:
122
+ self.passwords.append(password_id)
123
+
124
+ def delete_password(self, password_id: UUID) -> None:
125
+ self.passwords.remove(password_id)
@@ -0,0 +1,49 @@
1
+ from typing import Union, Literal
2
+
3
+ from passphera_core.entities import Password, Generator, User
4
+
5
+
6
+ class InvalidAlgorithmException(Exception):
7
+ def __init__(self, algorithm: str) -> None:
8
+ self.algorithm = algorithm
9
+ super().__init__(f"Invalid algorithm: '{algorithm}'")
10
+
11
+
12
+ class EntityNotFoundException(Exception):
13
+ def __init__(self, entity: Union[Password, Generator, User]) -> None:
14
+ self.entity = entity
15
+ entity_type = entity.__class__.__name__
16
+ super().__init__(f"{entity_type} not found")
17
+
18
+
19
+ class DuplicateEntityException(Exception):
20
+ def __init__(
21
+ self,
22
+ entity: Union[Password, User],
23
+ duplicate_field: Literal['context', 'email', 'username'] = None
24
+ ) -> None:
25
+ self.entity = entity
26
+ self.duplicate_field = duplicate_field
27
+ message = self._build_message(entity, duplicate_field)
28
+ super().__init__(message)
29
+
30
+ def _build_message(self, entity: Union[Password, User], duplicate_field: str | None) -> str:
31
+ if isinstance(entity, Password):
32
+ return self._build_password_message(entity)
33
+ elif isinstance(entity, User):
34
+ return self._build_user_message(entity, duplicate_field)
35
+ return "Duplicate entity detected"
36
+
37
+ @staticmethod
38
+ def _build_password_message(password: Password) -> str:
39
+ if hasattr(password, 'context') and password.context:
40
+ return f"Password for context '{password.context}' already exists"
41
+ return "Duplicate password detected"
42
+
43
+ @staticmethod
44
+ def _build_user_message(user: User, duplicate_field: str) -> str:
45
+ if duplicate_field == 'email':
46
+ return f"User with email '{user.email}' already exists"
47
+ elif duplicate_field == 'username':
48
+ return f"User with username '{user.username}' already exists"
49
+ return "Duplicate user detected"
@@ -0,0 +1,92 @@
1
+ from abc import ABC, abstractmethod
2
+ from uuid import UUID
3
+
4
+ from passphera_core.entities import Password, Generator, GeneratorConfig, User
5
+
6
+
7
+ class PasswordRepository(ABC):
8
+ @abstractmethod
9
+ def save(self, password: Password) -> None:
10
+ pass
11
+
12
+ @abstractmethod
13
+ def update(self, password: Password) -> None:
14
+ pass
15
+
16
+ @abstractmethod
17
+ def delete(self, password_id: UUID) -> None:
18
+ pass
19
+
20
+ @abstractmethod
21
+ def find_by_id(self, password_id: UUID) -> Password:
22
+ pass
23
+
24
+
25
+ class GeneratorRepository(ABC):
26
+ @abstractmethod
27
+ def save(self, generator: Generator) -> None:
28
+ pass
29
+
30
+ @abstractmethod
31
+ def update(self, generator: Generator) -> None:
32
+ pass
33
+
34
+ @abstractmethod
35
+ def delete(self, generator_id: UUID) -> None:
36
+ pass
37
+
38
+ @abstractmethod
39
+ def find_by_id(self, generator_id: UUID) -> Generator:
40
+ pass
41
+
42
+ @abstractmethod
43
+ def find_by_user_id(self, user_id: UUID) -> Generator:
44
+ pass
45
+
46
+
47
+ class GeneratorConfigRepository(ABC):
48
+ @abstractmethod
49
+ def save(self, generator_config: GeneratorConfig) -> None:
50
+ pass
51
+
52
+ @abstractmethod
53
+ def update(self, generator_config: GeneratorConfig) -> None:
54
+ pass
55
+
56
+ @abstractmethod
57
+ def delete(self, generator_config_id: UUID) -> None:
58
+ pass
59
+
60
+ @abstractmethod
61
+ def find_by_id(self, generator_config_id: UUID) -> GeneratorConfig:
62
+ pass
63
+
64
+ @abstractmethod
65
+ def find_by_generator_id(self, generator_id: UUID) -> GeneratorConfig:
66
+ pass
67
+
68
+
69
+ class UserRepository(ABC):
70
+ @abstractmethod
71
+ def save(self, user: User) -> None:
72
+ pass
73
+
74
+ @abstractmethod
75
+ def update(self, user: User) -> None:
76
+ pass
77
+
78
+ @abstractmethod
79
+ def delete(self, user_id: UUID) -> None:
80
+ pass
81
+
82
+ @abstractmethod
83
+ def find_by_id(self, user_id: UUID) -> User:
84
+ pass
85
+
86
+ @abstractmethod
87
+ def find_by_username(self, username: str) -> User:
88
+ pass
89
+
90
+ @abstractmethod
91
+ def find_by_email(self, email: str) -> User:
92
+ pass
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: passphera-core
3
- Version: 0.8.1
3
+ Version: 0.9.0
4
4
  Summary: The core system of passphera project
5
5
  Home-page: https://github.com/passphera/core
6
6
  Author: Fathi Abdelmalek
7
7
  Author-email: abdelmalek.fathi.2001@gmail.com
8
- Classifier: Development Status :: 2 - Pre-Alpha
8
+ Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python
@@ -1,8 +1,9 @@
1
1
  README.md
2
2
  setup.py
3
3
  passphera_core/__init__.py
4
+ passphera_core/entities.py
4
5
  passphera_core/exceptions.py
5
- passphera_core/generator.py
6
+ passphera_core/interfaces.py
6
7
  passphera_core.egg-info/PKG-INFO
7
8
  passphera_core.egg-info/SOURCES.txt
8
9
  passphera_core.egg-info/dependency_links.txt
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setup(
7
7
  name='passphera-core',
8
- version='0.8.1',
8
+ version='0.9.0',
9
9
  author='Fathi Abdelmalek',
10
10
  author_email='abdelmalek.fathi.2001@gmail.com',
11
11
  url='https://github.com/passphera/core',
@@ -16,7 +16,7 @@ setup(
16
16
  python_requires='>=3',
17
17
  install_requires=['cipherspy'],
18
18
  classifiers=[
19
- "Development Status :: 2 - Pre-Alpha",
19
+ "Development Status :: 3 - Alpha",
20
20
  "License :: OSI Approved :: MIT License",
21
21
  "Operating System :: OS Independent",
22
22
  "Programming Language :: Python",
@@ -1,3 +0,0 @@
1
- class InvalidAlgorithmException(Exception):
2
- def __init__(self, algorithm: str) -> None:
3
- super().__init__(f"Invalid algorithm name [{algorithm}]")
@@ -1,190 +0,0 @@
1
- from cipherspy.cipher import *
2
-
3
- from .exceptions import InvalidAlgorithmException
4
-
5
-
6
- class PasswordGenerator:
7
- """
8
- A strong password generator use multiple cipher algorithms to cipher a given plain text
9
- """
10
- def __init__(
11
- self,
12
- shift: int = 3,
13
- multiplier: int = 3,
14
- key: str = "hill",
15
- algorithm: str = 'hill',
16
- prefix: str = 'secret',
17
- postfix: str = 'secret',
18
- characters_replacements: dict = None,
19
- ):
20
- """
21
- :param shift: number of characters to shift each character (default 3)
22
- :param multiplier: number of characters to shift each character (default 3)
23
- :param key: cipher key string (default "secret")
24
- :param algorithm: main cipher algorithm name (default 'playfair')
25
- :param characters_replacements: replace characters with the given values (default {})
26
- :param text: plain text to be ciphered
27
- """
28
- if characters_replacements is None:
29
- characters_replacements = {}
30
- self._shift: int = shift
31
- self._multiplier: int = multiplier
32
- self._key: str = key
33
- self._algorithm_name: str = algorithm.lower()
34
- self._algorithm = self._set_algorithm()
35
- self._prefix: str = prefix
36
- self._postfix: str = postfix
37
- self._characters_replacements: dict = characters_replacements
38
-
39
- @property
40
- def shift(self) -> int:
41
- """
42
- Returns the shift value for the cipher algorithm
43
- Eg: ```shift = pg.shift```
44
- :return: int: The shift value for the cipher algorithm
45
- """
46
- return self._shift
47
-
48
- @shift.setter
49
- def shift(self, shift: int) -> None:
50
- """
51
- Sets the shift value for the cipher algorithm
52
- Eg: ```pg.shift = 3```
53
- :param shift: The shift value for the cipher algorithm
54
- :return:
55
- """
56
- self._shift = shift
57
-
58
- @property
59
- def multiplier(self) -> int:
60
- """
61
- Returns the multiplier value for the cipher algorithm
62
- Eg: ```multiplier = pg.multiplier```
63
- :return: int: The multiplier value for the cipher algorithm
64
- """
65
- return self._multiplier
66
-
67
- @multiplier.setter
68
- def multiplier(self, multiplier: int) -> None:
69
- """
70
- Sets the multiplier value for the cipher algorithm
71
- Eg: ```pg.multiplier = 3```
72
- :param multiplier: The multiplier value for the cipher algorithm
73
- :return:
74
- """
75
- self._multiplier = multiplier
76
-
77
- @property
78
- def key(self) -> str:
79
- """
80
- Returns the key string for the cipher algorithm
81
- Eg: ```key_str = pg.key_str```
82
- :return: str: The key string for the cipher algorithm
83
- """
84
- return self._key
85
-
86
- @key.setter
87
- def key(self, key: str) -> None:
88
- """
89
- Sets the key string for the cipher algorithm
90
- Eg: ```pg.key = 'secret key'```
91
- :param key: The key string for the cipher algorithm
92
- :return:
93
- """
94
- self._key = key
95
-
96
- @property
97
- def algorithm(self) -> str:
98
- """
99
- Returns the main cipher algorithm name
100
- Eg: ```algorithm = pg.algorithm```
101
- :return: str: The main cipher algorithm name
102
- """
103
- return self._algorithm_name
104
-
105
- @algorithm.setter
106
- def algorithm(self, algorithm: str) -> None:
107
- """
108
- Sets the main cipher algorithm
109
- Eg: ```pg.algorithm = 'playfair'```
110
- :param algorithm: The name of the main cipher algorithm
111
- :return:
112
- """
113
- self._algorithm_name = algorithm.lower()
114
- self._algorithm = self._set_algorithm()
115
-
116
- @property
117
- def characters_replacements(self) -> dict:
118
- """
119
- Returns the dictionary of the characters replacements
120
- Eg: ```print(pg.characters_replacements) # {'a': '@1', 'b': '#2'}```
121
- :return: dict: The dictionary of the characters replacements
122
- """
123
- return self._characters_replacements
124
-
125
- def _set_algorithm(self):
126
- """
127
- Return new instance of the used algorithm to the given one by it's name
128
- :return: new algorithm class
129
- """
130
- match self._algorithm_name:
131
- case 'caesar':
132
- return CaesarCipher(self._shift)
133
- case 'affine':
134
- return AffineCipher(self._shift, self._multiplier)
135
- case 'playfair':
136
- return PlayfairCipher(self._key)
137
- case 'hill':
138
- return HillCipher(self._key)
139
- case _:
140
- raise InvalidAlgorithmException(self._algorithm_name)
141
-
142
- def _update_algorithm_properties(self) -> None:
143
- """
144
- Update the main cipher algorithm
145
- """
146
- self._algorithm = self._set_algorithm()
147
-
148
- def replace_character(self, char: str, replacement: str) -> None:
149
- """
150
- Replace a character with another character or set of characters
151
- Eg: pg.replace_character('a', '@1')
152
- :param char: The character to be replaced
153
- :param replacement: The (character|set of characters) to replace the first one
154
- :return:
155
- """
156
- self._characters_replacements[char[0]] = replacement
157
-
158
- def reset_character(self, char: str) -> None:
159
- """
160
- Reset a character to it's original value (remove it's replacement from characters_replacements)
161
- :param char: The character to be reset to its original value
162
- :return:
163
- """
164
- if char in self._characters_replacements:
165
- del self._characters_replacements[char]
166
-
167
- def generate_raw_password(self, text: str) -> str:
168
- """
169
- Generate a raw password string using the given parameters
170
- :return: str: The generated raw password
171
- """
172
- self._update_algorithm_properties()
173
- return self._algorithm.encrypt(f"{self._prefix}{text}{self._postfix}")
174
-
175
- def generate_password(self, text: str) -> str:
176
- """
177
- Generate a strong password string using the raw password (add another layer of encryption to it)
178
- :return: str: The generated strong password
179
- """
180
- old_algorithm = self._algorithm_name
181
- self._algorithm_name = 'affine'
182
- password = self.generate_raw_password(text)
183
- self._algorithm_name = old_algorithm
184
- password = self.generate_raw_password(password)
185
- for char, replacement in self._characters_replacements.items():
186
- password = password.replace(char, replacement)
187
- for char in password:
188
- if char in text:
189
- password = password.replace(char, char.upper())
190
- return password
File without changes
File without changes