def parse(self, request, msg, name): if not isinstance(msg, dict): raise InvalidMessage('The message must be a dict') if 'type' not in msg: raise InvalidMessage('The type is missing') if isinstance(msg['type'], list): if len(msg['type']) != 1: raise InvalidMessage('Type is multivalued: %s' % msg['type']) msg_type = msg['type'][0] else: msg_type = msg['type'] if 'value' not in msg: raise InvalidMessage('The value is missing') if isinstance(msg['value'], list): if len(msg['value']) != 1: raise InvalidMessage('Value is multivalued: %s' % msg['value']) msg_value = msg['value'][0] else: msg_value = msg['value'] if msg_type not in list(self.types.keys()): raise UnknownMessageType("Type '%s' is unknown" % msg_type) if msg_type not in self.allowed: raise UnallowedMessage("Message type '%s' not allowed" % ( msg_type,)) handler = self.types[msg_type](request) handler.parse(msg_value, name) return handler
def parse(self, msg, name): """Parses the message. We check that the message is properly formatted. :param msg: a json-encoded value containing a JWS or JWE+JWS token :raises InvalidMessage: if the message cannot be parsed or validated :returns: A verified payload """ try: jtok = JWT(jwt=msg) except Exception as e: raise InvalidMessage('Failed to parse message: %s' % str(e)) try: token = jtok.token if isinstance(token, JWE): token.decrypt(self.kkstore.server_keys[KEY_USAGE_ENC]) # If an encrypted payload is received then there must be # a nested signed payload to verify the provenance. payload = token.payload.decode('utf-8') token = JWS() token.deserialize(payload) elif isinstance(token, JWS): pass else: raise TypeError("Invalid Token type: %s" % type(jtok)) # Retrieve client keys for later use self.client_keys = [ JWK(**self._get_key(token.jose_header, KEY_USAGE_SIG)), JWK(**self._get_key(token.jose_header, KEY_USAGE_ENC)) ] # verify token and get payload token.verify(self.client_keys[KEY_USAGE_SIG]) claims = json_decode(token.payload) except Exception as e: logger.debug('Failed to validate message', exc_info=True) raise InvalidMessage('Failed to validate message: %s' % str(e)) check_kem_claims(claims, name) self.name = name self.payload = claims.get('value') self.msg_type = 'kem' return { 'type': self.msg_type, 'value': { 'kid': self.client_keys[KEY_USAGE_ENC].key_id, 'claims': claims } }
def check_kem_claims(claims, name): if 'sub' not in claims: raise InvalidMessage('Missing subject in payload') if claims['sub'] != name: raise InvalidMessage('Key name %s does not match subject %s' % ( name, claims['sub'])) if 'exp' not in claims: raise InvalidMessage('Missing expiration time in payload') if claims['exp'] - (10 * 60) > int(time.time()): raise InvalidMessage('Message expiration too far in the future') if claims['exp'] < int(time.time()): raise InvalidMessage('Message Expired')
def _get_key(self, header, usage): if 'kid' not in header: raise InvalidMessage("Missing key identifier") key = self.kkstore.find_key(header['kid'], usage) if key is None: raise UnknownPublicKey('Key found [kid:%s]' % header['kid']) return json_decode(key)
def parse(self, msg, name): """Parses a simple message :param req: ignored :param msg: the json-decoded value :raises UnknownMessageType: if the type is not 'simple' :raises InvalidMessage: if the message cannot be parsed or validated """ # On requests we imply 'simple' if there is no input message if msg is None: return if not isinstance(msg, string_types): raise InvalidMessage("The 'value' attribute is not a string") self.payload = msg