Beispiel #1
0
    def validate_token(self, token):
        buf = ssh.base64url_decode(token)
        hmac_token = protocol.VerifiablePayload.deserialize(buf)

        t = protocol.Token.deserialize(hmac_token.payload)

        if t.valid_to < self.now_func():
            s = "Token expired at " + time.strftime("%Y-%m-%d %H:%M:%S UTC",
                                                    time.gmtime(t.valid_to))
            raise exceptions.TokenExpiredException(s)

        if t.valid_from > self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(t.valid_from))
            raise exceptions.TokenExpiredException("Token created at %s" % s)

        if (t.valid_to - t.valid_from) > MAX_TOKEN_LIFETIME:
            raise exceptions.InvalidInputException("Token lifetime too long")

        if not hmac_token.verify(self._hmac):
            raise exceptions.InvalidInputException("Token hmac verification "
                                                   "failed, not matching our "
                                                   "secret")

        return t.username
Beispiel #2
0
def create_response(challenge, server_name, signer_plug=None, packing=xdr_packing):
    """Called by a client with the challenge provided by the server
    to generate a response using the local ssh-agent"""

    b = ssh.base64url_decode(challenge)

    hmac_challenge = protocol.VerifiablePayload.deserialize(packing, b)

    challenge = protocol.Challenge.deserialize(packing, hmac_challenge.payload)

    if challenge.server_name != server_name:
        s = "Possible MITM attack. Challenge originates from '%s' " "and not '%s'" % (
            challenge.server_name,
            server_name,
        )
        raise exceptions.InvalidInputException(s)
    if not signer_plug:
        signer_plug = ssh.AgentSigner()

    challenge = protocol.Challenge.deserialize(packing, hmac_challenge.payload)

    signature = signer_plug.sign(challenge.serialize(packing))

    signer_plug.close()

    response = protocol.Response(signature=signature, hmac_challenge=hmac_challenge)

    return ssh.base64url_encode(response.serialize(packing))
Beispiel #3
0
    def validate_token(self, token):
        buf = ssh.base64url_decode(token)
        hmac_token = protocol.VerifiablePayload.deserialize(buf)

        t = protocol.Token.deserialize(hmac_token.payload)

        if t.valid_to < self.now_func():
            s = "Token expired at " + time.strftime("%Y-%m-%d %H:%M:%S UTC",
                                                    time.gmtime(t.valid_to))
            raise exceptions.TokenExpiredException(s)

        if t.valid_from > self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(t.valid_from))
            raise exceptions.TokenExpiredException("Token created at %s" % s)

        if (t.valid_to - t.valid_from) > MAX_TOKEN_LIFETIME:
            raise exceptions.InvalidInputException("Token lifetime too long")

        if not hmac_token.verify(self._hmac):
            raise exceptions.InvalidInputException("Token hmac verification "
                                                   "failed, not matching our "
                                                   "secret")

        return t.username
Beispiel #4
0
    def test_create_challenge_v1(self):
        auth_server = server.AuthServer("secret", DummyKeyProvider(),
                                        "server.name")
        challenge = auth_server.create_challenge("noa", 1)
        cb = ssh.base64url_decode(challenge)

        decoded_challenge = msgpack_protocol.Challenge.deserialize(cb)

        self.assertEquals("\xfb\xa1\xeao\xd3y", decoded_challenge.fingerprint)
Beispiel #5
0
    def test_create_challenge_v1(self):
        auth_server = server.AuthServer("secret", DummyKeyProvider(),
                                        "server.name")
        challenge = auth_server.create_challenge("noa", 1)
        cb = ssh.base64url_decode(challenge)

        decoded_challenge = msgpack_protocol.Challenge.deserialize(cb)

        self.assertEquals("\xfb\xa1\xeao\xd3y", decoded_challenge.fingerprint)
Beispiel #6
0
    def test_create_challenge(self):
        auth_server = server.AuthServer("gurka", DummyKeyProvider(), "server_name")
        s = auth_server.create_challenge("noa")
        cb = ssh.base64url_decode(s)

        verifiable_payload = protocol.VerifiablePayload.deserialize(cb)

        challenge = protocol.Challenge.deserialize(verifiable_payload.payload)

        self.assertEquals("\xfb\xa1\xeao\xd3y", challenge.fingerprint)
