示例#1
0
def sign_request(request, id, key, hashmod=None, params=None):
    """Sign the given request using MAC access authentication.

    This function implements the client-side request signing algorithm as
    expected by the server, i.e. MAC access authentication as defined by
    RFC-TODO.  It takes a WebOb Request object and inserts the appropriate
    signature into its Authorization header.
    """
    # Use explicitly-given parameters, or those from the request.
    if params is None:
        params = utils.parse_authz_header(request, {})
        if params and params.pop("scheme") != "MAC":
            params.clear()
    # Give sensible values to any parameters that weren't specified.
    params["id"] = id
    if "ts" not in params:
        params["ts"] = str(int(time.time()))
    if "nonce" not in params:
        params["nonce"] = os.urandom(5).encode("hex")
    # Calculate the signature and add it to the parameters.
    params["mac"] = get_signature(request, key, hashmod, params)
    # Serialize the parameters back into the authz header.
    # WebOb has logic to do this that's not perfect, but good enough for us.
    request.authorization = ("MAC", params)
    return request.headers["Authorization"]
示例#2
0
def sign_request(request, id, key, hashmod=None, params=None):
    """Sign the given request using MAC access authentication.

    This function implements the client-side request signing algorithm as
    expected by the server, i.e. MAC access authentication as defined by
    RFC-TODO.  It takes a WebOb Request object and inserts the appropriate
    signature into its Authorization header.
    """
    # Use explicitly-given parameters, or those from the request.
    if params is None:
        params = utils.parse_authz_header(request, {})
        if params and params.pop("scheme") != "MAC":
            params.clear()
    # Give sensible values to any parameters that weren't specified.
    params["id"] = id
    if "ts" not in params:
        params["ts"] = str(int(time.time()))
    if "nonce" not in params:
        params["nonce"] = str(binascii.hexlify(os.urandom(5)).decode('ascii'))
    # Calculate the signature and add it to the parameters.
    params["mac"] = str(get_signature(request, key, hashmod, params))
    # Serialize the parameters back into the authz header.
    # WebOb has logic to do this that's not perfect, but good enough for us.
    request.authorization = ("MAC", params)
    return request.headers["Authorization"]
 def test_check_signature_fails_with_busted_signature(self):
     req = Request.blank("/")
     sign_request(req, "myid", "mykey")
     signature = parse_authz_header(req)["mac"]
     authz = req.environ["HTTP_AUTHORIZATION"]
     authz = authz.replace(signature, "XXX" + signature)
     req.environ["HTTP_AUTHORIZATION"] = authz
     self.assertFalse(check_signature(req, "mykey"))
示例#4
0
 def test_check_signature_fails_with_busted_signature(self):
     req = Request.blank("/")
     sign_request(req, "myid", "mykey")
     signature = parse_authz_header(req)["mac"]
     authz = req.environ["HTTP_AUTHORIZATION"]
     authz = authz.replace(signature, "XXX" + signature)
     req.environ["HTTP_AUTHORIZATION"] = authz
     self.assertFalse(check_signature(req, "mykey"))
示例#5
0
def get_id(request, params=None):
    """Get the MAC id from the given request.

    This function extracts the claimed MAC id from the authorization header of
    the given request.  It does not verify the signature, since that would
    require looking up the corresponding MAC secret key.
    """
    if params is None:
        params = utils.parse_authz_header(request, {})
    if params.get("scheme") != "MAC":
        return None
    return params.get("id", None)
示例#6
0
def get_id(request, params=None):
    """Get the MAC id from the given request.

    This function extracts the claimed MAC id from the authorization header of
    the given request.  It does not verify the signature, since that would
    require looking up the corresponding MAC secret key.
    """
    if params is None:
        params = utils.parse_authz_header(request, {})
    if params.get("scheme") != "MAC":
        return None
    return params.get("id", None)
示例#7
0
def get_signature(request, key, hashmod=None, params=None):
    """Get the MAC signature for the given request.

    This function calculates the MAC signature for the given request and
    returns it as a string.

    If the "params" parameter is not None, it is assumed to be a pre-parsed
    dict of MAC parameters as one might find in the Authorization header.  If
    it is missing or None then the Authorization header from the request will
    be parsed to determine the necessary parameters.
    """
    if params is None:
        params = utils.parse_authz_header(request, {})
    if hashmod is None:
        hashmod = sha1
    sigstr = utils.get_normalized_request_string(request, params)
    return b64encode(hmac.new(key, sigstr, hashmod).digest())
示例#8
0
def get_signature(request, key, hashmod=None, params=None):
    """Get the MAC signature for the given request.

    This function calculates the MAC signature for the given request and
    returns it as a string.

    If the "params" parameter is not None, it is assumed to be a pre-parsed
    dict of MAC parameters as one might find in the Authorization header.  If
    it is missing or None then the Authorization header from the request will
    be parsed to determine the necessary parameters.
    """
    if params is None:
        params = utils.parse_authz_header(request, {})
    if hashmod is None:
        hashmod = sha1
    sigstr = utils.get_normalized_request_string(request, params)
    return b64encode(hmac.new(key, sigstr, hashmod).digest())
