1# -*- coding: utf-8 -*-
2"""
3oauthlib.oauth1.rfc5849.signature
4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6This module represents a direct implementation of `section 3.4`_ of the spec.
7
8Terminology:
9 * Client: software interfacing with an OAuth API
10 * Server: the API provider
11 * Resource Owner: the user who is granting authorization to the client
12
13Steps for signing a request:
14
151. Collect parameters from the uri query, auth header, & body
162. Normalize those parameters
173. Normalize the uri
184. Pass the normalized uri, normalized parameters, and http method to
19   construct the base string
205. Pass the base string and any keys needed to a signing function
21
22.. _`section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
23"""
24from __future__ import absolute_import, unicode_literals
25
26import binascii
27import hashlib
28import hmac
29try:
30    import urlparse
31except ImportError:
32    import urllib.parse as urlparse
33from . import utils
34from oauthlib.common import urldecode, extract_params, safe_string_equals
35from oauthlib.common import bytes_type, unicode_type
36
37
38def construct_base_string(http_method, base_string_uri,
39                          normalized_encoded_request_parameters):
40    """**String Construction**
41    Per `section 3.4.1.1`_ of the spec.
42
43    For example, the HTTP request::
44
45        POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
46        Host: example.com
47        Content-Type: application/x-www-form-urlencoded
48        Authorization: OAuth realm="Example",
49            oauth_consumer_key="9djdj82h48djs9d2",
50            oauth_token="kkk9d7dh3k39sjv7",
51            oauth_signature_method="HMAC-SHA1",
52            oauth_timestamp="137131201",
53            oauth_nonce="7d8f3e4a",
54            oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D"
55
56        c2&a3=2+q
57
58    is represented by the following signature base string (line breaks
59    are for display purposes only)::
60
61        POST&http%3A%2F%2Fexample.com%2Frequest&a2%3Dr%2520b%26a3%3D2%2520q
62        %26a3%3Da%26b5%3D%253D%25253D%26c%2540%3D%26c2%3D%26oauth_consumer_
63        key%3D9djdj82h48djs9d2%26oauth_nonce%3D7d8f3e4a%26oauth_signature_m
64        ethod%3DHMAC-SHA1%26oauth_timestamp%3D137131201%26oauth_token%3Dkkk
65        9d7dh3k39sjv7
66
67    .. _`section 3.4.1.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.1
68    """
69
70    # The signature base string is constructed by concatenating together,
71    # in order, the following HTTP request elements:
72
73    # 1.  The HTTP request method in uppercase.  For example: "HEAD",
74    #     "GET", "POST", etc.  If the request uses a custom HTTP method, it
75    #     MUST be encoded (`Section 3.6`_).
76    #
77    # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
78    base_string = utils.escape(http_method.upper())
79
80    # 2.  An "&" character (ASCII code 38).
81    base_string += '&'
82
83    # 3.  The base string URI from `Section 3.4.1.2`_, after being encoded
84    #     (`Section 3.6`_).
85    #
86    # .. _`Section 3.4.1.2`: http://tools.ietf.org/html/rfc5849#section-3.4.1.2
87    # .. _`Section 3.4.6`: http://tools.ietf.org/html/rfc5849#section-3.4.6
88    base_string += utils.escape(base_string_uri)
89
90    # 4.  An "&" character (ASCII code 38).
91    base_string += '&'
92
93    # 5.  The request parameters as normalized in `Section 3.4.1.3.2`_, after
94    #     being encoded (`Section 3.6`).
95    #
96    # .. _`Section 3.4.1.3.2`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
97    # .. _`Section 3.4.6`: http://tools.ietf.org/html/rfc5849#section-3.4.6
98    base_string += utils.escape(normalized_encoded_request_parameters)
99
100    return base_string
101
102
103def normalize_base_string_uri(uri, host=None):
104    """**Base String URI**
105    Per `section 3.4.1.2`_ of the spec.
106
107    For example, the HTTP request::
108
109        GET /r%20v/X?id=123 HTTP/1.1
110        Host: EXAMPLE.COM:80
111
112    is represented by the base string URI: "http://example.com/r%20v/X".
113
114    In another example, the HTTPS request::
115
116        GET /?q=1 HTTP/1.1
117        Host: www.example.net:8080
118
119    is represented by the base string URI: "https://www.example.net:8080/".
120
121    .. _`section 3.4.1.2`: http://tools.ietf.org/html/rfc5849#section-3.4.1.2
122
123    The host argument overrides the netloc part of the uri argument.
124    """
125    if not isinstance(uri, unicode_type):
126        raise ValueError('uri must be a unicode object.')
127
128    # FIXME: urlparse does not support unicode
129    scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
130
131    # The scheme, authority, and path of the request resource URI `RFC3986`
132    # are included by constructing an "http" or "https" URI representing
133    # the request resource (without the query or fragment) as follows:
134    #
135    # .. _`RFC3986`: http://tools.ietf.org/html/rfc3986
136
137    if not scheme or not netloc:
138        raise ValueError('uri must include a scheme and netloc')
139
140    # Per `RFC 2616 section 5.1.2`_:
141    #
142    # Note that the absolute path cannot be empty; if none is present in
143    # the original URI, it MUST be given as "/" (the server root).
144    #
145    # .. _`RFC 2616 section 5.1.2`: http://tools.ietf.org/html/rfc2616#section-5.1.2
146    if not path:
147        path = '/'
148
149    # 1.  The scheme and host MUST be in lowercase.
150    scheme = scheme.lower()
151    netloc = netloc.lower()
152
153    # 2.  The host and port values MUST match the content of the HTTP
154    #     request "Host" header field.
155    if host is not None:
156        netloc = host.lower()
157
158    # 3.  The port MUST be included if it is not the default port for the
159    #     scheme, and MUST be excluded if it is the default.  Specifically,
160    #     the port MUST be excluded when making an HTTP request `RFC2616`_
161    #     to port 80 or when making an HTTPS request `RFC2818`_ to port 443.
162    #     All other non-default port numbers MUST be included.
163    #
164    # .. _`RFC2616`: http://tools.ietf.org/html/rfc2616
165    # .. _`RFC2818`: http://tools.ietf.org/html/rfc2818
166    default_ports = (
167        ('http', '80'),
168        ('https', '443'),
169    )
170    if ':' in netloc:
171        host, port = netloc.split(':', 1)
172        if (scheme, port) in default_ports:
173            netloc = host
174
175    return urlparse.urlunparse((scheme, netloc, path, params, '', ''))
176
177
178# ** Request Parameters **
179#
180#    Per `section 3.4.1.3`_ of the spec.
181#
182#    In order to guarantee a consistent and reproducible representation of
183#    the request parameters, the parameters are collected and decoded to
184#    their original decoded form.  They are then sorted and encoded in a
185#    particular manner that is often different from their original
186#    encoding scheme, and concatenated into a single string.
187#
188# .. _`section 3.4.1.3`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3
189
190def collect_parameters(uri_query='', body=[], headers=None,
191                       exclude_oauth_signature=True, with_realm=False):
192    """**Parameter Sources**
193
194    Parameters starting with `oauth_` will be unescaped.
195
196    Body parameters must be supplied as a dict, a list of 2-tuples, or a
197    formencoded query string.
198
199    Headers must be supplied as a dict.
200
201    Per `section 3.4.1.3.1`_ of the spec.
202
203    For example, the HTTP request::
204
205        POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
206        Host: example.com
207        Content-Type: application/x-www-form-urlencoded
208        Authorization: OAuth realm="Example",
209            oauth_consumer_key="9djdj82h48djs9d2",
210            oauth_token="kkk9d7dh3k39sjv7",
211            oauth_signature_method="HMAC-SHA1",
212            oauth_timestamp="137131201",
213            oauth_nonce="7d8f3e4a",
214            oauth_signature="djosJKDKJSD8743243%2Fjdk33klY%3D"
215
216        c2&a3=2+q
217
218    contains the following (fully decoded) parameters used in the
219    signature base sting::
220
221        +------------------------+------------------+
222        |          Name          |       Value      |
223        +------------------------+------------------+
224        |           b5           |       =%3D       |
225        |           a3           |         a        |
226        |           c@           |                  |
227        |           a2           |        r b       |
228        |   oauth_consumer_key   | 9djdj82h48djs9d2 |
229        |       oauth_token      | kkk9d7dh3k39sjv7 |
230        | oauth_signature_method |     HMAC-SHA1    |
231        |     oauth_timestamp    |     137131201    |
232        |       oauth_nonce      |     7d8f3e4a     |
233        |           c2           |                  |
234        |           a3           |        2 q       |
235        +------------------------+------------------+
236
237    Note that the value of "b5" is "=%3D" and not "==".  Both "c@" and
238    "c2" have empty values.  While the encoding rules specified in this
239    specification for the purpose of constructing the signature base
240    string exclude the use of a "+" character (ASCII code 43) to
241    represent an encoded space character (ASCII code 32), this practice
242    is widely used in "application/x-www-form-urlencoded" encoded values,
243    and MUST be properly decoded, as demonstrated by one of the "a3"
244    parameter instances (the "a3" parameter is used twice in this
245    request).
246
247    .. _`section 3.4.1.3.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
248    """
249    headers = headers or {}
250    params = []
251
252    # The parameters from the following sources are collected into a single
253    # list of name/value pairs:
254
255    # *  The query component of the HTTP request URI as defined by
256    #    `RFC3986, Section 3.4`_.  The query component is parsed into a list
257    #    of name/value pairs by treating it as an
258    #    "application/x-www-form-urlencoded" string, separating the names
259    #    and values and decoding them as defined by
260    #    `W3C.REC-html40-19980424`_, Section 17.13.4.
261    #
262    # .. _`RFC3986, Section 3.4`: http://tools.ietf.org/html/rfc3986#section-3.4
263    # .. _`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424
264    if uri_query:
265        params.extend(urldecode(uri_query))
266
267    # *  The OAuth HTTP "Authorization" header field (`Section 3.5.1`_) if
268    #    present.  The header's content is parsed into a list of name/value
269    #    pairs excluding the "realm" parameter if present.  The parameter
270    #    values are decoded as defined by `Section 3.5.1`_.
271    #
272    # .. _`Section 3.5.1`: http://tools.ietf.org/html/rfc5849#section-3.5.1
273    if headers:
274        headers_lower = dict((k.lower(), v) for k, v in headers.items())
275        authorization_header = headers_lower.get('authorization')
276        if authorization_header is not None:
277            params.extend([i for i in utils.parse_authorization_header(
278                authorization_header) if with_realm or i[0] != 'realm'])
279
280    # *  The HTTP request entity-body, but only if all of the following
281    #    conditions are met:
282    #     *  The entity-body is single-part.
283    #
284    #     *  The entity-body follows the encoding requirements of the
285    #        "application/x-www-form-urlencoded" content-type as defined by
286    #        `W3C.REC-html40-19980424`_.
287
288    #     *  The HTTP request entity-header includes the "Content-Type"
289    #        header field set to "application/x-www-form-urlencoded".
290    #
291    # .._`W3C.REC-html40-19980424`: http://tools.ietf.org/html/rfc5849#ref-W3C.REC-html40-19980424
292
293    # TODO: enforce header param inclusion conditions
294    bodyparams = extract_params(body) or []
295    params.extend(bodyparams)
296
297    # ensure all oauth params are unescaped
298    unescaped_params = []
299    for k, v in params:
300        if k.startswith('oauth_'):
301            v = utils.unescape(v)
302        unescaped_params.append((k, v))
303
304    # The "oauth_signature" parameter MUST be excluded from the signature
305    # base string if present.
306    if exclude_oauth_signature:
307        unescaped_params = list(filter(lambda i: i[0] != 'oauth_signature',
308                                       unescaped_params))
309
310    return unescaped_params
311
312
313def normalize_parameters(params):
314    """**Parameters Normalization**
315    Per `section 3.4.1.3.2`_ of the spec.
316
317    For example, the list of parameters from the previous section would
318    be normalized as follows:
319
320    Encoded::
321
322    +------------------------+------------------+
323    |          Name          |       Value      |
324    +------------------------+------------------+
325    |           b5           |     %3D%253D     |
326    |           a3           |         a        |
327    |          c%40          |                  |
328    |           a2           |       r%20b      |
329    |   oauth_consumer_key   | 9djdj82h48djs9d2 |
330    |       oauth_token      | kkk9d7dh3k39sjv7 |
331    | oauth_signature_method |     HMAC-SHA1    |
332    |     oauth_timestamp    |     137131201    |
333    |       oauth_nonce      |     7d8f3e4a     |
334    |           c2           |                  |
335    |           a3           |       2%20q      |
336    +------------------------+------------------+
337
338    Sorted::
339
340    +------------------------+------------------+
341    |          Name          |       Value      |
342    +------------------------+------------------+
343    |           a2           |       r%20b      |
344    |           a3           |       2%20q      |
345    |           a3           |         a        |
346    |           b5           |     %3D%253D     |
347    |          c%40          |                  |
348    |           c2           |                  |
349    |   oauth_consumer_key   | 9djdj82h48djs9d2 |
350    |       oauth_nonce      |     7d8f3e4a     |
351    | oauth_signature_method |     HMAC-SHA1    |
352    |     oauth_timestamp    |     137131201    |
353    |       oauth_token      | kkk9d7dh3k39sjv7 |
354    +------------------------+------------------+
355
356    Concatenated Pairs::
357
358    +-------------------------------------+
359    |              Name=Value             |
360    +-------------------------------------+
361    |               a2=r%20b              |
362    |               a3=2%20q              |
363    |                 a3=a                |
364    |             b5=%3D%253D             |
365    |                c%40=                |
366    |                 c2=                 |
367    | oauth_consumer_key=9djdj82h48djs9d2 |
368    |         oauth_nonce=7d8f3e4a        |
369    |   oauth_signature_method=HMAC-SHA1  |
370    |      oauth_timestamp=137131201      |
371    |     oauth_token=kkk9d7dh3k39sjv7    |
372    +-------------------------------------+
373
374    and concatenated together into a single string (line breaks are for
375    display purposes only)::
376
377        a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9dj
378        dj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1
379        &oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7
380
381    .. _`section 3.4.1.3.2`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3.2
382    """
383
384    # The parameters collected in `Section 3.4.1.3`_ are normalized into a
385    # single string as follows:
386    #
387    # .. _`Section 3.4.1.3`: http://tools.ietf.org/html/rfc5849#section-3.4.1.3
388
389    # 1.  First, the name and value of each parameter are encoded
390    #     (`Section 3.6`_).
391    #
392    # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
393    key_values = [(utils.escape(k), utils.escape(v)) for k, v in params]
394
395    # 2.  The parameters are sorted by name, using ascending byte value
396    #     ordering.  If two or more parameters share the same name, they
397    #     are sorted by their value.
398    key_values.sort()
399
400    # 3.  The name of each parameter is concatenated to its corresponding
401    #     value using an "=" character (ASCII code 61) as a separator, even
402    #     if the value is empty.
403    parameter_parts = ['{0}={1}'.format(k, v) for k, v in key_values]
404
405    # 4.  The sorted name/value pairs are concatenated together into a
406    #     single string by using an "&" character (ASCII code 38) as
407    #     separator.
408    return '&'.join(parameter_parts)
409
410
411def sign_hmac_sha1_with_client(base_string, client):
412    return sign_hmac_sha1(base_string,
413                          client.client_secret,
414                          client.resource_owner_secret
415                          )
416
417
418def sign_hmac_sha1(base_string, client_secret, resource_owner_secret):
419    """**HMAC-SHA1**
420
421    The "HMAC-SHA1" signature method uses the HMAC-SHA1 signature
422    algorithm as defined in `RFC2104`_::
423
424        digest = HMAC-SHA1 (key, text)
425
426    Per `section 3.4.2`_ of the spec.
427
428    .. _`RFC2104`: http://tools.ietf.org/html/rfc2104
429    .. _`section 3.4.2`: http://tools.ietf.org/html/rfc5849#section-3.4.2
430    """
431
432    # The HMAC-SHA1 function variables are used in following way:
433
434    # text is set to the value of the signature base string from
435    # `Section 3.4.1.1`_.
436    #
437    # .. _`Section 3.4.1.1`: http://tools.ietf.org/html/rfc5849#section-3.4.1.1
438    text = base_string
439
440    # key is set to the concatenated values of:
441    # 1.  The client shared-secret, after being encoded (`Section 3.6`_).
442    #
443    # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
444    key = utils.escape(client_secret or '')
445
446    # 2.  An "&" character (ASCII code 38), which MUST be included
447    #     even when either secret is empty.
448    key += '&'
449
450    # 3.  The token shared-secret, after being encoded (`Section 3.6`_).
451    #
452    # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
453    key += utils.escape(resource_owner_secret or '')
454
455    # FIXME: HMAC does not support unicode!
456    key_utf8 = key.encode('utf-8')
457    text_utf8 = text.encode('utf-8')
458    signature = hmac.new(key_utf8, text_utf8, hashlib.sha1)
459
460    # digest  is used to set the value of the "oauth_signature" protocol
461    #         parameter, after the result octet string is base64-encoded
462    #         per `RFC2045, Section 6.8`.
463    #
464    # .. _`RFC2045, Section 6.8`: http://tools.ietf.org/html/rfc2045#section-6.8
465    return binascii.b2a_base64(signature.digest())[:-1].decode('utf-8')
466
467_jwtrs1 = None
468
469#jwt has some nice pycrypto/cryptography abstractions
470def _jwt_rs1_signing_algorithm():
471    global _jwtrs1
472    if _jwtrs1 is None:
473        import jwt.algorithms as jwtalgo
474        _jwtrs1 = jwtalgo.RSAAlgorithm(jwtalgo.hashes.SHA1)
475    return _jwtrs1
476
477def sign_rsa_sha1(base_string, rsa_private_key):
478    """**RSA-SHA1**
479
480    Per `section 3.4.3`_ of the spec.
481
482    The "RSA-SHA1" signature method uses the RSASSA-PKCS1-v1_5 signature
483    algorithm as defined in `RFC3447, Section 8.2`_ (also known as
484    PKCS#1), using SHA-1 as the hash function for EMSA-PKCS1-v1_5.  To
485    use this method, the client MUST have established client credentials
486    with the server that included its RSA public key (in a manner that is
487    beyond the scope of this specification).
488
489    .. _`section 3.4.3`: http://tools.ietf.org/html/rfc5849#section-3.4.3
490    .. _`RFC3447, Section 8.2`: http://tools.ietf.org/html/rfc3447#section-8.2
491
492    """
493    if isinstance(base_string, unicode_type):
494        base_string = base_string.encode('utf-8')
495    # TODO: finish RSA documentation
496    alg = _jwt_rs1_signing_algorithm()
497    key = _prepare_key_plus(alg, rsa_private_key)
498    s=alg.sign(base_string, key)
499    return binascii.b2a_base64(s)[:-1].decode('utf-8')
500
501
502def sign_rsa_sha1_with_client(base_string, client):
503    if not client.rsa_key:
504        raise ValueError('rsa_key is required when using RSA signature method.')
505    return sign_rsa_sha1(base_string, client.rsa_key)
506
507
508def sign_plaintext(client_secret, resource_owner_secret):
509    """Sign a request using plaintext.
510
511    Per `section 3.4.4`_ of the spec.
512
513    The "PLAINTEXT" method does not employ a signature algorithm.  It
514    MUST be used with a transport-layer mechanism such as TLS or SSL (or
515    sent over a secure channel with equivalent protections).  It does not
516    utilize the signature base string or the "oauth_timestamp" and
517    "oauth_nonce" parameters.
518
519    .. _`section 3.4.4`: http://tools.ietf.org/html/rfc5849#section-3.4.4
520
521    """
522
523    # The "oauth_signature" protocol parameter is set to the concatenated
524    # value of:
525
526    # 1.  The client shared-secret, after being encoded (`Section 3.6`_).
527    #
528    # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
529    signature = utils.escape(client_secret or '')
530
531    # 2.  An "&" character (ASCII code 38), which MUST be included even
532    #     when either secret is empty.
533    signature += '&'
534
535    # 3.  The token shared-secret, after being encoded (`Section 3.6`_).
536    #
537    # .. _`Section 3.6`: http://tools.ietf.org/html/rfc5849#section-3.6
538    signature += utils.escape(resource_owner_secret or '')
539
540    return signature
541
542
543def sign_plaintext_with_client(base_string, client):
544    return sign_plaintext(client.client_secret, client.resource_owner_secret)
545
546
547def verify_hmac_sha1(request, client_secret=None,
548                     resource_owner_secret=None):
549    """Verify a HMAC-SHA1 signature.
550
551    Per `section 3.4`_ of the spec.
552
553    .. _`section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
554
555    To satisfy `RFC2616 section 5.2`_ item 1, the request argument's uri
556    attribute MUST be an absolute URI whose netloc part identifies the
557    origin server or gateway on which the resource resides. Any Host
558    item of the request argument's headers dict attribute will be
559    ignored.
560
561    .. _`RFC2616 section 5.2`: http://tools.ietf.org/html/rfc2616#section-5.2
562
563    """
564    norm_params = normalize_parameters(request.params)
565    uri = normalize_base_string_uri(request.uri)
566    base_string = construct_base_string(request.http_method, uri, norm_params)
567    signature = sign_hmac_sha1(base_string, client_secret,
568                               resource_owner_secret)
569    return safe_string_equals(signature, request.signature)
570
571def _prepare_key_plus(alg, keystr):
572    if isinstance(keystr, bytes_type):
573        keystr = keystr.decode('utf-8')
574    return alg.prepare_key(keystr)
575
576def verify_rsa_sha1(request, rsa_public_key):
577    """Verify a RSASSA-PKCS #1 v1.5 base64 encoded signature.
578
579    Per `section 3.4.3`_ of the spec.
580
581    Note this method requires the jwt and cryptography libraries.
582
583    .. _`section 3.4.3`: http://tools.ietf.org/html/rfc5849#section-3.4.3
584
585    To satisfy `RFC2616 section 5.2`_ item 1, the request argument's uri
586    attribute MUST be an absolute URI whose netloc part identifies the
587    origin server or gateway on which the resource resides. Any Host
588    item of the request argument's headers dict attribute will be
589    ignored.
590
591    .. _`RFC2616 section 5.2`: http://tools.ietf.org/html/rfc2616#section-5.2
592    """
593    norm_params = normalize_parameters(request.params)
594    uri = normalize_base_string_uri(request.uri)
595    message = construct_base_string(request.http_method, uri, norm_params).encode('utf-8')
596    sig = binascii.a2b_base64(request.signature.encode('utf-8'))
597
598    alg = _jwt_rs1_signing_algorithm()
599    key = _prepare_key_plus(alg, rsa_public_key)
600    return alg.verify(message, key, sig)
601
602
603def verify_plaintext(request, client_secret=None, resource_owner_secret=None):
604    """Verify a PLAINTEXT signature.
605
606    Per `section 3.4`_ of the spec.
607
608    .. _`section 3.4`: http://tools.ietf.org/html/rfc5849#section-3.4
609    """
610    signature = sign_plaintext(client_secret, resource_owner_secret)
611    return safe_string_equals(signature, request.signature)
612