1""" 
    2This module is an implementation of `section 3.4`_ of RFC 5849. 
    3 
    4**Usage** 
    5 
    6Steps for signing a request: 
    7 
    81. Collect parameters from the request using ``collect_parameters``. 
    92. Normalize those parameters using ``normalize_parameters``. 
    103. Create the *base string URI* using ``base_string_uri``. 
    114. Create the *signature base string* from the above three components 
    12   using ``signature_base_string``. 
    135. Pass the *signature base string* and the client credentials to one of the 
    14   sign-with-client functions. The HMAC-based signing functions needs 
    15   client credentials with secrets. The RSA-based signing functions needs 
    16   client credentials with an RSA private key. 
    17 
    18To verify a request, pass the request and credentials to one of the verify 
    19functions. The HMAC-based signing functions needs the shared secrets. The 
    20RSA-based verify functions needs the RSA public key. 
    21 
    22**Scope** 
    23 
    24All of the functions in this module should be considered internal to OAuthLib, 
    25since they are not imported into the "oauthlib.oauth1" module. Programs using 
    26OAuthLib should not use directly invoke any of the functions in this module. 
    27 
    28**Deprecated functions** 
    29 
    30The "sign_" methods that are not "_with_client" have been deprecated. They may 
    31be removed in a future release. Since they are all internal functions, this 
    32should have no impact on properly behaving programs. 
    33 
    34.. _`section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4 
    35""" 
    36 
    37import binascii 
    38import hashlib 
    39import hmac 
    40import ipaddress 
    41import logging 
    42import urllib.parse as urlparse 
    43import warnings 
    44 
    45from oauthlib.common import extract_params, safe_string_equals, urldecode 
    46 
    47from . import utils 
    48import contextlib 
    49 
    50log = logging.getLogger(__name__) 
    51 
    52 
    53# ==== Common functions ========================================== 
    54 
    55def signature_base_string( 
    56        http_method: str, 
    57        base_str_uri: str, 
    58        normalized_encoded_request_parameters: str) -> str: 
    59    """ 
    60    Construct the signature base string. 
    61 
    62    The *signature base string* is the value that is calculated and signed by 
    63    the client. It is also independently calculated by the server to verify 
    64    the signature, and therefore must produce the exact same value at both 
    65    ends or the signature won't verify. 
    66 
    67    The rules for calculating the *signature base string* are defined in 
    68    section 3.4.1.1`_ of RFC 5849. 
    69 
    70    .. _`section 3.4.1.1`: https://tools.ietf.org/html/rfc5849#section-3.4.1.1 
    71    """ 
    72 
    73    # The signature base string is constructed by concatenating together, 
    74    # in order, the following HTTP request elements: 
    75 
    76    # 1.  The HTTP request method in uppercase.  For example: "HEAD", 
    77    #     "GET", "POST", etc.  If the request uses a custom HTTP method, it 
    78    #     MUST be encoded (`Section 3.6`_). 
    79    # 
    80    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    81    base_string = utils.escape(http_method.upper()) 
    82 
    83    # 2.  An "&" character (ASCII code 38). 
    84    base_string += '&' 
    85 
    86    # 3.  The base string URI from `Section 3.4.1.2`_, after being encoded 
    87    #     (`Section 3.6`_). 
    88    # 
    89    # .. _`Section 3.4.1.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.2 
    90    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    91    base_string += utils.escape(base_str_uri) 
    92 
    93    # 4.  An "&" character (ASCII code 38). 
    94    base_string += '&' 
    95 
    96    # 5.  The request parameters as normalized in `Section 3.4.1.3.2`_, after 
    97    #     being encoded (`Section 3.6`). 
    98    # 
    99    # .. _`Sec 3.4.1.3.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.3.2 
    100    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    101    base_string += utils.escape(normalized_encoded_request_parameters) 
    102 
    103    return base_string 
    104 
    105 
    106def base_string_uri(uri: str, host: str = None) -> str: 
    107    """ 
    108    Calculates the _base string URI_. 
    109 
    110    The *base string URI* is one of the components that make up the 
    111     *signature base string*. 
    112 
    113    The ``host`` is optional. If provided, it is used to override any host and 
    114    port values in the ``uri``. The value for ``host`` is usually extracted from 
    115    the "Host" request header from the HTTP request. Its value may be just the 
    116    hostname, or the hostname followed by a colon and a TCP/IP port number 
    117    (hostname:port). If a value for the``host`` is provided but it does not 
    118    contain a port number, the default port number is used (i.e. if the ``uri`` 
    119    contained a port number, it will be discarded). 
    120 
    121    The rules for calculating the *base string URI* are defined in 
    122    section 3.4.1.2`_ of RFC 5849. 
    123 
    124    .. _`section 3.4.1.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.2 
    125 
    126    :param uri: URI 
    127    :param host: hostname with optional port number, separated by a colon 
    128    :return: base string URI 
    129    """ 
    130 
    131    if not isinstance(uri, str): 
    132        raise ValueError('uri must be a string.') 
    133 
    134    # FIXME: urlparse does not support unicode 
    135    output = urlparse.urlparse(uri) 
    136    scheme = output.scheme 
    137    hostname = output.hostname 
    138    port = output.port 
    139    path = output.path 
    140    params = output.params 
    141 
    142    # The scheme, authority, and path of the request resource URI `RFC3986` 
    143    # are included by constructing an "http" or "https" URI representing 
    144    # the request resource (without the query or fragment) as follows: 
    145    # 
    146    # .. _`RFC3986`: https://tools.ietf.org/html/rfc3986 
    147 
    148    if not scheme: 
    149        raise ValueError('missing scheme') 
    150 
    151    # Per `RFC 2616 section 5.1.2`_: 
    152    # 
    153    # Note that the absolute path cannot be empty; if none is present in 
    154    # the original URI, it MUST be given as "/" (the server root). 
    155    # 
    156    # .. _`RFC 2616 5.1.2`: https://tools.ietf.org/html/rfc2616#section-5.1.2 
    157    if not path: 
    158        path = '/' 
    159 
    160    # 1.  The scheme and host MUST be in lowercase. 
    161    scheme = scheme.lower() 
    162    # Note: if ``host`` is used, it will be converted to lowercase below 
    163    if hostname is not None: 
    164        hostname = hostname.lower() 
    165 
    166    # 2.  The host and port values MUST match the content of the HTTP 
    167    #     request "Host" header field. 
    168    if host is not None: 
    169        # NOTE: override value in uri with provided host 
    170        # Host argument is equal to netloc. It means it's missing scheme. 
    171        # Add it back, before parsing. 
    172 
    173        host = host.lower() 
    174        host = f"{scheme}://{host}" 
    175        output = urlparse.urlparse(host) 
    176        hostname = output.hostname 
    177        port = output.port 
    178 
    179    # 3.  The port MUST be included if it is not the default port for the 
    180    #     scheme, and MUST be excluded if it is the default.  Specifically, 
    181    #     the port MUST be excluded when making an HTTP request `RFC2616`_ 
    182    #     to port 80 or when making an HTTPS request `RFC2818`_ to port 443. 
    183    #     All other non-default port numbers MUST be included. 
    184    # 
    185    # .. _`RFC2616`: https://tools.ietf.org/html/rfc2616 
    186    # .. _`RFC2818`: https://tools.ietf.org/html/rfc2818 
    187 
    188    if hostname is None: 
    189        raise ValueError('missing host') 
    190 
    191    # NOTE: Try guessing if we're dealing with IP or hostname 
    192    with contextlib.suppress(ValueError): 
    193        hostname = ipaddress.ip_address(hostname) 
    194 
    195 
    196    if isinstance(hostname, ipaddress.IPv6Address): 
    197        hostname = f"[{hostname}]" 
    198    elif isinstance(hostname, ipaddress.IPv4Address): 
    199        hostname = f"{hostname}" 
    200 
    201    if port is not None and not (0 < port <= 65535): 
    202        raise ValueError('port out of range')  # 16-bit unsigned ints 
    203    if (scheme, port) in (('http', 80), ('https', 443)): 
    204        netloc = hostname  # default port for scheme: exclude port num 
    205    elif port: 
    206        netloc = f"{hostname}:{port}"  # use hostname:port 
    207    else: 
    208        netloc = hostname 
    209 
    210    v = urlparse.urlunparse((scheme, netloc, path, params, '', '')) 
    211 
    212    # RFC 5849 does not specify which characters are encoded in the 
    213    # "base string URI", nor how they are encoded - which is very bad, since 
    214    # the signatures won't match if there are any differences. Fortunately, 
    215    # most URIs only use characters that are clearly not encoded (e.g. digits 
    216    # and A-Z, a-z), so have avoided any differences between implementations. 
    217    # 
    218    # The example from its section 3.4.1.2 illustrates that spaces in 
    219    # the path are percent encoded. But it provides no guidance as to what other 
    220    # characters (if any) must be encoded (nor how); nor if characters in the 
    221    # other components are to be encoded or not. 
    222    # 
    223    # This implementation **assumes** that **only** the space is percent-encoded 
    224    # and it is done to the entire value (not just to spaces in the path). 
    225    # 
    226    # This code may need to be changed if it is discovered that other characters 
    227    # are expected to be encoded. 
    228    # 
    229    # Note: the "base string URI" returned by this function will be encoded 
    230    # again before being concatenated into the "signature base string". So any 
    231    # spaces in the URI will actually appear in the "signature base string" 
    232    # as "%2520" (the "%20" further encoded according to section 3.6). 
    233 
    234    return v.replace(' ', '%20') 
    235 
    236 
    237def collect_parameters(uri_query='', body=None, headers=None, 
    238                       exclude_oauth_signature=True, with_realm=False): 
    239    """ 
    240    Gather the request parameters from all the parameter sources. 
    241 
    242    This function is used to extract all the parameters, which are then passed 
    243    to ``normalize_parameters`` to produce one of the components that make up 
    244    the *signature base string*. 
    245 
    246    Parameters starting with `oauth_` will be unescaped. 
    247 
    248    Body parameters must be supplied as a dict, a list of 2-tuples, or a 
    249    form encoded query string. 
    250 
    251    Headers must be supplied as a dict. 
    252 
    253    The rules where the parameters must be sourced from are defined in 
    254    `section 3.4.1.3.1`_ of RFC 5849. 
    255 
    256    .. _`Sec 3.4.1.3.1`: https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1 
    257    """ 
    258    if body is None: 
    259        body = [] 
    260    headers = headers or {} 
    261    params = [] 
    262 
    263    # The parameters from the following sources are collected into a single 
    264    # list of name/value pairs: 
    265 
    266    # *  The query component of the HTTP request URI as defined by 
    267    #    `RFC3986, Section 3.4`_.  The query component is parsed into a list 
    268    #    of name/value pairs by treating it as an 
    269    #    "application/x-www-form-urlencoded" string, separating the names 
    270    #    and values and decoding them as defined by W3C.REC-html40-19980424 
    271    #    `W3C-HTML-4.0`_, Section 17.13.4. 
    272    # 
    273    # .. _`RFC3986, Sec 3.4`: https://tools.ietf.org/html/rfc3986#section-3.4 
    274    # .. _`W3C-HTML-4.0`: https://www.w3.org/TR/1998/REC-html40-19980424/ 
    275    if uri_query: 
    276        params.extend(urldecode(uri_query)) 
    277 
    278    # *  The OAuth HTTP "Authorization" header field (`Section 3.5.1`_) if 
    279    #    present.  The header's content is parsed into a list of name/value 
    280    #    pairs excluding the "realm" parameter if present.  The parameter 
    281    #    values are decoded as defined by `Section 3.5.1`_. 
    282    # 
    283    # .. _`Section 3.5.1`: https://tools.ietf.org/html/rfc5849#section-3.5.1 
    284    if headers: 
    285        headers_lower = {k.lower(): v for k, v in headers.items()} 
    286        authorization_header = headers_lower.get('authorization') 
    287        if authorization_header is not None: 
    288            params.extend([i for i in utils.parse_authorization_header( 
    289                authorization_header) if with_realm or i[0] != 'realm']) 
    290 
    291    # *  The HTTP request entity-body, but only if all of the following 
    292    #    conditions are met: 
    293    #     *  The entity-body is single-part. 
    294    # 
    295    #     *  The entity-body follows the encoding requirements of the 
    296    #        "application/x-www-form-urlencoded" content-type as defined by 
    297    #        W3C.REC-html40-19980424 `W3C-HTML-4.0`_. 
    298 
    299    #     *  The HTTP request entity-header includes the "Content-Type" 
    300    #        header field set to "application/x-www-form-urlencoded". 
    301    # 
    302    # .. _`W3C-HTML-4.0`: https://www.w3.org/TR/1998/REC-html40-19980424/ 
    303 
    304    # TODO: enforce header param inclusion conditions 
    305    bodyparams = extract_params(body) or [] 
    306    params.extend(bodyparams) 
    307 
    308    # ensure all oauth params are unescaped 
    309    unescaped_params = [] 
    310    for k, v in params: 
    311        if k.startswith('oauth_'): 
    312            v = utils.unescape(v) 
    313        unescaped_params.append((k, v)) 
    314 
    315    # The "oauth_signature" parameter MUST be excluded from the signature 
    316    # base string if present. 
    317    if exclude_oauth_signature: 
    318        unescaped_params = list(filter(lambda i: i[0] != 'oauth_signature', 
    319                                       unescaped_params)) 
    320 
    321    return unescaped_params 
    322 
    323 
    324def normalize_parameters(params) -> str: 
    325    """ 
    326    Calculate the normalized request parameters. 
    327 
    328    The *normalized request parameters* is one of the components that make up 
    329    the *signature base string*. 
    330 
    331    The rules for parameter normalization are defined in `section 3.4.1.3.2`_ of 
    332    RFC 5849. 
    333 
    334    .. _`Sec 3.4.1.3.2`: https://tools.ietf.org/html/rfc5849#section-3.4.1.3.2 
    335    """ 
    336 
    337    # The parameters collected in `Section 3.4.1.3`_ are normalized into a 
    338    # single string as follows: 
    339    # 
    340    # .. _`Section 3.4.1.3`: https://tools.ietf.org/html/rfc5849#section-3.4.1.3 
    341 
    342    # 1.  First, the name and value of each parameter are encoded 
    343    #     (`Section 3.6`_). 
    344    # 
    345    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    346    key_values = [(utils.escape(k), utils.escape(v)) for k, v in params] 
    347 
    348    # 2.  The parameters are sorted by name, using ascending byte value 
    349    #     ordering.  If two or more parameters share the same name, they 
    350    #     are sorted by their value. 
    351    key_values.sort() 
    352 
    353    # 3.  The name of each parameter is concatenated to its corresponding 
    354    #     value using an "=" character (ASCII code 61) as a separator, even 
    355    #     if the value is empty. 
    356    parameter_parts = ['{}={}'.format(k, v) for k, v in key_values] 
    357 
    358    # 4.  The sorted name/value pairs are concatenated together into a 
    359    #     single string by using an "&" character (ASCII code 38) as 
    360    #     separator. 
    361    return '&'.join(parameter_parts) 
    362 
    363 
    364# ==== Common functions for HMAC-based signature methods ========= 
    365 
    366def _sign_hmac(hash_algorithm_name: str, 
    367               sig_base_str: str, 
    368               client_secret: str, 
    369               resource_owner_secret: str): 
    370    """ 
    371    **HMAC-SHA256** 
    372 
    373    The "HMAC-SHA256" signature method uses the HMAC-SHA256 signature 
    374    algorithm as defined in `RFC4634`_:: 
    375 
    376        digest = HMAC-SHA256 (key, text) 
    377 
    378    Per `section 3.4.2`_ of the spec. 
    379 
    380    .. _`RFC4634`: https://tools.ietf.org/html/rfc4634 
    381    .. _`section 3.4.2`: https://tools.ietf.org/html/rfc5849#section-3.4.2 
    382    """ 
    383 
    384    # The HMAC-SHA256 function variables are used in following way: 
    385 
    386    # text is set to the value of the signature base string from 
    387    # `Section 3.4.1.1`_. 
    388    # 
    389    # .. _`Section 3.4.1.1`: https://tools.ietf.org/html/rfc5849#section-3.4.1.1 
    390    text = sig_base_str 
    391 
    392    # key is set to the concatenated values of: 
    393    # 1.  The client shared-secret, after being encoded (`Section 3.6`_). 
    394    # 
    395    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    396    key = utils.escape(client_secret or '') 
    397 
    398    # 2.  An "&" character (ASCII code 38), which MUST be included 
    399    #     even when either secret is empty. 
    400    key += '&' 
    401 
    402    # 3.  The token shared-secret, after being encoded (`Section 3.6`_). 
    403    # 
    404    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    405    key += utils.escape(resource_owner_secret or '') 
    406 
    407    # Get the hashing algorithm to use 
    408 
    409    m = { 
    410        'SHA-1': hashlib.sha1, 
    411        'SHA-256': hashlib.sha256, 
    412        'SHA-512': hashlib.sha512, 
    413    } 
    414    hash_alg = m[hash_algorithm_name] 
    415 
    416    # Calculate the signature 
    417 
    418    # FIXME: HMAC does not support unicode! 
    419    key_utf8 = key.encode('utf-8') 
    420    text_utf8 = text.encode('utf-8') 
    421    signature = hmac.new(key_utf8, text_utf8, hash_alg) 
    422 
    423    # digest  is used to set the value of the "oauth_signature" protocol 
    424    #         parameter, after the result octet string is base64-encoded 
    425    #         per `RFC2045, Section 6.8`. 
    426    # 
    427    # .. _`RFC2045, Sec 6.8`: https://tools.ietf.org/html/rfc2045#section-6.8 
    428    return binascii.b2a_base64(signature.digest())[:-1].decode('utf-8') 
    429 
    430 
    431def _verify_hmac(hash_algorithm_name: str, 
    432                 request, 
    433                 client_secret=None, 
    434                 resource_owner_secret=None): 
    435    """Verify a HMAC-SHA1 signature. 
    436 
    437    Per `section 3.4`_ of the spec. 
    438 
    439    .. _`section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4 
    440 
    441    To satisfy `RFC2616 section 5.2`_ item 1, the request argument's uri 
    442    attribute MUST be an absolute URI whose netloc part identifies the 
    443    origin server or gateway on which the resource resides. Any Host 
    444    item of the request argument's headers dict attribute will be 
    445    ignored. 
    446 
    447    .. _`RFC2616 section 5.2`: https://tools.ietf.org/html/rfc2616#section-5.2 
    448 
    449    """ 
    450    norm_params = normalize_parameters(request.params) 
    451    bs_uri = base_string_uri(request.uri) 
    452    sig_base_str = signature_base_string(request.http_method, bs_uri, 
    453                                         norm_params) 
    454    signature = _sign_hmac(hash_algorithm_name, sig_base_str, 
    455                           client_secret, resource_owner_secret) 
    456    match = safe_string_equals(signature, request.signature) 
    457    if not match: 
    458        log.debug('Verify HMAC failed: signature base string: %s', sig_base_str) 
    459    return match 
    460 
    461 
    462# ==== HMAC-SHA1 ================================================= 
    463 
    464def sign_hmac_sha1_with_client(sig_base_str, client): 
    465    return _sign_hmac('SHA-1', sig_base_str, 
    466                      client.client_secret, client.resource_owner_secret) 
    467 
    468 
    469def verify_hmac_sha1(request, client_secret=None, resource_owner_secret=None): 
    470    return _verify_hmac('SHA-1', request, client_secret, resource_owner_secret) 
    471 
    472 
    473def sign_hmac_sha1(base_string, client_secret, resource_owner_secret): 
    474    """ 
    475    Deprecated function for calculating a HMAC-SHA1 signature. 
    476 
    477    This function has been replaced by invoking ``sign_hmac`` with "SHA-1" 
    478    as the hash algorithm name. 
    479 
    480    This function was invoked by sign_hmac_sha1_with_client and 
    481    test_signatures.py, but does any application invoke it directly? If not, 
    482    it can be removed. 
    483    """ 
    484    warnings.warn('use sign_hmac_sha1_with_client instead of sign_hmac_sha1', 
    485                  DeprecationWarning) 
    486 
    487    # For some unknown reason, the original implementation assumed base_string 
    488    # could either be bytes or str. The signature base string calculating 
    489    # function always returned a str, so the new ``sign_rsa`` only expects that. 
    490 
    491    base_string = base_string.decode('ascii') \ 
    492        if isinstance(base_string, bytes) else base_string 
    493 
    494    return _sign_hmac('SHA-1', base_string, 
    495                      client_secret, resource_owner_secret) 
    496 
    497 
    498# ==== HMAC-SHA256 =============================================== 
    499 
    500def sign_hmac_sha256_with_client(sig_base_str, client): 
    501    return _sign_hmac('SHA-256', sig_base_str, 
    502                      client.client_secret, client.resource_owner_secret) 
    503 
    504 
    505def verify_hmac_sha256(request, client_secret=None, resource_owner_secret=None): 
    506    return _verify_hmac('SHA-256', request, 
    507                        client_secret, resource_owner_secret) 
    508 
    509 
    510def sign_hmac_sha256(base_string, client_secret, resource_owner_secret): 
    511    """ 
    512    Deprecated function for calculating a HMAC-SHA256 signature. 
    513 
    514    This function has been replaced by invoking ``sign_hmac`` with "SHA-256" 
    515    as the hash algorithm name. 
    516 
    517    This function was invoked by sign_hmac_sha256_with_client and 
    518    test_signatures.py, but does any application invoke it directly? If not, 
    519    it can be removed. 
    520    """ 
    521    warnings.warn( 
    522        'use sign_hmac_sha256_with_client instead of sign_hmac_sha256', 
    523        DeprecationWarning) 
    524 
    525    # For some unknown reason, the original implementation assumed base_string 
    526    # could either be bytes or str. The signature base string calculating 
    527    # function always returned a str, so the new ``sign_rsa`` only expects that. 
    528 
    529    base_string = base_string.decode('ascii') \ 
    530        if isinstance(base_string, bytes) else base_string 
    531 
    532    return _sign_hmac('SHA-256', base_string, 
    533                      client_secret, resource_owner_secret) 
    534 
    535 
    536# ==== HMAC-SHA512 =============================================== 
    537 
    538def sign_hmac_sha512_with_client(sig_base_str: str, 
    539                                 client): 
    540    return _sign_hmac('SHA-512', sig_base_str, 
    541                      client.client_secret, client.resource_owner_secret) 
    542 
    543 
    544def verify_hmac_sha512(request, 
    545                       client_secret: str = None, 
    546                       resource_owner_secret: str = None): 
    547    return _verify_hmac('SHA-512', request, 
    548                        client_secret, resource_owner_secret) 
    549 
    550 
    551# ==== Common functions for RSA-based signature methods ========== 
    552 
    553_jwt_rsa = {}  # cache of RSA-hash implementations from PyJWT jwt.algorithms 
    554 
    555 
    556def _get_jwt_rsa_algorithm(hash_algorithm_name: str): 
    557    """ 
    558    Obtains an RSAAlgorithm object that implements RSA with the hash algorithm. 
    559 
    560    This method maintains the ``_jwt_rsa`` cache. 
    561 
    562    Returns a jwt.algorithm.RSAAlgorithm. 
    563    """ 
    564    if hash_algorithm_name in _jwt_rsa: 
    565        # Found in cache: return it 
    566        return _jwt_rsa[hash_algorithm_name] 
    567    else: 
    568        # Not in cache: instantiate a new RSAAlgorithm 
    569 
    570        # PyJWT has some nice pycrypto/cryptography abstractions 
    571        import jwt.algorithms as jwt_algorithms  # noqa: PLC0415 
    572        m = { 
    573            'SHA-1': jwt_algorithms.hashes.SHA1, 
    574            'SHA-256': jwt_algorithms.hashes.SHA256, 
    575            'SHA-512': jwt_algorithms.hashes.SHA512, 
    576        } 
    577        v = jwt_algorithms.RSAAlgorithm(m[hash_algorithm_name]) 
    578 
    579        _jwt_rsa[hash_algorithm_name] = v  # populate cache 
    580 
    581        return v 
    582 
    583 
    584def _prepare_key_plus(alg, keystr): 
    585    """ 
    586    Prepare a PEM encoded key (public or private), by invoking the `prepare_key` 
    587    method on alg with the keystr. 
    588 
    589    The keystr should be a string or bytes.  If the keystr is bytes, it is 
    590    decoded as UTF-8 before being passed to prepare_key. Otherwise, it 
    591    is passed directly. 
    592    """ 
    593    if isinstance(keystr, bytes): 
    594        keystr = keystr.decode('utf-8') 
    595    return alg.prepare_key(keystr) 
    596 
    597 
    598def _sign_rsa(hash_algorithm_name: str, 
    599              sig_base_str: str, 
    600              rsa_private_key: str): 
    601    """ 
    602    Calculate the signature for an RSA-based signature method. 
    603 
    604    The ``alg`` is used to calculate the digest over the signature base string. 
    605    For the "RSA_SHA1" signature method, the alg must be SHA-1. While OAuth 1.0a 
    606    only defines the RSA-SHA1 signature method, this function can be used for 
    607    other non-standard signature methods that only differ from RSA-SHA1 by the 
    608    digest algorithm. 
    609 
    610    Signing for the RSA-SHA1 signature method is defined in 
    611    `section 3.4.3`_ of RFC 5849. 
    612 
    613    The RSASSA-PKCS1-v1_5 signature algorithm used defined by 
    614    `RFC3447, Section 8.2`_ (also known as PKCS#1), with the `alg` as the 
    615    hash function for EMSA-PKCS1-v1_5.  To 
    616    use this method, the client MUST have established client credentials 
    617    with the server that included its RSA public key (in a manner that is 
    618    beyond the scope of this specification). 
    619 
    620    .. _`section 3.4.3`: https://tools.ietf.org/html/rfc5849#section-3.4.3 
    621    .. _`RFC3447, Section 8.2`: https://tools.ietf.org/html/rfc3447#section-8.2 
    622    """ 
    623 
    624    # Get the implementation of RSA-hash 
    625 
    626    alg = _get_jwt_rsa_algorithm(hash_algorithm_name) 
    627 
    628    # Check private key 
    629 
    630    if not rsa_private_key: 
    631        raise ValueError('rsa_private_key required for RSA with ' + 
    632                         alg.hash_alg.name + ' signature method') 
    633 
    634    # Convert the "signature base string" into a sequence of bytes (M) 
    635    # 
    636    # The signature base string, by definition, only contain printable US-ASCII 
    637    # characters. So encoding it as 'ascii' will always work. It will raise a 
    638    # ``UnicodeError`` if it can't encode the value, which will never happen 
    639    # if the signature base string was created correctly. Therefore, using 
    640    # 'ascii' encoding provides an extra level of error checking. 
    641 
    642    m = sig_base_str.encode('ascii') 
    643 
    644    # Perform signing: S = RSASSA-PKCS1-V1_5-SIGN (K, M) 
    645 
    646    key = _prepare_key_plus(alg, rsa_private_key) 
    647    s = alg.sign(m, key) 
    648 
    649    # base64-encoded per RFC2045 section 6.8. 
    650    # 
    651    # 1. While b2a_base64 implements base64 defined by RFC 3548. As used here, 
    652    #    it is the same as base64 defined by RFC 2045. 
    653    # 2. b2a_base64 includes a "\n" at the end of its result ([:-1] removes it) 
    654    # 3. b2a_base64 produces a binary string. Use decode to produce a str. 
    655    #    It should only contain only printable US-ASCII characters. 
    656 
    657    return binascii.b2a_base64(s)[:-1].decode('ascii') 
    658 
    659 
    660def _verify_rsa(hash_algorithm_name: str, 
    661                request, 
    662                rsa_public_key: str): 
    663    """ 
    664    Verify a base64 encoded signature for a RSA-based signature method. 
    665 
    666    The ``alg`` is used to calculate the digest over the signature base string. 
    667    For the "RSA_SHA1" signature method, the alg must be SHA-1. While OAuth 1.0a 
    668    only defines the RSA-SHA1 signature method, this function can be used for 
    669    other non-standard signature methods that only differ from RSA-SHA1 by the 
    670    digest algorithm. 
    671 
    672    Verification for the RSA-SHA1 signature method is defined in 
    673    `section 3.4.3`_ of RFC 5849. 
    674 
    675    .. _`section 3.4.3`: https://tools.ietf.org/html/rfc5849#section-3.4.3 
    676 
    677        To satisfy `RFC2616 section 5.2`_ item 1, the request argument's uri 
    678        attribute MUST be an absolute URI whose netloc part identifies the 
    679        origin server or gateway on which the resource resides. Any Host 
    680        item of the request argument's headers dict attribute will be 
    681        ignored. 
    682 
    683        .. _`RFC2616 Sec 5.2`: https://tools.ietf.org/html/rfc2616#section-5.2 
    684    """ 
    685 
    686    try: 
    687        # Calculate the *signature base string* of the actual received request 
    688 
    689        norm_params = normalize_parameters(request.params) 
    690        bs_uri = base_string_uri(request.uri) 
    691        sig_base_str = signature_base_string( 
    692            request.http_method, bs_uri, norm_params) 
    693 
    694        # Obtain the signature that was received in the request 
    695 
    696        sig = binascii.a2b_base64(request.signature.encode('ascii')) 
    697 
    698        # Get the implementation of RSA-with-hash algorithm to use 
    699 
    700        alg = _get_jwt_rsa_algorithm(hash_algorithm_name) 
    701 
    702        # Verify the received signature was produced by the private key 
    703        # corresponding to the `rsa_public_key`, signing exact same 
    704        # *signature base string*. 
    705        # 
    706        #     RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S) 
    707 
    708        key = _prepare_key_plus(alg, rsa_public_key) 
    709 
    710        # The signature base string only contain printable US-ASCII characters. 
    711        # The ``encode`` method with the default "strict" error handling will 
    712        # raise a ``UnicodeError`` if it can't encode the value. So using 
    713        # "ascii" will always work. 
    714 
    715        verify_ok = alg.verify(sig_base_str.encode('ascii'), key, sig) 
    716 
    717        if not verify_ok: 
    718            log.debug('Verify failed: RSA with ' + alg.hash_alg.name + 
    719                      ': signature base string=%s' + sig_base_str) 
    720        return verify_ok 
    721 
    722    except UnicodeError: 
    723        # A properly encoded signature will only contain printable US-ASCII 
    724        # characters. The ``encode`` method with the default "strict" error 
    725        # handling will raise a ``UnicodeError`` if it can't decode the value. 
    726        # So using "ascii" will work with all valid signatures. But an 
    727        # incorrectly or maliciously produced signature could contain other 
    728        # bytes. 
    729        # 
    730        # This implementation treats that situation as equivalent to the 
    731        # signature verification having failed. 
    732        # 
    733        # Note: simply changing the encode to use 'utf-8' will not remove this 
    734        # case, since an incorrect or malicious request can contain bytes which 
    735        # are invalid as UTF-8. 
    736        return False 
    737 
    738 
    739# ==== RSA-SHA1 ================================================== 
    740 
    741def sign_rsa_sha1_with_client(sig_base_str, client): 
    742    # For some reason, this function originally accepts both str and bytes. 
    743    # This behaviour is preserved here. But won't be done for the newer 
    744    # sign_rsa_sha256_with_client and sign_rsa_sha512_with_client functions, 
    745    # which will only accept strings. The function to calculate a 
    746    # "signature base string" always produces a string, so it is not clear 
    747    # why support for bytes would ever be needed. 
    748    sig_base_str = sig_base_str.decode('ascii')\ 
    749        if isinstance(sig_base_str, bytes) else sig_base_str 
    750 
    751    return _sign_rsa('SHA-1', sig_base_str, client.rsa_key) 
    752 
    753 
    754def verify_rsa_sha1(request, rsa_public_key: str): 
    755    return _verify_rsa('SHA-1', request, rsa_public_key) 
    756 
    757 
    758def sign_rsa_sha1(base_string, rsa_private_key): 
    759    """ 
    760    Deprecated function for calculating a RSA-SHA1 signature. 
    761 
    762    This function has been replaced by invoking ``sign_rsa`` with "SHA-1" 
    763    as the hash algorithm name. 
    764 
    765    This function was invoked by sign_rsa_sha1_with_client and 
    766    test_signatures.py, but does any application invoke it directly? If not, 
    767    it can be removed. 
    768    """ 
    769    warnings.warn('use _sign_rsa("SHA-1", ...) instead of sign_rsa_sha1', 
    770                  DeprecationWarning) 
    771 
    772    if isinstance(base_string, bytes): 
    773        base_string = base_string.decode('ascii') 
    774 
    775    return _sign_rsa('SHA-1', base_string, rsa_private_key) 
    776 
    777 
    778# ==== RSA-SHA256 ================================================ 
    779 
    780def sign_rsa_sha256_with_client(sig_base_str: str, client): 
    781    return _sign_rsa('SHA-256', sig_base_str, client.rsa_key) 
    782 
    783 
    784def verify_rsa_sha256(request, rsa_public_key: str): 
    785    return _verify_rsa('SHA-256', request, rsa_public_key) 
    786 
    787 
    788# ==== RSA-SHA512 ================================================ 
    789 
    790def sign_rsa_sha512_with_client(sig_base_str: str, client): 
    791    return _sign_rsa('SHA-512', sig_base_str, client.rsa_key) 
    792 
    793 
    794def verify_rsa_sha512(request, rsa_public_key: str): 
    795    return _verify_rsa('SHA-512', request, rsa_public_key) 
    796 
    797 
    798# ==== PLAINTEXT ================================================= 
    799 
    800def sign_plaintext_with_client(_signature_base_string, client): 
    801    # _signature_base_string is not used because the signature with PLAINTEXT 
    802    # is just the secret: it isn't a real signature. 
    803    return sign_plaintext(client.client_secret, client.resource_owner_secret) 
    804 
    805 
    806def sign_plaintext(client_secret, resource_owner_secret): 
    807    """Sign a request using plaintext. 
    808 
    809    Per `section 3.4.4`_ of the spec. 
    810 
    811    The "PLAINTEXT" method does not employ a signature algorithm.  It 
    812    MUST be used with a transport-layer mechanism such as TLS or SSL (or 
    813    sent over a secure channel with equivalent protections).  It does not 
    814    utilize the signature base string or the "oauth_timestamp" and 
    815    "oauth_nonce" parameters. 
    816 
    817    .. _`section 3.4.4`: https://tools.ietf.org/html/rfc5849#section-3.4.4 
    818 
    819    """ 
    820 
    821    # The "oauth_signature" protocol parameter is set to the concatenated 
    822    # value of: 
    823 
    824    # 1.  The client shared-secret, after being encoded (`Section 3.6`_). 
    825    # 
    826    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    827    signature = utils.escape(client_secret or '') 
    828 
    829    # 2.  An "&" character (ASCII code 38), which MUST be included even 
    830    #     when either secret is empty. 
    831    signature += '&' 
    832 
    833    # 3.  The token shared-secret, after being encoded (`Section 3.6`_). 
    834    # 
    835    # .. _`Section 3.6`: https://tools.ietf.org/html/rfc5849#section-3.6 
    836    signature += utils.escape(resource_owner_secret or '') 
    837 
    838    return signature 
    839 
    840 
    841def verify_plaintext(request, client_secret=None, resource_owner_secret=None): 
    842    """Verify a PLAINTEXT signature. 
    843 
    844    Per `section 3.4`_ of the spec. 
    845 
    846    .. _`section 3.4`: https://tools.ietf.org/html/rfc5849#section-3.4 
    847    """ 
    848    signature = sign_plaintext(client_secret, resource_owner_secret) 
    849    match = safe_string_equals(signature, request.signature) 
    850    if not match: 
    851        log.debug('Verify PLAINTEXT failed') 
    852    return match