示例#9
0
def check_signature(request, key, hashmod=None, params=None, nonces=None):
    """Check that the request is correctly signed with the given MAC key.

    This function checks the MAC signature in the given request against its
    expected value, returning True if they match and false otherwise.

    If the "params" parameter is not None, it is assumed to be a pre-parsed
    dict of MAC parameters as one might find in the Authorization header.  If
    it is missing or  None then the Authorization header from the request will
    be parsed to determine the necessary parameters.

    If the "nonces" parameter is not None, it must be a NonceCache object
    used to check validity of the signature nonce.  If not specified then a
    default global cache will be used.  To disable nonce checking (e.g. during
    testing) pass nonces=False.
    """
    global DEFAULT_NONCE_CACHE
    if nonces is None:
        nonces = DEFAULT_NONCE_CACHE
        if nonces is None:
            nonces = DEFAULT_NONCE_CACHE = NonceCache()
    if params is None:
        params = utils.parse_authz_header(request, {})
    if params.get("scheme") != "MAC":
        return False
    # Any KeyError here indicates a missing parameter.
    # Any ValueError here indicates an invalid parameter.
    try:
        id = params["id"]
        timestamp = int(params["ts"])
        nonce = params["nonce"]
        # Check validity of the signature.
        expected_sig = get_signature(request, key, hashmod, params)
        if utils.strings_differ(params["mac"], expected_sig):
            return False
        # Check freshness of the nonce.
        # This caches it so future use of the nonce will fail.
        # We do this *after* successul sig check to avoid DOS attacks.
        if nonces is not False:
            if not nonces.check_nonce(id, timestamp, nonce):
                return False
    except (KeyError, ValueError):
        return False
    return True
示例#10
0
def check_signature(request, key, hashmod=None, params=None, nonces=None):
    """Check that the request is correctly signed with the given MAC key.

    This function checks the MAC signature in the given request against its
    expected value, returning True if they match and false otherwise.

    If the "params" parameter is not None, it is assumed to be a pre-parsed
    dict of MAC parameters as one might find in the Authorization header.  If
    it is missing or  None then the Authorization header from the request will
    be parsed to determine the necessary parameters.

    If the "nonces" parameter is not None, it must be a NonceCache object
    used to check validity of the signature nonce.  If not specified then a
    default global cache will be used.  To disable nonce checking (e.g. during
    testing) pass nonces=False.
    """
    global DEFAULT_NONCE_CACHE
    if nonces is None:
        nonces = DEFAULT_NONCE_CACHE
        if nonces is None:
            nonces = DEFAULT_NONCE_CACHE = NonceCache()
    if params is None:
        params = utils.parse_authz_header(request, {})
    if params.get("scheme") != "MAC":
        return False
    # Any KeyError here indicates a missing parameter.
    # Any ValueError here indicates an invalid parameter.
    try:
        id = params["id"]
        timestamp = int(params["ts"])
        nonce = params["nonce"]
        # Check validity of the signature.
        expected_sig = get_signature(request, key, hashmod, params)
        if utils.strings_differ(params["mac"], expected_sig):
            return False
        # Check freshness of the nonce.
        # This caches it so future use of the nonce will fail.
        # We do this *after* successul sig check to avoid DOS attacks.
        if nonces is not False:
            if not nonces.check_nonce(id, timestamp, nonce):
                return False
    except (KeyError, ValueError):
        return False
    return True
示例#11
0
def get_signature(request, key, hashmod=None, params=None):
    """Get the MAC signature for the given request.

    This function calculates the MAC signature for the given request and
    returns it as a string.

    If the "params" parameter is not None, it is assumed to be a pre-parsed
    dict of MAC parameters as one might find in the Authorization header.  If
    it is missing or None then the Authorization header from the request will
    be parsed to determine the necessary parameters.
    """
    if params is None:
        params = utils.parse_authz_header(request, {})
    if hashmod is None:
        hashmod = sha1
    sigstr = utils.get_normalized_request_string(request, params)
    # The spec mandates that ids and keys must be ascii.
    # It's therefore safe to encode like this before doing the signature.
    sigstr = sigstr.encode("ascii")
    key = key.encode("ascii")
    return utils.b64encode(hmac.new(key, sigstr, hashmod).digest())
示例#12
0
def get_signature(request, key, hashmod=None, params=None):
    """Get the MAC signature for the given request.

    This function calculates the MAC signature for the given request and
    returns it as a string.

    If the "params" parameter is not None, it is assumed to be a pre-parsed
    dict of MAC parameters as one might find in the Authorization header.  If
    it is missing or None then the Authorization header from the request will
    be parsed to determine the necessary parameters.
    """
    if params is None:
        params = utils.parse_authz_header(request, {})
    if hashmod is None:
        hashmod = sha1
    sigstr = utils.get_normalized_request_string(request, params)
    # The spec mandates that ids and keys must be ascii.
    # It's therefore safe to encode like this before doing the signature.
    sigstr = sigstr.encode("ascii")
    key = key.encode("ascii")
    return utils.b64encode(hmac.new(key, sigstr, hashmod).digest())
