descriptors.py raw

   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