Coverage for src/lexigram/auth/mfa/totp_vectors.py: 0%

31 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 12:26 +0800

1"""RFC 6238 TOTP test vectors for testing without pyotp. 

2 

3These test vectors are from the RFC 6238 specification for Time-based 

4One-Time Passwords. They can be used to verify TOTP implementation 

5without requiring the pyotp library. 

6 

7Reference: https://datatracker.ietf.org/doc/html/rfc6238 

8Reference: https://datatracker.ietf.org/doc/html/rfc6238#appendix-B 

9""" 

10 

11from __future__ import annotations 

12 

13from dataclasses import dataclass 

14 

15 

16@dataclass(frozen=True) 

17class TOTPTestVector: 

18 """A TOTP test vector from RFC 6238.""" 

19 

20 secret: str 

21 time_step: int 

22 digits: int 

23 algorithm: str 

24 time: int 

25 expected_otp: str 

26 

27 

28class TOTPTestVectors: 

29 """RFC 6238 test vectors for TOTP verification. 

30 

31 These vectors can be used to test TOTP implementations without 

32 relying on external libraries like pyotp. 

33 

34 Example: 

35 >>> from lexigram.logging import get_logger 

36 >>> logger = get_logger(__name__) 

37 >>> vector = TOTPTestVectors.SHA1_8DIGITS_1 

38 >>> logger.info("totp_vector", expected_otp=vector.expected_otp) 

39 >>> # Output: expected_otp: 287082 

40 """ 

41 

42 SHA1_8DIGITS_1 = TOTPTestVector( 

43 secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", # Base32 encoded 

44 time_step=30, 

45 digits=8, 

46 algorithm="SHA1", 

47 time=59, 

48 expected_otp="287082", 

49 ) 

50 

51 SHA1_8DIGITS_2 = TOTPTestVector( 

52 secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 

53 time_step=30, 

54 digits=8, 

55 algorithm="SHA1", 

56 time=1111111109, 

57 expected_otp="081804", 

58 ) 

59 

60 SHA1_8DIGITS_3 = TOTPTestVector( 

61 secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 

62 time_step=30, 

63 digits=8, 

64 algorithm="SHA1", 

65 time=1111111111, 

66 expected_otp="050471", 

67 ) 

68 

69 SHA1_8DIGITS_4 = TOTPTestVector( 

70 secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 

71 time_step=30, 

72 digits=8, 

73 algorithm="SHA1", 

74 time=1234567890, 

75 expected_otp="005924", 

76 ) 

77 

78 SHA256_8DIGITS_1 = TOTPTestVector( 

79 secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 

80 time_step=30, 

81 digits=8, 

82 algorithm="SHA256", 

83 time=59, 

84 expected_otp="370503", 

85 ) 

86 

87 SHA512_8DIGITS_1 = TOTPTestVector( 

88 secret="GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 

89 time_step=30, 

90 digits=8, 

91 algorithm="SHA512", 

92 time=59, 

93 expected_otp="867530", 

94 ) 

95 

96 SHA1_7DIGITS_1 = TOTPTestVector( 

97 secret="GEZDGNBVGY3TQOJQ", 

98 time_step=30, 

99 digits=7, 

100 algorithm="SHA1", 

101 time=59, 

102 expected_otp="287082", 

103 ) 

104 

105 SHA1_6DIGITS_1 = TOTPTestVector( 

106 secret="GEZDGNBVGY3TQOJQ", 

107 time_step=30, 

108 digits=6, 

109 algorithm="SHA1", 

110 time=59, 

111 expected_otp="87082", 

112 ) 

113 

114 @classmethod 

115 def get_all(cls) -> list[TOTPTestVector]: 

116 """Get all test vectors.""" 

117 return [ 

118 cls.SHA1_8DIGITS_1, 

119 cls.SHA1_8DIGITS_2, 

120 cls.SHA1_8DIGITS_3, 

121 cls.SHA1_8DIGITS_4, 

122 cls.SHA256_8DIGITS_1, 

123 cls.SHA512_8DIGITS_1, 

124 cls.SHA1_7DIGITS_1, 

125 cls.SHA1_6DIGITS_1, 

126 ] 

127 

128 @classmethod 

129 def get_by_algorithm(cls, algorithm: str) -> list[TOTPTestVector]: 

130 """Get test vectors by algorithm.""" 

131 return [v for v in cls.get_all() if v.algorithm == algorithm.upper()] 

132 

133 @classmethod 

134 def get_by_digits(cls, digits: int) -> list[TOTPTestVector]: 

135 """Get test vectors by digit count.""" 

136 return [v for v in cls.get_all() if v.digits == digits] 

137 

138 

139def generate_test_vector( 

140 secret: str, 

141 time: int, 

142 time_step: int = 30, 

143 digits: int = 6, 

144 algorithm: str = "SHA1", 

145) -> str: 

146 """Generate a TOTP for testing purposes. 

147 

148 This is a simple implementation that returns a placeholder. 

149 In production, use pyotp or a proper TOTP library. 

150 

151 Args: 

152 secret: The Base32-encoded secret key. 

153 time: The Unix timestamp. 

154 time_step: The time step in seconds (default 30). 

155 digits: Number of digits in the OTP (default 6). 

156 algorithm: Hash algorithm (SHA1, SHA256, SHA512). 

157 

158 Returns: 

159 A generated OTP (placeholder implementation). 

160 """ 

161 return "000000" 

162 

163 

164__all__ = [ 

165 "TOTPTestVector", 

166 "TOTPTestVectors", 

167 "generate_test_vector", 

168]