1 #!/usr/bin/env python3
2 # Copyright (c) 2019 Pieter Wuille
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Utility functions related to output descriptors"""
6 7 import re
8 9 INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
10 CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
11 GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd]
12 13 def descsum_polymod(symbols):
14 """Internal function that computes the descriptor checksum."""
15 chk = 1
16 for value in symbols:
17 top = chk >> 35
18 chk = (chk & 0x7ffffffff) << 5 ^ value
19 for i in range(5):
20 chk ^= GENERATOR[i] if ((top >> i) & 1) else 0
21 return chk
22 23 def descsum_expand(s):
24 """Internal function that does the character to symbol expansion"""
25 groups = []
26 symbols = []
27 for c in s:
28 if c not in INPUT_CHARSET:
29 return None
30 v = INPUT_CHARSET.find(c)
31 symbols.append(v & 31)
32 groups.append(v >> 5)
33 if len(groups) == 3:
34 symbols.append(groups[0] * 9 + groups[1] * 3 + groups[2])
35 groups = []
36 if len(groups) == 1:
37 symbols.append(groups[0])
38 elif len(groups) == 2:
39 symbols.append(groups[0] * 3 + groups[1])
40 return symbols
41 42 def descsum_create(s):
43 """Add a checksum to a descriptor without"""
44 symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
45 checksum = descsum_polymod(symbols) ^ 1
46 return s + '#' + ''.join(CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31] for i in range(8))
47 48 def descsum_check(s, require=True):
49 """Verify that the checksum is correct in a descriptor"""
50 if '#' not in s:
51 return not require
52 if s[-9] != '#':
53 return False
54 if not all(x in CHECKSUM_CHARSET for x in s[-8:]):
55 return False
56 symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
57 return descsum_polymod(symbols) == 1
58 59 def drop_origins(s):
60 '''Drop the key origins from a descriptor'''
61 desc = re.sub(r'\[.+?\]', '', s)
62 if '#' in s:
63 desc = desc[:desc.index('#')]
64 return descsum_create(desc)
65