Coverage for kryptic_cypher/cypher.py: 88%

26 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2024-11-23 22:56 +0000

1""" 

2Module that contains the code for cyphers in our system. 

3 

4Defines both the interfaces as well as a registery decorator to register classes as cyphers. 

5 

6```python 

7from kryptic_cypher.cypher import register_cypher, Cypher, CypherWithKey 

8 

9@register_cypher 

10class MyCypher(Cypher): 

11 def encode(self, text: str) -> str: 

12 # Do my encryption here  

13 return text 

14  

15 def decode(self, text: str) -> str: 

16 # Do my decryption here 

17 return text 

18 

19 

20@register_cypher 

21class MyCypherWithKey(CypherWithKey): 

22 def encode(self, text: str, key: str) -> str: 

23 # Do my encryption here  

24 return text 

25  

26 def decode(self, text: str, key: str) -> str: 

27 # Do my decryption here 

28 return text 

29``` 

30 

31You can use this if you want to import and leverage any existing cyphers that have been registered with us. 

32""" 

33 

34from abc import ABC, abstractmethod 

35from logging import getLogger 

36from typing import IO 

37 

38 

39logger = getLogger(__name__) 

40 

41 

42class ValidationResult: 

43 def __init__(self, success: bool, messages: list[str]) -> None: 

44 self.success = success 

45 self.messages = messages 

46 

47 @classmethod 

48 def ok(cls): 

49 return cls(True, []) 

50 

51 @classmethod 

52 def fail(cls, messages: list[str]): 

53 return cls(False, messages) 

54 

55 

56class Cypher(ABC): 

57 """A Cypher that does not require a key to encode/decode.""" 

58 

59 @abstractmethod 

60 def encode(self, text: str) -> IO: # pragma: no cover 

61 """Encode the given text using the given key. 

62 

63 **Parameters** 

64 - text (str): The text to encode 

65 

66 **Returns** 

67 - IO: The encoded text 

68 """ 

69 pass 

70 

71 @abstractmethod 

72 def decode(self, text: str) -> IO: # pragma: no cover 

73 """Decode the given text using the given key. 

74 

75 **Parameters** 

76 - text (str): The text to decode 

77 

78 **Returns** 

79 - IO: The decoded text 

80 """ 

81 pass 

82 

83 

84class CypherWithKey(ABC): 

85 """A Cypher that requires a key to encode/decode.""" 

86 

87 @classmethod 

88 @abstractmethod 

89 def validate_key(cls, key: str) -> ValidationResult: # pragma: no cover 

90 """ 

91 Validate the key for the Cypher. If the key is invalid, it should return a ValidationResult 

92 with a message explaining why the key is invalid. 

93 

94 **Parameters** 

95 - key (str): The key to validate 

96 

97 **Returns** 

98 - ValidationResult: ValidationResult indicating if the key is valid or not 

99 """ 

100 pass 

101 

102 @abstractmethod 

103 def encode(self, text: str, key: str) -> IO: # pragma: no cover 

104 """Encode the given text using the given key. 

105 

106 **Parameters** 

107 - text (str): The text to encode 

108 - key (str): The key to use 

109 

110 **Returns** 

111 - IO: The encoded text 

112 """ 

113 pass 

114 

115 @abstractmethod 

116 def decode(self, text: str, key: str) -> IO: # pragma: no cover 

117 """Decode the given text using the given key. 

118 

119 **Parameters** 

120 - text (str): The text to decode 

121 - key (str): The key to use 

122 

123 **Returns** 

124 - IO: The decoded text 

125 """ 

126 pass 

127 

128 

129registered_cyphers: dict[str, Cypher | CypherWithKey] = {} 

130 

131 

132def register_cypher(cypher: type) -> None: 

133 """Class Decorator to register type as cypher. 

134 Must Implment Cypher or CypherWithKey and must have a constructor that takes no args. 

135 

136 **Parameters** 

137 - cypher (type): Type that implements Cypher or CypherWithKey 

138 

139 **Raises** 

140 - ValueError: If cypher does not implement Cypher or CypherWithKey 

141 """ 

142 if not issubclass(cypher, Cypher) and not issubclass(cypher, CypherWithKey): 

143 raise ValueError(f"Invalid cypher Class: {cypher}") 

144 name = cypher.__name__ 

145 if name in registered_cyphers: 

146 logger.warning(f"Duplicate cypher: {name}") 

147 return 

148 registered_cyphers[name] = cypher() 

149 logger.debug(f"Registered cypher: {name}")