Beispiel #7
0
    def test_create_challenge(self):
        auth_server = server.AuthServer("gurka", DummyKeyProvider(),
                                        "server.name")
        s = auth_server.create_challenge("noa")
        cb = ssh.base64url_decode(s)

        verifiable_payload = protocol.VerifiablePayload.deserialize(cb)

        challenge = protocol.Challenge.deserialize(verifiable_payload.payload)

        self.assertEquals("\xfb\xa1\xeao\xd3y", challenge.fingerprint)
Beispiel #8
0
    def create_token(self, response):
        """
        This method verifies that the response given from the client
        is valid and if so returns a token used for authentication.
        """
        try:
            s = ssh.base64url_decode(response)
        except exceptions.CrtAuthError as why:
            raise exceptions.InvalidInputException(why)

        r = protocol.Response.deserialize(s)

        if not r.hmac_challenge.verify(self._hmac):
            s = "Challenge hmac verification failed, not matching our secret"
            raise exceptions.InvalidInputException(s)

        # verify the integrity of the challenge in the response

        challenge = protocol.Challenge.deserialize(r.hmac_challenge.payload)

        if self.server_name != challenge.server_name:
            s = "Got challenge with the wrong server_name encoded"
            raise exceptions.InvalidInputException(s)

        key = self.key_provider.get_key(challenge.username)

        if not key.verify_signature(r.signature, r.hmac_challenge.payload):
            raise exceptions.InvalidInputException("Client did not provide "
                                                   "proof that it controls "
                                                   "the secret key")

        if challenge.valid_from > self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(challenge.valid_from))
            raise exceptions.InvalidInputException("Response with challenge "
                                                   "created as %s too new "
                                                   % s)

        if challenge.valid_to < self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(challenge.valid_from))
            raise exceptions.InvalidInputException("Response with challenge "
                                                   "created as %s too old "
                                                   % s)

        expire_time = int(self.now_func()) + self.token_lifetime

        return self._make_token(challenge.username, expire_time)
Beispiel #9
0
    def parse_request(request):
        """
        This method contains logic to detect a v1 and beyond request and
        differentiate it from a version 0 request, which is just an ascii
        username. While all v1 requests are also valid usernames (the curse
        and blessing of base64 encoding) it is pretty unlikely that a username
        happens to also decode to a valid msgpack message with the correct
        magic values.

        @return a tuple containing username then version
        """
        if len(request) % 4 == 1:
            # strings of length 1, 5, 9.. has invalid base64 padding, so they
            # must be ascii usernames.
            return request, 0
        binary = ssh.base64url_decode(request)
        if len(binary) < 4:
            return request, 0
        if ord(binary[0]) > 4 or binary[1] != 'q':
            # This code handles version values up to 4. Should give plenty
            # of time to forget all about the unversioned version 0
            return request, 0
        b = ord(binary[2])
        if (b < 0xa1 or b > 0xbf) and b != 0xd9:
            # third byte does not indicate a string longer than 0 and shorter
            # than 256 octets long (According to UTF_8 rfc3629, a unicode
            # char can encode to at most 4 UTF-8 bytes, and username values
            # in crtauth is limited to 64 characters, thus the max number of
            # bytes a username can be is 64 * 4 == 256
            return request, 0
        if b == 0xd9:
            username_start = 4
            username_len = ord(binary[3])
        else:
            username_start = 3
            username_len = ord(binary[2]) & 0x1f

        if len(binary) - username_start < username_len:
            # decoded string is too short
            return request, 0

        return binary[username_start:username_start + username_len], 1
Beispiel #10
0
    def parse_request(request):
        """
        This method contains logic to detect a v1 and beyond request and
        differentiate it from a version 0 request, which is just an ascii
        username. While all v1 requests are also valid usernames (the curse
        and blessing of base64 encoding) it is pretty unlikely that a username
        happens to also decode to a valid msgpack message with the correct
        magic values.

        @return a tuple containing username then version
        """
        if len(request) % 4 == 1:
            # strings of length 1, 5, 9.. has invalid base64 padding, so they
            # must be ascii usernames.
            return request, 0
        binary = ssh.base64url_decode(request)
        if len(binary) < 4:
            return request, 0
        if ord(binary[0]) > 4 or binary[1] != 'q':
            # This code handles version values up to 4. Should give plenty
            # of time to forget all about the unversioned version 0
            return request, 0
        b = ord(binary[2])
        if (b < 0xa1 or b > 0xbf) and b != 0xd9:
            # third byte does not indicate a string longer than 0 and shorter
            # than 256 octets long (According to UTF_8 rfc3629, a unicode
            # char can encode to at most 4 UTF-8 bytes, and username values
            # in crtauth is limited to 64 characters, thus the max number of
            # bytes a username can be is 64 * 4 == 256
            return request, 0
        if b == 0xd9:
            username_start = 4
            username_len = ord(binary[3])
        else:
            username_start = 3
            username_len = ord(binary[2]) & 0x1f

        if len(binary) - username_start < username_len:
            # decoded string is too short
            return request, 0

        return binary[username_start:username_start + username_len], 1
