# -*- coding: utf-8 -*-
# Copyright (c) 2019 Pieter Wuille
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utility functions related to output descriptors"""
import re
INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
GENERATOR = [0xF5DEE51989, 0xA9FDCA3312, 0x1BAB10E32D, 0x3706B1677A, 0x644D626FFD]
[docs]def descsum_polymod(symbols):
# Internal function that computes the descriptor checksum.
chk = 1
for value in symbols:
top = chk >> 35
chk = (chk & 0x7FFFFFFFF) << 5 ^ value
for i in range(5):
chk ^= GENERATOR[i] if ((top >> i) & 1) else 0
return chk
[docs]def descsum_expand(s):
# Internal function that does the character to symbol expansion
groups = []
symbols = []
for c in s:
if c not in INPUT_CHARSET:
return None
v = INPUT_CHARSET.find(c)
symbols.append(v & 31)
groups.append(v >> 5)
if len(groups) == 3:
symbols.append(groups[0] * 9 + groups[1] * 3 + groups[2])
groups = []
if len(groups) == 1:
symbols.append(groups[0])
elif len(groups) == 2:
symbols.append(groups[0] * 3 + groups[1])
return symbols
[docs]def descsum_create(s):
"""Add a checksum to a descriptor without"""
symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
checksum = descsum_polymod(symbols) ^ 1
return (
s
+ "#"
+ "".join(CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31] for i in range(8))
)
[docs]def descsum_create_only(s):
"""Returns only the checksum of the descriptor"""
symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
checksum = descsum_polymod(symbols) ^ 1
return "".join(CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31] for i in range(8))
[docs]def descsum_check(s, require=True):
"""Verify that the checksum is correct in a descriptor"""
if "#" not in s:
return not require
if s[-9] != "#":
return False
if not all(x in CHECKSUM_CHARSET for x in s[-8:]):
return False
symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
return descsum_polymod(symbols) == 1
[docs]def drop_origins(s):
"""Drop the key origins from a descriptor"""
# formerly lazy quantifier r"\[.+?\]" XXX Following has not been tested yet!
desc = re.sub(r"\[[^]]+\]", "", s) # greedily negated character class
if "#" in s:
desc = desc[: desc.index("#")]
return descsum_create(desc)