示例#1
0
    def parse_endpoint(self,
                       metrics,
                       token,
                       version="v1",
                       ckey_header=None,
                       auth_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
            (from Crypto-Key: p256ecdsa=)
        :param auth_header: The Authorization header bearing the VAPID info

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """
        token = self.fernet.decrypt(repad(token).encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            public_key = crypto_key.get_label('p256ecdsa')
        if auth_header:
            vapid_auth = parse_auth_header(auth_header)
            if not vapid_auth:
                raise VapidAuthException("Invalid Auth token")
            metrics.increment("updates.notification.auth.{}".format(
                vapid_auth['scheme']))
            # pull the public key from the VAPID auth header if needed
            try:
                if vapid_auth['version'] != 1:
                    public_key = vapid_auth['k']
            except KeyError:
                raise VapidAuthException("Missing Public Key")
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if not auth_header:
                raise VapidAuthException("Missing Authorization Header")
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise VapidAuthException("Invalid key data")
            try:
                decoded_key = base64url_decode(public_key)
            except TypeError:
                raise VapidAuthException("Invalid key data")
            if not constant_time.bytes_eq(
                    sha256(decoded_key).digest(), token[32:]):
                raise VapidAuthException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    version=version,
                    public_key=public_key)
示例#2
0
 def test_parse_and_get_label(self):
     assert CryptoKey.parse_and_get_label(
         self.valid_key,
         "p256ecdsa") == ("BF92zdI_AKcH5Q31_Rr-04bPqOHU_Qg6lAawHbvfQrY"
                          "xV_vIsAsHSyaiuyfofvxT8ZVIXccykd4V2Z7iJVfreT8")
     assert CryptoKey.parse_and_get_label(self.valid_key, "missing") is None
     assert CryptoKey.parse_and_get_label("invalid key", "missing") is None
示例#3
0
 def test_parse_lenient(self):
     ckey = CryptoKey(self.valid_key.replace('"', ''))
     str = ckey.to_string()
     ckey2 = CryptoKey(str)
     assert ckey.get_keyid("p256dh") == ckey2.get_keyid("p256dh")
     assert ckey.get_label("p256ecdsa") is not None
     assert ckey.get_label("p256ecdsa") == ckey2.get_label("p256ecdsa")
示例#4
0
 def test_string(self):
     ckey = CryptoKey(self.valid_key)
     str = ckey.to_string()
     ckey2 = CryptoKey(str)
     assert ckey.get_keyid("p256dh") == ckey2.get_keyid("p256dh")
     assert ckey.get_label("p256ecdsa") is not None
     assert ckey.get_label("p256ecdsa") == ckey2.get_label("p256ecdsa")
示例#5
0
 def test_string(self):
     ckey = CryptoKey(self.valid_key)
     str = ckey.to_string()
     ckey2 = CryptoKey(str)
     ok_(ckey.get_keyid("p256dh"), ckey2.get_keyid("p256dh"))
     ok_(ckey.get_label("p256ecdsa") is not None)
     ok_(ckey.get_label("p256ecdsa"), ckey2.get_label("p256ecdsa"))
示例#6
0
 def test_parse(self):
     ckey = CryptoKey(self.valid_key)
     eq_(ckey.get_keyid("p256dh"),
         {"keyid": "p256dh",
          "dh": "BDw9T0eImd4ax818VcYqDK_DOhcuDswKero"
                "YyNkdhYmygoLSDlSiWpuoWYUSSFxi25cyyNTR5k9Ny93DzZc0UI4"})
     eq_(ckey.get_label("p256ecdsa"),
         "BF92zdI_AKcH5Q31_Rr-04bPqOHU_Qg6lAawHbvfQrY"
         "xV_vIsAsHSyaiuyfofvxT8ZVIXccykd4V2Z7iJVfreT8")
     ok_(ckey.get_keyid("missing") is None)
     ok_(ckey.get_label("missing") is None)
示例#7
0
 def validate_crypto_key(self, value):
     if CryptoKey.parse_and_get_label(value, "dh"):
         raise InvalidRequest(
             "Do not include 'dh' in aes128gcm "
             "Crypto-Key header",
             status_code=400,
             errno=110)
示例#8
0
 def validate_crypto_key(self, value):
     """Must contain a dh value"""
     dh = CryptoKey.parse_and_get_label(value, "dh")
     if not dh or not VALID_BASE64_URL.match("dh"):
         raise InvalidRequest("Invalid dh value in Encryption-Key header",
                              status_code=400,
                              errno=110)
示例#9
0
 def validate_encryption(self, value):
     if CryptoKey.parse_and_get_label(value, "salt"):
         raise InvalidRequest(
             "Do not include 'salt' in aes128gcm "
             "Encryption header",
             status_code=400,
             errno=110)
示例#10
0
 def validate_encryption(self, value):
     """Must contain a salt value"""
     salt = CryptoKey.parse_and_get_label(value, "salt")
     if not salt or not VALID_BASE64_URL.match(salt):
         raise InvalidRequest("Invalid salt value in Encryption header",
                              status_code=400,
                              errno=110)
示例#11
0
    def parse_endpoint(self, token, version="v0", ckey_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
        (from Crypto-Key: p256ecdsa=)

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """

        token = self.fernet.decrypt(token.encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            label = crypto_key.get_label('p256ecdsa')
            try:
                public_key = base64url_decode(label)
            except:
                # Ignore missing and malformed app server keys.
                pass

        if version == 'v0':
            if not VALID_V0_TOKEN.match(token):
                raise InvalidTokenException("Corrupted push token")
            items = token.split(':')
            return dict(uaid=items[0], chid=items[1], public_key=public_key)
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise InvalidTokenException("Invalid key data")
            if not constant_time.bytes_eq(
                    sha256(public_key).digest(), token[32:]):
                raise InvalidTokenException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    public_key=public_key)
示例#12
0
    def parse_endpoint(self, token, version="v0", ckey_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
        (from Crypto-Key: p256ecdsa=)

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """

        token = self.fernet.decrypt(token.encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            label = crypto_key.get_label('p256ecdsa')
            try:
                public_key = base64url_decode(label)
            except:
                # Ignore missing and malformed app server keys.
                pass

        if version == 'v0':
            if not VALID_V0_TOKEN.match(token):
                raise InvalidTokenException("Corrupted push token")
            items = token.split(':')
            return dict(uaid=items[0], chid=items[1], public_key=public_key)
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise InvalidTokenException("Invalid key data")
            if not constant_time.bytes_eq(sha256(public_key).digest(),
                                          token[32:]):
                raise InvalidTokenException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    public_key=public_key)
示例#13
0
 def test_parse_lenient(self):
     ckey = CryptoKey(self.valid_key.replace('"', ''))
     str = ckey.to_string()
     ckey2 = CryptoKey(str)
     ok_(ckey.get_keyid("p256dh"), ckey2.get_keyid("p256dh"))
     ok_(ckey.get_label("p256ecdsa") is not None)
     ok_(ckey.get_label("p256ecdsa"), ckey2.get_label("p256ecdsa"))
示例#14
0
 def test_string(self):
     ckey = CryptoKey(self.valid_key)
     str = ckey.to_string()
     ckey2 = CryptoKey(str)
     ok_(ckey.get_keyid("p256dh"), ckey2.get_keyid("p256dh"))
     ok_(ckey.get_label("p256ecdsa") is not None)
     ok_(ckey.get_label("p256ecdsa"), ckey2.get_label("p256ecdsa"))
示例#15
0
 def validate_crypto_key(self, value):
     """Must not contain a dh value"""
     dh = CryptoKey.parse_and_get_label(value, "dh")
     if dh:
         raise InvalidRequest(
             "dh value in Crypto-Key header not valid for 01 or earlier "
             "webpush-encryption",
             status_code=400,
             errno=110,
         )
示例#16
0
 def test_parse(self):
     ckey = CryptoKey(self.valid_key)
     assert ckey.get_keyid("p256dh") == {
         "keyid":
         "p256dh",
         "dh":
         "BDw9T0eImd4ax818VcYqDK_DOhcuDswKero"
         "YyNkdhYmygoLSDlSiWpuoWYUSSFxi25cyyNTR5k9Ny93DzZc0UI4"
     }
     assert ckey.get_label("p256ecdsa") == (
         "BF92zdI_AKcH5Q31_Rr-04bPqOHU_Qg6lAawHbvfQrY"
         "xV_vIsAsHSyaiuyfofvxT8ZVIXccykd4V2Z7iJVfreT8")
     assert ckey.get_keyid("missing") is None
     assert ckey.get_label("missing") is None
示例#17
0
 def test_parse_different_order(self):
     ckey = CryptoKey(self.valid_key)
     ckey2 = CryptoKey(','.join(self.valid_key.split(',')[::-1]))
     assert ckey.get_keyid("p256dh") == ckey2.get_keyid("p256dh")
     assert ckey.get_label("p256ecdsa") is not None
     assert ckey.get_label("p256ecdsa") == ckey2.get_label("p256ecdsa")
示例#18
0
 def test_parse_invalid(self):
     with pytest.raises(CryptoKeyException) as ex:
         CryptoKey("invalid key")
     assert ex.value.message == "Invalid Crypto Key value"
示例#19
0
 def test_parse_different_order(self):
     ckey = CryptoKey(self.valid_key)
     ckey2 = CryptoKey(','.join(self.valid_key.split(',')[::-1]))
     ok_(ckey.get_keyid("p256dh"), ckey2.get_keyid("p256dh"))
     ok_(ckey.get_label("p256ecdsa") is not None)
     ok_(ckey.get_label("p256ecdsa"), ckey2.get_label("p256ecdsa"))