aboutsummaryrefslogblamecommitdiff
path: root/frozen_deps/ecdsa/der.py
blob: 8c1de9bac5c7e66c5e99889d61e549c7e994e059 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                      
                                                                   








                                                       
                   




                                                                      
                                                                    










































                                                                               
                        

                         



                                                                         



















                                                                             






                                                    



                                                     
                                                                            




                     
                                                


                             





                                                         



                                  




                                                                        
                                          

                                               











                                                                            

                                                             






                                                                               

                                               





                            

                                                               



                                                                          

                                                               



                                                      

                                                                         

















                                 


                                                               







                                                                           

                                                      








                                                                     



                                                           














                                                                            
                          























                                                                           
                              

                                                                           
                     

                                                                             
                              




                                                                           
                                                                    











































                                                                               




                                                                     





                                                                        

                                               

















                                                                               
 










                                                                               

                      















                                                                
 



                                  






                                                 





                                                      


                                                                               

                                                        
from __future__ import division

import binascii
import base64
import warnings
from itertools import chain
from six import int2byte, b, text_type
from ._compat import str_idx_as_int


class UnexpectedDER(Exception):
    pass


def encode_constructed(tag, value):
    return int2byte(0xA0 + tag) + encode_length(len(value)) + value


def encode_integer(r):
    assert r >= 0  # can't support negative numbers yet
    h = ("%x" % r).encode()
    if len(h) % 2:
        h = b("0") + h
    s = binascii.unhexlify(h)
    num = str_idx_as_int(s, 0)
    if num <= 0x7F:
        return b("\x02") + encode_length(len(s)) + s
    else:
        # DER integers are two's complement, so if the first byte is
        # 0x80-0xff then we need an extra 0x00 byte to prevent it from
        # looking negative.
        return b("\x02") + encode_length(len(s) + 1) + b("\x00") + s


# sentry object to check if an argument was specified (used to detect
# deprecated calling convention)
_sentry = object()


def encode_bitstring(s, unused=_sentry):
    """
    Encode a binary string as a BIT STRING using :term:`DER` encoding.

    Note, because there is no native Python object that can encode an actual
    bit string, this function only accepts byte strings as the `s` argument.
    The byte string is the actual bit string that will be encoded, padded
    on the right (least significant bits, looking from big endian perspective)
    to the first full byte. If the bit string has a bit length that is multiple
    of 8, then the padding should not be included. For correct DER encoding
    the padding bits MUST be set to 0.

    Number of bits of padding need to be provided as the `unused` parameter.
    In case they are specified as None, it means the number of unused bits
    is already encoded in the string as the first byte.

    The deprecated call convention specifies just the `s` parameters and
    encodes the number of unused bits as first parameter (same convention
    as with None).

    Empty string must be encoded with `unused` specified as 0.

    Future version of python-ecdsa will make specifying the `unused` argument
    mandatory.

    :param s: bytes to encode
    :type s: bytes like object
    :param unused: number of bits at the end of `s` that are unused, must be
        between 0 and 7 (inclusive)
    :type unused: int or None

    :raises ValueError: when `unused` is too large or too small

    :return: `s` encoded using DER
    :rtype: bytes
    """
    encoded_unused = b""
    len_extra = 0
    if unused is _sentry:
        warnings.warn(
            "Legacy call convention used, unused= needs to be specified",
            DeprecationWarning,
        )
    elif unused is not None:
        if not 0 <= unused <= 7:
            raise ValueError("unused must be integer between 0 and 7")
        if unused:
            if not s:
                raise ValueError("unused is non-zero but s is empty")
            last = str_idx_as_int(s, -1)
            if last & (2 ** unused - 1):
                raise ValueError("unused bits must be zeros in DER")
        encoded_unused = int2byte(unused)
        len_extra = 1
    return b("\x03") + encode_length(len(s) + len_extra) + encoded_unused + s


def encode_octet_string(s):
    return b("\x04") + encode_length(len(s)) + s


def encode_oid(first, second, *pieces):
    assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second
    body = b"".join(
        chain(
            [encode_number(40 * first + second)],
            (encode_number(p) for p in pieces),
        )
    )
    return b"\x06" + encode_length(len(body)) + body


def encode_sequence(*encoded_pieces):
    total_len = sum([len(p) for p in encoded_pieces])
    return b("\x30") + encode_length(total_len) + b("").join(encoded_pieces)


def encode_number(n):
    b128_digits = []
    while n:
        b128_digits.insert(0, (n & 0x7F) | 0x80)
        n = n >> 7
    if not b128_digits:
        b128_digits.append(