def test_token_validation(self): manager = tokenlib.TokenManager(timeout=0.2) token = manager.make_token({"hello": "world"}) token_bytes = decode_token_bytes(token) # Proper token == valid. data = manager.parse_token(token) self.assertEquals(data["hello"], "world") # Badly-encoded bytes == not valid. bad_token = "@" + token[1:] with self.assertRaises(errors.MalformedTokenError): manager.parse_token(bad_token) # Bad signature == not valid. bad_token = encode_token_bytes(b"X" * 50) with self.assertRaises(errors.InvalidSignatureError): manager.parse_token(bad_token) bad_token_bytes = token_bytes[:-1] bad_token_bytes += b"X" if token_bytes[-1] == b"Z" else b"Z" bad_token = encode_token_bytes(bad_token_bytes) with self.assertRaises(errors.InvalidSignatureError): manager.parse_token(bad_token) # Modified payload == not valid. bad_token = encode_token_bytes(b"admin" + token_bytes[6:]) with self.assertRaises(errors.InvalidSignatureError): manager.parse_token(bad_token) # Expired token == not valid. time.sleep(0.2) with self.assertRaises(errors.ExpiredTokenError): manager.parse_token(token)
def test_get_derived_secret_errors_out_for_malformed_tokens(self): manager = tokenlib.TokenManager() digest_size = manager.hashmod_digest_size bad_token = encode_token_bytes(b"{}" + (b"X" * digest_size)) with self.assertRaises(errors.MalformedTokenError): manager.get_derived_secret(bad_token) bad_token = encode_token_bytes(b"42" + (b"X" * digest_size)) with self.assertRaises(errors.MalformedTokenError): manager.get_derived_secret(bad_token) bad_token = encode_token_bytes(b"NOTJSON" + (b"X" * digest_size)) with self.assertRaises(errors.MalformedTokenError): manager.get_derived_secret(bad_token)
def get_derived_secret(self, token): """Get the derived secret key associated with the given token. A per-token secret key is calculated by deriving it from the master secret with HKDF. """ try: payload = decode_token_bytes(token)[:-self.hashmod_digest_size] salt = json.loads(payload.decode("utf8"))["salt"].encode("ascii") except (TypeError, KeyError, ValueError) as e: raise errors.MalformedTokenError(str(e)) info = HKDF_INFO_DERIVE + token.encode("ascii") secret = HKDF(self.secret, salt=salt, info=info, size=self.hashmod_digest_size, hashmod=self.hashmod) return encode_token_bytes(secret)
def make_token(self, data): """Generate a new token embedding the given dict of data. The token is a JSON dump of the given data along with an expiry time and salt. It has a HMAC signature appended and is b64-encoded for transmission. """ data = data.copy() if "salt" not in data: data["salt"] = hexlify(os.urandom(3)).decode("ascii") if "expires" not in data: data["expires"] = time.time() + self.timeout payload = json.dumps(data).encode("utf8") sig = self._get_signature(payload) assert len(sig) == self.hashmod_digest_size return encode_token_bytes(payload + sig)