Coverage for silkaj/public_key.py: 97%

37 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-20 12:29 +0000

1# Copyright 2016-2025 Maël Azimi <m.a@moul.re> 

2# 

3# Silkaj is free software: you can redistribute it and/or modify 

4# it under the terms of the GNU Affero General Public License as published by 

5# the Free Software Foundation, either version 3 of the License, or 

6# (at your option) any later version. 

7# 

8# Silkaj is distributed in the hope that it will be useful, 

9# but WITHOUT ANY WARRANTY; without even the implied warranty of 

10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

11# GNU Affero General Public License for more details. 

12# 

13# You should have received a copy of the GNU Affero General Public License 

14# along with Silkaj. If not, see <https://www.gnu.org/licenses/>. 

15 

16import hashlib 

17import re 

18from typing import Any, Optional, Union 

19 

20import base58 

21 

22from silkaj.constants import PUBKEY_PATTERN, SHORT_PUBKEY_SIZE 

23from silkaj.tools import message_exit 

24 

25PUBKEY_DELIMITED_PATTERN = f"^{PUBKEY_PATTERN}$" 

26CHECKSUM_SIZE = 3 

27CHECKSUM_PATTERN = f"[1-9A-HJ-NP-Za-km-z]{ {CHECKSUM_SIZE}} " 

28PUBKEY_CHECKSUM_PATTERN = f"^{PUBKEY_PATTERN}:{CHECKSUM_PATTERN}$" 

29 

30 

31def is_pubkey_and_check(pubkey: str) -> Union[str, bool]: 

32 """ 

33 Checks if the given argument contains a pubkey. 

34 If so, verifies the checksum if needed and returns the pubkey. 

35 Exits if the checksum is wrong. 

36 Else, return False 

37 """ 

38 if re.search(re.compile(PUBKEY_PATTERN), pubkey): 

39 if check_pubkey_format(pubkey, True): 

40 return validate_checksum(pubkey) 

41 return pubkey 

42 return False 

43 

44 

45def check_pubkey_format(pubkey: str, display_error: bool = True) -> Optional[bool]: 

46 """ 

47 Checks if a pubkey has a checksum. 

48 Exits if the pubkey is invalid. 

49 """ 

50 if re.search(re.compile(PUBKEY_DELIMITED_PATTERN), pubkey): 

51 return False 

52 if re.search(re.compile(PUBKEY_CHECKSUM_PATTERN), pubkey): 

53 return True 

54 if display_error: 

55 message_exit(f"Error: bad format for following public key: {pubkey}") 

56 return None 

57 

58 

59def validate_checksum(pubkey_checksum: str) -> Any: 

60 """ 

61 Check pubkey checksum after the pubkey, delimited by ":". 

62 If check pass: return pubkey 

63 Else: exit. 

64 """ 

65 pubkey, checksum = pubkey_checksum.split(":") 

66 if checksum == gen_checksum(pubkey): 

67 return pubkey 

68 message_exit( 

69 f"Error: public key '{pubkey}' does not match checksum '{checksum}'.\n\ 

70Please verify the public key.", 

71 ) 

72 return None 

73 

74 

75def gen_checksum(pubkey: str) -> str: 

76 """ 

77 Returns the checksum of the input pubkey (encoded in b58) 

78 """ 

79 pubkey_byte = base58.b58decode(pubkey) 

80 _hash = hashlib.sha256(hashlib.sha256(pubkey_byte).digest()).digest() 

81 return str(base58.b58encode(_hash)[:3].decode("utf-8")) 

82 

83 

84def gen_pubkey_checksum( 

85 pubkey: str, 

86 short: Optional[bool] = False, 

87 length: Optional[int] = SHORT_PUBKEY_SIZE, 

88) -> str: 

89 """ 

90 Returns "<pubkey>:<checksum>" in full form. 

91 returns `length` first chars of pubkey and checksum in short form. 

92 `length` defaults to SHORT_PUBKEY_SIZE. 

93 """ 

94 short_pubkey = f"{pubkey[:length]}" if short else pubkey 

95 return f"{short_pubkey}:{gen_checksum(pubkey)}"