Exemplo n.º 1
0
def randomart(s, height=9, width=17, length=64, border=True, tag=''):
    """Produce a easy to compare visual representation of a string.
    Follows the algorithm laid out here http://www.dirk-loss.de/sshvis/drunken_bishop.pdf
    with the substitution of Keccak for MD5.

    s: the string to create a representation of
    height: (optional) the height of the representation to generate, default 9
    width: (optional) the width of the representation to generate, default 17
    length: (optional) the length of the random walk, essentially how many
        points are plotted in the representation, default 64
    border: (optional) whether to put a border around the representation,
        default True
    tag: (optional) a short string to be incorporated into the border,
        does nothing if border is False, defaults to the empty string
    """
    k = Keccak()
    k.absorb(s)
    # we reverse the endianness so that increasing length produces a radically
    # different randomart
    i = bytes2int(reversed(k.squeeze(int(ceil(length / 4.0)))))

    field = [ [0 for _ in xrange(width)]
              for __ in xrange(height) ]
    start = (height // 2,
             width // 2)
    position = start
    directions = ((-1, -1),
                  (-1, 1),
                  (1, -1),
                  (1, 1))
    for j in xrange(length):
        row_off, col_off = directions[(i>>(j*2)) % 4]
        position = (min(max(position[0] + row_off, 0),
                        height - 1),
                    min(max(position[1] + col_off, 0),
                        width - 1))
        field[position[0]][position[1]] += 1

    field[start[0]][start[1]] = 15
    field[position[0]][position[1]] = 16
    chars = ' .o+=*BOX@%&#/^SE'

    if border:
        if len(tag) > width - 2:
            tag = tag[:width-2]
        if tag:
            tag_pad_len = (width - len(tag) - 2) / 2.0
            first_row = '+' + ('-'*int(floor(tag_pad_len))) \
                        + '['+tag+']' \
                        + ('-'*int(ceil(tag_pad_len))) + '+\n'
        else:
            first_row = '+' + ('-'*width) + '+\n'
        last_row = '\n+' + ('-'*width) + '+'
        return first_row \
               + '\n'.join('|'+''.join(chars[cell] for cell in row)+'|'
                           for row in field) \
               + last_row
    else:
        return '\n'.join(''.join(chars[cell] for cell in row)
                         for row in field)
Exemplo n.º 2
0
def encode(s, compact=False):
    """From a byte string, produce a list of words that durably encodes the string.

    s: the byte string to be encoded
    compact: instead of using the length encoding scheme, pad by prepending a 1 bit

    The words in the encoding dictionary were chosen to be common and unambiguous.
    The encoding also includes a checksum. The encoding is constructed so that
    common errors are extremely unlikely to produce a valid encoding.
    """
    if not isinstance(s, bytes):
        raise TypeError("mnemonic.encode can only encode byte strings")

    k = Keccak()
    k.absorb(s)
    checksum_length = max(1, (len(s)-1).bit_length())
    checksum = k.squeeze(checksum_length)

    length = chr(checksum_length) if compact else encode_varint(len(s), endian='little')

    s += checksum
    s += length

    word_index = 0
    i = bytes2int(s)
    retval = [None] * int(floor(log(i, len(words)) + 1))
    for j in xrange(len(retval)):
        assert i > 0
        word_index += i % len(words)
        word_index %= len(words)
        retval[j] = words[word_index]
        i //= len(words)
    assert i == 0
    return tuple(retval)
Exemplo n.º 3
0
def encode(s, compact=False):
    """From a byte string, produce a list of words that durably encodes the string.

    s: the byte string to be encoded
    compact: instead of using the length encoding scheme, pad by prepending a 1 bit

    The words in the encoding dictionary were chosen to be common and unambiguous.
    The encoding also includes a checksum. The encoding is constructed so that
    common errors are extremely unlikely to produce a valid encoding.
    """
    if not isinstance(s, bytes):
        raise TypeError("mnemonic.encode can only encode byte strings")

    k = Keccak()
    k.absorb(s)
    checksum_length = max(1, (len(s) - 1).bit_length())
    checksum = k.squeeze(checksum_length)

    length = chr(checksum_length) if compact else encode_varint(
        len(s), endian='little')

    s += checksum
    s += length

    word_index = 0
    i = bytes2int(s)
    retval = [None] * int(floor(log(i, len(words)) + 1))
    for j in xrange(len(retval)):
        assert i > 0
        word_index += i % len(words)
        word_index %= len(words)
        retval[j] = words[word_index]
        i //= len(words)
    assert i == 0
    return tuple(retval)
Exemplo n.º 4
0
def unpad_and_checksum(s, compact, checksum):
    """Check length padding and checksum for a string
    return the string without length padding or checksum
    raise ValueError if either are wrong"""
    assert isinstance(s, bytes)
    if checksum:
        if compact:
            checksum_length = ord(s[-1])
            consumed = 1
            length = len(s) - checksum_length - consumed
        else:
            (length, consumed) = decode_varint(s, endian='little')
            checksum_length = max(1, (length - 1).bit_length())

        s = s[:-consumed]
        s, checksum = s[:-checksum_length], s[-checksum_length:]
        if len(s) != length:
            raise ValueError("Invalid length")

        k = Keccak()
        k.absorb(s)
        if k.squeeze(checksum_length) != checksum:
            raise ValueError("Invalid checksum")

        return s
    else:
        if compact:
            return s[:-1]
        else:
            (length, consumed) = decode_varint(s, endian='little')
            s = s[:-consumed]
            if len(s) != length:
                raise ValueError("Invalid length")
            return s
Exemplo n.º 5
0
def unpad_and_checksum(s, compact, checksum):
    """Check length padding and checksum for a string
    return the string without length padding or checksum
    raise ValueError if either are wrong"""
    assert isinstance(s, bytes)
    if checksum:
        if compact:
            checksum_length = ord(s[-1])
            consumed = 1
            length = len(s) - checksum_length - consumed
        else:
            (length, consumed) = decode_varint(s, endian='little')
            checksum_length = max(1, (length-1).bit_length())

        s = s[:-consumed]
        s, checksum = s[:-checksum_length], s[-checksum_length:]
        if len(s) != length:
            raise ValueError("Invalid length")

        k = Keccak()
        k.absorb(s)
        if k.squeeze(checksum_length) != checksum:
            raise ValueError("Invalid checksum")

        return s
    else:
        if compact:
            return s[:-1]
        else:
            (length, consumed) = decode_varint(s, endian='little')
            s = s[:-consumed]
            if len(s) != length:
                raise ValueError("Invalid length")
            return s
Exemplo n.º 6
0
def decode(w, compact=False, permissive=False):
    """From a list of words, or a whitespace-separated string of words, produce
    the original string that was encoded.

    w: the list of words, or whitespace delimited words to be decoded
    compact: compact encoding was used instead of length encoding
    permissive: if there are spelling errors, correct them instead of throwing
        an error (will still throw ValueError if spelling can't be corrected)

    Raises ValueError if the encoding is invalid.
    """
    if isinstance(w, bytes):
        w = w.split()

    indexes = [None] * len(w)
    for i, word in enumerate(w):
        if word in rwords:
            indexes[i] = rwords[word]
        elif permissive:
            for nearby in dldist(word, 1):
                if nearby in rwords:
                    indexes[i] = rwords[nearby]
                    break
        if indexes[i] is None:
            raise ValueError('Unrecognized word %s' % repr(word))

    # because we don't directly encode the mantissas, we have to extract them
    values = reduce(
        lambda (last_index, accum), index:
        (index, accum + [(index - last_index) % len(words)]), indexes,
        (0, []))[1]
    i = sum(mantissa * len(words)**radix
            for radix, mantissa in enumerate(values))
    # we don't need to worry about truncating null bytes because of the encoded length on the end
    s = int2bytes(i)

    if compact:
        checksum_length = ord(s[-1])
        consumed = 1
        length = len(s) - checksum_length - consumed
    else:
        (length, consumed) = decode_varint(s, endian='little')
        checksum_length = max(1, (length - 1).bit_length())

    s = s[:-consumed]
    s, checksum = s[:-checksum_length], s[-checksum_length:]
    if len(s) != length:
        raise ValueError("Invalid length")

    k = Keccak()
    k.absorb(s)
    if k.squeeze(checksum_length) != checksum:
        raise ValueError("Invalid checksum")

    return s
Exemplo n.º 7
0
def decode(w, compact=False, permissive=False):
    """From a list of words, or a whitespace-separated string of words, produce
    the original string that was encoded.

    w: the list of words, or whitespace delimited words to be decoded
    compact: compact encoding was used instead of length encoding
    permissive: if there are spelling errors, correct them instead of throwing
        an error (will still throw ValueError if spelling can't be corrected)

    Raises ValueError if the encoding is invalid.
    """
    if isinstance(w, bytes):
        w = w.split()

    indexes = [None]*len(w)
    for i,word in enumerate(w):
        if word in rwords:
            indexes[i] = rwords[word]
        elif permissive:
            for nearby in dldist(word, 1):
                if nearby in rwords:
                    indexes[i] = rwords[nearby]
                    break
        if indexes[i] is None:
            raise ValueError('Unrecognized word %s' % repr(word))

    # because we don't directly encode the mantissas, we have to extract them
    values = reduce(lambda (last_index, accum), index: (index,
                                                        accum + [(index - last_index) % len(words)]),
                    indexes,
                    (0, []))[1]
    i = sum(mantissa * len(words)**radix for radix, mantissa in enumerate(values))
    # we don't need to worry about truncating null bytes because of the encoded length on the end
    s = int2bytes(i)

    if compact:
        checksum_length = ord(s[-1])
        consumed = 1
        length = len(s) - checksum_length - consumed
    else:
        (length, consumed) = decode_varint(s, endian='little')
        checksum_length = max(1, (length-1).bit_length())

    s = s[:-consumed]
    s, checksum = s[:-checksum_length], s[-checksum_length:]
    if len(s) != length:
        raise ValueError("Invalid length")

    k = Keccak()
    k.absorb(s)
    if k.squeeze(checksum_length) != checksum:
        raise ValueError("Invalid checksum")

    return s
Exemplo n.º 8
0
def randomart(s, height=9, width=17, length=64, border=True, tag=''):
    """Produce a easy to compare visual representation of a string.
    Follows the algorithm laid out here http://www.dirk-loss.de/sshvis/drunken_bishop.pdf
    with the substitution of Keccak for MD5.

    s: the string to create a representation of
    height: (optional) the height of the representation to generate, default 9
    width: (optional) the width of the representation to generate, default 17
    length: (optional) the length of the random walk, essentially how many
        points are plotted in the representation, default 64
    border: (optional) whether to put a border around the representation,
        default True
    tag: (optional) a short string to be incorporated into the border,
        does nothing if border is False, defaults to the empty string
    """
    k = Keccak()
    k.absorb(s)
    # we reverse the endianness so that increasing length produces a radically
    # different randomart
    i = bytes2int(reversed(k.squeeze(int(ceil(length / 4.0)))),
                  endian='little')

    field = [[0 for _ in xrange(width)] for __ in xrange(height)]
    start = (height // 2, width // 2)
    position = start
    directions = ((-1, -1), (-1, 1), (1, -1), (1, 1))
    for j in xrange(length):
        row_off, col_off = directions[(i >> (j * 2)) % 4]
        position = (min(max(position[0] + row_off, 0), height - 1),
                    min(max(position[1] + col_off, 0), width - 1))
        field[position[0]][position[1]] += 1

    field[start[0]][start[1]] = 15
    field[position[0]][position[1]] = 16
    chars = ' .o+=*BOX@%&#/^SE'

    if border:
        if len(tag) > width - 2:
            tag = tag[:width - 2]
        if tag:
            tag_pad_len = (width - len(tag) - 2) / 2.0
            first_row = '+' + ('-'*int(floor(tag_pad_len))) \
                        + '['+tag+']' \
                        + ('-'*int(ceil(tag_pad_len))) + '+\n'
        else:
            first_row = '+' + ('-' * width) + '+\n'
        last_row = '\n+' + ('-' * width) + '+'
        return first_row \
               + '\n'.join('|'+''.join(chars[cell] for cell in row)+'|'
                           for row in field) \
               + last_row
    else:
        return '\n'.join(''.join(chars[cell] for cell in row) for row in field)
Exemplo n.º 9
0
def pad_and_checksum(s, compact, checksum):
    """Apply length padding and checksum to a string"""
    assert isinstance(s, bytes)
    if checksum:
        k = Keccak()
        k.absorb(s)
        checksum_length = max(1, (len(s)-1).bit_length())
        checksum = k.squeeze(checksum_length)

        length = chr(checksum_length) if compact else encode_varint(len(s), endian='little')
        return s + checksum + length
    else:
        length = '\x01' if compact else encode_varint(len(s), endian='little')
        return s + length
Exemplo n.º 10
0
def pad_and_checksum(s, compact, checksum):
    """Apply length padding and checksum to a string"""
    assert isinstance(s, bytes)
    if checksum:
        k = Keccak()
        k.absorb(s)
        checksum_length = max(1, (len(s) - 1).bit_length())
        checksum = k.squeeze(checksum_length)

        length = chr(checksum_length) if compact else encode_varint(
            len(s), endian='little')
        return s + checksum + length
    else:
        length = '\x01' if compact else encode_varint(len(s), endian='little')
        return s + length
Exemplo n.º 11
0
def oaep_keccak(m,
                label='',
                out_len=None,
                hash_len=32,
                random=random,
                keccak_args=dict()):
    """Perform OAEP (as specified by PKCS#1v2.1) with Keccak as the one-way function and mask-generating function

    All lengths specified in *bytes*
    m: message to be padded
    label: (optional) to be associated with the message, the default is the empty string
    out_len: (optional) the length of the message after padding, the default is len(m) + 2*hash_len + 2
    hash_len: (optional) the length of the output of the hash algorithm, the default is 32
    random: (optional) source of entropy for the random seed generation, the default is python's random module
    keccak_args: (optional) parameters for the Keccak sponge function, the defaults are the Keccak defaults
    """
    if out_len is not None and len(m) > out_len - 2 * hash_len - 2:
        raise ValueError(
            "Message too long to specified output and hash lengths")

    # hash the label
    k = Keccak(**keccak_args)
    k.absorb(label)
    lhash = k.squeeze(hash_len)

    if out_len is not None:
        pad_string = '\x00' * (out_len - len(m) - 2 * hash_len - 2)
    else:
        pad_string = ''

    # pad m
    padded = lhash + pad_string + '\x01' + m

    # generate rand_seed, a hash_len-byte random string
    rand_seed = random.getrandbits(hash_len * 8)
    rand_seed = int2bytes(rand_seed)

    # expand rand_seed to the length of padded
    k = Keccak(**keccak_args)
    k.absorb(rand_seed)
    mask = k.squeeze(len(padded))

    # XOR the message with the expanded r
    masked = ''.join(imap(chr, imap(xor, imap(ord, padded), imap(ord, mask))))

    # hash masked to generate the seed mask
    k = Keccak(**keccak_args)
    k.absorb(masked)
    seed_mask = k.squeeze(len(rand_seed))

    # mask the seed
    masked_seed = ''.join(
        imap(chr, imap(xor, imap(ord, rand_seed), imap(ord, seed_mask))))

    # concatenate the two together
    return '\x00' + masked_seed + masked
Exemplo n.º 12
0
def unoaep_keccak(m, label='', hash_len=32, keccak_args=dict()):
    """Recover a message padded with OAEP (as specified by PKCS#1v2.1) with Keccak as the one-way function and mask-generating function

    All lengths specified in *bytes*
    m: message to be decoded
    label: (optional) the label expected on the message, the default is the empty string
    hash_len: (optional) the length of the output of the hash algorithm, the default is 32
    keccak_args: (optional) parameters for the Keccak sponge function, the defaults are the Keccak defaults
    """
    # hash the label
    k = Keccak(**keccak_args)
    k.absorb(label)
    lhash = k.squeeze(hash_len)

    # split the three parts of the OAEP'd message
    leading_null, masked_seed, masked = m[0], m[1:hash_len + 1], m[hash_len +
                                                                   1:]

    # recover rand_seed
    k = Keccak(**keccak_args)
    k.absorb(masked)
    seed_mask = k.squeeze(len(masked_seed))

    rand_seed = ''.join(
        imap(chr, imap(xor, imap(ord, masked_seed), imap(ord, seed_mask))))

    # recover the original message
    k = Keccak(**keccak_args)
    k.absorb(rand_seed)
    mask = k.squeeze(len(masked))

    padded = ''.join(imap(chr, imap(xor, imap(ord, masked), imap(ord, mask))))

    # find the '\x01' separator byte without leaking timing info
    ## these need to be longs because of Python's small int caching
    separator_index = 0L
    separator = 0L
    for i in xrange(hash_len, len(padded)):
        # the result of ord is unavoidably a small int
        char = ord(padded[i])
        nonnull = int(char != 0)
        # set separator and separator_index, but only if char is nonnull and
        # they haven't already been set
        separator |= -long(separator == 0) & -nonnull & char
        separator_index |= -long(separator_index == 0) & -nonnull & i

    # check that lhash matches, the separator is correct, and the leading NUL is
    # preserved without leaking which one failed
    if sum([
            0L,  # force the sum to be a long
            secure_compare(lhash, padded[:hash_len]),
            separator == 1L,
            leading_null == chr(0L)
    ]) != 3L:
        raise ValueError("Decryption failed")
Exemplo n.º 13
0
def unoaep_keccak(m, label='', hash_len=32, keccak_args=dict()):
    """Recover a message padded with OAEP (as specified by PKCS#1v2.1) with Keccak as the one-way function and mask-generating function

    All lengths specified in *bytes*
    m: message to be decoded
    label: (optional) the label expected on the message, the default is the empty string
    hash_len: (optional) the length of the output of the hash algorithm, the default is 32
    keccak_args: (optional) parameters for the Keccak sponge function, the defaults are the Keccak defaults
    """
    # hash the label
    k = Keccak(**keccak_args)
    k.absorb(label)
    lhash = k.squeeze(hash_len)

    # split the three parts of the OAEP'd message
    leading_null, masked_seed, masked = m[0], m[1:hash_len+1], m[hash_len+1:]

    # recover rand_seed
    k = Keccak(**keccak_args)
    k.absorb(masked)
    seed_mask = k.squeeze(len(masked_seed))

    rand_seed = ''.join(imap(chr, imap(xor, imap(ord, masked_seed),
                                            imap(ord, seed_mask))))

    # recover the original message
    k = Keccak(**keccak_args)
    k.absorb(rand_seed)
    mask = k.squeeze(len(masked))

    padded = ''.join(imap(chr, imap(xor, imap(ord, masked),
                                         imap(ord, mask))))

    # find the '\x01' separator byte without leaking timing info
    ## these need to be longs because of Python's small int caching
    separator_index = 0L
    separator = 0L
    for i in xrange(hash_len,len(padded)):
        # the result of ord is unavoidably a small int
        char = ord(padded[i])
        nonnull = int(char != 0)
        # set separator and separator_index, but only if char is nonnull and
        # they haven't already been set
        separator |= -long(separator == 0) & -nonnull & char
        separator_index |= -long(separator_index == 0) & -nonnull & i

    # check that lhash matches, the separator is correct, and the leading NUL is
    # preserved without leaking which one failed
    if sum([ 0L, # force the sum to be a long
             secure_compare(lhash, padded[:hash_len]),
             separator == 1L,
             leading_null == chr(0L) ]) != 3L:
        raise ValueError("Decryption failed")
Exemplo n.º 14
0
def oaep_keccak(m, label='', out_len=None, hash_len=32, random=random, keccak_args=dict()):
    """Perform OAEP (as specified by PKCS#1v2.1) with Keccak as the one-way function and mask-generating function

    All lengths specified in *bytes*
    m: message to be padded
    label: (optional) to be associated with the message, the default is the empty string
    out_len: (optional) the length of the message after padding, the default is len(m) + 2*hash_len + 2
    hash_len: (optional) the length of the output of the hash algorithm, the default is 32
    random: (optional) source of entropy for the random seed generation, the default is python's random module
    keccak_args: (optional) parameters for the Keccak sponge function, the defaults are the Keccak defaults
    """
    if out_len is not None and len(m) > out_len - 2*hash_len - 2:
        raise ValueError("Message too long to specified output and hash lengths")
    
    # hash the label
    k = Keccak(**keccak_args)
    k.absorb(label)
    lhash = k.squeeze(hash_len)

    if out_len is not None:
        pad_string = '\x00' * (out_len - len(m) - 2*hash_len - 2)
    else:
        pad_string = ''
    
    # pad m
    padded = lhash + pad_string + '\x01' + m

    # generate rand_seed, a hash_len-byte random string
    rand_seed = random.getrandbits(hash_len*8)
    rand_seed = int2bytes(rand_seed)

    # expand rand_seed to the length of padded
    k = Keccak(**keccak_args)
    k.absorb(rand_seed)
    mask = k.squeeze(len(padded))

    # XOR the message with the expanded r
    masked = ''.join(imap(chr, imap(xor, imap(ord, padded),
                                         imap(ord, mask))))

    # hash masked to generate the seed mask
    k = Keccak(**keccak_args)
    k.absorb(masked)
    seed_mask = k.squeeze(len(rand_seed))

    # mask the seed
    masked_seed = ''.join(imap(chr, imap(xor, imap(ord, rand_seed),
                                              imap(ord, seed_mask))))

    # concatenate the two together
    return '\x00' + masked_seed + masked