Beispiel #11
0
def create_response(challenge, server_name, signer_plug=None):
    """Called by a client with the challenge provided by the server
    to generate a response using the local ssh-agent"""

    b = ssh.base64url_decode(challenge)

    if b[0] == 'v':
        # this is version 0 challenge
        hmac_challenge = protocol.VerifiablePayload.deserialize(b)
        challenge = protocol.Challenge.deserialize(hmac_challenge.payload)
        to_sign = hmac_challenge.payload
        version_1 = False
    elif b[0] == '\x01':
        # version 1
        challenge = msgpack_protocol.Challenge.deserialize(b)
        to_sign = b
        version_1 = True
    else:
        raise exceptions.ProtocolError("invalid first byte of challenge")

    if challenge.server_name != server_name:
        s = ("Possible MITM attack. Challenge originates from '%s' "
             "and not '%s'" % (challenge.server_name, server_name))
        raise exceptions.InvalidInputException(s)

    if not signer_plug:
        signer_plug = ssh.AgentSigner()

    signature = signer_plug.sign(to_sign, challenge.fingerprint)

    signer_plug.close()

    if version_1:
        response = msgpack_protocol.Response(challenge=b, signature=signature)
    else:
        response = protocol.Response(
            signature=signature, hmac_challenge=hmac_challenge)

    return ssh.base64url_encode(response.serialize())
Beispiel #12
0
def create_response(challenge, server_name, signer_plug=None):
    """Called by a client with the challenge provided by the server
    to generate a response using the local ssh-agent"""

    b = ssh.base64url_decode(challenge)

    if b[0] == 'v':
        # this is version 0 challenge
        hmac_challenge = protocol.VerifiablePayload.deserialize(b)
        challenge = protocol.Challenge.deserialize(hmac_challenge.payload)
        to_sign = hmac_challenge.payload
        version_1 = False
    elif b[0] == '\x01':
        # version 1
        challenge = msgpack_protocol.Challenge.deserialize(b)
        to_sign = b
        version_1 = True
    else:
        raise exceptions.ProtocolError("invalid first byte of challenge")

    if challenge.server_name != server_name:
        s = ("Possible MITM attack. Challenge originates from '%s' "
             "and not '%s'" % (challenge.server_name, server_name))
        raise exceptions.InvalidInputException(s)

    if not signer_plug:
        signer_plug = ssh.AgentSigner()

    signature = signer_plug.sign(to_sign, challenge.fingerprint)

    signer_plug.close()

    if version_1:
        response = msgpack_protocol.Response(challenge=b, signature=signature)
    else:
        response = protocol.Response(signature=signature,
                                     hmac_challenge=hmac_challenge)

    return ssh.base64url_encode(response.serialize())
Beispiel #13
0
    def create_token(self, response):
        """
        This method verifies that the response given from the client
        is valid and if so returns a token used for authentication.
        """
        s = ssh.base64url_decode(response)

        if s[0] == 'r':
            # this is a version 0 response
            version_1 = False
            if self.lowest_supported_version > 0:
                raise exceptions.ProtocolVersionError(
                    "Client needs to support at least version %d" %
                    self.lowest_supported_version)
            r = protocol.Response.deserialize(s)
            if not r.hmac_challenge.verify(self._hmac):
                raise exceptions.InvalidInputException(
                    "Challenge hmac verification failed, not matching  secret")
            challenge = protocol.Challenge.deserialize(
                r.hmac_challenge.payload)
        elif s[0] == '\x01':
            # this is a version 1 response
            version_1 = True
            r = msgpack_protocol.Response.deserialize(s)
            challenge = msgpack_protocol.Challenge.deserialize_authenticated(
                r.challenge, self.secret)
        else:
            raise exceptions.ProtocolError("invalid first byte of response")

        # verify the integrity of the challenge in the response
        if self.server_name != challenge.server_name:
            s = "Got challenge with the wrong server_name encoded"
            raise exceptions.InvalidInputException(s)

        key = self.key_provider.get_key(challenge.username)

        if version_1:
            if not key.verify_signature(r.signature, r.challenge):
                raise exceptions.InvalidInputException(
                    "Client did not provide proof that it controls "
                    "the secret key")
        else:
            if not key.verify_signature(r.signature, r.hmac_challenge.payload):
                raise exceptions.InvalidInputException(
                    "Client did not provide proof that it controls "
                    "the secret key")

        if challenge.valid_from > self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(challenge.valid_from))
            raise exceptions.InvalidInputException("Response with challenge "
                                                   "created as %s too new " %
                                                   s)

        if challenge.valid_to < self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(challenge.valid_from))
            raise exceptions.InvalidInputException("Response with challenge "
                                                   "created as %s too old " %
                                                   s)

        expire_time = int(self.now_func()) + self.token_lifetime

        return self._make_token(challenge.username, expire_time)
