bertt / mapbox-vector-tile-cs

A .NET library for decoding a Mapbox vector tile
MIT License
74 stars 14 forks source link

Decryption of Encrypted Vector Tiles #15

Closed ImCarrot closed 6 years ago

ImCarrot commented 7 years ago

Hi, I'll try to be brief, but I'll share the whole picture.

Problem Statement

I am using vector tile from tippecanoe from mapbox to create .pbtiles from my geojson data. The issue is, on a web client when I see the inspect element and download the .pbf and run it by this (mapbox-vector-tile-cs) library, I am able to successfully get the data from the tile. Which means that any one with some basic google search can also steal my data from the vector tiles.

What I was able to achieve

To avoid the security concern, with the short timeline I have, I came up with a quick and dirty way. After tippecanoe creates the .mbtiles sqlite db, I run a java utility I made to encrypt the data in the blob using AES 256 encryption and stored it in two different ways in two different sqlite db's:

  1. Stored as bytes into a different .mbtiles sqlite db (which get's stored as Blob). Along with z, x, y and metadata

  2. Encoded the encrypted data as base64 and then stored the base64encoded encrypted tile data into a string data type column. Along with z, x, y and metadata.

    and stored the key (base64 encoded) and initialization vector (base64 encoded) into a file.

The API side (Question 1)

Now, when I get the non encrypted .pbf from the API, a header of type gzip and application/x-protobuf is set that helps to convert the unencrypted blob data to a protobuf and returns a .pbf file that gets downloaded.

Now when I try to get the encrypted data from the API with the same header as the non encrypted on, the download of the .pbf fails saying Failed - Network error. I realized that it's being caused as the header application/x-protobuf is trying to package the file into a .pbf while the contents of the blob might not be matching what's expected and hence the result.

I removed the header application/x-protobuf and since I can't gzip now, i removed the header of gzip too. Now the data gets displayed on the chrome browser instead of being downloaded, I figure as now it's just a random response.

The question is, How can I make it to send a .pbf that has encrypted data in it and this((mapbox-vector-tile-cs)) library can parse the data? I know the data will be need to be decrypted first before I pass it for parsing assuming that it's decrypted and I have the data that was stored into the blob of the .mbtiles.

This Library with a UWP project (Question 2)

So now currently as mentioned above (since i don't have a solution to the headers part) I removed the headers and let the API return me a direct response.

The Issue now I am facing is that when I pass in the decryted (I checked the decryption was successful and the decrypted data is an exact match to the what was stored in the Blob) Blob data to the

var layerInfos = VectorTileParser.Parse(stream);

code line returns me an IEnumerable<Tile> that is not null but has 0 layers in it. while the actual tile contains 5 layers in it.

My Question is, how do I get this((mapbox-vector-tile-cs)) library to return me the layers.

The code to fetch the tile from the server and decrypt before I send it for parsing is as below:

 //this code downloads the tile, layerInfos is returned as an empty collection
 private async Task<bool> ProcessTile(TileData t, int xOffset, int yOffset)
 {
      var stream = await GetTileFromWeb(EncryptedTileURL,true);

      if (stream == null)
          return false;

      var layerInfos = VectorTileParser.Parse(stream);

      if (layerInfos.Count == 0)
          return false;

      return true;
 }

The tiles are fetched from the server using a GetTileFromWeb() method:

    private async Task<Stream> GetTileFromWeb(Uri uri, bool GetEnc = false)
    {
        var handler = new HttpClientHandler();

        if (!GetEnc)
            handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

        var gzipWebClient = new HttpClient(handler);

        var bytes = gzipWebClient.GetByteArrayAsync(uri).Result;

        if (GetEnc)
        {
            var decBytes = await DecryptData(bytes);
            return decBytes;
        }

        var stream = new MemoryStream(bytes);
        return stream;
    }

PS: Sorry for such a long question, I am not used to such elaborate detail, but seemed I need to share more as Encryption is my forte while map data vector tiles isn't.

bertt commented 6 years ago

Encryption of vector tiles is not part of specification nor this implementation. Maybe you've to look at other methods of data distribution (like webservices with proper authentication/authorisation) to reach your goal. Closing this issue for now.