Coverage for silkaj/public_key.py: 97%
37 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-04 17:03 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-04 17:03 +0000
1# Copyright 2016-2024 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} \
70 '.\n\
71Please verify the public key.",
72 )
73 return None
76def gen_checksum(pubkey: str) -> str:
77 """
78 Returns the checksum of the input pubkey (encoded in b58)
79 """
80 pubkey_byte = base58.b58decode(pubkey)
81 _hash = hashlib.sha256(hashlib.sha256(pubkey_byte).digest()).digest()
82 return str(base58.b58encode(_hash)[:3].decode("utf-8"))
85def gen_pubkey_checksum(
86 pubkey: str,
87 short: Optional[bool] = False,
88 length: Optional[int] = SHORT_PUBKEY_SIZE,
89) -> str:
90 """
91 Returns "<pubkey>:<checksum>" in full form.
92 returns `length` first chars of pubkey and checksum in short form.
93 `length` defaults to SHORT_PUBKEY_SIZE.
94 """
95 short_pubkey = f"{pubkey[:length]}…" if short else pubkey
96 return f"{short_pubkey}:{gen_checksum(pubkey)}"