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
« 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/>.
16import hashlib
17import re
18from typing import Any, Optional, Union
20import base58
22from silkaj.constants import PUBKEY_PATTERN, SHORT_PUBKEY_SIZE
23from silkaj.tools import message_exit
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}$"
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
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
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
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"))
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)}"