示例#13
0
    def test_parse_authz_header(self):
        def req(authz):
            """Make a fake request with the given authz header."""
            class request:
                environ = {"HTTP_AUTHORIZATION": authz}

            return request

        # Test parsing of a single unquoted parameter.
        params = parse_authz_header(req('Digest realm=hello'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['realm'], 'hello')

        # Test parsing of multiple parameters with mixed quotes.
        params = parse_authz_header(req('Digest test=one, again="two"'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['test'], 'one')
        self.assertEquals(params['again'], 'two')

        # Test parsing of an escaped quote and empty string.
        params = parse_authz_header(req('Digest test="\\"",again=""'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['test'], '"')
        self.assertEquals(params['again'], '')

        # Test parsing of embedded commas, escaped and non-escaped.
        params = parse_authz_header(req('Digest one="1\\,2", two="3,4"'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['one'], '1,2')
        self.assertEquals(params['two'], '3,4')

        # Test parsing on various malformed inputs
        self.assertRaises(ValueError, parse_authz_header, req(None))
        self.assertRaises(ValueError, parse_authz_header, req(""))
        self.assertRaises(ValueError, parse_authz_header, req(" "))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken raw-token'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="unclosed-quote'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm=unopened-quote"'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="unescaped"quote"'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="escaped-end-quote\\"'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="duplicated",,what=comma'))

        # Test all those again, but returning a default value
        self.assertEquals(None, parse_authz_header(req(None), None))
        self.assertEquals(None, parse_authz_header(req(""), None))
        self.assertEquals(None, parse_authz_header(req(" "), None))
        self.assertEquals(None,
                          parse_authz_header(req('Broken raw-token'), None))
        self.assertEquals(
            None, parse_authz_header(req('Broken realm="unclosed-quote'),
                                     None))
        self.assertEquals(
            None, parse_authz_header(req('Broken realm=unopened-quote"'),
                                     None))
        self.assertEquals(
            None,
            parse_authz_header(req('Broken realm="unescaped"quote"'), None))
        self.assertEquals(
            None,
            parse_authz_header(req('Broken realm="escaped-end-quote\\"'),
                               None))
        self.assertEquals(
            None,
            parse_authz_header(req('Broken realm="duplicated",,what=comma'),
                               None))
示例#14
0
    def test_parse_authz_header(self):
        def req(authz):
            """Make a fake request with the given authz header."""
            class request:
                environ = {"HTTP_AUTHORIZATION": authz}
            return request

        # Test parsing of a single unquoted parameter.
        params = parse_authz_header(req('Digest realm=hello'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['realm'], 'hello')

        # Test parsing of multiple parameters with mixed quotes.
        params = parse_authz_header(req('Digest test=one, again="two"'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['test'], 'one')
        self.assertEquals(params['again'], 'two')

        # Test parsing of an escaped quote and empty string.
        params = parse_authz_header(req('Digest test="\\"",again=""'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['test'], '"')
        self.assertEquals(params['again'], '')

        # Test parsing of embedded commas, escaped and non-escaped.
        params = parse_authz_header(req('Digest one="1\\,2", two="3,4"'))
        self.assertEquals(params['scheme'], 'Digest')
        self.assertEquals(params['one'], '1,2')
        self.assertEquals(params['two'], '3,4')

        # Test parsing on various malformed inputs
        self.assertRaises(ValueError, parse_authz_header, req(None))
        self.assertRaises(ValueError, parse_authz_header, req(""))
        self.assertRaises(ValueError, parse_authz_header, req(" "))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken raw-token'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="unclosed-quote'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm=unopened-quote"'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="unescaped"quote"'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="escaped-end-quote\\"'))
        self.assertRaises(ValueError, parse_authz_header,
                          req('Broken realm="duplicated",,what=comma'))

        # Test all those again, but returning a default value
        self.assertEquals(None, parse_authz_header(req(None), None))
        self.assertEquals(None, parse_authz_header(req(""), None))
        self.assertEquals(None, parse_authz_header(req(" "), None))
        self.assertEquals(None,
                          parse_authz_header(req('Broken raw-token'), None))
        self.assertEquals(None, parse_authz_header(
                          req('Broken realm="unclosed-quote'), None))
        self.assertEquals(None, parse_authz_header(
                          req('Broken realm=unopened-quote"'), None))
        self.assertEquals(None, parse_authz_header(
                          req('Broken realm="unescaped"quote"'), None))
        self.assertEquals(None, parse_authz_header(
                          req('Broken realm="escaped-end-quote\\"'), None))
        self.assertEquals(None, parse_authz_header(
                          req('Broken realm="duplicated",,what=comma'), None))