In order to comply with the EU General Data Protection Regulation (GDPR) all the MAC addresses detected by Accuware WiFi Location Monitor (made available through the real-time API and the CSV service) are hashed one-way which means that:
- starting from the hashed MAC address it is IMPOSSIBLE to compute the original MAC address.
- starting from the original MAC address it is possible to compute the hashed MAC address (with the algorithm described below).
Specifically there are 2 types of hash available:
- Partial Hash (applied by default for every new site created) – only the last part (last 3 octets) of the MAC address is hashed allowing thus to identify the vendor of the Wi-Fi chip using an official repository like this one or using an API like this one. The partially hashed MAC addresses have the first 3 octets identical to the first 3 octets of the original MAC address, then the “H” letter and then the hash of the remaining part of the original MAC address:
- EXAMPLE: original MAC address: 14:8F:C6:11:22:22 → hashed MAC address: 14:8F:C6:H5:21:YD (14:8F:C6:XX:XX:XX -> Apple Inc.)
- Fully Hash – the MAC address is completely hashed. The fully hashed MAC addresses begins with the “H” letter:
- EXAMPLE: original MAC address: 14:8F:C6:11:22:22 → hashed MAC address: HK:ZB:KZ:R5:21:YD
The location of a station is represented in JSON format as in the following example:
{ "mac": "148FC6H521YD", "name": "Tablet", "desc": "iPad 3", "loc": { "lat": 32.797585, "lng": -117.249064, "levelId": 0, "prec": 5 //do not consider this field }, "lrrt": 1, "rss": { "AC8674106778": -81, "AC8674102580": -63, "AC8674106780": -80 } }
How to convert a MAC address into a hashed MAC address
Below you can find 2 examples of algorithms (written in Ruby and Java) that allows to compute the hashed MAC address (partial or full hashed) starting from a MAC address.
NOTE: the variable site key passed to the hashing function MUST be hard coded to a string provided by Accuware (which is NOT to the siteID!)
Ruby
Code
require 'digest/md5' def hash_mac(mac_str, site_key, keep_vendor) hash = Digest::MD5.hexdigest(mac_str.upcase + site_key).to_i(16).to_s(36)[-11..-1] keep_vendor ? (mac_str[0..5] + 'h' + hash[6..10]).upcase : ('h' + hash).upcase end
Example
hash_mac("000011112222", "abcd", false) => "HKZBKZR521YD" hash_mac("000011112222", "abcd", true) => "000011H521YD"
C#
Code
using System; using System.Numerics; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; namespace AccuwareMacHasher { class Program { public static byte[] addByteToArray(byte[] bArray, byte newByte) { byte[] newArray = new byte[bArray.Length + 1]; bArray.CopyTo(newArray, 1); newArray[0] = newByte; return newArray; } static int Main(string[] args){ if (args.Length < 2 || args.Length > 3) { Console.WriteLine("Please enter valid arguments"); Console.WriteLine("Usage: AccuwareMacHasher [-partial] MAC_ADDRESS SITE_KEY"); Console.WriteLine("Example: AccuwareMacHasher 01-23-45-67-89-0A ABCD"); Console.WriteLine("Example: AccuwareMacHasher -partial 01-23-45-67-89-0A ABCD"); return 1; } bool partial = false; string mac = ""; string site = ""; if (args.Length == 3 && args[0].Equals("-partial", StringComparison.CurrentCultureIgnoreCase)){ partial = true; mac = args[1]; site = args[2]; } else{ mac = args[0]; site = args[1]; } bool isValid = Regex.IsMatch(mac, @ "(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}|(?:[0-9a-fA-F]{2}-){5}[0-9a-fA-F]{2}|(?:[0-9a-fA-F]{2}){5}[0-9a-fA-F]{2}"); if (!isValid) { Console.WriteLine("Please provide a valid Mac Address."); return 1; } mac = mac.Replace(" ", "").Replace(":", "").Replace("-", ""); byte[] inputBytes = Encoding.ASCII.GetBytes(mac.ToUpper() + site); Console.WriteLine(mac + site); MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = md5.ComputeHash(inputBytes); byte[] resultb = addByteToArray(result, 0); string hash = ToBase36String(resultb); Console.WriteLine(hash); hash = hash.Substring(hash.Length - 11); if (partial){ Console.WriteLine(mac.Substring(0, 6).ToUpper() + "H" + hash.Substring(6)); } else{ Console.WriteLine("H" + hash); } Console.WriteLine("Press any key to close"); Console.ReadKey(); return 0; } public static string ToBase36String(byte[] toConvert) { const string alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (BitConverter.IsLittleEndian) { Array.Reverse(toConvert); } BigInteger dividend = new BigInteger(toConvert); var builder = new StringBuilder(); while (dividend != 0) { BigInteger remainder; dividend = BigInteger.DivRem(dividend, 36, out remainder); builder.Insert(0, alphabet[Math.Abs(((int) remainder))]); } return builder.ToString(); } } }
JAVA
Code
public static String HASHED_MAC_PREFIX = "H"; /** * Hash a MAC address */ static public String hashMac(String macString, String siteKey, boolean keepVendor) { byte[] macBytes = (macString + siteKey).getBytes(); MessageDigest md = null; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { return null; } byte[] digest = md.digest(macBytes); BigInteger bi = new BigInteger(1, digest); String biString = bi.toString(36); String hash = biString.substring(biString.length() - 11, biString.length()); if (keepVendor) { return mac.toString().substring(0, 6) + HASHED_MAC_PREFIX + hash.substring(6, 11); } else { return HASHED_MAC_PREFIX + hash; } }