Commit message: gxps-archive: Ensure gxps_archive_read_entry() fills the GError in case of failure

And fix the callers to not overwrite the GError.
CVE: CVE-2018-10733
CWE: CWE-125
Vulnerability description: There is a heap-based buffer over-read in the function ft_font_face_hash of gxps-fonts.c in libgxps through 0.3.0. A crafted input will lead to a remote denial of service attack.

Project: libgxps
File: gxps-archive.c

gxps_archive_initable_init (GInitable     *initable,
			    GCancellable  *cancellable,
			    GError       **error)
{
	GXPSArchive          *archive;
	ZipArchive           *zip;
	struct archive_entry *entry;
	const gchar          *pathname;

	archive = GXPS_ARCHIVE (initable);

	if (archive->initialized) {
		if (archive->init_error) {
			g_propagate_error (error, g_error_copy (archive->init_error));
			return FALSE;
		}

		return TRUE;
	}

	archive->initialized = TRUE;

	zip = gxps_zip_archive_create (archive->filename);
	if (zip->error) {
		g_propagate_error (&archive->init_error, zip->error);
		g_propagate_error (error, g_error_copy (archive->init_error));
		gxps_zip_archive_destroy (zip);
		return FALSE;
	}

        while (gxps_zip_archive_iter_next (zip, &entry)) {
                /* FIXME: We can ignore directories here */
                pathname = archive_entry_pathname (entry);
                if (pathname != NULL)
                        g_hash_table_add (archive->entries, g_strdup (pathname));
                archive_read_data_skip (zip->archive);
        }

	gxps_zip_archive_destroy (zip);

	return TRUE;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_class_init (GXPSArchiveClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->set_property = gxps_archive_set_property;
	object_class->finalize = gxps_archive_finalize;

	g_object_class_install_property (object_class,
					 PROP_FILE,
					 g_param_spec_object ("file",
							      "File",
							      "The archive file",
							      G_TYPE_FILE,
							      G_PARAM_WRITABLE |
							      G_PARAM_CONSTRUCT_ONLY));
}

Project: libgxps
File: gxps-fonts.c

init_ft_lib (void)
{
	static gsize ft_init = 0;

	if (g_once_init_enter (&ft_init)) {
		FT_Init_FreeType (&ft_lib);

		g_once_init_leave (&ft_init, (gsize)1);
	}
}

Project: libgxps
File: gxps-archive.c

gxps_zip_archive_iter_next (ZipArchive            *zip,
                            struct archive_entry **entry)
{
        int result;

        result = archive_read_next_header (zip->archive, entry);
        if (result >= ARCHIVE_WARN && result <= ARCHIVE_OK) {
                if (result < ARCHIVE_OK) {
                        g_warning ("Error: %s\n", archive_error_string (zip->archive));
                        archive_set_error (zip->archive, ARCHIVE_OK, "No error");
                        archive_clear_error (zip->archive);
                }

                return TRUE;
        }

        return result != ARCHIVE_FATAL && result != ARCHIVE_EOF;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_class_init (GXPSArchiveInputStreamClass *klass)
{
	GObjectClass      *object_class = G_OBJECT_CLASS (klass);
	GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass);

	object_class->finalize = gxps_archive_input_stream_finalize;

	istream_class->read_fn = gxps_archive_input_stream_read;
	istream_class->skip = gxps_archive_input_stream_skip;
	istream_class->close_fn = gxps_archive_input_stream_close;
}

Project: libgxps
File: gxps-fonts.c

gxps_fonts_new_font_face (GXPSArchive *zip,
			  const gchar *font_uri,
			  GError     **error)
{
	GHashTable        *ft_cache;
	FtFontFace         ft_face;
	FtFontFace        *ft_font_face;
	FT_Face            face;
	cairo_font_face_t *font_face;
	guchar            *font_data;
	gsize              font_data_len;

        if (!gxps_archive_read_entry (zip, font_uri,
                                      &font_data, &font_data_len,
                                      error)) {
                return NULL;
        }

	ft_face.font_data = font_data;
	ft_face.font_data_len = (gssize)font_data_len;

	ft_cache = get_ft_font_face_cache ();
	font_face = g_hash_table_lookup (ft_cache, &ft_face);
	if (font_face) {
		g_free (font_data);

		return font_face;
	}

	if (!gxps_fonts_new_ft_face (font_uri, font_data, font_data_len, &face)) {
		g_set_error (error,
			     GXPS_ERROR,
			     GXPS_ERROR_FONT,
			     "Failed to load font %s", font_uri);
		g_free (font_data);

		return NULL;
	}

	font_face = cairo_ft_font_face_create_for_ft_face (face, 0);
	if (cairo_font_face_set_user_data (font_face,
					   &ft_cairo_key,
					   face,
					   (cairo_destroy_func_t) FT_Done_Face)) {
		g_set_error (error,
			     GXPS_ERROR,
			     GXPS_ERROR_FONT,
			     "Failed to load font %s: %s",
			     font_uri,
			     cairo_status_to_string (cairo_font_face_status (font_face)));
		cairo_font_face_destroy (font_face);
		FT_Done_Face (face);

		return NULL;
	}

	ft_font_face = ft_font_face_new (font_data, (gssize)font_data_len);
	g_hash_table_insert (ft_cache, ft_font_face, font_face);

	return font_face;
}

Project: libgxps
File: gxps-archive.c

_archive_read (struct archive *archive,
	       void           *data,
	       const void    **buffer)
{
	ZipArchive *zip = (ZipArchive *)data;
	gssize read_bytes;

	*buffer = zip->buffer;
	read_bytes = g_input_stream_read (G_INPUT_STREAM (zip->stream),
					  zip->buffer,
					  sizeof (zip->buffer),
					  NULL,
					  &zip->error);
	return read_bytes;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_set_property (GObject      *object,
			   guint         prop_id,
			   const GValue *value,
			   GParamSpec   *pspec)
{
	GXPSArchive *archive = GXPS_ARCHIVE (object);

	switch (prop_id) {
	case PROP_FILE:
		archive->filename = g_value_dup_object (value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

Project: libgxps
File: gxps-archive.c

gxps_archive_finalize (GObject *object)
{
	GXPSArchive *archive = GXPS_ARCHIVE (object);

	g_clear_pointer (&archive->entries, g_hash_table_unref);
	g_clear_object (&archive->filename);
	g_clear_error (&archive->init_error);
	g_clear_object (&archive->resources);

	G_OBJECT_CLASS (gxps_archive_parent_class)->finalize (object);
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_skip (GInputStream  *stream,
				gsize          count,
				GCancellable  *cancellable,
				GError       **error)
{
	return 0;
}

Project: libgxps
File: gxps-archive.c

gxps_zip_archive_destroy (ZipArchive *zip)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
	/* This is a deprecated synonym for archive_read_free() in libarchive
	 * 3.0; but is not deprecated in libarchive 2.0, which we continue to
	 * support. */
	archive_read_finish (zip->archive);
G_GNUC_END_IGNORE_DEPRECATIONS
	g_slice_free (ZipArchive, zip);
}

Project: libgxps
File: gxps-fonts.c

ft_font_face_new (guchar *font_data,
		  gsize   font_data_len)
{
	FtFontFace *ff;

	ff = g_slice_new (FtFontFace);

	ff->font_data = font_data;
	ff->font_data_len = font_data_len;

	return ff;
}

Project: libgxps
File: gxps-archive.c

_archive_close (struct archive *archive,
		void *data)
{
	ZipArchive *zip = (ZipArchive *)data;

	g_clear_object (&zip->stream);

	return ARCHIVE_OK;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_has_entry (GXPSArchive *archive,
			const gchar *path)
{
	if (path == NULL)
		return FALSE;

	if (path[0] == '/')
		path++;

	return g_hash_table_contains (archive->entries, path);
}

Project: libgxps
File: gxps-archive.c

gxps_archive_get_resources (GXPSArchive *archive)
{
	g_return_val_if_fail (GXPS_IS_ARCHIVE (archive), NULL);

	if (archive->resources == NULL)
		archive->resources = g_object_new (GXPS_TYPE_RESOURCES,
		                                   "archive", archive,
		                                   NULL);

	return archive->resources;
}

Project: libgxps
File: gxps-fonts.c

ft_font_face_free (FtFontFace *font_face)
{
	if (!font_face)
		return;

	g_slice_free (FtFontFace, font_face);
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_next_piece (GXPSArchiveInputStream *stream)
{
        gchar *dirname;
        gchar *prefix;

        if (!stream->is_interleaved)
                return;

        dirname = g_path_get_dirname (archive_entry_pathname (stream->entry));
        if (!dirname)
                return;

        stream->piece++;
        prefix = g_strdup_printf ("%s/[%u]", dirname, stream->piece);
        g_free (dirname);

        while (gxps_zip_archive_iter_next (stream->zip, &stream->entry)) {
                if (g_str_has_prefix (archive_entry_pathname (stream->entry), prefix)) {
                        const gchar *suffix = archive_entry_pathname (stream->entry) + strlen (prefix);

                        if (g_ascii_strcasecmp (suffix, ".piece") == 0 ||
                            g_ascii_strcasecmp (suffix, ".last.piece") == 0)
                                break;
                }
                archive_read_data_skip (stream->zip->archive);
        }

        g_free (prefix);
}

Project: libgxps
File: gxps-images.c

gxps_images_create_from_tiff (GXPSArchive *zip,
			      const gchar *image_uri,
			      GError     **error)
{
#ifdef HAVE_LIBTIFF
	TIFF       *tiff;
	TiffBuffer  buffer;
	GXPSImage  *image;
	gint        width, height;
	guint16     res_unit;
	float       res_x, res_y;
	gint        stride;
	guchar     *data;
	guchar     *p;

        if (!gxps_archive_read_entry (zip, image_uri,
                                      &buffer.buffer,
                                      &buffer.buffer_len,
                                      error)) {
                return NULL;
        }

	buffer.pos = 0;

	_tiff_push_handlers ();

	tiff = TIFFClientOpen ("libgxps-tiff", "r", &buffer,
			       _tiff_read,
			       _tiff_write,
			       _tiff_seek,
			       _tiff_close,
			       _tiff_size,
			       _tiff_map_file,
			       _tiff_unmap_file);

	if (!tiff || _tiff_error) {
		fill_tiff_error (error, image_uri);
		if (tiff)
			TIFFClose (tiff);
		_tiff_pop_handlers ();
		g_free (buffer.buffer);
		return NULL;
	}

	if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width) || _tiff_error) {
		fill_tiff_error (error, image_uri);
		TIFFClose (tiff);
		_tiff_pop_handlers ();
		g_free (buffer.buffer);
		return NULL;
	}

	if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height) || _tiff_error) {
		fill_tiff_error (error, image_uri);
		TIFFClose (tiff);
		_tiff_pop_handlers ();
		g_free (buffer.buffer);
		return NULL;
	}

	if (width <= 0 || height <= 0) {
		fill_tiff_error (error, image_uri);
		TIFFClose (tiff);
		_tiff_pop_handlers ();
		g_free (buffer.buffer);
		return NULL;
	}

	image = g_slice_new (GXPSImage);
	image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
						     width, height);
	image->res_x = 96;
	image->res_y = 96;

	if (!TIFFGetField (tiff, TIFFTAG_RESOLUTIONUNIT, &res_unit))
		res_unit = 0;
	if (TIFFGetField (tiff, TIFFTAG_XRESOLUTION, &res_x)) {
		if (res_unit == 2) { /* inches */
			image->res_x = res_x;
		} else if (res_unit == 3) { /* centimeters */
			image->res_x = res_x * CENTIMETERS_PER_INCH;
		}
	}
	if (TIFFGetField (tiff, TIFFTAG_YRESOLUTION, &res_y)) {
		if (res_unit == 2) { /* inches */
			image->res_y = res_y;
		} else if (res_unit == 3) { /* centimeters */
			image->res_y = res_y * CENTIMETERS_PER_INCH;
		}
	}

	if (cairo_surface_status (image->surface)) {
		g_set_error (error,
			     GXPS_ERROR,
			     GXPS_ERROR_IMAGE,
			     "Error loading TIFF image %s: %s",
			     image_uri,
			     cairo_status_to_string (cairo_surface_status (image->surface)));
		gxps_image_free (image);
		TIFFClose (tiff);
		_tiff_pop_handlers ();
		g_free (buffer.buffer);
		return NULL;
	}

	data = cairo_image_surface_get_data (image->surface);
	if (!TIFFReadRGBAImageOriented (tiff, width, height,
					(uint32 *)data,
					ORIENTATION_TOPLEFT, 1) || _tiff_error) {
		fill_tiff_error (error, image_uri);
		gxps_image_free (image);
		TIFFClose (tiff);
		_tiff_pop_handlers ();
		g_free (buffer.buffer);
		return NULL;
	}

	TIFFClose (tiff);
	_tiff_pop_handlers ();
	g_free (buffer.buffer);

	stride = cairo_image_surface_get_stride (image->surface);
	p = data;
	while (p < data + (height * stride)) {
		guint32 *pixel = (guint32 *)p;
		guint8   r = TIFFGetR (*pixel);
		guint8   g = TIFFGetG (*pixel);
		guint8   b = TIFFGetB (*pixel);
		guint8   a = TIFFGetA (*pixel);

		*pixel = (a << 24) | (r << 16) | (g << 8) | b;

		p += 4;
	}

	cairo_surface_mark_dirty (image->surface);

	return image;
#else
	return NULL;
#endif /* #ifdef HAVE_LIBTIFF */
}

Project: libgxps
File: gxps-archive.c

caseless_equal (gconstpointer v1,
                gconstpointer v2)
{
	return g_ascii_strcasecmp (v1, v2) == 0;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_new (GFile   *filename,
		  GError **error)
{
	return g_initable_new (GXPS_TYPE_ARCHIVE,
			       NULL, error,
			       "file", filename,
			       NULL);
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_close (GInputStream  *stream,
				 GCancellable  *cancellable,
				 GError       **error)
{
	GXPSArchiveInputStream *istream = GXPS_ARCHIVE_INPUT_STREAM (stream);

	if (g_cancellable_set_error_if_cancelled (cancellable, error))
		return FALSE;

	g_clear_pointer (&istream->zip, gxps_zip_archive_destroy);

	return TRUE;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_finalize (GObject *object)
{
	GXPSArchiveInputStream *stream = GXPS_ARCHIVE_INPUT_STREAM (object);

	g_clear_pointer (&stream->zip, gxps_zip_archive_destroy);

	G_OBJECT_CLASS (gxps_archive_input_stream_parent_class)->finalize (object);
}

Project: libgxps
File: gxps-archive.c

_archive_open (struct archive *archive,
	       void           *data)
{
	ZipArchive *zip = (ZipArchive *)data;

	zip->stream = g_file_read (zip->file, NULL, &zip->error);

	return (zip->error) ? ARCHIVE_FATAL : ARCHIVE_OK;
}

Project: libgxps
File: gxps-archive.c

gxps_zip_archive_create (GFile *filename)
{
	ZipArchive *zip;

	zip = g_slice_new0 (ZipArchive);
	zip->file = filename;
	zip->archive = archive_read_new ();
	archive_read_support_format_zip (zip->archive);
	archive_read_open2 (zip->archive,
			    zip,
			    _archive_open,
			    _archive_read,
			    _archive_skip,
			    _archive_close);
	return zip;
}

Project: libgxps
File: gxps-archive.c

initable_iface_init (GInitableIface *initable_iface)
{
	initable_iface->init = gxps_archive_initable_init;
}

Project: libgxps
File: gxps-fonts.c

gxps_fonts_get_font (GXPSArchive *zip,
		     const gchar *font_uri,
		     GError     **error)
{
	GHashTable        *fonts_cache;
	cairo_font_face_t *font_face = NULL;

	fonts_cache = g_object_get_data (G_OBJECT (zip), FONTS_CACHE_KEY);
	if (fonts_cache) {
		font_face = g_hash_table_lookup (fonts_cache, font_uri);
		if (font_face)
			return font_face;
	}

	font_face = gxps_fonts_new_font_face (zip, font_uri, error);
	if (font_face) {
		if (!fonts_cache) {
			fonts_cache = g_hash_table_new_full (g_str_hash,
							     g_str_equal,
							     (GDestroyNotify)g_free,
							     (GDestroyNotify)cairo_font_face_destroy);
			g_object_set_data_full (G_OBJECT (zip), FONTS_CACHE_KEY,
						fonts_cache,
						(GDestroyNotify)g_hash_table_destroy);
		}

		g_hash_table_insert (fonts_cache,
				     g_strdup (font_uri),
				     cairo_font_face_reference (font_face));
	}

	return font_face;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_open (GXPSArchive *archive,
		   const gchar *path)
{
	GXPSArchiveInputStream *stream;
	gchar                  *first_piece_path = NULL;

	if (path == NULL)
		return NULL;

	if (path[0] == '/')
		path++;

	if (!g_hash_table_contains (archive->entries, path)) {
                first_piece_path = g_build_path ("/", path, "[0].piece", NULL);
                if (!g_hash_table_contains (archive->entries, first_piece_path)) {
                        g_free (first_piece_path);

                        return NULL;
                }
                path = first_piece_path;
        }

	stream = (GXPSArchiveInputStream *)g_object_new (GXPS_TYPE_ARCHIVE_INPUT_STREAM, NULL);
	stream->zip = gxps_zip_archive_create (archive->filename);
        stream->is_interleaved = first_piece_path != NULL;

        while (gxps_zip_archive_iter_next (stream->zip, &stream->entry)) {
                if (g_ascii_strcasecmp (path, archive_entry_pathname (stream->entry)) == 0)
                        break;
                archive_read_data_skip (stream->zip->archive);
        }

        g_free (first_piece_path);

	return G_INPUT_STREAM (stream);
}

Project: libgxps
File: gxps-fonts.c

ft_font_face_equal (gconstpointer v1,
		    gconstpointer v2)
{
	FtFontFace *ft_face_1 = (FtFontFace *)v1;
	FtFontFace *ft_face_2 = (FtFontFace *)v2;

	if (ft_face_1->font_data_len != ft_face_2->font_data_len)
		return FALSE;

	return memcmp (ft_face_1->font_data, ft_face_2->font_data, ft_face_1->font_data_len) == 0;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_init (GXPSArchiveInputStream *istream)
{
}

Project: libgxps
File: gxps-fonts.c

parse_guid (gchar *string, unsigned short guid[16])
{
	// Maps bytes to positions in guidString
	static const int indexes[] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34};
	int i;

	if (strlen (string) <= 35) {
		return FALSE;
	}

	for (i = 0; i < 16; i++) {
		guint hex1;
		guint hex2;

		if (!hex_int (string + indexes[i], 1, &hex1) ||
		    !hex_int (string + indexes[i] + 1, 1, &hex2))
			return FALSE;

		guid[i] = hex1 * 16 + hex2;
	}

	return TRUE;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_input_stream_is_last_piece (GXPSArchiveInputStream *stream)
{
        return g_str_has_suffix (archive_entry_pathname (stream->entry), ".last.piece");
}

Project: libgxps
File: gxps-archive.c

_archive_skip (struct archive *archive,
	       void           *data,
	       __LA_INT64_T    request)
{
	ZipArchive *zip = (ZipArchive *)data;

	if (!g_seekable_can_seek (G_SEEKABLE (zip->stream)))
		return 0;

	g_seekable_seek (G_SEEKABLE (zip->stream),
			 request,
			 G_SEEK_CUR,
			 NULL,
			 &zip->error);

	if (zip->error) {
		g_clear_error (&zip->error);
		request = 0;
	}

	return request;
}

Project: libgxps
File: gxps-fonts.c

get_ft_font_face_cache (void)
{
	if (g_once_init_enter (&ft_font_face_cache)) {
		GHashTable *h;

		h = g_hash_table_new_full (ft_font_face_hash,
					   ft_font_face_equal,
					   (GDestroyNotify)ft_font_face_free,
					   (GDestroyNotify)cairo_font_face_destroy);
		g_once_init_leave (&ft_font_face_cache, (gsize)h);
	}

	return (GHashTable *)ft_font_face_cache;
}

Project: libgxps
File: gxps-archive.c

caseless_hash (gconstpointer v)
{
	gchar *lower;
	guint ret;

	lower = g_ascii_strdown (v, -1);
	ret = g_str_hash (lower);
	g_free (lower);

	return ret;
}

Project: libgxps
File: gxps-fonts.c

hex_int (const gchar *spec,
	 gint         len,
	 guint       *c)
{
	const gchar *end;

	*c = 0;
	for (end = spec + len; spec != end; spec++) {
		if (!g_ascii_isxdigit (*spec))
			return FALSE;

		*c = g_ascii_xdigit_value (*spec);
	}

	return TRUE;
}

Project: libgxps
File: gxps-archive.c

gxps_archive_init (GXPSArchive *archive)
{
	archive->entries = g_hash_table_new_full (caseless_hash, caseless_equal, g_free, NULL);
}

Project: libgxps
File: gxps-archive.c

gxps_archive_read_entry (GXPSArchive *archive,
			 const gchar *path,
			 guchar     **buffer,
			 gsize       *bytes_read,
			 GError     **error)
{
	GInputStream *stream;
	gssize        entry_size;
	gboolean      retval;

	stream = gxps_archive_open (archive, path);
	if (!stream) {
                g_set_error (error,
                             G_IO_ERROR,
                             G_IO_ERROR_NOT_FOUND,
                             "The entry '%s' was not found in archive", path);
		return FALSE;
        }

	entry_size = archive_entry_size (GXPS_ARCHIVE_INPUT_STREAM (stream)->entry);
	if (entry_size <= 0) {
		gssize bytes;
		guchar buf[BUFFER_SIZE];
		guint  buffer_size = BUFFER_SIZE * 4;

		/* In some cases, I don't know why, archive_entry_size() returns 0,
		 * but the entry can be read, so let's try here.
		 */
		*bytes_read = 0;
		*buffer = g_malloc (buffer_size);
		do {
			bytes = g_input_stream_read (stream, &buf, BUFFER_SIZE, NULL, error);
			if (bytes < 0) {
				g_free (*buffer);
				g_object_unref (stream);

				return FALSE;
			}

			if (*bytes_read + bytes > buffer_size) {
				buffer_size += BUFFER_SIZE * 4;
				*buffer = g_realloc (*buffer, buffer_size);
			}
			memcpy (*buffer + *bytes_read, buf, bytes);
			*bytes_read += bytes;
		} while (bytes > 0);

		g_object_unref (stream);

		if (*bytes_read == 0) {
                        g_set_error (error,
                                     G_IO_ERROR,
                                     G_IO_ERROR_INVALID_DATA,
                                     "The entry '%s' is empty in archive", path);
			g_free (*buffer);
			return FALSE;
		}

		return TRUE;
	}

	*buffer = g_malloc (entry_size);
	retval = g_input_stream_read_all (stream,
					  *buffer, entry_size,
					  bytes_read, NULL,
					  error);
	if (!retval)
		g_free (*buffer);

	g_object_unref (stream);

	return retval;
}

Project: libgxps
File: gxps-fonts.c

gxps_fonts_new_ft_face (const gchar *font_uri,
			guchar      *font_data,
			gsize        font_data_len,
			FT_Face     *face)
{
	init_ft_lib ();

	if (FT_New_Memory_Face (ft_lib, font_data, font_data_len, 0, face)) {
		/* Failed to load, probably obfuscated font */
		gchar         *base_name;
		unsigned short guid[16];

		base_name = g_path_get_basename (font_uri);
		if (!parse_guid (base_name, guid)) {
			g_warning ("Failed to parse guid for font %s\n", font_uri);
			g_free (base_name);

			return FALSE;
		}
		g_free (base_name);

		if (font_data_len >= 32) {
			// Obfuscation - xor bytes in font binary with bytes from guid (font's filename)
			static const gint mapping[] = {15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3};
			gint i;

			for (i = 0; i < 16; i++) {
				font_data[i] ^= guid[mapping[i]];
				font_data[i + 16] ^= guid[mapping[i]];
			}

			if (FT_New_Memory_Face (ft_lib, font_data, font_data_len, 0, face))
				return FALSE;
		} else {
			g_warning ("Font file is too small\n");
			return FALSE;
		}
	}

	return TRUE;
}

Project: libgxps
File: gxps-fonts.c

ft_font_face_hash (gconstpointer v)
{
	FtFontFace *ft_face = (FtFontFace *)v;
	guchar     *bytes = ft_face->font_data;
	gssize      len = ft_face->font_data_len;
	guint       hash = 5381;

	while (len--) {
		guchar c = *bytes++;

		hash *= 33;
		hash ^= c;
	}

	return hash;
}