Beispiel #14
0
 def test_read_binary_key(self):
     key = rsa.RSAPublicKey(ssh.base64url_decode(s.split(" ")[1]))
     self.assertEqual(key.fingerprint(), "\xfb\xa1\xeao\xd3y")
     self.assertEqual(key.decoded, inner_s)
     self.assertEqual(key.encoded[:15], "\x00\x00\x00\x07ssh-rsa"
                                        "\x00\x00\x00\x01")
Beispiel #15
0
 def test_b64_roundtrip(self):
     l = ["a", "ab", "abc", "abcd"]
     for i in l:
         self.assertEquals(ssh.base64url_decode(ssh.base64url_encode(i)), i)
Beispiel #16
0
 def test_b64_roundtrip(self):
     l = ["a", "ab", "abc", "abcd"]
     for i in l:
         self.assertEquals(ssh.base64url_decode(ssh.base64url_encode(i)), i)
Beispiel #17
0
 def test_read_binary_key(self):
     key = rsa.RSAPublicKey(ssh.base64url_decode(s.split(" ")[1]))
     self.assertEqual(key.fingerprint(), "\xfb\xa1\xeao\xd3y")
     self.assertEqual(key.decoded, inner_s)
     self.assertEqual(key.encoded[:15], "\x00\x00\x00\x07ssh-rsa"
                      "\x00\x00\x00\x01")
Beispiel #18
0
    def create_token(self, response):
        """
        This method verifies that the response given from the client
        is valid and if so returns a token used for authentication.
        """
        s = ssh.base64url_decode(response)

        if s[0] == 'r':
            # this is a version 0 response
            version_1 = False
            if self.lowest_supported_version > 0:
                raise exceptions.ProtocolVersionError(
                    "Client needs to support at least version %d"
                    % self.lowest_supported_version
                )
            r = protocol.Response.deserialize(s)
            if not r.hmac_challenge.verify(self._hmac):
                raise exceptions.InvalidInputException(
                    "Challenge hmac verification failed, not matching  secret"
                )
            challenge = protocol.Challenge.deserialize(r.hmac_challenge.payload)
        elif s[0] == '\x01':
            # this is a version 1 response
            version_1 = True
            r = msgpack_protocol.Response.deserialize(s)
            challenge = msgpack_protocol.Challenge.deserialize_authenticated(
                r.challenge, self.secret)
        else:
            raise exceptions.ProtocolError("invalid first byte of response")

        # verify the integrity of the challenge in the response
        if self.server_name != challenge.server_name:
            s = "Got challenge with the wrong server_name encoded"
            raise exceptions.InvalidInputException(s)

        key = self.key_provider.get_key(challenge.username)

        if challenge.valid_from > self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(challenge.valid_from))
            raise exceptions.InvalidInputException("Response with challenge "
                                                   "created as %s too new "
                                                   % s)

        if challenge.valid_to < self.now_func():
            s = time.strftime("%Y-%m-%d %H:%M:%S UTC",
                              time.gmtime(challenge.valid_from))
            raise exceptions.InvalidInputException("Response with challenge "
                                                   "created as %s too old "
                                                   % s)

        if version_1:
            if not key.verify_signature(r.signature, r.challenge):
                raise exceptions.InvalidInputException(
                    "Client did not provide proof that it controls "
                    "the secret key")
        else:
            if not key.verify_signature(r.signature, r.hmac_challenge.payload):
                raise exceptions.InvalidInputException(
                    "Client did not provide proof that it controls "
                    "the secret key")

        expire_time = int(self.now_func()) + self.token_lifetime

        return self._make_token(challenge.username, expire_time)